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; }; 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; 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] ~ { 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]; RegisterKeyType[tt, colorName, triple, ReportColor]; 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"]; Š G3dTimeTreesImpl.mesa Copyright Σ 1988, 1992 by Xerox Corporation. All rights reserved. Glassner, July 19, 1990 12:21:11 pm PDT Bloomenthal, July 22, 1992 11:28 pm PDT Type Declarations TimeTree Support Node Support Key Support -- linear insertion sort Key Head Support note that CoerceKeyHeads[] may leave a phase error for cyclic paths; I'll fix that later make sure that quaternions take the shortest route between two rotations Matrix Support Procs try to get m1 as close as possible to oldQ force polar decomposition Evaluating Nodes Affecting Nodes Evaluating Trees GetGlobalTransform: PUBLIC PROC [node: Node] RETURNS [Matrix] ~ { GetNodeTransform: PROC [node: Node] RETURNS [Matrix] ~ { IF node.transformKeyHead # NIL THEN RETURN[NARROW[node.transformKeyHead.strobe, Matrix]]; IF node.strobedTransform # NIL THEN RETURN [node.strobedTransform] ELSE RETURN[node.localTransform]; }; GetParentChain: PROC [node: Node, m: Matrix] RETURNS [Matrix] ~ { cnode: Node _ node.parent; composite: Matrix _ G3dMatrix.CopyMatrix[m]; WHILE cnode # NIL DO m2: Matrix _ GetNodeTransform[cnode]; IF m2 # NIL THEN composite _ G3dMatrix.Mul[composite, m2]; -- chain down cnode _ cnode.parent; ENDLOOP; RETURN[composite]; }; RETURN[GetParentChain[node, GetNodeTransform[node]]]; }; IF ckh.flavorInfo.gotReportProc = FALSE THEN -- now in ReportNodes ckh.flavorInfo.reportProc _ LookupReportProc[timeTree, ckh.flavorInfo.name]; IF ckh.flavorInfo.reportProc # NIL THEN { ckh.flavorInfo.reportProc[ckh.strobe, node.object, node.clientData]; }; -- and the distinguished transform ReportTimeTree[timeTree]; -- picked up by the eventual draw or render command Sequence Support Constants Initialization Procs -- the reals -- the triples -- the matrices Report Procs IF shape # NIL THEN shape.matrix _ G3dMatrix.Mul[shape.matrix, transform]; Assignment Procs Interpolation Procs Debugging Debug Stuff Report Procs Test and Debug Start Code Κ)£–"cedarcode" style•NewlineDelimiter ™™Jšœ Οeœ6™BJ™'J™'J˜JšΟk œžœ˜šJ˜—šΡblnœžœž˜Jšžœuž˜Jšžœ ˜J˜—Jšœž˜headšΟl™Jšœžœ˜5Jšœ žœ˜ Jšœžœ˜-Jšœžœ˜-Jšœ žœ˜$Jšœ žœ˜$Jšœžœ˜1Jšœžœ˜/Jšœžœ#˜;Jšœžœ&˜AJšœ žœ˜!Jšœžœ˜2Jšœ žœ˜(Jšœ žœ˜)Jšœ žœ˜#Jšžœžœžœ˜J˜Jšœžœ"˜:Jšœžœ˜1Jšœžœ!˜8Jšœžœ"˜:Jšœ žœ˜)Jšœ žœ˜"Jšœ žœ˜'Jšœ žœ˜$Jšœ žœ˜)Jšœžœ˜.Jšœžœ˜3Jšœ žœ˜)Jšœžœ˜.Jšœ žœ˜*Jšœžœ˜/Jšœžœ˜/Jšœžœ˜4Jšœžœ˜1Jšœžœ ˜6J˜Jšœžœ˜.Jšœžœ˜3Jšœ žœ˜)Jšœžœ˜,Jšœžœ˜,Jšœžœ#˜Jšžœžœžœ˜(J˜J˜—š‘œžœžœ-˜Hš‘œžœ˜J˜š žœžœžœžœžœžœž˜HJšžœ žœ%˜5Jšžœ˜—J˜—Jšœ žœžœ˜Jšœ˜J˜——š  ™ š‘ œžœžœ˜Jšœ˜Jšœžœ˜ Jšœžœ˜ Jšœžœžœ˜Jšœžœ˜Jšœ˜Jšœ žœ˜&Jšžœ žœžœ.˜CJšžœ žœžœ3˜HJ˜!J˜J˜—š‘œžœžœžœ˜Aš‘ œžœ˜!Jšžœžœžœžœ˜#š žœžœžœ!žœžœž˜=šžœžœ˜Jšœ žœ"˜0J˜!J˜—Jšžœ˜—J˜—J˜J˜J˜—š‘œžœžœ˜7Jšœžœ˜Jšœžœ!˜4J˜—J˜š‘œžœžœžœ˜TJšœ žœ˜+šžœ(žœžœ˜4J˜AJ˜—Jšœ>˜>Jšœ9žœ˜?J˜J˜—š ‘œžœžœžœžœ˜Cšžœž˜Jšžœ3˜7—J˜$J˜J˜—š ‘œžœžœžœžœžœ˜FJšžœžœžœžœ˜HJ˜J˜—š ‘œžœžœžœžœ˜UJ˜Jš žœžœžœžœžœ˜'š žœžœžœžœžœž˜;Jšžœ+žœžœžœ ˜JJšžœ˜—Jšžœžœ˜ J˜J˜—š ‘œžœžœ žœ"žœžœ˜Ššžœžœžœ˜#J˜4Jšžœžœžœžœ˜šžœžœžœ˜J˜&Jšœžœ˜#J˜—šžœžœžœ˜J˜&Jšœžœ˜#J˜—J˜—J˜J˜—š‘œžœžœžœ-˜qšžœžœžœ˜#J˜4Jšžœžœžœ+žœ˜@J˜-J˜—J˜J˜—š ‘œžœžœžœžœ˜]Jšœžœ˜Jšœžœ˜(J˜šžœžœž˜J˜,J˜,J˜(Jšžœ!˜(—Jšœžœ#˜FJ˜J˜—š ‘œžœžœ-žœžœ-˜œšœžœ˜/JšœW˜W—J˜J˜—š‘œžœžœA˜dšžœžœžœ˜#šžœ(žœžœ˜3J˜A—J˜OJ˜—J˜J˜—J˜J˜š‘œžœ!˜9šžœ žœ˜šžœ˜Jšœžœ ˜(Jšžœ˜J˜—šžœ˜š‘ œžœžœ˜Jšœžœ#˜9J˜Jš žœžœžœ žœžœ˜8J˜Jš žœžœžœžœžœ˜GJ˜J˜—š‘ œžœžœ˜J˜J˜—JšΟc™Jšœ žœžœ˜šžœžœžœž˜&šžœ žœžœ žœ˜9šžœ˜ Jšžœ˜Jšžœ˜—Jšœ žœ˜J˜—Jšžœ˜—Jšžœžœ žœ-˜AJ˜——J˜——š ™š‘œžœ˜7JšœX™Xš‘ œžœ˜J™Hšžœžœžœž˜-Jšœžœ˜.Jšœžœ˜0Jšžœ'žœ"˜OJšžœ'žœ"˜OJšžœ'žœ"˜OJšžœ˜—J˜—š‘œžœ˜#Jšœžœ˜Jšœžœ˜Jšœžœ˜Jšœžœ˜Jšœžœ˜Jšœžœ˜9šžœžœžœž˜+J˜šžœžœž˜%šœ˜Jšœžœ˜šžœžœ˜Jšœžœ)˜F—Jšœ!žœ˜(J˜J˜—J˜Jšžœ&˜-—J˜J˜J˜%J˜$J˜@J˜"J˜#J˜!J˜)J˜+J˜'J˜5J˜/J˜0J˜.J˜(J˜%Jšœžœ˜J˜Jšœ’ ˜'J˜——š ™š ‘œžœžœžœžœ˜JJš žœžœžœžœžœ˜)š žœžœžœ!žœžœž˜=Jšžœ+žœžœžœ ˜JJšžœ˜—Jšžœžœ˜ J˜J˜—š‘ œžœžœžœžœžœžœ˜IJ˜.šžœ ž˜Jšžœžœ˜Jšžœžœžœ˜—J˜J˜—š‘ œžœžœ9˜SJšžœžœžœžœ˜+šžœž˜˜%Jšœžœ ˜<—˜&Jšœžœ(˜<—Jšžœ˜—JšœΟtœ£ž£˜#J˜J˜—š ‘œžœžœžœžœžœ˜CJšžœžœžœžœ˜#š žœžœžœ!žœžœž˜=J˜ Jšžœ˜—J˜Jšœžœ˜&Jš žœžœžœžœžœ˜>J˜J˜J˜—š‘œžœžœ˜NJšžœžœžœžœ˜1Jšžœžœ˜?J˜J˜Jšžœ˜ J˜J˜—š‘ œžœžœžœ˜YJšœ žœžœ)˜=Jšœžœ˜%Jš žœžœžœžœžœ˜˜>Jšœ:˜:JšœB˜B—šœ™Jšœ4˜4—šœ™Jšœ'˜'—Jšœ˜——š  ™ š‘ œžœ˜&Jšœ žœžœžœ ˜#Jšœžœ ˜Jšžœ žœžœ'˜:J˜J˜—š‘œžœ˜'Jšœ žœžœžœ ˜$Jšœžœ ˜Jšžœ žœžœ)˜J˜J˜—š‘œžœ˜,Jšœžœžœžœ ˜)Jšœžœ ˜Jšžœ žœžœ3˜FJ˜J˜—š‘ œžœ˜$Jšœžœ žœ ˜#Jšœžœ ˜Jšžœ žœžœ8˜KJ˜J˜—š‘œžœ˜(Jšœžœ ˜#Jšœžœ ˜Jšžœ žœžœ7™Jšžœ žœžœ˜šžœ ž˜Jšžœ/˜3Jšžœ%˜)—J˜—J˜——š ™š ‘ œžœžœžœ žœ˜HJš œžœžœžœžœ ˜-J˜4J˜J˜—š ‘œžœžœžœ žœ˜JJš œžœžœžœžœ ˜/J˜6J˜J˜—š ‘œžœžœžœžœ˜PJš œžœžœžœžœ˜5J˜˜SJš žœžœžœžœžœ˜\—J˜J˜—š ‘œžœžœ žœžœžœ˜Oš‘ œžœ˜!šžœžœžœ ž˜šœžœ˜2Jšžœžœžœžœ˜J—Jšžœ˜—J˜—Jšœžœ ˜$šœžœ6˜KJšžœžœ˜)—J˜J˜J˜—J˜Jšœ žœ˜J˜š‘œžœžœ˜(Jšœžœ˜*Jšœ žœžœ˜2J˜Jšžœ˜ J˜J˜—š‘œžœžœžœ˜*šžœž˜ šžœ˜J˜ J˜—šžœ˜Jšœžœ˜Jšœžœžœ˜6J˜——J˜—J˜šΠbn œ˜&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˜ J˜Jšœ˜—Jšœ˜—šœ˜J˜#J˜(J˜*J˜ J˜—Jšœ˜J˜—šœ˜J˜—Jšœ˜———š  ™ ˜JšœS˜SJ˜———…—ŒφΎ#