-- File: SweepGeometryImpl.mesa
-- Last edited by Bier on December 18, 1982 1:31 am
-- Author: Eric Allan Bier on July 3, 1983 1:29 pm
-- Contents: Implementation of a simple, straight-line, sweep geometry package for fast interactive graphics

DIRECTORY
 CoordSys,
 DisplayList3d,
 Draw3d,
 CSGGraphics,
 Graphics,
 Matrix3d,
 RealFns,
 Shading,
 SV2d,
 SVPolygon2d,
 SVPolygon3d,
 SweepGeometry,
 SVVector3d;

SweepGeometryImpl: PROGRAM
IMPORTS CSGGraphics, Draw3d, RealFns, SVPolygon2d, SVPolygon3d, SVVector3d
EXPORTS SweepGeometry =
BEGIN


Camera: TYPE = CSGGraphics.Camera;
Poly3d: TYPE = REF Poly3dObj;
Poly3dObj: TYPE = SVPolygon3d.Poly3dObj;
Point2d: TYPE = SV2d.Point2d;
Point3d: TYPE = Matrix3d.Point3d;
Vector: TYPE = SVVector3d.Vector;

LinearMesh: TYPE = REF LinearMeshRecord;
LinearMeshRecord: TYPE = SweepGeometry.LinearMeshRecord;
RevoluteMesh: TYPE = REF RevoluteMeshRecord;
RevoluteMeshRecord: TYPE = SweepGeometry.RevoluteMeshRecord;
ToroidalMesh: TYPE = REF ToroidalMeshRecord;
ToroidalMeshRecord: TYPE = SweepGeometry.ToroidalMeshRecord;

LightSource: TYPE = REF LightSourceObj;
LightSourceObj: TYPE = Shading.LightSourceObj;
LightSourceList: TYPE = LIST OF LightSource;

MasterObject: TYPE = REF MasterObjectRec;
MasterObjectRec: TYPE = DisplayList3d.MasterObjectRec;
Path: TYPE = REF PathObj;
PathObj: TYPE = SV2d.PathObj;
Polygon: TYPE = REF PolygonObj;
PolygonObj: TYPE = SV2d.PolygonObj;
Assembly: TYPE = REF AssemblyObj;
AssemblyObj: TYPE = DisplayList3d.AssemblyObj;
CoordSystem: TYPE = REF CoordSysObj;
CoordSysObj: TYPE = CoordSys.CoordSysObj;
PlanarSurface: TYPE = REF PlanarSurfaceObj;
PlanarSurfaceObj: TYPE = DisplayList3d.PlanarSurfaceObj;
PlanarSurfaceList: TYPE = DisplayList3d.PlanarSurfaceList;


TooFewPointsForShadedSweep: SIGNAL = CODE;

LinearSweep: PUBLIC PROC [poly: Polygon, frontDepth: REAL ← 0.5, backDepth: REAL ← -0.5] RETURNS [meshRecord: LinearMesh] = {
 frontToBack, clockWise, thisNorm: Vector;
 iPlusOne: NAT;

IF NOT SVPolygon2d.IsClockwisePoly[poly] THEN SVPolygon2d.InvertPolyInPlace[poly];

 meshRecord ← NEW[LinearMeshRecord];
FOR i: NAT IN[1..poly.len] DO
  FOR j: NAT IN[1..2] DO
   meshRecord.array[i][j][1] ← poly[i-1][1];
   meshRecord.array[i][j][2] ← poly[i-1][2];
  ENDLOOP;
  meshRecord.array[i][1][3] ← frontDepth;
  meshRecord.array[i][2][3] ← backDepth;
ENDLOOP;
 meshRecord.len ← poly.len;

-- now iterate over all surfaces and find their normals.
-- front and back surfaces are trivial since we know there normals are aligned with the z axis.
-- If len <=2, then there are no front and back surfaces.

IF poly.len <=2 THEN SIGNAL TooFewPointsForShadedSweep;
 meshRecord.surfaces.front.normal ← [0,0,1];
 meshRecord.surfaces.back.normal ← [0,0,-1];
-- for simplicity, I make the path be clockwise when determining outward pointing normal.  
-- with linear sweep shapes, the [i][1] to [i][2] and [i][1] to [i+1][1] vectors are orthogonal.
-- Their cross product is the surface normal. It will have a zero z component, initially.
FOR i: NAT IN[1..poly.len] DO
  iPlusOne ← IF i = poly.len THEN 1 ELSE i + 1;
  frontToBack ← SVVector3d.Difference[meshRecord.array[i][2],meshRecord.array[i][1]];
  clockWise ←
   SVVector3d.Difference[meshRecord.array[iPlusOne][1],meshRecord.array[i][1]];
  thisNorm ← SVVector3d.CrossProduct[clockWise,frontToBack];
  meshRecord.surfaces.sides[i].normal ← thisNorm;
ENDLOOP;
 }; -- end of ShadedLinearSweep


RevoluteSweep: PUBLIC PROC [path: Path, linesOfLongitude: NAT ← 10] RETURNS [meshRecord: RevoluteMesh] = {
 leftToRight, rightToLeft, thisNorm: Vector;
 degreesPerLong: REAL;
 thisAngle: REAL;
 jPlusOne: NAT;
 poly: Polygon;
-- Add two points to path (the extensions of the first and last points to the y axis) and make it a polygon.
 poly ← SVPolygon2d.CreatePoly[path.len+2];
 poly ← SVPolygon2d.PutPolyPoint[poly, 0 , [0, path[0][2]]];
 poly ← SVPolygon2d.PutPolyPoint[poly, path.len + 1, [0, path[path.len -1][2]]];
 poly ← SVPolygon2d.PartPolyGetsPartPath[path, 0, poly, 1, path.len];
-- Correct the data if necessary so all x's are positive. Then make sure the poly is clockwise.

FOR i: NAT IN[0..poly.len) DO
  IF poly[i][1] < 0 THEN poly[i][1] ← -poly[i][1];
ENDLOOP;
IF NOT SVPolygon2d.IsClockwisePoly[poly] THEN SVPolygon2d.InvertPolyInPlace[poly];

 path ← SVPolygon2d.SubPathOfPoly[poly, 1, path.len];

-- CREATE A NEW REVOLUTE SWEEP from the points in poly
 degreesPerLong ← 360.0/linesOfLongitude;
 meshRecord ← NEW[RevoluteMeshRecord];
FOR i: NAT IN[1..path.len] DO
  FOR j: NAT IN[1..linesOfLongitude] DO
  -- for each line of latitude, for each point of longitude, calculate its position in 3-space by "sweeping" the source point of array2d by the appropriate angle.
   sin, cos: REAL;
   thisAngle ← j*degreesPerLong;
   sin ← RealFns.SinDeg[thisAngle];
   cos ← RealFns.CosDeg[thisAngle];
   meshRecord.array[i][j][1] ← path[i-1][1]*cos; -- assign new x from source x
   meshRecord.array[i][j][3] ← -path[i-1][1]*sin; -- assign new z from source x
   meshRecord.array[i][j][2] ← path[i-1][2];  -- assign new y directly from source y
  ENDLOOP; -- next point of longitude
ENDLOOP; -- next line of latitude
 meshRecord.linesOfLongitude ← linesOfLongitude;
 meshRecord.linesOfLatitude ← path.len;

-- FIND THE NORMALS. I assume for now that the shape was drawn top to bottom, when determing normals
-- the top and bottom surfaces are trivial since their normals are aligned with the y-axis.
 meshRecord.surfaces.top.normal ← [0,1,0];
 meshRecord.surfaces.bottom.normal ← [0,-1,0];

-- Now, iterate over the side faces

  -- the [i][j] (upper-left corner) to the [i+1][j+1] (lower-right corner) diagonal vector
  -- and [i][j+1] (upper right) to the [i+1][j] (lower left) diagonal vector
  -- are two vectors lying in the plane. Their cross-product is a surface normal.
FOR i: NAT IN[1..path.len-1] DO
  FOR j: NAT IN[1..linesOfLongitude] DO
   jPlusOne ← IF j = linesOfLongitude THEN 1 ELSE j + 1;
   leftToRight ← SVVector3d.Difference[meshRecord.array[i+1][jPlusOne],
               meshRecord.array[i][j]];
   rightToLeft ← SVVector3d.Difference[meshRecord.array[i+1][j],
               meshRecord.array[i][jPlusOne]];
   thisNorm ← SVVector3d.CrossProduct[rightToLeft,leftToRight];
   meshRecord.surfaces.sides[i][j].normal ← thisNorm;
  ENDLOOP;
ENDLOOP;
  }; -- end of ShadedRevoluteSweep

ToroidalSweep: PUBLIC PROC [poly: Polygon, linesOfLongitude: NAT ← 10] RETURNS [meshRecord: ToroidalMesh] = {
 leftToRight, rightToLeft, thisNorm: Vector;
 degreesPerLong: REAL;
 thisAngle: REAL;
 iPlusOne, jPlusOne: NAT;
-- Correct the data if necessary so all x's are positive. Then make sure the poly is clockwise.
FOR i: NAT IN[0..poly.len) DO
  IF poly[i][1] < 0 THEN poly[i][1] ← -poly[i][1];
ENDLOOP;
IF NOT SVPolygon2d.IsClockwisePoly[poly] THEN SVPolygon2d.InvertPolyInPlace[poly];

-- CREATE A NEW TOROIDAL SWEEP from the points in poly
 degreesPerLong ← 360.0/linesOfLongitude;
 meshRecord ← NEW[ToroidalMeshRecord];
FOR i: NAT IN[1..poly.len] DO
  FOR j: NAT IN[1..linesOfLongitude] DO
  -- for each line of latitude, for each point of longitude, calculate its position in 3-space by "sweeping" the source point of array2d by the appropriate angle.
   sin, cos: REAL;
   thisAngle ← j*degreesPerLong;
   sin ← RealFns.SinDeg[thisAngle];
   cos ← RealFns.CosDeg[thisAngle];
   meshRecord.array[i][j][1] ← poly[i-1][1]*cos; -- assign new x from source x
   meshRecord.array[i][j][3] ← -poly[i-1][1]*sin; -- assign new z from source x
   meshRecord.array[i][j][2] ← poly[i-1][2];  -- assign new y directly from source y
  ENDLOOP; -- next point of longitude
ENDLOOP; -- next line of latitude
 meshRecord.linesOfLongitude ← linesOfLongitude;
 meshRecord.linesOfLatitude ← poly.len;

-- FIND THE NORMALS. I assume for now that the shape was drawn top to bottom, when determing normals

-- Iterate over the side faces

  -- the [i][j] (upper-left corner) to the [i+1][j+1] (lower-right corner) diagonal vector
  -- and [i][j+1] (upper right) to the [i+1][j] (lower left) diagonal vector
  -- are two vectors lying in the plane. Their cross-product is a surface normal.
FOR i: NAT IN[1..poly.len] DO
  iPlusOne ← IF i = poly.len THEN 1 ELSE i + 1;
  FOR j: NAT IN[1..linesOfLongitude] DO
   jPlusOne ← IF j = linesOfLongitude THEN 1 ELSE j + 1;
   leftToRight ← SVVector3d.Difference[meshRecord.array[iPlusOne][jPlusOne],
               meshRecord.array[i][j]];
   rightToLeft ← SVVector3d.Difference[meshRecord.array[iPlusOne][j],
               meshRecord.array[i][jPlusOne]];
   thisNorm ← SVVector3d.CrossProduct[rightToLeft,leftToRight];
   meshRecord.surfaces.sides[i][j].normal ← thisNorm;
  ENDLOOP;
ENDLOOP;
 }; -- end of ToroidalSweep
  
GetLinearPoly: PUBLIC PROC [linMesh: LinearMesh] RETURNS [poly: Polygon] = {
-- use the front (linMesh.array[i][1]) polygon ignoring z ([3]) values.
 poly ← SVPolygon2d.CreatePoly[linMesh.len];
FOR i: NAT IN[1..linMesh.len] DO
  poly[i-1] ← [linMesh.array[i][1][1], linMesh.array[i][1][2]];
ENDLOOP;
 poly.len ← linMesh.len;
 }; -- end of GetLinearPoly

GetRevolutePath: PUBLIC PROC [revMesh: RevoluteMesh] RETURNS [path: Path] = {
-- ignore z values in the [i][revMesh.linesOfLongitude] elements
 path ← SVPolygon2d.CreatePath[revMesh.linesOfLatitude];
FOR i: NAT IN[1..revMesh.linesOfLatitude] DO
  path[i - 1] ← [revMesh.array[i][revMesh.linesOfLongitude][1],
       revMesh.array[i][revMesh.linesOfLongitude][2]];
ENDLOOP;
 path.len ← revMesh.linesOfLatitude;
 }; -- end of GetRevolutePath

  
LineDrawLinearSweep: PUBLIC PROC [dc: Graphics.Context, meshRecord: LinearMesh, camera: Camera, localCS: CoordSystem] = {
-- cx, cy: REAL; (to draw debugging numbers at vertices)
IF meshRecord.len <= 0 THEN RETURN;
 CSGGraphics.SetCP[dc, meshRecord.array[1][1], camera, localCS]; -- First Point of front plane
  FOR i: NAT IN[2..meshRecord.len] DO-- Draw front plane
  CSGGraphics.DrawTo[dc, meshRecord.array[i][1], camera, localCS];
ENDLOOP;
CSGGraphics.DrawTo[dc, meshRecord.array[1][1], camera, localCS];
  CSGGraphics.SetCP[dc, meshRecord.array[1][2], camera, localCS]; -- First point of rear plane
  FOR i: NAT IN[2..meshRecord.len] DO-- Draw rear plane
  CSGGraphics.DrawTo[dc, meshRecord.array[i][2], camera, localCS];
ENDLOOP;
CSGGraphics.DrawTo[dc, meshRecord.array[1][2], camera, localCS];
    
  FOR i: NAT IN[1..meshRecord.len] DO -- Draw Lines between Planes
   CSGGraphics.SetCP[dc, meshRecord.array[i][1], camera, localCS];
   CSGGraphics.DrawTo[dc, meshRecord.array[i][2], camera, localCS];
   ENDLOOP;
  };

LineDrawRevoluteSweep: PUBLIC PROC [dc: Graphics.Context, meshRecord: RevoluteMesh, camera: Camera, localCS: CoordSystem] = {
-- To draw a revolute sweep shape, we simple draw a line from each meshArray[i][j] to each four mesh neighbors meshArray[i-1][j], meshArray[i+1][j], meshArray[i][j-1], meshArray[i][j+1] with provision for wraparound at the extremes of j but not i.
-- Draw Lines Of Latitude
FOR lat: NAT IN [1..meshRecord.linesOfLatitude] DO
  CSGGraphics.SetCP[dc, meshRecord.array[lat][1], camera, localCS]; -- First point of this line of latitude
  FOR long: NAT IN[2..meshRecord.linesOfLongitude] DO
   CSGGraphics.DrawTo[dc, meshRecord.array[lat][long], camera, localCS];
  ENDLOOP;
  -- Wrap around to first point of this line of latitude
  CSGGraphics.DrawTo[dc, meshRecord.array[lat][1], camera, localCS]; 

ENDLOOP;

-- Draw Lines Of Longitude
FOR long: NAT IN[1..meshRecord.linesOfLongitude] DO
  CSGGraphics.SetCP[dc, meshRecord.array[1][long], camera, localCS]; -- First point of this line of longitude
  FOR lat: NAT IN[1..meshRecord.linesOfLatitude] DO
   CSGGraphics.DrawTo[dc, meshRecord.array[lat][long], camera, localCS];
  ENDLOOP;
  -- no wrap around for longitude
ENDLOOP;
 };

LineDrawToroidalSweep: PUBLIC PROC [dc: Graphics.Context, meshRecord: ToroidalMesh, camera: Camera, localCS: CoordSystem] = {
-- To draw a toroidal sweep shape, we simple draw a line from each meshArray[i][j] to each four mesh neighbors meshArray[i-1][j], meshArray[i+1][j], meshArray[i][j-1], meshArray[i][j+1] with provision for wraparound at the extremes of i and j.
-- Draw Lines Of Latitude
FOR lat: NAT IN [1..meshRecord.linesOfLatitude] DO
  CSGGraphics.SetCP[dc, meshRecord.array[lat][1], camera, localCS]; -- First point of this line of latitude
  FOR long: NAT IN[2..meshRecord.linesOfLongitude] DO
   CSGGraphics.DrawTo[dc, meshRecord.array[lat][long], camera, localCS];
  ENDLOOP;
  -- Wrap around to first point of this line of latitude
  CSGGraphics.DrawTo[dc, meshRecord.array[lat][1], camera, localCS];
ENDLOOP;

-- Draw Lines Of Longitude
FOR long: NAT IN[1..meshRecord.linesOfLongitude] DO
  CSGGraphics.SetCP[dc, meshRecord.array[1][long], camera, localCS]; -- First point of this line of longitude
  FOR lat: NAT IN[1..meshRecord.linesOfLatitude] DO
   CSGGraphics.DrawTo[dc, meshRecord.array[lat][long], camera, localCS];
  ENDLOOP;
  CSGGraphics.DrawTo[dc, meshRecord.array[1][long], camera, localCS];
ENDLOOP;
 }; -- end of LineDrawToroidalSweep

AverageVertex: PRIVATE PROC [meshRecord: LinearMesh] RETURNS [pt: Point2d] = {
 pt ← [meshRecord.array[1][1][1], meshRecord.array[1][1][2]];
FOR i: NAT IN [1..meshRecord.len] DO
  pt[1] ← pt[1] + meshRecord.array[i][1][1];
  pt[2] ← pt[2] + meshRecord.array[i][1][2];
ENDLOOP;
 pt[1] ← pt[1]/meshRecord.len;
 pt[2] ← pt[2]/meshRecord.len;
 };

DrawNormalsLinearSweep: PUBLIC PROC [dc: Graphics.Context, meshRecord: LinearMesh, camera: Camera, localCS: CoordSystem] = {
 thisNorm: Vector;
 upperLeftPoint, lowerRightPoint, midPoint: Point3d;
 iPlusOne: NAT;
 midPoint2d: Point2d;
-- draw front vector
 thisNorm ← meshRecord.surfaces.front.normal;
 midPoint2d ← AverageVertex[meshRecord];
 midPoint ← [midPoint2d[1], midPoint2d[2], meshRecord.array[1][1][3]];
 Draw3d.DrawLocalVector[dc,thisNorm,midPoint,camera,localCS];
-- draw bottom vector
 thisNorm ← meshRecord.surfaces.back.normal;
 midPoint ← [midPoint2d[1], midPoint2d[2], meshRecord.array[1][2][3]];
 Draw3d.DrawLocalVector[dc,thisNorm,midPoint,camera,localCS];
-- draw side vectors
FOR i: NAT IN[1..meshRecord.len] DO
  iPlusOne ← IF i = meshRecord.len THEN 1 ELSE i + 1;
  thisNorm ← meshRecord.surfaces.sides[i].normal;
  upperLeftPoint ← meshRecord.array[iPlusOne][1];
  lowerRightPoint ← meshRecord.array[i][2];
  midPoint ← SVVector3d.Add[upperLeftPoint,lowerRightPoint];
  midPoint ← SVVector3d.Scale[midPoint,0.5];
  -- we have the normal in local coordinates.
  Draw3d.DrawLocalVector[dc,thisNorm,midPoint,camera,localCS];
ENDLOOP;
 }; -- end of DrawLinearNormals


DrawNormalsRevoluteSweep: PUBLIC PROC [dc: Graphics.Context, meshRecord: RevoluteMesh, camera: Camera, localCS: CoordSystem] = {
 thisNorm: Vector;
 upperLeftPoint, lowerRightPoint, midPoint: Point3d;
 iPlusOne, jPlusOne: NAT;
-- draw top normal
 thisNorm ← meshRecord.surfaces.top.normal;
 midPoint ← [0, meshRecord.array[1][1][2], 0];
 Draw3d.DrawLocalVector[dc,thisNorm,midPoint,camera,localCS];
-- draw bottom normal
 thisNorm ← meshRecord.surfaces.bottom.normal;
 midPoint ← [0, meshRecord.array[meshRecord.linesOfLatitude][1][2], 0];
 Draw3d.DrawLocalVector[dc,thisNorm,midPoint,camera,localCS];
-- draw the side normals
FOR i: NAT IN[1..meshRecord.linesOfLatitude-1] DO
  iPlusOne ← i + 1;
  FOR j: NAT IN[1..meshRecord.linesOfLongitude] DO
   jPlusOne ← IF j = meshRecord.linesOfLongitude THEN 1 ELSE j + 1;
   thisNorm ← meshRecord.surfaces.sides[i][j].normal;
   upperLeftPoint ← meshRecord.array[i][j];
   lowerRightPoint ← meshRecord.array[iPlusOne][jPlusOne];
   midPoint ← SVVector3d.Add[upperLeftPoint,lowerRightPoint];
   midPoint ← SVVector3d.Scale[midPoint,0.5];
   -- we have the normal in local coordinates.
   Draw3d.DrawLocalVector[dc,thisNorm,midPoint,camera,localCS];
  ENDLOOP;
ENDLOOP;
 }; -- end of DrawNormalsRevoluteSweep

DrawNormalsToroidalSweep: PUBLIC PROC [dc: Graphics.Context, meshRecord: ToroidalMesh, camera: Camera, localCS: CoordSystem] = {thisNorm: Vector;
 upperLeftPoint, lowerRightPoint, midPoint: Point3d;
 iPlusOne, jPlusOne: NAT;
FOR i: NAT IN[1..meshRecord.linesOfLatitude] DO
  iPlusOne ← IF i = meshRecord.linesOfLatitude THEN 1 ELSE i + 1;
  FOR j: NAT IN[1..meshRecord.linesOfLongitude] DO
   jPlusOne ← IF j = meshRecord.linesOfLongitude THEN 1 ELSE j + 1;
   thisNorm ← meshRecord.surfaces.sides[i][j].normal;
   upperLeftPoint ← meshRecord.array[i][j];
   lowerRightPoint ← meshRecord.array[iPlusOne][jPlusOne];
   midPoint ← SVVector3d.Add[upperLeftPoint,lowerRightPoint];
   midPoint ← SVVector3d.Scale[midPoint,0.5];
   -- we have the normal in local coordinates.
   Draw3d.DrawLocalVector[dc,thisNorm,midPoint,camera,localCS];
  ENDLOOP;
ENDLOOP;
 }; -- end of DrawNormalsToroidalSweep


CountPlanarSurfacesLinearSweep: PUBLIC PROC [meshRecord: LinearMesh] RETURNS [len: NAT] = {
 len ← meshRecord.len + 2;
 };
CountVerticesLinearSweep: PUBLIC PROC [meshRecord: LinearMesh] RETURNS [len: NAT] = {
 len ← 2*meshRecord.len;
 };

SortedLinearSurface: TYPE = REF SortedLinearSurfaceObj;
SortedLinearSurfaceObj: TYPE = RECORD [post: NAT, meshRecord: LinearMesh];


PlanarSurfacesLinearSweep: PUBLIC PROC [meshRecord: LinearMesh, assembly: Assembly, cameraCS: CoordSystem] RETURNS [psl: PlanarSurfaceList] = {
 poly: Poly3d ← SVPolygon3d.CreatePoly[meshRecord.len];
 iPlusOne: NAT;
 avgDepth: REAL;
 mo: MasterObject ← NARROW[assembly.object];
 localCS: CoordSystem ← assembly.coordSys;
 thisSortedSurface: PlanarSurface;
-- Top is [0]. Bottom is [len+1].
  
-- put front face on the list
FOR i: NAT IN[1..meshRecord.len] DO
  poly ← SVPolygon3d.AddPolyPoint[poly, meshRecord.array[i][1]];
ENDLOOP;
 avgDepth ← AverageDepth[poly, localCS, cameraCS];
 thisSortedSurface ← NEW[PlanarSurfaceObj ←
   [whichSurface: NEW[SortedLinearSurfaceObj ← [0, meshRecord]],
   assembly: assembly,
   normal: meshRecord.surfaces.front.normal,
   mo: mo,
   depth: avgDepth]];
 psl ← CONS[thisSortedSurface, psl];

  -- put back face on the list
 SVPolygon3d.ClearPoly[poly];
FOR i: NAT IN[1..meshRecord.len] DO
  poly ← SVPolygon3d.AddPolyPoint[poly, meshRecord.array[i][2]];
ENDLOOP;
 avgDepth ← AverageDepth[poly, localCS, cameraCS];
 thisSortedSurface ← NEW[PlanarSurfaceObj ←
   [whichSurface: NEW[SortedLinearSurfaceObj ← [meshRecord.len+1, meshRecord]],
   assembly: assembly,
   normal: meshRecord.surfaces.back.normal,
   mo: mo,
   depth: avgDepth]];
 psl ← CONS[thisSortedSurface, psl];

  -- put side faces on the list
FOR i: NAT IN[1..meshRecord.len] DO
  SVPolygon3d.ClearPoly[poly];
  iPlusOne ← IF i = meshRecord.len THEN 1 ELSE i + 1;
  poly ← SVPolygon3d.AddPolyPoint[poly, meshRecord.array[i][1]];
  poly ← SVPolygon3d.AddPolyPoint[poly, meshRecord.array[i][2]];
  poly ← SVPolygon3d.AddPolyPoint[poly, meshRecord.array[iPlusOne][2]];
  poly ← SVPolygon3d.AddPolyPoint[poly, meshRecord.array[iPlusOne][1]];
  avgDepth ← AverageDepth[poly,localCS, cameraCS];
  thisSortedSurface ← NEW[PlanarSurfaceObj ←
   [whichSurface: NEW[SortedLinearSurfaceObj ← [i, meshRecord]],
   assembly: assembly,
   normal: meshRecord.surfaces.sides[i].normal,
   mo: mo,
   depth: avgDepth]];
 psl ← CONS[thisSortedSurface, psl];
ENDLOOP;
 }; -- end of PlanarSurfacesLinearSweep

DrawPlanarSurfaceLinearSweep: PUBLIC PROC [dc: Graphics.Context, ps: PlanarSurface, lightSources: LightSourceList, camera: Camera] = {
 poly3d: Poly3d;
 post, postPlusOne: NAT;
 avgDepth: REAL;
 meshRecord: LinearMesh;
 thisLinSurface: SortedLinearSurface;
 localCS: CoordSystem;

 thisLinSurface ← NARROW[ps.whichSurface];
 meshRecord ← thisLinSurface.meshRecord;
 localCS ← ps.assembly.coordSys;
 post ← thisLinSurface.post;
 avgDepth ← ps.depth;

SELECT post FROM
 0 => {-- draw front face
  poly3d ← SVPolygon3d.CreatePoly[meshRecord.len];
   FOR i: NAT IN[1..meshRecord.len] DO
    poly3d ← SVPolygon3d.AddPolyPoint[poly3d, meshRecord.array[i][1]];
   ENDLOOP;
  CSGGraphics.DrawAreaNormalAbsolute[dc, ps.normal, poly3d, ps.assembly.artwork, lightSources, camera, localCS];
  };
 meshRecord.len + 1 => { -- draw back face
   poly3d ← SVPolygon3d.CreatePoly[meshRecord.len];
   FOR i: NAT IN[1..meshRecord.len] DO
    poly3d ← SVPolygon3d.AddPolyPoint[poly3d, meshRecord.array[i][2]];
   ENDLOOP;
   CSGGraphics.DrawAreaNormalAbsolute[dc, ps.normal, poly3d, ps.assembly.artwork, lightSources, camera, localCS];
   };
IN [1..meshRecord.len] => {-- draw side face
  poly3d ← SVPolygon3d.CreatePoly[4];
  postPlusOne ← IF post = meshRecord.len THEN 1 ELSE post + 1;
  poly3d ← SVPolygon3d.AddPolyPoint[poly3d, meshRecord.array[post][1]];
  poly3d ← SVPolygon3d.AddPolyPoint[poly3d, meshRecord.array[post][2]];
  poly3d ← SVPolygon3d.AddPolyPoint[poly3d, meshRecord.array[postPlusOne][2]];
  poly3d ← SVPolygon3d.AddPolyPoint[poly3d, meshRecord.array[postPlusOne][1]];
  CSGGraphics.DrawAreaNormalAbsolute[dc, ps.normal, poly3d, ps.assembly.artwork, lightSources, camera, localCS];
  };
ENDCASE => ERROR;
 }; -- end of DrawPlanarSurfaceLinearSweep

MaxDepth: PROC [poly: Poly3d] RETURNS [maxDepth: REAL] = {
 maxDepth ← poly[0][3];
FOR i: NAT IN[1..poly.len) DO
  IF poly[i][3] < maxDepth THEN maxDepth ← poly[i][3];
ENDLOOP;
 };

AverageDepth: PROC [poly: Poly3d, localCS, camera: CoordSystem] RETURNS [avgDepth: REAL] = {
 sum: REAL ← 0;
 realLen: REAL ← poly.len;
 localPoint: Point3d;
FOR i: NAT IN[0..poly.len) DO
  localPoint ← poly[i];
  localPoint ← CSGGraphics.LocalToCamera[localPoint, localCS];
  sum ← sum + localPoint[3];
ENDLOOP;
 avgDepth ← sum/realLen;
 };

SortedRevoluteSurface: TYPE = REF SortedRevoluteSurfaceObj;
SortedRevoluteSurfaceObj: TYPE = RECORD [lat, long: NAT, meshRecord: RevoluteMesh];

CountPlanarSurfacesRevoluteSweep: PUBLIC PROC [meshRecord: RevoluteMesh] RETURNS [len: NAT] = {
 len ← (meshRecord.linesOfLongitude)*(meshRecord.linesOfLatitude-1) + 2;
 };

CountVerticesRevoluteSweep: PUBLIC PROC [meshRecord: RevoluteMesh] RETURNS [len: NAT] = {
 len ← meshRecord.linesOfLongitude*meshRecord.linesOfLatitude;
 };

PlanarSurfacesRevoluteSweep: PUBLIC PROC [meshRecord: RevoluteMesh, assembly: Assembly, cameraCS: CoordSystem] RETURNS [psl: PlanarSurfaceList] = {
 poly3d: Poly3d ← SVPolygon3d.CreatePoly[meshRecord.linesOfLongitude];
 avgDepth: REAL;
 thisSortedSurface: PlanarSurface;
 mo: MasterObject ← NARROW[assembly.object];
 jPlusOne: NAT;
 localCS: CoordSystem ← assembly.coordSys;

 psl ← NIL;
  -- Put the top surface on the list
 SVPolygon3d.ClearPoly[poly3d];
FOR j: NAT IN[1..meshRecord.linesOfLongitude] DO
  poly3d ← SVPolygon3d.AddPolyPoint[poly3d, meshRecord.array[1][j]];
ENDLOOP;
 avgDepth ← AverageDepth[poly3d, localCS,cameraCS];
 thisSortedSurface ← NEW[PlanarSurfaceObj ←
   [whichSurface: NEW[SortedRevoluteSurfaceObj ← [0,0,meshRecord]],
   assembly: assembly,
   normal: meshRecord.surfaces.top.normal,
   mo: mo,
   depth: avgDepth]];
 psl ← CONS[thisSortedSurface, psl];

  -- Put the bottom surface on the list
 SVPolygon3d.ClearPoly[poly3d];
FOR j: NAT IN[1..meshRecord.linesOfLongitude] DO
  poly3d ← SVPolygon3d.AddPolyPoint[poly3d,
   meshRecord.array[meshRecord.linesOfLatitude][j]];
ENDLOOP;
 avgDepth ← AverageDepth[poly3d, localCS, cameraCS];
 thisSortedSurface ← NEW[PlanarSurfaceObj ←
   [whichSurface: NEW[SortedRevoluteSurfaceObj ←
     [meshRecord.linesOfLatitude,0,meshRecord]],
   assembly: assembly,
   normal: meshRecord.surfaces.bottom.normal,
   mo: mo,
   depth: avgDepth]];
 psl ← CONS[thisSortedSurface, psl];

  -- Put side surfaces on the list
  
FOR i: NAT IN[1..meshRecord.linesOfLatitude-1] DO
  FOR j: NAT IN[1..meshRecord.linesOfLongitude] DO
   SVPolygon3d.ClearPoly[poly3d];
   jPlusOne ← IF j = meshRecord.linesOfLongitude THEN 1 ELSE j + 1;
   poly3d ← SVPolygon3d.AddPolyPoint[poly3d, meshRecord.array[i][j]];
   poly3d ← SVPolygon3d.AddPolyPoint[poly3d, meshRecord.array[i][jPlusOne]];
   poly3d ← SVPolygon3d.AddPolyPoint[poly3d, meshRecord.array[i+1][jPlusOne]];
   poly3d ← SVPolygon3d.AddPolyPoint[poly3d, meshRecord.array[i+1][j]];
   avgDepth ← AverageDepth[poly3d, localCS, cameraCS];
   thisSortedSurface ← NEW[PlanarSurfaceObj ←
     [whichSurface: NEW[SortedRevoluteSurfaceObj ← [i,j,meshRecord]],
     assembly: assembly,
     normal: meshRecord.surfaces.sides[i][j].normal,
     mo: mo,
     depth: avgDepth]];
   psl ← CONS[thisSortedSurface, psl];
  ENDLOOP;
ENDLOOP;
 }; -- end of PlanarSurfacesRevoluteSweep

DrawPlanarSurfaceRevoluteSweep: PUBLIC PROC [dc: Graphics.Context, ps: PlanarSurface, lightSources: LightSourceList, camera: Camera] = {
 thisRevSurface: SortedRevoluteSurface;
 meshRecord: RevoluteMesh;
 lat, long, longPlusOne: NAT;
 poly3d: Poly3d;
 avgDepth: REAL;
 localCS: CoordSystem;
 thisRevSurface ← NARROW[ps.whichSurface];
 meshRecord ← thisRevSurface.meshRecord;
 localCS ← ps.assembly.coordSys;
 lat ← thisRevSurface.lat;
 long ← thisRevSurface.long; -- we have surface [lat][long]
 avgDepth ← ps.depth;
IF long = 0 THEN-- either top or bottom surface
  {IF lat = 0 THEN -- top surface
   -- Draw the top surface
   {poly3d ← SVPolygon3d.CreatePoly[meshRecord.linesOfLongitude];
    FOR j: NAT IN[1..meshRecord.linesOfLongitude] DO
     poly3d ← SVPolygon3d.AddPolyPoint[poly3d, meshRecord.array[1][j]];
    ENDLOOP;
   CSGGraphics.DrawAreaNormalAbsolute[dc, ps.normal, poly3d, ps.assembly.artwork, lightSources, camera, localCS]}
  ELSE
   -- Draw the bottom surface
   {poly3d ← SVPolygon3d.CreatePoly[meshRecord.linesOfLongitude];
    FOR j: NAT IN[1..meshRecord.linesOfLongitude] DO
     poly3d ← SVPolygon3d.AddPolyPoint[poly3d,
      meshRecord.array[meshRecord.linesOfLatitude][j]];
    ENDLOOP;
   CSGGraphics.DrawAreaNormalAbsolute[dc, ps.normal, poly3d, ps.assembly.artwork, lightSources, camera, localCS]};
  }
ELSE
  {-- Draw the side face
   poly3d ← SVPolygon3d.CreatePoly[4];
   longPlusOne ← IF long = meshRecord.linesOfLongitude THEN 1 ELSE long + 1;
   poly3d ← SVPolygon3d.AddPolyPoint[poly3d, meshRecord.array[lat][long]];
   poly3d ← SVPolygon3d.AddPolyPoint[poly3d, meshRecord.array[lat][longPlusOne]];
   poly3d ← SVPolygon3d.AddPolyPoint[poly3d, meshRecord.array[lat+1][longPlusOne]];
   poly3d ← SVPolygon3d.AddPolyPoint[poly3d, meshRecord.array[lat+1][long]];
   CSGGraphics.DrawAreaNormalAbsolute[dc, ps.normal, poly3d, ps.assembly.artwork, lightSources, camera, localCS];
  };

}; -- end of DrawPlanarSurfaceRevoluteSweep

SortedToroidalSurface: TYPE = REF SortedToroidalSurfaceObj;
SortedToroidalSurfaceObj: TYPE = RECORD [lat, long: NAT, meshRecord: ToroidalMesh];

CountPlanarSurfacesToroidalSweep: PUBLIC PROC [meshRecord: ToroidalMesh] RETURNS [len: NAT] = {
  len ← (meshRecord.linesOfLongitude)*(meshRecord.linesOfLatitude);
  };

CountVerticesToroidalSweep: PUBLIC PROC [meshRecord: ToroidalMesh] RETURNS [len: NAT] = {
 len ← meshRecord.linesOfLongitude*meshRecord.linesOfLatitude;
 };

PlanarSurfacesToroidalSweep: PUBLIC PROC [meshRecord: ToroidalMesh, assembly: Assembly, cameraCS: CoordSystem] RETURNS [psl: PlanarSurfaceList] = {
 iPlusOne, jPlusOne: NAT;
 poly3d: Poly3d ← SVPolygon3d.CreatePoly[4];
 avgDepth: REAL;
 mo: MasterObject ← NARROW[assembly.object];
 localCS: CoordSystem ← assembly.coordSys;
 thisSortedSurface: PlanarSurface;

   -- Put side surfaces on the list
  
FOR i: NAT IN[1..meshRecord.linesOfLatitude] DO
  iPlusOne ← IF i = meshRecord.linesOfLatitude THEN 1 ELSE i + 1;
  FOR j: NAT IN[1..meshRecord.linesOfLongitude] DO
   SVPolygon3d.ClearPoly[poly3d];
   jPlusOne ← IF j = meshRecord.linesOfLongitude THEN 1 ELSE j + 1;
   poly3d ← SVPolygon3d.AddPolyPoint[poly3d, meshRecord.array[i][j]];
   poly3d ← SVPolygon3d.AddPolyPoint[poly3d, meshRecord.array[i][jPlusOne]];
   poly3d ← SVPolygon3d.AddPolyPoint[poly3d, meshRecord.array[iPlusOne][jPlusOne]];
   poly3d ← SVPolygon3d.AddPolyPoint[poly3d, meshRecord.array[iPlusOne][j]];
   avgDepth ← AverageDepth[poly3d, localCS, cameraCS];
   thisSortedSurface ← NEW[PlanarSurfaceObj ←
     [whichSurface: NEW[SortedToroidalSurfaceObj ← [i,j,meshRecord]],
     assembly: assembly,
     normal: meshRecord.surfaces.sides[i][j].normal,
     mo: mo,
     depth: avgDepth]];
   psl ← CONS[thisSortedSurface, psl];
  ENDLOOP;
ENDLOOP;
 }; -- end of PlanarSurfacesToroidalSweep

DrawPlanarSurfaceToroidalSweep: PUBLIC PROC [dc: Graphics.Context, ps: PlanarSurface, lightSources: LightSourceList, camera: Camera] = {
 lat, long, longPlusOne, latPlusOne: NAT;
 poly3d: Poly3d ← SVPolygon3d.CreatePoly[4];
 avgDepth: REAL;
 localCS: CoordSystem;
 meshRecord: ToroidalMesh;
 thisTorSurface: SortedToroidalSurface;
 thisTorSurface ← NARROW[ps.whichSurface];
 localCS ← ps.assembly.coordSys;
 meshRecord ← thisTorSurface.meshRecord;
 lat ← thisTorSurface.lat;
 long ← thisTorSurface.long; -- we have surface [lat][long]
 avgDepth ← ps.depth;
-- Draw the side face
 latPlusOne ← IF lat = meshRecord.linesOfLatitude THEN 1 ELSE lat + 1;
 longPlusOne ← IF long = meshRecord.linesOfLongitude THEN 1 ELSE long + 1;
 poly3d ← SVPolygon3d.AddPolyPoint[poly3d, meshRecord.array[lat][long]];
 poly3d ← SVPolygon3d.AddPolyPoint[poly3d, meshRecord.array[lat][longPlusOne]];
 poly3d ← SVPolygon3d.AddPolyPoint[poly3d, meshRecord.array[latPlusOne][longPlusOne]];
 poly3d ← SVPolygon3d.AddPolyPoint[poly3d, meshRecord.array[latPlusOne][long]];
 CSGGraphics.DrawAreaNormalAbsolute[dc, ps.normal, poly3d, ps.assembly.artwork, lightSources, camera, localCS];
 }; -- end of DrawPlanarSurfaceToroidalSweep

END.