DIRECTORY Cubic2, CubicSplines, Angles2d, GGBasicTypes, GGBoundBox, GGCircles, Lines2d, GGModelTypes, GGParseIn, GGParseOut, GGSegment, GGSegmentTypes, GGTransform, Vectors2d, Imager, ImagerTransformation, ImagerPath, ImagerBackdoor, IO, Rope; GGSegmentImplB: CEDAR PROGRAM IMPORTS Angles2d, GGBoundBox, GGCircles, Lines2d, GGParseIn, GGParseOut, GGSegment, GGTransform, Vectors2d, 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; StrokeEnd: TYPE = Imager.StrokeEnd; Vector: TYPE = GGBasicTypes.Vector; X: NAT = CubicSplines.X; Y: NAT = CubicSplines.Y; defaultStrokeWidth: REAL _ 2.0; defaultStrokeEnd: StrokeEnd _ round; 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 _ Vectors2d.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, strokeEnd: round, 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, strokeEnd: round, 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: Lines2d.CreateEdge[p0, p1] ]]; seg _ NEW[SegmentObj _ [ class: GGSegment.FetchSegmentClass[$Line], looks: NIL, strokeWidth: defaultStrokeWidth, strokeEnd: defaultStrokeEnd, 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: Lines2d.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]; Lines2d.FillEdge[seg.lo, seg.hi, lineSegment.edge]; UpdateLineBoundBox[seg]; }; LineEndPointMoved: PROC [seg: Segment, lo: BOOL, newPoint: Point] = { lineSegment: LineData _ NARROW[seg.data]; Lines2d.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 _ Lines2d.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 _ Lines2d.NearestPointOnEdge[testPoint, lineData.edge]; tangent _ Lines2d.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] _ Lines2d.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, strokeEnd: defaultStrokeEnd, 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 Angles2d.IsInCCWInterval2[0.0, arc.theta0, arc.deltaTheta] THEN GGBoundBox.EnlargeByPoint[boundBox, Vectors2d.Add[arc.circle.origin, [arc.circle.radius, 0.0]]]; IF Angles2d.IsInCCWInterval2[90.0, arc.theta0, arc.deltaTheta] THEN GGBoundBox.EnlargeByPoint[boundBox, Vectors2d.Add[arc.circle.origin, [0.0, arc.circle.radius]]]; IF Angles2d.IsInCCWInterval2[180.0, arc.theta0, arc.deltaTheta] THEN GGBoundBox.EnlargeByPoint[boundBox, Vectors2d.Add[arc.circle.origin, [-arc.circle.radius, 0.0]]]; IF Angles2d.IsInCCWInterval2[-90.0, arc.theta0, arc.deltaTheta] THEN GGBoundBox.EnlargeByPoint[boundBox, Vectors2d.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 _ Vectors2d.Sub[data.arc.p0, data.arc.circle.origin]; p1Rel: Point _ Vectors2d.Sub[data.p1, data.arc.circle.origin]; p2Rel: Point _ Vectors2d.Sub[data.arc.p2, data.arc.circle.origin]; caretRel: Point _ Vectors2d.Sub[pos, data.arc.circle.origin]; cpAngle: REAL _ Vectors2d.AngleCCWBetweenVectors[p0Rel, p1Rel]; caretAngle: REAL _ Vectors2d.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 _ Vectors2d.Add[rotVec, data.arc.circle.origin]; } ELSE { -- following arc, cp is closer to p0 than is caret caretAngle _ Vectors2d.AngleCCWBetweenVectors[caretRel, p2Rel]; -- now we want angle from caret to p2 rotVec _ ImagerTransformation.Transform[ImagerTransformation.Rotate[caretAngle/2.0], caretRel]; data2.p1 _ Vectors2d.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, February 19, 1987 0:32:03 am PST Pier, February 25, 1987 3:20:16 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 �ÊX��˜�™Icodešœ<™<KšÏnœ;™CKšœ#Ïk™&Kšœ&™&Kšœ&ž™)K™�K™JK™�—šž ˜ Kšœàžœ˜é—K˜�šœžœžœ˜Kšžœ˜ˆKšžœ ž˜—˜�Kšœžœ˜Kšœžœ˜)Kšœ žœ˜'Kšœžœ ˜3Kšœžœ)˜EKšœžœ˜#Kšœ žœ˜$Kšœ žœ˜*Kšœžœ˜Kšœžœ˜Kšœžœ˜!Kšœ žœ˜'Kšœžœžœ˜)Kšœžœ"˜7Kšœžœ˜-Kšœžœ%˜=Kšœžœ!˜5Kšžœžœ žœ˜Kšœžœ˜#Kšœžœ˜#Kšœžœ˜Kšœžœ˜—K˜�Kšœžœ˜Kšœ$˜$K˜�Kšœžœ+˜HKšœžœ'žœ˜XKšœžœ@˜]Kšœžœ.˜Nšœžœ,˜LKšžœ˜K˜—šœžœ+˜JKšœ žœ˜K˜—šœžœ.˜PKšœ žœ˜K˜—šœžœ(˜DKšœ žœ˜ Kšœ˜K˜—šœžœ*˜HKšœ žœ˜ Kšœ˜K˜—šœžœ%˜>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˜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šœ˜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šœ˜Kšœ+ ˜EKšœ˜Kšœ˜Kšœ˜—Kšœ˜K˜K˜�—šœžœžœ˜>Kšœ™Kšœ˜K˜K˜�—šœžœžœ˜>Kšœ™Kšœ"žœP™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šœ˜Kšœ* ˜HKšœ˜Kšœ˜Kšœ˜—Kšœ˜K˜K˜�—šœžœžœ˜=Kšœ™Kšœ˜Kšœ˜K˜�—šœžœžœ˜=Kšœ™Kšœ!˜!Kšœ˜Kšœ˜K˜�—šœžœ'˜@Kšœžœ˜!K˜Kšžœžœžœ)˜?šžœ˜KšœOžœžœ˜gKšœ,˜,šžœ<ž˜BšœD˜DKšœ˜——šžœ=ž˜CšœD˜DKšœ˜——šžœ>ž˜DšœD˜DKšœ˜——šžœ>ž˜DšœD˜DKšœ˜——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šœfžœ#žœ*™»Kšœžœ˜!Kšœžœ˜šžœžœ˜Kšœ.žœžœ ˜EKšœ8˜8K˜—šžœ˜KšœA˜AKšœžœžœ3˜PKšžœ ˜ K˜—K˜ K˜K˜�—šœ™K™�—š œžœžœžœ žœ˜bKšœžœ˜!Kšœ˜Kšžœžœžœ˜,Kšžœžœ4™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šœ/žœžœ-™oKšœžœ˜!Kšžœžœžœ˜"šžœ ž˜Kšœ,˜,Kšœ&˜&Kšœ,˜,Kšžœžœ˜—K˜K˜�—–| -- [seg: GGSegmentTypes.Segment, controlPointNum: NAT, selectClass: GGSegmentTypes.SelectionClass] RETURNS [selected: BOOL]šœ-˜8Kšœ/žœ.žœžœ™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šœB˜BK˜>K˜BK˜=Kšœ žœ2˜?Kšœžœ5˜EK˜K˜"K˜"Kšœžœ˜Kšœžœ˜K˜K˜-šžœžœ 2˜QKšœ`˜`K˜9K˜—šžœ 2˜9Kšœ@ %˜eK˜_K˜9K˜—K˜<K˜<Kšœ˜Kšœ˜šžœžœžœ A˜\K˜K˜Kšžœ˜K˜—K˜—K˜�™K™�—šœžœ˜Kšœžœ7˜OKšœ)˜)Kšœžœ?˜MKšœ)˜)K˜�Kšœžœ;˜IKšœ)˜)Kšœžœ7˜EKšœ)˜)K˜—K˜�K˜K™�Kšžœ˜K˜�—�…—����Qp��wº��