DIRECTORY
ImagerBasic USING [Context, Vec, IntVec, TransformRecord, Transformation, TransformRep,
ImagingSpace, TransformType],
Imager,
ImagerTransform,
Real USING [Fix, Float],
RealFns USING [SinDeg, CosDeg];
Client Procedures
Translate:
PUBLIC
PROC [context: Context, dx, dy:
REAL] = {
transform: Transformation ← NEW[
TransformRep ← [1., 0., 0., 0., 1., 0.]];
transform.c ← dx; transform.f ← dy;
ConcatToContext[context, client, transform];
};
Rotate:
PUBLIC PROC [context: Context, degrees:
REAL] = {
transform: Transformation ← NEW[
TransformRep ← [1., 0., 0., 0., 1., 0.]];
transform.a ← transform.e ← RealFns.CosDeg[degrees];
transform.d ← RealFns.SinDeg[degrees];
transform.b ← -transform.d;
ConcatToContext[context, client, transform];
};
Scale:
PUBLIC PROC [context: Context, sx, sy:
REAL] = {
transform: Transformation ← NEW[
TransformRep ← [1., 0., 0., 0., 1., 0.]];
transform.a ← sx; transform.e ← sy;
ConcatToContext[context, client, transform];
};
IntTranslate:
PUBLIC
PROC [context: Context, dx, dy:
INTEGER] = {
OPEN context.clientTransform;
IF type = hard
THEN
{ transformation.c ← transformation.c + dx; transformation.f ← transformation.f + dy; }
ELSE
{ x ← x + dx; y ← y + dy; };
ValidateCompositeTransform[context]; -- update composite transform
};
IntRotate:
PUBLIC PROC [context: Context, degrees:
INTEGER] = {
transform: TransformRecord;
WHILE degrees >= 360 DO degrees ← degrees - 360; ENDLOOP;
WHILE degrees < 0 DO degrees ← degrees + 360; ENDLOOP;
SELECT degrees FROM
0 => { transform ← [0, 0, identity, NIL];
context.clientTransform ← EasyConcat[context.clientTransform, transform]; };
90 => { transform ← [0, 0, rot90 , NIL];
context.clientTransform ← EasyConcat[context.clientTransform, transform]; };
180 => { transform ← [0, 0, rot180 , NIL];
context.clientTransform ← EasyConcat[context.clientTransform, transform]; };
270 => { transform ← [0, 0, rot270 , NIL];
context.clientTransform ← EasyConcat[context.clientTransform, transform]; };
ENDCASE => Rotate[context, Real.Float[degrees]];
ValidateCompositeTransform[context]; -- update composite transform
};
IntScale:
PUBLIC PROC [context: Context, sx, sy:
INTEGER] = {
transform: TransformRecord;
IF (sx = 1)
AND (sy = 1)
THEN { transform ← [0, 0, identity,
NIL];
context.clientTransform ← EasyConcat[context.clientTransform, transform]; }
ELSE IF (sx =-1)
AND (sy = 1)
THEN { transform ← [0, 0, mirrorX,
NIL];
context.clientTransform ← EasyConcat[context.clientTransform, transform]; }
ELSE IF (sx = 1)
AND (sy =-1)
THEN { transform ← [0, 0, mirrorY,
NIL];
context.clientTransform ← EasyConcat[context.clientTransform, transform]; }
ELSE IF (sx =-1)
AND (sy =-1)
THEN { transform ← [0, 0, rot180,
NIL];
context.clientTransform ← EasyConcat[context.clientTransform, transform]; }
ELSE Scale[context, Real.Float[sx], Real.Float[sy]];
ValidateCompositeTransform[context]; -- update composite transform
};
ResetTransform:
PUBLIC PROC [context: Context] = {
transform: Transformation ← NEW[TransformRep ← [1., 0., 0., 0., 1., 0.]];
context.clientTransform.transformation ← transform;
context.clientTransform.type ← identity;
context.clientTransform.x ← context.clientTransform.y ← 0;
ValidateCompositeTransform[context]; -- update composite transform
};
Unpublicized Procedures
SetTransform: PUBLIC PROC [ context: Context, transformSpace: ImagingSpace,
transform: TransformRecord] = {
SELECT transformSpace
FROM
client => context.clientTransform ← transform;
viewer => context.viewerTransform ← transform;
device => context.deviceTransform ← transform;
ENDCASE => ERROR;
ValidateCompositeTransform[context]; -- update composite transform
};
GetTransform: PUBLIC PROC [ context: Context, transformSpace: ImagingSpace]
RETURNS [TransformRecord] = {
SELECT transformSpace
FROM
client => RETURN[context.clientTransform];
viewer => RETURN[context.viewerTransform];
device => RETURN[context.deviceTransform];
ENDCASE => ERROR;
};
ConcatToContext: PUBLIC PROC [context: Context, transformSpace: ImagingSpace,
transform: Transformation] = {
Concatenate a "hard" (floating point) transform to the proper existing transform
SELECT transformSpace
FROM
client => {
OPEN context.clientTransform;
IF type # hard THEN transformation ← LoadTransform[type, x, y];
transformation ← Concat[transformation, transform]; type ← hard;
};
viewer => {
OPEN context.viewerTransform;
IF type # hard THEN transformation ← LoadTransform[type, x, y];
transformation ← Concat[transformation, transform]; type ← hard;
};
device => {
OPEN context.deviceTransform;
IF type # hard THEN transformation ← LoadTransform[type, x, y];
transformation ← Concat[transformation, transform]; type ← hard;
};
ENDCASE => ERROR;
ValidateCompositeTransform[context]; -- update composite transform
};
Transform:
PUBLIC PROC [transform: TransformRecord, p: Vec]
RETURNS [Vec] = {
OPEN transform;
SELECT type
FROM
identity => RETURN[ [ x: p.x + x, y: p.y + y] ];
rot90 => RETURN[ [ x: -p.y + x, y: p.x + y] ];
rot180 => RETURN[ [ x: -p.x + x, y: -p.y + y] ];
rot270 => RETURN[ [ x: p.y + x, y: -p.x + y] ];
mirrorX => RETURN[ [ x: -p.x + x, y: p.y + y] ];
mirrorY => RETURN[ [ x: p.x + x, y: -p.y + y] ];
mirror45Deg => RETURN[ [ x: p.y + x, y: p.x + y] ];
mirror135Deg => RETURN[ [ x: -p.y + x, y: -p.x + y] ];
hard =>
{
OPEN transformation;
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 [transform: TransformRecord, p: Vec]
RETURNS [Vec] = {
OPEN transform;
SELECT type
FROM
identity => RETURN[ [ x: p.x - x, y: p.y - y] ];
rot90 => RETURN[ [ x: p.y - y, y: -p.x + x] ];
rot180 => RETURN[ [ x: -p.x + x, y: -p.y + y] ];
rot270 => RETURN[ [ x: -p.y + y, y: p.x - x] ];
mirrorX => RETURN[ [ x: -p.x + x, y: p.y - y] ];
mirrorY => RETURN[ [ x: p.x - x, y: -p.y + y] ];
mirror45Deg => RETURN[ [ x: p.y - y, y: p.x - x] ];
mirror135Deg => RETURN[ [ x: -p.y + y, y: -p.x + x] ];
hard =>
{
m: Transformation;
m ← Invert[transformation];
RETURN[ [ x: p.x * m.a + p.y * m.b + m.c, y: p.x * m.d + p.y * m.e + m.f ] ];
};
ENDCASE => { SIGNAL TransformTypeNone[]; RETURN[ p ]; };
};
TransformVec:
PUBLIC PROC [transform: TransformRecord, p: Vec]
RETURNS [Vec] = {
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 => {
OPEN transformation;
RETURN[ [ x: p.x * a + p.y * b, y: p.x * d + p.y * e ] ];
};
ENDCASE => { SIGNAL TransformTypeNone[]; RETURN[ p ]; };
};
InverseTransformVec:
PUBLIC PROC [transform: TransformRecord, p: Vec]
RETURNS [Vec] = {
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 =>
{
m: Transformation;
m ← Invert[transformation];
RETURN[ [ x: p.x * m.a + p.y * m.b, y: p.x * m.d + p.y * m.e ] ];
};
ENDCASE => { SIGNAL TransformTypeNone[]; RETURN[ p ]; };
};
IntTransform:
PUBLIC PROC [transform: TransformRecord, p: IntVec
] RETURNS [IntVec] = {
OPEN transform;
SELECT type
FROM
identity => RETURN[ [ x: p.x + x, y: p.y + y] ];
rot90 => RETURN[ [ x: -p.y + x, y: p.x + y] ];
rot180 => RETURN[ [ x: -p.x + x, y: -p.y + y] ];
rot270 => RETURN[ [ x: p.y + x, y: -p.x + y] ];
mirrorX => RETURN[ [ x: -p.x + x, y: p.y + y] ];
mirrorY => RETURN[ [ x: p.x + x, y: -p.y + y] ];
mirror45Deg => RETURN[ [ x: p.y + x, y: p.x + y] ];
mirror135Deg => RETURN[ [ x: -p.y + x, y: -p.x + y] ];
hard =>
{
OPEN transformation;
RETURN[ [ x: Real.Fix[p.x * a + p.y * b + c], y: Real.Fix[p.x * d + p.y * e + f] ] ];
};
ENDCASE => { SIGNAL TransformTypeNone[]; RETURN[ p ]; };
};
InverseIntTransform:
PUBLIC PROC [transform: TransformRecord, p: IntVec
] RETURNS [IntVec] = {
OPEN transform;
SELECT type
FROM
identity => RETURN[ [ x: p.x - x, y: p.y - y] ];
rot90 => RETURN[ [ x: p.y - y, y: -p.x + x] ];
rot180 => RETURN[ [ x: -p.x + x, y: -p.y + y] ];
rot270 => RETURN[ [ x: -p.y + y, y: p.x - x] ];
mirrorX => RETURN[ [ x: -p.x + x, y: p.y - y] ];
mirrorY => RETURN[ [ x: p.x - x, y: -p.y + y] ];
mirror45Deg => RETURN[ [ x: p.y - y, y: p.x - x] ];
mirror135Deg => RETURN[ [ x: -p.y + y, y: -p.x + x] ];
hard =>
{
m: Transformation;
m ← Invert[transformation];
RETURN[ [ x: Real.Fix[p.x * m.a + p.y * m.b + m.c],
y: Real.Fix[p.x * m.d + p.y * m.e + m.f] ] ];
};
ENDCASE => { SIGNAL TransformTypeNone[]; RETURN[ p ]; };
};
TransformIntVec:
PUBLIC PROC [transform: TransformRecord, p: IntVec]
RETURNS [IntVec] = {
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 => {
OPEN transformation;
RETURN[ [ x: Real.Fix[p.x * a + p.y * b], y: Real.Fix[p.x * d + p.y * e] ] ];
};
ENDCASE => { SIGNAL TransformTypeNone[]; RETURN[ p ]; };
};
InverseTransformIntVec:
PUBLIC PROC [transform: TransformRecord, p: IntVec]
RETURNS [IntVec] = {
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 =>
{
m: Transformation;
m ← Invert[transformation];
RETURN[ [ x: Real.Fix[p.x * m.a + p.y * m.b], y: Real.Fix[p.x * m.d + p.y * m.e] ] ];
};
ENDCASE => { SIGNAL TransformTypeNone[]; RETURN[ p ]; };
};
Internal Procedures
LoadTransform:
PROC [type: TransformType, x, y:
INTEGER]
RETURNS [Transformation] = {
SELECT type
FROM
identity => RETURN[ NEW[TransformRep ← [ 1., 0., x, 0., 1., y]] ];
rot90 => RETURN[ NEW[TransformRep ← [ 0., -1., x, 1., 0., y]] ];
rot180 => RETURN[ NEW[TransformRep ← [-1., 0., x, 0., -1., y]] ];
rot270 => RETURN[ NEW[TransformRep ← [ 0., 1., x, -1., 0., y]] ];
mirrorX => RETURN[ NEW[TransformRep ← [-1., 0., x, 0., 1., y]] ];
mirrorY => RETURN[ NEW[TransformRep ← [ 1., 0., x, 0., -1., y]] ];
mirror45Deg => RETURN[ NEW[TransformRep ← [ 0., 1., x, 1., 0., y]] ];
mirror135Deg => RETURN[ NEW[TransformRep ← [ 0., -1., x, -1., 0., y]] ];
ENDCASE =>
{ SIGNAL TransformTypeNone[];
RETURN[ NEW[TransformRep ← [ 1., 0., x, 0., 1., y]] ]; };
};
EasyConcat:
PROC [m, n: TransformRecord]
RETURNS [TransformRecord] = {
t: IntVec;
catch trivial cases
IF n.type = identity AND m.type # hard
THEN RETURN[ [m.x + n.x, m.y + n.y, m.type,
LoadTransform[m.type, m.x + n.x, m.y + n.y]] ]
ELSE
IF m.type = identity
AND n.type # hard
THEN {
t.x ← m.x; t.y ← m.y; t ← IntTransform[n, t];
RETURN[ [t.x, t.y, n.type, LoadTransform[n.type, t.x, t.y]] ] }
ELSE
IF n.type = mirrorY
AND m.type # hard
THEN {
t.x ← m.x; t.y ← m.y; t ← IntTransform[n, t];
SELECT m.type
FROM
rot90 => RETURN[ [t.x, t.y, mirror135Deg, LoadTransform[mirror135Deg, t.x, t.y]] ];
rot180 => RETURN[ [t.x, t.y, mirrorX, LoadTransform[mirrorX, t.x, t.y]] ];
rot270 => RETURN[ [t.x, t.y, mirror45Deg, LoadTransform[mirror45Deg, t.x, t.y]] ];
mirrorX => RETURN[ [t.x, t.y, rot180, LoadTransform[rot180, t.x, t.y]] ];
mirrorY => RETURN[ [t.x, t.y, identity, LoadTransform[identity, t.x, t.y]] ];
mirror45Deg => RETURN[ [t.x, t.y, rot270, LoadTransform[rot270, t.x, t.y]] ];
mirror135Deg => RETURN[ [t.x, t.y, rot90, LoadTransform[rot90, t.x, t.y]] ];
ENDCASE => {
SIGNAL TransformTypeNone[];
RETURN[ [t.x, t.y, mirrorY, LoadTransform[mirrorY, t.x, t.y]] ]; }
}
ELSE {
-- not a trivial case, expand and use concat
p, q: Transformation;
IF m.type # hard THEN p ← LoadTransform[m.type, m.x, m.y]
ELSE p ← m.transformation;
IF n.type # hard THEN q ← LoadTransform[n.type, n.x, n.y]
ELSE q ← n.transformation;
RETURN MakeEasy[Concat[p,q], epsilonHardness];
};
};
Concat:
PROC [m, n: Transformation]
RETURNS [Transformation] = {
transform: Transformation ← NEW[TransformRep ← [1., 0., 0., 0., 1., 0.]];
transform.a ← m.a*n.a + m.d*n.b; transform.d ← m.a*n.d + m.d*n.e;
transform.b ← m.b*n.a + m.e*n.b; transform.e ← m.b*n.d + m.e*n.e;
transform.c ← m.c*n.a + m.f*n.b + n.c; transform.f ← m.c*n.d + m.f*n.e + n.f;
RETURN[transform];
};
Invert:
PROC [m: Transformation]
RETURNS [Transformation] = {
transform: Transformation ← NEW[TransformRep ← [1., 0., 0., 1., 0., 0.]];
det: REAL;
det ← m.a*m.e - m.d*m.b; -- compute determinant
transform.a ← m.e / det; transform.d ← -m.d / det;
transform.b ← -m.b / det; transform.e ← m.a / det;
transform.c ← (m.b * m.f - m.e * m.c) / det;
transform.f ← (m.d * m.c - m.a * m.f) / det;
RETURN[transform];
};
MakeEasy:
PROC [m: Transformation, epsilon:
REAL]
RETURNS [TransformRecord] = {
Make non-hard if a, b, c, d all within epsilon of values for one of the non-hard cases
IF
(ABS[m.a] < epsilon
-- test for rotation or axis interchange ops
AND ABS[m.e] < epsilon
AND (ABS[ABS[m.d] - 1.0] < epsilon)
AND (ABS[ABS[m.b] - 1.0] < epsilon) )
THEN {
x, y: INT;
x ← Real.Fix[m.c]; y ← Real.Fix[m.f];
IF m.b > 0
THEN
IF m.c > 0 THEN RETURN[ [x, y, mirror45Deg,
NEW[TransformRep ← [0., 1., m.c, 1., 0., m.f]] ]]
ELSE RETURN[ [x, y, rot90,
NEW[TransformRep ← [0., -1., m.c, 1., 0., m.f]] ]]
ELSE
IF m.c > 0 THEN RETURN[ [x, y, rot270,
NEW[TransformRep ← [0., 1., m.c, -1., 0., m.f]] ]]
ELSE RETURN[ [x, y, mirror135Deg,
NEW[TransformRep ← [0., -1., m.c, -1., 0., m.f]] ]]
}
ELSE IF
(ABS[m.d] < epsilon
-- test for null or mirror ops
AND ABS[m.b] < epsilon
AND (ABS[ABS[m.a] - 1.0] < epsilon)
AND (ABS[ABS[m.e] - 1.0] < epsilon) )
THEN {
x, y: INT;
x ← Real.Fix[m.c]; y ← Real.Fix[m.f];
IF m.a > 0
THEN
IF m.e > 0 THEN RETURN[ [x, y, identity,
NEW[TransformRep ← [ 1., 0., m.e, 0., 1., m.f]]]]
ELSE RETURN[ [x, y, mirrorY,
NEW[TransformRep ← [ 1., 0., m.e, 0., -1., m.f]]]]
ELSE
IF m.e > 0 THEN RETURN[ [x, y, mirrorX,
NEW[TransformRep ← [-1., 0., m.e, 0., 1., m.f]]]]
ELSE RETURN[ [x, y, rot180,
NEW[TransformRep ← [-1., 0., m.e, 0., -1., m.f]]]]
}
ELSE RETURN[ [0, 0, hard, m] ]; -- can't make it non-hard
};
ValidateCompositeTransform:
PROC [context: Context] = {
context.transform ← EasyConcat[
EasyConcat[context.clientTransform,context.viewerTransform],context.deviceTransform];
};