ImagerTransformationImpl.mesa
Copyright © 1984, 1985 by Xerox Corporation. All rights reserved.
Michael Plass, June 13, 1985 1:50:50 pm PDT
Doug Wyatt, May 22, 1985 1:01:04 pm PDT
DIRECTORY
Imager USING [Error, ErrorDesc],
ImagerTransformation USING [FactoredTransformation, Rectangle, Transformation, TransformationRep],
Real USING [FScale, RoundI, RoundLI],
RealFns USING [ArcTanDeg, CosDeg, SinDeg, SqRt],
Vector2 USING [VEC];
ImagerTransformationImpl: CEDAR PROGRAM
IMPORTS Imager, Real, RealFns
EXPORTS ImagerTransformation
~ BEGIN OPEN ImagerTransformation;
VEC: TYPE ~ Vector2.VEC;
N.B. The leftmost transformation in any concatenation or pre-transformation may have a form bigger than 10, which is carried along. Any other use of such a form is an error. The intent is to allow the interpress master writer to support a limited implementation of GetT and SetT, so that the path ImagerInterpress -> interpress -> Interpress -> imager -> ImagerInterpress works.
formErrorDesc: Imager.ErrorDesc ~ [code: $invalidTransformation, explanation: "Transformation has illegal form"];
Validate: PROC [m: Transformation] ~ {
form: NAT ← 0;
tx, ty: INTEGER ← 0;
IF m.form > 10 THEN form ← m.form
ELSE IF m.b=0 AND m.d=0 THEN { form ← 1;
IF m.a=+1 THEN { IF m.e=+1 THEN form ← 3 ELSE IF m.e=-1 THEN form ← 4 }
ELSE IF m.a=-1 THEN { IF m.e=+1 THEN form ← 5 ELSE IF m.e=-1 THEN form ← 6 };
}
ELSE IF m.a=0 AND m.e=0 THEN { form ← 2;
IF m.b=+1 THEN { IF m.d=+1 THEN form ← 7 ELSE IF m.d=-1 THEN form ← 8 }
ELSE IF m.b=-1 THEN { IF m.d=+1 THEN form ← 9 ELSE IF m.d=-1 THEN form ← 10 };
};
IF ABS[m.c]<=INTEGER.LAST/2 THEN tx ← Real.RoundI[m.c];
IF ABS[m.f]<=INTEGER.LAST/2 THEN ty ← Real.RoundI[m.f];
m.form ← form;
m.tx ← tx; m.ty ← ty;
m.integerTrans ← (tx=m.c AND ty=m.f);
};
Create: PUBLIC PROC [a, b, c, d, e, f: REAL] RETURNS [Transformation] ~ {
new: Transformation ~ NEW[TransformationRep ← [a: a, b: b, c: c, d: d, e: e, f: f]];
Validate[new]; RETURN[new];
};
Scale: PUBLIC PROC [s: REAL] RETURNS [Transformation] ~ {
new: Transformation ~ NEW[TransformationRep ← [a: s, b: 0, c: 0, d: 0, e: s, f: 0]];
Validate[new]; RETURN[new];
};
Scale2: PUBLIC PROC [s: VEC] RETURNS [Transformation] ~ {
new: Transformation ~ NEW[TransformationRep ← [a: s.x, b: 0, c: 0, d: 0, e: s.y, f: 0]];
Validate[new]; RETURN[new];
};
Rotate: PUBLIC PROC [r: REAL] RETURNS [Transformation] ~ {
cos: REAL ~ RealFns.CosDeg[r];
sin: REAL ~ RealFns.SinDeg[r];
new: Transformation ~ NEW[TransformationRep ← [a: cos, b: -sin, c: 0, d: sin, e: cos, f: 0]];
Validate[new]; RETURN[new];
};
Translate: PUBLIC PROC [t: VEC] RETURNS [Transformation] ~ {
new: Transformation ~ NEW[TransformationRep ← [a: 1, b: 0, c: t.x, d: 0, e: 1, f: t.y]];
Validate[new]; RETURN[new];
};
Copy: PUBLIC PROC [m: Transformation] RETURNS [Transformation] ~ {
IF m=NIL THEN RETURN[Scale[1]]
ELSE RETURN[NEW[TransformationRep ← m^]];
};
Concat: PUBLIC PROC [m, n: Transformation] RETURNS [Transformation] ~ {
new: Transformation ~ Copy[m];
ApplyPostConcat[new, n]; RETURN[new];
};
PreScale: PUBLIC PROC [m: Transformation, s: REAL] RETURNS [Transformation] ~ {
new: Transformation ~ Copy[m];
ApplyPreScale[new, s]; RETURN[new];
};
PreScale2: PUBLIC PROC [m: Transformation, s: VEC] RETURNS [Transformation] ~ {
new: Transformation ~ Copy[m];
ApplyPreScale2[new, s]; RETURN[new];
};
PreRotate: PUBLIC PROC [m: Transformation, r: REAL] RETURNS [Transformation] ~ {
new: Transformation ~ Copy[m];
ApplyPreRotate[new, r]; RETURN[new];
};
PreTranslate: PUBLIC PROC [m: Transformation, t: VEC] RETURNS [Transformation] ~ {
new: Transformation ~ Copy[m];
ApplyPreTranslate[new, t]; RETURN[new];
};
PostScale: PUBLIC PROC [m: Transformation, s: REAL] RETURNS [Transformation] ~ {
new: Transformation ~ Copy[m];
ApplyPostScale[new, s]; RETURN[new];
};
PostScale2: PUBLIC PROC [m: Transformation, s: VEC] RETURNS [Transformation] ~ {
new: Transformation ~ Copy[m];
ApplyPostScale2[new, s]; RETURN[new];
};
PostRotate: PUBLIC PROC [m: Transformation, r: REAL] RETURNS [Transformation] ~ {
new: Transformation ~ Copy[m];
ApplyPostRotate[new, r]; RETURN[new];
};
PostTranslate: PUBLIC PROC [m: Transformation, t: VEC] RETURNS [Transformation] ~ {
new: Transformation ~ Copy[m];
ApplyPostTranslate[new, t]; RETURN[new];
};
TranslateTo: PUBLIC PROC [m: Transformation, t: VEC] RETURNS [Transformation] ~ {
new: Transformation ~ Copy[m];
ApplyTranslateTo[new, t]; RETURN[new];
};
Cat: PUBLIC PROC [m1, m2, m3, m4: Transformation ← NIL] RETURNS [Transformation] ~ {
new: Transformation ~ NEW[TransformationRep];
ApplyCat[new, m1, m2, m3, m4]; RETURN[new];
};
ApplyPreConcat: PUBLIC PROC [m, p: Transformation] ~ {
old: TransformationRep ~ m^;
IF p=NIL THEN RETURN;
IF p.form > 10 THEN ERROR Imager.Error[formErrorDesc];
m.a ← p.a*old.a + p.d*old.b; m.d ← p.a*old.d + p.d*old.e;
m.b ← p.b*old.a + p.e*old.b; m.e ← p.b*old.d + p.e*old.e;
m.c ← p.c*old.a + p.f*old.b + old.c; m.f ← p.c*old.d + p.f*old.e + old.f;
Validate[m];
};
ApplyPreScale: PUBLIC PROC [m: Transformation, s: REAL] ~ {
old: TransformationRep ~ m^;
m.a ← s*old.a;  m.d ← s*old.d;
m.b ← s*old.b;  m.e ← s*old.e;
Validate[m];
};
ApplyPreScale2: PUBLIC PROC [m: Transformation, s: VEC] ~ {
old: TransformationRep ~ m^;
m.a ← s.x*old.a;  m.d ← s.x*old.d;
m.b ← s.y*old.b;  m.e ← s.y*old.e;
Validate[m];
};
ApplyPreRotate: PUBLIC PROC [m: Transformation, r: REAL] ~ {
old: TransformationRep ~ m^;
cos: REAL ~ RealFns.CosDeg[r];
sin: REAL ~ RealFns.SinDeg[r];
m.a ← cos*old.a + sin*old.b; m.d ← cos*old.d + sin*old.e;
m.b ← cos*old.b - sin*old.a; m.e ← cos*old.e - sin*old.d;
Validate[m];
};
ApplyPreTranslate: PUBLIC PROC [m: Transformation, t: VEC] ~ {
old: TransformationRep ~ m^;
m.c ← t.x*old.a + t.y*old.b + old.c; m.f ← t.x*old.d + t.y*old.e + old.f;
Validate[m];
};
ApplyPostConcat: PUBLIC PROC [m, p: Transformation] ~ {
old: TransformationRep ~ m^;
IF p=NIL THEN RETURN;
IF m.form > 10 THEN ERROR Imager.Error[formErrorDesc];
m.form ← p.form;
m.a ← old.a*p.a + old.d*p.b; m.d ← old.a*p.d + old.d*p.e;
m.b ← old.b*p.a + old.e*p.b; m.e ← old.b*p.d + old.e*p.e;
m.c ← old.c*p.a + old.f*p.b + p.c; m.f ← old.c*p.d + old.f*p.e + p.f;
Validate[m];
};
ApplyPostScale: PUBLIC PROC [m: Transformation, s: REAL] ~ {
old: TransformationRep ~ m^;
IF m.form > 10 THEN ERROR Imager.Error[formErrorDesc];
m.a ← old.a*s;  m.d ← old.d*s;
m.b ← old.b*s;  m.e ← old.e*s;
m.c ← old.c*s;  m.f ← old.f*s;
Validate[m];
};
ApplyPostScale2: PUBLIC PROC [m: Transformation, s: VEC] ~ {
old: TransformationRep ~ m^;
IF m.form > 10 THEN ERROR Imager.Error[formErrorDesc];
m.a ← old.a*s.x;  m.d ← old.d*s.y;
m.b ← old.b*s.x;  m.e ← old.e*s.y;
m.c ← old.c*s.x;  m.f ← old.f*s.y;
Validate[m];
};
ApplyPostRotate: PUBLIC PROC [m: Transformation, r: REAL] ~ {
old: TransformationRep ~ m^;
cos: REAL ~ RealFns.CosDeg[r];
sin: REAL ~ RealFns.SinDeg[r];
IF m.form > 10 THEN ERROR Imager.Error[formErrorDesc];
m.a ← old.a*cos - old.d*sin; m.d ← old.a*sin + old.d*cos;
m.b ← old.b*cos - old.e*sin; m.e ← old.b*sin + old.e*cos;
m.c ← old.c*cos - old.f*sin; m.f ← old.c*sin + old.f*cos;
Validate[m];
};
ApplyPostTranslate: PUBLIC PROC [m: Transformation, t: VEC] ~ {
old: TransformationRep ~ m^;
IF m.form > 10 THEN ERROR Imager.Error[formErrorDesc];
m.c ← old.c+t.x;  m.f ← old.f+t.y;
Validate[m];
};
ApplyTranslateTo: PUBLIC PROC [m: Transformation, t: VEC] ~ {
tx, ty: INTEGER ← 0;
IF m.form > 10 THEN ERROR Imager.Error[formErrorDesc];
IF ABS[t.x]<=INTEGER.LAST/2 THEN tx ← Real.RoundI[t.x];
IF ABS[t.y]<=INTEGER.LAST/2 THEN ty ← Real.RoundI[t.y];
m.c ← t.x; m.f ← t.y;
m.tx ← tx; m.ty ← ty;
m.integerTrans ← (tx=t.x AND ty=t.y);
};
ApplyCat: PUBLIC PROC [m: Transformation, m1, m2, m3, m4: Transformation ← NIL] ~ {
count: NAT ← 0;
Apply: PROC [p: Transformation] ~ {
IF count=0 THEN m^ ← p^ ELSE ApplyPostConcat[m, p];
count ← count+1;
};
IF m1#NIL THEN Apply[m1];
IF m2#NIL THEN Apply[m2];
IF m3#NIL THEN Apply[m3];
IF m4#NIL THEN Apply[m4];
IF count=0 THEN { m^ ← [a: 1, b: 0, c: 0, d: 0, e: 1, f: 0]; Validate[m] };
};
Invert: PUBLIC PROC [m: Transformation] RETURNS [Transformation] ~ {
det: REAL ~ m.a*m.e - m.d*m.b;
a: REAL ~ m.e/det;  d: REAL ~ -m.d/det;
b: REAL ~ -m.b/det;  e: REAL ~ m.a/det;
c: REAL ~ (m.b*m.f - m.e*m.c)/det; f: REAL ~ (m.d*m.c - m.a*m.f)/det;
RETURN[Create[a: a, b: b, c: c, d: d, e: e, f: f]];
};
Transform: PUBLIC PROC [m: Transformation, v: VEC] RETURNS [VEC] ~ {
IF m=NIL THEN RETURN[v];
SELECT m.form FROM
0 => RETURN[[v.x*m.a + v.y*m.b + m.c, v.x*m.d + v.y*m.e + m.f]];
1 => RETURN[[v.x*m.a + m.c, v.y*m.e + m.f]];
2 => RETURN[[v.y*m.b + m.c, v.x*m.d + m.f]];
3 => RETURN[[m.c + v.x, m.f + v.y]];
4 => RETURN[[m.c + v.x, m.f - v.y]];
5 => RETURN[[m.c - v.x, m.f + v.y]];
6 => RETURN[[m.c - v.x, m.f - v.y]];
7 => RETURN[[m.c + v.y, m.f + v.x]];
8 => RETURN[[m.c + v.y, m.f - v.x]];
9 => RETURN[[m.c - v.y, m.f + v.x]];
10 => RETURN[[m.c - v.y, m.f - v.x]];
ENDCASE => ERROR Imager.Error[formErrorDesc];
};
TransformVec: PUBLIC PROC [m: Transformation, v: VEC] RETURNS [VEC] ~ {
IF m=NIL THEN RETURN[v];
SELECT m.form FROM
0 => RETURN[[v.x*m.a + v.y*m.b, v.x*m.d + v.y*m.e]];
1 => RETURN[[v.x*m.a, v.y*m.e]]; -- b=0, d=0
2 => RETURN[[v.y*m.b, v.x*m.d]]; -- a=0, e=0
3 => RETURN[[+ v.x, + v.y]]; -- a=+1, b=0, d=0, e=+1
4 => RETURN[[+ v.x, - v.y]]; -- a=+1, b=0, d=0, e=-1
5 => RETURN[[- v.x, + v.y]]; -- a=-1, b=0, d=0, e=+1
6 => RETURN[[- v.x, - v.y]]; -- a=-1, b=0, d=0, e=-1
7 => RETURN[[+ v.y, + v.x]]; -- a=0, b=+1, d=+1, e=0
8 => RETURN[[+ v.y, - v.x]]; -- a=0, b=+1, d=-1, e=0
9 => RETURN[[- v.y, + v.x]]; -- a=0, b=-1, d=+1, e=0
10 => RETURN[[- v.y, - v.x]]; -- a=0, b=-1, d=-1, e=0
ENDCASE => ERROR Imager.Error[formErrorDesc];
};
InverseTransform: PUBLIC PROC [m: Transformation, v: VEC] RETURNS [VEC] ~ {
IF m=NIL THEN RETURN[v];
RETURN[InverseTransformVec[m, [v.x - m.c, v.y - m.f]]];
};
InverseTransformVec: PUBLIC PROC [m: Transformation, v: VEC] RETURNS [VEC] ~ {
IF m=NIL THEN RETURN[v];
SELECT m.form FROM
0 => { D: REAL ~ m.a*m.e-m.b*m.d; RETURN[[(v.x*m.e-v.y*m.b)/D, (v.y*m.a-v.x*m.d)/D]] };
1 => RETURN[[v.x/m.a, v.y/m.e]]; -- b=0, d=0
2 => RETURN[[v.y/m.d, v.x/m.b]]; -- a=0, e=0
3 => RETURN[[+ v.x, + v.y]]; -- a=+1, b=0, d=0, e=+1
4 => RETURN[[+ v.x, - v.y]]; -- a=+1, b=0, d=0, e=-1
5 => RETURN[[- v.x, + v.y]]; -- a=-1, b=0, d=0, e=+1
6 => RETURN[[- v.x, - v.y]]; -- a=-1, b=0, d=0, e=-1
7 => RETURN[[+ v.y, + v.x]]; -- a=0, b=+1, d=+1, e=0
8 => RETURN[[- v.y, + v.x]]; -- a=0, b=+1, d=-1, e=0
9 => RETURN[[+ v.y, - v.x]]; -- a=0, b=-1, d=+1, e=0
10 => RETURN[[- v.y, - v.x]]; -- a=0, b=-1, d=-1, e=0
ENDCASE => ERROR Imager.Error[formErrorDesc];
};
InlineRound: PROC [x: REAL] RETURNS [REAL] ~ INLINE {
RETURN[IF ABS[x]<100000000B THEN REAL[Real.RoundLI[x]] ELSE x];
};
DRound: PUBLIC PROC [v: VEC] RETURNS [VEC] ~ {
RETURN[[InlineRound[v.x], InlineRound[v.y]]]
};
RoundXY: PUBLIC PROC [m: Transformation, v: VEC] RETURNS [VEC] ~ {
RETURN[InverseTransform[m, DRound[Transform[m, v]]]];
};
RoundXYVec: PUBLIC PROC [m: Transformation, v: VEC] RETURNS [VEC] ~ {
RETURN[InverseTransformVec[m, DRound[TransformVec[m, v]]]];
};
TransformRectangle: PUBLIC PROC [m: Transformation, r: Rectangle]
RETURNS
[Rectangle] ~ {
IF m=NIL THEN RETURN[r];
IF m.form=0 THEN {
p0: VEC ~ Transform[m, [r.x, r.y]];
p1: VEC ~ Transform[m, [r.x+r.w, r.y]];
p2: VEC ~ Transform[m, [r.x+r.w, r.y+r.h]];
p3: VEC ~ Transform[m, [r.x, r.y+r.h]];
xmin, xmax: REAL ← p0.x;
ymin, ymax: REAL ← p0.y;
IF p1.x<xmin THEN xmin ← p1.x ELSE IF p1.x>xmax THEN xmax ← p1.x;
IF p1.y<ymin THEN ymin ← p1.y ELSE IF p1.y>ymax THEN ymax ← p1.y;
IF p2.x<xmin THEN xmin ← p2.x ELSE IF p2.x>xmax THEN xmax ← p2.x;
IF p2.y<ymin THEN ymin ← p2.y ELSE IF p2.y>ymax THEN ymax ← p2.y;
IF p3.x<xmin THEN xmin ← p3.x ELSE IF p3.x>xmax THEN xmax ← p3.x;
IF p3.y<ymin THEN ymin ← p3.y ELSE IF p3.y>ymax THEN ymax ← p3.y;
RETURN[[x: xmin, y: ymin, w: xmax-xmin, h: ymax-ymin]];
}
ELSE {
p0: VEC ~ Transform[m, [r.x, r.y]];
p2: VEC ~ Transform[m, [r.x+r.w, r.y+r.h]];
xmin, xmax: REAL ← p0.x;
ymin, ymax: REAL ← p0.y;
IF p2.x<xmin THEN xmin ← p2.x ELSE IF p2.x>xmax THEN xmax ← p2.x;
IF p2.y<ymin THEN ymin ← p2.y ELSE IF p2.y>ymax THEN ymax ← p2.y;
RETURN[[x: xmin, y: ymin, w: xmax-xmin, h: ymax-ymin]];
};
};
InverseTransformRectangle: PUBLIC PROC [m: Transformation, r: Rectangle]
RETURNS
[Rectangle] ~ {
IF m=NIL THEN RETURN[r];
IF m.form=0 THEN {
p0: VEC ~ InverseTransform[m, [r.x, r.y]];
p1: VEC ~ InverseTransform[m, [r.x+r.w, r.y]];
p2: VEC ~ InverseTransform[m, [r.x+r.w, r.y+r.h]];
p3: VEC ~ InverseTransform[m, [r.x, r.y+r.h]];
xmin, xmax: REAL ← p0.x;
ymin, ymax: REAL ← p0.y;
IF p1.x<xmin THEN xmin ← p1.x ELSE IF p1.x>xmax THEN xmax ← p1.x;
IF p1.y<ymin THEN ymin ← p1.y ELSE IF p1.y>ymax THEN ymax ← p1.y;
IF p2.x<xmin THEN xmin ← p2.x ELSE IF p2.x>xmax THEN xmax ← p2.x;
IF p2.y<ymin THEN ymin ← p2.y ELSE IF p2.y>ymax THEN ymax ← p2.y;
IF p3.x<xmin THEN xmin ← p3.x ELSE IF p3.x>xmax THEN xmax ← p3.x;
IF p3.y<ymin THEN ymin ← p3.y ELSE IF p3.y>ymax THEN ymax ← p3.y;
RETURN[[x: xmin, y: ymin, w: xmax-xmin, h: ymax-ymin]];
}
ELSE IF m.form <= 10 THEN {
p0: VEC ~ InverseTransform[m, [r.x, r.y]];
p2: VEC ~ InverseTransform[m, [r.x+r.w, r.y+r.h]];
xmin, xmax: REAL ← p0.x;
ymin, ymax: REAL ← p0.y;
IF p2.x<xmin THEN xmin ← p2.x ELSE IF p2.x>xmax THEN xmax ← p2.x;
IF p2.y<ymin THEN ymin ← p2.y ELSE IF p2.y>ymax THEN ymax ← p2.y;
RETURN[[x: xmin, y: ymin, w: xmax-xmin, h: ymax-ymin]];
}
ELSE ERROR Imager.Error[formErrorDesc];
};
RestrictAbsTo: PROC [limit: REAL, real: REAL] RETURNS [REAL] ~ {
Like a MOD, but almost symmetric around zero.
limit ← ABS[limit];
WHILE real < -limit DO real ← real + 2*limit ENDLOOP;
WHILE real >= limit DO real ← real - 2*limit ENDLOOP;
RETURN[real]
};
NumericalInstability: SIGNAL[real: REAL] ~ CODE;
maxRelError: REAL ← 1.0e-4;
Factor: PUBLIC PROC [m: Transformation] RETURNS [FactoredTransformation] ~ {
IF m=NIL THEN RETURN[[r1: 0, s: [1, 1], r2: 0, t: [0, 0]]];
SELECT m.form FROM
0 => {
t: TransformationRep ~ m^;
theta: REAL ~ RestrictAbsTo[45,
0.5*RealFns.ArcTanDeg[2*(t.a*t.b+t.d*t.e), (t.a*t.a-t.b*t.b+t.d*t.d-t.e*t.e)]];
cosTheta: REAL ~ RealFns.CosDeg[theta];
sinTheta: REAL ~ RealFns.SinDeg[theta];
aa: REAL ~ cosTheta*t.a + sinTheta*t.b;
bb: REAL ~ -sinTheta*t.a + cosTheta*t.b;
dd: REAL ~ cosTheta*t.d + sinTheta*t.e;
ee: REAL ~ -sinTheta*t.d + cosTheta*t.e;
phi: REAL ~ RestrictAbsTo[90,
IF ABS[aa]>ABS[ee] THEN RealFns.ArcTanDeg[-dd, aa] ELSE RealFns.ArcTanDeg[bb, ee]];
cosPhi: REAL ~ RealFns.CosDeg[phi];
sinPhi: REAL ~ RealFns.SinDeg[phi];
a: REAL ~ aa*cosPhi - dd*sinPhi;
b: REAL ~ bb*cosPhi - ee*sinPhi;
d: REAL ~ aa*sinPhi + dd*cosPhi;
e: REAL ~ bb*sinPhi + ee*cosPhi;
mag: REAL ~ ABS[a]+ABS[d];
IF ABS[b]+ABS[d] > maxRelError*mag THEN SIGNAL NumericalInstability[ABS[b]+ABS[d]];
RETURN[[r1: -theta, s: [a, e], r2: -phi, t: [t.c, t.f]]]
};
1 => RETURN[[r1: 0, s: [m.a, m.e], r2: 0, t: [m.c, m.f]]];
2 => {
IF m.b > 0 THEN RETURN[[r1: 0, s: [-m.d, m.b], r2: -90, t: [m.c, m.f]]]
ELSE RETURN[[r1: 0, s: [m.d, -m.b], r2: 90, t: [m.c, m.f]]];
};
3 => RETURN[[r1: 0, s: [1, 1], r2: 0, t: [m.c, m.f]]];
4 => RETURN[[r1: 0, s: [1, -1], r2: 0, t: [m.c, m.f]]];
5 => RETURN[[r1: 0, s: [-1, 1], r2: 0, t: [m.c, m.f]]];
6 => RETURN[[r1: 0, s: [-1, -1], r2: 0, t: [m.c, m.f]]];
7 => RETURN[[r1: 0, s: [1, -1], r2: 90, t: [m.c, m.f]]];
8 => RETURN[[r1: 0, s: [1, 1], r2: -90, t: [m.c, m.f]]];
9 => RETURN[[r1: 0, s: [1, 1], r2: 90, t: [m.c, m.f]]];
10 => RETURN[[r1: 0, s: [-1, 1], r2: 90, t: [m.c, m.f]]];
ENDCASE => ERROR Imager.Error[formErrorDesc];
};
Equal: PUBLIC PROC [s, t: Transformation] RETURNS [BOOL] ~ {
RETURN [s.a=t.a AND s.b=t.b AND s.c=t.c AND s.d=t.d AND s.e=t.e AND s.f=t.f];
};
WithinQuarterPixel: PROC [p, q: VEC] RETURNS [BOOL] ~ {
RETURN [ABS[p.x-q.x] <= 0.25 AND ABS[p.y-q.y] <= 0.25]
};
CloseEnough: PUBLIC PROC [s, t: Transformation, range: REAL] RETURNS [BOOL] ~ {
p00: VEC ← Transform[s, InverseTransform[t, [0, 0]]];
p10: VEC ← Transform[s, InverseTransform[t, [range, 0]]];
p01: VEC ← Transform[s, InverseTransform[t, [0, range]]];
RETURN [
WithinQuarterPixel[p00, [0, 0]] AND
WithinQuarterPixel[p10, [range, 0]] AND
WithinQuarterPixel[p10, [0, range]]
]
};
CloseToTranslation: PUBLIC PROC [s, t: Transformation, range: REAL] RETURNS [BOOL] ~ {
p10: VEC ← TransformVec[s, InverseTransformVec[t, [range, 0]]];
p01: VEC ← TransformVec[s, InverseTransformVec[t, [0, range]]];
RETURN [
WithinQuarterPixel[p10, [range, 0]] AND
WithinQuarterPixel[p10, [0, range]]
]
};
SingularValues: PUBLIC PROC [m: Transformation] 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 ← RealFns.SqRt[t3*t3+t4*t4];
x: REAL ← RealFns.SqRt[Real.FScale[t1+t2+s, -1]];
y: REAL ← RealFns.SqRt[Real.FScale[t1+t2-s, -1]];
RETURN[[x, y]]
};
fuzz: REAL ← 2.0e-6;
Different: PROC [a, b: REAL] RETURNS [BOOL] ~ {
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: VEC] ~ {
IF Different[p.x, q.x] THEN ERROR;
IF Different[p.y, q.y] THEN ERROR;
};
TestTransformation: PROC [r: Transformation] ~ {
p: VEC ← [3.14159, -265.35];
ShouldBeSameT[PreTranslate[r, p], Concat[Translate[p], r]];
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[PreRotate[r, 33.5]]];
};
SelfTest: PROC ~ {
ShouldBeSameT[Rotate[90], Concat[Rotate[45], Rotate[45]]];
TestTransformation[PreTranslate[PreScale2[Rotate[61], [1, 5]], [12, 6]]];
TestTransformation[Create[1, 2, 4, 9, 3, 1]];
TestTransformation[Create[3, 1, 4, 1, 5, 9]];
TestTransformation[Create[2, 0, 0, 0, 2, 0]];
TestTransformation[Create[0, 3, 0, 4, 0, 0]];
TestTransformation[Create[1, 0, 0, 0, 1, 0]];
TestTransformation[Create[1, 0, 0, 0, -1, 808]];
TestTransformation[Create[-1, 0, 0, 0, 1, 0]];
TestTransformation[Create[-1, 0, 0, 0, -1, 0]];
TestTransformation[Create[0, 1, 0, 1, 0, 0]];
TestTransformation[Create[0, 1, 0, -1, 0, 0]];
TestTransformation[Create[0, -1, 0, 1, 0, 0]];
TestTransformation[Create[0, -1, 0, -1, 0, 0]];
};
TransformRectangle: PUBLIC PROC [m: Transformation, 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: Transformation, 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]]
};
END.