G3dTubeCmdImpl.mesa
Copyright Ó 1985, 1992 by Xerox Corporation. All rights reserved.
Bloomenthal, October 20, 1992 4:33 pm PDT
DIRECTORY Args, Commander, Controls, Draw2d, FileNames, FS, G2dBasic, G2dContour, G3dBasic, G3dControl, G3dCurve, G3dDraw, G3dMatrix, G3dPlane, G3dSpline, G3dTool, G3dTube, G3dVector, G3dView, Icons, IO, ImagerInterpress, Menus, MessageWindow, Real, Rope, ViewerOps;
G3dTubeCmdImpl: CEDAR PROGRAM
IMPORTS Args, Controls, Draw2d, FileNames, FS, G2dContour, G3dControl, G3dCurve, G3dDraw, G3dMatrix, G3dPlane, G3dSpline, G3dTool, G3dTube, G3dVector, G3dView, Icons, IO, ImagerInterpress, MessageWindow, Real, Rope, ViewerOps
~ BEGIN
Types
ClickProc:   TYPE ~ Controls.ClickProc;
Control:    TYPE ~ Controls.Control;
ControlList:   TYPE ~ Controls.ControlList;
Mouse:    TYPE ~ Controls.Mouse;
OuterData:   TYPE ~ Controls.OuterData;
Zip:     TYPE ~ Draw2d.Zip;
Contour:    TYPE ~ G2dContour.Contour;
Pair:     TYPE ~ G3dBasic.Pair;
Triple:    TYPE ~ G3dBasic.Triple;
Quad:     TYPE ~ G3dBasic.Quad;
Hold:     TYPE ~ G3dControl.Hold;
Matrix:    TYPE ~ G3dMatrix.Matrix;
Plane:     TYPE ~ G3dPlane.Plane;
RenderTool:   TYPE ~ G3dTool.Tool;
SplineRep:   TYPE ~ G3dSpline.SplineRep;
STREAM:    TYPE ~ IO.STREAM;
ROPE:     TYPE ~ Rope.ROPE;
Details:    TYPE ~ G3dTube.Details;
DetailType:   TYPE ~ G3dTube.DetailType;
RadiusMode:   TYPE ~ G3dTube.RadiusMode;
Tube:     TYPE ~ G3dTube.Tube;
TubeProc:   TYPE ~ G3dTube.TubeProc;
TubeRep:    TYPE ~ G3dTube.TubeRep;
Pendings:    TYPE ~ REF PendingsRep;
PendingsRep:  TYPE ~ RECORD [
epsilon:      BOOL ¬ FALSE,
size:       BOOL ¬ FALSE,
r:        BOOL ¬ FALSE,
taper:       BOOL ¬ FALSE,
tens:       BOOL ¬ FALSE,
tw0:       BOOL ¬ FALSE,
tw1:       BOOL ¬ FALSE,
cres:       BOOL ¬ FALSE,
holdPosition:     BOOL ¬ FALSE,
holdTangent:     BOOL ¬ FALSE,
holdRoll:      BOOL ¬ FALSE,
shape:       BOOL ¬ FALSE,
skin:       BOOL ¬ FALSE
];
Pick:     TYPE ~ REF PickRep;
PickRep:    TYPE ~ RECORD [
type:       {point, segment, subTree} ¬ point,
pos, tan:      Triple ¬ G3dBasic.origin,
tube:       Tube ¬ NIL,
t:        REAL ¬ 1.0,
selected0, selected1:   Tube ¬ NIL,
dividePending:    BOOL ¬ FALSE,
plane:       Plane ¬ [0.0, 0.0, 0.0, 0.0],
mouse:      Mouse
];
ActionType:   TYPE ~ {none, select, camera, spline, shape, button};
Tool:     TYPE ~ REF ToolRep;
ToolRep:    TYPE ~ RECORD [
Controls:
controls:     ControlList ¬ NIL,   -- list of the following controls:
size:      Control ¬ NIL,    -- size of tube
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
taper:      Control ¬ NIL,    -- tube taper
hold:      Hold ¬ NIL,     -- handle on a point and its tangent
lim:      Control ¬ NIL,    -- vector limit
Viewing:
renderTool:    RenderTool ¬ NIL,
State:
details:     Details ¬ NIL,
pendings:     Pendings ¬ NIL,
noDraw:     BOOL ¬ FALSE,
vectors:     BOOL ¬ TRUE,
neverMoused:   BOOL ¬ TRUE,
mouse:     Mouse ¬ [[0, 0], none, left],
mousePrev:    Mouse ¬ [[0, 0], none, left],
pick:      Pick ¬ NIL,
action:     ActionType ¬ none,
radiusMode:   RadiusMode ¬ linear,
round:     BOOL ¬ FALSE,
xformIO:     BOOL ¬ FALSE,    -- view-xform points when writing file
Geometry:
ground:     Plane ¬ [],     -- something new
frill:      Tube ¬ NIL,     -- the frilled tube
tube:      Tube ¬ NIL     -- all the tube splines
];
Initialization
TubeDesignCmd: Commander.CommandProc ~ {
NewControl: PROC [name: ROPE, min, max, init: REAL, lin: BOOL ¬ TRUE, precision: NAT ¬ 3]
RETURNS [c: Control] ~ {
c ¬ Controls.NewControl[
name, vSlider, t, min, max, init, TubeControl,, precision,,,,,,,,, IF lin THEN lin ELSE exp];
};
err: ROPE;
t: Tool ~ NEW[ToolRep];
SELECT Args.NArgs[cmd] FROM
0 => t.tube ¬ ResetTube[];
1 => IF (err ¬ TubeFromFile[t, Args.GetRope[cmd]]) # NIL THEN RETURN[$Failure, err];
ENDCASE => RETURN[$Failure, designUsage];
t.details ¬ NEW[G3dTube.DetailsRep];
t.pendings ¬ NEW[PendingsRep];
t.hold ¬ G3dControl.InitHold[TubeControl, t];
t.tw0  ¬ NewControl["tw0", -1000., 1000., 0.0];
t.tw1  ¬ NewControl["tw1", -1000., 1000., 0.0];
t.tens0 ¬ NewControl["tens0", -3.0, 3.0, 0.0];
t.tens1 ¬ NewControl["tens1", -3.0, 3.0, 0.0];
t.r0  ¬ NewControl["r0",  0.0, 0.5, 0.0];
t.r1  ¬ NewControl["r1",  0.0, 0.5, 0.0];
t.size  ¬ NewControl["size", 0.0, 4.0, 1.0];
t.epsilon ¬ NewControl["eps", 0.0, 0.2, 0.03, FALSE];
t.taper  ¬ NewControl["taper", -3.0, 3.0, 0.0, FALSE];
t.cres  ¬ NewControl["cres", 2.0, 25.0, 6.0, TRUE, 0];
t.lim  ¬ NewControl["lim", 0.0, 1000.0, 500.0, TRUE, 0];
t.pendings ¬ NEW[PendingsRep ¬ [shape: TRUE]];
MakePick[t];
SetPick[t];
Moused[t, [[0, 0], up, left], $Buttons];
t.renderTool ¬ G3dTool.MakeTool[
name: Rope.Concat["3dTube ", IF t.tube # NIL THEN t.tube.name ELSE NIL],
extraButtons: LIST[
Controls.ClickButton["Tube Ops", TubeOps, t],
Controls.ClickButton["IO Ops", IOOps, t],
Controls.ClickButton["Draw Ops", DrawOps, t]],
extraControls: LIST[
t.hold.x, t.hold.y, t.hold.z, t.hold.pitch, t.hold.yaw, -- t.hold.roll, -- t.hold.mag,
t.tw0, t.tw1, t.r0, t.r1, t.tens0, t.tens1,
t.lim, t.epsilon, t.taper, t.size, t.cres],
controlSizes: [20, 200, 55, 20, 55, 150, 150],
client: [data: t, mouse: MouseProc, draw: DrawProc, destroy: DestroyProc],
ops: [FALSE, FALSE, FALSE, FALSE, FALSE, FALSE],
useArcBalls: FALSE,
arcBallSize: 80,
icon: icon];
};
ClearPendings: PROC [pendings: Pendings] ~ {
pendings.epsilon ¬ pendings.size ¬ pendings.r ¬ pendings.tw0 ¬ pendings.tw1 ¬
pendings.cres ¬ pendings.holdPosition ¬ pendings.holdTangent ¬ pendings.shape ¬
pendings.skin ¬ FALSE;
};
Tube Ops
TubeOps: ClickProc ~ {
t: Tool ¬ NARROW[clientData];
SELECT Controls.PopUpRequest[["Tube Ops"], LIST[
-- 1 -- ["New Tube", "reset the current tube to the default tube"],
-- 2 -- ["Select All", "select the entire tube"],
-- 3 -- ["Branch", "add a branch to the current tube at the selected point"],
-- 4 -- ["Delete", "delete the current selected branch"],
-- 5 -- Controls.BoolRequest[t.round, "Round ends"],
-- 6 -- ["Wt Rad", "select different radius weighting scheme"],
-- 7 -- ["Radius Mode", "cycle through radius mode"],
-- 8 -- Controls.BoolRequest[t.xformIO, "Xform points"]]]
FROM
1 => ReplaceTube[t, ResetTube[]];
2 => G3dTube.SelectAll[t.tube];
3 => Branch[t];
4 => Delete[t];
5 => {
t.round ¬ NOT t.round;
t.pendings.shape ¬ TRUE;
};
6 => WeightRadii[t];
7 => {
t.radiusMode ¬ SELECT Controls.MultiRequest["Radius mode is ", LIST[
["Linear", t.radiusMode = linear],
["Square", t.radiusMode = square]]]
FROM 1 => linear, 2 => square, ENDCASE => linear;
};
8 => t.xformIO ¬ NOT t.xformIO;
ENDCASE;
IF Controls.GetPopUpButton[] = right THEN CheckPendingAndRePaint[t];
};
Drawing
DrawProc: G3dTool.DrawProc ~ {
t: Tool ~ NARROW[clientData];
IF NOT t.noDraw THEN {
IF whatChanged # $Camera THEN Draw2d.Clear[context];
IF t.details[spline] AND t.details[ends] THEN
G3dTube.DrawSplineEnds[context, t.tube, view];
IF t.details[enabled]
THEN G3dTube.DrawTube[context, t.tube, t.details, view, vp]
ELSE IF t.details[spline] THEN G3dTube.DrawSplines[context, t.tube, view, vp];
IF t.details[pick] AND t.pick # NIL AND t.pick.tube # NIL THEN {
IF NOT t.neverMoused THEN {
pairClip: G3dView.PairClip ~ G3dView.TransformAndClipInZ[t.pick.pos, view];
Imager.SetStrokeWidth[context, 1.0];
G3dDraw.Vector[context, t.pick.pos, t.pick.tan, view, vp];
IF NOT pairClip.clipped THEN {
Draw2d.Circle[context, pairClip.pair, 3.0, TRUE];
IF NOT t.pick.t IN (0.0..1.0) THEN Draw2d.Mark[context, pairClip.pair, asterisk];
};
};
};
};
};
DrawOps: ClickProc ~ {
t: Tool ¬ NARROW[clientData];
SELECT Controls.PopUpRequest[["LF Ops"], LIST[
-- 1 -- Controls.BoolRequest[t.vectors, "Vectors"],
-- 2 -- Controls.BoolRequest[t.details[skeleton], "Skeleton"],
-- 3 -- Controls.BoolRequest[t.details[spline], "Splines"],
-- 4 -- Controls.BoolRequest[t.details[ends], "Ends"],
-- 5 -- Controls.BoolRequest[t.details[pick], "Pick"],
-- 6 -- Controls.BoolRequest[t.details[label], "Label"],
-- 7 -- Controls.BoolRequest[t.details[autoSimplify], "Auto"],
-- 8 -- Controls.BoolRequest[t.details[contours], "Contours"],
-- 9 -- Controls.BoolRequest[t.details[lines], "Lines"],
-- 10 -- Controls.BoolRequest[t.details[xSections], "XSections"],
-- 11 -- Controls.BoolRequest[t.details[points], "Points"],
-- 12 -- Controls.BoolRequest[t.details[normals], "Normals"],
-- 13 -- Controls.BoolRequest[t.details[curvature], "Curvature"],
-- 14 -- Controls.BoolRequest[t.details[velocity], "Velocity"],
-- 15 -- Controls.BoolRequest[t.details[acceleration], "Acceleration"]]]
FROM
1 => t.vectors ¬ NOT t.vectors;
2 => TogDetail[t, skeleton];
3 => TogDetail[t, spline];
4 => TogDetail[t, ends];
5 => TogDetail[t, pick];
6 => TogDetail[t, label];
7 => TogDetail[t, autoSimplify];
8 => TogDetail[t, contours];
9 => TogDetail[t, lines];
10 => TogDetail[t, xSections];
11 => TogDetail[t, points];
12 => TogDetail[t, normals];
13 => TogDetail[t, curvature];
14 => TogDetail[t, velocity];
15 => TogDetail[t, acceleration];
ENDCASE;
IF Controls.GetPopUpButton[] = right THEN CheckPendingAndRePaint[t, TRUE];
};
TogDetail: PROC [t: Tool, detailType: DetailType] ~ {
button: Controls.MouseButton ¬ Controls.GetPopUpButton[];
m: Mouse ~ [[0, 0], up, button];
wasShape: BOOL ~ t.details[shape];
wasSkin: BOOL ~ t.details[skin];
t.details[detailType] ¬ NOT t.details[detailType];
t.details[shape] ¬
t.details[xSections] OR t.details[curvature] OR t.details[velocity] OR t.details[acceleration];
t.details[skin] ¬ t.details[contours] OR t.details[lines] OR t.details[normals];
Moused[t, m, $Buttons];
IF wasShape # t.details[shape] THEN t.pendings.shape ¬ t.details[shape];
IF wasSkin # t.details[skin] THEN t.pendings.skin ¬ t.details[skin];
IF detailType # autoSimplify AND button = right THEN CheckPendingAndRePaint[t, TRUE];
};
User Interaction
Moused: PROC [
t: Tool,
mouse: Mouse,
whatChanged: REF ANY,
msg: ROPE ¬ NIL]
~ {
c: Control ¬ SELECT whatChanged FROM
t.hold.pitch, t.hold.yaw, t.hold.roll, t.hold.mag, t.hold.x, t.hold.y, t.hold.z,
t.epsilon, t.taper, t.size, t.r0, t.r1, t.tw0, t.tw1, t.cres => NARROW[whatChanged],
ENDCASE => NIL;
t.mousePrev ¬ t.mouse;
t.mouse ¬ mouse;
t.action ¬ SELECT whatChanged FROM
$Buttons => button,
$Select => select,
t.hold.pitch, t.hold.yaw, t.hold.roll, t.hold.mag, t.hold.x, t.hold.y, t.hold.z => spline,
t.epsilon, t.taper => spline,
$Pull, t.size, t.r0, t.r1, t.tw0, t.tw1, t.cres => shape,
ENDCASE => none;
t.details[enabled] ¬ NOT t.details[autoSimplify] OR t.action = shape OR t.mouse.state = up;
t.noDraw ← t.mouse.state = up AND (c = NIL OR c.whatChanged # $TypedIn) AND
t.action # button AND t.action # select AND NOT t.details[autoSimplify];
IF NOT t.noDraw THEN SELECT whatChanged FROM
t.size => t.pendings.size ¬ TRUE;
t.r0, t.r1 => t.pendings.r ¬ TRUE;
t.tw0 => t.pendings.tw0 ¬ TRUE;
t.tw1 => t.pendings.tw1 ¬ TRUE;
t.tens0, t.tens1 => t.pendings.tens ¬ TRUE;
t.cres => t.pendings.cres ¬ TRUE;
t.epsilon => t.pendings.epsilon ¬ TRUE;
t.taper => t.pendings.taper ¬ TRUE;
$Pull, t.hold.x, t.hold.y, t.hold.z => t.pendings.holdPosition ¬ TRUE;
t.hold.pitch, t.hold.yaw, t.hold.mag => t.pendings.holdTangent ¬ TRUE;
t.hold.roll => t.pendings.holdRoll ¬ TRUE;
ENDCASE;
IF t.action = spline AND t.pick.dividePending THEN DivideSpline[t];
IF t.renderTool # NIL THEN {
IF msg = NIL
THEN Controls.TypescriptClear[t.renderTool.typescript]
ELSE Controls.TypescriptWrite[t.renderTool.typescript, msg];
};
};
DivideSpline: PROC [t: Tool] ~ {
DividePick[t.pick];
Controls.SetSliderDialValue[t.hold.mag, G3dVector.Length[t.pick.tube.v1]];
};
CheckPendingAndRePaint: PROC [t: Tool, forceReDraw: BOOL ¬ FALSE] ~ {
IF t.pick.tube = NIL THEN
t.pendings.holdPosition ¬ t.pendings.holdTangent ¬ t.pendings.holdRoll ¬ FALSE;
t.pendings.shape ¬ t.pendings.shape OR t.pendings.holdPosition OR
t.pendings.holdTangent OR t.pendings.holdRoll;
t.pendings.skin ¬
t.pendings.skin OR t.pendings.shape OR t.pendings.r OR t.pendings.tens OR
t.pendings.tw0 OR t.pendings.tw1 OR t.pendings.taper OR
t.pendings.epsilon OR t.pendings.cres;
IF t.pendings.holdPosition THEN
ChangePosition[t.pick, [t.hold.x.value, t.hold.y.value, t.hold.z.value]];
IF t.pendings.holdTangent THEN ChangeTangent[t.pick, t.hold];
IF t.pendings.holdRoll THEN
RollSubTree[t.pick, Controls.GetSliderDialDeltaValue[t.hold.roll]];
IF t.pendings.size OR t.pendings.taper
THEN G3dTube.ReScale[t.tube, t.size.value, t.taper.value];
IF t.pendings.epsilon THEN G3dTube.PropagateEpsilon[t.tube, t.epsilon.value];
IF t.pendings.cres
THEN G3dTube.PropagateCircleRes[t.tube, Real.Round[t.cres.value]];
IF t.pendings.tens THEN ChangeTension[t.pick, t.tens0.value, t.tens1.value];
IF t.pendings.shape OR t.pendings.skin THEN G3dTube.InvalidateTubeAttributes[t.tube];
IF t.details[skin] OR t.details[shape] THEN {
IF t.pendings.r THEN
ChangeRadii[t.pick, t.r0.value, t.r1.value, t.size.value, t.epsilon.value];
IF t.pendings.tw0 THEN {
ChangeTw0[t.pick, t.tw0.value, t.tw0.valuePrev];
IF t.pick.selected1 # NIL THEN Controls.SetSliderDialValue[t.tw1, t.pick.selected1.tw1];
};
IF t.pendings.tw1 THEN ChangeTw1[t.pick, t.tw1.value, t.tw1.valuePrev];
IF t.pendings.shape OR t.pendings.skin THEN MakeDetails[t];
};
IF forceReDraw THEN t.noDraw ¬ FALSE;
IF t.vectors THEN G3dTool.Repaint[t.renderTool, t];
ClearPendings[t.pendings];
};
TubeControl: Controls.ControlProc ~ {
t: Tool ~ NARROW[control.clientData];
Moused[t, control.mouse, control];
IF NOT t.noDraw AND (control.mouse.button = right OR control.whatChanged = $TypedIn)
THEN CheckPendingAndRePaint[t];
};
MouseProc: Controls.MouseProc ~ {
t: Tool ~ NARROW[clientData];
whatChanged: REF ANY ¬ $Select;
view: Matrix ¬ GetView[viewer, t];
Moused[t, mouse, whatChanged];
t.neverMoused ¬ FALSE;
IF mouse.state = up THEN RETURN;
IF -- mouse.state = down OR -- NOT mouse.controlKey THEN {
ScreenPick[t, mouse, view, t.pick];
G3dControl.SetHold[t.hold, t.pick.pos, t.pick.tan];
SetPick[t];
};
IF mouse.controlKey
THEN {
p: Pair ¬ [mouse.pos.x, mouse.pos.y];
plane: Quad ¬ [t.pick.plane.x, t.pick.plane.y, t.pick.plane.z, t.pick.plane.w];
q: Triple ¬ G3dMatrix.TripleFromScreenAndPlane[p, plane, view];
Controls.SetSliderDialValue[t.hold.x, q.x];
Controls.SetSliderDialValue[t.hold.y, q.y];
Controls.SetSliderDialValue[t.hold.z, q.z];
whatChanged ¬ t.hold.x -- $Pull -- ;
}
ELSE IF t.mouse.button = left THEN {
point: Triple ¬ G3dSpline.Position[t.pick.tube.spline, t.pick.t];
message: ROPE ¬ IO.PutFLR["%g%g (%6.3f, %6.3f, %6.3f)", LIST[
IO.rope[IF t.tube.name # NIL THEN t.tube.name ELSE "Tube"],
IO.rope[G3dTube.Where[t.pick.tube, t.tube]],
IO.real[point.x], IO.real[point.y], IO.real[point.z]]];
MessageWindow.Append[message, TRUE];
};
G3dTool.Repaint[t.renderTool, t];
CheckPendingAndRePaint[t];
};
MouseProc: Controls.MouseProc ~ {
t: Tool ~ NARROW[clientData];
t.neverMoused ← FALSE;
Moused[t, mouse, $Select];
IF t.mouse.state = up THEN RETURN;
ScreenPick[t.tube, t.mouse, t.renderTool.view, t.pick];
G3dControl.SetHold[t.hold, t.pick.pos, t.pick.tan];
IF t.mouse.button = left THEN {
IF t.mouse.state = down THEN {
point: Triple ← G3dSpline.Position[t.pick.tube.spline, t.pick.t];
message: ROPEIO.PutFR["%g%g (%6.3f, %6.3f, %6.3f)",
IO.rope[IF t.tube.name # NIL THEN t.tube.name ELSE "Tube"],
IO.rope[G3dTube.Where[t.pick.tube, t.tube]],
IO.real[point.x], IO.real[point.y], IO.real[point.z]];
MessageWindow.Append[message, TRUE];
};
};
SetPick[t];
G3dTool.Repaint[t.renderTool, clientData];
};
SetPick: PROC [t: Tool] ~ {
adjust controls to selected splines settings:
IF t.pick.selected0 # NIL THEN {
Controls.SetSliderDialValue[t.r0, t.pick.selected0.r0];
Controls.SetSliderDialValue[t.tw0, t.pick.selected0.tw0];
Controls.SetSliderDialValue[t.tens0, t.pick.selected0.tens0];
};
IF t.pick.selected1 # NIL THEN {
Controls.SetSliderDialValue[t.r1, t.pick.selected1.r1];
Controls.SetSliderDialValue[t.tw1, t.pick.selected1.tw1];
Controls.SetSliderDialValue[t.tens1, t.pick.selected1.tens1];
};
};
Tube Modification
MakeDetails: PROC [t: Tool] ~ {
G3dTube.Make[t.tube, t.details[skin], t.round, NIL, t.epsilon.value];
};
ResetTube: PROC RETURNS [tube: Tube] ~ {
tube ¬ NEW[TubeRep ¬ [p0: [-0.5,0.,0.], p1: [0.5,0.,0.], v0: [1.,0.,1.], v1: [1.,0.,1.]]];
G3dTube.SetSpline[tube];
tube.name ¬ "Tube";
};
RenameViewer: PROC [t: Tool] ~ {
IF Rope.Equal[t.tube.name, "Tube", FALSE] THEN RETURN;
t.renderTool.outer.name ¬ Rope.Concat["3dTube ", t.tube.name];
t.renderTool.outer.label ¬ t.tube.name;
ViewerOps.PaintViewer[t.renderTool.outer, caption];
};
ReplaceTube: PROC [t: Tool, tube: Tube] ~ {
t.tube ¬ tube;
IF tube.name = NIL THEN tube.name ¬ "Tube";
RenameViewer[t];
t.pendings ¬ NEW[PendingsRep ¬ [shape: TRUE]];
MakePick[t];
SetPick[t];
Moused[t, [[0, 0], up, left], $Buttons];
CheckPendingAndRePaint[t, TRUE];
};
Branch: PROC [t: Tool] ~ {
IF t.pick.t = 0.0 AND t.pick.tube.prev = NIL THEN RETURN;
IF t.pick.dividePending THEN DivideSpline[t];
G3dTube.AddBranch[t.pick.tube, G3dTube.NewBranch[t.pick.tube]];
CheckPendingAndRePaint[t, TRUE];
};
Delete: PROC [t: Tool] ~ {
G3dTube.Delete[t.pick.selected0];
t.pick.selected0 ¬ t.pick.selected1 ¬ NIL;
G3dTube.UnSelectAll[t.tube];
IF NOT G3dTube.Find[t.pick.tube, t.tube] THEN t.pick.tube ¬ NIL;
CheckPendingAndRePaint[t, TRUE];
};
WeightRadii: PROC [t: Tool] ~ {
G3dTube.SetDescendantRadii[t.tube, t.radiusMode];
t.pendings.skin ¬ TRUE;
CheckPendingAndRePaint[t, TRUE];
};
FromControl: PROC [control: Control, t: REAL ¬ 0.0] RETURNS [contour: Contour] ~ {
ips: G2dBasic.IntegerPairSequence ¬ Controls.GetContour[control];
IF ips # NIL THEN {
offset: Pair ¬ [0.5*(control.w-1), 0.5*(control.h-1)];
scale: Pair ¬ [IF offset.x>0 THEN 1./offset.x ELSE 1., IF offset.y>0 THEN 1./offset.y ELSE 1.];
contour ¬ G2dContour.FromIntegerPairs[ips, Controls.IsContourClosed[control], t];
RETURN[G2dContour.Thin[G2dContour.Scale[G2dContour.Offset[contour, offset], scale]]];
};
};
GetContour: PROC [t: Tool] ~ {
c: Control ~ Controls.LastControlMoused[];
IF c # NIL AND c.type = contour AND NOT c.viewer.destroyed
THEN {
G3dTube.AddContour[t.tube, G2dContour.Orient[FromControl[c, t.pick.t]]];
Moused[t, [[0, 0], up, left], $Buttons, "Contour received\n"];
t.pendings.skin ¬ TRUE;
CheckPendingAndRePaint[t, TRUE];
}
ELSE
Controls.TypescriptWrite[t.renderTool.typescript, "1st middle-click a contour viewer\n"];
};
GetTube: PROC [t: Tool] ~ {
tube: Tube ~ G3dTube.LastTubePicked[FALSE];
IF tube # NIL
THEN ReplaceTube[t, tube]
ELSE Controls.TypescriptWrite[t.renderTool.typescript, "1st click tube viewer\n"];
};
DestroyProc: Controls.DestroyProc ~ {
t: Tool ~ NARROW[clientData];
MessageWindow.Clear[];
IF G3dTube.LastTubePicked[FALSE] = t.tube THEN G3dTube.SetLastTubePicked[NIL];
};
IO
IOOps: ClickProc ~ {
t: Tool ¬ NARROW[clientData];
SELECT Controls.PopUpRequest[["Misc Ops"], LIST[
-- 1 -- ["Read Tube", "read a tube from a file"],
-- 2 -- ["Get Tube", "get a tube from another viewer"],
-- 3 -- ["Get Contour", "get a contour from a viewer"],
-- 4 -- ["Write Tube", "write a tube to a file"],
-- 5 -- ["Write Curve", "write a curve to a file"],
-- 6 -- ["Write Interpress", "write an Interpress file of the current image"],
-- 7 -- ["Shape-Out", "write a shape file consisting of points and polygons"]]]
FROM
1 => ReadTube[t];
2 => GetTube[t];
3 => GetContour[t];
4 => WriteTube[t];
5 => WriteCurve[t];
6 => WriteInterpress[t];
7 => WritePointsPolys[t];
ENDCASE;
};
GetView: PROC [v: Controls.Viewer ¬ NIL, t: Tool] RETURNS [m: Matrix] ~ {
m ¬ G3dTool.GetViewTransform[v, t.renderTool];
};
WriteTube: PROC [t: Tool] ~ {
fileName: ROPE ¬ Controls.TypescriptReadFileName[t.renderTool.typescript];
IF fileName # NIL THEN {
m: Matrix ~ IF t.xformIO THEN GetView[, t] ELSE NIL;
t.tube.name ¬ G3dTube.ExtractTubeName[fileName];
G3dTube.TubeToFile[t.tube, fileName, m];
RenameViewer[t];
};
};
WritePointsPolys: PROC [t: Tool] ~ {
nPoints, nPolys: INT;
m: Matrix ~ IF t.xformIO THEN GetView[, t] ELSE NIL;
fileName: ROPE ¬ Controls.TypescriptReadFileName[t.renderTool.typescript];
IF fileName = NIL THEN RETURN;
[nPoints, nPolys] ¬ G3dTube.WritePointsPolys[t.tube, fileName, m];
Controls.TypescriptWrite[t.renderTool.typescript,
IO.PutFR["%g points and %g polys written\n", IO.int[nPoints], IO.int[nPolys]]];
};
WriteInterpress: PROC [t: Tool] ~ {
fileName: ROPE ¬ Controls.TypescriptReadFileName[t.renderTool.typescript];
IF fileName # NIL THEN {
ref: ImagerInterpress.Ref ¬ ImagerInterpress.Create[fileName];
G3dTube.WriteIP[ref, t.tube, t.details, G3dTool.GetViewTransform[, t.renderTool]];
ImagerInterpress.Close[ref];
};
};
WriteCurve: PROC [t: Tool] ~ {
fileName: ROPE ¬ Controls.TypescriptReadFileName[t.renderTool.typescript];
IF fileName # NIL THEN {
out: STREAM ¬ FS.StreamOpen[fileName, $create];
c: G3dCurve.Curve ¬ G3dTube.CurveFromTube[t.tube];
name: ROPE ¬ IF t.tube.name # NIL THEN Rope.Concat["from ", t.tube.name] ELSE NIL;
IF t.xformIO THEN G3dCurve.Transform[c, GetView[, t]];
G3dCurve.Write[out, c, name];
};
};
ReadTube: PROC [tool: Tool] ~ {
name: ROPE ¬ Controls.TypescriptReadFileName[tool.renderTool.typescript];
tool.tube ¬ G3dTube.TubeFromFile[name];
IF tool.tube # NIL THEN ReplaceTube[tool, tool.tube] ELSE Blink["Couldn't read tube"];
};
TubeFromFile: PROC [t: Tool, name: ROPE] RETURNS [error: ROPE] ~ {
r: ROPE ¬ FileNames.ResolveRelativePath[name];
stream: STREAM ¬ FS.StreamOpen[FS.ExpandName[r].fullFName ! FS.Error => GOTO noOpen];
t.tube ¬ G3dTube.ReadTube[stream];
IO.Close[stream];
IF t.tube = NIL THEN RETURN["No tube in file."];
t.tube.name ¬ G3dTube.ExtractTubeName[r];
EXITS
noOpen => error ¬ IO.PutFR1["Couldn't open %g.\n", IO.rope[name]];
};
WriteTubeInfo: PROC [tube: Tube, o: OuterData] ~ {
nPoints, nPolys, minCres, maxCres: INTEGER;
[nPoints, nPolys, minCres, maxCres] ¬ G3dTube.Info[tube];
Controls.TypescriptWrite[o.typescript, IO.PutFLR["%g points, %g polygons, cres: %g-%g\n",
LIST[IO.int[nPoints], IO.int[nPolys], IO.int[minCres], IO.int[maxCres]]]];
};
Pick Procedures
MakePick: PROC [t: Tool] ~ {
t.pick ¬ NEW[PickRep ¬ [tube: t.tube]];
t.pendings.holdPosition ¬ t.pendings.holdTangent ¬ t.pendings.holdRoll ¬ FALSE;
SetPoint[t];
IF t.hold.x.viewer # NIL THEN G3dControl.SetHold[t.hold, t.pick.pos, t.pick.tan];
};
ScreenPick: PROC [tool: Tool, mouse: Mouse, view: Matrix, pick: Pick] ~ {
IF pick = NIL THEN RETURN;
pick.mouse ¬ mouse;
[[pick.tube, pick.t]] ¬
G3dTube.NearestTube2d[tool.tube, [mouse.pos.x, mouse.pos.y], mouse. state= down, view];
SELECT TRUE FROM
mouse.button = right => {
pick.type ¬ subTree;
SetSubTree[tool];
};
mouse.shiftKey => {
pick.type ¬ segment;
SetSelection[tool];
};
ENDCASE => {
pick.type ¬ point;
SetPoint[tool];
};
};
UpdatePick: PROC [t: Tool] ~ {
pick: Pick ¬ t.pick;
IF pick.tube = NIL THEN RETURN;
G3dTube.SetLastTubePicked[pick.tube];
pick.dividePending ¬ pick.t IN (0.0..1.0);
pick.tan ¬ G3dSpline.Tangent[pick.tube.spline, pick.t];
pick.pos ¬ --pick.origPos ¬-- G3dSpline.Position[pick.tube.spline, pick.t];
IF t.renderTool # NIL THEN {
Compute the plane perpendicular to the view direction and passing through pick.pos:
Find the 3d vector that presently points along z-axis after the view transform.
Thus, transform the z-axis by the inverse of the view transformation.
Note, the view transformation being considered includes the viewport.
To transform a vector, premultiply by the matrix's inverse (i.e., inverse of the inverse).
nrm: Quad ¬ G3dMatrix.TransformH[[0,0,1], GetView[, t]];
pick.plane ¬ G3dPlane.FromPointAndNormal[t.pick.pos, [nrm.x, nrm.y, nrm.z], TRUE];
};
};
SetPoint: PROC [tool: Tool] ~ {
IF tool.pick.tube = NIL THEN RETURN;
IF tool.pick.t = 0.0 AND tool.pick.tube.prev # NIL THEN {
tool.pick.t ¬ 1.0;
tool.pick.tube ¬ tool.pick.tube.prev;
};
IF tool.pick.selected0 # NIL THEN tool.pick.selected0.selected ¬ FALSE;
IF tool.pick.selected1 # NIL THEN tool.pick.selected1.selected ¬ FALSE;
tool.pick.selected0 ¬ tool.pick.selected1 ¬ NIL;
UpdatePick[tool];
};
SetSelection: PROC [tool: Tool] ~ {
pick: Pick ¬ tool.pick;
pick.selected1 ¬ pick.tube;
IF pick.mouse.button = middle OR pick.selected0 = NIL
THEN pick.selected0 ¬ pick.selected1;
pick.t ¬ IF pick.t > 0.5 THEN 1.0 ELSE 0.0;
FOR t: Tube ¬ pick.selected0, t.next WHILE t # NIL DO-- ensure selected0 before selected1
IF t = pick.selected1 THEN EXIT;
REPEAT
FINISHED => {
temp: Tube ¬ pick.selected0;
pick.selected0 ¬ pick.selected1;
pick.selected1 ¬ temp;
};
ENDLOOP;
G3dTube.UnSelectAll[G3dTube.First[pick.tube]];   -- set selected splines
FOR t: Tube ¬ pick.selected0, t.next WHILE t # NIL AND t # pick.selected1.next DO
t.selected ¬ TRUE;
ENDLOOP;
UpdatePick[tool];
};
SetSubTree: PROC [tool: Tool] ~ {
tool.pick.selected0 ¬ tool.pick.selected1 ¬ tool.pick.tube;
tool.pick.t ¬ IF tool.pick.t > 0.5 THEN 1.0 ELSE 0.0;
G3dTube.UnSelectAll[G3dTube.First[tool.pick.tube]];
G3dTube.SelectAll[tool.pick.tube];
UpdatePick[tool];
};
DividePick: PROC [pick: Pick] ~ {
IF pick.tube # NIL AND pick.dividePending AND pick.t IN (0.0..1.0) THEN {
tubeProc: TubeProc ~ {tube.prev ¬ newTube};
newTube: Tube ¬ NEW[TubeRep ¬ [
spline: NEW[SplineRep],
xSpline: NEW[SplineRep],
prev: pick.tube,
selected: pick.tube.selected,
next: pick.tube.next,
branches: pick.tube.branches,
p1: pick.tube.p1,
r1: pick.tube.r1,
tw1: pick.tube.tw1]];
G3dTube.DivideContours[pick.tube, pick.tube, newTube, pick.t];
pick.tube.next ¬ newTube;
pick.tube.branches ¬ NIL;
G3dTube.ApplyToBranches[newTube, tubeProc, FALSE];
pick.tube.p1 ¬ newTube.p0 ¬ pick.pos --pick.origPos--;
pick.tube.r1 ¬ newTube.r0 ¬ 0.5*(pick.tube.r0+newTube.r1);
pick.tube.tw1 ¬ newTube.tw0 ¬ 0.5*(pick.tube.tw0+newTube.tw1);
[] ¬ G3dSpline.Reparameterize[pick.tube.spline, pick.t, 1.0, newTube.spline];
[] ¬ G3dSpline.Reparameterize[pick.tube.spline, 0.0, pick.t, pick.tube.spline];
pick.tube.v0 ¬ G3dSpline.Tangent[pick.tube.spline, 0.0];
pick.tube.v1 ¬ G3dSpline.Tangent[pick.tube.spline, 1.0];
newTube.v0 ¬ G3dSpline.Tangent[newTube.spline, 0.0];
newTube.v1 ¬ G3dSpline.Tangent[newTube.spline, 1.0];
pick.tube.v1 ← newTube.v0 ← [
0.25*(3.0*(newTube.p1.x-pick.tube.p0.x)-pick.tube.v0.x-newTube.v1.x),
0.25*(3.0*(newTube.p1.y-pick.tube.p0.y)-pick.tube.v0.y-newTube.v1.y),
0.25*(3.0*(newTube.p1.z-pick.tube.p0.z)-pick.tube.v0.z-newTube.v1.z)];
pick.t ¬ 1.0;
pick.tan ¬ pick.tube.v1;
};
pick.dividePending ¬ FALSE;
};
NewSpline: PROC [pick: Pick] ~ {
tubeProc: TubeProc ~ {G3dTube.SetSpline[tube]};
[] ¬ tubeProc[pick.tube];
G3dTube.ApplyToBranches[pick.tube, tubeProc];
};
RollSubTree: PROC [pick: Pick, rollAngle: REAL] ~ {
IF G3dTube.NBranches[pick.tube] = 0 THEN RETURN;
IF pick.t = 0.0
THEN G3dTube.Rotate[pick.tube, [pick.tube.p0, pick.tube.v0], rollAngle, TRUE, TRUE]
ELSE {
v: Triple ¬ IF pick.tube.next#NIL THEN pick.tube.next.v0 ELSE pick.tube.branches[0].v0;
G3dTube.Rotate[pick.tube, [pick.tube.p1, v], rollAngle, TRUE, FALSE];
};
NewSpline[pick];
};
ChangeTangent: PROC [pick: Pick, hold: Hold] ~ {
IF pick.tube # NIL THEN {
tangent: Triple ¬ G3dControl.VectorFromHold[hold];
SELECT pick.type FROM
point, segment => {
IF pick.t = 0.0
THEN pick.tube.v0 ¬ tangent
ELSE {
If T = new vector, V0 = beginning vector of outgoing tube, and V1 = end
of incoming tube, then T|V0|/|T| is a vector in direction T with length of V0.
We would like V0 length to increase, however, by |T|/|V1|.
So, V0 should be assigned T|V0||T|/|T||V1| = T|V0|/|V1|.
tubeProc: TubeProc ~ {
IF tube # NIL THEN tube.v0 ¬
G3dVector.Mul[tangent, G3dVector.Length[tube.v0]*iV1];
};
iV1: REAL ~ IF G3dVector.Null[pick.tube.v1] THEN 0.0 ELSE 1.0/G3dVector.Length[pick.tube.v1];
pick.tube.v1 ¬ tangent;
IF iV1 # 0.0 THEN G3dTube.ApplyToBranches[pick.tube, tubeProc];
};
};
subTree => {
axis: Triple ¬ G3dVector.Cross[tangent, pick.tan];
base: Triple ¬ IF pick.t = 0.0 THEN pick.tube.p0 ELSE pick.tube.p1;
angle: REAL ¬ G3dVector.AngleBetween[tangent, pick.tan, , TRUE];
G3dTube.Rotate[pick.tube, [base, axis], angle, TRUE, pick.t = 0.0];
};
ENDCASE;
pick.tan ¬ tangent;
NewSpline[pick];
};
};
ChangePosition: PROC [pick: Pick, --change-- position: Triple] ~ {
pick.pos ¬ position;
pick.pos ← G3dVector.Add[pick.origPos, change];
IF pick.tube = NIL THEN RETURN;
IF pick.t = 0.0
THEN pick.tube.p0 ¬ pick.pos
ELSE {
pick.tube.p1 ¬ pick.pos;
FOR n: NAT IN [0..G3dTube.NBranches[pick.tube]) DO
pick.tube.branches[n].p0 ¬ pick.pos;
ENDLOOP;
IF pick.tube.next # NIL THEN pick.tube.next.p0 ¬ pick.pos;
};
NewSpline[pick];
};
ChangeRadii: PROC [pick: Pick, r0, r1, scale, epsilon: REAL] ~ {
numberSelected: INTEGER ~ NumberSelected[pick];
IF numberSelected > 0 THEN {
r: REAL ¬ r0;
dr: REAL ~ (r1-r)/REAL[numberSelected];
IF pick.selected0.prev # NIL THEN pick.selected0.prev.r1 ¬ r;
FOR t: Tube ¬ pick.selected0, t.next WHILE t # NIL AND t # pick.selected1.next DO
t.r0 ¬ r;
r ¬ r+dr;
t.r1 ¬ r;
ENDLOOP;
IF pick.selected1.next # NIL THEN pick.selected1.next.r0 ¬ r;
RemakeSelected[pick, scale, epsilon];
};
};
ChangeTension: PROC [pick: Pick, tens0, tens1: REAL] ~ {
numberSelected: INTEGER ~ NumberSelected[pick];
IF numberSelected > 0 THEN {
tens: REAL ¬ tens0;
dtens: REAL ~ (tens1-tens0)/REAL[numberSelected];
FOR t: Tube ¬ pick.selected0, t.next WHILE t # NIL AND t # pick.selected1.next DO
t.tens0 ¬ tens;
tens ¬ tens+dtens;
t.tens1 ¬ tens;
G3dTube.SetSpline[t];
ENDLOOP;
};
};
ChangeTw0: PROC [pick: Pick, tw0, tw0Prev: REAL] ~ {
nBefore: INTEGER ~ BeforeSelected[pick];
change: REAL ~ tw0-tw0Prev;
IF nBefore > 0 THEN {
dchange: REAL ¬ 0.0;
ddchange: REAL ¬ change/REAL[nBefore];
FOR t: Tube ¬ G3dTube.First[t], t.next WHILE t # pick.selected0 DO
t.tw0 ¬ t.tw0+dchange;
dchange ¬ dchange+ddchange;
t.tw1 ¬ t.tw1+dchange;
ENDLOOP;
};
FOR t: Tube ¬ pick.selected0, t.next WHILE t # NIL DO
t.tw0 ¬ t.tw0+change;
t.tw1 ¬ t.tw1+change;
ENDLOOP;
};
ChangeTw1: PROC [pick: Pick, tw1, tw1Prev: REAL] ~ {
nSelected: INTEGER ~ NumberSelected[pick];
change: REAL ~ tw1-tw1Prev;
IF nSelected > 0 THEN {
dchange: REAL ¬ 0.0;
ddchange: REAL ¬ change/REAL[nSelected];
FOR t: Tube ¬ pick.selected0, t.next WHILE t # pick.selected1.next DO
t.tw0 ¬ t.tw0+dchange;
dchange ¬ dchange+ddchange;
t.tw1 ¬ t.tw1+dchange;
ENDLOOP;
};
IF pick.selected1 # NIL THEN
FOR t: Tube ¬ pick.selected1.next, t.next WHILE t # NIL DO
t.tw0 ¬ t.tw0+change;
t.tw1 ¬ t.tw1+change;
ENDLOOP;
};
NumberSelected: PROC [pick: Pick] RETURNS [INTEGER] ~ {
n: INTEGER ¬ 0;
IF pick.selected0 = NIL OR pick.selected1 = NIL THEN RETURN[0];
FOR t: Tube ¬ pick.selected0, t.next WHILE t # NIL AND t # pick.selected1.next DO
n ¬ n+1;
ENDLOOP;
RETURN[n];
};
BeforeSelected: PROC [pick: Pick] RETURNS [INTEGER] ~ {
n: INTEGER ¬ 0;
FOR t: Tube ¬ pick.selected0.prev, t.prev WHILE t # NIL DO n ¬ n+1; ENDLOOP;
RETURN[n];
};
AfterSelected: PROC [pick: Pick] RETURNS [INTEGER] ~ {
n: INTEGER ¬ 0;
FOR t: Tube ¬ pick.selected1.next, t.next WHILE t # NIL DO n ¬ n+1; ENDLOOP;
RETURN[n];
};
RemakeSelected: PROC [pick: Pick, scale, epsilon: REAL] ~ {
t0: Tube ¬ IF pick.selected0.prev # NIL THEN pick.selected0.prev ELSE pick.selected0;
t1: Tube ¬ IF pick.selected1.next # NIL THEN pick.selected1.next ELSE pick.selected1;
FOR t: Tube ¬ t0, t.next WHILE t # t1.next AND t # NIL DO
t.epsilon ¬ epsilon;
t.scale ¬ scale;
G3dTube.MakeSectionXSections[t];
ENDLOOP;
};
Miscellaneous
Blink: PROC [rope: ROPE] ~ {
MessageWindow.Append[rope, TRUE];
MessageWindow.Blink[];
};
Start Code
icon: Icons.IconFlavor ¬ Icons.NewIconFromFile["G3dUser.icons", 5];
designUsage: ROPE ~ "tube <tube-file>: Model tubular structures.";
G3dTool.Register["Tube", TubeDesignCmd, designUsage];
END.