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. ς BiScrollersTransformsImpl.mesa Copyright c 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. Κe•NewlineDelimiter ™code™Kšœ Οmœ1™