DIRECTORY AtomButtonsTypes, Feedback, GGBasicTypes, GGBoundBox, GGInterfaceTypes, GGModelTypes, GGScene, GGOutline, GGParseIn, GGParseOut, GGSegmentTypes, GGSelect, GGSequence, GGShapes, GGTraj, GGUtility, Imager, ImagerColor, ImagerPath, ImagerTransformation, IO, Rope, Vectors2d, ViewerClasses; GGOutlineImplA: CEDAR PROGRAM IMPORTS Feedback, GGBoundBox, GGScene, GGOutline, GGParseIn, GGParseOut, GGSequence, GGShapes, GGTraj, GGUtility, Imager, ImagerColor, ImagerPath, ImagerTransformation, IO, Vectors2d EXPORTS GGOutline = BEGIN OPEN GGOutline; BitVector: TYPE = GGBasicTypes.BitVector; BoundBox: TYPE = GGBasicTypes.BoundBox; CameraData: TYPE = GGModelTypes.CameraData; Circle: TYPE = GGBasicTypes.Circle; Color: TYPE = Imager.Color; DefaultData: TYPE = GGModelTypes.DefaultData; FeedbackData: TYPE = AtomButtonsTypes.FeedbackData; StrokeJoint: TYPE = Imager.StrokeJoint; StrokeEnd: TYPE = Imager.StrokeEnd; ControlPointGenerator: TYPE = GGModelTypes.ControlPointGenerator; FeatureData: TYPE = GGInterfaceTypes.FeatureData; GGData: TYPE = GGInterfaceTypes.GGData; Joint: TYPE = GGSegmentTypes.Joint; JointGenerator: TYPE = GGModelTypes.JointGenerator; Line: TYPE = GGBasicTypes.Line; AlignBag: TYPE = GGInterfaceTypes.AlignBag; Outline: TYPE = REF OutlineObj; OutlineObj: TYPE = GGModelTypes.OutlineObj; OutlineClass: TYPE = REF OutlineClassObj; OutlineClassObj: TYPE = GGModelTypes.OutlineClassObj; OutlineDescriptor: TYPE = GGModelTypes.OutlineDescriptor; OutlineHitData: TYPE = REF OutlineHitDataObj; OutlineHitDataObj: TYPE = GGOutline.OutlineHitDataObj; OutlineParts: TYPE = REF OutlinePartsObj; OutlinePartsObj: TYPE = GGOutline.OutlinePartsObj; OutlineSequence: TYPE = GGSelect.OutlineSequence; OutlineSequenceGenerator: TYPE = GGSelect.OutlineSequenceGenerator; Point: TYPE = GGBasicTypes.Point; PointGenerator: TYPE = GGModelTypes.PointGenerator; PointPairGenerator: TYPE = GGModelTypes.PointPairGenerator; Scene: TYPE = GGModelTypes.Scene; Segment: TYPE = GGSegmentTypes.Segment; SegmentGenerator: TYPE = GGModelTypes.SegmentGenerator; SelectMode: TYPE = GGModelTypes.SelectMode; SelectionClass: TYPE = GGSegmentTypes.SelectionClass; Sequence: TYPE = GGModelTypes.Sequence; SequenceOfReal: TYPE = GGBasicTypes.SequenceOfReal; SliceClass: TYPE = REF SliceClassObj; SliceClassObj: TYPE = GGModelTypes.SliceClassObj; SliceDescriptor: TYPE = GGModelTypes.SliceDescriptor; SliceParts: TYPE = GGModelTypes.SliceParts; Traj: TYPE = REF TrajObj; TrajEnd: TYPE = GGModelTypes.TrajEnd; TrajGenerator: TYPE = REF TrajGeneratorObj; TrajGeneratorObj: TYPE = GGModelTypes.TrajGeneratorObj; TrajObj: TYPE = GGModelTypes.TrajObj; TrajPartType: TYPE = GGModelTypes.TrajPartType; TriggerBag: TYPE = GGInterfaceTypes.TriggerBag; Vector: TYPE = GGBasicTypes.Vector; Viewer: TYPE = ViewerClasses.Viewer; OutlineDescribeProc: TYPE = GGModelTypes.OutlineDescribeProc; OutlineFileoutProc: TYPE = GGModelTypes.OutlineFileoutProc; OutlineFileinProc: TYPE = GGModelTypes.OutlineFileinProc; OutlineUnionPartsProc: TYPE = GGModelTypes.OutlineUnionPartsProc; OutlineDifferencePartsProc: TYPE = GGModelTypes.OutlineDifferencePartsProc; OutlineAugmentPartsProc: TYPE = GGModelTypes.OutlineAugmentPartsProc; OutlinePointsInDescriptorProc: TYPE = GGModelTypes.OutlinePointsInDescriptorProc; OutlinePointPairsInDescriptorProc: TYPE = GGModelTypes.OutlinePointPairsInDescriptorProc; OutlineNextPointProc: TYPE = GGModelTypes.OutlineNextPointProc; OutlineNextPointPairProc: TYPE = GGModelTypes.OutlineNextPointPairProc; OutlineClosestPointProc: TYPE = GGModelTypes.OutlineClosestPointProc; OutlineClosestPointAndTangentProc: TYPE = GGModelTypes.OutlineClosestPointAndTangentProc; OutlineClosestSegmentProc: TYPE = GGModelTypes.OutlineClosestSegmentProc; OutlineSetArrowsProc: TYPE = GGModelTypes.OutlineSetArrowsProc; OutlineGetArrowsProc: TYPE = GGModelTypes.OutlineGetArrowsProc; Problem: PUBLIC SIGNAL [msg: Rope.ROPE] = Feedback.Problem; fillColor: PUBLIC Imager.Color _ Imager.MakeGray[0.5]; NoOpClosestPointAndTangent: PUBLIC OutlineClosestPointAndTangentProc = { }; NoOpSetArrows: PUBLIC OutlineSetArrowsProc = { }; NoOpGetArrows: PUBLIC OutlineGetArrowsProc = { }; MakeOutlineClass: PROC [] RETURNS [class: OutlineClass] = { class _ NEW[OutlineClassObj _ [ type: $Outline, getBoundBox: OutlineBoundBox, getTightBox: OutlineTightBox, copy: OutlineCopy, drawParts: OutlineDrawParts, drawTransform: OutlineDrawTransform, drawSelectionFeedback: OutlineDrawSelectionFeedback, drawAttractorFeedback: OutlineDrawAttractorFeedback, transform: OutlineTransform, describe: OutlineDescribe, describeHit: OutlineDescribeHit, fileout: OutlineFileout, filein: OutlineFilein, emptyParts: OutlineEmptyParts, newParts: OutlineNewParts, unionParts: OutlineUnionParts, differenceParts: OutlineDifferenceParts, movingParts: OutlineMovingParts, augmentParts: OutlineAugmentParts, setSelectedFields: OutlineSetSelectedFields, pointsInDescriptor: OutlinePointsInDescriptor, pointPairsInDescriptor: OutlinePointPairsInDescriptor, nextPoint: OutlineNextPoint, nextPointPair: OutlineNextPointPair, closestPoint: OutlineClosestPoint, closestJointToHitData: OutlineClosestJointToHitData, closestPointAndTangent: NoOpClosestPointAndTangent, closestSegment: OutlineClosestSegment, lineIntersection: OutlineLineIntersection, circleIntersection: OutlineCircleIntersection, hitDataAsSimpleCurve: OutlineHitDataAsSimpleCurve, setDefaults: OutlineSetDefaults, setStrokeWidth: OutlineSetStrokeWidth, getStrokeWidth: OutlineGetStrokeWidth, setStrokeEnd: OutlineSetStrokeEnd, getStrokeEnd: OutlineGetStrokeEnd, setStrokeJoint: OutlineSetStrokeJoint, getStrokeJoint: OutlineGetStrokeJoint, setStrokeColor: OutlineSetStrokeColor, getStrokeColor: OutlineGetStrokeColor, setFillColor: OutlineSetFillColor, getFillColor: OutlineGetFillColor, setArrows: NoOpSetArrows, getArrows: NoOpGetArrows, setDashed: OutlineSetDashed, getDashed: OutlineGetDashed ]]; }; FetchSliceClass: PUBLIC PROC [name: ATOM] RETURNS [class: OutlineClass] = { class _ globalOutlineClass; }; FetchOutlineClass: PROC [] RETURNS [class: OutlineClass] = { class _ globalOutlineClass; }; CreateOutline: PUBLIC PROC [traj: Traj, fillColor: Color] RETURNS [outline: Outline] = { boundBox: BoundBox _ GGBoundBox.CopyBoundBox[traj.boundBox]; outline _ NEW[OutlineObj _ [ class: FetchOutlineClass[], fillColor: fillColor, children: LIST[traj], parent: NIL, boundBox: boundBox]]; outline.nullDescriptor _ DescriptorFromParts[outline, CreateEmptyParts[outline]]; traj.parent _ outline; IF traj.role = hole THEN traj.role _ fence; }; OutlineBoundBox: PROC [slice: Outline, parts: SliceParts] RETURNS [box: BoundBox] = { IF parts = NIL THEN box _ slice.boundBox ELSE { outlineParts: OutlineParts _ NARROW[parts]; boxList: LIST OF BoundBox _ NIL; thisBox: BoundBox; FOR list: LIST OF Sequence _ outlineParts.seqs, list.rest UNTIL list = NIL DO IF list.first # NIL THEN { thisBox _ GGSequence.ComputeBoundBox[list.first]; boxList _ CONS[thisBox, boxList]; }; ENDLOOP; box _ GGBoundBox.BoundBoxOfBoxes[boxList]; }; }; OutlineTightBox: PROC [slice: Outline, parts: SliceParts] RETURNS [box: BoundBox] = { outlineParts: OutlineParts; boxList: LIST OF BoundBox _ NIL; thisBox: BoundBox; IF parts = NIL THEN parts _ slice.class.newParts[slice, NIL, slice].parts; outlineParts _ NARROW[parts]; FOR list: LIST OF Sequence _ outlineParts.seqs, list.rest UNTIL list = NIL DO IF list.first # NIL THEN { thisBox _ GGSequence.ComputeTightBox[list.first]; boxList _ CONS[thisBox, boxList]; }; ENDLOOP; box _ GGBoundBox.BoundBoxOfBoxes[boxList]; }; UpdateBoundBox: PUBLIC PROC [slice: Outline] = { outlineBoxes: LIST OF BoundBox _ NIL; trajGen: TrajGenerator; IF slice=NIL THEN RETURN; trajGen _ TrajsInOutline[slice]; FOR traj: Traj _ GGScene.NextTraj[trajGen], GGScene.NextTraj[trajGen] UNTIL traj = NIL DO outlineBoxes _ CONS[traj.boundBox, outlineBoxes]; ENDLOOP; UpdateBoundBoxFromList[slice.boundBox, outlineBoxes]; }; UpdateBoundBoxFromList: PROC [bBox: BoundBox, list: LIST OF BoundBox] = { IF list = NIL THEN ERROR; bBox.loX _ list.first.loX; bBox.hiX _ list.first.hiX; bBox.loY _ list.first.loY; bBox.hiY _ list.first.hiY; FOR bBoxList: LIST OF BoundBox _ list.rest, bBoxList.rest UNTIL bBoxList = NIL DO bBox.loX _ MIN[bBox.loX, bBoxList.first.loX]; bBox.hiX _ MAX[bBox.hiX, bBoxList.first.hiX]; bBox.loY _ MIN[bBox.loY, bBoxList.first.loY]; bBox.hiY _ MAX[bBox.hiY, bBoxList.first.hiY]; ENDLOOP; }; OutlineCopy: PROC [slice: Outline] RETURNS [copy: Outline] = { holeGen: TrajGenerator; newTraj, fence: Traj; fence _ FenceOfOutline[slice]; newTraj _ GGTraj.CopyTraj[fence]; copy _ CreateOutline[newTraj, slice.fillColor]; holeGen _ HolesOfOutline[slice]; FOR hole: Traj _ GGScene.NextTraj[holeGen], GGScene.NextTraj[holeGen] UNTIL hole = NIL DO newTraj _ GGTraj.CopyTraj[hole]; copy.children _ AppendTrajList[copy.children, LIST[newTraj]]; newTraj.parent _ copy; ENDLOOP; }; OutlineDrawParts: PROC [slice: Outline, parts: SliceParts, dc: Imager.Context, camera: CameraData, quick: BOOL] = { IF parts # NIL THEN ERROR Problem[msg: "OutlineDrawParts NYI"]; DrawOutline[dc, slice]; }; OutlineDrawTransform: PROC [sliceD: OutlineDescriptor, dc: Imager.Context, camera: CameraData, transform: ImagerTransformation.Transformation] = { sliceParts: OutlineParts _ NARROW[sliceD.parts]; BuildOutline: Imager.PathProc = { BuildPath: Imager.PathProc = { seg: Segment; firstPoint: Point _ GGTraj.FetchJointPos[traj, 0]; moveTo[ [firstPoint.x, firstPoint.y] ]; FOR i: INT IN [0..GGTraj.HiSegment[traj]] DO seg _ GGTraj.FetchSegment[traj, i]; seg.class.buildPath[seg, lineTo, curveTo, conicTo, arcTo]; ENDLOOP; }; BuildPathTransformSeq: Imager.PathProc = { seg: Segment; firstPoint: Point _ GGTraj.FetchJointPos[traj, 0]; IF thisSeq.segments[0] OR thisSeq.joints[0] THEN firstPoint _ ImagerTransformation.Transform[transform, firstPoint]; moveTo[ [firstPoint.x, firstPoint.y] ]; FOR i: INT IN [0..GGTraj.HiSegment[traj]] DO seg _ GGTraj.FetchSegment[traj, i]; seg.class.buildPathTransform[seg, transform, thisSeq.segments[i], thisSeq.joints[i], thisSeq.joints[(i+1) MOD thisSeq.traj.segCount], thisSeq.controlPoints[i], lineTo, curveTo, conicTo, arcTo]; ENDLOOP; }; imagerHole: ImagerPath.Trajectory; cc, holeCC: BOOL; traj _ fence; thisSeq _ FindSequenceInList[traj, sliceParts.seqs]; IF thisSeq # NIL THEN { BuildPathTransformSeq[moveTo, lineTo, curveTo, conicTo, arcTo]; cc _ GGTraj.IsClockwiseTrajTransformSeq[thisSeq, transform]; } ELSE { BuildPath[moveTo, lineTo, curveTo, conicTo, arcTo]; cc _ GGTraj.IsClockwiseTraj[traj]; }; FOR holeList: LIST OF Traj _ slice.children.rest, holeList.rest UNTIL holeList = NIL DO traj _ holeList.first; thisSeq _ FindSequenceInList[traj, sliceParts.seqs]; IF thisSeq # NIL THEN { imagerHole _ ImagerPath.TrajectoryListFromPath[BuildPathTransformSeq].first; holeCC _ GGTraj.IsClockwiseTrajTransformSeq[thisSeq, transform]; } ELSE { imagerHole _ ImagerPath.TrajectoryListFromPath[BuildPath].first; holeCC _ GGTraj.IsClockwiseTraj[traj]; }; IF cc=holeCC THEN ImagerPath.MapTrajectoryBackward[imagerHole, moveTo, lineTo, curveTo, conicTo, arcTo] ELSE ImagerPath.MapTrajectory[imagerHole, moveTo, lineTo, curveTo, conicTo, arcTo]; ENDLOOP; }; traj: Traj; thisSeq: Sequence; slice: Outline _ sliceD.slice; fence: Traj _ slice.children.first; IF fence.role = fence AND slice.fillColor#NIL THEN { transformedColor: Color; WITH slice.fillColor SELECT FROM cc: ImagerColor.ConstantColor => { Imager.SetColor[dc, cc]; }; sc: ImagerColor.SampledColor => { IF OutlineCompleteParts[slice, sliceD.parts] THEN { transformedColor _ ImagerColor.MakeSampledColor[pa: sc.pa, um: ImagerTransformation.Concat[sc.um, transform], colorOperator: sc.colorOperator]; Imager.SetColor[dc, transformedColor]; }; }; ENDCASE => ERROR; Imager.MaskFill[dc, BuildOutline]; }; thisSeq _ FindSequenceInList[fence, sliceParts.seqs]; IF thisSeq # NIL THEN GGTraj.DrawTrajTransformSeq[dc, thisSeq, transform] ELSE GGTraj.DrawTraj[dc, fence]; FOR holeList: LIST OF Traj _ slice.children.rest, holeList.rest UNTIL holeList = NIL DO thisSeq _ FindSequenceInList[holeList.first, sliceParts.seqs]; IF thisSeq # NIL THEN GGTraj.DrawTrajTransformSeq[dc, thisSeq, transform] ELSE GGTraj.DrawTraj[dc, holeList.first]; ENDLOOP; }; OutlineDrawSelectionFeedback: PROC [slice: Outline, selectedParts: SliceParts, hotParts: SliceParts, dc: Imager.Context, camera: CameraData, dragInProgress, caretIsMoving, hideHot, quick: BOOL] = { normalOutlineParts, hotOutlineParts: OutlineParts; normalList, hotList: LIST OF Sequence; normalOutlineParts _ NARROW[selectedParts]; hotOutlineParts _ NARROW[hotParts]; IF caretIsMoving OR dragInProgress THEN RETURN; IF selectedParts = NIL AND hotParts = NIL THEN RETURN; IF selectedParts # NIL AND hotParts # NIL THEN { hotList _ hotOutlineParts.seqs; FOR normalList _ normalOutlineParts.seqs, normalList.rest UNTIL normalList = NIL DO IF normalList.first # NIL THEN GGTraj.DrawSelectionFeedback[normalList.first.traj, normalList.first, hotList.first, dc, camera, dragInProgress, caretIsMoving, hideHot, quick] ELSE IF hotList.first # NIL THEN GGTraj.DrawSelectionFeedback[hotList.first.traj, normalList.first, hotList.first, dc, camera, dragInProgress, caretIsMoving, hideHot, quick]; hotList _ hotList.rest; ENDLOOP; } ELSE IF selectedParts # NIL THEN { FOR normalList _ normalOutlineParts.seqs, normalList.rest UNTIL normalList = NIL DO IF normalList.first # NIL THEN GGTraj.DrawSelectionFeedback[normalList.first.traj, normalList.first, NIL, dc, camera, dragInProgress, caretIsMoving, hideHot, quick]; ENDLOOP; } ELSE { FOR hotList _ hotOutlineParts.seqs, hotList.rest UNTIL hotList = NIL DO IF hotList.first # NIL THEN GGTraj.DrawSelectionFeedback[hotList.first.traj, NIL, hotList.first, dc, camera, dragInProgress, caretIsMoving, hideHot, quick]; ENDLOOP; }; }; OutlineDrawAttractorFeedback: PUBLIC PROC [sliceD: OutlineDescriptor, selectedParts: SliceParts, dragInProgress: BOOL, dc: Imager.Context, camera: CameraData] = { success: BOOL; partType: TrajPartType; traj: Traj; seg: Segment; jointNum: NAT; [success, partType, traj, ----, jointNum, ----, ----, seg] _ UnpackSimpleDescriptorOld[sliceD]; SELECT partType FROM joint => { previous: Segment _ GGTraj.PreviousSegment[traj, jointNum]; this: Segment _ IF jointNum <= GGTraj.HiSegment[traj] THEN GGTraj.FetchSegment[traj, jointNum] ELSE NIL; IF previous#NIL THEN DrawCpsAndJoints[dc, previous]; IF this#NIL THEN DrawCpsAndJoints[dc, this]; }; segment, controlPoint => DrawCpsAndJoints[dc, seg]; ENDCASE; -- none }; DrawOutline: PROC [dc: Imager.Context, outline: Outline] = { BuildOutline: Imager.PathProc = { BuildPath: Imager.PathProc = { BuildTrajPath[traj, moveTo, lineTo, curveTo, conicTo, arcTo]; }; imagerHole: ImagerPath.Trajectory; cc: BOOL; traj _ fence; cc _ GGTraj.IsClockwiseTraj[traj]; BuildTrajPath[traj, moveTo, lineTo, curveTo, conicTo, arcTo]; FOR holeList: LIST OF Traj _ outline.children.rest, holeList.rest UNTIL holeList = NIL DO traj _ holeList.first; imagerHole _ ImagerPath.TrajectoryListFromPath[BuildPath].first; IF cc=GGTraj.IsClockwiseTraj[traj] THEN ImagerPath.MapTrajectoryBackward[imagerHole, moveTo, lineTo, curveTo, conicTo, arcTo] ELSE ImagerPath.MapTrajectory[imagerHole, moveTo, lineTo, curveTo, conicTo, arcTo]; ENDLOOP; }; fence, traj: Traj; fence _ outline.children.first; IF fence.role = fence AND outline.fillColor#NIL THEN { Imager.SetColor[dc, outline.fillColor]; Imager.MaskFill[dc, BuildOutline]; }; GGTraj.DrawTraj[dc, fence]; FOR holeList: LIST OF Traj _ outline.children.rest, holeList.rest UNTIL holeList = NIL DO GGTraj.DrawTraj[dc, holeList.first]; ENDLOOP; }; FindSequenceInList: PROC [traj: Traj, seqList: LIST OF Sequence] RETURNS [seq: Sequence] = { FOR list: LIST OF Sequence _ seqList, list.rest UNTIL list = NIL DO IF list.first # NIL AND list.first.traj = traj THEN RETURN[list.first]; ENDLOOP; RETURN[NIL]; }; BuildTrajPath: PROC [traj: Traj, moveTo: ImagerPath.MoveToProc, lineTo: ImagerPath.LineToProc, curveTo: ImagerPath.CurveToProc, conicTo: ImagerPath.ConicToProc, arcTo: ImagerPath.ArcToProc] = { DoBuildTrajPath: Imager.PathProc = { seg: Segment; firstPoint: Point _ GGTraj.FetchJointPos[traj, 0]; moveTo[ [firstPoint.x, firstPoint.y] ]; FOR i: INT IN [0..GGTraj.HiSegment[traj]] DO seg _ GGTraj.FetchSegment[traj, i]; seg.class.buildPath[seg, lineTo, curveTo, conicTo, arcTo]; ENDLOOP; }; DoBuildTrajPath[moveTo, lineTo, curveTo, conicTo, arcTo]; }; DrawCpsAndJoints: PROC [dc: Imager.Context, seg: Segment] = { point: Point; GGShapes.DrawJoint[dc, seg.lo]; GGShapes.DrawJoint[dc, seg.hi]; FOR j:INT IN [0..seg.class.controlPointCount[seg]) DO point _ seg.class.controlPointGet[seg, j]; GGShapes.DrawCP[dc, point]; ENDLOOP; }; OutlineTransform: PUBLIC PROC [sliceD: OutlineDescriptor, transform: ImagerTransformation.Transformation] = { outlineParts: OutlineParts _ NARROW[sliceD.parts]; slice: Outline _ sliceD.slice; transformedColor: Color; FOR list: LIST OF Sequence _ outlineParts.seqs, list.rest UNTIL list = NIL DO IF list.first # NIL THEN GGTraj.TransformSequence[list.first, transform]; ENDLOOP; UpdateBoundBox[slice]; IF slice.fillColor # NIL THEN { WITH slice.fillColor SELECT FROM cc: ImagerColor.ConstantColor => {}; sc: ImagerColor.SampledColor => { IF OutlineCompleteParts[slice, sliceD.parts] THEN { transformedColor _ ImagerColor.MakeSampledColor[pa: sc.pa, um: ImagerTransformation.Concat[sc.um, transform], colorOperator: sc.colorOperator]; slice.fillColor _ transformedColor; }; }; ENDCASE => ERROR; }; }; OneSequenceOnly: PROC [realParts: OutlineParts] RETURNS [theSeq: Sequence] = { theSeq _ NIL; FOR list: LIST OF Sequence _ realParts.seqs, list.rest UNTIL list = NIL DO IF list.first # NIL THEN { IF theSeq # NIL THEN RETURN[NIL] ELSE theSeq _ list.first; }; ENDLOOP; }; OutlineDescribe: PROC [sliceD: OutlineDescriptor] RETURNS [rope: Rope.ROPE] = { realParts: OutlineParts; theSeq: Sequence; IF sliceD.parts = NIL THEN RETURN["an Outline"]; realParts _ NARROW[sliceD.parts]; IF (theSeq _ OneSequenceOnly[realParts]) # NIL THEN { rope _ GGSequence.Describe[theSeq]; } ELSE { FOR list: LIST OF Sequence _ realParts.seqs, list.rest UNTIL list = NIL DO IF list.first # NIL THEN RETURN["several parts of a multi-trajectory outline"]; ENDLOOP; rope _ "No outline parts to speak of."; }; }; OutlineDescribeHit: PROC [slice: Outline, hitData: REF ANY] RETURNS [rope: Rope.ROPE] = { outlineHitData: OutlineHitData _ NARROW[hitData]; rope _ GGTraj.DescribeHit[outlineHitData.traj, outlineHitData.hitType, outlineHitData.segNum, outlineHitData.cpNum, outlineHitData.jointNum]; }; OutlineFileout: PROC [slice: Outline, f: IO.STREAM] = { count: NAT; f.PutF["Outline:\n"]; f.PutF["fillColor: "]; GGParseOut.WriteColor[f, slice.fillColor]; f.PutChar[IO.CR]; count _ 0; FOR trajList: LIST OF Traj _ slice.children, trajList.rest UNTIL trajList = NIL DO count _ count + 1; ENDLOOP; f.PutF["Trajectories: [%g]\n", [integer[count]]]; FOR trajList: LIST OF Traj _ slice.children, trajList.rest UNTIL trajList = NIL DO GGTraj.Fileout[f, trajList.first]; ENDLOOP; f.PutChar[IO.CR]; }; OutlineFilein: PROC [f: IO.STREAM, version: REAL, feedback: FeedbackData] RETURNS [slice: Outline] = { fillColor: Color; count: NAT; fence, hole: Traj; hasCircle: BOOL _ FALSE; GGParseIn.ReadBlankAndRope[f, "fillColor:"]; fillColor _ GGParseIn.ReadColor[f, version]; IF version < 8702.26 THEN { -- read and discard StrokeEnd from older formats GGParseIn.ReadBlankAndRope[f, "strokeEnd:"]; [] _ GGParseIn.ReadStrokeEnd[f]; }; GGParseIn.ReadBlankAndRope[f, "Trajectories:"]; GGParseIn.ReadBlankAndRope[f, "["]; count _ GGParseIn.ReadBlankAndNAT[f]; GGParseIn.ReadBlankAndRope[f, "]"]; [fence, hasCircle] _ GGTraj.Filein[f, version]; IF hasCircle THEN fillColor _ NIL; -- needed for the transition from circle/disc class slice _ GGOutline.CreateOutline[fence, fillColor]; FOR i: NAT IN [1..count-1] DO [hole, ----] _ GGTraj.Filein[f, version]; slice _ GGOutline.AddHole[slice, hole]; ENDLOOP; }; IsEmpty: PROC [realParts: OutlineParts] RETURNS [BOOL] = { FOR list: LIST OF Sequence _ realParts.seqs, list.rest UNTIL list = NIL DO IF list.first # NIL AND NOT GGSequence.IsEmpty[list.first] THEN RETURN[FALSE]; ENDLOOP; RETURN[TRUE]; }; OutlineEmptyParts: PROC [sliceD: OutlineDescriptor] RETURNS [BOOL] = { realParts: OutlineParts _ NARROW[sliceD.parts]; RETURN[IsEmpty[realParts]]; }; CreateEmptyParts: PROC [slice: Outline] RETURNS [empty: SliceParts] = { realParts: OutlineParts; empty _ realParts _ NEW[OutlinePartsObj]; realParts.seqs _ NIL; FOR list: LIST OF Traj _ slice.children, list.rest UNTIL list = NIL DO realParts.seqs _ CONS[NIL, realParts.seqs]; ENDLOOP; }; IsComplete: PROC [realParts: OutlineParts] RETURNS [BOOL] = { FOR list: LIST OF Sequence _ realParts.seqs, list.rest UNTIL list = NIL DO IF list.first = NIL THEN RETURN[FALSE]; IF NOT GGSequence.IsComplete[list.first] THEN RETURN[FALSE]; ENDLOOP; RETURN[TRUE]; }; OutlineCompleteParts: PROC [slice: Outline, parts: SliceParts] RETURNS [BOOL] = { realParts: OutlineParts _ NARROW[parts]; RETURN[IsComplete[realParts]]; }; OutlineNewParts: PROC [slice: Outline, hitData: REF ANY, mode: SelectMode] RETURNS [sliceD: OutlineDescriptor] = { realParts: OutlineParts; ptr: LIST OF Sequence; trajGen: GGModelTypes.TrajGenerator; parts: SliceParts; realParts _ NEW[OutlinePartsObj]; [realParts.seqs, ptr] _ GGUtility.StartSequenceList[]; SELECT mode FROM literal => { traj: Traj; hitType: GGModelTypes.TrajPartType; segNum, cpNum, jointNum: INT; seq: Sequence; outlineHitData: OutlineHitData _ NARROW[hitData]; [traj, hitType, segNum, cpNum, jointNum] _ GGOutline.UnpackHitData[hitData]; SELECT hitType FROM joint => seq _ GGSequence.CreateFromJoint[traj, jointNum]; controlPoint => seq _ GGSequence.CreateFromControlPoint[traj, segNum, cpNum]; segment => seq _ GGSequence.CreateSimpleFromSegment[traj, segNum]; ENDCASE => ERROR; sliceD _ DescriptorFromSequence[slice, seq]; }; none => { outlineHitData: OutlineHitData _ NARROW[hitData]; sliceD _ GGOutline.DescriptorFromParts[slice, CreateEmptyParts[slice]]; }; slice => { completeSeq: Sequence; trajGen _ TrajsInOutline[slice]; FOR traj: Traj _ GGScene.NextTraj[trajGen], GGScene.NextTraj[trajGen] UNTIL traj = NIL DO completeSeq _ GGSequence.CreateComplete[traj]; [realParts.seqs, ptr] _ GGUtility.AddSequence[completeSeq, realParts.seqs, ptr]; ENDLOOP; parts _ realParts; }; joint => { outlineHitData: OutlineHitData _ NARROW[hitData]; SELECT outlineHitData.hitType FROM joint => { -- hit a joint traj: Traj _ outlineHitData.traj; jointNum: NAT _ outlineHitData.jointNum; seq: Sequence _ GGSequence.CreateJointToJoint[traj, jointNum, jointNum]; parts _ PartsFromSequence[slice, seq]; }; segment => { -- we are in the middle of a segment. Select nearest joint or cp success: BOOL; segNum, cpNum, jointNum: NAT; jointPoint, cpPoint, caretPt: Point; traj: Traj; seg: Segment; seq: Sequence; traj _ outlineHitData.traj; segNum _ outlineHitData.segNum; seg _ GGTraj.FetchSegment[traj, segNum]; caretPt _ outlineHitData.hitPoint; jointNum _ NearestJointToHitSpot[caretPt, traj, -1, segNum, segment]; jointPoint _ GGTraj.FetchJointPos[traj, jointNum]; [cpPoint, cpNum, success] _ seg.class.closestControlPoint[seg, caretPt, GGUtility.plusInfinity]; IF NOT success THEN seq _ GGSequence.CreateJointToJoint[traj, jointNum, jointNum] ELSE { cpDist: REAL _ Vectors2d.DistanceSquared[cpPoint, caretPt]; jointDist: REAL _ Vectors2d.DistanceSquared[jointPoint, caretPt]; seq _ IF cpDist < jointDist THEN GGSequence.CreateFromControlPoint[traj, segNum, cpNum] ELSE GGSequence.CreateJointToJoint[traj, jointNum, jointNum]; }; parts _ PartsFromSequence[slice, seq]; }; controlPoint => { traj: Traj; segNum, cpNum: NAT; controlPointSeq: Sequence; traj _ outlineHitData.traj; segNum _ outlineHitData.segNum; cpNum _ outlineHitData.cpNum; controlPointSeq _ GGSequence.CreateFromControlPoint[traj, segNum, cpNum]; parts _ PartsFromSequence[slice, controlPointSeq]; }; ENDCASE => ERROR; }; segment => { outlineHitData: OutlineHitData _ NARROW[hitData]; SELECT outlineHitData.hitType FROM joint => { traj: Traj _ outlineHitData.traj; jointNum: NAT _ outlineHitData.jointNum; this: INT _ IF jointNum>GGTraj.HiSegment[traj] THEN -1 ELSE jointNum; previous: INT _ GGTraj.PreviousSegmentNum[traj, jointNum]; parts _ PartsFromSequence[slice, GGSequence.CreateFromSegment[traj, IF this#-1 THEN this ELSE previous]]; }; segment, controlPoint => { seq: Sequence _ GGSequence.CreateFromSegment[outlineHitData.traj, outlineHitData.segNum]; parts _ PartsFromSequence[slice, seq]; }; ENDCASE => ERROR; }; traj => { outlineHitData: OutlineHitData _ NARROW[hitData]; traj: Traj _ outlineHitData.traj; seq: Sequence _ GGSequence.CreateComplete[traj]; parts _ PartsFromSequence[slice, seq]; }; topLevel => { sliceD _ slice.class.newParts[slice, NIL, slice]; }; ENDCASE => ERROR; IF sliceD = NIL THEN sliceD _ DescriptorFromParts[slice, parts]; }; OutlineUnionParts: PROC [partsA: OutlineDescriptor, partsB: OutlineDescriptor] RETURNS [aPlusB: OutlineDescriptor] = { realPartsA: OutlineParts _ NARROW[partsA.parts]; realPartsB: OutlineParts _ NARROW[partsB.parts]; union: OutlineParts; listA: LIST OF Sequence _ realPartsA.seqs; listB: LIST OF Sequence _ realPartsB.seqs; ptr: LIST OF Sequence; IF partsA.parts = NIL THEN RETURN[partsB]; IF partsB.parts = NIL THEN RETURN[partsA]; IF IsEmpty[realPartsA] THEN RETURN[partsB]; IF IsEmpty[realPartsB] THEN RETURN[partsA]; IF IsComplete[realPartsA] THEN RETURN[partsA]; IF IsComplete[realPartsB] THEN RETURN[partsB]; union _ NEW[OutlinePartsObj]; [union.seqs, ptr] _ GGUtility.StartSequenceList[]; UNTIL listA = NIL DO IF listA.first = NIL THEN [union.seqs, ptr] _ GGUtility.AddSequence[listB.first, union.seqs, ptr] ELSE IF listB.first = NIL THEN [union.seqs, ptr] _ GGUtility.AddSequence[listA.first, union.seqs, ptr] ELSE { newSeq: Sequence _ GGSequence.Union[listA.first, listB.first]; [union.seqs, ptr] _ GGUtility.AddSequence[newSeq, union.seqs, ptr]; }; listA _ listA.rest; listB _ listB.rest; ENDLOOP; aPlusB _ GGOutline.DescriptorFromParts[partsA.slice, union]; }; OutlineDifferenceParts: PROC [partsA: OutlineDescriptor, partsB: OutlineDescriptor] RETURNS [aMinusB: OutlineDescriptor] = { realPartsA, realPartsB: OutlineParts; diff: OutlineParts; listA: LIST OF Sequence; listB: LIST OF Sequence; ptr: LIST OF Sequence; IF partsA.parts = NIL OR partsB = NIL OR partsB.parts = NIL THEN RETURN[partsA]; realPartsA _ NARROW[partsA.parts]; realPartsB _ NARROW[partsB.parts]; listA _ realPartsA.seqs; listB _ realPartsB.seqs; diff _ NEW[OutlinePartsObj]; [diff.seqs, ptr] _ GGUtility.StartSequenceList[]; UNTIL listA = NIL DO IF listA.first = NIL THEN [diff.seqs, ptr] _ GGUtility.AddSequence[NIL, diff.seqs, ptr] ELSE IF listB.first = NIL THEN [diff.seqs, ptr] _ GGUtility.AddSequence[listA.first, diff.seqs, ptr] ELSE { newSeq: Sequence _ GGSequence.Difference[listA.first, listB.first]; [diff.seqs, ptr] _ GGUtility.AddSequence[newSeq, diff.seqs, ptr]; }; listA _ listA.rest; listB _ listB.rest; ENDLOOP; aMinusB _ GGOutline.DescriptorFromParts[partsA.slice, diff]; }; OutlineMovingParts: PROC [slice: Outline, selectedParts: SliceParts] RETURNS [background, overlay, rubber, drag: OutlineDescriptor] = { OPEN GGUtility; outlineParts: OutlineParts _ NARROW[selectedParts]; bkgdParts, overParts, rubberParts, dragParts: OutlineParts; bkgdSeq, overSeq, rubberSeq, dragSeq: Sequence; bkgdPtr, overPtr, rubberPtr, dragPtr: LIST OF Sequence; children: LIST OF Traj; filled: BOOL _ slice.fillColor # NIL; nullD: OutlineDescriptor _ slice.nullDescriptor; IF IsEmpty[outlineParts] THEN { background _ overlay _ rubber _ drag _ nullD; RETURN; }; IF IsComplete[outlineParts] THEN { background _ overlay _ rubber _ nullD; drag _ DescriptorFromParts[slice, selectedParts]; RETURN; }; bkgdParts _ NEW[OutlinePartsObj]; overParts _ NEW[OutlinePartsObj]; rubberParts _ NEW[OutlinePartsObj]; dragParts _ NEW[OutlinePartsObj]; [bkgdParts.seqs, bkgdPtr] _ StartSequenceList[]; [overParts.seqs, overPtr] _ StartSequenceList[]; [rubberParts.seqs, rubberPtr] _ StartSequenceList[]; [dragParts.seqs, dragPtr] _ StartSequenceList[]; children _ slice.children; FOR list: LIST OF Sequence _ outlineParts.seqs, list.rest UNTIL list = NIL DO IF list.first = NIL THEN { [rubberParts.seqs, rubberPtr] _ AddSequence[NIL, rubberParts.seqs, rubberPtr]; [dragParts.seqs, dragPtr] _ AddSequence[NIL, dragParts.seqs, dragPtr]; IF filled THEN { overSeq _ GGSequence.CreateComplete[children.first]; [overParts.seqs, overPtr] _ AddSequence[overSeq, overParts.seqs, overPtr]; [bkgdParts.seqs, bkgdPtr] _ AddSequence[NIL, bkgdParts.seqs, bkgdPtr]; } ELSE { bkgdSeq _ GGSequence.CreateComplete[children.first]; [bkgdParts.seqs, bkgdPtr] _ AddSequence[bkgdSeq, bkgdParts.seqs, bkgdPtr]; [overParts.seqs, overPtr] _ AddSequence[NIL, overParts.seqs, overPtr]; }; } ELSE { [bkgdSeq, overSeq, rubberSeq, dragSeq] _ GGSequence.TrajMovingParts[list.first]; IF filled THEN { [bkgdParts.seqs, bkgdPtr] _ AddSequence[NIL, bkgdParts.seqs, bkgdPtr]; [overParts.seqs, overPtr] _ AddSequence[overSeq, overParts.seqs, overPtr]; [rubberParts.seqs, rubberPtr] _ AddSequence[rubberSeq, rubberParts.seqs, rubberPtr]; [dragParts.seqs, dragPtr] _ AddSequence[dragSeq, dragParts.seqs, dragPtr]; } ELSE { [bkgdParts.seqs, bkgdPtr] _ AddSequence[overSeq, bkgdParts.seqs, bkgdPtr]; [overParts.seqs, overPtr] _ AddSequence[NIL, overParts.seqs, overPtr]; [rubberParts.seqs, rubberPtr] _ AddSequence[rubberSeq, rubberParts.seqs, rubberPtr]; [dragParts.seqs, dragPtr] _ AddSequence[dragSeq, dragParts.seqs, dragPtr]; }; }; children _ children.rest; ENDLOOP; background _ GGOutline.DescriptorFromParts[slice, bkgdParts]; overlay _ GGOutline.DescriptorFromParts[slice, overParts]; rubber _ GGOutline.DescriptorFromParts[slice, rubberParts]; drag _ GGOutline.DescriptorFromParts[slice, dragParts]; }; NearestJointToHitSpot: PUBLIC PROC [caretPt: Point, traj: Traj, hitJointNum: INT, hitSegNum: INT, hitType: HitType] RETURNS [jointNum: NAT] = { nextNum: NAT; p1, p2: Point; d1, d2: REAL; SELECT hitType FROM joint => { jointNum _ hitJointNum; }; segment, controlPoint => { nextNum _ GGTraj.FollowingJoint[traj, hitSegNum]; p1 _ GGTraj.FetchJointPos[traj, hitSegNum]; p2 _ GGTraj.FetchJointPos[traj, nextNum]; d1 _ Vectors2d.DistanceSquared[p1, caretPt]; d2 _ Vectors2d.DistanceSquared[p2, caretPt]; IF d1 <= d2 THEN jointNum _ hitSegNum ELSE jointNum _ nextNum; }; ENDCASE => ERROR Problem[msg: "NearestJointToHitSpot NYI"]; }; -- end of NearestJointToHitSpot NearestJointToHitData: PUBLIC PROC [hitData: REF ANY] RETURNS [jointNum: NAT, traj: Traj] = { outlineHitData: OutlineHitData _ NARROW[hitData]; nextNum: NAT; p1, p2: Point; d1, d2: REAL; traj _ outlineHitData.traj; SELECT outlineHitData.hitType FROM joint => { jointNum _ outlineHitData.jointNum; }; segment, controlPoint => { nextNum _ GGTraj.FollowingJoint[traj, outlineHitData.segNum]; p1 _ GGTraj.FetchJointPos[traj, outlineHitData.segNum]; p2 _ GGTraj.FetchJointPos[traj, nextNum]; d1 _ Vectors2d.DistanceSquared[p1, outlineHitData.hitPoint]; d2 _ Vectors2d.DistanceSquared[p2, outlineHitData.hitPoint]; IF d1 <= d2 THEN jointNum _ outlineHitData.segNum ELSE jointNum _ nextNum; }; ENDCASE => ERROR; }; OutlineAugmentParts: PROC [sliceD: OutlineDescriptor, selectClass: SelectionClass] RETURNS [more: OutlineDescriptor] = { sliceParts: OutlineParts _ NARROW[sliceD.parts]; FOR list: LIST OF Sequence _ sliceParts.seqs, list.rest UNTIL list = NIL DO IF list.first # NIL THEN GGSequence.AugmentParts[list.first, selectClass]; ENDLOOP; more _ GGOutline.DescriptorFromParts[sliceD.slice, sliceParts]; }; OutlineSetSelectedFields: PROC [sliceD: OutlineDescriptor, selected: BOOL, selectClass: SelectionClass] = { sliceParts: OutlineParts _ NARROW[sliceD.parts]; FOR list: LIST OF Sequence _ sliceParts.seqs, list.rest UNTIL list = NIL DO IF list.first # NIL THEN GGTraj.SetSelectedFields[list.first, selected, selectClass]; ENDLOOP; }; PointGeneratorData: TYPE = REF PointGeneratorDataObj; PointGeneratorDataObj: TYPE = RECORD [ seqPtr: LIST OF Sequence, pointGen: GGModelTypes.PointGenerator -- the point generator for the first sequence of seqPtr ]; PointPairGeneratorData: TYPE = REF PointPairGeneratorDataObj; PointPairGeneratorDataObj: TYPE = RECORD [ seqPtr: LIST OF Sequence, pointGen: GGModelTypes.PointPairGenerator -- the pair generator for first sequence of seqPtr ]; OutlinePointsInDescriptor: PUBLIC PROC [sliceD: OutlineDescriptor] RETURNS [pointGen: GGModelTypes.OutlinePointGenerator] = { realParts: OutlineParts _ NARROW[sliceD.parts]; seqPtr: LIST OF Sequence _ realParts.seqs; trajPointGen: GGModelTypes.PointGenerator; pointGenData: PointGeneratorData; trajPointGen _ IF seqPtr.first = NIL THEN NIL ELSE GGTraj.PointsInDescriptor[seqPtr.first]; pointGenData _ NEW[PointGeneratorDataObj _ [seqPtr: seqPtr, pointGen: trajPointGen]]; pointGen _ NEW[GGModelTypes.OutlinePointGeneratorObj _ [sliceD, 0, 0, pointGenData]]; }; OutlinePointPairsInDescriptor: PUBLIC PROC [sliceD: OutlineDescriptor] RETURNS [pointPairGen: GGModelTypes.OutlinePointPairGenerator] = { realParts: OutlineParts _ NARROW[sliceD.parts]; seqPtr: LIST OF Sequence _ realParts.seqs; trajPointGen: GGModelTypes.PointPairGenerator; pointGenData: PointPairGeneratorData; trajPointGen _ IF seqPtr.first = NIL THEN NIL ELSE GGTraj.PointPairsInDescriptor[seqPtr.first]; pointGenData _ NEW[PointPairGeneratorDataObj _ [seqPtr: seqPtr, pointGen: trajPointGen]]; pointPairGen _ NEW[GGModelTypes.OutlinePointPairGeneratorObj _ [sliceD, 0, 0, pointGenData]]; }; OutlineNextPoint: PUBLIC PROC [pointGen: GGModelTypes.OutlinePointGenerator] RETURNS [pointAndDone: GGModelTypes.PointAndDone] = { pgd: PointGeneratorData _ NARROW[pointGen.classSpecific]; seqPtr: LIST OF Sequence _ pgd.seqPtr; IF seqPtr = NIL THEN RETURN[[[0,0], TRUE]]; WHILE TRUE DO IF seqPtr.first # NIL THEN { pointAndDone _ GGTraj.NextPoint[pgd.pointGen]; IF NOT pointAndDone.done THEN { pgd.seqPtr _ seqPtr; RETURN; }; seqPtr _ seqPtr.rest; -- on to the next sequence }; UNTIL seqPtr = NIL OR seqPtr.first # NIL DO seqPtr _ seqPtr.rest ENDLOOP; IF seqPtr = NIL THEN RETURN[[[0,0], TRUE]]; pgd.pointGen _ GGTraj.PointsInDescriptor[seqPtr.first]; ENDLOOP; }; OutlineNextPointPair: PUBLIC PROC [pointPairGen: GGModelTypes.OutlinePointPairGenerator] RETURNS [pointPairAndDone: GGModelTypes.PointPairAndDone] = { pgd: PointPairGeneratorData _ NARROW[pointPairGen.classSpecific]; seqPtr: LIST OF Sequence _ pgd.seqPtr; IF seqPtr = NIL THEN RETURN[[[0,0], [0,0], TRUE]]; WHILE TRUE DO IF seqPtr.first # NIL THEN { pointPairAndDone _ GGTraj.NextPointPair[pgd.pointGen]; IF NOT pointPairAndDone.done THEN { pgd.seqPtr _ seqPtr; RETURN; }; seqPtr _ seqPtr.rest; -- on to the next sequence }; UNTIL seqPtr = NIL OR seqPtr.first # NIL DO seqPtr _ seqPtr.rest ENDLOOP; IF seqPtr = NIL THEN RETURN[[[0,0], [0,0], TRUE]]; pgd.pointGen _ GGTraj.PointPairsInDescriptor[seqPtr.first]; ENDLOOP; }; GoodPointType: TYPE = {joint, intersectionPoint, midpoint, controlPoint, slice, none}; OutlineClosestPoint: PROC [sliceD: OutlineDescriptor, testPoint: Point, tolerance: REAL] RETURNS [bestPoint: Point, bestDist: REAL, hitData: REF ANY, success: BOOL] = { parts: OutlineParts _ NARROW[sliceD.parts]; thisDist: REAL; thisSegNum, bestSegNum, thisCP, bestCP, thisJointNum, bestJointNum: NAT; thisPoint: Point; thisSuccess: BOOL; bestTraj: Traj; bestType: GoodPointType _ none; outlineHitData: OutlineHitData; success _ FALSE; bestDist _ GGUtility.plusInfinity; FOR list: LIST OF Sequence _ parts.seqs, list.rest UNTIL list = NIL DO [thisDist, thisSegNum, thisCP, thisPoint, thisSuccess] _ GGTraj.NearestControlPoint[testPoint, list.first, tolerance]; IF thisSuccess AND thisDist < bestDist THEN { bestType _ controlPoint; bestPoint _ thisPoint; bestDist _ thisDist; bestTraj _ list.first.traj; bestSegNum _ thisSegNum; bestCP _ thisCP; success _ TRUE; }; ENDLOOP; FOR list: LIST OF Sequence _ parts.seqs, list.rest UNTIL list = NIL DO [thisDist, thisJointNum, thisPoint, thisSuccess] _ GGTraj.NearestJoint[testPoint, list.first, tolerance]; IF thisSuccess AND thisDist < bestDist THEN { bestType _ joint; bestPoint _ thisPoint; bestDist _ thisDist; bestTraj _ list.first.traj; bestJointNum _ thisJointNum; success _ TRUE; }; ENDLOOP; IF success THEN { SELECT bestType FROM controlPoint => { hitData _ outlineHitData _ NEW[OutlineHitDataObj _ [ bestTraj, controlPoint, bestSegNum, bestCP, -1, bestPoint]]; }; joint => { hitData _ outlineHitData _ NEW[OutlineHitDataObj _ [ bestTraj, joint, -1, -1, bestJointNum, bestPoint]]; }; ENDCASE => ERROR; }; }; OutlineClosestJointToHitData: PUBLIC PROC [sliceD: OutlineDescriptor, mapPoint: Point, hitData: REF ANY] RETURNS [jointD: OutlineDescriptor, point: Point] = { traj: Traj; hitType: GGModelTypes.TrajPartType; segNum, cpNum, jointNum: INT; hitPoint: Point; jointSeq: Sequence; [traj, hitType, segNum, cpNum, jointNum, hitPoint] _ GGOutline.UnpackHitData[hitData]; SELECT hitType FROM joint => { jointSeq _ GGSequence.CreateFromJoint[traj, jointNum]; point _ mapPoint; }; controlPoint => { jointSeq _ GGSequence.CreateFromControlPoint[traj, segNum, cpNum]; point _ mapPoint; }; segment => { success: BOOL; jointPoint, cpPoint: Point; seg: Segment; seg _ GGTraj.FetchSegment[traj, segNum]; [jointNum, ----] _ GGOutline.NearestJointToHitData[hitData]; jointPoint _ GGTraj.FetchJointPos[traj, jointNum]; [cpPoint, cpNum, success] _ seg.class.closestControlPoint[seg, mapPoint, GGUtility.plusInfinity]; IF NOT success THEN { -- its a joint for sure jointSeq _ GGSequence.CreateFromJoint[traj, jointNum]; point _ jointPoint; } ELSE { -- could be a cp instead of a joint cpDist: REAL _ Vectors2d.DistanceSquared[cpPoint, mapPoint]; jointDist: REAL _ Vectors2d.DistanceSquared[jointPoint, mapPoint]; tisAJoint: BOOL _ jointDist <= cpDist; IF tisAJoint THEN { jointSeq _ GGSequence.CreateFromJoint[traj, jointNum]; point _ jointPoint; } ELSE { jointSeq _ GGSequence.CreateFromControlPoint[traj, segNum, cpNum]; point _ cpPoint; }; }; }; ENDCASE => ERROR; jointD _ GGOutline.DescriptorFromSequence[sliceD.slice, jointSeq]; }; OutlineClosestSegment: PUBLIC PROC [sliceD: OutlineDescriptor, testPoint: Point, tolerance: REAL] RETURNS [bestPoint: Point, bestDist: REAL, hitData: REF ANY, success: BOOL] = { parts: OutlineParts _ NARROW[sliceD.parts]; thisDist: REAL; thisSegNum, bestSegNum: NAT; thisPoint: Point; thisSuccess: BOOL; bestTraj: Traj; outlineHitData: OutlineHitData; success _ FALSE; bestDist _ GGUtility.plusInfinity; FOR list: LIST OF Sequence _ parts.seqs, list.rest UNTIL list = NIL DO [thisDist, thisSegNum, thisPoint, thisSuccess] _ GGTraj.NearestSegment[testPoint, list.first, tolerance]; IF thisSuccess AND thisDist < bestDist THEN { bestPoint _ thisPoint; bestDist _ thisDist; bestTraj _ list.first.traj; bestSegNum _ thisSegNum; success _ TRUE; }; ENDLOOP; IF success THEN { hitData _ outlineHitData _ NEW[OutlineHitDataObj _ [ bestTraj, segment, bestSegNum, -1, -1, bestPoint]]; }; }; OutlineLineIntersection: PUBLIC PROC [sliceD: OutlineDescriptor, line: Line] RETURNS [points: LIST OF Point, pointCount: NAT] = { segGen: GGModelTypes.SegmentGenerator; thesePoints: LIST OF Point; thisCount: NAT; seq: Sequence; parts: OutlineParts _ NARROW[sliceD.parts]; points _ NIL; pointCount _ 0; FOR list: LIST OF Sequence _ parts.seqs, list.rest UNTIL list = NIL DO seq _ list.first; IF seq = NIL THEN LOOP; segGen _ GGSequence.SegmentsInSequence[seq]; FOR seg: Segment _ GGSequence.NextSegment[segGen], GGSequence.NextSegment[segGen] UNTIL seg = NIL DO [thesePoints, thisCount] _ seg.class.lineIntersection[seg, line]; FOR list: LIST OF Point _ thesePoints, list.rest UNTIL list = NIL DO points _ CONS[list.first, points]; ENDLOOP; pointCount _ pointCount + thisCount; ENDLOOP; ENDLOOP; }; OutlineCircleIntersection: PROC [sliceD: OutlineDescriptor, circle: Circle] RETURNS [points: LIST OF Point, pointCount: NAT] = { segGen: GGModelTypes.SegmentGenerator; thesePoints: LIST OF Point; thisCount: NAT; seq: Sequence; parts: OutlineParts _ NARROW[sliceD.parts]; points _ NIL; pointCount _ 0; FOR list: LIST OF Sequence _ parts.seqs, list.rest UNTIL list = NIL DO seq _ list.first; IF seq = NIL THEN LOOP; segGen _ GGSequence.SegmentsInSequence[seq]; FOR seg: Segment _ GGSequence.NextSegment[segGen], GGSequence.NextSegment[segGen] UNTIL seg = NIL DO [thesePoints, thisCount] _ seg.class.circleIntersection[seg, circle]; FOR list: LIST OF Point _ thesePoints, list.rest UNTIL list = NIL DO points _ CONS[list.first, points]; ENDLOOP; ENDLOOP; pointCount _ pointCount + thisCount; ENDLOOP; }; OutlineHitDataAsSimpleCurve: PROC [slice: Outline, hitData: REF ANY] RETURNS [simpleCurve: REF ANY] = { outlineHitData: OutlineHitData _ NARROW[hitData]; traj: Traj _ outlineHitData.traj; SELECT outlineHitData.hitType FROM joint, controlPoint => RETURN[NIL]; segment => { segNum: INT _ outlineHitData.segNum; seg: Segment _ GGTraj.FetchSegment[traj, segNum]; simpleCurve _ seg.class.asSimpleCurve[seg, outlineHitData.hitPoint]; }; ENDCASE => ERROR; }; OutlineSetDefaults: PROC [slice: Outline, parts: SliceParts, defaults: DefaultData] = { realParts: OutlineParts _ NARROW[parts]; slice.fillColor _ defaults.fillColor; FOR list: LIST OF Sequence _ realParts.seqs, list.rest UNTIL list = NIL DO IF list.first # NIL THEN { seq: Sequence _ list.first; GGSequence.SetStrokeWidth[seq, defaults.strokeWidth]; GGTraj.SetTrajStrokeJoint[seq.traj, defaults.strokeJoint]; GGSequence.SetStrokeEnd[seq, defaults.strokeEnd]; GGSequence.SetStrokeDashed[seq, defaults.dashed, GGUtility.CopyPattern[defaults.pattern], defaults.offset, defaults.length]; GGSequence.SetStrokeColor[seq, defaults.strokeColor]; }; ENDLOOP; }; OutlineSetStrokeWidth: PROC [slice: Outline, parts: SliceParts, strokeWidth: REAL] RETURNS [box: BoundBox] = { realParts: OutlineParts _ NARROW[parts]; FOR list: LIST OF Sequence _ realParts.seqs, list.rest UNTIL list = NIL DO IF list.first # NIL THEN GGSequence.SetStrokeWidth[list.first, strokeWidth]; ENDLOOP; UpdateBoundBox[slice]; box _ slice.boundBox; }; OutlineGetStrokeWidth: PROC [slice: Outline, parts: SliceParts] RETURNS [strokeWidth: REAL] = { realParts: OutlineParts _ NARROW[parts]; FOR list: LIST OF Sequence _ realParts.seqs, list.rest UNTIL list = NIL DO IF list.first # NIL THEN RETURN[GGSequence.GetStrokeWidth[list.first]]; ENDLOOP; RETURN[-1.0]; }; OutlineSetStrokeEnd: PROC [slice: Outline, parts: SliceParts, strokeEnd: StrokeEnd] = { realParts: OutlineParts _ NARROW[parts]; FOR list: LIST OF Sequence _ realParts.seqs, list.rest UNTIL list = NIL DO IF list.first # NIL THEN GGSequence.SetStrokeEnd[list.first, strokeEnd]; ENDLOOP; }; OutlineGetStrokeEnd: PROC [slice: Outline, parts: SliceParts] RETURNS [strokeEnd: StrokeEnd] = { realParts: OutlineParts _ NARROW[parts]; FOR list: LIST OF Sequence _ realParts.seqs, list.rest UNTIL list = NIL DO IF list.first # NIL THEN RETURN[GGSequence.GetStrokeEnd[list.first]]; ENDLOOP; RETURN[round]; }; OutlineSetStrokeJoint: PROC [slice: Outline, parts: SliceParts, strokeJoint: StrokeJoint] = { realParts: OutlineParts _ NARROW[parts]; FOR list: LIST OF Sequence _ realParts.seqs, list.rest UNTIL list = NIL DO IF list.first # NIL THEN GGTraj.SetTrajStrokeJoint[list.first.traj, strokeJoint]; ENDLOOP; }; OutlineGetStrokeJoint: PROC [slice: Outline, parts: SliceParts] RETURNS [strokeJoint: StrokeJoint] = { realParts: OutlineParts _ NARROW[parts]; FOR list: LIST OF Sequence _ realParts.seqs, list.rest UNTIL list = NIL DO IF list.first # NIL THEN RETURN[GGTraj.GetTrajStrokeJoint[list.first.traj]]; ENDLOOP; RETURN[round]; }; OutlineSetStrokeColor: PROC [slice: Outline, parts: SliceParts, color: Color] = { realParts: OutlineParts _ NARROW[parts]; FOR list: LIST OF Sequence _ realParts.seqs, list.rest UNTIL list = NIL DO IF list.first # NIL THEN GGSequence.SetStrokeColor[list.first, color]; ENDLOOP; }; OutlineGetStrokeColor: PROC [slice: Outline, parts: SliceParts] RETURNS [color: Color] = { realParts: OutlineParts _ NARROW[parts]; FOR list: LIST OF Sequence _ realParts.seqs, list.rest UNTIL list = NIL DO IF list.first # NIL THEN RETURN[GGSequence.GetStrokeColor[list.first]]; ENDLOOP; RETURN[NIL]; }; OutlineSetFillColor: PROC [slice: Outline, color: Color] = { slice.fillColor _ color; }; OutlineGetFillColor: PROC [slice: Outline] RETURNS [color: Color] = { color _ slice.fillColor; }; OutlineSetDashed: PUBLIC PROC [slice: Outline, parts: SliceParts, dashed: BOOL, pattern: SequenceOfReal _ NIL, offset: REAL _ 0.0, length: REAL _ -1.0] = { realParts: OutlineParts _ NARROW[parts]; FOR list: LIST OF Sequence _ realParts.seqs, list.rest UNTIL list = NIL DO IF list.first # NIL THEN GGSequence.SetStrokeDashed[list.first, dashed, pattern, offset, length]; ENDLOOP; }; OutlineGetDashed: PROC [slice: Outline, parts: SliceParts] RETURNS [dashed: BOOL, pattern: SequenceOfReal, offset, length: REAL] = { realParts: OutlineParts _ NARROW[parts]; FOR list: LIST OF Sequence _ realParts.seqs, list.rest UNTIL list = NIL DO IF list.first # NIL THEN { [dashed, pattern, offset, length] _ GGSequence.GetStrokeDashed[list.first]; RETURN; }; ENDLOOP; RETURN[FALSE, NIL, 0.0, 0.0]; }; AppendTrajList: PUBLIC PROC [list1, list2: LIST OF Traj] RETURNS [result: LIST OF Traj] = { pos: LIST OF Traj; newCell: LIST OF Traj; IF list1 = NIL THEN RETURN[list2]; result _ CONS[list1.first, NIL]; pos _ result; FOR l: LIST OF Traj _ list1.rest, l.rest UNTIL l = NIL DO newCell _ CONS[l.first, NIL]; pos.rest _ newCell; pos _ newCell; ENDLOOP; pos.rest _ list2; }; globalOutlineClass: OutlineClass; Init: PROC [] = { globalOutlineClass _ MakeOutlineClass[]; }; Init[]; END. ^GGOutlineImplA.mesa Copyright c 1986 by Xerox Corporation. All rights reserved. Last edited by Bier on June 2, 1986 5:10:01 pm PDT Contents: Procedures to implement the Outline Slice Class in Gargoyle. Outlines consist of Trajectories which in turn consist of Segments. Outline is the most important slice class. NoOp Class Routines Style SIGNAL Problem [msg: "All slice classes must have this proc."]; SIGNAL Problem [msg: "All slice classes must have this proc."]; The Outline Class Fundamentals Drawing Transforming Textual Description Parts Part Generators Hit Testing Style lineEnds: lineEnds, Fundamentals Computes it from traj boxes. Drawing GGModelTypes.OutlineDrawPartsProc PathProc: TYPE ~ PROC[moveTo: MoveToProc, lineTo: LineToProc, curveTo: CurveToProc, conicTo: ConicToProc, arcTo: ArcToProc]; Fill in the outline if necessary. Draw the strokes. Drawing Utilities Fill the outline if necessary. Someday this will be a call to path.class.buildPath[traj, moveTo, lineTo, curveTo, conicTo, arcTo]; path.class.buildPath[traj, moveTo, lineTo, curveTo, conicTo, arcTo]; Draw the strokes. Will be path.class.drawBorder[fence, dc, ggData]; Will be path.class.drawBorder[holeList.first, dc, ggData]; Transforming Textual Description f.PutF[" strokeEnd: "]; GGParseOut.WriteStrokeEnd[f, slice.lineEnds]; Parts Pass on the real work to GGSequence. Filled outlines have the property that if any of their subparts are on the overlay plane, then all of their parts are on the overlay plane. This trajectory is not at all selected. It will not move. If this outline is filled, put it on the overlay. Otherwise, leave it in the background. If the outline is filled, it has no background parts. Otherwise, all of its non-moving parts can be left on the background. If selectClass = normal, then for each segment mentioned in parts, place it, its adjacent joints, and its control points in more. If selectClass # normal then just place each segment and its control points in more. This mutates the OutlineDescriptor which may mean trouble. -- Bier, March 9, 1987 Set the selected fields of all of the joints and segments mentioned in sliceD. Hit Testing Finds the intersection of the line with those slice parts mentioned in sliceD. simpleCurve will be of type Edge, Arc, etc. There will be Conic and Bezier types as well. Style maybe should implement parts=NIL to mean all parts this is inefficient but easy to implement Sets the stroke width of the named parts of slice to be strokeWidth. The following is very conservative and wasteful Get the stroke width of the first segment you come to. Sets the stroke end of the named parts of slice to be strokeEnd. Get the stroke end of the first segment you come to. Sets the stroke Joint of the named parts of slice to be strokeEnd. Get the stroke joint of the first segment you come to. Sets the stroke color of the named parts of slice to be color. Get the stroke color of the named parts of slice. Sets the fill color of the slice to be color. Get the fill color of the slice. Get the dash pattern of the named parts of slice. Utility Routines Non-destructive (copies the first list). Ê1ŘJ˜Icodešœ™Kšœ Ïmœ1™K˜Kšœ˜Kšœ˜Kšœ!˜!Kšœ/˜/Kšœ ˜ šžœCžœžœž˜YKšœ ˜ Kšœ.žœ ˜=Kšœ˜Kšžœ˜—K˜K˜—K™Kšœ™K˜šŸœžœTžœ˜sKšœ!™!Kšžœ žœžœžœ&˜?Kšœ˜K˜K˜—K™šŸœžœx˜’Kšœžœ˜0šÐbn œ˜!Kšœ žœžœh™}š¡ œ˜K˜ Kšœ2˜2Kšœ'˜'šžœžœžœž˜,Kšœ#˜#Kšœ:˜:—Kšžœ˜K˜—š¡œ˜*K˜ Kšœ2˜2KšžœžœžœD˜tKšœ'˜'šžœžœžœž˜,Kšœ#˜#KšœjžœT˜ÁKšžœ˜—K˜—K˜Kšœ"˜"Kšœ žœ˜Kšœ ˜ K˜Kšœ4˜4šžœ žœžœ˜Kšœ?˜?Kšœ=˜=K˜—šžœ˜Kšœ3˜3Kšœ#˜#K˜—š žœ žœžœ+žœ žœž˜WKšœ˜Kšœ4˜4šžœ žœžœ˜KšœL˜LKšœ@˜@K˜—šžœ˜Kšœ@˜@Kšœ&˜&K˜—Kšžœ žœV˜gKšžœO˜SKšžœ˜—K˜—K˜Kšœ ˜ K˜Kšœ˜šœ#˜#Kš !™!—šžœžœžœžœ˜4Kšœ˜šžœžœž˜ šœ"˜"Kšœ˜Kšœ˜—šœ!˜!šžœ+žœ˜3Kšœ˜Kšœ&˜&K˜—K˜—Kšžœžœ˜—K˜"K˜Kš ™—Kšœ5˜5Kšžœ žœžœ5žœ˜jš žœ žœžœ+žœ žœž˜WKšœ>˜>Kšžœ žœžœ5žœ%˜sKšžœ˜—K˜K˜—K˜šŸœžœšžœ˜ÅK˜2Kšœžœžœ ˜&Kšœžœ˜+Kšœžœ ˜#Kšžœžœžœžœ˜/Kš žœžœžœ žœžœžœ˜6š žœžœžœ žœžœ˜0Kšœ˜šžœ7žœžœž˜SKšžœžœžœ˜®KšžœžœžœžœŽ˜®Kšœ˜Kšžœ˜—K˜—šžœžœžœžœ˜"šžœ7žœžœž˜SKšžœžœžœGžœ=˜¥Kšžœ˜—K˜—šžœ˜šžœ.žœ žœž˜GKšžœžœžœ2žœL˜œKšžœ˜—K˜—K˜K˜—K˜š¡œžœžœHžœ-˜¢Jšœ žœ˜Jšœ˜Jšœ ˜ Jšœ ˜ Jšœ žœ˜JšœÏcœA˜_šžœ ž˜šœ ˜ Jšœ;˜;Jš œžœ$žœ%žœžœ˜hJšžœ žœžœ ˜4Jšžœžœžœ˜,J˜—Jšœ4˜4Jšžœ¢˜—K˜—K™K™K˜šŸ œžœ+˜˜Qšžœ˜Kšœžœ/˜;Kšœ žœ2˜AKšœžœžœ7˜WKšžœ9˜=K˜—Kšœ&˜&K˜—˜Kšœ ˜ Kšœžœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜KšœI˜IKšœ2˜2K˜—Kšžœžœ˜—J˜—˜ Jšœ!žœ ˜1šžœž˜"šœ ˜ Jšœ!˜!Jšœ žœ˜(Kš œžœžœ!žœžœ ˜EJšœ žœ-˜:JšœDžœ žœžœ ˜iK˜—šœ˜KšœY˜YKšœ&˜&K˜—Kšžœžœ˜—J˜—˜ Jšœ!žœ ˜1Jšœ!˜!Jšœ0˜0Jšœ&˜&J˜—˜ Jšœ%žœ ˜1J˜—Jšžœžœ˜—Jšžœ žœžœ,˜@K˜K˜—šŸœžœ8žœ ˜vKšœžœ˜0Kšœžœ˜0Kšœ˜Kšœžœžœ˜*Kšœžœžœ˜*Kšœžœžœ ˜K˜Kšžœžœžœžœ ˜*Kšžœžœžœžœ ˜*Kšžœžœžœ ˜+Kšžœžœžœ ˜+Kšžœžœžœ ˜.Kšžœžœžœ ˜.Kšœžœ˜Kšœ2˜2šžœ žœž˜KšžœžœžœH˜aKšžœžœžœžœH˜fšžœ˜Kšœ>˜>KšœC˜CK˜—Kšœ˜K˜Kšžœ˜—Kšœ<˜˜WKšœ@™@Kšœžœ˜(š žœžœžœ&žœžœž˜JJšžœžœžœ0˜HJšžœ˜—K˜K˜—š¡œžœ%žœ˜`Kšœ4™4Kšœžœ˜(š žœžœžœ&žœžœž˜JJšžœžœžœžœ&˜EJšžœ˜—Jšžœ˜K˜K˜—š¡œžœB˜]KšœB™BKšœžœ˜(š žœžœžœ&žœžœž˜JJšžœžœžœ9˜QJšžœ˜—K˜K˜—š¡œžœ%žœ˜fKšœ6™6Kšœžœ˜(š žœžœžœ&žœžœž˜JJšžœžœžœžœ-˜LJšžœ˜—Jšžœ˜K˜K˜—š¡œžœ6˜QKšœ>™>Kšœžœ˜(š žœžœžœ&žœžœž˜JJšžœžœžœ.˜FJšžœ˜—K˜K˜—š¡œžœ%žœ˜ZKšœ1™1Kšœžœ˜(š žœžœžœ&žœžœž˜JJšžœžœžœžœ(˜GJšžœ˜—Jšžœžœ˜ K˜K˜K˜—šŸ¡ œžœ#˜