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,
Geometry3dVector USING [Add, Cross, Mul, Normalize, Ortho, Sub, Triple, TripleSequence],
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],
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];
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;
WriteVertexToShapeFileStream: PROC[vertex: Geometry3dVector.Triple, stream: IO.STREAM] ~ BEGIN
IO.PutF[stream, "\t%g\t%g\t%g\n", IO.real[vertex.x], IO.real[vertex.y], IO.real[vertex.z]];
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;
WriteVertexToShapeFileStream[vertex, shapeOutStream];
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"];
For each point P in the cell, write four vertices. These vertices are obtained as follows. Let v1 be a unit vector in the direction from P towards the next point in the cell. (For the last point in the cell, let v1 point back to the first one.) Let v2 be an arbitrary unit vector orthogonal to v1. Let v3 be the cross product of v1 and v2. The four vertices are P + s(v2), P + s(v3), P - s(v2), and P - s(v3), where s is a small scalar.
FOR i: NAT IN [0..points.length) DO
p, q, v1, v2, v3, vertex1, vertex2, vertex3, vertex4: Geometry3dVector.Triple;
scale: REAL ~ 0.025;
p ← points[i];
q ← IF i = (points.length-1) THEN points[0] ELSE points[i+1];
v1 ← Geometry3dVector.Normalize[Geometry3dVector.Sub[q, p]];
v2 ← Geometry3dVector.Ortho[v1];
v3 ← Geometry3dVector.Cross[v1, v2];
vertex1 ← Geometry3dVector.Add[p, Geometry3dVector.Mul[v2, scale]];
vertex2 ← Geometry3dVector.Add[p, Geometry3dVector.Mul[v3, scale]];
vertex3 ← Geometry3dVector.Sub[p, Geometry3dVector.Mul[v2, scale]];
vertex4 ← Geometry3dVector.Sub[p, Geometry3dVector.Mul[v3, scale]];
WriteVertexToShapeFileStream[vertex1, shapeOutStream];
WriteVertexToShapeFileStream[vertex2, shapeOutStream];
WriteVertexToShapeFileStream[vertex3, shapeOutStream];
WriteVertexToShapeFileStream[vertex4, shapeOutStream];
ENDLOOP;
Write four lateral 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 * 4) + 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+5], IO.int[j+4]];
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+6], IO.int[j+5]];
IO.PutF[shapeOutStream, "\t%g\t%g\t%g\t%g\t%g\n", IO.int[4], IO.int[j+2], IO.int[j+3], IO.int[j+7], IO.int[j+6]];
IO.PutF[shapeOutStream, "\t%g\t%g\t%g\t%g\t%g\n", IO.int[4], IO.int[j+3], IO.int[j], IO.int[j+4], IO.int[j+7]];
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.