MODULE Circle; (* Procedures and predicates for circles. *) IMPORT Math, R2, PS; (* QUARTER CIRCLES *) (* A quarter circle is defined by its center of curvature "a", the current point, and a direction (clockwise or counter-clockwise). *) PRIVATE CONST K = 0.5519149706; /* This constant is used to position the control points for drawing a quarter circle. It was provided by Lyle Ramshaw. It produces a Bezier curve approximation to a true quarter circle that coincides with the circle at the endpoints and at two other points. The Bezier curve starts and ends going outside the circle, but the midpoint of the curve is inside the circle. The worst relative error between the curve and the constant is slightly less than 2 parts in 10,000. */ PROC DrawQuarter(a) IS VAR b IN b := PS.CurrentPoint(); IF VAR c = (1, -K) REL (a, b), d = (K, -1) REL (a, b), e = (0, -1) REL (a, b) IN PS.CurveTo(c, d, e) END FI END END; PROC DrawQuarterCC(a) IS VAR b IN b := PS.CurrentPoint(); IF VAR c = (1, K) REL (a, b), d = (K, 1) REL (a, b), e = (0, 1) REL (a, b) IN PS.CurveTo(c, d, e) END FI END END; UI PointTool(DrawQuarter); UI PointTool(DrawQuarterCC); (* Add a clockwise or counter-clockwise quarter-circle, respectively, to the current path. In each case, the quarter-circle starts at the current point and has center "a". *) (* CIRCLES *) (* A circle "[a, b]" is defined by its center "a" and a point "b" on the circle. *) PROC Draw(a, b) IS IF VAR c ~ (0, -1) REL (a, b), d ~ (-1, 0) REL (a, b), e ~ (0, 1) REL (a, b) IN PS.MoveTo(b); DrawQuarter(a); DrawQuarter(a); DrawQuarter(a); DrawQuarter(a); PS.Close() END FI END; PROC DrawCC(a, b) IS IF VAR c ~ (0, 1) REL (a, b), d ~ (-1, 0) REL (a, b), e ~ (0, -1) REL (a, b) IN PS.MoveTo(b); DrawQuarterCC(a); DrawQuarterCC(a); DrawQuarterCC(a); DrawQuarterCC(a); PS.Close() END FI END; UI PointTool(Draw); UI PointTool(DrawCC); (* Add a clockwise or counter-clockwise circle "[a, b]", respectively, to the current path. The current point becomes "b". *) PROC DrawR(c, rad) IS Draw(c, R2.PlusX(c, rad)) END; PROC DrawCCR(c, rad) IS DrawCC(c, R2.PlusX(c, rad)) END; (* Add a clockwise or counter-clockwise circle, respectively, with center point "c" and a radius of "r" points to the current path. *) PRED On(p, a, b) IS (a, p) CONG (a, b) END; UI PointTool(On); (* The point "p" is on the circle "[a, b]". *) PRIVATE FUNC r2 = Dist2(a, b) IS (E ax, ay, bx, by, dx, dy :: a = (ax, ay) AND b = (bx, by) AND dx = ax - bx AND dy = ay - by AND r2 = dx * dx + dy * dy) END; /* "r2" is the square of the distance between the points "a" and "b". */ FUNC res = Area(a, b) IS res = Math.Pi * Dist2(a, b) END; (* "res" is the area of the circle "[a, b]". *)