G3dShapeImpl.mesa
Copyright Ó 1985, 1992 by Xerox Corporation. All rights reserved.
Bloomenthal, April 17, 1993 1:20 pm PDT
Crow, June 9, 1989 10:23:35 am PDT
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
Types and Constants
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;
huge: REAL ~ Real.LargestNumber;
Error: PUBLIC SIGNAL [code: ATOM, reason: ROPE] = CODE;
File IO
ShapeFromFile:
PUBLIC
PROC [fileName:
ROPE]
RETURNS [s: Shape] ~ {
in:
STREAM ¬
PFS.StreamOpen[
PFS.PathFromRope[FileNames.ResolveRelativePath[fileName]]];
! FS.Error => 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];
};
IF n < maxNTriples THEN t ← G3dIO.GetTriple[line] ELSE Skip[line];
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.GetLine[in, line];
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.GetLine[in, line];
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: Line ← G3dIO.GetLine[in, line];
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;
ENDLOOP;
TestEndData[];
SetProps[fields, types];
};
Eq[key, "Surfaces"] => {
-- read polygons
line: Line ← G3dIO.GetLine[in, line];
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;
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 vertices.valid[color] THEN
IO.PutF[out, "%5.4f %5.4f %5.4f",
IO.real[v.color.x], IO.real[v.color.y], IO.real[v.color.z]];
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 ~ {
Quad file format for Pat Hanrahan's MinneView program on the Iris:
File = Header Quadrilateral*
Header = [C][N]POLY (Inclusion of C indicates color information, N indicates normals)
Quadrilateral = Vertex Vertex Vertex Vertex
Vertex = Point [Color] [Normal]
Point = x y z (real numbers)
Color = r g b a (real numbers, 0<=r,g,b,a<=1, a=opacity, a=1 means opaque)
Normal = x y z (real numbers [not necessarily unitized])
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
see /PCedar/SunRPCRuntime/SunRPCImpl.mesa
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;
};
Creation
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]
~ {
If faceted, colors are applied to surfaces (facets) rather than vertices (not implemented).
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;
};
Copying/Combining
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;
};
};
Transformations
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;
};
Vertex Procedures
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]];
computing the angle presumes the angle is always less than 180 degrees
1-cos(a) = 0 for 0°, 1 for 90°, and 2 for 180°
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];
};
Polygon Procedures
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.FrontFacingWithPerspective[f.normal, f.center, inverse]
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
really, this should test all vertices and if two disagree, raise an error
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;
NegateFaceNormals[shape]; -- don't want this (defeats the point of reversing polygons, no?)
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;
};
Sequences
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;
};