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: BOOLEANFALSE] = {
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: BOOLEANFALSE] = {
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: BOOLEANFALSE] = {
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: BOOLEANFALSE] = {
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;
ENDLOOP;
Set new Bits.
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;
ENDLOOP;
Set old Bits.
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.