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
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
Imported Types
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;
Local Types
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;
Tool for Plotting Height Fields
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;
find old left and right positions
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);
delete inbetween nodes
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;
};
build new nodes
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;
link them in
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]];
initialize the sequences and lists
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);
get the heights along the near slice
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];
};
get the heights along the far slice (we'll compute it even if not needed!)
farSlice[xIndex] ¬ LerpPair[bBL, bBR, xAlpha];
farSlice[xIndex].y ¬ farSlice[xIndex].y + GetVal[xIndex, yIndex+1, xValue, nextyValue];
ENDLOOP;
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
lineList.length ¬ 1;
lineList[0] ¬ [nearSlice[ht.nXSamples-1], farSlice[ht.nXSamples-1]];
IF ht.drawY THEN UpdateLines[lineList];
-- now the h/v pairs
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;
draw the outline (so we know we're alive!)
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];
};
Test Command
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];
val ← r2/2.0;
RETURN[200.0 * val];
};
HeightPlotTest: Commander.CommandProc ~ {
refSincData: SincData ¬ NEW[SincDataRep];
MakeHeightToolFromProc["HieghtPlotTest", Sinc, SincPrep, refSincData];
};
Start Code
icon: Icons.IconFlavor ¬ Icons.NewIconFromFile["G3dUser.icons", 4];
G3dTool.Register["3dHeightPlotTest", HeightPlotTest, "Usage: 3dHeightPlotTest (test)"];
END.