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; 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; 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] = { diff: Vector; IF traj.segCount = 0 THEN ERROR NotYetImplemented; -- a single closed segment. 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]; }; ExtractTraj: PUBLIC PROC [original: Traj, start: INT, len: INT] RETURNS [piece: Traj] = { 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 ]]; piece.boundBox _ GGBoundBox.BoundBoxOfTraj[piece]; }; CopyTraj: PUBLIC PROC [original: Traj] RETURNS [copy: Traj] = { 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 ]]; copy.boundBox _ GGBoundBox.BoundBoxOfTraj[original]; }; CreateScene: PUBLIC PROC [] RETURNS [scene: Scene] = { scene _ NEW[SceneObj _ [entities: NIL]]; }; AddOutline: PUBLIC PROC [scene: Scene, outline: Outline, priority: INT _ -1] = { scene.entities _ CONS[outline, scene.entities]; }; DeleteOutline: PUBLIC PROC [scene: Scene, outline: Outline] = { scene.entities _ GGUtility.DeleteEntityFromList[outline, scene.entities]; }; TopLevelEntities: PUBLIC PROC [scene: Scene] RETURNS [entityGenerator: EntityGenerator] = { list: LIST OF REF ANY _ scene.entities; entityGenerator _ NEW[EntityGeneratorObj _ [ list: list ]]; }; EntitiesInCluster: PUBLIC PROC [cluster: Cluster] RETURNS [entityGenerator: EntityGenerator] = { 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] = { list: LIST OF Traj _ ListTrajsInScene[scene]; trajGen _ NEW[TrajGeneratorObj _ [ list: list ]]; }; TrajsInOutline: PUBLIC PROC [outline: Outline] RETURNS [trajGen: TrajGenerator] = { 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] = { list: LIST OF Traj _ outline.children.rest; trajGen _ NEW[TrajGeneratorObj _ [ list: list ]]; }; FenceOfOutline: PUBLIC PROC [outline: Outline] RETURNS [fence: Traj] = { 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] = { 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; }; }; AppendTrajs: PUBLIC PROC [l1, l2: LIST OF Traj] RETURNS [result: LIST OF Traj] = { 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] = { 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; }; OutlineOfTraj: PUBLIC PROC [traj: Traj] RETURNS [outline: Outline] = { outline _ traj.outline; }; 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] = { 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 ] ]]; }; 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] = { 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] = { 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] = { 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] = { 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]]; InitializeCombine[seq1, seq2, jointBits1, jointBits2, segBits1, segBits2]; FOR i: NAT IN [0..GGObjects.HiSegment[seq1.traj]] DO IF segBits1[i] AND segBits2[i] THEN RETURN[TRUE]; ENDLOOP; 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; 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; 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; 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]]; InitializeCombine[new, old, newJointBits, oldJointBits, newSegmentBits, oldSegmentBits]; 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; 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; 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 { 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; 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 { 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 { lastSegmentBit _ FALSE; } ELSE lastSegmentBit _ oldSegmentBits[i]; REPEAT FINISHED => { IF NOT lastSegmentBit AND oldJointBits[GGObjects.HiSegment[new.traj] + 1] THEN { start _ end _ GGObjects.HiSegment[new.traj] + 1; GGObjects.AddPart[newMinusOld, start, end]; } ELSE IF lastSegmentBit AND NOT oldJointBits[GGObjects.HiSegment[new.traj] + 1] THEN { 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] = { 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]]; InitializeCombine[new, old, newJointBits, oldJointBits, newSegmentBits, oldSegmentBits]; 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; 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; 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 { 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; 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 { 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 { lastSegmentBit _ FALSE; } ELSE lastSegmentBit _ oldSegmentBits[i]; REPEAT FINISHED => { IF NOT lastSegmentBit AND oldJointBits[GGObjects.HiSegment[new.traj] + 1] THEN { start _ end _ GGObjects.HiSegment[new.traj] + 1; GGObjects.AddPart[newMinusOld, start, end]; } ELSE IF lastSegmentBit AND NOT oldJointBits[GGObjects.HiSegment[new.traj] + 1] THEN { 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] = { 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]; }; TransformTraj: PUBLIC PROC [traj: Traj, transform: ImagerTransformation.Transformation] = { 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 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] = { 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] = { seg: Segment; joint: Joint; segGen: SegmentGenerator; fullCycle: BOOL; seg _ PreviousSegment[traj, start]; 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; seg _ FetchSegment[traj, end]; MoveEndPointSegment[seg, TRUE, GGTransform.Transform[transform, seg.lo]]; 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] = { transform: ImagerTransformation.Transformation; transform _ ImagerTransformation.Translate[[vector[1], vector[2]]]; TransformSegment[seg, transform]; }; 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. &GGObjectsImpl.mesa Copyright c 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 Creating Trajectories and Outlines Moves seg so that the specified end of seg coincides with the specified end of traj. In other words, seg is translated. 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. For now, just translate into place and assume size is ok. 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. 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. I must create a new outline and copy the extrapoints as well. 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. I must copy the extrapoints as well. 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 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. AddCluster: PUBLIC PROC [scene: Scene, cluster: Cluster, priority: INT _ -1] = { Like AddOutline, but adds a cluster. }; Browsing the Scene Hierarchy -- Generators Generates all of the outlines and clusters in the scene. Generates all of the outlines and clusters directly under the cluster. Generates all of the trajectories in the scene, whereever they occur. Generates the fence and all of the holes of outline. Generates all of the holes of outline. Returns the unique fence trajectory of outline. Generates all of the boundBoxes in the scene, whereever they occur. Utility Routines Destructive append. Destructive append. Browsing the Scene Hierarchy -- Up and Down levels Finds the unique outline to which traj belongs. Finding and Moving Joints Moves the given joint and tells all interested segments that it has moved. Use sparingly Returns -1 if it doesn't exist. Returns -1 if it doesn't exist. Returns a sequence which represents the entire trajectory. Destructive append. Adds a new Subsequence to this Sequence. Assumes that [start..end] is disjoint from all previous parts. Make the bitmaps reflect the sequences Compare the segment bit vectors. Compare the joint bit vectors. Initialize Set new Bits. Set old Bits. Make the bitmaps reflect the sequences Combine the segment bit vectors. Combine the joint bit vectors. Convert union back to sequences. The last joint is a lone part. Convert diff back to sequences. 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. Another chance to kludge. (See kludge comment above). We pretend that this segment doesn't exist. The last joint is a lone part. One last kludge. Not yet done. Make the bitmaps reflect the sequences Combine the segment bit vectors. Combine the joint bit vectors. Convert union back to sequences (new = union, old = difference). The last joint is a lone part. Convert diff back to sequences. 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. Another chance to kludge. (See kludge comment above). We pretend that this segment doesn't exist. The last joint is a lone part. One last kludge. Is the index mentioned in any of the ranges of seq? Transforming Trajectories Individually translates each joint and control point of the trajectory. Individually translates each joint and control point of the subsequence of traj from start to end inclusive. [start..end] is a range of jointCount joints. From jointCount-1 to jointCount+1 segments are affected, depending on how the joints are placed. [start..end] MOD segCount is a range of jointCount joints. jointCount+1 segments are affected. The first segment moves on one end only. The intermediate segments move both ends. The last segment moves one end only. All of the relevant joints move. A convenience routine which does a TransformSegment, where transform is a simple translation. Segment Style Κ,z˜head1™Icodešœ Οmœ1™˜>J˜—˜š žœžœžœ%žœ žœž˜KJšœ žœ!˜0—Jšžœ˜J˜—Jšžœžœ˜—Jšžœ˜J˜J˜—š Ÿœžœžœžœ žœžœ˜[Jšœ žœ˜šžœ žœžœžœžœ#žœ žœž˜WJšžœžœž˜˜š žœžœžœ%žœ žœž˜KJšœ žœ!˜0—Jšžœ˜J˜—˜Jšœ>˜>J˜—Jšžœžœ˜—Jšžœ˜J˜J˜—š Ÿœž œžœ žœžœ ˜QJšœ žœ˜šžœ žœžœžœžœ!žœ žœž˜UJšžœžœž˜šœ˜Jšœ>˜>J˜—˜š žœžœžœ%žœ žœž˜KJšœ žœ˜'—Jšžœ˜J˜—Jšžœžœ˜—Jšžœ˜J˜J˜—š Ÿœžœžœ žœžœ ˜QJšœ˜J˜J˜—š Ÿœžœžœ žœžœ ˜QJšœ!˜!J˜—š Ÿœžœžœžœ žœžœ ˜WJšœ žœ˜šžœ žœžœžœžœ#žœ žœž˜WJšžœžœž˜˜š žœžœžœ%žœ žœž˜KJšœ žœ˜'—Jšžœ˜J˜—˜Jšœ>˜>J˜—Jšžœžœ˜—Jšžœ˜J˜J˜—J˜š Ÿœžœžœ žœžœ ˜LJšœ žœ˜š žœžœž œžœž˜0Jšœ žœ!˜/—Jšžœ˜J˜J˜—J˜J™2J˜šŸ œž œžœ˜FJšœ/™/Jšœ˜J˜J˜—J™J™J˜š Ÿ œžœžœžœžœ˜CJšœ!˜!J˜J˜—šŸ œž œžœžœ˜MJšœžœ%˜1Jšœ˜J˜—J˜šŸœž œžœžœ˜BJšžœ ž˜Jšœ%˜%Jšœ/˜/Jšžœžœ˜J˜J˜—š Ÿœžœžœžœžœ žœ˜RJšžœ ž˜šœ ˜ Jšœ$˜$Jšœ˜J˜—Jšœ'žœ˜9Jšžœžœ˜J˜J˜—š Ÿ œžœžœžœžœ˜NJšœ ˜ Jšžœ žœžœžœ˜2Jšœžœ#˜1Jšœ˜Jšœ˜J˜—šŸ œžœžœžœ˜AJšœžœ+˜@Jšœ˜Jšœ˜J˜—šŸ œžœžœžœ˜DJ™JJšœžœ#˜8Jšœ˜Jšœ˜šžœ žœ˜Jšœ&˜&Jšœ˜Jšœ˜—šžœžœ˜Jšœ%˜%Jšœ˜J˜—Jšœ˜J˜—š Ÿ œžœžœžœžœžœ˜DJšžœ ž˜Jšœžœ žœ˜3Jšœžœžœ˜Jšžœžœ˜J˜—J˜šŸœžœžœ˜Ešœžœ ˜Jšœ˜šœ ˜ Jšœžœ˜Jšœžœ˜ Jšœžœ˜ Jšœž˜ J˜—Jšœ˜—J˜J˜—J™ J™šŸ œž œžœžœ˜KJšœ1˜1J˜—š Ÿ œžœžœžœ žœ˜MJ™Jšœ˜šžœžœžœž˜#Jšœ ˜ Jšžœžœžœ˜$—Jšžœ˜Jšžœ˜ ˜J˜——š Ÿœžœžœ žœ žœ˜SJ™J˜šžœžœžœž˜%Jšœ ˜ Jšžœžœžœ˜$—Jšžœ˜Jšžœ˜ J˜J˜J™—š Ÿœžœžœžœžœ˜[šœžœ˜Jšœ ˜ Jšœžœ˜ Jšœžœ˜Jšœ0˜0Jšœ˜—J˜J˜—šŸœžœžœžœ˜Išœžœ˜Jšœ ˜ Jšœžœ˜ Jšœžœ˜ Jšœ žœ˜—J˜J˜—šŸœžœžœžœ˜LJ™:šœžœ˜Jšœ ˜ Jšœžœ˜ Jšœžœ˜Jšœ4˜4—J˜J˜—š Ÿœžœžœžœžœ˜\Jšœ5˜5J˜J˜—š Ÿœžœžœžœžœ˜\Jšœžœ ˜1Jšœ7˜7J˜J˜—šŸœž œžœ˜9Jšœ}™}Jšœžœžœ˜!šžœžœžœ˜Jšœ žœžœ˜$Jšžœ˜J˜—Jšžœ žœžœ žœ˜*Jšœ žœžœ˜!Jšœ˜J˜—J˜Jšœ žœžœ ˜!šœ žœžœ˜Iprocš œžœžœžœžœ˜ M˜—š Ÿœžœžœžœžœ˜GJšœ5˜5Jšœ žœ"˜0Jšœ žœ"˜0Jšœ žœ&˜6šœ žœ&˜6J™&—šœJ˜JJ™ —šžœžœžœ%ž˜4Jš žœ žœ žœžœžœ˜1—šžœ˜J™—šžœžœžœ#ž˜2Jš žœžœžœžœžœ˜5—Jšžœ˜Jšžœžœ˜J˜J˜—šŸœžœžœžœ-˜gJšžœžœžœ˜"Jšžœžœ6˜Ršžœ˜Jšœ6˜6Jšžœ žœ‘.˜KJšžœ žœ ˜J˜—J˜J˜—šŸœžœ_˜vJ˜J˜J™ šžœžœžœ$ž˜3Jšœžœ˜Jšœžœ˜—Jšžœ˜šžœžœžœ"ž˜1Jšœžœ˜Jšœžœ˜—šžœ˜J™ —Jšœ!˜!šžœXžœ žœž˜rJšœžœ˜"—Jšžœ˜J˜"šžœžœ,žœ ž˜MJšœžœ˜—šžœ˜J™ —Jšœ!˜!šžœXžœ žœž˜rJšœžœ˜"—Jšžœ˜J˜"šžœžœ,žœ ž˜MJšœžœ˜—Jšžœ˜˜J˜——šŸœžœžœ-˜dJšœ\žœ˜aJšœE˜EJšœ žœ˜Jšœžœ!˜5Jšœžœ!˜5Jšœžœ%˜7šœžœ%˜7J™&—šœX˜XJ™ —šžœžœžœ$ž˜3Jšœ$žœ˜9Jšœ#žœžœ˜=Jšœ$˜$Jšœ#˜#—šžœ˜J™—šžœžœžœ"ž˜1Jšœ žœ˜3Jšœžœžœ˜7Jšœ ˜ Jšœ˜—šžœ˜J™J™ —Jšœ0˜0Jšœžœ˜šžœžœžœ$ž˜3šžœžœžœžœ˜LJ˜ J˜—Jšžœžœžœžœ˜5Jšœ˜šœžœ9˜PJ˜Jšœ%˜%J˜—Jšœ#˜#—šž˜šžœ˜ šžœžœžœ1žœ˜PJšœ™Jšœ0˜0Jšœ%˜%J˜—šžœžœžœ˜Jšœ(˜(Jšœ%˜%J˜—J˜——šžœ˜J™—Jšœ6˜6Jšœžœ˜šžœžœžœ$ž˜3šžœžœžœžœ˜LJ˜ J˜—šžœžœžœžœ˜0JšœΉ™ΉJ˜ Jšœ+˜+J˜—Jšœ˜šœžœ9˜PJ˜Jšœ+˜+J˜—šœ1˜1J™cJšœ˜J˜—Jšœ(˜(—šž˜šžœ˜ šžœžœžœ1žœ˜PJšœ™Jšœ0˜0Jšœ+˜+J˜—š žœžœžœžœ1žœ˜UJ™Jšœ$˜$Jšœ+˜+J˜—šžœžœžœ˜Jšœ(˜(Jšœ+˜+J˜—J˜——Jšžœ˜J˜J˜—šŸœžœžœ-˜fJ™ Jšœ\žœ˜aJšœE˜EJšœ žœ˜Jšœžœ!˜5Jšœžœ!˜5Jšœžœ!˜3šœžœ!˜3J™&—šœX˜XJ™ —šžœžœžœ$ž˜3Jšœ$žœ˜9Jšœ#žœžœ˜=Jšœ$˜$Jšœ#˜#—šžœ˜J™—šžœžœžœ"ž˜1Jšœ žœ˜3Jšœžœžœ˜7Jšœ ˜ Jšœ˜—šžœ˜J™J™@—Jšœ0˜0Jšœ5˜5šžœžœžœ$ž˜3šžœžœžœžœ˜LJ˜ J˜—Jšžœžœžœžœ˜5Jšœ˜šœžœ9˜PJ˜Jšœ%˜%J˜—Jšœ#˜#—šž˜šžœ˜ šžœžœžœ1žœ˜PJšœ™Jšœ0˜0Jšœ%˜%J˜—šžœžœžœ˜Jšœ(˜(Jšœ%˜%J˜—J˜——šžœ˜J™—Jšœ6˜6Jšœžœ˜šžœžœžœ$ž˜3šžœžœžœžœ˜LJ˜ J˜—šžœžœžœžœ˜0JšœΉ™ΉJ˜ Jšœ+˜+J˜—Jšœ˜šœžœ9˜PJ˜Jšœ+˜+J˜—šœ1˜1J™cJšœ˜J˜—Jšœ(˜(—šž˜šžœ˜ šžœžœžœ1žœ˜PJšœ™Jšœ0˜0Jšœ+˜+J˜—š žœžœžœžœ1žœ˜UJ™Jšœ$˜$Jšœ+˜+J˜—šžœžœžœ˜Jšœ(˜(Jšœ+˜+J˜—J˜——Jšžœ˜J˜J˜J˜—šŸœž œžœžœ˜?Jšžœžœ žœ žœ˜*J˜—J˜šŸ œžœžœžœ˜QJš žœžœžœžœžœ˜&Jšœ˜Jšœ˜J˜J˜—š Ÿ œžœžœ žœžœžœ˜HJ™3Jšžœ žœžœ˜(š žœ žœžœ&žœ žœž˜RJšžœQžœžœžœ˜e—Jšžœ˜Jšžœžœ˜J˜—J˜J™J˜šŸ œžœžœB˜\J™GJ˜ J˜ šžœžœžœ ž˜/Jšœ˜Jšœ!˜!—Jšžœ˜šžœžœžœž˜-Jšœžœ˜-Jšœ<˜<—Jšžœ˜Jšœ0˜0J˜J˜—šŸœžœžœD˜aJšœ2˜2Jšœ2˜2Jšœ$˜$Jšœ˜Jšœ)˜)J˜J˜—šŸœžœžœ˜GJšžœžœ˜Jšžœ˜Jšœ+˜+Jšœ˜Jšœ)˜)J˜J˜—šŸœžœžœE˜cJšœ žœ˜Jšžœ žœ#˜2šžœ˜š žœ žœžœ&žœ žœž˜RJ™lJšœ˜Jšœ˜Jšžœž˜Jšœ œ*˜?Jšœ œ*˜HJšžœžœ˜—Jšžœ˜Jšœ8˜8J˜—J˜J˜J˜—šŸœžœžœžœ6˜uJ™J˜ J˜ Jšœ˜šžœ žœ˜Jšœ"˜"Jšœžœ,˜JJ˜—Jšœ Ÿœ˜/šžœ0žœžœž˜EJšœ!˜!—Jšžœ˜šžœžœ˜Jšœ˜Jšœžœ,˜IJ˜—šžœžœžœž˜Jšœžœ˜-Jšœ<˜<—Jšžœ˜J˜J˜—šŸœžœžœžœ6˜wJ™_J˜ J˜ Jšœ˜šœ žœ˜J™(—Jšœ#˜#šœžœ,˜JJ™)—Jšœ Ÿœ˜/šžœ0žœžœž˜EJšœ!˜!—šžœ˜J™$—Jšœ˜šœžœ,˜IJ™ —Jšœ žœ˜š žœžœžœžœ ž˜>Jšœžœ˜-Jšœ<˜