DIRECTORY BasicTime, CubicSplines, FS, GGBasicTypes, GGBoundBox, GGBuiltinShapes, AtomButtons, 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 = AtomButtons.ScalarButtonClient; ScalarButtonHandle: TYPE = AtomButtons.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 = AtomButtons.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 }; }; 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] = { 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]; }; Weld: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; 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]; 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]; GGSelect.SelectEntireOutline[newOutline, gargoyleData.scene, normal]; weldPoint _ GGTraj.FetchJointPos[newTraj, GGTraj.HiJoint[IF firstEnd=hi THEN firstTraj ELSE secondTraj]]; GGCaret.SetAttractor[gargoyleData.caret, weldPoint, NIL]; GGCaret.SitOn[gargoyleData.caret, NIL]; 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]; 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]; 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]; }; 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] = { ReplaceWithLine: GGSelect.RunProc = { 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] = { 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] = { 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; 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] = { 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 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=$Conic THEN GOTO ConicsAreNotDone; GGSelect.DeselectAll[gargoyleData.scene, normal]; GGOutline.SaveSelectionInTraj[traj, normal, gargoyleData.scene]; GGOutline.SaveSelectionInTraj[traj, hot, gargoyleData.scene]; [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; }; 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]; } EXITS Abort => NULL; }; -- end TransRotScale SixPointTransform: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { 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] = { 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; 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; 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]; }; AreaSelectAll: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; 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]; AreaSelectAux[gargoyleData, TRUE, TRUE]; }; AreaSelectExtend: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; AreaSelectAux[gargoyleData, FALSE, TRUE]; }; AreaSelectNewAndDelete: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; 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 => { 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]; 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 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; 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 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] = { 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]; }; IPSnapShot: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; 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. 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 28, 1987 3:02:30 pm PST Shape Menu Operations add a "picture frame" of a specified size to the image, origin at [0.0, 0.0], strokeWidth=9. Edit Menu Operations 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. Replace the old outlines with the new welded one. Select the new outline. Move the caret to the weld spot Compute the new bounding box. Modify the Traj in place to close it. move the caret to the weld spot 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. Replace all selected runs with line segments. RunProc: TYPE = PROC [run: Sequence] RETURNS [traj: Traj]; event.first = $DescribeCurve, event.rest = $Selected or $Caret Adds a new control point to a segment Must save select data now so that segment copy (in CSControlPointAdd) will reflect selection data. Deletes all selected control points from segments Save select data now so that segment copy (in CSControlPointAdd) will reflect selection data. [Artwork node; type 'ArtworkInterpress on' to command tool] Transform Menu Operations 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. 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. The boundbox before anything has moved. The boundbox after everything has moved. Area Selection Operations Select all objects. Select all objects within area(s) bounded by original selection(s) and deselect original selection Add all objects within area(s) bounded by original selection(s) to current selection Select all objects within area(s) bounded by original selection(s) and delete original selection N.B.: DeleteOutline deletes too much Select all degenerate (i.e. segments having co-located endpoints) segments. outer loop on each individual selected object inner loop on each individual scene object Stuffing 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. Snapshots 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. Ê2C˜codešœ™Kšœ<™Kšœœ ˜0šœ˜Kšœ;  ˜Ešœ˜KšœR˜RKšœœœ%˜4šœœœ˜K–B[gargoyleData: REF ANY, msg: ROPE, msgType: GGError.MsgType]šœV˜VKšœ%˜%Kšœ˜K˜—šœ#œœ˜/K–B[gargoyleData: REF ANY, msg: ROPE, msgType: GGError.MsgType]šœZ˜ZKšœ%˜%Kšœ˜K˜—šœœ˜KšœŽ˜ŽKšœ„˜„Kšœœ˜—K˜—Kšœœ˜—K˜K˜—šžœœœ œœœœœœ˜NJ™%Jšœœ ˜0Jšœ œœ˜J˜Jšœœ˜ J˜J˜Jšœ œ˜J˜J˜7J–![caret: GGInterfaceTypes.Caret]šœ5˜5š˜Jšœ œœœ ˜)šœ œ˜˜Jš œ œ œ œ œ9˜iJš œœ œœœ ˜;J˜—˜Jšœ œœ ˜8Jš œ œ œ œ œ6˜fJš œœ œœœ ˜;J˜—Jšœœ˜—šœœœ ˜6Jš¡b™b—Jšœ1˜1JšœA Ðcz  ˜WJšœ> ˜WJ˜BJ˜&Jšœœ+œœ˜@¡’˜ r jHÄŽæ ¢ ¨ÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “ •  —¡¡¨  Š¡²“Á attractor– k é r jÇÄ, a ¢ ¨ÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “ •  —¡¡¨  Š¡²“ÁnewSeg1– k é r jÄ+[ ¢ ¨ÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “ •  —¡¡¨  Š¡²“ÁnewSeg2– k é¢¯“¡¡¨¢·“¢°“-™ÄA ­Ä¥W—-—ÄvéÄKЇ—˜¢¯“¡¡¨¢·“¢°“¤3™÷—˜ r j½ý ¢ ¨ÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “ •  —¡¡¨  Š¡²“ÁnewRun– k é r j °“¢¯“¡¡¨ÄbKQÄLî­ÄbKQÄ'ýS¡¹¢¯“¡¡¨ÄbKQÄ'ýSÄMé Ä'ýS¡¹¢¯“¡¡¨ÄMé Ä'ýSÄMé ÄLî­¡¹¢¯“¡¡¨ÄMé ÄLî­ÄbKQÄLî­¡¹ k é r jèÄE[ ¢ ¨ÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “ •  —¡¡¨  Š¡²“ÁnewJoint– k é k é k é k gšž=™=J˜7J˜'Jšœœ,œœ˜=Jšœœ,œœ˜=Jšœ(˜(Jšœ"œ˜'Jšœ2œœ ˜OJ˜!Jšœ`˜`Jšœ,˜,K–)[traj: GGModelTypes.Traj, index: NAT]šœ(˜(Jšœ"œ˜'J˜2KšœŒœ œœ˜ºš˜˜Jšœ^˜^Kšœ%˜%K˜—˜JšœN˜NKšœ%˜%K˜——Kšœ˜—J˜—K˜K™šž œœœ œœœœœœ˜Lšžœœ˜Kšœ^˜^Kšœ%˜%K˜—Kšœœ ˜0š œœœœœœ ˜`JšœY˜YKšœ%˜%Jšœ '˜3Jšœ˜—šœœ (˜@JšœY˜YKšœ%˜%Jšœ˜Jšœ˜—šœ˜Kšœ;˜;Kšœ/˜/šœ ˜Kšœh˜hKšœh˜hKš œ œ%œ?œœ˜šKš œ œ%œ>œœ˜˜Kš œ œ%œIœœ˜¤Kš œ œ%œIœœ˜¤Kšœœ˜—Kšœ)˜)K˜—š˜Kšœ œ˜—Jšœ ˜J˜—šžœœœ œœœœœœ˜PKšœ‚™‚Kšœœ ˜0KšœP˜PKšœœœ˜Kšœ/˜/Kšœ˜šœœœ˜Kšœ&˜&Kšœœœœ˜Kšœœœ˜0šœœœ˜Kšœ4˜4Kšœ˜—Kšœ˜—Kšœ*˜*Kšœ)˜)š˜šœ ˜ Jšœœ ˜0Jšœh˜hJšœ%˜%Jšœ˜——Jšœ ˜J˜—šžœœœ œœœœœœ˜QKšœÿ™ÿKšœœ ˜0KšœP˜PKšœœœ˜Kšœ/˜/Kšœ˜šœœœ˜Kšœ&˜&Kšœœœœ˜Kšœœœ˜0šœœœ˜Kšœ4˜4Kšœ˜—Kšœ˜—Kšœ+˜+Kšœ)˜)š˜šœ ˜ Jšœœ ˜0Jšœi˜iJšœ%˜%Jšœ˜——Jšœ ˜J˜—šžœœQ˜fKšœ˜Kš¡'™'Kšœ_˜_Jšœ?˜?š œ œœDœ œ˜lšœœ˜šœ˜KšœD˜DJ˜—šœ ˜ JšœJ˜JJ˜—Jšœœ˜—Jšœ˜—Jš¡(™(Jšœ˜KšœŒœ œœ˜ºKšœ˜—J™K™šž œœœ œœœœœœ˜LKšœœ ˜0K™Kšœ1˜1Kšœ/˜/Kšœœ˜)Kšœyœ œœ˜§Kšœ ˜—šž œœœ œœœœœœ˜LKšœœ ˜0K™bKšœœœ˜(K˜K˜—šžœœœ œœœœœœ˜OKšœœ ˜0K™TKšœœœ˜)Kšœ˜K˜—šžœœœ œœœœœœ˜UKšœœ ˜0K™`KšœS ˜mKšœO˜OKšœ/œ œ˜Cš œœœHœœ˜~šœœ˜–;[seq: GGModelTypes.Sequence, scene: GGModelTypes.Scene]˜ Kš¡$™$KšœF˜FKšœ<˜[scene: GGModelTypes.Scene, cluster: GGModelTypes.Cluster]šœ˜KšœD˜DKšœ6˜6Kšœ˜—Kšœœ˜—Kšœ˜—šœœœ &˜BKšœ0˜0KšœŒœ œœ˜ºK˜—Kšœ˜K˜—šžœœœ œœœœœœ˜SKšœœ ˜0KšœK™KKšœD˜DKšœ1˜1šœGœœ˜[Kšœ;˜;šœoœ œ˜‡šœœœ˜EKšœL˜LKšœ9˜9K˜—Kšœ˜ —Kšœ˜—Kšœyœ œœ˜§Kšœ˜K˜—š ž œœ#œœ œœ˜Zšžœœ$œœ˜BKšœœœœ˜oK˜—Kšœ˜Kšœ˜K–_[gargoyleData: GGInterfaceTypes.GargoyleData, selectClass: GGInterfaceTypes.SelectionClass]šœ3œœ˜AKšœA˜Ašœœ3 ˜YKš¡-™-—š œœœIœœ˜{K˜Kšœœœ˜šœœ˜šœ ˜ KšœH˜HKšœ˜K˜—šœ˜KšœB˜BKšœ˜K˜—Kšœœ˜Kš¡*™*—KšœA˜Aš œ œœCœ œ˜qKšœœœ˜&KšœœCœ ˜`šœ œ˜šœ˜šœ+œ œ˜?KšœB˜BK˜—Kšœ˜—šœ˜šœ'œ œ˜;Kšœe˜eK˜—Kšœ˜—Kšœœ˜—Kšœ˜—Kšœ˜—Kš œœzœ œœ˜µKšœ ˜K˜—K™K™šŸœœœ œœœœœœ˜FKšœ%˜%K˜K˜—šŸ œœœ œœœœœœ˜LKšœ&˜&K˜K˜—šŸ œœ œœœœœœ2˜oK™Êšžœœ˜+šžœœ˜KšœH˜HKšœL˜LKšœ&˜&Kšœ0˜0Kšœ,˜,Kšœ7˜7Kšœ*˜*Kšœ-˜-Kšœ˜—Kšœ ˜ K˜—Kšœœ ˜0K–O[m: ImagerTransformation.Transformation, r: ImagerTransformation.Rectangle]šœ…˜…šœ3œ˜;KšœZ˜ZJšœ%˜%K˜—Kšœtœ˜~K˜—K˜K™ šŸ œœœ œœœœœœ˜IKšœœ ˜0K™ÉKšœuœ œœ™¤K™Êšžœœ˜/K˜"Kšœ-˜-Kšœ)˜)Kšœ%˜%Kšœ-˜-K˜—Kšœ)˜)Kšœ˜Kšœœ˜Kšœ œ˜Kšœœ˜#Kšœœ˜Kšœœ˜Kšœ œ˜Kšœœ˜K˜Kšœ6˜6Kšœœ œœ˜Kšœ*˜*Kšœ œ:˜FKšœ6˜6Kš¡˜Kšœ6˜6Kšœ˜Kš¡˜Kšœ1˜1Kšœ œ3˜?Kšœ4˜4K˜—šžœœœœœ œœ˜rKšœœ˜Kšœ œ˜"šœœ,˜Ašœœ ˜KšœU˜UKšœ œ˜Kšœ˜ K˜—Kšœ˜—–9[base: ROPE, start: INT _ 0, len: INT _ 2147483647]šœ œlœœ˜‡Kšœb˜bKšœ œ˜K˜—Kšœ œœ* ˜`K˜K˜—šž œœ˜K˜ K˜1K˜?K˜—K˜K˜ J˜Kšœ˜—…—îÞ1