GGTraj.mesa
Contents: Procedures to implement the Traj Slice Class. Trajectories consist of Segments.
Copyright Ó 1986, 1987, 1992 by Xerox Corporation. All rights reserved.
Pier, February 17, 1988 12:03:17 pm PST
Bier, January 28, 1993 3:39 pm PST
DIRECTORY
GGBasicTypes, GGCoreTypes, GGInterfaceTypes, GGModelTypes, GGSegmentTypes, GGTrajTypes, Imager, ImagerPath, ImagerTransformation, IO, Rope, Rosary;
GGTraj:
CEDAR
DEFINITIONS =
BEGIN
StrokeJoint: TYPE = Imager.StrokeJoint;
BoundBox: TYPE = GGCoreTypes.BoundBox;
Camera: TYPE = GGModelTypes.Camera;
Circle: TYPE = GGBasicTypes.Circle;
EditConstraints: TYPE = GGModelTypes.EditConstraints;
FenceHoleOpen: TYPE = GGModelTypes.FenceHoleOpen;
HitType: TYPE = GGModelTypes.TrajPartType;
Joint: TYPE = GGSegmentTypes.Joint;
Line: TYPE = GGCoreTypes.Line;
MakeCurveProc: TYPE = GGTrajTypes.MakeCurveProc;
Point: TYPE = GGBasicTypes.Point;
Scene: TYPE = GGModelTypes.Scene;
Segment: TYPE = GGSegmentTypes.Segment;
SelectedObjectData: TYPE = GGSegmentTypes.SelectedObjectData;
SelectionClass: TYPE = GGSegmentTypes.SelectionClass;
Slice: TYPE = GGModelTypes.Slice;
SliceClass: TYPE = GGModelTypes.SliceClass;
SliceClassObj: TYPE = GGModelTypes.SliceClassObj;
SliceDescriptor: TYPE = GGModelTypes.SliceDescriptor;
SliceDescriptorObj: TYPE = GGModelTypes.SliceDescriptorObj;
SliceObj: TYPE = GGModelTypes.SliceObj;
SliceParts: TYPE = GGModelTypes.SliceParts;
TrajData: TYPE = GGModelTypes.TrajData;
TrajParts: TYPE = GGModelTypes.TrajParts;
TrajPartType: TYPE = GGModelTypes.TrajPartType;
TrajEnd: TYPE = GGModelTypes.TrajEnd;
Vector: TYPE = GGBasicTypes.Vector;
SegCPSequence: TYPE = REF SegCPSequenceObj;
SegCPSequenceObj:
TYPE =
RECORD [
cPoints: SEQUENCE len: NAT OF ShowPoint];
ShowPoint: TYPE = RECORD [cPoint: Point, show: BOOL ¬ TRUE];
TrajHitData: TYPE = REF TrajHitDataObj;
TrajHitDataObj:
TYPE =
RECORD [
hitType: HitType,
segNum: INT,
cpNum: INT,
jointNum: INT,
hitPoint: Point
];
RunProc: TYPE = GGTrajTypes.RunProc;
Routine Creation
The lifetime of a trajectory goes something like this: It begins as a single point. The sole purpose of this point is to be an "endpoint" to which the first segment can be attached. Subsequent segments are also added to endpoints. In Gargoyle, trajectories never appear at the top level of a scene; they are always part of an outline. Usually a CreateTraj call will be followed immediately by a GGScene.CreateOutline call.
Trajectory-Only Routines
CreateTraj:
PROC [point: Point]
RETURNS [slice: Slice];
Will there be an arrowhead on the lo end of traj? On the hi end?
CreateTrajFromData: PROC [role: FenceHoleOpen, segCount: NAT, segments: Rosary.ROSARY, joints: Rosary.ROSARY, extraPoints: LIST OF Joint, visibleJoints: BOOL ¬ TRUE, strokeJoint: Imager.StrokeJoint ¬ round, loArrow, hiArrow: BOOL ¬ FALSE, selectedInPart: SelectedObjectData ¬ [FALSE, FALSE, FALSE, FALSE]] RETURNS [slice: Slice];
AddSegment:
PROC [slice: Slice, trajEnd: TrajEnd, seg: Segment, segEnd: TrajEnd]
RETURNS [success:
BOOL];
Moves seg so that the specified end of seg coincides with the specified end of traj. In other words, seg is translated. If success = FALSE, AddSegment sends an error message to Feedback and returns.
CloseWithSegment:
PROC [slice: Slice, seg: Segment, segEnd: TrajEnd];
Like AddSegment, except that here we rotate, scale, and translate seg 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.
CloseByDistorting:
PROC [slice: Slice, distortEnd: TrajEnd];
Move either the first joint or the last joint (depending on the distortEnd argument) to coincide with the other end. This will reduce the number of joints in the trajectory by one (making all sequences which refer to this trajectory obsolete. new traj will have role = fence.
DeleteControlPoints:
PROC [trajD: SliceDescriptor, scene: Scene]
RETURNS [bBox: BoundBox];
Deletes selected control points and returns bounding box of area requiring refresh.
DeleteSequence:
PROC [seq: SliceDescriptor]
RETURNS [smallerOutline: Slice, openTrajOutlines:
LIST
OF Slice];
Deletes those trajectory parts described in seq, and returns the new smaller Outline (possibly still with holes) and new trajectories enclosed in Outlines, that are pieces of the original seq.traj.
ReplaceFirstRun:
PROC [trajD: SliceDescriptor, runProc: RunProc, segmentsOnly:
BOOL, selectNewRuns:
BOOL]
RETURNS [newOutline: Slice];
Used by GGEventImplE.SetCurveAux. trajD summarizes those active bits in trajD.slice that are set. Find the first run of active bits, and replace it as described by runProc. If segmentsOnly is set, ignore runs that consist of a single joint. If selectNewRuns is set, set the "normal" bits of any new segments that are added.
SegToSegMatchCp:
PROC [seg: Segment, traj: Slice, makeCurve: MakeCurveProc, type:
ATOM];
Replace segs with new segs made by 'makeCurve' maintaining control point data if possible. Variable type is a hint as to the type of curve that 'makeCurve' will make. Segments that are already of this type will be left alone.
SegAndCpsToSegs:
PROC [seg: Segment, traj: Slice, makeCurve: MakeCurveProc, type:
ATOM];
Like SegToSegMatchCp, but every control point of the old segment is used as an endpoint for a new segment, so one old segment may produce many new segments.
OnlyChild:
PROC [slice: Slice]
RETURNS [
BOOL];
Returns TRUE if traj's parent outline has no holes.
SetTrajRole: PROC [traj: Slice, role: FenceHoleOpen];
GetTrajRole: PROC [traj: Slice] RETURNS [role: FenceHoleOpen];
Building Trajectories from Parts
CopyTrajFromRun:
PROC [slice: Slice, run: TrajParts]
RETURNS [copy: Slice];
Creates a new trajectory from the consecutive set of segments described by run. All of the segments are copied, so run's slice is untouched.
CopyTrajFromRange:
PROC [slice: Slice, start:
INT, len:
INT]
RETURNS [piece: Slice];
Like CopyTrajFromRun, but we specify the run with a start segment and a number of segments.
Concat:
PROC [fixed: Slice, fixedEnd: TrajEnd, moving: Slice, movingEnd: TrajEnd]
RETURNS [longer: Slice];
Creates a new trajectory by concatenating two existing trajectories. Copies are made, so fixed and moving are unchanged. moving is translated so that its movingEnd touches the fixedEnd of fixed.
SpliceIn:
PROC [runDescriptor: SliceDescriptor, slice: Slice]
RETURNS [newSlice: Slice];
Run describes a part of its traj that is to be replaced by slice. The result is similar to Rope.Cat[Rope.Substr[...], newRope, Rope.Substr[...]] except that a Traj can be a circular structure.
Orientation
ReverseTraj: PROC [slice: Slice];
IsClockwiseTraj: PROC [slice: Slice] RETURNS [BOOL];
IsClockwiseTrajTransformSeq: PROC [descriptor: SliceDescriptor, transform: ImagerTransformation.Transformation] RETURNS [BOOL];
Drawing
DrawConstrained: PROC [dc: Imager.Context, selDescriptor: SliceDescriptor, segNum: NAT, seg: Segment, transform: ImagerTransformation.Transformation, entire, lo, hi: BOOL, controlPoints: GGBasicTypes.BitVector, editConstraints: EditConstraints, lineTo: ImagerPath.LineToProc, curveTo: ImagerPath.CurveToProc, conicTo: ImagerPath.ConicToProc, arcTo: ImagerPath.ArcToProc] RETURNS [cPointsInSeg: SegCPSequence];
DrawPolyline: PROC [dc: Imager.Context, traj: Slice];
Transforming
ConstrainJoint: PROC [slice: Slice, editConstraints: EditConstraints, jointNum: NAT] RETURNS [constrained: BOOL];
ConstrainCP: PROC [slice: Slice, editConstraints: EditConstraints, segNum: NAT, cpNum: NAT] RETURNS [constrained: BOOL];
MatchShape: PROC [traj1, traj2: Slice] RETURNS [BOOL];
Accessing Parts
FetchSegment: PROC [slice: Slice, index: NAT] RETURNS [seg: Segment];
FetchSegmentTraj: PROC [trajData: TrajData, index: NAT] RETURNS [seg: Segment];
FetchJoint: PROC [slice: Slice, index: NAT] RETURNS [joint: Joint];
FetchJointPos: PROC [slice: Slice, index: NAT] RETURNS [point: Point];
FetchJointPosTraj: PROC [trajData: TrajData, index: NAT] RETURNS [point: Point];
FetchJointNormal: PROC [slice: Slice, index: NAT] RETURNS [normal: Vector];
LastJointPos: PROC [slice: Slice] RETURNS [point: Point];
SetJointPos: PROC [slice: Slice, index: NAT, newPos: Point];
HiSegment: PROC [slice: Slice] RETURNS [highestIndex: NAT];
HiSegmentTraj:
PROC [trajData: TrajData]
RETURNS [highestIndex:
NAT];
Returns traj.segCount-1 for both open and closed trajectories.
HiJoint: PROC [slice: Slice] RETURNS [highestIndex: NAT];
HiJointTraj: PROC [trajData: TrajData] RETURNS [highestIndex: NAT];
PreviousSegment: PROC [slice: Slice, segNum: NAT] RETURNS [prev: Segment];
PreviousSegmentNum:
PROC [slice: Slice, segNum:
NAT]
RETURNS [prevNum:
INT];
Returns -1 if no PreviousSegmentNum
FollowingSegmentNum:
PROC [slice: Slice, segNum:
NAT]
RETURNS [followNum:
INT];
Returns -1 if no FollowingSegmentNum
FollowingJoint:
PROC [slice: Slice, index:
NAT]
RETURNS [nextIndex:
INT];
segCount for open trajectories, segCount -1 for closed trajectories.
Returns -1 if no FollowingJoint
IsEndJoint: PROC [slice: Slice, index: NAT] RETURNS [BOOL];
Parts
SaveSelection: PROC [slice: Slice, selectClass: SelectionClass, scene: Scene];
SaveSelectionInSequence: PROC [descriptor: SliceDescriptor, selectClass: SelectionClass];
SaveSelectionInParts: PROC [slice: Slice, parts: SliceParts, selectClass: SelectionClass];
ClearSelection: PROC [slice: Slice, selectClass: SelectionClass];
ClearSelections:
PROC [slice: Slice];
Set all of the selection fields to FALSE (faster than several calls to ClearSelection).
RemakeSelection: PROC [slice: Slice, selectClass: SelectionClass] RETURNS [parts: SliceParts];
Hit Testing
UnpackSimpleDescriptor: PROC [traj: Slice, parts: SliceParts] RETURNS [success: BOOL ¬ FALSE, partType: TrajPartType ¬ none, trajData: TrajData, joint: Joint ¬ NIL, jointNum: NAT ¬ 999, cp: Point ¬ [0,0], cpNum: NAT ¬ 999, seg: Segment ¬ NIL, segNum: NAT ¬ 999];
UnpackHitData: PROC [hitData: REF ANY] RETURNS [hitType: HitType, segNum, cpNum, jointNum: INT, hitPoint: Point];
NearestSegment: PROC [testPoint: Point, descriptor: SliceDescriptor, tolerance: REAL] RETURNS [bestDist: REAL, bestSeg: NAT, bestPoint: Point, bestNormal: Vector, success: BOOL];
NearestJoint: PROC [testPoint: Point, descriptor: SliceDescriptor, tolerance: REAL] RETURNS [bestDist: REAL, bestJoint: NAT, bestPoint: Point, bestNormal: Vector, success: BOOL];
NearestControlPoint: PROC [testPoint: Point, descriptor: SliceDescriptor, tolerance: REAL] RETURNS [bestDist: REAL, bestSeg: NAT, bestControlPoint: NAT, bestPoint: Point, bestNormal: Vector, success: BOOL];
Use sparingly:
IndexOfJoint:
PROC [joint: Joint, slice: Slice]
RETURNS [index:
INT];
Returns -1 if it doesn't exist.
IndexOfSegment: PROC [segment: Segment, slice: Slice] RETURNS [index: INT];
END.