File: SVTransformsImpl.mesa
Last edited by Bier on January 4, 1985 6:07:07 pm PST
Copyright © 1984 by Xerox Corporation. All rights reserved.
Contents: Procedures for Scaling, Translating, and Rotating assemblies with respect to an arbitrary coordinate system
DIRECTORY
CoordSys,
Matrix3d,
Rope,
SV3d,
SVError,
SVModelTypes,
SVSceneTypes,
SVTransforms,
SVVector3d;
SVTransformsImpl: PROGRAM
IMPORTS CoordSys, Matrix3d, Rope, SVError, SVVector3d
EXPORTS SVTransforms =
BEGIN
Assembly: TYPE = SVSceneTypes.Assembly;
AssemblyList: TYPE = SVSceneTypes.AssemblyList;
Camera: TYPE = SVModelTypes.Camera;
CoordSystem: TYPE = SVModelTypes.CoordSystem;
CoordSysList: TYPE = SVModelTypes.CoordSysList;
Matrix4by4: TYPE = SV3d.Matrix4by4;
Point3d: TYPE = SV3d.Point3d;
Scene: TYPE = SVSceneTypes.Scene;
Shape: TYPE = SVSceneTypes.Shape;
Vector: TYPE = SV3d.Vector;
IncTransf:
PUBLIC
PROC [
C: CoordSystem,
D: CoordSystem,
M: Matrix4by4, DFixed:
BOOL ←
FALSE] = {
Incrementally transform C with respect to D by M. If D is a descendent of C, this could result in D moving as well. If this is not desired, set DFixed to TRUE and IncTransf makes sure that D doesn't move.
P: CoordSystem;
IF C.parent = NIL THEN ERROR; -- cannot translate WORLD for now
IF D = NIL THEN ERROR;
P ← C.parent;
IF MatrixHasScaling[
M]
THEN {
SVError.Append["SVTransform.IncTransf got illegal matrix. Ignored."];
SVError.Blink[];
RETURN};
SELECT TRUE FROM
D.parent =
NIL => {
-- D = WORLD
CWORLD, WORLDP, PWORLD: Matrix4by4;
CWORLD ← CoordSys.FindInTermsOfWorld[C];
PWORLD ← CoordSys.FindInTermsOfWorld[P];
WORLDP ← Matrix3d.Inverse[PWORLD];
C.mat ← Matrix3d.MatMult[Matrix3d.MatMult[WORLDP, M], CWORLD];
};
C =
D => {
-- local tranformation
C.mat ← Matrix3d.MatMult[C.mat, M];
};
D =
P => {
-- transformation relative to parent
C.mat ← Matrix3d.MatMult[M, C.mat];
};
ENDCASE => {
-- general transformation
CWORLD, DWORLD, CD, WORLDP, PWORLD: Matrix4by4;
CWORLD ← CoordSys.FindInTermsOfWorld[C];
DWORLD ← CoordSys.FindInTermsOfWorld[D]; -- we will preserve this relationship if DFixed
PWORLD ← CoordSys.FindInTermsOfWorld[P];
WORLDP ← Matrix3d.Inverse[PWORLD];
CD ← CoordSys.FindAInTermsOfB[C, D];
C.mat ← Matrix3d.MatMult[Matrix3d.MatMult[Matrix3d.MatMult[WORLDP, DWORLD], M], CD];
IF DFixed
THEN {
Make sure that D hasn't moved.
DPWORLD, DDP: Matrix4by4;
DPWORLD ← CoordSys.FindInTermsOfWorld[D.parent];
DDP ← Matrix3d.WorldToLocal[DPWORLD, DWORLD];
D.mat ← DDP;
};
};
};
IncTransfMatrix:
PUBLIC
PROC [
C: CoordSystem,
D
WORLD: Matrix4by4,
M: Matrix4by4] = {
P: CoordSystem;
CWORLD, CD, WORLDP, PWORLD: Matrix4by4;
IF C.parent = NIL THEN ERROR; -- cannot translate WORLD for now
P ← C.parent;
IF MatrixHasScaling[
M]
THEN {
SVError.Append["SVTransform.IncTransfMatrix got illegal matrix. Ignored."];
SVError.Blink[];
RETURN};
CWORLD ← CoordSys.FindInTermsOfWorld[C];
PWORLD ← CoordSys.FindInTermsOfWorld[P];
WORLDP ← Matrix3d.Inverse[PWORLD];
CD ← Matrix3d.WorldToLocal[DWORLD, CWORLD];
C.mat ← Matrix3d.MatMult[Matrix3d.MatMult[Matrix3d.MatMult[WORLDP, DWORLD], M], CD];
};
PlaneOriginRotate:
PUBLIC
PROC [
C: CoordSystem,
D: CoordSystem, degrees:
REAL, plane:
NAT] = {
Rotates C about an axis parallel to the plane axis of D, passing thru the origin of C.
M, PivotWORLD, CWORLD, DWORLD: Matrix4by4;
origin: Point3d;
xAxis, yAxis, zAxis: Vector;
CWORLD ← CoordSys.FindInTermsOfWorld[C];
DWORLD ← CoordSys.FindInTermsOfWorld[D];
origin ← Matrix3d.OriginOfMatrix[CWORLD];
xAxis ← Matrix3d.XAxisOfMatrix[DWORLD];
yAxis ← Matrix3d.YAxisOfMatrix[DWORLD];
zAxis ← Matrix3d.ZAxisOfMatrix[DWORLD];
PivotWORLD ← Matrix3d.MakeMatFromAxes[xAxis, yAxis, zAxis, origin];
SELECT plane FROM
1 => M ← Matrix3d.MakeRotateXMat[degrees];
2 => M ← Matrix3d.MakeRotateYMat[degrees];
3 => M ← Matrix3d.MakeRotateZMat[degrees];
ENDCASE => ERROR;
IncTransfMatrix[C, PivotWORLD, M];
};
AbsTransf:
PUBLIC
PROC [
C: CoordSystem,
D: CoordSystem,
N: Matrix4by4, DFixed:
BOOL ←
FALSE] = {
Absolutely transform C with respect to D by M. If D is a descendent of C, this could result in D moving as well. If this is not desired, set DFixed to TRUE and AbsTransf makes sure that D doesn't move.
P: CoordSystem;
IF Rope.Equal[C.name, "SCREEN", TRUE] THEN {C.mat ← N; RETURN};
IF C.parent = NIL THEN ERROR; -- cannot transform WORLD.
IF D = NIL THEN ERROR;
P ← C.parent;
IF MatrixHasScaling[
N]
THEN {
SVError.Append["SVTransform.AbsTransf got illegal matrix. Ignored."];
SVError.Blink[];
RETURN};
SELECT TRUE FROM
D.parent =
NIL => {
-- D = WORLD
WORLDP, PWORLD: Matrix4by4;
PWORLD ← CoordSys.FindInTermsOfWorld[P];
WORLDP ← Matrix3d.Inverse[PWORLD];
C.mat ← Matrix3d.MatMult[WORLDP, N];
};
C =
D => {
-- local tranformation
C.mat ← Matrix3d.MatMult[C.mat, N];
};
D =
P => {
-- transformation relative to parent
C.mat ← N;
};
ENDCASE => {
-- general transformation
DWORLD, WORLDP, PWORLD: Matrix4by4;
DWORLD ← CoordSys.FindInTermsOfWorld[D];
PWORLD ← CoordSys.FindInTermsOfWorld[P];
WORLDP ← Matrix3d.Inverse[PWORLD];
C.mat ← Matrix3d.MatMult[Matrix3d.MatMult[WORLDP, DWORLD], N];
IF DFixed
THEN {
Now make sure that D hasn't moved.
DPWORLD, DDP: Matrix4by4;
DPWORLD ← CoordSys.FindInTermsOfWorld[D.parent];
DDP ← Matrix3d.WorldToLocal[DPWORLD, DWORLD];
D.mat ← DDP;
};
};
};
TugTransf:
PUBLIC
PROC [
C: CoordSystem, tugBoat: CoordSystem,
D: CoordSystem,
N: Matrix4by4] = {
F: Matrix4by4 ← CoordSys.FindAInTermsOfB[C, tugBoat];
AbsTransf[tugBoat, D, N];
AbsTransf[C, tugBoat, F];
};
TugTransfLeaveTug:
PUBLIC PROC [
C: CoordSystem, tugBoat: CoordSystem,
D: CoordSystem,
N: Matrix4by4] = {
Like TugTransf, but returns the Tugboat to its original position.
F: Matrix4by4;
saveTugMat: Matrix4by4 ← tugBoat.mat;
F ← CoordSys.FindAInTermsOfB[C, tugBoat];
AbsTransf[tugBoat, D, N];
AbsTransf[C, tugBoat, F];
tugBoat.mat ← saveTugMat;
};
A set of routines which call IncTransf
Translate:
PUBLIC
PROC [
C: CoordSystem,
D: CoordSystem, tx, ty, tz:
REAL, DFixed:
BOOL ←
FALSE] = {
M: Matrix4by4;
M ← Matrix3d.MakeTranslateMat[tx, ty, tz];
IncTransf[C, D, M, DFixed];
};
XRotate:
PUBLIC
PROC [
C: CoordSystem,
D: CoordSystem, degrees:
REAL, DFixed:
BOOL ←
FALSE] = {
Simple rotate C's coordsys wrt D by degrees about the x axis
M: Matrix4by4;
M ← Matrix3d.MakeRotateXMat[degrees];
IncTransf[C, D, M, DFixed];
};
YRotate:
PUBLIC
PROC [
C: CoordSystem,
D: CoordSystem, degrees:
REAL, DFixed:
BOOL ←
FALSE] = {
Simple rotate C's coordsys wrt D by degrees about the y axis
M: Matrix4by4;
M ← Matrix3d.MakeRotateYMat[degrees];
IncTransf[C, D, M, DFixed];
};
ZRotate:
PUBLIC
PROC [
C: CoordSystem,
D: CoordSystem, degrees:
REAL, DFixed:
BOOL ←
FALSE] = {
Simple rotate C's coordsys wrt D by degrees about the z axis
M: Matrix4by4;
M ← Matrix3d.MakeRotateZMat[degrees];
IncTransf[C, D, M, DFixed];
};
Here is an example of the way these operations used to be performed. This is fairly fast because it uses Matrix3d.RotateAboutXAxis instead of Matrix3d.MatMult, but the IncTransf, AbsTransf, TugTransf pathway is much cleaner.
XRotateAwrtB: PUBLIC PROC [a: CoordSystem, b: CoordSystem, degrees: REAL] = {
aWorld, bWorld, aInTermsOfb, newAWorld, withRespectToWorld, aInTermsOfWRT: Matrix4by4;
parent: CoordSystem;
IF b = NIL THEN ERROR
ELSE bWorld ← CoordSys.FindInTermsOfWorld[b];
aWorld ← CoordSys.FindInTermsOfWorld[a];
aInTermsOfb ← IF a = b THEN Matrix3d.Identity[] ELSE Matrix3d.WorldToLocal[bWorld,aWorld];
aInTermsOfb ← Matrix3d.RotateAboutXAxis[aInTermsOfb, degrees]; -- must be a global translate.
newAWorld ← Matrix3d.MatMult[bWorld, aInTermsOfb];
IF a.parent = NIL THEN ERROR; -- cannot translate WORLD for now
parent ← a.parent;
withRespectToWorld ← CoordSys.FindInTermsOfWorld[parent];
aInTermsOfWRT ← Matrix3d.WorldToLocal[withRespectToWorld, newAWorld];
a.mat ← aInTermsOfWRT;
};
ScaleAwrtB: PRIVATE PROC [a: CoordSystem, b: CoordSystem, sx, sy, sz: REAL] = {
Because scaling is only allowed at primitive assemblies, we define the arbitrary scaling of an assembly with respect to a coordinate system as follows: If the assembly is a primitive, change its scalar vector
aWorld, bWorld, aInTermsOfb, newAWorld, withRespectToWorld, aInTermsOfWRT: Matrix4by4;
parent: CoordSystem;
IF b = NIL THEN ERROR
ELSE bWorld ← CoordSys.FindInTermsOfWorld[b];
aWorld ← CoordSys.FindInTermsOfWorld[a];
aInTermsOfb ← IF a = b THEN Matrix3d.Identity[] ELSE Matrix3d.WorldToLocal[bWorld,aWorld];
aInTermsOfb ← Matrix3d.Scale[aInTermsOfb, sx, sy, sz]; -- must be a global translate.
newAWorld ← Matrix3d.MatMult[bWorld, aInTermsOfb];
IF a.parent = NIL THEN ERROR; -- cannot translate WORLD for now
parent ← a.parent;
withRespectToWorld ← CoordSys.FindInTermsOfWorld[parent];
aInTermsOfWRT ← Matrix3d.WorldToLocal[withRespectToWorld, newAWorld];
a.mat ← aInTermsOfWRT;
};
A set of routines which call AbsTransf
AbsTranslateOnly:
PUBLIC
PROC [C: CoordSystem, D: CoordSystem, v
D: Point3d] = {
Places C so that the rotation from C to D remains as it is, but the translation becomes vD.
P: CoordSystem; -- parent of C
vP: Vector;
IF Rope.Equal[
C.name, "SCREEN",
TRUE]
THEN {
C.mat[1][4] ← vD[1];
C.mat[2][4] ← vD[2];
C.mat[3][4] ← vD[3];
RETURN};
IF C.parent = NIL THEN ERROR; -- cannot transform WORLD.
IF D = NIL THEN ERROR;
P ← C.parent;
SELECT TRUE FROM
D.parent =
NIL => {
-- D = WORLD
WORLDP, PWORLD: Matrix4by4;
PWORLD ← CoordSys.FindInTermsOfWorld[P];
WORLDP ← Matrix3d.Inverse[PWORLD];
vP ← Matrix3d.Update[WORLDP, vD];
C.mat[1][4] ← vP[1];
C.mat[2][4] ← vP[2];
C.mat[3][4] ← vP[3];
};
C =
D => {
-- local tranformation
C.mat ← Matrix3d.LocalTranslate[C.mat, vD[1], vD[2], vD[3]];
};
D =
P => {
-- transformation relative to parent
C.mat[1][4] ← vD[1];
C.mat[2][4] ← vD[2];
C.mat[3][4] ← vD[3];
};
ENDCASE => {
-- general transformation
DWORLD, WORLDP, PWORLD: Matrix4by4;
DWORLD ← CoordSys.FindInTermsOfWorld[D];
PWORLD ← CoordSys.FindInTermsOfWorld[P];
WORLDP ← Matrix3d.Inverse[PWORLD];
vP ← Matrix3d.Update[WORLDP, Matrix3d.Update[DWORLD, vD]];
C.mat[1][4] ← vP[1];
C.mat[2][4] ← vP[2];
C.mat[3][4] ← vP[3];
};
};
Align:
PUBLIC
PROC [
C: CoordSystem,
D: CoordSystem] = {
Find CD. Compute the magnitude of its axes. Make CD have axes of the same magnitude, aligned with respect to the nearest axes of D.
CD: Matrix4by4;
xAxisChoices, zAxisChoices: ARRAY[1..3] OF NAT;
xAxis, zAxis, newXAxis, newYAxis, newZAxis: Vector;
xNegArray, zNegArray: ARRAY[1..3] OF BOOL;
origin: Point3d;
CD ← CoordSys.FindAInTermsOfB[C, D];
xAxis ← Matrix3d.XAxisOfMatrix[CD];
zAxis ← Matrix3d.ZAxisOfMatrix[CD];
origin ← Matrix3d.OriginOfMatrix[CD];
[xAxisChoices, xNegArray] ← IndicesOfMax[xAxis]; -- Find largest component
This direction is now taken. z axis must find another.
[zAxisChoices, zNegArray] ← IndicesOfMax[zAxis];
IF zAxisChoices[1] = xAxisChoices[1] THEN zAxisChoices[1] ← zAxisChoices[2];
zAxis gets second choice if xAxis has its first choice
newXAxis ← [0,0,0];
newXAxis[xAxisChoices[1]] ← SVVector3d.Magnitude[xAxis];
IF xNegArray[xAxisChoices[1]]
THEN
newXAxis[xAxisChoices[1]] ← -newXAxis[xAxisChoices[1]];
newZAxis ← [0,0,0];
newZAxis[zAxisChoices[1]] ← SVVector3d.Magnitude[zAxis];
IF zNegArray[zAxisChoices[1]]
THEN
newZAxis[zAxisChoices[1]] ← -newZAxis[zAxisChoices[1]];
newYAxis ← SVVector3d.CrossProduct[newZAxis, newXAxis];
CD ← Matrix3d.MakeMatFromAxes[newXAxis, newYAxis, newZAxis, origin];
AbsTransf[C, D, CD];
}; -- end of Align
IndicesOfMax:
PRIVATE
PROC [v: Vector]
RETURNS [indices:
ARRAY[1..3]
OF
NAT, negArray:
ARRAY[1..3]
OF
BOOL] = {
Bubble sort
temp: REAL;
tempIndex: NAT;
IF v[1] < 0 THEN {negArray[1] ← TRUE; v[1] ← -v[1]}
ELSE negArray[1] ← FALSE;
IF v[2] < 0 THEN {negArray[2] ← TRUE; v[2] ← -v[2]}
ELSE negArray[2] ← FALSE;
IF v[3] < 0 THEN {negArray[3] ← TRUE; v[3] ← -v[3]}
ELSE negArray[3] ← FALSE;
indices ← [1,2,3];
IF v[1] < v[2]
THEN {
temp ← v[1]; v[1] ← v[2]; v[2] ← temp;
tempIndex ← indices[1]; indices[1] ← indices[2]; indices[2] ← tempIndex};
IF v[2] < v[3]
THEN {
temp ← v[2]; v[2] ← v[3]; v[3] ← temp;
tempIndex ← indices[2]; indices[2] ← indices[3]; indices[3] ← tempIndex};
IF v[1] < v[2]
THEN {
temp ← v[1]; v[1] ← v[2]; v[2] ← temp;
tempIndex ← indices[1]; indices[1] ← indices[2]; indices[2] ← tempIndex};
};
Abut:
PUBLIC
PROC [C: CoordSystem, D: CoordSystem] = {
Leaving the rotation of C as it is, put C's origin coincident with the origin of D.
AbsTranslateOnly[C, D, [0,0,0]];
};
AbutX:
PUBLIC
PROC [C: CoordSystem, D: CoordSystem] = {
Leave the rotation of C as it is. Put C's origin in the x = 0 plane of D.
vD: Vector;
vD ← CoordSys.FindTranslationOfAinTermsOfB[C, D];
AbsTranslateOnly[C, D, [0, vD[2], vD[3]]];
};
AbutY:
PUBLIC
PROC [C: CoordSystem, D: CoordSystem] = {
Leave the rotation of C as it is. Put C's origin in the y = 0 plane of D.
vD: Vector;
vD ← CoordSys.FindTranslationOfAinTermsOfB[C, D];
AbsTranslateOnly[C, D, [vD[1], 0, vD[3]]];
};
AbutZ:
PUBLIC
PROC [C: CoordSystem, D: CoordSystem] = {
Leave the rotation of C as it is. Put C's origin in the z = 0 plane of D.
vD: Vector;
vD ← CoordSys.FindTranslationOfAinTermsOfB[C, D];
AbsTranslateOnly[C, D, [vD[1], vD[2], 0]];
};
NormalizeRot:
PUBLIC PROC [
C: CoordSystem,
D: CoordSystem] = {
Leave translation between C and D as is. The axes of C become parallel to the axes of D.
oldMat, newMat: Matrix4by4;
oldMat ← CoordSys.FindAInTermsOfB[a: C, b: D];
newMat ← Matrix3d.NormalizeNoRotation[oldMat];
AbsTransf[C, D, newMat];
};
Normalize:
PUBLIC PROC [
C: CoordSystem,
D: CoordSystem] = {
C becomes identical to D.
AbsTransf[C, D, Matrix3d.Identity[]];
};
A set of routines which specialize TugTransf
NormalizeRotTug:
PUBLIC
PROC [
C: CoordSystem, tugBoat: CoordSystem,
D: CoordSystem] = {
F: Matrix4by4 ← CoordSys.FindAInTermsOfB[C, tugBoat];
NormalizeRot[tugBoat, D];
AbsTransf[C, tugBoat, F, TRUE];
};
NormalizeTug:
PUBLIC
PROC [
C: CoordSystem, tugBoat: CoordSystem,
D: CoordSystem] = {
F: Matrix4by4 ← CoordSys.FindAInTermsOfB[C, tugBoat];
Normalize[tugBoat, D];
AbsTransf[C, tugBoat, F, TRUE];
};
AlignTug:
PUBLIC
PROC [C: CoordSystem, tugBoat: CoordSystem, D: CoordSystem] = {
F: Matrix4by4 ← CoordSys.FindAInTermsOfB[C, tugBoat];
Align[tugBoat, D];
AbsTransf[C, tugBoat, F, TRUE];
};
AbutTug:
PUBLIC
PROC [C: CoordSystem, tugBoat: CoordSystem, D: CoordSystem] = {
F: Matrix4by4 ← CoordSys.FindAInTermsOfB[C, tugBoat];
Abut[tugBoat, D];
AbsTransf[C, tugBoat, F, TRUE];
};
AbutXTug:
PUBLIC
PROC [
C: CoordSystem, tugBoat: CoordSystem,
D: CoordSystem] = {
F: Matrix4by4 ← CoordSys.FindAInTermsOfB[C, tugBoat];
AbutX[tugBoat, D];
AbsTransf[C, tugBoat, F, TRUE];
};
AbutYTug:
PUBLIC
PROC [
C: CoordSystem, tugBoat: CoordSystem,
D: CoordSystem] = {
F: Matrix4by4 ← CoordSys.FindAInTermsOfB[C, tugBoat];
AbutY[tugBoat, D];
AbsTransf[C, tugBoat, F, TRUE];
};
AbutZTug:
PUBLIC
PROC [
C: CoordSystem, tugBoat: CoordSystem,
D: CoordSystem] = {
F: Matrix4by4 ← CoordSys.FindAInTermsOfB[C, tugBoat];
AbutZ[tugBoat, D];
AbsTransf[C, tugBoat, F, TRUE];
};
Routines for updating cached matrices:
TellAboutCameraAndWorld1: PUBLIC PROC [a: Assembly, camera: Camera, scene: Scene] = {
Flatten the tree to get the leaf assemblies then update their cached values.
camera.coordSys.wrtWorld ← CoordSys.FindInTermsOfWorld[camera.coordSys];
FOR list: LIST OF Assembly ← DisplayList3d.ListOfPrimAssemblies[a, scene], list.rest
UNTIL list = NIL DO
list.first.coordSys.wrtCamera ← Matrix3d.LocalScale[CoordSys.FindInTermsOfCamera[list.first.coordSys, camera.coordSys], list.first.scalars[1], list.first.scalars[2], list.first.scalars[3]];
list.first.coordSys.wrtWorld ← Matrix3d.LocalScale[CoordSys.FindInTermsOfWorld[list.first.coordSys], list.first.scalars[1], list.first.scalars[2], list.first.scalars[3]];
list.first.coordSys.cameraWRTlocal ← Matrix3d.Inverse[list.first.coordSys.wrtCamera];
list.first.coordSys.worldWRTlocal ← Matrix3d.Inverse[list.first.coordSys.wrtWorld];
ENDLOOP;
}; -- end of TellAboutCameraAndWorld1
DirtyAssembly:
PUBLIC
PROC [a: Assembly] = {
This assembly has moved. Mark its coordinate system and those of its children as dirty.
WITH a.shape
SELECT
FROM
shape: Shape => {
a.coordSys.dirty ← TRUE;
};
alist: AssemblyList => {
a.coordSys.dirty ← TRUE;
DirtyAssemblyList[alist];
};
ENDCASE => ERROR;
}; -- end of DirtyAssembly
DirtyAssemblyList:
PRIVATE
PROC [alist: AssemblyList] = {
FOR list:
LIST
OF Assembly ← alist.list, list.rest
UNTIL list =
NIL
DO
DirtyAssembly[list.first];
ENDLOOP;
};
TidyUpCoordSysTree:
PUBLIC
PROC [root: CoordSystem] = {
If a component is almost a multiple of .5, make it exactly so.
parent: CoordSystem ← root.parent;
IF root.scalarsOnly
THEN {
IF root.children # NIL THEN ERROR; -- scalars-only coordinate systems must be leaves.
root.scalars ← Matrix3d.TidyUpVector[root.scalars];
}
ELSE {
root.mat ← Matrix3d.TidyUpMatrix[root.mat];
IF root.children #
NIL
THEN {
-- Process the children as well
FOR list: CoordSysList ← root.children, list.rest
UNTIL list =
NIL
DO
TidyUpCoordSysTree[list.first];
ENDLOOP;
};
};
}; -- end of TidyUpCoordSysTree
TellAboutCameraAndWorld:
PUBLIC
PROC [root: CoordSystem, camera: Camera, scene: Scene] = {
Tree walk through the tree and concatenate all matrices (will later concatenate matrices as it goes). This computes values for intermediate matrices as well as primitive matrices. Can be started using any assembly as root.
camera.coordSys.wrtWorld ← CoordSys.FindInTermsOfWorld[camera.coordSys];
TellAboutCameraAndWorldAux[root, camera, scene];
};
TellAboutCameraAndWorldAux:
PRIVATE
PROC [root: CoordSystem, camera: Camera, scene: Scene] = {
parent: CoordSystem ← root.parent;
IF root.scalarsOnly
THEN {
IF root.children #
NIL
THEN
ERROR;
-- scalars only coordinate systems must be leaves.
Our matrices result from scaling our parent's matrices.
root.wrtCamera ← Matrix3d.LocalScale[CoordSys.FindInTermsOfCamera[parent, camera.coordSys], root.scalars[1], root.scalars[2], root.scalars[3]];
root.wrtWorld ← Matrix3d.LocalScale[CoordSys.FindInTermsOfWorld[parent], root.scalars[1], root.scalars[2], root.scalars[3]];
root.cameraWRTlocal ← Matrix3d.Inverse[root.wrtCamera];
root.worldWRTlocal ← Matrix3d.Inverse[root.wrtWorld];
}
ELSE {
root.wrtCamera ← CoordSys.FindInTermsOfCamera[root, camera.coordSys];
root.wrtWorld ← CoordSys.FindInTermsOfWorld[root];
root.cameraWRTlocal ← Matrix3d.Inverse[root.wrtCamera];
root.worldWRTlocal ← Matrix3d.Inverse[root.wrtWorld];
IF root.children #
NIL
THEN {
-- Process the children as well
FOR list: CoordSysList ← root.children, list.rest
UNTIL list =
NIL
DO
TellAboutCameraAndWorldAux[list.first, camera, scene];
ENDLOOP;
};
};
}; -- end of TellAboutCameraAndWorldAux
TellAboutCameraAndWorldInList:
PRIVATE
PROC [clist: CoordSysList, camera: Camera, scene: Scene] = {
FOR list: CoordSysList ← clist, list.rest
UNTIL list =
NIL
DO
TellAboutCameraAndWorld[list.first, camera, scene];
ENDLOOP;
};
TellAboutParent:
PUBLIC
PROC [cs: CoordSystem] = {
Updates the wrtWorld, and wrtCamera fields of cs by premultiplying cs.mat by cs.parent.wrtWorld and cs.parent.wrtCamera respectively
cs.wrtWorld ← Matrix3d.MatMult[cs.parent.wrtWorld, cs.mat];
cs.wrtCamera ← Matrix3d.MatMult[cs.parent.wrtCamera, cs.mat];
cs.worldWRTlocal ← Matrix3d.Inverse[cs.wrtWorld];
cs.cameraWRTlocal ← Matrix3d.Inverse[cs.wrtCamera];
};
Scaling operations (IncTransf derivatives but more complicated).
There are five ways to scale a cluster assembly
1) (ScalePrimitives) Scale each primitive in place, evenly or differentially.
2) (ScaleEven) If sx = sy = sz. Scale primitives in place and then translate all intermediate assemblies so the cluster assembly scales evenly as a unit
3) (ScaleNoShear) IF sx # sy or sy # sz. Scale the primitives evenly so that their dimension in the direction of scaling increases or decreases to the same quantity it would have reached by method 3. This preserves shape of primitives. Global shape is not preserved (but almost).
4) (ScaleEvenChildren) Perform a ScaleEven on each of the children of the cluster assembly but leave their relative translations fixed. This is equivalent to performing a ScaleEven (child, child's cs, scalar) on each child individually.
5) (ScaleDistancesChildren) Doesn't change the size or shape of any of the children of assembly. Simply translates all of the children as they would be translated by a Scale even of assembly.
ScalePrimitive:
PUBLIC
PROC [a: Assembly, sx, sy, sz:
REAL] = {
shape: Shape;
scalarCS: CoordSystem;
IF NOT ISTYPE[a.shape, Shape] THEN ERROR;
shape ← NARROW[a.shape];
scalarCS ← shape.coordSys;
scalarCS.scalars[1] ← scalarCS.scalars[1]*sx;
scalarCS.scalars[2] ← scalarCS.scalars[2]*sy;
scalarCS.scalars[3] ← scalarCS.scalars[3]*sz;
};
ScalePrimitives:
PUBLIC
PROC [a: Assembly, sx, sy, sz:
REAL] = {
WITH a.shape
SELECT
FROM
shape: Shape => ScalePrimitive[a, sx, sy, sz];
alist: AssemblyList => {
FOR list:
LIST
OF Assembly ← alist.list, list.rest
UNTIL list =
NIL
DO
ScalePrimitives[list.first, sx, sy, sz];
ENDLOOP;
};
ENDCASE; -- IF a.shape = NIL or something else, do nothing.
};
ScaleEven:
PUBLIC
PROC [a: Assembly, cs: CoordSystem, scalar:
REAL] = {
Just a bit tricky.
If a is a Primitive and cs is any coordinate frame, just scale and translate it from the center of cs by scalar times its current translation.
If a is a cluster, and cs is any coordinate frame, evenly scale each component of a about a's center, then translate a as a unit, the appropriate distance from the center of cs.
displacements: Vector;
WITH a.shape
SELECT
FROM
shape: Shape => {
ScalePrimitive[a, scalar, scalar, scalar];
now translate a's coordSys so that its displacement from the origin of cs is scaled by scalar
displacements ← CoordSys.FindTranslationOfAinTermsOfB[a.coordSys, cs];
displacements ← SVVector3d.Scale[displacements, scalar];
AbsTranslateOnly[a.coordSys, cs, displacements];
};
alist: AssemblyList => {
FOR list:
LIST
OF Assembly ← alist.list, list.rest
UNTIL list =
NIL
DO
ScaleEven[list.first, a.coordSys, scalar];
Recursively scale subassemblies with respect to their own coordinate systems
ENDLOOP;
Now translate a's coordSys so that its displacement from the origin of cs is scaled by scalar
displacements ← CoordSys.FindTranslationOfAinTermsOfB[a.coordSys, cs];
displacements ← SVVector3d.Scale[displacements, scalar];
AbsTranslateOnly[a.coordSys, cs, displacements];
};
ENDCASE; -- IF a.shape = NIL or something else, do nothing.
};
AssemblyHasNoChildren: PUBLIC ERROR = CODE;
ScaleEvenChildren:
PUBLIC PROC [a: Assembly, cs: CoordSystem, scalar:
REAL] ~ {
WITH a.shape
SELECT
FROM
shape: Shape => ERROR AssemblyHasNoChildren;
alist: AssemblyList => {
FOR list:
LIST
OF Assembly ← alist.list, list.rest
UNTIL list =
NIL
DO
ScaleEven[list.first, list.first.coordSys, scalar];
ENDLOOP;
};
ENDCASE => ERROR AssemblyHasNoChildren; -- IF a.shape = NIL or something else, report a problem.
};
ScaleDistancesChildren:
PUBLIC PROC [a: Assembly, cs: CoordSystem, sx, sy, sz:
REAL] = {
For each child of a, find its current displacement from the center of cs. Scale this displacement by [sx, sy, sz].
displacements: Vector;
WITH a.shape
SELECT
FROM
shape: Shape => ERROR AssemblyHasNoChildren;
alist: AssemblyList => {
FOR list:
LIST
OF Assembly ← alist.list, list.rest
UNTIL list =
NIL
DO
displacements ← CoordSys.FindTranslationOfAinTermsOfB[list.first.coordSys, cs];
displacements[1] ← displacements[1]*sx;
displacements[2] ← displacements[2]*sy;
displacements[3] ← displacements[3]*sz;
AbsTranslateOnly[list.first.coordSys, cs, displacements];
ENDLOOP;
};
ENDCASE => ERROR AssemblyHasNoChildren; -- IF a.shape = NIL or something else, report a problem.
};
ScaleAndShear:
PUBLIC PROC [a: Assembly, cs: CoordSystem, sx, sy, sz:
REAL] = {
};
not yet implemented.
ScaleNoShear:
PUBLIC PROC [a: Assembly, cs: CoordSystem, sx, sy, sz:
REAL] = {
};
not yet implemented.
Scale:
PUBLIC
PROC [a: Assembly, cs: CoordSystem, sx, sy, sz:
REAL] = {
IF ISTYPE[a.shape, Shape] AND cs = a.coordSys THEN ScalePrimitive[a, sx, sy, sz]
ELSE ScaleEven[a, cs, sx];
};
MatrixHasScaling:
PRIVATE
PROC [mat: Matrix4by4]
RETURNS [
BOOL] = {
sx, sy, sz: REAL;
almostZero: REAL ← 1.0e-4;
[sx, sy, sz] ← Matrix3d.ScaleFromMatrix[mat];
IF ABS[sx - 1.0] > almostZero OR ABS[sy-1.0] > almostZero OR ABS[sz - 1.0] > almostZero
THEN RETURN[TRUE]
ELSE RETURN [FALSE];
};
END.