File: SweepGeometryImpl.mesa
Last edited by Bier on January 9, 1985 3:26:32 pm PST
Contents: Implementation of a simple, straight-line, sweep geometry package for fast interactive graphics
DIRECTORY
CSGGraphics,
Graphics,
IO,
RealFns,
SV2d,
SV3d,
SVDraw3d,
SVModelTypes,
SVPolygon2d,
SVPolygon3d,
SVSceneTypes,
SVVector3d,
SweepGeometry;
SweepGeometryImpl: PROGRAM
IMPORTS CSGGraphics, IO, SVDraw3d, RealFns, SVPolygon2d, SVPolygon3d, SVVector3d
EXPORTS SweepGeometry =
BEGIN
Assembly: TYPE = SVSceneTypes.Assembly;
Camera: TYPE = SVModelTypes.Camera;
CoordSystem: TYPE = SVModelTypes.CoordSystem;
LightSource: TYPE = SVModelTypes.LightSource;
LightSourceList: TYPE = LIST OF LightSource;
LinearMesh: TYPE = REF LinearMeshRecord;
LinearMeshRecord: TYPE = SweepGeometry.LinearMeshRecord;
MasterObject: TYPE = SVSceneTypes.MasterObject;
Path: TYPE = SV2d.Path;
PlanarSurface: TYPE = REF PlanarSurfaceObj;
PlanarSurfaceObj: TYPE = SVSceneTypes.PlanarSurfaceObj;
PlanarSurfaceList: TYPE = SVSceneTypes.PlanarSurfaceList;
Point2d: TYPE = SV2d.Point2d;
Point3d: TYPE = SV3d.Point3d;
Poly3d: TYPE = SV3d.Poly3d;
Polygon: TYPE = SV2d.Polygon;
RevoluteMesh: TYPE = REF RevoluteMeshRecord;
RevoluteMeshRecord: TYPE = SweepGeometry.RevoluteMeshRecord;
Shape: TYPE = SVSceneTypes.Shape;
ToroidalMesh: TYPE = REF ToroidalMeshRecord;
ToroidalMeshRecord: TYPE = SweepGeometry.ToroidalMeshRecord;
Vector: TYPE = SV3d.Vector;
globalImpatient: BOOLFALSE;
TooFewPointsForShadedSweep: PUBLIC 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 poly.len <=2 THEN SIGNAL TooFewPointsForShadedSweep;
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.
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.Sub[meshRecord.array[i][2],meshRecord.array[i][1]];
clockWise ← SVVector3d.Sub[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.Sub[meshRecord.array[i+1][jPlusOne],
meshRecord.array[i][j]];
rightToLeft ← SVVector3d.Sub[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.Sub[meshRecord.array[iPlusOne][jPlusOne],
meshRecord.array[i][j]];
rightToLeft ← SVVector3d.Sub[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
IF globalImpatient THEN
FOR lat: NAT ← 1, lat+(meshRecord.linesOfLatitude-1) UNTIL lat > meshRecord.linesOfLatitude DO
There has to be a better way to phrase the above loop.
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
ELSE
FOR lat: NAT IN [1..meshRecord.linesOfLatitude] DO
There has to be a better way to phrase the above loop.
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]];
SVDraw3d.DrawLocalVector[dc,thisNorm,midPoint,camera,localCS];
Draw bottom vector
thisNorm ← meshRecord.surfaces.back.normal;
midPoint ← [midPoint2d[1], midPoint2d[2], meshRecord.array[1][2][3]];
SVDraw3d.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.
SVDraw3d.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];
SVDraw3d.DrawLocalVector[dc,thisNorm,midPoint,camera,localCS];
Draw bottom normal
thisNorm ← meshRecord.surfaces.bottom.normal;
midPoint ← [0, meshRecord.array[meshRecord.linesOfLatitude][1][2], 0];
SVDraw3d.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.
SVDraw3d.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.
SVDraw3d.DrawLocalVector[dc,thisNorm,midPoint,camera,localCS];
ENDLOOP;
ENDLOOP;
}; -- end of DrawNormalsToroidalSweep
PolyListLinear: PUBLIC PROC [f: IO.STREAM, meshRecord: LinearMesh] = {
postPlusOne: NAT;
OUTPUT THE VERTEX DESCRIPTIONS
f.PutF["vertices [%g]:\n", IO.int[2*meshRecord.len]];
FOR i: NAT IN[1..meshRecord.len] DO
f.PutF["  %g %g %g\n",
IO.real[meshRecord.array[i][1][1]],
IO.real[meshRecord.array[i][1][2]],
IO.real[meshRecord.array[i][1][3]] ];
f.PutF["v%g  %g %g %g\n", IO.int[i],
IO.real[meshRecord.array[i][1][1]],
IO.real[meshRecord.array[i][1][2]],
IO.real[meshRecord.array[i][1][3]] ];
ENDLOOP;
FOR i: NAT IN[1..meshRecord.len] DO
f.PutF["  %g %g %g\n",
IO.real[meshRecord.array[i][2][1]],
IO.real[meshRecord.array[i][2][2]],
IO.real[meshRecord.array[i][2][3]] ];
f.PutF["v%g  %g %g %g\n", IO.int[i+meshRecord.len],
IO.real[meshRecord.array[i][2][1]],
IO.real[meshRecord.array[i][2][2]],
IO.real[meshRecord.array[i][2][3]] ];
ENDLOOP;
f.PutChar[IO.CR];
OUTPUT THE FACE DESCRIPTIONS (Clockwise)
f.PutF["faces [%g]:\n", IO.int[2+meshRecord.len]];
Describe the front face.
f.PutF["f  ("];
FOR i: NAT IN[1..meshRecord.len] DO
f.PutF["%g ", IO.int[i]];
ENDLOOP;
f.PutF[")\n"];
Describe the back face.
f.PutF["f  ("];
FOR i: NAT DECREASING IN[1..meshRecord.len] DO
f.PutF["%g ", IO.int[i+meshRecord.len]];
ENDLOOP;
f.PutF[")\n"];
Describe the side faces.
FOR post: NAT IN[1..meshRecord.len] DO
postPlusOne ← IF post = meshRecord.len THEN 1 ELSE post + 1;
f.PutF["f  (%g %g %g %g )\n",
IO.int[postPlusOne],
IO.int[postPlusOne+meshRecord.len],
IO.int[post+meshRecord.len],
IO.int[post]];
ENDLOOP;
f.PutChar[IO.CR];
}; -- end of PolyListLinear
PolyListRevolute: PUBLIC PROC [f: IO.STREAM, meshRecord: RevoluteMesh] = {
longPlusOne: NAT;
OUTPUT THE VERTEX DESCRIPTIONS
f.PutF["vertices [%g]:\n", IO.int[meshRecord.linesOfLatitude*meshRecord.linesOfLongitude]];
FOR lat: NAT IN[1..meshRecord.linesOfLatitude] DO
FOR long: NAT IN[1..meshRecord.linesOfLongitude] DO
f.PutF["  %g %g %g\n",
IO.real[meshRecord.array[lat][long][1]],
IO.real[meshRecord.array[lat][long][2]],
IO.real[meshRecord.array[lat][long][3]] ];
f.PutF["v%g.%g  %g %g %g\n", IO.int[lat], IO.int[long], IO.real[meshRecord.array[lat][long][1]], IO.real[meshRecord.array[lat][long][2]], IO.real[meshRecord.array[lat][long][3]] ];
IO.int[(lat-1)*meshRecord.linesOfLongitude + long],
ENDLOOP;
ENDLOOP;
f.PutChar[IO.CR];
OUTPUT THE FACE DESCRIPTIONS (Clockwise)
f.PutF["faces [%g]:\n", IO.int[(meshRecord.linesOfLatitude-1)*meshRecord.linesOfLongitude+2]];
Describe the top face
f.PutF["f  ("];
FOR j: NAT DECREASING IN[1..meshRecord.linesOfLongitude] DO
f.PutF["%g ", IO.int[j]];
f.PutF["v%g.%g ", IO.int[1], IO.int[j]];
ENDLOOP;
f.PutF[")\n"];
Describe the bottom face
f.PutF["f  ("];
FOR j: NAT IN[1..meshRecord.linesOfLongitude] DO
f.PutF["%g ", IO.int[(meshRecord.linesOfLatitude-1)*meshRecord.linesOfLongitude + j]];
f.PutF["v%g.%g ", IO.int[meshRecord.linesOfLatitude], IO.int[j]];
ENDLOOP;
f.PutF[")\n"];
Describe the side faces
FOR lat: NAT IN[1..meshRecord.linesOfLatitude-1] DO
FOR long: NAT IN[1..meshRecord.linesOfLongitude] DO
longPlusOne ← IF long = meshRecord.linesOfLongitude THEN 1 ELSE long + 1;
f.PutF["f  (%g %g ",
IO.int[lat*meshRecord.linesOfLongitude + long],
IO.int[lat*meshRecord.linesOfLongitude + longPlusOne]];
f.PutF["%g %g )\n",
IO.int[(lat-1)*meshRecord.linesOfLongitude + longPlusOne],
IO.int[(lat-1)*meshRecord.linesOfLongitude + long]];
f.PutF["f  (v%g.%g v%g.%g ",
IO.int[lat+1], IO.int[long],
IO.int[lat+1], IO.int[longPlusOne]];
f.PutF["v%g.%g v%g.%g )\n",
IO.int[lat], IO.int[longPlusOne],
IO.int[lat], IO.int[long]];
ENDLOOP;
ENDLOOP;
f.PutChar[IO.CR];
}; -- end of PolyListRevolute
PolyListToroidal: PUBLIC PROC [f: IO.STREAM, meshRecord: ToroidalMesh] = {
longPlusOne, latPlusOne: NAT;
OUTPUT THE VERTEX DESCRIPTIONS
f.PutF["vertices [%g]:\n", IO.int[meshRecord.linesOfLatitude*meshRecord.linesOfLongitude]];
FOR lat: NAT IN[1..meshRecord.linesOfLatitude] DO
FOR long: NAT IN[1..meshRecord.linesOfLongitude] DO
f.PutF["  %g %g %g\n",
IO.real[meshRecord.array[lat][long][1]],
IO.real[meshRecord.array[lat][long][2]],
IO.real[meshRecord.array[lat][long][3]] ];
f.PutF["v%g.%g  %g %g %g\n", IO.int[lat], IO.int[long], IO.real[meshRecord.array[lat][long][1]], IO.real[meshRecord.array[lat][long][2]], IO.real[meshRecord.array[lat][long][3]] ];
ENDLOOP;
ENDLOOP;
f.PutChar[IO.CR];
OUTPUT THE FACE DESCRIPTIONS (Clockwise)
Describe the side faces
f.PutF["faces [%g]:\n", IO.int[meshRecord.linesOfLatitude*meshRecord.linesOfLongitude]];
FOR lat: NAT IN[1..meshRecord.linesOfLatitude] DO
FOR long: NAT IN[1..meshRecord.linesOfLongitude] DO
latPlusOne ← IF lat = meshRecord.linesOfLatitude THEN 1 ELSE lat + 1;
longPlusOne ← IF long = meshRecord.linesOfLongitude THEN 1 ELSE long + 1;
f.PutF["f  (%g %g ",
IO.int[(latPlusOne-1)*meshRecord.linesOfLongitude + long],
IO.int[(latPlusOne-1)*meshRecord.linesOfLongitude + longPlusOne]];
f.PutF["%g %g )\n",
IO.int[(lat-1)*meshRecord.linesOfLongitude + longPlusOne],
IO.int[(lat-1)*meshRecord.linesOfLongitude + long]];
f.PutF["f  (v%g.%g v%g.%g ",
IO.int[latPlusOne], IO.int[long],
IO.int[latPlusOne], IO.int[longPlusOne]];
f.PutF["v%g.%g v%g.%g )\n",
IO.int[lat], IO.int[longPlusOne],
IO.int[lat], IO.int[long]];
ENDLOOP;
ENDLOOP;
}; -- end of PolyListToroidal
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;
shape: Shape ← NARROW[assembly.shape, Shape];
mo: MasterObject ← shape.mo;
localCS: CoordSystem ← shape.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 ← AverageDepthInCamera[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 ← AverageDepthInCamera[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 ← AverageDepthInCamera[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;
shape: Shape;
meshRecord: LinearMesh;
thisLinSurface: SortedLinearSurface;
localCS: CoordSystem;
thisLinSurface ← NARROW[ps.whichSurface];
meshRecord ← thisLinSurface.meshRecord;
shape ← NARROW[ps.assembly.shape];
localCS ← shape.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;
};
AverageDepthInCamera: 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;
shape: Shape ← NARROW[assembly.shape];
mo: MasterObject ← shape.mo;
jPlusOne: NAT;
localCS: CoordSystem ← shape.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 ← AverageDepthInCamera[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 ← AverageDepthInCamera[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 ← AverageDepthInCamera[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;
shape: Shape;
localCS: CoordSystem;
thisRevSurface ← NARROW[ps.whichSurface];
meshRecord ← thisRevSurface.meshRecord;
shape ← NARROW[ps.assembly.shape];
localCS ← shape.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;
shape: Shape ← NARROW[assembly.shape, Shape];
mo: MasterObject ← shape.mo;
localCS: CoordSystem ← shape.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 ← AverageDepthInCamera[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;
shape: Shape;
localCS: CoordSystem;
meshRecord: ToroidalMesh;
thisTorSurface: SortedToroidalSurface;
thisTorSurface ← NARROW[ps.whichSurface];
shape ← NARROW[ps.assembly.shape];
localCS ← shape.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.