<<>> <> <> <> <> DIRECTORY Atom, Basics, CedarProcess, Convert, FileNames, FS, G2dBasic, G3dBasic, G3dIO, G3dMatrix, G3dPolygon, G3dQuaternion, G3dShape, G3dVector, IO, PFS, Process, Real, RealFns, Rope; G3dShapeImpl: CEDAR PROGRAM IMPORTS Atom, Basics, CedarProcess, Convert, FileNames, FS, G2dBasic, G3dBasic, G3dIO, G3dMatrix, G3dPolygon, G3dQuaternion, G3dVector, IO, PFS, Process, Real, RealFns, Rope EXPORTS G3dShape ~ BEGIN <> PropList: TYPE ~ Atom.PropList; Box3d: TYPE ~ G3dBasic.Box; IntegerSequence: TYPE ~ G3dBasic.IntegerSequence; IntegerSequenceRep: TYPE ~ G3dBasic.IntegerSequenceRep; NatSequence: TYPE ~ G3dBasic.NatSequence; NatSequenceRep: TYPE ~ G3dBasic.NatSequenceRep; NatTable: TYPE ~ G3dBasic.NatTable; Surface: TYPE ~ G3dBasic.Surface; SurfaceSequence: TYPE ~ G3dBasic.SurfaceSequence; SurfaceSequenceRep: TYPE ~ G3dBasic.SurfaceSequenceRep; Pair: TYPE ~ G3dBasic.Pair; PairSequence: TYPE ~ G3dBasic.PairSequence; PairSequenceRep: TYPE ~ G3dBasic.PairSequenceRep; Ray: TYPE ~ G3dBasic.Ray; RealSequence: TYPE ~ G3dBasic.RealSequence; RealSequenceRep: TYPE ~ G3dBasic.RealSequenceRep; Triple: TYPE ~ G3dBasic.Triple; TripleSequence: TYPE ~ G3dBasic.TripleSequence; TripleSequenceRep: TYPE ~ G3dBasic.TripleSequenceRep; Matrix: TYPE ~ G3dMatrix.Matrix; AxisAngle: TYPE ~ G3dQuaternion.AxisAngle; Quaternion: TYPE ~ G3dQuaternion.Quaternion; Edge: TYPE ~ G3dShape.Edge; EdgeRep: TYPE ~ G3dShape.EdgeRep; EdgeSequence: TYPE ~ G3dShape.EdgeSequence; Face: TYPE ~ G3dShape.Face; FaceRep: TYPE ~ G3dShape.FaceRep; FaceSequence: TYPE ~ G3dShape.FaceSequence; FaceSequenceRep: TYPE ~ G3dShape.FaceSequenceRep; ScreenSequence: TYPE ~ G3dShape.ScreenSequence; Shape: TYPE ~ G3dShape.Shape; ShapeRep: TYPE ~ G3dShape.ShapeRep; ShapeSequence: TYPE ~ G3dShape.ShapeSequence; ShapeSequenceRep: TYPE ~ G3dShape.ShapeSequenceRep; SurfaceProc: TYPE ~ G3dShape.SurfaceProc; Validity: TYPE ~ G3dShape.Validity; Vertex: TYPE ~ G3dShape.Vertex; VertexRep: TYPE ~ G3dShape.VertexRep; VertexSequence: TYPE ~ G3dShape.VertexSequence; VertexSequenceRep: TYPE ~ G3dShape.VertexSequenceRep; STREAM: TYPE ~ IO.STREAM; ROPE: TYPE ~ Rope.ROPE; huge: REAL ~ Real.LargestNumber; Error: PUBLIC SIGNAL [code: ATOM, reason: ROPE] = CODE; <> ShapeFromFile: PUBLIC PROC [fileName: ROPE] RETURNS [s: Shape] ~ { in: STREAM ¬ PFS.StreamOpen[PFS.PathFromRope[FileNames.ResolveRelativePath[fileName]]]; < Error[error.code, error.explanation]];>> s ¬ ShapeFromStream[in]; s.fileName ¬ fileName; s.name ¬ FileNames.GetShortName[fileName]; IO.Close[in]; }; ShapeFromStream: PUBLIC PROC [in: STREAM] RETURNS [s: Shape] ~ { Eq: PROC [r1, r2: ROPE] RETURNS [BOOL] ~ {RETURN[Rope.Equal[r1, r2, FALSE]]}; nVerts, nPolys: INT ¬ 0; line: G3dIO.Line ¬ G3dIO.ObtainLine[]; countFromOne, triangulated, tallyOnPolys: BOOL ¬ FALSE; map: IntegerSequence ¬ NIL; IF in = NIL THEN RETURN; s ¬ NEW[ShapeRep]; DO ENABLE G3dIO.Error => G3dIO.Error[reason, lineText, lineNumber]; -- raise error here Types: TYPE ~ RECORD[SEQUENCE max: INT OF {xyz,nrm,col,tex,tra,map,vtx,prop,tally}]; TestEndData: PROC ~ { index: INT ¬ IO.GetIndex[in]; line ¬ G3dIO.GetLine[in, line ! IO.EndOfStream => GOTO EOF]; IF line.type = data THEN G3dIO.Error["Excessive data found", line.rope, G3dIO.LineNumber[line]]; IO.SetIndex[in, index]; EXITS EOF => NULL; }; PutProp: PROC [key: ROPE, value: REF ANY] ~ { prop: REF ANY ¬ Convert.AtomFromRope[key]; IF Atom.GetPropFromList[s.props, prop] # NIL THEN { IO.SetIndex[in, index]; line ¬ G3dIO.GetLine[in, line]; G3dIO.Error[IO.PutFR1["Redundant key (%g)", IO.rope[key]], line.rope, G3dIO.LineNumber[line]]; }; s.props ¬ Atom.PutPropOnList[s.props, Convert.AtomFromRope[key], value]; }; StopAtComma: PROC [rope: ROPE] RETURNS [ROPE] ~ { RETURN[Rope.Substr[rope, 0, Rope.Index[rope, 0, ","]]]; }; GetIntegerCheckingComma: PROC [line: G3dIO.Line] RETURNS [i: INT] ~ { word: ROPE ¬ StopAtComma[G3dIO.GetWord[line]]; i ¬ Convert.IntFromRope[word ! Convert.Error => G3dIO.ErrorReport[convert, line]]; }; SubE: PROC [r: ROPE] RETURNS [b: BOOL] ~ {b ¬ Eq[Rope.Substr[rope,, Rope.Size[r]], r]}; GetTriple: PROC [l: G3dIO.Line, n: INT] RETURNS [t: Triple] ~ { Skip: PROC [l: G3dIO.Line] ~ { [] ¬ G3dIO.GetWord[l]; [] ¬ G3dIO.GetWord[l]; [] ¬ G3dIO.GetWord[l]; }; <> t ¬ G3dIO.GetTriple[line]; }; SetProps: PROC [fields: G3dIO.FieldSequence, types: REF Types] ~ { FOR f: INT IN [0..fields.length) DO IF types[f] = prop THEN PutProp[fields[f].id, fields[f].sequence]; ENDLOOP; }; AllocateFields: PROC [field: G3dIO.Field, nLines: INT] ~ { field.sequence ¬ SELECT field.type FROM integer => NEW[IntegerSequenceRep[nLines]], real => NEW[RealSequenceRep[nLines]], pair => NEW[PairSequenceRep[nLines]], triple => NEW[TripleSequenceRep[nLines]], nats => NEW[SurfaceSequenceRep[nLines]], ENDCASE => NIL; }; ReadProp: PROC [line: G3dIO.Line, f: G3dIO.Field, n: INT] ~ { SELECT f.type FROM integer => NARROW[f.sequence, IntegerSequence][n] ¬ G3dIO.GetInteger[line]; real => NARROW[f.sequence, RealSequence][n] ¬ G3dIO.GetReal[line]; pair => NARROW[f.sequence, PairSequence][n] ¬ G3dIO.GetPair[line]; triple => NARROW[f.sequence, TripleSequence][n] ¬ GetTriple[line, n]; nats => NARROW[f.sequence, NatTable][n] ¬ G3dIO.GetNats[line]; ENDCASE => NULL; }; rope: ROPE; key: ROPE ¬ G3dIO.NextKeyWord[in ! G3dIO.Error => EXIT]; index: INT ¬ IO.GetIndex[in]; -- may need this later to report PutProp error SELECT TRUE FROM Eq[key, "SurfaceType"] => { -- read shape information <> line ¬ G3dIO.FindKeyWord[in, key,,, line]; DO ENABLE G3dIO.Error => CONTINUE; IF (rope ¬ G3dIO.GetWord[line]) = NIL THEN EXIT; IF Rope.Find[rope, "--"] = 0 THEN EXIT; -- ignore remaining comment SELECT TRUE FROM SubE["Triangulated"] => triangulated ¬ TRUE; SubE["InsideVisible"] => s.showBackfaces ¬ TRUE; SubE["CountFromOne"] => countFromOne ¬ TRUE; SubE["TallyOnPolys"] => tallyOnPolys ¬ TRUE; ENDCASE => s.type ¬ Atom.MakeAtom[StopAtComma[rope]]; ENDLOOP; }; Eq[key, "DataSize"] => { -- read vertex/surface sizes: <> line ¬ G3dIO.FindKeyWord[in, key,,, line]; DO ENABLE Error => CONTINUE; IF (rope ¬ G3dIO.GetWord[line]) = NIL THEN EXIT; IF Rope.Find[rope, "--"] = 0 THEN EXIT; -- ignore remaining comment SELECT TRUE FROM SubE["Vertices"] => nVerts ¬ GetIntegerCheckingComma[line]; SubE["Polygons"], SubE["Surfaces"], SubE["Patch"], SubE["Bezier"] => nPolys ¬ GetIntegerCheckingComma[line]; ENDCASE; ENDLOOP; }; Eq[key, "Vertices"] => { -- read vertices <> dum: G3dIO.Line ¬ line ¬ G3dIO.FindKeyWord[in, key,,, line]; fields: G3dIO.FieldSequence ¬ G3dIO.InitializeFields[line]; types: REF Types ¬ NEW[Types[fields.length]]; IF nVerts = 0 THEN nVerts ¬ G3dIO.NumberOfLinesToConvert[in]; s.vertices ¬ NEW[VertexSequenceRep[nVerts]]; s.vertices.length ¬ nVerts; FOR n: INT IN [0..fields.length) DO f: G3dIO.Field ¬ fields[n]; SELECT TRUE FROM Eq[f.id, "index"] => types[n] ¬ map; Eq[f.id, "xyzCoords"] => types[n] ¬ xyz; Eq[f.id, "normalVec"] => {types[n] ¬ nrm; s.vertices.valid[normal] ¬ TRUE}; Eq[f.id, "rgbColor"] => {types[n] ¬ col; s.vertices.valid[color] ¬ TRUE}; Eq[f.id, "textureCoords"] => {types[n] ¬ tex; s.vertices.valid[texture] ¬ TRUE}; Eq[f.id, "transmittance"] => {types[n] ¬ tra; s.vertices.valid[transmit] ¬ TRUE}; ENDCASE => {types[n] ¬ prop; AllocateFields[f, nVerts]}; IF types[n] = map THEN map ¬ NEW[IntegerSequenceRep[nVerts]]; ENDLOOP; FOR n: INT IN [0..nVerts) DO -- line by line data conversions v: Vertex ¬ s.vertices[n] ¬ NEW[VertexRep]; CedarProcess.CheckAbort[]; line ¬ G3dIO.GetDataLine[in, line ! IO.EndOfStream => GOTO eof]; FOR f: INT IN [0..fields.length) DO SELECT types[f] FROM xyz => v.point ¬ GetTriple[line, n]; nrm => v.normal ¬ GetTriple[line, n]; col => v.color ¬ GetTriple[line, n]; tex => v.texture ¬ G3dIO.GetPair[line]; tra => v.transmittance ¬ G3dIO.GetReal[line]; map => map[n] ¬ G3dIO.GetInteger[line]; prop => ReadProp[line, fields[f], n]; ENDCASE => NULL; ENDLOOP; REPEAT eof => NULL; ENDLOOP; TestEndData[]; SetProps[fields, types]; }; Eq[key, "Surfaces"] => { -- read polygons <> tallied: BOOL ¬ FALSE; dum: G3dIO.Line ¬ line ¬ G3dIO.FindKeyWord[in, key,,, line]; fields: G3dIO.FieldSequence ¬ G3dIO.InitializeFields[line]; types: REF Types ¬ NEW[Types[fields.length]]; IF nPolys = 0 THEN nPolys ¬ G3dIO.NumberOfLinesToConvert[in]; s.surfaces ¬ NEW[SurfaceSequenceRep[nPolys]]; s.surfaces.length ¬ nPolys; FOR f: INT IN [0..fields.length) DO NewFaces: PROC ~ {IF s.faces=NIL THEN s.faces ¬NEW[FaceSequenceRep[nPolys]]}; types[f] ¬ SELECT TRUE FROM Eq[fields[f].id, "index"] => map, Eq[fields[f].id, "nSides"] => tally, Eq[fields[f].id, "vertices"] => vtx, Eq[fields[f].id, "normalVec"] => nrm, Eq[fields[f].id, "rgbColor"] => col, Eq[fields[f].id, "transmittance"] => tra, ENDCASE => prop; SELECT types[f] FROM tally => tallied ¬ TRUE; nrm => {NewFaces[]; s.faces.valid[normal] ¬ TRUE}; col => {NewFaces[]; s.faces.valid[color] ¬ TRUE}; tra => {NewFaces[]; s.faces.valid[transmit] ¬ TRUE}; prop => AllocateFields[fields[f], nPolys]; ENDCASE; ENDLOOP; FOR n: INT IN [0..nPolys) DO -- line by line data conversions nSides: INT ¬ 3; face: Face ¬ IF s.faces # NIL THEN (s.faces[n] ¬ NEW[FaceRep]) ELSE NIL; CedarProcess.CheckAbort[]; line ¬ G3dIO.GetDataLine[in, line ! IO.EndOfStream => GOTO eof]; FOR f: INT IN [0..fields.length) DO SELECT types[f] FROM map => [] ¬ G3dIO.GetWord[line]; -- skip the surface index tally => nSides ¬ G3dIO.GetInteger[line]; -- should speed things vtx => SELECT TRUE FROM triangulated, tallied => { seq: NatSequence ¬ s.surfaces[n].vertices ¬ NEW[NatSequenceRep[nSides]]; seq.length ¬ nSides; FOR i: INT IN [0..nSides) DO seq[i] ¬ Convert.IntFromRope[G3dIO.GetWord[line] ! Convert.Error => G3dIO.ErrorReport[convert, line]]; ENDLOOP; }; ENDCASE => s.surfaces[n].vertices ¬ G3dIO.GetNats[line]; nrm => face.normal ¬ GetTriple[line, n]; col => face.color ¬ GetTriple[line, n]; tra => face.transmittance ¬ G3dIO.GetReal[line]; prop => ReadProp[line, fields[f], n]; ENDCASE => NULL; ENDLOOP; REPEAT eof => NULL; ENDLOOP; TestEndData[]; SetProps[fields, types]; }; ENDCASE => { line ¬ G3dIO.GetLine[in, line]; PutProp[key, Rope.Substr[line.rope, Rope.Length[key]+1]]; }; ENDLOOP; G3dIO.ReleaseLine[line]; IF s.surfaces # NIL THEN { -- tests IF map # NIL THEN -- take care of indexed vertices: FOR n: INT IN [0..map.length) DO IF map[n] # n THEN { -- map is not a no-op FOR n: INT IN [0..s.surfaces.length) DO poly: NatSequence ¬ s.surfaces[n].vertices; FOR nn: INT IN [0..poly.length) DO poly[nn] ¬ map[poly[nn]]; ENDLOOP; ENDLOOP; EXIT; }; ENDLOOP; IF countFromOne THEN FOR n: INT IN [0..s.surfaces.length) DO poly: NatSequence ¬ s.surfaces[n].vertices; FOR nn: INT IN [0..poly.length) DO poly[nn] ¬ poly[nn]-1; ENDLOOP; ENDLOOP; s.triangulated ¬ TRUE; FOR n: INT IN [0..s.surfaces.length) DO -- check polygon validity: poly: NatSequence ¬ s.surfaces[n].vertices; IF poly = NIL THEN G3dIO.Error[IO.PutFR1["non existent polygon %g", IO.int[n]], NIL, 0]; IF poly.length # 3 AND s.triangulated THEN s.triangulated ¬ FALSE; FOR nn: INT IN [0..poly.length) DO IF poly[nn] >= nVerts THEN G3dIO.Error[ IO.PutFR1["polygon %g refers to non-existent vertex", IO.int[n]], NIL, 0]; ENDLOOP; ENDLOOP; s.matrix ¬ G3dMatrix.Identity[]; s.objectExtent ¬ BoundingBox[s]; }; }; ShapeToFile: PUBLIC PROC [ fileName: ROPE, shape: Shape, index: BOOL ¬ FALSE, tallyOnPolys: BOOL ¬ FALSE, comment: ROPE ¬ NIL] ~ { IF fileName # NIL AND shape # NIL THEN { out: IO.STREAM ¬ FS.StreamOpen[FileNames.ResolveRelativePath[fileName], $create]; IF comment # NIL THEN IO.PutF1[out, "Comment~ %g\n\n", IO.rope[comment]]; ShapeToStream[shape, out, index, tallyOnPolys]; }; }; ShapeToStream: PUBLIC PROC [ shape: Shape, out: STREAM, index: BOOL ¬ FALSE, tallyOnPolys: BOOL ¬ FALSE] ~ { IO.PutF1[out, "ShapeName~ %g\n", IO.rope[shape.name]]; IO.PutF1[out, "SurfaceType~ %g\n", IO.rope[Atom.GetPName[shape.type]]]; IO.PutF[out, "DataSize~ vertices: %g, polygons: %g\n\n", IO.int[shape.vertices.length], IO.int[shape.surfaces.length]]; IO.PutRope[out, "Vertices~"]; IF index THEN IO.PutRope[out, " index: integer"]; IO.PutRope[out, " xyzCoords: triple"]; IF shape.vertices.valid[normal] THEN IO.PutRope[out, " normalVec: triple"]; IF shape.vertices.valid[color] THEN IO.PutRope[out, " rgbColor: triple"]; IF shape.vertices.valid[texture] THEN IO.PutRope[out, " textureCoords: pair"]; IO.PutRope[out, "\n\n"]; FOR n: INT IN [0..shape.vertices.length) DO v: Vertex ¬ shape.vertices[n]; IF index THEN IO.PutF1[out, "%g\t", IO.int[n]]; G3dIO.WriteTriple[out, v.point]; IF shape.vertices.valid[normal] THEN G3dIO.WriteTriple[out, v.normal]; IF shape.vertices.valid[color] THEN G3dIO.WriteTriple[out, v.color]; <> <> <> IF shape.vertices.valid[texture] THEN G3dIO.WritePair[out, v.texture]; IO.PutRope[out, "\n"]; ENDLOOP; IO.PutRope[out, "\nSurfaces~ "]; IF index THEN IO.PutRope[out, " index: integer"]; IO.PutRope[out, " vertices: nats\n\n"]; FOR n: INT IN [0..shape.surfaces.length) DO polygon: NatSequence ~ shape.surfaces[n].vertices; IF index THEN IO.PutF1[out, "%g\t", IO.int[n]]; IF tallyOnPolys THEN IO.PutF1[out, "%g\t", IO.int[polygon.length]]; FOR nn: INT IN [0..polygon.length) DO IO.PutF1[out, "%5g ", IO.int[polygon[nn]]]; ENDLOOP; IO.PutRope[out, "\n"]; ENDLOOP; IO.Close[out]; }; ShapeToStreamPerFormat: PUBLIC PROC [shape: Shape, out: STREAM, format: ATOM] RETURNS [success: BOOL ¬ TRUE] ~ { WriteShapeAsQuad: PROC ~ { <> <> <
> <> <> <> <> <> WriteQuad: PROC [surface: Surface, start: CARD] ~ { WriteVertex: PROC [v: Vertex] ~ { G3dIO.WriteTriple[out, v.point]; IF shape.vertices.valid[normal] THEN G3dIO.WriteTriple[out, v.normal]; IF shape.vertices.valid[color] THEN { G3dIO.WriteTriple[out, v.color]; IO.PutRope[out, "1.0 "]; }; IO.PutRope[out, "\n"]; }; WriteVertex[shape.vertices[surface.vertices[0]]]; WriteVertex[shape.vertices[surface.vertices[start]]]; WriteVertex[shape.vertices[surface.vertices[MIN[start+1, surface.vertices.length-1]]]]; WriteVertex[shape.vertices[surface.vertices[MIN[start+2, surface.vertices.length-1]]]]; IO.PutRope[out, "\n"]; }; IF shape.vertices.valid[color] THEN IO.PutRope[out, "C"]; IF shape.vertices.valid[normal] THEN IO.PutRope[out, "N"]; IO.PutRope[out, "POLY\n"]; FOR s: INT IN [0..shape.surfaces.length) DO surface: Surface ¬ shape.surfaces[s]; lastWritten: INT ¬ 1; WHILE lastWritten < INT[surface.vertices.length] DO IF surface.vertices.length-lastWritten > 1 THEN WriteQuad[surface, lastWritten]; lastWritten ¬ lastWritten+2; ENDLOOP; ENDLOOP; }; WriteShapeAsDotNone: PROC ~ { <<.none format for University of Calgary's GraphicsJungle>> huskFileType: CHAR ~ 'x; huskOpenObject: CHAR ~ 'o; huskCloseObject: CHAR ~ 'O; huskPolygon: CHAR ~ 'p; huskNormalPoly: CHAR ~ 133+0C; huskColourfulPoly: CHAR ~ 129+0C; oneDRealRep: PACKED ARRAY [0..1] OF CARD32 ~ LOOPHOLE[DREAL[1.0]]; drealHi: [0..1] ~ IF oneDRealRep[1] = 0 THEN 0 ELSE 1; drealLo: [0..1] ~ 1-drealHi; WriteShort: PROC [i: INTEGER] ~ {IO.PutHWord[out, Basics.HFromInt16[i]]}; --16 bit int WriteDouble: PROC [dreal: DREAL] ~ { -- 64 bit double precision real <> a: PACKED ARRAY [0..1] OF CARD32 ¬ LOOPHOLE[dreal]; IO.PutFWord[out, Basics.FFromCard32[a[drealHi]]]; IO.PutFWord[out, Basics.FFromCard32[a[drealLo]]]; }; WritePoint: PROC [p: Triple] ~ { WriteDouble[p.x]; WriteDouble[p.y]; WriteDouble[p.z]; }; WriteChar: PROC [c: CHAR] ~ {IO.PutChar[out, c]}; WriteString: PROC [r: ROPE] ~ {IO.PutRope[out, r]; IO.PutChar[out, 0C]}; WriteOpcode: PROC [c: CHAR, n: INTEGER] ~ {WriteChar[c]; WriteShort[n]}; WriteOpcode[huskFileType, 0]; WriteOpcode[huskOpenObject, Rope.Length[shape.name]+1]; WriteString[shape.name]; FOR s: INT IN [0..shape.surfaces.length) DO polygon: G3dShape.NatSequence ¬ shape.surfaces[s].vertices; dsize: INT ¬ 3*polygon.length*8; -- 8 = sizeof(double) ssize: INT ¬ 3*polygon.length*2; -- 2 = sizeof(short) IF polygon.length < 3 THEN LOOP; WriteOpcode[huskColourfulPoly, ssize+2]; WriteShort[polygon.length]; FOR i: INT IN [0..polygon.length) DO -- color for each vertex color: Triple ¬ shape.vertices[polygon[i]].color; IF NOT shape.vertices.valid[color] THEN color ¬ [1.0, 1.0, 1.0]; WriteShort[Real.Round[65535.0*color.x]]; WriteShort[Real.Round[65535.0*color.y]]; WriteShort[Real.Round[65535.0*color.z]]; ENDLOOP; WriteOpcode[huskNormalPoly, dsize+2]; WriteShort[polygon.length]; FOR i: INT IN [0..polygon.length) DO -- normal for each vertex WritePoint[shape.vertices[polygon[i]].normal]; ENDLOOP; WriteOpcode[huskPolygon, dsize+2]; WriteShort[polygon.length]; FOR i: INT IN [0..polygon.length) DO -- position for each vertex WritePoint[shape.vertices[polygon[i]].point]; ENDLOOP; ENDLOOP; WriteOpcode[huskCloseObject, 0]; }; IF shape = NIL OR out = NIL OR shape.vertices = NIL OR shape.vertices.length = 0 OR shape.surfaces = NIL OR shape.surfaces.length = 0 THEN RETURN[FALSE] ELSE SELECT format FROM $Quad => WriteShapeAsQuad[]; $DotNone => WriteShapeAsDotNone[]; ENDCASE; }; <> ShapeFromData: PUBLIC PROC [ name: ROPE ¬ NIL, surfaces: SurfaceSequence, vertices: TripleSequence, normals: TripleSequence ¬ NIL, colors: TripleSequence ¬ NIL, transmittances: RealSequence ¬ NIL, textures: PairSequence ¬ NIL, showBackfaces: BOOL ¬ TRUE, faceted: BOOL ¬ FALSE, type: ATOM ¬ $ConvexPolygon] RETURNS [s: Shape] ~ { <> s ¬ NEW[ShapeRep]; s­ ¬ [name: name, surfaces: surfaces, showBackfaces: showBackfaces, type: type]; s.vertices ¬ NEW[VertexSequenceRep[vertices.length]]; s.vertices.length ¬ vertices.length; s.vertices.valid[normal] ¬ normals # NIL; s.vertices.valid[color] ¬ colors # NIL; s.vertices.valid[texture] ¬ textures # NIL; s.vertices.valid[transmit] ¬ transmittances # NIL; FOR n: CARDINAL IN [0..vertices.length) DO v: Vertex ¬ s.vertices[n] ¬ NEW[VertexRep ¬ [point: vertices[n]]]; IF normals # NIL AND normals.length > n THEN v.normal ¬ normals[n]; IF colors # NIL AND colors.length > n THEN v.color ¬ colors[n]; IF transmittances # NIL AND transmittances.length > n THEN v.transmittance ¬ transmittances[n]; IF textures # NIL AND textures.length > n THEN v.texture ¬ textures[n]; ENDLOOP; }; <> CopyShape: PUBLIC PROC [shape: Shape] RETURNS [s: Shape] ~ { s ¬ NEW[ShapeRep ¬ []]; -- default top-level fields s.clientData ¬ shape.clientData; s.sphereExtent ¬ shape.sphereExtent; -- computed when original read in s.objectExtent ¬ shape.objectExtent; s.showBackfaces ¬ shape.showBackfaces; s.triangulated ¬ shape.triangulated; s.matrix ¬ G3dMatrix.CopyMatrix[shape.matrix]; s.props ¬ NIL; -- make physical copy of proplist FOR list: PropList ¬ shape.props, list.rest UNTIL list = NIL DO element: Atom.DottedPair ¬ NEW[Atom.DottedPairNode ¬ list.first­]; s.props ¬ CONS[element, s.props]; ENDLOOP; IF shape.vertices # NIL THEN { s.vertices ¬ NEW[VertexSequenceRep[shape.vertices.length]]; -- copy vertices s.vertices.valid ¬ shape.vertices.valid; s.vertices.length ¬ shape.vertices.length; FOR i: INT IN [0..shape.vertices.length) DO s.vertices[i] ¬ NEW[VertexRep ¬ shape.vertices[i]­]; ENDLOOP; }; IF s.faces # NIL THEN { s.faces ¬ NEW[FaceSequenceRep[shape.faces.length]]; -- copy face data s.faces.valid ¬ shape.faces.valid; s.faces.length ¬ shape.faces.length; FOR i: INT IN [0..shape.faces.length) DO s.faces[i] ¬ NEW[FaceRep ¬ shape.faces[i]­]; ENDLOOP; }; s.surfaces ¬ G3dBasic.CopySurfaceSequence[shape.surfaces]; -- copy surface definition }; CombineShapes: PUBLIC PROC [shape1, shape2: Shape] RETURNS [s: Shape] ~ { q1: Quaternion ¬ shape1.orientation; q2: Quaternion ¬ shape2.orientation; q2a: Quaternion ¬ IF G3dQuaternion.Dot[q1, q2] < 0 THEN G3dQuaternion.Neg[q2] ELSE q2; q: Quaternion ¬ G3dQuaternion.Bisect[q1, q2a]; a: AxisAngle ¬ G3dQuaternion.ToAxisAngle[q]; midPos: Triple ¬ G3dVector.Midpoint[shape1.translation, shape2.translation]; midBase: Triple ¬ G3dVector.Midpoint[shape1.rotationBase, shape2.rotationBase]; s ¬ NEW[ShapeRep]; s.clientData ¬ IF shape1.clientData # NIL THEN shape1.clientData ELSE shape2.clientData; s.name ¬ Rope.Concat[shape1.name, shape2.name]; FOR l: PropList ¬ shape1.props, l.rest WHILE l#NIL DO s.props ¬ CONS[l.first, s.props]; ENDLOOP; FOR l: PropList ¬ shape2.props, l.rest WHILE l#NIL DO s.props ¬ CONS[l.first, s.props]; ENDLOOP; TransformShape[s, midPos, [midBase, a.unitAxis], a.theta, 0.5*(shape1.scale+shape2.scale)]; SELECT TRUE FROM shape1 = NIL OR shape1.vertices = NIL OR shape1.surfaces = NIL => RETURN[shape2]; shape2 = NIL OR shape2.vertices = NIL OR shape2.surfaces = NIL => RETURN[shape1]; ENDCASE => { CopyVertex: PROC [v: Vertex] RETURNS [vv: Vertex] ~ {vv ¬ NEW[VertexRep ¬ v­]}; s.vertices ¬ NEW[VertexSequenceRep[shape1.vertices.length+shape2.vertices.length]]; s.vertices.length ¬ s.vertices.maxLength; FOR i: INT IN [0..shape1.vertices.length) DO s.vertices[i] ¬ CopyVertex[shape1.vertices[i]]; ENDLOOP; FOR i: INT IN [0..shape2.vertices.length) DO s.vertices[i+shape1.vertices.length] ¬ CopyVertex[shape2.vertices[i]]; ENDLOOP; s.surfaces ¬ NEW[SurfaceSequenceRep[shape1.surfaces.length+shape2.surfaces.length]]; s.surfaces.length ¬ s.surfaces.maxLength; FOR i: INT IN [0..shape1.surfaces.length) DO s.surfaces[i] ¬ [NIL, G2dBasic.CopyNatSequence[shape1.surfaces[i].vertices]]; ENDLOOP; FOR i: INT IN [0..shape2.surfaces.length) DO n: NatSequence ¬ G2dBasic.CopyNatSequence[shape2.surfaces[i].vertices]; FOR j: INT IN [0..n.length) DO n[j] ¬ n[j]+shape1.vertices.length; ENDLOOP; s.surfaces[i+shape1.surfaces.length] ¬ [NIL, n]; ENDLOOP; }; }; <> ComputeMatrix: PUBLIC PROC [shape: Shape] ~ { a: AxisAngle ¬ G3dQuaternion.ToAxisAngle[shape.orientation]; TransformShape[shape, shape.translation, [shape.rotationBase, a.unitAxis], a.theta, shape.scale]; }; <<>> SetMatrix: PUBLIC PROC [shape: Shape, matrix: Matrix] ~ { shape.matrix ¬ matrix; }; <<>> MatrixFromPars: PROC [translate: Triple, axis: Ray, rotation, scale: REAL, out: Matrix] RETURNS [m: Matrix] ~ { m ¬ G3dMatrix.Scale[out, scale, m]; IF rotation # 0.0 THEN m ¬ G3dMatrix.Rotate[m, axis.axis, rotation,, axis.base, m]; m ¬ G3dMatrix.Translate[m, translate, m]; }; TransformShape: PUBLIC PROC [ shape: Shape, translate: Triple ¬ [], axis: Ray ¬ G3dShape.zAxis, rotation: REAL ¬ 0.0, scale: REAL ¬ 1.0, concat: BOOL ¬ FALSE] ~ { q: Quaternion ¬ shape.orientation; IF rotation # 0.0 AND axis.axis # [] THEN q ¬ G3dQuaternion.FromAxisAngle[axis.axis, rotation]; IF NOT concat THEN { shape.rotationBase ¬ axis.base; shape.orientation ¬ q; shape.translation ¬ translate; shape.scale ¬ scale; }; IF NOT concat THEN shape.matrix ¬ G3dMatrix.Identity[shape.matrix]; shape.matrix ¬ MatrixFromPars[translate, axis, rotation, scale, shape.matrix]; shape.renderValid ¬ FALSE; }; <<>> TransformVertices: PUBLIC PROC [shape: Shape, matrix: Matrix] ~ { IF shape # NIL AND shape.vertices # NIL THEN { nMatrix: Matrix ¬ G3dMatrix.MakeVectorTransform[matrix, G3dMatrix.ObtainMatrix[]]; FOR n: INT IN [0..shape.vertices.length) DO v: Vertex ¬ shape.vertices[n]; v.point ¬ G3dMatrix.Transform[v.point, matrix]; v.normal ¬ G3dMatrix.Transform[v.normal, nMatrix]; ENDLOOP; G3dMatrix.ReleaseMatrix[nMatrix]; shape.renderValid ¬ FALSE; }; }; ApplyTransformsToShape: PUBLIC PROC [shape: Shape] ~ { IF shape = NIL THEN RETURN; TransformVertices[shape, shape.matrix]; shape.matrix ¬ G3dMatrix.Identity[]; shape.objectExtent ¬ BoundingBox[shape]; shape.sphereExtent ¬ BoundingSphere[shape]; shape.renderValid ¬ FALSE; IF shape.faces # NIL THEN { shape.faces.valid[normal] ¬ FALSE; shape.faces.valid[center] ¬ FALSE; }; IF shape.vertices # NIL THEN shape.vertices.valid[normal] ¬ TRUE; }; <> DoWithVertices: PUBLIC PROC [shape: Shape, vertexProc: G3dShape.VertexProc] ~ { IF shape # NIL AND shape.vertices # NIL AND vertexProc # NIL THEN FOR n: INT IN [0..shape.vertices.length) DO IF NOT vertexProc[shape.vertices[n], n] THEN EXIT; ENDLOOP; }; VertexValid: PUBLIC PROC [shape: Shape, test: Validity] RETURNS [b: BOOL ¬ FALSE] ~ { IF shape # NIL AND shape.vertices # NIL THEN RETURN[shape.vertices.valid[test]]; }; SetVertexNormals: PUBLIC PROC [shape: Shape] ~ { IF shape.vertices = NIL THEN RETURN; shape.vertices.valid[normal] ¬ TRUE; IF NOT FaceValid[shape, normal] THEN SetFaceNormals[shape, FALSE]; FOR i: INT IN [0..shape.vertices.length) DO shape.vertices[i].normal ¬ [0.0, 0.0, 0.0]; ENDLOOP; FOR i: INT IN [0..shape.surfaces.length) DO poly: NatSequence ¬ shape.surfaces[i].vertices; faceNormal: Triple ¬ shape.faces[i].normal; vPrev: Vertex ¬ shape.vertices[poly[poly.length-1]]; v: Vertex ¬ shape.vertices[poly[0]]; vec1: Triple ¬ G3dVector.Unit[G3dVector.Sub[vPrev.point, v.point]]; FOR j: INT IN [0..poly.length) DO vNext: Vertex ¬ shape.vertices[poly[(j+1) MOD poly.length]]; <> <<1-cos(>> vec2: Triple ¬ G3dVector.Unit[G3dVector.Sub[vNext.point, v.point]]; weight: REAL ¬ 1.0-G3dVector.Dot[vec1, vec2]; v.normal ¬ G3dVector.Mul[G3dVector.Add[v.normal, faceNormal], weight]; vPrev ¬ v; v ¬ vNext; vec1 ¬ G3dVector.Negate[vec2]; ENDLOOP; ENDLOOP; FOR i: INT IN [0..shape.vertices.length) DO v: Vertex ¬ shape.vertices[i]; v.normal ¬ G3dVector.Unit[v.normal]; ENDLOOP; }; SetFwdFacingVertices: PUBLIC PROC [shape: Shape, view: Matrix, screens: ScreenSequence] ~ { Action: SurfaceProc ~ { FOR i: INT IN [0..surface.length) DO screens[surface[i]].fwdFacing ¬ TRUE; ENDLOOP; }; IF shape = NIL OR shape.vertices = NIL OR screens = NIL THEN RETURN; FOR n: INT IN [0..shape.vertices.length) DO screens[n].fwdFacing ¬ FALSE; ENDLOOP; DoWithFacingPolygons[shape, view, Action]; }; UnitizeVertexNormals: PUBLIC PROC [shape: Shape] ~ { IF shape # NIL AND shape.vertices # NIL THEN FOR i: INT IN [0..shape.vertices.length) DO shape.vertices[i].normal ¬ G3dVector.Unit[shape.vertices[i].normal]; ENDLOOP; }; NegateVertexNormals: PUBLIC PROC [shape: Shape] ~ { IF shape = NIL OR shape.vertices = NIL THEN RETURN; FOR n: INT IN [0..shape.vertices.length) DO shape.vertices[n].normal ¬ G3dVector.Negate[shape.vertices[n].normal]; ENDLOOP; shape.renderValid ¬ FALSE; shape.normalsNegated ¬ NOT shape.normalsNegated; }; NumberOfVertices: PUBLIC PROC [shapes: ShapeSequence] RETURNS [num: INT ¬ 0] ~ { IF shapes # NIL THEN FOR n: INT IN [0..shapes.length) DO s: Shape ¬ shapes[n]; IF s # NIL AND s.vertices # NIL THEN num ¬ num+s.vertices.length; ENDLOOP; }; SetExtent: PUBLIC PROC [screens: ScreenSequence] ~ { b: G2dBasic.Box ¬ [[huge, huge], [-huge, -huge]]; FOR i: INT IN [0..screens.length) DO p: Pair ¬ screens[i].pos; b ¬ [[MIN[b.min.x, p.x], MIN[b.min.y, p.y]], [MAX[b.max.x, p.x], MAX[b.max.y, p.y]]]; ENDLOOP; screens.extent ¬ b; screens.extentValid ¬ TRUE; }; InterpolateVertex: PUBLIC PROC [t: REAL, v0, v1: Vertex] RETURNS [v: Vertex] ~ { InterpPair: PROC [t: REAL, p0, p1: Pair] RETURNS [Pair] ~ { RETURN[[p0.x+t*(p1.x-p0.x), p0.y+t*(p1.y-p0.y)]]; }; v ¬ NEW[VertexRep]; v.point ¬ G3dVector.Interp[t, v0.point, v1.point]; IF v0.normal # [] OR v1.normal # [] THEN v.normal ¬ G3dVector.Unit[G3dVector.Interp[t, v0.normal, v1.normal]]; IF v0.color # [] OR v1.color # [] THEN v.color ¬ G3dVector.Interp[t, v0.color, v1.color]; IF v0.texture#[0,0] OR v1.texture#[0,0] THEN v.texture ¬ InterpPair[t, v0.texture, v1.texture]; }; <> DoWithAllPolygons: PUBLIC PROC [shape: Shape, action: SurfaceProc] ~ { IF shape # NIL AND action # NIL THEN FOR n: INT IN [0..shape.faces.length) DO Process.CheckForAbort[]; IF NOT action[shape.surfaces[n].vertices, n] THEN RETURN; ENDLOOP; }; DoWithFacingPolygons: PUBLIC PROC [ shape: Shape, view: Matrix, frontFacingAction, backFacingAction: SurfaceProc ¬ NIL] ~ { IF shape # NIL AND view # NIL AND (frontFacingAction # NIL OR backFacingAction # NIL) THEN { persp: BOOL ¬ G3dMatrix.HasPerspective[view]; inverse: Matrix ¬ IF persp THEN G3dMatrix.Invert[view, G3dMatrix.ObtainMatrix[] ! G3dMatrix.singular => GOTO BadMatrix] ELSE NIL; q: G3dBasic.Quad ¬ [inverse[3][0], inverse[3][1], inverse[3][2], inverse[3][3]]; camera: Triple ¬ [q.x/q.w, q.y/q.w, q.z/q.w]; IF NOT FaceValid[shape, normal] THEN SetFaceNormals[shape]; IF NOT FaceValid[shape, center] THEN SetFaceCenters[shape]; FOR i: INT IN [0..shape.faces.length) DO visible: BOOL; poly: NatSequence ¬ shape.surfaces[i].vertices; Process.CheckForAbort[]; IF poly.length < 3 THEN visible ¬ TRUE ELSE { f: Face ¬ shape.faces[i]; visible ¬ IF persp <> THEN G3dVector.Dot[G3dVector.Sub[camera, f.center], f.normal] > 0.0 ELSE G3dVector.FrontFacingNoPerspective[f.normal, view]; }; IF visible AND frontFacingAction # NIL THEN {IF NOT frontFacingAction[poly, i] THEN RETURN}; IF NOT visible AND backFacingAction # NIL THEN {IF NOT backFacingAction[poly, i] THEN RETURN}; ENDLOOP; IF persp THEN G3dMatrix.ReleaseMatrix[inverse]; }; EXITS BadMatrix => NULL; }; FaceValid: PUBLIC PROC [shape: Shape, test: Validity] RETURNS [b: BOOL ¬ FALSE] ~ { IF shape # NIL AND shape.faces # NIL THEN RETURN[shape.faces.valid[test]]; }; SetFwdFacingFaces: PUBLIC PROC [shape: Shape, view: Matrix] ~ { BackFaces: SurfaceProc ~ {shape.faces[index].fwdFacing ¬ FALSE}; FrontFaces: SurfaceProc ~ {shape.faces[index].fwdFacing ¬ TRUE}; DoWithFacingPolygons[shape, view, FrontFaces, BackFaces]; }; GetPolygonPoints: PUBLIC PROC [shape: Shape, nPoly: INT, points: TripleSequence ¬ NIL] RETURNS [TripleSequence] ~ { poly: NatSequence ¬ shape.surfaces[nPoly].vertices; IF poly = NIL OR shape.vertices = NIL THEN RETURN[NIL]; IF points = NIL OR points.maxLength < poly.length THEN points ¬ NEW[TripleSequenceRep[poly.length]]; points.length ¬ poly.length; FOR n: INT IN [0..poly.length) DO points[n] ¬ shape.vertices[poly[n]].point; ENDLOOP; RETURN[points]; }; NewFaces: PROC [shape: Shape] ~ { shape.faces ¬ NEW[FaceSequenceRep[shape.surfaces.length]]; FOR n: INT IN [0..shape.faces.length ¬ shape.surfaces.length) DO poly: NatSequence ¬ shape.surfaces[n].vertices; color: Triple ¬ [0.0, 0.0, 0.0]; FOR i: INT IN [0..poly.length) DO color ¬ G3dVector.Add[color, shape.vertices[poly[i]].color]; ENDLOOP; shape.faces[n] ¬ NEW[FaceRep ¬ [color: G3dVector.Div[color, REAL[poly.length]]]]; ENDLOOP; }; SetFaceNormals: PUBLIC PROC [shape: Shape, complyWithVertices: BOOL ¬ TRUE] ~ { IF NOT FaceValid[shape, normal] THEN { points: TripleSequence ¬ NEW[TripleSequenceRep[100]]; IF shape.faces = NIL THEN NewFaces[shape]; shape.faces.valid[normal] ¬ TRUE; FOR n: INT IN [0..shape.surfaces.length) DO points ¬ GetPolygonPoints[shape, n, points]; shape.faces[n].normal ¬ G3dPolygon.PolygonNormal[points,, TRUE]; ENDLOOP; IF complyWithVertices AND shape.vertices # NIL <> THEN FOR n: INT IN [0..shape.surfaces.length) DO poly: NatSequence ¬ shape.surfaces[n].vertices; normal: Triple ¬ shape.faces[n].normal; IF G3dVector.Dot[normal, shape.vertices[poly[0]].normal] < 0.0 THEN shape.faces[n].normal ¬ G3dVector.Negate[normal]; ENDLOOP; }; }; SetFaceCenters: PUBLIC PROC [shape: Shape] ~ { IF NOT FaceValid[shape, center] THEN { points: TripleSequence ¬ NEW[TripleSequenceRep[100]]; IF shape.faces = NIL OR shape.faces.length < shape.surfaces.length THEN NewFaces[shape]; shape.faces.valid[center] ¬ TRUE; FOR n: INT IN [0..shape.surfaces.length) DO points ¬ GetPolygonPoints[shape, n, points]; shape.faces[n].center ¬ G3dPolygon.PolygonCenter[points]; ENDLOOP; }; }; UnitizeFaceNormals: PUBLIC PROC [shape: Shape] ~ { IF shape # NIL AND shape.faces # NIL THEN FOR i: INT IN [0..shape.faces.length) DO shape.faces[i].normal ¬ G3dVector.Unit[shape.faces[i].normal]; ENDLOOP; }; ReversePolygons: PUBLIC PROC [shape: Shape] ~ { IF shape = NIL OR shape.surfaces = NIL THEN RETURN; FOR n: INT IN [0..shape.surfaces.length) DO shape.surfaces[n].vertices ¬ G3dPolygon.PolygonReverse[shape.surfaces[n].vertices]; ENDLOOP; <> shape.surfacesReversed ¬ NOT shape.surfacesReversed; shape.renderValid ¬ FALSE; }; NegateFaceNormals: PUBLIC PROC [shape: Shape] ~ { IF shape = NIL OR shape.vertices = NIL OR NOT shape.vertices.valid[normal] THEN RETURN; FOR n: INT IN [0..shape.faces.length) DO face: Face ¬ shape.faces[n]; face.normal ¬ G3dVector.Negate[face.normal]; ENDLOOP; shape.renderValid ¬ FALSE; }; Triangulate: PUBLIC PROC [shape: Shape] ~ { old: SurfaceSequence ¬ shape.surfaces; IF shape = NIL OR shape.surfaces = NIL THEN RETURN; shape.surfaces ¬ NEW[SurfaceSequenceRep[old.length]]; FOR i: INT IN [0..old.length) DO poly: NatSequence ¬ old[i].vertices; n0: INT ¬ poly[0]; n1: INT ¬ poly[1]; FOR ii: INT IN [2..poly.length) DO n2: INT ¬ poly[ii]; nats: NatSequence ¬ NEW[NatSequenceRep[3]]; nats[0] ¬ n0; nats[1] ¬ n1; nats[2] ¬ n2; nats.length ¬ 3; shape.surfaces ¬ G3dBasic.AddToSurfaceSequence[shape.surfaces, [NIL, nats]]; n1 ¬ n2; ENDLOOP; ENDLOOP; shape.renderValid ¬ FALSE; shape.triangulated ¬ TRUE; }; NumberOfPolygons: PUBLIC PROC [shapes: ShapeSequence] RETURNS [num: INT ¬ 0] ~ { IF shapes # NIL THEN FOR n: INT IN [0..shapes.length) DO s: Shape ¬ shapes[n]; IF s # NIL AND s.surfaces # NIL THEN num ¬ num+s.surfaces.length; ENDLOOP; }; <> MakeEdges: PUBLIC PROC [shape: Shape] RETURNS [edges: EdgeSequence] ~ { EdgeInfo: TYPE ~ RECORD [maxV, poly0, poly1: INT ¬ -1]; ListSequence: TYPE ~ RECORD [ length: INT ¬ 0, element: SEQUENCE maxLength: INT OF LIST OF EdgeInfo ]; MaxNEdges: PROC RETURNS [max: INT ¬ 0] ~ { FOR n: INT IN [0..shape.surfaces.length) DO max ¬ max+shape.surfaces[n].vertices.length; ENDLOOP; }; IF shape # NIL AND shape.surfaces # NIL THEN { maxNEdges: INT ¬ MaxNEdges[]; lists: REF ListSequence ¬ NEW[ListSequence[maxNEdges]]; edges ¬ NEW[G3dShape.EdgeSequenceRep[maxNEdges]]; FOR poly: INT IN [0..shape.surfaces.length) DO polygon: NatSequence ~ shape.surfaces[poly].vertices; n0: INT ¬ polygon[polygon.length-1]; FOR i: INT IN [0..polygon.length) DO minV, maxV: INT; n1: INT ¬ polygon[i]; IF n0 < n1 THEN {minV ¬ n0; maxV ¬ n1} ELSE {minV ¬ n1; maxV¬ n0}; <> <> IF minV+1 > lists.length THEN { IF lists.maxLength <= minV+1 THEN { new: REF ListSequence ¬ NEW[ListSequence[2*(minV+1)]]; -- fudge it high FOR n: INT IN [0..lists.length) DO new[n] ¬ lists[n]; ENDLOOP; lists ¬ new; }; lists.length ¬ minV+1; }; FOR l: LIST OF EdgeInfo ¬ lists[minV], l.rest WHILE l # NIL DO <> IF l.first.maxV = maxV THEN { l.first.poly1 ¬ poly; EXIT; }; REPEAT <> FINISHED => { lists[minV] ¬ CONS[[maxV: maxV, poly0: poly], lists[minV]]; }; ENDLOOP; n0 ¬ n1; ENDLOOP; ENDLOOP; FOR minV: INT IN [0..lists.length) DO FOR l: LIST OF EdgeInfo ¬ lists[minV], l.rest WHILE l # NIL DO e: Edge ¬ edges[edges.length] ¬ NEW[EdgeRep]; e­ ¬ [minV, l.first.maxV, l.first.poly0, l.first.poly1]; edges.length ¬ edges.length+1; ENDLOOP; ENDLOOP; }; }; FindEdge: PUBLIC PROC [edges: EdgeSequence, v0, v1: INT] RETURNS [index: INT ¬ 0] ~ { IF edges = NIL OR edges.length = 0 THEN RETURN[-1]; IF v0 > v1 THEN {temp: INT ¬ v0; v0 ¬ v1; v1 ¬ temp}; WHILE edges[index].v0 < v0 DO index ¬ index+1; IF index = INT[edges.length] THEN RETURN[-1]; ENDLOOP; DO IF index = INT[edges.length] OR v0 # edges[index].v0 THEN RETURN[-1]; IF v1 = edges[index].v1 THEN RETURN[index]; index ¬ index+1; ENDLOOP; }; <> ObjectScale: PUBLIC PROC [shape: Shape] RETURNS [REAL ¬ 1.0] ~ { IF shape # NIL THEN { i: G3dBasic.Box ¬ shape.objectExtent; b:REAL¬MAX[ABS[i.min.x],ABS[i.min.y],ABS[i.min.z],ABS[i.max.x],ABS[i.max.y],ABS[i.max.z]]; RETURN[IF b > 0.0001 THEN 1.0/b ELSE 1.0]; }; }; PointsFromShape: PUBLIC PROC [shape: Shape, points: TripleSequence ¬ NIL] RETURNS [TripleSequence] ~ { IF points = NIL OR points.maxLength < shape.vertices.length THEN points ¬ NEW[TripleSequenceRep[shape.vertices.length]]; FOR n: INT IN [0..shape.vertices.length) DO points[n] ¬ shape.vertices[n].point; ENDLOOP; points.length ¬ shape.vertices.length; RETURN[points]; }; FindShape: PUBLIC PROC [shapes: ShapeSequence, shapeName: ROPE] RETURNS [s: Shape] ~ { IF shapes # NIL THEN FOR n: INT IN [0..shapes.length) DO IF Rope.Equal[shapes[n].name, shapeName, FALSE] THEN RETURN[shapes[n]]; ENDLOOP; }; SetTextureCoords: PUBLIC PROC [shape: Shape, textures: PairSequence] ~ { IF shape # NIL AND textures # NIL THEN FOR n: INT IN [0..MIN[shape.vertices.length, textures.length]) DO shape.vertices[n].texture ¬ textures[n]; ENDLOOP; }; <> BoundingBox: PUBLIC PROC [shape: Shape] RETURNS [mm: Box3d] ~ { mm ¬ [[huge, huge, huge], [-huge, -huge, -huge]]; FOR i: INT IN [0..shape.vertices.length) DO p: Triple ~ shape.vertices[i].point; IF p.x < mm.min.x THEN mm.min.x ¬ p.x; IF p.x > mm.max.x THEN mm.max.x ¬ p.x; IF p.y < mm.min.y THEN mm.min.y ¬ p.y; IF p.y > mm.max.y THEN mm.max.y ¬ p.y; IF p.z < mm.min.z THEN mm.min.z ¬ p.z; IF p.z > mm.max.z THEN mm.max.z ¬ p.z; ENDLOOP; }; BoundingSphere: PUBLIC PROC [shape: Shape] RETURNS [s: G3dShape.Sphere] ~ { box3d: Box3d ¬ BoundingBox[shape]; s.center ¬ G3dVector.Midpoint[box3d.min, box3d.max]; s.radius ¬ 0.0; FOR n: INT IN [0..shape.vertices.length) DO -- find radius r: REAL ¬ G3dVector.SquareDistance[shape.vertices[n].point, s.center]; IF r > s.radius THEN s.radius ¬ r; ENDLOOP; s.radius ¬ RealFns.SqRt[s.radius]; }; <> CopyShapeSequence: PUBLIC PROC [shapes: ShapeSequence] RETURNS [s: ShapeSequence] ~ { IF shapes = NIL THEN RETURN; s ¬ NEW[ShapeSequenceRep[shapes.length]]; s.length ¬ shapes.length; FOR n: INT IN [0..shapes.length) DO s[n] ¬ shapes[n]; ENDLOOP; }; AddToShapeSequence: PUBLIC PROC [shapes: ShapeSequence, shape: Shape] RETURNS [ShapeSequence] ~ { IF shapes = NIL THEN shapes ¬ NEW[ShapeSequenceRep[1]]; IF shapes.length = shapes.maxLength THEN shapes ¬ LengthenShapeSequence[shapes]; shapes[shapes.length] ¬ shape; shapes.length ¬ shapes.length+1; RETURN[shapes]; }; <<>> LengthenShapeSequence: PUBLIC PROC [shapes: ShapeSequence, amount: REAL ¬ 1.3] RETURNS [new: ShapeSequence] ~ { newLength: INT ¬ MAX[Real.Ceiling[amount*shapes.maxLength], 3]; new ¬ NEW[ShapeSequenceRep[newLength]]; FOR i: INT IN [0..shapes.length) DO new[i] ¬ shapes[i]; ENDLOOP; new.length ¬ shapes.length; }; CopyVertexSequence: PUBLIC PROC [vertices: VertexSequence] RETURNS [i: VertexSequence] ~ { IF vertices = NIL THEN RETURN; i ¬ NEW[VertexSequenceRep[vertices.length]]; i.length ¬ vertices.length; FOR n: INT IN [0..vertices.length) DO i[n] ¬ vertices[n]; ENDLOOP; }; AddToVertexSequence: PUBLIC PROC [vertices: VertexSequence, vertex: Vertex] RETURNS [new: VertexSequence] ~ { IF (new ¬ vertices) = NIL THEN new ¬ NEW[VertexSequenceRep[1]]; IF new.length = new.maxLength THEN new ¬ LengthenVertexSequence[new]; new[new.length] ¬ vertex; new.length ¬ new.length+1; }; <<>> LengthenVertexSequence: PUBLIC PROC [vertices: VertexSequence, amount: REAL ¬ 1.3] RETURNS [new: VertexSequence] ~ { newLength: INT ¬ 5+Real.Round[amount*vertices.length]; new ¬ NEW[VertexSequenceRep[newLength]]; new.valid ¬ vertices.valid; FOR i: INT IN [0..vertices.length) DO new[i] ¬ vertices[i]; ENDLOOP; new.length ¬ vertices.length; }; CopyFaceSequence: PUBLIC PROC [faces: FaceSequence] RETURNS [i: FaceSequence] ~ { IF faces = NIL THEN RETURN; i ¬ NEW[FaceSequenceRep[faces.length]]; i.length ¬ faces.length; FOR n: INT IN [0..faces.length) DO i[n] ¬ faces[n]; ENDLOOP; }; AddToFaceSequence: PUBLIC PROC [faces: FaceSequence, face: Face] RETURNS [new: FaceSequence] ~ { IF (new ¬ faces) = NIL THEN new ¬ NEW[FaceSequenceRep[1]]; IF new.length = new.maxLength THEN new ¬ LengthenFaceSequence[new]; new[new.length] ¬ face; new.length ¬ new.length+1; }; <<>> LengthenFaceSequence: PUBLIC PROC [faces: FaceSequence, amount: REAL ¬ 1.3] RETURNS [new: FaceSequence] ~ { newLength: INT ¬ MAX[Real.Round[amount*faces.length], 3]; new ¬ NEW[FaceSequenceRep[newLength]]; new.valid ¬ faces.valid; FOR i: INT IN [0..faces.length) DO new[i] ¬ faces[i]; ENDLOOP; new.length ¬ faces.length; }; END.