ImagerTransformImpl.mesa
Created March 11, 1983
Last Edit By:
Plass, August 12, 1983 1:46 pm
Crow, July 31, 1983 3:31 pm
DIRECTORY
ImagerBasic USING [Pair, IntPair, Rectangle, IntRectangle, Transformation, TransformType],
ImagerTransform,
Real   USING [FixI, RoundLI, Float],
RealFns  USING [SinDeg, CosDeg];
ImagerTransformImpl: CEDAR PROGRAM
IMPORTS Real, RealFns
EXPORTS ImagerTransform
= BEGIN OPEN ImagerBasic;
epsilonHardness: REAL ← .00001; -- How far from non-hard you can get and still be easy
Public Signals
TransformTypeNone: PUBLIC SIGNAL = CODE;
Public Procedures
Translate: PUBLIC PROC [dx, dy: REAL] RETURNS [Transformation] = {
RETURN [[1, 0, dx, 0, 1, dy, identity]]
};
PreTranslate: PUBLIC PROC [dx, dy: REAL, m: Transformation] RETURNS [Transformation] = {
m.c ← dx*m.a + dy*m.b + m.c; m.f ← dx*m.d + dy*m.e + m.f;
RETURN [m]
};
PreIntTranslate: PUBLIC PROC [dx, dy: INTEGER, m: Transformation] RETURNS [Transformation] = {
m.c ← dx*m.a + dy*m.b + m.c; m.f ← dx*m.d + dy*m.e + m.f;
RETURN [m]
};
Rotate: PUBLIC PROC [degrees: REAL] RETURNS [n: Transformation] = {
WHILE degrees >= 360. DO degrees ← degrees - 360.; ENDLOOP;
WHILE degrees < 0. DO degrees ← degrees + 360.; ENDLOOP;
SELECT degrees FROM
0.  => n ← [ 1., 0., 0., 0., 1., 0., identity ];
90.  => n ← [ 0.,-1., 0., 1., 0., 0., rot90  ];
180. => n ← [-1., 0., 0., 0.,-1., 0., rot180 ];
270. => n ← [ 0., 1., 0.,-1., 0., 0., rot270 ];
ENDCASE => { n.a ← RealFns.CosDeg[degrees]; n.d ← RealFns.SinDeg[degrees];
     n.b ← -n.d;       n.e ← n.a;
     n.c ← 0.;        n.f ← 0.;
     n.type ← hard; };
};
PreRotate: PUBLIC PROC [degrees: REAL, m: Transformation] RETURNS [Transformation] = {
RETURN [Concat[Rotate[degrees], m]];
};
Scale: PUBLIC PROC [sx, sy: REAL] RETURNS [Transformation] = {
transformType: TransformType ← SELECT TRUE FROM
ABS[sx] # 1 OR ABS[sy] # 1 => hard,
(sx = 1) AND (sy = 1) => identity,
(sx =-1) AND (sy = 1) => mirrorX,
(sx = 1) AND (sy =-1) => mirrorY,
(sx =-1) AND (sy =-1) => rot180,
ENDCASE => hard;
RETURN [[ sx, 0., 0., 0., sy, 0., transformType ]];
};
PreScale: PUBLIC PROC [sx, sy: REAL, m: Transformation] RETURNS [Transformation] = {
RETURN [Concat[Scale[sx, sy], m]];
};
Transform: PUBLIC PROC [p: Pair, transform: Transformation] RETURNS [Pair] = {
OPEN transform;
SELECT type FROM
identity  => RETURN[ [ x: p.x + c, y: p.y + f] ];
rot90   => RETURN[ [ x: -p.y + c, y: p.x + f] ];
rot180   => RETURN[ [ x: -p.x + c, y: -p.y + f] ];
rot270   => RETURN[ [ x: p.y + c, y: -p.x + f] ];
mirrorX  => RETURN[ [ x: -p.x + c, y: p.y + f] ];
mirrorY  => RETURN[ [ x: p.x + c, y: -p.y + f] ];
mirror45Deg => RETURN[ [ x: p.y + c, y: p.x + f] ];
mirror135Deg => RETURN[ [ x: -p.y + c, y: -p.x + f] ];
hard   => RETURN[ [ x: p.x * a + p.y * b + c, y: p.x * d + p.y * e + f ] ];
ENDCASE  => { SIGNAL TransformTypeNone[]; RETURN[ p ]; };
};
InverseTransform: PUBLIC PROC [p: Pair, transform: Transformation] RETURNS [Pair] = {
OPEN transform;
SELECT type FROM
identity  => RETURN[ [ x: p.x - c, y: p.y - f] ];
rot90   => RETURN[ [ x: p.y - f, y: -p.x + c] ];
rot180   => RETURN[ [ x: -p.x + c, y: -p.y + f] ];
rot270   => RETURN[ [ x: -p.y + f, y: p.x - c] ];
mirrorX  => RETURN[ [ x: -p.x + c, y: p.y - f] ];
mirrorY  => RETURN[ [ x: p.x - c, y: -p.y + f] ];
mirror45Deg => RETURN[ [ x: p.y - f, y: p.x - c] ];
mirror135Deg => RETURN[ [ x: -p.y + f, y: -p.x + c] ];
hard   => RETURN[Transform[p, Invert[transform]]];
ENDCASE  => { SIGNAL TransformTypeNone[]; RETURN[ p ]; };
};
TransformVec: PUBLIC PROC [p: Pair, transform: Transformation] RETURNS [Pair] = {
OPEN transform;
SELECT type FROM
identity  => RETURN[p];
rot90   => RETURN[ [ x: -p.y, y: p.x] ];
rot180   => RETURN[ [ x: -p.x, y: -p.y] ];
rot270   => RETURN[ [ x: p.y, y: -p.x] ];
mirrorX  => RETURN[ [ x: -p.x, y: p.y] ];
mirrorY  => RETURN[ [ x: p.x, y: -p.y] ];
mirror45Deg => RETURN[ [ x: p.y, y: p.x] ];
mirror135Deg => RETURN[ [ x: -p.y, y: -p.x] ];
hard   => RETURN[ [ x: p.x * a + p.y * b, y: p.x * d + p.y * e ] ];
ENDCASE  => { SIGNAL TransformTypeNone[]; RETURN[ p ]; };
};
InverseTransformVec: PUBLIC PROC [p: Pair, transform: Transformation] RETURNS [Pair] = {
OPEN transform;
SELECT type FROM
identity  => RETURN[ [ x: p.x, y: p.y] ];
rot90   => RETURN[ [ x: p.y, y: -p.x] ];
rot180   => RETURN[ [ x: -p.x, y: -p.y] ];
rot270   => RETURN[ [ x: -p.y, y: p.x] ];
mirrorX  => RETURN[ [ x: -p.x, y: p.y] ];
mirrorY  => RETURN[ [ x: p.x, y: -p.y] ];
mirror45Deg => RETURN[ [ x: p.y, y: p.x] ];
mirror135Deg => RETURN[ [ x: -p.y, y: -p.x] ];
hard   => RETURN[TransformVec[p, Invert[transform]]];
ENDCASE  => { SIGNAL TransformTypeNone[]; RETURN[ p ]; };
};
IntTransform: PUBLIC PROC [p: IntPair, transform: Transformation] RETURNS [IntPair] = {
OPEN transform;
SELECT type FROM
identity  => RETURN[ [ x: p.x + Real.FixI[c], y: p.y + Real.FixI[f] ] ];
rot90   => RETURN[ [ x: -p.y + Real.FixI[c], y: p.x + Real.FixI[f] ] ];
rot180   => RETURN[ [ x: -p.x + Real.FixI[c], y: -p.y + Real.FixI[f] ] ];
rot270   => RETURN[ [ x: p.y + Real.FixI[c], y: -p.x + Real.FixI[f] ] ];
mirrorX  => RETURN[ [ x: -p.x + Real.FixI[c], y: p.y + Real.FixI[f] ] ];
mirrorY  => RETURN[ [ x: p.x + Real.FixI[c], y: -p.y + Real.FixI[f] ] ];
mirror45Deg => RETURN[ [ x: p.y + Real.FixI[c], y: p.x + Real.FixI[f] ] ];
mirror135Deg => RETURN[ [ x: -p.y + Real.FixI[c], y: -p.x + Real.FixI[f] ] ];
hard   => RETURN[ [ x: Real.FixI[p.x * a + p.y * b + c],
         y: Real.FixI[p.x * d + p.y * e + f] ] ];
ENDCASE  => { SIGNAL TransformTypeNone[]; RETURN[ p ]; };
};
InverseIntTransform: PUBLIC PROC [p: IntPair, transform: Transformation] RETURNS [IntPair] = {
OPEN transform;
SELECT type FROM
identity  => RETURN[ [ x: p.x - Real.FixI[c], y: p.y - Real.FixI[f]  ] ];
rot90   => RETURN[ [ x: p.y - Real.FixI[f], y: -p.x + Real.FixI[c]  ] ];
rot180   => RETURN[ [ x: -p.x + Real.FixI[c], y: -p.y + Real.FixI[f]  ] ];
rot270   => RETURN[ [ x: -p.y + Real.FixI[f], y: p.x - Real.FixI[c]  ] ];
mirrorX  => RETURN[ [ x: -p.x + Real.FixI[c], y: p.y - Real.FixI[f]  ] ];
mirrorY  => RETURN[ [ x: p.x - Real.FixI[c], y: -p.y + Real.FixI[f]  ] ];
mirror45Deg => RETURN[ [ x: p.y - Real.FixI[f], y: p.x - Real.FixI[c]  ] ];
mirror135Deg => RETURN[ [ x: -p.y + Real.FixI[f], y: -p.x + Real.FixI[c]  ] ];
hard   => RETURN[IntTransform[p, Invert[transform]]];
ENDCASE  => { SIGNAL TransformTypeNone[]; RETURN[ p ]; };
};
TransformIntVec: PUBLIC PROC [p: IntPair, transform: Transformation] RETURNS [IntPair] = {
OPEN transform;
SELECT type FROM
identity  => RETURN[p];
rot90   => RETURN[ [ x: -p.y, y: p.x] ];
rot180   => RETURN[ [ x: -p.x, y: -p.y] ];
rot270   => RETURN[ [ x: p.y, y: -p.x] ];
mirrorX  => RETURN[ [ x: -p.x, y: p.y] ];
mirrorY  => RETURN[ [ x: p.x, y: -p.y] ];
mirror45Deg => RETURN[ [ x: p.y, y: p.x] ];
mirror135Deg => RETURN[ [ x: -p.y, y: -p.x] ];
hard   => {
RETURN[ [ x: Real.FixI[p.x * a + p.y * b], y: Real.FixI[p.x * d + p.y * e] ] ];
};
ENDCASE  => { SIGNAL TransformTypeNone[]; RETURN[ p ]; };
};
InverseTransformIntVec: PUBLIC PROC [p: IntPair, transform: Transformation] RETURNS [IntPair] = {
OPEN transform;
SELECT type FROM
identity  => RETURN[ [ x: p.x, y: p.y] ];
rot90   => RETURN[ [ x: p.y, y: -p.x] ];
rot180   => RETURN[ [ x: -p.x, y: -p.y] ];
rot270   => RETURN[ [ x: -p.y, y: p.x] ];
mirrorX  => RETURN[ [ x: -p.x, y: p.y] ];
mirrorY  => RETURN[ [ x: p.x, y: -p.y] ];
mirror45Deg => RETURN[ [ x: p.y, y: p.x] ];
mirror135Deg => RETURN[ [ x: -p.y, y: -p.x] ];
hard   => RETURN[TransformIntVec[p, Invert[transform]]];
ENDCASE  => { SIGNAL TransformTypeNone[]; RETURN[ p ]; };
};
This always returns a rectangle, thus "hard" transforms will cause a bounding box to be returned
TransformRectangle: PUBLIC PROC [rect: Rectangle, transform: Transformation]
                   RETURNS [Rectangle] = {
IF transform.b = 0. AND transform.d = 0.
THEN {                 -- No rotation or skew
[[rect.x, rect.y]] ← Transform[[rect.x, rect.y], transform ];
[[rect.w, rect.h]] ← TransformVec[[rect.w, rect.h], transform ];
IF rect.w < 0 THEN { rect.x ← rect.x + rect.w; rect.w ← ABS[rect.w]; };
IF rect.h < 0 THEN { rect.y ← rect.y + rect.h; rect.h ← ABS[rect.h]; };
}
ELSE { p1, p2, p3, p4: Pair;     -- Hard case, find resulting bounding box
p1 ← Transform[[rect.x, rect.y], transform ];
p2 ← Transform[[rect.x + rect.w, rect.y], transform ];
p3 ← Transform[[rect.x + rect.w, rect.y + rect.h], transform ];
p4 ← Transform[[rect.x, rect.y + rect.h], transform ];
rect.x ← MIN[p1.x, p2.x, p3.x, p4.x];
rect.y ← MIN[p1.y, p2.y, p3.y, p4.y];
rect.w ← MAX[p1.x, p2.x, p3.x, p4.x] - rect.x;
rect.h ← MAX[p1.y, p2.y, p3.y, p4.y] - rect.y;
};
RETURN [ rect ];
};
TransformIntRectangle: PUBLIC PROC[rect: IntRectangle, transform: Transformation]
                   RETURNS [IntRectangle] = {
IF transform.b = 0. AND transform.d = 0.
THEN {                 -- No rotation or skew
[[rect.x, rect.y]] ← IntTransform[[rect.x, rect.y], transform ];
[[rect.w, rect.h]] ← TransformIntVec[[rect.w, rect.h], transform ];
IF rect.w < 0 THEN { rect.x ← rect.x + rect.w; rect.w ← ABS[rect.w]; };
IF rect.h < 0 THEN { rect.y ← rect.y + rect.h; rect.h ← ABS[rect.h]; };
}
Hard case, find resulting integer bounding box enclosing real bounding box
ELSE { p1, p2, p3, p4: Pair;   t: REAL;
p1 ← Transform[[Real.Float[rect.x], Real.Float[rect.y]], transform ];
p2 ← Transform[[Real.Float[rect.x + rect.w], Real.Float[rect.y]], transform ];
p3 ← Transform[[Real.Float[rect.x + rect.w], Real.Float[rect.y + rect.h]], transform ];
p4 ← Transform[[Real.Float[rect.x], Real.Float[rect.y + rect.h]], transform ];
t ← MAX[MIN[p1.x, p2.x, p3.x, p4.x, LAST[INTEGER]-1], FIRST[INTEGER]+1];
rect.x ← Real.RoundLI[t]; IF rect.x > t THEN rect.x ← rect.x - 1;   -- Floor[t]
t ← MAX[MIN[p1.y, p2.y, p3.y, p4.y, LAST[INTEGER]-1], FIRST[INTEGER]+1];
rect.y ← Real.RoundLI[t]; IF rect.y > t THEN rect.y ← rect.y - 1;
t ← MAX[MIN[MAX[p1.x, p2.x, p3.x, p4.x] - rect.x, LAST[INTEGER]-1], 0];
rect.w ← Real.RoundLI[t]; IF rect.w < t THEN rect.w ← rect.w + 1;   -- Ceiling[t]
t ← MAX[MIN[MAX[p1.y, p2.y, p3.y, p4.y] - rect.y, LAST[INTEGER]-1], 0];
rect.h ← Real.RoundLI[t]; IF rect.h < t THEN rect.h ← rect.h + 1;
};
RETURN [ rect ];
};
Interpretation of easy transforms
identity  => [TransformationRep ← [ 1., 0., x, 0., 1., y] ];
rot90   => [TransformationRep ← [ 0., -1., x, 1., 0., y] ];
rot180   => [TransformationRep ← [-1., 0., x, 0., -1., y] ];
rot270   => [TransformationRep ← [ 0., 1., x, -1., 0., y] ];
mirrorX  => [TransformationRep ← [-1., 0., x, 0., 1., y] ];
mirrorY  => [TransformationRep ← [ 1., 0., x, 0., -1., y] ];
mirror45Deg => [TransformationRep ← [ 0., 1., x, 1., 0., y] ];
mirror135Deg => [TransformationRep ← [ 0., -1., x, -1., 0., y] ];
Concat: PUBLIC PROC [pre, post: Transformation] RETURNS [prod: Transformation] = {
catch trivial cases
IF post.type = identity THEN {
prod ← pre;
prod.c ← pre.c + post.c; prod.f ← pre.f + post.f;
}
ELSE IF pre.type = identity THEN {
prod ← post;
prod.c ← pre.c*post.a + pre.f*post.b + post.c; prod.f ← pre.c*post.d + pre.f*post.e + post.f;
}
ELSE IF post.type = mirrorY THEN {
t: Pair;
t.x ← pre.c + post.c; t.y ← -pre.f + post.f;
SELECT pre.type FROM
rot90   => prod ← [ 0., -1., t.x, -1., 0., t.y, mirror135Deg ];
rot180  => prod ← [-1., 0., t.x, 0., 1., t.y, mirrorX    ];
rot270  => prod ← [ 0., 1., t.x, 1., 0., t.y, mirror45Deg ];
mirrorX => prod ← [-1., 0., t.x, 0., -1., t.y, rot180    ];
mirrorY => prod ← [ 1., 0., t.x, 0., 1., t.y, identity  ];
mirror45Deg => prod ← [ 0., 1., t.x, -1., 0., t.y, rot270  ];
mirror135Deg => prod ← [ 0., -1., t.x, 1., 0., t.y, rot90  ];
hard  => { prod ← [ a: pre.a, d: -pre.d,
        b: pre.b, e: -pre.e,
        c: t.x,  f: t.y,  type: pre.type ] };
ENDCASE => { SIGNAL TransformTypeNone[];
    prod ← [ 1., 0., t.x, 0., -1., t.y, mirrorY  ]; }
}
ELSE {    -- not a trivial case
prod.a ← pre.a*post.a + pre.d*post.b;   prod.d ← pre.a*post.d + pre.d*post.e;
prod.b ← pre.b*post.a + pre.e*post.b;   prod.e ← pre.b*post.d + pre.e*post.e;
prod.c ← pre.c*post.a + pre.f*post.b + post.c; prod.f ← pre.c*post.d + pre.f*post.e + post.f;
prod.type ← hard;
Make non-hard if a, b, d, e all within epsilonHardness of values for one of the non-hard cases  
IF (ABS[prod.a] < epsilonHardness-- test for rotation or axis interchange ops
AND ABS[prod.e] < epsilonHardness
AND (ABS[ABS[prod.d] - 1.0] < epsilonHardness)
AND (ABS[ABS[prod.b] - 1.0] < epsilonHardness) )
THEN {
IF prod.d > 0
THEN
IF prod.b > 0
THEN prod ← [0., 1., prod.c, 1., 0., prod.f, mirror45Deg ]
ELSE prod ← [0., -1., prod.c, 1., 0., prod.f, rot90   ]
ELSE
IF prod.b > 0
THEN prod ← [0., 1., prod.c, -1., 0., prod.f, rot270   ]
ELSE prod ← [0., -1., prod.c, -1., 0., prod.f, mirror135Deg ]
}
ELSE IF (ABS[prod.d] < epsilonHardness   -- test for null or mirror ops
AND ABS[prod.b] < epsilonHardness
AND (ABS[ABS[prod.a] - 1.0] < epsilonHardness)
AND (ABS[ABS[prod.e] - 1.0] < epsilonHardness) )
THEN {
IF prod.a > 0
THEN
IF prod.e > 0
THEN prod ← [ 1., 0., prod.c, 0., 1., prod.f, identity  ]
ELSE prod ← [ 1., 0., prod.c, 0., -1., prod.f, mirrorY  ]
ELSE
IF prod.e > 0
THEN prod ← [-1., 0., prod.c, 0., 1., prod.f, mirrorX  ]
ELSE prod ← [-1., 0., prod.c, 0., -1., prod.f, rot180   ]
};      -- do nothing if can't make it non-hard
};
};
Invert: PUBLIC PROC [m: Transformation] RETURNS [tfm: Transformation] = {
det: REAL ← m.a*m.e - m.d*m.b;      -- compute determinant
tfm ← [
a: m.e / det,       d: -m.d / det,
b: -m.b / det,       e: m.a / det,
c: (m.b * m.f - m.e * m.c) / det,  f: (m.d * m.c - m.a * m.f) / det,
type: hard
];
};
Create: PUBLIC PROC [a, b, c, d, e, f: REAL] RETURNS [tfm: Transformation] = {
tfm ← [a, b, c, d, e, f, hard];
};
END.
Michael Plass, August 12, 1983 1:46 pm: Changed Real.RoundI to Real.RoundLI
Michael Plass, August 12, 1983 2:42 pm: Added clip to INTEGER bounds in TransformIntRectangle.