-- COGCart.mesa: Basic geometrical operations in the Cartesian plane
-- last modified by Stolfi - September 20, 1982 7:27 pm
DIRECTORY
Real USING [SqRt],
COGRandom USING [Toss];
COGCart: CEDAR DEFINITIONS
IMPORTS Real, COGRandom =
BEGIN OPEN Real, Rand: COGRandom;
-- BASIC DATA TYPES - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Point: TYPE = RECORD [x, y: REAL];
Vector: TYPE = Point;
Line: TYPE = RECORD [a, b, c: REAL];
-- The line [a, b, c] consists of the points [x, y] for which a*x+b*y+c = 0 in Cartesian coordinates. A line with a=b=0, c#0 is said to be at empty (no point belongs to it); a line with a=b=c=0 is indeterminate (any point is in it). A line with c=0 and a#0 or b#0 is a line through the origin. The vector [a, b] is perpendicular to the line [a, b, c].
-- The lines r=[a, b, c] and r'=[d*a, d*b, d*c] are essentially equivalent for any d>0. If d<0, the two lines have the same points but are considered to have opposite orientations, so that, for example, the distances from a point to those two lines will have opposite signs. The orientation of line r is by definition such that the vector [a, b] points to the left of r. The empty line [0, 0, c] may be thought as a circle of infinite radius centerd at the origin; by definition, its orientation is counterclockwise iff c>0.
Box: TYPE = RECORD [min, max: Point];
-- PROCEDURES FOR POINTS AND VECTORS - - - - - - - - - - - - - - - - - - - - - - - - - -
Add: PROC [a, b: Vector] RETURNS [Vector] = INLINE
-- Adds two vectors (or points).
{RETURN [[a.x+b.x, a.y+b.y]]};
Sub: PROC [a, b: Vector] RETURNS [Vector] = INLINE
-- Subtracts two vectors (or points).
{RETURN [[a.x-b.x, a.y-b.y]]};
Mul: PROC [a: REAL, u: Vector] RETURNS [w: Vector] = INLINE
-- Multiplies the vector u by a real number a.
{RETURN [[u.x*a, u.y*a]]};
Way: PROC [alpha: REAL, a, b: Point] RETURNS [Point] = INLINE
-- Computes the point alpha the way from a to b..
{beta: REAL = (1.0-alpha);
RETURN [[beta*a.x+alpha*b.x, beta*a.y+alpha*b.y]]};
Dot: PROC [u, v: Vector] RETURNS [REAL] = INLINE
-- Dot product of u and v.
{RETURN [u.x*v.x+u.y*v.y]};
Rot: PROC [u: Vector, c, s: REAL] RETURNS [ur: Vector] = INLINE
-- Rotates the vector u by an angle whose cosine is c and whose sine is s.
{RETURN [[c*u.x-s*u.y, c*u.y+s*u.x]]};
Length: PROC [v: Vector] RETURNS [REAL] = TRUSTED INLINE
-- Length of vector v.
{RETURN [SqRt[v.x*v.x+v.y*v.y]]};
LengthSq: PROC [v: Vector] RETURNS [REAL] = INLINE
-- Length of vector v, squared (saves a square root extraction).
{RETURN [v.x*v.x+v.y*v.y]};
Dist: PROC [a, b: Point] RETURNS [REAL] = INLINE
-- Distance between points p and q.
{RETURN [Length[Sub[a, b]]]};
DistSq: PROC [a, b: Point] RETURNS [REAL] = INLINE
-- Distance between points p and q, squared (saves a square root extraction).
{RETURN [LengthSq[Sub[a, b]]]};
-- PROCEDURES FOR LINES - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Bisector: PROC [a, b: Point] RETURNS [Line];
-- Finds the bisector of points a and b. If both points are coincident, the line is indeterminate. The bisector is oriented 90 counterclockwise from q-p.
Intercept: PROC [r, s: Line] RETURNS [Point];
-- Computes the intersection of lines r and s.
-- PROCEDURES FOR DIRECTIONS AND ANGLES - - - - - - - - - - - - - - - - - - - - - - - -
-- An angle between vectors, lines or points can be measured and compared by converting it to a vector that makes the same angle with the x-axis. This is done by the procedures below.
AddAngles: PROC [u, v: Vector] RETURNS [Vector] = INLINE
-- The result will make with the x-axis an angle equal to angle(u, x-axis)+angle(v, x-axis).
{RETURN [[u.x*v.x-u.y*v.y, u.x*v.y+u.y*v.x]]};
SubAngles: PROC [u, v: Vector] RETURNS [Vector] = INLINE
-- The result will make with the x-axis an angle equal to angle(u, v).
{RETURN [[-u.x*v.x+u.y*v.y, -u.x*v.y-u.y*v.x]]};
PositiveAngle: PROC [u, v: Vector] RETURNS [BOOLEAN] = INLINE
-- TRUE iff the angle from u to v is between 0 and pi (exclusive both).
{RETURN [u.x*v.y-v.x*u.y > 0.0]};
Convex: PROC [a, b, c: Point] RETURNS [BOOLEAN];
-- Returns TRUE iff the angle pqr is strictly convex, i.e. iff from some point o the points p, q and r are seen counterclockwise in that order.
CircumCenter: PROC [a, b, c: Point] RETURNS [Vector] = INLINE
-- Computes the circumcenter of the points p, q and r. Assumes they are given in counterclockwise order around the triangle pqr.
{RETURN [Intercept[Bisector[a, b], Bisector[b, c]]]};
-- POINT TRANSFORMATIONS FOR PLOTTING - - - - - - - - - - - - - - - - - - - - - - - - - -
ScaleFactors: TYPE = RECORD
[sx, sy: REAL, -- stretching
tx, ty: REAL -- translation (after stretching)
];
ScalePoint: PROC [p: Point, sf: ScaleFactors] RETURNS [q: Point] = INLINE
-- Transforms point p according to the scale factors sf.
{RETURN [[p.x*sf.sx + sf.tx, p.y*sf.sy + sf.ty]]};
ScaleVector: PROC [v: Vector, sf: ScaleFactors] RETURNS [q: Point] = INLINE
-- Transforms the vector v according to the scale factors sf (will not translate the origin).
{RETURN [[v.x*sf.sx, v.y*sf.sy]]};
UnScalePoint: PROC [q: Point, sf: ScaleFactors] RETURNS [p: Point] = INLINE
-- Transforms point q according to the inverse of the scale factors sf.
{RETURN [[(q.x - sf.tx)/sf.sx, (q.y - sf.ty)/sf.sy]]};
BoxToBoxScale: PROC [bo, bd: Box, mf: REAL ← 0.1] RETURNS [sf: ScaleFactors];
-- Computes the scale factors (with same stretching in x and y) that map the box bo so that it fits into the box bd with the given extra margin mf around it (expressed as a fraction of the dimensions of bd).
-- MISCELLANEOUS - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Random: PROC [box: Box] RETURNS [v: Vector] = INLINE
-- Returns a random vector strictly inside the given box.
{RETURN [[Rand.Toss [box.min.x, box.max.x], Rand.Toss [box.min.y, box.max.y]]]};
END.