<<>> <> <> <> <> DIRECTORY G2dBasic, G2dSpline, G3dBasic, G3dMatrix, G3dQuaternion, G3dRender, G3dShape, G3dSpline, G3dVector, LinearSystem, Real, Rope, SVD, G3dTimeTrees; G3dTimeTreesImpl: CEDAR PROGRAM IMPORTS G2dBasic, G2dSpline, G3dBasic, G3dMatrix, G3dQuaternion, G3dRender, G3dSpline, G3dVector, LinearSystem, Real, Rope, SVD EXPORTS G3dTimeTrees ~ BEGIN <> Spline1dSequence: TYPE ~ G2dSpline.Spline1dSequence; Pair: TYPE ~ G3dBasic.Pair; PairSequence: TYPE ~ G3dBasic.PairSequence; RealSequence: TYPE ~ G3dBasic.RealSequence; Matrix: TYPE ~ G3dMatrix.Matrix; Triple: TYPE ~ G3dMatrix.Triple; TripleSequence: TYPE ~ G3dBasic.TripleSequence; Quaternion: TYPE ~ G3dQuaternion.Quaternion; QuaternionSequence: TYPE ~ G3dTimeTrees.QuaternionSequence; QuaternionSequenceRep: TYPE ~ G3dTimeTrees.QuaternionSequenceRep; Shape: TYPE ~ G3dShape.Shape; SplineSequence: TYPE ~ G3dSpline.SplineSequence; ColumnN: TYPE ~ LinearSystem.ColumnN; MatrixN: TYPE ~ LinearSystem.MatrixN; RowN: TYPE ~ LinearSystem.RowN; ROPE: TYPE ~ Rope.ROPE; TTInterpolateProc: TYPE ~ G3dTimeTrees.TTInterpolateProc; TTReportProc: TYPE ~ G3dTimeTrees.TTReportProc; TTNodeActionProc: TYPE ~ G3dTimeTrees.TTNodeActionProc; InterpolationType: TYPE ~ G3dTimeTrees.InterpolationType; KeyType: TYPE ~ G3dTimeTrees.KeyType; Key: TYPE ~ G3dTimeTrees.Key; KeyRep: TYPE ~ G3dTimeTrees.KeyRep; Node: TYPE ~ G3dTimeTrees.Node; NodeRep: TYPE ~ G3dTimeTrees.NodeRep; FlavorInfo: TYPE ~ G3dTimeTrees.FlavorInfo; FlavorInfoRep: TYPE ~ G3dTimeTrees.FlavorInfoRep; KeyHead: TYPE ~ G3dTimeTrees.KeyHead; KeyHeadRep: TYPE ~ G3dTimeTrees.KeyHeadRep; TimeTree: TYPE ~ G3dTimeTrees.TimeTree; TimeTreeRep: TYPE ~ G3dTimeTrees.TimeTreeRep; KeySequence: TYPE ~ G3dTimeTrees.KeySequence; KeySequenceRep: TYPE ~ G3dTimeTrees.KeySequenceRep; NodeSequence: TYPE ~ G3dTimeTrees.NodeSequence; NodeSequenceRep: TYPE ~ G3dTimeTrees.NodeSequenceRep; RrsrtpNode: TYPE ~ G3dTimeTrees.RrsrtpNode; RrsrtpNodeRep: TYPE ~ G3dTimeTrees.RrsrtpNodeRep; RealRef: TYPE ~ G3dTimeTrees.RealRef; TripleRef: TYPE ~ G3dTimeTrees.TripleRef; MatrixRef: TYPE ~ G3dTimeTrees.MatrixRef; MatrixAccelerators: TYPE ~ G3dTimeTrees.MatrixAccelerators; MatrixAcceleratorsRep: TYPE ~ G3dTimeTrees.MatrixAcceleratorsRep; TripleAccelerators: TYPE ~ G3dTimeTrees.TripleAccelerators; TripleAcceleratorsRep: TYPE ~ G3dTimeTrees.TripleAcceleratorsRep; RealAccelerators: TYPE ~ G3dTimeTrees.RealAccelerators; RealAcceleratorsRep: TYPE ~ G3dTimeTrees.RealAcceleratorsRep; TransformOrder: TYPE ~ G3dTimeTrees.TransformOrder; Error: PUBLIC SIGNAL [reason: ROPE] = CODE; <> CreateTimeTree: PUBLIC PROC RETURNS [timeTree: TimeTree] ~ { timeTree ¬ NEW[TimeTreeRep ¬ []]; timeTree.activeNode ¬ timeTree.root ¬ NEW[NodeRep ¬ [name: "root"]]; timeTree.activeNode.timeTree ¬ timeTree; }; PushTimeTree: PUBLIC PROC [timeTree: TimeTree] ~ { newNode: Node ¬ NEW[NodeRep ¬ []]; newNode.parent ¬ timeTree.activeNode; newNode.timeTree ¬ timeTree; newNode.locked ¬ timeTree.activeNode.locked; timeTree.activeNode.children ¬ AddToNodeSequence[timeTree.activeNode.children, newNode]; timeTree.activeNode ¬ newNode; }; PopTimeTree: PUBLIC PROC [timeTree: TimeTree] ~ { IF timeTree.activeNode.parent # NIL THEN timeTree.activeNode ¬ timeTree.activeNode.parent; }; RegisterKeyType: PUBLIC PROC [timeTree: TimeTree, name: ROPE, type: KeyType, report: TTReportProc ¬ NIL, interp: TTInterpolateProc ¬ NIL, interpType: InterpolationType ¬ smooth] ~ { flavor: FlavorInfo ¬ NEW[FlavorInfoRep ¬ [ name: name, type: type, interpProc: interp, reportProc: report, interpolationType: interpType]]; timeTree.flavorList ¬ CONS[flavor, timeTree.flavorList]; }; <> SetNodeObject: PUBLIC PROC [ timeTree: TimeTree, object: REF ¬ NIL, clientData: REF ¬ NIL] RETURNS [Node] ~ { timeTree.activeNode.object ¬ object; timeTree.activeNode.clientData ¬ clientData; RETURN[timeTree.activeNode]; }; NodeFromName: PUBLIC PROC [timeTree: TimeTree, name: ROPE] RETURNS [Node] ~ { GetName: PROC [n: Node] ~ { IF Rope.Equal[n.name, name, FALSE] THEN { result ¬ n; RETURN; }; FOR i: INT IN [0 .. n.children.length) DO IF result = NIL THEN GetName[n.children[i]]; ENDLOOP; RETURN; }; result: Node ¬ NIL; GetName[timeTree.root]; RETURN[result]; }; RenameNode: PUBLIC PROC [timeTree: TimeTree, oldName, newName: ROPE] ~ { Rename: PROC [n: Node] ~ { IF Rope.Equal[n.name, oldName, FALSE] THEN { n.name ¬ newName; done ¬ TRUE; RETURN; }; FOR i: INT IN [0 .. n.children.length) DO IF NOT done THEN Rename[n.children[i]]; ENDLOOP; }; done: BOOL ¬ FALSE; Rename[timeTree.root]; }; ClearNodeObject: PUBLIC PROC [node: REF ANY] ~ { IF node # NIL THEN { n: Node ¬ NARROW[node]; n.object ¬ NIL; }; }; PruneNode: PUBLIC PROC [node: Node] ~ { parent: Node ¬ node.parent; new: NodeSequence ¬ NIL; FOR i: INT IN [0 .. parent.children.length) DO IF parent.children[i] # node THEN new ¬ AddToNodeSequence[new, parent.children[i]]; ENDLOOP; node.parent ¬ NIL; parent.children ¬ new; }; GraftNode: PUBLIC PROC [orphan, newParent: Node] ~ { IF newParent # NIL THEN newParent.children ¬ AddToNodeSequence[newParent.children, orphan]; }; MoveNode: PUBLIC PROC [moveNode: Node, newParent: Node] ~ { PruneNode[moveNode]; GraftNode[moveNode, newParent]; }; SetLocalTransform: PUBLIC PROC [tt: TimeTree, m: Matrix] ~ { IF tt = NIL OR tt.activeNode = NIL THEN RETURN; tt.activeNode.localTransform ¬ G3dMatrix.CopyMatrix[m]; }; GetActiveNode: PUBLIC PROC [tt: TimeTree] RETURNS [REF ANY] ~ { RETURN[tt.activeNode]; }; SetActiveNode: PUBLIC PROC [tt: TimeTree, n: REF ANY] ~ { tt.activeNode ¬ NARROW[n]; }; GetRoot: PUBLIC PROC [node: Node] RETURNS [root: TimeTree] ~ { IF node # NIL THEN root ¬ node.timeTree; }; EnumerateNodes: PUBLIC PROC [tt: TimeTree, action: TTNodeActionProc] ~ { ActOn: PROC [node: Node] ~ { continue ¬ action[node]; IF node.children # NIL THEN FOR i: INT IN [0 .. node.children.length) DO IF continue THEN continue ¬ action[node.children[i]]; ENDLOOP; }; continue: BOOL ¬ TRUE; ActOn[tt.root]; }; <> InsertKey: PUBLIC PROC [ timeTree: TimeTree, time: REAL, data: REF, name: ROPE ¬ NIL, keyHead: KeyHead ¬ NIL] ~ { key: Key ¬ NEW[KeyRep ¬ [time, data]]; IF keyHead = NIL THEN keyHead ¬ GetKeyHeadFromName[timeTree, name]; IF keyHead = NIL THEN keyHead ¬ PrependNewKeyHead[timeTree, name, data]; InsertKeyInKeyHead[key, keyHead]; }; InsertDirtyKeys: PUBLIC PROC [timeTree: TimeTree, time: REAL] ~ { InsertKeys: PROC [node: Node] ~ { IF node.keyHeads = NIL THEN RETURN; FOR k: LIST OF KeyHead ¬ node.keyHeads, k.rest UNTIL k=NIL DO IF k.first.dirty THEN { key: Key ¬ NEW[KeyRep ¬ [time, k.first.strobe]]; InsertKeyInKeyHead[key, k.first]; }; ENDLOOP; }; InsertKeys[timeTree.root]; }; MakeNewTransformKeyHead: PROC RETURNS [kh: KeyHead] ~ { kh ¬ NEW[KeyHeadRep ¬ []]; kh.flavorInfo ¬ NEW[FlavorInfoRep ¬ [type: matrix]]; }; InsertTransform: PUBLIC PROC [timeTree: TimeTree, time: REAL, transform: Matrix] ~ { key: Key ¬ NEW[KeyRep ¬ [time, transform]]; IF timeTree.activeNode.transformKeyHead = NIL THEN { timeTree.activeNode.transformKeyHead ¬ MakeNewTransformKeyHead[]; }; InsertKeyInKeyHead[key, timeTree.activeNode.transformKeyHead]; timeTree.activeNode.transformKeyHead.acceleratorsValid ¬ FALSE; }; SetTransformDirty: PUBLIC PROC [node: Node, dirty: BOOL ¬ TRUE] ~ { IF node.transformKeyHead = NIL THEN node.transformKeyHead ¬ MakeNewTransformKeyHead[]; node.transformKeyHead.dirty ¬ dirty; }; GetTransformDirty: PUBLIC PROC [node: Node] RETURNS [BOOL ¬ FALSE] ~ { IF node.transformKeyHead # NIL THEN RETURN[node.transformKeyHead.dirty]; }; GetKeyHeadFromName: PROC [timeTree: TimeTree, name: ROPE ¬ NIL] RETURNS [KeyHead] ~ { an: Node ¬ timeTree.activeNode; IF an.keyHeads = NIL THEN RETURN [NIL]; FOR k: LIST OF KeyHead ¬ an.keyHeads, k.rest UNTIL k=NIL DO IF Rope.Equal[name, k.first.flavorInfo.name, FALSE] THEN RETURN [k.first]; ENDLOOP; RETURN[NIL]; }; SetKeyHeadProcs: PUBLIC PROC [ timeTree: TimeTree, keyName: ROPE, interpProc: TTInterpolateProc ¬ NIL, reportProc: TTReportProc ¬ NIL] ~ { IF timeTree.activeNode # NIL THEN { kh: KeyHead ¬ GetKeyHeadFromName[timeTree, keyName]; IF kh = NIL THEN RETURN; IF interpProc # NIL THEN { kh.flavorInfo.interpProc ¬ interpProc; kh.flavorInfo.gotInterpProc ¬ TRUE; }; IF reportProc # NIL THEN { kh.flavorInfo.reportProc ¬ reportProc; kh.flavorInfo.gotReportProc ¬ TRUE; }; }; }; SetKeyHeadInterpType: PUBLIC PROC [timeTree: TimeTree, keyName: ROPE, interpType: InterpolationType ¬ smooth] ~ { IF timeTree.activeNode # NIL THEN { kh: KeyHead ¬ GetKeyHeadFromName[timeTree, keyName]; IF kh = NIL THEN kh ¬ PrependNewKeyHead[timeTree, keyName, NIL]; kh.flavorInfo.interpolationType ¬ interpType; }; }; PrependNewKeyHead: PROC [timeTree: TimeTree, name: ROPE, data: REF] RETURNS [kh: KeyHead] ~ { kh ¬ NEW[KeyHeadRep ¬ []]; kh.flavorInfo ¬ NEW[FlavorInfoRep ¬ []]; kh.flavorInfo.name ¬ name; WITH data SELECT FROM d: MatrixRef => kh.flavorInfo.type ¬ matrix; d: TripleRef => kh.flavorInfo.type ¬ triple; d: RealRef => kh.flavorInfo.type ¬ real; ENDCASE => kh.flavorInfo.type ¬ unknown; timeTree.activeNode.keyHeads ¬ CONS[kh, timeTree.activeNode.keyHeads]; }; SetTransformProcs: PUBLIC PROC [timeTree: TimeTree, report: TTReportProc ¬ NIL, interp: TTInterpolateProc ¬ NIL, interpType: InterpolationType ¬ smooth] ~ { timeTree.transformFlavor ¬ NEW[FlavorInfoRep ¬ [interpProc: interp, reportProc: report, interpolationType: interpType, type: matrix]]; }; SetTransformInterpType: PUBLIC PROC [timeTree: TimeTree, interpType: InterpolationType ¬ smooth] ~ { IF timeTree.activeNode # NIL THEN { IF timeTree.activeNode.transformKeyHead = NIL THEN timeTree.activeNode.transformKeyHead ¬ MakeNewTransformKeyHead[]; timeTree.activeNode.transformKeyHead.flavorInfo.interpolationType ¬ interpType; }; }; InsertKeyInKeyHead: PROC [newKey: Key, kh: KeyHead] ~ { IF kh.keys = NIL THEN { kh.keys ¬ AddToKeySequence[NIL, newKey]; RETURN; } ELSE { InsertKeyAt: PROC [p: INT] ~ { new: KeySequence ¬ NEW[KeySequenceRep[kh.keys.length+1]]; new.length ¬ kh.keys.length+1; FOR i: INT IN [0 .. p) DO new[i] ¬ kh.keys[i]; ENDLOOP; new[p] ¬ newKey; FOR i: INT IN [p .. kh.keys.length) DO new[i+1] ¬ kh.keys[i]; ENDLOOP; kh.keys ¬ new; }; ReplaceKeyAt: PROC [p: INT] ~ { kh.keys[p] ¬ newKey; }; <<-- linear insertion sort>> inserted: BOOL ¬ FALSE; FOR i: INT IN [0 .. kh.keys.length) DO IF newKey.time <= kh.keys[i].time AND NOT inserted THEN { IF newKey.time # kh.keys[i].time THEN InsertKeyAt[i] ELSE ReplaceKeyAt[i]; inserted ¬ TRUE; }; ENDLOOP; IF NOT inserted THEN kh.keys ¬ AddToKeySequence[kh.keys, newKey]; }; }; <> ComputeKeyHeadAccelerators: PROC [keyHead: KeyHead] ~ { <> CoerceKeyHead: PROC ~ { <> FOR i: INT IN [0 .. keyHead.keys.length-1) DO r0: RrsrtpNode ¬ NARROW[keyHead.keys[i].data]; r1: RrsrtpNode ¬ NARROW[keyHead.keys[i+1].data]; IF G3dQuaternion.Dot[r0.q1, r1.q1] < 0.0 THEN r1.q1 ¬ G3dQuaternion.Neg[r1.q1]; IF G3dQuaternion.Dot[r0.q2, r1.q2] < 0.0 THEN r1.q2 ¬ G3dQuaternion.Neg[r1.q2]; IF G3dQuaternion.Dot[r0.qc, r1.qc] < 0.0 THEN r1.qc ¬ G3dQuaternion.Neg[r1.qc]; ENDLOOP; }; ComputeMatrixAccelerators: PROC ~ { tKnots: TripleSequence ¬ NIL; sKnots: TripleSequence ¬ NIL; qs1: QuaternionSequence ¬ NIL; qs2: QuaternionSequence ¬ NIL; qsc: QuaternionSequence ¬ NIL; ma: MatrixAccelerators ¬ NEW[MatrixAcceleratorsRep ¬ []]; FOR i: INT IN [0 .. keyHead.keys.length) DO rrsrtp: RrsrtpNode; WITH keyHead.keys[i].data SELECT FROM m: Matrix => { oldQ: Matrix ¬ NIL; IF i>0 THEN oldQ ¬ G3dQuaternion.ToMatrix[NARROW[keyHead.keys[i-1].data, RrsrtpNode].q1]; rrsrtp ¬ MatrixToRrsrtp[m, oldQ, FALSE]; keyHead.keys[i].data ¬ rrsrtp; }; r: RrsrtpNode => rrsrtp ¬ r; ENDCASE => Error["Bad data in matrix field"]; sKnots ¬ G3dBasic.AddToTripleSequence[sKnots, rrsrtp.scale]; tKnots ¬ G3dBasic.AddToTripleSequence[tKnots, rrsrtp.translate]; qs1 ¬ G3dQuaternion.AddToQuaternionSequence[qs1, rrsrtp.q1]; qs2 ¬ G3dQuaternion.AddToQuaternionSequence[qs2, rrsrtp.q2]; qsc ¬ G3dQuaternion.AddToQuaternionSequence[qsc, rrsrtp.qc]; ENDLOOP; ma.scaleSpline ¬ G3dSpline.Interpolate[sKnots]; ma.transSpline ¬ G3dSpline.Interpolate[tKnots]; G3dQuaternion.ConformQuaternionSequence[qs1]; G3dQuaternion.ConformQuaternionSequence[qs2]; G3dQuaternion.ConformQuaternionSequence[qsc]; ma.q1 ¬ qs1; ma.q2 ¬ qs2; ma.qc ¬ qsc; keyHead.accelerators ¬ ma; CoerceKeyHead[]; }; ComputeTripleAccelerators: PROC ~ { knots: TripleSequence ¬ NIL; ta: TripleAccelerators ¬ NEW[TripleAcceleratorsRep ¬ []]; FOR i: INT IN [0 .. keyHead.keys.length) DO t: REF Triple ¬ NARROW[keyHead.keys[i].data]; knots ¬ G3dBasic.AddToTripleSequence[knots, t­]; ENDLOOP; ta.tSpline ¬ G3dSpline.Interpolate[knots]; keyHead.accelerators ¬ ta; }; ComputeRealAccelerators: PROC ~ { knots: RealSequence ¬ NIL; ra: RealAccelerators ¬ NEW[RealAcceleratorsRep ¬ []]; FOR i: INT IN [0 .. keyHead.keys.length) DO r: REF REAL ¬ NARROW[keyHead.keys[i].data]; knots ¬ G2dBasic.AddToRealSequence[knots, r­]; ENDLOOP; ra.vSpline ¬ G2dSpline.Interpolate1d[knots]; keyHead.accelerators ¬ ra; }; IF keyHead.flavorInfo.type = unknown THEN { -- catch unassigned keyheads IF keyHead.keys # NIL AND keyHead.keys.length > 0 THEN { WITH keyHead.keys[0].data SELECT FROM r: RealRef => keyHead.flavorInfo.type ¬ real; t: TripleRef => keyHead.flavorInfo.type ¬ triple; m: MatrixRef => keyHead.flavorInfo.type ¬ matrix; ENDCASE => keyHead.flavorInfo.type ¬ unknown; }; }; SELECT keyHead.flavorInfo.type FROM matrix => ComputeMatrixAccelerators[]; triple => ComputeTripleAccelerators[]; real => ComputeRealAccelerators[]; ENDCASE; keyHead.acceleratorsValid ¬ TRUE; }; <> RrsrtpToMatrix: PUBLIC PROC [rrsrtp: RrsrtpNode] RETURNS [m: Matrix] ~ { qm1: Matrix ¬ G3dQuaternion.MakeRotateQ[rrsrtp.q1]; qmc: Matrix ¬ G3dQuaternion.MakeRotateQ[rrsrtp.qc]; qm2: Matrix ¬ G3dQuaternion.MakeRotateQ[rrsrtp.q2]; smat: Matrix ¬ G3dMatrix.MakeScale[rrsrtp.scale]; tmat: Matrix ¬ G3dMatrix.MakeTranslate[rrsrtp.translate]; iterm: Matrix ¬ G3dMatrix.Mul[qmc, G3dMatrix.Mul[smat, qm2]]; m ¬ G3dMatrix.Mul[qm1, G3dMatrix.Mul[iterm, tmat]]; }; RrsrtpComponentsToMatrix: PUBLIC PROC [q1: Quaternion, qc: Quaternion, s: Triple, q2: Quaternion, t: Triple, p: Triple] RETURNS [Matrix] ~ { rrsrtp: RrsrtpNode ¬ NEW[RrsrtpNodeRep ¬ [q1: q1, qc: qc, scale: s, q2: q2, translate: t, perspective: p]]; RETURN[RrsrtpToMatrix[rrsrtp]]; }; FindSVD: PROC [m: Matrix] RETURNS [u, w, v: Matrix] ~ { aMat: LinearSystem.MatrixN ¬ LinearSystem.Create[3, 3]; vMat: LinearSystem.MatrixN ¬ LinearSystem.Create[3, 3]; wCol: LinearSystem.ColumnN ¬ NEW[LinearSystem.VecSeq[3]]; u ¬ G3dMatrix.Identity[]; v ¬ G3dMatrix.Identity[]; FOR i: INT IN [0 .. 3) DO FOR j: INT IN [0 .. 3) DO aMat[i][j] ¬ m[i][j]; ENDLOOP; ENDLOOP; SVD.SVDecomp[aMat, 3, 3, wCol, vMat]; FOR i: INT IN [0 .. 3) DO FOR j: INT IN [0 .. 3) DO u[i][j] ¬ aMat[i][j]; v[i][j] ¬ vMat[j][i]; -- use V Transpose! ENDLOOP; ENDLOOP; w ¬ G3dMatrix.MakeScale[[wCol[0], wCol[1], wCol[2]]]; }; GetConformanceMatrix: PROC [m1, m2, oldQ: Matrix, propagate: BOOL ¬ TRUE] RETURNS [conf: Matrix] ~ { IF propagate THEN { <> IF oldQ = NIL THEN conf ¬ G3dMatrix.Identity[] ELSE { c: Matrix ¬ G3dMatrix.Mul[G3dMatrix.Transpose[m1], oldQ]; u, w, v: Matrix; [u, w, v] ¬ FindSVD[c]; conf ¬ G3dMatrix.Mul[u, v]; }; } ELSE { <> conf ¬ G3dMatrix.CopyMatrix[m2]; }; }; MatrixToRrsrtp: PUBLIC PROC [m: Matrix, oldQ: Matrix ¬ NIL, propagate: BOOL ¬ TRUE] RETURNS [rrsrtp: RrsrtpNode] ~ { CheckConstruction: PROC ~ { -- debugging smat: Matrix ¬ G3dMatrix.MakeScale[rrsrtp.scale]; tmat: Matrix ¬ G3dMatrix.MakeTranslate[rrsrtp.translate]; r1mat: Matrix ¬ G3dQuaternion.ToMatrix[rrsrtp.q1]; r2mat: Matrix ¬ G3dQuaternion.ToMatrix[rrsrtp.q2]; rcmat: Matrix ¬ G3dQuaternion.ToMatrix[rrsrtp.qc]; iterm: Matrix ¬ G3dMatrix.Mul[rcmat, G3dMatrix.Mul[smat, r2mat]]; composite: Matrix ¬ G3dMatrix.Mul[r1mat, G3dMatrix.Mul[iterm, tmat]]; CheckEqualMats[composite, rrsrtp.matrix]; }; ExtractReflections: PROC [m: Matrix] RETURNS [REAL] ~ { Sgn: PROC [a: REAL] RETURNS [REAL] ~ { IF a < 0.0 THEN RETURN[-1.0] ELSE RETURN[1.0]; }; row1: Triple ¬ [m[0][0], m[0][1], m[0][2]]; row2: Triple ¬ [m[1][0], m[1][1], m[1][2]]; row3: Triple ¬ [m[2][0], m[2][1], m[2][2]]; b: Triple ¬ G3dVector.Cross[row1, row2]; dot: REAL ¬ G3dVector.Dot[row3, b]; RETURN[Sgn[dot]]; }; m1, m2, m3: REAL; aMat: Matrix ¬ G3dMatrix.Identity[]; cMat: Matrix ¬ G3dMatrix.Identity[]; firstMat: Matrix ¬ G3dMatrix.Identity[]; secondMat: Matrix ¬ G3dMatrix.Identity[]; scaleMat: Matrix ¬ G3dMatrix.Identity[]; confMat: Matrix ¬ G3dMatrix.Identity[]; rrsrtp ¬ NEW[RrsrtpNodeRep ¬ []]; rrsrtp.matrix ¬ G3dMatrix.CopyMatrix[m]; rrsrtp.translate ¬ [m[3][0], m[3][1], m[3][2]]; [aMat, scaleMat, secondMat] ¬ FindSVD[m]; cMat ¬ GetConformanceMatrix[aMat, secondMat, oldQ, propagate]; firstMat ¬ G3dMatrix.Mul[aMat, cMat]; confMat ¬ G3dMatrix.Transpose[cMat]; rrsrtp.scale ¬ [scaleMat[0][0], scaleMat[1][1], scaleMat[2][2]]; m1 ¬ ExtractReflections[firstMat]; m2 ¬ ExtractReflections[secondMat]; m3 ¬ ExtractReflections[confMat]; firstMat ¬ G3dMatrix.Scale[firstMat, m1]; secondMat ¬ G3dMatrix.Scale[secondMat, m2]; confMat ¬ G3dMatrix.Scale[confMat, m3]; rrsrtp.scale ¬ G3dVector.Mul[rrsrtp.scale, m1*m2*m3]; rrsrtp.q1 ¬ G3dQuaternion.FromMatrix[firstMat]; rrsrtp.q2 ¬ G3dQuaternion.FromMatrix[secondMat]; rrsrtp.qc ¬ G3dQuaternion.FromMatrix[confMat]; rrsrtp.matrix ¬ G3dMatrix.CopyMatrix[m]; rrsrtp.perspective ¬ [0.0, 0.0, 0.0]; rrsrtp.decomposed ¬ TRUE; CheckConstruction[]; -- debugging }; <> GetNodeKeyHead: PUBLIC PROC [node: Node, name: ROPE] RETURNS [KeyHead] ~ { IF node.keyHeads = NIL THEN RETURN [NIL]; FOR k: LIST OF KeyHead ¬ node.keyHeads, k.rest UNTIL k=NIL DO IF Rope.Equal[name, k.first.flavorInfo.name, FALSE] THEN RETURN [k.first]; ENDLOOP; RETURN[NIL]; }; GetStrobedKey: PUBLIC PROC [node: Node, name: ROPE] RETURNS [REF ANY] ~ { keyHead: KeyHead ¬ GetNodeKeyHead[node, name]; IF keyHead # NIL THEN RETURN[keyHead.strobe] ELSE RETURN[NIL]; }; TransformNode: PUBLIC PROC [ node: Node, matrix: Matrix, order: TransformOrder] ~ { IF node.transformKeyHead = NIL THEN RETURN; SELECT order FROM pre => node.transformKeyHead.strobe ¬ G3dMatrix.Mul[matrix, NARROW[node.transformKeyHead.strobe]]; post => node.transformKeyHead.strobe ¬ G3dMatrix.Mul[NARROW[node.transformKeyHead.strobe], matrix]; ENDCASE; node.transformKeyHead.dirty ¬ TRUE; }; SaveNode: PUBLIC PROC [node: Node, clientBackup: REF ANY ¬ NIL] ~ { IF node.keyHeads = NIL THEN RETURN; FOR k: LIST OF KeyHead ¬ node.keyHeads, k.rest UNTIL k=NIL DO k.first.backup ¬ k.first.strobe; ENDLOOP; node.transformKeyHead.backup ¬ node.transformKeyHead.strobe; node.clientBackup ¬ clientBackup; }; RestoreNode: PUBLIC PROC [node: Node] RETURNS [REF ANY] ~ { IF node.keyHeads = NIL THEN RETURN[NIL]; FOR k: LIST OF KeyHead ¬ node.keyHeads, k.rest UNTIL k=NIL DO k.first.strobe ¬ k.first.backup; ENDLOOP; node.transformKeyHead.strobe ¬ node.transformKeyHead.backup; RETURN[node.clientBackup]; }; <> LockActiveNode: PUBLIC PROC [timeTree: TimeTree] ~ { IF timeTree.activeNode # NIL THEN timeTree.activeNode.locked ¬ TRUE; }; UnlockActiveNode: PUBLIC PROC [timeTree: TimeTree] ~ { IF timeTree.activeNode # NIL THEN timeTree.activeNode.locked ¬ FALSE; }; <> LookupInterpProc: PROC [timeTree: TimeTree, name: ROPE] RETURNS [TTInterpolateProc] ~ { FOR f: LIST OF FlavorInfo ¬ timeTree.flavorList, f.rest UNTIL f = NIL DO IF Rope.Equal[f.first.name, name, FALSE] THEN RETURN [f.first.interpProc]; ENDLOOP; RETURN[NIL]; }; LookupReportProc: PROC [timeTree: TimeTree, name: ROPE] RETURNS [TTReportProc] ~ { FOR f: LIST OF FlavorInfo ¬ timeTree.flavorList, f.rest UNTIL f = NIL DO IF Rope.Equal[f.first.name, name, FALSE] THEN RETURN [f.first.reportProc]; ENDLOOP; RETURN[NIL]; }; <> <> <> <> <> <> <<};>> <> <> <> <> <> <> <> <> <> <<};>> <> <<};>> <<>> GetGlobalTransform: PUBLIC PROC [node: Node] RETURNS [Matrix] ~ { GetParentGlobal: PROC [node: Node] RETURNS [Matrix] ~ { parent: Node ¬ node.parent; IF parent = NIL THEN RETURN [G3dMatrix.Identity[]]; IF parent.globalTransform = NIL THEN RETURN[GetParentGlobal[parent]] ELSE RETURN[parent.globalTransform]; }; parentGlobal: Matrix ¬ GetParentGlobal[node]; IF node.localTransform # NIL THEN RETURN[G3dMatrix.Mul[node.localTransform, parentGlobal]] ELSE RETURN[parentGlobal]; }; PropagateStrobedMatrices: PROC [node: Node] ~ { PropagateNode: PROC [node: Node] ~ { IF node = NIL THEN RETURN; node.globalTransform ¬ GetGlobalTransform[node]; IF node.children # NIL THEN FOR i: INT IN [0 .. node.children.length) DO PropagateNode[node.children[i]]; ENDLOOP; }; PropagateNode[node]; }; StrobeMatrix: PROC [kh: KeyHead, segment: CARDINAL, alpha: REAL] RETURNS [Matrix] ~ { ma: MatrixAccelerators ¬ NARROW[kh.accelerators]; t, s, p: Triple ¬ []; q1, q2, qc: Quaternion ¬ []; sm1: CARDINAL ¬ MAX[0, segment-1]; sm0: CARDINAL ¬ segment; sp0: CARDINAL ¬ MIN[segment+1, kh.keys.length-1]; sp1: CARDINAL ¬ MIN[segment+2, kh.keys.length-1]; rrsrtpm1: RrsrtpNode ¬ NARROW[kh.keys[sm1].data]; rrsrtpm0: RrsrtpNode ¬ NARROW[kh.keys[sm0].data]; rrsrtpp0: RrsrtpNode ¬ NARROW[kh.keys[sp0].data]; rrsrtpp1: RrsrtpNode ¬ NARROW[kh.keys[sp1].data]; SELECT kh.flavorInfo.interpolationType FROM constant => { t ¬ rrsrtpm0.translate; s ¬ rrsrtpm0.scale; q1 ¬ ma.q1[sm0]; q2 ¬ ma.q2[sm0]; qc ¬ ma.qc[sm0]; }; linear => { t ¬ G3dVector.Interp[alpha, rrsrtpm0.translate, rrsrtpp0.translate]; s ¬ G3dVector.Interp[alpha, rrsrtpm0.scale, rrsrtpp0.scale]; q1 ¬ G3dQuaternion.Slerp[ma.q1[sm0], ma.q1[sp0], alpha]; q2 ¬ G3dQuaternion.Slerp[ma.q2[sm0], ma.q2[sp0], alpha]; qc ¬ G3dQuaternion.Slerp[ma.qc[sm0], ma.qc[sp0], alpha]; }; smooth => { t ¬ G3dSpline.Position[ma.transSpline[segment], alpha]; s ¬ G3dSpline.Position[ma.scaleSpline[segment], alpha]; q1 ¬ G3dQuaternion.CatmullRom[ma.q1[sm1], ma.q1[sm0], ma.q1[sp0], ma.q1[sp1], alpha]; q2 ¬ G3dQuaternion.CatmullRom[ma.q2[sm1], ma.q2[sm0], ma.q2[sp0], ma.q2[sp1], alpha]; qc ¬ G3dQuaternion.CatmullRom[ma.qc[sm1], ma.qc[sm0], ma.qc[sp0], ma.qc[sp1], alpha]; }; ENDCASE => Error["illegal interpolation type"]; RETURN[RrsrtpComponentsToMatrix[q1, qc, s, q2, t, p]]; }; StrobeTriple: PROC [kh: KeyHead, segment: INT, alpha: REAL] RETURNS [REF Triple] ~ { t: Triple; lo: Triple ¬ NARROW[kh.keys[segment].data, TripleRef]­; SELECT kh.flavorInfo.interpolationType FROM constant => t ¬ lo; linear => { segp1: INT ¬ (segment+1) MOD kh.keys.length; hi: Triple ¬ NARROW[kh.keys[segp1].data, TripleRef]­; t ¬ G3dVector.Interp[alpha, lo, hi]; }; smooth => { ta: TripleAccelerators ¬ NARROW[kh.accelerators]; t ¬ G3dSpline.Position[ta.tSpline[segment], alpha]; }; ENDCASE => Error["illegal interpolation type"]; RETURN[NEW[Triple ¬ t]]; }; StrobeReal: PROC [kh: KeyHead, segment: INT, alpha: REAL] RETURNS [REF REAL] ~ { r: REAL; SELECT kh.flavorInfo.interpolationType FROM constant => { r ¬ NARROW[kh.keys[segment].data, RealRef]­; }; linear => { segp1: INT ¬ (segment+1) MOD kh.keys.length; oldr: REAL ¬ NARROW[kh.keys[segment].data, RealRef]­; newr: REAL ¬ NARROW[kh.keys[segp1].data, RealRef]­; r ¬ oldr + (alpha * (newr - oldr)); }; smooth => { ra: RealAccelerators ¬ NARROW[kh.accelerators]; r ¬ G2dSpline.Position1d[ra.vSpline[segment], alpha]; }; ENDCASE => Error["illegal interpolation type"]; RETURN[NEW[REAL ¬ r]]; }; StrobeNode: PROC [timeTree: TimeTree, node: Node, time: REAL] ~ { GetSegment: PROC [kh: KeyHead, time: REAL] RETURNS [INT] ~ { IF kh = NIL OR kh.keys = NIL THEN RETURN [0]; FOR i: INT IN [0 .. kh.keys.length) DO IF time < kh.keys[i].time THEN RETURN[IF i > 0 THEN i-1 ELSE 0]; ENDLOOP; RETURN[IF kh.keys.length > 1 THEN kh.keys.length-2 ELSE 0]; }; GetAlpha: PROC [kh: KeyHead, segment: CARDINAL, time: REAL] RETURNS [REAL] ~ { leftTime: REAL ¬ kh.keys[segment].time; rightTime: REAL ¬ kh.keys[MIN[segment+1, kh.keys.length-1]].time; dTime: REAL ¬ rightTime - leftTime; IF dTime = 0.0 THEN dTime ¬ 1.0; RETURN [(time - leftTime)/dTime]; }; FOR kh: LIST OF KeyHead ¬ node.keyHeads, kh.rest UNTIL kh=NIL DO IF kh.first.keys # NIL AND kh.first.keys.length > 0 THEN { ckh: KeyHead ¬ kh.first; segment: INT; alpha: REAL; IF NOT kh.first.acceleratorsValid THEN ComputeKeyHeadAccelerators[kh.first]; segment ¬ GetSegment[kh.first, time]; alpha ¬ GetAlpha[kh.first, segment, time]; SELECT kh.first.flavorInfo.type FROM matrix => kh.first.strobe ¬ StrobeMatrix[kh.first, segment, alpha]; triple => kh.first.strobe ¬ StrobeTriple[kh.first, segment, alpha]; real => kh.first.strobe ¬ StrobeReal[kh.first, segment, alpha]; ENDCASE; IF ckh.flavorInfo.gotInterpProc = FALSE THEN ckh.flavorInfo.interpProc ¬ LookupInterpProc[timeTree, ckh.flavorInfo.name]; IF ckh.flavorInfo.interpProc # NIL THEN ckh.strobe ¬ kh.first.flavorInfo.interpProc[time, ckh.keys, node.object, node.clientData]; <> <> <> <> <<};>> }; ENDLOOP; <<-- and the distinguished transform>> IF node.transformKeyHead # NIL THEN { kh: KeyHead ¬ node.transformKeyHead; segment: INT; alpha: REAL; IF kh.keys # NIL THEN { IF NOT kh.acceleratorsValid THEN ComputeKeyHeadAccelerators[kh]; segment ¬ GetSegment[kh, time]; alpha ¬ GetAlpha[kh, segment, time]; kh.strobe ¬ StrobeMatrix[kh, segment, alpha]; node.localTransform ¬ NARROW[kh.strobe, Matrix]; }; }; }; ReportNodes: PROC [timeTree: TimeTree] ~ { ReportKeyHeads: PROC [node: Node] ~ { FOR kh: LIST OF KeyHead ¬ node.keyHeads, kh.rest UNTIL kh=NIL DO IF kh.first.flavorInfo.gotReportProc = FALSE THEN kh.first.flavorInfo.reportProc ¬ LookupReportProc[timeTree, kh.first.flavorInfo.name]; IF kh.first.flavorInfo.reportProc # NIL THEN { kh.first.flavorInfo.reportProc[kh.first.strobe, node.object, node.clientData]; }; ENDLOOP; IF timeTree.transformFlavor.reportProc # NIL THEN { timeTree.transformFlavor.reportProc[node.globalTransform, node.object, node.clientData]; }; }; ReportNode: PROC [node: Node] ~ { ReportKeyHeads[node]; IF node.children # NIL THEN FOR i: INT IN [0 .. node.children.length) DO ReportNode[node.children[i]]; ENDLOOP; }; ReportNode[timeTree.root]; }; StrobeTimeTree: PUBLIC PROC [timeTree: TimeTree, time: REAL] ~ { EvaluateNode: PROC [node: Node] ~ { IF node = NIL THEN RETURN; StrobeNode[timeTree, node, time]; IF node.children # NIL THEN FOR i: INT IN [0 .. node.children.length) DO EvaluateNode[node.children[i]]; ENDLOOP; }; IF timeTree = NIL THEN RETURN; EvaluateNode[timeTree.root]; <> }; ReportTimeTree: PUBLIC PROC [timeTree: TimeTree] ~ { IF timeTree = NIL THEN RETURN; PropagateStrobedMatrices[timeTree.root]; ReportNodes[timeTree]; }; <> AddToNodeSequence: PROC [nodes: NodeSequence, node: Node] RETURNS [NodeSequence] ~ { IF nodes = NIL THEN nodes ¬ NEW[NodeSequenceRep[1]]; IF nodes.length = nodes.maxLength THEN nodes ¬ LengthenNodes[nodes]; nodes[nodes.length] ¬ node; nodes.length ¬ nodes.length+1; RETURN[nodes]; }; LengthenNodes: PROC [nodes: NodeSequence, amount: REAL ¬ 1.3] RETURNS [new: NodeSequence] ~ { newLength: NAT ¬ MAX[Real.Ceiling[amount*nodes.maxLength], 3]; new ¬ NEW[NodeSequenceRep[newLength]]; FOR i: NAT IN [0..nodes.length) DO new[i] ¬ nodes[i]; ENDLOOP; new.length ¬ nodes.length; }; AddToKeySequence: PROC [keys: KeySequence, key: Key] RETURNS [KeySequence] ~ { IF keys = NIL THEN keys ¬ NEW[KeySequenceRep[1]]; IF keys.length = keys.maxLength THEN keys ¬ LengthenKeys[keys]; keys[keys.length] ¬ key; keys.length ¬ keys.length+1; RETURN[keys]; }; LengthenKeys: PROC [keys: KeySequence, amount: REAL ¬ 1.3] RETURNS [new: KeySequence] ~ { newLength: NAT ¬ MAX[Real.Ceiling[amount*keys.maxLength], 3]; new ¬ NEW[KeySequenceRep[newLength]]; FOR i: NAT IN [0..keys.length) DO new[i] ¬ keys[i]; ENDLOOP; new.length ¬ keys.length; }; <> diffuseName: ROPE = "diffuseReflectivity"; specularName: ROPE = "specularReflectivity"; metallicityName: ROPE = "metallicity"; shininessName: ROPE = "shininess"; transmittanceName: ROPE = "transmittance"; colorName: ROPE = "color"; transformName: ROPE = "transform"; <> InitializeGraphicsTimeTree: PUBLIC PROC [tt: TimeTree] ~ { <<-- the reals>> RegisterKeyType[tt, diffuseName, real, ReportDiffuse]; RegisterKeyType[tt, specularName, real, ReportSpecular]; RegisterKeyType[tt, metallicityName, real, ReportMetallicity]; RegisterKeyType[tt, shininessName, real, ReportShininess]; RegisterKeyType[tt, transmittanceName, real, ReportTransmittance]; <<-- the triples>> RegisterKeyType[tt, colorName, triple, ReportColor]; <<-- the matrices>> SetTransformProcs[tt, ReportTransform]; }; <> ReportDiffuse: PUBLIC TTReportProc ~ { diffuse: REF REAL ¬ NARROW[strobe]; shape: Shape ¬ NARROW[object]; IF shape # NIL THEN G3dRender.SetDiffuse[shape, diffuse­]; }; ReportSpecular: PUBLIC TTReportProc ~ { specular: REF REAL ¬ NARROW[strobe]; shape: Shape ¬ NARROW[object]; IF shape # NIL THEN G3dRender.SetSpecular[shape, specular­]; }; ReportMetallicity: PUBLIC TTReportProc ~ { metallicity: REF REAL ¬ NARROW[strobe]; shape: Shape ¬ NARROW[object]; IF shape # NIL THEN G3dRender.SetMetallicity[shape, metallicity­]; }; ReportShininess: PUBLIC TTReportProc ~ { shininess: REF REAL ¬ NARROW[strobe]; shape: Shape ¬ NARROW[object]; IF shape # NIL THEN G3dRender.SetShininess[shape, shininess­]; }; ReportTransmittance: PUBLIC TTReportProc ~ { transmittance: REF REAL ¬ NARROW[strobe]; shape: Shape ¬ NARROW[object]; IF shape # NIL THEN G3dRender.SetTransmittance[shape, transmittance­]; }; ReportColor: PUBLIC TTReportProc ~ { color: REF Triple ¬ NARROW[strobe]; shape: Shape ¬ NARROW[object]; IF shape # NIL THEN G3dRender.SetColor[shape, [color.x, color.y, color.z]]; }; ReportTransform: PUBLIC TTReportProc ~ { transform: Matrix ¬ NARROW[strobe]; shape: Shape ¬ NARROW[object]; <> IF shape # NIL THEN { IF transform # NIL THEN shape.matrix ¬ G3dMatrix.CopyMatrix[transform] ELSE shape.matrix ¬ G3dMatrix.Identity[]; }; }; <> AssignDiffuse: PUBLIC PROC [tt: TimeTree, time: REAL, diffuse: REAL] ~ { localDiffuse: REF REAL ¬ NEW[REAL ¬ diffuse]; [] ¬ InsertKey[tt, time, localDiffuse, diffuseName]; }; AssignSpecular: PUBLIC PROC [tt: TimeTree, time: REAL, specular: REAL] ~ { localSpecular: REF REAL ¬ NEW[REAL ¬ specular]; [] ¬ InsertKey[tt, time, localSpecular, specularName]; }; AssignMetallicity: PUBLIC PROC [tt: TimeTree, time: REAL, metallicity: REAL] ~ { localMetallicity: REF REAL ¬ NEW[REAL ¬ metallicity]; [] ¬ InsertKey[tt, time, localMetallicity, metallicityName]; }; AssignShininess: PUBLIC PROC [tt: TimeTree, time: REAL, shininess: REAL] ~ { localShininess: REF REAL ¬ NEW[REAL ¬ shininess]; [] ¬ InsertKey[tt, time, localShininess, shininessName]; }; AssignTransmittance: PUBLIC PROC [tt: TimeTree, time: REAL, transmittance: REAL] ~ { localTransmittance: REF REAL ¬ NEW[REAL ¬ transmittance]; [] ¬ InsertKey[tt, time, localTransmittance, transmittanceName]; }; AssignColor: PUBLIC PROC [tt: TimeTree, time: REAL, color: Triple] ~ { localColor: REF Triple ¬ NEW[Triple ¬ color]; [] ¬ InsertKey[tt, time, localColor, colorName]; }; AssignTransform: PUBLIC PROC [tt: TimeTree, time: REAL, transform: Matrix] ~ { localTransform: Matrix ¬ G3dMatrix.CopyMatrix[transform]; [] ¬ InsertTransform[tt, time, localTransform]; }; <> SetDiffuseInterpolationType: PUBLIC PROC [tt: TimeTree, type: InterpolationType] ~ { SetKeyHeadInterpType[tt, diffuseName, type]; }; SetSpecularInterpolationType: PUBLIC PROC [tt: TimeTree, type: InterpolationType] ~ { SetKeyHeadInterpType[tt, specularName, type]; }; SetMetallicityInterpolationType: PUBLIC PROC [tt: TimeTree, type: InterpolationType] ~ { SetKeyHeadInterpType[tt, metallicityName, type]; }; SetShininessInterpolationType: PUBLIC PROC [tt: TimeTree, type: InterpolationType] ~ { SetKeyHeadInterpType[tt, shininessName, type]; }; SetTransmittanceInterpolationType: PUBLIC PROC [tt: TimeTree, type: InterpolationType] ~ { SetKeyHeadInterpType[tt, transmittanceName, type]; }; SetColorInterpolationType: PUBLIC PROC [tt: TimeTree, type: InterpolationType] ~ { SetKeyHeadInterpType[tt, colorName, type]; }; SetTransformInterpolationType: PUBLIC PROC [tt: TimeTree, type: InterpolationType] ~ { SetTransformInterpType[tt, type]; }; <> CheckEqualMats: PROC [m1, m2: Matrix, error: REAL ¬ 0.0001] ~ { FOR i: INT IN [0 .. 4) DO FOR j: INT IN [0 .. 4) DO delta: REAL ¬ ABS[m1[i][j] - m2[i][j]]; IF delta > error THEN { Error["matrix check error!"]; }; ENDLOOP; ENDLOOP; }; END... <> <> ReportDiffuse: PUBLIC TTReportProc ~ { diffuse: REF REAL ¬ NARROW[strobe]; RealResponse[object, diffuse­, "diffuse"]; }; ReportSpecular: PUBLIC TTReportProc ~ { specular: REF REAL ¬ NARROW[strobe]; RealResponse[object, specular­, "specular"]; }; ReportMetallicity: PUBLIC TTReportProc ~ { metallicity: REF REAL ¬ NARROW[strobe]; RealResponse[object, metallicity­, "metallicity"]; }; ReportShininess: PUBLIC TTReportProc ~ { shininess: REF REAL ¬ NARROW[strobe]; RealResponse[object, shininess­, "shininess"]; }; ReportTransmittance: PUBLIC TTReportProc ~ { transmittance: REF REAL ¬ NARROW[strobe]; RealResponse[object, transmittance­, "transmittance"]; }; ReportColor: PUBLIC TTReportProc ~ { color: REF Triple ¬ NARROW[strobe]; TripleResponse[object, color­, "color"]; }; ReportTransform: PUBLIC TTReportProc ~ { transform: Matrix ¬ NARROW[strobe]; MatrixResponse[object, transform, "transform"]; }; <> DummyObject: TYPE ~ REF DummyObjectRep; DummyObjectRep: TYPE ~ RECORD [ time: REAL ¬ 1.0, name: ROPE ¬ "untitled", real: REAL ¬ 1.0, color: Triple ¬ [0.0, 0.0, 0.0], transform: Matrix ¬ NIL ]; RealResponse: PUBLIC PROC [object: REF ANY, data: REAL, dataName: ROPE] ~ { dummy: DummyObject ¬ NARROW[object]; TerminalIO.PutRope[IO.PutFR["Object %g responding. Set value of %g to %g\n", IO.rope[dummy.name], IO.rope[dataName], IO.real[data]]]; }; TripleResponse: PUBLIC PROC [object: REF ANY, data: Triple, dataName: ROPE] ~ { dummy: DummyObject ¬ NARROW[object]; TerminalIO.PutRope[IO.PutFR["Object %g responding. Set value of %g to %g %g %g\n", IO.rope[dummy.name], IO.rope[dataName], IO.real[data.x], IO.real[data.y], IO.real[data.z]]]; }; MatrixResponse: PUBLIC PROC [object: REF ANY, data: Matrix, dataName: ROPE] ~ { PrintMatrix: PROC [m: Matrix] ~ { FOR i: INT IN [0 .. 4) DO TerminalIO.PutRope[IO.PutFR["[%5g %5g %5g %5g]\n", IO.real[m[i][0]], IO.real[m[i][1]], IO.real[m[i][2]], IO.real[m[i][3]]]]; ENDLOOP; }; dummy: DummyObject ¬ NARROW[object]; TerminalIO.PutRope[IO.PutFR["Object %g responding. Set value of %g to:\n", IO.rope[dummy.name], IO.rope[dataName]]]; PrintMatrix[data]; }; dummyIndex: INT ¬ 0; NewDummy: PROC RETURNS [DummyObject] ~ { d: DummyObject ¬ NEW[DummyObjectRep ¬ []]; d.name ¬ IO.PutFR["Dummy %g", IO.int[dummyIndex]]; dummyIndex ¬ dummyIndex + 1; RETURN[d]; }; PrintObjectName: PUBLIC PROC [ob: REF] ~ { IF ob = NIL THEN { TerminalIO.PutRope["No object"]; } ELSE { d: DummyObject ¬ NARROW[ob]; TerminalIO.PutRope[IO.PutFR["<%g>", IO.rope[d.name]]]; }; }; TreeTestCmd: Commander.CommandProc ~ { PlaceSet: PROC [time, v: REAL] ~ { AssignDiffuse[tt, time, v]; AssignTransform[tt, time, G3dMatrix.MakeTranslate[[v, v, v]]]; }; tt: TimeTree ¬ CreateTimeTree[]; interpType: InterpolationType ¬ smooth; IntializeGraphicsTimeTree[tt]; dummyIndex ¬ 0; PushTimeTree[tt]; [] ¬ SetNodeObject[tt, NewDummy[]]; SetDiffuseInterpolationType[tt, linear]; SetTransformInterpolationType[tt, linear]; PlaceSet[0.0, 0.0]; -- obj 0 PlaceSet[10.0, 10.0]; PushTimeTree[tt]; [] ¬ SetNodeObject[tt, NewDummy[]]; SetDiffuseInterpolationType[tt, linear]; SetTransformInterpolationType[tt, linear]; PlaceSet[0.0, 10.0]; -- obj 1 PlaceSet[10.0, 20.0]; PushTimeTree[tt]; [] ¬ SetNodeObject[tt, NewDummy[]]; SetDiffuseInterpolationType[tt, smooth]; AssignDiffuse[tt, 0.0, 3.0]; AssignDiffuse[tt, 10.0, 13.0]; PopTimeTree[tt]; PushTimeTree[tt]; [] ¬ SetNodeObject[tt, NewDummy[]]; SetDiffuseInterpolationType[tt, smooth]; SetTransformInterpolationType[tt, smooth]; PlaceSet[0.0, 0.0]; -- obj 2 PlaceSet[10.0, 20.0]; PopTimeTree[tt]; PushTimeTree[tt]; [] ¬ SetNodeObject[tt, NewDummy[]]; SetDiffuseInterpolationType[tt, smooth]; SetTransformInterpolationType[tt, smooth]; PlaceSet[0.0, -10.0]; -- obj 3 PlaceSet[10.0, 20.0]; PopTimeTree[tt]; PopTimeTree[tt]; PushTimeTree[tt]; [] ¬ SetNodeObject[tt, NewDummy[]]; SetDiffuseInterpolationType[tt, linear]; SetTransformInterpolationType[tt, linear]; PlaceSet[0.0, -20.0]; -- obj 4 PlaceSet[10.0, 0.0]; PopTimeTree[tt]; StrobeTimeTree[tt, 4.0]; }; <> Commander.Register["treetest", TreeTestCmd, "treetest: test g3dTimeTrees stuff\n"];