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
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
Type Declarations
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;
TimeTree Support
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];
};
Node Support
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];
};
Key Support
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];
};
};
Key Head Support
ComputeKeyHeadAccelerators: PROC [keyHead: KeyHead] ~ {
note that CoerceKeyHeads[] may leave a phase error for cyclic paths; I'll fix that later
CoerceKeyHead: PROC ~ {
make sure that quaternions take the shortest route between two rotations
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;
};
Matrix Support Procs
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 {
try to get m1 as close as possible to oldQ
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 {
force polar decomposition
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
};
Evaluating Nodes
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];
};
Affecting Nodes
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;
};
Evaluating Trees
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] ~ {
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]]];
};
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];
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];
};
};
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[timeTree];  -- picked up by the eventual draw or render command
};
ReportTimeTree: PUBLIC PROC [timeTree: TimeTree] ~ {
IF timeTree = NIL THEN RETURN;
PropagateStrobedMatrices[timeTree.root];
ReportNodes[timeTree];
};
Sequence Support
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;
};
Constants
diffuseName:   ROPE = "diffuseReflectivity";
specularName:  ROPE = "specularReflectivity";
metallicityName:  ROPE = "metallicity";
shininessName:  ROPE = "shininess";
transmittanceName: ROPE = "transmittance";
colorName:   ROPE = "color";
transformName:  ROPE = "transform";
Initialization Procs
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];
};
Report Procs
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 shape.matrix ← G3dMatrix.Mul[shape.matrix, transform];
IF shape # NIL THEN {
IF transform # NIL
THEN shape.matrix ¬ G3dMatrix.CopyMatrix[transform]
ELSE shape.matrix ¬ G3dMatrix.Identity[];
};
};
Assignment Procs
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];
};
Interpolation Procs
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];
};
Debugging
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...
Debug Stuff
Report Procs
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"];
};
Test and Debug
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];
};
Start Code
Commander.Register["treetest", TreeTestCmd, "treetest: test g3dTimeTrees stuff\n"];