GGEventImplD.mesa
Copyright © 1986 by Xerox Corporation. All rights reserved.
Last edited by Bier on September 8, 1986 11:02:49 pm PDT
Contents: Once an event reaches the front of the slack-process queue, it is dispatched to one of the procedures in this module.
Pier, February 2, 1987 11:09:26 am PST
DIRECTORY
--CombinePoly,-- --GGTouch,-- --PrincOpsUtils,-- Ascii, Atom, BasicTime, ColorToolViewer, ColorToolViewerExtras, CubicSplines, FileNames, FS, GGAlign, GGBasicTypes, GGBoundBox, GGCaret, GGError, GGEvent, GGFileIn, GGFileOut, GGGravity, GGInterfaceTypes, GGModelTypes, GGMultiGravity, GGObjects, GGSegmentTypes, GGSelect, GGSequence, GGStatistics, GGTouch, GGUtility, GGVector, GGWindow, Icons, Imager, ImagerColor, ImagerColorPrivate, IO, IPMaster, List, NamedColors, PrincOpsUtils, Random, Rope, Rosary, SlackProcess, ViewerClasses, ViewerOps, ViewerTools;
GGEventImplD: CEDAR PROGRAM
IMPORTS --CombinePoly,-- --GGTouch,-- --PrincOpsUtils,-- Ascii, Atom, BasicTime, ColorToolViewer, ColorToolViewerExtras, FileNames, FS, GGAlign, GGCaret, GGError, GGEvent, GGFileIn, GGFileOut, GGGravity, GGMultiGravity, GGObjects, GGSelect, GGSequence, GGStatistics, GGTouch, GGUtility, GGVector, GGWindow, Icons, Imager, ImagerColor, ImagerColorPrivate, IO, List, NamedColors, PrincOpsUtils, Random, Rope, SlackProcess, ViewerOps, ViewerTools
EXPORTS GGEvent = BEGIN
BoundBox: TYPE = GGBoundBox.BoundBox;
EntityGenerator: TYPE = GGModelTypes.EntityGenerator;
FeatureData: TYPE = GGInterfaceTypes.FeatureData;
GargoyleData: TYPE = GGInterfaceTypes.GargoyleData;
Joint: TYPE = GGModelTypes.Joint;
JointGenerator: TYPE = GGModelTypes.JointGenerator;
ObjectBag: TYPE = GGGravity.ObjectBag;
Outline: TYPE = GGModelTypes.Outline;
OutlineDescriptor: TYPE = GGModelTypes.OutlineDescriptor;
Point: TYPE = GGBasicTypes.Point;
Scene: TYPE = GGModelTypes.Scene;
SegAndIndex: TYPE = GGSequence.SegAndIndex;
Segment: TYPE = GGSegmentTypes.Segment;
SegmentGenerator: TYPE = GGModelTypes.SegmentGenerator;
Sequence: TYPE = GGModelTypes.Sequence;
SequenceGenerator: TYPE = GGModelTypes.SequenceGenerator;
Slice: TYPE = GGModelTypes.Slice;
SliceDescriptor: TYPE = GGModelTypes.SliceDescriptor;
SliceDescriptorGenerator: TYPE = GGModelTypes.SliceDescriptorGenerator;
SliceGenerator: TYPE = GGModelTypes.SliceGenerator;
SliceParts: TYPE = GGModelTypes.SliceParts;
Traj: TYPE = GGModelTypes.Traj;
TrajGenerator: TYPE = GGModelTypes.TrajGenerator;
TriggerBag: TYPE = GGGravity.TriggerBag;
Vector: TYPE = GGBasicTypes.Vector;
Viewer: TYPE = ViewerClasses.Viewer;
PaintActionArea: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
ViewerOps.PaintViewer[
viewer: gargoyleData.actionArea,
hint: client,
whatChanged: gargoyleData,
clearClient: FALSE];
};
File Operations
NotNewVersion: PROC [gargoyleData: GargoyleData] = {
need to repaint the caption because viewer is no longer edited
gargoyleData.outer.newVersion ← FALSE;
gargoyleData.outer.icon ← cleanIcon;
ViewerOps.PaintViewer[gargoyleData.outer, caption];
};
GetGargoyleFileName: PROC [event: LIST OF REF ANY, currentWDir: Rope.ROPE, feedback: Viewer, emergency: BOOLFALSE] RETURNS [fullName: Rope.ROPENIL, success: BOOLTRUE, versionSpecified: BOOLFALSE] = {
ASSERT: event is either NIL or event.first=filename
RopeFromRef: PROC [ref: REF ANY] RETURNS [rope: Rope.ROPE] ~ {
WITH ref SELECT FROM
text: REF TEXT => RETURN[Rope.FromRefText[text] ];
r: Rope.ROPE => RETURN[r];
ENDCASE => ERROR;
};
fileName: Rope.ROPE;
fileName ← IF event#NIL AND event.first#NIL THEN RopeFromRef[event.first] ELSE ViewerTools.GetSelectionContents[];
[fullName, success, versionSpecified] ← GGUtility.GetGargoyleFileName[fileName, currentWDir, feedback, emergency];
};
Get: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
ASSERT: event.first=$Get, event.rest=NIL OR filename
fullName: Rope.ROPE;
success, versionSpecified: BOOLFALSE;
[fullName, success, versionSpecified] ← GetGargoyleFileName[event.rest, gargoyleData.currentWDir, gargoyleData.feedback];
IF NOT success THEN {
GGError.Append[gargoyleData.feedback, "Could not find requested file", oneLiner];
GOTO Abort;
};
ClearAux[event, gargoyleData];
GetMergeAux[fullName, versionSpecified, $Get, "Getting", gargoyleData];
NotNewVersion[gargoyleData];
EXITS
Abort => GGError.Blink[NARROW[clientData, GargoyleData].feedback];
};
Merge: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
ASSERT: event.first=$Merge, event.rest=NIL OR filename
fullName: Rope.ROPE;
success, versionSpecified: BOOLFALSE;
[fullName, success, versionSpecified] ← GetGargoyleFileName[event.rest, gargoyleData.currentWDir, gargoyleData.feedback];
IF NOT success THEN {
GGError.Append[gargoyleData.feedback, "Could not find requested file", oneLiner];
GOTO Abort;
};
GetMergeAux[fullName, versionSpecified, $Merge, "Merging", gargoyleData];
EXITS
Abort => GGError.Blink[NARROW[clientData, GargoyleData].feedback];
};
GetMergeAux: PROC [fullName: Rope.ROPE, versionSpecified: BOOLFALSE, event: ATOM, opName: Rope.ROPE, gargoyleData: GargoyleData] = {
ASSERT: event=$Merge or $Get
f: IO.STREAM;
fsName: Rope.ROPE;
TIMING VARIABLES
startTime: BasicTime.GMT;
endTime: BasicTime.GMT;
totalTime: INT;
msgRope: Rope.ROPE;
f ← FS.StreamOpen[fullName, $read ! FS.Error, IO.Error => {
msgRope ← IO.PutFR["Could not find: %g", [rope[fullName]]];
GGError.Append[gargoyleData.feedback, msgRope, oneLiner];
GOTO Abort;
};];
msgRope ← IO.PutFR["%g %g . . . ", [rope[opName]], [rope[fullName]]];
GGError.Append[gargoyleData.feedback, msgRope, begin];
START TIMING
startTime ← BasicTime.Now[];
GGFileIn.FileinSceneAndOptions[f, gargoyleData];
f.Close[];
endTime ← BasicTime.Now[];
totalTime ← BasicTime.Period[startTime, endTime];
msgRope ← IO.PutFR[" Done in time (%r)", [integer[totalTime]]];
GGError.Append[gargoyleData.feedback, msgRope, end];
here if successfully read a new GG file
IF event=$Get OR gargoyleData.outer.file=NIL THEN { -- update viewer name on Get or on first Merge if no Get preceeded it.
fsName ← FS.FileInfo[name: fullName, wDir: gargoyleData.currentWDir].fullFName;
gargoyleData.outer.file ← FileNames.StripVersionNumber[fsName];
gargoyleData.outer.label ← FileNames.GetShortName[path: fsName, stripOffVersionNumber: TRUE];
gargoyleData.outer.name ← IF versionSpecified THEN Rope.Concat["Gargoyle: ", fsName] ELSE Rope.Cat["Gargoyle: ", gargoyleData.outer.file, " (!", FileNames.Tail[fsName, '!], ")"];
};
GGEvent.SawTextFinish[LIST[$SawTextFinish], gargoyleData];
GGWindow.RestoreScreenAndInvariants[paintAction: $PaintEntireScene, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: FALSE, edited: event=$Merge, okToClearFeedback: FALSE];
EXITS
Abort => GGError.Blink[gargoyleData.feedback];
}; -- end GetMergeAux
Store: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
ASSERT: event.first=$Store or $Save, event.rest=NIL OR filename
Save is simply Store invoked with event.first=gargoyleData.outer.file. KAP February 17, 1986
tKeep: CARDINAL ← 0;
f: IO.STREAM;
fullName, fsName: Rope.ROPE;
success, versionSpecified: BOOLFALSE;
TIMING VARIABLES
startTime: BasicTime.GMT;
endTime: BasicTime.GMT;
totalTime: INT;
msgRope, opName: Rope.ROPE;
ofile: FS.OpenFile;
emergency: BOOL ← event.first=$Emergency;
[fullName, success, versionSpecified] ← GetGargoyleFileName[event.rest, gargoyleData.currentWDir, gargoyleData.feedback, emergency];
IF NOT success THEN RETURN;
tKeep ← FS.FileInfo[name: fullName ! FS.Error => CONTINUE].keep;
ofile ← FS.Create[name: fullName, setPages: FALSE, setKeep: TRUE, keep: MAX[tKeep, 2] ! FS.Error => {
msgRope ← IO.PutFR["Could not create: %g", [rope[fullName]]];
IF NOT emergency THEN GGError.Append[gargoyleData.feedback, msgRope, oneLiner];
GOTO Abort;
};];
f ← FS.StreamFromOpenFile[openFile: ofile, accessRights: $write];
opName ← IF event.first = NIL THEN "someSave" ELSE Atom.GetPName[NARROW[event.first]];
msgRope ← IO.PutFR["%g: %g . . . ", [rope[opName]], [rope[fullName]]];
IF NOT emergency THEN GGError.Append[gargoyleData.feedback, msgRope, begin];
START TIMING
startTime ← BasicTime.Now[];
GGFileOut.FileoutSceneAndOptions[f, gargoyleData, fullName];
f.Close[];
endTime ← BasicTime.Now[];
totalTime ← BasicTime.Period[startTime, endTime];
msgRope ← IO.PutFR[" Done in time (%r)", [integer[totalTime]]];
IF NOT emergency THEN GGError.Append[gargoyleData.feedback, msgRope, end];
fsName ← FS.FileInfo[name: fullName, wDir: gargoyleData.currentWDir].fullFName; --latest version
gargoyleData.outer.file ← FileNames.StripVersionNumber[fsName];
gargoyleData.outer.label ← FileNames.GetShortName[path: fsName, stripOffVersionNumber: TRUE];
gargoyleData.outer.name ← IF versionSpecified THEN Rope.Concat["Gargoyle: ", fsName] ELSE Rope.Cat["Gargoyle: ", gargoyleData.outer.file, " (!", FileNames.Tail[fsName, '!], ")"];
NotNewVersion[gargoyleData];
GGEvent.SawTextFinish[NIL, gargoyleData];
EXITS
Abort => {
gargoyleData: GargoyleData ← NARROW[clientData];
IF NOT event.first=$Emergency THEN GGError.Blink[gargoyleData.feedback];
};
};
Save: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
IF gargoyleData.outer.file#NIL THEN Store[event: LIST[$Save, gargoyleData.outer.file], clientData: clientData]
ELSE {
GGError.Append[gargoyleData.feedback, "Can't save an unnamed viewer: try Store", oneLiner];
GGError.Blink[gargoyleData.feedback];
};
};
Split: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];};
Reset: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
fileName: Rope.ROPE ← gargoyleData.outer.file;
ClearAux[event, gargoyleData];
can only reset to the latest version. No version numbers supported for resetting
GetMergeAux[fileName, FALSE, $Get, "Restoring", gargoyleData];
need to repaint the caption because viewer is no longer edited
NotNewVersion[gargoyleData];
};
Clear: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
ClearAux[event, gargoyleData];
gargoyleData.outer.file ← NIL;
gargoyleData.outer.label ← "Gargoyle";
gargoyleData.outer.name ← "Gargoyle";
GGEvent.SawTextFinish[NIL, gargoyleData];
GGWindow.RestoreScreenAndInvariants[paintAction: $PaintEntireScene, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: FALSE, edited: FALSE, okToClearFeedback: TRUE];
NotNewVersion[gargoyleData];
}; -- end Clear
ClearAux: PROC [event: LIST OF REF ANY, gargoyleData: GargoyleData] = {
gargoyleData.refresh.overlayList ← NIL;
GGSelect.DeselectAllAllClasses[gargoyleData.scene];
gargoyleData.scene ← GGObjects.CreateScene[];
gargoyleData.caret ← NEW[GGInterfaceTypes.CaretObj];
gargoyleData.anchor ← NEW[GGInterfaceTypes.CaretObj];
GGEvent.StandardAlignments[LIST[$StandardAlignments], gargoyleData];
gargoyleData.refresh.suppressRefresh ← FALSE; -- moved to abort processing code
gargoyleData.aborted ← ALL[FALSE];
};
Group Operations
AddToGroup: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
someAddition: BOOLFALSE;
seqGen: SequenceGenerator;
gargoyleData: GargoyleData ← NARROW[clientData];
name: Rope.ROPEIF event.rest = NIL THEN ViewerTools.GetSelectionContents[] ELSE NARROW[event.rest.first];
IF name=NIL OR Rope.Equal[name, ""] THEN {
GGError.AppendHerald[gargoyleData.feedback, "Select a group name", oneLiner];
RETURN;
};
seqGen ← GGSelect.SelectedSequences[gargoyleData.scene, normal];
FOR seq: Sequence ← GGSequence.NextSequence[seqGen], GGSequence.NextSequence[seqGen] UNTIL seq=NIL DO
segGen: SegmentGenerator ← GGSequence.SegmentsInSequence[seq];
FOR seg: Segment ← GGSequence.NextSegment[segGen], GGSequence.NextSegment[segGen] UNTIL seg=NIL DO
duplicate: BOOLFALSE;
FOR prop: LIST OF REF ANY ← seg.props, prop.rest UNTIL prop=NIL DO
nextProp: Rope.ROPENARROW[prop.first];
IF Rope.Equal[name, nextProp, FALSE] THEN {
duplicate ← TRUE; -- already in this group
EXIT;
};
ENDLOOP;
IF NOT duplicate THEN seg.props ← List.Append[seg.props, LIST[name]];
someAddition ← TRUE;
ENDLOOP;
ENDLOOP;
GGError.PutF[gargoyleData.feedback, oneLiner, IF someAddition THEN "Added selected segments to group %g" ELSE "No segment selections to add to group %g", [rope[name]] ];
};
SelectGroup: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
name: Rope.ROPEIF event.rest = NIL THEN ViewerTools.GetSelectionContents[] ELSE NARROW[event.rest.first];
trajGen: TrajGenerator ← GGObjects.TrajsInScene[gargoyleData.scene];
segGen: SegmentGenerator;
seq: Sequence;
segsFound: BOOL;
GGSelect.DeselectAll[gargoyleData.scene, normal]; -- get rid of old selection
FOR traj: Traj ← GGObjects.NextTraj[trajGen], GGObjects.NextTraj[trajGen] UNTIL traj = NIL DO
segGen ← GGSequence.SegmentsInTraj[traj];
segsFound ← FALSE;
FOR next: SegAndIndex ← GGSequence.NextSegmentAndIndex[segGen], GGSequence.NextSegmentAndIndex[segGen] UNTIL next.seg=NIL DO
FOR prop: LIST OF REF ANY ← next.seg.props, prop.rest UNTIL prop=NIL DO
nextProp: Rope.ROPENARROW[prop.first];
IF Rope.Equal[name, nextProp, FALSE] THEN {
IF NOT segsFound THEN seq ← GGSequence.CreateEmpty[traj];
segsFound ← TRUE;
IF NOT seq.segments[next.index] THEN {
seq.segments[next.index] ← TRUE;
seq.segCount ← seq.segCount + 1;
};
};
ENDLOOP; -- next prop
ENDLOOP; -- next segment
IF segsFound THEN GGSelect.SelectSequence[seq, gargoyleData.scene, normal];
ENDLOOP; -- next trajectory
GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE];
IF GGSelect.NoSelections[gargoyleData.scene, normal] THEN GGError.AppendHerald[gargoyleData.feedback, "No such group", oneLiner ]
ELSE GGError.PutF[gargoyleData.feedback, oneLiner, "Group %g selected", [rope[name]] ];
};
RemoveFromGroup: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
someRemoval: BOOLFALSE;
newProps: LIST OF REF ANY;
seqGen: SequenceGenerator;
gargoyleData: GargoyleData ← NARROW[clientData];
name: Rope.ROPEIF event.rest = NIL THEN ViewerTools.GetSelectionContents[] ELSE NARROW[event.rest.first];
IF name=NIL OR Rope.Equal[name, ""] THEN {
GGError.AppendHerald[gargoyleData.feedback, "Select a group name", oneLiner];
RETURN;
};
seqGen ← GGSelect.SelectedSequences[gargoyleData.scene, normal];
FOR seq: Sequence ← GGSequence.NextSequence[seqGen], GGSequence.NextSequence[seqGen] UNTIL seq=NIL DO
segGen: SegmentGenerator ← GGSequence.SegmentsInSequence[seq];
FOR seg: Segment ← GGSequence.NextSegment[segGen], GGSequence.NextSegment[segGen] UNTIL seg=NIL DO
newProps ← NIL;
FOR prop: LIST OF REF ANY ← seg.props, prop.rest UNTIL prop=NIL DO
nextProp: Rope.ROPENARROW[prop.first];
IF NOT Rope.Equal[name, nextProp, FALSE] THEN newProps ← List.Append[newProps, LIST[nextProp]] ELSE someRemoval ← TRUE;
ENDLOOP;
seg.props ← newProps;
ENDLOOP;
ENDLOOP;
GGError.PutF[gargoyleData.feedback, oneLiner, IF someRemoval THEN "Removed selected segments from group %g" ELSE "No member segments selected to remove from group %g", [rope[name]] ];
};
PrintGroupsOfSelected: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
groupList: LIST OF REF ANY;
seqGen: SequenceGenerator;
gargoyleData: GargoyleData ← NARROW[clientData];
IF GGSelect.NoSelections[gargoyleData.scene, normal] THEN {
GGError.AppendHerald[gargoyleData.feedback, "No selections => no groups", oneLiner];
RETURN;
};
seqGen ← GGSelect.SelectedSequences[gargoyleData.scene, normal];
FOR seq: Sequence ← GGSequence.NextSequence[seqGen], GGSequence.NextSequence[seqGen] UNTIL seq=NIL DO
segGen: SegmentGenerator ← GGSequence.SegmentsInSequence[seq];
FOR seg: Segment ← GGSequence.NextSegment[segGen], GGSequence.NextSegment[segGen] UNTIL seg=NIL DO
FOR prop: LIST OF REF ANY ← seg.props, prop.rest UNTIL prop=NIL DO
nextProp: Rope.ROPENARROW[prop.first];
duplicate: BOOLFALSE;
FOR group: LIST OF REF ANY ← groupList, group.rest UNTIL group=NIL DO
nextGroup: Rope.ROPENARROW[group.first];
IF Rope.Equal[nextGroup, nextProp, FALSE] THEN {
duplicate ← TRUE; -- already on the group list
EXIT;
};
ENDLOOP;
IF NOT duplicate THEN groupList ← List.Append[groupList, LIST[nextProp]];
ENDLOOP;
ENDLOOP;
ENDLOOP;
IF groupList#NIL THEN {
GGError.Append[gargoyleData.feedback, "Groups of Selected: ", begin];
FOR group: LIST OF REF ANY ← groupList, group.rest UNTIL group=NIL DO
nextGroup: Rope.ROPENARROW[group.first];
GGError.PutF[gargoyleData.feedback, middle, "%g ", [rope[nextGroup]] ];
ENDLOOP;
GGError.Append[gargoyleData.feedback, "", end];
}
ELSE GGError.Append[gargoyleData.feedback, "No Groups of Selected", oneLiner];
};
PrintAllGroups: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
groupList: LIST OF REF ANY;
gargoyleData: GargoyleData ← NARROW[clientData];
trajGen: TrajGenerator ← GGObjects.TrajsInScene[gargoyleData.scene];
FOR traj: Traj ← GGObjects.NextTraj[trajGen], GGObjects.NextTraj[trajGen] UNTIL traj = NIL DO
segGen: SegmentGenerator ← GGSequence.SegmentsInTraj[traj];
FOR seg: Segment ← GGSequence.NextSegment[segGen], GGSequence.NextSegment[segGen] UNTIL seg=NIL DO
FOR prop: LIST OF REF ANY ← seg.props, prop.rest UNTIL prop=NIL DO
nextProp: Rope.ROPENARROW[prop.first];
duplicate: BOOLFALSE;
FOR group: LIST OF REF ANY ← groupList, group.rest UNTIL group=NIL DO
nextGroup: Rope.ROPENARROW[group.first];
IF Rope.Equal[nextGroup, nextProp, FALSE] THEN {
duplicate ← TRUE; -- already on the group list
EXIT;
};
ENDLOOP;
IF NOT duplicate THEN groupList ← List.Append[groupList, LIST[nextProp]];
ENDLOOP;
ENDLOOP;
ENDLOOP;
IF groupList#NIL THEN {
GGError.Append[gargoyleData.feedback, "Groups: ", begin];
FOR group: LIST OF REF ANY ← groupList, group.rest UNTIL group=NIL DO
nextGroup: Rope.ROPENARROW[group.first];
GGError.PutF[gargoyleData.feedback, middle, "%g ", [rope[nextGroup]] ];
ENDLOOP;
GGError.Append[gargoyleData.feedback, "", end];
}
ELSE GGError.Append[gargoyleData.feedback, "No Groups", oneLiner];
};
Area and Line Colors
RGBFromRopeError: SIGNAL = CODE;
AreaColorFromColorTool: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
IF ColorToolIsBound[gargoyleData] THEN {
color: Imager.Color;
red, green, blue: REAL;
[red: red, green: green, blue: blue] ← ColorToolViewer.GetRGBValue[];
color ← ImagerColor.ColorFromRGB[rgb: [red, green, blue]];
AreaColorAux[color, gargoyleData];
};
};
LineColorFromColorTool: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
IF ColorToolIsBound[gargoyleData] THEN {
color: Imager.Color;
red, green, blue: REAL;
[red: red, green: green, blue: blue] ← ColorToolViewer.GetRGBValue[];
color ← ImagerColor.ColorFromRGB[rgb: [red, green, blue]];
LineColorAux[color, gargoyleData];
};
};
AreaColorFollowColorTool: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
IF ColorToolIsBound[gargoyleData] THEN {
color: Imager.Color ← ColorToolViewerExtras.GetSpecialColor[]; -- "animation" color
AreaColorAux[color, gargoyleData];
};
};
LineColorFollowColorTool: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
IF ColorToolIsBound[gargoyleData] THEN {
color: Imager.Color ← ColorToolViewerExtras.GetSpecialColor[]; -- "animation" color
LineColorAux[color, gargoyleData];
};
};
AreaColorToColorTool: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
IF ColorToolIsBound[gargoyleData] THEN {
red, green, blue: REAL;
color: Imager.Color ← NIL;
entityGen: EntityGenerator ← GGSelect.SelectedStuff[gargoyleData.scene, normal];
WITH GGObjects.NextEntity[entityGen] SELECT FROM
outlineD: OutlineDescriptor => color ← outlineD.slice.class.getFillColor[outlineD.slice];
sliceD: SliceDescriptor => color ← sliceD.slice.class.getFillColor[sliceD.slice];
ENDCASE => ERROR;
IF color#NIL THEN {
cc: Imager.ConstantColor ← NARROW[color];
red ← ImagerColorPrivate.ComponentFromColor[cc, $Red];
green ← ImagerColorPrivate.ComponentFromColor[cc, $Green];
blue ← ImagerColorPrivate.ComponentFromColor[cc, $Blue];
ColorToolViewer.SetRGBValue[red, green, blue];
}
ELSE GGError.Append[gargoyleData.feedback, ". . . Object has NIL fill color", oneLiner];
};
};
LineColorToColorTool: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
IF ColorToolIsBound[gargoyleData] THEN {
red, green, blue: REAL;
color: Imager.Color ← NIL;
entityGen: EntityGenerator ← GGSelect.SelectedStuff[gargoyleData.scene, normal];
WITH GGObjects.NextEntity[entityGen] SELECT FROM
outlineD: OutlineDescriptor => color ← outlineD.slice.class.getStrokeColor[outlineD.slice, outlineD.parts];
sliceD: SliceDescriptor => color ← sliceD.slice.class.getStrokeColor[sliceD.slice, sliceD.parts];
ENDCASE => ERROR;
IF color#NIL THEN {
cc: Imager.ConstantColor ← NARROW[color];
red ← ImagerColorPrivate.ComponentFromColor[cc, $Red];
green ← ImagerColorPrivate.ComponentFromColor[cc, $Green];
blue ← ImagerColorPrivate.ComponentFromColor[cc, $Blue];
ColorToolViewer.SetRGBValue[red, green, blue];
}
ELSE GGError.Append[gargoyleData.feedback, ". . . Object has NIL stroke color", oneLiner];
};
};
AreaColorFromSelectedName: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
name: Rope.ROPEIF event.rest = NIL THEN ViewerTools.GetSelectionContents[] ELSE NARROW[event.rest.first];
color: Imager.Color ← ImagerColor.ColorFromRGB[ImagerColor.RGBFromHSL[NamedColors.RopeToHSL[name !
NamedColors.UndefinedName => GOTO UndefinedName;
NamedColors.BadGrammar => GOTO BadGrammar;
]]];
AreaColorAux[color, gargoyleData];
EXITS
UndefinedName => {GGError.AppendHerald[NARROW[clientData, GargoyleData].feedback, "Undefined Color Name", oneLiner]; GGError.Blink[NARROW[clientData, GargoyleData].feedback];};
BadGrammar => {GGError.AppendHerald[NARROW[clientData, GargoyleData].feedback, "Bad Color Name", oneLiner]; GGError.Blink[NARROW[clientData, GargoyleData].feedback];};
};
LineColorFromSelectedName: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
name: Rope.ROPEIF event.rest = NIL THEN ViewerTools.GetSelectionContents[] ELSE NARROW[event.rest.first];
color: Imager.Color ← ImagerColor.ColorFromRGB[ImagerColor.RGBFromHSL[NamedColors.RopeToHSL[name !
NamedColors.UndefinedName => GOTO UndefinedName;
NamedColors.BadGrammar => GOTO BadGrammar;
]]];
LineColorAux[color, gargoyleData];
EXITS
UndefinedName => {GGError.AppendHerald[NARROW[clientData, GargoyleData].feedback, "Undefined Color Name", oneLiner]; GGError.Blink[NARROW[clientData, GargoyleData].feedback];};
BadGrammar => {GGError.AppendHerald[NARROW[clientData, GargoyleData].feedback, "Bad Color Name", oneLiner]; GGError.Blink[NARROW[clientData, GargoyleData].feedback];};
};
AreaColorFromSelectedRGB: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
name: Rope.ROPEIF event.rest = NIL THEN ViewerTools.GetSelectionContents[] ELSE NARROW[event.rest.first];
color: Imager.Color ← ImagerColor.ColorFromRGB[RGBFromRope[name ! RGBFromRopeError => GOTO SyntaxError]];
AreaColorAux[color, gargoyleData];
EXITS
SyntaxError => {GGError.AppendHerald[NARROW[clientData, GargoyleData].feedback, "RGB Syntax is R: [0.0..1.0] G: [0.0..1.0] B: [0.0..1.0]", oneLiner]; GGError.Blink[NARROW[clientData, GargoyleData].feedback];};
};
LineColorFromSelectedRGB: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
name: Rope.ROPEIF event.rest = NIL THEN ViewerTools.GetSelectionContents[] ELSE NARROW[event.rest.first];
color: Imager.Color ← ImagerColor.ColorFromRGB[RGBFromRope[name ! RGBFromRopeError => GOTO SyntaxError]];
LineColorAux[color, gargoyleData];
EXITS
SyntaxError => {GGError.AppendHerald[NARROW[clientData, GargoyleData].feedback, "RGB Syntax is R: [0.0..1.0] G: [0.0..1.0] B: [0.0..1.0]", oneLiner]; GGError.Blink[NARROW[clientData, GargoyleData].feedback];};
};
SelectMatchingAreaColor: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
rgb: ImagerColor.RGB;
name: Rope.ROPEIF event.rest = NIL THEN ViewerTools.GetSelectionContents[] ELSE NARROW[event.rest.first];
entityGen: EntityGenerator ← GGObjects.TopLevelEntitiesInScene[gargoyleData.scene];
noneFlag: BOOL ← Rope.Equal[name, "none", FALSE];
IF NOT noneFlag THEN rgb ← SELECT event.first FROM
$SelectMatchingAreaCNS =>
ImagerColor.RGBFromHSL[NamedColors.RopeToHSL[name !
NamedColors.UndefinedName => GOTO UndefinedName;
NamedColors.BadGrammar => GOTO BadGrammar;
]],
$SelectMatchingAreaRGB =>
RGBFromRope[name ! RGBFromRopeError => GOTO SyntaxError]
ENDCASE => ERROR;
GGSelect.DeselectAll[gargoyleData.scene, normal];
FOR entity: REF ANY ← GGObjects.NextEntity[entityGen], GGObjects.NextEntity[entityGen] UNTIL entity = NIL DO
WITH entity SELECT FROM
outline: Outline => {
fillColor: Imager.Color ← outline.class.getFillColor[outline];
IF RGBEqualsColor[rgb, fillColor, noneFlag] THEN GGSelect.SelectEntireOutline[outline, gargoyleData.scene, normal];
};
slice: Slice => {
fillColor: Imager.Color ← slice.class.getFillColor[slice];
IF RGBEqualsColor[rgb, fillColor, noneFlag] THEN GGSelect.SelectSlice[slice, slice.class.newParts[slice, NIL, topLevel], gargoyleData.scene, normal];
};
ENDCASE => ERROR;
ENDLOOP;
GGError.PutFHerald[gargoyleData.feedback, oneLiner, "Areas with fill color %g selected", [rope[name]] ];
GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE];
EXITS
SyntaxError => {GGError.AppendHerald[NARROW[clientData, GargoyleData].feedback, "RGB Syntax is R: [0.0..1.0] G: [0.0..1.0] B: [0.0..1.0]", oneLiner]; GGError.Blink[NARROW[clientData, GargoyleData].feedback];};
UndefinedName => {GGError.AppendHerald[NARROW[clientData, GargoyleData].feedback, "Undefined Color Name", oneLiner]; GGError.Blink[NARROW[clientData, GargoyleData].feedback];};
BadGrammar => {GGError.AppendHerald[NARROW[clientData, GargoyleData].feedback, "Bad Color Name", oneLiner]; GGError.Blink[NARROW[clientData, GargoyleData].feedback];};
};
SelectMatchingLineColor: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
rgb: ImagerColor.RGB;
name: Rope.ROPEIF event.rest = NIL THEN ViewerTools.GetSelectionContents[] ELSE NARROW[event.rest.first];
trajGen: TrajGenerator ← GGObjects.TrajsInScene[gargoyleData.scene];
sliceGen: SliceGenerator ← GGObjects.SlicesInScene[gargoyleData.scene];
noneFlag: BOOL ← Rope.Equal[name, "none", FALSE];
IF NOT noneFlag THEN rgb ← SELECT event.first FROM
$SelectMatchingLineCNS =>
ImagerColor.RGBFromHSL[NamedColors.RopeToHSL[name !
NamedColors.UndefinedName => GOTO UndefinedName;
NamedColors.BadGrammar => GOTO BadGrammar;
]],
$SelectMatchingLineRGB =>
RGBFromRope[name ! RGBFromRopeError => GOTO SyntaxError]
ENDCASE => ERROR;
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 RGBEqualsColor[rgb, segAndIndex.seg.color, noneFlag] THEN {
seq: Sequence ← GGSequence.CreateFromSegment[traj, segAndIndex.index];
GGSelect.SelectSequence[seq, gargoyleData.scene, normal];
};
ENDLOOP;
ENDLOOP;
FOR slice: Slice ← GGObjects.NextSlice[sliceGen], GGObjects.NextSlice[sliceGen] UNTIL slice = NIL DO
SLICES CAN'T ENUMERATE THEIR PARTS!!
ENDLOOP;
GGError.PutFHerald[gargoyleData.feedback, oneLiner, "Segments with color %g selected", [rope[name]] ];
GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE];
EXITS
SyntaxError => {GGError.AppendHerald[NARROW[clientData, GargoyleData].feedback, "RGB Syntax is R: [0.0..1.0] G: [0.0..1.0] B: [0.0..1.0]", oneLiner]; GGError.Blink[NARROW[clientData, GargoyleData].feedback];};
UndefinedName => {GGError.AppendHerald[NARROW[clientData, GargoyleData].feedback, "Undefined Color Name", oneLiner]; GGError.Blink[NARROW[clientData, GargoyleData].feedback];};
BadGrammar => {GGError.AppendHerald[NARROW[clientData, GargoyleData].feedback, "Bad Color Name", oneLiner]; GGError.Blink[NARROW[clientData, GargoyleData].feedback];};
};
RGBEqualsColor: PROC [rgb: ImagerColor.RGB, color: Imager.Color, noneFlag: BOOL] RETURNS [BOOL] = {
epsilon: REAL = 1.0E-3;
IF color=NIL THEN RETURN [noneFlag]; -- fillColor is none
IF noneFlag THEN RETURN [color=NIL] ELSE {
cc: Imager.ConstantColor ← NARROW[color];
r: REAL ← ImagerColorPrivate.ComponentFromColor[cc, $Red];
g: REAL ← ImagerColorPrivate.ComponentFromColor[cc, $Green];
b: REAL ← ImagerColorPrivate.ComponentFromColor[cc, $Blue];
RETURN[(r=rgb.R AND g=rgb.G AND b=rgb.B) OR (ABS[r-rgb.R]<epsilon AND ABS[g-rgb.G]<epsilon AND ABS[b-rgb.B]<epsilon ) ];
};
};
AreaColorBlack: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
AreaColorAux[Imager.black, gargoyleData];
};
LineColorBlack: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
LineColorAux[Imager.black, gargoyleData];
};
PrintAreaColor: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
entity: REF ANY;
isProcessBlack: Rope.ROPE;
entityGen: EntityGenerator ← GGSelect.SelectedStuff[gargoyleData.scene, normal];
IF entityGen=NIL OR (entity ← GGObjects.NextEntity[entityGen])=NIL OR GGObjects.NextEntity[entityGen]#NIL THEN GGError.AppendHerald[gargoyleData.feedback, "Select exactly one entity for PrintFillColor", oneLiner]
ELSE {
red, green, blue: REAL;
color: Imager.ConstantColor;
WITH entity SELECT FROM
outlineD: OutlineDescriptor => color ← NARROW[outlineD.slice.class.getFillColor[outlineD.slice]];
sliceD: SliceDescriptor => color ← NARROW[sliceD.slice.class.getFillColor[sliceD.slice]];
ENDCASE => ERROR;
IF color#NIL THEN {
red ← ImagerColorPrivate.ComponentFromColor[color, $Red];
green ← ImagerColorPrivate.ComponentFromColor[color, $Green];
blue ← ImagerColorPrivate.ComponentFromColor[color, $Blue];
IF color = Imager.black THEN isProcessBlack ← " (process black)"
ELSE IF ImagerColor.GrayFromColor[color]=1.0 THEN isProcessBlack ← " (CMY black)"
ELSE isProcessBlack ← "";
GGError.Append[
gargoyleData.feedback,
IO.PutFR["R: %1.2f G: %1.2f B: %1.2f CNS: %g%g", [real[red]], [real[green]], [real[blue]], [rope[NamedColors.HSLToRope[ImagerColor.HSLFromRGB[[red,green,blue]]]]], [rope[isProcessBlack]] ],
oneLiner];
}
ELSE GGError.Append[gargoyleData.feedback, "No Fill Color", oneLiner];
};
};
PrintLineColor: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
entity: REF ANY;
isProcessBlack: Rope.ROPE;
entityGen: EntityGenerator ← GGSelect.SelectedStuff[gargoyleData.scene, normal];
IF entityGen=NIL OR (entity ← GGObjects.NextEntity[entityGen])=NIL OR GGObjects.NextEntity[entityGen]#NIL THEN GGError.AppendHerald[gargoyleData.feedback, "Select exactly one entity for PrintLineColor", oneLiner]
ELSE {
red, green, blue: REAL;
color: Imager.ConstantColor;
WITH entity SELECT FROM
outlineD: OutlineDescriptor => color ← NARROW[outlineD.slice.class.getStrokeColor[outlineD.slice, outlineD.parts]];
sliceD: SliceDescriptor => color ← NARROW[sliceD.slice.class.getStrokeColor[sliceD.slice, sliceD.parts]];
ENDCASE => ERROR;
IF color#NIL THEN {
red ← ImagerColorPrivate.ComponentFromColor[color, $Red];
green ← ImagerColorPrivate.ComponentFromColor[color, $Green];
blue ← ImagerColorPrivate.ComponentFromColor[color, $Blue];
IF color = Imager.black THEN isProcessBlack ← " (process black)"
ELSE IF ImagerColor.GrayFromColor[color]=1.0 THEN isProcessBlack ← " (CMY black)"
ELSE isProcessBlack ← "";
GGError.Append[
gargoyleData.feedback,
IO.PutFR["R: %1.2f G: %1.2f B: %1.2f CNS: %g%g", [real[red]], [real[green]], [real[blue]], [rope[NamedColors.HSLToRope[ImagerColor.HSLFromRGB[[red,green,blue]]]]], [rope[isProcessBlack]] ],
oneLiner];
}
ELSE GGError.Append[gargoyleData.feedback, "No Stroke Color", oneLiner];
};
};
AreaColorWhite: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
AreaColorAux[Imager.white, gargoyleData];
};
LineColorWhite: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
LineColorAux[Imager.white, gargoyleData];
};
AreaColorGray: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
AreaColorAux[GGObjects.fillColor, gargoyleData];
};
LineColorGray: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
LineColorAux[GGObjects.fillColor, gargoyleData];
};
AreaColorNone: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
AreaColorAux[NIL, gargoyleData];
};
LineColorNone: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
LineColorAux[NIL, gargoyleData];
};
AreaColorAux: PROC [color: Imager.Color, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
entityGen: EntityGenerator ← GGSelect.SelectedStuff[gargoyleData.scene, normal];
FOR entity: REF ANY ← GGObjects.NextEntity[entityGen], GGObjects.NextEntity[entityGen] UNTIL entity = NIL DO
WITH entity SELECT FROM
outlineD: OutlineDescriptor => outlineD.slice.class.setFillColor[outlineD.slice, color];
sliceD: SliceDescriptor => sliceD.slice.class.setFillColor[sliceD.slice, color];
ENDCASE => ERROR;
ENDLOOP;
GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedInPlace, gargoyleData: gargoyleData, remake: none, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE];
};
LineColorAux: PROC [color: Imager.Color, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
entityGen: EntityGenerator ← GGSelect.SelectedStuff[gargoyleData.scene, normal];
FOR entity: REF ANY ← GGObjects.NextEntity[entityGen], GGObjects.NextEntity[entityGen] UNTIL entity = NIL DO
WITH entity SELECT FROM
outlineD: OutlineDescriptor => outlineD.slice.class.setStrokeColor[outlineD.slice, outlineD.parts, color];
sliceD: SliceDescriptor => sliceD.slice.class.setStrokeColor[sliceD.slice, sliceD.parts, color];
ENDCASE => ERROR;
ENDLOOP;
GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedInPlace, gargoyleData: gargoyleData, remake: none, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE];
};
ColorToolIsBound: PROC [gargoyleData: GargoyleData] RETURNS [BOOL] = TRUSTED {
IF PrincOpsUtils.IsBound[LOOPHOLE[ColorToolViewer.GetRGBValue]] THEN RETURN[TRUE];
GGError.AppendHerald[gargoyleData.feedback, "Please start ColorTool and retry this operation", oneLiner];
RETURN[FALSE];
};
RGBFromRope: PROC [name: Rope.ROPE] RETURNS [rgb: ImagerColor.RGB] = {
ENABLE IO.Error, IO.EndOfStream => GOTO RGBError;
Check: PROC [x: REAL] = {
IF x NOT IN [0.0..1.0] THEN SIGNAL RGBFromRopeError;
};
rs: IO.STREAMIO.RIS[name];
IF Ascii.Upper[rs.GetChar[]]#'R THEN GOTO RGBError;
IF rs.GetChar[]#': THEN GOTO RGBError;
rgb.R ← rs.GetReal[]; [] ← rs.SkipWhitespace[];
IF Ascii.Upper[rs.GetChar[]]#'G THEN GOTO RGBError;
IF rs.GetChar[]#': THEN GOTO RGBError;
rgb.G ← rs.GetReal[]; [] ← rs.SkipWhitespace[];
IF Ascii.Upper[rs.GetChar[]]#'B THEN GOTO RGBError;
IF rs.GetChar[]#': THEN GOTO RGBError;
rgb.B ← rs.GetReal[];
Check[rgb.R]; Check[rgb.G]; Check[rgb.B];
EXITS
RGBError => SIGNAL RGBFromRopeError;
};
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[]];
};
cleanIcon: Icons.IconFlavor ← unInit; -- filled in by Init
Init: PROC = { -- copied from GGWindowImpl
cleanIcon ← Icons.NewIconFromFile["Gargoyle.icons", 1]; -- done here so file will come from working directory into which Gargoyle was brought over, e.g. ///Commands/
};
Init[];
END.