ImagerTransformImpl:
CEDAR PROGRAM
IMPORTS Real, RealFns, Vector2
EXPORTS ImagerTransform
~ BEGIN OPEN ImagerTransform;
Ref: TYPE ~ ImagerTransform.Ref;
Rep: TYPE ~ ImagerTransform.Rep;
VEC: TYPE ~ Vector2.VEC;
identity: Ref ~ Create[a: 1, b: 0, c: 0, d: 0, e: 1, f: 0];
rot90: Ref ~ Create[a: 0.0, b: -1.0, c: 0.0, d: 1.0, e: 0.0, f: 0.0];
rot180: Ref ~ Concat[rot90, rot90];
rot270: Ref ~ Concat[rot90, rot180];
FromRec:
PUBLIC
PROC[r: Rep]
RETURNS[Ref] ~ {
RETURN[NEW[Rep ← r]];
};
Create:
PUBLIC
PROC[a, b, c, d, e, f:
REAL]
RETURNS[Ref] ~ {
RETURN[NEW[Rep ← [a, b, c, d, e, f]]];
};
Copy:
PUBLIC
PROC[m: Ref]
RETURNS[Ref] ~ {
RETURN[NEW[Rep ← m^]];
};
Scale:
PUBLIC
PROC[s:
REAL]
RETURNS[Ref] ~ {
IF s=1.0 THEN RETURN[identity]
ELSE RETURN[NEW[Rep ← [a: s, b: 0, c: 0, d: 0, e: s, f: 0]]];
};
Scale2:
PUBLIC
PROC[sx, sy:
REAL]
RETURNS[Ref] ~ {
RETURN[NEW[Rep ← [a: sx, b: 0, c: 0, d: 0, e: sy, f: 0]]];
};
Rotate:
PUBLIC
PROC[a:
REAL]
RETURNS[Ref] ~ {
SELECT a
FROM
0.0 => RETURN[identity];
90.0 => RETURN[rot90];
180.0 => RETURN[rot180];
-90.0, 270.0 => RETURN[rot270];
ENDCASE => {
cos: REAL ~ RealFns.CosDeg[a];
sin: REAL ~ RealFns.SinDeg[a];
RETURN[NEW[Rep ← [a: cos, b: -sin, c: 0, d: sin, e: cos, f: 0]]];
};
};
Translate:
PUBLIC
PROC[x, y:
REAL]
RETURNS[Ref] ~ {
RETURN[NEW[Rep ← [a: 1, b: 0, c: x, d: 0, e: 1, f: y]]];
};
Concat:
PUBLIC
PROC[m, n: Ref]
RETURNS[Ref] ~ {
a: REAL ~ m.a*n.a + m.d*n.b;
d: REAL ~ m.a*n.d + m.d*n.e;
b: REAL ~ m.b*n.a + m.e*n.b;
e: REAL ~ m.b*n.d + m.e*n.e;
c: REAL ~ m.c*n.a + m.f*n.b + n.c;
f: REAL ~ m.c*n.d + m.f*n.e + n.f;
RETURN[NEW[Rep ← [a, b, c, d, e, f]]];
};
Invert:
PUBLIC
PROC[m: Ref]
RETURNS[Ref] ~ {
det: REAL ~ m.a*m.e - m.d*m.b;
RETURN[
NEW[Rep ← [
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
]]];
};
PreMultiply:
PUBLIC
PROC[m, pre: Ref] ~ {
rep: Rep ← m^;
rep.a ← pre.a*m.a + pre.d*m.b;
rep.d ← pre.a*m.d + pre.d*m.e;
rep.b ← pre.b*m.a + pre.e*m.b;
rep.e ← pre.b*m.d + pre.e*m.e;
rep.c ← pre.c*m.a + pre.f*m.b + m.c;
rep.f ← pre.c*m.d + pre.f*m.e + m.f;
m^ ← rep;
};
PreScale:
PUBLIC
PROC[m: Ref, s:
REAL] ~ {
rep: Rep ← m^;
rep.a ← s*m.a;
rep.d ← s*m.d;
rep.b ← s*m.b;
rep.e ← s*m.e;
m^ ← rep;
};
PreScale2:
PUBLIC
PROC[m: Ref, sx, sy:
REAL] ~ {
rep: Rep ← m^;
rep.a ← sx*m.a;
rep.d ← sx*m.d;
rep.b ← sy*m.b;
rep.e ← sy*m.e;
m^ ← rep;
};
PreRotate:
PUBLIC
PROC[m: Ref, a:
REAL] ~ {
rep: Rep ← m^;
SELECT a
FROM
0.0 => NULL;
90.0 => {
rep.a ← m.b;
rep.d ← m.e;
rep.b ← -m.a;
rep.e ← -m.d;
};
180.0 => {
rep.a ← -m.a;
rep.d ← -m.d;
rep.b ← -m.b;
rep.e ← -m.e;
};
-90.0, 270.0 => {
rep.a ← -m.b;
rep.d ← -m.e;
rep.b ← m.a;
rep.e ← m.d;
};
ENDCASE => {
cos: REAL ~ RealFns.CosDeg[a];
sin: REAL ~ RealFns.SinDeg[a];
rep.a ← cos*m.a + sin*m.b;
rep.d ← cos*m.d + sin*m.e;
rep.b ← cos*m.b - sin*m.a;
rep.e ← cos*m.e - sin*m.d;
};
m^ ← rep;
};
PreTranslate:
PUBLIC
PROC[m: Ref, x, y:
REAL] ~ {
rep: Rep ← m^;
rep.c ← x*m.a + y*m.b + m.c;
rep.f ← x*m.d + y*m.e + m.f;
m^ ← rep;
};
PostMultiply:
PUBLIC
PROC[m, post: Ref] ~ {
rep: Rep ← m^;
rep.a ← m.a*post.a+m.d*post.b;
rep.b ← m.b*post.a+m.e*post.b;
rep.c ← m.c*post.a+m.f*post.b+post.c;
rep.d ← m.a*post.d+m.d*post.e;
rep.e ← m.b*post.d+m.e*post.e;
rep.f ← m.c*post.d+m.f*post.e+post.f;
m^ ← rep;
};
PostScale:
PUBLIC
PROC[m: Ref, s:
REAL] ~ {
rep: Rep ← m^;
rep.a ← m.a*s;
rep.b ← m.b*s;
rep.c ← m.c*s;
rep.d ← m.d*s;
rep.e ← m.e*s;
rep.f ← m.f*s;
m^ ← rep;
};
PostScale2:
PUBLIC
PROC[m: Ref, sx, sy:
REAL] ~ {
rep: Rep ← m^;
rep.a ← m.a*sx;
rep.b ← m.b*sx;
rep.c ← m.c*sx;
rep.d ← m.d*sy;
rep.e ← m.e*sy;
rep.f ← m.f*sy;
m^ ← rep;
};
PostRotate:
PUBLIC
PROC[m: Ref, a:
REAL] ~ {
rep: Rep ← m^;
SELECT a
FROM
0.0 => NULL;
90.0 => {
rep.a ← m.b;
rep.b ← -m.a;
rep.d ← m.e;
rep.e ← -m.d;
};
180.0 => {
rep.a ← -m.a;
rep.b ← -m.b;
rep.d ← -m.d;
rep.e ← -m.e;
};
-90.0, 270.0 => {
rep.a ← -m.b;
rep.b ← m.a;
rep.d ← -m.e;
rep.e ← m.d;
};
ENDCASE => {
cos: REAL ~ RealFns.CosDeg[a];
sin: REAL ~ RealFns.SinDeg[a];
rep.a ← m.a*cos + m.b*sin;
rep.b ← m.b*cos - m.a*sin;
rep.d ← m.d*cos + m.e*sin;
rep.e ← m.e*cos - m.d*sin;
};
m^ ← rep;
};
PostTranslate:
PUBLIC
PROC[m: Ref, x, y:
REAL] ~ {
rep: Rep ← m^;
rep.c ← m.c+x;
rep.f ← m.f+y;
m^ ← rep;
};
Transform:
PUBLIC
PROC[m: Ref, v:
VEC]
RETURNS[
VEC] ~ {
RETURN[[v.x*m.a + v.y*m.b + m.c, v.x*m.d + v.y*m.e + m.f]];
};
TransformVec:
PUBLIC
PROC[m: Ref, v:
VEC]
RETURNS[
VEC] ~ {
RETURN[[v.x*m.a + v.y*m.b, v.x*m.d + v.y*m.e]];
};
InverseTransform:
PUBLIC
PROC[m: Ref, v:
VEC]
RETURNS[
VEC] ~ {
RETURN[InverseTransformVec[m, [v.x-m.c, v.y-m.f]]];
};
InverseTransformVec:
PUBLIC
PROC[m: Ref, v:
VEC]
RETURNS[
VEC] ~ {
det: REAL ~ m.a*m.e - m.b*m.d;
RETURN[[x: (v.x*m.e - v.y*m.b)/det, y: (v.y*m.a - v.x*m.d)/det]];
};
Rectangle: TYPE ~ ImagerTransform.Rectangle;
IntRectangle: TYPE ~ ImagerTransform.IntRectangle;
TransformRectangle:
PUBLIC
PROC[m: Ref, rect: Rectangle]
RETURNS[Rectangle] ~ {
w: VEC ← [rect.w*m.a, rect.w*m.d];
h: VEC ← [rect.h*m.b, rect.h*m.e];
p0: VEC ← Transform[m, [rect.x, rect.y]];
p1: VEC ← p0.InlineAdd[w];
p2: VEC ← p1.InlineAdd[h];
p3: VEC ← p0.InlineAdd[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[m: Ref, rect: Rectangle]
RETURNS[IntRectangle] ~ {
new: Rectangle ← TransformRectangle[m, rect];
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:
VEC]
RETURNS[
BOOLEAN] ~ {
RETURN [ABS[p.x-q.x] <= 0.25 AND ABS[p.y-q.y] <= 0.25]
};
CloseEnough:
PUBLIC
PROC[s, t: Ref, rangeSize:
REAL]
RETURNS[
BOOLEAN] ~ {
p00: VEC ← Transform[s, InverseTransform[t, [0, 0]]];
p10: VEC ← Transform[s, InverseTransform[t, [rangeSize, 0]]];
p01: VEC ← Transform[s, InverseTransform[t, [0, rangeSize]]];
RETURN [
WithinQuarterPixel[p00, [0, 0]] AND
WithinQuarterPixel[p10, [rangeSize, 0]] AND
WithinQuarterPixel[p10, [0, rangeSize]]
]
};
CloseToTranslation:
PUBLIC
PROC[s, t: Ref, rangeSize:
REAL]
RETURNS[
BOOLEAN] ~ {
p10: VEC ← TransformVec[s, InverseTransformVec[t, [rangeSize, 0]]];
p01: VEC ← TransformVec[s, InverseTransformVec[t, [0, rangeSize]]];
RETURN [
WithinQuarterPixel[p10, [rangeSize, 0]] AND
WithinQuarterPixel[p10, [0, rangeSize]]
]
};
SingularValues:
PUBLIC
PROC[m: Ref]
RETURNS[
VEC] ~ {
t1: REAL ← m.a*m.a + m.d*m.d;
t2: REAL ← m.b*m.b + m.e*m.e;
t3: REAL ← t1 - t2;
t4: REAL ← Real.FScale[m.a*m.b + m.d*m.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: Ref] ~ {
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:
VEC] ~ {
IF Different[p.x, q.x] THEN ERROR;
IF Different[p.y, q.y] THEN ERROR;
};
PreTrans:
PROC[x, y:
REAL, r: Ref]
RETURNS[Ref] ~ {
m: Ref ~ NEW[Rep ← r^];
PreTranslate[m, x, y];
RETURN[m];
};
PreRot:
PROC[a:
REAL, r: Ref]
RETURNS[Ref] ~ {
m: Ref ~ NEW[Rep ← r^];
PreRotate[m, a];
RETURN[m];
};
PreScl:
PROC[sx, sy:
REAL, r: Ref]
RETURNS[Ref] ~ {
m: Ref ~ NEW[Rep ← r^];
PreScale2[m, sx, sy];
RETURN[m];
};
SelfTest:
PROC[r: Ref] ~ {
p: VEC ← [3.14159, -265.35];
ShouldBeSameT[PreTrans[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[r, p], Transform[Invert[r], p]];
ShouldBeSamePt[InverseTransformVec[r, p], TransformVec[Invert[r], p]];
ShouldBeSamePt[TransformVec[r, TransformVec[r, p]], TransformVec[Concat[r, r], p]];
ShouldBeSamePt[SingularValues[r], SingularValues[PreRot[33.5, r]]];
};
SelfTest[PreTrans[12, 6, PreScl[1, 5, Rotate[61]]]];
SelfTest[Create[1, 2, 4, 9, 3, 1]];
SelfTest[Create[3, 1, 4, 1, 5, 9]];
END.