File: IntTrans.mesa
Written by Martin Newell, July 1979
Converted to Mesa 6: October 20, 1980 4:34 PM
last edited June 10, 1981 4:27 PM
Last Edited by: McCreight, January 30, 1985 3:18:29 pm PST
Bertrand Serlet May 21, 1987 4:19:25 pm PDT
Transformations are held in homogeneous form, in REALs. The current transformation is
held locally in two forms - homogeneous (ground truth) and a divided form for
application to a set of input vectors. The transformation stack is a doubly-linked
list of homogeneous arrays.
DIRECTORY
Basics, IntTransDefs, Real;
IntTrans: CEDAR PROGRAM
IMPORTS Basics, Real
EXPORTS IntTransDefs =
BEGIN
OPEN IntTransDefs;
Define a DividedTransform for internal use in transformaing points and vectors:
DividedTransform: TYPE = REF DividedTransformRecord ← NIL;
DividedTransformRecord: TYPE = RECORD[
type: [ident..translate+rotscale],
a11,a21,a12,a22: REAL,
a31,a32: INT];
Context: TYPE = IntTransDefs.Transform;
T: DividedTransform = NEW[DividedTransformRecord];
The current transformation divided (T[3,3]=1)
contextStack: ContextStack ← NIL;
TH: Transform = NEW[TransformRecord]; --Top of current context, in homogeneous form
LH: Transform = NEW[TransformRecord];
The local transformation
inTransforms,needsDivision: BOOLEAN; --state flags
Identity: TransformRecord = [
type: ident,
a11: 1, a21: 0, a31: 0,
a12: 0, a22: 1, a32: 0,
a33: 1];
InitTransformation: PUBLIC PROCEDURE RETURNS [BOOLEAN] =
Initialize (or re-initialize) Transformation package
BEGIN
contextStack ← NIL;
TH^ ← Identity;
LH^ ← Identity;
inTransforms ← TRUE;
needsDivision ← TRUE;
RETURN[TRUE];
END;
FinishTransformation: PUBLIC PROCEDURE RETURNS [BOOLEAN] =
BEGIN
RETURN[TRUE];
END;
FreezeContext: PUBLIC PROCEDURE RETURNS[cs: ContextStack] =
Create new context based on existing context state
BEGIN
Push[];
cs ← contextStack;
contextStack ← NIL; -- I guess...
END;
SwapContext: PUBLIC PROCEDURE[cs: ContextStack] =
BEGIN
contextStack ← cs;
Pop[];
END;
Push: PUBLIC PROCEDURE =
Push current transformation - start a new relative coordinate system
BEGIN
IF inTransforms THEN Concatenate[LH,TH,TH];
contextStack ← CONS[NEW[TransformRecord ← TH^], contextStack];
LH^ ← Identity;
inTransforms ← TRUE;
needsDivision ← TRUE;
END;
Pop: PUBLIC PROCEDURE =
Pop current transformation - return to previous coordinate system
BEGIN
IF contextStack = NIL THEN ERROR TransformationStackUnderflow;
TH^ ← contextStack.first^;
contextStack ← contextStack.rest;
inTransforms ← FALSE;
needsDivision ← TRUE;
END;
Rotate: PUBLIC PROCEDURE[xRot,yRot: INT] =
Rotate x axis to direction of (xRot,yRot)
BEGIN
t: REAL;
IF ~inTransforms THEN ERROR TransformationBadContext;
BEGIN OPEN LH;
SELECT TRUE FROM
xRot = 0 => BEGIN
t ← -a12; a12 ← a11; a11 ← t;
t ← -a22; a22 ← a21; a21 ← t;
t ← -a32; a32 ← a31; a31 ← t;
IF yRot<0 THEN a33 ← -a33;
END;
yRot = 0 => IF xRot<0 THEN a33 ← -a33;
ENDCASE => BEGIN
c: REAL = xRot; s: REAL = yRot;
t ← a11*c - a12*s; a12 ← a11*s + a12*c; a11 ← t;
t ← a21*c - a22*s; a22 ← a21*s + a22*c; a21 ← t;
t ← a31*c - a32*s; a32 ← a31*s + a32*c; a31 ← t;
a33 ← a33*Real.SqRt[c*c + s*s];
END;
type ← Basics.BITOR[type,rotscale];
END;
END;
Translate: PUBLIC PROCEDURE[xTrans,yTrans: INT] =
Translate by (xTrans,yTrans)
BEGIN
xT: REAL ← xTrans;
yT: REAL ← yTrans;
IF ~inTransforms THEN ERROR TransformationBadContext;
BEGIN OPEN LH;
a31 ← a31 + a33*xT;
a32 ← a32 + a33*yT;
type ← Basics.BITOR[type,translate];
END;
END;
Mirror: PUBLIC PROCEDURE[coord: CoordName] =
Mirror coordinates
BEGIN
IF ~inTransforms THEN ERROR TransformationBadContext;
BEGIN OPEN LH;
SELECT coord FROM
x => BEGIN
a11 ← -a11;
a21 ← -a21;
a31 ← -a31;
END;
y => BEGIN
a12 ← -a12;
a22 ← -a22;
a32 ← -a32;
END;
ENDCASE;
type ← Basics.BITOR[type,rotscale];
END;
END;
Scale: PUBLIC PROCEDURE[numerator,denominator: INT] =
Scale by numerator/denominator
BEGIN
num: REAL ← numerator;
denom: REAL ← denominator;
IF ~inTransforms THEN ERROR TransformationBadContext;
BEGIN OPEN LH;
a11 ← a11*num;
a21 ← a21*num;
a31 ← a31*num;
a12 ← a12*num;
a22 ← a22*num;
a32 ← a32*num;
a33 ← a33*denom;
type ← Basics.BITOR[type,rotscale];
END;
END;
GetLocal: PUBLIC PROCEDURE RETURNS[localTransform: TransformRecord] =
Return incremental transformation built since last Push
BEGIN
RETURN[LH^];
END;
local: Transform ← NEW[TransformRecord];
ApplyLocal: PUBLIC PROCEDURE[transform: TransformRecord] =
Apply transformation to current local transformation
BEGIN
IF ~inTransforms THEN ERROR TransformationBadContext;
local^ ← transform;
Concatenate[LH,local,LH];
END;
GetCurrent: PUBLIC PROCEDURE RETURNS[currentTransform: TransformRecord] =
Return current cummulative transformation
BEGIN
IF inTransforms THEN
BEGIN
Concatenate[LH,TH,TH];
inTransforms ← FALSE;
END;
RETURN[TH^];
END;
TransformPoint: PUBLIC PROCEDURE[x,y: INT] RETURNS[xT,yT: INT] =
Transform the point (x,y) by current cumulative transformation
BEGIN
SELECT TRUE FROM
inTransforms => BEGIN
Concatenate[LH,TH,TH];
inTransforms ← FALSE;
DivideTransform[TH,T];
needsDivision ← FALSE;
END;
needsDivision => BEGIN
DivideTransform[TH,T];
needsDivision ← FALSE;
END;
ENDCASE;
BEGIN OPEN T;
SELECT type FROM
ident =>  BEGIN
xT ← x;
yT ← y;
END;
translate => BEGIN
xT ← x + a31;
yT ← y + a32;
END;
rotscale => BEGIN
X: REAL = x;
Y: REAL = y;
xT ← Real.Round[X*a11 + Y*a21];
yT ← Real.Round[X*a12 + Y*a22];
END;
ENDCASE => BEGIN
X: REAL = x;
Y: REAL = y;
xT ← a31 + Real.Round[X*a11 + Y*a21];
yT ← a32 + Real.Round[X*a12 + Y*a22];
END;
END;
END;
TransformVector: PUBLIC PROCEDURE[x,y: INT] RETURNS[xT,yT: INT] =
Transform the direction (x,y) by current cumulative transformation
BEGIN
SELECT TRUE FROM
inTransforms => BEGIN
Concatenate[LH,TH,TH];
inTransforms ← FALSE;
DivideTransform[TH,T];
needsDivision ← FALSE;
END;
needsDivision => BEGIN
DivideTransform[TH,T];
needsDivision ← FALSE;
END;
ENDCASE;
BEGIN OPEN T;
SELECT type FROM
ident, translate =>
BEGIN
xT ← x;
yT ← y;
END;
ENDCASE => BEGIN
X: REAL = x;
Y: REAL = y;
xT ← Real.Round[X*a11 + Y*a21];
yT ← Real.Round[X*a12 + Y*a22];
END;
END;
END;
RTransformPoint: PUBLIC PROCEDURE[x,y: REAL] RETURNS[xT,yT: REAL] =
Transform the point (x,y) by current cumulative transformation - in REALs
BEGIN
IF inTransforms
THENBEGIN
Concatenate[LH,TH,TH];
inTransforms ← FALSE;
END;
BEGIN OPEN TH;
SELECT type FROM
ident =>  BEGIN
xT ← x;
yT ← y;
END;
translate => BEGIN
xT ← x + a31;
yT ← y + a32;
END;
rotscale => BEGIN
xT ← (x*a11 + y*a21)/a33;
yT ← (x*a12 + y*a22)/a33;
END;
ENDCASE => BEGIN
xT ← (a31 + x*a11 + y*a21)/a33;
yT ← (a32 + x*a12 + y*a22)/a33;
END;
END;
END;
RTransformVector: PUBLIC PROCEDURE[x,y: REAL] RETURNS[xT,yT: REAL] =
Transform the direction (x,y) by current cumulative transformation - in REALs
BEGIN
IF inTransforms
THENBEGIN
Concatenate[LH,TH,TH];
inTransforms ← FALSE;
END;
BEGIN OPEN TH;
SELECT type FROM
ident, translate =>
BEGIN
xT ← x;
yT ← y;
END;
ENDCASE => BEGIN
xT ← (x*a11 + y*a21)/a33;
yT ← (x*a12 + y*a22)/a33;
END;
END;
END;
Concatenate: PROCEDURE [A,B,C: Transform] =
A*B => C, A=C or B=C is OK
BEGIN
SELECT TRUE FROM
A.type=ident => IF C#B THEN C^ ← B^; --(not uncommon for ApplyLocal)
B.type=ident => IF C#A THEN C^ ← A^;
ENDCASE =>
BEGIN
T: TransformRecord;
T.a11 ← A.a11*B.a11 + A.a12*B.a21;
T.a21 ← A.a21*B.a11 + A.a22*B.a21;
T.a31 ← A.a31*B.a11 + A.a32*B.a21 + A.a33*B.a31;
T.a12 ← A.a11*B.a12 + A.a12*B.a22;
T.a22 ← A.a21*B.a12 + A.a22*B.a22;
T.a32 ← A.a31*B.a12 + A.a32*B.a22 + A.a33*B.a32;
T.a33 ← A.a33*B.a33;
T.type ← Basics.BITOR[A.type,B.type];
C^ ← T;
END;
END;
DivideTransform: PROCEDURE[AH: Transform, A: DividedTransform] =
AH/AH(3,3) => A
BEGIN
IF AH.a33=1
THENBEGIN
A.a11 ← AH.a11;
A.a21 ← AH.a21;
A.a31 ← Real.Round[AH.a31];
A.a12 ← AH.a12;
A.a22 ← AH.a22;
A.a32 ← Real.Round[AH.a32];
END
ELSEBEGIN
ah33: REAL = AH.a33;
A.a11 ← AH.a11/ah33;
A.a21 ← AH.a21/ah33;
A.a31 ← Real.Round[AH.a31/ah33];
A.a12 ← AH.a12/ah33;
A.a22 ← AH.a22/ah33;
A.a32 ← Real.Round[AH.a32/ah33];
END;
A.type ← AH.type;
END;
TransformationStackUnderflow: PUBLIC ERROR = CODE;
TransformationBadContext: PUBLIC ERROR = CODE;
END.