Rectangle: TYPE ~ ImagerBox.Rectangle;
ErrorDesc: TYPE ~ ImagerError.ErrorDesc;
bogusForm: NAT = NAT.LAST;
Bitch:
PROC [m: Transformation] = {
ERROR ImagerError.Error[[$illegalTransformation, "Transformation has illegal form", LIST[[$transformation, m]]]];
};
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.
CheckForm:
PROC [m: Transformation] ~
INLINE {
IF m.form > 10 THEN Bitch[m];
};
CISDeg:
PUBLIC
PROC [degrees:
REAL]
RETURNS [
VEC] ~ {
Computes the cos (x) and sin (y) of a value in degrees. Careful to get multiples of 90 exact.
i: INT ~ Real.Round[degrees/90.0];
d: REAL ~ degrees-(i*90.0);
cosd: REAL ~ IF d = 0.0 THEN 1.0 ELSE RealFns.CosDeg[d];
sind: REAL ~ IF d = 0.0 THEN 0.0 ELSE RealFns.SinDeg[d];
SELECT
LOOPHOLE[i,
CARD]
MOD 4
FROM
0 => RETURN [[cosd, sind]];
1 => RETURN [[-sind, cosd]];
2 => RETURN [[-cosd, -sind]];
3 => RETURN [[sind, -cosd]];
ENDCASE => ERROR;
};
Unit:
PROC [r:
REAL]
RETURNS [
BOOL] ~
INLINE {
Tests for r=1.0, but allows a small amount of floating-point fuzz.
RETURN [r+16.0 = 17.0]
};
floatHalfLastInt:
REAL ¬
INTEGER.
LAST/2;
Validate:
PROC [m: Transformation] ~ {
form: NAT ¬ 0;
integerTrans: BOOL ¬ TRUE;
tx, ty: INTEGER ¬ 0;
SELECT
TRUE
FROM
m.form > 10 => form ¬ m.form;
(m.b=0
AND m.d=0) => {
form ¬ 1;
IF Unit[m.a] THEN { IF Unit[m.e] THEN form ¬ 3 ELSE IF Unit[-m.e] THEN form ¬ 4 }
ELSE IF Unit[-m.a] THEN { IF Unit[m.e] THEN form ¬ 5 ELSE IF Unit[-m.e] THEN form ¬ 6 };
};
(m.a=0
AND m.e=0) => {
form ¬ 2;
IF Unit[m.b] THEN { IF Unit[m.d] THEN form ¬ 7 ELSE IF Unit[-m.d] THEN form ¬ 8 }
ELSE IF Unit[-m.b] THEN { IF Unit[m.d] THEN form ¬ 9 ELSE IF Unit[-m.d] THEN form ¬ 10 };
};
ENDCASE => NULL;
IF ABS[m.c]<=floatHalfLastInt THEN tx ¬ Real.Floor[m.c+0.5] ELSE integerTrans ¬ FALSE;
IF ABS[m.f]<=floatHalfLastInt THEN ty ¬ Real.Floor[m.f+0.5] ELSE integerTrans ¬ FALSE;
m.form ¬ form;
m.tx ¬ tx; m.ty ¬ ty;
m.integerTrans ¬ integerTrans;
};
nScratchMax: NAT ~ 5;
nScratch: NAT ¬ 0;
scratch:
ARRAY [0..nScratchMax)
OF Transformation ¬
ALL[
NIL];
alwaysAlloc: BOOL ¬ FALSE;
New:
ENTRY
PROC [rep: TransformationRep]
RETURNS [Transformation] ~ {
m: Transformation ¬ NIL;
IF nScratch = 0
OR alwaysAlloc
THEN TRUSTED { m ¬ SafeStorage.GetUntracedZone[].NEW[TransformationRep] }
ELSE {nScratch ¬ nScratch-1; m ¬ scratch[nScratch]; scratch[nScratch] ¬ NIL};
m ¬ rep;
RETURN [m]
};
MultipleReleaseOfScratch: ERROR ~ CODE;
Destroy:
PUBLIC ENTRY
PROC [m: Transformation] ~ {
IF m = NIL THEN RETURN WITH ERROR MultipleReleaseOfScratch;
IF m.form = bogusForm THEN RETURN WITH ERROR MultipleReleaseOfScratch;
FOR i:
NAT
IN [0..nScratch)
DO
IF m = scratch[i] THEN RETURN WITH ERROR MultipleReleaseOfScratch;
ENDLOOP;
m.a ¬ m.b¬ m.c ¬ m.d ¬ m.e ¬ m.f ¬ Real.TrappingNaN;
m.form ¬ bogusForm; m.integerTrans ¬ FALSE;
IF nScratch < nScratchMax THEN { scratch[nScratch] ¬ m; nScratch ¬ nScratch + 1 };
};
Create:
PUBLIC
PROC [a, b, c, d, e, f:
REAL]
RETURNS [Transformation] ~ {
new: Transformation ~ New[[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[[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[[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] ~ {
cis: VEC ~ CISDeg[r];
new: Transformation ~ New[[a: cis.x, b: -cis.y, c: 0, d: cis.y, e: cis.x, f: 0]];
Validate[new]; RETURN[new];
};
Translate:
PUBLIC
PROC [t:
VEC]
RETURNS [Transformation] ~ {
new: Transformation ~ New[[a: 1, b: 0, c: t.x, d: 0, e: 1, f: t.y]];
Validate[new]; RETURN[new];
};
SFToXY:
PUBLIC
PROC [scanMode: ScanMode, sSize, fSize:
INT]
RETURNS [Transformation] ~ {
a: REAL ~ SELECT scanMode.slow FROM right => 1.0, left => -1.0, ENDCASE => 0.0;
b: REAL ~ SELECT scanMode.fast FROM right => 1.0, left => -1.0, ENDCASE => 0.0;
d: REAL ~ SELECT scanMode.slow FROM up => 1.0, down => -1.0, ENDCASE => 0.0;
e: REAL ~ SELECT scanMode.fast FROM up => 1.0, down => -1.0, ENDCASE => 0.0;
tX: REAL ~ MAX[-(a*sSize + b*fSize), 0.0];
tY: REAL ~ MAX[-(d*sSize + e*fSize), 0.0];
RETURN[Create[a, b, tX, d, e, tY]];
};
XYToSF:
PUBLIC
PROC [scanMode: ScanMode, sSize, fSize:
INT]
RETURNS [Transformation] ~ {
m: Transformation ~ SFToXY[scanMode, sSize, fSize];
ApplyInvert[m];
RETURN[m];
};
Copy:
PUBLIC
PROC [m: Transformation]
RETURNS [Transformation] ~ {
IF m=NIL THEN RETURN [Scale[1]];
IF m.form = bogusForm THEN Bitch[m];
RETURN[New[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];
};
Invert:
PUBLIC
PROC [m: Transformation]
RETURNS [Transformation] ~ {
new: Transformation ~ Copy[m];
ApplyInvert[new]; RETURN[new];
};
ApplyPreConcat:
PUBLIC
PROC [m, p: Transformation] ~ {
old: TransformationRep ~ m;
IF p=NIL THEN RETURN;
CheckForm[p];
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;
cis: VEC ~ CISDeg[r];
m.a ¬ cis.x*old.a + cis.y*old.b; m.d ¬ cis.x*old.d + cis.y*old.e;
m.b ¬ cis.x*old.b - cis.y*old.a; m.e ¬ cis.x*old.e - cis.y*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];
};
ApplySFToXY:
PUBLIC
PROC [m: Transformation, scanMode: ScanMode, sSize, fSize:
INT] ~ {
old: TransformationRep ~ m;
X: TYPE ~ PROC [r: REAL] RETURNS [REAL];
A: X~INLINE{RETURN [SELECT scanMode.slow FROM right => r, left => -r, ENDCASE => 0.0]};
B: X~INLINE{RETURN [SELECT scanMode.fast FROM right => r, left => -r, ENDCASE => 0.0]};
D: X~INLINE{RETURN [SELECT scanMode.slow FROM up => r, down => -r, ENDCASE => 0.0]};
E: X~INLINE{RETURN [SELECT scanMode.fast FROM up => r, down => -r, ENDCASE => 0.0]};
c: REAL ~ MAX[-(A[sSize] + B[fSize]), 0.0];
f: REAL ~ MAX[-(D[sSize] + E[fSize]), 0.0];
m.a ¬ A[old.a] + D[old.b];
m.b ¬ B[old.a] + E[old.b];
m.c ¬ c*old.a + f*old.b + old.c;
m.d ¬ A[old.d] + D[old.e];
m.e ¬ B[old.d] + E[old.e];
m.f ¬ c*old.d + f*old.e + old.f;
Validate[m];
};
ApplyPostConcat:
PUBLIC
PROC [m, p: Transformation] ~ {
old: TransformationRep ~ m;
IF p=NIL THEN RETURN;
CheckForm[m];
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;
CheckForm[m];
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;
CheckForm[m];
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;
cis: VEC ~ CISDeg[r];
CheckForm[m];
m.a ¬ old.a*cis.x - old.d*cis.y; m.d ¬ old.a*cis.y + old.d*cis.x;
m.b ¬ old.b*cis.x - old.e*cis.y; m.e ¬ old.b*cis.y + old.e*cis.x;
m.c ¬ old.c*cis.x - old.f*cis.y; m.f ¬ old.c*cis.y + old.f*cis.x;
Validate[m];
};
ApplyPostTranslate:
PUBLIC
PROC [m: Transformation, t:
VEC] ~ {
old: TransformationRep ~ m;
CheckForm[m];
m.c ¬ old.c+t.x; m.f ¬ old.f+t.y;
Validate[m];
};
ApplyXYToSF:
PUBLIC
PROC [m: Transformation, scanMode: ScanMode, sSize, fSize:
INT] ~ {
post: Transformation ~ XYToSF[scanMode, sSize, fSize];
ApplyPostConcat[m, post];
Destroy[post];
};
ApplyTranslateTo:
PUBLIC
PROC [m: Transformation, t:
VEC] ~ {
integerTrans: BOOL ¬ TRUE;
tx, ty: INTEGER ¬ 0;
CheckForm[m];
m.c ¬ t.x; m.f ¬ t.y;
IF ABS[m.c]<=floatHalfLastInt THEN tx ¬ Real.Floor[m.c+0.5] ELSE integerTrans ¬ FALSE;
IF ABS[m.f]<=floatHalfLastInt THEN ty ¬ Real.Floor[m.f+0.5] ELSE integerTrans ¬ FALSE;
m.tx ¬ tx; m.ty ¬ ty;
m.integerTrans ¬ integerTrans;
};
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] };
};
ApplyInvert:
PUBLIC
PROC [m: Transformation] ~ {
old: TransformationRep ~ m;
det: REAL ~ old.a*old.e - old.d*old.b;
m.a ¬ old.e/det; m.d ¬ -old.d/det;
m.b ¬ -old.b/det; m.e ¬ old.a/det;
m.c ¬ (old.b*old.f - old.e*old.c)/det; m.f ¬ (old.d*old.c - old.a*old.f)/det;
Validate[m];
};
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 => { CheckForm[m]; ERROR }
};
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 => { CheckForm[m]; ERROR };
};
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 => { CheckForm[m]; ERROR };
};
intLimit: REAL ¬ REAL[100000000B];
InlineRound:
PROC [x:
REAL]
RETURNS [
REAL] ~
INLINE {
RETURN[IF ABS[x]<intLimit THEN REAL[Real.Floor[x+0.5]] 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];
SELECT m.form
FROM
0 => {
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]];
};
<= 10 => {
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]];
};
ENDCASE => { CheckForm[m]; ERROR };
};
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]
};
EasyTransformation:
PUBLIC
PROC [m: Transformation]
RETURNS [
BOOL] ~ {
RETURN[m.form>=3 AND m.integerTrans]
};
EasyTransformBox:
PUBLIC
PROC [m: Transformation, x, y, w, h:
INTEGER, clip:
SF.Box]
RETURNS [
SF.Box] ~ {
assert: BOOL[TRUE..TRUE] ~ m.form>=3 AND m.integerTrans;
x0: INT ~ INT[x]; y0: INT ~ INT[y]; x1: INT ~ x0+INT[w]; y1: INT ~ y0+INT[h];
ts: INT ~ INT[m.tx]; tf: INT ~ INT[m.ty];
s0, s1, f0, f1: INT;
box: SF.Box ¬ clip;
SELECT m.form
FROM
3 => { s0 ¬ ts + x0; s1 ¬ ts + x1; f0 ¬ tf + y0; f1 ¬ tf + y1 };
4 => { s0 ¬ ts + x0; s1 ¬ ts + x1; f0 ¬ tf - y0; f1 ¬ tf - y1 };
5 => { s0 ¬ ts - x0; s1 ¬ ts - x1; f0 ¬ tf + y0; f1 ¬ tf + y1 };
6 => { s0 ¬ ts - x0; s1 ¬ ts - x1; f0 ¬ tf - y0; f1 ¬ tf - y1 };
7 => { s0 ¬ ts + y0; s1 ¬ ts + y1; f0 ¬ tf + x0; f1 ¬ tf + x1 };
8 => { s0 ¬ ts + y0; s1 ¬ ts + y1; f0 ¬ tf - x0; f1 ¬ tf - x1 };
9 => { s0 ¬ ts - y0; s1 ¬ ts - y1; f0 ¬ tf + x0; f1 ¬ tf + x1 };
10 => { s0 ¬ ts - y0; s1 ¬ ts - y1; f0 ¬ tf - x0; f1 ¬ tf - x1 };
ENDCASE => ERROR;
IF s0 > s1 THEN { s2: INT ~ s0; s0 ¬ s1; s1 ¬ s2 };
IF f0 > f1 THEN { f2: INT ~ f0; f0 ¬ f1; f1 ¬ f2 };
IF s0 > box.max.s THEN s0 ¬ s1 ¬ box.max.s;
IF s1 < box.min.s THEN s0 ¬ s1 ¬ box.min.s;
IF f0 > box.max.f THEN f0 ¬ f1 ¬ box.max.f;
IF f1 < box.min.f THEN f0 ¬ f1 ¬ box.min.f;
IF s0 > box.min.s THEN box.min.s ¬ s0;
IF s1 < box.max.s THEN box.max.s ¬ s1;
IF f0 > box.min.f THEN box.min.f ¬ f0;
IF f1 < box.max.f THEN box.max.f ¬ f1;
RETURN[box];
};
Singular:
PUBLIC
PROC [m: Transformation]
RETURNS [
BOOL] ~ {
det: REAL ~ ABS[m.a*m.e - m.d*m.b]; -- magnitude of determinant
IF det >= 0.01 THEN RETURN [FALSE];
IF det = 0 THEN RETURN [TRUE];
RETURN [MAX[ABS[m.a], ABS[m.b], ABS[m.d], ABS[m.e]] >= (Real.LargestNumber)*0.125*det]
};
NumericalInstability: PUBLIC 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 => { CheckForm[m]; ERROR };
};
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[p01, [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[p01, [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]];
};