File: SVMouseEventImplA.mesa
Last edited by: Eric Bier on July 10, 1987 12:16:33 pm PDT
Copyright © 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
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;
The Solidviews FSM
Problem: PUBLIC SIGNAL [msg: Rope.ROPE] = Feedback.Problem;
InitializeFSM: PUBLIC PROC [svData: SVData] = {
svData.state ← $None;
svData.mouseMode ← $None;
};
EasyAbort: MouseProc = {
Many actions don't change the scene at all until the End proc is called. For these cases, we just restore the selections and caret position.
editToolData: EditToolData ← svData.editToolData;
scene: Scene ← svData.scene;
SVScene.RestoreSelections[scene];
SVCaret.Copy[from: svData.drag.savedCaret, to: editToolData.skitter]; -- restore original caret
FinishAbort[svData];
};
FinishAbort: PROC [svData: SVData] = {
SVRefresh.MoveOverlayToBackground[svData];
Feedback.AppendHerald[svData.feedback, ". . . Aborted.", end];
SVWindow.RestoreScreenAndInvariants[paintAction: $PaintEntireScene, svData: svData, remake: triggerBag, backgndOK: FALSE, edited: FALSE, okToClearFeedback: FALSE];
};
ResetMouseMachinery: PUBLIC PROC [svData: SVData] = {
svData.mouseMode ← $None;
svData.state ← $None;
SVRefresh.MoveOverlayToBackground[svData];
};
HandleMouse: PUBLIC PROC [event: LIST OF REF ANY, svData: SVData] = {
atom: ATOM;
vec: Imager.VECNARROW[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];
$SingleRay => SVEvent.SingleRay[svData, controlPoint];
$DeleteFrame => SVMouseEvent.DeleteFrame[svData];
$Paint => SVMouseEvent.Paint[svData, controlPoint];
$OpenAISFile => SVArtworkUser.OpenFile[artworkToolData];
$CloseAISFile => SVArtworkUser.CloseFile[artworkToolData];
$ExtendSkitter => SVMouseEvent.ExtendSkitter[svData, controlPoint, first];
$ExtendCoordSkitter => SVMouseEvent.ExtendCoordSkitter[svData, controlPoint];
$None => {
atomName: Rope.ROPE;
atom ← NARROW[event.first];
atomName ← Atom.GetPName[atom];
IF Rope.Equal[Rope.Substr[atomName, 0, 5], "Start", TRUE] THEN {
svData.mouseMode ← Atom.MakeAtom[Rope.Substr[atomName, 5]];
HandleMouse[event, svData];
}
ELSE {}; -- ignore other actions
};
ENDCASE => SIGNAL Problem[msg: "Unimplemented Mode"];
};
HandleMouseless: PUBLIC PROC [event: LIST OF REF ANY, svData: SVData] = {
An easy way to handle the Abort action.
event ← LIST[event.first, NEW[Imager.VEC ← [0.0, 0.0]]];
HandleMouse[event, svData];
};
HandleGuarded: PROC [startProc: StartProc, duringProc, endProc, abortProc: MouseProc, continueProc: StartProc, event: LIST OF REF ANY, svData: SVData, worldPt: Point2d] = {
genericAction, atom: ATOM;
atomName: Rope.ROPE;
atom ← NARROW[event.first];
atomName ← Atom.GetPName[atom];
genericAction ← IF Rope.Equal[Rope.Substr[atomName, 0, 5], "Start", TRUE] THEN $Start ELSE atom;
HandleGuardedAux[startProc, duringProc, endProc, abortProc, continueProc, genericAction, event, svData, worldPt];
};
HandleGuardedAux: PROC [startProc: StartProc, duringProc, endProc, abortProc: MouseProc, continueProc: StartProc, genericAction: ATOM, event: LIST OF REF ANY, svData: SVData, worldPt: Point2d] = {
SELECT svData.state FROM
$None => {
SELECT genericAction FROM
$Start => {
svData.drag.currentPoint ← worldPt;
[] ← InputFocus.SetInputFocus[svData.actionArea];
IF startProc[event, svData, worldPt] THEN svData.state ← $Main
ELSE {abortProc[event, svData, worldPt]; svData.state ← $Aborted;}; };
ENDCASE;
};
$Main => {
SELECT genericAction FROM
$During => {
duringProc[event, svData, worldPt];
svData.drag.currentPoint ← worldPt;
};
$Abort => {abortProc[event, svData, worldPt]; svData.state ← $Aborted};
$GuardUp => svData.state ← $GuardUp;
$MouseUp => svData.state ← $MouseUp;
$Start => { -- the mouse must have gone out of the window while the mouse button was done. Abort the current action and start a new one.
abortProc[event, svData, worldPt]; svData.state ← $None;
svData.mouseMode ← $None;
HandleMouse[event, svData];
};
ENDCASE;
};
$GuardUp => {
SELECT genericAction FROM
$AllUp => {
endProc[event, svData, svData.drag.currentPoint];
svData.mouseMode ← $None;
svData.state ← $None;
};
$Abort => {abortProc[event, svData, worldPt]; svData.state ← $Aborted};
ENDCASE;
};
$MouseUp => {
SELECT genericAction FROM
$AllUp => {
endProc[event, svData, svData.drag.currentPoint];
svData.mouseMode ← $None;
svData.state ← $None;
};
$Abort => {abortProc[event, svData, worldPt]; svData.state ← $Aborted};
$Start => { -- we may be starting another action of this mode or some other mode.
IF Restart[event, svData] AND continueProc # NIL THEN {
IF continueProc[event, svData, worldPt] THEN {
svData.state ← $Main;
svData.drag.currentPoint ← worldPt;
}
ELSE {abortProc[event, svData, worldPt]; svData.state ← $Aborted;};
}
ELSE {
svData.mouseMode ← $None;
endProc[event, svData, svData.drag.currentPoint];
svData.state ← $None;
HandleMouse[event, svData];
};
};
ENDCASE;
};
$Aborted => {
SELECT genericAction FROM
$AllUp => {svData.state ← $None; svData.mouseMode ← $None};
ENDCASE;
};
ENDCASE => SIGNAL Problem[msg: "Unknown generic state"];
};
HandleUnGuarded: PROC [startProc: StartProc, duringProc, endProc, abortProc: MouseProc, event: LIST OF REF ANY, svData: SVData, worldPt: Point2d] = {
genericAction, atom: ATOM;
atomName: Rope.ROPE;
atom ← NARROW[event.first];
atomName ← Atom.GetPName[atom];
genericAction ← IF Rope.Equal[Rope.Substr[atomName, 0, 5], "Start", TRUE] THEN $Start ELSE atom;
HandleUnGuardedAux[startProc, duringProc, endProc, abortProc, genericAction, event, svData, worldPt];
};
HandleUnGuardedAux: PROC [startProc: StartProc, duringProc, endProc, abortProc: MouseProc, genericAction: ATOM, event: LIST OF REF ANY, svData: SVData, worldPt: Point2d] = {
SELECT svData.state FROM
$None => {
SELECT genericAction FROM
$Start => {
[] ← InputFocus.SetInputFocus[svData.actionArea];
IF startProc[event, svData, worldPt] THEN svData.state ← $Main
ELSE {abortProc[event, svData, worldPt]; svData.state ← $Aborted}
};
ENDCASE;
};
$Main => {
SELECT genericAction FROM
$During => duringProc[event, svData, worldPt];
$MouseUp, $AllUp => {
endProc[event, svData, worldPt];
svData.state ← $None;
svData.mouseMode ← $None;
};
$Abort => {abortProc[event, svData, worldPt]; svData.state ← $Aborted};
$Start => { -- the mouse must have gone out of the window while the mouse button was done. Abort the current action and start a new one.
abortProc[event, svData, worldPt]; svData.state ← $None;
svData.mouseMode ← $None;
HandleMouse[event, svData];
};
ENDCASE;
};
$Aborted => {
SELECT genericAction FROM
$AllUp => {svData.state ← $None; svData.mouseMode ← $None};
$MouseUp => {svData.state ← $None; svData.mouseMode ← $None};
ENDCASE;
};
ENDCASE => SIGNAL Problem[msg: "Unknown generic state"];
};
Restart: PROC [event: LIST OF REF ANY, svData: SVData] RETURNS [BOOL] = {
mouseMode: ATOM ← svData.mouseMode;
state: ATOMNARROW[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];
};
Caret Procs
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 = {
Cast a ray into the scene from this point. If it hits anything, use the surface normal at that point to derive a coordinate system whose z axis is the parallel to the normal.
editToolData: EditToolData ← svData.editToolData;
CodeTimer.StartInt[$DuringSkitter, $Solidviews];
[] ← DragTheSkitter[svData, cameraPoint, "SkitterMotion"];
SVViewersOnScene.PaintSceneAllViewers[paintAction: $DuringSkitterPos, editToolData: editToolData, scene: svData.scene, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE];
CodeTimer.StopInt[$DuringSkitter, $Solidviews];
}; -- end DuringSkitter
EndSkitter: MouseProc = {
CodeTimer.StartInt[$EndSkitter, $Solidviews];
[] ← DragTheSkitter[svData, cameraPoint, "SkitterMotion"];
SVWindow.NewCaretPos[svData];
SVWindow.RestoreScreenAndInvariants[paintAction: $SkitterMoved, svData: svData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE];
CodeTimer.StopInt[$EndSkitter, $Solidviews];
};
Copy and Drag
UpdateSceneForCopy: PROC [scene: Scene, feedback: FeedbackData] RETURNS [newSlices: LIST OF Slice, success: BOOLTRUE] = {
sliceDGen: SliceDescriptorGenerator;
newSlice: Slice;
Copy selected slices.
sliceDGen ← SVSelect.SelectedSlices[scene, normal];
FOR sliceD: SliceDescriptor ← SVSelect.NextSliceDescriptor[sliceDGen], SVSelect.NextSliceDescriptor[sliceDGen] UNTIL sliceD = NIL DO
newSlice ← SVAssembly.Copy[sliceD.slice];
newSlice ← SVScene.CopyAssemblyAndSonsUniqueNames[sliceD.slice, scene, scene, scene.assembly, feedback];
SVSelect.DeselectSlice[sliceD.slice, sliceD.parts, scene, normal];
newSlices ← CONS[newSlice, newSlices];
SVSelect.SelectEntireSlice[newSlice, scene, normal];
ENDLOOP;
};
StartCopyAndDrag: PUBLIC StartProc = {
Make copies of all selected objects. The selected objects can be part of trajectories, trajectories, outlines, or whole slices. Sort the resulting slices by the priority ordering of the objects they came from. Add the new objects on top. This can be done in two ways: Sort the selections by priority order to begin with, or sort the new objects after they are created.
newSlices: LIST OF Slice;
scene: Scene ← svData.scene;
SaveSavedState[svData]; -- must do this before any possible aborts occur
[newSlices, success] ← UpdateSceneForCopy[scene, svData.feedback]; -- adds new shapes and selects them
IF NOT success THEN RETURN;
SVAlign.UpdateBagsForNewSlices[newSlices, svData];
success ← StartMotion[svData: svData, opName: "copy", cameraPoint: cameraPoint, saveState: FALSE, needAnchor: FALSE, backgroundOK: FALSE];
IF NOT success THEN RETURN[FALSE];
DuringDrag[NIL, svData, cameraPoint];
};
Motion Procs
TransformObjectsAfterMove: PROC [scene: Scene, transform: Matrix4by4] = {
sGen: SliceDescriptorGenerator;
sGen ← SVSelect.SelectedSlices[scene, normal];
FOR sliceD: SliceDescriptor ← SVSelect.NextSliceDescriptor[sGen], SVSelect.NextSliceDescriptor[sGen] UNTIL sliceD = NIL DO
SVAssembly.Transform[sliceD, scene, transform];
ENDLOOP;
};
StartMotion: PROC [svData: SVData, opName: Rope.ROPE, cameraPoint: Point2d, saveState: BOOLTRUE, needAnchor: BOOLFALSE, backgroundOK: BOOLFALSE] 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];
Set Transform
svData.drag.transform ← Matrix3d.Identity[];
Prepare Refresh
movingBox ← SVBoundBox.BoundBoxOfMoving[svData.scene, svData.camera];
svData.refresh.startBoundBox^ ← movingBox^;
IF NOT backgroundOK THEN SVRefresh.SplitBackgroundAndOverlay[svData, movingBox];
repaintNeeded ← SVAlign.StaticToDynamicBags[svData];
IF repaintNeeded THEN SVWindow.RestoreScreenAndInvariants[paintAction: $None, svData: svData, remake: bitMap, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE]
ELSE SVWindow.RestoreScreenAndInvariants[paintAction: $None, svData: svData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE];
EXITS
NoSelections => {
svData.drag.assembly ← NIL;
Feedback.Append[svData.feedback, "Please select a source to drag.", oneLiner];
Feedback.Blink[svData.feedback];
success ← FALSE;
};
NoAnchor => {
Feedback.PutFHerald[svData.feedback, oneLiner, "Anchor needed to %g", [rope[opName]]];
Feedback.Blink[svData.feedback];
success ← FALSE;
};
END;
};
StartDrag: StartProc = {
Drag the source assembly. The origin of this assembly defines a plane parallel to the plane of the screen. We compute this plane. We cast a single ray at this plane and compute the current displacement between the assembly origin and this point. As we drag, we maintain this displacement.
startSuccess: BOOL ← StartMotion[svData, "Drag", cameraPoint];
IF NOT startSuccess THEN RETURN[FALSE];
DuringDrag[event, svData, cameraPoint];
};
StartRotate: PUBLIC StartProc = {
startSuccess: BOOL ← StartMotion[svData, "rotate", cameraPoint, TRUE, TRUE];
IF NOT startSuccess THEN RETURN[FALSE];
DuringRotate[NIL, svData, cameraPoint];
};
StartScale: PUBLIC StartProc = {
anchorPoint: Point3d;
originalVector: Vector3d;
startSuccess: BOOL ← StartMotion[svData, "scale", cameraPoint, TRUE, TRUE];
IF NOT startSuccess THEN RETURN[FALSE];
anchorPoint ← Matrix3d.OriginOfMatrix[SVCaret.GetPosition[svData.scene.anchor]];
originalVector ← SVVector3d.Sub[svData.drag.startPoint, anchorPoint];
IF originalVector = [0.0, 0.0, 0.0] THEN {
Feedback.AppendHerald[svData.feedback, "Move caret away from anchor before scaling.", oneLiner];
Feedback.Blink[svData.feedback];
RETURN[FALSE];
};
DuringScale[NIL, svData, cameraPoint];
};
DuringDrag: MouseProc = {
camera: Camera ← svData.camera;
scene: Scene ← svData.scene;
newOrigin, oldOrigin: Point3d;
newSkitterWORLD: Matrix4by4;
d: Vector3d;
newSkitterWORLD ← DragTheSkitter[svData, cameraPoint, "Drag"];
oldOrigin ← Matrix3d.OriginOfMatrix[svData.drag.startSkitter];
newOrigin ← Matrix3d.OriginOfMatrix[newSkitterWORLD];
d ← SVVector3d.Sub[newOrigin, oldOrigin];
svData.drag.transform ← Matrix3d.MakeTranslateMat[d[1], d[2], d[3]];
SVWindow.RestoreScreenAndInvariants[paintAction: $DuringMotion, svData: svData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE];
};
DuringRotate: PUBLIC MouseProc = {
originalVector, newVector: Vector3d;
mapPoint: Point3d;
anchorPoint: Point3d ← Matrix3d.OriginOfMatrix[SVCaret.GetPosition[svData.scene.anchor]];
mapPoint ← Matrix3d.OriginOfMatrix[DragTheSkitter[svData, cameraPoint, "Scaling"]];
originalVector ← SVVector3d.Sub[svData.drag.startPoint, anchorPoint];
newVector ← SVVector3d.Sub[mapPoint, anchorPoint];
svData.drag.transform ← RotateAboutPoint[anchorPoint, originalVector, newVector];
SVWindow.RestoreScreenAndInvariants[paintAction: $DuringMotion, svData: svData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE];
};
DuringScale: PUBLIC MouseProc = {
epsilon: REAL = 1.0e-3;
originalVector, newVector: Vector3d;
mapPoint: Point3d;
ratio: REAL;
anchorPoint: Point3d ← Matrix3d.OriginOfMatrix[SVCaret.GetPosition[svData.scene.anchor]];
mapPoint ← Matrix3d.OriginOfMatrix[DragTheSkitter[svData, cameraPoint, "Scaling"]];
originalVector ← SVVector3d.Sub[svData.drag.startPoint, anchorPoint];
newVector ← SVVector3d.Sub[mapPoint, anchorPoint];
IF RealFns.AlmostZero[newVector[1], -10] AND RealFns.AlmostZero[newVector[2], -10] AND RealFns.AlmostZero[newVector[3], -10] THEN RETURN; -- can't scale to zero
ratio ← SVVector3d.Magnitude[newVector]/SVVector3d.Magnitude[originalVector];
svData.drag.transform ← ScaleAboutPoint[anchorPoint, ratio];
SVWindow.RestoreScreenAndInvariants[paintAction: $DuringMotion, svData: svData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE];
};
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];
};
Transformation Utilities
ScaleAboutPoint: PUBLIC PROC [origin: Point3d, scalar: REAL] RETURNS [m: Matrix4by4] = {
m ← Matrix3d.MakeTranslateMat[-origin[1], -origin[2], -origin[3]];
m ← Matrix3d.Scale[m, scalar, scalar, scalar];
m ← Matrix3d.Translate[m, origin[1], origin[2], origin[3]];
};
RotateAboutPoint: PUBLIC PROC [origin: Point3d, originalVector, newVector: Vector3d] RETURNS [m: Matrix4by4] = {
degrees: REAL;
axis: Vector3d;
epsilon: REAL = 1.0E-5;
I have seen this crossproduct be as high as 1.058926e-7 for vectors that should be parallel.
degrees ← SVVector3d.AngleCCWBetweenVectors[originalVector, newVector];
axis ← SVVector3d.CrossProduct[originalVector, newVector];
IF SVVector3d.Magnitude[axis] < epsilon THEN { -- the vectors are close to parallel.
m ← Matrix3d.Identity[];
}
ELSE {
m ← Matrix3d.MakeTranslateMat[-origin[1], -origin[2], -origin[3]];
m ← Matrix3d.RotateAxis[m, axis, degrees];
m ← Matrix3d.Translate[m, origin[1], origin[2], origin[3]];
};
};
Old Skitter Motions
RayToPoint: PUBLIC PROC [cameraRay: Ray, cameraPt: Point3d] RETURNS [dSquared: REAL] = {
Use dot products to find the distance from the ray to this point.
magRaySquared, dotProd, dotProdSquared, rCosThetaSquared, rSquared: REAL;
screenToPoint: Vector3d;
basePt: Point3d;
direction: Vector3d;
[basePt, direction] ← SVRay.GetCameraRay[cameraRay];
magRaySquared ← SVVector3d.MagnitudeSquared[direction];
screenToPoint ← SVVector3d.Sub[cameraPt, basePt];
rSquared ← SVVector3d.MagnitudeSquared[screenToPoint];
dotProd ← SVVector3d.DotProduct[direction, screenToPoint];
dotProdSquared ← dotProd*dotProd;
rCosThetaSquared ← dotProdSquared/magRaySquared;
dSquared ← rSquared - rCosThetaSquared; -- Pythagorean Theorem
};
NearestAssemblyToRay: PROC [cameraRay: Ray, root: Slice, camera: Camera] RETURNS [nearest: Slice, dSquared: REAL] = {
sonD: REAL;
sonA: Slice;
origin: Point3d;
WITH root.shape SELECT FROM
shape: Shape => { -- A primitive. Just compute distance.
nearest ← root;
origin ← Matrix3d.OriginOfMatrix[CoordSys.WRTCamera[root.coordSys, camera.coordSys]];
dSquared ← RayToPoint[cameraRay, origin];
};
alist: SliceList => { -- Take the minimum of myself and my subtrees, giving preference to the subtrees
nearest ← root;
origin ← Matrix3d.OriginOfMatrix[CoordSys.WRTCamera[root.coordSys, camera.coordSys]];
dSquared ← RayToPoint[cameraRay, origin];
FOR sons: LIST OF Slice ← alist.list, sons.rest UNTIL sons = NIL DO
[sonA, sonD] ← NearestAssemblyToRay[cameraRay, sons.first, camera];
IF sonD <= dSquared THEN {
nearest ← sonA;
dSquared ← sonD;
};
ENDLOOP;
};
ENDCASE => ERROR;
};
StartCoordSkitter: StartProc = {
SVEvent.Selected[NIL, svData];
DuringCoordSkitter[event, svData, cameraPoint];
};
DuringCoordSkitter: MouseProc = {
The user is trying to point at an existing coordinate frame. The skitter will highlight the frame which is nearest (in Euclidean distance) to the ray from eyepoint thru mouse point.
cameraRay: Ray ← SVRay.GetRayFromPool[];
camera: Camera ← svData.camera;
scene: Scene ← svData.scene;
nearest: Slice;
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 = {
Use the distinguished target assembly's coordinate system as the platform for each tight rope step. We key off of the plane of the target's tool. If plane = 1, we stay along the x-axis, 2 the y-axis, 3 the z-axis. 4 currently works as the x-axis, but might someday compute the nearest axis.
planeSel: Selection;
planeAssem: Slice;
scene: Scene ← svData.scene;
planeSel ← SVSelections.TopPlane[];
SVSelections.SetModeSkitter[svData, tightrope];
IF planeSel = NIL THEN {
Feedback.Append[svData.feedback, "Please make a plane selection to specify tightrope.", oneLiner];
Feedback.Blink[svData.feedback];
RETURN;
};
planeAssem ← planeSel.coincident;
IF planeAssem.toolMasterObject = NIL OR planeAssem.toolMasterObject.mainBody = NIL THEN
SVScene.AddOrResizeToolToAssembly[planeAssem, scene];
DuringTightRope[event, svData, cameraPoint];
};
TightRopeAux: PRIVATE PROC [svData: SVData, cameraPoint: Point2d, target: Slice] RETURNS [skitterWORLD: Matrix4by4] = {
Compute the new position of the skitter given the mouse position in CAMERA coordinates. This is somewhat tricky. The effect I would like is this: We are constraining the skitter to move along an axis (a line) in 3-space. This line projects onto a line on the screen (using the current camera projection). If we drop a perpendicular from the mousept to this 2d line, that is where the skitter should appear in 2-space. Compute the corresponding point in 3-space and we are done.
skitterTarget: Matrix4by4;
newOriginCamera, newOriginTarget: Point3d;
plane: NAT;
toolData: ToolData;
cameraDir: Vector3d;
camera1, camera2: Point3d;
screen1, screen2, q: Point2d;
ropeShadow, normalLine: TrigLine;
parallel: BOOL;
camera: Camera ← svData.camera;
targetCS: CoordSystem ← target.coordSys;
targetCamera: Matrix4by4;
worldCS: CoordSystem ← svData.scene.coordSysRoot;
cameraCS: CoordSystem ← camera.coordSys;
DoDrawIntersection: PROC [dc: Imager.Context] = {
SVDraw3d.DrawX[dc, q, camera];
SVDraw3d.DrawX[dc, cameraPoint, camera];
};
targetCamera ← CoordSys.WRTCamera[targetCS, camera.coordSys];
camera1 ← Matrix3d.OriginOfMatrix[targetCamera];
toolData ← NARROW[target.toolMasterObject.mainBody];
plane ← toolData.plane;
plane ← IF plane > 3 THEN 3 ELSE plane;
SELECT plane FROM
1 => cameraDir ← Matrix3d.XAxisOfMatrix[targetCamera];
2 => cameraDir ← Matrix3d.YAxisOfMatrix[targetCamera];
3 => cameraDir ← Matrix3d.ZAxisOfMatrix[targetCamera];
ENDCASE => ERROR;
camera2 ← SVVector3d.Add[camera1, cameraDir];
screen1 ← SVGraphics.DoProjection[camera1, camera];
screen2 ← SVGraphics.DoProjection[camera2, camera];
ropeShadow ← SVLines2d.TrigLineFromPoints[screen1, screen2];
normalLine ← SVLines2d.TrigLineNormalToTrigLineThruPoint[ropeShadow, cameraPoint];
[q, parallel] ← SVLines2d.TrigLineMeetsTrigLine[ropeShadow, normalLine];
IF parallel THEN ERROR;
For debugging...
SVEvent.Painter[DoDrawIntersection, svData];
Now for the hard part. If we represent the tightrope parametrically as:
R = p + t*d.
x = p1 + t*d1.
y = p2 + t*d2.
z = p3 + t*d3.
Then we wish to find the value of t so that SVGraphics.DoProjection[R, camera] = skitterScreenPt (call it q); Let R = [x, y, z] then we wish, for perspective:
xf/(f-z) = q1. and yf/(f-z) = q2. Using the first equation:
(p1 + t*d1)f = (f - p3 - t*d3)*q1.
t(d1*f + d3*q1) = (f-p3)*q1 - (p1*f).
For orthogonal projection, we wish:
x = q1, y = q2.
(p1 + t*d1) = q1.
t*d1 = q1-p1.
t ← (q1-p1)/d1;
BEGIN
p: Point3d;
d: Vector3d;
t: REAL;
p ← camera1;
d ← cameraDir;
SELECT camera.projection FROM
orthogonal => {
IF ABS[d[1]] > ABS[d[2]] THEN t ← (q[1]-p[1])/d[1]
ELSE t ← (q[2]-p[2])/d[2];
newOriginCamera[1] ← p[1] + t*d[1];
newOriginCamera[2] ← p[2] + t*d[2];
newOriginCamera[3] ← p[3] + t*d[3];
};
perspective => {
denom1, denom2: REAL;
f: REAL ← camera.focalLength;
denom1 ← (d[1]*f + d[3]*q[1]);
denom2 ← (d[2]*f + d[3]*q[2]);
IF ABS[denom1] > ABS[denom2] THEN t ← ((f-p[3])*q[1] - (p[1]*f))/denom1
ELSE t ← ((f-p[3])*q[2] - (p[2]*f))/denom2;
newOriginCamera[1] ← p[1] + t*d[1];
newOriginCamera[2] ← p[2] + t*d[2];
newOriginCamera[3] ← p[3] + t*d[3];
};
ENDCASE => ERROR;
END;
newOriginTarget ← CoordSys.FromCSToCS[newOriginCamera, cameraCS, targetCS];
The new skitter coordinate frame is the old target frame translated by newOriginCamera. We then express the results in WORLD coordinates.
skitterTarget ← Matrix3d.MakeTranslateMat[newOriginTarget[1], newOriginTarget[2], newOriginTarget[3]];
skitterWORLD ← CoordSys.FromCSToCSMat[skitterTarget, targetCS, worldCS];
};
DuringTightRope: MouseProc = {
First find the cameraPoint in the coordinate system of the target. Use only the plane-th component of the resulting point in 3-space. Draw a line from the target origin to the new skitter origin.
planeAssem: Slice;
planeSel: Selection;
skitterWORLD: Matrix4by4;
camera: Camera ← svData.camera;
skitter: Skitter ← svData.editToolData.skitter;
planeSel ← SVSelections.TopPlane[];
IF planeSel = NIL THEN RETURN;
planeAssem ← planeSel.coincident;
skitterWORLDTightRopeAux[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] = {
targetPlanePt is in CAMERA coords.
newRay: Ray;
newRay ← SVRay.GetRayFromPool[];
SVRay.StuffWorldRayFromCamera[newRay, cameraPoint, camera];
[planePtWorld, parallel] ← SVCastRays.RayMeetsPlaneOfCoordSystem[target.coordSys, newRay, plane, depth];
SVRay.ReturnRayToPool[newRay];
};
DuringWallWalk: MouseProc = {
Cast a ray at the target plane (like for dragging). Use the intersection point as the new skitter origin.
planeAssem: Slice;
planeSel: Selection;
targetWORLD, skitterWORLD: Matrix4by4;
hitPointWORLD: Point3d;
camera: Camera ← svData.camera;
scene: Scene ← svData.scene;
worldCS: CoordSystem ← scene.coordSysRoot;
toolData: ToolData;
parallel: BOOL;
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;
};
Cast a ray at the wall walk plane.
[hitPointWORLD, parallel] ← RayMeetsTopPlane[cameraPoint, planeAssem, 0, toolData.plane, camera];
IF parallel THEN {
Feedback.Append[svData.feedback, "DuringWallWalk: plane is parallel to ray.", oneLiner];
Feedback.Blink[svData.feedback];
RETURN;
};
targetWORLD ← CoordSys.WRTWorld[planeAssem.coordSys];
skitterWORLD ← Matrix3d.Translate[targetWORLD, hitPointWORLD[1], hitPointWORLD[2], hitPointWORLD[3]];
SVCaret.SetAssemblyAndPrimitive[skitter, NIL, NIL];
SVCaret.PositionFromMatrix[svData.editToolData.skitter, cameraPoint, skitterWORLD];
SVWindow.RestoreScreenAndInvariants[$SkitterMoved, svData];
};
EndWallWalk: MouseProc = {
DuringWallWalk[event, svData, cameraPoint];
};
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
Paint: PUBLIC MouseProc = {
Move the skitter as usual. Compute the corresponding artwork point as the artwork "Position" operation. Find the corresponding ais pixels as in ray tracing. Color in a block of them with the current artworkToolData.paintColor.
editToolData: EditToolData ← svData.editToolData;
artworkToolData: ArtworkToolData ← NARROW[editToolData.artworkTool.data];
camera: Camera ← svData.camera;
artwork: Artwork ← artworkToolData.artwork;
assembly: Slice;
skitterWORLD, assemWORLD, skitterassem: Matrix4by4;
imagePoint: Point2d;
skitterPoint, surfPtassem: Point3d;
normal: Vector3d;
paintColor: Color ← artworkToolData.paintColor;
DuringSkitter[event, svData, cameraPoint];
[assembly, ----, svData] ← SVSelections.GetSkitterData[editToolData];
skitterWORLD ← SVCaret.GetPosition[editToolData.skitter];
assemWORLD ← CoordSys.WRTWorld[assembly.coordSys];
skitterassem ← Matrix3d.AInTermsOfB[skitterWORLD, assemWORLD];
normal ← Matrix3d.ZAxisOfMatrix[skitterassem];
Our paint brush is the set of points [{-2,-1,0,1,2}, {-2,-1,0,1,2}] in the coordinate system of the skitter. We convert them to master object coordinates and then to image points.
FOR i: INT IN [-2..2] DO
FOR j: INT IN [-2..2] DO
skitterPoint ← [i, j, 0.0];
surfPtassem ← Matrix3d.Update[skitterPoint, skitterassem];
imagePoint ← SVArtworkUser.GetImagePointFromSurfacePoint[artwork, surfPtassem, normal];
Feedback.AppendTypescript[IO.PutFR["%g, %g", [real[imagePoint[1]]], [real[imagePoint[2]]] ]];
SetImageColorAtPoint[artwork, imagePoint, paintColor];
ENDLOOP;
ENDLOOP;
SVWindow.RestoreScreenAndInvariants[$DrawPaint, svData];
};
SetImageColorAtPoint: PROC [artwork: Artwork, imagePoint: Point2d, paintColor: Color] = {
Assume for now that artwork.file is an 8 bit-per-pixel ais file with rd scanning with origin in its lower left corner.
imagePoint is in screen dots.
Find the bounding box of the AIS file.
artworkPoint: Point2d;
IF artwork.source = NIL THEN RETURN;
BEGIN
uAIS, vAIS: REAL; -- u and v in AIS dots at this ais file's resolution
uInch, vInch: REAL; -- u and v in inches
artworkPoint ← SVMatrix2d.Update[artwork.artWRTPad.padWRTLocal, imagePoint]; -- in screen dots
uInch ← artworkPoint[1]/72.0; vInch ← artworkPoint[2]/72.0;
uAIS ← uInch*artwork.resolution; vAIS ← vInch*artwork.resolution;
IF artwork.isColorFile THEN {
redValue, greenValue, blueValue: CARDINAL;
realRedValue, realGreenValue, realBlueValue: REAL;
[realRedValue, realGreenValue, realBlueValue] ← Shading.ExtractRGB[paintColor];
If [u,v] is outside the AIS image, do nothing.
IF uInch <= - artwork.halfWidth OR uInch >= artwork.halfWidth
OR vInch <= -artwork.halfHeight OR vInch >= artwork.halfHeight THEN RETURN;
redValue ← Real.Fix[realRedValue*255.0];
AIS.WriteSample[artwork.redWindow, redValue, Real.Fix[-vAIS + artwork.halfHeightDots], Real.Fix[uAIS + artwork.halfWidthDots]];
greenValue ← Real.Fix[realGreenValue*255.0];
AIS.WriteSample[artwork.greenWindow, greenValue, Real.Fix[-vAIS + artwork.halfHeightDots], Real.Fix[uAIS + artwork.halfWidthDots]];
blueValue ← Real.Fix[realBlueValue*255.0];
AIS.WriteSample[artwork.blueWindow, blueValue, Real.Fix[-vAIS + artwork.halfHeightDots], Real.Fix[uAIS + artwork.halfWidthDots]];
}
ELSE {-- artwork is a black and white file
blackValue: CARDINAL;
realBlackValue: REAL;
realBlackValue ← ImagerColorPrivate.IntensityFromColor[NARROW[paintColor]];
IF uInch <= - artwork.halfWidth OR uInch >= artwork.halfWidth
OR vInch <= -artwork.halfHeight OR vInch >= artwork.halfHeight
THEN RETURN;
blackValue ← Real.Fix[realBlackValue*255.0];
AIS.WriteSample[artwork.blackWindow, blackValue, Real.Fix[-vAIS + artwork.halfHeightDots], Real.Fix[uAIS + artwork.halfWidthDots]];
};
END;
}; -- end of SetImageColorAtPoint
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;
rayWorldSVRay.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.