DIRECTORY AtomButtons, AtomButtonsTypes, BasicTime, CodeTimer, ColorTool, CubicSplines, Feedback, FS, GGAlign, GGBasicTypes, GGBoundBox, GGBuiltinShapes, GGCaret, GGEvent, GGInterface, GGInterfaceTypes, GGModelTypes, GGOutline, GGParseIn, GGRefresh, GGScene, GGSegment, GGSegmentTypes, GGSelect, GGSequence, GGSlice, GGState, GGTraj, GGTransform, GGUtility, GGWindow, Imager, ImagerArtwork, ImagerInterpress, ImagerTransformation, IO, List, Rope, TiogaMenuOps, TIPUser, Vectors2d, ViewerClasses, ViewerOps, ViewerTools; GGEventImplC: CEDAR PROGRAM IMPORTS BasicTime, CodeTimer, ColorTool, Feedback, FS, GGAlign, GGBoundBox, GGBuiltinShapes, GGCaret, GGEvent, GGInterface, GGOutline, GGParseIn, GGRefresh, GGScene, GGSegment, GGSelect, GGSequence, GGSlice, GGState, GGTraj, GGTransform, GGUtility, GGWindow, Imager, ImagerArtwork, ImagerInterpress, ImagerTransformation, IO, List, Rope, TiogaMenuOps, TIPUser, Vectors2d, ViewerOps, ViewerTools EXPORTS GGEvent = BEGIN BitVector: TYPE = GGModelTypes.BitVector; BoundBox: TYPE = GGModelTypes.BoundBox; ControlPointGenerator: TYPE = GGModelTypes.ControlPointGenerator; EntityGenerator: TYPE = GGModelTypes.EntityGenerator; GGData: TYPE = GGInterfaceTypes.GGData; 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; WalkProc: TYPE = GGModelTypes.WalkProc; SetGravityExtent: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; graphicsState: AtomButtonsTypes.GraphicsState _ ggData.hitTest.gravityExtentButton; inches: REAL _ NARROW[event.rest.first, REF REAL]^; GGWindow.SetGravityExtent[ggData, inches]; }; SetShowColors: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; boolRope: Rope.ROPE _ NARROW[event.rest.first]; showColors: BOOL _ GGUtility.RopeToBool[boolRope]; GGState.SetShowColors[ggData, showColors]; }; SetGravity: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; boolRope: Rope.ROPE _ NARROW[event.rest.first]; setGravity: BOOL _ GGUtility.RopeToBool[boolRope]; GGState.SetGravity[ggData, setGravity]; }; SetMidpoints: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; boolRope: Rope.ROPE _ NARROW[event.rest.first]; setMidpoints: BOOL _ GGUtility.RopeToBool[boolRope]; GGState.SetMidpoints[ggData, setMidpoints]; }; SetHeuristics: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; boolRope: Rope.ROPE _ NARROW[event.rest.first]; setHeuristics: BOOL _ GGUtility.RopeToBool[boolRope]; GGState.SetHeuristics[ggData, setHeuristics]; }; SetDefaultFont: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; fontRope: Rope.ROPE _ NARROW[event.rest.first]; }; PolygonInCircle: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { outline: Outline; bBox: BoundBox; ggData: GGData _ NARROW[clientData]; caretPoint: Point _ GGCaret.GetPoint[ggData.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, ggData.hitTest.scaleUnit, ggData.defaults]; GGScene.AddOutline[ggData.scene, outline, -1]; GGSelect.DeselectAll[ggData.scene, normal]; GGSelect.SelectEntireSlice[outline, ggData.scene, normal]; bBox _ GGBoundBox.BoundBoxOfSelected[ggData.scene, normal]; ggData.refresh.startBoundBox^ _ bBox^; ggData.refresh.addedObject _ outline; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectAdded, ggData: ggData, remake: sceneBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE]; }; NewBox: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { slice: Slice; sliceParts: SliceDescriptor; bBox: BoundBox; ggData: GGData _ NARROW[clientData]; caretPoint: Point _ GGCaret.GetPoint[ggData.caret]; sideLength: REAL _ NARROW[event.rest.first, REF REAL]^; slice _ GGBuiltinShapes.Box[caretPoint, sideLength*ggData.hitTest.scaleUnit, ggData.defaults]; GGScene.AddSlice[ggData.scene, slice, -1]; GGSelect.DeselectAll[ggData.scene, normal]; sliceParts _ slice.class.newParts[slice, NIL, slice]; GGSelect.SelectSlice[sliceParts, ggData.scene, normal]; bBox _ GGBoundBox.BoundBoxOfSelected[ggData.scene, normal]; ggData.refresh.startBoundBox^ _ bBox^; ggData.refresh.addedObject _ slice; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectAdded, ggData: ggData, remake: sceneBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE]; }; NewCircle: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { slice: Slice; sliceParts: SliceDescriptor; bBox: BoundBox; ggData: GGData _ NARROW[clientData]; caretPoint: Point _ GGCaret.GetPoint[ggData.caret]; radius: REAL _ NARROW[event.rest.first, REF REAL]^; slice _ GGBuiltinShapes.Circle[caretPoint, radius*ggData.hitTest.scaleUnit, ggData.defaults]; GGScene.AddSlice[ggData.scene, slice, -1]; GGSelect.DeselectAll[ggData.scene, normal]; sliceParts _ slice.class.newParts[slice, NIL, slice]; GGSelect.SelectSlice[sliceParts, ggData.scene, normal]; bBox _ GGBoundBox.BoundBoxOfSelected[ggData.scene, normal]; ggData.refresh.startBoundBox^ _ bBox^; ggData.refresh.addedObject _ slice; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectAdded, ggData: ggData, remake: sceneBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE]; }; NewKnotchedLine: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { outline: Outline; bBox: BoundBox; ggData: GGData _ NARROW[clientData]; caretPoint: Point _ GGCaret.GetPoint[ggData.caret]; length: REAL _ NARROW[event.rest.first, REF REAL]^; segCount: INT _ NARROW[event.rest.rest.first, REF INT]^; p1: Point; p1 _ Vectors2d.Add[caretPoint, [length*ggData.hitTest.scaleUnit, 0.0]]; outline _ GGBuiltinShapes.KnotchedLine[p0: caretPoint, p1: p1, segmentCount: segCount]; GGScene.AddOutline[ggData.scene, outline, -1]; GGSelect.DeselectAll[ggData.scene, normal]; GGSelect.SelectEntireSlice[outline, ggData.scene, normal]; bBox _ GGBoundBox.BoundBoxOfSelected[ggData.scene, normal]; ggData.refresh.startBoundBox^ _ bBox^; ggData.refresh.addedObject _ outline; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectAdded, ggData: ggData, remake: sceneBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE]; }; NewArrow: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { OPEN Vectors2d; outline: Outline; traj: Traj; seg: Segment; bBox: BoundBox; success: BOOL; ggData: GGData _ 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[ggData.caret]; shaftLength _ shaftLength * ggData.hitTest.scaleUnit; -- convert to screen dots. barbLength _ barbLength * ggData.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, fillColor: Imager.black]; GGScene.AddOutline[ggData.scene, outline, -1]; GGSelect.DeselectAll[ggData.scene, normal]; GGSelect.SelectEntireSlice[outline, ggData.scene, normal]; bBox _ GGBoundBox.BoundBoxOfSelected[ggData.scene, normal]; ggData.refresh.startBoundBox^ _ bBox^; ggData.refresh.addedObject _ outline; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectAdded, ggData: ggData, remake: sceneBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE]; }; Frame: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ 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[]]; [] _ sliceD.slice.class.setStrokeWidth[sliceD.slice, sliceD.parts, 9.0]; GGScene.AddSlice[ggData.scene, sliceD.slice, -1]; ggData.refresh.startBoundBox^ _ sliceD.slice.boundBox^; ggData.refresh.addedObject _ sliceD.slice; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectAdded, ggData: ggData, remake: sceneBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE]; }; ApplyAllDefaults: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; bBox: BoundBox; sliceDescGen: SliceDescriptorGenerator; IF GGSelect.NoSelections[ggData.scene, normal] THEN RETURN; bBox _ GGBoundBox.BoundBoxOfSelected[ggData.scene, normal]; sliceDescGen _ GGSelect.SelectedSlices[ggData.scene, normal]; FOR sliceD: SliceDescriptor _ GGSelect.NextSliceDescriptor[sliceDescGen], GGSelect.NextSliceDescriptor[sliceDescGen] UNTIL sliceD=NIL DO sliceD.slice.class.setDefaults[sliceD.slice, sliceD.parts, ggData.defaults]; ENDLOOP; GGBoundBox.EnlargeByBox[bBox: bBox, by: GGBoundBox.BoundBoxOfSelected[ggData.scene, normal]]; ggData.refresh.startBoundBox^ _ bBox^; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: none, backgndOK: FALSE, edited: TRUE, okToClearFeedback: FALSE]; }; SetAllDefaults: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { }; ShowAllDefaults: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; Feedback.AppendTypescript[ggData.feedback, "--------------------------------------------", oneLiner]; GGEvent.ShowDefaultFontValues[clientData, event]; GGEvent.ShowDefaultLineColor[clientData, event]; GGEvent.ShowDefaultFillColor[clientData, event]; GGEvent.ShowDefaultStrokeValues[clientData, event]; Feedback.AppendTypescript[ggData.feedback, "--------------------------------------------", oneLiner]; }; StandardDefaults: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; ggData.defaults^ _ [ strokeWidth: 2.0, strokeJoint: round, strokeEnd: round, dashed: FALSE, pattern: NIL, offset: 0.0, length: 0.0, strokeColor: Imager.black, fillColor: GGOutline.fillColor, font: ggData.defaults.font ]; ShowAllDefaults[clientData, event]; }; Weld: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ 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[ggData.scene, normal]; [firstOutSeq, secondOutSeq, firstTraj, secondTraj, success] _ GetWeldArguments[ggData, outSeqGen]; IF NOT success THEN RETURN; IF secondOutSeq = NIL THEN {WeldToSelf[ggData, 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[newTraj, firstOutline.class.getFillColor[firstOutline]]; GGInterface.DeleteOutline[firstOutline, ggData.scene]; GGInterface.DeleteOutline[secondOutline, ggData.scene]; GGScene.AddOutline[ggData.scene, newOutline, -1]; GGSelect.SelectEntireSlice[newOutline, ggData.scene, normal]; weldPoint _ GGTraj.FetchJointPos[newTraj, GGTraj.HiJoint[IF firstEnd=hi THEN firstTraj ELSE secondTraj]]; GGCaret.SetAttractor[ggData.caret, weldPoint, NIL]; GGCaret.SitOn[ggData.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]; ggData.refresh.startBoundBox^ _ firstBox^; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: triggerBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE]; }; -- end of Weld GetWeldArguments: PROC [ggData: GGData, 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 Feedback.AppendHerald[ggData.feedback, "Select one or two open trajectories for a weld.", oneLiner]; Feedback.Blink[ggData.feedback]; RETURN[NIL, NIL, NIL, NIL, FALSE]; }; firstTraj _ firstOutSeq.fenceSeq.traj; IF firstTraj.role # open THEN { Feedback.AppendHerald[ggData.feedback, "Select one or two OPEN trajectories for a weld.", oneLiner]; Feedback.Blink[ggData.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 { Feedback.AppendHerald[ggData.feedback, "Select one or two OPEN trajectories for a weld.", oneLiner]; Feedback.Blink[ggData.feedback]; RETURN[NIL, NIL, NIL, NIL, FALSE]; }; success _ TRUE; }; WeldToSelf: PROC [ggData: GGData, traj: Traj] = { restoreBox: BoundBox; outline: Outline _ GGOutline.OutlineOfTraj[traj]; restoreBox _ GGTraj.GetBoundBox[traj]; GGOutline.SaveSelectionsInOutline[outline, ggData.scene]; GGSelect.DeselectEntityAllClasses[outline, ggData.scene]; GGTraj.CloseByDistorting[traj, lo]; outline.class.setFillColor[outline, ggData.defaults.fillColor]; GGOutline.RemakeSelectionsFromOutline[outline, ggData.scene]; GGSelect.SelectEntireSlice[outline, ggData.scene, normal]; GGCaret.SetAttractor[ggData.caret, GGTraj.FetchJointPos[traj, 0], NIL]; GGCaret.SitOn[ggData.caret, NIL]; -- this is important! GGBoundBox.EnlargeByBox[bBox: restoreBox, by: GGTraj.GetBoundBox[traj]]; ggData.refresh.startBoundBox^ _ restoreBox^; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, 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 _ Vectors2d.DistanceSquared[firstLo, secondLo]; d12 _ Vectors2d.DistanceSquared[firstLo, secondHi]; d21 _ Vectors2d.DistanceSquared[firstHi, secondLo]; d22 _ Vectors2d.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 [clientData: REF ANY, event: LIST OF REF ANY] = { MakeReplacement: GGSelect.RunProc = { GetPt: PROC[p0, dir: Point] RETURNS [pt: Point] = { gScale: REAL _ 0.33; gAngle: REAL _ 33; pt _ Vectors2d.Add[Vectors2d.Scale[Vectors2d.VectorPlusAngle[dir,gAngle], gScale*Vectors2d.Magnitude[dir] ], p0]; }; middlePoint: Point _ GGCaret.GetPoint[ggData.caret]; runSegGen: SegmentGenerator _ GGSequence.SegmentsInSequence[run]; FOR runSeg: Segment _ GGSequence.NextSegment[runSegGen], GGSequence.NextSegment[runSegGen] UNTIL runSeg=NIL DO firstPoint: Point _ runSeg.lo; lastPoint: Point _ runSeg.hi; IF traj=NIL THEN { traj _ GGTraj.CreateTraj[firstPoint]; GGTraj.SetTrajStrokeJoint[traj, run.traj.strokeJoint]; }; SELECT runSeg.class.type FROM $Line => { line: Segment _ GGSegment.MakeLine[firstPoint, middlePoint, List.Append[runSeg.props, NIL]]; GGSegment.CopyLooks[runSeg, line]; IF NOT GGTraj.AddSegment[traj, hi, line, lo] THEN ERROR; line _ GGSegment.MakeLine[middlePoint, lastPoint, List.Append[runSeg.props, NIL]]; GGSegment.CopyLooks[runSeg, line]; IF NOT GGTraj.AddSegment[traj, hi, line, lo] THEN ERROR; }; $Arc => { arc: Segment _ GGSegment.MakeArc[firstPoint, middlePoint, lastPoint, List.Append[runSeg.props, NIL]]; GGSegment.CopyLooks[runSeg, arc]; IF NOT GGTraj.AddSegment[traj, hi, arc, lo] THEN ERROR; }; $Conic => { conic: Segment _ GGSegment.MakeConic[firstPoint, middlePoint, lastPoint, 0.7, List.Append[runSeg.props, NIL]]; GGSegment.CopyLooks[runSeg, conic]; IF NOT GGTraj.AddSegment[traj, hi, conic, lo] THEN ERROR; }; $Bezier => { length: REAL _ Vectors2d.Distance[firstPoint, middlePoint]; dir: Point _ Vectors2d.Sub[middlePoint, firstPoint]; p1: Point _ GetPt[firstPoint, dir]; p2: Point _ GetPt[middlePoint, [-dir.x, -dir.y]]; bezier: Segment _ GGSegment.MakeBezier[firstPoint, p1, p2, middlePoint, List.Append[runSeg.props, NIL]]; GGSegment.CopyLooks[runSeg, bezier]; IF NOT GGTraj.AddSegment[traj, hi, bezier, lo] THEN ERROR; -- first Bezier dir _ Vectors2d.Sub[lastPoint, middlePoint]; p1 _ GetPt[middlePoint, dir]; p2 _ GetPt[lastPoint, [-dir.x, -dir.y]]; bezier _ GGSegment.MakeBezier[middlePoint, p1, p2, lastPoint, List.Append[runSeg.props, NIL]]; GGSegment.CopyLooks[runSeg, bezier]; IF NOT GGTraj.AddSegment[traj, hi, bezier, lo] THEN ERROR; -- last Bezier }; $CubicSpline => { cSpline: Segment; 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]; cSpline _ GGSegment.MakeCubicSpline[cps, naturalAL, List.Append[runSeg.props, NIL]]; GGSegment.CopyLooks[runSeg, cSpline]; IF NOT GGTraj.AddSegment[traj, hi, cSpline, lo] THEN ERROR; }; ENDCASE => ERROR; -- unknown segment type ENDLOOP; }; ggData: GGData _ NARROW[clientData]; ggData.refresh.startBoundBox^ _ GGSelect.ForEachOutlineRun[ggData.scene, normal, MakeReplacement]^; GGCaret.NoAttractor[ggData.caret]; GGCaret.SitOn[ggData.caret, NIL]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: triggerBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE] }; -- end SplitSegment Splice: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ReplaceWithLine: GGSelect.RunProc = { seg: Segment; p0, p1: Point; jointNum: INT; firstRunSeg: Segment _ GGSequence.NextSegment[GGSequence.SegmentsInSequence[run]]; 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]; GGSegment.CopyLooks[firstRunSeg, seg]; traj _ GGTraj.CreateTraj[p0]; GGTraj.SetTrajStrokeJoint[traj, run.traj.strokeJoint]; IF NOT GGTraj.AddSegment[traj, hi, seg, lo] THEN ERROR; }; ggData: GGData _ NARROW[clientData]; bBox: BoundBox; bBox _ GGSelect.ForEachOutlineRun[ggData.scene, normal, ReplaceWithLine]; ggData.refresh.startBoundBox^ _ bBox^; GGCaret.NoAttractor[ggData.caret]; GGCaret.SitOn[ggData.caret, NIL]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: triggerBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE]; }; -- end Splice DescribeCurve: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; SELECT event.rest.first FROM $Caret => GGEvent.DescribeCaretObject[clientData, event]; -- for now $Selected => { sliceDescGen: SliceDescriptorGenerator _ GGSelect.SelectedSlices[ggData.scene, normal]; sliceD: SliceDescriptor _ GGSelect.NextSliceDescriptor[sliceDescGen]; IF sliceD=NIL THEN { Feedback.Append[ggData.feedback, "Select some object for description", oneLiner]; Feedback.Blink[ggData.feedback]; RETURN; }; IF GGSelect.NextSliceDescriptor[sliceDescGen]#NIL THEN { Feedback.Append[ggData.feedback, "Select only one object for description", oneLiner]; Feedback.Blink[ggData.feedback]; RETURN; }; Feedback.Append[ggData.feedback, sliceD.slice.class.describe[sliceD], oneLiner]; }; ENDCASE => ERROR; }; AddControlPoint: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; attractor: SliceDescriptor; seg, newSeg: Segment; segNum: INT; traj, newRun, newTraj: Traj; refreshBox: BoundBox; success: BOOL; partType: TrajPartType; caretPos: Point _ GGCaret.GetPoint[ggData.caret]; attractor _ GGCaret.GetAttractor[ggData.caret]; BEGIN IF attractor = NIL THEN GOTO NotOnSpline; IF attractor.slice.class.type # $Outline THEN GOTO NotOnSpline; [success, partType, traj, ----, ----, ----, ----, seg, segNum] _ GGOutline.UnpackSimpleDescriptor[attractor]; IF NOT success OR partType # segment THEN GOTO NotOnSpline; IF seg.class.type # $CubicSpline THEN GOTO NotOnSpline; GGSelect.DeselectAll[ggData.scene, normal]; GGTraj.SaveSelection[traj, normal, ggData.scene]; -- to clear field bits GGTraj.SaveSelection[traj, hot, ggData.scene]; -- to save hot field bits newSeg _ GGSegment.CSControlPointAdd[seg, caretPos]; newRun _ GGTraj.CreateTraj[newSeg.lo]; GGTraj.SetTrajStrokeJoint[newRun, traj.strokeJoint]; 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, ggData.scene]; GGCaret.NoAttractor[ggData.caret]; GGCaret.SitOn[ggData.caret, NIL]; ggData.refresh.startBoundBox^ _ refreshBox^; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: triggerBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE]; EXITS NotOnSpline => { Feedback.Append[ggData.feedback, "Caret must lie on a cubic spline to add a control point", oneLiner]; Feedback.Blink[ggData.feedback]; }; END; }; DeleteControlPoint: PUBLIC PROC [clientData: REF ANY, event: LIST OF 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[ggData.refresh.startBoundBox, outlineBox]; -- refresh old outline GGTraj.SaveSelection[seq.traj, hot, ggData.scene]; -- to be restored by SubstituteForSeg GGTraj.SaveSelection[seq.traj, normal, ggData.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]]; newRun: Traj _ GGTraj.CreateTraj[newSeg.lo]; GGTraj.SetTrajStrokeJoint[newRun, seq.traj.strokeJoint]; IF NOT GGTraj.AddSegment[newRun, hi, newSeg, lo] THEN ERROR; [bBox: refreshBox] _ GGSelect.SubstituteForSegment[seq.traj, i, newRun, ggData.scene]; GGCaret.SetAttractor[ggData.caret, newSeg.lo, NIL]; GGBoundBox.EnlargeByBox[ggData.refresh.startBoundBox, refreshBox]; } ELSE { Feedback.Append[ggData.feedback, "Only delete control points from Cubic Splines", oneLiner]; Feedback.Blink[ggData.feedback]; }; }; ENDLOOP; }; ggData: GGData _ NARROW[clientData]; outSeqGen: GGSelect.OutlineSequenceGenerator _ GGSelect.SelectedOutlineSequences[ggData.scene, normal]; ggData.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[ggData.caret, NIL]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: triggerBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: FALSE]; }; AddJoint: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; attractor: SliceDescriptor; seg, newSeg1, newSeg2: Segment; segNum: INT; traj, newRun, newTraj: Traj; refreshBox, oldBox: BoundBox; newJoint: Joint; success: BOOL; partType: TrajPartType; caretPos: Point _ GGCaret.GetPoint[ggData.caret]; attractor _ GGCaret.GetAttractor[ggData.caret]; BEGIN IF attractor = NIL THEN GOTO NotASegment; IF attractor.slice.class.type # $Outline THEN GOTO NotASegment; [success, partType, traj, ----, ----, ----, ----, seg, segNum] _ GGOutline.UnpackSimpleDescriptor[attractor]; IF NOT success OR partType # segment THEN GOTO NotASegment; IF seg.class.type=$Conic THEN GOTO ConicsAreNotDone; GGSelect.DeselectAll[ggData.scene, normal]; GGTraj.SaveSelection[traj, normal, ggData.scene]; GGTraj.SaveSelection[traj, hot, ggData.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, ggData.scene]; GGTraj.SetTrajStrokeJoint[newTraj, traj.strokeJoint]; GGBoundBox.EnlargeByBox[refreshBox, oldBox]; GGCaret.NoAttractor[ggData.caret]; GGCaret.SitOn[ggData.caret, NIL]; ggData.refresh.startBoundBox^ _ refreshBox^; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: triggerBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE]; EXITS NotASegment => { Feedback.Append[ggData.feedback, "Caret must lie on a segment to add a joint", oneLiner]; Feedback.Blink[ggData.feedback]; }; ConicsAreNotDone => { Feedback.Append[ggData.feedback, "Can't add joints to Conics", oneLiner]; Feedback.Blink[ggData.feedback]; }; END; }; TransRotScale: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ComplainAboutAnchor: PROC = { Feedback.AppendHerald[ggData.feedback, "Place an anchor for transform origin", oneLiner]; Feedback.Blink[ggData.feedback]; }; ggData: GGData _ NARROW[clientData]; scalar: REAL _ GGParseIn.ReadBlankAndReal[IO.RIS[rope: NARROW[event.rest.first]] ! IO.Error => { Feedback.AppendHerald[ggData.feedback, "Select a meaningful real number", oneLiner]; Feedback.Blink[ggData.feedback]; GOTO Abort; -- can't put RETURN in an error handler }]; IF scalar = 0.0 THEN { -- If either nothing or 0.0 was selected Feedback.AppendHerald[ggData.feedback, "Select a meaningful real number", oneLiner]; Feedback.Blink[ggData.feedback]; RETURN; }; { anchorPoint: Point _ GGCaret.GetPoint[ggData.anchor]; transform: ImagerTransformation.Transformation; SELECT event.first FROM $TranslateX => transform _ ImagerTransformation.Translate[[ggData.hitTest.scaleUnit*scalar, 0.0]]; $TranslateY => transform _ ImagerTransformation.Translate[[0.0, ggData.hitTest.scaleUnit*scalar]]; $Rotate => IF GGCaret.Exists[ggData.anchor] THEN transform _ GGTransform.RotateAboutPoint[anchorPoint, scalar] ELSE {ComplainAboutAnchor[]; RETURN}; $Scale => IF GGCaret.Exists[ggData.anchor] THEN transform _ GGTransform.ScaleAboutPoint[anchorPoint, scalar] ELSE {ComplainAboutAnchor[]; RETURN}; $ScaleX => IF GGCaret.Exists[ggData.anchor] THEN transform _ GGTransform.ScaleUnevenAboutPoint[anchorPoint, scalar, 1.0] ELSE {ComplainAboutAnchor[]; RETURN}; $ScaleY => IF GGCaret.Exists[ggData.anchor] THEN transform _ GGTransform.ScaleUnevenAboutPoint[anchorPoint, 1.0, scalar] ELSE {ComplainAboutAnchor[]; RETURN}; ENDCASE => ERROR; DoTheTransforms[ggData, transform]; } EXITS Abort => NULL; }; -- end TransRotScale SixPointTransform: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; hotGen: SequenceGenerator _ GGSelect.SelectedSequences[ggData.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[ggData, transform]; EXITS Abort => { ggData: GGData _ NARROW[clientData]; Feedback.AppendHerald[ggData.feedback, "Not enough arguments for a six-point transform", oneLiner]; Feedback.Blink[ggData.feedback]; }; }; -- end SixPointTransform FourPointTransform: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; hotGen: SequenceGenerator _ GGSelect.SelectedSequences[ggData.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[ggData, transform]; EXITS Abort => { ggData: GGData _ NARROW[clientData]; Feedback.AppendHerald[ggData.feedback, "Not enough arguments for a four-point transform", oneLiner]; Feedback.Blink[ggData.feedback]; }; }; -- end SixPointTransform DoTheTransforms: PROC [ggData: GGData, transform: ImagerTransformation.Transformation] = { sliceDescGen: SliceDescriptorGenerator _ GGSelect.SelectedSlices[ggData.scene, normal]; ggData.refresh.startBoundBox^ _ GGBoundBox.BoundBoxOfMoving[ggData.scene, normal]^; FOR sliceD: SliceDescriptor _ GGSelect.NextSliceDescriptor[sliceDescGen], GGSelect.NextSliceDescriptor[sliceDescGen] UNTIL sliceD=NIL DO sliceD.slice.class.transform[sliceD, transform]; ENDLOOP; GGBoundBox.EnlargeByBox[bBox: ggData.refresh.startBoundBox, by: GGBoundBox.BoundBoxOfMoving[ggData.scene, normal]]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: triggerBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE]; }; AreaSelectNew: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; AreaSelectAux[ggData, TRUE, TRUE]; }; AreaSelectExtend: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; AreaSelectAux[ggData, FALSE, TRUE]; }; AreaSelectNewAndDelete: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; IF GGSelect.NoSelections[ggData.scene, normal] THEN RETURN ELSE { -- there were some original selections oldSelectedGen: SliceDescriptorGenerator _ GGSelect.SelectedSlices[ggData.scene, normal]; -- save original selection startBox: BoundBox _ GGBoundBox.BoundBoxOfSelected[ggData.scene, normal]; AreaSelectAux[ggData: ggData, new: TRUE, paint: FALSE]; FOR sliceD: SliceDescriptor _ GGSelect.NextSliceDescriptor[oldSelectedGen], GGSelect.NextSliceDescriptor[oldSelectedGen] UNTIL sliceD=NIL DO GGSelect.DeselectEntityAllClasses[sliceD.slice, ggData.scene]; GGScene.DeleteSlice[ggData.scene, sliceD.slice]; ENDLOOP; GGCaret.NoAttractor[ggData.caret]; GGCaret.SitOn[ggData.caret, NIL]; ggData.refresh.startBoundBox^ _ startBox^; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: triggerBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE]; }; }; AreaSelectDegenerate: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; sliceGen: SliceGenerator _ GGScene.SlicesInScene[ggData.scene]; GGSelect.DeselectAll[ggData.scene, normal]; FOR slice: Slice _ GGScene.NextSlice[sliceGen], GGScene.NextSlice[sliceGen] UNTIL slice = NIL DO -- for every slice in the scene DegenerateProc: WalkProc = { RETURN [seg.lo.x=seg.hi.x AND seg.lo.y=seg.hi.y]; }; sliceD: SliceDescriptor _ GGSlice.WalkSegments[slice, DegenerateProc]; -- get a descriptor of degenerate (zero length) segments IF sliceD#NIL THEN GGSelect.SelectSlice[sliceD, ggData.scene, normal]; -- and select it IF GGSlice.IsWhitespace[slice] THEN GGSelect.SelectEntireSlice[slice, ggData.scene, normal]; ENDLOOP; Feedback.PutFHerald[ggData.feedback, oneLiner, "Degenerate segments and strings selected"]; GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, ggData: ggData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE]; }; AreaSelectAux: PROC [ggData: GGData, 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: SliceDescriptorGenerator; sceneGen: SliceGenerator; IF GGSelect.NoSelections[ggData.scene, normal] THEN RETURN; selectedGen _ GGSelect.SelectedSlices[ggData.scene, normal]; IF new THEN GGSelect.DeselectAll[ggData.scene, normal]; -- get rid of old selection FOR sliceD: SliceDescriptor _ GGSelect.NextSliceDescriptor[selectedGen], GGSelect.NextSliceDescriptor[selectedGen] UNTIL sliceD=NIL DO sBox: BoundBox _ sliceD.slice.class.getBoundBox[sliceD.slice, sliceD.parts]; sceneGen _ GGScene.TopLevelSlicesInScene[ggData.scene]; FOR next: Slice _ GGScene.NextSlice[sceneGen], GGScene.NextSlice[sceneGen] UNTIL next=NIL DO IF next=sliceD.slice THEN LOOP; IF NOT GGSelect.IsSelectedInFull[next, ggData.scene, normal] AND Within[next.class.getBoundBox[next, NIL], sBox] THEN GGSelect.SelectEntireSlice[next, ggData.scene, normal]; ENDLOOP; ENDLOOP; IF paint THEN GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, ggData: ggData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE]; }; -- end AreaSelectAux StuffIt: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { StuffItAux[event, clientData, print]; }; StuffItScreen: PUBLIC PROC [clientData: REF ANY, event: LIST OF 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 _ ggData.camera.quality; tempStyle: GGInterfaceTypes.DisplayStyle _ ggData.camera.displayStyle; ggData.camera.quality _ quality; ggData.camera.displayStyle _ displayStyle; context.TranslateT[t: [-bRect.x, -bRect.y]]; GGRefresh.InterpressEntireScene[context, ggData]; ggData.camera.quality _ tempQuality; ggData.camera.displayStyle _ tempStyle; }; Imager.DoSaveAll[context, DoIt]; }; ggData: GGData _ NARROW[clientData]; bRect: Imager.Rectangle _ GGBoundBox.RectangleFromBoundBox[GGBoundBox.BoundBoxOfSelected[ggData.scene, normal] ]; IF GGSelect.NoSelections[ggData.scene, normal] THEN { Feedback.AppendHerald[ggData.feedback, "Select some objects for stuffing", oneLiner]; Feedback.Blink[ggData.feedback]; } ELSE { ImagerArtwork.PasteArtwork[action: DoStuff, bounds: [0.0, 0.0, bRect.w, bRect.h], m: ImagerArtwork.Points[], clip: TRUE]; }; }; Refresh: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; doNotClear: BOOL _ event#NIL AND event.rest#NIL AND event.rest.first=$DoNotClearFeedback; GGWindow.RestoreScreenAndInvariants[paintAction: $PaintEntireScene, ggData: ggData, remake: none, backgndOK: FALSE, edited: FALSE, okToClearFeedback: NOT doNotClear]; }; OpenTypescript: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; [] _ Feedback.OpenTypescript["Gargoyle Typescript", $Gargoyle, 120]; }; Help: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; category: ATOM _ NARROW[event.rest.first]; openHeight: INTEGER _ 140; help: ViewerClasses.Viewer; name: Rope.ROPE; SELECT category FROM $MouseActions => {name _ "GGHelp.Tioga"; openHeight _ 140}; $Fonts => {name _ "GGFontSampler.Tioga"; openHeight _ 210}; ENDCASE => {name _ "GargoyleDoc.Tioga"; openHeight _ 115}; IF (help _ ViewerOps.FindViewer[FS.ExpandName[name, ggData.originalWDir].fullFName])#NIL THEN { -- viewer already exists IF help.column#right THEN ViewerOps.ChangeColumn[help, right]; } ELSE { help _ ViewerOps.CreateViewer[flavor: $Text, info: [iconic: TRUE, column: right, openHeight: openHeight], paint: FALSE]; TiogaMenuOps.Load[viewer: help, fileName: Rope.Concat[ggData.originalWDir, name]]; }; ViewerOps.SetOpenHeight[viewer: help, clientHeight: openHeight]; ViewerOps.OpenIcon[icon: help, bottom: FALSE, paint: FALSE]; -- must do Open before Top ViewerOps.TopViewer[viewer: help, paint: FALSE]; ViewerOps.ComputeColumn[right]; -- repaint right column }; IPSnapShot: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; DoMakeInterpress: PROC [dc: Imager.Context] = { Imager.ScaleT[dc, pixelsPerMeter]; tempStyle _ ggData.camera.displayStyle; ggData.camera.displayStyle _ print; GGRefresh.SnapShot[dc, ggData]; ggData.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] _ GGUtility.GetInterpressFileName["snapshot.ip", ggData.currentWDir, ggData.feedback]; IF NOT success THEN RETURN; ipRef _ ImagerInterpress.Create[fullName]; msgRope _ IO.PutFR["Writing to IP file: %g . . . ", [rope[fullName]]]; Feedback.Append[ggData.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]]]; Feedback.Append[ggData.feedback, msgRope, end]; }; ReloadTipTable: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; newTable: TIPUser.TIPTable; actionArea: ViewerClasses.Viewer; bad: BOOL _ FALSE; tableName, msg: Rope.ROPE; Feedback.Append[ggData.feedback, "Reloading tip table...", begin]; tableName _ Rope.Concat[ggData.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 {Feedback.Append[ggData.feedback, msg, oneLiner]; RETURN}; Feedback.Append[ggData.feedback, "Done.", end]; IF newTable = NIL THEN ERROR; actionArea _ ggData.actionArea; actionArea.tipTable _ newTable; }; tryIncrementalUpdate: BOOL _ TRUE; SawTextFinish: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; slice: Slice _ ggData.refresh.textInProgress; CodeTimer.StartInt[$SawTextFinish, $Gargoyle]; IF slice#NIL AND Rope.Length[GGSlice.GetText[slice: slice]]=0 THEN { -- backspaced to nothing GGSelect.DeselectEntityAllClasses[slice, ggData.scene]; GGScene.DeleteSlice[ggData.scene, slice]; }; IF ggData.refresh.textInProgress#NIL THEN { -- fix up alignment triggers IF tryIncrementalUpdate THEN GGAlign.UpdateBagsForNewSlices[LIST[slice], ggData] -- the text is not necessarily new, but I believe this will work anyway. Bier, April 20, 1987 ELSE GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsSelected, ggData: ggData, remake: triggerBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: FALSE]; ggData.refresh.textInProgress _ NIL; -- terminates typed input }; IF ggData.refresh.areaFollowColorTool THEN { GGEvent.AreaColorFromColorTool[clientData, event]; ColorTool.RemoveProc[$GG, ViewerOps.FindViewer["ColorTool"]]; ggData.refresh.areaFollowColorTool _ FALSE; }; IF ggData.refresh.lineFollowColorTool THEN { GGEvent.LineColorFromColorTool[clientData, event]; ColorTool.RemoveProc[$GG, ViewerOps.FindViewer["ColorTool"]]; ggData.refresh.lineFollowColorTool _ FALSE; }; CodeTimer.StopInt[$SawTextFinish, $Gargoyle]; }; PrintRope: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; rope: Rope.ROPE _ NARROW[event.rest.first]; Feedback.Append[feedback: ggData.feedback, msg: rope, msgType: oneLiner] }; InitStats: PROC [] = { interval: CodeTimer.Interval; interval _ CodeTimer.CreateInterval[$AddChar]; CodeTimer.AddInt[interval, $Gargoyle]; interval _ CodeTimer.CreateInterval[$SawTextFinish]; CodeTimer.AddInt[interval, $Gargoyle]; }; InitStats[]; END. /^GGEventImplC.mesa Copyright c 1986 by Xerox Corporation. All rights reserved. Last Edited by: Pier, May 14, 1987 4:07:07 pm PDT Contents: Once an event reaches the front of the slack-process queue, it is dispatched to one of the procedures in this module. Bier, April 20, 1987 6:52:01 pm PDT Setting Gargoyle State GGState.SetDefaultFont[ggData, fontRope]; 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 GGEvent.SetDefaultStrokeValues[event, clientData]; GGEvent.SetDefaultLineColor[event, clientData]; GGEvent.SetDefaultFillColor[event, clientData]; GGEvent.SetDefaultFontValues[event, clientData]; GGEvent.ShowAllDefaults[event, 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. 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. outline.class.setFillColor[outline, GGOutline.fillColor]; 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 AddControlPointOLDD: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { Adds a new control point to a segment ggData: GGData _ NARROW[clientData]; attractor: REF ANY; seg, newSeg: Segment; segNum: INT; traj, newRun, newTraj: Traj; refreshBox: BoundBox; success: BOOL; partType: TrajPartType; caretPos: Point _ GGCaret.GetPoint[ggData.caret]; attractor _ GGCaret.GetAttractor[ggData.caret]; BEGIN IF attractor = NIL THEN GOTO NotOnSpline; WITH attractor SELECT FROM oD: OutlineDescriptor => { [success, partType, traj, ----, ----, ----, ----, seg, segNum] _ GGOutline.UnpackSimpleDescriptorOld[oD]; IF NOT success OR partType # segment THEN GOTO NotOnSpline; }; sD: SliceDescriptor => { IF sD.slice.class.type # $Outline THEN GOTO NotOnSpline; [success, partType, traj, ----, ----, ----, ----, seg, segNum] _ GGOutline.UnpackSimpleDescriptor[sD]; IF NOT success OR partType # segment THEN GOTO NotOnSpline; }; ENDCASE => ERROR; IF seg.class.type # $CubicSpline THEN GOTO NotOnSpline; Must save select data now so that segment copy (in CSControlPointAdd) will reflect selection data. GGSelect.DeselectAll[ggData.scene, normal]; GGTraj.SaveSelection[traj, normal, ggData.scene]; -- to clear field bits GGTraj.SaveSelection[traj, hot, ggData.scene]; -- to save hot field bits newSeg _ GGSegment.CSControlPointAdd[seg, caretPos]; newRun _ GGTraj.CreateTraj[newSeg.lo]; GGTraj.SetTrajStrokeJoint[newRun, traj.strokeJoint]; 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, ggData.scene]; GGCaret.NoAttractor[ggData.caret]; GGCaret.SitOn[ggData.caret, NIL]; ggData.refresh.startBoundBox^ _ refreshBox^; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: triggerBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE]; EXITS NotOnSpline => { Feedback.Append[ggData.feedback, "Caret must lie on a cubic spline to add a control point", oneLiner]; Feedback.Blink[ggData.feedback]; }; END; }; 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 AddJointOLDD: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ 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[ggData.caret]; attractor _ GGCaret.GetAttractor[ggData.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; Save select data now so that segment copy (in CSControlPointAdd) will reflect selection data. GGSelect.DeselectAll[ggData.scene, normal]; GGTraj.SaveSelection[traj, normal, ggData.scene]; GGTraj.SaveSelection[traj, hot, ggData.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, ggData.scene]; GGTraj.SetTrajStrokeJoint[newTraj, traj.strokeJoint]; GGBoundBox.EnlargeByBox[refreshBox, oldBox]; GGCaret.NoAttractor[ggData.caret]; GGCaret.SitOn[ggData.caret, NIL]; ggData.refresh.startBoundBox^ _ refreshBox^; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: triggerBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE]; EXITS NotASegment => { Feedback.Append[ggData.feedback, "Caret must lie on a segment to add a joint", oneLiner]; Feedback.Blink[ggData.feedback]; }; ConicsAreNotDone => { Feedback.Append[ggData.feedback, "Can't add joints to Conics", oneLiner]; Feedback.Blink[ggData.feedback]; }; END; }; 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 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 AreaSelectNewAndDeleteOLDD: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; Select all objects within area(s) bounded by original selection(s) and delete original selection IF GGSelect.NoSelections[ggData.scene, normal] THEN RETURN ELSE { -- there were some original selections oldSelectedGen: EntityGenerator _ GGSelect.SelectedSlices[ggData.scene, normal]; -- save original selection startBox: BoundBox _ GGBoundBox.BoundBoxOfSelected[ggData.scene, normal]; AreaSelectAux[ggData: ggData, new: TRUE, paint: FALSE]; FOR formerSelected: REF ANY _ GGScene.NextEntity[oldSelectedGen], GGScene.NextEntity[oldSelectedGen] UNTIL formerSelected=NIL DO WITH formerSelected SELECT FROM outlineD: OutlineDescriptor => { N.B.: DeleteOutline deletes too much GGSelect.DeselectEntityAllClasses[outlineD.slice, ggData.scene]; GGScene.DeleteOutline[ggData.scene, outlineD.slice]; }; sliceD: SliceDescriptor => { GGSelect.DeselectEntityAllClasses[sliceD.slice, ggData.scene]; GGSlice.DeleteSlice[ggData.scene, sliceD.slice]; }; ENDCASE => ERROR; ENDLOOP; GGCaret.NoAttractor[ggData.caret]; GGCaret.SitOn[ggData.caret, NIL]; ggData.refresh.startBoundBox^ _ startBox^; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: triggerBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE]; }; }; Select all objects within area(s) bounded by original selection(s) and delete original selection Select all degenerate (i.e. segments having co-located endpoints) segments. Select all text slices with only whitespace in them WalkProc: TYPE = PROC [seg: Segment] RETURNS [keep: BOOL]; AreaSelectAuxOLDD: PROC [ggData: GGData, 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[ggData.scene, normal] THEN RETURN; selectedGen _ GGSelect.SelectedSlices[ggData.scene, normal]; IF new THEN GGSelect.DeselectAll[ggData.scene, normal]; -- get rid of old selection outer loop on each individual selected object FOR nextSelected: REF ANY _ GGScene.NextEntity[selectedGen], GGScene.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 _ GGScene.TopLevelEntitiesInScene[ggData.scene]; FOR nextEntity: REF ANY _ GGScene.NextEntity[sceneGen], GGScene.NextEntity[sceneGen] UNTIL nextEntity=NIL DO IF nextEntity=currentEntity THEN LOOP; IF NOT GGSelect.IsSelectedInFull[nextEntity, ggData.scene, normal] THEN -- don't reprocess WITH nextEntity SELECT FROM outline: Outline => { IF Within[outline.class.getBoundBox[outline, NIL], sBox] THEN { GGSelect.SelectEntireSlice[outline, ggData.scene, normal]; }; }; slice: Slice => { IF Within[slice.class.getBoundBox[slice, NIL], sBox] THEN { GGSelect.SelectSlice[slice.class.newParts[slice, NIL, topLevel], ggData.scene, normal ]; }; }; ENDCASE => ERROR; ENDLOOP; ENDLOOP; IF paint THEN GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, ggData: ggData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE]; }; -- end AreaSelectAux 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, ggData: ggData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE]; We wish to have the same effect as these 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. Miscellaneous The following has nothing to do with text. We take advantage of the fact that every mouse "Start" operation calls SawTextFinish, so we implicitly complete any FollowColorTool operation here. ÊFÓ˜codešœ™Kšœ<™Kšœžœ ˜$šžœž˜Kšœ;Ÿ ˜Ešœ˜KšœW˜WKšœE˜Ešžœžœžœ˜K–B[gargoyleData: REF ANY, msg: ROPE, msgType: GGError.MsgType]šœQ˜QKšœ ˜ Kšžœ˜K˜—šžœ,žœžœ˜8K–B[gargoyleData: REF ANY, msg: ROPE, msgType: GGError.MsgType]šœU˜UKšœ ˜ Kšžœ˜K˜—KšœP˜PK˜—Kšžœžœ˜—K˜K˜—šœžœžœžœžœ žœžœžœžœ™RJ™%Jšœžœ ™$Jšœ žœžœ™J™Jšœžœ™ J™J™Jšœ žœ™J™J™1J–![caret: GGInterfaceTypes.Caret]šœ/™/šž™Jšžœ žœžœžœ ™)šžœ žœž™™Jš œŸœŸœŸœŸœ9™iJš žœžœ žœžœžœ ™;J™—™Jšžœ žœžœ ™8Jš œŸœŸœŸœŸœ6™fJš žœžœ žœžœžœ ™;J™—Jšžœžœ™—šžœžœžœ ™7Jš b™b—Jšœ+™+Jšœ2ŸÐczŸ ™HJšœ/Ÿ™HJ™4J™&Jšœ4™4Jšžœžœ+žœžœ™@¡’˜ 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•Artwork Interpress•Bounds:0.0 mm xmin 0.0 mm ymin 126.6472 mm xmax 38.80556 mm ymax –G41.62778 mm topLeading 41.62778 mm topIndent 1.411111 mm bottomLeading šœ=™=J™7J™'Jšžœžœ,žœžœ™=Jšžœžœ,žœžœ™=Jšœ(™(Jšœ"žœ™'Jšžœ2žœžœŸ™OJ™!JšœZ™ZJšœ5™5Jšœ,™,K–)[traj: GGModelTypes.Traj, index: NAT]šœ"™"Jšœžœ™!J™,Kšœ€žœ žœžœ™®šž™™JšœY™YKšœ ™ K™—™JšœI™IKšœ ™ K™——Kšžœ™—J™J™—šœžœžœžœžœ žœžœžœžœ˜GJšœžœ ˜$Jšœ˜J˜Jšœžœ˜ J˜J˜J˜Jšœ žœ˜Jšœ˜J˜1J–![caret: GGInterfaceTypes.Caret]šœ/˜/šž˜Jšžœ žœžœžœ ˜)šžœ'žœžœ ˜?Jš œŸœŸœŸœŸœ=˜mJš žœžœ žœžœžœ ˜;—šžœžœžœ˜4Jš ]™]—Jšœ+˜+Jšœ1˜1Jšœ.˜.L–G41.62778 mm topLeading 41.62778 mm topIndent 1.411111 mm bottomLeading –:0.0 mm xmin 0.0 mm ymin 126.6472 mm xmax 38.80556 mm ymax – Interpress–õInterpress/Xerox/3.0  f j k j¡¥“ÄWB ¤ ¨  ¡£€S ¢ ¨ r j ¡ ¢ ¨¡¡¨ÄWB ¤ ¨ r j¡¥“ÄWB ¤ ¨¡¡¨¢¯“¡¡¨¢·“¢°“ÑS™Û3—P÷—v —˜£¯“¡¡¨¢·“¢°“ÄA¶—Ä1?r™ÄC{—Ä4Ïr—ÄE@—Ä1?r—˜¢¯“¡¡¨¢·“¢°“-™Ä×ßÄb#ÄVoÊÄT!*A¡’Ä#iGĨnÄO;ŽÄwá¼>@¡’˜ 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šœZ˜ZJšœ5˜5Jšœ,˜,K–)[traj: GGModelTypes.Traj, index: NAT]šœ"˜"Jšœžœ˜!J˜,Kšœ€žœ žœžœ˜®šž˜˜JšœY˜YKšœ ˜ K˜—˜JšœI˜IKšœ ˜ K˜——Kšžœ˜—J˜—K˜K™š œžœžœžœžœ žœžœžœžœ˜Lšœžœ˜KšœY˜YKšœ ˜ K˜—Kšœžœ ˜$š œžœžœžœžœžœ ˜`JšœT˜TKšœ ˜ JšžœŸ'˜3Jšœ˜—šžœžœŸ(˜@JšœT˜TKšœ ˜ Jšžœ˜Jšœ˜—šœ˜Kšœ5˜5Kšœ/˜/šžœ ž˜Kšœb˜bKšœb˜bKš œ žœžœ?žœžœ˜”Kš œ žœžœ>žœžœ˜’Kš œ žœžœIžœžœ˜žKš œ žœžœIžœžœ˜žKšžœžœ˜—Kšœ#˜#K˜—šž˜Kšœ žœ˜—JšœŸ˜J˜—šœžœžœžœžœ žœžœžœžœ˜PKšœëŸ™‚Kšœžœ ˜$KšœJ˜JKšœžœžœ˜Kšœ/˜/Kšœ˜šžœžœžœž˜Kšœ&˜&Kšžœžœžœžœ˜Kšžœžœžœ˜0šžœžœžœž˜Kšœ4˜4Kšžœ˜—Kšžœ˜—Kšœ*˜*Kšœ#˜#šž˜šœ ˜ Jšœžœ ˜$Jšœc˜cJšœ ˜ Jšœ˜——JšœŸ˜J˜—šœžœžœžœžœ žœžœžœžœ˜QKšœèŸ™ÿKšœžœ ˜$KšœJ˜JKšœžœžœ˜Kšœ/˜/Kšœ˜šžœžœžœž˜Kšœ&˜&Kšžœžœžœžœ˜Kšžœžœžœ˜0šžœžœžœž˜Kšœ4˜4Kšžœ˜—Kšžœ˜—Kšœ+˜+Kšœ#˜#šž˜šœ ˜ Jšœžœ ˜$Jšœd˜dJšœ ˜ Jšœ˜——JšœŸ˜J˜—šœžœE˜ZKšœW˜WKš '™'KšœS˜Sšžœržœžœž˜ˆKšœ0˜0Jšžœ˜—Jš (™(Jšœs˜sKšœ€žœ žœžœ˜®Kšœ˜—J™K™š œžœžœžœžœ žœžœžœžœ˜LKšœžœ ˜$K™bKšœžœžœ˜"K˜K˜—šœžœžœžœžœ žœžœžœžœ˜OKšœžœ ˜$K™TKšœžœžœ˜#Kšœ˜K˜—šœžœžœžœžœ žœžœžœžœ™YKšœžœ ™$K™`K–K[scene: GGModelTypes.Scene, selectClass: GGSegmentTypes.SelectionClass]šžœ-žœž™:šžœŸ&™-KšœQŸ™kKšœI™IKšœ#žœ žœ™7š žœžœžœJžœžœž™€šžœžœž™–;[seq: GGModelTypes.Sequence, scene: GGModelTypes.Scene]™ Kš $™$Kšœ@™@Kšœ4™4K™—–>[scene: GGModelTypes.Scene, cluster: GGModelTypes.Cluster]šœ™Kšœ>™>Kšœ0™0Kšœ™—Kšžœžœ™—Kšžœ™—K–)[traj: GGModelTypes.Traj, index: NAT]šœ"™"Jšœžœ™!Kšœ*™*Kšœ€žœ žœžœ™®Kšœ™—Kšœ™K™—šœžœžœžœžœ žœžœžœžœ˜UKšœžœ ˜$K™`K–K[scene: GGModelTypes.Scene, selectClass: GGSegmentTypes.SelectionClass]šžœ-žœž˜:šžœŸ&˜-KšœZŸ˜tKšœI˜IKšœ#žœ žœ˜7š žœ œ& œ%žœžœž˜ŒKšœ>˜>Kšœ0˜0Kšžœ˜—Kšœ"˜"Jšœžœ˜!Kšœ*˜*Kšœ€žœ žœžœ˜®Kšœ˜—Kšœ˜K˜—šœžœžœžœžœ žœžœžœžœ˜SKšœžœ ˜$KšœK™KK™3Kšœ?˜?Kšœ+˜+š žœIžœ žœžœŸ˜€šœ˜Kš  œžœžœžœžœ™:Kšœžœžœ˜2K˜—Kšœ6œŸ8˜K–s[sliceD: GGModelTypes.SliceDescriptor, scene: GGModelTypes.Scene, selectClass: GGSegmentTypes.SelectionClass]šžœžœžœ5Ÿ˜WKšžœžœ9˜\Kšžœ˜—K–Û[feedback: ViewerClasses.Viewer, msgType: GGError.MsgType, format: ROPE _ NIL, v1: IO.Value _ [null[]], v2: IO.Value _ [null[]], v3: IO.Value _ [null[]], v4: IO.Value _ [null[]], v5: IO.Value _ [null[]]]šœ[˜[Kšœmžœ žœžœ˜›Kšœ˜K˜—š œžœžœžœ žœžœ™Ršœžœ$žœžœ™BKšžœžœžœžœ™oK™—Kšœ™Kšœ™K–_[gargoyleData: GGInterfaceTypes.GargoyleData, selectClass: GGInterfaceTypes.SelectionClass]šžœ-žœžœ™;Kšœ<™<šžœžœ-Ÿ™SKš -™-—š žœžœžœEžœžœž™wK™Kšœžœžœ™šžœžœž™šœ ™ KšœH™HKšœ™K™—šœ™KšœB™BKšœ™K™—Kšžœžœ™Kš *™*—Kšœ9™9š žœ žœžœ?žœ žœž™mKšžœžœžœ™&Kšžœžœ=žœŸ™Zšžœ žœž™šœ™šžœ+žœ žœ™?Kšœ:™:K™—Kšœ™—šœ™šžœ'žœ žœ™;Kšœ1žœ$™XK™—Kšœ™—Kšžœžœ™—Kšžœ™—Kšžœ™—Kš žœžœnžœ žœžœ™©KšœŸ™K™—š œžœžœžœ žœžœ˜Nšœžœ$žœžœ˜BKšžœžœžœžœ˜oK˜—Kšœ&˜&Kšœ˜K–_[gargoyleData: GGInterfaceTypes.GargoyleData, selectClass: GGInterfaceTypes.SelectionClass]šžœ-žœžœ˜;Kšœ<˜<šžœžœ-Ÿ˜SKš -™-—šžœpžœžœž˜†šœM˜MKš *™*—Kšœ7˜7šžœIžœžœž˜]Kšžœžœžœ˜š žœžœ7žœ%žœ žœ˜vKšœ7˜7—Kšžœ˜—Kšžœ˜—Kš žœžœnžœ žœžœ˜©KšœŸ˜K˜—K™K™šÐbnœžœžœžœžœ žœžœžœžœ˜FKšœ%˜%K˜K˜—š¢ œžœžœžœžœ žœžœžœžœ˜LKšœ&˜&K˜K˜—š¢ œžœ žœžœžœžœžœžœ2˜oK™Êšœžœ˜+šœžœ˜KšœB˜BKšœF˜FKšœ ˜ Kšœ*˜*Kšœ,˜,Kšœ1˜1Kšœ$˜$Kšœ'˜'Kšœ˜—Kšœ ˜ K˜—Kšœžœ ˜$K–O[m: ImagerTransformation.Transformation, r: ImagerTransformation.Rectangle]šœq˜qšžœ-žœ˜5KšœU˜UJšœ ˜ K˜—šžœ˜Kšœsžœ˜yK˜—K˜—K˜šœžœžœžœžœ žœžœžœžœ˜FKšœžœ ˜$Kš œ žœ žœžœ žœžœ&˜YK–Â[paintAction: ATOM, gargoyleData: GGInterfaceTypes.GargoyleData, remake: GGWindow.ForegroundParts _ triggerBag, backgndOK: BOOL _ FALSE, edited: BOOL _ TRUE, okToClearFeedback: BOOL]šœmžœ žœžœ ˜¦Kšœ˜K˜—šœžœžœžœžœ žœžœžœžœ˜MKšœžœ ˜$KšœD˜DK˜K˜—šœžœžœžœžœ žœžœžœžœ˜CKšœžœ ˜$Kšœ žœžœ˜*Kšœ žœ˜Kšœ˜Kšœ žœ˜šžœ ž˜Kšœ;˜;Kšœ;˜;Kšžœ3˜:—š žœžœ3žœžœŸ˜xKšžœžœ%˜>K˜—šžœ˜Kšœ<žœ1žœ˜xKšœR˜RK˜—Kšœ@˜@Kšœ'žœ žœŸ˜WKšœ)žœ˜0Kšœ Ÿ˜7K˜K˜—K™K™ š¢ œžœžœžœžœ žœžœžœžœ˜IKšœžœ ˜$K™ÉKšœižœ žœžœ™˜K™Æšœžœ˜/K˜"Kšœ'˜'Kšœ#˜#Kšœ˜Kšœ'˜'K˜—Kšœ)˜)Kšœ˜Kšœžœ˜Kšœ žœ˜Kšœžœ˜#Kšœžœ˜Kšœžœ˜Kšœ žœ˜Kšœžœ˜K˜Kšœj˜jKšžœžœ žœžœ˜Kšœ*˜*Kšœ žœ:˜FKšœ1˜1Kš ˜Kšœ6˜6Kšœ˜Kš ˜Kšœ1˜1Kšœ žœ3˜?Kšœ/˜/K˜—K˜K™ šœžœžœžœžœ žœžœžœžœ˜MKšœžœ ˜$Kšœ˜K˜!Kšœžœžœ˜Kšœžœ˜KšœB˜BKšœ=˜=šœ3˜3šœžœ ˜Kšœžœ˜ Kšœ=˜=Kšžœ˜ —šœ˜Kšœžœ˜ KšœC˜CKšžœ˜ ——Kšžœžœ3žœ˜FKšœ/˜/Kšžœ žœžœžœ˜Kšœ˜Kšœ˜K˜K˜—Kšœžœžœ˜"š¢ œžœžœžœžœ žœžœžœžœ˜LKšœžœ ˜$Kšœ-˜-Kšœ.˜.š žœžœžœ.žœŸ˜]Kšœ7˜7Kšœ)˜)K˜—šžœžœžœŸ˜HKšžœžœ žœŸ^˜¯Kšžœyžœ žœžœ˜¬Kšœ žœŸ˜>K˜—Kšœs  œ?™¿šžœ$žœ˜,Kšœ2˜2Kšœ=˜=Kšœ%žœ˜+K˜—šžœ$žœ˜,Kšœ2˜2Kšœ=˜=Kšœ%žœ˜+K˜—Kšœ-˜-K˜—K˜š œžœžœžœžœ žœžœžœžœ˜HKšœžœ ˜$Kšœ žœžœ˜+K–M[feedback: Feedback.FeedbackData, msg: ROPE, msgType: Feedback.MsgType]šœH˜HK˜K˜—š œžœ˜Kšœ˜Kšœ.˜.Kšœ&˜&Kšœ4˜4Kšœ&˜&K˜—K˜K˜ J˜Kšžœ˜—…—§x©