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: BOOLFALSE] 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: BOOLTRUE, preferIntegerCoefficients: BOOLFALSE] 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: BOOLTRUE, preferIntegerCoefficients: BOOLFALSE] 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: BOOLFALSE, preferIntegerCoefficients: BOOLFALSE, 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.