G3dSweepImpl.mesa
Copyright Ó 1985, 1992 by Xerox Corporation. All rights reserved.
Glassner, September 21, 1989 12:28:23 pm PDT
Bloomenthal, July 15, 1992 6:09 pm PDT
DIRECTORY Args, FileNames, FS, G2dBasic, G2dVector, G3dBasic, G3dMatrix, G3dQuaternion, G3dShape, G3dSpline, G3dSweep, G3dTube, G3dVector, IO, Real, Rope;
G3dSweepImpl: CEDAR PROGRAM
IMPORTS Args, FileNames, FS, G2dBasic, G2dVector, G3dBasic, G3dMatrix, G3dQuaternion, G3dShape, G3dSpline, G3dTube, G3dVector, IO, Real, Rope
EXPORTS G3dSweep
~ BEGIN
Imported Types
Arg:      TYPE ~ Args.Arg; 
AxisAngle:    TYPE ~ G3dQuaternion.AxisAngle;
XSection:     TYPE ~ G3dTube.XSection;
Matrix:     TYPE ~ G3dMatrix.Matrix;
NatSequence:    TYPE ~ G3dBasic.NatSequence;
NatSequenceRep:   TYPE ~ G3dBasic.NatSequenceRep;
Quaternion:    TYPE ~ G3dQuaternion.Quaternion;
ROPE:      TYPE ~ Rope.ROPE;
Shape:     TYPE ~ G3dShape.Shape;
ShapeRep:    TYPE ~ G3dShape.ShapeRep;
STREAM:     TYPE ~ IO.STREAM;
Triple:     TYPE ~ G3dBasic.Triple;
TripleSequence:   TYPE ~ G3dBasic.TripleSequence;
Tube:      TYPE ~ G3dTube.Tube;
Vertex:     TYPE ~ G3dShape.Vertex;
VertexRep:    TYPE ~ G3dShape.VertexRep;
VertexSequence:   TYPE ~ G3dShape.VertexSequence;
Error:     PUBLIC SIGNAL [code: ATOM, reason: ROPE] = CODE;
Local Types
BlendType:    TYPE ~ G3dSweep.BlendType;
FileType:     TYPE ~ G3dSweep.FileType;
KeyData:     TYPE ~ G3dSweep.KeyData;
KeyDataRep:    TYPE ~ G3dSweep.KeyDataRep;
KeyList:     TYPE ~ G3dSweep.KeyList;
KeyListRep:    TYPE ~ G3dSweep.KeyListRep;
PathProc:     TYPE ~ G3dSweep.PathProc;
RotateProc:    TYPE ~ G3dSweep.RotateProc;
ScaleProc:     TYPE ~ G3dSweep.ScaleProc;
SurfaceDescription:  TYPE ~ G3dSweep.SurfaceDescription;
SurfaceDescriptionRep: TYPE ~ G3dSweep.SurfaceDescriptionRep;
SubdivisionType:  TYPE ~ G3dSweep.SubdivisionType;
ShapeBlendProc:   TYPE ~ G3dSweep.ShapeBlendProc;
TranslateProc:   TYPE ~ G3dSweep.TranslateProc;
XformProc:    TYPE ~ G3dSweep.XformProc;
InterpolateData:    TYPE ~ RECORD [
segment:      INT,    -- index # of near segment
near, far:      KeyData,   -- near and far key entries
alpha:       REAL    -- interpolation from near=0.0 to far=1.0
];
Constants
pi: REAL = 3.141593;
piOver2: REAL = 1.570796;
piTimes2: REAL = 6.283186;
degreesToRadians: REAL = .01745329;
radiansToDegrees: REAL = 57.29577;
Support for KeyLists From Files and Streams
KeyListFromFile: PUBLIC PROC [filename: ROPE, type: FileType, b: BlendType] RETURNS [KeyList] ~ {
stream: STREAM ¬ FS.StreamOpen[FileNames.ResolveRelativePath[filename]];
RETURN [KeyListFromStream[stream, type, b]];
};
KeyListFromStream: PUBLIC PROC [stream: STREAM, type: FileType, b: BlendType] RETURNS [KeyList] ~ {
GetNewlineRope: PROC [input: STREAM] RETURNS [r: ROPE] ~ {
DO
c: CHAR ¬ IO.PeekChar[input];
IF c = '\n OR c = IO.CR OR c= IO.LF THEN [] ¬ IO.GetChar[input] ELSE EXIT;
ENDLOOP;
r ¬ IO.GetLineRope[input];
};
TriplesFromKeys: PROC [key: KeyData, cyclic: BOOL ¬ FALSE] RETURNS [ts: TripleSequence] ~ {
firstTriple: Triple ¬ key.triple;
ts ¬ NIL;
WHILE key # NIL DO
ts ¬ G3dBasic.AddToTripleSequence[ts, key.triple];
key ¬ key.next;
ENDLOOP;
IF cyclic THEN ts ¬ G3dBasic.AddToTripleSequence[ts, firstTriple];
};
keyHead: KeyData ¬ NIL;
keyList: KeyList ¬ NEW[KeyListRep];
keyList.type ¬ b;
DO
ENABLE {
IO.EndOfStream => EXIT;
IO.Error => CONTINUE;  -- this shouldn't happen
};
d: KeyData ¬ NEW[KeyDataRep ¬ []];
r: ROPE ¬ GetNewlineRope[stream];
aA, rA, xA, yA, zA, vA, radiansA: Arg;
SELECT type FROM
shape => {
[aA, rA] ¬ Args.ArgsGetFromRope[r, "%rs"];
d.alpha ¬ aA.real;
d.rope ¬ rA.rope;
};
path => {
[aA, xA, yA, zA] ¬ Args.ArgsGetFromRope[r, "%rrrr"];
d.alpha ¬ aA.real;
d.triple ¬ [xA.real, yA.real, zA.real];
};
scale => {
[aA, xA, yA, zA] ¬ Args.ArgsGetFromRope[r, "%rrrr"];
d.alpha ¬ aA.real;
d.triple ¬ [xA.real, yA.real, zA.real];
};
rotate => {
[aA, xA, yA, zA, vA, radiansA] ¬ Args.ArgsGetFromRope[r, "%rrrrr-radians%b"];
d.alpha ¬ aA.real;
d.quaternion ¬ G3dQuaternion.FromAxisAngle[
[xA.real, yA.real, zA.real],
IF radiansA.ok THEN vA.real ELSE vA.real * degreesToRadians
];
};
translate => {
[aA, xA, yA, zA] ¬ Args.ArgsGetFromRope[r, "%rrrr"];
d.alpha ¬ aA.real;
d.triple ¬ [xA.real, yA.real, zA.real];
};
ENDCASE;
IF keyHead = NIL
THEN { keyHead ¬ d; }
ELSE { -- insert in appropriate place, with repititions! (important for multiple keys)
IF d.alpha <= keyHead.alpha
THEN {
keyHead.prev ¬ d;
d.next ¬ keyHead;
keyHead ¬ d;
}
ELSE {
l: KeyData ¬ keyHead;
WHILE l # NIL AND d.alpha > l.alpha DO l ¬ l.next; ENDLOOP;
IF l = NIL
THEN {
l ¬ keyHead;
WHILE l.next # NIL DO l ¬ l.next; ENDLOOP;
l.next ¬ d;
d.prev ¬ l;
}
ELSE {
d.next ¬ l;
d.prev ¬ l.prev;
l.prev.next ¬ d;
l.prev ¬ d;
};
};
};
ENDLOOP;
IO.Close[stream];
keyList.keys ¬ keyHead;
SELECT b FROM
straight => keyList.tube ¬ NIL;
smooth => {
keyList.knots ¬ TriplesFromKeys[keyHead, FALSE];
keyList.tube ¬ G3dTube.NewTube[keyList.knots];
};
cyclic => {
keyList.knots ¬ TriplesFromKeys[keyHead, TRUE];
keyList.tube ¬ G3dTube.NewTube[keyList.knots];
};
ENDCASE;
RETURN[keyList];
};
Sweep Support Procedures
GetSegment: PROC [alpha: REAL, keyList: KeyList] RETURNS [id: InterpolateData] ~ {
key: KeyData ¬ keyList.keys;
id.segment ¬ 0;
id.alpha ¬ 0.0;
id.near ¬ key;
id.far ¬ IF key.next # NIL THEN key.next ELSE key;
IF alpha <= key.alpha THEN RETURN;
DO
IF key.next = NIL THEN {
IF key.prev # NIL
THEN {
id.alpha ¬ 1.0;
id.segment ¬ id.segment - 1;
id.near ¬ key.prev;
id.far ¬ key;
}
ELSE {
id.alpha ¬ 0.0;
id.near ¬ id.far ¬ key;
};
RETURN;
};
IF alpha >= key.alpha AND alpha < key.next.alpha THEN EXIT;
key ¬ key.next;
id.near ¬ key;
id.far ¬ key.next;
id.segment ¬ id.segment + 1;
ENDLOOP;
IF key.next.alpha # key.alpha
THEN id.alpha ¬ (alpha - key.alpha) / (key.next.alpha - key.alpha)
ELSE id.alpha ¬ 0.0;
RETURN;
};
GetTubeSegment: PROC [tube: Tube, segment: INT] RETURNS [Tube] ~ {
WHILE tube # NIL AND segment > 0 DO
tube ¬ tube.next;
segment ¬ segment-1;
ENDLOOP;
RETURN[tube];
};
LerpTriple: PROC [a: REAL, lo, hi: Triple] RETURNS [Triple] ~ {
RETURN[G3dVector.Interp[a, lo, hi]];
};
LerpReal: PROC [a, lo, hi: REAL] RETURNS [REAL] ~ {
RETURN[lo + (a * (hi-lo))];
};
LinearInterpolateTriple: PROC [alpha: REAL, keyList: KeyList] RETURNS [Triple] ~ {
id: InterpolateData ¬ GetSegment[alpha, keyList];
RETURN[LerpTriple[id.alpha, id.near.triple, id.far.triple]];
};
LinearInterpolateReal: PROC [alpha: REAL, keyList: KeyList] RETURNS [REAL] ~ {
id: InterpolateData ¬ GetSegment[alpha, keyList];
RETURN[LerpReal[id.alpha, id.near.real, id.far.real]];
};
SplineInterpolateTriple: PROC [alpha: REAL, keyList: KeyList] RETURNS [Triple] ~ {
MyTubePosition: PROC [alpha: REAL, keyList: KeyList] RETURNS [Triple] ~ {
id: InterpolateData ¬ GetSegment[alpha, keyList];
tube: Tube ¬ GetTubeSegment[keyList.tube, id.segment];
RETURN[G3dSpline.Position[tube.spline, id.alpha]];
};
RETURN[MyTubePosition[alpha, keyList]];
};
SplineInterpolateXSection: PROC [alpha: REAL, keyList: KeyList] RETURNS [Matrix] ~ {
MyTubeXSection: PROC [alpha: REAL, keyList: KeyList] RETURNS [XSection] ~ {
id: InterpolateData ¬ GetSegment[alpha, keyList];
tube: Tube ¬ GetTubeSegment[keyList.tube, id.segment];
IF NOT keyList.tube.xSectionsValid THEN G3dTube.MakeXSections[keyList.tube];
RETURN[G3dTube.GetXSection[tube, id.alpha]];
};
RETURN[MyTubeXSection[alpha, keyList].matrix];
};
LinearInterpolateQuaternion: PROC [a: REAL, k: KeyList] RETURNS [Quaternion] ~ {
id: InterpolateData ¬ GetSegment[a, k];
RETURN[G3dQuaternion.Slerp[id.near.quaternion, id.far.quaternion, id.alpha]];
};
SmoothInterpolateQuaternion: PROC [a: REAL, k: KeyList] RETURNS [Quaternion] ~ {
id: InterpolateData ¬ GetSegment[a, k];
nn, n, f, ff: Quaternion;
IF id.near.prev # NIL
THEN nn ¬ id.near.prev.quaternion
ELSE nn ¬ id.near.quaternion;
n ¬ id.near.quaternion;
f ¬ id.far.quaternion;
IF id.far.next # NIL
THEN ff ¬ id.far.next.quaternion
ELSE ff ¬ id.far.quaternion;
RETURN[G3dQuaternion.CatmullRom[nn, n, f, ff, id.alpha]];
};
CyclicInterpolateQuaternion: PROC [a: REAL, k: KeyList] RETURNS [Quaternion] ~ {
id: InterpolateData ¬ GetSegment[a, k];
nn, n, f, ff: Quaternion;
IF id.near.prev # NIL
THEN nn ¬ id.near.prev.quaternion
ELSE {
kp: KeyData ¬ k.keys;
WHILE kp.next # NIL DO kp ¬ kp.next; ENDLOOP;
nn ¬ kp.quaternion;
};
n ¬ id.near.quaternion;
f ¬ id.far.quaternion;
IF id.far.next # NIL
THEN ff ¬ id.far.next.quaternion
ELSE ff ¬ k.keys.quaternion;
RETURN[G3dQuaternion.CatmullRom[nn, n, f, ff, id.alpha]];
};
Shape Blending And Combining Procedures
LerpShapes: PROC [s0, s1: Shape, alpha: REAL] RETURNS [Shape] ~ {
numVerts: INT ¬ s0.vertices.length;
shape: Shape ¬ G3dShape.CopyShape[s0];
IF s0.vertices.length # s1.vertices.length THEN RETURN[s0];
FOR i: INT IN [0 .. numVerts) DO
shape.vertices[i].point ¬ LerpTriple[alpha, s0.vertices[i].point, s1.vertices[i].point];
shape.vertices[i].normal ¬ G3dVector.Unit[LerpTriple[alpha, s0.vertices[i].normal, s1.vertices[i].normal]];
ENDLOOP;
G3dShape.SetFaceNormals[shape];
G3dShape.SetFaceCenters[shape];
G3dShape.SetVertexNormals[shape];
RETURN[shape];
};
Qverts:  TYPE ~ REF QvertsRep;
QvertsRep: TYPE ~ RECORD [
v0, v1, v2, v3:  INT ¬ -1,
next:     Qverts ¬ NIL
];
BuildBridges: PROC [sweep, instance: Shape, close: BOOL] RETURNS [Shape] ~ {
InsertQvert: PUBLIC PROC [v0, v1, v2, v3: INT] ~ {
AlreadyGot: PUBLIC PROC [q: Qverts] RETURNS [BOOL] ~ {
qlist: Qverts ¬ headq;
WHILE qlist # NIL DO
dumb test, should use some sorting
IF qlist.v0 = q.v0 AND qlist.v2 = q.v2 THEN {
IF qlist.v1 = q.v1 AND qlist.v3 = q.v3 THEN RETURN[TRUE];
IF qlist.v1 = q.v3 AND qlist.v3 = q.v1 THEN RETURN[TRUE];
};
qlist ¬ qlist.next;
ENDLOOP;
RETURN[FALSE];
};
newq: Qverts ¬ NEW[QvertsRep ¬ []];
smalli, smallv: INT;
rotate verts to canonical position, lowest index first
smallv ¬ v0;
smalli ¬ 0;
IF v1 < smallv THEN { smallv ¬ v1; smalli ¬ 1; };
IF v2 < smallv THEN { smallv ¬ v2; smalli ¬ 2; };
IF v3 < smallv THEN { smallv ¬ v3; smalli ¬ 3; };
SELECT smalli FROM
0 => { newq.v0 ¬ v0; newq.v1 ¬ v1; newq.v2 ¬ v2; newq.v3 ¬ v3; };
1 => { newq.v0 ¬ v1; newq.v1 ¬ v2; newq.v2 ¬ v3; newq.v3 ¬ v0; };
2 => { newq.v0 ¬ v2; newq.v1 ¬ v3; newq.v2 ¬ v0; newq.v3 ¬ v1; };
3 => { newq.v0 ¬ v3; newq.v1 ¬ v0; newq.v2 ¬ v1; newq.v3 ¬ v2; };
ENDCASE;
IF AlreadyGot[newq] THEN RETURN;
IF headq = NIL
THEN {headq ¬ tailq ¬ newq}
ELSE {tailq.next ¬ newq; tailq ¬ newq};
};
headq, tailq, wq: Qverts ¬ NIL;
bridgeShape: Shape ¬ G3dShape.CopyShape[sweep];
numInstances: INT ¬ bridgeShape.vertices.length / instance.vertices.length;
lastInstance: INT ¬ IF close THEN numInstances ELSE numInstances-1;
centerVertices: VertexSequence ¬ NIL;
instanceVerts: INT ¬ instance.vertices.length;
bridgeShape.surfaces ¬ NIL;
build list of joined non-duplicated edges in the instance
FOR p: INT IN [0 .. instance.surfaces.length) DO
numVerts: INT ¬ instance.surfaces[p].vertices.length;
maxVert: INT ¬ numVerts;
IF instance.surfaces[p].clientData # NIL THEN {
sd: SurfaceDescription ¬ NARROW[instance.surfaces[p].clientData];
IF sd.open THEN maxVert ¬ maxVert-1;
};
FOR v: INT IN [0 .. maxVert) DO
v0: INT ¬ instance.surfaces[p].vertices[v];
v1: INT ¬ instance.surfaces[p].vertices[(v+1) MOD numVerts];
p0: INT ¬ v0;
p1: INT ¬ v1;
p2: INT ¬ v0 + numVerts;
p3: INT ¬ v1 + numVerts;
InsertQvert[p2, p3, p1, p0];
ENDLOOP;
ENDLOOP;
append a set of these connecting polygons between each set of instances in the sweep
FOR j: INT IN [0 .. lastInstance) DO
offset: INT ¬ j * instance.vertices.length;
wq ¬ headq;
WHILE wq # NIL DO
center: Vertex ¬ NEW[VertexRep ¬ []];
v0, v1, v2, v3, v4: INT;
v0 ¬ (wq.v0 + offset) MOD bridgeShape.vertices.length;
v1 ¬ (wq.v1 + offset) MOD bridgeShape.vertices.length;
v2 ¬ (wq.v2 + offset) MOD bridgeShape.vertices.length;
v3 ¬ (wq.v3 + offset) MOD bridgeShape.vertices.length;
v4 ¬ IF centerVertices # NIL THEN centerVertices.length ELSE 0;
v4 ¬ v4 + bridgeShape.vertices.length;
IF instance.clientData # NIL THEN {
fixedVertices: G3dBasic.BoolSequence ¬ NARROW[instance.clientData];
IF fixedVertices[wq.v0 MOD instanceVerts] THEN v0 ¬ wq.v0 MOD instanceVerts;
IF fixedVertices[wq.v1 MOD instanceVerts] THEN v1 ¬ wq.v1 MOD instanceVerts;
IF fixedVertices[wq.v2 MOD instanceVerts] THEN v2 ¬ wq.v2 MOD instanceVerts;
IF fixedVertices[wq.v3 MOD instanceVerts] THEN v3 ¬ wq.v3 MOD instanceVerts;
};
AddValidQuadrilateralToShape[bridgeShape, v0, v3, v2, v1, none];
wq ¬ wq.next;
ENDLOOP;
ENDLOOP;
IF centerVertices # NIL THEN FOR i: INT IN [0 .. centerVertices.length) DO
bridgeShape.vertices ¬ G3dShape.AddToVertexSequence[bridgeShape.vertices, centerVertices[i]];
ENDLOOP;
bridgeShape.faces ¬ NIL;
bridgeShape.vertices.valid ¬ ALL[FALSE];
G3dShape.SetFaceNormals[bridgeShape, FALSE];
G3dShape.SetFaceCenters[bridgeShape];
G3dShape.SetVertexNormals[bridgeShape];
IF sweep.surfaces = NIL
THEN sweep ¬ bridgeShape    -- don't save useless vertices
ELSE sweep ¬ AppendShape[sweep, bridgeShape];
RETURN[sweep];
};
BuildInstances: PROC [sweep, instance: Shape, tubeTexture: BOOL ¬ TRUE] ~ {
numVerts: INT ¬ instance.vertices.length;
numInstances: INT ¬ sweep.vertices.length / numVerts;
FOR i: INT IN [0 .. numInstances) DO
offset: INT ¬ i * numVerts;
FOR s: INT IN [0 .. instance.surfaces.length) DO
n: NatSequence ¬ G2dBasic.CopyNatSequence[instance.surfaces[s].vertices];
IF instance.clientData # NIL
THEN {
fixedVertices: G3dBasic.BoolSequence ¬ NARROW[instance.clientData];
FOR j: INT IN [0 .. n.length) DO
IF NOT fixedVertices[j] THEN n[j] ¬ n[j] + offset;
ENDLOOP;
}
ELSE {
FOR j: INT IN [0 .. n.length) DO
n[j] ¬ n[j] + offset;
ENDLOOP;
};
sweep.surfaces ¬ G3dBasic.AddToSurfaceSequence[sweep.surfaces, [NIL, n]];
ENDLOOP;
ENDLOOP;
};
append vertices in s2 to s1
AppendVertices: PROC [s1, s2: Shape] ~ {
IF s2 = NIL OR s2.vertices = NIL THEN RETURN;
FOR i: INT IN [0 .. s2.vertices.length) DO
v: Vertex ¬ NEW[VertexRep ¬ s2.vertices[i]­];
s1.vertices ¬ G3dShape.AddToVertexSequence[s1.vertices, v];
ENDLOOP;
};
append s2 to s1
AppendShape: PROC [s1, s2: Shape, addPolys: BOOL ¬ TRUE] RETURNS [Shape] ~ {
Inner: PUBLIC PROC RETURNS [Shape] ~ {
offset: INT ¬ 0;
IF s1 # NIL THEN offset ¬ s1.vertices.length;
IF s2.vertices # NIL THEN FOR i: INT IN [0 .. s2.vertices.length) DO
v: Vertex ¬ NEW[VertexRep ¬ s2.vertices[i]­];
s1.vertices ¬ G3dShape.AddToVertexSequence[s1.vertices, v];
ENDLOOP;
IF addPolys AND s2.surfaces # NIL THEN {
FOR i: INT IN [0 .. s2.surfaces.length) DO
n: NatSequence ¬ G2dBasic.CopyNatSequence[s2.surfaces[i].vertices];
FOR j: INT IN [0 .. n.length) DO
n[j] ¬ n[j] + offset;
ENDLOOP;
s1.surfaces ¬ G3dBasic.AddToSurfaceSequence[s1.surfaces, [NIL, n]];
ENDLOOP;
};
RETURN[s1];
};
IF s1 = NIL THEN RETURN [G3dShape.CopyShape[s2]] ELSE RETURN [Inner[]];
};
Merge2Shapes: PUBLIC PROC [s1, s2: Shape, addPolys: BOOL ¬ TRUE] RETURNS [s: Shape] ~ {
offset: INT ¬ 0;
s ¬ NEW[ShapeRep ¬ []];
IF s1 # NIL THEN {
IF s1.vertices # NIL THEN {
FOR i: INT IN [0 .. s1.vertices.length) DO
v: Vertex ¬ NEW[VertexRep ¬ s1.vertices[i]­];
s.vertices ¬ G3dShape.AddToVertexSequence[s.vertices, v];
ENDLOOP;
offset ¬ s1.vertices.length;
};
IF s1.surfaces # NIL THEN FOR i: INT IN [0 .. s1.surfaces.length) DO
n: NatSequence ¬ G2dBasic.CopyNatSequence[s1.surfaces[i].vertices];
s.surfaces ¬ G3dBasic.AddToSurfaceSequence[s.surfaces, [NIL, n]];
ENDLOOP;
};
FOR i: INT IN [0 .. s2.vertices.length) DO
v: Vertex ¬ NEW[VertexRep ¬ s2.vertices[i]­];
s.vertices ¬ G3dShape.AddToVertexSequence[s.vertices, v];
ENDLOOP;
IF addPolys THEN {
FOR i: INT IN [0 .. s2.surfaces.length) DO
n: NatSequence ¬ G2dBasic.CopyNatSequence[s2.surfaces[i].vertices];
FOR j: INT IN [0 .. n.length) DO
n[j] ¬ n[j] + offset;
ENDLOOP;
s.surfaces ¬ G3dBasic.AddToSurfaceSequence[s.surfaces, [NIL, n]];
ENDLOOP;
};
RETURN[s];
};
Polygon To Shape Utility Procedures
AddTriangleToShape: PUBLIC PROC [shape: Shape, v0, v1, v2: INT] ~ {
vSequence: NatSequence ¬ NEW[NatSequenceRep[3]];
vSequence.length ¬ 3;
vSequence[0] ¬ v0;
vSequence[1] ¬ v1;
vSequence[2] ¬ v2;
shape.surfaces ¬ G3dBasic.AddToSurfaceSequence[shape.surfaces, [NIL, vSequence]];
};
AddGeneralQuadrilateralToShape: PUBLIC PROC [
shape: Shape,
v0, v1, v2, v3: INT,
op: SubdivisionType ¬ none
] ~ {
AddQuadrilateralToShape: PROC [shape: Shape, v0, v1, v2, v3: INT] ~ {
vSequence: NatSequence ¬ NEW[NatSequenceRep[4]];
vSequence.length ¬ 4;
vSequence[0] ¬ v0;
vSequence[1] ¬ v1;
vSequence[2] ¬ v2;
vSequence[3] ¬ v3;
shape.surfaces ¬ G3dBasic.AddToSurfaceSequence[shape.surfaces, [NIL, vSequence]];
};
AverageVertices: PROC [v0, v1: Vertex] RETURNS [Vertex] ~ {
new: Vertex ¬ NEW[VertexRep ¬ []];
new.point ¬ G3dVector.Mul[G3dVector.Add[v0.point, v1.point], 0.5];
new.normal ¬ G3dVector.Mul[G3dVector.Add[v0.normal, v1.normal], 0.5];
new.color ¬ G3dVector.Mul[G3dVector.Add[v0.color, v1.color], 0.5];
new.texture ¬ G2dVector.Mul[G2dVector.Add[v0.texture, v1.texture], 0.5];
new.transmittance ¬ 0.5 * (v0.transmittance + v1.transmittance);
RETURN[new];
};
SELECT op FROM
none => {
AddQuadrilateralToShape[shape, v0, v1, v2, v3];
};
halfTriangulate => {
AddTriangleToShape[shape, v0, v1, v2];
AddTriangleToShape[shape, v0, v2, v3];
};
quarterTriangulate => {
midIndex: INT ¬ IF shape.vertices # NIL THEN shape.vertices.length ELSE 0;
v01: Vertex ¬ AverageVertices[shape.vertices[v0], shape.vertices[v1]];
v23: Vertex ¬ AverageVertices[shape.vertices[v2], shape.vertices[v3]];
mid: Vertex ¬ AverageVertices[v01, v23];
shape.vertices ¬ G3dShape.AddToVertexSequence[shape.vertices, mid];
AddTriangleToShape[shape, v0, v1, midIndex];
AddTriangleToShape[shape, v1, v2, midIndex];
AddTriangleToShape[shape, v2, v3, midIndex];
AddTriangleToShape[shape, v3, v0, midIndex];
};
ENDCASE;
};
AddValidQuadrilateralToShape: PROC [
shape: Shape,
v0, v1, v2, v3: INT,
op: SubdivisionType ¬ halfTriangulate
] ~ {
CheckTriangle: PROC [shape: Shape, v0, v1, v2: INT] ~ {
IF v0#v1 AND v0#v2 AND v1#v2 THEN AddTriangleToShape[shape, v0, v1, v2];
};
IF v0#v1 AND v0#v2 AND v0#v3 AND v1#v2 AND v1#v3 AND v2#v3
THEN AddGeneralQuadrilateralToShape[shape, v0, v1, v2, v3, op]
ELSE {
SELECT TRUE FROM
v0=v1 => CheckTriangle[shape, v0, v2, v3];
v0=v2 => CheckTriangle[shape, v0, v1, v3];
v0=v3 => CheckTriangle[shape, v0, v1, v2];
v1=v2 => CheckTriangle[shape, v0, v1, v3];
v1=v3 => CheckTriangle[shape, v0, v1, v2];
v2=v3 => CheckTriangle[shape, v0, v1, v2];
ENDCASE;
};
};
Simple Sweep Setup Procedures
SimpleSetupShapeBlendProc: PUBLIC PROC [lo, hi: ROPE] RETURNS [REF ANY] ~ {
r: ROPE ¬ NIL;
r ¬ Rope.Cat[r, "0.0 ", lo, "\n"];
r ¬ Rope.Cat[r, "0.0 ", hi, "\n"];
RETURN[KeyListFromStream[IO.RIS[r], shape, straight]];
};
SimpleSetupRotateProc: PUBLIC PROC [lo, hi: AxisAngle] RETURNS [REF ANY] ~ {
r: ROPE ¬ NIL;
steps: INT ¬ 4;
FOR step: INT IN [0 .. steps) DO
alpha: REAL ¬ REAL[step] / REAL[steps-1];
r ¬ Rope.Cat[
r,
IO.PutFR1["%g ", IO.real[alpha]],
TripleToRope[LerpTriple[alpha, lo.unitAxis, hi.unitAxis]],
IO.PutFR1[" %g -radians\n", IO.real[LerpReal[alpha, lo.theta, hi.theta]]]
];
ENDLOOP;
RETURN[KeyListFromStream[IO.RIS[r], rotate, straight]];
};
SimpleSetupPathProc: PUBLIC PROC [lo, hi: Triple] RETURNS [REF ANY] ~ {
r: ROPE ¬ NIL;
r ¬ Rope.Cat[r, "0.0 ", TripleToRope[lo], "\n"];
r ¬ Rope.Cat[r, "1.0 ", TripleToRope[hi], "\n"];
RETURN[KeyListFromStream[IO.RIS[r], path, straight]];
};
SimpleSetupScaleProc: PUBLIC PROC [lo, hi: Triple] RETURNS [REF ANY] ~ {
r: ROPE ¬ NIL;
r ¬ Rope.Cat[r, "0.0 ", TripleToRope[lo], "\n"];
r ¬ Rope.Cat[r, "1.0 ", TripleToRope[hi], "\n"];
RETURN[KeyListFromStream[IO.RIS[r], scale, straight]];
};
SimpleSetupTranslateProc: PUBLIC PROC [lo, hi: Triple] RETURNS [REF ANY] ~ {
r: ROPE ¬ NIL;
r ¬ Rope.Cat[r, "0.0 ", TripleToRope[lo], "\n"];
r ¬ Rope.Cat[r, "1.0 ", TripleToRope[hi], "\n"];
RETURN[KeyListFromStream[IO.RIS[r], translate, straight]];
};
Sweep Default Setup Procedures
SetupDefaultShapeBlendProc: PUBLIC PROC [f: ROPE, b: BlendType] RETURNS [REF ANY] ~ {
RETURN[KeyListFromFile[f, shape, b]];
};
SetupDefaultPathProc: PUBLIC PROC [f: ROPE, b: BlendType] RETURNS [REF ANY] ~ {
RETURN[KeyListFromFile[f, path, b]];
};
SetupDefaultScaleProc: PUBLIC PROC [f: ROPE, b: BlendType] RETURNS [REF ANY] ~ {
RETURN[KeyListFromFile[f, scale, b]];
};
SetupDefaultRotateProc: PUBLIC PROC [f: ROPE, b: BlendType] RETURNS [REF ANY] ~ {
RETURN[KeyListFromFile[f, rotate, b]];
};
SetupDefaultTranslateProc: PUBLIC PROC [f: ROPE, b: BlendType] RETURNS [REF ANY] ~ {
RETURN[KeyListFromFile[f, translate, b]];
};
Sweep Default Procedures
DefaultShapeBlendProc: PUBLIC ShapeBlendProc ~ {
-- unimplemented (still a research topic!)
RETURN[NIL];
};
DefaultPathProc: PUBLIC PathProc ~ {
keyList: KeyList ¬ NARROW[clientData];
m: Matrix;
SELECT keyList.type FROM
straight => m ¬ G3dMatrix.MakeTranslate[LinearInterpolateTriple[t, keyList]];
smooth => m ¬ SplineInterpolateXSection[t, keyList];
cyclic => m ¬ SplineInterpolateXSection[t, keyList];
ENDCASE;
RETURN[m];
};
DefaultScaleProc: PUBLIC ScaleProc ~ {
keyList: KeyList ¬ NARROW[clientData];
triple: Triple;
SELECT keyList.type FROM
straight => triple ¬ LinearInterpolateTriple[t, keyList];
smooth => triple ¬ SplineInterpolateTriple[t, keyList];
cyclic => triple ¬ SplineInterpolateTriple[t, keyList];
ENDCASE;
RETURN[triple];
};
DefaultRotateProc: PUBLIC RotateProc ~ {
keyList: KeyList ¬ NARROW[clientData];
q: Quaternion;
SELECT keyList.type FROM
straight => q ¬ LinearInterpolateQuaternion[t, keyList];
smooth => q ¬ SmoothInterpolateQuaternion[t, keyList];
cyclic => q ¬ CyclicInterpolateQuaternion[t, keyList];
ENDCASE;
RETURN[G3dQuaternion.ToAxisAngle[q]];
};
DefaultTranslateProc: PUBLIC TranslateProc ~ {
keyList: KeyList ¬ NARROW[clientData];
triple: Triple;
SELECT keyList.type FROM
straight => triple ¬ LinearInterpolateTriple[t, keyList];
smooth => triple ¬ SplineInterpolateTriple[t, keyList];
cyclic => triple ¬ SplineInterpolateTriple[t, keyList];
ENDCASE;
RETURN[triple];
};
Sweep Construction
MakeSweepShape: PUBLIC PROC [
shapeProc: ShapeBlendProc,
shapeData: REF ANY ¬ NIL,
shapeBlend: BlendType ¬ smooth,
pathProc: PathProc ¬ NIL,
pathData: REF ANY ¬ NIL,
pathBlend: BlendType ¬ smooth,
scaleProc: ScaleProc ¬ NIL,
scaleData: REF ANY ¬ NIL,
scaleBlend: BlendType ¬ smooth,
rotateProc: RotateProc ¬ NIL,
rotateData: REF ANY ¬ NIL,
rotateBlend: BlendType ¬ smooth,
translateProc: TranslateProc ¬ NIL,
translateData: REF ANY ¬ NIL,
translateBlend: BlendType ¬ smooth,
xformProc: XformProc ¬ NIL,
xformData: REF ANY ¬ NIL,
xformBlend: BlendType ¬ smooth,
steps: INT ¬ 10,
tLo: REAL ¬ 0.0,
tHi: REAL ¬ 1.0,
firstInstance: BOOL ¬ FALSE,
lastInstance: BOOL ¬ FALSE,
reverseFirstInstance: BOOL ¬ FALSE,
reverseLastInstance: BOOL ¬ TRUE,
anchor: BOOL ¬ FALSE,
instance: BOOL ¬ TRUE,
connect: BOOL ¬ TRUE,
localXSections: BOOL ¬ FALSE,
tubeTexture: BOOL ¬ TRUE,
close: BOOL ¬ FALSE
] RETURNS [Shape] ~ {
sweepShape: Shape ¬ NIL;
protoShape, thisInstance: Shape;
pathXSection, scaleXSection, rotateXSection, translateXSection, xformXSection: Matrix;
anchorMat: Matrix ¬ NIL;
firstShape, lastShape: Shape ¬ NIL;
IF shapeProc = NIL THEN protoShape ¬ NARROW[shapeData];
steps ¬ ABS[steps];
FOR step: INT IN [0 .. steps) DO
localalpha: REAL ¬
IF close
THEN IF steps > 0 THEN REAL[step]/REAL[steps] ELSE 0.0
ELSE IF steps > 1 THEN REAL[step]/REAL[steps-1] ELSE 0.0;
alpha: REAL ¬ tLo + localalpha * (tHi - tLo);
final: Matrix;
build the appropriate transformation matrices for this step
pathXSection ¬ IF pathProc # NIL
THEN pathProc[alpha, pathBlend, pathData]
ELSE G3dMatrix.Identity[];
scaleXSection ¬ IF scaleProc # NIL
THEN G3dMatrix.MakeScale[scaleProc[alpha, scaleBlend, scaleData]]
ELSE G3dMatrix.Identity[];
IF rotateProc # NIL
THEN {
aa: AxisAngle ¬ rotateProc[alpha, rotateBlend, rotateData];
rotateXSection ¬ G3dMatrix.MakeRotate[axis: aa.unitAxis, theta: aa.theta, degrees: FALSE];
}
ELSE rotateXSection ¬ G3dMatrix.Identity[];
translateXSection ¬ IF translateProc # NIL
THEN G3dMatrix.MakeTranslate[translateProc[alpha, translateBlend, translateData]]
ELSE G3dMatrix.Identity[];
xformXSection ¬ IF xformProc # NIL
THEN xformProc[alpha, xformBlend, xformData]
ELSE G3dMatrix.Identity[];
if localXSections, then use only the translation component along the path
IF localXSections THEN { 
trans: Triple ¬ [pathXSection[3][0], pathXSection[3][1], pathXSection[3][2]];
pathXSection ¬ G3dMatrix.MakeTranslate[trans];
};
if anchored, then always undo the very first path transformation
IF anchor THEN {
IF anchorMat = NIL
THEN {
trans: Triple ¬ [pathXSection[3][0], pathXSection[3][1], pathXSection[3][2]];
tmat: Matrix ¬ G3dMatrix.MakeTranslate[trans];
anchorMat ¬ G3dMatrix.Invert[tmat];
pathXSection[3][0] ¬ pathXSection[3][1] ¬ pathXSection[3][2] ¬ 0.0;
}
ELSE pathXSection ¬ G3dMatrix.Mul[pathXSection, anchorMat];
};
build the final transform, in the order: path, scale, rotate, translate, xform
final ¬
G3dMatrix.Mul[xformXSection,
G3dMatrix.Mul[translateXSection,
G3dMatrix.Mul[rotateXSection,
G3dMatrix.Mul[scaleXSection, pathXSection]]]];
determine the new shape and transform it under the final transform
IF shapeProc # NIL THEN
protoShape ¬ shapeProc[alpha, shapeBlend, shapeData];
thisInstance ¬ G3dShape.CopyShape[protoShape];
G3dShape.TransformVertices[thisInstance, final];
IF tubeTexture THEN FOR i: INT IN [0 .. thisInstance.vertices.length) DO
thisInstance.vertices[i].texture.y ¬ localalpha;
ENDLOOP;
save this shape if it's the first or last
IF firstInstance AND (step = 0) THEN {
firstShape ¬ G3dShape.CopyShape[thisInstance];
IF reverseFirstInstance THEN G3dShape.ReversePolygons[firstShape];
};
IF lastInstance AND (step = steps-1) THEN {
lastShape ¬ G3dShape.CopyShape[thisInstance];
IF reverseLastInstance THEN G3dShape.ReversePolygons[lastShape];
};
append the instance to the instances collection
IF sweepShape = NIL
THEN {
sweepShape ¬ G3dShape.CopyShape[thisInstance];
sweepShape.surfaces ¬ NIL;
}
ELSE AppendVertices[sweepShape, thisInstance];
ENDLOOP;
build the instances
IF instance THEN BuildInstances[sweepShape, thisInstance, tubeTexture];
build the bridges
IF connect THEN sweepShape ¬ BuildBridges[sweepShape, thisInstance, close];
insert the first and last instances if requested
IF firstInstance THEN sweepShape ¬ IF sweepShape = NIL
THEN G3dShape.CopyShape[firstShape]
ELSE Merge2Shapes[sweepShape, firstShape];
IF lastInstance THEN sweepShape ¬ IF sweepShape = NIL
THEN G3dShape.CopyShape[lastShape]
ELSE Merge2Shapes[sweepShape, lastShape];
combine the instances and the bridges
RETURN[sweepShape];
};
Utility Procs
TripleToRope: PROC [t: Triple] RETURNS [ROPE] ~ {
RETURN[IO.PutFR["%g %g %g", IO.real[t.x], IO.real[t.y], IO.real[t.z]]];
};
Lcm: PUBLIC PROC [a, b: INT] RETURNS [c: INT] ~ { -- returns least common multiple (a,b)
c ¬ a;
WHILE c < a*b DO
r: REAL ¬ REAL[c]/REAL[b];
IF r = Real.Floor[r] THEN EXIT;
c ¬ c+a;
ENDLOOP;
RETURN[c];
};
Start Procedures
END.