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º��