GGMouseEventImplB.mesa
Copyright Ó 1986, 1987, 1988, 1989, 1992 by Xerox Corporation. All rights reserved.
Contents: Once a mouse event reaches the front of the slack-process queue, it is dispatched to one of the procedures in this module.
Kurlander July 17, 1986 2:40:45 pm PDT
Eisenman, August 7, 1987 1:41:04 pm PDT
Maureen Stone, October 2, 1987 3:57:53 pm PDT
Pier, October 2, 1992 12:37 pm PDT
Bier, July 5, 1993 1:17 pm PDT
Doug Wyatt, April 17, 1992 4:19 pm PDT
DIRECTORY
Atom, Basics, CodeTimer, ColorTool, Feedback, FeedbackTypes, GGAlign, GGBasicTypes, GGCaret, GGDescribe, GGDragTypes, GGEvent, GGHistory, GGHistoryTypes, GGInterfaceTypes, GGModelTypes, GGMouseEvent, GGMultiGravity, GGOutline, GGParent, GGRefresh, GGRefreshTypes, GGScene, GGSegment, GGSegmentTypes, GGSelect, GGSequence, GGSlice, GGSliceOps, GGState, GGTraj, GGUserInput, GGUtility, GGWindow, Imager, ImagerTransformation, InputFocus, IO, Menus, RealFns, Rope, Vectors2d;
GGMouseEventImplB:
CEDAR
PROGRAM
IMPORTS Basics, CodeTimer, ColorTool, Feedback, GGAlign, GGCaret, GGDescribe, GGEvent, GGHistory, GGMouseEvent, GGMultiGravity, GGOutline, GGParent, GGRefresh, GGScene, GGSegment, GGSelect, GGSequence, GGSlice, GGSliceOps, GGState, GGTraj, GGUserInput, GGUtility, GGWindow, ImagerTransformation, IO, Rope, Vectors2d
EXPORTS GGMouseEvent, GGInterfaceTypes = BEGIN
DragDataObj: PUBLIC TYPE = GGDragTypes.DragDataObj;
AlignBag: TYPE = GGInterfaceTypes.AlignBag;
AlignmentPoint: TYPE = GGInterfaceTypes.AlignmentPoint;
BoundBox: TYPE = GGModelTypes.BoundBox;
Caret: TYPE = GGInterfaceTypes.Caret;
Color: TYPE = Imager.Color;
DefaultData: TYPE = GGModelTypes.DefaultData;
ExtendMode: TYPE = GGInterfaceTypes.ExtendMode;
FeatureCycler: TYPE = GGInterfaceTypes.FeatureCycler;
FeatureData: TYPE = GGModelTypes.FeatureData;
GravityType: TYPE = GGInterfaceTypes.GravityType;
MsgRouter: TYPE = FeedbackTypes.MsgRouter;
GGData: TYPE = GGInterfaceTypes.GGData;
HistoryEvent: TYPE = GGHistoryTypes.HistoryEvent;
Joint: TYPE = GGModelTypes.Joint;
MouseButton: TYPE = Menus.MouseButton;
MouseProc: TYPE = GGMouseEvent.MouseProc;
Point: TYPE = GGBasicTypes.Point;
RefreshDataObj: PUBLIC TYPE = GGRefreshTypes.RefreshDataObj;
ROPE: TYPE = Rope.ROPE;
Scene: TYPE = GGModelTypes.Scene;
Segment: TYPE = GGSegmentTypes.Segment;
SelectMode: TYPE = GGModelTypes.SelectMode;
Sequence: TYPE = GGModelTypes.Sequence;
Slice: TYPE = GGModelTypes.Slice;
SliceClass: TYPE = GGModelTypes.SliceClass;
SliceDescriptor: TYPE = GGModelTypes.SliceDescriptor;
SliceDescriptorGenerator: TYPE = GGModelTypes.SliceDescriptorGenerator;
SliceGenerator: TYPE = GGModelTypes.SliceGenerator;
SliceParts: TYPE = GGModelTypes.SliceParts;
StartProc: TYPE = GGMouseEvent.StartProc;
TouchGroup: TYPE = GGSegmentTypes.TouchGroup;
Traj: TYPE = GGModelTypes.Traj;
TrajData: TYPE = GGModelTypes.TrajData;
TrajEnd: TYPE = GGModelTypes.TrajEnd;
TrajParts: TYPE = GGModelTypes.TrajParts;
TrajPartType: TYPE = GGModelTypes.TrajPartType;
TriggerBag: TYPE = GGAlign.TriggerBag;
UserInputProc: TYPE = GGUserInput.UserInputProc;
Vector: TYPE = GGBasicTypes.Vector;
Problem: SIGNAL [msg: Rope.ROPE] = Feedback.Problem;
SetCaretAttractorEndpoint:
PUBLIC
PROC [ggData: GGData, mapPoint: Point, normal: Vector, testPoint: Point, feature: FeatureData, hitData:
REF
ANY] = {
IF feature=
NIL
THEN GGCaret.SetAttractor[ggData.caret, mapPoint, normal,
NIL]
ELSE {
shape: REF ANY ¬ feature.shape;
SELECT feature.type
FROM
slice => {
pos: Point;
sliceD: SliceDescriptor ¬ NARROW[shape];
jointD: SliceDescriptor;
[jointD, pos, normal] ¬ GGSliceOps.ClosestJointToHitData[sliceD, mapPoint, testPoint, hitData];
IF Vectors2d.MagnitudeSquared[normal] = 0
THEN {
normal ¬ [0, -1];};
GGCaret.SetAttractor[ggData.caret, pos, normal, jointD];
};
ENDCASE => GGCaret.SetAttractor[ggData.caret, mapPoint, normal, NIL];
};
};
Special Behaviors
SetStrokeColorRemote:
PUBLIC
PROC [ggData: GGData, sliceD: SliceDescriptor]
RETURNS [doNormalBehavior:
BOOL ¬
FALSE, done:
BOOL ¬
FALSE] = {
theirData: GGData ¬ GGState.GetGGInputFocus[];
theirScene: Scene;
ourColor: Imager.Color;
success: BOOL ¬ FALSE;
IF theirData =
NIL
THEN {
Feedback.Append[ggData.router, oneLiner, $Complaint, "SetStrokeColorRemote failed: Place input focus in a Gargoyle viewer"];
RETURN;
}
ELSE IF theirData = ggData THEN {
Feedback.Append[ggData.router, oneLiner, $Complaint, "SetStrokeColorRemote failed: Place input focus in a non-palette Gargoyle viewer"];
RETURN;
}
ELSE theirScene ¬ theirData.scene;
[ourColor, success] ¬ GGSliceOps.GetStrokeColor[sliceD.slice, sliceD.parts];
IF success
THEN {
GGEvent.LineColorAux[theirData, ourColor, GGDescribe.ColorToRope[ourColor], TRUE, $Set];
SendColorToColorTool[ggData, ourColor];
}
ELSE {
Feedback.Append[ggData.router, oneLiner, $Complaint, "SetStrokeColorRemote: Select an object with a unique stroke color"];
};
};
SetFillColorRemote:
PUBLIC PROC [ggData: GGData, sliceD: SliceDescriptor]
RETURNS [doNormalBehavior:
BOOL ¬
FALSE, done:
BOOL ¬
FALSE] = {
theirData: GGData ¬ GGState.GetGGInputFocus[];
theirScene: Scene;
ourColor: Imager.Color;
success: BOOL ¬ FALSE;
IF theirData =
NIL
THEN {
Feedback.Append[ggData.router, oneLiner, $Complaint, "SetFillColorRemote failed: Place input focus in a Gargoyle viewer"];
RETURN;
}
ELSE IF theirData = ggData THEN {
Feedback.Append[ggData.router, oneLiner, $Complaint, "SetFillColorRemote failed: Place input focus in a non-palette Gargoyle viewer"];
RETURN;
}
ELSE theirScene ¬ theirData.scene;
[ourColor, success] ¬ GGSliceOps.GetFillColor[sliceD.slice, sliceD.parts];
IF success
THEN {
GGEvent.AreaColorAux[theirData, ourColor, GGDescribe.ColorToRope[ourColor], TRUE, $Set];
SendColorToColorTool[ggData, ourColor];
}
ELSE {
Feedback.Append[ggData.router, oneLiner, $Complaint, "SetFillColorRemote failed: Select an object with a unique fill color"];
};
};
LocalSymbols: TYPE = REF LocalSymbolsObj;
LocalSymbolsObj:
TYPE =
RECORD [
sliceD: SliceDescriptor,
rotation: ImagerTransformation.Transformation,
ggData: GGData,
currentEvent: HistoryEvent
];
SendColorToColorTool:
PROC [ggData: GGData, color: Color] = {
IF Basics.IsBound[ColorTool.SetColor]
THEN {
IF color=
NIL
THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "NIL fill color—not sent to ColorTool"]
ELSE {
noColorTool: BOOL ¬ FALSE;
ColorTool.SetColor[color,
NIL !
ColorTool.NoColorToolViewer => { noColorTool ¬ TRUE; CONTINUE }];
};
};
};
StartSelect:
PUBLIC StartProc = {
SELECT input.first
FROM
$StartSelectJoint => ggData.drag.selectState ¬ joint;
$StartSelectSegment => ggData.drag.selectState ¬ segment;
$StartSelectTrajectory => ggData.drag.selectState ¬ traj;
$StartSelectTopLevel => ggData.drag.selectState ¬ topLevel;
ENDCASE => ERROR;
IF NOT GGRefresh.EmptyOverlay[ggData] THEN ERROR;
GGMouseEvent.SaveSavedState[ggData];
DuringSelect[NIL, ggData, worldPt];
};
DuringSelect:
PUBLIC MouseProc = {
While a joint, segment, traj, or top level object is being selected, gravity is forced to be LinesPreferred. The object bag should consist only of trajectories and slices. The caret is moved to the segment endpoint of the nearest segment or traj as appropriate or tracks the cursor if none are nearby. Feedback is in the form of highlighted joints.
opRope: Rope.ROPE = "Selecting";
resultPoint: Point;
normal: Vector;
feature: FeatureData;
hitData: REF ANY;
CodeTimer.StartInt[$DuringSelect, $Gargoyle];
GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, caret: TRUE, selectedCPs: TRUE, attractor: TRUE];
SELECT GGState.GetSelectMode[ggData]
FROM
joint => [resultPoint, normal, feature, hitData] ¬ GGMultiGravity.PointsPreferred[worldPt, ggData.hitTest.t, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData, FALSE];
segment, segmentRange => [resultPoint, normal, feature, hitData] ¬ GGMultiGravity.LinesPreferred[worldPt, ggData.hitTest.t, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData];
traj, topLevel, slice => [resultPoint, normal, feature, hitData] ¬ GGMultiGravity.FacesPreferred[worldPt, ggData.hitTest.t, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData];
none => {
resultPoint ¬ worldPt;
normal ¬ [0, -1];
feature ¬ NIL;
hitData ¬ NIL;
};
ENDCASE => ERROR;
GGMouseEvent.SetCaretAttractorEndpoint[ggData, resultPoint, normal, worldPt, feature, hitData];
GGSelect.DeselectAll[ggData.scene, normal];
IF feature#NIL THEN [] ¬ SelectAndDescribeSlicePart[feature, hitData, ggData, ggData.drag.selectState, opRope] ELSE Feedback.PutF[ggData.router, oneLiner, $DuringMouse, "%g nothing", [rope[opRope]]];
GGWindow.RestoreScreenAndInvariants[paintAction: $DuringSelect, ggData: ggData, remake: none, edited: FALSE, selectionChanged: TRUE, okToSkipCapture: TRUE];
CodeTimer.StopInt[$DuringSelect, $Gargoyle];
}; -- end DuringSelect
EndSelect:
PUBLIC MouseProc = {
resultPoint: Point;
normal: Vector;
feature: FeatureData;
hitData: REF ANY;
featureCycler: GGInterfaceTypes.FeatureCycler;
SELECT GGState.GetSelectMode[ggData]
FROM
joint => featureCycler ¬ GGMultiGravity.PointsPreferredCycler[worldPt, ggData.hitTest.t, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData, FALSE];
segment => featureCycler ¬ GGMultiGravity.LinesPreferredCycler[worldPt, ggData.hitTest.t, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData];
ENDCASE => featureCycler ¬ GGMultiGravity.FacesPreferredCycler[worldPt, ggData.hitTest.t, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData];
GGState.SetSelectionCycler[ggData, featureCycler];
[resultPoint, normal, feature, hitData] ¬ GGMultiGravity.FirstFeature[featureCycler];
Put Caret on a joint (if any).
GGMouseEvent.SetCaretAttractorEndpoint[ggData, resultPoint, normal, worldPt, feature, hitData];
GGWindow.NewCaretPos[ggData];
Deselect all.
GGSelect.DeselectAll[ggData.scene, normal];
Dispatch to the proper EndSelect handler for final selection.
EndSelectAux[ggData, resultPoint, feature, hitData, ggData.drag.selectState, RopeFromSelectMode[ggData.drag.selectState]];
SELECT ggData.drag.selectState FROM
joint => EndSelectAux[ggData, resultPoint, feature, hitData, joint, "joint"];
segment => EndSelectAux[ggData, resultPoint, feature, hitData, segment, "segment"];
traj => EndSelectAux[ggData, resultPoint, feature, hitData, traj, "trajectory"];
topLevel => EndSelectAux[ggData, resultPoint, feature, hitData, topLevel, "object"];
ENDCASE => ERROR;
GGWindow.RestoreScreenAndInvariants[paintAction: $None, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: FALSE]; -- The Start and During will already have updated the screen properly
}; -- end EndSelect
SelectModeFromAtom:
PROC [atom:
ATOM]
RETURNS [selectMode: SelectMode] = {
selectMode ¬
SELECT atom
FROM
$Joint => joint,
$Segment => segment,
$Traj => traj,
ENDCASE => topLevel;
};
RopeFromSelectMode:
PROC [selectMode: SelectMode]
RETURNS [rope:
ROPE] = {
rope ¬
SELECT selectMode
FROM
joint => "joint",
segment => "segment",
traj => "trajectory",
ENDCASE => "object";
};
<<Select: GGUserInput.UserInputProc = {
PROC [ggData: GGData, event: LIST OF REF ANY];
resultPoint: Point;
normal: Vector;
feature: FeatureData;
hitData: REF ANY;
featureCycler: GGInterfaceTypes.FeatureCycler;
worldPt: Point;
selectMode: SelectMode ¬ topLevel;
GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, caret: TRUE, selectedCPs: TRUE, attractor: TRUE];
IF event #
NIL
AND event.rest #
NIL
THEN {
WITH event.rest.first
SELECT
FROM
p: REF Point => worldPt ¬ p^;
ENDCASE => worldPt ¬ [0,0];
IF event.rest.rest #
NIL
THEN {
WITH event.rest.rest.first
SELECT
FROM
a: ATOM => selectMode ¬ SelectModeFromAtom[a];
ENDCASE => selectMode ¬ topLevel;
};
};
SELECT selectMode
FROM
joint => featureCycler ¬ GGMultiGravity.PointsPreferredCycler[worldPt, ggData.hitTest.t, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData, FALSE];
segment => featureCycler ¬ GGMultiGravity.LinesPreferredCycler[worldPt, ggData.hitTest.t, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData];
ENDCASE => featureCycler ¬ GGMultiGravity.FacesPreferredCycler[worldPt, ggData.hitTest.t, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData];
GGState.SetSelectionCycler[ggData, featureCycler];
[resultPoint, normal, feature, hitData] ¬ GGMultiGravity.FirstFeature[featureCycler];
GGMouseEvent.SetCaretAttractorEndpoint[ggData, resultPoint, normal, worldPt, feature, hitData];
GGWindow.NewCaretPos[ggData];
GGSelect.DeselectAll[ggData.scene, normal];
EndSelectAux[ggData, resultPoint, feature, hitData, selectMode, RopeFromSelectMode[selectMode]];
GGWindow.RestoreScreenAndInvariants[paintAction: $DuringSelect, ggData: ggData, remake: none, edited: FALSE, selectionChanged: TRUE, okToSkipCapture: FALSE]; -- The Start and During will already have updated the screen properly
}; -- Select
>>
Select: GGUserInput.UserInputProc = {
PROC [ggData: GGData, event: LIST OF REF ANY];
event: LIST[$Select, worldPt: REF Point, selectMode: {$Joint, $Segment, $Traj, $TopLevel}, gravType: {$PreferPoints, $PreferLines, $PreferFaces}, gravExtent: REF REAL]
resultPoint: Point;
normal: Vector;
feature: FeatureData;
hitData: REF ANY;
featureCycler: GGInterfaceTypes.FeatureCycler;
worldPt: Point ¬ [0,0];
selectMode: SelectMode ¬ topLevel;
ggData.hitTest.t is the gravity extent
ggData.hitTest.gravityType is the gravity type
gravExtent: REAL ¬ ggData.hitTest.t;
gravType: GravityType ¬ ggData.hitTest.gravityType;
gravTypeSpecified: BOOL ¬ FALSE;
listIndex: NAT ¬ 0;
FOR loe:
LIST
OF
REF ¬ event, loe.rest
UNTIL loe=
NIL
DO
el: REF ← loe.first;
SELECT listIndex
FROM
0 => IF NARROW[el, ATOM]#$Select THEN ERROR;
1 => {
WITH el SELECT FROM
p:
REF Point => worldPt ¬ p^;
ENDCASE => worldPt ¬ [0,0];
};
2 => {
WITH el
SELECT
FROM
a: ATOM => selectMode ¬ SelectModeFromAtom[a];
ENDCASE => selectMode ¬ topLevel;
};
3 => {
WITH el
SELECT
FROM
g: ATOM => {gravType ¬ GravTypeFromAtom[g]; gravTypeSpecified¬ TRUE;};
ENDCASE => NULL; -- don't change type
};
4 => {
WITH el
SELECT
FROM
r: REF REAL => gravExtent ¬ r^;
ENDCASE => NULL; -- don't change extent
};
ENDCASE => EXIT;
listIndex ¬ listIndex+1;
ENDLOOP;
GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, caret: TRUE, selectedCPs: TRUE, attractor: TRUE];
IF gravTypeSpecified
THEN
SELECT gravType
FROM
pointsPreferred => featureCycler ¬ GGMultiGravity.PointsPreferredCycler[worldPt, gravExtent, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData, FALSE];
linesPreferred => featureCycler ¬ GGMultiGravity.LinesPreferredCycler[worldPt, gravExtent, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData];
ENDCASE => featureCycler ¬ GGMultiGravity.FacesPreferredCycler[worldPt, gravExtent, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData]
ELSE
SELECT selectMode
FROM
joint => featureCycler ¬ GGMultiGravity.PointsPreferredCycler[worldPt, gravExtent, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData, FALSE];
segment => featureCycler ¬ GGMultiGravity.LinesPreferredCycler[worldPt, gravExtent, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData];
ENDCASE => featureCycler ¬ GGMultiGravity.FacesPreferredCycler[worldPt, gravExtent, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData];
GGState.SetSelectionCycler[ggData, featureCycler];
[resultPoint, normal, feature, hitData] ¬ GGMultiGravity.FirstFeature[featureCycler];
GGMouseEvent.SetCaretAttractorEndpoint[ggData, resultPoint, normal, worldPt, feature, hitData];
GGWindow.NewCaretPos[ggData];
GGSelect.DeselectAll[ggData.scene, normal];
EndSelectAux[ggData, resultPoint, feature, hitData, selectMode, RopeFromSelectMode[selectMode]];
GGWindow.RestoreScreenAndInvariants[paintAction: $DuringSelect, ggData: ggData, remake: none, edited: FALSE, selectionChanged: TRUE, okToSkipCapture: FALSE]; -- The Start and During will already have updated the screen properly
}; -- Select
GravTypeFromAtom:
PROC [atom:
ATOM]
RETURNS [gravType: GravityType] = {
gravType ¬
SELECT atom
FROM
$PreferLines => linesPreferred,
$PreferPoints => pointsPreferred,
$PreferFaces => facesPreferred,
ENDCASE => facesPreferred;
};
SelectFromFeature:
PUBLIC
PROC [ggData: GGData, testPoint: Point, point: Point, normal: Vector, feature: FeatureData, hitData:
REF
ANY] = {
This routine is used, for instance, for CycleSelection
selectMode: SelectMode ¬ GGState.GetSelectMode[ggData];
IF selectMode = none THEN GOTO NoSelectMode;
GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, caret: TRUE, selectedParts: TRUE, selectedCPs: TRUE, attractor: TRUE];
SetCaretAttractorEndpoint[ggData, point, normal, point, feature, hitData];
GGWindow.NewCaretPos[ggData];
GGSelect.DeselectAll[ggData.scene, normal];
SELECT selectMode
FROM
joint => EndSelectAux[ggData, point, feature, hitData, joint, "joint"];
segment => EndSelectAux[ggData, point, feature, hitData, segment, "segment"];
traj => EndSelectAux[ggData, point, feature, hitData, traj, "trajectory"];
topLevel => EndSelectAux[ggData, point, feature, hitData, topLevel, "object"];
ENDCASE => ERROR;
GGWindow.RestoreScreenAndInvariants[paintAction: $EndSelect, ggData: ggData, remake: none, edited: FALSE, selectionChanged: TRUE, okToSkipCapture: FALSE];
EXITS
NoSelectMode => {
Feedback.Append[ggData.router, oneLiner, $Complaint, "SelectFromFeature failed: unknown selection grain. Make a selection first."];
};
};
EndSelectAux:
PROC [ggData: GGData, resultPoint: Point, feature: FeatureData, hitData:
REF
ANY, mode: SelectMode, opName: Rope.
ROPE] = {
Prepare for a subsequent Add operation and for Extend.
IF feature =
NIL
THEN {
GGCaret.SitOn[ggData.caret, NIL];
Feedback.PutF[ggData.router, oneLiner, $Feedback, "No near %g found.", [rope[opName]]];
}
ELSE {
Make Selection and Prepare for Extend.
SELECT mode
FROM
joint => GGState.SetExtendMode[ggData, joint];
segment => GGState.SetExtendMode[ggData, segmentRange];
traj => GGState.SetExtendMode[ggData, traj];
topLevel => GGState.SetExtendMode[ggData, topLevel];
ENDCASE => ERROR;
BEGIN
sliceD: SliceDescriptor ¬ SelectSlicePart[feature, hitData, ggData, mode];
featureCycler: FeatureCycler ¬ GGState.GetSelectionCycler[ggData];
cycleCount: NAT ¬ featureCycler.count;
sliceType: ATOM ¬ GGSliceOps.GetType[sliceD.slice];
SitTheCaret[ggData.caret, sliceD, hitData, mode];
GGState.SetSliceToExtend[ggData, sliceD];
IF cycleCount > 1 THEN Feedback.PutF[ggData.router, oneLiner, $Feedback, Rope.Concat[GGSliceOps.Describe[sliceD], " selected (%g more)"], [integer[cycleCount-1]]]
ELSE Feedback.Append[ggData.router, oneLiner, $Feedback, Rope.Concat[GGSliceOps.Describe[sliceD], " selected"]];
END;
};
}; -- end EndSelectAux
SelectAndDescribeSlicePart:
PROC [feature: FeatureData, hitData:
REF
ANY, ggData: GGData, mode: SelectMode, opRope: Rope.
ROPE]
RETURNS [selectedD: SliceDescriptor]= {
selectedD ¬ SelectSlicePart[feature, hitData, ggData, mode];
Feedback.PutFL[ggData.router, oneLiner, $Feedback, "%g %g", LIST[[rope[opRope]], [rope[GGSliceOps.Describe[selectedD]]]] ];
};
SelectSlicePart:
PUBLIC
PROC [feature: FeatureData, hitData:
REF
ANY, ggData: GGData, mode: SelectMode]
RETURNS [selectedD: SliceDescriptor] = {
Find out which point is being selected and highlight it.
SELECT feature.type
FROM
slice => {
slice: Slice ¬ NARROW[feature.shape, SliceDescriptor].slice;
selectedD ¬ GGSliceOps.NewParts[slice, hitData, mode];
GGSelect.SelectSlice[selectedD, ggData.scene, normal];
};
slopeLine, angleLine, distanceLine, intersectionPoint, radiiCircle, midpoint => ERROR;
ENDCASE => ERROR Problem[msg: "unimplemented result type"];
};
SitTheCaret:
PROC [caret: Caret, sliceD: SliceDescriptor, hitData:
REF
ANY, mode: SelectMode] = {
The only use for the chair is the StartAdd command, which is a favor to the $Traj class. Thus, we only sit on $Outline and $Traj slices, or $Cluster slices that contain such slices.
childD: SliceDescriptor ¬ NIL;
IF GGParent.IsParent[sliceD.slice]
THEN {
childD ¬ GGParent.FirstIncludedChild[sliceD.slice, sliceD.parts, leaf, $Traj];
};
IF childD = NIL THEN GGCaret.SitOn[caret, NIL]
ELSE {
SELECT mode
FROM
joint => {
GGCaret.SitOn[caret, sliceD];
};
segment, traj, topLevel => {
jointD: SliceDescriptor;
jointD ¬ GGSliceOps.ClosestJointToHitData[sliceD, caret.point, caret.point, hitData].jointD;
GGCaret.SitOn[caret, jointD];
};
ENDCASE => ERROR;
};
};
StartDeselect:
PUBLIC StartProc = {
SELECT input.first
FROM
$StartDeselectJoint => ggData.drag.selectState ¬ joint;
$StartDeselectSegment => ggData.drag.selectState ¬ segment;
$StartDeselectTrajectory => ggData.drag.selectState ¬ traj;
$StartDeselectTopLevel => ggData.drag.selectState ¬ topLevel;
ENDCASE => ERROR;
IF NOT GGRefresh.EmptyOverlay[ggData] THEN ERROR; -- nothing on overlay
GGMouseEvent.SaveSavedState[ggData];
ggData.drag.transform ¬ ImagerTransformation.Scale[1.0]; -- needed for DuringDeselect to work properly
DuringDeselect[NIL, ggData, worldPt];
};
DuringDeselect:
PUBLIC MouseProc = {
While a joint, cp, segment, traj, or top level object is being deselected, gravity is forced to be LinesPreferred. The object bag should consist only of trajectories and slices. The caret is moved to the segment endpoint of the nearest segment or traj as appropriate or tracks the cursor if none are nearby. Feedback is in the form of removing highlighted joints or cps.
opRope: Rope.ROPE = "Deselecting";
resultPoint: Point;
normal: Vector;
feature: FeatureData;
hitData: REF ANY;
ggData.drag.currentPoint ¬ worldPt;
GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, caret: TRUE, selectedParts: TRUE, selectedCPs: TRUE, attractor: TRUE];
SELECT GGState.GetSelectMode[ggData]
FROM
joint => [resultPoint, normal, feature, hitData] ¬ GGMultiGravity.PointsPreferred[worldPt, ggData.hitTest.t, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData, FALSE];
segment => [resultPoint, normal, feature, hitData] ¬ GGMultiGravity.LinesPreferred[worldPt, ggData.hitTest.t, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData];
ENDCASE => [resultPoint, normal, feature, hitData] ¬ GGMultiGravity.LinesPreferred[worldPt, ggData.hitTest.t, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData];
Reselect all.
GGScene.RestoreSelections[ggData.scene];
IF feature#NIL THEN DuringDeselectAux[feature, hitData, resultPoint, ggData, ggData.drag.selectState, opRope] ELSE Feedback.PutF[ggData.router, oneLiner, $DuringMouse, "%g nothing", [rope[opRope]]];
GGWindow.RestoreScreenAndInvariants[paintAction: $DuringSelect, ggData: ggData, remake: none, edited: FALSE, selectionChanged: TRUE, okToSkipCapture: TRUE];
}; -- end DuringDeselect
DuringDeselectAux:
PROC [feature: FeatureData, hitData:
REF
ANY, caretPt: Point, ggData: GGData, mode: GGModelTypes.SelectMode, opRope: Rope.
ROPE] = {
description: Rope.ROPE;
SELECT feature.type
FROM
slice => {
deselectedD: SliceDescriptor ¬ SliceDeselectAux[feature, hitData, mode, ggData.scene];
description ¬ IF deselectedD=NIL THEN "nothing" ELSE GGSliceOps.Describe[deselectedD];
Feedback.PutFL[ggData.router, oneLiner, $DuringMouse, "%g %g", LIST[[rope[opRope]], [rope[description]]] ];
};
slopeLine, angleLine, distanceLine, intersectionPoint, radiiCircle, midpoint => ERROR;
ENDCASE => ERROR;
};
SliceDeselectAux:
PROC [feature: FeatureData, hitData:
REF
ANY, mode: GGModelTypes.SelectMode, scene: Scene]
RETURNS [deselectedD: SliceDescriptor] = {
shapeD: SliceDescriptor ¬ NARROW[feature.shape];
sliceD: SliceDescriptor ¬ GGSelect.FindSelectedSlice[shapeD.slice, normal];
IF sliceD#
NIL
THEN {
newD: SliceDescriptor ¬ GGSliceOps.NewParts[sliceD.slice, hitData, mode];
diffD: SliceDescriptor ¬ GGSliceOps.DifferenceParts[sliceD, newD];
GGSelect.DeselectSlice[sliceD.slice, sliceD.parts, scene, normal];
IF NOT GGSliceOps.IsEmptyParts[diffD] THEN GGSelect.SelectSlice[diffD, scene, normal];
deselectedD ¬ newD;
}
ELSE deselectedD ¬ shapeD; -- must have already deselected the slice DuringDeselect
};
EndDeselect:
PUBLIC MouseProc = {
resultPoint: Point;
normal: Vector;
feature: FeatureData;
hitData: REF ANY;
SELECT GGState.GetSelectMode[ggData]
FROM
joint => [resultPoint, normal, feature, hitData] ¬ GGMultiGravity.PointsPreferred[worldPt, ggData.hitTest.t, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData, FALSE];
segment => [resultPoint, normal, feature, hitData] ¬ GGMultiGravity.LinesPreferred[worldPt, ggData.hitTest.t, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData];
ENDCASE => [resultPoint, normal, feature, hitData] ¬ GGMultiGravity.LinesPreferred[worldPt, ggData.hitTest.t, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData];
Clear the overlay plane.
GGRefresh.MoveOverlayToBackground[ggData]; -- WHY? KAP. April 17, 1992
And Dispatch to the proper EndDeselect handler.
SELECT ggData.drag.selectState
FROM
joint => EndDeselectAux[ggData, resultPoint, feature, hitData, joint, "joint"];
segment => EndDeselectAux[ggData, resultPoint, feature, hitData, segment, "segment"];
traj => EndDeselectAux[ggData, resultPoint, feature, hitData, traj, "trajectory"];
topLevel => EndDeselectAux[ggData, resultPoint, feature, hitData, topLevel, "top level object"];
ENDCASE => ERROR;
};
EndDeselectAux:
PROC [ggData: GGData, resultPoint: Point, feature: FeatureData, hitData:
REF
ANY, mode: SelectMode, selectRope: Rope.
ROPE] = {
GGCaret.SitOn[ggData.caret, NIL];
IF feature =
NIL
THEN {
Feedback.PutF[ggData.router, oneLiner, $Feedback, "No near %g found", [rope[selectRope]]];
}
ELSE {
SELECT feature.type
FROM
slice => {
gone: SliceDescriptor ¬ SliceDeselectAux[feature, hitData, mode, ggData.scene];
IF gone#NIL AND gone.parts#NIL THEN Feedback.PutFL[ggData.router, oneLiner, $Feedback, "%g %g", LIST[[rope[GGSliceOps.Describe[gone]]], [rope["deselected"]]]];
};
ENDCASE => SIGNAL Problem [msg: IO.PutFR1["Unexpected feature type for deselect %g.", [rope[selectRope]]]];
};
GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, ggData: ggData, remake: none, edited: FALSE, selectionChanged: TRUE, okToSkipCapture: FALSE];
ggData.drag.selectState ¬ none; -- added to help DescribeFeature work. KAP.
};
StartExtend
Select:
PUBLIC StartProc = {
SELECT input.first
FROM
$StartExtSelectJoint => GGState.SetExtendMode[ggData, joint];
$StartExtSelectSegment => GGState.SetExtendMode[ggData, segment];
$StartExtSelectTrajectory => GGState.SetExtendMode[ggData, traj];
$StartExtSelectTopLevel => GGState.SetExtendMode[ggData, topLevel];
ENDCASE => ERROR;
RETURN StartExtendSelection[input, ggData, worldPt];
};
StartExtendSelection:
PUBLIC StartProc = {
IF NOT GGRefresh.EmptyOverlay[ggData] THEN ERROR;
GGMouseEvent.SaveSavedState[ggData]; -- must do this before any possible aborts occur
ggData.drag.currentPoint ¬ worldPt;
ggData.drag.transform ¬ ImagerTransformation.Scale[1.0];
DuringExtendSelection[LIST[$StartExtendSelection], ggData, worldPt];
};
DuringExtendSelection:
PUBLIC MouseProc= {
opRope: Rope.ROPE = IF input.first=$EndExtendSelection THEN "Extended to" ELSE "Extending to";
resultPoint: Point;
normal: Vector;
feature: FeatureData;
hitData: REF ANY;
extendMode: ExtendMode ¬ GGState.GetExtendMode[ggData];
GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, caret: TRUE, selectedCPs: TRUE, attractor: TRUE];
SELECT extendMode
FROM
joint => [resultPoint, normal, feature, hitData] ¬ GGMultiGravity.PointsPreferred[worldPt, ggData.hitTest.t, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData, FALSE];
segment, segmentRange => [resultPoint, normal, feature, hitData] ¬ GGMultiGravity.LinesPreferred[worldPt, ggData.hitTest.t, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData];
traj, topLevel, slice => [resultPoint, normal, feature, hitData] ¬ GGMultiGravity.FacesPreferred[worldPt, ggData.hitTest.t, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData];
none => {
resultPoint ¬ worldPt;
normal ¬ [0, -1];
feature ¬ NIL;
hitData ¬ NIL;
};
ENDCASE => ERROR;
Put Caret on a joint (if any).
SetCaretAttractorEndpoint[ggData, resultPoint, normal, worldPt, feature, hitData];
Reselect all.
GGSelect.DeselectAll[ggData.scene, normal];
GGScene.RestoreSelections[ggData.scene];
IF feature#
NIL
THEN {
SELECT extendMode
FROM
joint, segment, segmentRange, traj, topLevel => DuringExtendSelectionFeedback[feature, hitData, resultPoint, ggData, opRope];
slice, none => NULL; -- NoOp
ENDCASE => ERROR;
}
ELSE Feedback.PutF[ggData.router, oneLiner, $DuringMouse, "%g nothing", [rope[opRope]]];
GGWindow.RestoreScreenAndInvariants[paintAction: $DuringSelect, ggData: ggData, remake: none, edited: FALSE, selectionChanged: TRUE, okToSkipCapture: TRUE];
}; -- end DuringExtendSelection
DuringExtendSelectionFeedback:
PROC [feature: FeatureData, hitData:
REF
ANY, caretPt: Point, ggData: GGData, opRope: Rope.
ROPE] = {
depending on extend mode, check if feature is in that mode, then select the new feature.
extendMode: ExtendMode ¬ GGState.GetExtendMode[ggData];
SELECT extendMode
FROM
joint, segment, traj, topLevel => [] ¬ SelectAndDescribeSlicePart[feature, hitData, ggData, extendMode, opRope];
segmentRange => {
IF
ISTYPE[feature.shape, SliceDescriptor]
THEN {
sliceD: SliceDescriptor ¬ NARROW[feature.shape]; -- clumsy code
traj: Traj;
trajToExtend: SliceDescriptor;
hitType: GGModelTypes.TrajPartType;
segNum, cpNum, jointNum, segToExtendNum: INT;
hitPoint: Point;
SELECT GGSliceOps.GetType[sliceD.slice]
FROM
$Outline => [traj, hitType, segNum, cpNum, jointNum, hitPoint] ¬ GGOutline.UnpackHitData[hitData];
$Traj => {
traj ¬ sliceD.slice;
[hitType, segNum, cpNum, jointNum, hitPoint] ¬ GGTraj.UnpackHitData[hitData];
};
ENDCASE => GOTO RegularSelectMechanism;
IF (hitType = segment
OR hitType = controlPoint)
THEN {
[trajToExtend, segToExtendNum] ¬ GGOutline.UnpackOneSegmentDescriptor[GGState.GetSliceToExtend[ggData]];
IF trajToExtend#
NIL
AND trajToExtend.slice = traj
THEN {
-- we're extending segments in the same trajectory
seq: TrajParts ¬ GGSequence.CreateFromSegments[NARROW[traj.data], segToExtendNum, segNum];
GGSelect.SelectSlice[GGSlice.DescriptorFromParts[traj, seq], ggData.scene, normal]; -- select current hit
Feedback.PutFL[ggData.router, oneLiner, $DuringMouse, "%g %g", LIST[[rope[opRope]], [rope[GGUtility.DescribeSegment[traj, segNum] ]]] ];
}
ELSE {
-- you extended to a different traj
GGState.SetSliceToExtend[ggData, SelectAndDescribeSlicePart[feature, hitData, ggData, segment, opRope]];
}
}
ELSE GOTO RegularSelectMechanism;
}
ELSE GOTO RegularSelectMechanism;
EXITS
RegularSelectMechanism => [] ¬ SelectAndDescribeSlicePart[feature, hitData, ggData, segment, opRope];
};
ENDCASE => ERROR; -- should have been weeded about before calling this Proc
};
End
ExtendSelection:
PUBLIC MouseProc = {
GGCaret.DoNotSit[ggData.caret]; -- simple but not always correct thing to do
simply paint new selections directly
DuringExtendSelection[LIST[$EndExtendSelection], ggData, worldPt];
GGWindow.RestoreScreenAndInvariants[paintAction: $None, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: FALSE]; -- DuringExtendSelection did the final repaint
}; -- end EndExtendSelection
Bezier Stuff
StartAddBezier:
PUBLIC StartProc = {
If we have the EndPoint of a Bezier Selected then set up the conditions to drag out a new Bezier in its trajectory. Otherwise we will drag a single tangent for what will become a Bezier.
IF GGCaret.Exists[ggData.caret]
THEN {
caret: Caret;
oldTraj, newTraj: Traj;
ancestor, oldOutline: Slice;
trajEnd: TrajEnd;
newNormalD, newHotD: SliceDescriptor;
startBox: BoundBox ← GGRefresh.GetABox[ggData: ggData, dragInProgress: TRUE, selectedCPs: TRUE, into: NIL]; -- because UpdateSceneForAddBezier will DeselectAll
GGHistory.NewCapture["Add Bezier spline", ggData]; -- capture scene BEFORE UPDATE
caret ¬ ggData.caret;
GGMouseEvent.SaveSavedState[ggData]; -- used for abort
[oldTraj, newTraj, trajEnd, ancestor] ¬ UpdateSceneForAddBezier[ggData.scene, worldPt, caret, ggData.defaults, ggData];
oldOutline ¬ IF oldTraj=NIL THEN NIL ELSE GGParent.GetTopLevelAncestor[oldTraj];
[newNormalD, newHotD] ¬ GGMouseEvent.UpdateSelectionsForAdd[ggData.scene, oldTraj, newTraj, trajEnd];
GGMouseEvent.UpdateCaretForAdd[caret, ancestor, newNormalD, worldPt];
[] ¬ GGAlign.UpdateBagsForAdd[oldOutline, newNormalD, trajEnd, ggData]; -- should be ok
We have added the NEW segment and the bags are correct and static.
[] ¬ GGMouseEvent.StartMotion[ggData: ggData, opName: "add", bagType: $Drag, worldPt: worldPt, saveState: FALSE, needAnchor: FALSE, backgroundOK: TRUE, startBox: startBox]; -- boundbox may be wrong
DuringBezierDrag[NIL, ggData, worldPt]; -- draw it the first time
}
ELSE Feedback.Append[ggData.router, oneLiner, $Complaint, "Start Bezier failed: caret required for adding new Bezier"];
};
UpdateSceneForAddBezier:
PROC [scene: Scene, worldPt: Point, caret: Caret, defaults: DefaultData, ggData: GGData]
RETURNS [oldTraj, newTraj: Traj, trajEnd: TrajEnd, newOutline: Slice] = {
caretPoint, newCaretPoint: Point;
jointNum: NAT;
newSeg, extendFromSeg, newBezier: Segment;
chair: SliceDescriptor;
thisTraj: SliceDescriptor;
thisTrajParts: TrajParts;
thisTrajData: TrajData;
thisOutlineSlice: Slice;
addToBezier: BOOL ¬ FALSE;
Some how must look through all selected stuff and see if I have a selected Bezier endjount. If so then addToBezier is set to true.
sliceDesc: SliceDescriptor ¬ GGScene.FirstSelectedSlice[scene, leaf, normal, $Outline];
caretPoint ¬ GGCaret.GetPoint[caret];
newCaretPoint ¬ GGMouseEvent.DragTheCaret[worldPt, ggData, "Dragging:"];
IF sliceDesc #
NIL
THEN {
-- otherwise nothing selected.
thisOutlineSlice ¬ sliceDesc.slice;
thisTraj ¬ GGParent.FirstIncludedChild[sliceDesc.slice, sliceDesc.parts, first, $Traj];
thisTrajParts ¬ NARROW[thisTraj.parts];
thisTrajData ¬ NARROW[thisTraj.slice.data];
IF thisTrajData.role = open
AND GGSequence.ContainsJoint[thisTrajParts, 0]
THEN {
IF GGTraj.FetchSegment[thisTraj.slice, 0].class.type = $Bezier
THEN {
trajEnd ¬ lo;
addToBezier ¬ TRUE;
};
}
ELSE {
IF thisTrajData.role = open
AND GGSequence.ContainsJoint[thisTrajParts, GGTraj.HiJoint[thisTraj.slice]]
THEN {
IF GGTraj.FetchSegment[thisTraj.slice, GGTraj.HiSegment[thisTraj.slice]].class.type = $Bezier
THEN {
trajEnd ¬ hi;
addToBezier ¬ TRUE;
};
};
};
};
IF addToBezier
AND ggData.drag.editConstraints # none
THEN {
-- Lay down a new Bezier and a tangent guide.
oldCtrlPt, newCtrlPt: Point;
oldTraj ¬ newTraj ¬ thisTraj.slice; -- the traj that the Bezier belongs to.
extendFromSeg ¬ GGTraj.FetchSegment[newTraj, IF trajEnd=lo THEN 0 ELSE GGTraj.HiSegment[newTraj]];
IF trajEnd = lo
THEN {
oldCtrlPt ¬ extendFromSeg.class.controlPointGet[extendFromSeg, 0];
newCtrlPt ¬ Vectors2d.Add[extendFromSeg.lo, Vectors2d.VectorFromPoints[oldCtrlPt, extendFromSeg.lo]];
newBezier ¬ GGSegment.MakeBezier[extendFromSeg.lo, newCtrlPt, newCaretPoint, newCaretPoint, NIL]; -- by the lo in AddSegment.
}
ELSE {
oldCtrlPt ¬ extendFromSeg.class.controlPointGet[extendFromSeg, 1];
newCtrlPt ¬ Vectors2d.Add[extendFromSeg.hi, Vectors2d.VectorFromPoints[oldCtrlPt, extendFromSeg.hi]];
newBezier ¬ GGSegment.MakeBezier[extendFromSeg.hi, newCtrlPt, newCaretPoint, newCaretPoint, NIL];
};
newSeg ¬ GGSegment.MakeLine[newCaretPoint, worldPt, NIL]; -- ctrl tangent.
GGSegment.SetDefaults[newSeg, defaults];
GGSegment.CopyLooks[extendFromSeg, newBezier];
newSeg.strokeWidth ¬ 1;
[] ¬ GGTraj.AddSegment[newTraj, trajEnd, newBezier, lo];
[] ¬ GGTraj.AddSegment[newTraj, trajEnd, newSeg, lo];
ggData.drag.bezierDrag.draggingBezier ¬ TRUE;
ggData.drag.bezierDrag.trajEnd ¬ trajEnd;
ggData.drag.bezierDrag.bezierNum ¬ IF trajEnd=lo THEN 1 ELSE GGTraj.HiSegment[newTraj] - 1;
ggData.drag.bezierDrag.traj ¬ newTraj;
ggData.drag.bezierDrag.outlineSlice ¬ thisOutlineSlice;
newOutline ¬ thisOutlineSlice; -- that the Bezier was in.
}
ELSE {
-- We are not adding to a Bezier in constrained mode, so can only add first tangent guide.
newSeg ¬ GGSegment.MakeLine[caretPoint, worldPt, NIL]; -- maybe drag caret first!!!?
GGSegment.SetDefaults[newSeg, defaults];
newSeg.strokeWidth ¬ 1;
IF GGCaret.SittingOnEnd[caret]
THEN {
-- Extending a Traj
[chair, newTraj, jointNum] ¬ GGMouseEvent.SafelyGetCaretTraj[caret];
oldTraj ¬ newTraj;
trajEnd ¬
SELECT jointNum
FROM
0 => lo,
GGTraj.HiJoint[newTraj] => hi,
ENDCASE => ERROR;
extendFromSeg ¬ GGTraj.FetchSegment[newTraj, IF trajEnd=lo THEN 0 ELSE GGTraj.HiSegment[newTraj]];
[] ¬ GGTraj.AddSegment[newTraj, trajEnd, newSeg, lo];
newOutline ¬ chair.slice;
}
ELSE {
-- create a new traj.
oldTraj ¬ NIL;
trajEnd ¬ hi;
newTraj ¬ GGTraj.CreateTraj[caretPoint];
GGSliceOps.SetStrokeJoint[newTraj, NIL, defaults.strokeJoint, NIL];
[] ¬ GGTraj.AddSegment[newTraj, trajEnd, newSeg, lo];
newOutline ¬ GGOutline.CreateOutline[newTraj, defaults.fillColor];
GGScene.AddSlice[scene, newOutline, -1];
};
ggData.drag.bezierDrag.trajEnd ¬ trajEnd; -- here used to track tangent.
ggData.drag.bezierDrag.bezierNum ¬ IF trajEnd=lo THEN 0 ELSE GGTraj.HiSegment[newTraj];
ggData.drag.bezierDrag.traj ¬ newTraj;
ggData.drag.bezierDrag.outlineSlice ¬ newOutline;
};
};
DuringBezierDrag:
PUBLIC MouseProc = {
totalDragVector: Vector;
mapPoint: Point;
mapPoint ¬ GGMouseEvent.DragTheCaret[worldPt, ggData, "Dragging:"];
totalDragVector ¬ Vectors2d.Sub[mapPoint, ggData.drag.startPoint];
ggData.drag.transform ¬ ImagerTransformation.Translate[[totalDragVector.x, totalDragVector.y]];
IF ggData.drag.bezierDrag.draggingBezier
THEN {
newCP: Point;
dragCP: Vector;
bezier: Segment ¬ GGTraj.FetchSegment[ggData.drag.bezierDrag.traj, ggData.drag.bezierDrag.bezierNum];
IF ggData.drag.bezierDrag.trajEnd = lo
THEN {
dragCP ¬ Vectors2d.Sub[mapPoint, bezier.lo];
newCP ¬ Vectors2d.Sub[bezier.lo, dragCP]
}
ELSE {
dragCP ¬ Vectors2d.Sub[mapPoint, bezier.hi];
newCP ¬ Vectors2d.Sub[bezier.hi, dragCP];
};
GGSegment.BZControlPointMovedTo[bezier, newCP, IF ggData.drag.bezierDrag.trajEnd = lo THEN 0 ELSE 1];
};
GGWindow.RestoreScreenAndInvariants[paintAction: $DuringDrag, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: TRUE];
}; -- end DuringBezierDrag
ContinueBezierAdd:
PUBLIC StartProc = {
Remember the endjoint of the tangent vector and delete the segment. Replace it with a Bezier using the information for one end and extend the traj. as in AddBezier. Get ready for the drag.
bezierSetCP, bezierSetJoint, caretPoint, newCaretPoint: Point;
trajEnd: TrajEnd;
extendFromSeg, newBezier, newSeg: Segment;
oldTraj: Traj ¬ ggData.drag.bezierDrag.traj;
tangentSeg: Segment;
thisTraj: Traj;
hiSegNum: NAT ¬ GGTraj.HiSegment[oldTraj];
priority: INT;
newNormal, newHot: Sequence;
delSeq: TrajParts;
newOutlines: LIST OF Slice;
thisOutlineSlice, oldOutlineSlice: Slice;
thisOutline: GGOutline.OutlineData;
ancestorD: SliceDescriptor;
GGMouseEvent.TransformObjectsAfterMove[ggData.scene, ggData.drag.transform, ggData.drag.editConstraints, NIL]; -- update the tangent line. don't try to record undoable transforms here. A capture event is the current event.
GGHistory.PushCurrent[ggData]; -- push captured history event onto history list
GGRefresh.MoveOverlayToBackground[ggData];
GGWindow.NewCaretPos[ggData];
GGMouseEvent.SaveSavedState[ggData]; -- used for abort
newCaretPoint ¬ GGMouseEvent.DragTheCaret[worldPt, ggData, "Dragging:"]; -- use Gravity for now.
caretPoint ¬ GGCaret.GetPoint[ggData.caret];
IF ggData.drag.bezierDrag.draggingBezier
THEN {
IF ggData.drag.bezierDrag.trajEnd = hi THEN tangentSeg ¬ GGTraj.FetchSegment[oldTraj, ggData.drag.bezierDrag.bezierNum + 1]
ELSE tangentSeg ¬ GGTraj.FetchSegment[oldTraj, 0];
}
ELSE tangentSeg ¬ GGTraj.FetchSegment[oldTraj, ggData.drag.bezierDrag.bezierNum];
IF ggData.drag.bezierDrag.trajEnd = hi
THEN {
trajEnd ¬ hi;
bezierSetJoint ¬ tangentSeg.lo;
bezierSetCP ¬ tangentSeg.hi;
IF hiSegNum # 0
THEN
{
extendFromSeg ¬ GGTraj.FetchSegment[oldTraj, hiSegNum -1];
};
}
ELSE {
trajEnd ¬ lo;
bezierSetJoint ¬ tangentSeg.hi;
bezierSetCP ¬ tangentSeg.lo;
IF hiSegNum # 0
THEN
{
extendFromSeg ¬ GGTraj.FetchSegment[oldTraj, 1];
};
};
oldOutlineSlice ¬ ggData.drag.bezierDrag.outlineSlice;
priority ¬ GGScene.GetPriority[scene: ggData.scene, slice: oldOutlineSlice];
newBezier ¬ GGSegment.MakeBezier[bezierSetJoint, bezierSetCP, newCaretPoint, newCaretPoint, NIL];
newSeg ¬ GGSegment.MakeLine[newCaretPoint, worldPt, NIL];
IF hiSegNum # 0
THEN {
-- The traj contains more than just control vector.
GGSegment.CopyLooks[extendFromSeg, newBezier]}
ELSE GGSegment.SetDefaults[newBezier, ggData.defaults];
GGSegment.SetDefaults[newSeg, ggData.defaults];
newSeg.strokeWidth ¬ 1;
delSeq ¬ GGSequence.LastSegAndJoint[NARROW[ggData.drag.bezierDrag.traj.data], ggData.drag.bezierDrag.trajEnd]; -- the tangent segment.
newOutlines ¬ GGScene.DeleteSequence[GGSlice.DescriptorFromParts[ggData.drag.bezierDrag.traj, delSeq], ggData.scene].newOutlines;
ancestorD ¬ GGParent.TopLevelDescriptorFromChildDescriptor[GGSlice.DescriptorFromParts[ggData.drag.bezierDrag.traj, delSeq]];
IF newOutlines =
NIL
THEN {
-- Only a control vector in this outline.
thisTraj ¬ GGTraj.CreateTraj[newBezier.lo];
thisOutlineSlice ¬ GGOutline.CreateOutline[thisTraj, ggData.defaults.fillColor];
GGSliceOps.SetStrokeJoint[thisTraj, NIL, ggData.defaults.strokeJoint, NIL];
[] ¬ GGTraj.AddSegment[thisTraj, trajEnd, newBezier, lo];
[] ¬ GGTraj.AddSegment[thisTraj, trajEnd, newSeg, lo];
GGScene.AddSlice[ggData.scene, thisOutlineSlice, priority];
}
ELSE {
thisOutlineSlice ¬ newOutlines.first;
thisOutline ¬ NARROW[thisOutlineSlice.data];
thisTraj ¬ thisOutline.children.first;
[] ¬ GGTraj.AddSegment[thisTraj, trajEnd, newBezier, lo];
[] ¬ GGTraj.AddSegment[thisTraj, trajEnd, newSeg, lo];
};
ggData.drag.bezierDrag.draggingBezier ¬ TRUE;
ggData.drag.bezierDrag.bezierNum ¬ IF trajEnd=lo THEN 1 ELSE hiSegNum;
ggData.drag.bezierDrag.traj ¬ thisTraj;
ggData.drag.bezierDrag.outlineSlice ¬ thisOutlineSlice;
GGHistory.NewCapture["Add Bezier spline", ggData]; -- capture scene BEFORE UPDATE
[newNormal, newHot] ¬ GGMouseEvent.UpdateSelectionsForAdd[ggData.scene, oldTraj, thisTraj, trajEnd];
GGMouseEvent.UpdateCaretForAdd[ggData.caret, thisOutlineSlice, newNormal, worldPt];
[] ¬ GGAlign.UpdateBagsForAdd[oldOutlineSlice, ancestorD, trajEnd, ggData];
[] ¬ GGMouseEvent.ContinueMotion[ggData: ggData, opName: "add", bagType: $Drag, worldPt: worldPt, startBox: NIL];
DuringBezierDrag[NIL, ggData, worldPt]; -- draw it the first time
};
EndBezierAdd:
PUBLIC MouseProc = {
Caret is sitting on a Line segment (the tangent segment) that has been appended to the trajectory that is being extended.
The endpoint of the Line is selected.
delSeq: TrajParts;
newOutlines: LIST OF Slice;
point: Point;
normal: Vector;
newEndJoint: NAT;
CodeTimer.StartInt[$EndBezierAdd, $Gargoyle];
SELECT ggData.drag.bezierDrag.trajEnd
FROM
lo => newEndJoint ¬ 1;
hi => newEndJoint ¬ GGTraj.HiJoint[ggData.drag.bezierDrag.traj] -1;
ENDCASE;
point ¬ GGTraj.FetchJointPos[ggData.drag.bezierDrag.traj, newEndJoint];
normal ¬ GGTraj.FetchJointNormal[ggData.drag.bezierDrag.traj, newEndJoint];
delSeq ¬ GGSequence.LastSegAndJoint[NARROW[ggData.drag.bezierDrag.traj.data], ggData.drag.bezierDrag.trajEnd]; -- the tangent segment.
[----, newOutlines] ¬ GGScene.DeleteSequence[GGSlice.DescriptorFromParts[ggData.drag.bezierDrag.traj, delSeq], ggData.scene];
IF newOutlines =
NIL
THEN {
-- we removed the last segment
GGCaret.SitOn[ggData.caret, NIL];
}
ELSE {
jointParts: TrajParts;
jointD, outD: SliceDescriptor;
shortOutline: Slice;
shortTraj: Traj;
shortOutline ¬ newOutlines.first;
shortTraj ¬ GGParent.FirstChild[shortOutline, first, $Traj];
jointParts ¬ GGSequence.CreateFromJoint[NARROW[shortTraj.data], IF ggData.drag.bezierDrag.trajEnd = lo THEN 0 ELSE newEndJoint];
jointD ¬ GGSlice.DescriptorFromParts[shortTraj, jointParts]; -- now have traj descriptor. Make an outline descriptor
outD ¬ GGParent.TopLevelDescriptorFromChildDescriptor[jointD];
GGCaret.SitOn[ggData.caret, outD];
GGSelect.SelectSlice[sliceD: outD, scene: ggData.scene, selectClass: normal];
};
GGCaret.SetAttractor[ggData.caret, point, normal, NIL];
ggData.refresh.startBoundBox ¬ GGSliceOps.GetBoundBox[ggData.drag.bezierDrag.traj];
GGRefresh.NullStartBox[ggData];
GGRefresh.EnlargeStartBox[ggData, GGSliceOps.GetBoundBox[ggData.drag.bezierDrag.traj], NIL];
ggData.drag.bezierDrag.draggingBezier ¬ FALSE;
ggData.drag.bezierDrag.outlineSlice ¬ NIL;
ggData.drag.bezierDrag.traj ¬ NIL;
GGHistory.PushCurrent[ggData]; -- put captured scene on history list
GGRefresh.MoveOverlayToBackground[ggData];
GGWindow.NewCaretPos[ggData];
GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: triggerBag, edited: TRUE, okToSkipCapture: FALSE];
CodeTimer.StopInt[$EndBezierAdd, $Gargoyle];
}; -- end EndBezierAdd
AbortBezierAdd:
PUBLIC PROC [input:
LIST
OF
REF
ANY, ggData: GGData, worldPt: Point] = {
delSeq, partSeq: TrajParts;
delD: SliceDescriptor;
newOutlines: LIST OF Slice;
newEndJoint: NAT;
SELECT ggData.drag.bezierDrag.trajEnd
FROM
lo => newEndJoint ¬ 1;
hi => newEndJoint ¬ GGTraj.HiJoint[ggData.drag.bezierDrag.traj] -1;
ENDCASE;
delSeq ¬ GGSequence.LastSegAndJoint[NARROW[ggData.drag.bezierDrag.traj.data], ggData.drag.bezierDrag.trajEnd]; -- the tangent segment.
delD ¬ GGSlice.DescriptorFromParts[ggData.drag.bezierDrag.traj, delSeq];
IF ggData.drag.bezierDrag.draggingBezier
THEN {
-- also delete the bezier
ggData.drag.bezierDrag.draggingBezier ¬ FALSE;
partSeq ¬ GGSequence.CreateFromSegment[NARROW[ggData.drag.bezierDrag.traj.data], ggData.drag.bezierDrag.bezierNum];
delD ¬ GGSequence.Union[delD, GGSlice.DescriptorFromParts[ggData.drag.bezierDrag.traj, partSeq]];
IF ggData.drag.bezierDrag.trajEnd = hi THEN newEndJoint ¬ newEndJoint -1
ELSE newEndJoint ¬ newEndJoint +1;
};
[----, newOutlines] ¬ GGScene.DeleteSequence[delD, ggData.scene];
IF newOutlines =
NIL
THEN {
-- we removed the last segment
GGCaret.SitOn[ggData.caret, NIL];
}
ELSE {
jointParts: TrajParts;
jointD, outD: SliceDescriptor;
shortOutline: Slice;
shortTraj: Traj;
IF newOutlines.rest # NIL THEN SIGNAL Problem[msg: "failed assertion"];
shortOutline ¬ newOutlines.first;
shortTraj ¬ GGParent.FirstChild[shortOutline, first, $Traj];
jointParts ¬ GGSequence.CreateFromJoint[NARROW[shortTraj.data], IF ggData.drag.bezierDrag.trajEnd = lo THEN 0 ELSE newEndJoint];
jointD ¬ GGSlice.DescriptorFromParts[shortTraj, jointParts]; -- traj descriptor
outD ¬ GGParent.TopLevelDescriptorFromChildDescriptor[jointD];
GGCaret.SitOn[ggData.caret, outD];
GGSelect.SelectSlice[sliceD: outD, scene: ggData.scene, selectClass: normal];
};
GGCaret.SetAttractor[ggData.caret, GGTraj.FetchJointPos[ggData.drag.bezierDrag.traj, newEndJoint], GGTraj.FetchJointNormal[ggData.drag.bezierDrag.traj, newEndJoint], NIL];
ggData.drag.bezierDrag.outlineSlice ¬ NIL;
ggData.drag.bezierDrag.traj ¬ NIL;
GGMouseEvent.FinishAbort[ggData];
};
GGUserInput.RegisterAction[$Select, Select, none, FALSE];
END.