File: SVMouseEventImplA.mesa
Last edited by: Eric Bier on May 29, 1987 6:10:04 pm PDT
Copyright © 1984 by Xerox Corporation. All rights reserved.
Contents: Interactive solidviews operations like dragging and stretching objects.
DIRECTORY
AIS, Atom, AtomButtonsTypes, CodeTimer, CoordSys, SVCaret, SVRay, SVGraphics, DisplayListToTree, Feedback, Imager, ImagerColorPrivate, ImagerPath, InputFocus, Matrix3d, Real, RealFns, Rope, Shading, SV2d, SV3d, SVAlign, SVArtworkUser, SVAssembly, SVBasicTypes, SVBoundBox, SVCastRays, SVDescribe, SVEvent, SVGravity, SVInterfaceTypes, SVLines2d, SVMatrix2d, SVModelTypes, SVMouseEvent, SVRayTypes, SVRefresh, SVScene, SVSceneTypes, SVSelect, SVSelections, SVState, SVVector3d, SVViewersOnScene, SVWindow;
SVMouseEventImplA:
CEDAR
PROGRAM
IMPORTS AIS, Atom, CodeTimer, CoordSys, SVRay, SVGraphics, DisplayListToTree, Feedback, ImagerColorPrivate, InputFocus, Matrix3d, Real, RealFns, Rope, Shading, SVAlign, SVArtworkUser, SVAssembly, SVBoundBox, SVCaret, SVCastRays, SVDescribe, SVEvent, SVGravity, SVLines2d, SVMatrix2d, SVMouseEvent, SVRefresh, SVScene, SVSelect, SVSelections, SVState, SVVector3d, SVViewersOnScene, SVWindow
EXPORTS SVMouseEvent =
BEGIN
Artwork: TYPE = SVModelTypes.Artwork;
ArtworkToolData: TYPE = SVInterfaceTypes.ArtworkToolData;
Slice: TYPE = SVSceneTypes.Slice;
SliceList: TYPE = SVSceneTypes.SliceList;
BoundBox: TYPE = SVBasicTypes.BoundBox;
Camera: TYPE = SVModelTypes.Camera;
Classification: TYPE = SVRayTypes.Classification;
Color: TYPE = Imager.Color;
CoordSystem: TYPE = SVModelTypes.CoordSystem;
EditToolData: TYPE = SVInterfaceTypes.EditToolData;
FeedbackData: TYPE = AtomButtonsTypes.FeedbackData;
FeatureData: TYPE = SVInterfaceTypes.FeatureData;
GravityType: TYPE = SVInterfaceTypes.GravityType;
Matrix4by4: TYPE = SV3d.Matrix4by4;
Point2d: TYPE = SV2d.Point2d;
Point3d: TYPE = SV3d.Point3d;
Primitive: TYPE = SVRayTypes.Primitive;
Ray: TYPE = SVRayTypes.Ray;
Scene: TYPE = SVSceneTypes.Scene;
SearchDepth: TYPE = SVRayTypes.SearchDepth;
Selection: TYPE = SVInterfaceTypes.Selection;
Shape: TYPE = SVSceneTypes.Shape;
SliceDescriptor: TYPE = SVSceneTypes.SliceDescriptor;
SliceDescriptorGenerator: TYPE = SVSceneTypes.SliceDescriptorGenerator;
ToolData: TYPE = SVSceneTypes.ToolData;
TrigLine: TYPE = SV2d.TrigLine;
Vector3d: TYPE = SV3d.Vector3d;
SVData: TYPE = SVInterfaceTypes.SVData;
MouseProc: TYPE = SVMouseEvent.MouseProc;
StartProc: TYPE = SVMouseEvent.StartProc;
The Solidviews FSM
Problem: PUBLIC SIGNAL [msg: Rope.ROPE] = Feedback.Problem;
InitializeFSM:
PUBLIC
PROC [svData: SVData] = {
svData.state ← $None;
svData.mouseMode ← $None;
};
EasyAbort: MouseProc = {
Many actions don't change the scene at all until the End proc is called. For these cases, we just restore the selections and caret position.
editToolData: EditToolData ← svData.editToolData;
scene: Scene ← svData.scene;
SVScene.RestoreSelections[scene];
SVCaret.Copy[from: svData.drag.savedCaret, to: editToolData.skitter]; -- restore original caret
FinishAbort[svData];
};
FinishAbort:
PROC [svData: SVData] = {
SVRefresh.MoveOverlayToBackground[svData];
Feedback.AppendHerald[svData.feedback, ". . . Aborted.", end];
SVWindow.RestoreScreenAndInvariants[paintAction: $PaintEntireScene, svData: svData, remake: triggerBag, backgndOK: FALSE, edited: FALSE, okToClearFeedback: FALSE];
};
ResetMouseMachinery:
PUBLIC
PROC [svData: SVData] = {
svData.mouseMode ← $None;
svData.state ← $None;
SVRefresh.MoveOverlayToBackground[svData];
};
HandleMouse:
PUBLIC
PROC [event:
LIST
OF
REF
ANY, svData: SVData] = {
atom: ATOM;
vec: Imager.VEC ← NARROW[event.rest.first, REF Imager.VEC]^;
point: Point2d ← [vec.x, vec.y];
SELECT svData.mouseMode
FROM
$Drag => HandleGuarded[StartDrag, DuringDrag, EndMotion, EasyAbort, NIL, event, svData, point];
$Rotate => HandleGuarded[StartRotate, DuringRotate, EndMotion, EasyAbort, NIL, event, svData, point];
$Scale => HandleGuarded[StartScale, DuringScale, EndMotion, EasyAbort, NIL, event, svData, point];
$CopyAndDrag => HandleGuarded[StartCopyAndDrag, DuringDrag, EndMotion, EasyAbort, NIL, event, svData, point];
$SelectJoint => HandleUnGuarded[SVMouseEvent.StartSelectJoint, SVMouseEvent.DuringSelect, SVMouseEvent.EndSelect, EasyAbort, event, svData, point];
$SelectSegment => HandleUnGuarded[SVMouseEvent.StartSelectSegment, SVMouseEvent.DuringSelect, SVMouseEvent.EndSelect, EasyAbort, event, svData, point];
$SelectTraj => HandleUnGuarded[SVMouseEvent.StartSelectTraj, SVMouseEvent.DuringSelect, SVMouseEvent.EndSelect, EasyAbort, event, svData, point];
$SelectTopLevel => HandleUnGuarded[SVMouseEvent.StartSelectTopLevel, SVMouseEvent.DuringSelect, SVMouseEvent.EndSelect, EasyAbort, event, svData, point];
$ExtendSelection => HandleUnGuarded[SVMouseEvent.StartExtendSelection, SVMouseEvent.DuringExtendSelection, SVMouseEvent.EndExtendSelection, EasyAbort, event, svData, point];
$ExtendSelectJoint => HandleUnGuarded[SVMouseEvent.StartExtendSelectJoint, SVMouseEvent.DuringExtendSelection, SVMouseEvent.EndExtendSelection, EasyAbort, event, svData, point];
$ExtendSelectSegment => HandleUnGuarded[SVMouseEvent.StartExtendSelectSegment, SVMouseEvent.DuringExtendSelection, SVMouseEvent.EndExtendSelection, EasyAbort, event, svData, point];
$ExtendSelectTraj => HandleUnGuarded[SVMouseEvent.StartExtendSelectTraj, SVMouseEvent.DuringExtendSelection, SVMouseEvent.EndExtendSelection, EasyAbort, event, svData, point];
$ExtendSelectTopLevel => HandleUnGuarded[SVMouseEvent.StartExtendSelectTopLevel, SVMouseEvent.DuringExtendSelection, SVMouseEvent.EndExtendSelection, EasyAbort, event, svData, point];
$Frame => HandleUnGuarded[SVMouseEvent.FrameUpLeft, SVMouseEvent.FrameDownRightMove, SVMouseEvent.FrameDownRightEnd, EasyAbort, event, svData, point];
$Skitter => HandleGuarded[StartSkitter, DuringSkitter, EndSkitter, EasyAbort, NIL, event, svData, point];
$BackSkitter => HandleGuarded[StartBackSkitter, DuringBackSkitter, EndBackSkitter, EasyAbort, NIL, event, svData, point];
$TopLevelSkitter => HandleGuarded[StartTopLevelSkitter, DuringTopLevelSkitter, EndTopLevelSkitter, EasyAbort, NIL, event, svData, point];
$CoordSkitter => HandleGuarded[StartCoordSkitter, DuringCoordSkitter, EndCoordSkitter, EasyAbort, NIL, event, svData, point];
$TightRope => HandleGuarded[StartTightRope, DuringTightRope, EndTightRope, EasyAbort, NIL, event, svData, point];
$WallWalk => HandleGuarded[StartWallWalk, DuringWallWalk, EndWallWalk, EasyAbort, NIL, event, svData, point];
$SingleRay => SVEvent.SingleRay[svData, controlPoint];
$DeleteFrame => SVMouseEvent.DeleteFrame[svData];
$Paint => SVMouseEvent.Paint[svData, controlPoint];
$OpenAISFile => SVArtworkUser.OpenFile[artworkToolData];
$CloseAISFile => SVArtworkUser.CloseFile[artworkToolData];
$ExtendSkitter => SVMouseEvent.ExtendSkitter[svData, controlPoint, first];
$ExtendCoordSkitter => SVMouseEvent.ExtendCoordSkitter[svData, controlPoint];
$None => {
atomName: Rope.ROPE;
atom ← NARROW[event.first];
atomName ← Atom.GetPName[atom];
IF Rope.Equal[Rope.Substr[atomName, 0, 5], "Start",
TRUE]
THEN {
svData.mouseMode ← Atom.MakeAtom[Rope.Substr[atomName, 5]];
HandleMouse[event, svData];
}
ELSE {}; -- ignore other actions
};
ENDCASE => SIGNAL Problem[msg: "Unimplemented Mode"];
HandleMouseless:
PUBLIC
PROC [event:
LIST
OF
REF
ANY, svData: SVData] = {
An easy way to handle the Abort action.
event ← LIST[event.first, NEW[Imager.VEC ← [0.0, 0.0]]];
HandleMouse[event, svData];
};
HandleGuarded:
PROC [startProc: StartProc, duringProc, endProc, abortProc: MouseProc, continueProc: StartProc, event:
LIST
OF
REF
ANY, svData: SVData, worldPt: Point2d] = {
genericAction, atom: ATOM;
atomName: Rope.ROPE;
atom ← NARROW[event.first];
atomName ← Atom.GetPName[atom];
genericAction ← IF Rope.Equal[Rope.Substr[atomName, 0, 5], "Start", TRUE] THEN $Start ELSE atom;
HandleGuardedAux[startProc, duringProc, endProc, abortProc, continueProc, genericAction, event, svData, worldPt];
};
HandleGuardedAux:
PROC [startProc: StartProc, duringProc, endProc, abortProc: MouseProc, continueProc: StartProc, genericAction:
ATOM, event:
LIST
OF
REF
ANY, svData: SVData, worldPt: Point2d] = {
SELECT svData.state
FROM
$None => {
SELECT genericAction
FROM
$Start => {
svData.drag.currentPoint ← worldPt;
[] ← InputFocus.SetInputFocus[svData.actionArea];
IF startProc[event, svData, worldPt] THEN svData.state ← $Main
ELSE {abortProc[event, svData, worldPt]; svData.state ← $Aborted;}; };
ENDCASE;
};
$Main => {
SELECT genericAction
FROM
$During => {
duringProc[event, svData, worldPt];
svData.drag.currentPoint ← worldPt;
};
$Abort => {abortProc[event, svData, worldPt]; svData.state ← $Aborted};
$GuardUp => svData.state ← $GuardUp;
$MouseUp => svData.state ← $MouseUp;
$Start => {
-- the mouse must have gone out of the window while the mouse button was done. Abort the current action and start a new one.
abortProc[event, svData, worldPt]; svData.state ← $None;
svData.mouseMode ← $None;
HandleMouse[event, svData];
};
ENDCASE;
};
$GuardUp => {
SELECT genericAction
FROM
$AllUp => {
endProc[event, svData, svData.drag.currentPoint];
svData.mouseMode ← $None;
svData.state ← $None;
};
$Abort => {abortProc[event, svData, worldPt]; svData.state ← $Aborted};
ENDCASE;
};
$MouseUp => {
SELECT genericAction
FROM
$AllUp => {
endProc[event, svData, svData.drag.currentPoint];
svData.mouseMode ← $None;
svData.state ← $None;
};
$Abort => {abortProc[event, svData, worldPt]; svData.state ← $Aborted};
$Start => {
-- we may be starting another action of this mode or some other mode.
IF Restart[event, svData]
AND continueProc #
NIL
THEN {
IF
continueProc[event, svData, worldPt]
THEN {
svData.state ← $Main;
svData.drag.currentPoint ← worldPt;
}
ELSE {abortProc[event, svData, worldPt]; svData.state ← $Aborted;};
}
ELSE {
svData.mouseMode ← $None;
endProc[event, svData, svData.drag.currentPoint];
svData.state ← $None;
HandleMouse[event, svData];
};
};
ENDCASE;
};
$Aborted => {
SELECT genericAction FROM
$AllUp => {svData.state ← $None; svData.mouseMode ← $None};
ENDCASE;
};
ENDCASE => SIGNAL Problem[msg: "Unknown generic state"];
};
HandleUnGuarded:
PROC [startProc: StartProc, duringProc, endProc, abortProc: MouseProc, event:
LIST
OF
REF
ANY, svData: SVData, worldPt: Point2d] = {
genericAction, atom: ATOM;
atomName: Rope.ROPE;
atom ← NARROW[event.first];
atomName ← Atom.GetPName[atom];
genericAction ← IF Rope.Equal[Rope.Substr[atomName, 0, 5], "Start", TRUE] THEN $Start ELSE atom;
HandleUnGuardedAux[startProc, duringProc, endProc, abortProc, genericAction, event, svData, worldPt];
};
HandleUnGuardedAux:
PROC [startProc: StartProc, duringProc, endProc, abortProc: MouseProc, genericAction:
ATOM, event:
LIST
OF
REF
ANY, svData: SVData, worldPt: Point2d] = {
SELECT svData.state
FROM
$None => {
SELECT genericAction
FROM
$Start => {
[] ← InputFocus.SetInputFocus[svData.actionArea];
IF startProc[event, svData, worldPt] THEN svData.state ← $Main
ELSE {abortProc[event, svData, worldPt]; svData.state ← $Aborted}
};
ENDCASE;
};
$Main => {
SELECT genericAction
FROM
$During => duringProc[event, svData, worldPt];
$MouseUp, $AllUp => {
endProc[event, svData, worldPt];
svData.state ← $None;
svData.mouseMode ← $None;
};
$Abort => {abortProc[event, svData, worldPt]; svData.state ← $Aborted};
$Start => {
-- the mouse must have gone out of the window while the mouse button was done. Abort the current action and start a new one.
abortProc[event, svData, worldPt]; svData.state ← $None;
svData.mouseMode ← $None;
HandleMouse[event, svData];
};
ENDCASE;
};
$Aborted => {
SELECT genericAction
FROM
$AllUp => {svData.state ← $None; svData.mouseMode ← $None};
$MouseUp => {svData.state ← $None; svData.mouseMode ← $None};
ENDCASE;
};
ENDCASE => SIGNAL Problem[msg: "Unknown generic state"];
};
Restart:
PROC [event:
LIST
OF
REF
ANY, svData: SVData]
RETURNS [
BOOL] = {
mouseMode: ATOM ← svData.mouseMode;
state: ATOM ← NARROW[event.first];
SaveSavedState:
PROC [svData: SVData] = {
editToolData: EditToolData ← svData.editToolData;
SVScene.SaveSelections[svData.scene];
SVWindow.SaveCaretPos[svData];
SVCaret.Copy[from: editToolData.skitter, to: svData.drag.savedCaret];
};
Caret Procs
StartSkitter:
PUBLIC StartProc = {
CodeTimer.StartInt[$StartSkitter, $Solidviews];
SVEvent.Selected[NIL, svData];
InputFocus.SetInputFocus[svData.actionArea];
SVSelections.SetModeSkitter[svData, surface];
DuringSkitter[NIL, svData, cameraPoint];
CodeTimer.StopInt[$StartSkitter, $Solidviews];
}; -- end of StartSkitter
DragTheSkitter:
PROC [svData: SVData, cameraPoint: Point2d, action: Rope.
ROPE ← ""]
RETURNS [skitter
WORLD: Matrix4by4] = {
surfacePtWorld: Point3d;
normalWorld: Vector3d;
assembly: Slice;
feature: FeatureData;
camera: Camera ← svData.camera;
scene: Scene ← svData.scene;
gravityType: GravityType ← SVState.GetGravityType[svData];
searchDepth: SearchDepth ← first;
hitData: REF ANY;
[surfacePtWorld, normalWorld, feature, hitData] ← SVGravity.RayMap[cameraPoint, svData.hitTest.criticalR, NIL, svData.hitTest.sceneBag, svData];
assembly ← IF feature = NIL THEN NIL ELSE NARROW[feature.shape, SliceDescriptor].slice;
skitterWORLD ← SVSelections.PositionSkitter[cameraPoint, surfacePtWorld, normalWorld, assembly, svData];
Feedback.PutFHerald[svData.feedback, oneLiner, "%g Skitter on %g at [%g, %g, %g]",
[rope[action]], [rope[SVDescribe.DescribeFeature[feature, hitData, svData]]],
[real[surfacePtWorld[1]]], [real[surfacePtWorld[2]]], [real[surfacePtWorld[3]]] ];
};
DuringSkitter: MouseProc = {
Cast a ray into the scene from this point. If it hits anything, use the surface normal at that point to derive a coordinate system whose z axis is the parallel to the normal.
editToolData: EditToolData ← svData.editToolData;
CodeTimer.StartInt[$DuringSkitter, $Solidviews];
[] ← DragTheSkitter[svData, cameraPoint, "SkitterMotion"];
SVViewersOnScene.PaintSceneAllViewers[paintAction: $DuringSkitterPos, editToolData: editToolData, scene: svData.scene, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE];
CodeTimer.StopInt[$DuringSkitter, $Solidviews];
}; -- end DuringSkitter
EndSkitter: MouseProc = {
CodeTimer.StartInt[$EndSkitter, $Solidviews];
[] ← DragTheSkitter[svData, cameraPoint, "SkitterMotion"];
SVWindow.NewCaretPos[svData];
SVWindow.RestoreScreenAndInvariants[paintAction: $SkitterMoved, svData: svData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE];
CodeTimer.StopInt[$EndSkitter, $Solidviews];
};
Copy and Drag
UpdateSceneForCopy:
PROC [scene: Scene, feedback: FeedbackData]
RETURNS [newSlices:
LIST
OF Slice, success:
BOOL ←
TRUE] = {
sliceDGen: SliceDescriptorGenerator;
newSlice: Slice;
Copy selected slices.
sliceDGen ← SVSelect.SelectedSlices[scene, normal];
FOR sliceD: SliceDescriptor ← SVSelect.NextSliceDescriptor[sliceDGen], SVSelect.NextSliceDescriptor[sliceDGen]
UNTIL sliceD =
NIL
DO
newSlice ← SVAssembly.Copy[sliceD.slice];
newSlice ← SVScene.CopyAssemblyAndSonsUniqueNames[sliceD.slice, scene, scene, scene.assembly, feedback];
SVSelect.DeselectSlice[sliceD.slice, sliceD.parts, scene, normal];
newSlices ← CONS[newSlice, newSlices];
SVSelect.SelectEntireSlice[newSlice, scene, normal];
ENDLOOP;
StartCopyAndDrag:
PUBLIC StartProc = {
Make copies of all selected objects. The selected objects can be part of trajectories, trajectories, outlines, or whole slices. Sort the resulting slices by the priority ordering of the objects they came from. Add the new objects on top. This can be done in two ways: Sort the selections by priority order to begin with, or sort the new objects after they are created.
newSlices: LIST OF Slice;
scene: Scene ← svData.scene;
SaveSavedState[svData]; -- must do this before any possible aborts occur
[newSlices, success] ← UpdateSceneForCopy[scene, svData.feedback]; -- adds new shapes and selects them
IF NOT success THEN RETURN;
SVAlign.UpdateBagsForNewSlices[newSlices, svData];
success ← StartMotion[svData: svData, opName: "copy", cameraPoint: cameraPoint, saveState: FALSE, needAnchor: FALSE, backgroundOK: FALSE];
IF NOT success THEN RETURN[FALSE];
DuringDrag[NIL, svData, cameraPoint];
};
Motion Procs
TransformObjectsAfterMove:
PROC [scene: Scene, transform: Matrix4by4] = {
sGen: SliceDescriptorGenerator;
sGen ← SVSelect.SelectedSlices[scene, normal];
FOR sliceD: SliceDescriptor ← SVSelect.NextSliceDescriptor[sGen], SVSelect.NextSliceDescriptor[sGen]
UNTIL sliceD =
NIL
DO
SVAssembly.Transform[sliceD, scene, transform];
ENDLOOP;
};
StartMotion:
PROC [svData: SVData, opName: Rope.
ROPE, cameraPoint: Point2d, saveState:
BOOL ←
TRUE, needAnchor:
BOOL ←
FALSE, backgroundOK:
BOOL ←
FALSE]
RETURNS [success:
BOOL ← TRUE] = {
movingBox: BoundBox;
repaintNeeded: BOOL;
IF saveState THEN SaveSavedState[svData]; -- do this before aborts can occur
BEGIN
IF NOT SVRefresh.EmptyOverlay[svData] THEN ERROR; -- nothing on overlay
IF SVSelect.NoSelections[svData.scene, normal] THEN GOTO NoSelections;
SVRefresh.MoveAllSelectedToOverlay[svData, normal];
svData.drag.startSkitter ← SVSelections.GetPositionSkitter[svData.editToolData].skitterWORLD;
svData.drag.startPoint ← Matrix3d.OriginOfMatrix[svData.drag.startSkitter];
Set Transform
svData.drag.transform ← Matrix3d.Identity[];
Prepare Refresh
movingBox ← SVBoundBox.BoundBoxOfMoving[svData.scene, svData.camera];
svData.refresh.startBoundBox^ ← movingBox^;
IF NOT backgroundOK THEN SVRefresh.SplitBackgroundAndOverlay[svData, movingBox];
repaintNeeded ← SVAlign.StaticToDynamicBags[svData];
IF repaintNeeded THEN SVWindow.RestoreScreenAndInvariants[paintAction: $None, svData: svData, remake: bitMap, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE]
ELSE SVWindow.RestoreScreenAndInvariants[paintAction: $None, svData: svData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE];
EXITS
NoSelections => {
svData.drag.assembly ← NIL;
Feedback.Append[svData.feedback, "Please select a source to drag.", oneLiner];
Feedback.Blink[svData.feedback];
success ← FALSE;
};
END;
};
StartDrag: StartProc = {
Drag the source assembly. The origin of this assembly defines a plane parallel to the plane of the screen. We compute this plane. We cast a single ray at this plane and compute the current displacement between the assembly origin and this point. As we drag, we maintain this displacement.
startSuccess: BOOL ← StartMotion[svData, "Drag", cameraPoint];
IF NOT startSuccess THEN RETURN[FALSE];
DuringDrag[event, svData, cameraPoint];
};
StartRotate:
PUBLIC StartProc = {
startSuccess: BOOL ← StartMotion[svData, "rotate", cameraPoint, TRUE, TRUE];
IF NOT startSuccess THEN RETURN[FALSE];
DuringRotate[NIL, svData, cameraPoint];
};
StartScale:
PUBLIC StartProc = {
anchorPoint: Point3d;
originalVector: Vector3d;
startSuccess: BOOL ← StartMotion[svData, "scale", cameraPoint, TRUE, TRUE];
IF NOT startSuccess THEN RETURN[FALSE];
anchorPoint ← Matrix3d.OriginOfMatrix[SVCaret.GetPosition[svData.scene.anchor]];
originalVector ← SVVector3d.Sub[svData.drag.startPoint, anchorPoint];
IF originalVector = [0.0, 0.0, 0.0]
THEN {
Feedback.AppendHerald[svData.feedback, "Move caret away from anchor before scaling.", oneLiner];
Feedback.Blink[svData.feedback];
RETURN[FALSE];
};
DuringScale[NIL, svData, cameraPoint];
};
DuringDrag: MouseProc = {
camera: Camera ← svData.camera;
scene: Scene ← svData.scene;
newOrigin, oldOrigin: Point3d;
newSkitterWORLD: Matrix4by4;
d: Vector3d;
newSkitterWORLD ← DragTheSkitter[svData, cameraPoint, "Drag"];
oldOrigin ← Matrix3d.OriginOfMatrix[svData.drag.startSkitter];
newOrigin ← Matrix3d.OriginOfMatrix[newSkitterWORLD];
d ← SVVector3d.Sub[newOrigin, oldOrigin];
svData.drag.transform ← Matrix3d.MakeTranslateMat[d[1], d[2], d[3]];
SVWindow.RestoreScreenAndInvariants[paintAction: $DuringMotion, svData: svData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE];
};
DuringRotate:
PUBLIC MouseProc = {
originalVector, newVector: Vector3d;
mapPoint: Point3d;
anchorPoint: Point3d ← Matrix3d.OriginOfMatrix[SVCaret.GetPosition[svData.scene.anchor]];
mapPoint ← Matrix3d.OriginOfMatrix[DragTheSkitter[svData, cameraPoint, "Scaling"]];
originalVector ← SVVector3d.Sub[svData.drag.startPoint, anchorPoint];
newVector ← SVVector3d.Sub[mapPoint, anchorPoint];
svData.drag.transform ← RotateAboutPoint[anchorPoint, originalVector, newVector];
SVWindow.RestoreScreenAndInvariants[paintAction: $DuringMotion, svData: svData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE];
};
DuringScale:
PUBLIC MouseProc = {
epsilon: REAL = 1.0e-3;
originalVector, newVector: Vector3d;
mapPoint: Point3d;
ratio: REAL;
anchorPoint: Point3d ← Matrix3d.OriginOfMatrix[SVCaret.GetPosition[svData.scene.anchor]];
mapPoint ← Matrix3d.OriginOfMatrix[DragTheSkitter[svData, cameraPoint, "Scaling"]];
originalVector ← SVVector3d.Sub[svData.drag.startPoint, anchorPoint];
newVector ← SVVector3d.Sub[mapPoint, anchorPoint];
IF RealFns.AlmostZero[newVector[1], -10] AND RealFns.AlmostZero[newVector[2], -10] AND RealFns.AlmostZero[newVector[3], -10] THEN RETURN; -- can't scale to zero
ratio ← SVVector3d.Magnitude[newVector]/SVVector3d.Magnitude[originalVector];
svData.drag.transform ← ScaleAboutPoint[anchorPoint, ratio];
SVWindow.RestoreScreenAndInvariants[paintAction: $DuringMotion, svData: svData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE];
};
ScaleAboutPoint:
PUBLIC
PROC [origin: Point3d, scalar:
REAL]
RETURNS [m: Matrix4by4] = {
m ← Matrix3d.MakeTranslateMat[-origin[1], -origin[2], -origin[3]];
m ← Matrix3d.Scale[m, scalar, scalar, scalar];
m ← Matrix3d.Translate[m, origin[1], origin[2], origin[3]];
};
RotateAboutPoint:
PUBLIC
PROC [origin: Point3d, originalVector, newVector: Vector3d]
RETURNS [m: Matrix4by4] = {
degrees: REAL;
axis: Vector3d;
epsilon:
REAL = 1.0E-5;
I have seen this crossproduct be as high as 1.058926e-7 for vectors that should be parallel.
degrees ← SVVector3d.AngleCCWBetweenVectors[originalVector, newVector];
axis ← SVVector3d.CrossProduct[originalVector, newVector];
IF SVVector3d.Magnitude[axis] < epsilon
THEN {
-- the vectors are close to parallel.
m ← Matrix3d.Identity[];
}
ELSE {
m ← Matrix3d.MakeTranslateMat[-origin[1], -origin[2], -origin[3]];
m ← Matrix3d.RotateAxis[m, axis, degrees];
m ← Matrix3d.Translate[m, origin[1], origin[2], origin[3]];
};
};
EndMotion: MouseProc = {
editToolData: EditToolData ← NARROW[svData.editToolData];
scene: Scene ← svData.scene;
repaintForeground: BOOL;
TransformObjectsAfterMove[scene, svData.drag.transform];
SVRefresh.MoveOverlayToBackground[svData];
SVWindow.NewCaretPos[svData];
repaintForeground ← SVAlign.DynamicToStaticBags[svData];
SVViewersOnScene.PaintSceneAllViewers[paintAction: $FinishedDragging, editToolData: editToolData, scene: scene, remake: bitMap, backgndOK: FALSE, edited: TRUE, okToClearFeedback: FALSE];
};
StartCoordSkitter: StartProc = {
SVEvent.Selected[NIL, svData];
InputFocus.SetInputFocus[svData.actionArea];
DuringCoordSkitter[event, svData, cameraPoint];
};
DuringCoordSkitter: MouseProc = {
The user is trying to point at an existing coordinate frame. The skitter will highlight the frame which is nearest (in Euclidean distance) to the ray from eyepoint thru mouse point.
cameraRay: Ray ← SVRay.GetRayFromPool[];
camera: Camera ← svData.camera;
scene: Scene ← svData.scene;
nearest: Slice;
SVRay.StuffCameraRay[cameraRay, cameraPoint, camera];
[nearest, ----] ← NearestAssemblyToRay[cameraRay, scene.assembly, camera];
SVRay.ReturnRayToPool[cameraRay];
SVSelections.UpdateSkitter[nearest, NIL, svData];
SVSelections.PositionSkitterFromFrame[svData, cameraPoint, CoordSys.WRTWorld[nearest.coordSys]];
SVSelections.SetModeSkitter[svData, coordframe];
SVWindow.RestoreScreenAndInvariants[$SkitterMoved, svData];
};
EndCoordSkitter: MouseProc = {};
RayToPoint:
PUBLIC
PROC [cameraRay: Ray, cameraPt: Point3d]
RETURNS [dSquared:
REAL] = {
Use dot products to find the distance from the ray to this point.
magRaySquared, dotProd, dotProdSquared, rCosThetaSquared, rSquared: REAL;
screenToPoint: Vector3d;
basePt: Point3d;
direction: Vector3d;
[basePt, direction] ← SVRay.GetCameraRay[cameraRay];
magRaySquared ← SVVector3d.MagnitudeSquared[direction];
screenToPoint ← SVVector3d.Sub[cameraPt, basePt];
rSquared ← SVVector3d.MagnitudeSquared[screenToPoint];
dotProd ← SVVector3d.DotProduct[direction, screenToPoint];
dotProdSquared ← dotProd*dotProd;
rCosThetaSquared ← dotProdSquared/magRaySquared;
dSquared ← rSquared - rCosThetaSquared; -- Pythagorean Theorem
};
NearestAssemblyToRay:
PROC [cameraRay: Ray, root: Slice, camera: Camera]
RETURNS [nearest: Slice, dSquared:
REAL] = {
sonD: REAL;
sonA: Slice;
origin: Point3d;
WITH root.shape SELECT FROM
shape: Shape => {
-- A primitive. Just compute distance.
nearest ← root;
origin ← Matrix3d.OriginOfMatrix[CoordSys.WRTCamera[root.coordSys, camera.coordSys]];
dSquared ← RayToPoint[cameraRay, origin];
};
alist: SliceList => {
-- Take the minimum of myself and my subtrees, giving preference to the subtrees
nearest ← root;
origin ← Matrix3d.OriginOfMatrix[CoordSys.WRTCamera[root.coordSys, camera.coordSys]];
dSquared ← RayToPoint[cameraRay, origin];
FOR sons:
LIST
OF Slice ← alist.list, sons.rest
UNTIL sons =
NIL
DO
[sonA, sonD] ← NearestAssemblyToRay[cameraRay, sons.first, camera];
IF sonD <= dSquared
THEN {
nearest ← sonA;
dSquared ← sonD;
};
ENDLOOP;
};
ENDCASE => ERROR;
};
StartTightRope: StartProc = {
Use the distinguished target assembly's coordinate system as the platform for each tight rope step. We key off of the plane of the target's tool. If plane = 1, we stay along the x-axis, 2 the y-axis, 3 the z-axis. 4 currently works as the x-axis, but might someday compute the nearest axis.
planeSel: Selection;
planeAssem: Slice;
scene: Scene ← svData.scene;
planeSel ← SVSelections.TopPlane[];
SVSelections.SetModeSkitter[svData, tightrope];
IF planeSel =
NIL
THEN {
Feedback.Append[svData.feedback, "Please make a plane selection to specify tightrope.", oneLiner];
Feedback.Blink[svData.feedback];
RETURN;
};
planeAssem ← planeSel.coincident;
IF planeAssem.toolMasterObject =
NIL
OR planeAssem.toolMasterObject.mainBody =
NIL
THEN
SVScene.AddOrResizeToolToAssembly[planeAssem, scene];
DuringTightRope[event, svData, cameraPoint];
};
TightRopeAux:
PRIVATE
PROC [svData: SVData, cameraPoint: Point2d, target: Slice]
RETURNS [skitter
WORLD: Matrix4by4] = {
Compute the new position of the skitter given the mouse position in CAMERA coordinates. This is somewhat tricky. The effect I would like is this: We are constraining the skitter to move along an axis (a line) in 3-space. This line projects onto a line on the screen (using the current camera projection). If we drop a perpendicular from the mousept to this 2d line, that is where the skitter should appear in 2-space. Compute the corresponding point in 3-space and we are done.
skitterTarget: Matrix4by4;
newOriginCamera, newOriginTarget: Point3d;
plane: NAT;
toolData: ToolData;
cameraDir: Vector3d;
camera1, camera2: Point3d;
screen1, screen2, q: Point2d;
ropeShadow, normalLine: TrigLine;
parallel: BOOL;
camera: Camera ← svData.camera;
targetCS: CoordSystem ← target.coordSys;
targetCamera: Matrix4by4;
worldCS: CoordSystem ← svData.scene.coordSysRoot;
cameraCS: CoordSystem ← camera.coordSys;
DoDrawIntersection: PROC [dc: Imager.Context] = {
SVDraw3d.DrawX[dc, q, camera];
SVDraw3d.DrawX[dc, cameraPoint, camera];
};
targetCamera ← CoordSys.WRTCamera[targetCS, camera.coordSys];
camera1 ← Matrix3d.OriginOfMatrix[targetCamera];
toolData ← NARROW[target.toolMasterObject.mainBody];
plane ← toolData.plane;
plane ← IF plane > 3 THEN 3 ELSE plane;
SELECT plane
FROM
1 => cameraDir ← Matrix3d.XAxisOfMatrix[targetCamera];
2 => cameraDir ← Matrix3d.YAxisOfMatrix[targetCamera];
3 => cameraDir ← Matrix3d.ZAxisOfMatrix[targetCamera];
ENDCASE => ERROR;
camera2 ← SVVector3d.Add[camera1, cameraDir];
screen1 ← SVGraphics.DoProjection[camera1, camera];
screen2 ← SVGraphics.DoProjection[camera2, camera];
ropeShadow ← SVLines2d.TrigLineFromPoints[screen1, screen2];
normalLine ← SVLines2d.TrigLineNormalToTrigLineThruPoint[ropeShadow, cameraPoint];
[q, parallel] ← SVLines2d.TrigLineMeetsTrigLine[ropeShadow, normalLine];
IF parallel
THEN
ERROR;
For debugging...
SVEvent.Painter[DoDrawIntersection, svData];
Now for the hard part. If we represent the tightrope parametrically as:
R = p + t*d.
x = p1 + t*d1.
y = p2 + t*d2.
z = p3 + t*d3.
Then we wish to find the value of t so that SVGraphics.DoProjection[R, camera] = skitterScreenPt (call it q); Let R = [x, y, z] then we wish, for perspective:
xf/(f-z) = q1. and yf/(f-z) = q2. Using the first equation:
(p1 + t*d1)f = (f - p3 - t*d3)*q1.
t(d1*f + d3*q1) = (f-p3)*q1 - (p1*f).
For orthogonal projection, we wish:
x = q1, y = q2.
(p1 + t*d1) = q1.
t*d1 = q1-p1.
t ← (q1-p1)/d1;
BEGIN
p: Point3d;
d: Vector3d;
t: REAL;
p ← camera1;
d ← cameraDir;
SELECT camera.projection FROM
orthogonal => {
IF ABS[d[1]] > ABS[d[2]] THEN t ← (q[1]-p[1])/d[1]
ELSE t ← (q[2]-p[2])/d[2];
newOriginCamera[1] ← p[1] + t*d[1];
newOriginCamera[2] ← p[2] + t*d[2];
newOriginCamera[3] ← p[3] + t*d[3];
};
perspective => {
denom1, denom2: REAL;
f: REAL ← camera.focalLength;
denom1 ← (d[1]*f + d[3]*q[1]);
denom2 ← (d[2]*f + d[3]*q[2]);
IF ABS[denom1] > ABS[denom2] THEN t ← ((f-p[3])*q[1] - (p[1]*f))/denom1
ELSE t ← ((f-p[3])*q[2] - (p[2]*f))/denom2;
newOriginCamera[1] ← p[1] + t*d[1];
newOriginCamera[2] ← p[2] + t*d[2];
newOriginCamera[3] ← p[3] + t*d[3];
};
ENDCASE => ERROR;
END;
newOriginTarget ← CoordSys.FromCSToCS[newOriginCamera, cameraCS, targetCS];
The new skitter coordinate frame is the old target frame translated by newOriginCamera. We then express the results in WORLD coordinates.
skitterTarget ← Matrix3d.MakeTranslateMat[newOriginTarget[1], newOriginTarget[2], newOriginTarget[3]];
skitterWORLD ← CoordSys.FromCSToCSMat[skitterTarget, targetCS, worldCS];
};
DuringTightRope: MouseProc = {
First find the cameraPoint in the coordinate system of the target. Use only the plane-th component of the resulting point in 3-space. Draw a line from the target origin to the new skitter origin.
planeAssem: Slice;
planeSel: Selection;
skitterWORLD: Matrix4by4;
camera: Camera ← svData.camera;
planeSel ← SVSelections.TopPlane[];
IF planeSel = NIL THEN RETURN;
planeAssem ← planeSel.coincident;
skitterWORLD ← TightRopeAux[svData, cameraPoint, planeAssem];
SVSelections.UpdateSkitter[NIL, NIL, svData];
SVSelections.PositionSkitterFromFrame[svData, cameraPoint, skitterWORLD];
SVWindow.RestoreScreenAndInvariants[$SkitterMoved, svData];
};
EndTightRope: MouseProc = {
DuringTightRope[event, svData, cameraPoint];
};
StartWallWalk: StartProc = {
planeSel: Selection;
planeAssem: Slice;
scene: Scene ← svData.scene;
planeSel ← SVSelections.TopPlane[];
SVSelections.SetModeSkitter[svData, tightrope];
IF planeSel =
NIL
THEN {
Feedback.Append[svData.feedback, "Please make a plane selection to specify walk wall.", oneLiner];
Feedback.Blink[svData.feedback];
RETURN;
};
planeAssem ← planeSel.coincident;
IF planeAssem.toolMasterObject =
NIL
OR planeAssem.toolMasterObject.mainBody =
NIL
THEN
SVScene.AddOrResizeToolToAssembly[planeAssem, scene];
DuringWallWalk[event, svData, cameraPoint];
};
RayMeetsTopPlane:
PROC [cameraPoint: Point2d, target: Slice, depth:
REAL, plane:
NAT, camera: Camera]
RETURNS [planePt
World: Point3d, parallel:
BOOL] = {
targetPlanePt is in CAMERA coords.
newRay: Ray;
newRay ← SVRay.GetRayFromPool[];
SVRay.StuffWorldRayFromCamera[newRay, cameraPoint, camera];
[planePtWorld, parallel] ← SVCastRays.RayMeetsPlaneOfCoordSystem[target.coordSys, newRay, plane, depth];
SVRay.ReturnRayToPool[newRay];
};
DuringWallWalk: MouseProc = {
Cast a ray at the target plane (like for dragging). Use the intersection point as the new skitter origin.
planeAssem: Slice;
planeSel: Selection;
targetWORLD, skitterWORLD: Matrix4by4;
hitPointWORLD: Point3d;
camera: Camera ← svData.camera;
scene: Scene ← svData.scene;
worldCS: CoordSystem ← scene.coordSysRoot;
toolData: ToolData;
parallel: BOOL;
planeSel ← SVSelections.TopPlane[];
IF planeSel = NIL THEN RETURN;
planeAssem ← planeSel.coincident;
toolData ← NARROW[planeAssem.toolMasterObject.mainBody];
IF toolData =
NIL
THEN {
Feedback.Append[svData.feedback, "Wall walk plane is not defined.", oneLiner];
Feedback.Blink[svData.feedback];
RETURN;
};
Cast a ray at the wall walk plane.
[hitPointWORLD, parallel] ← RayMeetsTopPlane[cameraPoint, planeAssem, 0, toolData.plane, camera];
IF parallel
THEN
{
Feedback.Append[svData.feedback, "DuringWallWalk: plane is parallel to ray.", oneLiner];
Feedback.Blink[svData.feedback];
RETURN;
};
targetWORLD ← CoordSys.WRTWorld[planeAssem.coordSys];
skitterWORLD ← Matrix3d.Translate[targetWORLD, hitPointWORLD[1], hitPointWORLD[2], hitPointWORLD[3]];
SVSelections.UpdateSkitter[NIL, NIL, svData];
SVSelections.PositionSkitterFromFrame[svData, cameraPoint, skitterWORLD];
SVWindow.RestoreScreenAndInvariants[$SkitterMoved, svData];
};
EndWallWalk: MouseProc = {
DuringWallWalk[event, svData, cameraPoint];
};
ExtendSkitter: StartProc = {
Using the previous skitter assembly as one node and the current value as another, find the common root and set the skitter assembly to this value. If current is NIL, leave the skitter assembly as it is. Set skitter assembly to the new value.
editToolData: EditToolData ← svData.editToolData;
oldAssembly, assem, commonAncestor: Slice;
oldPrimitive, prim: Primitive;
oldSVData, vtd: SVData;
scene: Scene;
scene ← svData.scene;
[oldAssembly, oldPrimitive, oldSVData] ← SVSelections.GetSkitterData[editToolData];
DuringSkitter[event, svData, cameraPoint];
[assem, prim, vtd] ← SVSelections.GetSkitterData[editToolData];
IF vtd # oldSVData THEN {
Feedback.Append[svData.feedback, "Can't extend across viewers.", oneLiner];
Feedback.Blink[svData.feedback];
RETURN;
};
IF assem = NIL THEN SVSelections.UpdateSkitter[oldAssembly, oldPrimitive, vtd]
ELSE {
commonAncestor ← DisplayListToTree.CommonAncestor[assem, oldAssembly, scene.assembly];
SVSelections.UpdateSkitter[commonAncestor, prim, vtd];
};
};
ExtendCoordSkitter: StartProc = {
Using the previous skitter assembly as one node and the current value as another, find the common root and set the skitter assembly to this value. If current is NIL, leave the skitter assembly as it is. Set skitter assembly to the new value.
editToolData: EditToolData ← svData.editToolData;
oldAssembly, assem, commonAncestor: Slice;
oldPrimitive, prim: Primitive;
oldSVData, vtd: SVData;
scene: Scene;
scene ← svData.scene;
[oldAssembly, oldPrimitive, oldSVData] ← SVSelections.GetSkitterData[editToolData];
DuringCoordSkitter[event, svData, cameraPoint];
[assem, prim, vtd] ← SVSelections.GetSkitterData[editToolData];
IF vtd # oldSVData
THEN {
Feedback.Append[svData.feedback, "Can't extend across viewers.", oneLiner];
Feedback.Blink[svData.feedback];
RETURN;
};
IF assem = NIL THEN SVSelections.UpdateSkitter[oldAssembly, oldPrimitive, vtd]
ELSE {
commonAncestor ← DisplayListToTree.CommonAncestor[assem, oldAssembly, scene.assembly];
SVSelections.UpdateSkitter[commonAncestor, prim, vtd];
};
};
Paint:
PUBLIC MouseProc = {
Move the skitter as usual. Compute the corresponding artwork point as the artwork "Position" operation. Find the corresponding ais pixels as in ray tracing. Color in a block of them with the current artworkToolData.paintColor.
editToolData: EditToolData ← svData.editToolData;
artworkToolData: ArtworkToolData ← NARROW[editToolData.artworkTool.data];
camera: Camera ← svData.camera;
artwork: Artwork ← artworkToolData.artwork;
assembly: Slice;
skitterWORLD, assemWORLD, skitterassem: Matrix4by4;
imagePoint: Point2d;
skitterPoint, surfPtassem: Point3d;
normal: Vector3d;
paintColor: Color ← artworkToolData.paintColor;
DuringSkitter[event, svData, cameraPoint];
[assembly, ----, svData] ← SVSelections.GetSkitterData[editToolData];
[----, skitterWORLD] ← SVSelections.GetPositionSkitter[editToolData];
assemWORLD ← CoordSys.WRTWorld[assembly.coordSys];
skitterassem ← Matrix3d.AInTermsOfB[skitterWORLD, assemWORLD];
normal ← Matrix3d.ZAxisOfMatrix[skitterassem];
Our paint brush is the set of points [{-2,-1,0,1,2}, {-2,-1,0,1,2}] in the coordinate system of the skitter. We convert them to master object coordinates and then to image points.
FOR i:
INT
IN [-2..2]
DO
FOR j:
INT
IN [-2..2]
DO
skitterPoint ← [i, j, 0.0];
surfPtassem ← Matrix3d.Update[skitterPoint, skitterassem];
imagePoint ← SVArtworkUser.GetImagePointFromSurfacePoint[artwork, surfPtassem, normal];
Feedback.AppendTypescript[IO.PutFR["%g, %g", [real[imagePoint[1]]], [real[imagePoint[2]]] ]];
SetImageColorAtPoint[artwork, imagePoint, paintColor];
ENDLOOP;
ENDLOOP;
SVWindow.RestoreScreenAndInvariants[$DrawPaint, svData];
};
SetImageColorAtPoint:
PROC [artwork: Artwork, imagePoint: Point2d, paintColor: Color] = {
Assume for now that artwork.file is an 8 bit-per-pixel ais file with rd scanning with origin in its lower left corner.
imagePoint is in screen dots.
Find the bounding box of the AIS file.
artworkPoint: Point2d;
IF artwork.source = NIL THEN RETURN;
BEGIN
uAIS, vAIS: REAL; -- u and v in AIS dots at this ais file's resolution
uInch, vInch: REAL; -- u and v in inches
artworkPoint ← SVMatrix2d.Update[artwork.artWRTPad.padWRTLocal, imagePoint]; -- in screen dots
uInch ← artworkPoint[1]/72.0; vInch ← artworkPoint[2]/72.0;
uAIS ← uInch*artwork.resolution; vAIS ← vInch*artwork.resolution;
IF artwork.isColorFile
THEN {
redValue, greenValue, blueValue: CARDINAL;
realRedValue, realGreenValue, realBlueValue: REAL;
[realRedValue, realGreenValue, realBlueValue] ← Shading.ExtractRGB[paintColor];
If [u,v] is outside the AIS image, do nothing.
IF uInch <= - artwork.halfWidth OR uInch >= artwork.halfWidth
OR vInch <= -artwork.halfHeight OR vInch >= artwork.halfHeight THEN RETURN;
redValue ← Real.Fix[realRedValue*255.0];
AIS.WriteSample[artwork.redWindow, redValue, Real.Fix[-vAIS + artwork.halfHeightDots], Real.Fix[uAIS + artwork.halfWidthDots]];
greenValue ← Real.Fix[realGreenValue*255.0];
AIS.WriteSample[artwork.greenWindow, greenValue, Real.Fix[-vAIS + artwork.halfHeightDots], Real.Fix[uAIS + artwork.halfWidthDots]];
blueValue ← Real.Fix[realBlueValue*255.0];
AIS.WriteSample[artwork.blueWindow, blueValue, Real.Fix[-vAIS + artwork.halfHeightDots], Real.Fix[uAIS + artwork.halfWidthDots]];
}
ELSE {
-- artwork is a black and white file
blackValue: CARDINAL;
realBlackValue: REAL;
realBlackValue ← ImagerColorPrivate.IntensityFromColor[NARROW[paintColor]];
IF uInch <= - artwork.halfWidth OR uInch >= artwork.halfWidth
OR vInch <= -artwork.halfHeight OR vInch >= artwork.halfHeight
THEN RETURN;
blackValue ← Real.Fix[realBlackValue*255.0];
AIS.WriteSample[artwork.blackWindow, blackValue, Real.Fix[-vAIS + artwork.halfHeightDots], Real.Fix[uAIS + artwork.halfWidthDots]];
};
END;
}; -- end of SetImageColorAtPoint
StartDragAux:
PRIVATE
PROC [svData: SVData]
RETURNS [drageeSel: Selection, planeAssem: Slice, success:
BOOL] = {
scene: Scene ← svData.scene;
success ← TRUE;
drageeSel ← SVSelections.TopMovee[];
IF drageeSel =
NIL
THEN {
svData.drag.assembly ← NIL;
Feedback.Append[svData.feedback, "Please select a source to drag.", oneLiner];
Feedback.Blink[svData.feedback];
success ← FALSE;
RETURN;
};
svData.drag.assembly ← drageeSel.coincident;
planeAssem ← SVSelections.TopPlaneCoincident[];
svData.drag.target ← planeAssem;
IF planeAssem =
NIL
THEN {
Feedback.Append[svData.feedback, "Please select a dragging plane.", oneLiner];
Feedback.Blink[svData.feedback];
success ← FALSE;
RETURN;
};
Make sure the target has a working toolData.
SVScene.AddOrResizeToolToAssembly[planeAssem, scene];
};
StartDrag: StartProc = {
Drag the source assembly. The origin of this assembly defines a plane parallel to the plane of target. We compute this plane. We cast a single ray at this plane and compute the current displacement between the assembly origin and this point. As we drag, we maintain this displacement.
editToolData: EditToolData ← svData.editToolData;
camera: Camera ← svData.camera;
originCamera, hitPointCAMERA, originTarget: Point3d;
plane: NAT;
drageeSel: Selection;
dragee, planeAssem: Slice;
parallel: BOOL;
[drageeSel, planeAssem, success] ← StartDragAux[svData];
IF NOT success THEN RETURN;
dragee ← drageeSel.coincident;
Find the origin of assembly in terms of target.
originCamera ← Matrix3d.OriginOfMatrix[dragee.coordSys.wrtCamera];
originTarget ← CoordSys.FromCSToCS[originCamera, camera.coordSys, planeAssem.coordSys];
Compute the dragging plane.
[svData.drag.planeDepth, plane] ← DepthFromTarget[planeAssem, originTarget];
Cast a ray at the target plane. Returns result in CAMERA coordinates.
[hitPointCAMERA, parallel] ← RayMeetsTargetPlane[cameraPoint, planeAssem, svData.drag.planeDepth, plane, camera];
IF parallel THEN {
Feedback.Append[svData.feedback, "StartDrag: Dragging plane is parallel to ray.", oneLiner];
Feedback.Blink[svData.feedback];
svData.drag.target ← NIL;
RETURN;
};
Compute in CAMERA the displacement from hitPoint to origin.
svData.drag.cameraVector ← SVVector3d.Sub[originCamera, hitPointCAMERA];
Draw a coordinate frame at the current spot (as a form of highlighting).
SVEvent.Painter[DoEraseSelection, svData];
}; -- end of StartDrag
DepthFromTarget:
PRIVATE
PROC [target: Slice, targetPt: Point3d]
RETURNS [depth:
REAL, plane:
NAT] = {
toolData: ToolData;
toolData ← NARROW[target.toolMasterObject.mainBody];
IF toolData.plane > 3 THEN depth ← targetPt[3]
ELSE depth ← targetPt[toolData.plane];
plane ← toolData.plane;
};
RayMeetsTargetPlane:
PRIVATE
PROC [cameraPoint: Point2d, planeA: Slice, depth:
REAL, plane:
NAT, camera: Camera]
RETURNS [planePt
Camera: Point3d, parallel:
BOOL] = {
targetPlanePt is in CAMERA coords.
rayWorld: Ray;
worldCamera: Matrix4by4;
planePtWorld: Point3d;
rayWorld ← SVRay.GetRayFromPool[];
SVRay.StuffWorldRayFromCamera[rayWorld, cameraPoint, camera];
[planePtWorld, parallel] ← SVCastRays.RayMeetsPlaneOfCoordSystem[planeA.coordSys, rayWorld, plane, depth];
worldCamera ← CoordSys.FindAInTermsOfB[CoordSys.Parent[camera.coordSys], camera.coordSys];
planePtCamera ← Matrix3d.Update[planePtWorld, worldCamera];
SVRay.ReturnRayToPool[rayWorld];
};
RayMeetsCameraPlane:
PROC [cameraPoint: Point2d, depth:
REAL, camera: Camera]
RETURNS [planePt
Camera: Point3d, parallel:
BOOL] = {
rayWorld: Ray;
worldCamera: Matrix4by4;
planePtWorld: Point3d;
rayWorld ← SVRay.GetRayFromPool[];
SVRay.StuffWorldRayFromCamera[rayWorld, cameraPoint, camera];
[planePtWorld, parallel] ← SVCastRays.RayMeetsPlaneOfCoordSystem[camera.coordSys, rayWorld, 3, depth];
worldCamera ← CoordSys.FindAInTermsOfB[CoordSys.Parent[camera.coordSys], camera.coordSys];
planePtCamera ← Matrix3d.Update[planePtWorld, worldCamera];
SVRay.ReturnRayToPool[rayWorld];
};
SetDrag: MouseProc = {
Cast a ray at the dragging plane. Move the dragee so as to maintain the displacement between the dragee origin and the ray tracing point.
camera: Camera ← svData.camera;
scene: Scene ← svData.scene;
hitPointCAMERA, newOriginPoint: Point3d;
dragee, target: Slice;
toolData: ToolData;
parallel: BOOL;
dragee ← svData.drag.assembly;
IF dragee = NIL THEN RETURN;
target ← svData.drag.target;
IF target = NIL THEN RETURN;
toolData ← NARROW[target.toolMasterObject.mainBody];
IF toolData = NIL THEN {
Feedback.Append[svData.feedback, "Dragging plane is not defined", oneLiner];
Feedback.Blink[svData.feedback];
RETURN;
};
Cast a ray at the dragging plane.
[hitPointCAMERA, parallel] ← RayMeetsTargetPlane[cameraPoint, svData.drag.target, svData.drag.planeDepth, toolData.plane, camera];
IF parallel THEN {
Feedback.Append[svData.feedback, "SetDrag: Dragging plane is parallel to ray.", oneLiner];
Feedback.Blink[svData.feedback];
RETURN;
};
newOriginPoint ← SVVector3d.Add[hitPointCAMERA, svData.drag.cameraVector];
Place the assembly's coordinate systems at this new point.
SVTransforms.Abut[dragee.coordSys, camera.coordSys];
SVTransforms.Translate[dragee.coordSys, camera.coordSys, newOriginPoint[1], newOriginPoint[2], newOriginPoint[3]];
SVTransforms.TellAboutParent[dragee.coordSys];
Draw a coordinate frame and assembly at the new spot.
svData.refresh.dragObject ← dragee;
SVWindow.RestoreScreenAndInvariants[$DuringMotion, svData];
}; -- end of SetDrag
EndDrag: MouseProc = {
editToolData: EditToolData ← NARROW[svData.editToolData];
camera: Camera ← svData.camera;
scene: Scene ← svData.scene;
drageeSel: Selection;
assembly: Slice ← NARROW[svData.drag.assembly];
IF assembly = NIL THEN RETURN;
drageeSel ← SVSelections.TopMovee[];
SetDrag[event, svData, cameraPoint];
SVViewersOnScene.PaintSceneAllViewers[paintAction: $FinishedMotion, editToolData: editToolData, scene: scene, edited: TRUE];
};
InitStats:
PROC = {
interval: CodeTimer.Interval;
interval ← CodeTimer.CreateInterval[$StartSkitter];
CodeTimer.AddInt[interval, $Solidviews];
interval ← CodeTimer.CreateInterval[$DuringSkitter];
CodeTimer.AddInt[interval, $Solidviews];
interval ← CodeTimer.CreateInterval[$EndSkitter];
CodeTimer.AddInt[interval, $Solidviews];
};
InitStats[];
END.