ImagerTransformImpl.mesa
Michael Plass, February 8, 1984 8:27:05 am PST
DIRECTORY ImagerTransform, Real, RealFns;
ImagerTransformImpl: CEDAR PROGRAM
IMPORTS Real, RealFns
EXPORTS ImagerTransform ~ BEGIN
Pair: TYPE ~ ImagerTransform.Pair;
Rectangle: TYPE ~ ImagerTransform.Rectangle;
IntRectangle: TYPE ~ ImagerTransform.IntRectangle;
Transformation: TYPE ~ ImagerTransform.Transformation;
TransformationRep: TYPE ~ ImagerTransform.TransformationRep;
identity: Transformation ~ Create[a: 1, b: 0, c: 0, d: 0, e: 1, f: 0];
rot90: Transformation ~ Create[a: 0.0, b: -1.0, c: 0.0, d: 1.0, e: 0.0, f: 0.0];
rot180: Transformation ~ Concat[rot90, rot90];
rot270: Transformation ~ Concat[rot90, rot180];
Translate: PUBLIC PROC [dx, dy: REAL] RETURNS [Transformation] ~ {
RETURN [NEW[TransformationRep ← [a: 1, b: 0, c: dx, d: 0, e: 1, f: dy]]]
};
Scale: PUBLIC PROC [s: REAL] RETURNS [Transformation] ~ {
IF s = 1.0 THEN RETURN [identity]
ELSE RETURN [NEW[TransformationRep ← [a: s, b: 0, c: 0, d: 0, e: s, f: 0]]]
};
Scale2: PUBLIC PROC [sx, sy: REAL] RETURNS [Transformation] ~ {
RETURN [NEW[TransformationRep ← [a: sx, b: 0, c: 0, d: 0, e: sy, f: 0]]]
};
Rotate: PUBLIC PROC [degrees: REAL] RETURNS [Transformation] ~ {
SELECT degrees FROM
0.0 => RETURN [identity];
90.0 => RETURN [rot90];
180.0 => RETURN [rot180];
-90.0, 270.0 => RETURN [rot270];
ENDCASE => {
cos: REAL ~ RealFns.CosDeg[degrees];
sin: REAL ~ RealFns.SinDeg[degrees];
RETURN [NEW[TransformationRep ← [a: cos, b: -sin, c: 0, d: sin, e: cos, f: 0]]]
}
};
Concat: PUBLIC PROC [pre, post: Transformation] RETURNS [Transformation] ~ {
a: REAL ~ pre.a*post.a + pre.d*post.b;
d: REAL ~ pre.a*post.d + pre.d*post.e;
b: REAL ~ pre.b*post.a + pre.e*post.b;
e: REAL ~ pre.b*post.d + pre.e*post.e;
c: REAL ~ pre.c*post.a + pre.f*post.b + post.c;
f: REAL ~ pre.c*post.d + pre.f*post.e + post.f;
RETURN [NEW[TransformationRep ← [a, b, c, d, e, f]]]
};
Invert: PUBLIC PROC [m: Transformation] RETURNS [Transformation] ~ {
det: REAL ← m.a*m.e - m.d*m.b;
RETURN [NEW[TransformationRep ← [
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
]]];
};
Create: PUBLIC PROC [a, b, c, d, e, f: REAL] RETURNS [Transformation] ~ {
RETURN [NEW[TransformationRep ← [a, b, c, d, e, f]]]
};
FromRec: PUBLIC PROC [t: ImagerTransform.TransformationRec] RETURNS [Transformation] ~ {
RETURN [NEW[TransformationRep ← [t.a, t.b, t.c, t.d, t.e, t.f]]]
};
PreTranslate: PUBLIC PROC [dx, dy: REAL, m: Transformation] RETURNS [Transformation] ~ {
RETURN [NEW[TransformationRep ← [
m.a, m.b, dx*m.a + dy*m.b + m.c,
m.d, m.e, dx*m.d + dy*m.e + m.f
]]];
};
PreScale: PUBLIC PROC [s: REAL, m: Transformation] RETURNS [Transformation] ~ {
RETURN [Concat[Scale[s], m]];
};
PreScale2: PUBLIC PROC [sx, sy: REAL, m: Transformation] RETURNS [Transformation] ~ {
RETURN [Concat[Scale2[sx, sy], m]];
};
PreRotate: PUBLIC PROC [degrees: REAL, m: Transformation] RETURNS [Transformation] ~ {
RETURN [Concat[Rotate[degrees], m]];
};
Transform: PUBLIC PROC [p: Pair, transform: Transformation] RETURNS [Pair] ~ {
RETURN [[
x: p.x*transform.a + p.y*transform.b + transform.c,
y: p.x*transform.d + p.y*transform.e + transform.f
]];
};
InverseTransform: PUBLIC PROC [p: Pair, transform: Transformation] RETURNS [Pair] ~ {
det: REAL ~ transform.a*transform.e - transform.b*transform.d;
p.x ← p.x - transform.c;
p.y ← p.y - transform.f;
RETURN [[
x: (p.x*transform.e - p.y*transform.b)/det,
y: (p.y*transform.a - p.x*transform.d)/det
]];
};
TransformVec: PUBLIC PROC [p: Pair, transform: Transformation] RETURNS [Pair] ~ {
RETURN [[
x: p.x*transform.a + p.y*transform.b,
y: p.x*transform.d + p.y*transform.e
]];
};
InverseTransformVec: PUBLIC PROC [p: Pair, transform: Transformation] RETURNS [Pair] ~ {
det: REAL ~ transform.a*transform.e - transform.b*transform.d;
RETURN [[
x: (p.x*transform.e - p.y*transform.b)/det,
y: (p.y*transform.a - p.x*transform.d)/det
]];
};
TransformRectangle: PUBLIC PROC [rect: Rectangle, transform: Transformation] RETURNS [Rectangle] ~ {
Add: PUBLIC PROC [p, q: Pair] RETURNS [Pair] ~ INLINE {RETURN [[p.x+q.x, p.y+q.y]]};
w: Pair ← [rect.w*transform.a, rect.w*transform.d];
h: Pair ← [rect.h*transform.b, rect.h*transform.e];
p0: Pair ← Transform[[rect.x, rect.y], transform];
p1: Pair ← Add[p0, w];
p2: Pair ← Add[p1, h];
p3: Pair ← Add[p0, h];
rect.x ← MIN[p0.x, p1.x, p2.x, p3.x];
rect.y ← MIN[p0.y, p1.y, p2.y, p3.y];
rect.w ← MAX[p0.x, p1.x, p2.x, p3.x] - rect.x;
rect.h ← MAX[p0.y, p1.y, p2.y, p3.y] - rect.y;
RETURN [rect];
};
TransformIntRectangle: PUBLIC PROC [rect: Rectangle, transform: Transformation] RETURNS [IntRectangle] ~ {
new: Rectangle ← TransformRectangle[rect, transform];
xMax: REAL ← new.x+new.w;
yMax: REAL ← new.y+new.h;
x0: INT ← Real.RoundLI[new.x];
y0: INT ← Real.RoundLI[new.y];
x1: INT ← Real.RoundLI[xMax];
y1: INT ← Real.RoundLI[yMax];
WHILE x0>new.x DO x0 ← x0-1 ENDLOOP;
WHILE y0>new.y DO y0 ← y0-1 ENDLOOP;
WHILE x1<xMax DO x1 ← x1+1 ENDLOOP;
WHILE y1<yMax DO y1 ← y1+1 ENDLOOP;
RETURN [[x0, y0, x1-x0, y1-y0]]
};
WithinQuarterPixel: PROC [p, q: Pair] RETURNS [BOOLEAN] ~ {
RETURN [ABS[p.x-q.x] <= 0.25 AND ABS[p.y-q.y] <= 0.25]
};
CloseEnough: PUBLIC PROC [s, t: Transformation, rangeSize: REAL] RETURNS [BOOLEAN] ~ {
p00: Pair ← Transform[InverseTransform[[0, 0], t], s];
p10: Pair ← Transform[InverseTransform[[rangeSize, 0], t], s];
p01: Pair ← Transform[InverseTransform[[0, rangeSize], t], s];
RETURN [
WithinQuarterPixel[p00, [0, 0]] AND
WithinQuarterPixel[p10, [rangeSize, 0]] AND
WithinQuarterPixel[p10, [0, rangeSize]]
]
};
CloseToTranslation: PUBLIC PROC [s, t: Transformation, rangeSize: REAL] RETURNS [BOOLEAN] ~ {
p10: Pair ← TransformVec[InverseTransformVec[[rangeSize, 0], t], s];
p01: Pair ← TransformVec[InverseTransformVec[[0, rangeSize], t], s];
RETURN [
WithinQuarterPixel[p10, [rangeSize, 0]] AND
WithinQuarterPixel[p10, [0, rangeSize]]
]
};
SingularValues: PUBLIC PROC [transform: Transformation] RETURNS [Pair] ~ {
t1: REAL ← transform.a*transform.a + transform.d*transform.d;
t2: REAL ← transform.b*transform.b + transform.e*transform.e;
t3: REAL ← t1 - t2;
t4: REAL ← Real.FScale[transform.a*transform.b + transform.d*transform.e, 1];
s: REAL ← Real.SqRt[t3*t3+t4*t4];
x: REAL ← Real.SqRt[Real.FScale[t1+t2+s, -1]];
y: REAL ← Real.SqRt[Real.FScale[t1+t2-s, -1]];
RETURN [[x, y]]
};
fuzz: REAL ← 2.0e-6;
Different: PROC [a, b: REAL] RETURNS [BOOLEAN] ~ {
IF ABS[a-b] < fuzz THEN RETURN [FALSE];
RETURN [ABS[a - b]/MAX[ABS[a], ABS[b]] > fuzz];
};
ShouldBeSameT: PROC [s, t: Transformation] ~ {
IF Different[s.a, t.a] THEN ERROR;
IF Different[s.b, t.b] THEN ERROR;
IF Different[s.c, t.c] THEN ERROR;
IF Different[s.d, t.d] THEN ERROR;
IF Different[s.e, t.e] THEN ERROR;
IF Different[s.f, t.f] THEN ERROR;
};
ShouldBeSamePt: PROC [p, q: Pair] ~ {
IF Different[p.x, q.x] THEN ERROR;
IF Different[p.y, q.y] THEN ERROR;
};
SelfTest: PROC [r: Transformation] ~ {
p: Pair ← [3.14159, -265.35];
ShouldBeSameT[PreTranslate[p.x, p.y, r], Concat[Translate[p.x, p.y], r]];
ShouldBeSameT[Rotate[90], Concat[Rotate[45], Rotate[45]]];
ShouldBeSameT[Rotate[0], Concat[r, Invert[r]]];
ShouldBeSamePt[InverseTransform[p, r], Transform[p, Invert[r]]];
ShouldBeSamePt[InverseTransformVec[p, r], TransformVec[p, Invert[r]]];
ShouldBeSamePt[TransformVec[TransformVec[p, r], r], TransformVec[p, Concat[r, r]]];
ShouldBeSamePt[SingularValues[r], SingularValues[PreRotate[33.5, r]]];
};
SelfTest[PreTranslate[12, 6, PreScale2[1, 5, Rotate[61]]]];
SelfTest[Create[1, 2, 4, 9, 3, 1]];
SelfTest[Create[3, 1, 4, 1, 5, 9]];
END.