ThreeDHacksImpl.mesa
James Rauen, August 21, 1986 9:28:24 pm PDT
Last edited by: James Rauen January 18, 1988 5:55:05 pm PST
DIRECTORY
Atom USING [PutPropOnList],
CADTypes USING [VertexSequence, TriangleSequence],
FS USING [StreamOpen],
Geometry3dBasics,
Imager USING [black, Context, MaskFill, MaskStroke, PathProc, SetColor, SetStrokeEnd, SetStrokeJoint, SetStrokeWidth, StrokeEnd, StrokeJoint],
ImagerColor USING [RGB],
IO USING [Close, int, PutF, real, STREAM, RopeFromROS, ROS],
MessageWindow USING [Append, Blink],
Real USING [InlineFix],
RealFns USING [SqRt],
Rope USING [ROPE],
SceneUtilities USING [NewShape, ReadShape, ShapeFromData],
ShapeUtilities USING [XfmPtToDisplay, XfmPtToEyeSpace],
ThreeDBasics USING [Context, GetSurfaceType, ImagerProc, ImagerProcRec, NatSequence, PatchProc, PtrPatch, PtrPatchSequence, PutShading, RegisterSurfaceType, ShadingValue, ShadingSequence, ShapeClass, ShapeInstance, ShapeProc, Vertex, VertexSequence],
ThreeDHacks USING [],
ThreeDViewer USING [DrawInViewer],
Geometry3dVector USING [Add, Mul, Triple, TripleSequence];
ThreeDHacksImpl: CEDAR PROGRAM
IMPORTS Atom, FS, Geometry3dVector, Imager, IO, Real, RealFns, SceneUtilities, ShapeUtilities, ThreeDBasics, ThreeDViewer
EXPORTS ThreeDHacks
~ BEGIN
RegisterNewClasses: PUBLIC PROC [context3d: REF ThreeDBasics.Context] ~ BEGIN
Note: This is modeled after InitClasses in ThreeDWorld's StandardPatchProcs
I'm not sure yet what the validate proc is for, so it probably does the wrong thing now.
standardClass: ThreeDBasics.ShapeClass ← ThreeDBasics.GetSurfaceType[$Bezier];
standardClass.type ← $FatPoint;
standardClass.validate ← StandardPatchProcs.ValidatePatchShape;
standardClass.display ← NIL;
standardClass.displayPatch ← FatPointPatchDisplayProc;
ThreeDBasics.RegisterSurfaceType[standardClass, $FatPoint];
standardClass.type ← $FatSeg;
standardClass.validate ← StandardPatchProcs.ValidatePatchShape;
standardClass.displayPatch ← FatSegPatchDisplayProc;
ThreeDBasics.RegisterSurfaceType[standardClass, $FatSeg];
END;
FatPointPatchDisplayProc: ThreeDBasics.PatchProc ~ BEGIN
This procedure is called when ThreeDWorld wants to render the polygon comprising the FatPoint. (This procedure was inspired, in part, by Tilers.ConstantTiler)
Arguments: context: REF Context, patch: REF Patch, data: REF ANYNIL
dataList: LIST OF REAL;
coordinates, eyespaceCoordinates, screenCoordinates: Geometry3dVector.Triple;
Get the screen coordinates of the first vertex in the patch.
dataList ← LIST[patch.vtx[0].coord.sx, patch.vtx[0].coord.sy];
coordinates ← [
x: patch.vtx[0].coord.x,
y: patch.vtx[0].coord.y,
z: patch.vtx[0].coord.z];
[eyespaceCoordinates] ←
ShapeUtilities.XfmPtToEyeSpace[context, coordinates];
screenCoordinates ←
ShapeUtilities.XfmPtToDisplay[context, eyespaceCoordinates];
dataList ← LIST[screenCoordinates.x, screenCoordinates.y];
Draw a black circle, radius 10 pixels, centered at the screen coordinates of the first vertex.
ThreeDViewer.DrawInViewer[context,
NEW[ThreeDBasics.ImagerProcRec ← [proc: FatPointImagerProc, data: dataList]]];
RETURN[NIL];
END;
FatPointImagerProc: ThreeDBasics.ImagerProc ← BEGIN
Receives three arguments: context (ThreeDBasics.Context), imagerCtx (Imager.Context), and data (REF ANY). The data is narrowed to a list of two elements: screenX (REAL) and screenY (REAL).
dataList: LIST OF REALNARROW[data];
screenX: REAL ← dataList.first;
screenY: REAL ← dataList.rest.first;
Path: Imager.PathProc ~ BEGIN
moveTo[[screenX+10, screenY]];
arcTo[[screenX-10, screenY], [screenX+10, screenY]];
END;
Imager.SetColor[imagerCtx, Imager.black]; --Fill in the outline, colored black
Imager.MaskFill[imagerCtx, Path];
END;
FatSegPatchDisplayProc: ThreeDBasics.PatchProc ~ BEGIN
Arguments: context: REF Context, patch: REF Patch, data: REF ANYNIL
Get the screen coordinates of the first two vertices in the patch, which will be the endpoints of the section of the FatSeg being drawn.
dataList: LIST OF REAL;
p0Coordinates, p0EyespaceCoordinates, p0ScreenCoordinates, p1Coordinates, p1EyespaceCoordinates, p1ScreenCoordinates: Geometry3dVector.Triple;
Transform the coordinates of the segment's endpoints to screen coordinates.
p0Coordinates ← [
x: patch.vtx[0].coord.x,
y: patch.vtx[0].coord.y,
z: patch.vtx[0].coord.z];
[p0EyespaceCoordinates] ←
ShapeUtilities.XfmPtToEyeSpace[context, p0Coordinates];
p0ScreenCoordinates ←
ShapeUtilities.XfmPtToDisplay[context, p0EyespaceCoordinates];
p1Coordinates ← [
x: patch.vtx[1].coord.x,
y: patch.vtx[1].coord.y,
z: patch.vtx[1].coord.z];
[p1EyespaceCoordinates] ←
ShapeUtilities.XfmPtToEyeSpace[context, p1Coordinates];
p1ScreenCoordinates ←
ShapeUtilities.XfmPtToDisplay[context, p1EyespaceCoordinates];
dataList ← LIST[p0ScreenCoordinates.x, p0ScreenCoordinates.y, p1ScreenCoordinates.x, p1ScreenCoordinates.y];
Draw a rounded, thick black line between the two points.
ThreeDViewer.DrawInViewer[context,
NEW[ThreeDBasics.ImagerProcRec ← [proc: FatSegImagerProc, data: dataList]]];
Bail out if the patch is clipped.
IF patch.clipState = clipped THEN RETURN[NIL];
RETURN[NIL];
END;
FatSegImagerProc: ThreeDBasics.ImagerProc ← BEGIN
dataList: LIST OF REALNARROW[data];
firstScreenX: REAL ← dataList.first;
firstScreenY: REAL ← dataList.rest.first;
secondScreenX: REAL ← dataList.rest.rest.first;
secondScreenY: REAL ← dataList.rest.rest.rest.first;
Path: Imager.PathProc ~ BEGIN
moveTo[[firstScreenX, firstScreenY]];
lineTo[[secondScreenX, secondScreenY]];
END;
Imager.SetColor[imagerCtx, Imager.black];
Imager.SetStrokeWidth[imagerCtx, 5];
Imager.SetStrokeEnd[imagerCtx, round];
Imager.SetStrokeJoint[imagerCtx, round];
Imager.MaskStroke[imagerCtx, Path];
END;
MakeFatPoint: PUBLIC PROC [name: Rope.ROPE, position: Geometry3dVector.Triple] RETURNS [newFatPoint: REF ThreeDBasics.ShapeInstance] ~ BEGIN
MakeFatPoint: PUBLIC PROC [name: Rope.ROPE, position: Geometry3dVector.Triple] RETURNS [newFatPoint: REF ThreeDBasics.ShapeInstance] ~ BEGIN
surface: Geometry3dBasics.NatTable ← NEW[Geometry3dBasics.NatTableRep[1]];
patch1: Geometry3dBasics.NatSequence ← NEW[Geometry3dBasics.NatSequenceRep[3]];
vertices: Geometry3dBasics.TripleSequence ← NEW[Geometry3dBasics.TripleSequenceRep[3]];
surface.length ← 1;
patch1.length ← 3;
vertices.length ← 3;
vertices[0] ← position;
vertices[1] ← Geometry3dVector.Add[position, [x: 0.1, y: 0.0, z: 0.0]];
vertices[2] ← Geometry3dVector.Add[position, [x: 0.0, y: 0.1, z: 0.0]];
patch1[0] ← 0;
patch1[1] ← 1;
patch1[2] ← 2;
surface[0] ← patch1;
newFatPoint ← SceneUtilities.ShapeFromData[
name: name,
surface: surface,
vertices: vertices,
insideVisible: TRUE,
faceted: TRUE,
type: $ConvexPolygon];
END;
OldMakeFatPoint: PUBLIC PROC [name: Rope.ROPE, position: Geometry3dVector.Triple] RETURNS [newFatPoint: REF ThreeDBasics.ShapeInstance] ~ BEGIN
A FatPoint is, ideally, a single point located at position. To force ThreeDWorld to call the drawing procedure, it is necessary to have FatPoints contain a polygonal patch. So a FatPoint is composed of three vertices, the first one located at position and the others nearby, and one triangular patch defined by the three vertices.
Scratch variables.
nullVertex: ThreeDBasics.Vertex;
nullShade: ThreeDBasics.ShadingValue;
surface: REF ThreeDBasics.PtrPatchSequence;
Begin creating the newFatPoint.
newFatPoint ← SceneUtilities.NewShape[name];
newFatPoint.fileName ← "";
newFatPoint.numSurfaces ← 1;
newFatPoint.class^ ← ThreeDBasics.GetSurfaceType[$FatPoint];
newFatPoint.insideVisible ← TRUE;
Initialize the vertex and shade sequences.
newFatPoint.vertex ← NEW[ThreeDBasics.VertexSequence[3]];
newFatPoint.vertex.length ← 3;
newFatPoint.shade ← NEW[ThreeDBasics.ShadingSequence[3]];
newFatPoint.shade.length ← 3;
FOR i: NAT IN [0..3) DO
newFatPoint.vertex[i] ← NEW[ThreeDBasics.Vertex ← nullVertex];
newFatPoint.shade[i] ← NEW[ThreeDBasics.ShadingValue ← nullShade];
ENDLOOP;
ThreeDBasics.PutShading[newFatPoint, $Color, NEW[ImagerColor.RGB ← [0.7, 0.7, 0.7]]];
Put in the coordinates of the vertices.
newFatPoint.vertex[0].x ← position.x;
newFatPoint.vertex[0].y ← position.y;
newFatPoint.vertex[0].z ← position.z;
newFatPoint.vertex[1].x ← position.x + 0.10;
newFatPoint.vertex[1].y ← position.y;
newFatPoint.vertex[1].z ← position.z;
newFatPoint.vertex[2].x ← position.x;
newFatPoint.vertex[2].y ← position.y + 0.01;
newFatPoint.vertex[2].z ← position.z;
Build the patch.
newFatPoint.surface ← NEW[ThreeDBasics.PtrPatchSequence[1]];
surface ← NARROW[newFatPoint.surface, REF ThreeDBasics.PtrPatchSequence];
surface[0] ← NEW[ThreeDBasics.PtrPatch];
surface[0].vtxPtr ← NEW[ThreeDBasics.NatSequence[3]];
surface[0].vtxPtr.length ← 3;
surface[0].nVtces ← 3;
surface[0].type ← $FatPoint;
surface[0].oneSided ← FALSE;
surface[0].vtxPtr[0] ← 0;
surface[0].vtxPtr[1] ← 1;
surface[0].vtxPtr[2] ← 2;
surface[0].props ← Atom.PutPropOnList[NIL, $ShapeName, name];
Do the centroid and bounding radius.
newFatPoint.centroid.x ← position.x;
newFatPoint.centroid.y ← position.y;
newFatPoint.centroid.z ← position.z;
newFatPoint.boundingRadius ← 0.1;
END;
OldMakeFatSeg: PUBLIC PROC [name: Rope.ROPE, points: Geometry3dVector.TripleSequence] RETURNS [newFatSeg: REF ThreeDBasics.ShapeInstance] ~ BEGIN
Scratch variables.
nullVertex: ThreeDBasics.Vertex;
nullShade: ThreeDBasics.ShadingValue;
surface: REF ThreeDBasics.PtrPatchSequence;
radius: REAL;
Begin creating the newFatSeg. -- new version 9/22/86, for empty objects
newFatSeg ← SceneUtilities.NewShape[name];
There must be at least two points to make the FatSeg.
IF points.length < 2 THEN RETURN;
There must be at least two points to make the FatSeg. -- old version
Error: SIGNAL = CODE;
IF points.length < 2 THEN SIGNAL Error;
Begin creating the newFatSeg.
newFatSeg ← ThreeDScenes.NewShape[name];
newFatSeg.fileName ← "";
newFatSeg.numSurfaces ← points.length - 1;
newFatSeg.class^ ← ThreeDBasics.GetSurfaceType[$ConvexPolygon];
newFatSeg.insideVisible ← TRUE;
Initialize the vertex and shade sequences.
newFatSeg.vertex ← NEW[ThreeDBasics.VertexSequence[points.length]];
newFatSeg.shade ← NEW[ThreeDBasics.ShadingSequence[points.length]];
FOR i: NAT IN [0..points.length) DO
newFatSeg.vertex[i] ← NEW[ThreeDBasics.Vertex ← nullVertex];
newFatSeg.shade[i] ← NEW[ ThreeDBasics.ShadingValue ← nullShade];
ENDLOOP;
ThreeDBasics.PutShading[newFatSeg, $Color, NEW[ImagerColor.RGB ← [0.7, 0.7, 0.7]]];
Put in the coordinates of the vertices.
FOR i: NAT IN [0..points.length) DO
newFatSeg.vertex[i].x ← points[i].x;
newFatSeg.vertex[i].y ← points[i].y;
newFatSeg.vertex[i].z ← points[i].z;
ENDLOOP;
Build the patches.
newFatSeg.surface ← NEW[ThreeDBasics.PtrPatchSequence[points.length - 1]];
surface ← NARROW[newFatSeg.surface, REF ThreeDBasics.PtrPatchSequence];
FOR i: NAT IN [0..points.length - 1) DO
surface[i] ← NEW[ThreeDBasics.PtrPatch];
surface[i].vtxPtr ← NEW[ThreeDBasics.NatSequence[3]];
surface[i].vtxPtr.length ← 3;
surface[i].nVtces ← 3;
surface[i].type ← $ConvexPolygon;
surface[i].oneSided ← FALSE;
surface[i].vtxPtr[0] ← i;
surface[i].vtxPtr[1] ← i + 1;
surface[i].vtxPtr[2] ← i;
ENDLOOP;
Do the centroid and bounding radius.
newFatSeg.centroid.x ← newFatSeg.vertex[0].x;
newFatSeg.centroid.y ← newFatSeg.vertex[0].y;
newFatSeg.centroid.z ← newFatSeg.vertex[0].z;
newFatSeg.boundingRadius ← 0;
FOR i: NAT IN [0..points.length) DO
radius ← RealFns.SqRt[
  Sqr[newFatSeg.vertex[i].x - newFatSeg.centroid.x]
+ Sqr[newFatSeg.vertex[i].y - newFatSeg.centroid.y]
+ Sqr[newFatSeg.vertex[i].z - newFatSeg.centroid.z]];
IF radius > newFatSeg.boundingRadius
THEN newFatSeg.boundingRadius ← radius;
ENDLOOP;
END;
MakeFatPoint: PUBLIC PROC [name: Rope.ROPE, position: Geometry3dVector.Triple] RETURNS [newFatPoint: REF ThreeDBasics.ShapeInstance] ~ BEGIN
Streams and other variables
shapeOutStream: IO.STREAM;
shapeFileRope: Rope.ROPE;
tempFileName: Rope.ROPE ~ "[]<>Foo>TemporaryZeroCellFile";
tempFileOutStream: IO.STREAM;
u: REAL ~ 0.61833;
icosahedronVertices: LIST OF Geometry3dVector.Triple ← LIST[
[-u, 0, -1], [u, 0, -1], [u, 0, 1], [-u, 0, 1],
[-1, -u, 0], [-1, u, 0], [1, u, 0], [1, -u, 0],
[0, -1, u], [0, -1, -u], [0, 1, -u], [0, 1, u]];
icosahedronFaces: LIST OF Geometry3dVector.Triple ← LIST[
[1, 10, 2], [1, 2, 11], [1, 11, 6], [1, 6, 5], [1, 5, 10],
[4, 3, 9], [4, 9, 5], [4, 5, 6], [4, 6, 12], [4, 12, 3],
[7, 3, 12], [7, 12, 11], [7, 11, 2], [7, 2, 8], [7, 8, 3],
[6, 11, 12], [5, 9, 10], [9, 3, 8], [9, 8, 10], [10, 8, 2]];
scale: REAL ~ 0.05;
Open a rope output stream to write the shape file to.
shapeOutStream ← IO.ROS[];
Write some keylines.
IO.PutF[shapeOutStream, "TemporaryZeroCellFile\n"];
IO.PutF[shapeOutStream, "\nSurfaceType ~ ConvexPolygon, CountFromOne\n"];
IO.PutF[shapeOutStream, "\nVertices ~ xyzCoords: triple\n\n"];
Write the vertices of an icosahedron.
FOR i: NAT IN [0..12) DO
vertex: Geometry3dVector.Triple ← Geometry3dVector.Add[position, Geometry3dVector.Mul[icosahedronVertices.first, scale]];
icosahedronVertices ← icosahedronVertices.rest;
IO.PutF[shapeOutStream, "\t%g\t%g\t%g\n", IO.real[vertex.x], IO.real[vertex.y], IO.real[vertex.z]];
ENDLOOP;
Write the faces of an icosahedron.
IO.PutF[shapeOutStream, "\nPolygons ~ index: integer vertices: nats\n\n"];
FOR i: NAT IN [0..20) DO
IO.PutF[
shapeOutStream,
"\t%g\t%g\t%g\t%g\n",
IO.int[3],
IO.int[Real.InlineFix[icosahedronFaces.first.x]],
IO.int[Real.InlineFix[icosahedronFaces.first.y]],
IO.int[Real.InlineFix[icosahedronFaces.first.z]]
];
icosahedronFaces ← icosahedronFaces.rest;
ENDLOOP;
Close up the stream, get it as a rope, and write it to a temporary file.
shapeFileRope ← IO.RopeFromROS[shapeOutStream];
tempFileOutStream ← FS.StreamOpen[tempFileName, $create];
IO.PutF[tempFileOutStream, shapeFileRope];
IO.Close[tempFileOutStream];
Feed the file to ThreeDWorld constructors.
newFatPoint ← SceneUtilities.NewShape[name];
SceneUtilities.ReadShape[newFatPoint, tempFileName];
END;
MakeFatSeg: PUBLIC PROC [name: Rope.ROPE, points: Geometry3dVector.TripleSequence] RETURNS [newFatSeg: REF ThreeDBasics.ShapeInstance] ~ BEGIN
Streams and other variables
shapeOutStream: IO.STREAM;
shapeFileRope: Rope.ROPE;
tempFileName: Rope.ROPE ~ "[]<>Foo>TemporaryOneCellFile";
tempFileOutStream: IO.STREAM;
scale: REAL ~ 0.1;
Open a rope output stream to write the shape file to.
shapeOutStream ← IO.ROS[];
Write some keylines.
IO.PutF[shapeOutStream, "TemporaryOneCellFile\n"];
IO.PutF[shapeOutStream, "\nSurfaceType ~ ConvexPolygon, InsideVisible, CountFromOne\n"];
IO.PutF[shapeOutStream, "\nVertices ~ xyzCoords: triple\n\n"];
Write three vertices for each point in the cell.
FOR i: NAT IN [0..points.length) DO
FOR j: NAT IN [0..3) DO
offset: Geometry3dVector.Triple ← SELECT j FROM
0 => [0, 0, 0], 1 => [scale, 0, 0], 2 => [0, scale, 0], ENDCASE => [0, 0, 0];
vertex: Geometry3dVector.Triple ← Geometry3dVector.Add[points[i], offset];
IO.PutF[shapeOutStream, "\t%g\t%g\t%g\n", IO.real[vertex.x], IO.real[vertex.y], IO.real[vertex.z]];
ENDLOOP;
ENDLOOP;
Write three faces of a prism for each segment of the cell.
IO.PutF[shapeOutStream, "\nPolygons ~ index: integer vertices: nats\n\n"];
FOR i: NAT IN [0..points.length-1) DO
j: NAT ← (i * 3) + 1;
IO.PutF[shapeOutStream, "\t%g\t%g\t%g\t%g\t%g\n", IO.int[4], IO.int[j], IO.int[j+1], IO.int[j+4], IO.int[j+3]];
IO.PutF[shapeOutStream, "\t%g\t%g\t%g\t%g\t%g\n", IO.int[4], IO.int[j], IO.int[j+2], IO.int[j+5], IO.int[j+3]];
IO.PutF[shapeOutStream, "\t%g\t%g\t%g\t%g\t%g\n", IO.int[4], IO.int[j+1], IO.int[j+2], IO.int[j+5], IO.int[j+4]];
ENDLOOP;
Close up the stream, get it as a rope, and write it to a temporary file.
shapeFileRope ← IO.RopeFromROS[shapeOutStream];
tempFileOutStream ← FS.StreamOpen[tempFileName, $create];
IO.PutF[tempFileOutStream, shapeFileRope];
IO.Close[tempFileOutStream];
Feed the file to ThreeDWorld constructors.
newFatSeg ← SceneUtilities.NewShape[name];
SceneUtilities.ReadShape[newFatSeg, tempFileName];
END;
MakeTwoCell: PUBLIC PROC [name: Rope.ROPE, vertices: REF CADTypes.VertexSequence, triangles: REF CADTypes.TriangleSequence] RETURNS [newTwoCell: REF ThreeDBasics.ShapeInstance] ~ BEGIN
Streams and other variables
shapeOutStream: IO.STREAM;
shapeFileRope: Rope.ROPE;
tempFileName: Rope.ROPE ~ "[]<>Foo>TemporaryTwoCellFile";
tempFileOutStream: IO.STREAM;
Open a rope output stream to write the shape file to.
shapeOutStream ← IO.ROS[];
Write some keylines.
IO.PutF[shapeOutStream, "TemporaryTwoCellFile\n"];
IO.PutF[shapeOutStream, "\nSurfaceType ~ ConvexPolygon, InsideVisible, CountFromOne\n"];
IO.PutF[shapeOutStream, "\nVertices ~ xyzCoords: triple\n\n"];
Write the vertices, ignoring the first one (it's garbage).
FOR i: NAT IN [0..vertices.nVertices) DO
IO.PutF[
shapeOutStream,
"\t%g\t%g\t%g\n",
IO.real[vertices[i].x],
IO.real[vertices[i].y],
IO.real[vertices[i].z]];
ENDLOOP;
Write the polygons.
IO.PutF[shapeOutStream, "\nPolygons ~ index: integer vertices: nats\n\n"];
FOR i: NAT IN [0..triangles.nTriangles) DO
IO.PutF[
shapeOutStream,
"\t%g\t%g\t%g\t%g\n",
IO.int[3],
IO.int[triangles[i].firstVertex],
IO.int[triangles[i].secondVertex],
IO.int[triangles[i].thirdVertex]];
ENDLOOP;
Close up the stream, get it as a rope, and write it to a temporary file.
shapeFileRope ← IO.RopeFromROS[shapeOutStream];
tempFileOutStream ← FS.StreamOpen[tempFileName, $create];
IO.PutF[tempFileOutStream, shapeFileRope];
IO.Close[tempFileOutStream];
Feed the file to ThreeDWorld constructors.
newTwoCell ← SceneUtilities.NewShape[name];
ThreeDSurfaces.ReadShape[newTwoCell, tempFileName]; -- old version
IF triangles.nTriangles > 0 THEN
SceneUtilities.ReadShape[newTwoCell, tempFileName]; -- 9/22/86 - test put in for empty shapes
SIGNAL Foo;
END;
Foo: SIGNAL = CODE;
AnotherMakeTwoCell: PROC [name: Rope.ROPE, vertices: REF CADTypes.VertexSequence, triangles: REF CADTypes.TriangleSequence] RETURNS [newTwoCell: REF ThreeDBasics.ShapeInstance] ~ BEGIN
Scratch variables.
min, max: Geometry3dVector.Triple;
nullVertex: ThreeDBasics.Vertex;
nullShade: ThreeDBasics.ShadingValue;
surface: REF ThreeDBasics.PtrPatchSequence;
Begin creating the newTwoCell.
newTwoCell ← SceneUtilities.NewShape[name];
newTwoCell.fileName ← "";
newTwoCell.numSurfaces ← triangles.nTriangles;
newTwoCell.type ← $ConvexPolygon;
What about its class?
newTwoCell.insideVisible ← TRUE;
Initialize the vertex and shade sequences.
newTwoCell.vertex ← NEW[ThreeDBasics.VertexSequence[vertices.nVertices]];
newTwoCell.shade ← NEW[ThreeDBasics.ShadingSequence[vertices.nVertices]];
FOR i: NAT IN [0..vertices.nVertices) DO
newTwoCell.vertex[i] ← NEW[ThreeDBasics.Vertex ← nullVertex];
newTwoCell.shade[i] ← NEW[ ThreeDBasics.ShadingValue ← nullShade];
ENDLOOP;
ThreeDBasics.PutShading[newTwoCell, $Color, NEW[ImagerColor.RGB ← [0.2, 0.8, 0.8]]];
Put in the coordinates of the vertices.
FOR i: NAT IN [0..vertices.nVertices) DO
newTwoCell.vertex[i].x ← vertices[i].x;
newTwoCell.vertex[i].y ← vertices[i].y;
newTwoCell.vertex[i].z ← vertices[i].z;
ENDLOOP;
Build the patches.
newTwoCell.surface ← NEW[ThreeDBasics.PtrPatchSequence[triangles.nTriangles]];
surface ← NARROW[newTwoCell.surface, REF ThreeDBasics.PtrPatchSequence];
FOR i: NAT IN [0..triangles.nTriangles) DO
surface[i] ← NEW[ThreeDBasics.PtrPatch];
surface[i].vtxPtr ← NEW[ThreeDBasics.NatSequence[3]];
surface[i].vtxPtr.length ← 3;
surface[i].nVtces ← 3;
surface[i].type ← $ConvexPolygon;
surface[i].oneSided ← FALSE;
surface[i].vtxPtr[0] ← triangles[i].firstVertex;
surface[i].vtxPtr[1] ← triangles[i].secondVertex;
surface[i].vtxPtr[2] ← triangles[i].thirdVertex;
ENDLOOP;
Find approximation to the bounding sphere. (Shamelessly stolen from ThreeDSurfacesImpl)
min ← max ← [newTwoCell.vertex[0].x, newTwoCell.vertex[0].y, newTwoCell.vertex[0].z];
FOR i: NAT IN (0..newTwoCell.vertex.length) DO
IF newTwoCell.vertex[i] # NIL THEN BEGIN
IF newTwoCell.vertex[i].x < min.x
THEN min.x ← newTwoCell.vertex[i].x
ELSE IF newTwoCell.vertex[i].x > max.x THEN max.x ← newTwoCell.vertex[i].x;
IF newTwoCell.vertex[i].y < min.y
THEN min.y ← newTwoCell.vertex[i].y
ELSE IF newTwoCell.vertex[i].y > max.y THEN max.y ← newTwoCell.vertex[i].y;
IF newTwoCell.vertex[i].z < min.z
THEN min.z ← newTwoCell.vertex[i].z
ELSE IF newTwoCell.vertex[i].x > max.z THEN max.z ← newTwoCell.vertex[i].z;
END;
ENDLOOP;
newTwoCell.centroid.x ← (min.x + max.x) / 2;
newTwoCell.centroid.y ← (min.y + max.y) / 2;
newTwoCell.centroid.z ← (min.z + max.z) / 2;
newTwoCell.boundingRadius ← 0.;
FOR i: NAT IN [0..newTwoCell.vertex.length) DO
radius: REAL ← RealFns.SqRt[
  Sqr[newTwoCell.vertex[i].x - newTwoCell.centroid.x]
+ Sqr[newTwoCell.vertex[i].y - newTwoCell.centroid.y]
+ Sqr[newTwoCell.vertex[i].z - newTwoCell.centroid.z] ];
IF radius > newTwoCell.boundingRadius
THEN newTwoCell.boundingRadius ← radius;
ENDLOOP;
newTwoCell ← SceneUtilities.NewShape[name];
SceneUtilities.ReadShape[newTwoCell, "[]<>Users>Rauen.pa>AlgebraicSurfaces>ChampagneGlass.shape"];
END;
Sqr: PROC [number: REAL] RETURNS [result: REAL] ~ BEGIN
result ← number * number;
END;
END.