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
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];
If we ever allow text slices to have hot joints, we will have to be more careful here.
}
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];
If we ever allow text slices to have hot joints, we will have to be more careful here.
};
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];
IF IO.SkipWhitespace[stream: scratchStream]=Rope.Length[text] THEN RETURN[NIL]; -- only whitespace
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];
expects a font name of the form Helvetica7, Gacha11BI, Cream12B, ....
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];
expects a font name of the form Helvetica7, Gacha11BI, Cream12B, ....
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] = {
All selected IP Slices are now store-by-reference.
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] = {
All selected IP Slices are now store-by-value.
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] = {
Show whether the selected IP Slices is store-by-reference or store-by-value
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;
TIMING VARIABLES
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];
START TIMING
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] = {
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, 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;
TIMING VARIABLES
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];
START TIMING
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];
};
Hierarchy Menu
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];
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.
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];
Create the Polygon
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];
Add the other points.
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];
Implemented enough for non-gargoyle slices only.
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;
Now delete selected sequences.
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;
};
DeleteAllSelected:
PROC [gargoyleData: GargoyleData]
RETURNS [bBox: BoundBox] = {
DeleteRun: GGSelect.RunProc = {
traj ← NIL;
};
sliceDescGen: SliceDescriptorGenerator;
thisBox: BoundBox;
bBox ← GGBoundBox.BoundBoxOfSelected[gargoyleData.scene, normal];
Implemented enough for non-gargoyle slices only.
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];
Overly conservative. Just in case the caret was attracted to something that was just deleted.
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
Curve Menu
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] = {
Replace segs of the old type with segs of the new type, maintaining control point data if possible
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] = {
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.
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] = {
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.
RunOfSegs: GGSelect.RunProc = {
[run: GGModelTypes.Sequence] RETURNS [traj: GGModelTypes.Traj]
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 = {
[run: GGModelTypes.Sequence] RETURNS [traj: GGModelTypes.Traj]
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];
};
Edit Curve Menu
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];
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.
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;
};
View Menu
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;
};
Debug Menu
TestMultiGravity:
PUBLIC
PROC [event:
LIST
OF
REF
ANY, clientData:
REF
ANY] = {
};
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]]];
};
TestGravity:
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, 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.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;
[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.