TubeDesignmpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Bloomenthal, February 24, 1987 6:14:40 pm PST
DIRECTORY CedarProcess, Commander, Contours, Controls, Controls3d, Draw2d, Draw3d, Icons, Menus, Real, Render3d, Rope, ThreeDBasics, ThreeDMisc, TubeDefs, TubeIO, TubeContour, TubeDisplay, TubeGeometry, TubeMisc, TubePick, TubeRender, Vector3d, ViewerOps;
TubeDesignImpl: CEDAR PROGRAM
IMPORTS CedarProcess, Commander, Contours, Controls, Controls3d, Draw2d, Draw3d, Icons, Real, Render3d, Rope, TubeContour, TubeDisplay, TubeGeometry, TubeIO, TubePick, TubeMisc, TubeRender, Vector3d, ViewerOps
~ BEGIN
OPEN TubeDefs;
Types
UserData:   TYPE ~ RECORD [o: OuterData, p: ProgramData];
ActionType:  TYPE ~ {none, select, camera, spline, shape, button};
ProgramData:  TYPE ~ REF ProgramDataRep;
ProgramDataRep: TYPE ~ RECORD [
Controls:
controls:     ControlList ← NIL,   -- list of the following controls:
scale:      Control ← NIL,    -- scale
r0:       Control ← NIL,    -- radius 0
r1:       Control ← NIL,    -- radius 1
tw0:      Control ← NIL,    -- twist 0
tw1:      Control ← NIL,    -- twist 1
tens0:      Control ← NIL,    -- tension 0
tens1:      Control ← NIL,    -- tension 1
cres:      Control ← NIL,    -- circ. resolution
epsilon:     Control ← NIL,    -- spline flatness value
cam:      Camera ← NIL,    -- camera
hold:      Hold ← NIL,     -- handle on a point and its tangent
lim:      Control ← NIL,    -- vector limit
Buttons:
buttons:    ButtonList ← NIL,
Viewing:
outer:      Viewer ← NIL,
outerData:    OuterData ← NIL,
graphics:     Viewer ← NIL,
graphicsData:   GraphicsData ← NIL,
view:      Matrix ← NIL,
Rendering:
context3d:    Context3d ← NIL,
renderProcess:   CedarProcess.Process ← NIL,
vectors:     BOOLTRUE,
render:     BOOLFALSE,
renderStyle:    RenderStyle ← smooth,
backFaces:    BOOLFALSE,
noDraw:     BOOLFALSE,
State:
details:     Details ← NIL,
pendings:     Pendings ← NIL,
mouse:     Mouse ← [0, 0, none, left],
mousePrev:    Mouse ← [0, 0, none, left],
pick:      Pick ← NIL,
action:     ActionType ← none,
radiusMode:   RadiusMode ← linear,
round:     BOOLFALSE,
xformIO:     BOOLFALSE,    -- view-xform points when writing file
The tube:
tube:      Tube ← NIL     -- all the tube splines
];
Initialization
TubeDesign: Commander.CommandProc ~ {
p: ProgramData ~ NEW[ProgramDataRep];
InitProgramData[p];
InitViewer[p];
ReplaceTube[p, ResetTube[]];
};
InitProgramData: PROC [p: ProgramData] ~ {
p.view ← NEW[MatrixRep];
p.details ← NEW[DetailsRep];
p.pendings ← NEW[PendingsRep];
p.cam ← Controls3d.InitCamera[scale: 3.0, data: p, proc: CameraControl];
p.hold ← Controls3d.InitHold[TubeControl, p];
p.tw0 ← Controls.NewControl["tw0", vSlider, p, -1000., 1000., 0.0, TubeControl];
p.tw1 ← Controls.NewControl["tw1", vSlider, p, -1000., 1000., 0.0, TubeControl];
p.tens0 ← Controls.NewControl["tens0", vSlider, p, -3.0, 3.0, 0.0, TubeControl];
p.tens1 ← Controls.NewControl["tens1", vSlider, p, -3.0, 3.0, 0.0, TubeControl];
p.r0 ← Controls.NewControl["r0", vSlider, p, 0.0, 0.5, 0.0, TubeControl];
p.r1 ← Controls.NewControl["r1", vSlider, p, 0.0, 0.5, 0.0, TubeControl];
p.scale ← Controls.NewControl["scale", vSlider, p, 0.0, 4.0, 1.0, TubeControl];
p.epsilon ← Controls.NewControl["eps", vSlider, p, 0.0, 0.2, 0.03, TubeControl,,,,,,,,,,,, exp];
p.cres ← Controls.NewControl["cres", vSlider, p, 3.0, 25.0, 6.0, TubeControl, , TRUE];
p.lim ← Controls.NewControl["lim", vSlider, p, 0.0, 1000.0, 500.0, TubeControl, , TRUE];
p.controls ← LIST[
p.hold.x, p.hold.y, p.hold.z, p.hold.lng, p.hold.lat, p.hold.mag,
p.tw0, p.tw1, p.r0, p.r1, p.scale, p.tens0, p.tens1,
p.cam.xGlobal, p.cam.yGlobal, p.cam.zGlobal, p.cam.scale,
p.cam.hScreen, p.cam.vScreen, p.cam.fieldOfView,
p.lim, p.epsilon, p.cres];
p.buttons ← LIST[
tube:
[name: "Tube Ops:", proc: NIL, row: 3],
[name: "New", proc: NewTube, guarded: TRUE, row: 3, x: 75],
["Branch", Branch, 3],
["Delete", Delete, 3],
["Wt Rad", WeightRadii, 3],
["Linear", CycleRadiusMode, 3],
["Round-Off", TogRound, 3],
["Read", ReadFile, 3],
["Get", GetTube, 3],
["Get Contour", GetContour, 3],
["Write", WriteFile, 3],
miscellany:
[name: "Misc Ops:", proc: NIL, row: 2],
[name: "Rend-Off", proc: TogRender, row: 2, x: 75],
["Smooth", TogRenderStyle, 2],
["BFaces-Off", TogBackFaces, 2],
["Abort Rend", AbortRender, 2],
["Norm IO", TogXformIO, 2],
["PP-Out", WriteFile, 2],
["IP-Out", WriteFile, 2],
line drawing:
[name: "Draw Ops:", proc: NIL, row: 1],
[name: "Vectors-On", proc: TogVectors, row: 1, x: 75],
["Skel-Off", TogSkel, 1],
["Splines-On", TogSpline, 1],
["Ends-On", TogEnds, 1],
["Pick-On", TogPick, 1],
["Label-On", TogLabel, 1],
["Auto-Off", TogAuto, 1],
[name: "Contours-Off", proc: TogContours, row: 0, x: 75],
["Lines-Off", TogLines, 0],
["Frames-Off", TogFrames, 0],
["Normals-Off", TogNormals, 0],
["Curv-Off", TogCurv, 0],
["Vel-Off", TogVel, 0],
["Acc-Off", TogAcc, 0]];
};
InitViewer: PROC [p: ProgramData] ~ {
Controls.ControlRow[p.cam.xGlobal, 1];
p.outer ← Controls.OuterViewer[
name: "Tube Design",
buttons: p.buttons,
controls: p.controls,
controlSizes: [20, 200, 60, 20, 60, 150, 150],
typeScriptHeight: 18,
graphicsHeight: 300,
graphicsProc: ScreenPick,
graphicsShow: GraphicsShow,
data: p,
destroyProc: DestroyProc,
noOpen: TRUE];
p.outerData ← NARROW[p.outer.data];
p.buttons ← p.outerData.buttons;
p.graphics ← p.outerData.graphics;
p.graphicsData ← NARROW[p.graphics.data];
p.outer.icon ← Icons.NewIconFromFile["///Commands/Tube.icon", 0];
ViewerOps.OpenIcon[p.outer];
};
GraphicsShow: Controls.GraphicsShow ~ {
p: ProgramData ~ NARROW[data];
Action: PROC ~ {
p.view ← Controls3d.InitContext[context, p.cam, p.view];
IF p.details.spline AND p.details.ends
THEN TubeDisplay.DrawSplineEnds[p.tube, context, p.view];
IF p.details.enabled
THEN TubeDisplay.DrawTube[p.tube, context, p.details, p.view]
ELSE IF p.details.spline THEN TubeDisplay.DrawSplines[p.tube, context, p.view];
IF p.details.pick AND p.pick # NIL AND p.pick.tube # NIL THEN
Draw3d.Vector[context, p.pick.pos, p.pick.tan, p.view, , ,
IF p.pick.t IN (0.0..1.0) THEN cross ELSE none];
};
IF whatChanged # NIL AND p.noDraw THEN RETURN;
IF TubeDisplay.NVectors[p.tube, p.details] < p.lim.value
THEN Draw2d.DoWithBuffer[context, Action]
ELSE Action[];
};
User Interaction
Moused: PROC [p: ProgramData, mouse: Mouse, whatChanged: REF ANY, msg: ROPENIL] ~ {
p.mousePrev ← p.mouse;
p.mouse ← mouse;
p.action ← SELECT whatChanged FROM
p.buttons => button,
p.graphicsData => select,
p.cam => camera,
p.hold.lat, p.hold.lng, p.hold.mag, p.hold.x, p.hold.y, p.hold.z, p.epsilon => spline,
p.scale, p.r0, p.r1, p.tw0, p.tw1, p.cres => shape,
ENDCASE => none;
p.details.enabled ← NOT p.details.autoSimplify OR p.action = shape OR p.mouse.state = up;
p.noDraw ← p.mouse.state = up AND p.action # button AND NOT p.details.autoSimplify;
IF NOT p.noDraw THEN SELECT whatChanged FROM
p.scale => p.pendings.scale ← TRUE;
p.r0, p.r1 => p.pendings.r ← TRUE;
p.tw0 => p.pendings.tw0 ← TRUE;
p.tw1 => p.pendings.tw1 ← TRUE;
p.tens0, p.tens1 => p.pendings.tens ← TRUE;
p.cres => p.pendings.cres ← TRUE;
p.epsilon => p.pendings.epsilon ← TRUE;
p.hold.x, p.hold.y, p.hold.z => p.pendings.holdPosition ← TRUE;
p.hold.lng, p.hold.lat, p.hold.mag => p.pendings.holdTangent ← TRUE;
ENDCASE;
IF p.action = spline AND p.pick.dividePending THEN DivideSpline[p];
IF msg = NIL
THEN Controls.TypeScriptClear[p.outerData]
ELSE Controls.TypeScriptWrite[p.outerData, msg];
};
DivideSpline: PROC [p: ProgramData] ~ {
TubePick.DivideSpline[p.pick];
Controls.SetSliderDialValue[p.hold.mag, Vector3d.Length[p.pick.tube.v1]];
};
CheckPendingAndRePaint: PROC [p: ProgramData, forceReDraw: BOOLFALSE] ~ {
p.pendings.shape ← p.pendings.shape OR p.pendings.holdPosition OR p.pendings.holdTangent;
p.pendings.skin ←
p.pendings.skin OR p.pendings.shape OR p.pendings.r OR p.pendings.tens OR
p.pendings.tw0 OR p.pendings.tw1 OR p.pendings.epsilon;
IF p.pendings.holdPosition THEN
TubePick.ChangePosition[p.pick, [p.hold.x.value, p.hold.y.value, p.hold.z.value]];
IF p.pendings.holdTangent THEN TubePick.ChangeTangent[p.pick,
Vector3d.CartesianFromPolar[[p.hold.lng.value, p.hold.lat.value, p.hold.mag.value]]];
IF p.pendings.scale THEN TubeGeometry.ReScale[p.tube, p.scale.value, 0.0];
IF p.pendings.cres THEN TubeContour.PropagateCircleRes[p.tube, Real.RoundI[p.cres.value]];
IF p.pendings.tens THEN TubePick.ChangeTension[p.pick, p.tens0.value, p.tens1.value];
IF p.details.skin OR p.details.shape THEN {
IF p.pendings.r THEN
TubePick.ChangeRadii[p.pick, p.r0.value, p.r1.value, p.scale.value, p.epsilon.value];
IF p.pendings.tw0 THEN {
TubePick.ChangeTw0[p.pick, p.tw0.value, p.tw0.valuePrev];
IF p.pick.selected1 # NIL THEN Controls.SetSliderDialValue[p.tw1, p.pick.selected1.tw1];
};
IF p.pendings.tw1 THEN TubePick.ChangeTw1[p.pick, p.tw1.value, p.tw1.valuePrev];
IF p.pendings.shape OR p.pendings.skin THEN MakeDetails[p];
};
IF forceReDraw THEN p.noDraw ← FALSE;
IF p.vectors THEN ViewerOps.PaintViewer[p.graphics, client, FALSE, p];
IF p.render THEN {
IF p.renderProcess = NIL OR CedarProcess.GetStatus[p.renderProcess] # busy THEN {
IF p.pendings.skin OR p.pendings.cres OR p.pendings.scale OR p.pendings.r OR p.pendings.tens THEN TubeRender.AddTube[p.tube, p.context3d, p.renderStyle];
p.renderProcess ← CedarProcess.Fork[ForkRender, p];
};
};
TubeMisc.ClearPendings[p.pendings];
};
CameraControl: Controls3d.CameraProc ~ {
p: ProgramData ~ NARROW[data];
Moused[p, control.mouse, control];
IF p.noDraw THEN RETURN;
IF control.mouse.button = right THEN CheckPendingAndRePaint[p];
};
TubeControl: Controls.ControlProc ~ {
p: ProgramData ~ NARROW[control.data];
Moused[p, control.mouse, control];
IF p.noDraw THEN RETURN;
IF control.mouse.button = right THEN CheckPendingAndRePaint[p];
};
MakePick: PROC [p: ProgramData] ~ {
p.pick ← NEW[PickRep ← [tube: p.tube]];
p.pendings.holdPosition ← p.pendings.holdTangent ← FALSE;
TubePick.SetPickPoint[p.pick];
IF p.hold.x.viewer # NIL THEN Controls3d.FocusHold[p.pick.tan, p.hold];
};
ScreenPick: Controls.GraphicsProc ~ {
Proc called as result of mousing down in graphics viewer.
p: ProgramData ~ NARROW[graphics.data];
Moused[p, graphics.mouse, graphics];
IF p.mouse.state = up THEN RETURN;
TubePick.ScreenPick[p.tube, p.mouse, p.view, p.pick];
IF p.mouse.button = left THEN Controls3d.FocusHold[p.pick.tan, p.hold];
FocusPick[p];
ViewerOps.PaintViewer[graphics.viewer, client, FALSE, graphics];
};
FocusPick: PROC [p: ProgramData] ~ {
adjust controls to selected splines settings:
IF p.pick.selected0 # NIL THEN {
Controls.SetSliderDialValue[p.r0, p.pick.selected0.r0];
Controls.SetSliderDialValue[p.tw0, p.pick.selected0.tw0];
Controls.SetSliderDialValue[p.tens0, p.pick.selected0.tens0];
};
IF p.pick.selected1 # NIL THEN {
Controls.SetSliderDialValue[p.r1, p.pick.selected1.r1];
Controls.SetSliderDialValue[p.tw1, p.pick.selected1.tw1];
Controls.SetSliderDialValue[p.tens1, p.pick.selected1.tens1];
};
};
Tube Modification
NewTube: ClickProc ~ {ReplaceTube[UserDataFromClientData[clientData].p, ResetTube[]]};
MakeDetails: PROC [p: ProgramData] ~ {
TubeGeometry.MakeFrames[p.tube, p.epsilon.value, p.scale.value, 0, p.details.skin, p.round];
, p.view];
};
ResetTube: PROC RETURNS [tube: Tube] ~ {
tube ← NEW[TubeRep ← [p0: [-0.5,0.,0.], p1: [0.5,0.,0.], v0: [1.,1.,0.], v1: [1.,1.,0.]]];
TubeGeometry.SetCoeffs[tube];
tube.name ← "Tube";
};
ReplaceTube: PROC [p: ProgramData, tube: Tube] ~ {
p.tube ← tube;
IF tube.name = NIL THEN tube.name ← "Tube";
p.pendings ← NEW[PendingsRep ← [shape: TRUE]];
MakePick[p];
FocusPick[p];
Moused[p, [0, 0, up, left], p.buttons];
CheckPendingAndRePaint[p, TRUE];
};
Branch: ClickProc ~ {
p: ProgramData ~ UserDataFromClientData[clientData].p;
IF p.pick.t = 0.0 AND p.pick.tube.prev = NIL THEN RETURN;
IF p.pick.dividePending THEN DivideSpline[p];
TubeMisc.AddBranch[p.pick.tube, TubeMisc.NewBranch[p.pick.tube]];
CheckPendingAndRePaint[p, TRUE];
};
Delete: ClickProc ~ {
p: ProgramData ~ UserDataFromClientData[clientData].p;
TubeMisc.Delete[p.pick.selected0];
p.pick.selected0 ← p.pick.selected1 ← NIL;
TubeMisc.UnSelectAll[p.tube];
IF NOT TubeMisc.Find[p.pick.tube, p.tube] THEN p.pick.tube ← NIL;
CheckPendingAndRePaint[p, TRUE];
};
WeightRadii: ClickProc ~ {
p: ProgramData ~ UserDataFromClientData[clientData].p;
TubeMisc.SetDescendantRadii[p.tube, p.radiusMode];
p.pendings.skin ← TRUE;
CheckPendingAndRePaint[p, TRUE];
};
GetContour: ClickProc ~ {
u: UserData ~ UserDataFromClientData[clientData];
c: Control ~ Controls.LastControlMoused[];
IF c # NIL AND c.type = contour AND NOT c.viewer.destroyed
THEN {
TubeContour.AddContour[u.p.tube, Contours.Orient[Contours.FromControl[c, u.p.pick.t]]];
Moused[u.p, [0, 0, up, left], u.p.buttons, "Contour received\n"];
u.p.pendings.skin ← TRUE;
CheckPendingAndRePaint[u.p, TRUE];
}
ELSE {
Controls.TypeScriptWrite[u.o, "First middle-click a contour viewer\n"];
RETURN;
};
};
GetTube: ClickProc ~ {
u: UserData ~ UserDataFromClientData[clientData];
tube: Tube ~ TubePick.LastTubePicked[];
IF tube # NIL
THEN ReplaceTube[u.p, tube]
ELSE Controls.TypeScriptWrite[u.o, "First click a tube viewer\n"];
};
Shaded Imaging
DestroyProc: Controls.DestroyProc ~ {
p: ProgramData ~ NARROW[NARROW[outerData, OuterData].data];
Render3d.NullifyThreeDContext[p.context3d];
};
ForkRender: CedarProcess.ForkableProc ~ {
p: ProgramData ~ NARROW[data];
Render3d.Render[p.context3d, p.tube.name, p.cam];
};
AbortRender: ClickProc ~ {
u: UserData ~ UserDataFromClientData[clientData];
u.p.context3d.stopMe ← TRUE;
u.p.render ← FALSE;
Controls.ButtonReLabel[u.o, "Render-On", "Render-Off"];
};
IO
WriteFile: ClickProc ~ {
Equal: PROC [r: ROPE] RETURNS [BOOL] ~ {RETURN[Rope.Equal[button, r, FALSE]]};
button: ROPE ~ NARROW[parent, Viewer].name;
u: UserData ~ UserDataFromClientData[clientData];
m: Matrix ~ IF u.p.xformIO THEN u.p.cam.matrix ELSE NIL;
SELECT TRUE FROM
Equal["Write"] => TubeIO.QueryAndWriteTube[u.p.tube, u.o, m];
Equal["IP-Out"] => TubeIO.QueryAndWriteIP[u.p.tube, u.o, u.p.details, u.p.view];
Equal["PP-Out"] => TubeIO.QueryAndWritePointsPoly[u.p.tube, u.o, m];
ENDCASE;
};
ReadFile: ClickProc ~ {
u: UserData ~ UserDataFromClientData[clientData];
tube: Tube ~ TubeIO.QueryAndReadTube[u.o];
IF tube # NIL THEN ReplaceTube[u.p, tube];
};
Changing State
TogDetail: PROC [r: REF ANY, button: Menus.MouseButton, type: DetailType, name: ROPE] ~ {
u: UserData ~ UserDataFromClientData[r];
m: Mouse ~ [0, 0, up, SELECT button FROM red => left, yellow => middle, ENDCASE => right];
wasShape: BOOL ~ u.p.details.shape;
wasSkin: BOOL ~ u.p.details.skin;
TubeMisc.ToggleDetail[u.p.details, type, Rope.Cat[name, "-On"], Rope.Cat[name, "-Off"], u.o];
Moused[u.p, m, u.p.buttons];
IF wasShape # u.p.details.shape THEN u.p.pendings.shape ← u.p.details.shape;
IF wasSkin # u.p.details.skin THEN u.p.pendings.skin ← u.p.details.skin;
IF type # autoSimplify AND button = blue THEN CheckPendingAndRePaint[u.p, TRUE];
};
TogLabel:   ClickProc ~ {TogDetail[clientData, mouseButton, label, "Label"]};
TogAuto:   ClickProc ~ {TogDetail[clientData, mouseButton, autoSimplify, "Auto"]};
TogSkel:    ClickProc ~ {TogDetail[clientData, mouseButton, skeleton, "Skel"]};
TogPick:    ClickProc ~ {TogDetail[clientData, mouseButton, pick, "Pick"]};
TogSpline:   ClickProc ~ {TogDetail[clientData, mouseButton, spline, "Splines"]};
TogEnds:   ClickProc ~ {TogDetail[clientData, mouseButton, ends, "Ends"]};
TogContours: ClickProc ~ {TogDetail[clientData, mouseButton, contours, "Contours"]};
TogLines:   ClickProc ~ {TogDetail[clientData, mouseButton, lines, "Lines"]};
TogFrames:   ClickProc ~ {TogDetail[clientData, mouseButton, frames, "Frames"]};
TogNormals:  ClickProc ~ {TogDetail[clientData, mouseButton, normals, "Normals"]};
TogCurv:   ClickProc ~ {TogDetail[clientData, mouseButton, curvature, "Curv"]};
TogVel:    ClickProc ~ {TogDetail[clientData, mouseButton, velocity, "Vel"]};
TogAcc:    ClickProc ~ {TogDetail[clientData, mouseButton, acceleration, "Acc"]};
TogXformIO: ClickProc ~ {
u: UserData ~ UserDataFromClientData[clientData];
Controls.ButtonToggle[u.o, u.p.xformIO ← NOT u.p.xformIO, "Xfrm IO", "Norm IO"];
};
TogRender: ClickProc ~ {
u: UserData ~ UserDataFromClientData[clientData];
Controls.ButtonToggle[u.o, u.p.render ← NOT u.p.render, "Rend-On", "Rend-Off"];
IF u.p.render THEN {
IF u.p.context3d = NIL THEN u.p.context3d ← Render3d.InitContext3d[];
TubeRender.AddTube[u.p.tube, u.p.context3d, u.p.renderStyle];
};
IF mouseButton = blue THEN CheckPendingAndRePaint[u.p];
};
TogVectors: ClickProc ~ {
u: UserData ~ UserDataFromClientData[clientData];
Controls.ButtonToggle[u.o, u.p.vectors ← NOT u.p.vectors, "Vectors-On", "Vectors-Off"];
IF mouseButton = blue THEN CheckPendingAndRePaint[u.p, TRUE];
};
TogRenderStyle: ClickProc ~ {
RopeFromType: PROC [type: RenderStyle] RETURNS [rope: ROPE] ~ {
rope ← SELECT type FROM smooth => "Smooth", shiny => "Shiny", ENDCASE => "Faceted";
};
u: UserData ~ UserDataFromClientData[clientData];
old: RenderStyle ~ u.p.renderStyle;
u.p.renderStyle ← IF old = RenderStyle.LAST THEN RenderStyle.FIRST ELSE SUCC[old];
IF mouseButton = blue THEN CheckPendingAndRePaint[u.p];
Controls.ButtonReLabel[u.o, RopeFromType[old], RopeFromType[u.p.renderStyle]];
Render3d.SetRenderStyle[u.p.context3d, u.p.tube.name, u.p.renderStyle];
};
TogBackFaces: ClickProc ~ {
u: UserData ~ UserDataFromClientData[clientData];
u.p.backFaces ← NOT u.p.backFaces;
Controls.ButtonToggle[u.o, u.p.backFaces, "BFaces-On", "BFaces-Off"];
Render3d.SetInsideVisible[u.p.context3d, u.p.tube.name, u.p.backFaces];
IF mouseButton = blue THEN CheckPendingAndRePaint[u.p];
};
TogRound: ClickProc ~ {
u: UserData ~ UserDataFromClientData[clientData];
u.p.round ← NOT u.p.round;
u.p.pendings.shape ← TRUE;
Controls.ButtonToggle[u.o, u.p.round, "Round-On", "Round-Off"];
IF mouseButton = blue THEN CheckPendingAndRePaint[u.p];
};
CycleRadiusMode: ClickProc ~ {
u: UserData ~ UserDataFromClientData[clientData];
Roper: PROC [mode: RadiusMode] RETURNS [ROPE] ~ {
RETURN[SELECT mode FROM linear => "Linear", ENDCASE => "Square"];
};
old: RadiusMode ~ u.p.radiusMode;
u.p.radiusMode ← IF old = RadiusMode.LAST THEN RadiusMode.FIRST ELSE SUCC[old];
IF u.o # NIL THEN Controls.ButtonReLabel[u.o, Roper[old], Roper[u.p.radiusMode]];
};
Support
UserDataFromClientData: PROC [clientData: REF ANY] RETURNS [u: UserData] ~ {
u.p ← NARROW[(u.o ← NARROW[clientData]).data];
};
Start Code
Commander.Register["///Commands/Tube", TubeDesign, "\nModel tubular structures."];
END.