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; 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]; 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 [skitterWORLD: 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 = { 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; 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]; 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; }; 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]; }; 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]]; }; }; 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 = { 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] = { 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 = { 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; 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 [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; 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]]; SVSelections.UpdateSkitter[NIL, NIL, svData]; SVSelections.PositionSkitterFromFrame[svData, cameraPoint, skitterWORLD]; SVWindow.RestoreScreenAndInvariants[$SkitterMoved, svData]; }; EndWallWalk: MouseProc = { DuringWallWalk[event, svData, cameraPoint]; }; ExtendSkitter: StartProc = { 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 = { 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 = { 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]; 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 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; }; SVScene.AddOrResizeToolToAssembly[planeAssem, scene]; }; 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] = { 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]; }; 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 May 29, 1987 6:10:04 pm PDT Copyright c 1984 by Xerox Corporation. All rights reserved. Contents: Interactive solidviews operations like dragging and stretching objects. 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. $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]; 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. I have seen this crossproduct be as high as 1.058926e-7 for vectors that should be parallel. 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 dot products to find the distance from the ray to this 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. 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. 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. 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. Make sure the target has a working toolData. 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 targetPlanePt is in CAMERA coords. 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]; }; Κ%6˜Ihead™Jšœ8™8Jšœ Οmœ1™˜>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šœ ‘ œ˜,Jšœ ‘œ˜-J˜Jšœžœ˜(Jšœ.˜.Jšœ ˜J˜—š Ÿœžœ5žœžœ Οuœ˜zJšœ ’œ ˜Jšœ’œ ˜Jšœ˜J˜Jšœ˜Jšœ˜Jšœ:˜:Jšœ!˜!Jšœ žœžœ˜J˜Jšœ ’œ’œNžœ#˜J˜Jš œ žœ žœžœžœžœžœ'˜WJšœ’œ6’œ’œ˜hšœ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šœžœ˜J˜Lšžœ žœ "˜Lšž˜Lš žœžœ žœžœ ˜HLšžœ-žœžœ˜FL˜Lšœ3˜3JšœW’œ˜]JšœK˜KLš‘ ™ Lšœ,˜,Lš‘™LšœE˜ELšœ+˜+Lšžœžœžœ8˜PLšœ4˜4Lš žœžœQ‘œ žœ žœžœ˜§Lš žœQ‘œ žœ žœžœ˜•šž˜˜Jšœžœ˜JšœN˜NJšœ ˜ Jšœ žœ˜J˜——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šœ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šœ<˜