GGObjectsImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Last edited by Bier on November 7, 1985 9:27:37 pm PST
Contents: The procedural interface to the Gargoyle object modeler.
Stone, August 5, 1985 4:13:18 pm PDT
DIRECTORY
GGBoundBox,
GGError,
GGObjects,
GGModelTypes,
GGSegment,
GGTransform,
GGUtility,
GGVector,
Imager,
ImagerPath,
ImagerTransformation,
Rope,
Rosary;
GGObjectsImpl:
CEDAR
PROGRAM
IMPORTS GGBoundBox, GGError, GGObjects, GGSegment, GGTransform, GGUtility, GGVector, Imager, ImagerTransformation, Rosary
EXPORTS GGObjects =
BEGIN
BoundBox: TYPE = GGModelTypes.BoundBox;
BoundBoxGenerator: TYPE = REF BoundBoxGeneratorObj;
BoundBoxGeneratorObj: TYPE = GGModelTypes.BoundBoxGeneratorObj;
Camera: TYPE = GGModelTypes.Camera;
Cluster: TYPE = GGModelTypes.Cluster;
EntityGenerator: TYPE = REF EntityGeneratorObj;
EntityGeneratorObj: TYPE = GGModelTypes.EntityGeneratorObj;
JointGenerator: TYPE = REF JointGeneratorObj;
JointGeneratorObj: TYPE = GGModelTypes.JointGeneratorObj;
JointPair: TYPE = GGModelTypes.JointPair;
Outline: TYPE = REF OutlineObj;
OutlineObj: TYPE = GGModelTypes.OutlineObj;
Point: TYPE = GGModelTypes.Point;
Scene: TYPE = REF SceneObj;
SceneObj: TYPE = GGModelTypes.SceneObj;
SegmentGenerator: TYPE = REF SegmentGeneratorObj;
SegmentGeneratorObj: TYPE = GGModelTypes.SegmentGeneratorObj;
SequenceGenerator: TYPE = REF SequenceGeneratorObj;
SequenceGeneratorObj: TYPE = GGModelTypes.SequenceGeneratorObj;
Sequence: TYPE = REF SequenceObj;
SequenceObj: TYPE = GGModelTypes.SequenceObj;
TrajGenerator: TYPE = REF TrajGeneratorObj;
TrajGeneratorObj: TYPE = GGModelTypes.TrajGeneratorObj;
Vector: TYPE = GGModelTypes.Vector;
Traj: TYPE = REF TrajObj;
TrajObj:
TYPE = GGModelTypes.TrajObj;
TrajEnd: TYPE = GGModelTypes.TrajEnd; -- {lo, hi};
Joint: TYPE = REF JointObj;
JointObj:
TYPE = GGModelTypes.JointObj;
CurveType: TYPE = {line, bezier, conic};
Segment: TYPE = REF SegmentObj;
SegmentObj: TYPE = GGModelTypes.SegmentObj;
Creating Trajectories and Outlines
CreateTraj:
PUBLIC
PROC [point: Point]
RETURNS [traj: Traj] = {
firstJoint: Joint ← NEW[JointObj ← [point: point]];
bBox: BoundBox ← GGBoundBox.CreateBoundBox[point[1], point[2], point[1], point[2]];
traj ← NEW[TrajObj ← [open, 0, NIL, Rosary.FromItem[firstJoint], NIL, NIL, bBox, FALSE]];
};
OutlineFromTraj:
PUBLIC
PROC [traj: Traj, lineEnds: Imager.StrokeEnd ← square, fillColor: Imager.Color ← Imager.black]
RETURNS [outline: Outline] = {
boundBox: BoundBox ← GGBoundBox.CopyBoundBox[traj.boundBox];
outline ←
NEW[OutlineObj ← [
fillColor: fillColor,
lineEnds: lineEnds,
children: LIST[traj],
parent: NIL,
boundBox: boundBox]];
traj.outline ← outline;
};
SetLineEnds:
PUBLIC PROC [outline: Outline, lineEnds: Imager.StrokeEnd] = {
outline.lineEnds ← lineEnds;
};
SetFillColor:
PUBLIC PROC [outline: Outline, fillColor: Imager.Color] = {
outline.fillColor ← fillColor;
};
AddSegment:
PUBLIC
PROC [traj: Traj, trajEnd: TrajEnd, seg: Segment, segEnd: TrajEnd]
RETURNS [success:
BOOL] = {
diff: Vector;
loJoint, hiJoint: Joint;
Moves seg so that the specified end of seg coincides with the specified end of traj. In other words, seg is translated.
IF traj.role = fence
OR traj.role = hole
THEN {
GGError.Append["Can't AddSegment to a closed trajectory. Ignored.", oneLiner];
GGError.Blink[];
RETURN[FALSE];
};
success ← TRUE;
IF traj.segCount = 0
THEN {
-- this is the first segment
traj.segCount ← 1;
traj.segments ← Rosary.FromItem[seg];
loJoint ← NEW[JointObj ← [point: seg.lo]];
hiJoint ← NEW[JointObj ← [point: seg.hi]];
traj.joints ← Rosary.FromList[LIST[loJoint, hiJoint]];
traj.boundBox ← GGBoundBox.CopyBoundBox[seg.boundBox];
}
ELSE {
IF trajEnd = segEnd THEN GGSegment.ReverseSegment[seg];
IF trajEnd = lo
THEN {
diff ← GGVector.Sub[FetchJointPos[traj, 0], seg.hi];
TranslateSegment[seg, diff];
loJoint ← NEW[JointObj ← [point: seg.lo]];
traj.joints ← Rosary.Concat[Rosary.FromItem[loJoint], traj.joints];
traj.segments ← Rosary.Concat[Rosary.FromItem[seg], traj.segments];
traj.segCount ← traj.segCount + 1;
GGBoundBox.EnlargeBoundBox[traj.boundBox, seg.boundBox];
}
ELSE {
diff ← GGVector.Sub[FetchJointPos[traj, traj.segCount], seg.lo];
TranslateSegment[seg, diff];
hiJoint ← NEW[JointObj ← [point: seg.hi]];
traj.joints ← Rosary.Concat[traj.joints, Rosary.FromItem[hiJoint]];
traj.segments ← Rosary.Concat[traj.segments, Rosary.FromItem[seg]];
traj.segCount ← traj.segCount + 1;
GGBoundBox.EnlargeBoundBox[traj.boundBox, seg.boundBox];
}
}
NotYetImplemented: PUBLIC SIGNAL = CODE;
CloseWithSegment:
PUBLIC
PROC [traj: Traj, seg: Segment, segEnd: TrajEnd] = {
LikeAddSegment, except that here we rotate, scale, and translate traj as needed so that both of its endpoints coincide with the endpoints of traj (which must not already be closed). The endpoint correspondence is made so that the hi end of traj touches the named (segEnd) end of seg, leaving the other ends to touch each other.
diff: Vector;
IF traj.segCount = 0
THEN
ERROR NotYetImplemented;
-- a single closed segment.
For now, just translate into place and assume size is ok.
IF segEnd = hi THEN GGSegment.ReverseSegment[seg];
diff ← GGVector.Sub[LastJointPos[traj], seg.lo];
seg.lo ← GGVector.Add[seg.lo, diff];
seg.hi ← GGVector.Add[seg.hi, diff];
traj.segments ← Rosary.Concat[traj.segments, Rosary.FromItem[seg]];
traj.segCount ← traj.segCount + 1;
traj.role ← fence;
GGBoundBox.EnlargeBoundBox[traj.boundBox, seg.boundBox];
};
To make an outline with holes, use these:
AddHoles: PUBLIC PROC [outline: Outline, holes: LIST OF Outline] RETURNS [withMoreHoles: Outline, success: BOOL] = {};
outline may be a simple closed outline, or may already contain holes. holes must all be simple closed outlines. The trajectories are extracted from the holes outlines and become holes of outline.
Holes can be removed using Delete.
WeldTraj: PUBLIC PROC [fixed: Traj, fixedEnd: TrajEnd, moving: Traj, movingEnd: TrajEnd] RETURNS [longer: Traj] = {};
Moves moving so that the specified end moving coincides with the specified end fixed. In other words, moving is translated.
ExtractTraj:
PUBLIC
PROC [original: Traj, start:
INT, len:
INT]
RETURNS [piece: Traj] = {
First we pick out the desired subsection. Then, Rosary will call CopyEachSegment, which in turn maps CopySegmentAndBuild to each of the desired elements. The copies are made and are passed to procedure q of Rosary, which assembles them into a new Rosary.
originalSegments: Rosary.ROSARY ← original.segments;
desiredSegments: Rosary.Segment ← [originalSegments, start, len];
extractedSegments: Rosary.ROSARY;
originalJoints: Rosary.ROSARY ← original.joints;
desiredJoints: Rosary.Segment ← [originalJoints, start, len];
extractedJoints: Rosary.ROSARY;
CopyEachSegment:
PROC[q:
PROC[item: Rosary.Item, repeat:
INT ← 1]] = {
CopySegmentAndBuild:
PROC [item: Rosary.Item]
RETURNS [quit:
BOOLEAN ←
FALSE] = {
copy, oldSeg: Segment;
oldSeg ← NARROW[item];
copy ← GGSegment.CopySegment[oldSeg];
q[copy, 1];
};
[] ← Rosary.Map[desiredSegments, CopySegmentAndBuild];
};
CopyEachJoint:
PROC[q:
PROC[item: Rosary.Item, repeat:
INT ← 1]] = {
CopyJointAndBuild:
PROC [item: Rosary.Item]
RETURNS [quit:
BOOLEAN ←
FALSE] = {
copy, oldJoint: Joint;
oldJoint ← NARROW[item];
copy ← CopyJointSelectedFalse[oldJoint];
q[copy, 1];
};
[] ← Rosary.Map[desiredJoints, CopyJointAndBuild];
};
extractedSegments ← Rosary.FromProcProc[CopyEachSegment];
extractedJoints ← Rosary.FromProcProc[CopyEachJoint];
piece ←
NEW[TrajObj ← [fence, len, extractedSegments, extractedJoints,
NIL,
NIL, NIL, original.visibleJoints, original.strokeJoint ]];
I must create a new outline and copy the extrapoints as well.
piece.boundBox ← GGBoundBox.BoundBoxOfTraj[piece];
};
CopyTraj:
PUBLIC
PROC [original: Traj]
RETURNS [copy: Traj] = {
Rosary will call CopyEachSegment, which in turn maps CopySegmentAndBuild to each of the desired elements. The copies are made and are passed to procedure q of Rosary, which assembles them into a new Rosary.
originalSegments: Rosary.ROSARY ← original.segments;
desiredSegments: Rosary.Segment ← [originalSegments, 0, HiSegment[original]];
extractedSegments: Rosary.ROSARY;
originalJoints: Rosary.ROSARY ← original.joints;
desiredJoints: Rosary.Segment ← [originalJoints, 0, HiJoint[original]];
extractedJoints: Rosary.ROSARY;
CopyEachSegment:
PROC[q:
PROC[item: Rosary.Item, repeat:
INT ← 1]] = {
CopySegmentAndBuild:
PROC [item: Rosary.Item]
RETURNS [quit:
BOOLEAN ←
FALSE] = {
copy, oldSeg: Segment;
oldSeg ← NARROW[item];
copy ← GGSegment.CopySegment[oldSeg];
q[copy, 1];
};
[] ← Rosary.Map[desiredSegments, CopySegmentAndBuild];
};
CopyEachJoint:
PROC[q:
PROC[item: Rosary.Item, repeat:
INT ← 1]] = {
CopyJointAndBuild:
PROC [item: Rosary.Item]
RETURNS [quit:
BOOLEAN ←
FALSE] = {
copy, oldJoint: Joint;
oldJoint ← NARROW[item];
copy ← CopyJointSelectedFalse[oldJoint];
q[copy, 1];
};
[] ← Rosary.Map[desiredJoints, CopyJointAndBuild];
};
extractedSegments ← Rosary.FromProcProc[CopyEachSegment];
extractedJoints ← Rosary.FromProcProc[CopyEachJoint];
copy ←
NEW[TrajObj ← [original.role, original.segCount, extractedSegments, extractedJoints,
NIL,
NIL,
NIL, original
.visibleJoints
, original
.strokeJoint ]];
I must copy the extrapoints as well.
copy.boundBox ← GGBoundBox.BoundBoxOfTraj[original];
};
Cluster Operations
MakeCluster: PUBLIC PROC [entities: LIST OF REF ANY] RETURNS [newCluster: Cluster];
BreakCluster: PUBLIC PROC [oldCluster: Cluster] RETURNS [entities: LIST OF REF ANY];
Scene Operations
CreateScene:
PUBLIC PROC []
RETURNS [scene: Scene] = {
scene ← NEW[SceneObj ← [entities: NIL]];
};
AddOutline:
PUBLIC PROC [scene: Scene, outline: Outline, priority:
INT ← -1] = {
Add the given outline to the scene with the given priority (the lower the priority number, the closer the outline is to the front. If priority is -1, the outline will be the rear-most entity. If an entity with the given priority already exists, all priority numbers greater than or equal to it will be increased by one.
For now, priority is ignored.
scene.entities ← CONS[outline, scene.entities];
};
AddCluster: PUBLIC PROC [scene: Scene, cluster: Cluster, priority: INT ← -1] = {
Like AddOutline, but adds a cluster.
};
DeleteOutline:
PUBLIC PROC [scene: Scene, outline: Outline] = {
scene.entities ← GGUtility.DeleteEntityFromList[outline, scene.entities];
};
Browsing the Scene Hierarchy -- Generators
TopLevelEntities:
PUBLIC PROC [scene: Scene]
RETURNS [entityGenerator: EntityGenerator] = {
Generates all of the outlines and clusters in the scene.
list: LIST OF REF ANY ← scene.entities;
entityGenerator ←
NEW[EntityGeneratorObj ← [
list: list
]];
};
EntitiesInCluster:
PUBLIC
PROC [cluster: Cluster]
RETURNS [entityGenerator: EntityGenerator] = {
Generates all of the outlines and clusters directly under the cluster.
list: LIST OF REF ANY ← cluster.children;
entityGenerator ←
NEW[EntityGeneratorObj ← [
list: list
]];
};
NextEntity:
PUBLIC PROC [g: EntityGenerator]
RETURNS [next: REF ANY] = {
IF g.list = NIL THEN RETURN[NIL]
ELSE {
next ← g.list.first;
g.list ← g.list.rest;
};
};
TrajsInScene:
PUBLIC
PROC [scene: Scene]
RETURNS [trajGen: TrajGenerator] = {
Generates all of the trajectories in the scene, whereever they occur.
list: LIST OF Traj ← ListTrajsInScene[scene];
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
]];
};
HasHoles:
PUBLIC
PROC [outline: Outline]
RETURNS [
BOOL] = {
RETURN[outline.children.rest # NIL];
};
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
]];
};
FenceOfOutline:
PUBLIC PROC [outline: Outline]
RETURNS [fence: Traj] = {
Returns the unique fence trajectory of outline.
fence ← outline.children.first;
};
NextTraj:
PUBLIC PROC [g: TrajGenerator]
RETURNS [next: Traj] = {
IF g.list = NIL THEN RETURN[NIL]
ELSE {
next ← g.list.first;
g.list ← g.list.rest;
};
};
SegmentsInTraj:
PUBLIC
PROC [traj: Traj]
RETURNS [segGen: SegmentGenerator] = {
segGen ←
NEW[SegmentGeneratorObj ← [
traj: traj,
toGo: traj.segCount,
index: 0,
nextParts: NIL
]];
SegmentsInJointPair:
PUBLIC
PROC [traj: Traj, start:
NAT, end:
INT]
RETURNS [segGen: SegmentGenerator] = {
toGo: NAT;
IF start > HiJoint[traj] OR end > HiJoint[traj] OR end < -1 THEN ERROR;
IF traj.role = open
THEN {
IF end < start THEN toGo ← 0
ELSE toGo ← end - start;
}
ELSE {
IF start = end THEN toGo ← 0
ELSE IF start < end THEN toGo ← end - start
ELSE toGo ← traj.segCount - start + end;
};
segGen ←
NEW[SegmentGeneratorObj ← [
traj: traj,
toGo: toGo,
index: start,
nextParts: NIL
]];
};
SegmentsInSequence:
PUBLIC
PROC [seq: Sequence]
RETURNS [segGen: SegmentGenerator] = {
start, end: NAT;
IF seq.parts = NIL THEN ERROR;
IF seq.all THEN RETURN[SegmentsInTraj[seq.traj]];
start ← seq.parts.first.start;
end ← seq.parts.first.end;
segGen ← SegmentsInJointPair[seq.traj, start, end];
segGen.nextParts ← seq.parts.rest;
};
InMODRegion:
PUBLIC PROC [test:
NAT, start, end, mod:
NAT]
RETURNS [
BOOL] = {
IF start = end THEN RETURN [test = start];
IF start < end THEN RETURN [test IN [start..end]];
RETURN [test IN [start..mod) OR test IN [0..end]];
};
PreviousSegment:
PUBLIC
PROC [traj: Traj, segNum:
NAT]
RETURNS [prev: Segment] = {
IF traj.role = open
THEN {
IF segNum = 0 THEN RETURN[NIL]
ELSE prev ← FetchSegment[traj, segNum - 1];
}
ELSE {
prev ← FetchSegment[traj, (segNum-1+traj.segCount) MOD traj.segCount];
};
};
PreviousSegmentNum:
PUBLIC PROC [traj: Traj, segNum:
NAT]
RETURNS [prevNum:
NAT] = {
IF traj.role = open
THEN {
IF segNum = 0 THEN ERROR
ELSE prevNum ← segNum - 1;
}
ELSE {
prevNum ← (segNum-1+traj.segCount) MOD traj.segCount;
};
};
FollowingSegmentNum:
PUBLIC PROC [traj: Traj, segNum:
NAT]
RETURNS [followNum:
NAT] = {
IF traj.role = open
THEN {
IF segNum = HiSegment[traj] THEN ERROR
ELSE followNum ← segNum + 1;
}
ELSE {
followNum ← (segNum+1) MOD traj.segCount;
};
};
NextSegment:
PUBLIC
PROC [segGen: SegmentGenerator]
RETURNS [next: Segment] = {
start, end: NAT;
toGo: NAT;
UNTIL segGen.toGo > 0
DO
IF segGen.nextParts = NIL THEN RETURN[NIL];
start ← segGen.nextParts.first.start;
end ← segGen.nextParts.first.end;
segGen.nextParts ← segGen.nextParts.rest;
IF segGen.traj.role = open
THEN {
IF end < start THEN toGo ← 0
ELSE toGo ← end - start;
}
ELSE {
IF start = end THEN toGo ← 0
ELSE IF start < end THEN toGo ← end - start
ELSE toGo ← segGen.traj.segCount - end + start - 2;
};
segGen.toGo ← toGo;
segGen.index ← start;
ENDLOOP;
next ← FetchSegment[segGen.traj, segGen.index];
segGen.toGo ← segGen.toGo - 1;
IF segGen.traj.role = open
THEN {
segGen.index ← segGen.index + 1;
}
ELSE {
segGen.index ← (segGen.index + 1) MOD segGen.traj.segCount;
};
};
SegAndIndex: TYPE = GGObjects.SegAndIndex;
NextSegmentAndIndex:
PUBLIC
PROC [segGen: SegmentGenerator]
RETURNS [next: SegAndIndex] = {
start, end: NAT;
toGo: NAT;
UNTIL segGen.toGo > 0
DO
IF segGen.nextParts = NIL THEN RETURN[[NIL, 999]];
start ← segGen.nextParts.first.start;
end ← segGen.nextParts.first.end;
segGen.nextParts ← segGen.nextParts.rest;
IF segGen.traj.role = open
THEN {
IF end < start THEN toGo ← 0
ELSE toGo ← end - start;
}
ELSE {
IF start = end THEN toGo ← 0
ELSE IF start < end THEN toGo ← end - start
ELSE toGo ← segGen.traj.segCount - end + start - 2;
};
segGen.toGo ← toGo;
segGen.index ← start;
ENDLOOP;
next.seg ← FetchSegment[segGen.traj, segGen.index];
next.index ← segGen.index;
segGen.toGo ← segGen.toGo - 1;
IF segGen.traj.role = open
THEN {
segGen.index ← segGen.index + 1;
}
ELSE {
segGen.index ← (segGen.index + 1) MOD segGen.traj.segCount;
};
};
JointsInJointPair:
PUBLIC
PROC [traj: Traj, start:
NAT, end:
INT]
RETURNS [jointGen: JointGenerator] = {
toGo: NAT;
SELECT traj.role FROM
open => {
IF end < start THEN toGo ← 0
ELSE toGo ← end - start + 1;
};
fence, hole => {
IF start = end THEN toGo ← 1
ELSE IF start < end THEN toGo ← end - start + 1
ELSE toGo ← traj.segCount - start + end + 1;
};
ENDCASE => ERROR;
jointGen ←
NEW[JointGeneratorObj ← [
traj: traj,
toGo: toGo,
index: start]];
JointsInSequence:
PUBLIC
PROC [seq: Sequence]
RETURNS [jointGen: JointGenerator] = {
start, end: NAT;
IF seq.parts =
NIL
THEN {
jointGen ←
NEW[JointGeneratorObj ← [
traj: seq.traj,
toGo: 0,
index: 0]];
RETURN;
};
IF seq.all THEN RETURN[JointsInTraj[seq.traj]];
start ← seq.parts.first.start;
end ← seq.parts.first.end;
jointGen ← JointsInJointPair[seq.traj, start, end];
jointGen.nextParts ← seq.parts.rest;
};
JointsInTraj:
PUBLIC
PROC [traj: Traj]
RETURNS [jointGen: JointGenerator] = {
start, end: NAT;
start ← 0;
end ← GGObjects.HiJoint[traj];
jointGen ← JointsInJointPair[traj, start, end];
jointGen.nextParts ← NIL;
};
NextJoint:
PUBLIC PROC [jointGen: JointGenerator]
RETURNS [next:
INT] = {
start, end: NAT;
toGo: NAT;
UNTIL jointGen.toGo > 0
DO
IF jointGen.nextParts = NIL THEN RETURN[-1];
start ← jointGen.nextParts.first.start;
end ← jointGen.nextParts.first.end;
jointGen.nextParts ← jointGen.nextParts.rest;
IF jointGen.traj.role = open
THEN {
IF end < start THEN toGo ← 0
ELSE toGo ← end - start + 1;
}
ELSE {
IF start = end THEN toGo ← 1
ELSE IF start < end THEN toGo ← end - start + 1
ELSE toGo ← jointGen.traj.segCount - end + start - 1;
};
jointGen.toGo ← toGo;
jointGen.index ← start;
ENDLOOP;
next ← jointGen.index;
jointGen.toGo ← jointGen.toGo - 1;
IF jointGen.traj.role = open
THEN {
jointGen.index ← jointGen.index + 1;
}
ELSE {
jointGen.index ← (jointGen.index + 1) MOD jointGen.traj.segCount;
};
};
BoundBoxesInScene:
PUBLIC PROC [scene: Scene]
RETURNS [bBoxGen: BoundBoxGenerator] = {
Generates all of the boundBoxes in the scene, whereever they occur.
list: LIST OF BoundBox ← ListBoxesInScene[scene];
bBoxGen ←
NEW[BoundBoxGeneratorObj ← [
list: list
]];
};
NextBox:
PUBLIC PROC [g: BoundBoxGenerator]
RETURNS [next: BoundBox] = {
IF g.list = NIL THEN RETURN[NIL]
ELSE {
next ← g.list.first;
g.list ← g.list.rest;
};
};
Utility Routines
AppendTrajs:
PUBLIC
PROC [l1, l2:
LIST
OF Traj]
RETURNS [result:
LIST
OF Traj] = {
Destructive append.
z: LIST OF Traj ← l1;
IF z = NIL THEN RETURN[l2];
UNTIL z.rest = NIL DO z ← z.rest; ENDLOOP;
z.rest ← l2;
RETURN[l1];
};
AppendBoxes:
PUBLIC
PROC [l1, l2:
LIST
OF BoundBox]
RETURNS [result:
LIST
OF BoundBox] = {
Destructive append.
z: LIST OF BoundBox ← l1;
IF z = NIL THEN RETURN[l2];
UNTIL z.rest = NIL DO z ← z.rest; ENDLOOP;
z.rest ← l2;
RETURN[l1];
};
ListBoxesInScene:
PUBLIC
PROC [scene: Scene]
RETURNS [allBoxes:
LIST
OF BoundBox] = {
allBoxes ← NIL;
FOR entities:
LIST
OF
REF
ANY ← scene.entities, entities.rest
UNTIL entities =
NIL
DO
WITH entities.first SELECT FROM
cluster: Cluster => {
allBoxes ← AppendBoxes[ListBoxesInCluster[cluster], allBoxes];
};
outline: Outline => {
FOR trajs:
LIST
OF Traj ← outline.children, trajs.rest
UNTIL trajs =
NIL
DO
allBoxes ← CONS[trajs.first.boundBox, allBoxes];
ENDLOOP;
};
ENDCASE => ERROR;
ENDLOOP;
};
ListBoxesInCluster:
PUBLIC
PROC [cluster: Cluster]
RETURNS [allBoxes:
LIST
OF BoundBox] = {
allBoxes ← NIL;
FOR children:
LIST
OF
REF
ANY ← cluster.children, children.rest
UNTIL children =
NIL
DO
WITH children.first SELECT FROM
outline: Outline => {
FOR trajs:
LIST
OF Traj ← outline.children, trajs.rest
UNTIL trajs =
NIL
DO
allBoxes ← CONS[trajs.first.boundBox, allBoxes];
ENDLOOP;
};
cluster: Cluster => {
allBoxes ← AppendBoxes[ListBoxesInCluster[cluster], allBoxes];
};
ENDCASE => ERROR;
ENDLOOP;
};
ListTrajsInScene:
PUBLIC PROC [scene: Scene]
RETURNS [allTrajs:
LIST
OF Traj] = {
allTrajs ← NIL;
FOR entities:
LIST
OF
REF
ANY ← scene.entities, entities.rest
UNTIL entities =
NIL
DO
WITH entities.first SELECT FROM
cluster: Cluster => {
allTrajs ← AppendTrajs[ListTrajsInCluster[cluster], allTrajs];
};
outline: Outline => {
FOR trajs:
LIST
OF Traj ← outline.children, trajs.rest
UNTIL trajs =
NIL
DO
allTrajs ← CONS[trajs.first, allTrajs];
ENDLOOP;
};
ENDCASE => ERROR;
ENDLOOP;
};
ListTrajsInOutline:
PROC [outline: Outline]
RETURNS [trajList:
LIST
OF Traj] = {
trajList ← outline.children;
};
ListHolesOfOutline:
PROC [outline: Outline]
RETURNS [trajList:
LIST
OF Traj] = {
trajList ← outline.children.rest;
};
ListTrajsInCluster:
PUBLIC
PROC [cluster: Cluster]
RETURNS [allTrajs:
LIST
OF Traj] = {
allTrajs ← NIL;
FOR children:
LIST
OF
REF
ANY ← cluster.children, children.rest
UNTIL children =
NIL
DO
WITH children.first SELECT FROM
outline: Outline => {
FOR trajs:
LIST
OF Traj ← outline.children, trajs.rest
UNTIL trajs =
NIL
DO
allTrajs ← CONS[trajs.first, allTrajs];
ENDLOOP;
};
cluster: Cluster => {
allTrajs ← AppendTrajs[ListTrajsInCluster[cluster], allTrajs];
};
ENDCASE => ERROR;
ENDLOOP;
};
ListSegmentsInTraj:
PROC [traj: Traj]
RETURNS [segList:
LIST
OF Segment] = {
segList ← NIL;
FOR i:
NAT
DECREASING
IN [0..HiSegment[traj]]
DO
segList ← CONS[FetchSegment[traj, i], segList];
ENDLOOP;
};
Browsing the Scene Hierarchy -- Up and Down levels
OutlineOfTraj:
PUBLIC PROC [traj: Traj]
RETURNS [outline: Outline] = {
Finds the unique outline to which traj belongs.
outline ← traj.outline;
};
Finding and Moving Joints
HiSegment:
PUBLIC
PROC [traj: Traj]
RETURNS [highestIndex:
NAT] = {
highestIndex ← traj.segCount - 1;
};
FetchSegment:
PUBLIC PROC [traj: Traj, index:
NAT]
RETURNS [seg: Segment] = {
seg ← NARROW[Rosary.Fetch[traj.segments, index]];
};
HiJoint:
PUBLIC PROC [traj: Traj]
RETURNS [highestIndex:
NAT] = {
SELECT traj.role FROM
open => highestIndex ← traj.segCount;
fence, hole => highestIndex ← traj.segCount -1;
ENDCASE => ERROR;
};
FollowingJoint:
PUBLIC
PROC [traj: Traj, index:
NAT]
RETURNS [nextIndex:
NAT] = {
SELECT traj.role FROM
open => {
IF index = traj.segCount THEN ERROR;
nextIndex ← index + 1;
};
fence, hole => nextIndex ← (index + 1) MOD traj.segCount;
ENDCASE => ERROR;
};
FetchJointPos:
PUBLIC
PROC [traj: Traj, index:
NAT]
RETURNS [point: Point] = {
joint: Joint;
IF index < 0 OR index > HiJoint[traj] THEN ERROR;
joint ← NARROW[Rosary.Fetch[traj.joints, index]];
point ← joint.point
};
LastJointPos:
PUBLIC
PROC [traj: Traj]
RETURNS [point: Point] = {
joint: Joint ← NARROW[Rosary.Fetch[traj.joints, traj.segCount]];
point ← joint.point
};
SetJointPos:
PUBLIC
PROC [traj: Traj, index:
NAT, newPos: Point] = {
Moves the given joint and tells all interested segments that it has moved.
joint: Joint ← NARROW[Rosary.Fetch[traj.joints, index]];
segLeft, segRight: Segment;
joint.point ← newPos;
IF index > 0
THEN {
segLeft ← FetchSegment[traj, index-1];
segLeft.hi ← newPos;
};
IF index < traj.segCount
THEN {
segRight ← FetchSegment[traj, index];
segRight.lo ← newPos;
};
};
IsEndJoint:
PUBLIC
PROC [traj: Traj, index:
NAT]
RETURNS [
BOOL] = {
SELECT traj.role FROM
open => RETURN[index = 0 OR index = traj.segCount];
fence, hole => RETURN[FALSE];
ENDCASE => ERROR;
};
CopyJointSelectedFalse:
PROC [joint: Joint]
RETURNS [copy: Joint] = {
copy ←
NEW[JointObj ← [
point: joint.point,
selected: [
normal: FALSE,
copy: FALSE,
hot: FALSE,
active: FALSE
]
]];
};
Use sparingly
FetchJoint:
PUBLIC PROC [traj: Traj, index:
NAT]
RETURNS [joint: Joint] = {
joint ← NARROW[Rosary.Fetch[traj.joints, index]];
};
IndexOfJoint:
PUBLIC
PROC [joint: Joint, traj: Traj]
RETURNS [index:
INT] = {
Returns -1 if it doesn't exist.
thisJoint: Joint;
FOR i:
NAT
IN [0..HiJoint[traj]]
DO
thisJoint ← FetchJoint[traj, i];
IF thisJoint = joint THEN RETURN[i];
ENDLOOP;
RETURN[-1];
IndexOfSegment:
PUBLIC
PROC [segment: Segment, traj: Traj]
RETURNS [index:
INT] = {
Returns -1 if it doesn't exist.
thisSeg: Segment;
FOR i:
NAT
IN [0..HiSegment[traj]]
DO
thisSeg ← FetchSegment[traj, i];
IF thisSeg = segment THEN RETURN[i];
ENDLOOP;
RETURN[-1];
};
CreateSimpleSequence:
PUBLIC
PROC [traj: Traj, start, end:
NAT]
RETURNS [seq: Sequence] = {
seq ←
NEW[SequenceObj ← [
traj: traj,
all: FALSE,
parts: LIST[[start, end]],
boundBox: GGBoundBox.CopyBoundBox[traj.boundBox]
]];
};
CreateEmptySequence:
PUBLIC
PROC [traj: Traj]
RETURNS [seq: Sequence] = {
seq ←
NEW[SequenceObj ← [
traj: traj,
all: FALSE,
parts: NIL,
boundBox: NIL]];
};
CreateCompleteSequence:
PUBLIC
PROC [traj: Traj]
RETURNS [seq: Sequence] = {
Returns a sequence which represents the entire trajectory.
seq ←
NEW[SequenceObj ← [
traj: traj,
all: TRUE,
parts: LIST[[0,0]],
boundBox: GGBoundBox.CopyBoundBox[traj.boundBox] ]];
};
CreateSequenceFromJoint:
PUBLIC
PROC [traj: Traj, jointNum:
NAT]
RETURNS [seq: Sequence] = {
seq ← CreateSimpleSequence[traj, jointNum, jointNum];
};
CreateSequenceFromSegment:
PUBLIC
PROC [traj: Traj, segNum:
NAT]
RETURNS [seq: Sequence] = {
nextJointNum: NAT ← FollowingJoint[traj, segNum];
seq ← CreateSimpleSequence[traj, segNum, nextJointNum];
};
AddPart:
PUBLIC PROC [seq: Sequence, start, end:
NAT] = {
Destructive append. Adds a new Subsequence to this Sequence. Assumes that [start..end] is disjoint from all previous parts.
z: LIST OF JointPair ← seq.parts;
IF z =
NIL
THEN {
seq.parts ← CONS[[start, end], NIL];
RETURN;
};
UNTIL z.rest = NIL DO z ← z.rest; ENDLOOP;
z.rest ← CONS[[start, end], NIL];
};
BitArray: TYPE = REF BitArrayObj;
BitArrayObj:
TYPE =
RECORD [
seq: SEQUENCE len: NAT OF BOOL];
SequencesOverlap:
PUBLIC
PROC [seq1, seq2: Sequence]
RETURNS [
BOOL] = {
segBits1, segBits2, jointBits1, jointBits2: BitArray;
segBits1 ← NEW[BitArrayObj[seq1.traj.segCount]];
segBits2 ← NEW[BitArrayObj[seq2.traj.segCount]];
jointBits1 ← NEW[BitArrayObj[seq1.traj.segCount + 1]];
jointBits2 ←
NEW[BitArrayObj[seq2.traj.segCount + 1]];
Make the bitmaps reflect the sequences
InitializeCombine[seq1, seq2, jointBits1, jointBits2, segBits1, segBits2];
Compare the segment bit vectors.
FOR i:
NAT
IN [0..GGObjects.HiSegment[seq1.traj]]
DO
IF segBits1[i] AND segBits2[i] THEN RETURN[TRUE];
ENDLOOP;
Compare the joint bit vectors.
FOR i:
NAT
IN [0..GGObjects.HiJoint[seq1.traj]]
DO
IF jointBits1[i] AND jointBits2[i] THEN RETURN[TRUE];
ENDLOOP;
RETURN[FALSE];
};
CombineSequences:
PUBLIC
PROC [new, old: Sequence]
RETURNS [union: Sequence, newMinusOld: Sequence] = {
IF new.traj # old.traj THEN ERROR;
IF new.traj.role = open THEN [union, newMinusOld] ← CombineSequencesOpen[new, old]
ELSE {
[union, newMinusOld] ← CombineSequencesOpen[new, old];
IF new.all THEN union ← new; -- kludge until CombineSequencesClosed is done
IF old.all THEN union ← old;
};
};
InitializeCombine:
PROC [new, old: Sequence, newJointBits, oldJointBits, newSegmentBits, oldSegmentBits: BitArray] = {
segGen: SegmentGenerator;
jointGen: JointGenerator;
Initialize
FOR i:
NAT
IN [0..GGObjects.HiSegment[new.traj]]
DO
newSegmentBits[i] ← FALSE;
oldSegmentBits[i] ← FALSE;
ENDLOOP;
FOR i:
NAT
IN [0..GGObjects.HiJoint[new.traj]]
DO
newJointBits[i] ← FALSE;
oldJointBits[i] ← FALSE;
segGen ← SegmentsInSequence[new];
FOR next: GGObjects.SegAndIndex ← NextSegmentAndIndex[segGen], NextSegmentAndIndex[segGen]
UNTIL next.seg =
NIL
DO
newSegmentBits[next.index] ← TRUE;
ENDLOOP;
jointGen ← JointsInSequence[new];
FOR index:
INT ← NextJoint[jointGen], NextJoint[jointGen]
UNTIL index = -1
DO
newJointBits[index] ← TRUE;
segGen ← SegmentsInSequence[old];
FOR next: GGObjects.SegAndIndex ← NextSegmentAndIndex[segGen], NextSegmentAndIndex[segGen]
UNTIL next.seg =
NIL
DO
oldSegmentBits[next.index] ← TRUE;
ENDLOOP;
jointGen ← JointsInSequence[old];
FOR index:
INT ← NextJoint[jointGen], NextJoint[jointGen]
UNTIL index = -1
DO
oldJointBits[index] ← TRUE;
ENDLOOP;
CombineSequencesOpen:
PROC [new, old: Sequence]
RETURNS [union: Sequence, newMinusOld: Sequence] = {
unionJointBit, diffJointBit, unionSegmentBit, diffSegmentBit, lastSegmentBit, lastJointBit: BOOL;
newJointBits, oldJointBits, newSegmentBits, oldSegmentBits: BitArray;
start, end: NAT;
newSegmentBits ← NEW[BitArrayObj[new.traj.segCount]];
oldSegmentBits ← NEW[BitArrayObj[new.traj.segCount]];
newJointBits ← NEW[BitArrayObj[new.traj.segCount + 1]];
oldJointBits ←
NEW[BitArrayObj[new.traj.segCount + 1]];
Make the bitmaps reflect the sequences
InitializeCombine[new, old, newJointBits, oldJointBits, newSegmentBits, oldSegmentBits];
Combine the segment bit vectors.
FOR i:
NAT
IN [0..GGObjects.HiSegment[new.traj]]
DO
unionSegmentBit ← newSegmentBits[i] OR oldSegmentBits[i];
diffSegmentBit ← newSegmentBits[i] AND NOT oldSegmentBits[i];
newSegmentBits[i] ← unionSegmentBit;
oldSegmentBits[i] ← diffSegmentBit;
ENDLOOP;
Combine the joint bit vectors.
FOR i:
NAT
IN [0..GGObjects.HiJoint[new.traj]]
DO
unionJointBit ← newJointBits[i] OR oldJointBits[i];
diffJointBit ← newJointBits[i] AND NOT oldJointBits[i];
newJointBits[i] ← unionJointBit;
oldJointBits[i] ← diffJointBit;
ENDLOOP;
Convert union back to sequences.
union ← GGObjects.CreateEmptySequence[new.traj];
lastSegmentBit ← FALSE;
FOR i:
NAT
IN [0..GGObjects.HiSegment[new.traj]]
DO
IF
NOT lastSegmentBit
AND newJointBits[i]
THEN { -- The start of a new part
start ← i;
};
IF lastSegmentBit AND NOT newJointBits[i] THEN ERROR;
lastJointBit ← newJointBits[i];
IF lastJointBit AND
NOT newSegmentBits[i] THEN { -- the end of the current part
end ← i;
GGObjects.AddPart[union, start, end];
};
lastSegmentBit ← newSegmentBits[i];
REPEAT
FINISHED => {
IF
NOT lastSegmentBit
AND newJointBits[GGObjects.HiSegment[new.traj] + 1]
THEN {
The last joint is a lone part.
start ← end ← GGObjects.HiSegment[new.traj] + 1;
GGObjects.AddPart[union, start, end];
}
ELSE
IF lastSegmentBit
THEN {
end ← GGObjects.HiSegment[new.traj] + 1;
GGObjects.AddPart[union, start, end];
};
};
ENDLOOP;
Convert diff back to sequences.
newMinusOld ← GGObjects.CreateEmptySequence[new.traj];
lastSegmentBit ← FALSE;
FOR i:
NAT
IN [0..GGObjects.HiSegment[new.traj]]
DO
IF
NOT lastSegmentBit
AND oldJointBits[i]
THEN { -- The start of a new part
start ← i;
};
IF lastSegmentBit
AND
NOT oldJointBits[i]
THEN {
It is time to kludge. The LIST OF JointPair data structure isn't rich enough to represent this situation. Pretend that lastSegmentBit was never true and close the currently open part.
end ← i-1;
GGObjects.AddPart[newMinusOld, start, end];
};
lastJointBit ← oldJointBits[i];
IF lastJointBit AND
NOT oldSegmentBits[i] THEN { -- the end of the current part
end ← i;
GGObjects.AddPart[newMinusOld, start, end];
};
IF NOT lastJointBit AND oldSegmentBits[i] THEN {
Another chance to kludge. (See kludge comment above). We pretend that this segment doesn't exist.
lastSegmentBit ← FALSE;
}
ELSE lastSegmentBit ← oldSegmentBits[i];
REPEAT
FINISHED => {
IF
NOT lastSegmentBit
AND oldJointBits[GGObjects.HiSegment[new.traj] + 1]
THEN {
The last joint is a lone part.
start ← end ← GGObjects.HiSegment[new.traj] + 1;
GGObjects.AddPart[newMinusOld, start, end];
}
ELSE
IF lastSegmentBit
AND
NOT oldJointBits[GGObjects.HiSegment[new.traj] + 1]
THEN {
One last kludge.
end ← GGObjects.HiSegment[new.traj];
GGObjects.AddPart[newMinusOld, start, end];
}
ELSE
IF lastSegmentBit
THEN {
end ← GGObjects.HiSegment[new.traj] + 1;
GGObjects.AddPart[newMinusOld, start, end];
};
};
ENDLOOP;
};
CombineSequencesClosed:
PROC [new, old: Sequence]
RETURNS [union: Sequence, newMinusOld: Sequence] = {
Not yet done.
unionJointBit, diffJointBit, unionSegmentBit, diffSegmentBit, lastSegmentBit, lastJointBit: BOOL;
newJointBits, oldJointBits, newSegmentBits, oldSegmentBits: BitArray;
start, end: NAT;
newSegmentBits ← NEW[BitArrayObj[new.traj.segCount]];
oldSegmentBits ← NEW[BitArrayObj[new.traj.segCount]];
newJointBits ← NEW[BitArrayObj[new.traj.segCount]];
oldJointBits ←
NEW[BitArrayObj[new.traj.segCount]];
Make the bitmaps reflect the sequences
InitializeCombine[new, old, newJointBits, oldJointBits, newSegmentBits, oldSegmentBits];
Combine the segment bit vectors.
FOR i:
NAT
IN [0..GGObjects.HiSegment[new.traj]]
DO
unionSegmentBit ← newSegmentBits[i] OR oldSegmentBits[i];
diffSegmentBit ← newSegmentBits[i] AND NOT oldSegmentBits[i];
newSegmentBits[i] ← unionSegmentBit;
oldSegmentBits[i] ← diffSegmentBit;
ENDLOOP;
Combine the joint bit vectors.
FOR i:
NAT
IN [0..GGObjects.HiJoint[new.traj]]
DO
unionJointBit ← newJointBits[i] OR oldJointBits[i];
diffJointBit ← newJointBits[i] AND NOT oldJointBits[i];
newJointBits[i] ← unionJointBit;
oldJointBits[i] ← diffJointBit;
ENDLOOP;
Convert union back to sequences (new = union, old = difference).
union ← GGObjects.CreateEmptySequence[new.traj];
lastSegmentBit ← newSegmentBits[HiSegment[new.traj]];
FOR i:
NAT
IN [0..GGObjects.HiSegment[new.traj]]
DO
IF
NOT lastSegmentBit
AND newJointBits[i]
THEN { -- The start of a new part
start ← i;
};
IF lastSegmentBit AND NOT newJointBits[i] THEN ERROR;
lastJointBit ← newJointBits[i];
IF lastJointBit AND
NOT newSegmentBits[i] THEN { -- the end of the current part
end ← i;
GGObjects.AddPart[union, start, end];
};
lastSegmentBit ← newSegmentBits[i];
REPEAT
FINISHED => {
IF
NOT lastSegmentBit
AND newJointBits[GGObjects.HiSegment[new.traj] + 1]
THEN {
The last joint is a lone part.
start ← end ← GGObjects.HiSegment[new.traj] + 1;
GGObjects.AddPart[union, start, end];
}
ELSE
IF lastSegmentBit
THEN {
end ← GGObjects.HiSegment[new.traj] + 1;
GGObjects.AddPart[union, start, end];
};
};
ENDLOOP;
Convert diff back to sequences.
newMinusOld ← GGObjects.CreateEmptySequence[new.traj];
lastSegmentBit ← FALSE;
FOR i:
NAT
IN [0..GGObjects.HiSegment[new.traj]]
DO
IF
NOT lastSegmentBit
AND oldJointBits[i]
THEN { -- The start of a new part
start ← i;
};
IF lastSegmentBit
AND
NOT oldJointBits[i]
THEN {
It is time to kludge. The LIST OF JointPair data structure isn't rich enough to represent this situation. Pretend that lastSegmentBit was never true and close the currently open part.
end ← i-1;
GGObjects.AddPart[newMinusOld, start, end];
};
lastJointBit ← oldJointBits[i];
IF lastJointBit AND
NOT oldSegmentBits[i] THEN { -- the end of the current part
end ← i;
GGObjects.AddPart[newMinusOld, start, end];
};
IF NOT lastJointBit AND oldSegmentBits[i] THEN {
Another chance to kludge. (See kludge comment above). We pretend that this segment doesn't exist.
lastSegmentBit ← FALSE;
}
ELSE lastSegmentBit ← oldSegmentBits[i];
REPEAT
FINISHED => {
IF
NOT lastSegmentBit
AND oldJointBits[GGObjects.HiSegment[new.traj] + 1]
THEN {
The last joint is a lone part.
start ← end ← GGObjects.HiSegment[new.traj] + 1;
GGObjects.AddPart[newMinusOld, start, end];
}
ELSE
IF lastSegmentBit
AND
NOT oldJointBits[GGObjects.HiSegment[new.traj] + 1]
THEN {
One last kludge.
end ← GGObjects.HiSegment[new.traj];
GGObjects.AddPart[newMinusOld, start, end];
}
ELSE
IF lastSegmentBit
THEN {
end ← GGObjects.HiSegment[new.traj] + 1;
GGObjects.AddPart[newMinusOld, start, end];
};
};
ENDLOOP;
};
IsEmptySequence:
PUBLIC PROC [seq: Sequence]
RETURNS [
BOOL] = {
RETURN[(NOT seq.all) AND seq.parts = NIL];
};
NextSequence:
PUBLIC
PROC [seqGen: SequenceGenerator]
RETURNS [seq: Sequence] = {
IF seqGen.list = NIL THEN RETURN[NIL];
seq ← seqGen.list.first;
seqGen.list ← seqGen.list.rest;
};
InJointRange:
PUBLIC
PROC [index:
NAT, seq: Sequence]
RETURNS [
BOOL] = {
Is the index mentioned in any of the ranges of seq?
IF seq.all THEN ERROR NotYetImplemented;
FOR partList:
LIST
OF JointPair ← seq.parts, partList.rest
UNTIL partList =
NIL
DO
IF InMODRegion[index, partList.first.start, partList.first.end, seq.traj.segCount] THEN RETURN[TRUE];
ENDLOOP;
RETURN[FALSE];
};
Transforming Trajectories
TransformTraj:
PUBLIC
PROC [traj: Traj, transform: ImagerTransformation.Transformation] = {
Individually translates each joint and control point of the trajectory.
seg: Segment;
joint: Joint;
FOR i:
NAT
IN [0..GGObjects.HiSegment[traj]]
DO
seg ← FetchSegment[traj, i];
TransformSegment[seg, transform];
ENDLOOP;
FOR i:
NAT
IN [0..GGObjects.HiJoint[traj]]
DO
joint ← NARROW[Rosary.Fetch[traj.joints, i]];
joint.point ← GGTransform.Transform[transform, joint.point];
ENDLOOP;
traj.boundBox ← GGBoundBox.BoundBoxOfTraj[traj];
};
TransformSegment:
PUBLIC
PROC [seg: Segment, transform: ImagerTransformation.Transformation] = {
seg.lo ← GGTransform.Transform[transform, seg.lo];
seg.hi ← GGTransform.Transform[transform, seg.hi];
seg.class.transform[transform, seg];
seg.class.boundBox[seg];
GGBoundBox.AdjustForJoints[seg.boundBox];
};
MoveEndPointSegment:
PROC [seg: Segment, lo:
BOOL, newPoint: Point] = {
IF lo THEN seg.lo ← newPoint
ELSE seg.hi ← newPoint;
seg.class.endpointMoved[seg, lo, newPoint];
seg.class.boundBox[seg];
GGBoundBox.AdjustForJoints[seg.boundBox];
};
TransformSequence:
PUBLIC
PROC [seq: Sequence, transform: ImagerTransformation.Transformation] = {
start, end: NAT;
IF seq.all THEN TransformTraj[seq.traj, transform]
ELSE {
FOR partList:
LIST
OF JointPair ← seq.parts, partList.rest
UNTIL partList =
NIL
DO
Individually translates each joint and control point of the subsequence of traj from start to end inclusive.
start ← partList.first.start;
end ← partList.first.end;
SELECT seq.traj.role FROM
open => TransformOpenSequence[seq.traj, start, end, transform];
fence, hole => TransformClosedSequence[seq.traj, start, end, transform];
ENDCASE => ERROR;
ENDLOOP;
seq.traj.boundBox ← GGBoundBox.BoundBoxOfTraj[seq.traj];
};
};
TransformOpenSequence:
PUBLIC
PROC [traj: Traj, start, end:
NAT, transform: ImagerTransformation.Transformation] = {
[start..end] is a range of jointCount joints. From jointCount-1 to jointCount+1 segments are affected, depending on how the joints are placed.
seg: Segment;
joint: Joint;
segGen: SegmentGenerator;
IF start > 0
THEN {
seg ← FetchSegment[traj, start-1];
MoveEndPointSegment[seg, FALSE, GGTransform.Transform[transform, seg.hi]];
};
segGen ← SegmentsInJointPair[traj, start, end];
FOR seg ← NextSegment[segGen], NextSegment[segGen]
UNTIL seg =
NIL
DO
TransformSegment[seg, transform];
ENDLOOP;
IF end < traj.segCount
THEN {
seg ← FetchSegment[traj, end];
MoveEndPointSegment[seg, TRUE, GGTransform.Transform[transform, seg.lo]];
};
FOR i:
NAT
IN [start..end]
DO
joint ← NARROW[Rosary.Fetch[traj.joints, i]];
joint.point ← GGTransform.Transform[transform, joint.point];
ENDLOOP;
};
TransformClosedSequence:
PUBLIC
PROC [traj: Traj, start, end:
NAT, transform: ImagerTransformation.Transformation] = {
[start..end] MOD segCount is a range of jointCount joints. jointCount+1 segments are affected.
seg: Segment;
joint: Joint;
segGen: SegmentGenerator;
fullCycle:
BOOL;
The first segment moves on one end only.
seg ← PreviousSegment[traj, start];
MoveEndPointSegment[seg,
FALSE, GGTransform.Transform[transform, seg.hi]];
The intermediate segments move both ends.
segGen ← SegmentsInJointPair[traj, start, end];
FOR seg ← NextSegment[segGen], NextSegment[segGen]
UNTIL seg =
NIL
DO
TransformSegment[seg, transform];
ENDLOOP;
The last segment moves one end only.
seg ← FetchSegment[traj, end];
MoveEndPointSegment[seg,
TRUE, GGTransform.Transform[transform, seg.lo]];
All of the relevant joints move.
fullCycle ← FALSE;
FOR i:
NAT ← start, (i+1)
MOD traj.segCount
UNTIL fullCycle
DO
joint ← NARROW[Rosary.Fetch[traj.joints, i]];
joint.point ← GGTransform.Transform[transform, joint.point];
IF i = end THEN fullCycle ← TRUE;
ENDLOOP;
};
TranslateSegment:
PUBLIC
PROC [seg: Segment, vector: Vector] = {
A convenience routine which does a TransformSegment, where transform is a simple translation.
transform: ImagerTransformation.Transformation;
transform ← ImagerTransformation.Translate[[vector[1], vector[2]]];
TransformSegment[seg, transform];
};
Segment Style
SetStrokeWidth:
PUBLIC
PROC [seq: Sequence, strokeWidth:
REAL] = {
segGen: SegmentGenerator;
segGen ← SegmentsInSequence[seq];
FOR seg: Segment ← GGObjects.NextSegment[segGen], GGObjects.NextSegment[segGen]
UNTIL seg =
NIL
DO
seg.strokeWidth ← strokeWidth;
ENDLOOP;
};
SetColor:
PUBLIC PROC [seq: Sequence, color: Imager.Color] = {
segGen: SegmentGenerator;
segGen ← SegmentsInSequence[seq];
FOR seg: Segment ← GGObjects.NextSegment[segGen], GGObjects.NextSegment[segGen]
UNTIL seg =
NIL
DO
seg.color ← color;
ENDLOOP;
};
END.