GGEventImplC.mesa
Copyright c 1986 by Xerox Corporation. All rights reserved.
Last Edited by: Pier, December 22, 1986 3:10:36 pm PST
Contents: Once an event reaches the front of the slack-process queue, it is dispatched to one of the procedures in this module.
Bier, January 19, 1987 10:47:43 pm PST
DIRECTORY
BasicTime, CubicSplines, FS, GGBasicTypes, GGBoundBox, GGBuiltinShapes, GGButtons, GGCaret, GGError, GGEvent, GGGraphicsButton, GGInterface, GGInterfaceTypes, GGModelTypes, GGObjects, GGOutline, GGParseIn, GGRefresh, GGSegment, GGSegmentTypes, GGSelect, GGSequence, GGSlice, GGStatistics, GGTraj, GGTransform, GGVector, GGWindow, Imager, ImagerArtwork, ImagerTransformation, ImagerInterpress, IO, List, Rope, TIPUser, ViewerClasses, ViewerTools;
GGEventImplC:
CEDAR
PROGRAM
IMPORTS BasicTime, FS, GGCaret, GGBoundBox, GGBuiltinShapes, GGError, GGEvent, GGInterface, GGObjects, GGOutline, GGSelect, GGSequence, GGStatistics, GGParseIn, GGRefresh, GGSegment, GGSlice, GGTraj, GGTransform, GGVector, GGWindow, Imager, ImagerArtwork, ImagerTransformation, ImagerInterpress, IO, List, Rope, ViewerTools, TIPUser
EXPORTS GGEvent = BEGIN
BitVector: TYPE = GGModelTypes.BitVector;
BoundBox: TYPE = GGModelTypes.BoundBox;
ControlPointGenerator: TYPE = GGModelTypes.ControlPointGenerator;
EntityGenerator: TYPE = GGModelTypes.EntityGenerator;
GargoyleData: TYPE = GGInterfaceTypes.GargoyleData;
Joint: TYPE = GGSegmentTypes.Joint;
JointGenerator: TYPE = GGModelTypes.JointGenerator;
Outline: TYPE = GGModelTypes.Outline;
OutlineDescriptor: TYPE = GGModelTypes.OutlineDescriptor;
Point: TYPE = GGBasicTypes.Point;
ScalarButtonClient: TYPE = GGButtons.ScalarButtonClient;
ScalarButtonHandle: TYPE = GGButtons.ScalarButtonHandle;
Scene: TYPE = GGModelTypes.Scene;
Segment: TYPE = GGSegmentTypes.Segment;
SegmentGenerator: TYPE = GGModelTypes.SegmentGenerator;
Sequence: TYPE = GGModelTypes.Sequence;
SequenceGenerator: TYPE = GGModelTypes.SequenceGenerator;
Slice: TYPE = GGModelTypes.Slice;
SliceDescriptor: TYPE = GGModelTypes.SliceDescriptor;
SliceDescriptorGenerator: TYPE = GGModelTypes.SliceDescriptorGenerator;
SliceGenerator: TYPE = GGModelTypes.SliceGenerator;
SliceParts: TYPE = GGModelTypes.SliceParts;
Traj: TYPE = GGModelTypes.Traj;
TrajEnd: TYPE = GGModelTypes.TrajEnd;
TrajGenerator: TYPE = GGModelTypes.TrajGenerator;
TrajPartType: TYPE = GGModelTypes.TrajPartType;
TwoState: TYPE = GGButtons.TwoState;
Vector: TYPE = GGBasicTypes.Vector;
Viewer: TYPE = ViewerClasses.Viewer;
ReloadTipTable:
PUBLIC
PROC [event:
LIST
OF
REF
ANY, clientData:
REF
ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
newTable: TIPUser.TIPTable;
actionArea: ViewerClasses.Viewer;
bad: BOOL ← FALSE;
tableName, msg: Rope.ROPE;
GGError.Append[gargoyleData.feedback, "Reloading tip table...", begin];
tableName ← Rope.Concat[gargoyleData.originalWDir, "Gargoyle.TIP"];
newTable ← TIPUser.InstantiateNewTIPTable[tableName
!
FS.Error => {
bad ← TRUE;
msg ← Rope.Concat["Cannot read TIP table file: ", tableName];
CONTINUE};
TIPUser.InvalidTable => {
bad ← TRUE;
msg ← Rope.Concat["Error(s) saved on TIP.Errors for: ", tableName];
CONTINUE}];
IF bad THEN {GGError.Append[gargoyleData.feedback, msg, oneLiner]; RETURN};
GGError.Append[gargoyleData.feedback, "Done.", end];
IF newTable = NIL THEN ERROR;
actionArea ← gargoyleData.actionArea;
actionArea.tipTable ← newTable;
};
SawTextFinish:
PUBLIC
PROC [event:
LIST
OF
REF
ANY, clientData:
REF
ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
slice: Slice ← gargoyleData.refresh.textInProgress;
IF slice#
NIL
AND Rope.Length[GGSlice.GetText[slice: slice]]=0
THEN {
-- backspaced to nothing
GGSelect.DeselectEntityAllClasses[slice, gargoyleData.scene];
GGSlice.DeleteSlice[gargoyleData.scene, slice];
};
IF gargoyleData.refresh.textInProgress#
NIL
THEN {
-- fix up alignment triggers
GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsSelected, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: FALSE];
slice ← gargoyleData.refresh.textInProgress ← NIL; -- terminates typed input
};
};
Shape Menu Operations
PolygonInCircle:
PUBLIC
PROC [event:
LIST
OF
REF
ANY, clientData:
REF
ANY] = {
outline: Outline;
bBox: BoundBox;
gargoyleData: GargoyleData ← NARROW[clientData];
caretPoint: Point ← GGCaret.GetPoint[gargoyleData.caret];
sideCount: INT ← NARROW[event.rest.first, REF INT]^;
IF sideCount=-1
THEN {
rRope: Rope.ROPE ← ViewerTools.GetSelectionContents[];
sideCount ← IO.GetInt[IO.RIS[rRope] ! IO.EndOfStream, IO.Error => sideCount ← -1];
};
IF sideCount<=0 THEN RETURN;
outline ← GGBuiltinShapes.PolygonInCircle[sideCount, caretPoint, gargoyleData.hitTest.scaleUnit];
GGObjects.AddOutline[gargoyleData.scene, outline, -1];
GGSelect.DeselectAll[gargoyleData.scene, normal];
GGSelect.SelectEntireOutline[outline, gargoyleData.scene, normal];
bBox ← GGBoundBox.BoundBoxOfSelected[gargoyleData.scene, normal];
gargoyleData.refresh.startBoundBox^ ← bBox^;
gargoyleData.refresh.addedObject ← outline;
GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectAdded, gargoyleData: gargoyleData, remake: sceneBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE];
};
NewBox:
PUBLIC
PROC [event:
LIST
OF
REF
ANY, clientData:
REF
ANY] = {
slice: Slice;
sliceParts: SliceParts;
bBox: BoundBox;
gargoyleData: GargoyleData ← NARROW[clientData];
caretPoint: Point ← GGCaret.GetPoint[gargoyleData.caret];
sideLength: REAL ← NARROW[event.rest.first, REF REAL]^;
slice ← GGBuiltinShapes.Box[caretPoint, sideLength*gargoyleData.hitTest.scaleUnit];
GGObjects.AddSlice[gargoyleData.scene, slice, -1];
GGSelect.DeselectAll[gargoyleData.scene, normal];
sliceParts ← slice.class.newParts[slice, NIL, slice];
GGSelect.SelectSlice[slice, sliceParts, gargoyleData.scene, normal];
bBox ← GGBoundBox.BoundBoxOfSelected[gargoyleData.scene, normal];
gargoyleData.refresh.startBoundBox^ ← bBox^;
gargoyleData.refresh.addedObject ← slice;
GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectAdded, gargoyleData: gargoyleData, remake: sceneBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE];
};
NewCircle:
PUBLIC
PROC [event:
LIST
OF
REF
ANY, clientData:
REF
ANY] = {
slice: Slice;
sliceParts: SliceParts;
bBox: BoundBox;
gargoyleData: GargoyleData ← NARROW[clientData];
caretPoint: Point ← GGCaret.GetPoint[gargoyleData.caret];
radius: REAL ← NARROW[event.rest.first, REF REAL]^;
slice ← GGBuiltinShapes.Circle[caretPoint, radius*gargoyleData.hitTest.scaleUnit];
GGObjects.AddSlice[gargoyleData.scene, slice, -1];
GGSelect.DeselectAll[gargoyleData.scene, normal];
sliceParts ← slice.class.newParts[slice, NIL, slice];
GGSelect.SelectSlice[slice, sliceParts, gargoyleData.scene, normal];
bBox ← GGBoundBox.BoundBoxOfSelected[gargoyleData.scene, normal];
gargoyleData.refresh.startBoundBox^ ← bBox^;
gargoyleData.refresh.addedObject ← slice;
GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectAdded, gargoyleData: gargoyleData, remake: sceneBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE];
};
NewKnotchedLine:
PUBLIC
PROC [event:
LIST
OF
REF
ANY, clientData:
REF
ANY] = {
outline: Outline;
bBox: BoundBox;
gargoyleData: GargoyleData ← NARROW[clientData];
caretPoint: Point ← GGCaret.GetPoint[gargoyleData.caret];
length: REAL ← NARROW[event.rest.first, REF REAL]^;
segCount: INT ← NARROW[event.rest.rest.first, REF INT]^;
p1: Point;
p1 ← GGVector.Add[caretPoint, [length*gargoyleData.hitTest.scaleUnit, 0.0]];
outline ← GGBuiltinShapes.KnotchedLine[p0: caretPoint, p1: p1, segmentCount: segCount];
GGObjects.AddOutline[gargoyleData.scene, outline, -1];
GGSelect.DeselectAll[gargoyleData.scene, normal];
GGSelect.SelectEntireOutline[outline, gargoyleData.scene, normal];
bBox ← GGBoundBox.BoundBoxOfSelected[gargoyleData.scene, normal];
gargoyleData.refresh.startBoundBox^ ← bBox^;
gargoyleData.refresh.addedObject ← outline;
GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectAdded, gargoyleData: gargoyleData, remake: sceneBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE];
};
NewArrow:
PUBLIC
PROC [event:
LIST
OF
REF
ANY, clientData:
REF
ANY] = {
OPEN GGVector;
outline: Outline;
traj: Traj;
seg: Segment;
bBox: BoundBox;
success: BOOL;
gargoyleData: GargoyleData ← NARROW[clientData];
shaftLength: REAL ← NARROW[event.rest.first, REF REAL]^;
barbLength: REAL ← NARROW[event.rest.rest.first, REF REAL]^;
shaftBottom, shaftTop, barbLeft, barbRight: Point;
shaftBottom ← GGCaret.GetPoint[gargoyleData.caret];
shaftLength ← shaftLength * gargoyleData.hitTest.scaleUnit; -- convert to screen dots.
barbLength ← barbLength * gargoyleData.hitTest.scaleUnit; -- convert to screen dots.
shaftTop ← Add[shaftBottom, [0.0, shaftLength]];
barbLeft ← Add[shaftBottom, Scale[Normalize[[-1.0,1.0]], barbLength]];
barbRight ← Add[shaftBottom, Scale[Normalize[[1.0,1.0]], barbLength]];
traj ← GGTraj.CreateTraj[shaftTop];
seg ← GGSegment.MakeLine[shaftTop, shaftBottom, NIL];
success ← GGTraj.AddSegment[traj, hi, seg, lo];
IF NOT success THEN ERROR;
seg ← GGSegment.MakeLine[shaftBottom, barbLeft, NIL];
success ← GGTraj.AddSegment[traj, hi, seg, lo];
IF NOT success THEN ERROR;
seg ← GGSegment.MakeLine[barbLeft, shaftBottom, NIL];
success ← GGTraj.AddSegment[traj, hi, seg, lo];
IF NOT success THEN ERROR;
seg ← GGSegment.MakeLine[shaftBottom, barbRight, NIL];
success ← GGTraj.AddSegment[traj, hi, seg, lo];
IF NOT success THEN ERROR;
outline ← GGOutline.CreateOutline[traj: traj, lineEnds: round, fillColor: Imager.black];
GGObjects.AddOutline[gargoyleData.scene, outline, -1];
GGSelect.DeselectAll[gargoyleData.scene, normal];
GGSelect.SelectEntireOutline[outline, gargoyleData.scene, normal];
bBox ← GGBoundBox.BoundBoxOfSelected[gargoyleData.scene, normal];
gargoyleData.refresh.startBoundBox^ ← bBox^;
gargoyleData.refresh.addedObject ← outline;
GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectAdded, gargoyleData: gargoyleData, remake: sceneBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE];
};
Frame:
PUBLIC
PROC [event:
LIST
OF
REF
ANY, clientData:
REF
ANY] = {
add a "picture frame" of a specified size to the image, origin at [0.0, 0.0], strokeWidth=9.
gargoyleData: GargoyleData ← NARROW[clientData];
halfStrokeWidth: REAL = 9.0/2.0;
frameWidth: REAL ← NARROW[event.rest.first, REF REAL]^; -- in Gargoyle units (points)
frameLength: REAL ← NARROW[event.rest.rest.first, REF REAL]^; -- in Gargoyle units (points)
box: GGBoundBox.BoundBox ← GGBoundBox.CreateBoundBox[0.0-halfStrokeWidth, 0.0-halfStrokeWidth, frameWidth+halfStrokeWidth, frameLength+halfStrokeWidth];
sliceD: SliceDescriptor ← GGSlice.MakeBoxSlice[box, none, GGTransform.Identity[], 9.0];
GGObjects.AddSlice[gargoyleData.scene, sliceD.slice, -1];
gargoyleData.refresh.startBoundBox^ ← sliceD.slice.boundBox^;
gargoyleData.refresh.addedObject ← sliceD.slice;
GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectAdded, gargoyleData: gargoyleData, remake: sceneBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE];
};
Edit Menu Operations
Weld:
PUBLIC
PROC [event:
LIST
OF
REF
ANY, clientData:
REF
ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
One or two top level trajectories are selected. If one, Close it by distorting one of its ends. If two, of the four possible pairings of endpoints, choose the one with the endpoints closest together. Translate the second trajectory to the first. Weld.
outSeqGen: GGSelect.OutlineSequenceGenerator;
firstTraj, secondTraj, newTraj: Traj;
firstOutline, secondOutline, newOutline: Outline;
firstOutSeq, secondOutSeq: GGSelect.OutlineSequence;
weldPoint: Point;
firstEnd, secondEnd: TrajEnd;
firstBox, secondBox, newBox: BoundBox;
success: BOOL;
outSeqGen ← GGSelect.SelectedOutlineSequences[gargoyleData.scene, normal];
[firstOutSeq, secondOutSeq, firstTraj, secondTraj, success] ← GetWeldArguments[gargoyleData, outSeqGen];
IF NOT success THEN RETURN;
IF secondOutSeq = NIL THEN {WeldToSelf[gargoyleData, firstTraj]; RETURN;};
[firstEnd, secondEnd] ← ClosestEnds[firstTraj, secondTraj];
firstOutline ← GGOutline.OutlineOfTraj[firstTraj];
secondOutline ← GGOutline.OutlineOfTraj[secondTraj];
Replace the old outlines with the new welded one.
newTraj ← GGTraj.Concat[firstTraj, firstEnd, secondTraj, secondEnd];
newOutline ← GGOutline.CreateOutline[traj: newTraj, lineEnds: firstOutline.lineEnds, fillColor: firstOutline.fillColor];
GGInterface.DeleteOutline[firstOutline, gargoyleData.scene];
GGInterface.DeleteOutline[secondOutline, gargoyleData.scene];
GGObjects.AddOutline[gargoyleData.scene, newOutline, -1];
Select the new outline.
GGSelect.SelectEntireOutline[newOutline, gargoyleData.scene, normal];
Move the caret to the weld spot
weldPoint ← GGTraj.FetchJointPos[newTraj, GGTraj.HiJoint[IF firstEnd=hi THEN firstTraj ELSE secondTraj]];
GGCaret.SetAttractor[gargoyleData.caret, weldPoint, NIL];
GGCaret.SitOn[gargoyleData.caret,
NIL];
Compute the new bounding box.
firstBox ← GGTraj.GetBoundBox[firstTraj];
secondBox ← GGTraj.GetBoundBox[secondTraj];
newBox ← GGTraj.GetBoundBox[newTraj];
GGBoundBox.EnlargeByBox[bBox: firstBox, by: secondBox];
GGBoundBox.EnlargeByBox[bBox: firstBox, by: newBox];
gargoyleData.refresh.startBoundBox^ ← firstBox^;
GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE];
}; -- end of Weld
GetWeldArguments:
PROC [gargoyleData: GargoyleData, outSeqGen: GGSelect.OutlineSequenceGenerator]
RETURNS [firstOutSeq, secondOutSeq: GGSelect.OutlineSequence, firstTraj, secondTraj: Traj ←
NIL, success:
BOOL] = {
thirdOutSeq: GGSelect.OutlineSequence;
firstOutSeq ← GGSelect.NextOutlineSequences[outSeqGen];
secondOutSeq ← GGSelect.NextOutlineSequences[outSeqGen];
thirdOutSeq ← GGSelect.NextOutlineSequences[outSeqGen];
IF firstOutSeq =
NIL
OR thirdOutSeq #
NIL
THEN {
-- 0 or more than 2 selected
GGError.AppendHerald[gargoyleData.feedback, "Select one or two open trajectories for a weld.", oneLiner];
GGError.Blink[gargoyleData.feedback];
RETURN[NIL, NIL, NIL, NIL, FALSE];
};
firstTraj ← firstOutSeq.fenceSeq.traj;
IF firstTraj.role # open
THEN {
GGError.AppendHerald[gargoyleData.feedback, "Select one or two OPEN trajectories for a weld.", oneLiner];
GGError.Blink[gargoyleData.feedback];
RETURN[NIL, NIL, NIL, NIL, FALSE];
};
IF secondOutSeq = NIL THEN RETURN[firstOutSeq, NIL, firstTraj, NIL, TRUE];
secondTraj ← secondOutSeq.fenceSeq.traj;
IF secondTraj.role # open
THEN {
GGError.AppendHerald[gargoyleData.feedback, "Select one or two OPEN trajectories for a weld.", oneLiner];
GGError.Blink[gargoyleData.feedback];
RETURN[NIL, NIL, NIL, NIL, FALSE];
};
success ← TRUE;
};
WeldToSelf:
PROC [gargoyleData: GargoyleData, traj: Traj] = {
restoreBox: BoundBox;
outline: Outline ← GGOutline.OutlineOfTraj[traj];
restoreBox ← GGTraj.GetBoundBox[traj];
Modify the Traj in place to close it.
GGOutline.SaveSelectionsInOutline[outline, gargoyleData.scene];
GGSelect.DeselectEntityAllClasses[outline, gargoyleData.scene];
GGTraj.CloseByDistorting[traj, lo];
outline.class.setFillColor[outline, GGObjects.fillColor];
GGOutline.RemakeSelectionsFromOutline[outline, gargoyleData.scene];
GGSelect.SelectEntireOutline[outline, gargoyleData.scene, normal];
move the caret to the weld spot
GGCaret.SetAttractor[gargoyleData.caret, GGTraj.FetchJointPos[traj, 0], NIL];
GGCaret.SitOn[gargoyleData.caret, NIL]; -- this is important!
GGBoundBox.EnlargeByBox[bBox: restoreBox, by: GGTraj.GetBoundBox[traj]];
gargoyleData.refresh.startBoundBox^ ← restoreBox^;
GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE];
}; -- end WeldToSelf
ClosestEnds:
PROC [firstTraj, secondTraj: Traj]
RETURNS [firstEnd, secondEnd: TrajEnd] = {
firstLo, firstHi, secondLo, secondHi: Point;
d11, d12, d21, d22, d: REAL;
firstLo ← GGTraj.FetchJointPos[firstTraj, 0];
firstHi ← GGTraj.FetchJointPos[firstTraj, GGTraj.HiJoint[firstTraj]];
secondLo ← GGTraj.FetchJointPos[secondTraj, 0];
secondHi ← GGTraj.FetchJointPos[secondTraj, GGTraj.HiJoint[secondTraj]];
d11 ← GGVector.DistanceSquared[firstLo, secondLo];
d12 ← GGVector.DistanceSquared[firstLo, secondHi];
d21 ← GGVector.DistanceSquared[firstHi, secondLo];
d22 ← GGVector.DistanceSquared[firstHi, secondHi];
d ← MIN[d11, d12, d21, d22];
SELECT
TRUE
FROM
d11 = d => {firstEnd ← lo; secondEnd ← lo};
d12 = d => {firstEnd ← lo; secondEnd ← hi};
d21 = d => {firstEnd ← hi; secondEnd ← lo};
d22 = d => {firstEnd ← hi; secondEnd ← hi};
ENDCASE => ERROR;
};
SplitSegment:
PUBLIC
PROC [event:
LIST
OF
REF
ANY, clientData:
REF
ANY] = {
MakeReplacement: GGSelect.RunProc = {
GetPt:
PROC[p0, dir: Point]
RETURNS [pt: Point] = {
gScale: REAL ← 0.33;
gAngle: REAL ← 33;
pt ← GGVector.Add[GGVector.Scale[GGVector.VectorPlusAngle[dir,gAngle], gScale*GGVector.Magnitude[dir] ], p0];
};
RunProc: TYPE = PROC [run: Sequence] RETURNS [traj: Traj];
-- make new run of the appropriate type, having the same joints as that of the selected run, and with the caret position as an additional joint or control point.
firstJointNum: INT ← GGSequence.FirstJointNum[run];
firstPoint: Point ← GGTraj.FetchJointPos[run.traj, firstJointNum];
lastPoint: Point ← GGTraj.FetchJointPos[run.traj, GGSequence.LastJointNum[run, firstJointNum]];
middlePoint: Point ← GGCaret.GetPoint[gargoyleData.caret];
runSegs: SegmentGenerator ← GGSequence.SegmentsInSequence[run];
runSeg: Segment ← GGSequence.NextSegment[runSegs];
IF runSeg=NIL OR GGSequence.NextSegment[runSegs]#NIL THEN ERROR; -- I think. KAP.
traj ← GGTraj.CreateTraj[firstPoint];
SELECT GGTraj.FetchSegment[run.traj, firstJointNum].class.type
FROM
$Line => {
IF NOT GGTraj.AddSegment[traj, hi, GGSegment.MakeLine[firstPoint, middlePoint, List.Append[runSeg.props, NIL]], lo] THEN ERROR; -- copies props
IF NOT GGTraj.AddSegment[traj, hi, GGSegment.MakeLine[middlePoint, lastPoint, List.Append[runSeg.props, NIL]], lo] THEN ERROR;
};
$Arc => {
IF NOT GGTraj.AddSegment[traj, hi, GGSegment.MakeArc[firstPoint, middlePoint, lastPoint, List.Append[runSeg.props, NIL]], lo] THEN ERROR;
};
$Conic => {
IF NOT GGTraj.AddSegment[traj, hi, GGSegment.MakeConic[firstPoint, middlePoint, lastPoint, 0.7, List.Append[runSeg.props, NIL]], lo] THEN ERROR;
};
$Bezier => {
length: REAL ← GGVector.Distance[firstPoint, middlePoint];
dir: Point ← GGVector.Sub[middlePoint, firstPoint];
p1: Point ← GetPt[firstPoint, dir];
p2: Point ← GetPt[middlePoint, [-dir.x, -dir.y]];
IF NOT GGTraj.AddSegment[traj, hi, GGSegment.MakeBezier[firstPoint, p1, p2, middlePoint, List.Append[runSeg.props, NIL]], lo] THEN ERROR; -- first Bezier
dir ← GGVector.Sub[lastPoint, middlePoint];
p1 ← GetPt[middlePoint, dir];
p2 ← GetPt[lastPoint, [-dir.x, -dir.y]];
IF NOT GGTraj.AddSegment[traj, hi, GGSegment.MakeBezier[middlePoint, p1, p2, lastPoint, List.Append[runSeg.props, NIL]], lo] THEN ERROR; -- last Bezier
};
$CubicSpline => {
cps: CubicSplines.KnotSequence ← NEW[CubicSplines.KnotSequenceRec[3]];
cps[0] ← [firstPoint.x, firstPoint.y];
cps[1] ← [middlePoint.x, middlePoint.y];
cps[2] ← [lastPoint.x, lastPoint.y];
IF NOT GGTraj.AddSegment[traj, hi, GGSegment.MakeCubicSpline[cps, naturalAL, List.Append[runSeg.props, NIL]], lo] THEN ERROR;
};
ENDCASE => ERROR; -- unknown segment type
};
gargoyleData: GargoyleData ← NARROW[clientData];
gargoyleData.refresh.startBoundBox^ ← GGSelect.ForEachOutlineRun[gargoyleData.scene, gargoyleData.caret, normal, MakeReplacement]^;
GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE]
}; -- end SplitSegment
Splice:
PUBLIC
PROC [event:
LIST
OF
REF
ANY, clientData:
REF
ANY] = {
Replace all selected runs with line segments.
ReplaceWithLine: GGSelect.RunProc = {
RunProc: TYPE = PROC [run: Sequence] RETURNS [traj: Traj];
seg: Segment;
p0, p1: Point;
jointNum: INT;
success: BOOL;
jointNum ← GGSequence.FirstJointNum[run];
p0 ← GGTraj.FetchJointPos[run.traj, jointNum];
jointNum ← GGSequence.LastJointNum[run, jointNum];
p1 ← GGTraj.FetchJointPos[run.traj, jointNum];
seg ← GGSegment.MakeLine[p0, p1, NIL];
traj ← GGTraj.CreateTraj[p0];
success ← GGTraj.AddSegment[traj, hi, seg, lo];
IF NOT success THEN ERROR;
};
gargoyleData: GargoyleData ← NARROW[clientData];
bBox: BoundBox;
bBox ← GGSelect.ForEachOutlineRun[gargoyleData.scene, gargoyleData.caret, normal, ReplaceWithLine];
gargoyleData.refresh.startBoundBox^ ← bBox^;
GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE];
}; -- end Splice
DescribeCurve:
PUBLIC
PROC [event:
LIST
OF
REF
ANY, clientData:
REF
ANY] = {
event.first = $DescribeCurve, event.rest = $Selected or $Caret
gargoyleData: GargoyleData ← NARROW[clientData];
SELECT event.rest.first
FROM
$Caret => GGEvent.DescribeCaretObject[event, clientData]; -- for now
$Selected => {
selectedGen: EntityGenerator ← GGSelect.SelectedStuff[gargoyleData.scene, normal];
entity: REF ANY ← GGObjects.NextEntity[selectedGen];
IF entity=
NIL
THEN {
GGError.Append[gargoyleData.feedback, "Select some object for description", oneLiner];
GGError.Blink[gargoyleData.feedback];
RETURN;
};
IF GGObjects.NextEntity[selectedGen]#
NIL
THEN {
GGError.Append[gargoyleData.feedback, "Select only one object for description", oneLiner];
GGError.Blink[gargoyleData.feedback];
RETURN;
};
WITH entity
SELECT
FROM
outlineD: OutlineDescriptor => GGError.Append[gargoyleData.feedback, outlineD.slice.class.describe[outlineD.slice, outlineD.parts], oneLiner];
sliceD: SliceDescriptor => GGError.Append[gargoyleData.feedback, sliceD.slice.class.describe[sliceD.slice, sliceD.parts], oneLiner];
ENDCASE => ERROR;
};
ENDCASE => ERROR;
};
AddControlPoint:
PUBLIC
PROC [event:
LIST
OF
REF
ANY, clientData:
REF
ANY] = {
Adds a new control point to a segment
gargoyleData: GargoyleData ← NARROW[clientData];
attractor: REF ANY;
seg, newSeg: Segment;
segNum: INT;
traj, newRun, newTraj: Traj;
refreshBox: BoundBox;
success: BOOL;
partType: TrajPartType;
caretPos: Point ← GGCaret.GetPoint[gargoyleData.caret];
attractor ← GGCaret.GetAttractor[gargoyleData.caret];
BEGIN
IF attractor = NIL THEN GOTO NotASegment;
WITH attractor
SELECT
FROM
oD: OutlineDescriptor => {
[success, partType, traj, ----, ----, ----, ----, seg, segNum] ← GGOutline.UnpackSimpleDescriptorOld[oD];
IF NOT success OR partType # segment THEN GOTO NotASegment;
};
sD: SliceDescriptor => {
IF sD.slice.class.type # $Outline THEN GOTO NotASegment;
[success, partType, traj, ----, ----, ----, ----, seg, segNum] ← GGOutline.UnpackSimpleDescriptor[sD];
IF NOT success OR partType # segment THEN GOTO NotASegment;
};
ENDCASE => ERROR;
IF seg.class.type # $CubicSpline
THEN
GOTO NotASpline;
Must save select data now so that segment copy (in CSControlPointAdd) will reflect selection data.
GGSelect.DeselectAll[gargoyleData.scene, normal];
GGOutline.SaveSelectionInTraj[traj, normal, gargoyleData.scene]; -- to clear field bits
GGOutline.SaveSelectionInTraj[traj, hot, gargoyleData.scene]; -- to save hot field bits
newSeg ← GGSegment.CSControlPointAdd[seg, caretPos, gargoyleData];
newRun ← GGTraj.CreateTraj[newSeg.lo];
IF NOT GGTraj.AddSegment[newRun, hi, newSeg, lo] THEN ERROR;
IF (segNum ← GGTraj.IndexOfSegment[seg, traj]) = -1 THEN ERROR; -- inconsistent
[refreshBox, newTraj] ← GGSelect.SubstituteForSegment[traj, segNum, newRun, gargoyleData.scene];
GGCaret.NoAttractor[gargoyleData.caret];
GGCaret.SitOn[gargoyleData.caret, NIL];
gargoyleData.refresh.startBoundBox^ ← refreshBox^;
GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE];
EXITS
NotASegment => {
GGError.Append[gargoyleData.feedback, "Caret must lie on a segment to add a control point", oneLiner];
GGError.Blink[gargoyleData.feedback];
};
NotASpline => {
GGError.Append[gargoyleData.feedback, "Caret must be positioned on a spline", oneLiner];
GGError.Blink[gargoyleData.feedback];
};
END;
};
DeleteControlPoint:
PUBLIC
PROC [event:
LIST
OF
REF
ANY, clientData:
REF
ANY] = {
Deletes all selected control points from segments
DeleteSelectedCPs:
PROC [seq: Sequence] = {
AllFalse:
PROC [bitvec: BitVector]
RETURNS [
BOOL] = {
FOR i:
NAT
IN [0..bitvec.len)
DO
IF bitvec[i] = TRUE THEN RETURN[FALSE];
ENDLOOP;
RETURN[TRUE];
};
refreshBox: BoundBox;
outlineBox: BoundBox ← seq.traj.parent.class.getBoundBox[seq.traj.parent, NIL];
GGBoundBox.EnlargeByBox[gargoyleData.refresh.startBoundBox, outlineBox]; -- refresh old outline
GGOutline.SaveSelectionInTraj[seq.traj, hot, gargoyleData.scene]; -- to be restored by SubstituteForSeg
GGOutline.SaveSelectionInTraj[seq.traj, normal, gargoyleData.scene];
FOR i:
INT
IN [0..seq.traj.segCount)
DO
IF
NOT AllFalse[seq.controlPoints[i]]
AND
NOT seq.segments[i]
THEN {
oldSeg: Segment ← GGTraj.FetchSegment[seq.traj, i];
IF oldSeg.class.type = $CubicSpline
THEN {
newSeg: Segment ← GGSegment.CSControlPointDelete[oldSeg, seq.controlPoints[i], gargoyleData];
newRun: Traj ← GGTraj.CreateTraj[newSeg.lo];
IF NOT GGTraj.AddSegment[newRun, hi, newSeg, lo] THEN ERROR;
[bBox: refreshBox] ← GGSelect.SubstituteForSegment[seq.traj, i, newRun, gargoyleData.scene];
GGCaret.SetAttractor[gargoyleData.caret, newSeg.lo, NIL];
GGBoundBox.EnlargeByBox[gargoyleData.refresh.startBoundBox, refreshBox];
}
ELSE {
GGError.Append[gargoyleData.feedback, "Only delete control points from Splines", oneLiner];
GGError.Blink[gargoyleData.feedback];
};
};
ENDLOOP;
};
gargoyleData: GargoyleData ← NARROW[clientData];
outSeqGen: GGSelect.OutlineSequenceGenerator ← GGSelect.SelectedOutlineSequences[gargoyleData.scene, normal];
gargoyleData.refresh.startBoundBox ← GGBoundBox.NullBoundBox[]; -- start with empty refresh box, and increase size when necessary
FOR outSeq: GGSelect.OutlineSequence ← GGSelect.NextOutlineSequences[outSeqGen], GGSelect.NextOutlineSequences[outSeqGen]
UNTIL outSeq =
NIL
DO
IF outSeq.fenceSeq # NIL THEN DeleteSelectedCPs[outSeq.fenceSeq];
FOR hole: Sequence ← GGSequence.NextSequence[outSeq.holeSeqs], GGSequence.NextSequence[outSeq.holeSeqs]
UNTIL hole =
NIL
DO
DeleteSelectedCPs[hole]
ENDLOOP;
ENDLOOP;
GGCaret.SitOn[gargoyleData.caret, NIL];
GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: FALSE];
};
AddJoint:
PUBLIC
PROC [event:
LIST
OF
REF
ANY, clientData:
REF
ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
attractor: REF ANY;
seg, newSeg1, newSeg2: Segment;
segNum: INT;
traj, newRun, newTraj: Traj;
refreshBox, oldBox: BoundBox;
newJoint: Joint;
success: BOOL;
partType: TrajPartType;
caretPos: Point ← GGCaret.GetPoint[gargoyleData.caret];
attractor ← GGCaret.GetAttractor[gargoyleData.caret];
BEGIN
WITH attractor
SELECT
FROM
oD: OutlineDescriptor => {
[success, partType, traj, ----, ----, ----, ----, seg, segNum] ← GGOutline.UnpackSimpleDescriptorOld[oD];
IF NOT success OR partType # segment THEN GOTO NotASegment;
};
sD: SliceDescriptor => {
IF sD.slice.class.type # $Outline THEN GOTO NotASegment;
[success, partType, traj, ----, ----, ----, ----, seg, segNum] ← GGOutline.UnpackSimpleDescriptor[sD];
IF NOT success OR partType # segment THEN GOTO NotASegment;
};
ENDCASE => ERROR;
IF seg.class.type=$Conic
THEN
GOTO ConicsAreNotDone;
Save select data now so that segment copy (in CSControlPointAdd) will reflect selection data.
GGSelect.DeselectAll[gargoyleData.scene, normal];
GGOutline.SaveSelectionInTraj[traj, normal, gargoyleData.scene];
GGOutline.SaveSelectionInTraj[traj, hot, gargoyleData.scene];
[Artwork node; type 'ArtworkInterpress on' to command tool]
[newSeg1, newSeg2] ← seg.class.addJoint[seg, caretPos];
newRun ← GGTraj.CreateTraj[newSeg1.lo];
IF NOT GGTraj.AddSegment[newRun, hi, newSeg1, lo] THEN ERROR;
IF NOT GGTraj.AddSegment[newRun, hi, newSeg2, lo] THEN ERROR;
newJoint ← GGTraj.FetchJoint[newRun, 1];
newJoint.TselectedInFull.normal ← TRUE;
IF (segNum ← GGTraj.IndexOfSegment[seg, traj]) = -1 THEN ERROR; -- inconsistent
oldBox ← seg.class.boundBox[seg];
[refreshBox, newTraj] ← GGSelect.SubstituteForSegment[traj, segNum, newRun, gargoyleData.scene];
GGBoundBox.EnlargeByBox[refreshBox, oldBox];
GGCaret.NoAttractor[gargoyleData.caret];
GGCaret.SitOn[gargoyleData.caret, NIL];
gargoyleData.refresh.startBoundBox^ ← refreshBox^;
GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE];
EXITS
NotASegment => {
GGError.Append[gargoyleData.feedback, "Caret must lie on a segment to add a joint", oneLiner];
GGError.Blink[gargoyleData.feedback];
};
ConicsAreNotDone => {
GGError.Append[gargoyleData.feedback, "Can't add joints to Conics", oneLiner];
GGError.Blink[gargoyleData.feedback];
};
END;
};
Transform Menu Operations
TransRotScale:
PUBLIC
PROC [event:
LIST
OF
REF
ANY, clientData:
REF
ANY] = {
ComplainAboutAnchor:
PROC = {
GGError.AppendHerald[gargoyleData.feedback, "Place an anchor for transform origin", oneLiner];
GGError.Blink[gargoyleData.feedback];
};
gargoyleData: GargoyleData ← NARROW[clientData];
scalar:
REAL ← GGParseIn.ReadBlankAndReal[
IO.
RIS[rope:
NARROW[event.rest.first]] !
IO.Error => {
GGError.AppendHerald[gargoyleData.feedback, "Select a meaningful real number", oneLiner];
GGError.Blink[gargoyleData.feedback];
GOTO Abort; -- can't put RETURN in an error handler
}];
IF scalar = 0.0
THEN {
-- If either nothing or 0.0 was selected
GGError.AppendHerald[gargoyleData.feedback, "Select a meaningful real number", oneLiner];
GGError.Blink[gargoyleData.feedback];
RETURN;
};
{
anchorPoint: Point ← GGCaret.GetPoint[gargoyleData.anchor];
transform: ImagerTransformation.Transformation;
SELECT event.first
FROM
$TranslateX => transform ← ImagerTransformation.Translate[[gargoyleData.hitTest.scaleUnit*scalar, 0.0]];
$TranslateY => transform ← ImagerTransformation.Translate[[0.0, gargoyleData.hitTest.scaleUnit*scalar]];
$Rotate => IF GGCaret.Exists[gargoyleData.anchor] THEN transform ← GGTransform.RotateAboutPoint[anchorPoint, scalar] ELSE {ComplainAboutAnchor[]; RETURN};
$Scale => IF GGCaret.Exists[gargoyleData.anchor] THEN transform ← GGTransform.ScaleAboutPoint[anchorPoint, scalar] ELSE {ComplainAboutAnchor[]; RETURN};
$ScaleX => IF GGCaret.Exists[gargoyleData.anchor] THEN transform ← GGTransform.ScaleUnevenAboutPoint[anchorPoint, scalar, 1.0] ELSE {ComplainAboutAnchor[]; RETURN};
$ScaleY => IF GGCaret.Exists[gargoyleData.anchor] THEN transform ← GGTransform.ScaleUnevenAboutPoint[anchorPoint, 1.0, scalar] ELSE {ComplainAboutAnchor[]; RETURN};
ENDCASE => ERROR;
DoTheTransforms[gargoyleData, transform];
}
}; -- end TransRotScale
SixPointTransform:
PUBLIC
PROC [event:
LIST
OF
REF
ANY, clientData:
REF
ANY] = {
The first second and third selected objects should be one-segment trajectories indicating the six-point transform. All other selected objects are to be transformed. There are obvious problems with this scheme. Suggestions welcome. -- Bier, July 28, 1986.
gargoyleData: GargoyleData ← NARROW[clientData];
hotGen: SequenceGenerator ← GGSelect.SelectedSequences[gargoyleData.scene, hot];
points: ARRAY [0..5] OF Point;
transform: ImagerTransformation.Transformation;
seq: Sequence;
FOR i:
NAT
IN [0..1]
DO
seq ← GGSequence.NextSequence[hotGen];
IF seq = NIL THEN GOTO Abort;
IF GGTraj.HiJoint[seq.traj] < 2 THEN GOTO Abort;
FOR j:
NAT
IN [0..2]
DO
points[j + i*3] ← GGTraj.FetchJointPos[seq.traj, j];
ENDLOOP;
ENDLOOP;
transform ← GGTransform.SixPoints[points];
DoTheTransforms[gargoyleData, transform];
EXITS
Abort => {
gargoyleData: GargoyleData ← NARROW[clientData];
GGError.AppendHerald[gargoyleData.feedback, "Not enough arguments for a six-point transform", oneLiner];
GGError.Blink[gargoyleData.feedback];
};
}; -- end SixPointTransform
FourPointTransform:
PUBLIC
PROC [event:
LIST
OF
REF
ANY, clientData:
REF
ANY] = {
The first and second selected objects should be one-segment trajectories representing the four-point transform. All other selected objects are to be transformed. There are obvious problems with this scheme. Suggestions welcome. -- Bier, July 28, 1986.
gargoyleData: GargoyleData ← NARROW[clientData];
hotGen: SequenceGenerator ← GGSelect.SelectedSequences[gargoyleData.scene, hot];
points: ARRAY [0..3] OF Point;
transform: ImagerTransformation.Transformation;
seq: Sequence;
FOR i:
NAT
IN [0..1]
DO
seq ← GGSequence.NextSequence[hotGen];
IF seq = NIL THEN GOTO Abort;
IF GGTraj.HiJoint[seq.traj] < 1 THEN GOTO Abort;
FOR j:
NAT
IN [0..1]
DO
points[j + i*2] ← GGTraj.FetchJointPos[seq.traj, j];
ENDLOOP;
ENDLOOP;
transform ← GGTransform.FourPoints[points];
DoTheTransforms[gargoyleData, transform];
EXITS
Abort => {
gargoyleData: GargoyleData ← NARROW[clientData];
GGError.AppendHerald[gargoyleData.feedback, "Not enough arguments for a four-point transform", oneLiner];
GGError.Blink[gargoyleData.feedback];
};
}; -- end SixPointTransform
DoTheTransforms:
PROC [gargoyleData: GargoyleData, transform: ImagerTransformation.Transformation] = {
entityGen: EntityGenerator;
The boundbox before anything has moved.
gargoyleData.refresh.startBoundBox^ ← GGBoundBox.BoundBoxOfMoving[gargoyleData.scene, normal]^;
entityGen ← GGSelect.SelectedStuff[gargoyleData.scene, normal];
FOR entity:
REF
ANY ← GGObjects.NextEntity[entityGen], GGObjects.NextEntity[entityGen]
UNTIL entity =
NIL
DO
WITH entity
SELECT
FROM
sliceD: SliceDescriptor => {
sliceD.slice.class.transform[sliceD.slice, sliceD.parts, transform];
};
outlineD: OutlineDescriptor => {
outlineD.slice.class.transform[outlineD.slice, outlineD.parts, transform];
};
ENDCASE => ERROR;
ENDLOOP;
The boundbox after everything has moved.
GGBoundBox.EnlargeByBox[bBox: gargoyleData.refresh.startBoundBox, by: GGBoundBox.BoundBoxOfMoving[gargoyleData.scene, normal]];
GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE];
};
Area Selection Operations
AreaSelectAll:
PUBLIC
PROC [event:
LIST
OF
REF
ANY, clientData:
REF
ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
Select all objects.
GGSelect.DeselectAll[gargoyleData.scene, normal];
GGSelect.SelectAll[gargoyleData.scene, normal];
GGEvent.SawTextFinish[NIL, gargoyleData];
GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE];
}; -- end AreaSelectAll
AreaSelectNew:
PUBLIC
PROC [event:
LIST
OF
REF
ANY, clientData:
REF
ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
Select all objects within area(s) bounded by original selection(s) and deselect original selection
AreaSelectAux[gargoyleData, TRUE, TRUE];
};
AreaSelectExtend:
PUBLIC
PROC [event:
LIST
OF
REF
ANY, clientData:
REF
ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
Add all objects within area(s) bounded by original selection(s) to current selection
AreaSelectAux[gargoyleData, FALSE, TRUE];
};
AreaSelectNewAndDelete:
PUBLIC
PROC [event:
LIST
OF
REF
ANY, clientData:
REF
ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
Select all objects within area(s) bounded by original selection(s) and delete original selection
selectedGen: EntityGenerator ← GGSelect.SelectedStuff[gargoyleData.scene, normal]; -- save original selection
startBox: BoundBox ← GGBoundBox.BoundBoxOfSelected[gargoyleData.scene, normal];
AreaSelectAux[gargoyleData: gargoyleData, new: TRUE, paint: FALSE];
FOR formerSelected:
REF
ANY ← GGObjects.NextEntity[selectedGen], GGObjects.NextEntity[selectedGen]
UNTIL formerSelected=
NIL
DO
WITH formerSelected
SELECT
FROM
outlineD: OutlineDescriptor => {
N.B.: DeleteOutline deletes too much
GGSelect.DeselectEntityAllClasses[outlineD.slice, gargoyleData.scene];
GGObjects.DeleteOutline[gargoyleData.scene, outlineD.slice];
};
sliceD: SliceDescriptor => {
GGSelect.DeselectEntityAllClasses[sliceD.slice, gargoyleData.scene];
GGSlice.DeleteSlice[gargoyleData.scene, sliceD.slice];
};
ENDCASE => ERROR;
ENDLOOP;
IF
NOT startBox.null
THEN {
-- there were some original selections
gargoyleData.refresh.startBoundBox^ ← startBox^;
GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE];
};
};
AreaSelectDegenerate:
PUBLIC
PROC [event:
LIST
OF
REF
ANY, clientData:
REF
ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
Select all degenerate (i.e. segments having co-located endpoints) segments.
trajGen: TrajGenerator ← GGObjects.TrajsInScene[gargoyleData.scene];
GGSelect.DeselectAll[gargoyleData.scene, normal];
FOR traj: Traj ← GGObjects.NextTraj[trajGen], GGObjects.NextTraj[trajGen]
UNTIL traj=
NIL
DO
segGen: SegmentGenerator ← GGSequence.SegmentsInTraj[traj];
FOR next: GGSequence.SegAndIndex ← GGSequence.NextSegmentAndIndex[segGen], GGSequence.NextSegmentAndIndex[segGen]
UNTIL next.seg=
NIL
DO
IF next.seg.lo.x=next.seg.hi.x
AND next.seg.lo.y=next.seg.hi.y
THEN {
seq: Sequence ← GGSequence.CreateFromSegments[traj, next.index, next.index];
GGSelect.SelectSequence[seq, gargoyleData.scene, normal];
};
ENDLOOP;
ENDLOOP;
GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE];
};
AreaSelectAux:
PROC [gargoyleData: GargoyleData, new:
BOOL ←
TRUE, paint:
BOOL ←
TRUE] = {
Within:
PROC [test, bound: GGBoundBox.BoundBox]
RETURNS [
BOOL] = {
RETURN [ test.hiX <= bound.hiX AND test.loX >= bound.loX AND test.hiY <= bound.hiY AND test.loY >= bound.loY ];
};
selectedGen: EntityGenerator;
sceneGen: EntityGenerator;
IF GGSelect.NoSelections[gargoyleData.scene, normal] THEN RETURN;
selectedGen ← GGSelect.SelectedStuff[gargoyleData.scene, normal];
IF new
THEN GGSelect.DeselectAll[gargoyleData.scene, normal];
-- get rid of old selection
outer loop on each individual selected object
FOR nextSelected:
REF
ANY ← GGObjects.NextEntity[selectedGen], GGObjects.NextEntity[selectedGen]
UNTIL nextSelected=
NIL
DO
sBox: BoundBox;
currentEntity: REF ANY;
WITH nextSelected
SELECT
FROM
outlineD: OutlineDescriptor => {
sBox ← outlineD.slice.class.getBoundBox[outlineD.slice, outlineD.parts];
currentEntity ← outlineD.slice;
};
sliceD: SliceDescriptor => {
sBox ← sliceD.slice.class.getBoundBox[sliceD.slice, sliceD.parts];
currentEntity ← sliceD.slice;
};
ENDCASE => ERROR;
inner loop on each individual scene object
sceneGen ← GGObjects.TopLevelEntitiesInScene[gargoyleData.scene];
FOR nextEntity:
REF
ANY ← GGObjects.NextEntity[sceneGen], GGObjects.NextEntity[sceneGen]
UNTIL nextEntity=
NIL
DO
IF nextEntity=currentEntity THEN LOOP;
IF NOT GGSelect.IsSelectedInFull[nextEntity, gargoyleData.scene, normal] THEN -- don't reprocess
WITH nextEntity
SELECT
FROM
outline: Outline => {
IF Within[outline.class.getBoundBox[outline,
NIL], sBox]
THEN {
GGSelect.SelectEntireOutline[outline, gargoyleData.scene, normal];
};
};
slice: Slice => {
IF Within[slice.class.getBoundBox[slice,
NIL], sBox]
THEN {
GGSelect.SelectSlice[slice, slice.class.newParts[slice, NIL, topLevel], gargoyleData.scene, normal ];
};
};
ENDCASE => ERROR;
ENDLOOP;
ENDLOOP;
IF paint THEN GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE];
}; -- end AreaSelectAux
Stuffing
StuffIt:
PUBLIC
PROC [event:
LIST
OF
REF
ANY, clientData:
REF
ANY] = {
StuffItAux[event, clientData, print];
};
StuffItScreen:
PUBLIC
PROC [event:
LIST
OF
REF
ANY, clientData:
REF
ANY] = {
StuffItAux[event, clientData, screen];
};
StuffItAux:
PROC [event:
LIST
OF
REF
ANY, clientData:
REF
ANY, displayStyle: GGInterfaceTypes.DisplayStyle] = {
The bound box of the Gargoyle selection will be used as a clipping/translation box. The Gargoyle scene will be clipped to the box, translated so the box lower left corner is at [0.0, 0.0], and stuffed.
DoStuff:
PROC [context: Imager.Context] = {
DoIt:
PROC = {
tempQuality: GGInterfaceTypes.QualityMode ← gargoyleData.camera.quality;
tempStyle: GGInterfaceTypes.DisplayStyle ← gargoyleData.camera.displayStyle;
gargoyleData.camera.quality ← quality;
gargoyleData.camera.displayStyle ← displayStyle;
context.TranslateT[t: [-bRect.x, -bRect.y]];
GGRefresh.InterpressEntireScene[context, gargoyleData];
gargoyleData.camera.quality ← tempQuality;
gargoyleData.camera.displayStyle ← tempStyle;
};
Imager.DoSaveAll[context, DoIt];
};
gargoyleData: GargoyleData ← NARROW[clientData];
bRect: ImagerTransformation.Rectangle ← GGBoundBox.RectangleFromBoundBox[GGBoundBox.BoundBoxOfSelected[gargoyleData.scene, normal] ];
IF GGSelect.NoSelections[gargoyleData.scene, normal]
THEN {
GGError.AppendHerald[gargoyleData.feedback, "Select some objects for stuffing", oneLiner];
GGError.Blink[gargoyleData.feedback];
}
ELSE ImagerArtwork.PasteArtwork[action: DoStuff, bounds: [0.0, 0.0, bRect.w, bRect.h], m: ImagerArtwork.Points[], clip: TRUE];
};
Snapshots
IPSnapShot:
PUBLIC
PROC [event:
LIST
OF
REF
ANY, clientData:
REF
ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
This command is executed in the middle of a dragging operation to make an interpress master of the current state of the screen. Luckily, all dragging operations use the same painting commands, namely:
GGWindow.RestoreScreenAndInvariants[paintAction: $DuringMotion, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE];
We wish to have the same effect as these two commands except written to an interpress master. We will create an interpress master named snapshot.ip and use GGRefresh.SnapShot to draw the scene into it.
DoMakeInterpress:
PROC [dc: Imager.Context] = {
Imager.ScaleT[dc, pixelsPerMeter];
tempStyle ← gargoyleData.camera.displayStyle;
gargoyleData.camera.displayStyle ← print;
GGRefresh.SnapShot[dc, gargoyleData];
gargoyleData.camera.displayStyle ← tempStyle;
};
tempStyle: GGInterfaceTypes.DisplayStyle;
ipRef: ImagerInterpress.Ref;
fullName: Rope.ROPE;
success: BOOL;
pixelsPerMeter: REAL = 0.0254/72.0;
startTime: BasicTime.GMT;
endTime: BasicTime.GMT;
totalTime: INT;
msgRope: Rope.ROPE;
[fullName, success] ← CheckSnapShotName[gargoyleData];
IF NOT success THEN RETURN;
ipRef ← ImagerInterpress.Create[fullName];
msgRope ← IO.PutFR["Writing to IP file: %g . . . ", [rope[fullName]]];
GGError.Append[gargoyleData.feedback, msgRope, begin];
startTime ← BasicTime.Now[];
ImagerInterpress.DoPage[ipRef, DoMakeInterpress, 1.0];
ImagerInterpress.Close[ipRef];
endTime ← BasicTime.Now[];
totalTime ← BasicTime.Period[startTime, endTime];
msgRope ← IO.PutFR[" Done in time (%r)", [integer[totalTime]]];
GGError.Append[gargoyleData.feedback, msgRope, end];
};
CheckSnapShotName:
PROC [gargoyleData: GargoyleData]
RETURNS [fullName: Rope.
ROPE ←
NIL, success:
BOOL ←
TRUE] = {
cp: FS.ComponentPositions;
ipName: Rope.ROPE ← "snapshot.ip";
[fullName, cp, ] ←
FS.ExpandName[ipName, gargoyleData.currentWDir
!
FS.Error => {
GGError.Append[gargoyleData.feedback, " FS Error during name expansion", oneLiner];
success ← FALSE;
CONTINUE;
}
];
IF success
AND Rope.Equal[s1: Rope.Substr[base: fullName, start: cp.ext.start, len: cp.ext.length], s2: "gargoyle", case:
FALSE]
THEN {
GGError.Append[gargoyleData.feedback, " .gargoyle extension for IP files not allowed", oneLiner];
success ← FALSE;
};
IF success AND cp.ext.length=0 THEN fullName ← Rope.Concat[fullName, ".IP"]; -- add IP extension
};
InitStats:
PROC [] = {
interval: GGStatistics.Interval;
interval ← GGStatistics.CreateInterval[$AddChar];
GGStatistics.AddInterval[interval, GGStatistics.GlobalTable[]];
};
InitStats[];
END.