AnimationProcs.mesa
Copyright © 1984 by Xerox Corporation. All rights reserved.
Last Edited by: Crow, February 8, 1988 5:03:07 pm PST
DIRECTORY
Atom     USING [ GetPropFromList, PropList, PutPropOnList ],
Real     USING [ LargestNumber ],
RealFns    USING [ Power, SqRt ],
IO      USING [ PutRope, STREAM ],
Convert    USING [ RopeFromReal ],
Rope     USING [ Cat ],
G3dVector    USING [ Add, Length, Sub ],
G3dMatrix    USING [ Transform ],
ThreeDBasics  USING [ Context, SetPosition, ShapeInstance, ShapeProc, Triple ],
SceneUtilities  USING [ FindShape ];
AnimationProcs: CEDAR PROGRAM
IMPORTS Atom, Convert, G3dVector, G3dMatrix, IO, RealFns, Rope, SceneUtilities, ThreeDBasics
~ BEGIN
Basic Types
Context: TYPE ~ ThreeDBasics.Context;
Triple: TYPE ~ ThreeDBasics.Triple;       -- RECORD [ x, y, z: REAL];
ShapeInstance: TYPE ~ ThreeDBasics.ShapeInstance;
LORA: TYPE ~ LIST OF REF ANY;
Renamed Procedures
GetProp: PROC [propList: Atom.PropList, prop: REF ANY] RETURNS [REF ANY] ~
                     Atom.GetPropFromList;
PutProp: PROC [propList: Atom.PropList, prop: REF ANY, val: REF ANY]
   RETURNS
[Atom.PropList] ~ Atom.PutPropOnList;
Globals
radiansToDegrees: REAL ~ 180.0 / 3.1416;
force: REAL ← 2.0;          -- force of gravity (for twiddling)
Utility Procedures
Sqr: PROCEDURE [number: REAL] RETURNS [REAL] ~ INLINE { RETURN[number * number]; };
Procedures for EggRolling Animation
RollShape: ThreeDBasics.ShapeProc ~ {
PROC[context: REF Context, shape: REF ShapeInstance, data: REF ANYNIL] RETURNS[REF ShapeInstance]
Rotates and finds minimum height, returns as new base to rotate on
GetBase: PROC [shape: REF ShapeInstance] RETURNS[base: Triple] ~ {
base.z ← Real.LargestNumber;
FOR i: NAT IN [0..shape.vertex.length) DO
newVtx: Triple ← G3dMatrix.Transform[
[shape.vertex[i].x, shape.vertex[i].y, shape.vertex[i].z], shape.position
];
IF newVtx.z < base.z THEN base ← newVtx;
ENDLOOP;
};
shadowShape: REF ShapeInstance;
frameNumber: NAT ← context.frameNumber;
log: IO.STREAMNARROW[Atom.GetPropFromList[context.props, $Log]];
list: LORANARROW[GetProp[shape.shadingProps, $RollShape] ];
rotationRate: REALNARROW[ list.first, REF REAL ]^;
timePerFrame: REALNARROW[ list.rest.first, REF REAL ]^;
endTime: REALNARROW[ list.rest.rest.first, REF REAL ]^;
ctrOfMass: REF Triple ← IF list.rest.rest.rest # NIL
THEN NARROW[ list.rest.rest.rest.first ] ELSE NIL;
xfmdCtr, lastBase: Triple;
armLength, massLength, dRot, newMassLength: REAL;
IF ctrOfMass = NIL THEN {
compute center of mass based on vertex locations (crude approximation)
ctrOfMass ← NEW[ Triple ← [0.0,0.0,0.0] ];
FOR i: NAT IN [0..shape.vertex.length) DO
weight: REAL ← 4.0 * RealFns.Power[0.5 * (1.0 - shape.vertex[i].z), 3.0]
     * RealFns.SqRt[Sqr[shape.vertex[i].x] + Sqr[shape.vertex[i].y]];
ctrOfMass.x ← ctrOfMass.x + shape.vertex[i].x*weight;
ctrOfMass.y ← ctrOfMass.y + shape.vertex[i].y*weight;
ctrOfMass.z ← ctrOfMass.z + shape.vertex[i].z*weight;
ENDLOOP;
ctrOfMass.x ← ctrOfMass.x / shape.vertex.length;
ctrOfMass.y ← ctrOfMass.y / shape.vertex.length;
ctrOfMass.z ← ctrOfMass.z / shape.vertex.length;
};
xfmdCtr ← G3dMatrix.Transform[ ctrOfMass^, shape.position ]; -- Evaluate new center of mass
armLength ← G3dVector.Length[
G3dVector.Sub[ xfmdCtr, [shape.axisBase.x, shape.axisBase.y, xfmdCtr.z] ]
];
massLength ← G3dVector.Length[ G3dVector.Sub[xfmdCtr, shape.axisBase] ];
Get acceleration due to center of mass and base position
dRot ← (force / massLength) * armLength / massLength;
IF  (((shape.axisEnd.x - shape.axisBase.x) * (xfmdCtr.y - shape.axisBase.y)) -- which way?
 - ((shape.axisEnd.y - shape.axisBase.y) * (xfmdCtr.x - shape.axisBase.x))) < 0
THEN dRot ← -dRot;
rotationRate ← rotationRate + dRot * radiansToDegrees;   -- Adjust rotation rate
IF log # NIL THEN log.PutRope[ Rope.Cat[
" Rate - ", Convert.RopeFromReal[rotationRate],
", delta - ", Convert.RopeFromReal[dRot], "   "
] ];
shape.rotation ← rotationRate * timePerFrame;   -- Rotate shape incrementally
lastBaseshape.axisBase;
lastBase.z ← shape.axisBase.z + shape.location.z;
shape.axisBase ← GetBase[shape];         -- get new base for axis
shape.axisEnd ← G3dVector.Add[ shape.axisEnd, G3dVector.Sub[shape.axisBase, lastBase] ];
shape.location.z ← lastBase.z - shape.axisBase.z;    -- keep above surface
ThreeDBasics.SetPosition[shape: shape, concat: TRUE];   -- concatenate new rotation
shape.vtcesInValid ← TRUE;
Get acceleration due to change in length of moment arm
xfmdCtr ← G3dMatrix.Transform[ ctrOfMass^, shape.position ]; -- Evaluate new center of mass
newMassLength ← G3dVector.Length[ G3dVector.Sub[xfmdCtr, shape.axisBase] ];
rotationRate ← rotationRate * Sqr[massLength / newMassLength];
Damp rotation rate just to get things to stop eventually
rotationRate ← rotationRate
    * RealFns.Power[(endTime - frameNumber * timePerFrame) / endTime, 1.0];
shape.shadingProps ← PutProp[        -- Update parameters on property list
shape.shadingProps, $RollShape,
LIST[ NEW[REAL ← rotationRate], NEW[REAL ← timePerFrame], NEW[REAL ← endTime],
  ctrOfMass ]
];
shadowShape ← SceneUtilities.FindShape[context, "EggShadow"];
shadowShape.location ← G3dVector.Add[ shape.axisBase, [0.0, 0.1, -0.1] ];
ThreeDBasics.SetPosition[shape: shadowShape];   -- set new location under rolling egg
shadowShape.vtcesInValid ← TRUE;
RETURN[shape];
};
END.