GGOutlineImplB.mesa
Copyright © 1986 by Xerox Corporation. All rights reserved.
Last edited by Bier on January 22, 1987
Contents: Procedures to implement the Outline Slice Class in Gargoyle. Outlines consist of Trajectories which in turn consist of Segments. Outline is the most important slice class.
DIRECTORY
GGBasicTypes, GGError, GGInterfaceTypes, GGModelTypes, GGOutline, GGObjects, GGSelect, GGSegmentTypes, GGSequence, GGTraj, GGUtility, GGVector, GList, Imager, Rope;
GGOutlineImplB:
CEDAR
PROGRAM
IMPORTS GGError, GGObjects, GGOutline, GGSelect, GGSequence, GGTraj, GGUtility, GGVector, GList, Imager
EXPORTS GGOutline =
BEGIN
OPEN GGOutline;
BitVector: TYPE = GGBasicTypes.BitVector;
BoundBox: TYPE = GGBasicTypes.BoundBox;
CameraData: TYPE = GGModelTypes.CameraData;
Circle: TYPE = GGBasicTypes.Circle;
Color: TYPE = Imager.Color;
ControlPointGenerator: TYPE = GGModelTypes.ControlPointGenerator;
FeatureData: TYPE = GGInterfaceTypes.FeatureData;
GargoyleData: TYPE = GGInterfaceTypes.GargoyleData;
Joint: TYPE = GGSegmentTypes.Joint;
JointGenerator: TYPE = GGModelTypes.JointGenerator;
Line: TYPE = GGBasicTypes.Line;
ObjectBag: TYPE = GGInterfaceTypes.ObjectBag;
Outline: TYPE = REF OutlineObj;
OutlineObj: TYPE = GGModelTypes.OutlineObj;
OutlineClass: TYPE = REF OutlineClassObj;
OutlineClassObj: TYPE = GGModelTypes.OutlineClassObj;
OutlineDescriptor: TYPE = GGModelTypes.OutlineDescriptor;
OutlineParts: TYPE = REF OutlinePartsObj;
OutlinePartsObj: TYPE = GGOutline.OutlinePartsObj;
OutlineSequence: TYPE = GGSelect.OutlineSequence;
OutlineSequenceGenerator: TYPE = GGSelect.OutlineSequenceGenerator;
Point: TYPE = GGBasicTypes.Point;
PointGenerator: TYPE = GGModelTypes.PointGenerator;
PointPairGenerator: TYPE = GGModelTypes.PointPairGenerator;
Scene: TYPE = GGModelTypes.Scene;
Segment: TYPE = GGSegmentTypes.Segment;
SegmentGenerator: TYPE = GGModelTypes.SegmentGenerator;
SelectMode: TYPE = GGModelTypes.SelectMode;
SelectionClass: TYPE = GGSegmentTypes.SelectionClass;
Sequence: TYPE = GGModelTypes.Sequence;
SliceClass: TYPE = REF SliceClassObj;
SliceClassObj: TYPE = GGModelTypes.SliceClassObj;
SliceDescriptor: TYPE = GGModelTypes.SliceDescriptor;
SliceParts: TYPE = GGModelTypes.SliceParts;
Traj: TYPE = REF TrajObj;
TrajEnd: TYPE = GGModelTypes.TrajEnd;
TrajGenerator: TYPE = REF TrajGeneratorObj;
TrajGeneratorObj: TYPE = GGModelTypes.TrajGeneratorObj;
TrajObj: TYPE = GGModelTypes.TrajObj;
TrajPartType: TYPE = GGModelTypes.TrajPartType;
TriggerBag: TYPE = GGInterfaceTypes.TriggerBag;
Vector: TYPE = GGBasicTypes.Vector;
OutlineDescribeProc: TYPE = GGModelTypes.OutlineDescribeProc;
OutlineFileoutProc: TYPE = GGModelTypes.OutlineFileoutProc;
OutlineFileinProc: TYPE = GGModelTypes.OutlineFileinProc;
OutlineUnionPartsProc: TYPE = GGModelTypes.OutlineUnionPartsProc;
OutlineDifferencePartsProc: TYPE = GGModelTypes.OutlineDifferencePartsProc;
OutlineFixedPartsProc: TYPE = GGModelTypes.OutlineFixedPartsProc;
OutlineAugmentPartsProc: TYPE = GGModelTypes.OutlineAugmentPartsProc;
OutlinePointsInDescriptorProc: TYPE = GGModelTypes.OutlinePointsInDescriptorProc;
OutlinePointPairsInDescriptorProc: TYPE = GGModelTypes.OutlinePointPairsInDescriptorProc;
OutlineNextPointProc: TYPE = GGModelTypes.OutlineNextPointProc;
OutlineNextPointPairProc: TYPE = GGModelTypes.OutlineNextPointPairProc;
OutlineClosestPointProc: TYPE = GGModelTypes.OutlineClosestPointProc;
OutlineClosestPointAndTangentProc: TYPE = GGModelTypes.OutlineClosestPointAndTangentProc;
OutlineClosestSegmentProc: TYPE = GGModelTypes.OutlineClosestSegmentProc;
OutlineSetArrowsProc: TYPE = GGModelTypes.OutlineSetArrowsProc;
OutlineGetArrowsProc: TYPE = GGModelTypes.OutlineGetArrowsProc;
Problem: PUBLIC SIGNAL [msg: Rope.ROPE] = GGError.Problem;
Outline-Only operations.
UnpackOnePointDescriptorOld:
PUBLIC
PROC [outlineD: OutlineDescriptor]
RETURNS [traj: Traj, isACP:
BOOL, segNum, cpNum, jointNum:
NAT] = {
outlineD describes either a single control point or a single joint. Return the [segNum, cpNum] pair or the jointNum as appropriate.
parts: OutlineParts;
theSeq: Sequence;
IF outlineD = NIL THEN RETURN[NIL, FALSE, 999, 999, 999];
parts ← NARROW[outlineD.parts];
traj ← NIL;
FOR list:
LIST
OF Sequence ← parts.seqs, list.rest
UNTIL list =
NIL
DO
IF list.first #
NIL
THEN {
IF traj # NIL THEN RETURN[NIL, FALSE, 999, 999, 999]
ELSE {
theSeq ← list.first;
traj ← theSeq.traj;
[isACP, segNum, cpNum, jointNum] ← GGSequence.UnpackOnePointSequence[theSeq];
};
};
ENDLOOP;
};
UnpackSimpleDescriptorOld:
PUBLIC
PROC [outlineD: OutlineDescriptor]
RETURNS [success:
BOOL, partType: TrajPartType, traj: Traj, joint: Joint ←
NIL, jointNum:
NAT ← 999, cp: Point, cpNum:
NAT ← 999, seg: Segment ←
NIL, segNum:
NAT ← 999] = {
parts: OutlineParts;
theSeq: Sequence;
IF outlineD = NIL THEN RETURN[FALSE, joint, NIL, NIL, 999, [0,0]];
parts ← NARROW[outlineD.parts];
traj ← NIL;
FOR list:
LIST
OF Sequence ← parts.seqs, list.rest
UNTIL list =
NIL
DO
IF list.first #
NIL
THEN {
IF traj # NIL THEN RETURN[FALSE, joint, NIL, NIL, 999, [0,0]]
ELSE {
theSeq ← list.first;
traj ← theSeq.traj;
[success, partType, traj, joint, jointNum, cp, cpNum, seg, segNum] ← GGSequence.UnpackSimpleSequence[theSeq];
};
};
ENDLOOP;
};
UnpackSimpleDescriptor:
PUBLIC
PROC [sliceD: SliceDescriptor]
RETURNS [success:
BOOL, partType: TrajPartType, traj: Traj, joint: Joint ←
NIL, jointNum:
NAT ← 999, cp: Point, cpNum:
NAT ← 999, seg: Segment ←
NIL, segNum:
NAT ← 999] = {
ERROR Problem[msg: "Outlines are not slices yet"];
};
UnpackOnePointDescriptor:
PUBLIC
PROC [sliceD: SliceDescriptor]
RETURNS [traj: Traj, isACP:
BOOL, segNum, cpNum, jointNum:
NAT] = {
ERROR Problem[msg: "Outlines are not slices yet"];
};
UnpackOneSegmentDescriptorOld:
PUBLIC
PROC [outlineD: OutlineDescriptor]
RETURNS [traj: Traj, segNum:
NAT] = {
outlineD describes either a single control point or a single joint. Return the [segNum, cpNum] pair or the jointNum as appropriate.
parts: OutlineParts;
theSeq: Sequence;
IF outlineD = NIL THEN RETURN[NIL, 999];
parts ← NARROW[outlineD.parts];
traj ← NIL;
FOR list:
LIST
OF Sequence ← parts.seqs, list.rest
UNTIL list =
NIL
DO
IF list.first #
NIL
THEN {
IF traj # NIL THEN RETURN[NIL, 999]
ELSE {
theSeq ← list.first;
traj ← theSeq.traj;
[segNum] ← GGSequence.UnpackOneSegmentSequence[theSeq];
};
};
ENDLOOP;
};
UnpackOneSegmentDescriptor:
PUBLIC
PROC [sliceD: SliceDescriptor]
RETURNS [traj: Traj, segNum:
NAT] = {
ERROR Problem[msg: "Outlines are not slices yet"];
};
FindTrajInDescriptor:
PUBLIC
PROC [outlineD: OutlineDescriptor, traj: Traj]
RETURNS [seq: Sequence] = {
outlineParts: OutlineParts;
outlineParts ← NARROW[outlineD.parts];
FOR list:
LIST
OF Sequence ← outlineParts.seqs, list.rest
UNTIL list =
NIL
DO
IF list.first # NIL AND list.first.traj = traj THEN RETURN[list.first];
ENDLOOP;
RETURN[NIL];
};
PartsFromSequence:
PUBLIC PROC [outline: Outline, seq: Sequence]
RETURNS [sliceParts: SliceParts] = {
realParts: OutlineParts;
ptr: LIST OF Sequence;
trajGen: GGModelTypes.TrajGenerator;
sliceParts ← realParts ← NEW[OutlinePartsObj];
[realParts.seqs, ptr] ← GGUtility.StartSequenceList[];
trajGen ← TrajsInOutline[outline];
FOR traj: Traj ← GGObjects.NextTraj[trajGen], GGObjects.NextTraj[trajGen]
UNTIL traj =
NIL
DO
IF traj = seq.traj THEN [realParts.seqs, ptr] ← GGUtility.AddSequence[seq, realParts.seqs, ptr]
ELSE [realParts.seqs, ptr] ← GGUtility.AddSequence[NIL, realParts.seqs, ptr];
ENDLOOP;
};
CopyParts:
PUBLIC
PROC [outline: Outline, parts: SliceParts]
RETURNS [copy: SliceParts] = {
realParts, realCopyParts: OutlineParts;
copySeq: Sequence;
ptr: LIST OF Sequence;
realParts ← NARROW[parts];
copy ← realCopyParts ← NEW[OutlinePartsObj];
[realCopyParts.seqs, ptr] ← GGUtility.StartSequenceList[];
FOR list:
LIST
OF Sequence ← realParts.seqs, list.rest
UNTIL list =
NIL
DO
IF list.first = NIL THEN [realCopyParts.seqs, ptr] ← GGUtility.AddSequence[NIL, realCopyParts.seqs, ptr]
ELSE {
copySeq ← GGSequence.Copy[list.first];
[realCopyParts.seqs, ptr] ← GGUtility.AddSequence[copySeq, realCopyParts.seqs, ptr]
};
ENDLOOP;
};
AddHole:
PUBLIC
PROC [outline: Outline, hole: Traj]
RETURNS [holier: Outline] = {
Holes can be removed using DeleteSequence.
Should check for duplicates. -- Bier, February 20
holier ← outline.class.copy[outline];
holier.children ← AppendTrajList[holier.children, LIST[hole]];
hole.parent ← holier;
hole.role ← hole;
UpdateOutlineBoundBox[holier];
};
ReplaceFence:
PUBLIC PROC [outline: Outline, newFence: Traj]
RETURNS [newOutline: Outline] = {
Like CopyOutline except in one place.
holeGen: TrajGenerator;
newTraj: Traj;
newOutline ← CreateOutline[newFence, outline.lineEnds, outline.fillColor];
holeGen ← HolesOfOutline[outline];
FOR hole: Traj ← GGObjects.NextTraj[holeGen], GGObjects.NextTraj[holeGen]
UNTIL hole =
NIL
DO
newTraj ← GGTraj.CopyTraj[hole];
newOutline.children ← AppendTrajList[newOutline.children, LIST[newTraj]];
newTraj.parent ← newOutline;
ENDLOOP;
UpdateOutlineBoundBox[newOutline];
};
ReplaceHole:
PUBLIC PROC [outline: Outline, oldHole, newHole: Traj]
RETURNS [newOutline: Outline] = {
Like CopyOutline except in one place.
holeGen: TrajGenerator;
newTraj, fence: Traj;
fence ← FenceOfOutline[outline];
newTraj ← GGTraj.CopyTraj[fence];
newOutline ← CreateOutline[newTraj, outline.lineEnds, outline.fillColor];
holeGen ← HolesOfOutline[outline];
FOR hole: Traj ← GGObjects.NextTraj[holeGen], GGObjects.NextTraj[holeGen]
UNTIL hole =
NIL
DO
IF hole = oldHole
THEN {
newOutline.children ← AppendTrajList[newOutline.children, LIST[newHole]];
newHole.parent ← newOutline;
}
ELSE {
newTraj ← GGTraj.CopyTraj[hole];
newOutline.children ← AppendTrajList[newOutline.children, LIST[newTraj]];
newTraj.parent ← newOutline;
};
ENDLOOP;
UpdateOutlineBoundBox[newOutline];
};
OutlineOfTraj:
PUBLIC
PROC [traj: Traj]
RETURNS [outline: Outline] = {
Finds the unique outline to which traj belongs.
outline ← traj.parent;
};
SaveSelectionsInOutline:
PUBLIC
PROC [outline: Outline, scene: Scene] = {
outlineD: OutlineDescriptor;
outlineParts: OutlineParts;
hotness.
outlineD ← GGSelect.FindSelectedOutline[outline, scene, hot];
ClearSelectionInOutline[outline, hot];
IF outlineD #
NIL
THEN {
outlineParts ← NARROW[outlineD.parts];
FOR seqList: LIST OF Sequence ← outlineParts.seqs, seqList.rest
UNTIL seqList =
NIL
DO
IF seqList.first#NIL THEN SaveSelectionInSequence[seqList.first, hot];
ENDLOOP;
};
normal selection.
outlineD ← GGSelect.FindSelectedOutline[outline, scene, normal];
ClearSelectionInOutline[outline, normal];
IF outlineD #
NIL
THEN {
outlineParts ← NARROW[outlineD.parts];
FOR seqList: LIST OF Sequence ← outlineParts.seqs, seqList.rest
UNTIL seqList =
NIL
DO
IF seqList.first#NIL THEN SaveSelectionInSequence[seqList.first, normal];
ENDLOOP;
};
active selection.
outlineD ← GGSelect.FindSelectedOutline[outline, scene, active];
ClearSelectionInOutline[outline, active];
IF outlineD #
NIL
THEN {
outlineParts ← NARROW[outlineD.parts];
FOR seqList:
LIST
OF Sequence ← outlineParts.seqs, seqList.rest
UNTIL seqList =
NIL
DO
IF seqList.first#NIL THEN SaveSelectionInSequence[seqList.first, active];
ENDLOOP;
};
};
SaveSelectionInTraj:
PUBLIC
PROC [traj: Traj, selectClass: SelectionClass, scene: Scene] = {
seq: Sequence ← GGSelect.FindSelectedSequence[traj, scene, selectClass];
IF seq=NIL THEN ClearSelectionInTraj[traj, selectClass]
ELSE SaveSelectionInSequence[seq, selectClass];
};
ClearSelectionInTraj:
PROC [traj: Traj, selectClass: SelectionClass] = {
seg: Segment;
joint: Joint;
FOR i:
NAT
IN [0..GGTraj.HiSegment[traj]]
DO
seg ← GGTraj.FetchSegment[traj, i];
SetSegmentField[seg, FALSE, selectClass];
FOR j:
NAT
IN [0..seg.class.controlPointCount[seg]-1]
DO
SetControlPointField[seg, j, FALSE, selectClass];
ENDLOOP;
ENDLOOP;
FOR i:
NAT
IN [0..GGTraj.HiJoint[traj]]
DO
joint ← GGTraj.FetchJoint[traj, i];
SetJointField[joint, FALSE, selectClass];
ENDLOOP;
RemakeSelectionFromTraj:
PUBLIC
PROC [traj: Traj, scene: Scene, selectClass: SelectionClass] = {
seq: Sequence ← GGSequence.CreateEmpty[traj];
seg: Segment;
joint: Joint;
FOR i:
NAT
IN [0..GGTraj.HiSegment[traj]]
DO
seg ← GGTraj.FetchSegment[traj, i];
seq.segments[i] ← GetSegmentField[seg, selectClass];
IF seq.segments[i] THEN seq.segCount ← seq.segCount + 1;
FOR j:
NAT
IN [0..seg.class.controlPointCount[seg]-1]
DO
seq.controlPoints[i][j] ← GetControlPointField[seg, j, selectClass];
IF seq.controlPoints[i][j] THEN seq.controlPointCount ← seq.controlPointCount + 1;
ENDLOOP;
ENDLOOP;
FOR i:
NAT
IN [0..GGTraj.HiJoint[traj]]
DO
joint ← GGTraj.FetchJoint[traj, i];
seq.joints[i] ← GetJointField[joint, selectClass];
IF seq.joints[i] THEN seq.jointCount ← seq.jointCount + 1;
ENDLOOP;
GGSelect.SelectSequence[seq, scene, selectClass];
};
SaveSelectionInSequence:
PROC [seq: Sequence, selectClass: SelectionClass] = {
seg: Segment;
joint: Joint;
FOR i:
NAT
IN [0..GGTraj.HiSegment[seq.traj]]
DO
seg ← GGTraj.FetchSegment[seq.traj, i];
SetSegmentField[seg, seq.segments[i], selectClass];
FOR j:
NAT
IN [0..seg.class.controlPointCount[seg]-1]
DO
SetControlPointField[seg, j, seq.controlPoints[i][j], selectClass];
ENDLOOP;
ENDLOOP;
FOR i:
NAT
IN [0..GGTraj.HiJoint[seq.traj]]
DO
joint ← GGTraj.FetchJoint[seq.traj, i];
SetJointField[joint, seq.joints[i], selectClass];
ENDLOOP;
};
ClearSelectionInOutline:
PROC [outline: Outline, selectClass: SelectionClass] = {
trajGen: GGModelTypes.TrajGenerator;
trajGen ← TrajsInOutline[outline];
FOR traj: Traj ← GGObjects.NextTraj[trajGen], GGObjects.NextTraj[trajGen]
UNTIL traj =
NIL
DO
ClearSelectionInTraj[traj, selectClass];
ENDLOOP;
};
RemakeSelectionsFromOutline:
PUBLIC
PROC [outline: Outline, scene: Scene] = {
This outline has just been created by copying parts from an existing outline. The selection information was copied as well. Make sequences from the selection bits, and select these sequences.
trajGen: GGModelTypes.TrajGenerator;
trajGen ← TrajsInOutline[outline];
FOR traj: Traj ← GGObjects.NextTraj[trajGen], GGObjects.NextTraj[trajGen]
UNTIL traj =
NIL
DO
hotness.
RemakeSelectionFromTraj[traj, scene, hot];
normal selection.
RemakeSelectionFromTraj[traj, scene, normal];
active selection.
RemakeSelectionFromTraj[traj, scene, active];
ENDLOOP;
};
SequencesOfOutline:
PUBLIC
PROC [outlineD: OutlineDescriptor]
RETURNS [seqList:
LIST
OF Sequence] = {
outlineParts: OutlineParts ← NARROW[outlineD.parts];
ptr: LIST OF Sequence;
[seqList, ptr] ← GGUtility.StartSequenceList[];
FOR list:
LIST
OF Sequence ← outlineParts.seqs, list.rest
UNTIL list =
NIL
DO
IF list.first # NIL THEN [seqList, ptr] ← GGUtility.AddSequence[list.first, seqList, ptr];
ENDLOOP;
};
RemoveTraj:
PUBLIC
PROC [outlineD: OutlineDescriptor, traj: Traj] = {
Used by GGSelect to do a DeselectTraj.
outlineParts: OutlineParts ← NARROW[outlineD.parts];
seq: Sequence;
FOR list:
LIST
OF Sequence ← outlineParts.seqs, list.rest
UNTIL list =
NIL
DO
IF list.first#
NIL
AND list.first.traj = traj
THEN {
seq ← list.first;
GOTO Done;
};
REPEAT
Done => {
outlineParts.seqs ←
NARROW[GList.DSubst[
NIL, seq, outlineParts.seqs]];
Replace the old sequence with a NIL. No trajectory has actually been deleted. It is just no longer mentioned in outlineD.
};
FINISHED => NULL;
ENDLOOP;
};
NearestJointToHitData:
PUBLIC PROC [hitData:
REF
ANY]
RETURNS [jointNum:
NAT, traj: Traj] = {
outlineHitData: OutlineHitData ← NARROW[hitData];
nextNum: NAT;
p1, p2: Point;
d1, d2: REAL;
traj ← outlineHitData.traj;
SELECT outlineHitData.hitType
FROM
joint => {
jointNum ← outlineHitData.jointNum;
};
segment, controlPoint => {
nextNum ← GGTraj.FollowingJoint[traj, outlineHitData.segNum];
p1 ← GGTraj.FetchJointPos[traj, outlineHitData.segNum];
p2 ← GGTraj.FetchJointPos[traj, nextNum];
d1 ← GGVector.DistanceSquared[p1, outlineHitData.hitPoint];
d2 ← GGVector.DistanceSquared[p2, outlineHitData.hitPoint];
IF d1 <= d2 THEN jointNum ← outlineHitData.segNum
ELSE jointNum ← nextNum;
};
ENDCASE => ERROR;
};
UnpackHitData:
PUBLIC
PROC [hitData:
REF
ANY]
RETURNS [traj: Traj, hitType: TrajPartType, segNum, cpNum, jointNum:
INT, hitPoint: Point] = {
This will go away when the Caret machinery is revised.
outlineHitData: OutlineHitData ← NARROW[hitData];
traj ← outlineHitData.traj;
hitType ← outlineHitData.hitType;
segNum ← outlineHitData.segNum;
cpNum ← outlineHitData.cpNum;
jointNum ← outlineHitData.jointNum;
hitPoint ← outlineHitData.hitPoint;
};
UpdateDescriptorBoundBoxes:
PUBLIC
PROC [outlineD: OutlineDescriptor] = {
Update the bound boxes of all contained sequences.
parts: OutlineParts ← NARROW[outlineD.parts];
FOR list:
LIST
OF Sequence ← parts.seqs, list.rest
UNTIL list =
NIL
DO
IF list.first # NIL THEN GGSequence.UpdateBoundBox[list.first];
ENDLOOP;
};
Queries about Outlines.
HasHoles:
PUBLIC
PROC [outline: Outline]
RETURNS [
BOOL] = {
RETURN[outline.children.rest # NIL];
};
FenceOfOutline:
PUBLIC
PROC [outline: Outline]
RETURNS [fence: Traj] = {
Returns the unique fence trajectory of outline.
fence ← outline.children.first;
};
HolesOfOutline:
PUBLIC
PROC [outline: Outline]
RETURNS [trajGen: TrajGenerator] = {
Generates all of the holes of outline.
list: LIST OF Traj ← outline.children.rest;
trajGen ←
NEW[TrajGeneratorObj ← [
list: list
]];
};
TrajsInOutline:
PUBLIC
PROC [outline: Outline]
RETURNS [trajGen: TrajGenerator] = {
Generates the fence and all of the holes of outline.
list: LIST OF Traj ← outline.children;
trajGen ←
NEW[TrajGeneratorObj ← [
list: list
]];
};
END.