DIRECTORY AIS, Atom, AtomButtonsTypes, CodeTimer, CoordSys, SVCaret, SVRay, SVGraphics, Feedback, Imager, ImagerColorPrivate, ImagerPath, InputFocus, Matrix3d, Real, RealFns, Rope, Shading, SV2d, SV3d, SVAlign, SVArtworkUser, SVAssembly, SVBasicTypes, SVBoundBox, SVCastRays, SVDescribe, SVEditUser, SVEvent, SVGravity, SVInterfaceTypes, SVLines2d, SVMatrix2d, SVModelTypes, SVMouseEvent, SVRefresh, SVScene, SVSceneTypes, SVSelect, SVSelections, SVState, SVVector3d, SVViewersOnScene, SVWindow; SVMouseEventImplA: CEDAR PROGRAM IMPORTS AIS, Atom, CodeTimer, CoordSys, Feedback, ImagerColorPrivate, InputFocus, Matrix3d, Real, RealFns, Rope, Shading, SVAlign, SVArtworkUser, SVAssembly, SVBoundBox, SVCaret, SVCastRays, SVDescribe, SVEditUser, SVEvent, SVGraphics, SVGravity, SVLines2d, SVMatrix2d, SVMouseEvent, SVRay, 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 = SVSceneTypes.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 = SVSceneTypes.Primitive; Ray: TYPE = SVSceneTypes.Ray; Scene: TYPE = SVSceneTypes.Scene; SearchDepth: TYPE = SVSceneTypes.SearchDepth; Selection: TYPE = SVInterfaceTypes.Selection; Shape: TYPE = SVSceneTypes.Shape; Skitter: TYPE = SVSceneTypes.Skitter; 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; Problem: PUBLIC SIGNAL [msg: Rope.ROPE] = Feedback.Problem; InitializeFSM: PUBLIC PROC [svData: SVData] = { svData.state _ $None; svData.mouseMode _ $None; }; EasyAbort: MouseProc = { 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]; $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] = { 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]; RETURN[ FALSE ]; }; SaveSavedState: PROC [svData: SVData] = { editToolData: EditToolData _ svData.editToolData; SVScene.SaveSelections[svData.scene]; SVWindow.SaveCaretPos[svData]; SVCaret.Copy[from: editToolData.skitter, to: svData.drag.savedCaret]; }; StartSkitter: PUBLIC StartProc = { CodeTimer.StartInt[$StartSkitter, $Solidviews]; SVEvent.Selected[NIL, svData]; SVSelections.SetModeSkitter[svData, surface]; SaveSavedState[svData]; DuringSkitter[NIL, svData, cameraPoint]; CodeTimer.StopInt[$StartSkitter, $Solidviews]; }; -- end of StartSkitter DragTheSkitter: PROC [svData: SVData, cameraPt: Point2d, action: Rope.ROPE _ ""] RETURNS [skitterWorld: Matrix4by4] = { surfacePtWorld: Point3d; normalWorld: Vector3d; sliceD: SliceDescriptor; feature: FeatureData; camera: Camera _ svData.camera; scene: Scene _ svData.scene; gravityType: GravityType _ SVState.GetGravityType[svData]; searchDepth: SearchDepth _ first; hitData: REF ANY; skitter: Skitter _ svData.editToolData.skitter; [surfacePtWorld, normalWorld, feature, hitData] _ SVGravity.RayMap[cameraPt, svData.hitTest.t, svData.hitTest.alignBag, svData.hitTest.sceneBag, svData]; IF feature = NIL THEN SVCaret.SetAttractor[skitter, cameraPt, surfacePtWorld, normalWorld, NIL] ELSE { SELECT feature.type FROM slice => { sliceD _ IF feature = NIL THEN NIL ELSE NARROW[feature.shape]; SVCaret.SetAttractor[skitter, cameraPt, surfacePtWorld, normalWorld, sliceD]; IF sliceD # NIL THEN SVEditUser.SetCurrentAssemblyName[sliceD.slice, svData.editToolData]; }; anchor => { anchor: Skitter _ NARROW[feature.shape]; SVCaret.NoAttractor[skitter]; SVCaret.PositionFromMatrix[skitter, cameraPt, anchor.skitterWorld]; }; ENDCASE => SVCaret.SetAttractor[skitter, cameraPt, surfacePtWorld, normalWorld, NIL]; }; skitterWorld _ SVCaret.GetPosition[skitter]; 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 = { 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]; }; UpdateSceneForCopy: PROC [scene: Scene, feedback: FeedbackData] RETURNS [newSlices: LIST OF Slice, success: BOOL _ TRUE] = { sliceDGen: SliceDescriptorGenerator; newSlice: Slice; sliceDGen _ SVSelect.SelectedSlices[scene, normal]; FOR sliceD: SliceDescriptor _ SVSelect.NextSliceDescriptor[sliceDGen], SVSelect.NextSliceDescriptor[sliceDGen] UNTIL sliceD = NIL DO 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 = { 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]; }; 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; skitter: Skitter _ svData.editToolData.skitter; 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; IF needAnchor AND NOT SVCaret.Exists[svData.scene.anchor] THEN GOTO NoAnchor; SVRefresh.MoveAllSelectedToOverlay[svData, normal]; svData.drag.startSkitter _ SVCaret.GetPosition[svData.editToolData.skitter]; svData.drag.startPoint _ Matrix3d.OriginOfMatrix[svData.drag.startSkitter]; svData.drag.transform _ Matrix3d.Identity[]; 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; }; NoAnchor => { Feedback.PutFHerald[svData.feedback, oneLiner, "Anchor needed to %g", [rope[opName]]]; Feedback.Blink[svData.feedback]; success _ FALSE; }; END; }; StartDrag: StartProc = { 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]; }; EndMotion: MouseProc = { editToolData: EditToolData _ NARROW[svData.editToolData]; scene: Scene _ svData.scene; TransformObjectsAfterMove[scene, svData.drag.transform]; SVRefresh.MoveOverlayToBackground[svData]; SVWindow.NewCaretPos[svData]; [] _ SVAlign.DynamicToStaticBags[svData]; SVViewersOnScene.PaintSceneAllViewers[paintAction: $FinishedDragging, editToolData: editToolData, scene: scene, remake: bitMap, backgndOK: FALSE, edited: TRUE, 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; 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]]; }; }; RayToPoint: PUBLIC PROC [cameraRay: Ray, cameraPt: Point3d] RETURNS [dSquared: REAL] = { 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; }; StartCoordSkitter: StartProc = { SVEvent.Selected[NIL, svData]; DuringCoordSkitter[event, svData, cameraPoint]; }; DuringCoordSkitter: MouseProc = { cameraRay: Ray _ SVRay.GetRayFromPool[]; camera: Camera _ svData.camera; scene: Scene _ svData.scene; nearest: Slice; skitter: Skitter _ svData.editToolData.skitter; SVRay.StuffCameraRay[cameraRay, cameraPoint, camera]; [nearest, ----] _ NearestAssemblyToRay[cameraRay, scene.assembly, camera]; SVRay.ReturnRayToPool[cameraRay]; SVCaret.SetAssemblyAndPrimitive[skitter, nearest, NIL]; SVCaret.PositionFromMatrix[svData.editToolData.skitter, cameraPoint, CoordSys.WRTWorld[nearest.coordSys]]; SVSelections.SetModeSkitter[svData, coordframe]; SVWindow.RestoreScreenAndInvariants[$SkitterMoved, svData]; }; EndCoordSkitter: MouseProc = {}; StartTightRope: 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 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 [skitterWORLD: Matrix4by4] = { 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; 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; 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]; skitterTarget _ Matrix3d.MakeTranslateMat[newOriginTarget[1], newOriginTarget[2], newOriginTarget[3]]; skitterWORLD _ CoordSys.FromCSToCSMat[skitterTarget, targetCS, worldCS]; }; DuringTightRope: MouseProc = { planeAssem: Slice; planeSel: Selection; skitterWORLD: Matrix4by4; camera: Camera _ svData.camera; skitter: Skitter _ svData.editToolData.skitter; planeSel _ SVSelections.TopPlane[]; IF planeSel = NIL THEN RETURN; planeAssem _ planeSel.coincident; skitterWORLD _ TightRopeAux[svData, cameraPoint, planeAssem]; SVCaret.SetAssemblyAndPrimitive[skitter, NIL, NIL]; SVCaret.PositionFromMatrix[svData.editToolData.skitter, 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 [planePtWorld: Point3d, parallel: BOOL] = { newRay: Ray; newRay _ SVRay.GetRayFromPool[]; SVRay.StuffWorldRayFromCamera[newRay, cameraPoint, camera]; [planePtWorld, parallel] _ SVCastRays.RayMeetsPlaneOfCoordSystem[target.coordSys, newRay, plane, depth]; SVRay.ReturnRayToPool[newRay]; }; DuringWallWalk: MouseProc = { 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; skitter: Skitter _ svData.editToolData.skitter; 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; }; [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]]; SVCaret.SetAssemblyAndPrimitive[skitter, NIL, NIL]; SVCaret.PositionFromMatrix[svData.editToolData.skitter, cameraPoint, skitterWORLD]; SVWindow.RestoreScreenAndInvariants[$SkitterMoved, svData]; }; EndWallWalk: MouseProc = { DuringWallWalk[event, svData, cameraPoint]; }; Paint: PUBLIC MouseProc = { 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 _ SVCaret.GetPosition[editToolData.skitter]; assemWORLD _ CoordSys.WRTWorld[assembly.coordSys]; skitterassem _ Matrix3d.AInTermsOfB[skitterWORLD, assemWORLD]; normal _ Matrix3d.ZAxisOfMatrix[skitterassem]; 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]; SetImageColorAtPoint[artwork, imagePoint, paintColor]; ENDLOOP; ENDLOOP; SVWindow.RestoreScreenAndInvariants[$DrawPaint, svData]; }; SetImageColorAtPoint: PROC [artwork: Artwork, imagePoint: Point2d, paintColor: Color] = { 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 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 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. "File: SVMouseEventImplA.mesa Last edited by: Eric Bier on July 10, 1987 12:16:33 pm PDT Copyright c 1984 by Xerox Corporation. All rights reserved. Contents: Interactive solidviews operations like dragging and stretching objects. Bloomenthal, June 16, 1987 7:30:57 pm PDT The Solidviews FSM 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. $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]; An easy way to handle the Abort action. Caret Procs 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. Copy and Drag Copy selected slices. newSlice _ SVAssembly.Copy[sliceD.slice]; 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. Motion Procs Set Transform Prepare Refresh 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. Transformation Utilities I have seen this crossproduct be as high as 1.058926e-7 for vectors that should be parallel. Old Skitter Motions Use dot products to find the distance from the ray to this point. 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. 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. 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. DoDrawIntersection: PROC [dc: Imager.Context] = { SVDraw3d.DrawX[dc, q, camera]; SVDraw3d.DrawX[dc, cameraPoint, camera]; }; 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; The new skitter coordinate frame is the old target frame translated by newOriginCamera. We then express the results in WORLD coordinates. 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. targetPlanePt is in CAMERA coords. Cast a ray at the target plane (like for dragging). Use the intersection point as the new skitter origin. Cast a ray at the wall walk plane. Old Extending 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 _ svData.scene; skitter: Skitter _ editToolData.skitter; 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 SVCaret.SetAssemblyAndPrimitive[skitter, oldAssembly, oldPrimitive] 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]; }; }; Old Painting 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. 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. Feedback.AppendTypescript[IO.PutFR["%g, %g", [real[imagePoint[1]]], [real[imagePoint[2]]] ]]; 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. If [u,v] is outside the AIS image, do nothing. 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 [planePtCamera: 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 [planePtCamera: 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]; }; Κ"*˜Ihead™Jšœ:™:Jšœ Οmœ1™<™QIcode™)—J˜šΟk ˜ Jšžœβ˜εJ˜—•StartOfExpansion[]šœžœž˜ Jšžœžœσ˜ώJšžœ˜—J˜šž˜J˜Jšœ žœ˜%Jšœžœ$˜9Jšœžœ˜!Jšœ žœ˜)Jšœ žœ˜'Jšœžœ˜#Jšœžœ˜3Jšœžœ˜Jšœ žœ˜-Jšœžœ!˜3Jšœžœ!˜3Jšœ žœ ˜1Jšœ žœ ˜1Jšœ žœ˜#Jšœ žœ˜Jšœ žœ˜Jšœ žœ˜)Jšœžœ˜Jšœžœ˜!Jšœ žœ˜-Jšœ žœ˜-Jšœžœ˜!Jšœ žœ˜%Jšœžœ ˜5Jšœžœ)˜GJšœ žœ˜'Jšœ žœ˜Jšœ žœ˜Jšœžœ˜'J˜Lšœ žœ˜)Lšœ žœ˜)—J˜J™J˜LšΟnœžœžœ žœ˜;L˜šŸ œžœžœ˜/Lšœ˜Lšœ˜L˜L˜—šŸ œ˜L™Lšœ1˜1Lšœ˜Lšœ!˜!LšœFΟc˜_Lšœ˜L˜L˜—šŸ œžœ˜&Lšœ*˜*Lšœ>˜>Lšœsžœ žœžœ˜£L˜L˜—šŸœžœžœ˜5Lšœ˜Lšœ˜Lšœ*˜*L˜L˜—šŸ œžœžœ žœžœžœžœ˜ELšœžœ˜ Lš œ žœžœžœžœ˜Lšžœ‘ œ7˜F—Lšžœ˜—L˜—˜ šžœž˜šœ ˜ Lš‘ œ˜#Lšœ#˜#L˜—Lšœ ‘ œ3˜GLšœ$˜$Lšœ$˜$šœ  }˜‰Lš‘ œ/˜8Lšœ˜Lšœ˜L˜—Lšžœ˜—L˜—šœ ˜ šžœž˜šœ ˜ Lš‘œ‘œ˜1Lšœ˜Lšœ˜L˜—Lšœ ‘ œ3˜GLšžœ˜—L˜—˜ šžœž˜šœ ˜ Lš‘œ‘œ˜1Lšœ˜Lšœ˜L˜—Lšœ ‘ œ3˜Gšœ  E˜Qšžœžœžœžœ˜7šžœ‘ œž˜.Lšœ˜Lšœ#˜#L˜—Lšžœ‘ œ4˜CL˜—šžœ˜Lšœ˜Lš‘œ‘œ˜1Lšœ˜Lšœ˜L˜—L˜—Lšžœ˜—L˜—šœ ˜ Lšžœž˜Lšœ;˜;Lšžœ˜L˜—Lšžœžœ'˜8—L˜L˜—š ŸœžœJžœžœžœžœ'˜•Lšœžœ˜Lšœžœ˜Lšœžœ˜Lšœ˜Lš œžœ2žœžœžœ˜`Lšœe˜eL˜L˜—šŸœžœRžœ žœžœžœžœ'˜­šžœž˜˜ šžœž˜šœ ˜ Lšœ1˜1Lšžœ‘ œžœ˜>Lšžœ‘ œ2˜ALšœ˜—Lšžœ˜—L˜—˜ šžœž˜Lšœ ‘ œ˜.šœ˜Lš‘œ˜ Lšœ˜Lšœ˜L˜—Lšœ ‘ œ3˜Gšœ  }˜‰Lš‘ œ/˜8Lšœ˜Lšœ˜L˜—Lšžœ˜—L˜—šœ ˜ šžœž˜Lšœ;˜;Lšœ=˜=Lšžœ˜—L˜—Lšžœžœ'˜8—L˜L˜—J˜šŸœžœ žœžœžœžœžœžœ˜ILšœ žœ˜#Lšœžœžœ˜"šžœ˜Jšž˜J˜—˜L˜——šŸœžœ˜)Lšœ1˜1Lšœ%˜%Lšœ˜LšœE˜EL˜L˜—L™L™ L™šŸ œžœ˜"Jšœ/˜/Jšœ‘œžœ ˜Jšœ ‘œ˜-Lšœ˜Jšœžœ˜(Jšœ.˜.Jšœ ˜J˜—š Ÿœžœ2žœžœ Οuœ˜wJšœ ’œ ˜Jšœ’œ ˜Jšœ˜J˜Jšœ˜Jšœ˜Jšœ:˜:Jšœ!˜!Jšœ žœžœ˜Jšœ/˜/J˜Jšœ ’œ’œ}˜™J˜Lš žœ žœžœ2’œ’œžœ˜_šžœ˜šžœž˜˜ Jš œ žœ žœžœžœžœžœ˜>Jšœ1’œ’œ ˜MJšžœ žœžœF˜ZJ˜—˜ Jšœžœ˜(Jšœ˜Jšœ<’œ˜CJ˜—Jšžœ5’œ’œžœ˜U—J˜—Jšœ’œ ˜,šœR˜RLšœM˜MLšœ’œ’œ’œ˜R—J˜J˜—šŸ œ˜Jšœ―™―Jšœ1˜1J˜Jšœ0˜0Jšœ:˜:Jšœžœ žœžœ˜ΏJšœ/˜/Jšœ ˜J˜—šŸ œ˜L˜Lšœ-˜-Jšœ:˜:Lšœ˜Lšœižœ žœžœ˜˜Lšœ,˜,J˜J˜—L™L™ šŸœžœ(žœ žœžœžœžœ˜|Lšœ$˜$L˜Lš‘™Lšœ3˜3šžœlžœ žœž˜„Jšœ)™)Jšœh˜hJšœB˜BJšœ žœ˜&Jšœ4˜4Jšžœ˜—˜J˜——šŸœžœ˜&Lšœυ™υLšœ žœžœ˜Lšœ˜L˜Lšœ 0˜HJšœC #˜fJšžœžœ žœžœ˜Jšœ2˜2L–Ξ[gargoyleData: GGInterfaceTypes.GargoyleData, opName: ROPE, bagType: ATOM, worldPt: Lines2dTypes.Point, startBox: GGBasicTypes.BoundBox _ NIL, saveState: BOOL _ TRUE, needAnchor: BOOL _ FALSE]šœ[žœžœžœ˜ŠLš žœžœ žœžœžœ˜"Lšœ žœ˜%J˜L˜—L™L™ šŸœžœ*˜IL˜Lšœ.˜.šžœbžœ žœž˜zLšœ/˜/—Lšžœ˜L˜L˜—šŸ œžœžœ#žœžœžœžœžœžœžœ ž œ˜ΌLšœ˜Lšœžœ˜Lšœ/˜/J˜Lšžœ žœ "˜Lšž˜Lš žœžœ žœžœ ˜HLšžœ-žœžœ˜FLš žœ žœžœ%žœžœ ˜ML˜Lšœ3˜3JšœL˜LJšœK˜KLš‘ ™ Lšœ,˜,Lš‘™LšœE˜ELšœ+˜+Lšžœžœžœ8˜PLšœ4˜4Lš žœžœQ‘œ žœ žœžœ˜§Lš žœQ‘œ žœ žœžœ˜•šž˜˜Jšœžœ˜JšœN˜NJšœ ˜ Jšœ žœ˜J˜—˜ LšœV˜VLšœ ˜ Lšœ žœ˜L˜——Jšžœ˜—J˜J˜—šŸ œ˜J™€Jšœžœ,˜>Lš žœžœžœžœžœ˜'Jšœ'˜'Jšœ˜—šŸ œžœ˜!Lšœžœ.žœžœ˜LLš žœžœžœžœžœ˜'Lšœ žœ˜'L˜—šŸ œžœ˜ Lšœ˜Lšœ˜Lšœžœ-žœžœ˜KLš žœžœžœžœžœ˜'LšœP˜PLšœE˜Ešžœ"žœ˜*Lšœ`˜`Lšœ ˜ Lšžœžœ˜L˜—Lšœ žœ˜&L˜L˜—šŸ œ˜Jšœ˜Jšœ˜Jšœ˜Jšœ ’œ ˜Jšœ ˜ J˜Jšœ ’œ/˜>Jšœ>˜>Jšœ.’œ˜5Jšœ)˜)JšœD˜DJ˜Jšœižœ žœžœ˜˜Jšœ˜J˜—šŸ œžœ˜"L˜$Lšœ˜LšœY˜YLšœS˜SLšœE˜ELšœ2˜2Lšœ‘œ)˜QLšœižœ žœžœ˜˜L˜L˜—šŸ œžœ˜!Lšœ žœ ˜L˜$Lšœ˜Lšœžœ˜ LšœY˜YLšœS˜SLšœE˜ELšœ2˜2Lš žœ'žœ'žœ&žœžœ ˜ LšœM˜MLšœ‘œ˜Mšœ˜M˜——šŸœžœ/žœžœ˜uJšœžœ˜ J˜ J˜Jšžœ žœž˜šœ '˜9J˜JšœU˜UJšœ)˜)J˜—šœ P˜fJ˜JšœU˜UJšœ)˜)š žœžœžœžœžœž˜CJšœC˜Cšžœžœ˜J˜Jšœ˜J˜——Jšžœ˜J˜—Jšžœžœ˜J˜J˜—šŸœ˜ Jšœ‘œžœ ˜Jš‘œ˜/J˜J˜—šŸœ˜!J™ΆJšœžœ˜(Jšœ˜Jšœ˜Jšœ˜J˜/J˜Jšœ5˜5Jšœ  œ<˜JJšœ!˜!J˜Jšœ7˜7Jšœj˜jJšœ ‘œ˜0Jšœ;˜;J˜J˜—JšŸœ˜ šŸœ˜J™₯Jšœ˜J˜Jšœ˜J˜Jšœ#˜#Jšœ/˜/šžœ žœžœ˜Jšœb˜bJšœ ˜ Jšžœ˜J˜—Jšœ!˜!š žœžœžœ(žœž˜WMšœ5˜5—Jšœ,˜,J˜J˜—š Ÿ œžœžœ7žœ ’œ˜wJ™γJšœ’œ ˜Jšœ ’œ ’œ ˜*Jšœžœ˜ J˜J˜J˜J˜J˜!Jšœ žœ˜Jšœ˜Jšœ(˜(Jšœ’œ ˜Jšœ1˜1Jšœ(˜(šŸœžœ™1Jšœ™Jšœ(™(J™—J˜Jšœ’œ1˜=Jšœ(’œ˜0Jšœ žœ#˜4J˜Jšœžœ žœžœ˜'šžœž˜Jšœ‘ œ Ÿ œ’œ˜6Jšœ‘ œ Ÿ œ’œ˜6Jšœ‘ œ Ÿ œ’œ˜6—Jšžœžœ˜Jš‘œ&˜-Jš‘œŸ œ‘œ ˜3Jš‘œŸ œ‘œ ˜3Jšœ*‘œ‘œ˜Jšœ'’œ˜.J˜J™΄šžœžœžœ ž˜šžœžœžœ ž˜J˜Jšœ’œ(’œ˜:JšœH’œ ˜WJšœžœA™]Jšœ6˜6—Jšžœ˜—Jšžœ˜J˜8Jšœ˜J˜—šŸœžœ?˜YJšœv™vJšœ™Jšœ&™&Jšœ˜Jšžœžœžœžœ˜$šž˜Jšœ žœ 4˜FJšœžœ ˜(JšœM ˜^Jšœ<˜