BiScrollersTransformsImpl.mesa
Copyright © 1992 by Xerox Corporation. All rights reserved.
Bier, March 19, 1993 5:47 pm PST
Contents: A viewers-independent way to perform the BiScrollers transformations on a transformation matrix.
DIRECTORY
ImagerBox, BiScrollersTransforms, BiScrollersTransformsTypes, Geom2D, ImagerTransformation, Real, RealFns, Vector2;
BiScrollersTransformsImpl:
CEDAR
PROGRAM
IMPORTS Geom2D, ImagerBox, ImagerTransformation, Real, RealFns, Vector2
EXPORTS BiScrollersTransforms = BEGIN
AgeOp: TYPE = BiScrollersTransformsTypes.AgeOp;
Axis: TYPE = Geom2D.Axis;
Rect: TYPE = Geom2D.Rect;
PreservationPair: TYPE = BiScrollersTransformsTypes.PreservationPair;
Transform: TYPE = ImagerTransformation.Transformation;
ScaleOp: TYPE = BiScrollersTransformsTypes.ScaleOp;
RotateOp: TYPE = BiScrollersTransformsTypes.RotateOp;
Location: TYPE = BiScrollersTransformsTypes.Location;
Vec: TYPE = Vector2.VEC;
Scale:
PUBLIC
PROC [old: Transform, op: ScaleOp, cw, ch:
REAL, pp: PreservationPair, vanilla: Transform ←
NIL, mayStretch:
BOOL ←
FALSE]
RETURNS [new: Transform] = {
cv: Vec ~ ConstantVector[cw, ch, pp];
new ¬ old.PostTranslate[cv.Neg[]];
IF vanilla = NIL THEN vanilla ← Geom2D.id;
WITH op
SELECT
FROM
reset => {
v: Geom2D.Trans ¬ Geom2D.ToTrans[vanilla];
vd: REAL ¬ v.dxdx*v.dydy - v.dydx*v.dxdy;
od: REAL ¬ old.a*old.e - old.d*old.b;
new ¬ new.PostScale[
RealFns.SqRt[ABS[vd/ZeroProtect[od]]]*SGN[vd]*SGN[od]
];
};
byArg => new ¬ new.PostScale[arg];
diff => IF mayStretch THEN new ¬ new.PostScale2[[x, y]] ELSE new ¬ new.PostScale[x];
ENDCASE => ERROR;
new ¬ new.PostTranslate[cv];
};
ConstantVector:
PROC [cw, ch:
REAL, pp: PreservationPair]
RETURNS [cv: Vec] = {
cv ¬ [cw*pp[X], ch*pp[Y]];
};
SGN:
PROC [r:
REAL]
RETURNS [sgn: [-1 .. 1]] = {
sgn ¬
SELECT r
FROM
<0 => -1,
=0 => 0,
>0 => 1,
ENDCASE => ERROR};
ZeroProtect:
PROC [r:
REAL]
RETURNS [r0:
REAL] = {r0 ¬
IF r = 0.0
THEN 1.0
ELSE r};
Rotate:
PUBLIC
PROC [old: Transform, op: RotateOp, cw, ch:
REAL, pp: PreservationPair, vanilla: Transform ←
NIL]
RETURNS [new: Transform] = {
cv: Vec ~ ConstantVector[cw, ch, pp];
new ¬ old.PostTranslate[cv.Neg[]];
WITH op
SELECT
FROM
reset => {
v: Geom2D.Trans ¬ Geom2D.ToTrans[vanilla];
new ¬ new.PostRotate[
RealFns.ArcTanDeg[y: v.dydx, x: v.dxdx] -
RealFns.ArcTanDeg[y: old.d, x: old.a]
];
};
byArg => new ¬ new.PostRotate[arg];
ENDCASE => ERROR;
new ¬ new.PostTranslate[cv];
};
Shift:
PUBLIC
PROC [old: Transform, dx, dy:
REAL]
RETURNS [new: Transform] = {
new ¬ old.PostTranslate[[dx, dy]];
};
Align:
PUBLIC
PROC [old: Transform, client, viewer: Location, doX, doY:
BOOL ¬
TRUE, cw, ch:
REAL, extremaProc: ExtremaProc, clientData:
REF, ageOp: AgeOp ¬ remember]
RETURNS [new: Transform] = {
from, to: Vec ¬ [0.0, 0.0];
Blend:
PROC [
a:
REAL, b0, b1:
REAL]
RETURNS [c:
REAL] =
{c ¬ (1-a)*b0 + a*b1};
WITH client
SELECT
FROM
coord => from ¬ old.Transform[[x, y]];
fraction => {
vmin, vmax: REAL;
IF doX
THEN {
[vmin, vmax] ¬ ViewLimitsOfImage2[old, extremaProc, clientData, X];
from.x ¬ Blend[fx, vmin, vmax];
};
IF doY
THEN {
[vmin, vmax] ¬ ViewLimitsOfImage2[old, extremaProc, clientData, Y];
from.y ¬ Blend[fy, vmin, vmax];
};
};
ENDCASE => ERROR;
WITH viewer
SELECT
FROM
coord => to ¬ [x, y];
fraction => {
to ¬ [fx*cw, fy*ch];
};
ENDCASE => ERROR;
IF NOT doX THEN to.x ¬ from.x;
IF NOT doY THEN to.y ¬ from.y;
new ¬ old.PostTranslate[from.Neg[]].PostTranslate[to];
};
ViewLimitsOfImage:
PROC [t: Transform, min, max: Vec, axis: Axis]
RETURNS [vmin, vmax:
REAL] = {
min ¬ t.Transform[min];
max ¬ t.Transform[max];
SELECT axis
FROM
X => {vmin ¬ min.x; vmax ¬ max.x};
Y => {vmin ¬ min.y; vmax ¬ max.y};
ENDCASE => ERROR;
};
ViewLimitsOfImage2:
PROC [t: Transform, extremaProc: ExtremaProc, clientData:
REF, axis: Axis]
RETURNS [vmin, vmax:
REAL] = {
tn: Geom2D.Trans ¬ Geom2D.ToTrans[t];
norm, min, max: Vec;
SELECT axis
FROM
X => norm ¬ [tn.dxdx, tn.dxdy];
Y => norm ¬ [tn.dydx, tn.dydy];
ENDCASE => ERROR;
[min, max] ¬ extremaProc[clientData, norm];
min ¬ t.Transform[min];
max ¬ t.Transform[max];
SELECT axis
FROM
X => {vmin ¬ min.x; vmax ¬ max.x};
Y => {vmin ¬ min.y; vmax ¬ max.y};
ENDCASE => ERROR;
};
Round:
PROC [r:
REAL]
RETURNS [rr:
REAL] = {
i: INT ¬ Real.Round[r];
rr ¬ IF (i=0) # (r=0) THEN r ELSE REAL[i];
};
BoxScale:
PUBLIC
PROC [old: Transform, from, to: Rect
--both in viewer coords--, uniformly:
BOOL ¬
TRUE, mayStretch:
BOOL ←
TRUE, preferIntegerCoefficients:
BOOL ←
FALSE]
RETURNS [new: Transform] = {
ndx, ndy, odx, ody, sx, sy: REAL;
new ¬ old.PostTranslate[[
-(from.x + from.w/2),
-(from.y + from.h/2)]];
ndx ¬ to.w;
ndy ¬ to.h;
odx ¬ from.w;
ody ¬ from.h;
sx ¬ IF ndx=0 OR odx=0 THEN 1 ELSE ndx/odx;
sy ¬ IF ndy=0 OR ody=0 THEN 1 ELSE ndy/ody;
IF uniformly OR NOT mayStretch THEN sx ¬ sy ¬ MIN[sx, sy];
IF preferIntegerCoefficients THEN {sx ¬ Round[sx]; sy ¬ Round[sy]};
new ¬ new.PostScale2[[sx, sy]];
new ¬ new.PostTranslate[[
(to.x + to.w/2),
(to.y + to.h/2)]];
};
ExtremaProc: TYPE = BiScrollersTransforms.ExtremaProc;
Fit:
PUBLIC PROC [old: Transform, uniformly:
BOOL, cw, ch:
REAL, extremaProc: ExtremaProc, clientData:
REF, mayStretch:
BOOL ←
TRUE, preferIntegerCoefficients:
BOOL ←
FALSE]
RETURNS [new: Transform] = {
normX: Vec ¬ Norm[old, X];
normY: Vec ¬ Norm[old, Y];
minX, maxX, minY, maxY: Vec;
limits: ImagerBox.Box;
from, to: Geom2D.Rect;
[minX, maxX] ← extremaProc[clientData, normX];
[minY, maxY] ← extremaProc[clientData, normY];
[limits.xmin, limits.xmax] ¬ ViewLimitsOfImage[old, minX, maxX, X];
[limits.ymin, limits.ymax] ¬ ViewLimitsOfImage[old, minY, maxY, Y];
from ¬ ImagerBox.RectangleFromBox[limits];
to ¬ [0, 0, cw, ch];
new ¬ BoxScale[old, from, to, uniformly, mayStretch, preferIntegerCoefficients];
};
Norm:
PROC [t: Transform, axis: Axis]
RETURNS [norm: Vec] = {
tn: Geom2D.Trans ¬ Geom2D.ToTrans[t];
SELECT axis
FROM
X => norm ¬ [tn.dxdx, tn.dxdy];
Y => norm ¬ [tn.dydx, tn.dydy];
ENDCASE => ERROR;
};
BeVec:
PROC [ra:
REF
ANY]
RETURNS [Vec]
~ {RETURN [NARROW[ra, REF Vec]]};
BeReal:
PROC [ra:
REF
ANY]
RETURNS [
REAL]
~ {RETURN [NARROW[ra, REF REAL]]};
BeRect:
PROC [ra:
REF
ANY]
RETURNS [Rect]
~ {RETURN [NARROW[ra, REF Rect]]};
BeBool:
PROC [ra:
REF
ANY]
RETURNS [
BOOL] ~ {
RETURN [
SELECT ra
FROM
$FALSE => FALSE,
$TRUE => TRUE,
ENDCASE => ERROR]};
DoBSUserAction:
PUBLIC
PROC [old: Transform, input:
LIST
OF
REF, cw, ch:
REAL, pp: PreservationPair, vanilla, previous: Transform ←
NIL, mayStretch:
BOOL ←
FALSE, preferIntegerCoefficients:
BOOL ←
FALSE, extremaProc: ExtremaProc, clientData:
REF]
RETURNS [new: Transform] = {
age: AgeOp ¬ remember;
new ¬ old;
SELECT input.first
FROM
$First => {input ¬ input.rest};
$Last => {age ¬ ignore; input ¬ input.rest};
$Mid => {age ¬ ignore; input ¬ input.rest};
ENDCASE => NULL;
SELECT input.first
FROM
$Shift => {
vec: Vec ¬ BeVec[input.rest.first];
new ¬ Shift[new, vec.x, vec.y];
};
$Scale => {s: Vec ~ BeVec[input.rest.first];
new ¬ Scale[new, IF s.x=s.y THEN [byArg[s.x]] ELSE [diff[s.x, s.y]], cw, ch, pp, vanilla, mayStretch];
};
$ScaleReset => new ¬ Scale[new, [reset[]], cw, ch, pp, vanilla, mayStretch];
$Rotate => new ¬ Rotate[new, [byArg[BeReal[input.rest.first]]], cw, ch, pp, vanilla];
$RotateReset => new ¬ Rotate[new, [reset[]], cw, ch, pp, vanilla];
$FitXY, $FitUniformly => {
uniformly:
BOOL ~
SELECT input.first
FROM
$FitUniformly => TRUE,
$FitXY => FALSE,
ENDCASE => ERROR;
new ¬ Fit[new, uniformly, cw, ch, extremaProc, clientData, mayStretch, preferIntegerCoefficients];
};
$Vanilla => new ¬ vanilla;
$AlignFracs => {
client: Vec ~ BeVec[input.rest.first];
viewer: Vec ~ BeVec[input.rest.rest.first];
doX: BOOL ~ BeBool[input.rest.rest.rest.first];
doY: BOOL ~ BeBool[input.rest.rest.rest.rest.first];
new ¬ Align[old: new, client: [fraction[client.x, client.y]], viewer: [fraction[viewer.x, viewer.y]], doX: doX, doY: doY, cw: cw, ch: ch, extremaProc: extremaProc, clientData: clientData, ageOp: age]};
$BoxScale => {
from: Rect ~ BeRect[input.rest.first];
to: Rect ~ BeRect[input.rest.rest.first];
uniformly: BOOL ~ BeBool[input.rest.rest.rest.first];
new ¬ BoxScale[new, from, to, uniformly, mayStretch, preferIntegerCoefficients]};
$Prev => new ¬ previous;
ENDCASE => ERROR;
};
END.