GGSequenceImpl.mesa
Copyright Ó 1986, 1987, 1988, 1989 by Xerox Corporation. All rights reserved.
Last edited by Bier on July 16, 1987 2:25:24 pm PDT
Pier, November 17, 1989 4:01:59 pm PST
Kurlander July 28, 1986 2:41:32 pm PDT
Eisenman, October 5, 1987 9:53:39 am PDT
Bier, June 1, 1992 6:08 pm PDT
Doug Wyatt, December 18, 1989 3:22:34 pm PST
Contents: Sequences are a way to describe some part of a trajectory without actually pulling the trajectory apart. The new definition of sequence is that it is ANY subset of the parts of a trajectory (joints and/or segments). Procedures are provided which make sequences, perform boolean operations on them, and step through them.
DIRECTORY
Feedback, GGBasicTypes, GGCoreTypes, GGInterfaceTypes, GGModelTypes, GGParent, GGSegmentTypes, GGSequence, GGSlice, GGSliceOps, GGTraj, GGUtility, Imager, Rope;
GGSequenceImpl:
CEDAR
PROGRAM
IMPORTS Feedback, GGParent, GGSequence, GGSlice, GGSliceOps, GGTraj, GGUtility
EXPORTS GGSequence = BEGIN
BezierDragRecord: TYPE = GGInterfaceTypes.BezierDragRecord;
BitMatrix: TYPE = GGBasicTypes.BitMatrix;
BitMatrixObj: TYPE = GGBasicTypes.BitMatrixObj;
BitVector: TYPE = GGBasicTypes.BitVector;
BitVectorObj: TYPE = GGBasicTypes.BitVectorObj;
BoundBox: TYPE = GGCoreTypes.BoundBox;
Color: TYPE = Imager.Color;
ControlPointGenerator: TYPE = GGModelTypes.ControlPointGenerator;
ControlPointGeneratorObj: TYPE = GGModelTypes.ControlPointGeneratorObj;
EditConstraints: TYPE = GGModelTypes.EditConstraints;
Joint: TYPE = GGModelTypes.Joint;
JointGenerator: TYPE = GGModelTypes.JointGenerator;
JointGeneratorObj: TYPE = GGModelTypes.JointGeneratorObj;
JointWalkProc: TYPE = GGSequence.JointWalkProc;
Point: TYPE = GGBasicTypes.Point;
PointAndDone: TYPE = GGModelTypes.PointAndDone;
PointWalkProc: TYPE = GGModelTypes.PointWalkProc;
SegAndIndex: TYPE = GGSequence.SegAndIndex;
Segment: TYPE = GGSegmentTypes.Segment;
SegmentGenerator: TYPE = GGModelTypes.SegmentGenerator;
SegmentGeneratorObj: TYPE = GGModelTypes.SegmentGeneratorObj;
SegmentWalkProc: TYPE = GGSequence.SegmentWalkProc;
SelectionClass: TYPE = GGSegmentTypes.SelectionClass;
SequenceGenerator: TYPE = GGSequence.SequenceGenerator;
SequenceGeneratorObj: TYPE = GGSequence.SequenceGeneratorObj;
SequenceOfReal: TYPE = GGCoreTypes.SequenceOfReal;
Slice: TYPE = GGModelTypes.Slice;
SliceDescriptor: TYPE = GGModelTypes.SliceDescriptor;
SliceParts: TYPE = GGModelTypes.SliceParts;
StrokeEnd: TYPE = Imager.StrokeEnd;
Traj: TYPE = GGModelTypes.Traj;
TrajData: TYPE = GGModelTypes.TrajData;
TrajEnd: TYPE = GGModelTypes.TrajEnd;
TrajParts: TYPE = GGModelTypes.TrajParts;
TrajPartsObj: TYPE = GGModelTypes.TrajPartsObj;
TrajPartType: TYPE = GGModelTypes.TrajPartType;
Problem:
PUBLIC
SIGNAL [msg: Rope.
ROPE] = Feedback.Problem;
TrajParts Creation
CreateFromSegments:
PUBLIC
PROC [traj: TrajData, startSeg, endSeg:
NAT]
RETURNS [seq: TrajParts] = {
The sequence created will include all of the segments from startSeg to endSeg inclusive, and all of the joints which they touch. startSeg and endSeg must be legal TrajParts numbers for this trajectory. If the trajectory is closed, then startSeg > endSeg is allowed, in which case the segment sequence wraps around.
If startSeg = endSeg, then one segment is added.
If startSeg = endSeg + 1 (modulo the number of segments in traj), then the whole trajectory is selected.
segCount, temp: NAT;
IF startSeg >= traj.segCount OR endSeg >= traj.segCount THEN ERROR;
IF startSeg = endSeg THEN {seq ← CreateFromSegment[traj, startSeg]; RETURN};
IF traj.role = open
THEN {
IF startSeg > endSeg THEN {temp ← startSeg; startSeg ← endSeg; endSeg ← temp};
segCount ← endSeg - startSeg + 1;
}
ELSE {
IF startSeg = (endSeg + 1) MOD traj.segCount THEN {seq ← CreateComplete[traj]; RETURN};
segCount ← ((endSeg - startSeg + traj.segCount) MOD traj.segCount) + 1;
};
seq ←
NEW[TrajPartsObj ← [
segments: NewBitVector[traj.segCount],
joints: NewBitVector[GGTraj.HiJointTraj[traj] + 1],
controlPoints: NewBitMatrix[traj],
segCount: segCount,
controlPointCount: 0, -- will be updated by FillInControlPoints
jointCount: 0 -- will be updated by FillInJoints
]];
IF startSeg < endSeg
THEN {
FOR i:
NAT
IN [startSeg..endSeg]
DO
seq.segments[i] ← TRUE;
ENDLOOP;
FillInJoints[seq];
FillInControlPoints[seq];
}
ELSE {
FOR i:
NAT
IN [startSeg..traj.segCount)
DO
seq.segments[i] ← TRUE;
ENDLOOP;
FOR i:
NAT
IN [0..endSeg]
DO
seq.segments[i] ← TRUE;
ENDLOOP;
FillInJoints[seq];
FillInControlPoints[seq];
};
};
CreateJointToJoint:
PUBLIC
PROC [traj: TrajData, startJoint, endJoint:
NAT]
RETURNS [seq: TrajParts] = {
IF the traj is open, then select all segments in the interval [startJoint..endJoint]. If the traj is closed, there are three cases: 1) IF startJoint < endJoint then proceed as for an open traj (but there is no way to select the whole trajectory. 2) IF startJoint = endJoint then select a single joint. 3) IF endJoint < startJoint, wrap around, selecting all segments above startJoint, all segments below endJoint, all joints above startJoint including startJoint, all joints below endJoint, including endJoint.
temp, hiJoint: NAT;
hiJoint ← GGTraj.HiJointTraj[traj];
IF traj.role = open
AND startJoint = 0
AND endJoint = hiJoint
THEN {
seq ← CreateComplete[traj]; RETURN};
IF traj.role = open
OR startJoint < endJoint
THEN {
IF startJoint > endJoint THEN {temp ← startJoint; startJoint ← endJoint; endJoint ← temp};
IF startJoint < 0 OR endJoint > hiJoint THEN ERROR;
seq ←
NEW[TrajPartsObj ← [
segments: NewBitVector[traj.segCount],
joints: NewBitVector[hiJoint+1],
controlPoints: NewBitMatrix[traj],
segCount: endJoint - startJoint,
controlPointCount: 0, -- initialized by FillInControlPoints
jointCount: endJoint - startJoint + 1
]];
FOR i:
NAT
IN [startJoint..endJoint)
DO
seq.segments[i] ← TRUE;
seq.joints[i] ← TRUE;
ENDLOOP;
seq.joints[endJoint] ← TRUE;
}
ELSE {
IF startJoint = endJoint THEN {seq ← CreateFromJoint[traj, startJoint]; RETURN};
Closed trajectory with endJoint < startJoint (Case 3).
seq ←
NEW[TrajPartsObj ← [
segments: NewBitVector[traj.segCount],
joints: NewBitVector[hiJoint+1],
controlPoints: NewBitMatrix[traj],
segCount: traj.segCount - startJoint + endJoint,
controlPointCount: 0,
jointCount: traj.segCount - startJoint + endJoint + 1
]];
FOR i:
NAT
IN [startJoint..traj.segCount)
DO
seq.segments[i] ← TRUE;
seq.joints[i] ← TRUE;
ENDLOOP;
FOR i:
NAT
IN [0..endJoint)
DO
seq.segments[i] ← TRUE;
seq.joints[i] ← TRUE;
ENDLOOP;
seq.joints[endJoint] ← TRUE;
};
FillInControlPoints[seq]; -- after all is said and done
};
CreateEmpty:
PUBLIC
PROC [traj: TrajData]
RETURNS [seq: TrajParts] = {
seq ←
NEW[TrajPartsObj ← [
segments: NewBitVector[traj.segCount],
joints: NewBitVector[GGTraj.HiJointTraj[traj]+1],
controlPoints: NewBitMatrix[traj],
segCount: 0,
controlPointCount: 0,
jointCount: 0
]];
};
CreateComplete:
PUBLIC
PROC [traj: TrajData]
RETURNS [seq: TrajParts] = {
Returns a sequence which represents the entire trajectory.
jointCount: NAT ← GGTraj.HiJointTraj[traj] + 1;
seq ←
NEW[TrajPartsObj ← [
segments: NewBitVector[traj.segCount],
joints: NewBitVector[jointCount],
controlPoints: NewBitMatrix[traj],
segCount: traj.segCount,
controlPointCount: 0, -- set by FillInControlPoints
jointCount: jointCount
]];
FOR i:
NAT
IN [0..GGTraj.HiSegmentTraj[traj]]
DO
seq.segments[i] ← TRUE;
seq.joints[i] ← TRUE;
ENDLOOP;
seq.joints[jointCount-1] ← TRUE; -- will be redundant for open trajectories
FillInControlPoints[seq]; -- after all is said and done
};
CreateFromJoint:
PUBLIC
PROC [traj: TrajData, jointNum:
NAT]
RETURNS [seq: TrajParts] = {
seq ← CreateEmpty[traj];
seq.joints[jointNum] ← TRUE;
seq.jointCount ← 1;
};
CreateFromSegment:
PUBLIC
PROC [traj: TrajData, segNum:
NAT]
RETURNS [seq: TrajParts] = {
seq ← CreateEmpty[traj];
seq.segments[segNum] ← TRUE;
seq.segCount ← 1;
FillInJoints[seq];
FillInControlPoints[seq]; -- after all is said and done
};
CreateSimpleFromSegment:
PUBLIC
PROC [traj: TrajData, segNum:
NAT]
RETURNS [seq: TrajParts] = {
seq ← CreateEmpty[traj];
seq.segments[segNum] ← TRUE;
seq.segCount ← 1;
};
CreateFromControlPoint:
PUBLIC
PROC [traj: TrajData, segNum:
NAT, controlPointNum:
NAT]
RETURNS [seq: TrajParts] = {
seq ← CreateEmpty[traj];
seq.controlPoints[segNum][controlPointNum] ← TRUE;
seq.controlPointCount ← 1;
};
Copy:
PUBLIC
PROC [seq: TrajParts]
RETURNS [copy: TrajParts] = {
IF seq=NIL THEN RETURN[NIL];
IF IsObsolete[seq] THEN ERROR;
copy ←
NEW[TrajPartsObj ← [
segments: NewBitVector[seq.segments.len],
joints: NewBitVector[seq.joints.len],
controlPoints: NewBitMatrixFromSeq[seq],
segCount: seq.segCount,
controlPointCount: seq.controlPointCount,
jointCount: seq.jointCount
]];
FOR i: NAT IN [0..seq.segments.len) DO copy.segments[i] ← seq.segments[i]; ENDLOOP;
FOR i: NAT IN [0..seq.joints.len) DO copy.joints[i] ← seq.joints[i]; ENDLOOP;
FOR i:
NAT
IN [0..seq.controlPoints.len)
DO
cpCount: NAT ← seq.controlPoints[i].len;
FOR j:
NAT
IN [0..cpCount)
DO
copy.controlPoints[i][j] ← seq.controlPoints[i][j];
ENDLOOP;
ENDLOOP;
CreateEmptySeq:
PRIVATE
PROC [seq: TrajParts]
RETURNS [copy: TrajParts] = {
IF seq=NIL THEN RETURN[NIL];
copy ←
NEW[TrajPartsObj ← [
segments: NewBitVector[seq.segments.len],
joints: NewBitVector[seq.joints.len],
controlPoints: NewBitMatrixFromSeq[seq],
segCount: 0,
controlPointCount: 0,
jointCount: 0
]];
Fundamentals
ComputeBoundBox: PUBLIC PROC [seq: TrajParts] RETURNS [box: BoundBox] = {
IF seq=NIL THEN RETURN[NIL];
UpdateBoundBox[seq];
box ← seq.boundBox;
};
UpdateBoundBox: PUBLIC PROC [seq: TrajParts] = {
halfJointSize: REAL = GGModelTypes.halfJointSize + 1;
Algorithm:
ForAllJointsInSeq, if joint does not touch a segmentInSeq then update bound box with joint bound box.
ForAllCPInSeq, update bound box with cp bound box.
ForAllSegsInSeq, update bound box with segment bound box.
Sequences often arrives here with seq.boundBox = copy of their traj.boundBox (and thus too big), or with seq.boundBox=NIL. We ignore the current contents and calculate the update from scratch.
traj: TrajData;
box: BoundBox;
IF seq=NIL THEN RETURN;
traj ← seq.traj;
box ← seq.boundBox;
box.null ← TRUE;
ForAllJointsInSeq, if joint does not touch a segmentInSeq then update bound box with joint bound box.
IF seq.jointCount#0 THEN { -- ForAllJointsInTraj
IF traj.role = open THEN { -- ForAllJointsInOpenTraj
IF seq.joints[0] AND NOT seq.segments[0] THEN UpdateBySquare[box, GGTraj.FetchJointPos[traj, 0], halfJointSize]; -- boundary case for joint 0
FOR i: NAT IN [1..traj.segCount) DO -- loop for inner joints
IF seq.joints[i] AND NOT seq.segments[i] AND NOT seq.segments[i-1] THEN UpdateBySquare[box, GGTraj.FetchJointPos[traj, i], halfJointSize];
ENDLOOP;
IF seq.joints[traj.segCount] AND NOT seq.segments[traj.segCount-1] THEN UpdateBySquare[box, GGTraj.FetchJointPos[traj, traj.segCount], halfJointSize]; -- boundary case for last joint
}
ELSE { -- ForAllJointsInClosedTraj
FOR i: NAT IN [0..seq.traj.segCount) DO
IF seq.joints[i] AND NOT seq.segments[i] AND NOT seq.segments[(i -1 + seq.traj.segCount) MOD seq.traj.segCount] THEN UpdateBySquare[box, GGTraj.FetchJointPos[traj, i], halfJointSize];
ENDLOOP;
};
};
ForAllCPInSeq, update bound box with cp bound box.
IF seq.controlPointCount#0 THEN { -- if some control point is in the sequence then ...
FOR i: NAT IN [0..seq.traj.segCount) DO -- for each segment in the traj
IF NOT seq.segments[i] AND SomeSegCPInSeq[i, seq] THEN { -- if this segment is not already in the sequence and any control point of this segment is selected then ...
seg: Segment ← GGTraj.FetchSegment[traj, i]; -- get the segment
cpCount: NAT ← seq.controlPoints[i].len;
FOR j: NAT IN [0..cpCount) DO -- for all control points in the segment
IF seq.controlPoints[i][j] THEN { -- if this cp is in the sequence
point: Point ← seg.class.controlPointGet[seg, j];
UpdateBySquare[box, point, halfJointSize]; -- update the bounding box with the control point
};
ENDLOOP;
};
ENDLOOP;
};
ForAllSegsInSeq, update bound box with segment bound box.
IF seq.segCount#0 THEN { -- if some segment is in the sequence
FOR s: NAT IN [0..seq.traj.segCount) DO -- for each segment in the traj
IF seq.segments[s] THEN { -- if this segment is in the sequence
seg: Segment ← GGTraj.FetchSegment[traj, s]; -- get the segment
GGBoundBox.EnlargeByBox[bBox: box, by: seg.class.boundBox[seg]]; -- update the bounding box with the segment
};
ENDLOOP;
};
};
ComputeTightBox: PUBLIC PROC [seq: TrajParts] RETURNS [box: BoundBox] = {
halfJointSize: REAL = GGModelTypes.halfJointSize + 1;
Algorithm:
ForAllSegsInSeq, update bound box with segment tight box.
traj: TrajData;
IF seq=NIL THEN RETURN[NIL];
traj ← seq.traj;
box ← GGBoundBox.NullBoundBox[];
ForAllSegsInSeq, update bound box with segment bound box.
IF seq.segCount#0 THEN {
FOR s: NAT IN [0..seq.traj.segCount) DO -- for each segment in the traj
IF seq.segments[s] THEN { -- if this segment is in the sequence
seg: Segment ← GGTraj.FetchSegment[traj, s];
GGBoundBox.EnlargeByBox[bBox: box, by: seg.class.tightBox[seg]];
};
ENDLOOP;
};
seq.boundBox ← box; -- and, finally, store the new result
};
UpdateBySquare: PROC [bBox: BoundBox, p: Point, halfSquareSide: REAL] ~ { -- updates the accumulating bBox by a joint or CP
pHalf: REAL ← halfSquareSide+1;
loX, loY, hiX, hiY: REAL;
IF bBox.infinite THEN RETURN;
loX ← p.x-pHalf; loY ← p.y-pHalf; hiX ← p.x+pHalf; hiY ← p.y+pHalf;
IF bBox.null THEN {
bBox.loX ← loX;
bBox.hiX ← hiX;
bBox.loY ← loY;
bBox.hiY ← hiY;
bBox.null ← FALSE;
RETURN;
};
IF loX < bBox.loX THEN bBox.loX ← loX;
IF hiX > bBox.hiX THEN bBox.hiX ← hiX;
IF loY < bBox.loY THEN bBox.loY ← loY;
IF hiY > bBox.hiY THEN bBox.hiY ← hiY;
};
Textual Description
Describe: PUBLIC PROC [seq: TrajParts] RETURNS [rope: Rope.ROPE] = {
IF seq=NIL THEN RETURN[NIL] ELSE {
segNum, cpNum: NAT;
segCount: NAT ← seq.segCount;
jointCount: NAT ← seq.jointCount;
cpCount: NAT ← seq.controlPointCount;
SELECT TRUE FROM
segCount=1 => { -- single segment selected. Describe it
segGen: SegmentGenerator ← GGSequence.SegmentsInSequence[seq];
rope ← GGDescribe.DescribeSegment[seq.traj,
GGSequence.NextSegmentAndIndex[segGen].index];
};
segCount=0 AND cpCount=1 => { -- single CP selected. Describe it
cpGen: ControlPointGenerator ← GGSequence.ControlPointsInSequence[seq];
[segNum, cpNum] ← GGSequence.NextSegNumAndCPNum[cpGen];
rope ← GGDescribe.DescribeControlPoint[seq.traj, segNum, cpNum];
};
segCount=0 AND cpCount=0 AND jointCount=1 => { -- single joint selected. Describe it
jointGen: JointGenerator ← GGSequence.JointsInSequence[seq];
rope ← GGDescribe.DescribeJoint[seq.traj, GGSequence.NextJoint[jointGen]];
};
ENDCASE => rope ← GGDescribe.DescribeSequence[seq];
};
};
Procedures which mutate Segments (use with caution)
CopyInto:
PUBLIC
PROC [to: TrajParts, from: TrajParts] = {
IF IsObsolete[from] THEN ERROR;
FOR i:
NAT
IN [0..from.segments.len)
DO
cpCount: NAT ← from.controlPoints[i].len;
to.segments[i] ← from.segments[i];
FOR j:
NAT
IN [0..cpCount)
DO
to.controlPoints[i][j] ← from.controlPoints[i][j]; -- KAP. April 4, 1986
ENDLOOP;
ENDLOOP;
FOR i:
NAT
IN [0..from.joints.len)
DO
to.joints[i] ← from.joints[i];
ENDLOOP;
to.segCount ← from.segCount;
to.controlPointCount ← from.controlPointCount;
to.jointCount ← from.jointCount;
FillInJoints:
PUBLIC
PROC [seq: TrajParts] = {
a sequence with segments filled in. Fill in all the joints for each segment in the sequence, and calculate the new joint count. Leave in all isolated joints as well.
jointCount: NAT ← 0;
IF seq.segCount=0 THEN RETURN; -- can't do anything. seq may have a single joint or CP
IF seq.joints.len=seq.segments.len
THEN {
-- closed trajectory
FOR i:
NAT
IN [0..seq.segments.len)
DO
IF seq.segments[i] THEN {seq.joints[i] ← TRUE; seq.joints[(i+1) MOD seq.segments.len] ← TRUE;};
ENDLOOP;
}
ELSE
IF seq.joints.len=seq.segments.len+1
THEN {
-- open trajectory
FOR i:
NAT
IN [0..seq.segments.len)
DO
IF seq.segments[i] THEN {seq.joints[i] ← TRUE; seq.joints[i+1] ← TRUE};
ENDLOOP;
}
ELSE ERROR; -- KAP. November 20, 1987 4:42:37 pm PST
FOR i:
NAT
IN [0..seq.joints.len)
DO
IF seq.joints[i] THEN jointCount ← jointCount + 1;
ENDLOOP;
seq.jointCount ← jointCount;
FillInControlPoints:
PUBLIC
PROC [seq: TrajParts] = {
-- a sequence with segs and joints filled in. IF there are segments then fill in all the control points for each segment in the sequence, and calculate the new control point count.
cpCount: NAT;
IF seq.segCount=0 THEN RETURN; -- can't do anything. seq may have a single joint or CP.
seq.controlPointCount ← 0;
FOR i:
NAT
IN [0..seq.segments.len)
DO
cpCount ← seq.controlPoints[i].len;
IF seq.segments[i]
THEN {
FOR j:
NAT
IN [0..cpCount)
DO
-- fill them all in
seq.controlPoints[i][j] ← TRUE;
seq.controlPointCount ← seq.controlPointCount + 1;
ENDLOOP;
}
ELSE {
FOR j:
NAT
IN [0..cpCount)
DO
-- count the ones already there
IF seq.controlPoints[i][j] THEN seq.controlPointCount ← seq.controlPointCount + 1;
ENDLOOP;
};
ENDLOOP;
};
TrimSelectedParts:
PUBLIC
PROC [trimDescriptor: SliceDescriptor, selectedList:
LIST
OF SliceDescriptor] = {
Modifies, mutes, and munges trimDescriptor, so that it no longer includes any parts mentioned in selectedList. Used by GGAlign.RemoveMoving.
seq: TrajParts ← NARROW[trimDescriptor.parts];
selSeq: TrajParts ← FindPartsInList[trimDescriptor.slice, selectedList];
IF selSeq = NIL THEN RETURN; -- no selected parts, so nothing to trim
FOR i:
NAT
IN [0..seq.segments.len)
DO
FOR j:
NAT
IN [0..seq.controlPoints[i].len)
DO
IF selSeq.controlPoints[i][j]
AND seq.controlPoints[i][j]
THEN {
seq.controlPoints[i][j] ← FALSE;
seq.controlPointCount ← seq.controlPointCount - 1;
};
ENDLOOP;
IF selSeq.segments[i]
AND seq.segments[i]
THEN {
seq.segments[i] ← FALSE;
seq.segCount ← seq.segCount - 1;
};
ENDLOOP;
FOR i:
NAT
IN [0..seq.joints.len)
DO
IF selSeq.joints[i]
AND seq.joints[i]
THEN {
seq.joints[i] ← FALSE;
seq.jointCount ← seq.jointCount -1;
};
ENDLOOP;
};
TrimSelectedJointSegments:
PUBLIC
PROC [trimDescriptor: SliceDescriptor, selectedList:
LIST
OF SliceDescriptor] = {
Modifies, mutes, and munges seq, so that it no longer includes any segments whose joints are mentioned in selectedList. Used by GGAlign.RemoveMoving.
SomeSelectedJoint:
PROC [i:
NAT]
RETURNS [
BOOL] = {
RETURN[
selSeq.joints[i] OR selSeq.joints[GGTraj.FollowingJoint[trimDescriptor.slice, i]]
];
};
seq: TrajParts ← NARROW[trimDescriptor.parts];
selSeq: TrajParts ← FindPartsInList[trimDescriptor.slice, selectedList];
IF selSeq = NIL THEN RETURN; -- no joints selected, so nothing to trim
FOR i:
NAT
IN [0..seq.segments.len)
DO
IF seq.segments[i]
AND SomeSelectedJoint[i]
THEN {
seq.segments[i] ← FALSE;
seq.segCount ← seq.segCount - 1;
};
ENDLOOP;
};
TrimDanglingSegments: PUBLIC PROC [trimDescriptor: SliceDescriptor] = {
For all runs in seq, if either end of the run is a segment (not a joint), remove that segment.
seq: TrajParts ← NARROW[trimDescriptor.parts];
runGen: SequenceGenerator;
runCount: NAT;
haveTrimmed: BOOL;
firstSegNum, firstJointNum, lastSegNum, lastJointNum: INT;
IF IsComplete[seq] THEN RETURN; -- nothing is dangling if all joints are included
[runGen, runCount] ← RunsInSequence[seq];
FOR run: TrajParts ← NextSequence[runGen], NextSequence[runGen] UNTIL run = NIL DO
haveTrimmed ← FALSE;
firstSegNum ← FirstSegNum[run];
firstJointNum ← FirstJointNum[run];
lastSegNum ← LastSegNum[run, firstSegNum];
lastJointNum ← LastJointNum[run, firstJointNum];
IF firstJointNum = -1 THEN {
there are no joints in the run. It only has one segment. Remove that segment.
IF firstSegNum = -1 THEN ERROR; -- there must be something in the run
IF NOT seq.segments[firstSegNum] THEN ERROR;
seq.segments[firstSegNum] ← FALSE;
seq.segCount ← seq.segCount - 1;
LOOP;
};
IF lastJointNum = -1 THEN ERROR; -- if there was a first joint, there must be a last one (it is equal to the first joint if there is only one).
IF firstSegNum = -1 THEN LOOP; -- there are no segments so there is nothing to trim
IF lastSegNum = -1 THEN ERROR; -- if there was a first segment, there must be a last.
Assert: If we reach this point, there is at least one joint and at least one segment.
IF firstJointNum # firstSegNum THEN {
IF NOT seq.segments[lastSegNum] THEN ERROR;
seq.segments[firstSegNum] ← FALSE;
seq.segCount ← seq.segCount - 1;
haveTrimmed ← TRUE;
};
IF ((seq.traj.role = open AND lastSegNum + 1 # lastJointNum) OR
(seq.traj.role # open AND ((lastSegNum + 1) MOD seq.traj.segCount) # lastJointNum))
AND (NOT haveTrimmed OR lastSegNum # firstSegNum) THEN {
IF NOT seq.segments[lastSegNum] THEN ERROR;
seq.segments[lastSegNum] ← FALSE;
seq.segCount ← seq.segCount - 1;
};
ENDLOOP;
};
TrimControlPointSegments: PUBLIC PROC [seq: TrajParts, controlPointOn: BOOL] = {
trim segments with any included control points
SomeIncludedCP: PROC [i: NAT] RETURNS [BOOL] = {
FOR j: NAT IN [0..seq.controlPoints[i].len) DO
IF seq.controlPoints[i][j] = controlPointOn THEN RETURN[TRUE];
ENDLOOP;
RETURN[FALSE];
};
IF IsObsolete[seq] THEN ERROR;
FOR i: NAT IN [0..GGTraj.HiSegment[seq.traj]] DO
IF seq.segments[i] AND SomeIncludedCP[i] THEN {
seq.segments[i] ← FALSE;
seq.segCount ← seq.segCount - 1;
};
ENDLOOP;
};
TrimJointSegments: PUBLIC PROC [seq: TrajParts, jointOn: BOOL] = {
If jointOn then for all segments in seq, remove those that have a joint mentioned in seq. Otherwise, remove those segments that do NOT have a joint mentioned.
SomeSelectedJoint: PROC [i: NAT] RETURNS [BOOL] = {
RETURN[
seq.joints[i] = jointOn OR seq.joints[GGTraj.FollowingJoint[seq.traj, i]] = jointOn
];
};
FOR i: NAT IN [0..GGTraj.HiSegment[seq.traj]] DO
IF seq.segments[i] AND SomeSelectedJoint[i] THEN {
seq.segments[i] ← FALSE;
seq.segCount ← seq.segCount - 1;
};
ENDLOOP;
};
TrimSelectedControlPointSegments:
PUBLIC
PROC [trimDescriptor: SliceDescriptor, selectedList:
LIST
OF SliceDescriptor] = {
Modifies, mutes, and munges seq, so that it no longer includes any segments whose control points are mentioned in selectedList. Used by GGAlign.RemoveMoving.
SomeSelectedCP:
PROC [i:
NAT]
RETURNS [
BOOL] = {
FOR j:
NAT
IN [0..selSeq.controlPoints[i].len)
DO
IF selSeq.controlPoints[i][j] THEN RETURN[TRUE];
ENDLOOP;
RETURN[FALSE];
};
seq: TrajParts ← NARROW[trimDescriptor.parts];
selSeq: TrajParts ← FindPartsInList[trimDescriptor.slice, selectedList];
IF selSeq = NIL THEN RETURN; -- no CPs selected, so nothing to trim
FOR i:
NAT
IN [0..seq.segments.len)
DO
IF seq.segments[i]
AND SomeSelectedCP[i]
THEN {
seq.segments[i] ← FALSE;
seq.segCount ← seq.segCount - 1;
};
ENDLOOP;
};
AddNonSelectedJointSegments:
PROC [descriptor: SliceDescriptor, selected: TrajParts] = {
Adds to descriptor those segments whose joints are mentioned in selected, but who are not mentioned in selected themselves.
SomeSelectedJoint:
PROC [i:
NAT]
RETURNS [
BOOL] = {
RETURN[
selected.joints[i] OR selected.joints[GGTraj.FollowingJoint[descriptor.slice, i]]
];
};
seq: TrajParts ← NARROW[descriptor.parts];
IF selected = NIL THEN RETURN; -- no joints selected, so nothing to add
FOR i:
NAT
IN [0..seq.segments.len)
DO
IF
NOT seq.segments[i]
AND
NOT selected.segments[i]
AND SomeSelectedJoint[i]
THEN {
seq.segments[i] ← TRUE;
seq.segCount ← seq.segCount + 1;
};
ENDLOOP;
};
AddNonSelectedControlPointSegments:
PROC [descriptor: SliceDescriptor, selected: TrajParts] = {
Adds to descriptor those segments whose control points are mentioned in selected, but who are not mentioned in selected themselves.
SomeSelectedCP:
PROC [i:
NAT]
RETURNS [
BOOL] = {
FOR j:
NAT
IN [0..selected.controlPoints[i].len)
DO
IF selected.controlPoints[i][j] THEN RETURN[TRUE];
ENDLOOP;
RETURN[FALSE];
};
seq: TrajParts ← NARROW[descriptor.parts];
IF selected = NIL THEN RETURN; -- no CPs selected, so nothing to add
FOR i:
NAT
IN [0..seq.segments.len)
DO
IF
NOT seq.segments[i]
AND
NOT selected.segments[i]
AND SomeSelectedCP[i]
THEN {
seq.segments[i] ← TRUE;
seq.segCount ← seq.segCount + 1;
};
ENDLOOP;
};
AddConstrained:
PROC [descriptor: SliceDescriptor, selected: TrajParts] = {
Adds to descriptor those segments whose control points are constrained to move, but who are not mentioned in selected themselves.
SomeSelectedConstrainer:
PROC [i:
NAT]
RETURNS [
BOOL] = {
previous, following: INT;
previous ← GGTraj.PreviousSegmentNum[descriptor.slice, i];
following ← GGTraj.FollowingSegmentNum[descriptor.slice, i];
IF previous # -1 AND GGTraj.FetchSegment[descriptor.slice, previous].class.type = $Bezier AND selected.controlPoints[previous][1] THEN RETURN[TRUE];
IF following # -1 AND GGTraj.FetchSegment[descriptor.slice, following].class.type = $Bezier AND selected.controlPoints[following][0] THEN RETURN[TRUE];
RETURN[FALSE];
};
seq: TrajParts ← NARROW[descriptor.parts];
IF selected = NIL THEN RETURN; -- nothing to add
FOR i:
NAT
IN [0..seq.segments.len)
DO
IF GGTraj.FetchSegment[descriptor.slice, i].class.type = $Bezier
AND
NOT seq.segments[i]
AND
NOT selected.segments[i]
AND SomeSelectedConstrainer[i]
THEN {
seq.segments[i] ← TRUE;
seq.segCount ← seq.segCount + 1;
May want to add specific control points to rubber.
};
ENDLOOP;
};
AddNewBezier:
PROC [seq: TrajParts, selected: TrajParts, segNum:
NAT] = {
Adds to the seq a new bezier that is being dragged into shape.
IF selected = NIL THEN RETURN; -- nothing to add
IF
NOT seq.segments[segNum]
AND
NOT selected.segments[segNum]
THEN {
seq.segments[segNum] ← TRUE;
seq.segCount ← seq.segCount + 1;
};
};
DDifference:
PUBLIC
PROC [mutable, negative: SliceDescriptor] = {
IF mutable.slice.data # negative.slice.data THEN ERROR; -- different trajectories
DDifferenceSeq[NARROW[mutable.parts, TrajParts], NARROW[negative.parts, TrajParts]] ;
};
DDifferenceSeq:
PRIVATE
PROC [mutSeq, negSeq: TrajParts] = {
This is a destructive form of the Difference operation. mutable ← mutable - negative.
hiJoint: NAT;
IF IsObsolete[mutable] OR IsObsolete[negative] THEN ERROR;
FOR i:
NAT
IN [0..mutSeq.segments.len)
DO
cpCount: NAT ← mutSeq.controlPoints[i].len;
mutSeq.segments[i] ← mutSeq.segments[i] AND NOT negSeq.segments[i];
FOR j:
NAT
IN [0..cpCount)
DO
mutSeq.controlPoints[i][j] ← mutSeq.controlPoints[i][j] AND NOT negSeq.controlPoints[i][j];
ENDLOOP;
mutSeq.joints[i] ← mutSeq.joints[i] AND NOT negSeq.joints[i];
ENDLOOP;
hiJoint ← mutSeq.joints.len-1;
mutSeq.joints[hiJoint] ← mutSeq.joints[hiJoint] AND NOT negSeq.joints[hiJoint];
mutSeq.segCount ← CountSegments[mutSeq];
mutSeq.jointCount ← CountJoints[mutSeq];
mutSeq.controlPointCount ← CountControlPoints[mutSeq];
};
Utility Routines
FindPartsInList:
PUBLIC
PROC [traj: Traj, selectedList:
LIST
OF SliceDescriptor]
RETURNS [trajParts: TrajParts] = {
trajD: SliceDescriptor;
FOR sliceDList:
LIST
OF SliceDescriptor ← selectedList, sliceDList.rest
UNTIL sliceDList =
NIL
DO
sliceD: SliceDescriptor ← sliceDList.first;
trajD ←
IF GGSliceOps.GetType[sliceD.slice]=$Traj
THEN sliceD
ELSE
IF GGSliceOps.GetType[sliceD.slice]=$Outline
THEN GGParent.ChildDescriptorFromDescriptor[sliceD, traj]
ELSE NIL;
IF trajD#NIL THEN RETURN[NARROW[trajD.parts]];
ENDLOOP;
RETURN[NIL];
};
NewBitMatrixFromSeq:
PROC [seq: TrajParts]
RETURNS [bitMatrix: BitMatrix] = {
bitMatrix ← NEW[BitMatrixObj[seq.controlPoints.len]];
FOR j:
NAT
IN [0..seq.controlPoints.len)
DO
-- for each possible segment
bitMatrix[j] ← NewBitVector[seq.controlPoints[j].len]; -- for all possible CPs in segment
ENDLOOP;
};
NewBitMatrix:
PROC [traj: TrajData]
RETURNS [bitMatrix: BitMatrix] = {
j: NAT ← 0;
segGen: SegmentGenerator ← GGSequence.SegmentsInTraj[traj];
bitMatrix ← NEW[BitMatrixObj[traj.segCount]];
FOR seg: Segment ← GGSequence.NextSegment[segGen], GGSequence.NextSegment[segGen]
UNTIL seg =
NIL
DO
bitMatrix[j] ← NewBitVector[seg.class.controlPointCount[seg]];
j ← j+1;
ENDLOOP;
};
NewBitVector:
PROC [length:
NAT]
RETURNS [bitVec: BitVector] = {
bitVec ← NEW[BitVectorObj[length]];
SetAllBits[bitVec, FALSE];
};
SetAllBits:
PROC [bitVec: BitVector, bool:
BOOL] = {
FOR i:
NAT
IN [0..bitVec.len)
DO
bitVec[i] ← bool;
ENDLOOP;
};
SomeSegCPInSeq:
PROC [segNum:
NAT, seq: TrajParts]
RETURNS [
BOOL] = {
cpCount: NAT ← seq.controlPoints[segNum].len;
FOR j:
NAT
IN [0..cpCount)
DO
IF seq.controlPoints[segNum][j] THEN RETURN[TRUE];
ENDLOOP;
RETURN[FALSE];
};
Boolean Operations on Sequences
Union:
PUBLIC
PROC [a, b: SliceDescriptor]
RETURNS [union: SliceDescriptor] = {
hiJoint: NAT;
uSeq: TrajParts;
aSeq: TrajParts ← NARROW[a.parts];
aTraj: TrajData ← NARROW[a.slice.data];
bSeq: TrajParts ← NARROW[b.parts];
bTraj: TrajData ← NARROW[b.slice.data];
IF IsObsolete[a] OR IsObsolete[b] THEN ERROR;
IF aTraj # bTraj THEN ERROR;
IF aSeq = NIL THEN RETURN[b]; -- copy needed ??
IF bSeq = NIL THEN RETURN[a]; -- copy needed ??
IF IsEmpty[aSeq] THEN RETURN[b]; -- copy needed ??
IF IsEmpty[bSeq] THEN RETURN[a]; -- copy needed ??
IF IsComplete[aSeq] THEN RETURN[a]; -- copy needed ??
IF IsComplete[bSeq] THEN RETURN[b]; -- copy needed ??
uSeq ← CreateEmpty[aTraj];
FOR i:
NAT
IN [0..uSeq.segments.len)
DO
cpCount: NAT ← aSeq.controlPoints[i].len;
uSeq.segments[i] ← aSeq.segments[i] OR bSeq.segments[i];
FOR j:
NAT
IN [0..cpCount)
DO
uSeq.controlPoints[i][j] ← aSeq.controlPoints[i][j] OR bSeq.controlPoints[i][j];
ENDLOOP;
uSeq.joints[i] ← aSeq.joints[i] OR bSeq.joints[i];
ENDLOOP;
hiJoint ← uSeq.joints.len-1;
uSeq.joints[hiJoint] ← aSeq.joints[hiJoint] OR bSeq.joints[hiJoint];
uSeq.segCount ← CountSegments[uSeq];
uSeq.jointCount ← CountJoints[uSeq];
uSeq.controlPointCount ← CountControlPoints[uSeq];
union ← GGSlice.DescriptorFromParts[a.slice, uSeq];
};
Difference:
PUBLIC
PROC [a, b: SliceDescriptor]
RETURNS [aMinusB: SliceDescriptor] = {
hiJoint: NAT;
IF IsObsolete[a] OR IsObsolete[b] THEN ERROR;
dSeq: TrajParts;
aSeq: TrajParts ← NARROW[a.parts];
aTraj: TrajData ← NARROW[a.slice.data];
bSeq: TrajParts ← NARROW[b.parts];
bTraj: TrajData ← NARROW[b.slice.data];
dSeq ← CreateEmpty[aTraj];
FOR i:
NAT
IN [0..aSeq.segments.len)
DO
cpCount: NAT ← aSeq.controlPoints[i].len;
dSeq.segments[i] ← aSeq.segments[i] AND NOT bSeq.segments[i];
FOR j:
NAT
IN [0..cpCount)
DO
dSeq.controlPoints[i][j] ← aSeq.controlPoints[i][j] AND NOT bSeq.controlPoints[i][j];
ENDLOOP;
dSeq.joints[i] ← aSeq.joints[i] AND NOT bSeq.joints[i];
ENDLOOP;
hiJoint ← dSeq.joints.len-1;
dSeq.joints[hiJoint] ← aSeq.joints[hiJoint] AND NOT bSeq.joints[hiJoint];
dSeq.segCount ← CountSegments[dSeq];
dSeq.jointCount ← CountJoints[dSeq];
dSeq.controlPointCount ← CountControlPoints[dSeq];
aMinusB ← GGSlice.DescriptorFromParts[a.slice, dSeq];
};
Intersection:
PUBLIC
PROC [a, b: SliceDescriptor]
RETURNS [intersection: SliceDescriptor] = {
hiJoint: NAT;
IF IsObsolete[a] OR IsObsolete[b] THEN ERROR;
iSeq: TrajParts;
aSeq: TrajParts ← NARROW[a.parts];
aTraj: TrajData ← NARROW[a.slice.data];
bSeq: TrajParts ← NARROW[b.parts];
bTraj: TrajData ← NARROW[b.slice.data];
IF aTraj # bTraj THEN ERROR;
iSeq ← CreateEmpty[aTraj];
IF aSeq = NIL OR IsEmpty[aSeq] THEN RETURN[GGSlice.DescriptorFromParts[a.slice, iSeq]];
IF bSeq = NIL OR IsEmpty[bSeq] THEN RETURN[GGSlice.DescriptorFromParts[a.slice, iSeq]];
IF IsComplete[aSeq] AND IsComplete[bSeq] THEN RETURN[a]; -- copy needed ??
FOR i:
NAT
IN [0..aSeq.segments.len)
DO
cpCount: NAT ← aSeq.controlPoints[i].len;
iSeq.segments[i] ← aSeq.segments[i] AND bSeq.segments[i];
FOR j:
NAT
IN [0..cpCount)
DO
iSeq.controlPoints[i][j]← aSeq.controlPoints[i][j] AND bSeq.controlPoints[i][j];
ENDLOOP;
iSeq.joints[i] ← aSeq.joints[i] AND bSeq.joints[i];
ENDLOOP;
hiJoint ← iSeq.joints.len-1;
iSeq.joints[hiJoint] ← aSeq.joints[hiJoint] AND bSeq.joints[hiJoint];
iSeq.segCount ← CountSegments[iSeq];
iSeq.jointCount ← CountJoints[iSeq];
iSeq.controlPointCount ← CountControlPoints[iSeq];
intersection ← GGSlice.DescriptorFromParts[a.slice, iSeq];
};
TrajMovingParts:
PUBLIC
PROC [slice: Slice, parts: SliceParts, editConstraints: EditConstraints, bezierDrag: BezierDragRecord]
RETURNS [background, overlay, rubber, drag: TrajParts] = {
A trajectory doesn't know whether or not it is filled, so it cannot know the difference between background parts and overlay parts. My convention will be that background is NIL. Here is the algorithm for the rest:
All selected joints, control points and segments are "drag";
All unselected segments with selected joints or control points are "rubber".
All unselected segments with constrained control points are "rubber".
Everything else is overlay.
seq: TrajParts ← NARROW[parts];
traj: TrajData ← NARROW[slice.data];
background ← NIL;
drag ← Copy[seq];
rubber ← FindRubberFromSelected[slice, parts, editConstraints, bezierDrag];
overlay ← CreateComplete[traj];
DDifferenceSeq[overlay, drag];
DDifferenceSeq[overlay, rubber];
};
FindRubberFromSelected:
PROC [slice: Slice, parts: SliceParts, editConstraints: EditConstraints, bezierDrag: BezierDragRecord]
RETURNS [rubber: TrajParts] = {
rubberBabbyBuggyBumper: SliceDescriptor;
trajParts: TrajParts ← NARROW[parts];
trajData: TrajData ← NARROW[slice.data];
rubber ← CreateEmpty[trajData];
rubberBabbyBuggyBumper ← GGSlice.DescriptorFromParts[slice, rubber];
AddNonSelectedControlPointSegments[rubberBabbyBuggyBumper, trajParts];
AddNonSelectedJointSegments[rubberBabbyBuggyBumper, trajParts];
IF editConstraints # none THEN AddConstrained[rubberBabbyBuggyBumper, trajParts];
IF bezierDrag.draggingBezier AND trajData = bezierDrag.traj.data THEN AddNewBezier[rubber, trajParts, bezierDrag.bezierNum]; -- For now until more general.
};
Augment:
PUBLIC
PROC [descriptor: SliceDescriptor, trajEnd: TrajEnd, extend:
BOOL]
RETURNS [bigger: TrajParts] = {
A segment (and joint) have been added to one end (the trajEnd end) of traj, making seq obsolete. Create a new sequence bigger which refers to the same joints and segments that seq did, but for the new trajectory. If extend is TRUE, then bigger will include the newly added segment if seq included the old joint which it was attached to.
seq: TrajParts ← NARROW[descriptor.parts];
traj: TrajData ← NARROW[descriptor.slice.data];
IF traj.role = hole
OR traj.role = fence
THEN {
bigger ← AugmentClosed[descriptor, extend];
RETURN;
};
Copy the old information.
bigger ← CreateEmpty[traj];
bigger.segCount ← seq.segCount;
bigger.controlPointCount ← seq.controlPointCount;
bigger.jointCount ← seq.jointCount;
IF trajEnd = hi
THEN {
FOR i:
NAT
IN [0..seq.segments.len)
DO
cpCount: NAT ← seq.controlPoints[i].len;
bigger.segments[i] ← seq.segments[i];
bigger.joints[i] ← seq.joints[i];
FOR j:
NAT
IN [0..cpCount)
DO
bigger.controlPoints[i][j] ← seq.controlPoints[i][j];
ENDLOOP;
ENDLOOP;
At this point the counts and bit vectors are consistent.
IF seq.joints[seq.joints.len - 1]
THEN {
-- final joint included ??
bigger.joints[seq.joints.len - 1] ← TRUE; -- yes, so include it in bigger
IF extend
THEN {
bigger.segments[seq.segments.len] ← TRUE;
bigger.joints[seq.joints.len] ← TRUE;
SetAllBits[bigger.controlPoints[seq.segments.len], TRUE];
bigger.segCount ← bigger.segCount + 1;
bigger.jointCount ← bigger.jointCount + 1;
bigger.controlPointCount ← bigger.controlPointCount + bigger.controlPoints[seq.segments.len].len
};
};
}
ELSE {
FOR i:
NAT
IN [0..seq.segments.len)
DO
cpCount: NAT ← seq.controlPoints[i].len;
bigger.segments[i+1] ← seq.segments[i];
bigger.joints[i+2] ← seq.joints[i+1];
FOR j:
NAT
IN [0..cpCount)
DO
bigger.controlPoints[i+1][j] ← seq.controlPoints[i][j];
ENDLOOP;
ENDLOOP;
At this point the counts and bit vectors are consistent.
IF seq.joints[0]
THEN {
-- first joint included ??
bigger.joints[1] ← TRUE; -- yes, so include it in bigger
IF extend
THEN {
bigger.segments[0] ← TRUE;
bigger.joints[0] ← TRUE;
SetAllBits[bigger.controlPoints[0], TRUE];
bigger.segCount ← bigger.segCount + 1;
bigger.jointCount ← bigger.jointCount + 1;
bigger.controlPointCount ← bigger.controlPointCount + bigger.controlPoints[0].len
};
};
};
};
AugmentClosed:
PROC [descriptor: SliceDescriptor, extend:
BOOL]
RETURNS [bigger: TrajParts] = {
A segment has been added to one end (the high end) of seq.traj, making seq obsolete. Create a new sequence bigger which refers to the same joints and segments that seq did, but for the new trajectory. If extend is TRUE, then bigger will include the newly added segment if seq included the old joint which it was attached to.
seq: TrajParts ← NARROW[descriptor.parts];
traj: TrajData ← NARROW[descriptor.slice.data];
bigger ←
NEW[TrajPartsObj ← [
segments: NewBitVector[seq.segments.len+1],
joints: NewBitVector[seq.joints.len],
controlPoints: NewBitMatrix[traj], -- new BitMatrix one segment bigger
segCount: seq.segCount,
controlPointCount: seq.controlPointCount,
jointCount: seq.jointCount
]];
FOR i:
NAT
IN [0..seq.segments.len)
DO
cpCount: NAT ← seq.controlPoints[i].len;
bigger.segments[i] ← seq.segments[i];
bigger.joints[i] ← seq.joints[i];
FOR j:
NAT
IN [0..cpCount)
DO
bigger.controlPoints[i][j] ← seq.controlPoints[i][j]; -- KAP. April 4, 1986
ENDLOOP;
ENDLOOP;
IF seq.joints[seq.joints.len - 1]
THEN {
-- final joint included ??
bigger.joints[seq.joints.len - 1] ← TRUE; -- yes, so include it in bigger
At this point bigger.segCount, bigger.jointCount, bigger.controlPointCount are consistent with the segments, joints, and controlPoints bit vectors.
IF extend
THEN {
bigger.segments[seq.segments.len] ← TRUE;
SetAllBits[bigger.controlPoints[seq.segments.len], TRUE];
bigger.segCount ← bigger.segCount + 1;
bigger.controlPointCount ← bigger.controlPointCount + bigger.controlPoints[seq.segments.len].len;
};
};
};
IsObsolete:
PUBLIC
PROC [seq: TrajParts]
RETURNS [
BOOL] = {
TEST if this sequence cannot possibly encode its trajectory, probably because the trajectory has been modified. Not a guarantee that it accurately encodes anything.
j: NAT ← 0;
segGen: SegmentGenerator;
IF seq.segments.len # seq.traj.segCount THEN RETURN[TRUE];
IF seq.joints.len # (GGTraj.HiJoint[seq.traj] + 1) THEN RETURN[TRUE];
segGen ← SegmentsInTraj[seq.traj];
FOR nextSeg: Segment ← GGSequence.NextSegment[segGen], GGSequence.NextSegment[segGen] UNTIL nextSeg=NIL DO
IF nextSeg.class.controlPointCount[nextSeg]#seq.controlPoints[j].len THEN RETURN[TRUE];
j ← j+1;
ENDLOOP;
RETURN[FALSE];
THE ABOVE CODE IS ITSELF OBSOLETE
ERROR;
};
IsEmpty:
PUBLIC
PROC [seq: TrajParts]
RETURNS [
BOOL] = {
RETURN[seq=NIL OR (seq.jointCount = 0 AND seq.segCount = 0 AND seq.controlPointCount = 0)];
};
IsComplete:
PUBLIC
PROC [seq: TrajParts]
RETURNS [
BOOL] = {
RETURN[seq#
NIL
AND seq.segCount = seq.segments.len
AND
(seq.jointCount = seq.joints.len)];
};
IsConstrained:
PUBLIC PROC [descriptor: SliceDescriptor, segNum, cPNum:
NAT, editConstraints: EditConstraints]
RETURNS [
BOOL] = {
seq: TrajParts ← NARROW[descriptor.parts];
previous, following: INT;
IF GGTraj.FetchSegment[descriptor.slice, segNum].class.type = $Bezier
AND editConstraints # none
THEN {
IF cPNum = 0
THEN {
previous ← GGTraj.PreviousSegmentNum[descriptor.slice, segNum];
IF previous # -1 AND GGTraj.FetchSegment[descriptor.slice, previous].class.type = $Bezier AND seq.controlPoints[previous][1] THEN RETURN[TRUE]
}
ELSE {
following ← GGTraj.FollowingSegmentNum[descriptor.slice, segNum];
IF following # -1 AND GGTraj.FetchSegment[descriptor.slice, following].class.type = $Bezier AND seq.controlPoints[following][0] THEN RETURN[TRUE];
};
};
RETURN[FALSE];
};
Overlap:
PUBLIC
PROC [descriptor1, descriptor2: SliceDescriptor]
RETURNS [
BOOL] = {
IF IsObsolete[seq1] OR IsObsolete[seq2] THEN ERROR;
hiSegment, hiJoint: NAT;
seq1: TrajParts ← NARROW[descriptor1.parts];
traj1: TrajData ← NARROW[descriptor1.slice.data];
seq2: TrajParts ← NARROW[descriptor2.parts];
traj2: TrajData ← NARROW[descriptor2.slice.data];
IF traj1 # traj2 THEN ERROR;
Compare the segment bit vectors.
hiSegment ← seq1.segments.len;
FOR i:
NAT
IN [0..hiSegment)
DO
IF seq1.segments[i] AND seq2.segments[i] THEN RETURN[TRUE];
ENDLOOP;
Compare the joint bit vectors.
hiJoint ← seq1.joints.len;
FOR i:
NAT
IN [0..hiJoint)
DO
IF seq1.joints[i] AND seq2.joints[i] THEN RETURN[TRUE];
ENDLOOP;
FOR i:
NAT
IN [0..hiSegment)
DO
cpCount: NAT ← seq1.controlPoints[i].len;
FOR j:
NAT
IN [0..cpCount)
DO
IF seq1.controlPoints[i][j] AND seq2.controlPoints[i][j] THEN RETURN[TRUE];
ENDLOOP;
ENDLOOP;
RETURN[FALSE];
};
CountSegments:
PROC [seq: TrajParts]
RETURNS [segCount:
NAT ← 0] = {
hiSegment: NAT ← seq.segments.len;
FOR i:
NAT
IN [0..hiSegment)
DO
IF seq.segments[i] THEN segCount ← segCount + 1;
ENDLOOP;
};
CountJoints:
PROC [seq: TrajParts]
RETURNS [jointCount:
NAT ← 0] = {
hiJoint: NAT ← seq.joints.len;
FOR i:
NAT
IN [0..hiJoint)
DO
IF seq.joints[i] THEN jointCount ← jointCount + 1;
ENDLOOP;
};
CountControlPoints:
PROC [seq: TrajParts]
RETURNS [count:
NAT ← 0] = {
hiSegment: NAT ← seq.segments.len;
FOR i:
NAT
IN [0..hiSegment)
DO
cpCount: NAT ← seq.controlPoints[i].len;
FOR j:
NAT
IN [0..cpCount)
DO
IF seq.controlPoints[i][j] THEN count ← count + 1;
ENDLOOP;
ENDLOOP;
};
ContainsSegment:
PUBLIC
PROC [seq: TrajParts, segNum:
NAT]
RETURNS [
BOOL] = {
RETURN[seq.segments[segNum]];
};
ContainsSomeSegment:
PUBLIC
PROC [seq: TrajParts]
RETURNS [
BOOL] = {
RETURN[seq.segCount > 0];
};
ContainsSegmentParts:
PUBLIC
PROC [seq: TrajParts, segNum:
NAT]
RETURNS [
BOOL] = {
Returns TRUE if the sequence contains any part of a segment, including its end joints, its control points, or the segment proper.
RETURN [seq#
NIL
AND (seq.segments[segNum]
OR
NOT GGUtility.AllFalse[seq.controlPoints[segNum]] OR
seq.joints[segNum] OR
seq.joints[IF --seq.traj.role=open-- seq.segments.len#seq.joints.len THEN segNum+1 ELSE (segNum+1) MOD --seq.traj.segCount-- seq.segments.len]) ];
};
FirstSegment:
PROC [seq: TrajParts]
RETURNS [index:
NAT] ~ {
hiSegment: NAT ← seq.segments.len;
IF IsObsolete[seq] THEN ERROR;
IF
--seq.traj.role = open-- seq.segments.len#seq.joints.len
THEN {
FOR i:
NAT
IN [0..hiSegment)
DO
IF seq.segments[i] THEN GOTO Found;
REPEAT
Found => index ← i;
FINISHED => SIGNAL Problem[msg: "the first run begins with a segment."];
ENDLOOP;
}
ELSE {
FOR i:
NAT
IN [0..hiSegment)
DO
IF NOT seq.segments[(i - 1 + hiSegment) MOD hiSegment] AND seq.joints[i] THEN GOTO Found;
IF NOT seq.joints[i] AND seq.segments[i] THEN GOTO Found;
REPEAT
Found => index ← i;
FINISHED => SIGNAL Problem[msg: "there is no break in the sequence."];
ENDLOOP;
};
};
FirstSegNum:
PUBLIC
PROC [run: TrajParts]
RETURNS [
INT] = {
run had better be a run (a single non-empty contiguous sequence).
IF run.segCount = 0 THEN RETURN[-1];
IF GGSequence.IsComplete[run] THEN RETURN[0]; --DJK, to make work with complete, closed sequences
RETURN[FirstTransitionInRun[run]];
};
LastSegNum: PUBLIC PROC [run: TrajParts, firstSegNum: INT] RETURNS [lastNum: INT] = {
run had better be a run (a single non-empty contiguous sequence).
IF firstSegNum = -1 THEN RETURN[-1];
IF run.traj.role = open THEN lastNum ← firstSegNum + run.segCount - 1
ELSE lastNum ← (firstSegNum + run.segCount - 1 + run.traj.segCount) MOD run.traj.segCount;
};
LastSegNum:
PUBLIC
PROC [run: TrajParts, firstSegNum:
INT]
RETURNS [lastNum:
INT] = {
run had better be a run (a single non-empty contiguous sequence).
IF firstSegNum = -1 THEN RETURN[-1];
IF run.segments.len#run.joints.len THEN lastNum ← firstSegNum + run.segCount - 1
ELSE lastNum ← (firstSegNum + run.segCount - 1 + run.segments.len) MOD run.segments.len;
};
ContainsJoint:
PUBLIC
PROC [seq: TrajParts, jointNum:
NAT]
RETURNS [
BOOL] = {
RETURN[seq.joints[jointNum]];
};
FirstJointNum:
PUBLIC
PROC [run: TrajParts]
RETURNS [
INT] = {
run had better be a run (a single non-empty contiguous sequence).
firstSeg, nextJoint: NAT;
IF run.jointCount = 0 THEN RETURN[-1];
firstSeg ← FirstTransitionInRun[run];
IF run.joints[firstSeg]
THEN
RETURN[firstSeg]
ELSE {
IF
--run.traj.role = open-- run.segments.len#run.joints.len
THEN {
IF run.joints[firstSeg + 1] THEN RETURN[firstSeg + 1]
ELSE ERROR Problem[msg: "Broken invariant."];
}
ELSE {
nextJoint ← FollowingJoint[run, firstSeg];
IF run.joints[nextJoint] THEN RETURN[nextJoint]
ELSE ERROR Problem[msg: "Broken invariant."];
};
};
};
LastJointNum:
PUBLIC
PROC [run: TrajParts, firstJointNum:
INT]
RETURNS [lastNum:
INT] = {
IF firstJointNum = -1 THEN RETURN[-1];
IF --run.traj.role = open-- run.segments.len#run.joints.len THEN lastNum ← firstJointNum + run.jointCount - 1
ELSE lastNum ← (firstJointNum + run.jointCount - 1 + run.segments.len) MOD run.segments.len;
};
LastSegAndJoint:
PUBLIC
PROC [traj: TrajData, trajEnd: TrajEnd]
RETURNS [segAndJoint: TrajParts] = {
segAndJoint ← CreateEmpty[traj];
IF traj.role = open
THEN {
segAndJoint.jointCount ← 1;
segAndJoint.segCount ← 1;
IF trajEnd = lo
THEN {
segAndJoint.segments[0] ← TRUE;
segAndJoint.joints[0] ← TRUE;
}
ELSE {
hiSegment: NAT ← --GGTraj.HiSegment[traj]-- segAndJoint.segments.len-1;
segAndJoint.segments[hiSegment] ← TRUE;
segAndJoint.joints[hiSegment+1] ← TRUE;
};
}
ELSE ERROR;
};
FollowingJoint:
PRIVATE
PROC [seq: TrajParts, index:
NAT]
RETURNS [nextIndex:
INT] = {
open: BOOL ← seq.segments.len#seq.joints.len;
segCount: NAT ← seq.segments.len;
IF open
THEN nextIndex ←
IF index = segCount
THEN -1
ELSE index + 1
ELSE nextIndex ← (index + 1) MOD segCount;
};
UnpackOnePointSequence:
PUBLIC
PROC [seq: TrajParts]
RETURNS [isACP:
BOOL, segNum, cpNum, jointNum:
NAT] = {
seq is asserted to be either a single joint or a single control point. Return the [segNum, cpNum] pair or the jointNum as appropriate.
hiSegment: NAT ← seq.segments.len;
hiJoint: NAT ← seq.joints.len;
FOR i:
NAT
IN [0..hiJoint)
DO
IF seq.joints[i]
THEN {
isACP ← FALSE;
segNum ← 999;
cpNum ← 999;
jointNum ← i;
RETURN;
}
ENDLOOP;
FOR i:
NAT
IN [0..hiSegment)
DO
seg ← GGTraj.FetchSegment[seq.traj, i];
FOR j: NAT IN [0..seg.class.controlPointCount[seg] - 1] DO
FOR j:
NAT
IN [0..seq.controlPoints[i].len)
DO
IF seq.controlPoints[i][j]
THEN {
isACP ← TRUE;
segNum ← i;
cpNum ← j;
jointNum ← 999;
RETURN;
}
ENDLOOP;
ENDLOOP;
ERROR;
};
UnpackOneSegmentSequence:
PUBLIC
PROC [seq: TrajParts]
RETURNS [segNum:
NAT] = {
hiSegment: NAT ← seq.segments.len;
FOR i:
NAT
IN [0..hiSegment)
DO
IF seq.segments[i]
THEN {
segNum ← i;
RETURN;
}
ENDLOOP;
ERROR;
};
UnpackSimpleSequence:
PUBLIC
PROC [slice: Slice, parts: SliceParts]
RETURNS [success:
BOOL ← FALSE, partType: TrajPartType ← none, traj: TrajData, joint: Joint ←
NIL, jointNum:
NAT ← 999, cp: Point ← [0,0], cpNum:
NAT ← 999, seg: Segment ←
NIL, segNum:
NAT ← 999] = {
jointFound, cpFound, segFound: BOOL ← FALSE;
seq: TrajParts ← NARROW[parts];
seqTraj: TrajData ← NARROW[slice.data];
hiSegment: NAT ← seq.segments.len;
hiJoint: NAT ← seq.joints.len;
traj ← seqTraj;
Look for a joint.
FOR i:
NAT
IN [0..hiJoint)
DO
IF seq.joints[i]
THEN {
IF jointFound THEN {success ← FALSE; RETURN};
jointNum ← i;
joint ← GGTraj.FetchJoint[slice, i];
jointFound ← TRUE;
partType ← joint;
}
ENDLOOP;
FOR i:
NAT
IN [0..hiSegment)
DO
thisSeg: Segment ← GGTraj.FetchSegment[slice, i];
cpCount: NAT ← seq.controlPoints[i].len;
FOR j:
NAT
IN [0..cpCount)
DO
IF seq.controlPoints[i][j]
THEN {
IF cpFound THEN {success ← FALSE; RETURN};
segNum ← i;
seg ← thisSeg;
cpNum ← j;
cp ← thisSeg.class.controlPointGet[thisSeg, cpNum];
cpFound ← TRUE;
partType ← controlPoint;
}
ENDLOOP;
ENDLOOP;
IF jointFound AND cpFound THEN {success ← FALSE; RETURN};
FOR i:
NAT
IN [0..hiSegment)
DO
IF seq.segments[i]
THEN {
IF segFound THEN {success ← FALSE; RETURN};
segNum ← i;
seg ← GGTraj.FetchSegment[slice, i];
segFound ← TRUE;
partType ← segment;
}
ENDLOOP;
IF (jointFound AND segFound) OR (cpFound AND segFound) THEN {success ← FALSE; RETURN};
IF NOT jointFound AND NOT cpFound AND NOT segFound THEN ERROR;
success ← TRUE;
};
Generators
RunsInSequence:
PUBLIC
PROC [seq: TrajParts]
RETURNS [seqGen: SequenceGenerator, runCount:
NAT] = {
A run is a sequence with only one contiguous piece. In other words, each joint in the run touches at least one segment in the run (unless the run is a single joint), and each segment in the run touches at least one joint in the run (unless the run is a single segment). The run whose lowest joint has the lowest index will be returned first by NextSequence. The other runs will follow in ascending order.
IF IsObsolete[seq] THEN ERROR;
IF --seq.traj.role = open--
seq.segments.len#seq.joints.len
THEN [seqGen, runCount] ← RunsInSequenceOpen[seq]
ELSE [seqGen, runCount] ← RunsInSequenceClosed[seq];
};
RunsInSequenceOpen:
PROC [seq: TrajParts]
RETURNS [seqGen: SequenceGenerator, runCount:
NAT] = {
in: BOOL ← FALSE;
thisSeq: TrajParts;
hiSegment: NAT ← seq.segments.len-1; -- GGTraj.HiSegment[seq.traj]
hiJoint: NAT ← hiSegment + 1;
seqGen ← NEW[SequenceGeneratorObj ← [NIL]];
runCount ← 0;
IF IsEmpty[seq] THEN RETURN;
FOR i:
NAT
IN [0..hiSegment]
DO
[in, seqGen.list, thisSeq, runCount] ← ConsiderJoint[in, seqGen.list, seq, thisSeq, runCount, i];
[in, seqGen.list, thisSeq, runCount] ← ConsiderSegment[in, seqGen.list, seq, thisSeq, runCount, i];
ENDLOOP;
[in, seqGen.list, thisSeq, runCount] ← ConsiderJoint[in, seqGen.list, seq, thisSeq, runCount, hiJoint];
IF in THEN seqGen.list ← AppendToList[seqGen.list, CONS[thisSeq, NIL]];
};
RunsInSequenceClosed:
PROC [seq: TrajParts]
RETURNS [seqGen: SequenceGenerator, runCount:
NAT] = {
This is done in two parts. First we find the first joint or segment that is preceded by a segment or joint that is not in the sequence. Then we proceed as for open but mod traj.segCount.
segCount: NAT ← seq.segments.len;
in: BOOL ← FALSE;
thisSeq: TrajParts;
start: NAT;
natGen: NATGenerator;
seqGen ← NEW[SequenceGeneratorObj ← [NIL]];
runCount ← 0;
IF ControlPointsOnly[seq] THEN RETURN;
start ← FirstTransitionInRun[seq];
in ← FALSE;
natGen ← NATsInInterval[start, segCount, segCount];
FOR i:
INT ← NextNAT[natGen], NextNAT[natGen]
UNTIL i = -1
DO
[in, seqGen.list, thisSeq, runCount] ← ConsiderJoint[in, seqGen.list, seq, thisSeq, runCount, i];
[in, seqGen.list, thisSeq, runCount] ← ConsiderSegment[in, seqGen.list, seq, thisSeq, runCount, i];
ENDLOOP;
IF in THEN seqGen.list ← AppendToList[seqGen.list, CONS[thisSeq, NIL]];
};
ControlPointsOnly:
PROC [seq: TrajParts]
RETURNS [
BOOL] = {
RETURN[seq#NIL AND seq.jointCount = 0 AND seq.segCount = 0];
};
NextSequence:
PUBLIC
PROC [seqGen: SequenceGenerator]
RETURNS [seq: TrajParts] = {
IF seqGen.list = NIL THEN RETURN[NIL];
seq ← seqGen.list.first;
seqGen.list ← seqGen.list.rest;
};
FirstRun:
PUBLIC
PROC [seq: TrajParts]
RETURNS [runParts: TrajParts] = {
Interim implementation. Clearly, we don't really need to generate all of them just to get the first.
runCount: NAT;
seqGen: SequenceGenerator;
[seqGen, runCount] ← RunsInSequence[seq];
runParts ← IF runCount = 0 THEN NIL ELSE NextSequence[seqGen];
};
FirstTransitionInRun:
PROC [seq: TrajParts]
RETURNS [transitionNum:
NAT] = {
Finds the first transition from a joint that's not in the sequence to a segment in the sequence, or from a segment that's not in the sequence to a joint that's in the sequence. For example, with sequence 0 (0) 1 (1) 2 (2) 3 (3) 4, transition segment (1) -> joint 2 returns 2. joint 1 -> segment (2) returns 2. Hence, if the first run has any segments at all, transitionNum will be the number of the first segment.
hiSegment: NAT ← seq.segments.len-1; -- GGTraj.HiSegment[seq.traj];
IF --seq.traj.role = open-- seq.segments.len#seq.joints.len
THEN {
FOR i:
NAT
IN [0..hiSegment]
DO
IF seq.joints[i] THEN RETURN[i];
IF seq.segments[i] THEN RETURN[i];
ENDLOOP;
IF seq.joints[hiSegment+1] THEN RETURN[hiSegment+1];
SIGNAL Problem[msg: "there is no break in the sequence."];
}
ELSE {
FOR i:
NAT
IN [0..hiSegment]
DO
IF NOT seq.segments[(i -1 + seq.segments.len) MOD seq.segments.len] AND seq.joints[i] THEN GOTO FoundStart;
IF NOT seq.joints[i] AND seq.segments[i] THEN GOTO FoundStart;
REPEAT
FoundStart => transitionNum ← i;
FINISHED => SIGNAL Problem[msg: "there is no break in the sequence."];
ENDLOOP;
};
};
AppendToList:
PROC [list1, list2:
LIST
OF TrajParts]
RETURNS [result:
LIST
OF TrajParts] = {
pos: LIST OF TrajParts;
newCell: LIST OF TrajParts;
Non-destructive (copies the first list).
IF list1 = NIL THEN RETURN[list2];
result ← CONS[list1.first, NIL];
pos ← result;
FOR l:
LIST
OF TrajParts ← list1.rest, l.rest
UNTIL l =
NIL
DO
newCell ← CONS[l.first, NIL];
pos.rest ← newCell;
pos ← newCell;
ENDLOOP;
pos.rest ← list2;
};
ConsiderJoint:
PROC [in:
BOOL, list:
LIST
OF TrajParts, seq: TrajParts, thisSeq: TrajParts, runCount:
NAT, i:
NAT]
RETURNS [newIn:
BOOL, newList:
LIST
OF TrajParts, newThisSeq: TrajParts, newCount:
NAT] = {
newIn ← in;
newList ← list;
newThisSeq ← thisSeq;
newCount ← runCount;
SELECT
TRUE
FROM
NOT seq.joints[i] AND NOT in => {};
NOT seq.joints[i]
AND in => {
newIn ← FALSE;
newList ← AppendToList[list, CONS[thisSeq, NIL]];
newThisSeq ← NIL};
seq.joints[i]
AND
NOT in => {
newIn ← TRUE;
newThisSeq ← CreateEmptySeq[seq];
runCount ← runCount + 1; -- BUG! KAP March 11, 1986
newCount ← newCount + 1;
newThisSeq.joints[i] ← TRUE;
newThisSeq.jointCount ← newThisSeq.jointCount + 1;
};
seq.joints[i]
AND in => {
newThisSeq.joints[i] ← TRUE;
newThisSeq.jointCount ← newThisSeq.jointCount + 1;
};
ENDCASE => ERROR;
};
ConsiderSegment:
PROC [in:
BOOL, list:
LIST
OF TrajParts, seq: TrajParts, thisSeq: TrajParts, runCount:
NAT, i:
NAT]
RETURNS [newIn:
BOOL, newList:
LIST
OF TrajParts, newThisSeq: TrajParts, newCount:
NAT] = {
newIn ← in;
newList ← list;
newThisSeq ← thisSeq;
newCount ← runCount;
SELECT
TRUE
FROM
NOT seq.segments[i] AND NOT in => {};
NOT seq.segments[i]
AND in => {
newIn ← FALSE;
newList ← AppendToList[list, CONS[thisSeq, NIL]];
newThisSeq ← NIL};
seq.segments[i]
AND
NOT in => {
newIn ← TRUE;
newThisSeq ← CreateEmptySeq[seq];
newCount ← newCount + 1;
newThisSeq.segments[i] ← TRUE;
newThisSeq.segCount ← newThisSeq.segCount + 1;
ControlPointsTrue[newThisSeq, i];
};
seq.segments[i]
AND in => {
newThisSeq.segments[i] ← TRUE;
newThisSeq.segCount ← newThisSeq.segCount + 1;
ControlPointsTrue[newThisSeq, i];
};
ENDCASE => ERROR;
};
ControlPointsTrue:
PROC [seq: TrajParts, segNum:
NAT] = {
cpCount: NAT ← seq.controlPoints[segNum].len;
FOR j:
NAT
IN [0..cpCount)
DO
IF
NOT seq.controlPoints[segNum][j]
THEN {
seq.controlPoints[segNum][j] ← TRUE;
seq.controlPointCount ← seq.controlPointCount + 1;
};
ENDLOOP;
};
NATGenerator: TYPE = REF NATGeneratorObj;
NATGeneratorObj:
TYPE =
RECORD [
toGo: NAT,
next: NAT,
mod: NAT
];
NATsInInterval:
PROC [start, length, mod:
NAT]
RETURNS [natGen: NATGenerator] = {
natGen ←
NEW[NATGeneratorObj ← [
toGo: length,
next: start,
mod: mod]];
};
NextNAT:
PROC [natGen: NATGenerator]
RETURNS [nextNAT:
INT] = {
IF natGen.toGo = 0 THEN RETURN[-1];
natGen.toGo ← natGen.toGo - 1;
nextNAT ← natGen.next;
natGen.next ← (natGen.next + 1) MOD natGen.mod;
};
SegmentsInTraj:
PUBLIC
PROC [traj: TrajData]
RETURNS [segGen: SegmentGenerator] = {
We special case the complete trajectory case to reduce allocations.
segGen ←
NEW[SegmentGeneratorObj ← [
traj: traj,
toGo: traj.segCount,
index: 0,
seq: NIL,
completeSeq: TRUE
]];
WalkSegmentsInTraj:
PUBLIC
PROC [traj: TrajData, walkProc: SegmentWalkProc]
RETURNS [aborted:
BOOL ←
FALSE] = {
Calls walkProc for each segment in traj, until walkProc returns done=TRUE.
seg: Segment;
FOR i:
NAT
IN [0..GGTraj.HiSegmentTraj[traj]]
DO
seg ← GGTraj.FetchSegmentTraj[traj, i];
aborted ← walkProc[traj, seg, i];
IF aborted THEN RETURN;
ENDLOOP;
};
WalkSegmentsInSequence:
PUBLIC
PROC [traj: TrajData, seq: TrajParts, walkProc: SegmentWalkProc]
RETURNS [aborted:
BOOL ←
FALSE] = {
Calls walkProc for each segment mentioned in seq, until walkProc returns done=TRUE.
seg: Segment;
done: BOOL ← FALSE;
toGo: NAT ← IF seq = NIL THEN 0 ELSE seq.segCount;
segCount: NAT ← traj.segCount;
index, touched: NAT ← 0;
FOR i:
NAT
IN [1..toGo]
DO
UNTIL seq.segments[index]
DO
index ← (index + 1) MOD segCount;
touched ← touched + 1;
IF touched >= segCount THEN SIGNAL Problem[msg: "Broken invariant"];
ENDLOOP;
seg ← GGTraj.FetchSegmentTraj[traj, index];
aborted ← walkProc[traj, seg, index];
IF aborted THEN RETURN;
index ← (index + 1) MOD segCount;
ENDLOOP;
};
CountSegmentsInSequence:
PUBLIC
PROC [traj: TrajData, seq: TrajParts]
RETURNS [count:
NAT] = {
AddEmUp:
PROC [traj: TrajData, seg: Segment, index:
NAT]
RETURNS [done:
BOOL ←
FALSE] = {
count ← count + 1;
};
count ← 0;
[] ← WalkSegmentsInSequence[traj, seq, AddEmUp];
};
NextSegmentAndIndex:
PUBLIC
PROC [segGen: SegmentGenerator]
RETURNS [next: SegAndIndex] = {
Termination: segGen.toGo is always decremented.
Invariants:
1) When this procedure is called, seq.segments[segGen.index] has not yet been looked at OR segGen.toGo = 0.
2) touched < segCount OR segGen.toGo = 0.
segCount: NAT;
seq: TrajParts ← segGen.seq;
IF segGen.toGo = 0 THEN RETURN[[NIL, 999]];
segCount ← GGTraj.HiSegmentTraj[segGen.traj] + 1;
IF
NOT segGen.completeSeq
THEN {
-- If we are stepping through a complete traj, seq = NIL
UNTIL seq.segments[segGen.index]
DO
segGen.index ← (segGen.index + 1) MOD segCount;
segGen.touched ← segGen.touched + 1;
IF segGen.touched >= segCount THEN SIGNAL Problem[msg: "Broken invariant"];
ENDLOOP;
};
3) At this point in the procedure, seq.segments[segGen.index] is TRUE.
4) At this point, touched < segCount.
next.seg ← GGTraj.FetchSegmentTraj[segGen.traj, segGen.index];
next.index ← segGen.index;
segGen.index ← (segGen.index + 1) MOD segCount;
segGen.touched ← segGen.touched + 1;
segGen.toGo ← segGen.toGo - 1;
};
SegmentsInSequence:
PUBLIC
PROC [traj: TrajData, seq: TrajParts]
RETURNS [segGen: SegmentGenerator] = {
NIL TrajParts now legal in outline descriptors !!. KAP. September 16, 1987
IF IsComplete[seq]
THEN
{
segGen ← SegmentsInTraj[traj];
RETURN;
};
segGen ←
NEW[SegmentGeneratorObj ← [
traj: IF seq=NIL THEN NIL ELSE traj,
toGo: IF seq=NIL THEN 0 ELSE seq.segCount,
index: 0,
seq: seq, -- should we copy it? Bier January 27, 1986 5:37:01 pm PST
completeSeq: FALSE
]];
};
OrderedSegmentsInSequence:
PUBLIC
PROC [traj: TrajData, seq: TrajParts]
RETURNS [segGen: SegmentGenerator] = {
Returns a generator for all segments in seq, beginning with the lowest numbered one (in the case of open trajectories), or the first segment that follows a segment that is not in seq (for closed trajectories). If seq.traj is complete, then begins with segment 0.
IF IsEmpty[seq] THEN ERROR; -- why an error?? KAP December 22, 1986
IF IsObsolete[seq] THEN ERROR;
IF IsComplete[seq] THEN RETURN[SegmentsInTraj[traj]];
segGen ←
NEW[SegmentGeneratorObj ← [
traj: traj,
toGo: seq.segCount,
index: IF seq.segCount=0 THEN 0 ELSE FirstSegment[seq], -- KAP December 22, 1986
seq: seq, -- should we copy it? Bier January 27, 1986
completeSeq: FALSE
]];
};
NextSegment:
PUBLIC
PROC [segGen: SegmentGenerator]
RETURNS [next: Segment] = {
Termination: segGen.toGo is always decremented.
Invariants:
1) When this procedure is called, seq.segments[segGen.index] has not yet been looked at OR segGen.toGo = 0.
2) touched < segCount OR segGen.toGo = 0.
segCount: NAT;
seq: TrajParts ← segGen.seq;
IF segGen.toGo = 0 THEN RETURN[NIL];
segCount ← GGTraj.HiSegmentTraj[segGen.traj] + 1;
segCount ← seq.segments.len + 1; -- can't do this because seq=NIL for complete guys
IF
NOT segGen.completeSeq
THEN {
-- If we are stepping through a complete traj, seq = NIL
UNTIL seq.segments[segGen.index]
DO
segGen.index ← (segGen.index + 1) MOD segCount;
segGen.touched ← segGen.touched + 1;
IF segGen.touched >= segCount THEN SIGNAL Problem[msg: "Broken invariant"];
ENDLOOP;
};
3) At this point in the procedure, seq.segments[segGen.index] is TRUE.
4) At this point, touched < segCount.
next ← GGTraj.FetchSegmentTraj[segGen.traj, segGen.index];
segGen.index ← (segGen.index + 1) MOD segCount;
segGen.touched ← segGen.touched + 1;
segGen.toGo ← segGen.toGo - 1;
};
ControlPointsInSequence:
PUBLIC
PROC [traj: TrajData, seq: TrajParts]
RETURNS [cpGen: ControlPointGenerator] = {
NIL TrajParts now legal in outline descriptors !!. KAP. September 16, 1987
IF IsObsolete[seq] THEN ERROR; -- expensive. KAP. September 16, 1987
cpGen ←
NEW[ControlPointGeneratorObj ← [
traj: traj,
seq: seq,
toGo: IF seq=NIL THEN 0 ELSE seq.controlPointCount,
segIndex: 0,
cpIndex: 0
]];
};
WalkControlPointsInSequence:
PUBLIC
PROC [traj: TrajData, seq: TrajParts, walkProc: PointWalkProc] = {
hiSegment: NAT ← seq.segments.len-1;
seg: Segment;
done: BOOL ← FALSE;
FOR i:
NAT
IN [0..hiSegment]
DO
FOR j:
NAT
IN [0..seq.controlPoints[i].len)
DO
IF seq.controlPoints[i][j]
THEN {
seg ← GGTraj.FetchSegmentTraj[traj, i];
done ← walkProc[seg.class.controlPointGet[seg, j]];
IF done THEN RETURN;
};
ENDLOOP;
ENDLOOP;
};
NextControlPoint:
PUBLIC
PROC [cpGen: ControlPointGenerator]
RETURNS [next: PointAndDone] = {
IF cpGen.toGo = 0
THEN
RETURN[[[0,0],
TRUE]]
ELSE {
trajParts: TrajParts ← cpGen.seq;
seg: Segment;
segIndex: NAT ← cpGen.segIndex;
cpIndex: NAT ← cpGen.cpIndex;
hiSegment: NAT ← trajParts.segments.len;
FOR i:
NAT
IN [segIndex..hiSegment)
DO
FOR j:
NAT
IN [cpIndex..trajParts.controlPoints[i].len)
DO
IF trajParts.controlPoints[i][j]
THEN {
segIndex ← i;
cpIndex ← j;
GOTO Found;
};
ENDLOOP;
cpIndex ← 0;
REPEAT
Found => {
seg ← GGTraj.FetchSegmentTraj[cpGen.traj, segIndex];
next.point ← seg.class.controlPointGet[seg, cpIndex];
next.done ← FALSE;
};
FINISHED => ERROR;
ENDLOOP;
cpGen.segIndex ← segIndex;
cpGen.cpIndex ← cpIndex + 1;
cpGen.toGo ← cpGen.toGo - 1;
};
};
NextSegNumAndCPNum:
PUBLIC
PROC [cpGen: ControlPointGenerator]
RETURNS [segNum, cpNum:
NAT, done:
BOOL] = {
IF cpGen.toGo = 0
THEN
RETURN[0, 0,
TRUE]
ELSE {
seq: TrajParts ← cpGen.seq;
segIndex: NAT ← cpGen.segIndex;
cpIndex: NAT ← cpGen.cpIndex;
hiSegment: NAT ← seq.segments.len;
FOR i:
NAT
IN [segIndex..hiSegment)
DO
FOR j:
NAT
IN [cpIndex..seq.controlPoints[i].len)
DO
IF seq.controlPoints[i][j]
THEN {
segIndex ← i;
cpIndex ← j;
GOTO Found;
};
ENDLOOP;
cpIndex ← 0;
REPEAT
Found => {
segNum ← segIndex;
cpNum ← cpIndex;
done ← FALSE;
};
FINISHED => ERROR;
ENDLOOP;
cpGen.segIndex ← segIndex;
cpGen.cpIndex ← cpIndex + 1;
cpGen.toGo ← cpGen.toGo - 1;
};
};
JointsInSequence:
PUBLIC
PROC [seq: TrajParts]
RETURNS [jointGen: JointGenerator] = {
It would be nice to have a hiJoint field in the JointGeneratorObj, Bier, January 7, 1987
IF IsObsolete[seq] THEN ERROR; -- expensive (HorizontalBench.script). Bier, April 7, 1987
jointGen ←
NEW[JointGeneratorObj ← [
-- this allocate is expensive too.
traj: NIL, -- shouldn't be needed
toGo: IF seq=NIL THEN 0 ELSE seq.jointCount,
index: 0,
seq: seq,
completeSeq: FALSE -- for optimization.
]];
};
WalkJointPositionsInSequence:
PUBLIC
PROC [traj: TrajData, seq: TrajParts, walkProc: JointWalkProc] = {
Calls walkProc on each joint position in seq until walkProc returns done=TRUE.
jointPos: Point;
done: BOOL ← FALSE;
toGo: NAT ← IF seq = NIL THEN 0 ELSE seq.jointCount;
hiJoint: NAT ← seq.joints.len;
index: NAT ← 0;
FOR i:
NAT
IN [1..toGo]
DO
UNTIL seq.joints[index]
OR index > hiJoint
DO
index ← index + 1;
ENDLOOP;
IF index > hiJoint THEN ERROR;
jointPos ← GGTraj.FetchJointPosTraj[traj, index];
done ← walkProc[traj, jointPos, index];
IF done THEN RETURN;
index ← index + 1;
ENDLOOP;
};
WalkJointsInSequence:
PUBLIC
PROC [traj: TrajData, seq: TrajParts, walkProc: PointWalkProc] = {
Calls walkProc on each joint position in seq until walkProc returns done=TRUE.
jointPos: Point;
done: BOOL ← FALSE;
toGo: NAT ← IF seq = NIL THEN 0 ELSE seq.jointCount;
hiJoint: NAT ← seq.joints.len;
index: NAT ← 0;
FOR i:
NAT
IN [1..toGo]
DO
UNTIL seq.joints[index]
OR index > hiJoint
DO
index ← index + 1;
ENDLOOP;
IF index > hiJoint THEN ERROR;
jointPos ← GGTraj.FetchJointPosTraj[traj, index];
done ← walkProc[jointPos];
IF done THEN RETURN;
index ← index + 1;
ENDLOOP;
};
JointsInTraj:
PUBLIC
PROC [traj: TrajData]
RETURNS [jointGen: JointGenerator] = {
jointGen ←
NEW[JointGeneratorObj ← [
traj: NIL, -- shouldn't be needed
toGo: GGTraj.HiJointTraj[traj] + 1,
index: 0,
seq: NIL,
completeSeq: TRUE
]];
};
NextJoint:
PUBLIC
PROC [jointGen: JointGenerator]
RETURNS [next:
INT] = {
seq: TrajParts ← jointGen.seq;
index: NAT ← jointGen.index;
IF jointGen.toGo = 0 THEN RETURN[-1];
IF jointGen.completeSeq
THEN {
next ← index;
}
ELSE {
hiJoint: NAT ← GGTraj.HiJoint[jointGen.traj];
hiJoint: NAT ← seq.joints.len;
UNTIL seq.joints[index]
OR index > hiJoint
DO
index ← index + 1;
ENDLOOP;
IF index > hiJoint THEN ERROR;
next ← index;
};
jointGen.index ← index + 1;
jointGen.toGo ← jointGen.toGo - 1;
};
END.