DIRECTORY Cubic2, CubicSplines, GGAngle, GGBasicTypes, GGBoundBox, GGCircles, GGLines, GGModelTypes, GGParseIn, GGParseOut, GGSegment, GGSegmentTypes, GGTransform, GGVector, Imager, ImagerTransformation, ImagerPath, ImagerBackdoor, IO, Rope; GGSegmentImplB: CEDAR PROGRAM IMPORTS GGAngle, GGBoundBox, GGCircles, GGLines, GGParseIn, GGParseOut, GGSegment, GGTransform, GGVector, Imager, ImagerTransformation EXPORTS GGSegment = BEGIN Arc: TYPE = GGBasicTypes.Arc; BitVector: TYPE = GGModelTypes.BitVector; BoundBox: TYPE = GGModelTypes.BoundBox; BuildPathProc: TYPE = GGSegmentTypes.BuildPathProc; BuildPathTransformProc: TYPE = GGSegmentTypes.BuildPathTransformProc; Circle: TYPE = GGBasicTypes.Circle; ClassDef: TYPE = GGSegment.ClassDef; ClassDefRec: TYPE = GGSegment.ClassDefRec; Edge: TYPE = GGBasicTypes.Edge; Line: TYPE = GGBasicTypes.Line; Point: TYPE = GGBasicTypes.Point; Segment: TYPE = GGSegmentTypes.Segment; SegmentClass: TYPE = REF SegmentClassObj; SegmentClassObj: TYPE = GGSegmentTypes.SegmentClassObj; SegmentObj: TYPE = GGSegmentTypes.SegmentObj; SelectedObjectData: TYPE = GGSegmentTypes.SelectedObjectData; SelectionClass: TYPE = GGSegmentTypes.SelectionClass; VEC: TYPE = Imager.VEC; Vector: TYPE = GGBasicTypes.Vector; X: NAT = CubicSplines.X; Y: NAT = CubicSplines.Y; defaultStrokeWidth: REAL _ 2.0; NoOpControlPointMoved: PUBLIC GGSegmentTypes.ControlPointMovedProc = {}; NoOpControlPointGet: PUBLIC GGSegmentTypes.ControlPointGetProc = {RETURN[ [0.0, 0.0] ]}; NoOpControlPointCount: PUBLIC GGSegmentTypes.ControlPointCountProc = {controlPointCount _ 0}; NoOpControlPointFieldSet: PUBLIC GGSegmentTypes.ControlPointFieldSetProc = {}; NoOpControlPointFieldGet: PUBLIC GGSegmentTypes.ControlPointFieldGetProc = { ERROR; }; NoOpClosestControlPoint: PUBLIC GGSegmentTypes.ClosestControlPointProc = { success _ FALSE; }; NoOpClosestPointAndTangent: PUBLIC GGSegmentTypes.ClosestPointAndTangentProc = { success _ FALSE; }; NoOpLineIntersection: PUBLIC GGSegmentTypes.LineIntersectionProc = { points _ NIL; pointCount _ 0; }; NoOpCircleIntersection: PUBLIC GGSegmentTypes.CircleIntersectionProc = { points _ NIL; pointCount _ 0; }; NoOpAsSimpleCurve: PUBLIC GGSegmentTypes.AsSimpleCurveProc = { simpleCurve _ NIL; }; NoOpAddJoint: PUBLIC GGSegmentTypes.AddJointProc = { ERROR; }; NoOpFileOut: PUBLIC GGSegmentTypes.FileOutProc = {}; CircleData: TYPE = REF CircleDataRec; CircleDataRec: TYPE = RECORD []; -- doesn't need any data BuildCircleClass: PROC [] RETURNS [circleClass: SegmentClass] = { circleClass _ NEW[SegmentClassObj _ [ type: $Circle, boundBox: CircleBoundBox, endPointMoved: CircleEndPointMoved, fileIn: CircleFileIn ]]; }; CircleBoundBox: PROC [seg: Segment] RETURNS [bBox: BoundBox] = { radius: REAL; cpHalf: REAL _ GGModelTypes.halfJointSize + 1; radius _ GGVector.Distance[seg.lo, seg.hi]; seg.bBox^ _ [seg.lo.x - radius - cpHalf, seg.lo.y - radius - cpHalf, seg.lo.x + radius + cpHalf, seg.lo.y + radius + cpHalf, FALSE, FALSE]; bBox _ seg.bBox; }; CircleFileIn: PROC [f: IO.STREAM, loPoint, hiPoint: Point, version: REAL] RETURNS [seg: Segment] = { data: CircleData _ NEW[CircleDataRec]; seg _ NEW[SegmentObj _ [ class: GGSegment.FetchSegmentClass[$Circle], looks: NIL, strokeWidth: 1.0, color: Imager.black, lo: loPoint, hi: hiPoint, bBox: GGBoundBox.CreateBoundBox[0,0,0,0], data: data, props: NIL]]; [] _ seg.class.boundBox[seg]; }; CircleEndPointMoved: PROC [seg: Segment, lo: BOOL, newPoint: Point] = { }; DiscData: TYPE = REF DiscDataRec; DiscDataRec: TYPE = RECORD []; -- doesn't need any data BuildDiscClass: PROC [] RETURNS [discClass: SegmentClass] = { discClass _ NEW[SegmentClassObj _ [ type: $Disc, boundBox: CircleBoundBox, endPointMoved: CircleEndPointMoved, fileIn: DiscFileIn ]]; }; DiscFileIn: PROC [f: IO.STREAM, loPoint, hiPoint: Point, version: REAL] RETURNS [seg: Segment] = { data: CircleData _ NEW[CircleDataRec]; seg _ NEW[SegmentObj _ [ class: GGSegment.FetchSegmentClass[$Disc], looks: NIL, strokeWidth: 1.0, color: Imager.black, lo: loPoint, hi: hiPoint, bBox: GGBoundBox.CreateBoundBox[0,0,0,0], -- gets set one line down. data: data, props: NIL]]; [] _ seg.class.boundBox[seg]; }; LineData: TYPE = REF LineDataRec; LineDataRec: TYPE = RECORD [ edge: Edge -- so we don't have to allocate it all of the time ]; BuildLineClass: PROC [] RETURNS [lineClass: SegmentClass] = { OPEN GGSegment; lineClass _ NEW[SegmentClassObj _ [ type: $Line, copyData: LineCopyData, reverse: LineReverse, boundBox: LineBoundBox, tightBox: LineTightBox, transform: LineTransform, endPointMoved: LineEndPointMoved, controlPointMoved: NoOpControlPointMoved, describe: LineDescribe, fileOut: GGSegment.NoOpFileOut, fileIn: LineFileIn, buildPath: LineBuildPath, buildPathTransform: LineBuildPathTransform, controlPointGet: NoOpControlPointGet, controlPointCount: NoOpControlPointCount, controlPointFieldSet: NoOpControlPointFieldSet, controlPointFieldGet: NoOpControlPointFieldGet, closestPoint: LineClosestPoint, closestControlPoint: NoOpClosestControlPoint, closestPointAndTangent: LineClosestPointAndTangent, lineIntersection: LineLineIntersection, circleIntersection: LineCircleIntersection, asSimpleCurve: LineAsSimpleCurve, addJoint: LineAddJoint, setStrokeWidth: LineSetStrokeWidth ]]; }; LineSetStrokeWidth: PROC [seg: Segment, strokeWidth: REAL] = { seg.strokeWidth _ strokeWidth; UpdateLineBoundBox[seg]; }; MakeLine: PUBLIC PROC [p0, p1: Point, props: LIST OF REF ANY] RETURNS [seg: Segment] = { lineSegment: LineData _ NEW[LineDataRec _ [ edge: GGLines.CreateEdge[p0, p1] ]]; seg _ NEW[SegmentObj _ [ class: GGSegment.FetchSegmentClass[$Line], looks: NIL, strokeWidth: defaultStrokeWidth, color: Imager.black, lo: p0, hi: p1, bBox: GGBoundBox.CreateBoundBox[0,0,0,0], -- gets set one line down. data: lineSegment, props: props ]]; UpdateLineBoundBox[seg]; }; LineBoundBox: PROC [seg: Segment] RETURNS [bBox: BoundBox] = { bBox _ seg.bBox; }; LineTightBox: PROC [seg: Segment] RETURNS [bBox: BoundBox] = { loX, loY, hiX, hiY: REAL; loX _ MIN[seg.lo.x, seg.hi.x]; hiX _ MAX[seg.lo.x, seg.hi.x]; loY _ MIN[seg.lo.y, seg.hi.y]; hiY _ MAX[seg.lo.y, seg.hi.y]; bBox _ GGBoundBox.CreateBoundBox[loX, loY, hiX, hiY]; }; UpdateLineBoundBox: PROC [seg: Segment] = { loX, loY, hiX, hiY: REAL; cpHalf: REAL _ GGModelTypes.halfJointSize + 1; strokeWidth: REAL _ seg.strokeWidth; pad: REAL _ MAX[cpHalf, strokeWidth]; loX _ MIN[seg.lo.x, seg.hi.x]; hiX _ MAX[seg.lo.x, seg.hi.x]; loY _ MIN[seg.lo.y, seg.hi.y]; hiY _ MAX[seg.lo.y, seg.hi.y]; seg.bBox^ _ [loX - pad, loY - pad, hiX + pad, hiY + pad, FALSE, FALSE]; }; LineCopyData: PROC [seg: Segment] RETURNS [data: REF ANY] = { lineSegment: LineData _ NARROW[seg.data]; data _ NEW[LineDataRec _ [edge: GGLines.CreateEdge[seg.lo, seg.hi] ]]; }; LineReverse: PROC [seg: Segment] = { }; LineBuildPath: PROC [seg: Segment, lineTo: ImagerPath.LineToProc, curveTo: ImagerPath.CurveToProc, conicTo: ImagerPath.ConicToProc, arcTo: ImagerPath.ArcToProc] = { lineTo[seg.hi]; }; LineBuildPathTransform: PROC [seg: Segment, transform: ImagerTransformation.Transformation, entire, lo, hi: BOOL, controlPoints: BitVector, lineTo: ImagerPath.LineToProc, curveTo: ImagerPath.CurveToProc, conicTo: ImagerPath.ConicToProc, arcTo: ImagerPath.ArcToProc] = { hiPoint: Point; hiPoint _ IF hi OR entire THEN GGTransform.Transform[transform, seg.hi] ELSE [seg.hi.x, seg.hi.y]; lineTo[ [hiPoint.x, hiPoint.y] ]; }; LineTransform: PROC [seg: Segment, transform: ImagerTransformation.Transformation] = { lineSegment: LineData _ NARROW[seg.data]; GGLines.FillEdge[seg.lo, seg.hi, lineSegment.edge]; UpdateLineBoundBox[seg]; }; LineEndPointMoved: PROC [seg: Segment, lo: BOOL, newPoint: Point] = { lineSegment: LineData _ NARROW[seg.data]; GGLines.FillEdge[seg.lo, seg.hi, lineSegment.edge]; UpdateLineBoundBox[seg]; }; LineDescribe: PROC [seg: Segment, self, lo, hi: BOOL, cps: BitVector] RETURNS [rope: Rope.ROPE] = { rope _ "Line"; }; LineFileIn: PROC [f: IO.STREAM, loPoint, hiPoint: Point, version: REAL] RETURNS [seg: Segment] = { seg _ MakeLine[loPoint, hiPoint, NIL]; }; useBBox: BOOL _ TRUE; PointIsInBox: PROC [test: Point, box: GGBasicTypes.BoundBoxObj] RETURNS [BOOL] = { RETURN[ NOT (test.x < box.loX OR test.x > box.hiX OR test.y < box.loY OR test.y > box.hiY) ]; }; LineClosestPoint: PROC [seg: Segment, testPoint: Point, tolerance: REAL] RETURNS [point: Point, success: BOOL] = { lineData: LineData _ NARROW[seg.data]; success _ FALSE; IF useBBox THEN { bigBox: GGBasicTypes.BoundBoxObj _ [seg.bBox.loX-tolerance, seg.bBox.loY-tolerance, seg.bBox.hiX+tolerance, seg.bBox.hiY+tolerance, FALSE, FALSE]; IF NOT PointIsInBox[testPoint, bigBox] THEN RETURN; }; point _ GGLines.NearestPointOnEdge[testPoint, lineData.edge]; success _ TRUE; }; LineClosestPointAndTangent: PROC [seg: Segment, testPoint: Point, tolerance: REAL] RETURNS [point: Point, tangent: Vector, success: BOOL] = { lineData: LineData _ NARROW[seg.data]; success _ FALSE; IF useBBox THEN { bigBox: GGBasicTypes.BoundBoxObj _ [seg.bBox.loX-tolerance, seg.bBox.loY-tolerance, seg.bBox.hiX+tolerance, seg.bBox.hiY+tolerance, FALSE, FALSE]; IF NOT PointIsInBox[testPoint, bigBox] THEN RETURN; }; point _ GGLines.NearestPointOnEdge[testPoint, lineData.edge]; tangent _ GGLines.DirectionOfLine[lineData.edge.line]; success _ TRUE; }; LineLineIntersection: PROC [seg: Segment, line: Line] RETURNS [points: LIST OF Point, pointCount: NAT] = { lineData: LineData _ NARROW[seg.data]; failure: BOOL; ipoint: Point; [ipoint, failure] _ GGLines.LineMeetsEdge[line, lineData.edge]; IF failure THEN { pointCount _ 0; RETURN; }; points _ LIST[ipoint]; pointCount _ 1; }; LineCircleIntersection: PROC [seg: Segment, circle: Circle] RETURNS [points: LIST OF Point, pointCount: NAT] = { lineData: LineData _ NARROW[seg.data]; hitPoints: ARRAY[1..2] OF Point; [hitPoints, pointCount] _ GGCircles.CircleMeetsEdge[circle, lineData.edge]; points _ NIL; FOR i: NAT IN [1..pointCount] DO points _ CONS[hitPoints[i], points]; ENDLOOP; }; LineAsSimpleCurve: PROC [seg: Segment, point: Point] RETURNS [simpleCurve: REF ANY] = { lineData: LineData _ NARROW[seg.data]; edge: Edge _ lineData.edge; simpleCurve _ edge; }; LineAddJoint: PROC [seg: Segment, pos: Point] RETURNS [seg1, seg2: Segment] = { seg1 _ GGSegment.CopySegment[seg]; seg1.hi _ pos; UpdateLineBoundBox[seg1]; seg2 _ GGSegment.CopySegment[seg]; seg2.lo _ pos; UpdateLineBoundBox[seg2]; }; ArcData: TYPE = REF ArcDataRec; ArcDataRec: TYPE = RECORD [ p1: VEC, p1Selected: SelectedObjectData, arc: Arc]; BuildArcClass: PROC [] RETURNS [class: SegmentClass] = { OPEN GGSegment; class _ NEW[SegmentClassObj _ [ type: $Arc, copyData: ArcCopyData, reverse: ArcReverse, boundBox: ArcBoundBox, tightBox: ArcTightBox, transform: ArcTransform, endPointMoved: ArcEndPointMoved, controlPointMoved: ArcControlPointMoved, describe: ArcDescribe, fileOut: ArcFileOut, fileIn: ArcFileIn, buildPath: ArcBuildPath, buildPathTransform: ArcBuildPathTransform, controlPointGet: ArcControlPointGet, controlPointCount: ArcControlPointCount, controlPointFieldSet: ArcFieldSet, controlPointFieldGet: ArcFieldGet, closestPoint: ArcClosestPoint, closestControlPoint: ArcClosestControlPoint, closestPointAndTangent: NoOpClosestPointAndTangent, lineIntersection: NoOpLineIntersection, circleIntersection: NoOpCircleIntersection, asSimpleCurve: ArcAsSimpleCurve, addJoint: ArcAddJoint, setStrokeWidth: ArcSetStrokeWidth ]]; }; ArcSetStrokeWidth: PROC [seg: Segment, strokeWidth: REAL] = { seg.strokeWidth _ strokeWidth; UpdateBoundBoxOfArc[seg]; }; MakeArc: PUBLIC PROC [p0, p1, p2: Point, props: LIST OF REF ANY] RETURNS [seg: Segment] = { data: ArcData _ NEW[ArcDataRec _ [ p1: [p1.x,p1.y], p1Selected: [FALSE, FALSE, FALSE], arc: GGCircles.CreateArc[p0, p1, p2] ]]; seg _ NEW[SegmentObj _ [ class: GGSegment.FetchSegmentClass[$Arc], looks: NIL, strokeWidth: defaultStrokeWidth, color: Imager.black, lo: p0, hi: p2, bBox: GGBoundBox.CreateBoundBox[0,0,0,0], -- gets updated one line down. data: data, props: props ]]; UpdateBoundBoxOfArc[seg]; }; ArcBoundBox: PROC [seg: Segment] RETURNS [bBox: BoundBox] = { bBox _ seg.bBox; }; ArcTightBox: PROC [seg: Segment] RETURNS [bBox: BoundBox] = { bBox _ GGBoundBox.NullBoundBox[]; UpdateTightBoxOfArc[seg, bBox]; }; UpdateTightBoxOfArc: PROC [seg: Segment, boundBox: BoundBox] = { data: ArcData _ NARROW[seg.data]; arc: Arc _ data.arc; IF arc.edge # NIL THEN UpdateBoundBoxOfEdge[arc.edge, boundBox] ELSE { boundBox^ _ [loX: arc.p0.x, loY: arc.p0.y, hiX: arc.p0.x, hiY: arc.p0.y, null: FALSE, infinite: FALSE]; GGBoundBox.EnlargeByPoint[boundBox, arc.p2]; IF GGAngle.IsInCCWInterval2[0.0, arc.theta0, arc.deltaTheta] THEN GGBoundBox.EnlargeByPoint[boundBox, GGVector.Add[arc.circle.origin, [arc.circle.radius, 0.0]]]; IF GGAngle.IsInCCWInterval2[90.0, arc.theta0, arc.deltaTheta] THEN GGBoundBox.EnlargeByPoint[boundBox, GGVector.Add[arc.circle.origin, [0.0, arc.circle.radius]]]; IF GGAngle.IsInCCWInterval2[180.0, arc.theta0, arc.deltaTheta] THEN GGBoundBox.EnlargeByPoint[boundBox, GGVector.Add[arc.circle.origin, [-arc.circle.radius, 0.0]]]; IF GGAngle.IsInCCWInterval2[-90.0, arc.theta0, arc.deltaTheta] THEN GGBoundBox.EnlargeByPoint[boundBox, GGVector.Add[arc.circle.origin, [0.0, -arc.circle.radius]]]; }; }; UpdateBoundBoxOfEdge: PROC [edge: Edge, boundBox: BoundBox] = { boundBox^ _ [ loX: MIN[edge.start.x, edge.end.x], loY: MIN[edge.start.y, edge.end.y], hiX: MAX[edge.start.x, edge.end.x], hiY: MAX[edge.start.y, edge.end.y], null: FALSE, infinite: FALSE]; }; UpdateBoundBoxOfArc: PROC [seg: Segment] = { boundBox: BoundBox _ seg.bBox; cpHalf: REAL _ GGModelTypes.halfJointSize + 1; strokeWidth: REAL _ seg.strokeWidth; pad: REAL _ MAX[cpHalf, strokeWidth]; UpdateTightBoxOfArc[seg, boundBox]; GGBoundBox.EnlargeByOffset[boundBox, pad]; }; ArcCopyData: GGSegmentTypes.CopyDataProc = { arcData: ArcData _ NARROW[seg.data]; new: ArcData _ NEW[ArcDataRec]; new.p1 _ arcData.p1; new.p1Selected _ arcData.p1Selected; new.arc _ GGCircles.CreateEmptyArc[]; GGCircles.CopyArc[from: arcData.arc, to: new.arc]; RETURN[new]; }; ArcReverse: PROC [seg: Segment] = { data: ArcData _ NARROW[seg.data]; }; ArcTransform: PROC [seg: Segment, transform: ImagerTransformation.Transformation] = { data: ArcData _ NARROW[seg.data]; data.p1 _ ImagerTransformation.Transform[transform, data.p1]; GGCircles.FillArc[seg.lo, data.p1, seg.hi, data.arc]; UpdateBoundBoxOfArc[seg]; }; ArcEndPointMoved: PROC [seg: Segment, lo: BOOL, newPoint: Point] = { data: ArcData _ NARROW[seg.data]; p0: Point _ IF lo THEN newPoint ELSE seg.lo; p1: Point _ [data.p1.x, data.p1.y]; p2: Point _ IF lo THEN seg.hi ELSE newPoint; GGCircles.FillArc[seg.lo, data.p1, seg.hi, data.arc]; UpdateBoundBoxOfArc[seg]; }; ArcControlPointMoved: PROC [seg: Segment, transform: ImagerTransformation.Transformation, controlPointNum: NAT] = { p1Vec: VEC; p1: Point; data: ArcData _ NARROW[seg.data]; IF controlPointNum#0 THEN ERROR; p1Vec _ ImagerTransformation.Transform[transform, data.p1]; p1 _ [p1Vec.x, p1Vec.y]; data.p1 _ p1Vec; GGCircles.FillArc[seg.lo, data.p1, seg.hi, data.arc]; UpdateBoundBoxOfArc[seg]; }; TransformEndPoints: PROC [loPt, hiPt: Point, lo, hi: BOOL, transform: ImagerTransformation.Transformation] RETURNS [newLo, newHi: VEC] = { IF lo THEN newLo _ ImagerTransformation.Transform[transform, [loPt.x,loPt.y]] ELSE newLo _ [loPt.x,loPt.y]; IF hi THEN newHi _ ImagerTransformation.Transform[transform, [hiPt.x,hiPt.y]] ELSE newHi _ [hiPt.x,hiPt.y]; }; ArcBuildPath: PROC [seg: Segment, lineTo: ImagerPath.LineToProc, curveTo: ImagerPath.CurveToProc, conicTo: ImagerPath.ConicToProc, arcTo: ImagerPath.ArcToProc] = { data: ArcData _ NARROW[seg.data]; arcTo[data.p1, [seg.hi.x, seg.hi.y]]; }; ArcBuildPathTransform: PROC [seg: Segment, transform: ImagerTransformation.Transformation, entire, lo, hi: BOOL, controlPoints: BitVector, lineTo: ImagerPath.LineToProc, curveTo: ImagerPath.CurveToProc, conicTo: ImagerPath.ConicToProc, arcTo: ImagerPath.ArcToProc] = { data: ArcData _ NARROW[seg.data]; p0, p1, p2: VEC; IF entire THEN { [p0, p2] _ TransformEndPoints[seg.lo, seg.hi, TRUE, TRUE, transform]; p1 _ ImagerTransformation.Transform[transform, data.p1]; } ELSE { [p0, p2] _ TransformEndPoints[seg.lo, seg.hi, lo, hi, transform]; p1 _ IF controlPoints[0] THEN ImagerTransformation.Transform[transform, data.p1] ELSE data.p1; }; arcTo[p1,p2]; }; ArcDescribe: PROC [seg: Segment, self, lo, hi: BOOL, cps: BitVector] RETURNS [rope: Rope.ROPE] = { data: ArcData _ NARROW[seg.data]; arc: Arc _ data.arc; IF arc.edge # NIL THEN rope _ "Straight Arc" ELSE rope _ "Arc"; }; ArcFileOut: PROC [seg: Segment, f: IO.STREAM] = { data: ArcData _ NARROW[seg.data]; p1: Point _ [data.p1.x, data.p1.y]; GGParseOut.WritePoint[f, p1]; }; ArcFileIn: PROC [f: IO.STREAM, loPoint, hiPoint: Point, version: REAL] RETURNS [seg: Segment] = { p1: Point; p1 _ GGParseIn.ReadPoint[f]; seg _ MakeArc[loPoint, p1, hiPoint, NIL]; }; ArcFieldSet: GGSegmentTypes.ControlPointFieldSetProc = { data: ArcData _ NARROW[seg.data]; IF controlPointNum # 0 THEN ERROR; SELECT selectClass FROM normal => data.p1Selected.normal _ selected; hot => data.p1Selected.hot _ selected; active => data.p1Selected.active _ selected; ENDCASE => ERROR; }; ArcFieldGet: GGSegmentTypes.ControlPointFieldGetProc = { data: ArcData _ NARROW[seg.data]; IF controlPointNum # 0 THEN ERROR; SELECT selectClass FROM normal => selected _ data.p1Selected.normal; hot => selected _ data.p1Selected.hot; active => selected _ data.p1Selected.active; ENDCASE => ERROR; }; ArcControlPointGet: PROC [seg: Segment, controlPointNum: NAT] RETURNS [point: Point] = { data: ArcData _ NARROW[seg.data]; IF controlPointNum#0 THEN ERROR; RETURN[ [data.p1.x, data.p1.y] ]; }; ArcControlPointCount: PROC [seg: Segment] RETURNS [controlPointCount: NAT] = { data: ArcData _ NARROW[seg.data]; RETURN[1]; }; ArcClosestPoint: PROC [seg: Segment, testPoint: Point, tolerance: REAL] RETURNS [point: Point, success: BOOL] = { data: ArcData _ NARROW[seg.data]; point _ GGCircles.NearestPointOnArc[testPoint, data.arc]; success _ TRUE; }; ArcClosestControlPoint: PROC [seg: Segment, testPoint: Point, tolerance: REAL] RETURNS [point: Point, controlPointNum: NAT, success: BOOL] = { data: ArcData _ NARROW[seg.data]; success _ TRUE; controlPointNum _ 0; point _ [data.p1.x, data.p1.y]; }; ArcAsSimpleCurve: PROC [seg: Segment, point: Point] RETURNS [simpleCurve: REF ANY] = { arcData: ArcData _ NARROW[seg.data]; arc: Arc _ arcData.arc; simpleCurve _ arc; }; ArcAddJoint: PROC [seg: Segment, pos: Point] RETURNS [seg1, seg2: Segment] = { data: ArcData _ NARROW[seg.data]; data1, data2: ArcData; p0Rel: Point _ GGVector.Sub[data.arc.p0, data.arc.circle.origin]; p1Rel: Point _ GGVector.Sub[data.p1, data.arc.circle.origin]; p2Rel: Point _ GGVector.Sub[data.arc.p2, data.arc.circle.origin]; caretRel: Point _ GGVector.Sub[pos, data.arc.circle.origin]; cpAngle: REAL _ GGVector.AngleCCWBetweenVectors[p0Rel, p1Rel]; caretAngle: REAL _ GGVector.AngleCCWBetweenVectors[p0Rel, caretRel]; rotVec: Point; seg1 _ GGSegment.CopySegment[seg]; seg2 _ GGSegment.CopySegment[seg]; data1 _ NARROW[seg1.data]; data2 _ NARROW[seg2.data]; seg2.lo _ seg1.hi _ pos; seg1.lo _ data.arc.p0; seg2.hi _ data.arc.p2; IF caretAngle < cpAngle THEN { -- following arc, caret is closer to p0 than is cp rotVec _ ImagerTransformation.Transform[ImagerTransformation.Rotate[-caretAngle/2.0], caretRel]; data1.p1 _ GGVector.Add[rotVec, data.arc.circle.origin]; } ELSE { -- following arc, cp is closer to p0 than is caret caretAngle _ GGVector.AngleCCWBetweenVectors[caretRel, p2Rel]; -- now we want angle from caret to p2 rotVec _ ImagerTransformation.Transform[ImagerTransformation.Rotate[caretAngle/2.0], caretRel]; data2.p1 _ GGVector.Add[rotVec, data.arc.circle.origin]; }; data1.arc _ GGCircles.CreateArc[seg1.lo, data1.p1, seg1.hi]; data2.arc _ GGCircles.CreateArc[seg2.lo, data2.p1, seg2.hi]; UpdateBoundBoxOfArc[seg1]; UpdateBoundBoxOfArc[seg2]; IF NOT data.arc.ccw THEN { -- p0 to p2 always counter-clockwise. Need to take into account. GGSegment.ReverseSegment[seg1]; GGSegment.ReverseSegment[seg2]; RETURN [seg2, seg1]; }; }; Init: PROC [] = { classDef: ClassDef _ NEW[ClassDefRec _ [type: $Line, class: BuildLineClass[]]]; GGSegment.RegisterSegmentClass[classDef]; classDef _ NEW[GGSegment.ClassDefRec _ [type: $Arc, class: BuildArcClass[]]]; GGSegment.RegisterSegmentClass[classDef]; classDef _ NEW[ClassDefRec _ [type: $Circle, class: BuildCircleClass[]]]; GGSegment.RegisterSegmentClass[classDef]; classDef _ NEW[ClassDefRec _ [type: $Disc, class: BuildDiscClass[]]]; GGSegment.RegisterSegmentClass[classDef]; }; Init[]; END. πGGSegmentImplB.mesa Copyright c 1986 by Xerox Corporation. All rights reserved. Contents: Procedures which implement the Gargoyle segment classes. Bier, January 26, 1987 4:11:07 pm PST Pier, December 22, 1986 2:48:48 pm PST Kurlander, August 25, 1986 5:44:28 pm PDT Auxiliary module for GGSegmentImplA, because GGSegmentImplA got too big !! NoOpFileIn is not allowed. The Circle Class. THIS IS A VESTIGIAL CLASS KEPT FOR BACKWARD COMPATIBILITY GGSegmentTypes.BoundBoxProc Find the boundBox of the segment, allowing for the size of control points. GGSegmentTypes.FileInProc GGSegmentTypes.EndPointMovedProc Circles are simple. Nothing to do. The Disc Class. THIS IS A VESTIGIAL CLASS KEPT FOR BACKWARD COMPATIBILITY Discs are identical to circles and are filled with the parent outline fill color GGSegmentTypes.FileInProc The Line (straight line) Class Transforming Textual Description Drawing Control Point Access Hit Testing Editing GGSegmentTypes.BoundBoxProc GGSegmentTypes.TightBoxProc Find the boundBox of the segment, NOT allowing for the size of control points (or stroke width, whichever is larger). GGSegmentTypes.BoundBoxProc Find the boundBox of the segment, allowing for the size of control points (or stroke width, whichever is larger). GGSegmentTypes.CopyDataProc GGSegmentTypes.ReverseProc Lines are simple. Nothing to do. Line Drawing GGSegmentTypes.BuildPathProc GGSegmentTypes.BuildPathTransformProc Line Transforming GGSegmentTypes.TransformProc GGSegmentTypes.EndPointMovedProc Line Description GGSegmentTypes.FileInProc Line Hit Testing GGSegmentTypes.ClosestPointProc GGSegmentTypes.ClosestPointAndTangentProc GGSegmentTypes.LineIntersectionProc GGSegmentTypes.CircleIntersectionProc Line Editing GGSegmentTypes.AddJointProc The Arc Class Transforming Textual Description Drawing Control Point Access Hit Testing Editing Circular Arc through the three points GGSegmentTypes.BoundBoxProc GGSegmentTypes.TightBoxProc For when the arc is straight. GGSegmentTypes.CopyDataProc Copies the data in the "data" field of seg. This data is class-dependent. GGSegmentTypes.ReverseProc The "lo" end and "hi" end have switched roles. Update data structures as necessary. Arc Transforming GGSegmentTypes.TransformProc Apply the given transformation to all internal data of the segment. It is now in a new position, orientation, scaling, or skewing. GGSegmentTypes.EndPointMovedProc GGSegmentTypes.ControlPointMovedProc Arc Drawing GGSegmentTypes.BuildPathProc GGSegmentTypes.BuildPathTransformProc Like BuildPathProc but you should assume that transform has been applied to your lower joint iff lo = TRUE and to your higher joint iff hi = TRUE. This is for rubberbanding filled areas. Arc Description ELSE rope _ IO.PutFR ["Radius %g Arc", [real[arc.circle.radius]]]; GGSegmentTypes.FileOutProc GGSegmentTypes.FileInProc Arc Parts [seg: GGSegmentTypes.Segment, controlPointNum: NAT, selected: BOOL, selectClass: GGSegmentTypes.SelectionClass] [seg: GGSegmentTypes.Segment, controlPointNum: NAT, selectClass: GGSegmentTypes.SelectionClass] RETURNS [selected: BOOL] Arc Part Generators GGSegmentTypes.ControlPointGetProc GGSegmentTypes.ControlPointCountProc Arc Hit Testing GGSegmentTypes.ClosestControlPointProc Arc Editing GGSegmentTypes.AddJointProc Initialization ΚΎ˜™Icodešœ<™Kšœžœ˜K˜—š œžœ ˜4Kšžœ˜K˜—Kš œžœ!˜4Kšœ™K˜šΟbœ<™LK™—Kšœ žœžœ˜%šœžœžœΟc˜9K˜—šœžœžœ ˜Ašœžœ˜%K˜K˜K˜#K˜K˜—K˜K˜—šœžœžœ˜@Kšœ™K™JKšœžœ˜ Kšœžœ"˜.Kšœ+˜+Kšœ}žœžœ˜‹Kšœ˜K˜K˜—š  œžœžœžœ$žœžœ˜dKšœ™Kšœžœ˜&šœžœ˜K˜,Kšœžœ˜ Kšœ˜K˜Kšœ˜Kšœ*˜*Kšœžœ˜—K˜K˜K˜—šœžœžœ˜GKšœ ™ K™#K˜—K™šŸœ<™JKšœP™PK™—Kšœ žœžœ ˜!šœ žœžœ ˜7K˜—šœžœžœ˜=šœ žœ˜#Kšœ ˜ K˜K˜#Kšœ˜K˜—K˜K˜—š  œžœžœžœ$žœžœ˜bKšœ™Kšœžœ˜&šœžœ˜K˜*Kšœžœ˜ Kšœ˜K˜Kšœ˜Kšœ+ ˜EKšœ ˜ Kšœžœ˜ —K˜K˜—K˜šŸ™K˜—Kšœ žœžœ ˜!šœ žœžœ˜Kšœ  2˜=Kšœ˜K˜—šœžœžœ˜=KšΠbkŸ ˜šœ žœ˜#K˜ K˜K˜K˜šœ˜KšŸ ™ —K˜K˜!šœ)˜)KšŸ™—K˜Kšœ˜˜KšŸ™—Kšœ˜šœ+˜+KšŸ™—Kšœ%˜%Kšœ)˜)Kšœ/˜/šœ/˜/KšŸ ™ —K˜Kšœ-˜-K˜3J˜'J˜+šœ!˜!KšŸ™—Kšœ˜Kšœ"˜"K˜—K˜K˜—šœžœžœ˜>Kšœ˜Kšœ˜K˜K˜—šœžœžœžœžœžœžœžœ˜Xšœžœ˜+Kšœ ˜ Kšœ˜—šœžœ˜K˜*Kšœžœ˜ Kšœ ˜ K˜Kšœ˜Kšœ+ ˜EKšœ˜Kšœ ˜ Kšœ˜—Kšœ˜K˜K˜—š œžœžœ˜>Kšœ™Kšœ˜K˜K˜—š œžœžœ˜>Kšœ™K™uKšœžœ˜Kšœžœ˜Kšœžœ˜Kšœžœ˜Kšœžœ˜Kšœ5˜5K˜K˜—šœžœ˜+Kšœ™K™qKšœžœ˜Kšœžœ"˜.Kšœ$˜$Kšœžœžœ˜%Kšœžœ˜Kšœžœ˜Kšœžœ˜Kšœžœ˜Kšœ9žœžœ˜GK˜K˜—š  œžœžœžœžœ˜=Kšœ™Kšœžœ ˜)šœžœ8˜BKšœ˜—K˜K˜—š œžœ˜$Kšœ™K™!K˜K˜—šœ ™ K™—š œžœ‘˜€Kšœ™Kšœ˜K˜K˜—šœžœPžœ˜Kšœ%™%K˜šœ žœžœžœ)˜HKšžœ˜—Kšœ!˜!K˜K˜—šœ™K™—š œžœC˜VKšœ™Kšœžœ ˜)Kšœ3˜3Kšœ˜K˜K˜—šœžœžœ˜EKšœ ™ Kšœžœ ˜)Kšœ3˜3Kšœ˜K˜K˜—šœ™K™—š  œžœžœžœ žœ˜cKšœ˜K˜K˜—š  œžœžœžœ$žœžœ˜bKšœ™Kšœ!žœ˜&K˜K˜—šœ žœžœ˜K˜—š œžœ.žœžœ˜RKš žœžœžœžœžœ˜]K˜K˜—šœ™K™—š œžœ-žœžœžœ˜rKšœ™Kšœžœ ˜&Kšœ žœ˜•StartOfExpansion[bBox: GGBasicTypes.BoundBox]šžœ žœ˜K–[bBox: GGBasicTypes.BoundBox]šœ„žœžœ˜’Kšžœžœ!žœžœ˜3K˜—Kšœ=˜=Kšœ žœ˜K˜K˜—š œžœ-žœžœ*žœ˜Kšœ)™)Kšœžœ ˜&Kšœ žœ˜–[bBox: GGBasicTypes.BoundBox]šžœ žœ˜Kšœ„žœžœ˜’Kšžœžœ!žœžœ˜3K˜—Kšœ=˜=Kšœ6˜6Kšœ žœ˜K˜K˜—š œžœžœ žœžœžœ˜jKšœ#™#Kšœžœ ˜&Kšœ žœ˜Kšœ˜Kšœ?˜?šžœ žœ˜Kšœ˜Kšžœ˜K˜—Kšœ žœ ˜Kšœ˜K˜K˜—š œžœ žœ žœžœžœ˜pKšœ%™%Kšœžœ ˜&Kšœ žœžœ˜ KšœK˜KKšœ žœ˜ šžœžœžœž˜ Kšœ žœ˜$Kšžœ˜—K˜K˜—š œžœžœžœžœ˜WKšœžœ ˜&Kšœ˜Kšœ˜K˜—šœ ™ K™—š œžœžœ˜PK™K˜"K˜K˜K˜"K˜K˜K˜—K™šŸ ™ K˜—Kšœ žœžœ ˜šœ žœžœ˜Kšœžœ˜Kšœ˜šœ ˜ K˜——š œžœžœ˜8Kš‘Ÿ ˜šœžœ˜K˜ K˜K˜Kšœ˜˜KšŸ ™ —K˜K˜ ˜(KšŸ™—K˜K˜˜KšŸ™—Kšœ˜šœ*˜*KšŸ™—Kšœ$˜$Kšœ(˜(Kšœ"˜"šœ"˜"KšŸ ™ —K˜K˜,Kšœ3˜3Jšœ'˜'Jšœ+˜+šœ ˜ KšŸ™—K˜Kšœ!˜!K˜—K˜K˜—šœžœžœ˜=Kšœ˜Kšœ˜K˜K˜—šœžœžœžœžœžœžœžœ˜[K™%šœžœ˜"K˜Kšœ žœžœžœ˜"Kšœ$˜$K˜—šœžœ˜K˜)Kšœžœ˜ Kšœ ˜ K˜Kšœ˜Kšœ* ˜HKšœ ˜ Kšœ ˜ Kšœ˜—Kšœ˜K˜K˜—š œžœ,˜=Kšœ™Kšœ˜Kšœ˜K˜—š œžœžœ˜=Kšœ™Kšœ!˜!Kšœ˜Kšœ˜K˜—šœžœ'˜@Kšœžœ ˜!K˜Kšžœ žœžœ)˜?šžœ˜KšœOžœ žœ˜gKšœ,˜,šžœ;ž˜AšœC˜CKšœ˜——šžœ<ž˜BšœC˜CKšœ˜——šžœ=ž˜CšœC˜CKšœ˜——šžœ=ž˜CšœC˜CKšœ˜——K˜—K˜K˜—šœžœ%˜?šœ ˜ K™Kšœžœ˜#Kšœžœ˜#Kšœžœ˜#Kšœžœ˜#Kšœžœ žœ˜—K˜K˜—šœžœ˜,Kšœ˜Kšœžœ"˜.Kšœ žœ˜$Kšœžœžœ˜%Kšœ#˜#Kšœ*˜*K˜K˜—š œ!˜,Kšœ™K™JKšœžœ ˜$Kšœžœ ˜Kšœ˜Kšœ$˜$Kšœ%˜%Kšœ2˜2Kšžœ˜ K˜K˜—š œžœ˜#Kšœ™K™TKšœžœ ˜!K˜K˜—šœ™K™—š œžœC˜UKšœ™K™ƒKšœžœ ˜!Kšœ=˜=Kšœ5˜5Kšœ˜K˜K˜—šœžœžœ˜DKšœ ™ Kšœžœ ˜!Kšœ žœžœ žœ˜,Kšœ#˜#Kšœ žœžœžœ ˜,Kšœ5˜5Kšœ˜Kšœ˜K˜—šœžœQžœ˜sKšœ$™$Kšœžœ˜ Kšœ ˜ Kšœžœ ˜!Kšžœžœžœ˜ Kšœ;˜;Kšœ˜Kšœ˜Kšœ5˜5Kšœ˜Kšœ˜K˜—š œžœžœ2žœžœ˜ŠKšžœžœC˜MKšžœ˜KšžœžœC˜MKšžœ˜K˜K™—Kšœ ™ š œžœ‘˜£Kšœ™Kšœžœ ˜!Kšœ%˜%K˜K˜—šœžœPžœ˜ŒKšœ%™%Kšœ»™»Kšœžœ ˜!Kšœ žœ˜šžœžœ˜Kšœ.žœžœ ˜EKšœ8˜8K˜—šžœ˜KšœA˜AKšœžœžœ3˜PKšžœ ˜ K˜—K˜ K˜K˜—šœ™K™—š  œžœžœžœ žœ˜bKšœžœ ˜!Kšœ˜Kšžœ žœžœ˜,Kšžœ>™BKšžœ˜K˜K˜—š œžœžœžœ˜1Kšœ™Kšœžœ ˜!Kšœ#˜#Kšœ˜K˜K˜—š  œžœžœžœ$žœžœ˜aKšœ™K˜ K˜Kšœ$žœ˜)K˜K˜—šœ ™ K™—–s -- [seg: GGSegmentTypes.Segment, controlPointNum: NAT, selected: BOOL, selectClass: GGSegmentTypes.SelectionClass]š œ-˜8Kšœo™oKšœžœ ˜!Kšžœžœžœ˜"šžœ ž˜Kšœ,˜,Kšœ&˜&Kšœ,˜,Kšžœžœ˜—K˜K˜—–| -- [seg: GGSegmentTypes.Segment, controlPointNum: NAT, selectClass: GGSegmentTypes.SelectionClass] RETURNS [selected: BOOL]š œ-˜8Kšœx™xKšœžœ ˜!Kšžœžœžœ˜"šžœ ž˜Kšœ,˜,Kšœ&˜&Kšœ,˜,Kšžœžœ˜—K˜K˜—šœ™K™—šœžœ!žœžœ˜XKšœ"™"Kšœžœ ˜!Kšžœžœžœ˜ Kšžœ˜!Kšœ˜K˜—šœžœžœžœ˜NKšœ$™$Kšœžœ ˜!Kšžœ˜ Kšœ˜K˜—šœ™K™—š œžœ-žœžœžœ˜qKšœžœ ˜!Kšœ9˜9Kšœ žœ˜K˜K˜—š œžœ-žœžœ!žœ žœ˜ŽKšœ&™&Kšœžœ ˜!Kšœ žœ˜Kšœ˜Kšœ˜K˜K˜—š œžœžœžœžœ˜VKšœžœ ˜$Kšœ˜Kšœ˜K˜K˜K™—Kšœ ™ š œžœžœ˜OK™Kšœžœ ˜!K˜KšœA˜AK˜=K˜AK˜Kšœ žœ4˜DK˜K˜"K˜"Kšœžœ ˜Kšœžœ ˜K˜K˜-šžœžœ 2˜QKšœ`˜`K˜8K˜—šžœ 2˜9Kšœ? %˜dK˜_K˜8K˜—K˜