DIRECTORY Ascii, Atom, BasicTime, CombinePoly, CubicSplines, GGAlign, GGBasicTypes, GGBoundBox, GGButtons, GGCaret, GGError, GGEvent, GGFromImager, GGGravity, GGInterface, GGInterfaceTypes, GGModelTypes, GGMultiGravity, GGObjects, GGOutline, GGRefresh, GGSegment, GGSegmentTypes, GGSelect, GGSequence, GGShapes, GGSlice, GGStatistics, GGTouch, GGTraj, GGUtility, GGVector, GGWindow, Imager, ImagerInterpress, ImagerTransformation, Interpress, IO, IPMaster, MessageWindow, PrincOpsUtils, Random, Rope, Rosary, SlackProcess, ViewerTools; GGEventImplA: CEDAR PROGRAM IMPORTS Atom, BasicTime, CombinePoly, GGAlign, GGBoundBox, GGButtons, GGCaret, GGError, GGEvent, GGFromImager, GGGravity, GGInterface, GGMultiGravity, GGObjects, GGOutline, GGRefresh, GGSegment, GGSelect, GGSequence, GGShapes, GGSlice, GGStatistics, GGTouch, GGTraj, GGUtility, GGVector, GGWindow, Imager, ImagerInterpress, ImagerTransformation, Interpress, IO, MessageWindow, PrincOpsUtils, Random, Rope, SlackProcess, ViewerTools EXPORTS GGEvent = BEGIN BoundBox: TYPE = GGBoundBox.BoundBox; Slice: TYPE = GGModelTypes.Slice; SliceGenerator: TYPE = GGModelTypes.SliceGenerator; SliceDescriptor: TYPE = GGModelTypes.SliceDescriptor; SliceDescriptorGenerator: TYPE = GGModelTypes.SliceDescriptorGenerator; EntityGenerator: TYPE = GGModelTypes.EntityGenerator; FeatureData: TYPE = GGInterfaceTypes.FeatureData; GargoyleData: TYPE = GGInterfaceTypes.GargoyleData; Joint: TYPE = GGModelTypes.Joint; JointGenerator: TYPE = GGModelTypes.JointGenerator; ObjectBag: TYPE = GGGravity.ObjectBag; OutlineDescriptor: TYPE = GGModelTypes.OutlineDescriptor; TriggerBag: TYPE = GGGravity.TriggerBag; Outline: TYPE = GGModelTypes.Outline; Point: TYPE = GGBasicTypes.Point; Scene: TYPE = GGModelTypes.Scene; Segment: TYPE = GGSegmentTypes.Segment; SegAndIndex: TYPE = GGSequence.SegAndIndex; Sequence: TYPE = GGModelTypes.Sequence; SliceParts: TYPE = GGModelTypes.SliceParts; Traj: TYPE = GGModelTypes.Traj; TrajGenerator: TYPE = GGModelTypes.TrajGenerator; SegmentGenerator: TYPE = GGModelTypes.SegmentGenerator; SequenceGenerator: TYPE = GGModelTypes.SequenceGenerator; Vector: TYPE = GGBasicTypes.Vector; NotYetImplemented: PUBLIC SIGNAL = CODE; scratchStream: IO.STREAM _ IO.RIS[""]; EnterEditingMode: PROC [gargoyleData: GargoyleData, refChar: REF CHAR] = { IF refChar^=Ascii.BS THEN { -- try to enter editing of a single selected text slice sliceDescGen: SliceDescriptorGenerator _ GGSelect.SelectedSlices[gargoyleData.scene, normal]; sliceDesc: SliceDescriptor _ GGSelect.NextSliceDescriptor[sliceDescGen]; IF sliceDesc=NIL OR sliceDesc.slice.class.type#$Text OR GGSelect.NextSliceDescriptor[sliceDescGen]#NIL THEN { GGError.AppendHerald[gargoyleData.feedback, "Select a single text slice for editing", oneLiner]; GGError.Blink[gargoyleData.feedback]; RETURN; }; GGSelect.DeselectSlice[slice: sliceDesc.slice, parts: sliceDesc.parts, scene: gargoyleData.scene, selectClass: normal]; gargoyleData.refresh.textInProgress _ sliceDesc.slice; -- successfully enter editing mode GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE]; } ELSE { slice: Slice _ gargoyleData.refresh.textInProgress _ NewTextSlice[text: Rope.FromChar[refChar^], fontString: defaultFontString, gargoyleData: gargoyleData]; -- start a new text slice IF slice=NIL THEN RETURN; -- can't start a string with whitespace gargoyleData.refresh.startBoundBox^ _ slice.boundBox^; gargoyleData.refresh.addedObject _ slice; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectAdded, gargoyleData: gargoyleData, remake: sceneBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE]; }; }; -- end EnterEditingMode AddChar: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; slice: Slice; obBox: BoundBox; refChar: REF CHAR; IF NARROW[event.first, ATOM]#$AddChar THEN ERROR; refChar _ NARROW[event.rest.first]; IF gargoyleData.refresh.textInProgress=NIL THEN { GGCaret.NoAttractor[gargoyleData.caret]; -- so the feedback goes away. EnterEditingMode[gargoyleData, refChar]; RETURN; } ELSE { -- add to textInProgress GGStatistics.StartInterval[$AddChar, GGStatistics.GlobalTable[]]; slice _ gargoyleData.refresh.textInProgress; obBox _ GGBoundBox.CopyBoundBox[slice.boundBox]; -- remember old bound box for now IF refChar^=Ascii.BS THEN { GGSlice.BackspaceText[slice: slice]; gargoyleData.refresh.startBoundBox^ _ slice.boundBox^; -- bBox was updated by GGSlice GGBoundBox.EnlargeByBox[bBox: gargoyleData.refresh.startBoundBox, by: obBox]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, gargoyleData: gargoyleData, remake: sceneBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE]; } ELSE { GGSlice.AppendText[slice: slice, text: Rope.FromChar[refChar^] ]; gargoyleData.refresh.startBoundBox^ _ slice.boundBox^; -- boundBox was updated by GGSlice GGBoundBox.EnlargeByBox[bBox: gargoyleData.refresh.startBoundBox, by: obBox]; gargoyleData.refresh.addedObject _ slice; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectAdded, gargoyleData: gargoyleData, remake: none, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE]; }; GGStatistics.StopInterval[$AddChar, GGStatistics.GlobalTable[]]; }; }; -- end AddChar NewTextSlice: PRIVATE PROC [text, fontString: Rope.ROPE, gargoyleData: GargoyleData] RETURNS [slice: Slice] = { caretPos: Point _ GGCaret.GetPoint[gargoyleData.caret]; scratchStream _ IO.RIS[text, scratchStream]; slice _ GGSlice.MakeTextSlice[text, fontString, Imager.black, ImagerTransformation.Translate[caretPos], 1.0, , , , gargoyleData.feedback]; GGObjects.AddSlice[gargoyleData.scene, slice, -1]; gargoyleData.refresh.startBoundBox^ _ slice.boundBox^; GGSelect.DeselectAll[gargoyleData.scene, normal]; -- speeds up text entry gargoyleData.refresh.addedObject _ slice; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectAdded, gargoyleData: gargoyleData, remake: sceneBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE]; }; -- end NewTextSlice AddText: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; text: Rope.ROPE _ IF event.rest = NIL THEN ViewerTools.GetSelectionContents[] ELSE NARROW[event.rest.first]; [] _ NewTextSlice[text, defaultFontString, gargoyleData]; }; FontNameFromSelection: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; fontString: Rope.ROPE _ IF event.rest = NIL THEN ViewerTools.GetSelectionContents[] ELSE NARROW[event.rest.first]; slices: SliceDescriptorGenerator _ GGSelect.SelectedSlices[gargoyleData.scene, normal]; gargoyleData.refresh.startBoundBox^ _ GGBoundBox.BoundBoxOfSelected[gargoyleData.scene, normal]^; -- remember original bound box FOR sliceD: SliceDescriptor _ GGSelect.NextSliceDescriptor[slices], GGSelect.NextSliceDescriptor[slices] UNTIL sliceD=NIL DO GGSlice.SetTextFont[sliceD.slice, fontString, gargoyleData.feedback]; ENDLOOP; GGBoundBox.EnlargeByBox[bBox: gargoyleData.refresh.startBoundBox, by: GGBoundBox.BoundBoxOfSelected[gargoyleData.scene, normal] ]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: FALSE]; -- GGSlice.SetTextFont can post error messages }; -- end FontNameFromSelection PrintSelectedFontName: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; sliceDescGen: SliceDescriptorGenerator _ GGSelect.SelectedSlices[gargoyleData.scene, normal]; sliceDesc: SliceDescriptor _ GGSelect.NextSliceDescriptor[sliceDescGen]; IF sliceDesc=NIL OR sliceDesc.slice.class.type#$Text OR GGSelect.NextSliceDescriptor[sliceDescGen]#NIL THEN { GGError.AppendHerald[gargoyleData.feedback, "Select a single text slice for font printing", oneLiner]; GGError.Blink[gargoyleData.feedback]; } ELSE GGError.Append[gargoyleData.feedback, Rope.Concat["Font: ", GGSlice.GetTextFont[sliceDesc.slice].fontName], oneLiner]; }; SelectMatchingFont: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; name: Rope.ROPE _ IF event.rest = NIL THEN ViewerTools.GetSelectionContents[] ELSE NARROW[event.rest.first]; sliceGen: SliceGenerator _ GGObjects.SlicesInScene[gargoyleData.scene]; IF Rope.Equal[name, ""] THEN { GGError.AppendHerald[gargoyleData.feedback, "Select a font name for matching", oneLiner]; RETURN; }; GGSelect.DeselectAll[gargoyleData.scene, normal]; FOR slice: Slice _ GGObjects.NextSlice[sliceGen], GGObjects.NextSlice[sliceGen] UNTIL slice = NIL DO IF Rope.Equal[GGSlice.GetTextFont[slice].fontName, name, FALSE] THEN GGSelect.SelectSlice[slice: slice, parts: slice.class.newParts[slice, NIL, topLevel], scene: gargoyleData.scene, selectClass: normal]; ENDLOOP; GGError.PutFHerald[gargoyleData.feedback, oneLiner, "Text slices with font %g selected", [rope[name]] ]; GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE]; }; DefaultNameFromSelection: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { font: Imager.Font; fontName: Rope.ROPE; gargoyleData: GargoyleData _ NARROW[clientData]; fontString: Rope.ROPE _ IF event.rest = NIL THEN ViewerTools.GetSelectionContents[] ELSE NARROW[event.rest.first]; slice: Slice _ GGSlice.MakeTextSlice["DummySlice", fontString, Imager.black, ImagerTransformation.Scale[1.0], 1.0, , , , NIL]; -- dummy slice. Force font name verification and font creation before trusting to defaultFontString [font, fontName] _ GGSlice.GetTextFont[slice]; -- see if MakeTextSlice worked OK IF font#NIL AND Rope.Equal[fontString, fontName, FALSE] THEN { defaultFontString _ fontString; GGError.Append[gargoyleData.feedback, Rope.Concat["New Default Font: ", defaultFontString], oneLiner]; } ELSE { GGError.Append[gargoyleData.feedback, "New default font not found", oneLiner]; GGError.Blink[gargoyleData.feedback]; }; }; PrintDefaultFontName: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; GGError.Append[gargoyleData.feedback, Rope.Concat["Default Font: ", defaultFontString], oneLiner]; }; AmplifySpaceFromSelection: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; amplifySpace: REAL _ NARROW[event.rest.first, REF REAL]^; slices: SliceDescriptorGenerator _ GGSelect.SelectedSlices[gargoyleData.scene, normal]; gargoyleData.refresh.startBoundBox^ _ GGBoundBox.BoundBoxOfSelected[gargoyleData.scene, normal]^; -- remember original bound box FOR sliceD: SliceDescriptor _ GGSelect.NextSliceDescriptor[slices], GGSelect.NextSliceDescriptor[slices] UNTIL sliceD=NIL DO GGSlice.SetTextAmplifySpace[sliceD.slice, amplifySpace, gargoyleData.feedback]; ENDLOOP; GGBoundBox.EnlargeByBox[bBox: gargoyleData.refresh.startBoundBox, by: GGBoundBox.BoundBoxOfSelected[gargoyleData.scene, normal] ]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: FALSE]; -- GGSlice.SetTextAmplifySpace can post error messages }; -- end AmplifySpaceFromSelection PrintAmplifySpace: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; sliceDescGen: SliceDescriptorGenerator _ GGSelect.SelectedSlices[gargoyleData.scene, normal]; sliceDesc: SliceDescriptor _ GGSelect.NextSliceDescriptor[sliceDescGen]; IF sliceDesc=NIL OR sliceDesc.slice.class.type#$Text OR GGSelect.NextSliceDescriptor[sliceDescGen]#NIL THEN { GGError.AppendHerald[gargoyleData.feedback, "Select the single text slice whose amplify space need", oneLiner]; GGError.Blink[gargoyleData.feedback]; } ELSE GGError.PutF[gargoyleData.feedback, oneLiner, "Amplify space: %g", [real[GGSlice.GetTextAmplifySpace[sliceDesc.slice]]]]; }; DropShadowOn: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; offsetX: REAL _ NARROW[event.rest.first, REF REAL]^; offsetY: REAL _ NARROW[event.rest.rest.first, REF REAL]^; slices: SliceDescriptorGenerator _ GGSelect.SelectedSlices[gargoyleData.scene, normal]; gargoyleData.refresh.startBoundBox^ _ GGBoundBox.BoundBoxOfSelected[gargoyleData.scene, normal]^; -- remember original bound box FOR sliceD: SliceDescriptor _ GGSelect.NextSliceDescriptor[slices], GGSelect.NextSliceDescriptor[slices] UNTIL sliceD=NIL DO GGSlice.DropShadowOn[sliceD.slice, [offsetX, offsetY]]; ENDLOOP; GGBoundBox.EnlargeByBox[bBox: gargoyleData.refresh.startBoundBox, by: GGBoundBox.BoundBoxOfSelected[gargoyleData.scene, normal] ]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE]; }; DropShadowOff: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; slices: SliceDescriptorGenerator _ GGSelect.SelectedSlices[gargoyleData.scene, normal]; gargoyleData.refresh.startBoundBox^ _ GGBoundBox.BoundBoxOfSelected[gargoyleData.scene, normal]^; -- remember original bound box FOR sliceD: SliceDescriptor _ GGSelect.NextSliceDescriptor[slices], GGSelect.NextSliceDescriptor[slices] UNTIL sliceD=NIL DO GGSlice.DropShadowOff[sliceD.slice]; ENDLOOP; GGBoundBox.EnlargeByBox[bBox: gargoyleData.refresh.startBoundBox, by: GGBoundBox.BoundBoxOfSelected[gargoyleData.scene, normal] ]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE]; }; ReadIP: PROC [ipName: Rope.ROPE, clientData: REF ANY, opName: Rope.ROPE] RETURNS [scene: Scene _ NIL] = { ReadMaster: PROC [context: Imager.Context] = { Imager.ScaleT[context, 2834.646]; -- pointsPerMeter=2834.646 Interpress.DoPage[master: ipmaster, page: 1, context: context, log: NIL]; }; gargoyleData: GargoyleData _ NARROW[clientData]; ipmaster: Interpress.Master _ NIL; fullName: Rope.ROPE; startTime, endTime: BasicTime.GMT; totalTime: INT; success: BOOL _ FALSE; [fullName, success] _ GGUtility.GetInterpressFileName[ipName, gargoyleData.currentWDir, gargoyleData.feedback]; IF NOT success THEN RETURN; [ipmaster, success] _ GGUtility.OpenInterpressOrComplain[gargoyleData.feedback, fullName]; IF NOT success THEN RETURN; GGError.PutF[gargoyleData.feedback, begin, "%g %g . . . ", [rope[opName]], [rope[fullName]]]; startTime _ BasicTime.Now[]; scene _ GGFromImager.Capture[action: ReadMaster ! GGFromImager.Warning => { GGError.Append[gargoyleData.feedback, message, oneLiner]; RESUME; };]; endTime _ BasicTime.Now[]; totalTime _ BasicTime.Period[startTime, endTime]; GGError.PutF[gargoyleData.feedback, end, " Done in time (%r)", [integer[totalTime]]]; }; MergeIPEditable: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; ipName: Rope.ROPE _ NARROW[event.rest.first]; scene: Scene _ ReadIP[ipName, clientData, "MergeIPEditable"]; IF scene = NIL THEN RETURN; gargoyleData.scene _ GGObjects.MergeScenes[back: gargoyleData.scene, front: scene]; GGWindow.RestoreScreenAndInvariants[paintAction: $PaintEntireScene, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: FALSE]; }; MergeIPSlice: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { slice: Slice; gargoyleData: GargoyleData _ NARROW[clientData]; shortName, fullName: Rope.ROPE; startTime, endTime: BasicTime.GMT; totalTime: INT; ipMaster: Interpress.Master; success: BOOL; shortName _ NARROW[event.rest.first]; [fullName, success] _ GGUtility.GetInterpressFileName[shortName, gargoyleData.currentWDir, gargoyleData.feedback]; IF NOT success THEN RETURN; [ipMaster, success] _ GGUtility.OpenInterpressOrComplain[gargoyleData.feedback, fullName]; IF NOT success THEN RETURN; GGError.PutF[gargoyleData.feedback, begin, "MergeIPSlice %g . . . ", [rope[fullName]]]; startTime _ BasicTime.Now[]; slice _ GGSlice.MakeIPSliceFromMaster[ipMaster, 2834.646, fullName, gargoyleData.feedback, NIL, FALSE]; IF slice = NIL THEN RETURN; GGObjects.AddSlice[gargoyleData.scene, slice, -1]; endTime _ BasicTime.Now[]; totalTime _ BasicTime.Period[startTime, endTime]; GGError.PutF[gargoyleData.feedback, end, " Done in time (%r)", [integer[totalTime]]]; gargoyleData.refresh.startBoundBox^ _ slice.boundBox^; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, gargoyleData: gargoyleData, remake: sceneBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: FALSE]; }; -- end MergeIPSlice IncludeIPByReference: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; sliceDescGen: GGSelect.SliceDescriptorGenerator; sliceDescGen _ GGSelect.SelectedSlices[gargoyleData.scene, normal]; FOR sliceD: GGSelect.SliceDescriptor _ GGSelect.NextSliceDescriptor[sliceDescGen], GGSelect.NextSliceDescriptor[sliceDescGen] UNTIL sliceD = NIL DO IF sliceD.slice.class.type = $IP THEN GGSlice.SetIncludeByValue[sliceD.slice, FALSE]; ENDLOOP; }; IncludeIPByValue: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; sliceDescGen: GGSelect.SliceDescriptorGenerator; sliceDescGen _ GGSelect.SelectedSlices[gargoyleData.scene, normal]; FOR sliceD: GGSelect.SliceDescriptor _ GGSelect.NextSliceDescriptor[sliceDescGen], GGSelect.NextSliceDescriptor[sliceDescGen] UNTIL sliceD = NIL DO IF sliceD.slice.class.type = $IP THEN GGSlice.SetIncludeByValue[sliceD.slice, TRUE]; ENDLOOP; }; ShowIPIncludeMode: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; sliceDescGen: GGSelect.SliceDescriptorGenerator; includeByValue: BOOL; sliceDescGen _ GGSelect.SelectedSlices[gargoyleData.scene, normal]; FOR sliceD: GGSelect.SliceDescriptor _ GGSelect.NextSliceDescriptor[sliceDescGen], GGSelect.NextSliceDescriptor[sliceDescGen] UNTIL sliceD = NIL DO IF sliceD.slice.class.type = $IP THEN { includeByValue _ GGSlice.GetIncludeByValue[sliceD.slice]; IF includeByValue THEN GGError.Append[gargoyleData.feedback, "include by value", oneLiner] ELSE GGError.Append[gargoyleData.feedback, "include by reference", oneLiner]; RETURN; }; ENDLOOP; GGError.Append[gargoyleData.feedback, "ShowIPIncludeMode: No IP Slices selected", oneLiner]; GGError.Blink[gargoyleData.feedback]; }; ToIPAux: PROC [gargoyleData: GargoyleData, ipName: Rope.ROPE, actionAtom: ATOM, makeInterpress: PROC [dc: Imager.Context]] = { ipRef: ImagerInterpress.Ref; fullName: Rope.ROPE; success: BOOL; startTime: BasicTime.GMT; endTime: BasicTime.GMT; totalTime: INT; msgRope: Rope.ROPE; [fullName, success] _ GGUtility.GetInterpressFileName[ipName, gargoyleData.currentWDir, gargoyleData.feedback]; IF NOT success THEN RETURN; ipRef _ ImagerInterpress.Create[fullName]; msgRope _ IO.PutFR["%g %g . . . ", [rope[Atom.GetPName[actionAtom]]], [rope[fullName]]]; GGError.Append[gargoyleData.feedback, msgRope, begin]; startTime _ BasicTime.Now[]; ImagerInterpress.DoPage[ipRef, makeInterpress, 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]; GGEvent.SawTextFinish[NIL, gargoyleData]; }; ToIP: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { DoMakeInterpress: PROC [dc: Imager.Context] = { tempQuality: GGInterfaceTypes.QualityMode; tempStyle: GGInterfaceTypes.DisplayStyle; pixelsPerMeter: REAL = 0.0254/72.0; tempQuality _ gargoyleData.camera.quality; gargoyleData.camera.quality _ quality; tempStyle _ gargoyleData.camera.displayStyle; gargoyleData.camera.displayStyle _ print; Imager.ScaleT[dc, pixelsPerMeter]; GGRefresh.InterpressEntireScene[dc, gargoyleData]; gargoyleData.camera.quality _ tempQuality; gargoyleData.camera.displayStyle _ tempStyle; }; gargoyleData: GargoyleData _ NARROW[clientData]; ipName: Rope.ROPE _ NARROW[event.rest.first]; ToIPAux[gargoyleData, ipName, NARROW[event.first], DoMakeInterpress]; }; ToIPTestGravity: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { DoMakeInterpress: PROC [dc: Imager.Context] = { tempQuality: GGInterfaceTypes.QualityMode; tempStyle: GGInterfaceTypes.DisplayStyle; pixelsPerMeter: REAL = 0.0254/72.0; tempQuality _ gargoyleData.camera.quality; gargoyleData.camera.quality _ quality; tempStyle _ gargoyleData.camera.displayStyle; gargoyleData.camera.displayStyle _ print; Imager.ScaleT[dc, pixelsPerMeter]; TestGravity2[dc, gargoyleData]; gargoyleData.camera.quality _ tempQuality; gargoyleData.camera.displayStyle _ tempStyle; }; gargoyleData: GargoyleData _ NARROW[clientData]; ipName: Rope.ROPE _ NARROW[event.rest.first]; ToIPAux[gargoyleData, ipName, NARROW[event.first], DoMakeInterpress]; }; TestGravity2: PUBLIC PROC [dc: Imager.Context, gargoyleData: GargoyleData] = { xRandomStream, yRandomStream: Random.RandomStream; testPoint: Point; x, y: INT; totalCount, multiHitCount, uniHitCount, diffCount: NAT _ 0; uniPoint, multiPoint: Point; uniFeature, multiFeature: FeatureData; currentObjects: ObjectBag; sceneObjects: TriggerBag; xRandomStream _ Random.Create[gargoyleData.actionArea.cw]; yRandomStream _ Random.Create[gargoyleData.actionArea.ch]; GGAlign.SetBagsForAction[gargoyleData, $CaretPos]; gargoyleData.aborted[gravitytest] _ FALSE; -- in case there was one left over from prior abort UNTIL totalCount > 1000 DO IF gargoyleData.aborted[gravitytest] THEN { gargoyleData.aborted[gravitytest] _ FALSE; EXIT; }; x _ Random.NextInt[xRandomStream]; y _ Random.NextInt[yRandomStream]; testPoint _ [x, y]; testPoint _ GGWindow.ViewerToWorld[viewerPoint: testPoint, gargoyleData: gargoyleData]; gargoyleData.refresh.spotPoint _ testPoint; currentObjects _ gargoyleData.hitTest.currentObjectBag; sceneObjects _ gargoyleData.hitTest.sceneTriggerBag; [uniPoint, uniFeature] _ GGGravity.UniMap[testPoint, gargoyleData.hitTest.tolerance, currentObjects, sceneObjects, gargoyleData, TRUE]; [multiPoint, multiFeature] _ GGMultiGravity.Map[testPoint, gargoyleData.hitTest.tolerance, currentObjects, sceneObjects, gargoyleData, TRUE]; IF uniFeature = NIL AND multiFeature = NIL THEN { PaintSpot[dc, gargoyleData]; totalCount _ totalCount + 1; LOOP; }; IF uniFeature = NIL OR multiFeature = NIL OR uniFeature # multiFeature OR uniFeature.resultType # multiFeature.resultType OR uniPoint # multiPoint THEN { ReportResultsAndPaint2[dc, testPoint, uniPoint, uniFeature, multiPoint, multiFeature, gargoyleData]; totalCount _ totalCount + 1; diffCount _ diffCount + 1; IF multiFeature # NIL THEN multiHitCount _ multiHitCount + 1; IF uniFeature # NIL THEN uniHitCount _ uniHitCount + 1; } ELSE { multiHitCount _ multiHitCount + 1; uniHitCount _ uniHitCount + 1; totalCount _ totalCount + 1; gargoyleData.refresh.hitPoint _ multiPoint; PaintHitLine[dc, gargoyleData]; }; ENDLOOP; GGError.PutF[gargoyleData.feedback, oneLiner, "Tested %g total points. %g unihits. %g multihits. %g differences", [integer[totalCount]], [integer[uniHitCount]], [integer[multiHitCount]], [integer[diffCount]]]; }; -- end TestGravity2 PaintSpot: PROC [screen: Imager.Context, gargoyleData: GargoyleData] = { Imager.SetColor[screen, Imager.black]; GGShapes.DrawSpot[screen, gargoyleData.refresh.spotPoint]; }; PaintOddHitLine: PROC [screen: Imager.Context, gargoyleData: GargoyleData] = { Imager.SetColor[screen, Imager.black]; Imager.SetStrokeEnd[screen, round]; Imager.MaskVector[screen, [gargoyleData.refresh.spotPoint.x, gargoyleData.refresh.spotPoint.y], [gargoyleData.refresh.hitPoint.x, gargoyleData.refresh.hitPoint.y]]; GGShapes.DrawCP[screen, gargoyleData.refresh.spotPoint]; }; PaintHitLine: PROC [screen: Imager.Context, gargoyleData: GargoyleData] = { Imager.SetColor[screen, Imager.black]; Imager.SetStrokeEnd[screen, round]; Imager.MaskVector[screen, [gargoyleData.refresh.spotPoint.x, gargoyleData.refresh.spotPoint.y], [gargoyleData.refresh.hitPoint.x, gargoyleData.refresh.hitPoint.y]]; GGShapes.DrawFilledRect[screen, gargoyleData.refresh.spotPoint, 3.0]; }; ReportResultsAndPaint2: PROC [dc: Imager.Context, testPoint: Point, uniPoint: Point, uniFeature: FeatureData, multiPoint: Point, multiFeature: FeatureData, gargoyleData: GargoyleData] = { IF uniFeature = NIL THEN { gargoyleData.refresh.hitPoint _ multiPoint; } ELSE IF multiFeature = NIL THEN { gargoyleData.refresh.hitPoint _ uniPoint; } ELSE IF uniFeature # multiFeature THEN { gargoyleData.refresh.hitPoint _ multiPoint; } ELSE IF uniFeature.resultType # multiFeature.resultType THEN { gargoyleData.refresh.hitPoint _ multiPoint; } ELSE IF uniPoint # multiPoint THEN { gargoyleData.refresh.hitPoint _ uniPoint; PaintOddHitLine[dc, gargoyleData]; gargoyleData.refresh.hitPoint _ multiPoint; } ELSE ERROR; PaintOddHitLine[dc, gargoyleData]; }; ToIPScreen: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { DoMakeInterpress: PROC [dc: Imager.Context] = { tempQuality: GGInterfaceTypes.QualityMode; tempStyle: GGInterfaceTypes.DisplayStyle; pixelsPerMeter: REAL = 0.0254/72.0; tempQuality _ gargoyleData.camera.quality; gargoyleData.camera.quality _ quality; tempStyle _ gargoyleData.camera.displayStyle; gargoyleData.camera.displayStyle _ screen; Imager.ScaleT[dc, pixelsPerMeter]; GGRefresh.InterpressEntireScene[dc, gargoyleData]; gargoyleData.camera.quality _ tempQuality; gargoyleData.camera.displayStyle _ tempStyle; }; gargoyleData: GargoyleData _ NARROW[clientData]; ipName: Rope.ROPE _ NARROW[event.rest.first]; ToIPAux[gargoyleData, ipName, NARROW[event.first], DoMakeInterpress]; }; ToIPLit: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { DoMakeInterpress: PROC [dc: Imager.Context] = { switched: BOOL _ FALSE; tempQuality _ gargoyleData.camera.quality; gargoyleData.camera.quality _ showall; tempStyle _ gargoyleData.camera.displayStyle; gargoyleData.camera.displayStyle _ print; IF GGButtons.GetButtonState[gargoyleData.refresh.showColors] = off THEN { GGEvent.ToggleShowColors[NIL, gargoyleData]; switched _ TRUE; }; Imager.ScaleT[dc, pixelsPerMeter]; GGRefresh.PaintEntireScene[dc, gargoyleData]; gargoyleData.camera.quality _ tempQuality; gargoyleData.camera.displayStyle _ tempStyle; IF switched THEN GGEvent.ToggleShowColors[NIL, gargoyleData]; }; gargoyleData: GargoyleData _ NARROW[clientData]; 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; tempQuality: GGInterfaceTypes.QualityMode; tempStyle: GGInterfaceTypes.DisplayStyle; ipName: Rope.ROPE _ "litshot.ip"; [fullName, success] _ GGUtility.GetInterpressFileName[ipName, gargoyleData.currentWDir, gargoyleData.feedback]; 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]; GGEvent.SawTextFinish[NIL, gargoyleData]; }; PolyType: TYPE = {positive, negative}; PolyData: TYPE = REF PolyDataObj; PolyDataObj: TYPE = RECORD [ type: PolyType ]; HandleOverlap: PROC [currentClientdata, inputClientdata: REF] RETURNS [resultClientData: REF] = { resultClientData _ NEW[PolyDataObj _ [positive]]; }; HandleGlue: PROC [clientData1, clientData2: REF ANY] RETURNS [BOOL] = { RETURN[FALSE]; }; IsPositive: PROC [clientData: REF ANY] RETURNS [BOOL] = { polyData: PolyData _ NARROW[clientData]; IF polyData = NIL THEN RETURN[FALSE]; RETURN[polyData.type = positive]; }; UnionCombine: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; bBoxOfSelected: BoundBox _ GGBoundBox.BoundBoxOfSelected[gargoyleData.scene, normal]; IF bBoxOfSelected=NIL THEN RETURN; -- nothing selected IF PrincOpsUtils.IsBound[LOOPHOLE[CombinePoly.CreateDatabase]] THEN { startTime, endTime: BasicTime.GMT; totalTime: INT; GGError.PutF[gargoyleData.feedback, begin, "Combining selected convex polygons . . . "]; startTime _ BasicTime.Now[]; UnionCombineAux[gargoyleData]; endTime _ BasicTime.Now[]; totalTime _ BasicTime.Period[startTime, endTime]; GGError.PutF[gargoyleData.feedback, end, " Done in time (%r)", [integer[totalTime]]]; gargoyleData.refresh.startBoundBox^ _ bBoxOfSelected^; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: FALSE]; } ELSE { GGError.Append[gargoyleData.feedback, "Please run BasicCombiner.bcd and retry this operation", oneLiner]; GGError.Blink[gargoyleData.feedback]; }; }; -- end UnionCombine UnionCombineAux: PROC [clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; dbid: CombinePoly.DBID; polyID: CombinePoly.PolyID; seqGen: SequenceGenerator; regionGen: CombinePoly.RegionGenerator; jointGen: JointGenerator; pointGen: CombinePoly.PointGenerator; point, firstPoint, secondPoint: Point; polyData: REF ANY; firstPointAndDone, previousPointAndDone: CombinePoly.PointAndDone; success: BOOL; traj: Traj; seg: Segment; outline: Outline; jointNum: INT; dbid _ CombinePoly.CreateDatabase[]; seqGen _ GGSelect.SelectedSequences[gargoyleData.scene, normal]; FOR seq: Sequence _ GGSequence.NextSequence[seqGen], GGSequence.NextSequence[seqGen] UNTIL seq = NIL DO IF NOT GGSequence.IsComplete[seq] THEN LOOP; IF NOT seq.traj.role = fence THEN LOOP; polyData _ NEW[PolyDataObj _ [positive]]; jointGen _ GGSequence.JointsInSequence[seq]; jointNum _ GGSequence.NextJoint[jointGen]; firstPoint _ GGTraj.FetchJointPos[seq.traj, jointNum]; jointNum _ GGSequence.NextJoint[jointGen]; secondPoint _ GGTraj.FetchJointPos[seq.traj, jointNum]; polyID _ CombinePoly.CreatePolygon[firstPoint, secondPoint, polyData]; FOR jointNum: INT _ GGSequence.NextJoint[jointGen], GGSequence.NextJoint[jointGen] UNTIL jointNum = -1 DO point _ GGTraj.FetchJointPos[seq.traj, jointNum]; CombinePoly.AddPointToPolygon[point, polyID]; ENDLOOP; dbid _ CombinePoly.PolygonIntoDatabase[polyID, dbid, HandleOverlap, HandleGlue]; ENDLOOP; [] _ DeleteAllSelected[gargoyleData]; regionGen _ CombinePoly.MaximalRegions[dbid, IsPositive]; FOR region: CombinePoly.OutlineHolesDataAndDone _ CombinePoly.NextRegion[regionGen], CombinePoly.NextRegion[regionGen] UNTIL region.done DO pointGen _ region.outline; previousPointAndDone _ firstPointAndDone _ CombinePoly.NextPoint[pointGen]; IF previousPointAndDone.done THEN LOOP; traj _ GGTraj.CreateTraj[previousPointAndDone.point]; FOR pointAndDone: CombinePoly.PointAndDone _ CombinePoly.NextPoint[pointGen], CombinePoly.NextPoint[pointGen] UNTIL pointAndDone.done DO seg _ GGSegment.MakeLine[previousPointAndDone.point, pointAndDone.point, NIL]; success _ GGTraj.AddSegment[traj, hi, seg, lo]; IF NOT success THEN ERROR; previousPointAndDone _ pointAndDone; REPEAT FINISHED => { seg _ GGSegment.MakeLine[previousPointAndDone.point, firstPointAndDone.point, NIL]; GGTraj.CloseWithSegment[traj, seg, lo]; }; ENDLOOP; outline _ GGOutline.CreateOutline[traj, round, GGObjects.fillColor]; GGObjects.AddOutline[gargoyleData.scene, outline, -1]; ENDLOOP; }; OldDeleteAllSelected: PROC [gargoyleData: GargoyleData] RETURNS [bBox: BoundBox] = { sliceDescGen: SliceDescriptorGenerator; outSeqGen: GGSelect.OutlineSequenceGenerator; oldOutline: Outline; fenceSeq: Sequence; thisBox: BoundBox; bBox _ GGBoundBox.BoundBoxOfSelected[gargoyleData.scene, normal]; sliceDescGen _ GGSelect.SelectedSlices[gargoyleData.scene, normal]; FOR sliceD: SliceDescriptor _ GGSelect.NextSliceDescriptor[sliceDescGen], GGSelect.NextSliceDescriptor[sliceDescGen] UNTIL sliceD = NIL DO GGSelect.DeselectEntityAllClasses[sliceD.slice, gargoyleData.scene]; GGSlice.DeleteSlice[gargoyleData.scene, sliceD.slice]; ENDLOOP; outSeqGen _ GGSelect.SelectedOutlineSequences[gargoyleData.scene, normal]; WHILE outSeqGen.list # NIL DO FOR outSeq: GGSelect.OutlineSequence _ GGSelect.NextOutlineSequences[outSeqGen], GGSelect.NextOutlineSequences[outSeqGen] UNTIL outSeq = NIL DO fenceSeq _ outSeq.fenceSeq; FOR holeSeq: Sequence _ GGSequence.NextSequence[outSeq.holeSeqs], GGSequence.NextSequence[outSeq.holeSeqs] UNTIL holeSeq = NIL DO [oldOutline,----] _ GGInterface.DeleteSequence[holeSeq, gargoyleData.scene]; thisBox _ GGTraj.GetBoundBox[holeSeq.traj]; GGBoundBox.EnlargeByBox[bBox: bBox, by: thisBox]; GOTO SelectionsHaveChanged; ENDLOOP; IF fenceSeq # NIL THEN { [oldOutline,----] _ GGInterface.DeleteSequence[fenceSeq, gargoyleData.scene]; IF fenceSeq.traj.role = open THEN { GGSequence.UpdateBoundBox[fenceSeq]; GGBoundBox.EnlargeByBox[bBox: bBox, by: fenceSeq.boundBox]; } ELSE { thisBox _ GGTraj.GetBoundBox[fenceSeq.traj]; GGBoundBox.EnlargeByBox[bBox: bBox, by: thisBox]; }; GOTO SelectionsHaveChanged; }; REPEAT SelectionsHaveChanged => { outSeqGen _ GGSelect.SelectedOutlineSequences[gargoyleData.scene, normal]; }; ENDLOOP; ENDLOOP; }; DeleteAllSelected: PROC [gargoyleData: GargoyleData] RETURNS [bBox: BoundBox] = { DeleteRun: GGSelect.RunProc = { traj _ NIL; }; sliceDescGen: SliceDescriptorGenerator; thisBox: BoundBox; bBox _ GGBoundBox.BoundBoxOfSelected[gargoyleData.scene, normal]; sliceDescGen _ GGSelect.SelectedSlices[gargoyleData.scene, normal]; FOR sliceD: SliceDescriptor _ GGSelect.NextSliceDescriptor[sliceDescGen], GGSelect.NextSliceDescriptor[sliceDescGen] UNTIL sliceD = NIL DO GGSelect.DeselectEntityAllClasses[sliceD.slice, gargoyleData.scene]; GGSlice.DeleteSlice[gargoyleData.scene, sliceD.slice]; ENDLOOP; GGCaret.NoAttractor[gargoyleData.caret]; thisBox _ GGSelect.ForEachOutlineRun[gargoyleData.scene, gargoyleData.caret, normal, DeleteRun, FALSE]; GGBoundBox.EnlargeByBox[bBox: bBox, by: thisBox]; }; Delete: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; bBoxOfSelected: BoundBox; GGStatistics.StartInterval[$Delete, GGStatistics.GlobalTable[]]; bBoxOfSelected _ GGBoundBox.BoundBoxOfSelected[gargoyleData.scene, normal]; IF bBoxOfSelected=NIL THEN RETURN; -- nothing selected GGObjects.PushScene[gargoyleData.scene]; -- save for Undelete gargoyleData.refresh.startBoundBox^ _ DeleteAllSelected[gargoyleData]^; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE]; GGStatistics.StopInterval[$Delete, GGStatistics.GlobalTable[]]; }; -- end Delete Undelete: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; IF GGObjects.EmptySceneStack[gargoyleData.scene] OR NOT MessageWindow.Confirm["UNDELETE WILL LOSE MOST EDITS performed since last Delete"] THEN RETURN; GGSelect.DeselectAllAllClasses[gargoyleData.scene]; GGObjects.PopScene[gargoyleData.scene]; GGWindow.RestoreScreenAndInvariants[paintAction: $PaintEntireScene, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE]; }; SelectAll: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; GGStatistics.StartInterval[$SelectAll, GGStatistics.GlobalTable[]]; 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]; GGStatistics.StopInterval[$SelectAll, GGStatistics.GlobalTable[]]; }; AddHoles: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; seqGen: SequenceGenerator _ GGSelect.SelectedSequences[gargoyleData.scene, normal]; fenceSeq, firstHoleSeq: Sequence; fence, hole: Traj; outline: Outline; bBox: BoundBox _ GGBoundBox.BoundBoxOfSelected[gargoyleData.scene, normal]; fenceSeq _ GGSequence.NextSequence[seqGen]; IF fenceSeq = NIL OR fenceSeq.traj.role#fence THEN { GGError.AppendHerald[gargoyleData.feedback, "Select a closed trajectory to add holes to.", oneLiner]; GOTO Abort; }; fence _ fenceSeq.traj; outline _ GGOutline.OutlineOfTraj[fence]; firstHoleSeq _ GGSequence.NextSequence[seqGen]; IF firstHoleSeq = NIL THEN { GGError.AppendHerald[gargoyleData.feedback, "Extend your selection to some closed trajectories to make holes.", oneLiner]; GOTO Abort; }; FOR holeSeq: Sequence _ firstHoleSeq, GGSequence.NextSequence[seqGen] UNTIL holeSeq = NIL DO hole _ holeSeq.traj; IF NOT GGObjects.IsTopLevel[hole] THEN LOOP; IF holeSeq.traj.role#fence THEN { GGError.AppendHerald[gargoyleData.feedback, "Select only CLOSED trajectories as holes.", oneLiner]; LOOP; }; outline _ GGInterface.AddHole[outline, hole, gargoyleData.scene]; ENDLOOP; GGBoundBox.EnlargeByBox[bBox: bBox, by: outline.boundBox]; -- boundBox of old shape and new shape gargoyleData.refresh.startBoundBox^ _ bBox^; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: FALSE]; EXITS Abort => GGError.Blink[NARROW[clientData, GargoyleData].feedback]; }; -- end AddHoles gScale: REAL _ 0.33; gAngle: REAL _ 33; GetPt: PROC[p0, dir: Point] RETURNS [pt: Point] = { pt _ GGVector.Add[GGVector.Scale[GGVector.VectorPlusAngle[dir,gAngle], gScale*GGVector.Magnitude[dir] ], p0]; }; MakeCurveProc: TYPE = PROC [lo, hi: Point, props: LIST OF REF ANY] RETURNS [seg: Segment]; SetStraight: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { MakeStraightAux: MakeCurveProc = { seg _ GGSegment.MakeLine[lo, hi, props]; }; gargoyleData: GargoyleData _ NARROW[clientData]; SetCurveAux[gargoyleData, MakeStraightAux, $Line]; }; -- end SetStraight SetArc: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { MakeArcAux: MakeCurveProc = { p1: Point; p1 _ GGVector.Add[GGVector.Scale[GGVector.Sub[hi,lo], 0.5], lo]; seg _ GGSegment.MakeArc[lo, p1, hi, props]; }; gargoyleData: GargoyleData _ NARROW[clientData]; SetCurveAux[gargoyleData, MakeArcAux, $Arc]; }; -- end SetArc SetConic: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { MakeConicAux: MakeCurveProc = { p1: Point; p1 _ GGVector.Add[GGVector.Scale[GGVector.Sub[hi,lo], 0.5], lo]; seg _ GGSegment.MakeConic[lo, p1, hi, 0.7, props]; }; gargoyleData: GargoyleData _ NARROW[clientData]; SetCurveAux[gargoyleData, MakeConicAux, $Conic]; }; -- end SetConic SetBezier: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { MakeBezierAux: MakeCurveProc = { length: REAL; dir: Point; p1, p2: Point; length _ GGVector.Distance[lo, hi]; dir _ GGVector.Sub[hi,lo]; p1 _ GetPt[lo, dir]; p2 _ GetPt[hi, [-dir.x, -dir.y] ]; seg _ GGSegment.MakeBezier[lo, p1, p2, hi, props]; }; gargoyleData: GargoyleData _ NARROW[clientData]; SetCurveAux[gargoyleData, MakeBezierAux, $Bezier]; }; -- end SetBezier SegsToSegsMatchCp: PROC [seg: Segment, traj: Traj, makeCurve: MakeCurveProc, type: ATOM] = { tSeg: Segment; success: BOOL; IF seg.class.type = type THEN -- if seg is already the right type, then we don't change it success _ GGTraj.AddSegment[traj, hi, GGSegment.CopySegment[seg], lo] ELSE IF type = $Arc AND seg.class.controlPointCount[seg] = 1 THEN { -- if old and new segment types have single control points, keep the control points the same tSeg _ GGSegment.MakeArc[seg.lo, seg.class.controlPointGet[seg, 0], seg.hi, seg.props]; tSeg.color _ seg.color; tSeg.strokeWidth _ seg.strokeWidth; success _ GGTraj.AddSegment[traj, hi, tSeg , lo] } ELSE IF type = $Conic AND seg.class.controlPointCount[seg] = 1 THEN { -- if old and new segment types have single control points, keep the control points the same tSeg _ GGSegment.MakeConic[seg.lo, seg.class.controlPointGet[seg, 0], seg.hi, .7, seg.props]; tSeg.color _ seg.color; tSeg.strokeWidth _ seg.strokeWidth; success _ GGTraj.AddSegment[traj, hi, tSeg, lo] } ELSE { -- make segment of new type with old endpoint info tSeg _ makeCurve[seg.lo, seg.hi, seg.props]; tSeg.color _ seg.color; tSeg.strokeWidth _ seg.strokeWidth; success _ GGTraj.AddSegment[traj, hi, tSeg, lo]; }; IF NOT success THEN ERROR; }; SegsAndCpsToSegs: PROC [seg: Segment, traj: Traj, makeCurve: MakeCurveProc, type: ATOM] = { success: BOOL; IF seg.class.type = type THEN -- if seg is already the right type, then we don't change it success _ GGTraj.AddSegment[traj, hi, GGSegment.CopySegment[seg], lo] ELSE { -- seg isn't the right type, so we convert all of its point/controlpoint spans tSeg: Segment; last, next: Point _ seg.lo; FOR i:INT IN [0..seg.class.controlPointCount[seg]) DO next _ seg.class.controlPointGet[seg, i]; tSeg _ makeCurve[last, next, seg.props]; tSeg.color _ seg.color; tSeg.strokeWidth _ seg.strokeWidth; success _ GGTraj.AddSegment[traj, hi, tSeg, lo]; IF NOT success THEN ERROR; last _ next; ENDLOOP; tSeg _ makeCurve[last, seg.hi, seg.props]; tSeg.color _ seg.color; tSeg.strokeWidth _ seg.strokeWidth; success _ GGTraj.AddSegment[traj, hi, tSeg, lo]; IF NOT success THEN ERROR; }; }; SetCurveAux: PROC [gargoyleData: GargoyleData, makeCurve: MakeCurveProc, type: ATOM] = { RunOfSegs: GGSelect.RunProc = { segGen: GGSequence.SegmentGenerator _ GGSequence.SegmentsInSequence[run]; initialSeg: Segment _ GGSequence.NextSegment[segGen]; IF initialSeg = NIL THEN RETURN[NIL]; -- the run is a single vertex traj _ GGTraj.CreateTraj[initialSeg.lo]; FOR seg: Segment _ initialSeg, GGSequence.NextSegment[segGen] UNTIL seg = NIL DO SELECT seg.class.type FROM -- choose from old segment type $Line, $Arc, $Conic, $Bezier => SegsToSegsMatchCp[seg, traj, makeCurve, type]; -- modify traj $CubicSpline => { IF type=$Line OR (run.traj.segCount=1 AND run.traj.role#open) THEN SegsAndCpsToSegs[seg, traj, makeCurve, type] -- Splines are changed to multiple lines...(or multiple segs if spline was cyclic) ELSE SegsToSegsMatchCp[seg, traj, makeCurve, type]; -- ...but single everything else }; ENDCASE => ERROR; ENDLOOP; }; bBox: BoundBox; bBox _ GGSelect.ForEachOutlineRun[gargoyleData.scene, gargoyleData.caret, normal, RunOfSegs, TRUE, TRUE]; gargoyleData.refresh.startBoundBox^ _ bBox^; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE]; }; SetNaturalSpline: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; SetSpline[splineType: naturalAL, gargoyleData: gargoyleData]; }; SetBSpline: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; SetSpline[splineType: bspline, gargoyleData: gargoyleData]; }; SetSpline: PROC [splineType: CubicSplines.SplineType, gargoyleData: GargoyleData] = { RunToSpline: GGSelect.RunProc = { X: NAT = CubicSplines.X; Y: NAT = CubicSplines.Y; tSeg: Segment; controlPoint: Point; cyclic: BOOL _ run.traj.role#open AND GGSequence.IsComplete[run]; cps: CubicSplines.KnotSequence _ NEW[CubicSplines.KnotSequenceRec[IF cyclic THEN run.jointCount+run.controlPointCount+1 ELSE run.jointCount+run.controlPointCount]]; segGen: GGSequence.SegmentGenerator _ GGSequence.OrderedSegmentsInSequence[run]; initialSeg: Segment _ GGSequence.NextSegment[segGen]; success: BOOL; cpCount: NAT _ 0; IF initialSeg = NIL THEN RETURN[NIL]; IF cyclic AND splineType = naturalAL THEN splineType _ cyclicAL; traj _ GGTraj.CreateTraj[initialSeg.lo]; cps[cpCount] _ [initialSeg.lo.x, initialSeg.lo.y]; cpCount _ cpCount + 1; FOR seg: Segment _ initialSeg, GGSequence.NextSegment[segGen] UNTIL seg = NIL DO FOR i:INT IN [0..seg.class.controlPointCount[seg]) DO controlPoint _ seg.class.controlPointGet[seg, i]; cps[cpCount] _ [controlPoint.x, controlPoint.y]; cpCount _ cpCount + 1; ENDLOOP; cps[cpCount] _ [seg.hi.x, seg.hi.y]; cpCount _ cpCount + 1; ENDLOOP; IF cyclic THEN cps[cps.length-1] _ cps[0]; -- cumulative error sometimes causes small differences. DJK. tSeg _ GGSegment.MakeCubicSpline[cps, splineType, NIL]; tSeg.color _ initialSeg.color; tSeg.strokeWidth _ initialSeg.strokeWidth; success _ GGTraj.AddSegment[traj, hi, tSeg, lo]; IF NOT success THEN ERROR; }; gargoyleData.refresh.startBoundBox^ _ GGSelect.ForEachOutlineRun[gargoyleData.scene, gargoyleData.caret, normal, RunToSpline, TRUE, TRUE]^; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE] }; SelectMatchingCurve: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; name: Rope.ROPE _ IF event.rest = NIL THEN ViewerTools.GetSelectionContents[] ELSE NARROW[event.rest.first]; 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 segAndIndex: SegAndIndex _ GGSequence.NextSegmentAndIndex[segGen], GGSequence.NextSegmentAndIndex[segGen] UNTIL segAndIndex.seg = NIL DO IF Rope.Equal[segAndIndex.seg.class.describe[segAndIndex.seg, TRUE, TRUE, TRUE, NIL], name, FALSE] THEN { seq: Sequence _ GGSequence.CreateFromSegment[traj, segAndIndex.index]; GGSelect.SelectSequence[seq, gargoyleData.scene, normal]; }; ENDLOOP; ENDLOOP; GGError.PutFHerald[gargoyleData.feedback, oneLiner, "Curves of type %g selected", [rope[name]] ]; GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE]; }; Close: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; chair: REF ANY; traj: Traj; firstPoint, lastPoint: Point; seg: Segment; success: BOOL; chair _ GGCaret.GetChair[gargoyleData.caret]; BEGIN IF chair = NIL THEN GOTO NoCaretTraj; WITH chair SELECT FROM oD: OutlineDescriptor => { [success, ----, traj] _ GGOutline.UnpackSimpleDescriptorOld[oD]; IF NOT success THEN ERROR; }; sD: SliceDescriptor => { IF sD.slice.class.type # $Outline THEN GOTO NoCaretTraj; [success, ----, traj] _ GGOutline.UnpackSimpleDescriptor[sD]; IF NOT success THEN GOTO NoCaretTraj; }; ENDCASE => ERROR; IF traj.role = fence OR traj.role = hole THEN GOTO AlreadyClosed; GGError.AppendHerald[gargoyleData.feedback, "The caret trajectory will be closed.", oneLiner]; firstPoint _ GGTraj.FetchJointPos[traj, 0]; lastPoint _ GGTraj.LastJointPos[traj]; IF firstPoint # lastPoint THEN { seg _ GGSegment.MakeLine[lastPoint, firstPoint, NIL]; GGTraj.CloseWithSegment[traj, seg, lo]; GGSelect.ReselectTraj[traj, hi, gargoyleData.scene, TRUE]; } ELSE { GGOutline.SaveSelectionsInOutline[traj.parent, gargoyleData.scene]; GGSelect.DeselectEntityAllClasses[traj.parent, gargoyleData.scene]; GGTraj.CloseByDistorting[traj, hi]; GGOutline.RemakeSelectionsFromOutline[traj.parent, gargoyleData.scene]; }; GGOutline.OutlineOfTraj[traj].class.setFillColor[GGOutline.OutlineOfTraj[traj], GGObjects.fillColor]; GGSelect.SelectTraj[traj, gargoyleData.scene, normal]; GGCaret.SitOn[gargoyleData.caret, NIL]; GGCaret.NoAttractor[caret: gargoyleData.caret]; gargoyleData.refresh.startBoundBox^ _ traj.boundBox^; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: FALSE]; EXITS NoCaretTraj => { GGError.AppendHerald[gargoyleData.feedback, "There is no caret trajectory to close.", oneLiner]; GGError.Blink[gargoyleData.feedback]; }; AlreadyClosed => { GGError.AppendHerald[gargoyleData.feedback, "That trajectory is already closed.", oneLiner]; GGError.Blink[gargoyleData.feedback]; }; END; }; -- end Close ShowPoints: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; seqGen: SequenceGenerator _ GGSelect.SelectedSequences[gargoyleData.scene, normal]; FOR seq: Sequence _ GGSequence.NextSequence[seqGen], GGSequence.NextSequence[seqGen] UNTIL seq = NIL DO seq.traj.visibleJoints _ TRUE; ENDLOOP; }; HidePoints: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; seqGen: SequenceGenerator _ GGSelect.SelectedSequences[gargoyleData.scene, normal]; FOR seq: Sequence _ GGSequence.NextSequence[seqGen], GGSequence.NextSequence[seqGen] UNTIL seq = NIL DO seq.traj.visibleJoints _ FALSE; ENDLOOP; }; Refresh: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; doNotClear: BOOL _ event#NIL AND event.rest#NIL AND event.rest.first=$DoNotClearFeedback; GGWindow.RestoreScreenAndInvariants[paintAction: $PaintEntireScene, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: FALSE, edited: FALSE, okToClearFeedback: NOT doNotClear]; }; DisableRefresh: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; gargoyleData.refresh.suppressRefresh _ TRUE; }; EnableRefresh: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; gargoyleData.refresh.suppressRefresh _ FALSE; }; TestMultiGravity: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { }; TestGravity: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; xRandomStream, yRandomStream: Random.RandomStream; testPoint: Point; x, y: INT; totalCount, multiHitCount, uniHitCount, diffCount: NAT _ 0; uniPoint, multiPoint: Point; uniFeature, multiFeature: FeatureData; currentObjects: ObjectBag; sceneObjects: TriggerBag; xRandomStream _ Random.Create[gargoyleData.actionArea.cw]; yRandomStream _ Random.Create[gargoyleData.actionArea.ch]; GGAlign.SetBagsForAction[gargoyleData, $CaretPos]; gargoyleData.aborted[gravitytest] _ FALSE; -- in case there was one left over from prior abort UNTIL totalCount > 1000 DO IF gargoyleData.aborted[gravitytest] THEN { gargoyleData.aborted[gravitytest] _ FALSE; EXIT; }; x _ Random.NextInt[xRandomStream]; y _ Random.NextInt[yRandomStream]; testPoint _ [x, y]; testPoint _ GGWindow.ViewerToWorld[viewerPoint: testPoint, gargoyleData: gargoyleData]; gargoyleData.refresh.spotPoint _ testPoint; currentObjects _ gargoyleData.hitTest.currentObjectBag; sceneObjects _ gargoyleData.hitTest.sceneTriggerBag; [uniPoint, uniFeature] _ GGGravity.UniMap[testPoint, gargoyleData.hitTest.tolerance, currentObjects, sceneObjects, gargoyleData, TRUE]; [multiPoint, multiFeature] _ GGMultiGravity.Map[testPoint, gargoyleData.hitTest.tolerance, currentObjects, sceneObjects, gargoyleData, TRUE]; IF uniFeature = NIL AND multiFeature = NIL THEN { GGWindow.RestoreScreenAndInvariants[paintAction: $PaintSpot, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE]; totalCount _ totalCount + 1; LOOP; }; IF uniFeature = NIL OR multiFeature = NIL OR uniFeature # multiFeature OR uniFeature.resultType # multiFeature.resultType OR uniPoint # multiPoint THEN { ReportResultsAndPaint[testPoint, uniPoint, uniFeature, multiPoint, multiFeature, gargoyleData]; totalCount _ totalCount + 1; diffCount _ diffCount + 1; IF multiFeature # NIL THEN multiHitCount _ multiHitCount + 1; IF uniFeature # NIL THEN uniHitCount _ uniHitCount + 1; } ELSE { multiHitCount _ multiHitCount + 1; uniHitCount _ uniHitCount + 1; totalCount _ totalCount + 1; gargoyleData.refresh.hitPoint _ multiPoint; GGWindow.RestoreScreenAndInvariants[paintAction: $PaintHitLine, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE]; }; ENDLOOP; GGError.PutF[gargoyleData.feedback, oneLiner, "Tested %g total points. %g unihits. %g multihits. %g differences", [integer[totalCount]], [integer[uniHitCount]], [integer[multiHitCount]], [integer[diffCount]]]; }; -- end TestGravity ReportResultsAndPaint: PROC [testPoint: Point, uniPoint: Point, uniFeature: FeatureData, multiPoint: Point, multiFeature: FeatureData, gargoyleData: GargoyleData] = { multiMag, uniMag: REAL; IF uniFeature = NIL THEN { multiMag _ GGVector.Magnitude[GGVector.Sub[multiPoint, testPoint]]; GGError.PutFTypescript[gargoyleData.feedback, oneLiner, "No unihit at [%g, %g]. multihit distance: %g", [real[multiPoint.x]], [real[multiPoint.y]], [real[multiMag]]]; gargoyleData.refresh.hitPoint _ multiPoint; } ELSE IF multiFeature = NIL THEN { uniMag _ GGVector.Magnitude[GGVector.Sub[uniPoint, testPoint]]; GGError.PutFTypescript[gargoyleData.feedback, oneLiner, "No multihit at [%g, %g]. unihit distance: %g", [real[uniPoint.x]], [real[uniPoint.y]], [real[uniMag]]]; gargoyleData.refresh.hitPoint _ uniPoint; } ELSE IF uniFeature # multiFeature THEN { multiMag _ GGVector.Magnitude[GGVector.Sub[multiPoint, testPoint]]; uniMag _ GGVector.Magnitude[GGVector.Sub[uniPoint, testPoint]]; GGError.PutFTypescript[gargoyleData.feedback, oneLiner, "Features differ at [%g, %g] by %g to %g", [real[multiPoint.x]], [real[multiPoint.y]], [real[multiMag]], [real[uniMag]]]; gargoyleData.refresh.hitPoint _ multiPoint; } ELSE IF uniFeature.resultType # multiFeature.resultType THEN { GGError.PutFTypescript[gargoyleData.feedback, oneLiner, "Feature types differ at [%g, %g]", [real[multiPoint.x]], [real[multiPoint.y]]]; gargoyleData.refresh.hitPoint _ multiPoint; } ELSE IF uniPoint # multiPoint THEN { multiMag _ GGVector.Magnitude[GGVector.Sub[multiPoint, testPoint]]; uniMag _ GGVector.Magnitude[GGVector.Sub[uniPoint, testPoint]]; GGError.PutFTypescript[gargoyleData.feedback, oneLiner, "Points differ at [%g, %g] by %g to %g", [real[multiPoint.x]], [real[multiPoint.y]], [real[multiMag]], [real[uniMag]]]; gargoyleData.refresh.hitPoint _ uniPoint; GGWindow.RestoreScreenAndInvariants[paintAction: $PaintOddHitLine, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE]; gargoyleData.refresh.hitPoint _ multiPoint; } ELSE ERROR; GGWindow.RestoreScreenAndInvariants[paintAction: $PaintOddHitLine, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE]; }; Statistics: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; GGStatistics.PrintTable[gargoyleData.debug.typescriptOut, GGStatistics.GlobalTable[]]; }; ResetStatistics: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; GGStatistics.ResetTable[GGStatistics.GlobalTable[]]; }; DrawTouchPoints: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; GGWindow.RestoreScreenAndInvariants[paintAction: $PaintTouchPoints, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE]; }; DescribeTouchPoints: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; GGTouch.DescribeAllTouchPoints[gargoyleData]; }; DrawBoundBoxes: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; GGWindow.RestoreScreenAndInvariants[paintAction: $PaintBoundBoxes, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE]; }; DrawTightBoxes: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; GGWindow.RestoreScreenAndInvariants[paintAction: $PaintTightBoxes, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE]; }; DrawOutlineBoxes: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; GGWindow.RestoreScreenAndInvariants[paintAction: $PaintOutlineBoxes, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE]; }; DrawSelectionBox: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; GGWindow.RestoreScreenAndInvariants[paintAction: $PaintSelectionBox, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE]; }; DrawMovingBox: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; GGWindow.RestoreScreenAndInvariants[paintAction: $PaintMovingBox, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE]; }; DescribeCaretObject: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; description: Rope.ROPE _ "no object"; chair: REF ANY; chair _ GGCaret.GetChair[gargoyleData.caret]; IF chair # NIL THEN { WITH chair SELECT FROM outlineD: OutlineDescriptor => { description _ outlineD.slice.class.describe[outlineD.slice, outlineD.parts]; }; sliceD: SliceDescriptor => { description _ sliceD.slice.class.describe[sliceD.slice, sliceD.parts]; }; ENDCASE => ERROR; }; GGError.Append[gargoyleData.feedback, IO.PutFR["Caret is on %g.", [rope[description]]], oneLiner]; }; Typescript: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { GGError.OpenTypescript[NARROW[clientData, GargoyleData].feedback]; }; SlackLog: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; SlackProcess.OutputLog[gargoyleData.slackHandle, GGError.GetTypescriptStream[]]; }; defaultFontString: Rope.ROPE _ "Helvetica10"; Init: PROC [] = { GGStatistics.AddInterval[GGStatistics.CreateInterval[$SelectAll, NIL], GGStatistics.GlobalTable[]]; }; InitStats: PROC [] = { interval: GGStatistics.Interval; interval _ GGStatistics.CreateInterval[$Delete]; GGStatistics.AddInterval[interval, GGStatistics.GlobalTable[]]; }; Init[]; InitStats[]; END. †GGEventImplA.mesa Contents: Once an event reaches the front of the slack-process queue, it is dispatched to one of the procedures in this module. Stone, August 9, 1985 2:38:57 pm PDT Ken Pier, January 16, 1987 2:14:26 pm PST Bier, January 15, 1987 0:31:40 am PST Kurlander August 28, 1986 6:31:20 pm PDT If we ever allow text slices to have hot joints, we will have to be more careful here. If we ever allow text slices to have hot joints, we will have to be more careful here. IF IO.SkipWhitespace[stream: scratchStream]=Rope.Length[text] THEN RETURN[NIL]; -- only whitespace expects a font name of the form Helvetica7, Gacha11BI, Cream12B, .... expects a font name of the form Helvetica7, Gacha11BI, Cream12B, .... All selected IP Slices are now store-by-reference. All selected IP Slices are now store-by-value. Show whether the selected IP Slices is store-by-reference or store-by-value TIMING VARIABLES START TIMING Within the bounds of the viewer, randomly choose mouse positions. See if that mouse position is in range of any object. If so, draw a dot at that point. Repeat until 100 points have been drawn. TIMING VARIABLES START TIMING Hierarchy Menu Use Arnon's Combiner to find the union of all selected trajectories. The result of such a union may be a set of outlines with holes. For now, we assume that the result will be a set of outlines without holes (since holes are not implemented in Gargoyle yet. Create the Polygon Add the other points. Implemented enough for non-gargoyle slices only. Now delete selected sequences. Implemented enough for non-gargoyle slices only. Overly conservative. Just in case the caret was attracted to something that was just deleted. Curve Menu Replace segs of the old type with segs of the new type, maintaining control point data if possible Replace old segments with segments of the new type, but if the old segment had control points, use these also as joints of new segments. Use GGSelect.ForEachOutlineRun to replace all selected runs with runs of a new type. Strategy: Replace each selected run with a new trajectory which has a segment (of the proper type) for each joints/control point span. Note that we we copy verbatim all segs which were already of the specified type. The new segments inherit color and strokeWidth from the old ones. [run: GGModelTypes.Sequence] RETURNS [traj: GGModelTypes.Traj] [run: GGModelTypes.Sequence] RETURNS [traj: GGModelTypes.Traj] Edit Curve Menu N.B. It's a shame we have to remake the triggerBag here. This is because the closed trajectory might have been hot. Its hot sequence is obsolete. View Menu Debug Menu TestMultiGravity: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { gargoyleData: GargoyleData _ NARROW[clientData]; Within the bounds of the viewer, randomly choose mouse positions. See if that mouse position is in range of any object. If so, draw a dot at that point. Repeat until 100 points have been drawn. xRandomStream, yRandomStream: Random.RandomStream; testPoint: Point; x, y: INT; totalCount: NAT _ 0; features: GGMultiGravity.NearFeatures; points: GGMultiGravity.NearPoints; distances: GGMultiGravity.NearDistances; currentObjects: ObjectBag; sceneObjects: TriggerBag; count: NAT; xRandomStream _ Random.Create[gargoyleData.actionArea.cw]; yRandomStream _ Random.Create[gargoyleData.actionArea.ch]; GGAlign.SetBagsForAction[gargoyleData, $CaretPos]; GGWindow.RestoreScreenAndInvariants[paintAction: $PaintAlign, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE]; gargoyleData.hitTest.hitCount _ 0; gargoyleData.aborted[gravitytest] _ FALSE; -- in case there was one left over from prior abort UNTIL totalCount > 1000 DO IF gargoyleData.aborted[gravitytest] THEN { gargoyleData.aborted[gravitytest] _ FALSE; EXIT; }; x _ Random.NextInt[xRandomStream]; y _ Random.NextInt[yRandomStream]; testPoint _ [x, y]; testPoint _ GGWindow.ViewerToWorld[viewerPoint: testPoint, gargoyleData: gargoyleData]; gargoyleData.refresh.spotPoint _ testPoint; currentObjects _ gargoyleData.hitTest.currentObjectBag; sceneObjects _ gargoyleData.hitTest.sceneTriggerBag; [features, points, distances, count] _ GGMultiGravity.MultiMap[20, testPoint, gargoyleData.hitTest.tolerance, currentObjects, sceneObjects, gargoyleData, TRUE]; IF count > 0 THEN { FOR i: NAT IN [0..count-1] DO gargoyleData.refresh.hitPoint _ points[i]; IF distances[i] > gargoyleData.hitTest.tolerance THEN GOTO Done; GGWindow.RestoreScreenAndInvariants[paintAction: $PaintHitLine, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE]; gargoyleData.hitTest.hitCount _ gargoyleData.hitTest.hitCount + 1; REPEAT Done => { IF i = 0 THEN GGWindow.RestoreScreenAndInvariants[paintAction: $PaintSpot, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE]; }; ENDLOOP; } ELSE { GGWindow.RestoreScreenAndInvariants[paintAction: $PaintSpot, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE]; }; totalCount _ totalCount + 1; ENDLOOP; GGError.PutF[gargoyleData.feedback, oneLiner, "Tested %g total points. %g were hits", [integer[totalCount]], [integer[gargoyleData.hitTest.hitCount]]]; }; Within the bounds of the viewer, randomly choose mouse positions. See if that mouse position is in range of any object. If so, draw a dot at that point. Repeat until 100 points have been drawn. gargoyleData.hitTest.hitCount _ 0; Ê@l˜Icode™šœ€™€K™$Kšœ)™)Kšœ%™%Kšœ(™(—K™šÏk ˜ K˜Kšœ±œZ˜K˜—šÏn œœ˜KšœßœG˜¯Kšœ ˜—˜Kšœ œ˜%Kšœœ˜!Kšœœ˜3Kšœœ ˜5Kšœœ)˜GKšœœ ˜5Kšœ œ ˜1Kšœœ!˜3Kšœœ˜!Kšœœ˜3Kšœ œ˜&Kšœœ"˜9Kšœ œ˜(Kšœ œ˜%Kšœœ˜!Kšœœ˜!Kšœ œ˜'Kšœ œ˜+Kšœ œ˜'Kšœ œ˜+Kšœœ˜Kšœœ˜1Kšœœ!˜7Kšœœ"˜9Kšœœ˜#—K˜Kšžœœœœ˜(Kš œœœœœ˜&K˜šžœœ'œœ˜JšœœœÏc7˜SK•StartOfExpansion_[gargoyleData: GGInterfaceTypes.GargoyleData, selectClass: GGInterfaceTypes.SelectionClass]šœ^˜^KšœH˜Hš œ œœ"œ,œœ˜mKšœ`˜`Kšœ%˜%Kšœ˜K˜—K–|[slice: GGModelTypes.Slice, gargoyleData: GGInterfaceTypes.GargoyleData, selectClass: GGInterfaceTypes.SelectionClass]šœw˜wKšœ7Ÿ"˜YKš œ1Ïbœ7œ œœ˜§Kšœ˜—šœ˜Kšœ5  œ\Ÿ˜¶Kš œœœœŸ'˜AKšœ6˜6Kšœ)˜)Kš œ1  œ;œ œœ˜¦Kšœ˜—KšœŸ˜K˜—šžœœœ œœœœœœ˜FKšœœ ˜0Kšœ ˜ Kšœ˜Kšœ œœ˜Kš œœœ œœ˜1Kšœ œ˜#šœ%œœ˜1K–ß[caret: GGInterfaceTypes.Caret, point: GGBasicTypes.Point, attractor: REF ANY _ NIL, on: GGInterfaceTypes.CaretOn _ nothing, attractorJointNum: NAT _ 999, attractorCPNum: NAT _ 999, attractorSegNum: NAT _ 999]šœ)Ÿ˜FKšœ(˜(Kšœ˜Kšœ˜—šœŸ˜KšœA˜AKšœ,˜,Kšœ1Ÿ!˜Ršœœœ˜Kšœ$˜$Kšœ7Ÿ˜UKšœM˜Mš œ1 œ;œ œœ˜¸KšœV™V—Kšœ˜—šœ˜KšœA˜AKšœ7Ÿ"˜YKšœM˜MKšœ)˜)š œ1  œ7œ œœ˜¢KšœV™V—Kšœ˜—Kšœ@˜@Kšœ˜—KšœŸ˜K˜—š ž œœœœœ˜oKšœ7˜7Kšœœœ˜,K–0[stream: STREAM, flushComments: BOOL _ TRUE]š œœ9œœœŸ™bKšœŠ˜ŠKšœ2˜2Kšœ6˜6Kšœ2Ÿ˜IKšœ)˜)Kšœxœ œœ˜¦KšœŸ˜K˜—šžœœœ œœœœœœ˜FKšœœ ˜0Kš œ œœœœ$œœ˜lKšœ9˜9K˜K˜—šžœœœ œœœœœœ˜TKšœœ ˜0–)[rope: ROPE, oldStream: STREAM _ NIL]š œœœœœ$œœ˜rK–-[stream: STREAM, breakProc: IO.BreakProc]š E™E—KšœW˜WK–h[gargoyleData: GGInterfaceTypes.GargoyleData, selectClass: GGInterfaceTypes.SelectionClass _ normal]šœbŸ˜€šœfœœ˜|KšœE˜EKšœ˜—K–<[bBox: GGModelTypes.BoundBox, by: GGModelTypes.BoundBox]šœ‚˜‚KšœŒœ œœŸ.˜êKšœŸ˜K˜—šžœœœ œœœœœœ˜TKšœœ ˜0Kšœ]˜]KšœH˜Hš œ œœ"œ,œœ˜mKšœf˜fKšœ%˜%K˜—Kšœw˜{K˜K˜—šžœœœ œœœœœœ˜QKšœœ ˜0Kš œ œœœœ$œœ˜lKšœG˜Gšœœ˜K–K[feedback: ViewerClasses.Viewer, msg: ROPE, msgType: GGError.MsgType]šœY˜YKšœ˜K˜—Kšœ1˜1šœMœ œ˜dKšœ7œœGœ=˜Ë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[]]]šœh˜hKšœyœ œœ˜¨K˜K˜—šžœœœ œœœœœœ˜WKšœ˜Kšœœ˜Kšœœ ˜0–)[rope: ROPE, oldStream: STREAM _ NIL]š œœœœœ$œœ˜rK–-[stream: STREAM, breakProc: IO.BreakProc]š E™E—KšœzœŸc˜ãKšœ/Ÿ!˜Pš œœœ"œœ˜>Kšœ˜Kšœf˜fKšœ˜—šœ˜KšœN˜NKšœ%˜%K˜—Kšœ˜K˜—šžœœœ œœœœœœ˜SKšœœ ˜0Kšœb˜bK˜K˜—šžœœœ œœœœœœ˜XKšœœ ˜0Kš œœœœœ˜9KšœW˜WK–h[gargoyleData: GGInterfaceTypes.GargoyleData, selectClass: GGInterfaceTypes.SelectionClass _ normal]šœbŸ˜€šœfœœ˜|KšœO˜OKšœ˜—K–<[bBox: GGModelTypes.BoundBox, by: GGModelTypes.BoundBox]šœ‚˜‚KšœŒœ œœŸ6˜òKšœŸ ˜#K˜—šžœœœ œœœœœœ˜PKšœœ ˜0Kšœ]˜]KšœH˜Hš œ œœ"œ,œœ˜mKšœo˜oKšœ%˜%K˜—Kšœz˜~K˜K˜K˜—šž œœœ œœœœœœ˜KKšœœ ˜0Kš œ œœœœ˜4Kš œ œœœœ˜9KšœW˜WK–h[gargoyleData: GGInterfaceTypes.GargoyleData, selectClass: GGInterfaceTypes.SelectionClass _ normal]šœbŸ˜€šœfœœ˜|Kšœ7˜7Kšœ˜—K–<[bBox: GGModelTypes.BoundBox, by: GGModelTypes.BoundBox]šœ‚˜‚KšœŒœ œœ˜ºK˜K˜—šž œœœ œœœœœœ˜LKšœœ ˜0KšœW˜WK–h[gargoyleData: GGInterfaceTypes.GargoyleData, selectClass: GGInterfaceTypes.SelectionClass _ normal]šœbŸ˜€šœfœœ˜|Kšœ$˜$Kšœ˜—K–<[bBox: GGModelTypes.BoundBox, by: GGModelTypes.BoundBox]šœ‚˜‚KšœŒœ œœ˜ºK˜—K˜šžœœœœœœœœ˜išž œœ˜.Jš "Ðbc˜Kšœ+˜+K˜—šœœœ˜$Kšœ)˜)Kšœ"˜"Kšœ+˜+K˜—Kšœœ˜ Kšœ"˜"K˜K˜K˜—š£ œœœ œœœœœœ˜Išžœœ˜/Kšœ*˜*Kšœ)˜)Kšœœ˜#Kšœ*˜*Kšœ&˜&K˜Kšœ-˜-Kšœ*˜*K˜K˜"Kšœ  œ˜2K˜Kšœ*˜*Kšœ-˜-K˜—Kšœœ ˜0Kšœ œœ˜-Kšœœ!˜EK˜K˜—š£œœœ œœœœœœ˜Fšžœœ˜/Kšœ œœ˜Kšœ*˜*Kšœ&˜&Kšœ-˜-Kšœ)˜)šœAœœ˜vKšœ œ˜K˜—K˜"Kšœ-˜-Kšœ*˜*Kšœ-˜-Kšœ œœ˜=K˜—Kšœœ ˜0Kšœ˜Kšœœ˜Kšœ œ˜Kšœœ˜#Kšœ™Kšœœ˜Kšœœ˜Kšœ œ˜Kšœœ˜Kšœ*˜*Kšœ)˜)Kšœ œ˜!K˜Kšœo˜oKšœœ œœ˜Kšœ*˜*Kšœ œ:˜FKšœ6˜6Kšœ ™ Kš ˜Kšœ6˜6Kšœ˜Kš ˜Kšœ1˜1Kšœ œ3˜?Kšœ4˜4Kšœœ˜)K˜K˜—K™K˜Kšœ œ˜&Kšœ œœ ˜!šœ œœ˜K˜Kšœ˜—š ž œœ&œœœ˜aKšœœ˜1K˜—š ž œœœœœœ˜GKšœœ˜K˜K˜—š ž œœœœœœ˜9Kšœœ ˜(Kš œ œœœœ˜%Kšœ˜!K˜K˜—šž œœœ œœœœœœ˜KKšœœ ˜0KšœU˜UKš œœœœŸ˜6šœœœ˜EKšœœ˜"Kšœ œ˜KšœX˜XKš ˜Kšœ˜Kš ˜Kšœ1˜1KšœU˜UKšœ6˜6KšœŒœ œœ˜»K˜—šœ˜Kšœi˜iKšœ%˜%K˜—KšœŸ˜K˜—šžœœœœ˜/Kšœœ ˜0K™ƒKšœœ˜Kšœ˜K˜Kšœ'˜'K˜Kšœ%˜%K˜&Kšœ œœ˜KšœB˜BKšœ œ˜K˜ K˜ Kšœ˜Kšœ œ˜Kšœ$˜$Kšœ@˜@šœRœœ˜gKšœœœœ˜,Kšœœœœ˜'Kšœ œ˜)šœ,˜,Kš ™—Kšœ*˜*Kšœ6˜6Kšœ*˜*Kšœ7˜7šœF˜FKš ™—šœ œBœ˜iKšœ1˜1Kšœ-˜-Kšœ˜—KšœP˜PKšœ˜—Kšœ%˜%Kšœ9˜9šœtœ ˜‹Kšœ˜KšœK˜KKšœœœ˜'Kšœ5˜5šœkœ˜ˆKšœIœ˜NKšœ/˜/Kšœœ œœ˜Kšœ$˜$š˜šœ˜ KšœNœ˜SKšœ'˜'K˜——Kšœ˜—KšœD˜DKšœ6˜6Kšœ˜—˜K˜——šžœœœ˜UK˜'Kšœ-˜-Kšœ˜Kšœ˜K˜šœA˜AKš 0™0—KšœC˜Cšœrœ œ˜ŠKšœD˜DKšœ6˜6Jšœ˜Jš ™—JšœJ˜Jšœœ˜šœwœ œ˜Jšœ˜šœhœ œ˜Kšœ Ÿœ<˜LKšœ+˜+Kšœ1˜1Kšœ˜Jšœ˜—šœ œœ˜Kšœ Ÿœ=˜Mšœœ˜#Kšœ$˜$Kšœ;˜;K˜—šœ˜Kšœ,˜,Kšœ1˜1K˜—Kšœ˜K˜—š˜˜JšœJ˜JJ˜——šœ˜J˜——Jšœ˜—K˜K˜—šžœœœ˜Ršž œ˜Jšœœ˜ J˜—K˜'K˜šœA˜AKš 0™0—KšœC˜Cšœrœ œ˜ŠKšœD˜DKšœ6˜6Jšœ˜—šœ(˜(J™^—Jšœ`œ˜gKšœ1˜1K˜K˜—šžœœœ œœœœœœ˜EKšœœ ˜0Kšœ˜Kšœ@˜@KšœK˜KKš œœœœŸ˜6Kšœ=˜=Kšœ& œ˜GKšœŒœ œœ˜ºKšœ?˜?KšœŸ ˜K˜—šžœœœ œœœœœœ˜GKšœœ ˜0Kš œ/œœTœœ˜—Kšœ3˜3Kšœ'˜'Kšœœ œœ˜­Kšœ˜K˜—šž œœœ œœœœœœ˜HKšœœ ˜0KšœC˜CKšœ1˜1Kšœ/˜/Kšœœ˜)Kšœyœ œœ˜§KšœB˜BK˜K˜—šžœœœ œœœœœœ˜GKšœœ ˜0KšœS˜SKšœ!˜!Kšœ˜K˜KšœK˜KKšœ+˜+šœ œœœ˜4Kšœe˜eKšœ˜ K˜—Kšœ˜Kšœ)˜)Kšœ/˜/šœœœ˜Kšœz˜zKšœ˜ Kšœ˜—šœCœ œ˜\Kšœ˜Kšœœœœ˜,šœœ˜!Kšœc˜cKšœ˜K˜—Kš A˜AKšœ˜—Kšœ;Ÿ&˜aKšœ,˜,KšœŒœ œœ˜»š˜Kšœœ%˜B—KšœŸ˜K˜—K™ K˜Kšœœ˜Kšœœ˜šžœœœ˜3Kšœm˜mKšœ˜K˜—Kšœœœœœœœœ˜ZK˜šž œœœ œœœœœœ˜Jšžœ˜"Kšœ(˜(Kšœ˜—Kšœœ ˜0Kšœ2˜2KšœŸ˜K˜—šžœœœ œœœœœœ˜Ešž œ˜K˜ Kšœ@˜@Kšœ+˜+Kšœ˜—Kšœœ ˜0Kšœ,˜,KšœŸ ˜K˜—šžœœœ œœœœœœ˜Gšž œ˜K˜ Kšœ@˜@Kšœ2˜2Kšœ˜—Kšœœ ˜0Kšœ0˜0KšœŸ˜K˜—šž œœœ œœœœœœ˜Hšž œ˜ Kšœœ˜ Kšœ ˜ K˜Kšœ#˜#Kšœ˜Kšœ˜Kšœ"˜"Kšœ2˜2Kšœ˜—Kšœœ ˜0Kšœ2˜2KšœŸ˜K˜—šžœœ<œ˜\K™bK˜Kšœ œ˜šœœŸ<˜ZK˜E—š œœ œ&œŸ\˜ KšœW˜WK˜œ˜XKšœT™TK™œ–B -- [run: GGModelTypes.Sequence] RETURNS [traj: GGModelTypes.Traj]šž œ˜KšÐck>™>KšœI˜IK˜5Kš œœœœœŸ˜CK˜(šœ;œœ˜PšœœŸ˜:Kšœ]˜]šœ˜Kš œ œœœ.ŸR˜ÂKšœ1Ÿ ˜UK˜—Kšœœ˜—Kšœ˜—K˜—Kšœ˜Kšœ]œœ˜iKšœ,˜,KšœŒœ œœ˜ºK˜K˜—šžœœœ œœœœœœ˜OKšœœ ˜0K˜=K˜K˜—šž œœœ œœœœœœ˜IKšœœ ˜0K˜;K˜K˜—šž œœF˜Ušž œ˜!Kš¤>™>Kšžœœ˜Kšžœœ˜K˜K˜Kšœœœ˜BKš œ!œœœ(œ(˜¤KšœP˜PKšœ5˜5Kšœ œ˜Kšœ œ˜Kš œœœœœ˜%Kšœœœ˜@Kšœ(˜(Kšœ2˜2K˜šœ;œœ˜Pšœœœ'˜5Kšœ1˜1K˜0K˜Kšœ˜—Jšœ$˜$J˜Kšœ˜—KšœœŸ=˜hKšœ2œ˜7KšœJ˜JJšœ0˜0Jšœœ œœ˜Kšœ˜—Kšœ~œœ˜‹KšœŒœ œœ˜¹K˜K™—šžœœœ œœœœœœ˜RKšœœ ˜0Kš œ œœœœ$œœ˜lKšœD˜DKšœ1˜1šœGœœ˜]Kšœ;˜;šœkœœ˜Œ–[atom: ATOM]šœ<œœœœ œœ˜iKšœF˜FKšœ9˜9Kšœ˜—Kšœ˜—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[]]]šœa˜aKšœyœ œœ˜¨K˜—K˜K™šžœœœ œœœœœœ˜DKšœœ ˜0Kšœœœ˜Kšœ ˜ Kšœ˜K˜ Kšœ œ˜Kšœ-˜-š˜Kšœ œœœ ˜%šœœ˜˜Kšœ@˜@Kšœœ œœ˜K˜—šœ˜Kšœ œœ ˜8Kšœ=˜=Kšœœ œœ ˜%K˜—Kšœœ˜—Kšœœœ˜AKšœ^˜^Kšœ+˜+Kšœ&˜&šœœ˜ Kšœ0œ˜5Kšœ'˜'Kšœ4œ˜:K˜—šœ˜KšœC˜CKšœC˜CKšœ#˜#KšœG˜GK˜—Kšœe˜eK–x[traj: GGModelTypes.Traj, gargoyleData: GGInterfaceTypes.GargoyleData, selectClass: GGSegmentTypes.SelectionClass]šœ6˜6K–£[caret: GGInterfaceTypes.Caret, on: GGInterfaceTypes.CaretOn _ nothing, chair: REF ANY _ NIL, jointNum: NAT _ 999, cpNum: NAT _ 999, segNum: NAT _ 999]šœ"œ˜'K–![caret: GGInterfaceTypes.Caret]šœ/˜/Kšœ5˜5šœŒœ œœ˜»Kšœ”™”—š˜˜Kšœ`˜`Kšœ%˜%K˜—šœ˜Kšœ\˜\Kšœ%˜%K˜——Kšœ˜—KšœŸ ˜K˜—šž œœœ œœœœœœ˜IKšœœ ˜0KšœS˜SšœRœœ˜gKšœœ˜Kšœ˜—K˜K˜—šž œœœ œœœœœœ˜IKšœœ ˜0KšœS˜SšœRœœ˜gKšœœ˜Kšœ˜—K˜K˜—K™K™ šžœœœ œœœœœœ˜FKšœœ ˜0Kš œ œ œœ œœ&˜YKšœœ œœ ˜¸K˜K˜—šžœœœ œœœœœœ˜MKšœœ ˜0Kšœ'œ˜,K˜K˜—šž œœœ œœœœœœ˜LKšœœ ˜0Kšœ'œ˜-K˜K˜—K˜K™ šžœœœ œœœœœœ˜OK˜—šžœœœ œœœœœœ™OKšœœ ™0K™ÄKšœ2™2Kšœ™Kšœœ™ Kšœ œ™Kšœ&™&Kšœ"™"Kšœ(™(K™K™Kšœœ™ Kšœ:™:Kšœ:™:Kšœ2™2Kšœsœ œœ™¢Kšœ"™"Kšœ$œŸ3™_šœ™šœ#œ™+Kšœ$œ™*Kšœ™K™—Kšœ"™"Kšœ"™"K™KšœW™WKš œ ™+Kšœ7™7Kšœ4™4Kšœ6 œ\œ™ šœ œ™šœœœ™Kš œ ™*Kšœ/œœ™@Kšœuœ œœ™¤KšœB™Bš™šœ ™ Kšœ™ Kšœrœ œœ™¡K™——Kšœ™—K™—šœ™Kšœrœ œœ™¡K™—K™Kšœ™—Kšœ˜™˜K™K™—šž œœœ œœœœœœ˜JKšœœ ˜0K™ÄKšœ2˜2Kšœ˜Kšœœ˜ Kšœ3œ˜;Kšœ˜Kšœ&˜&K˜K˜Kšœ:˜:Kšœ:˜:Kšœ2˜2Kšœ"™"Kšœ$œŸ3˜_šœ˜šœ#œ˜+Kšœ$œ˜*Kšœ˜K˜—Kšœ"˜"Kšœ"˜"K˜KšœW˜WKš +˜+Kšœ7˜7Kšœ4˜4Kšœ# œXœ˜‡K–Ò[testPoint: GGBasicTypes.Point, criticalR: REAL, currentObjects: GGMultiGravity.ObjectBag, activeObjects: GGMultiGravity.TriggerBag, gargoyleData: GGInterfaceTypes.GargoyleData, intersections: BOOL]šœ œXœ˜š œœœœœ˜1Kšœrœ œœ˜¡K˜Kšœ˜K˜—šœœœœœœ1œœ˜™Kšœ_˜_K˜K˜Kšœœœ#˜=Kšœœœ˜7K˜—šœ˜Kšœ"˜"Kšœ˜K˜Kšœ+˜+Kšœuœ œœ˜¤K˜—Kšœ˜—KšœÔ˜ÔKšœŸ˜K˜—šžœœ‹˜¦Kšœœ˜šœœœ˜KšœC˜CKšœ§˜§Kšœ+˜+K˜—šœœœœ˜!Kšœ?˜?Kšœ¡˜¡Kšœ)˜)K˜—šœœœ˜(KšœC˜CKšœ?˜?Kšœ±˜±Kšœ+˜+K˜—šœœ1œ˜>Kšœˆ˜ˆKšœ+˜+K˜—šœœœ˜$KšœC˜CKšœ?˜?Kšœ¯˜¯Kšœ)˜)Kšœxœ œœ˜§Kšœ+˜+K˜—Kšœœ˜ Kšœxœ œœ˜§K˜K˜—šž œœœ œœœœœœ˜IKšœœ ˜0KšœV˜VK˜K˜—šžœœœ œœœœœœ˜NKšœœ ˜0Kšœ4˜4K˜K˜—šžœœœ œœœœœœ˜NKšœœ ˜0Kšœyœ œœ˜§K˜K˜—šžœœœ œœœœœœ˜RKšœœ ˜0Kšœ-˜-K˜K˜—šžœœœ œœœœœœ˜MKšœœ ˜0Kšœxœ œœ˜¦K˜K˜—šžœœœ œœœœœœ˜MKšœœ ˜0Kšœxœ œœ˜¦K˜K˜K˜—šžœœœ œœœœœœ˜OKšœœ ˜0Kšœzœ œœ˜¨K˜K˜—šžœœœ œœœœœœ˜OKšœœ ˜0Kšœzœ œœ˜¨K˜K˜—šž œœœ œœœœœœ˜LKšœœ ˜0Kšœwœ œœ˜¥K˜K˜—šžœœœ œœœœœœ˜RKšœœ ˜0Kšœœ˜%Kšœœœ˜Kšœ-˜-šœ œœ˜šœœ˜˜ KšœL˜LK˜—˜KšœF˜FK˜—Kšœœ˜—K˜—Kšœ&œ:˜bK˜K˜—šž œœœ œœœœœœ˜IKšœœ%˜BKšœ˜K˜—šžœœœ œœœœœœ˜GKšœœ ˜0KšœP˜PK˜—K˜Kšœœ˜-šžœœ˜KšœAœ˜cK˜K˜—šž œœ˜K˜ Kšœ0˜0Kšœ?˜?K˜K˜—K˜K˜ K˜Kšœ˜—…—å*;