DIRECTORY BiAxialMenu, BiAxials, BiAxialsPrivate, BiScrollers, FS, Geom2D, Imager, ImagerBox, ImagerFont, ImagerInterpress, ImagerTransformation, IO, Menus, PopUpButtons, Real, RealFns, Rope, RopeExtras, SimpleFeedback, Vector2, VFonts, ViewerClasses, ViewerTools; BiAxialsImpl: CEDAR PROGRAM IMPORTS BiScrollers, FS, Geom2D, Imager, ImagerBox, ImagerFont, ImagerInterpress, ImagerTransformation, IO, Menus, PopUpButtons, Real, RealFns, Rope, RopeExtras, SimpleFeedback, Vector2, VFonts, ViewerTools EXPORTS BiAxials, BiAxialMenu = BEGIN OPEN BiAxials, BS:BiScrollers, PUB:PopUpButtons; Viewer: TYPE ~ ViewerClasses.Viewer; BiScrollerStyle: TYPE ~ BiScrollers.BiScrollerStyle; ClassPrivate: TYPE ~ REF ClassPrivateRep; ClassPrivateRep: PUBLIC TYPE ~ BiAxialsPrivate.ClassPrivateRep; BiAxialSpecific: TYPE ~ REF BiAxialSpecificRec; BiAxialSpecificRec: TYPE ~ RECORD [ class: Class, labelPolicies: LabelPolicies, clientData: REF ANY, parms: ARRAY Axis OF AxisParms, state: ARRAY Axis OF AxisState _ [] ]; AxisParms: TYPE ~ RECORD [ labelPara, labelPerp: REAL, --label size, parallel and perp to axis, in viewer coords axisSep: REAL--viewer sep. betw. axis ctr. and client clip region-- _ 1.5, axisRad: REAL--viewer-- _ 0.5, tickLen: REAL--viewer-- _ 5.0, tickWid: REAL--viewer-- _ 2.0, tlSep: REAL--viewer-- _ 2.0, edgeDistv: REAL--viewer, labelPerp+tlSep+tickLen+axisRad+axisSep-- _ 0 ]; AxisState: TYPE ~ RECORD [ bMin, bMax: REAL _ 0.0, --bounds available to client, in client coords aMin, aMax: REAL _ 0.0, --intersection with client extent edgeDistc: REAL _ 0.0, --client width of edge crud clientVal: REAL _ 0.0, --client other coord of axis viewerVal: REAL _ 0.0 --viewer other coord of axis, incl axisSep effect ]; CreateClass: PUBLIC PROC [bsStyle: BS.BiScrollerStyle, bcc: ClassCommon, classData: REF ANY _ NIL] RETURNS [Class] ~ { cp: ClassPrivate; c: Class; IF bcc.vanilla=NIL THEN bcc.vanilla _ BS.GenID; IF bcc.menu=NIL THEN bcc.menu _ baMenu; cp _ NEW [ClassPrivateRep _ [bcc]]; cp.bsClass _ bsStyle.NewBiScrollerClass[[ flavor: bcc.flavor, extrema: Extrema, notify: bcc.notify, bsUserAction: BSUserAction, paint: Paint, modify: bcc.modify, destroy: bcc.destroy, copy: bcc.copy, set: bcc.set, get: bcc.get, init: bcc.init, finish: bcc.finish, save: bcc.save, caption: bcc.caption, adjust: bcc.adjust, menu: bcc.menu, tipTable: bcc.tipTable, icon: bcc.icon, cursor: bcc.cursor, mayStretch: bcc.mayStretch, offsetsMustBeIntegers: bcc.offsetsMustBeIntegers, preferIntegerCoefficients: bcc.preferIntegerCoefficients, vanilla: Vanilla, preserve: bcc.preserve ]]; c _ NEW [ClassRep _ [cp, classData]]; RETURN [c]}; DecomposeClass: PUBLIC PROC [c: Class] RETURNS [bsClass: BiScrollerClass, bcc: ClassCommon, classData: REF ANY] ~ { RETURN [c.private.bsClass, c.private.bcc, c.classData]}; Create: PUBLIC PROC [class: Class, labelPolicies: LabelPolicies, info: ViewerClasses.ViewerRec, paint: BOOL _ TRUE] RETURNS [ba: BiAxial] ~ { bsClass: BiScrollerClass ~ class.private.bsClass; lszx: VEC ~ labelPolicies[X].EstimateLabelSize[labelPolicies[X], X]; lszy: VEC ~ labelPolicies[Y].EstimateLabelSize[labelPolicies[Y], Y]; bas: BiAxialSpecific ~ NEW [BiAxialSpecificRec _ [class, labelPolicies, info.data, [X: [lszx.x, lszx.y], Y: [lszy.y, lszy.x]] ]]; FOR axis: Axis IN Axis DO bas.parms[axis].edgeDistv _ bas.parms[axis].labelPerp + bas.parms[axis].tlSep + bas.parms[axis].tickLen + bas.parms[axis].axisRad + bas.parms[axis].axisSep; ENDLOOP; info.data _ bas; ba _ bsClass.style.CreateBiScroller[bsClass, info, paint]; RETURN}; ClientDataOf: PUBLIC PROC [bs: BiAxial] RETURNS [REF ANY] ~ { bas: BiAxialSpecific ~ NARROW[bs.ClientDataOf]; RETURN [bas.clientData]}; Extrema: PROC [clientData: REF ANY, direction: VEC] RETURNS [min, max: VEC] --BS.ExtremaProc-- ~ { bas: BiAxialSpecific ~ NARROW[clientData]; [min, max] _ bas.class.private.bcc.extrema[bas.clientData, direction]; NULL--It's slightly bogus to use values (..edgeDistc) only calculated last time Paint was executed, but that's probably good enough--; IF direction.x < 0 THEN max.x _ max.x - bas.state[Y].edgeDistc ELSE min.x _ min.x - bas.state[Y].edgeDistc; IF direction.y < 0 THEN max.y _ max.y - bas.state[X].edgeDistc ELSE min.y _ min.y - bas.state[X].edgeDistc; RETURN}; Vanilla: PROC [bs: BiScroller] RETURNS [t: BS.Transform] --BS.TransformGenerator-- ~ { bas: BiAxialSpecific ~ NARROW[BS.ClientDataOf[bs]]; cp: ClassPrivate ~ bas.class.private; t _ cp.bcc.vanilla[bs]; t _ t.PostTranslate[[bas.parms[Y].edgeDistv, bas.parms[X].edgeDistv]]; RETURN}; BSUserAction: PROC [bs: BiScroller, input: LORA] ~ { bas: BiAxialSpecific ~ NARROW[BS.ClientDataOf[bs]]; orgInput: LORA _ input; paint: BOOL _ TRUE; age: BS.AgeOp _ remember; SELECT input.first FROM $First => {paint _ FALSE; input _ input.rest}; $Last => {age _ ignore; input _ input.rest}; $Mid => {paint _ FALSE; age _ ignore; input _ input.rest}; ENDCASE => NULL; SELECT input.first FROM $FitXY => Fit[bs, paint, FALSE]; $FitUniformly => Fit[bs, paint, TRUE]; $AlignFracs => { v: Viewer ~ BS.QuaViewer[bs, TRUE]; clientFrac: VEC ~ BeVec[input.rest.first]; inrViewer: VEC ~ BeVec[input.rest.rest.first]; doX: BOOL ~ BeBool[input.rest.rest.rest.first]; doY: BOOL ~ BeBool[input.rest.rest.rest.rest.first]; viewer: BS.Location ~ [coord[ bas.parms[Y].edgeDistv + inrViewer.x*(v.cw-bas.parms[Y].edgeDistv), bas.parms[X].edgeDistv + inrViewer.y*(v.ch-bas.parms[X].edgeDistv) ]]; cx, cy: REAL _ 0.0; min, max: VEC; IF doX THEN { [min, max] _ bas.class.private.bcc.extrema[bas.clientData, [1.0, 0.0]]; cx _ Blend[clientFrac.x, min.x, max.x]}; IF doY THEN { [min, max] _ bas.class.private.bcc.extrema[bas.clientData, [0.0, 1.0]]; cy _ Blend[clientFrac.y, min.y, max.y]}; BS.Align[bs, [coord[cx, cy]], viewer, doX, doY, paint, age]}; ENDCASE => BS.DoBSUserAction[bs, orgInput]; RETURN}; Fit: PROC [bs: BiScroller, paint, uniformly: BOOL] ~ { bas: BiAxialSpecific ~ NARROW[BS.ClientDataOf[bs]]; v: Viewer ~ BS.QuaViewer[bs, TRUE]; limits: Box; [limits.xmin, limits.xmax] _ ViewLimitsOfImage[bs, X]; [limits.ymin, limits.ymax] _ ViewLimitsOfImage[bs, Y]; BS.BoxScale[bs, limits.RectangleFromBox, ImagerBox.RectangleFromBox[[bas.parms[Y].edgeDistv, bas.parms[X].edgeDistv, v.cw, v.ch]], paint, uniformly]; RETURN}; ViewLimitsOfImage: PROC [ba: BiAxial, axis: Axis] RETURNS [vmin, vmax: REAL] = { bs: BiScroller ~ ba; bas: BiAxialSpecific ~ NARROW[BS.ClientDataOf[bs]]; t: BS.Transform _ bs.style.GetTransforms[bs].clientToViewer; 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] _ bas.class.private.bcc.extrema[bas.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; RETURN}; Paint: PROC [self: Viewer, context: Imager.Context, whatChanged: REF ANY, clear: BOOL] RETURNS [quit: BOOL _ FALSE] --ViewerClasses.PaintProc-- ~ {quit _ InnerPaint[self, context, screen, whatChanged, clear]}; InnerPaint: PROC [self: Viewer, context: Imager.Context, dest: ImageDestination, whatChanged: REF ANY, clear: BOOL] RETURNS [quit: BOOL _ FALSE] --ViewerClasses.PaintProc-- ~ { bs: BiScroller ~ BS.QuaBiScroller[self]; bas: BiAxialSpecific ~ NARROW[BS.ClientDataOf[bs]]; class: Class ~ bas.class; clipBox: Box; DrawAxes: PROC ~ { t, u: BS.Transform; clientVptMin, clientVptMax: VEC; Prepare: PROC [axis, other: Axis, vw: INTEGER, scale, offset: REAL, Cons: PROC [REAL, REAL] RETURNS [VEC], Uncons: PROC [VEC] RETURNS [a, o: REAL]] ~ { clientExtMin, clientExtMax: VEC; [clientExtMin, clientExtMax] _ bas.class.private.bcc.extrema[bas.clientData, Cons[1.0, 0.0]]; bas.state[axis].bMax _ Uncons[clientVptMax].a; bas.state[other].clientVal _ bas.state[axis].aMin _ bas.state[axis].bMin _ MAX[Uncons[clientExtMin].a, Uncons[clientVptMin].a]; bas.state[axis].aMax _ MIN[Uncons[clientExtMax].a, bas.state[axis].bMax]; RETURN}; PaintAxis: PROC [axis, other: Axis, vw: INTEGER, invScale, scale, offset: REAL, Cons: PROC [REAL, REAL] RETURNS [VEC], Uncons: PROC [VEC] RETURNS [a, o: REAL]] ~ { axdelt: VEC ~ Cons[0.0, bas.parms[axis].axisSep+bas.parms[axis].axisRad]; maxTick: REAL _ bas.state[axis].aMax; ConsumeTick: PROC [ coord: REAL--in client cordinates--, labelBounds: Box--Viewer scale--, DrawLabel: PROC [org: VEC] _ NIL ] ~ { clientTick: VEC ~ Cons[coord, bas.state[axis].clientVal]; viewerTick: VEC--at edge of axis-- ~ t.Transform[clientTick].Sub[axdelt]; tickEnd: VEC ~ viewerTick.Sub[Cons[0.0, bas.parms[axis].tickLen]]; boxCtr: VEC ~ [(labelBounds.xmin + labelBounds.xmax)*0.5, (labelBounds.ymin + labelBounds.ymax)*0.5]; boxOff: VEC ~ Cons[Uncons[boxCtr].a, Uncons[[labelBounds.xmax, labelBounds.ymax]].o]; context.MaskVector[p1: viewerTick, p2: tickEnd]; maxTick _ MAX[maxTick, coord]; IF DrawLabel#NIL THEN DrawLabel[tickEnd.Sub[Cons[0.0, bas.parms[axis].tlSep]].Sub[boxOff]]; RETURN}; context.SetStrokeWidth[bas.parms[axis].tickWid]; bas.labelPolicies[axis].EnumerateTicks[bas.labelPolicies[axis], bs, axis, dest, bas.state[axis].bMin, bas.state[axis].bMax, bas.state[axis].aMin, bas.state[axis].aMax, bas.parms[axis].labelPara*invScale, context, ConsumeTick]; IF maxTick >= bas.state[other].clientVal THEN { axendc: VEC ~ Cons[maxTick, bas.state[axis].clientVal]; axendv: VEC ~ t.Transform[axendc].Sub[Cons[0.0, bas.parms[axis].axisSep]]; context.SetStrokeWidth[bas.parms[axis].axisRad*2.0]; context.MaskVector[p1: [bas.state[Y].viewerVal, bas.state[X].viewerVal], p2: axendv]}; RETURN}; [t, u] _ bs.style.GetTransforms[bs]; [[bas.state[Y].edgeDistc, bas.state[X].edgeDistc]] _ u.TransformVec[[bas.parms[Y].edgeDistv, bas.parms[X].edgeDistv]]; clientVptMin _ u.Transform[[bas.parms[Y].edgeDistv, bas.parms[X].edgeDistv]]; clientVptMax _ u.Transform[[self.cw, self.ch]]; Prepare[X, Y, self.cw, t.a, t.c, ConsX, UnconsX]; Prepare[Y, X, self.ch, t.e, t.f, ConsY, UnconsY]; [[bas.state[Y].viewerVal, bas.state[X].viewerVal]] _ t .Transform[[bas.state[Y].clientVal, bas.state[X].clientVal]] .Sub[[bas.parms[Y].axisSep, bas.parms[X].axisSep]]; context.ConcatT[u]; PaintAxis[X, Y, self.cw, u.a, t.a, t.c, ConsX, UnconsX]; PaintAxis[Y, X, self.ch, u.e, t.e, t.f, ConsY, UnconsY]; RETURN}; IF clear THEN context.DoSave[DrawAxes]; clipBox _ [xmin: bas.state[X].bMin, xmax: bas.state[X].bMax, ymin: bas.state[Y].bMin, ymax: bas.state[Y].bMax]; context.ClipRectangle[ImagerBox.RectangleFromBox[clipBox]]; quit _ class.private.bcc.paint[self, context, clipBox, dest, whatChanged, clear]; RETURN}; ConsX: PROC [a, o: REAL] RETURNS [VEC] ~ {RETURN [[a, o]]}; ConsY: PROC [a, o: REAL] RETURNS [VEC] ~ {RETURN [[o, a]]}; UnconsX: PROC [v: VEC] RETURNS [a, o: REAL] ~ {RETURN [v.x, v.y]}; UnconsY: PROC [v: VEC] RETURNS [a, o: REAL] ~ {RETURN [v.y, v.x]}; CreateDrawingButton: PUBLIC PROC [viewerInfo: ViewerClasses.ViewerRec, ba: BiAxial, font: Font _ NIL] RETURNS [button: Viewer] = { IF viewerInfo.name = NIL THEN viewerInfo.name _ "Draw"; button _ drawClass.Instantiate[viewerInfo, ba, PUB.ImageForRope[rope: viewerInfo.name, font: font]]; RETURN}; drawClass: PUB.Class _ PUB.MakeClass[[ proc: DrawCtl, choices: LIST[ [$ToIP, "Create an interpress master of viewer contents"] ], doc: "Drawing control operations"]]; DrawCtl: PROC [view, instanceData, classData, key: REF ANY] --PUB.PopUpButtonProc-- = { ba: BiAxial ~ NARROW[instanceData]; bas: BiAxialSpecific ~ NARROW[BS.ClientDataOf[ba]]; SELECT key FROM $ToIP => { fileName: ROPE ~ ViewerTools.GetSelectionContents[]; writtenName: ROPE; width, height: REAL; [writtenName, width, height] _ ToIP[ba, fileName, ".ip" !FS.Error => { SimpleFeedback.Append[$BiAxials, oneLiner, $Error, Rope.Cat["File create error: ", error.explanation]]; GOTO Dun}]; SimpleFeedback.PutF[$BiAxials, oneLiner, $Error, "%g is %g by %g", [rope[FS.ExpandName[writtenName].fullFName]], [real[width]], [real[height]] ]; key _ key}; ENDCASE => ERROR; RETURN EXITS Dun => key _ key}; DrawButt: PROC [parent: Viewer, clientData: REF ANY _ NIL, mouseButton: ViewerClasses.MouseButton _ red, shift, control: BOOL _ FALSE] ~ { ba: BiAxial ~ BS.QuaBiScroller[parent]; DrawCtl[parent, ba, NIL, $ToIP]; RETURN}; ToIP: PUBLIC PROC [ba: BiAxial, fileName, defaultExtension: ROPE _ NIL] RETURNS [writtenName: ROPE, width, height: REAL] ~ { asBS: BiScroller ~ ba; self: Viewer ~ BS.QuaViewer[asBS, FALSE]; bas: BiAxialSpecific ~ NARROW[BS.ClientDataOf[asBS]]; xfm: BS.Transform ~ asBS.style.GetTransforms[asBS].clientToViewer; IF fileName.Length=0 THEN fileName _ self.name.Concat[defaultExtension] ELSE IF fileName.Find["."]<0 THEN fileName _ fileName.Concat[defaultExtension]; {file: ImagerInterpress.Ref ~ ImagerInterpress.Create[fileName]; PaintPage: PROC [context: Imager.Context] ~ { context.ConcatT[xfm]; [] _ InnerPaint[self, context, print, NIL, TRUE]; RETURN}; width _ self.cw/ppi; height _ self.ch/ppi; file.DoPage[PaintPage, Imager.metersPerInch/ppi]; file.Close[]; RETURN [fileName, width, height]}}; ppi: REAL _ 72.0; baMenu: PUBLIC Menus.Menu _ Menus.CopyMenu[BS.bsMenu]; LinearSpecific: TYPE ~ REF LinearSpecificRec; LinearSpecificRec: TYPE ~ RECORD [ axis: Axis, format: ROPE, labelChars: NAT, font: ARRAY ImageDestination OF Font, log: BOOL, bt, bc, cc, zc: REAL, lnbt, lnbc, lncc: REAL _ 1.0, labelSizeEstv: VEC _ [0.0, 0.0], labelWidthEstc: REAL _ 0.0, --for which below are valid bMin, bMax: REAL _ 0.0, --visible area bounds, for which below are valid aMin, aMax: REAL _ 0.0, --intersection with client bounds, for which below are valid imin, imax: INT _ 0, period: REAL _ 0.0, invert: BOOL _ FALSE]; ln10: REAL ~ RealFns.Ln[10.0]; nMax: INTEGER _ 10; decimalCoefs: RealSeq _ NEW [RealSequence[4]]; linearDefaultScreenFont, linearDefaultPrintFont: Font _ NIL; CreateLinearLabelPolicy: PUBLIC PROC [ axis: Axis, format: ROPE, --produces at most labelChars chars labelChars: NAT, ctl: LinearCoordToLabel, font: ARRAY ImageDestination OF Font _ ALL[NIL] --NIL means pick a standard default ] RETURNS [lp: LabelPolicy] ~ { ls: LinearSpecific ~ NEW [LinearSpecificRec _ [axis, format, labelChars, font, ctl.log, ctl.bt, ctl.bc, ctl.cc, ctl.zc]]; charEst: ROPE ~ RopeExtras.MakeConstantRope['7, labelChars]; screenEst, printEst: Imager.Rectangle; IF ls.font[screen] = NIL THEN ls.font[screen] _ linearDefaultScreenFont; IF ls.font[print] = NIL THEN ls.font[print] _ linearDefaultPrintFont; IF ls.log THEN { ls.lnbt _ RealFns.Ln[ctl.bt]; ls.lnbc _ RealFns.Ln[ctl.bc]; ls.lncc _ RealFns.Ln[ctl.cc]}; screenEst _ ls.font[screen].RopeBoundingBox[charEst] .BoxFromExtents .RectangleFromBox; printEst _ ls.font[print].RopeBoundingBox[charEst] .BoxFromExtents .RectangleFromBox; ls.labelSizeEstv _ [MAX[screenEst.w, printEst.w], MAX[screenEst.h, printEst.h]]; lp _ NEW [LabelPolicyRep _ [LinearEstimateLabelSize, LinearEnumerateTicks, NIL, ls]]; RETURN}; LinearEstimateLabelSize: PROC [lp: LabelPolicy, axis: Axis] RETURNS [VEC] ~ { ls: LinearSpecific ~ NARROW[lp.data]; RETURN [ls.labelSizeEstv]}; LinearPrepare: PROC [ls: LinearSpecific, ba: BiAxial, bMin, bMax, aMin, aMax, labelSize: REAL--in client cordinates--] ~ { minLabelSpacing: REAL ~ labelSize*1.1; aWidth, bWidth, rough: REAL; n: INTEGER; ls.bMin _ bMin; ls.bMax _ bMax; ls.aMin _ aMin; ls.aMax _ aMax; aWidth _ aMax - aMin; bWidth _ bMax - bMin; ls.labelWidthEstc _ labelSize; IF aWidth = 0.0 THEN { ls.imin _ ls.imax _ 1; IF ls.log THEN { --n ln bt = ln cc + x ln bc ls.period _ (ls.lncc + aMin * ls.lnbc) / ls.lnbt; ls.invert _ FALSE; } ELSE ls.period _ aMin; RETURN}; n _ Real.Floor[MAX[MIN[aWidth/minLabelSpacing, nMax], 1.0]]; rough _ aWidth/n; IF ls.log THEN { tp: REAL ~ ls.lnbt / ls.lnbc; offset, fact: REAL; smooth: REAL _ HarmonicCeiling[rough/tp]*tp; DO offset _ ls.lncc / (ls.lnbc * smooth); fact _ 1.0 / smooth; ls.imin _ MAX[Real.Ceiling[offset + fact*bMin], Real.Floor[offset + fact*aMin]]; ls.imax _ MIN[Real.Floor[offset + fact*bMax], Real.Ceiling[offset + fact*aMax]]; IF ls.imax < ls.imin THEN smooth _ HarmonicFloor[smooth*0.999/tp]*tp ELSE EXIT; ENDLOOP; ls.period _ smooth/tp; IF ls.period < 1.0 THEN {ls.period _ Real.Round[1.0/ls.period]; ls.invert _ TRUE} ELSE {ls.period _ Real.Round[ls.period]; ls.invert _ FALSE}; RETURN} ELSE { smooth: REAL _ PositionalCeiling[rough*ls.bc, 10.0, ln10, decimalCoefs]/ls.bc; aFirst, aLast, bFirst, bLast: REAL; PickOne: PROC ~ { ls.imin _ ls.imax _ 1; ls.period _ aMin; RETURN}; DO tmin, tmax: BOOL _ FALSE; aFirst _ aMin/smooth; aLast _ aMax/smooth; bFirst _ bMin/smooth; bLast _ bMax/smooth; IF ABS[aFirst]>INT.LAST OR ABS[aLast]>INT.LAST OR ABS[bFirst]>INT.LAST OR ABS[bLast]>INT.LAST THEN --aWidth is tiny compared to min & max-- {PickOne[]; RETURN}; ls.imin _ MAX[Real.Floor[aFirst], Real.Ceiling[bFirst]]; ls.imax _ MIN[Real.Ceiling[aLast], Real.Floor[bLast]]; SELECT TRUE FROM ls.imax < ls.imin => {rough _ rough*0.5; smooth _ PositionalCeiling[rough*ls.bc, 10.0, ln10, decimalCoefs]/ls.bc; ls.period _ smooth}; ls.imax = ls.imin => EXIT; smooth*ls.imin = smooth*(REAL[ls.imin]+1.0) => {PickOne[]; RETURN}; smooth*ls.imax = smooth*(REAL[ls.imax]-1.0) => {PickOne[]; RETURN}; smooth < minLabelSpacing => {ls.imax _ ls.imin; EXIT}; ENDCASE => EXIT; ENDLOOP; ls.period _ smooth; RETURN}; }; LinearEnumerateTicks: PROC [ labelPolicy: LabelPolicy, ba: BiAxial, axis: Axis, imageDest: ImageDestination, bMin, bMax: REAL, --bounds of visible area for ticks, client cordinates-- aMin, aMax: REAL, --intersection of above with extent of client data-- labelWidthEstc: REAL, --part of the above estimate, appropriately scaled ctx: Imager.Context--Viewer coordinates--, ConsumeTick: PROC [ coord: REAL--in client cordinates--, labelBounds: Box--Viewer scale--, DrawLabel: PROC [org: VEC] _ NIL ]] ~ { ls: LinearSpecific ~ NARROW[labelPolicy.data]; font: Font ~ ls.font[imageDest]; IF aMax < aMin THEN RETURN; IF aMin # ls.aMin OR aMax # ls.aMax OR bMin # ls.bMin OR bMax # ls.bMax OR labelWidthEstc # ls.labelWidthEstc THEN LinearPrepare[ls, ba, bMin, bMax, aMin, aMax, labelWidthEstc]; ctx.SetFont[font]; IF ls.log THEN { invert: BOOL ~ ls.invert; n: REAL ~ ls.period; bt: REAL ~ ls.bt; bc: REAL ~ ls.bc; cc: REAL ~ ls.cc; zc: REAL ~ ls.zc; FOR i: INT IN [ls.imin .. ls.imax] DO exp: REAL ~ IF invert THEN i/n ELSE i*n; labX: REAL ~ RealFns.Power[bt, exp]; cX: REAL ~ RealFns.Log[bc, labX/cc]; label: ROPE ~ IO.PutFR[ls.format, [real[IF cX>zc THEN labX ELSE 0.0]] ]; ext: ImagerFont.Extents ~ font.RopeBoundingBox[label]; DrawLogLabel: PROC [org: VEC] ~ { ctx.SetXY[org]; ctx.ShowRope[label]; RETURN}; ConsumeTick[cX, ImagerBox.BoxFromExtents[ext], DrawLogLabel]; ENDLOOP; } ELSE { FOR i: INT IN [ls.imin .. ls.imax] DO cx: REAL ~ i*ls.period; lx: REAL ~ ls.cc + cx*ls.bc; label: ROPE ~ IO.PutFR[ls.format, [real[lx]] ]; ext: ImagerFont.Extents ~ font.RopeBoundingBox[label]; DrawLinearLabel: PROC [org: VEC] ~ { ctx.SetXY[org]; ctx.ShowRope[label]; RETURN}; ConsumeTick[cx, ImagerBox.BoxFromExtents[ext], DrawLinearLabel]; ENDLOOP; }; RETURN}; PositionalCeiling: PUBLIC PROC [x, base, lnBase: REAL, coefs: RealSeq] RETURNS [REAL] ~ { lnx: REAL ~ RealFns.Ln[x]; ilog: INTEGER _ Real.Round[lnx/lnBase]; basen: REAL _ RIExp[base, ilog]; seekc: REAL; IF RealFns.AlmostEqual[x, basen, pcDist] THEN RETURN [basen]; IF basen > x THEN {ilog _ ilog - 1; basen _ basen/base}; seekc _ x/basen; FOR i: NAT IN (0..coefs.length) DO --basen * coefs[i-1] <= x c: REAL ~ coefs[i]; IF c > seekc OR RealFns.AlmostEqual[c, seekc, pcDist] THEN RETURN [basen * c]; ENDLOOP; RETURN [basen*base]}; pcDist: INTEGER _ -5; HarmonicCeiling: PUBLIC PROC [x: REAL] RETURNS [REAL] ~ { IF x >= INT.LAST THEN RETURN [x]; IF x >= 1.0 THEN {n: INT ~ Real.Ceiling[x]; RETURN [n]}; {y: REAL ~ 1.0 / x; IF y = 1.0 THEN RETURN [y]; RETURN [1.0 / HarmonicFloor[y] ]}}; HarmonicFloor: PUBLIC PROC [x: REAL] RETURNS [REAL] ~ { IF x >= INT.LAST THEN RETURN [x]; IF x >= 1.0 THEN {n: INT ~ Real.Floor[x]; RETURN [n]}; {y: REAL ~ 1.0 / x; IF y = 1.0 THEN RETURN [y]; RETURN [1.0 / HarmonicCeiling[y] ]}}; RIExp: PROC [base: REAL, exp: INTEGER] RETURNS [ans: REAL] ~ { inv: BOOL ~ exp < 0; pexp: NAT _ ABS[exp]; ans _ 1.0; WHILE pexp#0 DO IF (pexp MOD 2) # 0 THEN ans _ ans*base; IF (pexp _ pexp/2) # 0 THEN base _ base*base; ENDLOOP; IF inv THEN ans _ 1.0/ans; RETURN}; BeVec: PROC [ra: REF ANY] RETURNS [VEC] ~ {RETURN [NARROW[ra, REF VEC]^]}; BeBool: PROC [ra: REF ANY] RETURNS [BOOL] ~ {RETURN [SELECT ra FROM $FALSE => FALSE, $TRUE => TRUE, ENDCASE => ERROR]}; Blend: PROC [a: REAL, b0, b1: REAL] RETURNS [c: REAL] = {c _ (1-a)*b0 + a*b1}; Start: PROC ~ { decimalCoefs[0] _ 1.0; decimalCoefs[1] _ 2.0; decimalCoefs[2] _ 3.0; decimalCoefs[3] _ 5.0; linearDefaultPrintFont _ ImagerFont.Find["xerox/xc1-2-2/classic", substituteWithWarning ! Imager.Warning => {SimpleFeedback.PutF[$BiAxialsImpl, oneLiner, $Warning, "Imager.Warning[%g, %g] trying to Find the default print font for linear label policies.", [atom[error.code]], [rope[error.explanation]] ]; RESUME}; Imager.Error => {SimpleFeedback.PutF[$BiAxialsImpl, oneLiner, $Error, "Imager.Error[%g, %g] trying to Find the default print font for linear label policies; VFonts.defaultFont will be used instead.", [atom[error.code]], [rope[error.explanation]] ]; linearDefaultPrintFont _ VFonts.defaultFont; CONTINUE} ].Scale[10]; linearDefaultScreenFont _ VFonts.defaultFont; Menus.ReplaceMenuEntry[baMenu, Menus.FindEntry[baMenu, "Rotate"], NIL]; Menus.ReplaceMenuEntry[baMenu, Menus.FindEntry[baMenu, "Draw"], Menus.CreateEntry["Draw", DrawButt]]; RETURN}; Start[]; END. ` BiAxialsImpl.mesa Copyright Σ 1991 by Xerox Corporation. All rights reserved. Spreitze, October 23, 1991 10:48 am PDT When logarithmic: period = n, and invert=(p<0) (see comment on LinearAxisSpec). bt ^ (mi * np) = cc * bc ^ x <=> mi * np ln bt = ln cc + x ln bc <=> mi = (ln cc + x ln bc) / (np ln bt) between ticks, np ln bt = ln bc Dx Κ)•NewlineDelimiter ™code™K™˜IKšœ œ˜%šž œœ˜KšœŸœ˜$KšœŸœ˜!Kšž œœœ˜ Kšœ˜Kšœ œ*˜9Kšœ Ÿœ'˜IKšœ œ6˜BKšœœZ˜eKšœœJ˜UKšœ0˜0Kšœ œ˜Kšœ œœF˜[Kšœ˜—Kšœ0˜0Kšœβ˜βšœ'œ˜/Kšœ œœ,˜7Kšœ œœ œ*˜JK˜4KšœR œ˜V—Kšœ˜—Kšœ$˜$Kš œ œ œ* œ œ˜vKšœ1 œ œ˜MK˜/K˜1K˜1šœ6˜6Kšœ<˜Kšœ1œ˜<—Kšœ˜—šœ˜Kšœœ œ  œ˜NKšœœ˜#šžœœ˜K˜K˜Kšœ˜—š˜Kšœ œœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kš&œœ œœœœœœœœ œœœœœœœŸ(œ œ˜ Kšœ œ+˜8Kšœ œ)˜6šœœ˜˜(Kšœ% œ  œ˜HKšœ˜—Kšœœ˜Kšœœœ˜CKšœœœ˜CKšœ0œ˜6Kšœœ˜—Kšœ˜—K˜Kšœ˜—Kšœ˜—K˜šžœœ˜Kšœ˜Kšœ ˜ Kšœ ˜ Kšœ˜Kšœ œŸ7˜IKšœ œŸ4˜FKšœ  œœŸ2˜HKšœŸœ˜*šž œœ˜KšœŸœ˜$KšœŸœ˜!Kšž œœœ˜ Kšœ˜—K˜Kšœœ˜.K˜ Kšœ œœ˜Kšœœœœœ œ œœ< œ˜±K˜šœœ˜Kšœœ ˜Kšœœ ˜Kšœ œœ œ˜Kšœ œœ œ˜Kšœ œœ œ˜Kšœ œœ œ˜šœœœ˜%Kš œœœœœ˜(Kšœœ œ˜$Kšœœ œ œ˜$Kš œœœœ œœœ ˜HKšœ6˜6šž œœœ˜!K˜K˜Kšœ˜—Kšœ=˜=Kšœ˜—K˜—šœ˜šœœœ˜%Kšœœ˜Kšœœ œ  œ˜Kšœœœ˜/Kšœ6˜6šžœœœ˜$K˜K˜Kšœ˜—Kšœ@˜@Kšœ˜—K˜—Kšœ˜—K˜š žœœœœœœ˜YKšœœ˜Kšœœ˜'Kšœ’œœ˜ Kšœœ˜ Kš œ’œ œœ’œ˜=Kš œ’œœ’œ’œ˜8Kšœ’œ˜š œœœœŸΠcuŸ˜Kšœœ ˜Kšœœœ˜K˜ šœ˜Kšœœœ˜(Kšœœ˜-Kšœ˜—Kšœœ˜Kšœ˜—K˜š žœœœœœœ˜'Kš œœœœœ˜"—K˜šžœœœœœœœœ˜CKšœ œ˜Kšœ œ˜Kšœœ˜—K˜šžœœ£œœ œœœ˜5Kšœ £œ£œ˜—K˜šžœœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜šœY˜YKšœΦœ˜ήKšœ¦œ˜―Kšœ ˜ —K˜-KšœBœ˜GKšœe˜eKšœ˜—K˜K˜K˜Kšœ˜—…—Sψn