<> <> <> <> <> <> <<>> <> <<>> 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 = { <<[seg: GGSegmentTypes.Segment, controlPointNum: NAT, selected: BOOL, selectClass: GGSegmentTypes.SelectionClass]>> 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 = { <<[seg: GGSegmentTypes.Segment, controlPointNum: NAT, selectClass: GGSegmentTypes.SelectionClass] RETURNS [selected: BOOL]>> 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.