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 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; 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 ]; pi: REAL = 3.141593; piOver2: REAL = 1.570796; piTimes2: REAL = 6.283186; degreesToRadians: REAL = .01745329; radiansToDegrees: REAL = 57.29577; 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]; }; 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]]; }; 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 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; 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; 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; 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; }; 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; }; 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]; }; 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; }; }; 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]]; }; 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]]; }; 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]; }; 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; 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 { trans: Triple ¬ [pathXSection[3][0], pathXSection[3][1], pathXSection[3][2]]; pathXSection ¬ G3dMatrix.MakeTranslate[trans]; }; 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]; }; final ¬ G3dMatrix.Mul[xformXSection, G3dMatrix.Mul[translateXSection, G3dMatrix.Mul[rotateXSection, G3dMatrix.Mul[scaleXSection, pathXSection]]]]; 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; 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]; }; IF sweepShape = NIL THEN { sweepShape ¬ G3dShape.CopyShape[thisInstance]; sweepShape.surfaces ¬ NIL; } ELSE AppendVertices[sweepShape, thisInstance]; ENDLOOP; IF instance THEN BuildInstances[sweepShape, thisInstance, tubeTexture]; IF connect THEN sweepShape ¬ BuildBridges[sweepShape, thisInstance, close]; 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]; RETURN[sweepShape]; }; 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]; }; END. r 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 Imported Types Error: PUBLIC SIGNAL [code: ATOM, reason: ROPE] = CODE; Local Types Constants Support for KeyLists From Files and Streams Sweep Support Procedures Shape Blending And Combining Procedures dumb test, should use some sorting rotate verts to canonical position, lowest index first build list of joined non-duplicated edges in the instance append a set of these connecting polygons between each set of instances in the sweep append vertices in s2 to s1 append s2 to s1 Polygon To Shape Utility Procedures Simple Sweep Setup Procedures Sweep Default Setup Procedures Sweep Default Procedures Sweep Construction build the appropriate transformation matrices for this step if localXSections, then use only the translation component along the path if anchored, then always undo the very first path transformation build the final transform, in the order: path, scale, rotate, translate, xform determine the new shape and transform it under the final transform save this shape if it's the first or last append the instance to the instances collection build the instances build the bridges insert the first and last instances if requested combine the instances and the bridges Utility Procs Start Procedures ΚP–"cedarcode" style•NewlineDelimiter ™™Jšœ Οeœ6™BJ™,J™&J™JšΟk œžœnžœ ˜šJ˜—šΡbln œžœž˜Jšžœžœdžœ ˜ŽJšžœ ˜J˜—Jšœž˜headšΟl™Jšœ žœ ˜Jšœžœ˜-Jšœžœ˜&Jšœ žœ˜$Jšœžœ˜,Jšœžœ˜1Jšœžœ˜/Jšžœžœžœ˜Jšœ žœ˜!Jšœ žœ˜&Jšžœžœžœžœ˜Jšœ žœ˜#Jšœžœ˜1Jšœ žœ˜Jšœ žœ˜#Jšœžœ˜(šœžœ˜1J˜—Jš Οnœžœžœžœ žœžœ™;—š  ™ Jšœžœ˜(Jšœžœ˜'Jšœ žœ˜%Jšœžœ˜*Jšœ žœ˜%Jšœžœ˜*Jšœžœ˜'Jšœžœ˜*Jšœžœ˜)Jšœžœ˜8Jšœžœ"˜=Jšœžœ˜2Jšœžœ˜1Jšœžœ˜/Jšœžœ˜(J˜šœžœžœ˜#JšœžœΟc˜0Jšœ’˜6Jšœ žœ’)˜>J˜J˜——š  ™ Jšœžœ ˜Jšœ žœ ˜Jšœ žœ ˜Jšœžœ ˜#Jšœžœ ˜"—š +™+š‘œž œ žœ žœ˜aJšœžœžœ5˜HJšžœ&˜,J˜J˜—š‘œž œ žœ žœ˜cš ‘œžœ žœžœžœ˜:šž˜JšœžœžœΟs˜Jšžœ žœžœžœžœžœžœžœžœžœžœ˜JJšžœ˜—Jšœžœ˜J˜—š ‘œžœžœžœžœ˜[J˜!Jšœžœ˜ šžœžœž˜J˜2J˜Jšžœ˜—Jšžœžœ4˜BJ˜—Jšœžœ˜Jšœžœ ˜#J˜šž˜šžœ˜Jšžœžœ˜Jšžœ žœ’˜/J˜—Jšœ žœ˜"Jšœžœ˜!J˜&šžœž˜˜ J˜*J˜J˜J˜—˜ J˜4J˜J˜'J˜—˜ J˜4J˜J˜'J˜—˜ J˜MJ˜˜+J˜Jšžœ žœ žœ˜;J˜—J˜—˜J˜4J˜J˜'J˜—Jšžœ˜—šžœ ž˜Jšžœ˜šžœ’O˜Všžœ˜šžœ˜J˜J˜J˜ J˜—šžœ˜J˜Jš žœžœžœžœ žœ˜;šžœž˜ šžœ˜J˜ Jšžœ žœžœ žœ˜*J˜ J˜ J˜—šžœ˜J˜ J˜J˜J˜ J˜——J˜——J˜——Jšžœ˜—Jšžœ˜J˜šžœž˜ Jšœžœ˜˜ Jšœ)žœ˜0J˜.J˜—˜ Jšœ)žœ˜/J˜.J˜—Jšžœ˜—Jšžœ ˜J˜J˜——š ™š‘ œžœ žœžœ˜RJ˜J˜J˜J˜Jš œ žœ žœžœ žœ˜2Jšžœžœžœ˜"šž˜šžœ žœžœ˜šžœ ž˜šžœ˜J˜J˜J˜J˜ J˜—šžœ˜J˜J˜J˜——Jšžœ˜J˜—Jšžœžœžœžœ˜;J˜J˜J˜J˜Jšžœ˜—šžœ˜Jšžœ>˜BJšžœ˜—Jšžœ˜J˜J˜—š‘œžœžœžœ ˜Bšžœžœžœ ž˜#J˜J˜Jšžœ˜—Jšžœ˜ J˜—J˜š‘ œžœžœžœ ˜?Jšžœ˜$J˜J˜—š ‘œžœ žœžœžœ˜3Jšžœ˜J˜J˜—š‘œžœ žœžœ ˜RJ˜1Jšžœ6˜šžœ˜šžœžœž˜J˜*J˜*J˜*J˜*J˜*J˜*Jšžœ˜—J˜——J˜——š ™š‘œ£ž £œ žœ£ž£œž£žœ£œ£œ˜KJšœžœžœ˜J˜"J˜"Jšžœžœžœ˜6J˜J˜—š ‘œž œžœžœžœ˜LJšœžœžœ˜Jšœžœ˜šžœžœžœž˜ Jšœžœžœ žœ ˜)˜ Jšœ˜Jšžœžœ˜!Jšœ:˜:Jšžœžœ+˜IJšœ˜—Jšžœ˜—Jšžœžœžœ˜7J˜J˜—š ‘œž œžœžœžœ˜GJšœžœžœ˜J˜0J˜0Jšžœžœžœ˜5J˜J˜—š ‘œž œžœžœžœ˜HJšœžœžœ˜J˜0J˜0Jšžœžœžœ˜6J˜J˜—š ‘œž œžœžœžœ˜LJšœžœžœ˜J˜0J˜0Jšžœžœžœ˜:J˜J˜——š ™š‘œ£ž £œžœ£œ£œ £ž£œž£žœ£œ£œ˜UJšžœ˜%J˜J˜—š ‘œž œžœžœžœžœ˜OJšžœ˜$J˜J˜—š ‘œž œžœžœžœžœ˜PJšžœ˜%J˜J˜—š ‘œž œžœžœžœžœ˜QJšžœ ˜&J˜J˜—š ‘œž œžœžœžœžœ˜TJšžœ#˜)J˜——š ™š‘œžœ˜0J˜*J˜ J˜J˜—š‘œžœ ˜$Jšœžœ ˜&J˜ šžœž˜J˜MJ˜4J˜4Jšžœ˜—Jšžœ˜ J˜J˜—š‘œžœ˜&Jšœžœ ˜&J˜šžœž˜J˜9J˜7J˜7Jšžœ˜—Jšžœ ˜J˜J˜—š‘œžœ˜(Jšœžœ ˜&J˜šžœž˜J˜8J˜6J˜6Jšžœ˜—Jšžœ˜%J˜J˜—š‘œžœ˜.Jšœžœ ˜&J˜šžœž˜J˜9J˜7J˜7Jšžœ˜—Jšžœ ˜J˜——š ™š‘œž œ˜J˜Jšœ žœžœžœ˜J˜Jšœžœ˜Jšœ žœžœžœ˜J˜Jšœžœ˜Jšœ žœžœžœ˜J˜Jšœžœ˜Jšœ žœžœžœ˜J˜ Jšœžœ˜#Jšœžœžœžœ˜J˜#Jšœžœ˜Jšœ žœžœžœ˜J˜Jšœžœ˜Jšœžœ˜Jšœžœ˜Jšœžœžœ˜Jšœžœžœ˜Jšœžœžœ˜#Jšœžœžœ˜!Jšœžœžœ˜Jšœ žœžœ˜Jšœ žœžœ˜Jšœžœžœ˜Jšœ žœžœ˜Jšœžœž˜šœžœ ˜Jšœžœ˜J˜ J˜VJšœžœ˜J˜#J˜Jšžœ žœžœžœ ˜7J˜Jšœžœ˜šžœžœžœž˜ šœ žœ˜šžœ˜Jš žœžœ žœžœžœžœ˜6Jš žœžœ žœžœžœ žœ˜9——Jšœžœ"˜-J˜J˜J™;šœžœ ž˜ Jšžœ%˜)Jšžœ˜—šœžœ ž˜"Jšžœ=˜AJšžœ˜—šžœž˜šžœ˜J˜;JšœSžœ˜ZJ˜—Jšžœ'˜+—šœžœž˜*JšžœM˜QJšžœ˜—šœžœ ž˜"Jšžœ(˜,Jšžœ˜J˜—J™Išžœžœ˜J˜MJ˜.J˜J˜—J™@šžœžœ˜šžœ ž˜šžœ˜J˜MJ˜.J˜#J˜CJ˜—Jšžœ7˜;—J˜J˜—J™N˜˜˜ ˜J˜.J˜————J™Bšžœ žœž˜J˜5—J˜.J˜0J˜š žœ žœžœžœžœ%ž˜HJ˜0Jšžœ˜—J˜J™)šžœžœ žœ˜&J˜.Jšžœžœ&˜BJ˜—šžœžœžœ˜+J˜-Jšžœžœ%˜@J˜—J™J™/šžœž˜šžœ˜J˜.Jšœžœ˜J˜—Jšžœ*˜.J˜—Jšžœ˜J˜——J™šžœ žœ7˜GJ˜—J™Jšžœ žœ<˜KJ˜Jšœ0™0šžœžœžœž˜6Jšžœ˜#Jšžœ&˜*—šžœžœžœž˜5Jšžœ˜"Jšžœ%˜)—J˜J™%Jšžœ ˜J˜——š  ™ š‘ œžœ žœžœ˜1Jš žœžœžœ žœ žœ ˜GJ˜J˜—š ‘œž œžœžœžœ’&˜XJ˜šžœ žœ˜Jšœžœžœžœ˜Jšžœžœžœ˜J˜Jšžœ˜—Jšžœ˜ J˜——š ™J˜J˜—Jšžœ˜—…—c†ΰ