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