\documentclass{article}
\usepackage{axiom}
\begin{document}
\title{\$SPAD/src/graph/view3D component3d.c}
\author{The Axiom Team}
\maketitle
\begin{abstract}
\end{abstract}
\eject
\tableofcontents
\eject
\section{License}
<<license>>=
/*
Copyright (c) 1991-2002, The Numerical ALgorithms Group Ltd.
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

    - Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.

    - Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the
      distribution.

    - Neither the name of The Numerical ALgorithms Group Ltd. nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
@
<<*>>=
<<license>>

#define _COMPONENT3D_C
#include "axiom-c-macros.h"
#include "useproto.h"

#include "header.h"
#include "draw.h"

#include "Gfun.H1"
#include "util.H1"
#include "XSpadFill.H1"

#include "all_3d.H1"

#define axisLength	1.0   /* use 100.0, if data is not to be normalized */

#define samePoint(a,b) ((refPt3D(viewData,a)->x == refPt3D(viewData,b)->x) &&\
			(refPt3D(viewData,a)->y == refPt3D(viewData,b)->y) &&\
			(refPt3D(viewData,a)->z == refPt3D(viewData,b)->z))
#define MAX_POINT	1000.0
#define MIN_POINT	-1000.0


void 
#ifdef _NO_PROTO
scaleComponents ()
#else
scaleComponents (void)
#endif
{
  
  double xRange,yRange,zRange;
  int i;
  viewTriple *aPoint;
  
  /* Temporary range limits until the three dimensional clipping
     package is fully functional */
  
  if (viewData.xmin < MIN_POINT) viewData.xmin = MIN_POINT;
  if (viewData.xmax > MAX_POINT) viewData.xmax = MAX_POINT;
  if (viewData.ymin < MIN_POINT) viewData.ymin = MIN_POINT;
  if (viewData.ymax > MAX_POINT) viewData.ymax = MAX_POINT;
  if (viewData.zmin < MIN_POINT) viewData.zmin = MIN_POINT;
  if (viewData.zmax > MAX_POINT) viewData.zmax = MAX_POINT;
  
  xRange = viewData.xmax - viewData.xmin;
  yRange = viewData.ymax - viewData.ymin;
  zRange = viewData.zmax - viewData.zmin;
  
  /* We scale down, normalize the data, if it is coming from AXIOM
     (handled by viewman).  If the data is coming from a file (handled by
     viewAlone) then it should already been scaled down. 
     */
  
  /* Find the coordinate axis with the larges range of data and scale
     the others relative to it. 
     */
  /* compare x and y ranges */
  if (xRange > yRange) {
    if (xRange > zRange) {
      if (absolute(viewData.xmax) >= absolute(viewData.xmin))
	viewData.scaleToView = axisLength/(absolute(viewData.xmax));
      else
	viewData.scaleToView = axisLength/(absolute(viewData.xmin));
    } else {
      if (absolute(viewData.zmax) >= absolute(viewData.zmin))
	viewData.scaleToView = axisLength/(absolute(viewData.zmax));
      else
	viewData.scaleToView = axisLength/(absolute(viewData.zmin));
    }
  } else {
    if (yRange > zRange) {
      if (absolute(viewData.ymax) >= absolute(viewData.ymin))
	viewData.scaleToView = axisLength/(absolute(viewData.ymax));
      else
	viewData.scaleToView = axisLength/(absolute(viewData.ymin));
    } else {
      if (absolute(viewData.zmax) >= absolute(viewData.zmin))
	viewData.scaleToView = axisLength/(absolute(viewData.zmax));
      else
	viewData.scaleToView = axisLength/(absolute(viewData.zmin));
    }
  }
  
  /* We now normalize all the points in this program. The information
     needed to link the normalized set of points back to the real object
     space scale created in AXIOM is held in viewData.scaleToView. */
  viewData.xmin *= viewData.scaleToView;
  viewData.xmax *= viewData.scaleToView;
  viewData.ymin *= viewData.scaleToView;
  viewData.ymax *= viewData.scaleToView;
  viewData.zmin *= viewData.scaleToView;
  viewData.zmax *= viewData.scaleToView;
  viewData.clipXmin = viewData.xmin;
  viewData.clipXmax = viewData.xmax;
  viewData.clipYmin = viewData.ymin;
  viewData.clipYmax = viewData.ymax;
  viewData.clipZmin = viewData.zmin;
  viewData.clipZmax = viewData.zmax;
  
  for (i=0, aPoint=viewData.points; i<viewData.numOfPoints; i++,aPoint++) {
    aPoint->x *= viewData.scaleToView;
    aPoint->y *= viewData.scaleToView;
    aPoint->z *= viewData.scaleToView;
  }
  
} /* scaleComponents() */


/*
 void makeTriangle(a,b,c)   
 Given three indices to three points, a triangular polygon is created 
 and inserted into the polygon list of viewData. If two or more of the 
 points are coincidental, no polygon is created since that would be a 
 degenerate (collapsed) polygon.
 */

void
#ifdef _NO_PROTO
makeTriangle (a, b, c)
     int a,b,c;
#else
makeTriangle (int a, int b, int c)
#endif
{
  poly *aPoly;
  
  if (!(samePoint(a,b) || samePoint(b,c) || samePoint(c,a))) {
    /* create triangle only if the three vertex points are distinct */
    aPoly = (poly *)saymem("component.c",1,sizeof(poly));
    aPoly->num = aPoly->sortNum = viewData.numPolygons++;
    aPoly->split = aPoly->moved = no;
    aPoly->numpts = 3;
    aPoly->primitiveType = polygonComponent;
    aPoly->indexPtr = (int *)saymem("component.c",3,sizeof(int));
    *(aPoly->indexPtr)	   = a;
    *(aPoly->indexPtr + 1) = b;
    *(aPoly->indexPtr + 2) = c;
    aPoly->doNotStopDraw = yes;
    aPoly->next = viewData.polygons;
    viewData.polygons = aPoly;
  } /* if all points are unique */
  
} /* makeTriangle() */




/*
  void triangulate()
 
  Only if there is more than one list do we triangulate; a single list 
  is used for either a space curve or simply a point.  Actually, in that 
  case, we now make "flat" *polygons, flagged by the primitiveType field 
  (pointComponent, etc. in tube.h).  We need to examine two lists at a time
  (and if the structure is closed, the last and first as well). For every 
  three points in the two lists, alternating between one in one and two in
  the other, we construct triangles. If one list is shorter, then its last 
  point becomes the vertex for the remaining pairs of points from the other
  list.  It turns out that any distribution of points in the two lists 
  (preserving cyclic order) will produce the same desired polygon.
  */

void
#ifdef _NO_PROTO
triangulate ()
#else
triangulate (void)
#endif
{
  
  int u,l;
  int uBound,lBound;
  int i,j,k;
  LLPoint *anLLPoint;
  LPoint *list1,*list2;
  poly *aPoly;
  
  anLLPoint = viewData.lllp.llp;
  for (i=0; i<viewData.lllp.numOfComponents; i++,anLLPoint++) {
    if (anLLPoint->numOfLists > 1) {
      list2 = anLLPoint->lp;
      for (j=1; j<anLLPoint->numOfLists; j++) {
	list1 = list2;
	list2 = list1 + 1;
	u = l = 0;
	uBound = u+1 < list1->numOfPoints;
	lBound = l+1 < list2->numOfPoints;
	while (uBound || lBound) {
	  if (uBound) {
	    makeTriangle(*(list1->indices + u + 1),
			 *(list1->indices + u), *(list2->indices + l));
	    u++;
	    uBound = u+1 < list1->numOfPoints;
	  }
	  if (lBound) {
	    makeTriangle(*(list2->indices + l),
			 *(list2->indices + l + 1), *(list1->indices + u));
	    l++;
	    lBound = l+1 < list2->numOfPoints;
	  }
	} /* while (uBound || lBound) */
      } /* for j<anLLPoint->numOfLists */
    } /* if anLLPoint->numOfLists > 1 */ 
    else {
      /* if anLLPoint->numOfLists <= 1...assume this means =1 */
      /* Flat polygons are to be drawn when hidden 
	 surface algorithm is used.*/
      if (anLLPoint->numOfLists == 1) {
	if (anLLPoint->lp->numOfPoints == 1) {
	  /* this graph is a single point */
	  aPoly = (poly *)saymem("component.c",1,sizeof(poly));
	  aPoly->num = aPoly->sortNum = viewData.numPolygons++;
	  aPoly->split = aPoly->moved = no;
	  aPoly->primitiveType = pointComponent;
	  aPoly->numpts = 1;
	  aPoly->indexPtr = (int *)saymem("component.c",1,intSize);
	  *(aPoly->indexPtr) = *(anLLPoint->lp->indices);
	  aPoly->doNotStopDraw = yes;
	  aPoly->next = viewData.polygons;
	  viewData.polygons = aPoly;
	} else {
	  /* this graph is a curve */
	  for (k=0; k<anLLPoint->lp->numOfPoints-1; k++) {
	    aPoly = (poly *)saymem("component.c",1,sizeof(poly));
	    aPoly->num = aPoly->sortNum = viewData.numPolygons++;
	    aPoly->split = aPoly->moved = no;
	    aPoly->primitiveType = lineComponent; /* curveComponent */
	    aPoly->numpts = 2;
	    aPoly->indexPtr =
	      (int *)saymem("component.c",2,sizeof(int));
	    *(aPoly->indexPtr)	 = *(anLLPoint->lp->indices + k);
	    *(aPoly->indexPtr+1) = *(anLLPoint->lp->indices + k + 1);
	    aPoly->doNotStopDraw = yes;
	    aPoly->next = viewData.polygons;
	    viewData.polygons = aPoly;
	  }  /* for k */
	  if (anLLPoint->lp->prop.closed) {
	    aPoly = (poly *)saymem("component.c",1,sizeof(poly));
	    aPoly->num = aPoly->sortNum = viewData.numPolygons++;
	    aPoly->split = aPoly->moved = no;
	    aPoly->primitiveType = lineComponent; /* curveComponent */
	    aPoly->numpts = 2;
	    aPoly->indexPtr =
	      (int *)saymem("component.c",2,sizeof(int));
	    *(aPoly->indexPtr)	 = *(anLLPoint->lp->indices + k);
	    *(aPoly->indexPtr+1) = *(anLLPoint->lp->indices);
	    aPoly->doNotStopDraw = yes;
	    aPoly->next = viewData.polygons;
	    viewData.polygons = aPoly;
	  } /* if list of points is closed */
	}  /* else */
      } /* point, line, polygon, surface components are taken care of above */
    } /* else anLLPoint->numOfLists <= 1 */
  } /* for LLPoints in LLLPoints (i) */
  
} /* triangulate */



void
#ifdef _NO_PROTO
readComponentsFromViewman ()
#else
readComponentsFromViewman (void)
#endif
{
  int i,j,k;
  LLPoint *anLLPoint;
  LPoint *anLPoint;
  viewTriple *aPoint;
  /* maxLength holds the max(llp,lp) figure regarding how large to
     make the array of XPoints, i.e. quadMesh, for use in calling XDraw(). */
  int maxLength=0;
  
  int *anIndex;
  
  readViewman(&(viewData.numOfPoints),intSize);
  aPoint = viewData.points =
    (viewTriple *)saymem("component.c",viewData.numOfPoints,
			 sizeof(viewTriple));
  for (i=0; i<viewData.numOfPoints; i++, aPoint++) {
    readViewman(&(aPoint->x),floatSize);
    readViewman(&(aPoint->y),floatSize);
    readViewman(&(aPoint->z),floatSize);
    readViewman(&(aPoint->c),floatSize);
#ifdef NANQ_DEBUG
    if (!(aPoint->z < 0) && !(aPoint->z > 0) && !(aPoint->z == 0))
      fprintf(stderr,"%g\n", aPoint->z);
#endif
  }
  
  readViewman(&(viewData.lllp.numOfComponents),intSize);
  anLLPoint = viewData.lllp.llp =
    (LLPoint *)saymem("component.c, i",viewData.lllp.numOfComponents,
		      sizeof(LLPoint));
  for (i=0; i<viewData.lllp.numOfComponents; i++,anLLPoint++) {
    readViewman(&(anLLPoint->prop.closed),intSize);
    readViewman(&(anLLPoint->prop.solid),intSize);
    readViewman(&(anLLPoint->numOfLists),intSize);
    anLPoint = anLLPoint->lp =
      (LPoint *)saymem("component.c, ii",anLLPoint->numOfLists,
		       sizeof(LPoint));
    for (j=0; j<anLLPoint->numOfLists; j++,anLPoint++) {
      if (anLLPoint->numOfLists > maxLength)
	maxLength = anLLPoint->numOfLists;
      readViewman(&(anLPoint->prop.closed),intSize);
      readViewman(&(anLPoint->prop.solid),intSize);
      readViewman(&(anLPoint->numOfPoints),intSize);
      anIndex = anLPoint->indices =
	(int *)saymem("component.c, index",anLPoint->numOfPoints,intSize);
      if (anLPoint->numOfPoints > maxLength)
	maxLength = anLPoint->numOfPoints;
      for (k=0; k<anLPoint->numOfPoints; k++,anIndex++) {
	readViewman(anIndex,intSize);
	/* AXIOM arrays are one based, C arrays are zero based */
	if (!viewAloned) (*anIndex)--;
      }
    } /* for LPoints in LLPoints (j) */
  } /* for LLPoints in LLLPoints (i) */
  
  quadMesh = (XPoint *)saymem("component.c",maxLength+2,sizeof(XPoint));
  
} /* readComponentsFromViewman() */



/*
  void calcNormData()	    *
  Calculates the surface normals for the polygons that make up the tube.
  Also finds the fourth coefficient to the plane equation:
  Ax + By + Cz + D = 0  
  A,B, and C are in the normal N[3] and D is the planeConst.
  Figures out the color as well (from the average of the points) and 
  resets the moved flag    
  */

void
#ifdef _NO_PROTO
calcNormData ()
#else
calcNormData (void)
#endif
{
  
  poly *aPoly;
  int *index;
  
  for (aPoly = viewData.polygons; aPoly != NIL(poly); aPoly = aPoly->next) {
    index = aPoly->indexPtr;
    switch (aPoly->primitiveType) {
    case pointComponent:
    case lineComponent:
      aPoly->moved = 0;
      aPoly->color = refPt3D(viewData,*index)->c;
      break;
    default:
      /*
	The following line takes 3 consecutive points and asks
	for the normal vector defined by them. This assumes that
	these do not contain co-linear points. For some reason,
	co-linear points are allowed, this needs to be changed.
	*/
      getMeshNormal(refPt3D(viewData,*index)->x,
		    refPt3D(viewData,*index)->y,
		    refPt3D(viewData,*index)->z,
		    refPt3D(viewData,*(index+1))->x,
		    refPt3D(viewData,*(index+1))->y,
		    refPt3D(viewData,*(index+1))->z,
		    refPt3D(viewData,*(index+2))->x,
		    refPt3D(viewData,*(index+2))->y,
		    refPt3D(viewData,*(index+2))->z, 0.0, 1.0, aPoly->N);
      
      /* calculate the constant term, D,  for the plane equation */
      aPoly->planeConst =
	-(aPoly->N[0] * refPt3D(viewData,*index)->x +
	  aPoly->N[1] * refPt3D(viewData,*index)->y +
	  aPoly->N[2] * refPt3D(viewData,*index)->z);
      aPoly->moved = 0;
      aPoly->color = (refPt3D(viewData,*index)->c +
		      (refPt3D(viewData,*(index+1)))->c +
		      (refPt3D(viewData,*(index+2)))->c) / 3.0;
      break;
    } /* switch */
  }
  
}  /* calcNormData() */



/*
  viewPoints *make3DComponents()

  Read in all the 3D data from the viewport manager and construct the 
  model of it. The model is based upon a list of lists of lists of points. 
  Each top level list makes a component in 3-space. The interpretation 
  really begins at the level below that, where the list of lists of 
  points is. For 3D explicit equations of two variables, the closed 
  boolean for this level is False and the closed boolean for each sublist 
  is False as well. For 3D parameterized curves of one variable, the 
  closed boolean for this level is defined by the user from AXIOM , 
  (which defaults to False) and the closed boolean for each sublist is True.
  */

viewPoints *
#ifdef _NO_PROTO
make3DComponents ()
#else
make3DComponents (void)
#endif
{
  viewPoints *graphData;

  readComponentsFromViewman();

  /* The initial boundaries for the clipping region are set to those
     of the boundaries of the data region. */
  viewData.clipXmin = viewData.xmin;  viewData.clipXmax = viewData.xmax;
  viewData.clipYmin = viewData.ymin;  viewData.clipYmax = viewData.ymax;
  viewData.clipZmin = viewData.zmin;  viewData.clipZmax = viewData.zmax;
  
  /* normalize the data coordinates */
  if (viewData.scaleDown) scaleComponents();
  viewData.numPolygons = 0;
  /* initially the list of polygons is empty */
  viewData.polygons = NIL(poly);
  /* create the polygons; (sets viewData.polygons and viewData.numPolygons) */
  triangulate();
  /* calculate the plane equations for all the polygons */
  calcNormData();
  
  graphData = makeViewport();
  
  imageX = XCreateImage(/* display */        dsply,
			/* visual */         DefaultVisual(dsply,scrn),
			/* depth */          DefaultDepth(dsply,scrn),
			/* format */         ZPixmap,
			/* offset */         0,
			/* data */           NULL,
			/* width */          vwInfo.width,
			/* height */         1,
			/* bitmap_pad */     32,
			/* bytes_per_line */ 0);
  imageX->data = NIL(char);
  
  /* windowing displaying */
  writeTitle();
  postMakeViewport();
  drawViewport(Xoption);
  firstTime = yes;
  XMapWindow(dsply, graphData->viewWindow);
  XMapWindow(dsply, graphData->titleWindow);
  XFlush(dsply);
  
  return(graphData);
  
} /* make3DComponents */





void
#ifdef _NO_PROTO
draw3DComponents (dFlag)
     int dFlag;
#else
draw3DComponents (int dFlag)
#endif
{
  
  int	     i, j, k, hue, x1, y1, x2, y2;
  LLPoint    *anLLPoint;
  LPoint     *anLPoint;
  int	     *anIndex;
  int	     componentType;  /* what the component is to be interpreted as */
  int	     clip_a,clip_i;  /* for use in wire mesh mode clipping */
  XEvent     peekEvent;
  viewTriple *aLPt;
  XPoint     line[2];
  RGB	     col_rgb;
  
  calcEyePoint();
  while ((XPending(dsply) > 0) && (scanline > 0))
    XNextEvent(dsply,&peekEvent);
  switch (viewData.style) {
    
  case transparent:
    GSetLineAttributes(componentGC,0,LineSolid,CapButt,JoinMiter,dFlag);
    if (dFlag==Xoption) {
      if (mono || viewport->monoOn)
	GSetForeground(componentGC, (float)foregroundColor, dFlag);
      else
	GSetForeground(componentGC, (float) meshOutline, dFlag);
    } else {
      GSetForeground(componentGC, psBlack, dFlag);
    }
    /* no need to check "keep drawing" for ps */
    if (dFlag == Xoption) drawMore = keepDrawingViewport();
    
    /*
      This is where we interpret the list of lists of lists of points struct.
      We want to extract the following forms of data:
      - individual points (drawn as filled points)
      - lines (space curves)
      - defined polygon primitives
      - surfaces
      the last one is the one that will replace the function of 2 variables,
      tubes as well as 3D parameterized functions of 2 variables.
      Since there could be many other ways of constructing L L L Pts - much
      more than could be usefully interpreted - any other formats are
      currently not allowed. When they are, this comment should be updated
      appropriately.

      ************************************************************************
      
      Traverse each component.
      We decide here, before we begin traversing the
      component what we want to interpret it as.
      Here's the convention used to figure that out:
      - points:	#anLLPoint->numOfLists was 1
      #anLPoint->numOfPoints is 1
      - lines:	#anLLPoint->numOfLists was 1
      #anLPoint->numOfPoints > 1
      - polygons: #anLLPoint->numOfLists was 2
      #anLPoint->numOfPoints is 1
      - surface:	#anLLPoint->numOfLists was some m>1
      #anLPoint->numOfPoints all point lists are the same.
      
      */

    anLLPoint = viewData.lllp.llp;
    for (i=0; i<viewData.lllp.numOfComponents; i++,anLLPoint++) {
      /* initially, component type is unknown */
      componentType = stillDontKnow;
      if (anLLPoint->numOfLists == 1) {
	if (anLLPoint->lp->numOfPoints == 1) componentType = pointComponent;
	else componentType = lineComponent;
      } else if (anLLPoint->numOfLists == 2) {
	if ((anLLPoint->lp->numOfPoints == 1) &&
	    ((anLLPoint->lp+1)->numOfPoints > 2))
	  componentType = polygonComponent;
      }
      /* Check for corrupt data and NaN data is made in AXIOM . */
      if (componentType == stillDontKnow)
	componentType = surfaceComponent;
      
      anLPoint = anLLPoint->lp;
      
      switch (componentType) {
	
      case pointComponent:
	/* anLLPoint->numOfLists == anLLPoint->lp->numOfPoints == 1 here */
	aLPt = refPt3D(viewData,*(anLPoint->indices));
	project(aLPt,quadMesh,0);
	if (dFlag==Xoption) {
	  if (mono || viewport->monoOn)
	    GSetForeground(componentGC, (float)foregroundColor, dFlag);
	  else {
	    hue = hueValue(aLPt->c);
	    GSetForeground(componentGC, (float)XSolidColor(hue,2), dFlag);
	  }
	} else GSetForeground(componentGC, psBlack, dFlag);
	GFillArc(componentGC,viewport->viewWindow,quadMesh->x,quadMesh->y,
		 viewData.pointSize,viewData.pointSize,0,360*64,dFlag);
	break;
	
      case lineComponent:
	/* anLLPoint->numOfLists == 1 here */
	anIndex = anLPoint->indices;
	aLPt = refPt3D(viewData,*anIndex);
	project(aLPt,quadMesh,0);
	x1 = quadMesh[0].x;  y1 = quadMesh[0].y;  anIndex++;
	for (k=1; k<anLPoint->numOfPoints; k++,anIndex++) {
	  aLPt = refPt3D(viewData,*anIndex);
	  project(aLPt,quadMesh,k);
	  x2 = quadMesh[k].x; y2 = quadMesh[k].y;
	  if (dFlag==Xoption) {
	    if (mono || viewport->monoOn)
	      GSetForeground(componentGC, (float)foregroundColor, dFlag);
	    else {
	      hue = hueValue(aLPt->c);
	      GSetForeground(componentGC, (float)XSolidColor(hue,2), dFlag);
	    }
            if (!eqNANQ(x1) && !eqNANQ(y1) && !eqNANQ(x2) && !eqNANQ(y2))
  	      GDrawLine(componentGC,viewport->viewWindow,x1,y1,x2,y2,dFlag);
	  } else {
	    if (dFlag==PSoption && !mono && !viewport->monoOn) {
	      hue = getHue(aLPt->c);
	      col_rgb = hlsTOrgb((float)hue,0.5,0.8);
	      line[0].x = x1;  line[0].y = y1;
	      line[1].x = x2;  line[1].y = y2;
	      PSDrawColor(col_rgb.r,col_rgb.g,col_rgb.b,line,2);
	    } else {
	      if (foregroundColor == white)
		GSetForeground(componentGC, 0.0, dFlag);
	      else
		GSetForeground(componentGC, psBlack, dFlag);
              if (!eqNANQ(x1) && !eqNANQ(y1) && !eqNANQ(x2) && !eqNANQ(y2))
	        GDrawLine(componentGC,viewport->viewWindow,x1,y1,x2,y2,dFlag);
	    }
	  }
	  x1 = x2; y1 = y2;
	} /* for points in LPoints (k) */
	if (anLPoint->prop.closed) {
	  project(refPt3D(viewData,*(anLPoint->indices)),quadMesh,
		  anLPoint->numOfPoints);
	  x2 = quadMesh[anLPoint->numOfPoints].x;
	  y2 = quadMesh[anLPoint->numOfPoints].y;
	  if (dFlag==Xoption) {
	    if (mono || viewport->monoOn)
	      GSetForeground(componentGC, (float)foregroundColor, dFlag);
	    else {
	      hue = hueValue(aLPt->c);
	      GSetForeground(componentGC, (float)XSolidColor(hue,2), dFlag);
	    }
            if (!eqNANQ(x1) && !eqNANQ(y1) && !eqNANQ(x2) && !eqNANQ(y2))
  	      GDrawLine(componentGC,viewport->viewWindow,x1,y1,x2,y2,dFlag);
	  } 
	  else {
	    if (dFlag==PSoption && !mono && !viewport->monoOn) {
	      hue = getHue(aLPt->c);
	      col_rgb = hlsTOrgb((float)hue,0.5,0.8);
	      line[0].x = x1;  line[0].y = y1;
	      line[1].x = x2;  line[1].y = y2;
	      PSDrawColor(col_rgb.r,col_rgb.g,col_rgb.b,line,2);
	    } 
	    else {
	      if (foregroundColor == white)
		GSetForeground(componentGC, 0.0, dFlag);
	      else
		GSetForeground(componentGC, psBlack, dFlag);
              if (!eqNANQ(x1) && !eqNANQ(y1) && !eqNANQ(x2) && !eqNANQ(y2))
	        GDrawLine(componentGC,viewport->viewWindow,x1,y1,x2,y2,dFlag);
	    }
	  }
	}
	break;
	
      case polygonComponent:
	/* first pt of polygon is a single list */
	project(refPt3D(viewData,*(anLPoint->indices)),quadMesh,0);
	/* remaining points in the 2nd list (always of size 2 or greater) */
	x1 = quadMesh[0].x;  y1 = quadMesh[0].y;
	anLPoint = anLLPoint->lp + 1;
	anIndex = anLPoint->indices;
	for (k=1; k<=anLPoint->numOfPoints; k++,anIndex++) {
	  aLPt = refPt3D(viewData,*anIndex);
	  project(aLPt,quadMesh,k);
	  x2 = quadMesh[k].x;  y2 = quadMesh[k].y;
	  if (dFlag==Xoption) {
	    if (mono || viewport->monoOn)
	      GSetForeground(componentGC, (float)foregroundColor, dFlag);
	    else {
	      hue = hueValue(aLPt->c);
	      GSetForeground(componentGC, (float)XSolidColor(hue,2), dFlag);
	    }
            if (!eqNANQ(x1) && !eqNANQ(y1) && !eqNANQ(x2) && !eqNANQ(y2))
  	      GDrawLine(componentGC,viewport->viewWindow,x1,y1,x2,y2,dFlag);
	  } 
	  else {
	    if (dFlag==PSoption && !mono && !viewport->monoOn) {
	      hue = getHue(aLPt->c);
	      col_rgb = hlsTOrgb((float)hue,0.5,0.8);
	      line[0].x = x1;  line[0].y = y1;
	      line[1].x = x2;  line[1].y = y2;
	      PSDrawColor(col_rgb.r,col_rgb.g,col_rgb.b,line,2);
	    } 
	    else {
	      if (foregroundColor == white)
		GSetForeground(componentGC, 0.0, dFlag);
	      else
		GSetForeground(componentGC, psBlack, dFlag);
              if (!eqNANQ(x1) && !eqNANQ(y1) && !eqNANQ(x2) && !eqNANQ(y2))
	        GDrawLine(componentGC,viewport->viewWindow,x1,y1,x2,y2,dFlag);
	    }
	  }
	  x1 = x2; y1 = y2;
	} /* for points in LPoints (k) */
	project(refPt3D(viewData,*(anLLPoint->lp->indices)),quadMesh,k);
	x2 = quadMesh[k].x;  y2 = quadMesh[k].y;
	if (dFlag==Xoption) {
	  if (mono || viewport->monoOn)
	    GSetForeground(componentGC, (float)foregroundColor, dFlag);
	  else {
	    hue = hueValue(refPt3D(viewData,*anIndex)->c);
	    GSetForeground(componentGC, (float)XSolidColor(hue,2), dFlag);
	  }
          if (!eqNANQ(x1) && !eqNANQ(y1) && !eqNANQ(x2) && !eqNANQ(y2))
  	    GDrawLine(componentGC,viewport->viewWindow,x1,y1,x2,y2,dFlag);
	} else {
	  if (dFlag==PSoption && !mono && !viewport->monoOn) {
	    hue = getHue(refPt3D(viewData,*anIndex)->c);
	    col_rgb = hlsTOrgb((float)hue,0.5,0.8);
	    line[0].x = x1;  line[0].y = y1;
	    line[1].x = x2;  line[1].y = y2;
	    PSDrawColor(col_rgb.r,col_rgb.g,col_rgb.b,line,2);
	  } 
	  else {
	    if (foregroundColor == white)
	      GSetForeground(componentGC, 0.0, dFlag);
	    else
	      GSetForeground(componentGC, psBlack, dFlag);
            if (!eqNANQ(x1) && !eqNANQ(y1) && !eqNANQ(x2) && !eqNANQ(y2))
 	      GDrawLine(componentGC,viewport->viewWindow,x1,y1,x2,y2,dFlag);
	  }
	}
	/* close a polygon */
	break;
	
      case surfaceComponent:
	if (dFlag==Xoption) {
	  if (mono || viewport->monoOn)
	    GSetForeground(componentGC, (float)foregroundColor, dFlag);
	  else
	    GSetForeground(componentGC, (float) meshOutline, dFlag);
	}
	else {
	  GSetForeground(componentGC, psBlack, dFlag);
	}
	
	/* traverse down one direction first (all points 
	   in a list at a time) */
	for (j=0; drawMore && j<anLLPoint->numOfLists; j++,anLPoint++) {
	  anIndex = anLPoint->indices;
	  clip_a = 0;
	  for (k=0, clip_i=0;
	       drawMore && k<anLPoint->numOfPoints;
	       k++, anIndex++, clip_i++) {
	    aLPt = refPt3D(viewData,*anIndex);
	    project(aLPt,quadMesh,k);
	    
	    if (behindClipPlane(aLPt->pz) || 
		(viewData.clipStuff && 
		 outsideClippedBoundary(aLPt->x, aLPt->y, aLPt->z))) {
	      if (clip_i - clip_a > 1) {
		GDrawLines(componentGC,viewport->viewWindow,(quadMesh+clip_a),
			   clip_i-clip_a, CoordModeOrigin, dFlag );
	      }
	      clip_a = clip_i + 1;
	    }
	    
	    drawMore = keepDrawingViewport();
	  } /* for points in LPoints (k) */
	  if (drawMore) {
	    /* if drawMore is true, then the above loop terminated with
	       clip_i incremented properly */
	    if (anLPoint->prop.closed) {
	      /* If closed, then do the first point again - no need to project
		 just copy over from the first one */
	      aLPt = refPt3D(viewData,*(anLPoint->indices));
	      project(aLPt,quadMesh, anLPoint->numOfPoints);
	      if (behindClipPlane(aLPt->pz) || 
		  (viewData.clipStuff &&
		   outsideClippedBoundary(aLPt->x, aLPt->y, aLPt->z))) {
		if (clip_i - clip_a > 1) {
		  GDrawLines(componentGC, viewport->viewWindow,
			     (quadMesh+clip_a), clip_i-clip_a,
			     CoordModeOrigin, dFlag);
		}
		clip_a = clip_i + 1;
	      }
	      clip_i++;
	    } /* closed */
	    if (clip_i - clip_a > 1) {
	      GDrawLines(componentGC, viewport->viewWindow, (quadMesh+clip_a),
			 clip_i-clip_a, CoordModeOrigin, dFlag);
	    }
	  } /* drawMore */
	} /* for LPoints in LLPoints (j) */
	
	/* now traverse down the list in the other direction
	   (one point from each list at a time) */
	for (j=0; drawMore && j<anLLPoint->lp->numOfPoints; j++) {
	  clip_a = 0;
	  for (k=0, clip_i=0;
	       drawMore && k<anLLPoint->numOfLists;
	       k++, clip_i++) {
	    aLPt = refPt3D(viewData,*((anLLPoint->lp + k)->indices + j));
	    project(aLPt, quadMesh,k);
	    
	    if (behindClipPlane(aLPt->pz) || 
		(viewData.clipStuff &&
		 outsideClippedBoundary(aLPt->x, aLPt->y, aLPt->z))) {
	      if (clip_i - clip_a > 1) {
		GDrawLines(componentGC,viewport->viewWindow,quadMesh+clip_a,
			   clip_i-clip_a, CoordModeOrigin, dFlag );
	      }
	      clip_a = clip_i + 1;
	    }
	    drawMore = keepDrawingViewport();
	  } /* for points in LPoints (k) */
	  
	  if (drawMore) {
	    /* if drawMore is true, then the above loop terminated with
	       clip_i incremented properly */
	    if (anLLPoint->prop.closed) {
	      /* if closed, do the first point again - no need to project
		 just copy over from the first one */
	      aLPt = refPt3D(viewData,*((anLLPoint->lp + 0)->indices + j));
	      project(aLPt, quadMesh, anLLPoint->numOfLists);
	      if (behindClipPlane(aLPt->pz) || 
		  (viewData.clipStuff &&
		   outsideClippedBoundary(aLPt->x, aLPt->y, aLPt->z))) {
		if (clip_i - clip_a > 1) {
		  GDrawLines(componentGC, viewport->viewWindow,
			     quadMesh + clip_a, clip_i - clip_a,
			     CoordModeOrigin, dFlag);
		}
		clip_a = clip_i + 1;
	      }
	      clip_i++;
	    } /* closed */
	    if (clip_i - clip_a > 1) {
	      GDrawLines(componentGC, viewport->viewWindow, quadMesh+clip_a,
			 clip_i-clip_a, CoordModeOrigin, dFlag);
	    }
	  } /* drawMore */
	} /* for a point in each LPoint (j) */
	break;
      } /* switch componentType */
    } /* for LLPoints in LLLPoints (i) */
    break;
    
  case opaqueMesh:
    if (dFlag==Xoption) {
      GSetForeground(globGC, (float)opaqueForeground, dFlag);
      GSetForeground(opaqueGC, (float)opaqueOutline, dFlag);
    }
    else {
      GSetForeground(globGC, psBlack, dFlag);
      GSetForeground(opaqueGC, psBlack, dFlag);
    }
    GSetLineAttributes(opaqueGC,0,LineSolid,CapButt,JoinRound,dFlag);
    drawPolygons(dFlag);
    break;
    
  case render:
    if (viewData.outlineRenderOn) {
      GSetLineAttributes(renderGC,0,LineSolid,CapButt,JoinRound,dFlag);
      if (dFlag==Xoption) GSetForeground(renderGC,(float)black, dFlag);
      else GSetForeground(renderGC,psBlack, dFlag );
    }
    drawPolygons(dFlag);
    break;
    
  case smooth:
    drawPhong(dFlag);
    break;
    
  } /* switch on style */
  
} /* draw3DComponents() */

@
\eject
\begin{thebibliography}{99}
\bibitem{1} nothing
\end{thebibliography}
\end{document}