DIRECTORY CedarProcess, Commander, Controls, Draw2d, FS, G2dBasic, G3dBasic, G3dPlot, G3dTool, G3dVector, Icons, ImagerBackdoor, IO, MessageWindow, Process, Prop, Real, RealFns, Rope, ViewerAbort, ViewerClasses, ViewerOps, ViewerTools; G3dPlotImpl: CEDAR PROGRAM IMPORTS CedarProcess, Controls, Draw2d, FS, G3dTool, Icons, ImagerBackdoor, MessageWindow, Process, Prop, Real, RealFns, Rope, ViewerAbort, ViewerOps, ViewerTools EXPORTS G3dPlot ~ BEGIN ROPE: TYPE ~ Rope.ROPE; Viewer: TYPE ~ Controls.Viewer; ClickProc: TYPE ~ Controls.ClickProc; OuterData: TYPE ~ Controls.OuterData; Control: TYPE ~ Controls.Control; Typescript: TYPE ~ Controls.Typescript; Context: TYPE ~ Draw2d.Context; DrawProc: TYPE ~ Draw2d.DrawProc; Pair: TYPE ~ G3dPlot.Pair; PairSequence: TYPE ~ G3dPlot.PairSequence; PairSequenceRep: TYPE ~ G3dPlot.PairSequenceRep; Triple: TYPE ~ G2dBasic.Triple; IntPair: TYPE ~ G2dBasic.IntPair; Line: TYPE ~ G3dPlot.Line; LineSequence: TYPE ~ G3dPlot.LineSequence; LineSequenceRep: TYPE ~ G3dPlot.LineSequenceRep; RealSequence: TYPE ~ G3dBasic.RealSequence; RealSequenceRep: TYPE ~ G3dBasic.RealSequenceRep; HeightField: TYPE ~ G3dPlot.HeightField; HeightFieldRep: TYPE ~ G3dPlot.HeightFieldRep; HeightProc: TYPE ~ G3dPlot.HeightProc; PrepareProc: TYPE ~ G3dPlot.PrepareProc; IntRange: TYPE ~ G3dPlot.IntRange; RealRange: TYPE ~ G3dPlot.RealRange; HorizonType: TYPE ~ G3dPlot.HorizonType; HorizonList: TYPE ~ G3dPlot.HorizonList; HorizonListRep: TYPE ~ G3dPlot.HorizonListRep; HorizonNode: TYPE ~ G3dPlot.HorizonNode; HorizonNodeRep: TYPE ~ G3dPlot.HorizonNodeRep; HeightTool: TYPE ~ G3dPlot.HeightTool; HeightToolRep: TYPE ~ G3dPlot.HeightToolRep; MakeHeightTool: PROC [ht: HeightTool, name: ROPE, controls: LIST OF Controls.Control] ~ { ht.outer ¬ Controls.OuterViewer[ name: name, buttons: LIST[ Controls.ClickButton["Interpress Out", IPOutButton, ht], Controls.ClickButton["Hide X ", DrawXButton, ht], Controls.ClickButton["Hide Y ", DrawYButton, ht] ], controls: CONS[Controls.NewControl["Z Scale",, ht, -5.0, 5.0, 1.0, Controller], controls], typescriptHeight: 18, graphicsHeight: 300, drawProc: Draw, clientData: ht, noOpen: TRUE].parent; ht.outer.label ¬ name; ht.outerData ¬ NARROW[ht.outer.data]; ht.graphics ¬ ht.outerData.graphics; ht.outer.icon ¬ icon; IF ht.heightProc # NIL THEN SetHeightsFromProc[ht]; ViewerOps.OpenIcon[ht.outer]; }; SetHeightsFromProc: PROC [ht: HeightTool] ~ { dx: REAL ¬ (ht.xRange.h-ht.xRange.l)/MAX[1.0, (ht.nXSamples-1.0)]; dy: REAL ¬ (ht.yRange.h-ht.yRange.l)/MAX[1.0, (ht.nYSamples-1.0)]; ht.nSamples ¬ (ht.nXSamples+1)*(ht.nYSamples+1); IF ht.heightField = NIL THEN ht.heightField ¬ NEW[HeightFieldRep[ht.nYSamples+1]]; IF ht.heightField.maxLength < ht.nYSamples+1 THEN ht.heightField ¬ NEW[HeightFieldRep[ht.nYSamples+1]]; ht.heightField.length ¬ ht.nYSamples+1; FOR n: NAT IN [0..ht.nYSamples] DO IF ht.heightField[n] = NIL OR ht.heightField[n].maxLength < ht.nXSamples+1 THEN ht.heightField[n] ¬ NEW[RealSequenceRep[ht.nXSamples+1]]; ht.heightField[n].length ¬ ht.nXSamples+1; ENDLOOP; IF ht.prepareProc # NIL THEN ht.prepareProc[ht.xRange.l, ht.xRange.h, ht.yRange.l, ht.yRange.h, ht.nXSamples, ht.nYSamples, ht.clientData]; FOR iy: NAT IN [0..ht.nYSamples) DO y: REAL ¬ ht.yRange.l+iy*dy; reals: RealSequence ¬ ht.heightField[iy]; FOR ix: NAT IN [0..ht.nXSamples) DO reals[ix] ¬ ht.heightProc[ht.xRange.l+ix*dx, y, ht.clientData]; ENDLOOP; ENDLOOP; }; Draw: Controls.DrawProc ~ TRUSTED { ht: HeightTool ¬ NARROW[clientData]; IF CedarProcess.GetStatus[ht.drawProcess] # busy THEN { FOR c: LIST OF Control ¬ ht.outerData.controls, c.rest WHILE c # NIL DO SELECT TRUE FROM Rope.Equal[c.first.name, "Height"] => ht.zScale ¬ c.first.value; Rope.Equal[c.first.name, "xMin"] => ht.xRange.l ¬ c.first.value; Rope.Equal[c.first.name, "xMax"] => ht.xRange.h ¬ c.first.value; Rope.Equal[c.first.name, "yMin"] => ht.yRange.l ¬ c.first.value; Rope.Equal[c.first.name, "yMax"] => ht.yRange.h ¬ c.first.value; Rope.Equal[c.first.name, "xSamp"] => ht.nXSamples ¬ Real.Round[c.first.value]; Rope.Equal[c.first.name, "ySamp"] => ht.nYSamples ¬ Real.Round[c.first.value]; Rope.Equal[c.first.name, "yOffset"] => ht.yOffset ¬ Real.Round[c.first.value]; ENDCASE; ENDLOOP; context.propList ¬ Prop.Put[context.propList, $HeightTool, ht]; [] ¬ CedarProcess.Join[ht.drawProcess ¬ CedarProcess.Fork[DoDraw, context]]; context.propList ¬ Prop.Rem[context.propList, $HeightTool]; }; }; DoDraw: CedarProcess.ForkableProc ~ { Action: PROC ~ { Lerp: PROC [lo, hi, a: REAL] RETURNS [REAL] ~ { RETURN[lo + (a * (hi-lo))]; }; LerpPair: PROC [lo, hi: Pair, a: REAL] RETURNS [Pair] ~ { RETURN[[Lerp[lo.x, hi.x, a], Lerp[lo.y, hi.y, a]]]; }; IntersectPairs: PROC [j0, j1, k0, k1: Pair] RETURNS [Pair] ~ { Intersect: PROC [a, b: Triple] RETURNS [Pair] ~ { det: REAL ¬ a.x*b.y - b.x*a.y; IF det # 0.0 THEN det ¬ 1.0/det ELSE det ¬ 1.0; RETURN[[det*(a.y*b.z - b.y*a.z), det*(b.x*a.z - a.x*b.z)]]; }; a: Triple ¬ [j0.y-j1.y, j1.x-j0.x, j0.x*j1.y - j1.x*j0.y]; b: Triple ¬ [k0.y-k1.y, k1.x-k0.x, k0.x*k1.y - k1.x*k0.y]; RETURN[Intersect[a, b]]; }; InsertHorizon: PROC [h: HorizonList, left, right: Pair] ~ { InsertABeforeB: PROC [a, b: HorizonNode] ~ { a.prev ¬ b.prev; a.next ¬ b; b.prev.next ¬ a; b.prev ¬ a; }; InsertAAfterB: PROC [a, b: HorizonNode] ~ { a.prev ¬ b; a.next ¬ b.next; b.next.prev ¬ a; b.next ¬ a; }; DeleteA: PROC [a: HorizonNode] ~ { a.prev.next ¬ a.next; a.next.prev ¬ a.prev; a.prev ¬ a.next ¬ NIL; }; ll, lr, rl, rr: HorizonNode; newright, newleft: HorizonNode; oldly, oldry, lm, lb, rm, rb: REAL; ll ¬ lr ¬ rl ¬ rr ¬ h.firstSaved; WHILE lr # NIL AND lr.x <= left.x DO lr ¬ lr.next; ENDLOOP; ll ¬ lr.prev; WHILE rr # NIL AND rr.x <= right.x DO rr ¬ rr.next; ENDLOOP; rl ¬ rr.prev; lm ¬ (lr.ly - ll.ry) / MAX[1.0, (lr.x - ll.x)]; lb ¬ ll.ry - (ll.x * lm); oldly ¬ lb + (lm * left.x); rm ¬ (rr.ly - rl.ry) / MAX[1.0, (rr.x - rl.x)]; rb ¬ rr.ry - (rr.x * rm); oldry ¬ rb + (rm * right.x); IF lr # rr THEN { -- just wipe the links, the gc will pick up the nodes lr.prev ¬ rl.next ¬ NIL; ll.next ¬ rr; rr.prev ¬ ll; }; newleft ¬ NEW[HorizonNodeRep]; newleft.x ¬ left.x; newleft.ly ¬ oldly; newleft.ry ¬ left.y; newright ¬ NEW[HorizonNodeRep]; newright.x ¬ right.x; newright.ly ¬ right.y; newright.ry ¬ oldry; InsertAAfterB[newleft, ll]; InsertABeforeB[newright, rr]; IF ABS[ll.x - newleft.x] < ht.epsilon THEN { newleft.ly ¬ ll.ly; IF rr # ll THEN DeleteA[ll]; }; IF ABS[rr.x - newright.x] < ht.epsilon THEN { newright.ry ¬ rr.ry; IF rr # ll THEN DeleteA[rr]; }; }; UpdateAction: TYPE ~ { draw, insert }; LineAction: PROC [h: HorizonList, left, right: Pair, action: UpdateAction] ~ { Flush: PROC ~ { IF qLeft.x > qRight.x THEN { tmp: Pair ¬ qLeft; qLeft ¬ qRight; qRight ¬ tmp; }; IF pending AND ABS[qRight.x - qLeft.x] > ht.epsilon THEN IF action = draw THEN Draw2d.Line[context, qLeft, qRight, solid, zip] ELSE InsertHorizon[h, qLeft, qRight]; pending ¬ FALSE; }; Extend: PROC [left, right: Pair] ~ { IF NOT pending THEN { qLeft ¬ left; pending ¬ TRUE; }; qRight ¬ right; }; NearBy: PROC [a, b: Pair] RETURNS [BOOL] ~ { RETURN[(ABS[a.x - b.x] < ht.epsilon) AND (ABS[a.y - b.y] < ht.epsilon)]; }; pending: BOOL ¬ FALSE; qLeft, qRight: Pair; n: HorizonNode; m: REAL ¬ (right.y - left.y) / MAX[1.0, (right.x - left.x)]; b: REAL ¬ right.y - (right.x * m); n ¬ h.first; WHILE n # NIL AND n.x < left.x DO n ¬ n.next; ENDLOOP; n ¬ n.prev; WHILE n.x < right.x DO xWindow: RealRange ¬ [MAX[n.x, left.x], MIN[n.next.x, right.x]]; bandm: REAL ¬ (n.next.ly - n.ry) / MAX[1.0, (n.next.x - n.x)]; bandb: REAL ¬ n.ry - (n.x * bandm); oldLeft: Pair ¬ [xWindow.l, bandb + (bandm * xWindow.l)]; oldRight: Pair ¬ [xWindow.h, bandb + (bandm * xWindow.h)]; newLeft: Pair ¬ [xWindow.l, b + (m * xWindow.l)]; newRight: Pair ¬ [xWindow.h, b + (m * xWindow.h)]; leftSign: BOOL ¬ IF h.type = upper THEN newLeft.y >= oldLeft.y ELSE newLeft.y <= oldLeft.y; rightSign: BOOL ¬ IF h.type = upper THEN newRight.y >= oldRight.y ELSE newRight.y <= oldRight.y; IF NearBy[left, [n.x, n.ry]] THEN { IF h.type = upper THEN IF m > bandm THEN leftSign ¬ TRUE ELSE leftSign ¬ FALSE ELSE IF m < bandm THEN leftSign ¬ TRUE ELSE leftSign ¬ FALSE; }; IF NearBy[right, [n.next.x, n.next.ly]] THEN { IF h.type = upper THEN IF m < bandm THEN leftSign ¬ TRUE ELSE leftSign ¬ FALSE ELSE IF m > bandm THEN leftSign ¬ TRUE ELSE leftSign ¬ FALSE; }; IF leftSign # rightSign THEN { hitPt: Pair ¬ IntersectPairs[oldLeft, oldRight, newLeft, newRight]; IF leftSign = TRUE THEN { Extend[newLeft, hitPt]; Flush[]; } ELSE { Flush[]; Extend[hitPt, newRight]; }; } ELSE IF leftSign = TRUE THEN Extend[newLeft, newRight]; IF n.next = NIL THEN n ¬ n; n ¬ n.next; ENDLOOP; Flush[]; }; CopyAction: TYPE ~ { backup, restore }; CopyHorizonList: PROC [h: HorizonList, action: CopyAction] ~ { src: HorizonNode ¬ IF action = backup THEN h.first ELSE h.firstSaved; dst: HorizonNode ¬ NIL; WHILE src # NIL DO n: HorizonNode ¬ NEW[HorizonNodeRep]; n.x ¬ src.x; n.ly ¬ src.ly; n.ry ¬ src.ry; IF dst = NIL THEN { IF action = backup THEN h.firstSaved ¬ n ELSE h.first ¬ n; } ELSE { n.prev ¬ dst; dst.next ¬ n; }; dst ¬ n; src ¬ src.next; ENDLOOP; }; UpdateLines: PROC [list: LineSequence] ~ { FOR k: INT IN [0 .. 1] DO h: HorizonList ¬ IF k = 0 THEN upper ELSE lower; FOR i: INT IN [0 .. list.length) DO -- first draw all the lines LineAction[h, list[i].left, list[i].right, draw]; ENDLOOP; CopyHorizonList[h, backup]; FOR i: INT IN [0 .. list.length) DO -- now update visibility lists LineAction[h, list[i].left, list[i].right, insert]; ENDLOOP; CopyHorizonList[h, restore]; ENDLOOP; }; InitLists: PROC ~ { ul: HorizonNode ¬ NEW[HorizonNodeRep]; ur: HorizonNode ¬ NEW[HorizonNodeRep]; ll: HorizonNode ¬ NEW[HorizonNodeRep]; lr: HorizonNode ¬ NEW[HorizonNodeRep]; ul.x ¬ -1.0; ul.ly ¬ ul.ry ¬ -1.0; ul.next ¬ ur; ur.x ¬ bounds.w+1.0; ur.ly ¬ ur.ry ¬ -1.0; ur.prev ¬ ul; ll.x ¬ -1.0; ll.ly ¬ ll.ry ¬ bounds.h+1; ll.next ¬ lr; lr.x ¬ bounds.w+1.0; lr.ly ¬ lr.ry ¬ bounds.h+1; lr.prev ¬ ll; upper.first ¬ ul; lower.first ¬ ll; upper.type ¬ upper; lower.type ¬ lower; }; zip: Draw2d.Zip ¬ Draw2d.GetZip[context]; intWidth: INT ¬ Real.Round[canvasWidth]; nearSlice: PairSequence ¬ NEW[PairSequenceRep[ht.nXSamples]]; upper: HorizonList ¬ NEW[HorizonListRep]; lower: HorizonList ¬ NEW[HorizonListRep]; farSlice: PairSequence ¬ NEW[PairSequenceRep[ht.nXSamples]]; xDistance: REAL ¬ ht.xRange.h - ht.xRange.l; yDistance: REAL ¬ ht.yRange.h - ht.yRange.l; invYAlpha: REAL ¬ 1.0 / MAX[1.0, (ht.nYSamples-1.0)]; invXAlpha: REAL ¬ 1.0 / MAX[1.0, (ht.nXSamples-1.0)]; maxLines: INT ¬ MAX[ht.nXSamples, ht.nYSamples]; lineList: LineSequence ¬ NEW[LineSequenceRep[maxLines]]; InitLists[]; lineList.length ¬ 0; nearSlice.length ¬ farSlice.length ¬ ht.nXSamples; FOR yIndex: INT IN [0 .. ht.nYSamples) DO yAlpha: REAL ¬ yIndex * invYAlpha; nextyAlpha: REAL ¬ (yIndex+1) * invYAlpha; yValue: REAL ¬ ht.yRange.l + (yAlpha * yDistance); nextyValue: REAL ¬ ht.yRange.l + (nextyAlpha * yDistance); bFL: Pair ¬ LerpPair[pFL, pBL, yAlpha]; bFR: Pair ¬ LerpPair[pFR, pBR, yAlpha]; bBL: Pair ¬ LerpPair[pFL, pBL, nextyAlpha]; bBR: Pair ¬ LerpPair[pFR, pBR, nextyAlpha]; GetVal: PROC [xIndex, yIndex: INT, xValue, yValue: REAL] RETURNS [REAL] ~ { v: REAL ¬ IF ht.heightProc = NIL THEN ht.heightField[yIndex][xIndex] ELSE ht.heightProc[xValue, yValue, ht.clientData]; RETURN[ht.zScale * v]; }; Process.CheckForAbort[]; FOR xIndex: INT IN [0 .. ht.nXSamples) DO xAlpha: REAL ¬ xIndex * invXAlpha; xValue: REAL ¬ ht.xRange.l + (xAlpha * xDistance); IF yIndex > 0 THEN { nearSlice[xIndex] ¬ farSlice[xIndex]; } ELSE { nearSlice[xIndex] ¬ LerpPair[bFL, bFR, xAlpha]; nearSlice[xIndex].y ¬ nearSlice[xIndex].y + GetVal[xIndex, yIndex, xValue, yValue]; }; farSlice[xIndex] ¬ LerpPair[bBL, bBR, xAlpha]; farSlice[xIndex].y ¬ farSlice[xIndex].y + GetVal[xIndex, yIndex+1, xValue, nextyValue]; ENDLOOP; lineList.length ¬ 1; lineList[0] ¬ [nearSlice[ht.nXSamples-1], farSlice[ht.nXSamples-1]]; IF ht.drawY THEN UpdateLines[lineList]; FOR xIndex: INT DECREASING IN [0 .. ht.nXSamples-1) DO lineList.length ¬ 0; IF ht.drawX THEN { lineList[lineList.length] ¬ [nearSlice[xIndex], nearSlice[xIndex+1]]; lineList.length ¬ lineList.length+1; }; IF ht.drawY THEN { lineList[lineList.length] ¬ [nearSlice[xIndex], farSlice[xIndex]]; lineList.length ¬ lineList.length+1; }; UpdateLines[lineList]; ENDLOOP; ENDLOOP; Draw2d.Line[context, pFL, pFR, dotted, zip]; Draw2d.Line[context, pFR, pBR, dotted, zip]; Draw2d.Line[context, pBR, pBL, dotted, zip]; Draw2d.Line[context, pBL, pFL, dotted, zip]; }; AddPair: PROC [p1, p2: Pair] RETURNS [Pair] ~ { RETURN[[p1.x+p2.x, p1.y+p2.y]]; }; GetBounds: PROC RETURNS [bounds: Imager.Rectangle] ~ { bounds ¬ [0, 0, 100, 100]; bounds ¬ ImagerBackdoor.GetBounds[context ! Imager.Error => CONTINUE]; }; context: Context ¬ NARROW[data]; ht: HeightTool ¬ NARROW[Prop.Get[context.propList, $HeightTool]]; bounds: Imager.Rectangle ¬ GetBounds[]; pLL: Pair ¬ [0.05, 0.05]; -- these variables should be command-line or file pUR: Pair ¬ [0.95, 0.95]; pWidth: REAL ¬ 0.7; -- percent of width taken by one h line pRise: REAL ¬ 0.4; -- percent of height taken by plot baselines skew: REAL ¬ 0.05; -- amount by which baseline is tilted (skew*pWidth < pRise) screenWidth: INT ¬ Real.Round[bounds.w]; canvasWidth: REAL ¬ (pUR.x - pLL.x) * bounds.w; canvasHeight: REAL ¬ (pUR.y - pLL.y) * bounds.h; canvasLeft: REAL ¬ pLL.x * bounds.w; canvasBottom: REAL ¬ pLL.y * bounds.h; canvasRight: REAL ¬ canvasLeft + canvasWidth; canvasTop: REAL ¬ canvasBottom + canvasHeight; canvasOrigin: Pair ¬ [canvasLeft, canvasBottom]; pFL: Pair ¬ [0.0, skew*pWidth*canvasWidth]; -- front left pFR: Pair ¬ [(1.0-skew)*pWidth*canvasWidth, 0.0]; -- front right pBL: Pair ¬ [canvasWidth - pFR.x, pRise*canvasHeight]; -- back left pBR: Pair ¬ [canvasWidth, (pRise*canvasHeight)-pFL.y]; -- back right plotOffset: Pair ¬ [0.0, REAL[ht.yOffset]]; plotOrigin: Pair ¬ AddPair[plotOffset, canvasOrigin]; pFL ¬ AddPair[pFL, plotOrigin]; pFR ¬ AddPair[pFR, plotOrigin]; pBL ¬ AddPair[pBL, plotOrigin]; pBR ¬ AddPair[pBR, plotOrigin]; ViewerAbort.CallWithAbortEnabled[ht.graphics, Action]; }; IPOutButton: ViewerClasses.ClickProc ~ { fileName: Rope.ROPE ¬ ViewerTools.GetSelectionContents[]; IF Rope.IsEmpty[fileName] THEN Complain["\t\tPlease select a filename first."] ELSE Draw2d.IPOut[fileName, Draw, clientData ! FS.Error => {Complain[Rope.Concat["Bad name: ", fileName]]; CONTINUE}]; }; DrawXButton: ViewerClasses.ClickProc ~ { ht: HeightTool ¬ NARROW[clientData]; IF ht.drawX THEN { ht.drawX ¬ FALSE; Controls.ButtonRelabel[ht.outerData, "Hide X ", "Draw X "]; } ELSE { ht.drawX ¬ TRUE; Controls.ButtonRelabel[ht.outerData, "Draw X ", "Hide X "]; }; ViewerOps.PaintViewer[ht.outer, client]; }; DrawYButton: ViewerClasses.ClickProc ~ { ht: HeightTool ¬ NARROW[clientData]; IF ht.drawY THEN { ht.drawY ¬ FALSE; Controls.ButtonRelabel[ht.outerData, "Hide Y ", "Draw Y "]; } ELSE { ht.drawY ¬ TRUE; Controls.ButtonRelabel[ht.outerData, "Draw Y ", "Hide Y "]; }; ViewerOps.PaintViewer[ht.outer, client]; }; Complain: PROC [message: ROPE] ~ { MessageWindow.Append[message, TRUE]; MessageWindow.Blink[]; }; Controller: Controls.ControlProc ~ { IF control.mouse.state # down AND (control.mouse.button = right OR control.whatChanged = $TypedIn) THEN ViewerOps.PaintViewer[NARROW[control.clientData, HeightTool].graphics, client]; }; MakeHeightToolFromProc: PUBLIC PROC [ toolName: ROPE ¬ NIL, heightProc: HeightProc ¬ NIL, prepareProc: PrepareProc ¬ NIL, clientData: REF ANY ¬ NIL] ~ { ht: HeightTool ¬ NEW[HeightToolRep ¬ [ heightProc: heightProc, prepareProc: prepareProc, clientData: clientData]]; ht.epsilon ¬ .01; MakeHeightTool[ht, toolName, LIST[ Controls.NewControl["xMin",, ht, -100.0, 100.0, ht.xRange.l, Controller], Controls.NewControl["xMax",, ht, -100.0, 100.0, ht.xRange.h, Controller], Controls.NewControl["yMin",, ht, -100.0, 100.0, ht.yRange.l, Controller], Controls.NewControl["yMax",, ht, -100.0, 100.0, ht.yRange.h, Controller], Controls.NewControl["xSamp",, ht, 1, 100, ht.nXSamples, Controller,, 0], Controls.NewControl["ySamp",, ht, 1, 100, ht.nYSamples, Controller,, 0], Controls.NewControl["yOffset",, ht, -300, 300, ht.yOffset, Controller,, 0]]]; }; MakeHeightToolFromData: PUBLIC PROC [toolName: ROPE, heightField: HeightField] ~ { ht: HeightTool ¬ NEW[HeightToolRep ¬ [heightField: heightField]]; MakeHeightTool[ht, toolName, NIL]; }; SincData: TYPE ~ REF SincDataRep; SincDataRep: TYPE ~ RECORD [cycles, rScl, eScl: REAL]; SincPrep: PrepareProc ~ { d: SincData ¬ NARROW[clientData]; rMax: REAL ¬ RealFns.SqRt[(hx*hx)+(hy*hy)]; finalHeight: REAL ¬ 0.1; d.cycles ¬ 4.0; d.eScl ¬ RealFns.Ln[finalHeight]/rMax; d.rScl ¬ d.cycles * 2.0 * 3.1415926535 / rMax; }; Sinc: HeightProc ~ { d: SincData ¬ NARROW[clientData]; r2: REAL ¬ (x*x)+(y*y); r: REAL ¬ RealFns.SqRt[r2]; val: REAL ¬ RealFns.Exp[d.eScl * r] * RealFns.Cos[d.rScl * r]; RETURN[200.0 * val]; }; HeightPlotTest: Commander.CommandProc ~ { refSincData: SincData ¬ NEW[SincDataRep]; MakeHeightToolFromProc["HieghtPlotTest", Sinc, SincPrep, refSincData]; }; icon: Icons.IconFlavor ¬ Icons.NewIconFromFile["G3dUser.icons", 4]; G3dTool.Register["3dHeightPlotTest", HeightPlotTest, "Usage: 3dHeightPlotTest (test)"]; END. š G3dPlotImpl.mesa Copyright Σ 1985, 1992 by Xerox Corporation. All rights reserved. Glassner, May 31, 1989 11:13:08 am PDT Bloomenthal, July 22, 1992 11:20 pm PDT Imported Types Local Types Tool for Plotting Height Fields find old left and right positions delete inbetween nodes build new nodes link them in initialize the sequences and lists get the heights along the near slice get the heights along the far slice (we'll compute it even if not needed!) first the horizontal pass along bFL to bFR lineList.length _ 0; IF ht.drawX THEN { FOR xIndex: INT IN [0 .. ht.nXSamples-1) DO lineList[lineList.length] _ [nearSlice[xIndex], nearSlice[xIndex+1]]; lineList.length _ lineList.length+1; ENDLOOP; UpdateLines[lineList]; }; now the vertical stripes between the bands lineList.length _ 0; IF ht.drawY THEN { IF yIndex < ht.nYSamples-1 THEN { FOR xIndex: INT IN [0 .. ht.nXSamples) DO lineList[lineList.length] _ [nearSlice[xIndex], farSlice[xIndex]]; lineList.length _ lineList.length+1; ENDLOOP; UpdateLines[lineList]; }; }; -- first the right-most near-far edge -- now the h/v pairs draw the outline (so we know we're alive!) Test Command val _ r2/2.0; Start Code ΚB•NewlineDelimiter –"cedarcode" style™™Jšœ Οeœ6™BJšœ&™&™'J˜—JšΟk œ,žœJžœh˜λJ˜—šΡbln œžœž˜Jšžœ!žœx˜’Jšžœ˜J˜—Jšœž˜headšΟl™Jšžœžœžœ˜Jšœ žœ˜#Jšœžœ˜(Jšœžœ˜(Jšœ žœ˜%Jšœžœ˜*Jšœ žœ˜#Jšœ žœ˜$Jšœ žœ˜Jšœžœ˜,Jšœžœ˜1Jšœ žœ˜#Jšœ žœ˜%Jšœ žœ˜Jšœžœ˜,Jšœžœ˜1Jšœžœ˜-Jšœžœ˜2Jšœžœ˜+Jšœžœ˜0Jšœžœ˜)Jšœžœ˜+—š  ™ Jšœžœ˜&Jšœžœ˜'Jšœžœ˜*Jšœžœ˜+Jšœžœ˜0Jšœžœ˜*Jšœžœ˜/Jšœžœ˜)Jšœžœ˜.—š ™š Οnœžœžœ žœžœ˜Y˜ J˜ šœ žœ˜Jšœ8˜8Jšœ1˜1Jšœ0˜0Jšœ˜—Jšœ žœL˜ZJ˜J˜J˜J˜Jšœžœ ˜—J˜Jšœžœ˜%J˜$J˜Jšžœžœžœ˜3J˜J˜J˜—š‘œžœ˜-Jšœžœžœ˜BJšœžœžœ˜BJ˜0Jšžœžœžœžœ!˜Ršžœ+žœ˜2Jšœžœ!˜5—J˜'šžœžœžœž˜"šžœžœžœ-˜JJšžœžœ"˜>—J˜*Jšžœ˜—Jšžœžœžœo˜‹šžœžœžœž˜#Jšœžœ˜J˜)šžœžœžœž˜#J˜?Jšžœ˜—Jšžœ˜—J˜J˜—š‘œžœ˜#Jšœžœ ˜$šžœ/žœ˜7š žœžœžœ)žœžœž˜Gšžœžœž˜J˜@J˜@J˜@J˜@J˜@J˜NJ˜NJ˜NJšžœ˜—Jšžœ˜—J˜?J˜LJ˜;J˜—J˜J˜—š‘œ˜%J˜š‘œžœ˜J™Jš ‘œžœ žœžœžœžœ˜NJ˜š‘œžœžœžœ ˜9Jšžœ-˜3J˜J˜—š‘œžœžœ ˜>š‘ œžœžœ ˜1Jšœžœ˜Jšžœ žœžœ ˜/Jšžœ5˜;J˜—J˜:J˜:Jšžœ˜J˜J˜—š‘ œžœ(˜;š‘œžœ˜,J˜J˜ J˜J˜ J˜—š‘ œžœ˜+J˜ J˜J˜J˜ J˜—š‘œžœ˜"J˜J˜Jšœžœ˜J˜J˜—Jšœ˜J˜Jšœžœ˜#J˜!Jš žœžœžœžœžœ˜˜Nš‘œžœ˜šžœžœ˜J˜J˜J˜ J˜—šžœ žœžœ"ž˜8šžœ˜Jšžœ0˜4Jšžœ!˜%——Jšœ žœ˜J˜—š‘œžœ˜$Jšžœžœ žœžœ˜7J˜J˜—š‘œžœžœžœ˜,Jšžœžœžœžœ˜HJ˜—Jšœ žœžœ˜J˜J˜Jšœžœžœ˜Jšœžœ˜#J˜9J˜:J˜1J˜2šœ žœžœ˜"Jšžœžœ˜8—šœ žœžœ˜#Jšžœžœ˜<—šžœžœ˜#šžœ˜Jš žœžœ žœ žœžœ ž˜Jšœžœžœ žœ˜EJšœžœ˜šžœžœž˜Jšœžœ˜%J˜,šžœž˜ šžœ˜šžœ˜Jšžœ˜Jšžœ ˜—J˜—šžœ˜J˜ J˜ J˜——J˜J˜Jšžœ˜—J˜—J˜J˜š‘ œžœ˜*šžœžœžœ ž˜Jšœžœžœžœ˜0šžœžœžœ’˜AJšœ1˜1Jšžœ˜—J˜šžœžœžœ’˜DJšœ3˜3Jšžœ˜—J˜Jšžœ˜—J˜J˜—š‘ œžœ˜Jšœžœ˜&Jšœžœ˜&Jšœžœ˜&Jšœžœ˜&J˜2J˜:J˜8J˜@J˜J˜J˜J˜J˜—J˜J˜)Jšœ žœ˜(Jšœžœ ˜=Jšœžœ˜)Jšœžœ˜)Jšœžœ ˜J™ Jšžœ˜J˜J˜—š‘œ˜)Jšœžœ˜)JšœF˜FJ˜——š  ™ J˜CJ˜WJ˜—Jšžœ˜J˜—…—CθZΔ