GGEventImplA.mesa
Last edited by Bier on October 1, 1985 8:58:32 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.
Stone, August 9, 1985 2:38:57 pm PDT
DIRECTORY
BasicTime, CubicSplines, FS, GGAlign, GGCaret, GGDescribe, GGDrawProcess, GGError, GGEvent, GGGravity, GGInterfaceTypes, GGModelTypes, GGObjects, GGRefresh, GGSegment, GGSelect, GGTouch, GGVector, GGWindow, Imager, ImagerInterpress, IO, Random, Real, Rope, Rosary, ViewerClasses, ViewerIO, ViewerOps, ViewerTools;
GGEventImplA: CEDAR PROGRAM
IMPORTS BasicTime, FS, GGAlign, GGCaret, GGDescribe, GGDrawProcess, GGError, GGGravity, GGObjects, GGRefresh, GGSegment, GGSelect, GGTouch, GGVector, GGWindow, Imager, ImagerInterpress, IO, Random, Real, Rope, Rosary, ViewerIO, ViewerOps, ViewerTools
EXPORTS GGEvent =
BEGIN
Cluster: TYPE = GGModelTypes.Cluster;
EntityGenerator: TYPE = GGModelTypes.EntityGenerator;
GargoyleData: TYPE = GGInterfaceTypes.GargoyleData;
Joint: TYPE = GGModelTypes.Joint;
JointGenerator: TYPE = GGModelTypes.JointGenerator;
Outline: TYPE = GGModelTypes.Outline;
Point: TYPE = GGModelTypes.Point;
ScalarButtonClient: TYPE = GGInterfaceTypes.ScalarButtonClient;
Segment: TYPE = GGModelTypes.Segment;
Sequence: TYPE = GGModelTypes.Sequence;
Traj: TYPE = GGModelTypes.Traj;
TrajGenerator: TYPE = GGModelTypes.TrajGenerator;
TwoState: TYPE = GGInterfaceTypes.TwoState;
SegmentGenerator: TYPE = GGModelTypes.SegmentGenerator;
SequenceGenerator: TYPE = GGModelTypes.SequenceGenerator;
Vector: TYPE = GGModelTypes.Vector;
Viewer: TYPE = ViewerClasses.Viewer;
File Operations
Clear: PUBLIC PROC [event: LIST OF REF ANY, gargoyleData: GargoyleData] = {};
Reset: PUBLIC PROC [event: LIST OF REF ANY, gargoyleData: GargoyleData] = {};
Get: PUBLIC PROC [event: LIST OF REF ANY, gargoyleData: GargoyleData] = {};
Store: PUBLIC PROC [event: LIST OF REF ANY, gargoyleData: GargoyleData] = {};
Split: PUBLIC PROC [event: LIST OF REF ANY, gargoyleData: GargoyleData] = {};
Interpress: PUBLIC PROC [event: LIST OF REF ANY, gargoyleData: GargoyleData] = {
ipRef: ImagerInterpress.Ref;
wdir, ipName, fullName: Rope.ROPE;
success: BOOL;
pixelsPerMeter: REAL ← 0.0254 / 72.0;
TIMING VARIABLES
startTime: BasicTime.GMT;
endTime: BasicTime.GMT;
totalTime: INT;
msgRope: Rope.ROPE;
The Painting Call
DoMakeInterpress: PROC [dc: Imager.Context] = {
Imager.ScaleT[dc, pixelsPerMeter];
GGRefresh.InterpressEntireScene[dc, gargoyleData];
};
ipName ← ViewerTools.GetSelectionContents[];
IF Rope.Length[ipName] = 0 THEN RETURN;
wdir ← gargoyleData.originalWorkingDirectory;
success ← TRUE;
[fullName,,] ← FS.ExpandName[ipName, wdir
! FS.Error => IF error.group = user THEN {
success ← FALSE;
CONTINUE;
}
];
IF NOT success THEN RETURN;
ipRef ← ImagerInterpress.Create[fullName];
msgRope ← IO.PutFR["Writing to interpress file: %g . . . ", [rope[fullName]]];
GGError.Append[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[msgRope, end];
};
Master Menu Line
Hierarchy Menu
Delete: PUBLIC PROC [event: LIST OF REF ANY, gargoyleData: GargoyleData] = {
parentOutline: Outline;
entityGen: EntityGenerator;
entityGen ← GGSelect.SelectedEntities[gargoyleData, normal];
FOR entity: REF ANY ← GGObjects.NextEntity[entityGen], GGObjects.NextEntity[entityGen] UNTIL entity = NIL DO
GGSelect.DeselectEntity[entity, gargoyleData, normal];
GGSelect.DeselectEntity[entity, gargoyleData, copy];
GGSelect.DeselectEntity[entity, gargoyleData, hot];
GGSelect.DeselectEntity[entity, gargoyleData, active];
WITH entity SELECT FROM
cluster: Cluster => ERROR NotYetImplemented;
outline: Outline => {
GGObjects.DeleteOutline[gargoyleData.scene, outline];
GGTouch.OutlineDeleted[outline, gargoyleData];
};
seq: Sequence => {
parentOutline ← GGObjects.OutlineOfTraj[seq.traj];
IF GGObjects.HasHoles[parentOutline] THEN {
SELECT seq.traj.role FROM
hole => ERROR NotYetImplemented;
open => ERROR; -- impossible since the outline has holes
fence => {
GGError.Append["You can't delete the fence trajectory of an outline with holes.", oneLiner];
LOOP;
};
ENDCASE => ERROR;
}
ELSE {
IF seq.all THEN { -- the whole trajectory is selected
GGObjects.DeleteOutline[gargoyleData.scene, parentOutline];
GGTouch.OutlineDeleted[parentOutline, gargoyleData];
}
ELSE { -- part of a trajectory is selected. Remove the parts in a sensible fashion. This may require breaking up the trajectory into pieces. Remove all of the pieces from touching lists.
IF seq.parts.rest # NIL OR (seq.parts.first.start # 0 AND seq.parts.first.end # GGObjects.HiJoint[seq.traj]) THEN {
GGError.Append["Deleting parts of trajectories is not implemented.", oneLiner];
GGError.Blink[];
}
ELSE {
IF seq.traj.role = fence THEN seq.traj.role ← open;
GGTouch.SequenceDeleted[seq, gargoyleData];
IF seq.parts.first.start = 0 THEN {
seq.traj.joints ← Rosary.Substr[seq.traj.joints, seq.parts.first.end];
seq.traj.segments ← Rosary.Substr[seq.traj.segments, seq.parts.first.end];
seq.traj.segCount ← seq.traj.segCount - (seq.parts.first.end-seq.parts.first.start);
}
ELSE {
seq.traj.joints ← Rosary.Substr[seq.traj.joints, 0, seq.parts.first.start + 1];
seq.traj.segments ← Rosary.Substr[seq.traj.segments, 0, seq.parts.first.start];
seq.traj.segCount ← seq.traj.segCount - (seq.parts.first.end-seq.parts.first.start);
}
};
};
};
};
ENDCASE => ERROR;
ENDLOOP;
GGWindow.Painter[$PaintEntireScene, gargoyleData];
};
SelectAll: PUBLIC PROC [event: LIST OF REF ANY, gargoyleData: GargoyleData] = {
GGSelect.DeselectAll[gargoyleData, normal];
GGSelect.SelectAll[gargoyleData, normal];
GGWindow.Painter[$PaintEntireScene, gargoyleData];
};
NotYetImplemented: PUBLIC SIGNAL = CODE;
Curve Menu
SetStraight: PUBLIC PROC [event: LIST OF REF ANY, gargoyleData: GargoyleData] = {
seqGen: SequenceGenerator;
segmentGen: SegmentGenerator;
traj: Traj;
newSeg: Segment;
seqGen ← GGSelect.SelectedSequences[gargoyleData, normal];
FOR seq: Sequence ← GGObjects.NextSequence[seqGen], GGObjects.NextSequence[seqGen] UNTIL seq = NIL DO
segmentGen ← GGObjects.SegmentsInSequence[seq];
traj ← seq.traj;
FOR next: GGObjects.SegAndIndex ← GGObjects.NextSegmentAndIndex[segmentGen], GGObjects.NextSegmentAndIndex[segmentGen] UNTIL next.seg = NIL DO
newSeg ← GGSegment.MakeLine[next.seg.lo, next.seg.hi];
AddSegment[traj, newSeg, next];
ENDLOOP;
ENDLOOP;
GGWindow.Painter[$PaintEntireScene, gargoyleData];
};
SetArc: PUBLIC PROC [event: LIST OF REF ANY, gargoyleData: GargoyleData] = {
seqGen: SequenceGenerator;
segmentGen: SegmentGenerator;
traj: Traj;
newSeg: Segment;
p0, p1, p2: Point;
seqGen ← GGSelect.SelectedSequences[gargoyleData, normal];
FOR seq: Sequence ← GGObjects.NextSequence[seqGen], GGObjects.NextSequence[seqGen] UNTIL seq = NIL DO
segmentGen ← GGObjects.SegmentsInSequence[seq];
traj ← seq.traj;
FOR next: GGObjects.SegAndIndex ← GGObjects.NextSegmentAndIndex[segmentGen], GGObjects.NextSegmentAndIndex[segmentGen] UNTIL next.seg = NIL DO
Choose a random p1:
p0 ← next.seg.lo;
p2 ← next.seg.hi;
p1 ← GetPt[p0, GGVector.Sub[p2,p0], GGVector.Distance[p0,p1]];
newSeg ← GGSegment.MakeArc[p0, p1, p2];
AddSegment[traj, newSeg, next];
ENDLOOP;
ENDLOOP;
GGWindow.Painter[$PaintEntireScene, gargoyleData];
};
SetConic: PUBLIC PROC [event: LIST OF REF ANY, gargoyleData: GargoyleData] = {
seqGen: SequenceGenerator;
segmentGen: SegmentGenerator;
traj: Traj;
newSeg: Segment;
p0, p1, p2: Point;
seqGen ← GGSelect.SelectedSequences[gargoyleData, normal];
FOR seq: Sequence ← GGObjects.NextSequence[seqGen], GGObjects.NextSequence[seqGen] UNTIL seq = NIL DO
segmentGen ← GGObjects.SegmentsInSequence[seq];
traj ← seq.traj;
FOR next: GGObjects.SegAndIndex ← GGObjects.NextSegmentAndIndex[segmentGen], GGObjects.NextSegmentAndIndex[segmentGen] UNTIL next.seg = NIL DO
p0 ← next.seg.lo;
p2 ← next.seg.hi;
p1 ← GetPt[p0, GGVector.Sub[p2,p0], GGVector.Distance[p0,p1]];
newSeg ← GGSegment.MakeConic[p0, p1, p2, 0.7];
AddSegment[traj, newSeg, next];
ENDLOOP;
ENDLOOP;
GGWindow.Painter[$PaintEntireScene, gargoyleData];
};
random: Random.RandomStream ← Random.Create[10000,0];
NewV: PROC RETURNS[REAL] = {RETURN[Random.NextInt[random]/10000.0]};
GetPt: PROC[p0, dir: Point, maxL: REAL] RETURNS [pt: Point] = {
Choose a random p near the line p0, p2:
scale1: REAL ← NewV[]*maxL;
angle: REAL ← 180*NewV[]-90; --range -90..90
pt ← GGVector.Add[GGVector.Scale[GGVector.VectorPlusAngle[dir,angle], scale1], p0];
};
GetPtSequence: PROC[p0,p1: Point, n: NAT] RETURNS [CubicSplines.KnotSequence] ={
points: CubicSplines.KnotSequence ← NEW[CubicSplines.KnotSequenceRec[n+2]];
dir: Point ← GGVector.Sub[p1,p0];
inc: REAL ← GGVector.Distance[p0,p1]/(n-2);
maxL: REAL ← inc;
points[0] ← p0;
FOR i: NAT IN [1..points.length-1) DO
points[i] ← GetPt[p0, dir, maxL];
maxL ← maxL+inc;
ENDLOOP;
points[points.length-1] ← p1;
RETURN[points];
};
AddSegment: PROC[traj: Traj, newSeg: Segment, next: GGObjects.SegAndIndex] = {
traj.segments ← Rosary.Cat[ Rosary.Substr[traj.segments, 0, next.index], Rosary.FromItem[newSeg], Rosary.Substr[traj.segments, next.index+1, GGObjects.HiSegment[traj]-next.index] ];
};
SetBezier: PUBLIC PROC [event: LIST OF REF ANY, gargoyleData: GargoyleData] = {
seqGen: SequenceGenerator;
segmentGen: SegmentGenerator;
traj: Traj;
newSeg: Segment;
p0, p1, p2, p3: Point;
seqGen ← GGSelect.SelectedSequences[gargoyleData, normal];
FOR seq: Sequence ← GGObjects.NextSequence[seqGen], GGObjects.NextSequence[seqGen] UNTIL seq = NIL DO
segmentGen ← GGObjects.SegmentsInSequence[seq];
traj ← seq.traj;
FOR next: GGObjects.SegAndIndex ← GGObjects.NextSegmentAndIndex[segmentGen], GGObjects.NextSegmentAndIndex[segmentGen] UNTIL next.seg = NIL DO
Choose a random p1 and p2:
length: REAL ← GGVector.Distance[next.seg.lo, next.seg.hi];
dir: Point ← GGVector.Sub[p3,p0];
p0 ← next.seg.lo;
p1 ← GetPt[p0, dir,length*0.7];
p2 ← GetPt[p0, dir,length];
p3 ← next.seg.hi;
newSeg ← GGSegment.MakeBezier[p0, p1, p2, p3];
AddSegment[traj, newSeg, next];
ENDLOOP;
ENDLOOP;
GGWindow.Painter[$PaintEntireScene, gargoyleData];
};
SetSpline: PUBLIC PROC [event: LIST OF REF ANY, gargoyleData: GargoyleData] = {
seqGen: SequenceGenerator;
segmentGen: SegmentGenerator;
traj: Traj;
newSeg: Segment;
seqGen ← GGSelect.SelectedSequences[gargoyleData, normal];
FOR seq: Sequence ← GGObjects.NextSequence[seqGen], GGObjects.NextSequence[seqGen] UNTIL seq = NIL DO
segmentGen ← GGObjects.SegmentsInSequence[seq];
traj ← seq.traj;
FOR next: GGObjects.SegAndIndex ← GGObjects.NextSegmentAndIndex[segmentGen], GGObjects.NextSegmentAndIndex[segmentGen] UNTIL next.seg = NIL DO
Choose a random number of knots and positions:
length: REAL ← GGVector.Distance[next.seg.lo, next.seg.hi];
type: CubicSplines.SplineType ← naturalAL;
nKnots: INT ← Real.RoundLI[10*NewV[]];
cps: CubicSplines.KnotSequence ← GetPtSequence[next.seg.lo, next.seg.hi, nKnots];
newSeg ← GGSegment.MakeCubicSpline[cps, type];
AddSegment[traj, newSeg, next];
ENDLOOP;
ENDLOOP;
GGWindow.Painter[$PaintEntireScene, gargoyleData];
};
Edit Curve Menu
Close: PUBLIC PROC [event: LIST OF REF ANY, gargoyleData: GargoyleData] = {
traj: Traj;
firstPoint, lastPoint: Point;
seg: Segment;
[traj,----,----,----,----] ← GGCaret.GetChair[gargoyleData.caret];
IF traj = NIL THEN {
GGError.Append["There is no caret trajectory to close.", oneLiner];
GGError.Blink[];
RETURN;
};
IF traj.role = fence OR traj.role = hole THEN {
GGError.Append["That trajectory is already closed.", oneLiner];
GGError.Blink[];
RETURN;
};
GGError.Append["The caret trajectory will be closed.", oneLiner];
firstPoint ← GGObjects.FetchJointPos[traj, 0];
lastPoint ← GGObjects.LastJointPos[traj];
seg ← GGSegment.MakeLine[lastPoint, firstPoint];
GGObjects.CloseWithSegment[traj, seg, lo];
GGObjects.SetFillColor[GGObjects.OutlineOfTraj[traj], Imager.MakeGray[0.5]];
GGWindow.Painter[$PaintEntireScene, gargoyleData];
};
ShowPoints: PUBLIC PROC [event: LIST OF REF ANY, gargoyleData: GargoyleData] = {
seqGen: SequenceGenerator ← GGSelect.SelectedSequences[gargoyleData, normal];
FOR seq: Sequence ← GGObjects.NextSequence[seqGen], GGObjects.NextSequence[seqGen] UNTIL seq = NIL DO
seq.traj.visibleJoints ← TRUE;
ENDLOOP;
};
HidePoints: PUBLIC PROC [event: LIST OF REF ANY, gargoyleData: GargoyleData] = {
seqGen: SequenceGenerator ← GGSelect.SelectedSequences[gargoyleData, normal];
FOR seq: Sequence ← GGObjects.NextSequence[seqGen], GGObjects.NextSequence[seqGen] UNTIL seq = NIL DO
seq.traj.visibleJoints ← FALSE;
ENDLOOP;
};
View Menu
Refresh: PUBLIC PROC [event: LIST OF REF ANY, gargoyleData: GargoyleData] = {
GGWindow.Painter[$PaintEntireScene, gargoyleData];
};
DisableRefresh: PUBLIC PROC [event: LIST OF REF ANY, gargoyleData: GargoyleData] = {
gargoyleData.refresh.suppressRefresh ← TRUE;
};
EnableRefresh: PUBLIC PROC [event: LIST OF REF ANY, gargoyleData: GargoyleData] = {
gargoyleData.refresh.suppressRefresh ← FALSE;
};
Debug Menu
TestGravity: PUBLIC PROC [event: LIST OF REF ANY, 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: NAT ← 0;
s: IO.STREAM;
resultPoint: Point;
feature: REF ANY;
countRope: Rope.ROPE;
environ: GGGravity.ObjectBag;
xRandomStream ← Random.Create[gargoyleData.actionArea.cw];
yRandomStream ← Random.Create[gargoyleData.actionArea.ch];
environ ← GGGravity.CreateObjectBag[];
GGAlign.AddItemsForAction[gargoyleData, environ, $SelectPoint];
IF environ = NIL THEN {
GGError.Append["Add some trajectories to test gravity.", oneLiner];
GGError.Blink[];
RETURN;
};
gargoyleData.hitTest.environ ← environ;
GGWindow.Painter[$PaintAlign, gargoyleData];
gargoyleData.hitTest.hitCount ← 0;
UNTIL totalCount > 1000 DO
IF gargoyleData.aborted THEN {
gargoyleData.aborted ← FALSE;
EXIT;
};
x ← Random.NextInt[xRandomStream];
y ← Random.NextInt[yRandomStream];
testPoint ← [x, y];
testPoint ← GGWindow.ScreenToWorld[testPoint, gargoyleData.camera];
gargoyleData.refresh.spotPoint ← testPoint;
[resultPoint, feature] ← GGGravity.Map[testPoint, gargoyleData.hitTest.tolerance, environ, gargoyleData];
IF feature # NIL THEN {
gargoyleData.refresh.hitPoint ← resultPoint;
GGWindow.Painter[$PaintHitLine, gargoyleData];
gargoyleData.hitTest.hitCount ← gargoyleData.hitTest.hitCount + 1;
}
ELSE {
GGWindow.Painter[$PaintSpot, gargoyleData];
};
totalCount ← totalCount + 1;
ENDLOOP;
s ← IO.ROS[];
s.PutF["Tested %g total points. %g were hits", [integer[totalCount]], [integer[gargoyleData.hitTest.hitCount]]];
countRope ← IO.RopeFromROS[s];
GGError.Append[countRope, oneLiner];
};
DrawTouchPoints: PUBLIC PROC [event: LIST OF REF ANY, gargoyleData: GargoyleData] = {
GGWindow.Painter[$PaintTouchPoints, gargoyleData];
};
DescribeTouchPoints: PUBLIC PROC [event: LIST OF REF ANY, gargoyleData: GargoyleData] = {
GGTouch.DescribeAllTouchPoints[gargoyleData];
};
DrawBoundBoxes: PUBLIC PROC [event: LIST OF REF ANY, gargoyleData: GargoyleData] = {
GGWindow.Painter[$PaintBoundBoxes, gargoyleData];
};
DrawSelectionBox: PUBLIC PROC [event: LIST OF REF ANY, gargoyleData: GargoyleData] = {
GGWindow.Painter[$PaintSelectionBox, gargoyleData];
};
Typescript: PUBLIC PROC [event: LIST OF REF ANY, gargoyleData: GargoyleData] = {
newViewer: Viewer;
inHandle, outHandle: IO.STREAM;
[inHandle, outHandle, newViewer] ← MakeNewViewerAndHandle[];
GGError.SetErrorStream[outHandle];
gargoyleData.debug.typescript ← newViewer;
};
MakeNewViewerAndHandle: PRIVATE PROC [] RETURNS [in, out: IO.STREAM, newViewer: Viewer] = {
newViewer ← ViewerOps.CreateViewer[
flavor: $TypeScript,
info: [
name: "Gargoyle Script",
menu: NIL,
data: NIL,
iconic: TRUE,
column: right,
scrollable: TRUE,
icon: unInit
],
paint: FALSE];
ViewerOps.SetOpenHeight[newViewer, 120];
ViewerOps.OpenIcon[icon: newViewer, closeOthers: FALSE, bottom: TRUE, paint: TRUE];
[in, out] ← ViewerIO.CreateViewerStreams["gargoyle script", newViewer, NIL, TRUE];
};
SlackLog: PUBLIC PROC [event: LIST OF REF ANY, gargoyleData: GargoyleData] = {
GGDrawProcess.OutputLog[];
};
DescribeCaretObject: PUBLIC PROC [event: LIST OF REF ANY, gargoyleData: GargoyleData] = {
chair: Traj;
description, msgRope: Rope.ROPE;
isJoint: BOOL;
jointNum: NAT;
seg: Segment;
[chair, isJoint, ----, jointNum, seg] ← GGCaret.GetChair[gargoyleData.caret];
IF isJoint THEN {
description ← GGDescribe.DescribeJoint[chair, jointNum];
}
ELSE {
segNum: INT;
segNum ← GGObjects.IndexOfSegment[seg, chair];
IF segNum = -1 THEN description ← Rope.Concat["Nonexistent segment of ", GGDescribe.DescribeTraj[chair]]
ELSE description ← GGDescribe.DescribeSegment[chair, segNum];
};
msgRope ← IO.PutFR["Caret is on %g.", [rope[description]]];
GGError.Append[msgRope, oneLiner];
};
END.