# The Path interface

A `Path.T` is a sequence of straight and curved line segments, suitable for stroking or filling.

A segment is a directed arc in the Cartesian plane determined by two cubic polynomials `h(t)`, `v(t)`, where `t` ranges over the interval of real numbers `[0, 1]`. The segment is said to start at `(h(0), v(0))` and end at `(h(1), v(1))`. If `h` and `v` are linear functions of `t`, then the segment is linear: it consists of a line segment. If `h` and `v` are constant functions of `t`, then the segment is degenerate: it consists of a single point.

The segments of a path are grouped into contiguous subpaths, which can be open or closed. Within a subpath, each segment starts where the previous segment ends. In a closed subpath, the last segment ends where the first segment starts. (This may also happen for an open subpath, but this coincidence does not make the subpath closed.)

The current point of a path is the endpoint of the last segment of its last subpath, assuming this subpath is open. If the path is empty or if the last subpath is closed, the current point is undefined.

    INTERFACE Path;

    IMPORT Point, Rect;

    TYPE T <: ROOT;

The call `NEW(Path.T)` creates an empty path.

    PROCEDURE Reset(path: T);

Set `path` to be empty.

    PROCEDURE MoveTo(path: T; READONLY p: Point.T);

Extend `path` with a new degenerate segment that starts and ends at `p`. This begins a new subpath.

    PROCEDURE LineTo(path: T; READONLY p: Point.T);

Extend `path` with a linear segment that starts at its current point and ends at `p`.

    PROCEDURE CurveTo(path: T; READONLY q, r, s: Point.T);

Extend `path` with a curved segment that starts at its current point and ends at `s`.

`CurveTo` adds a curve that starts from the current point of `path` in the direction of `q`, and ends at `s` coming from the direction of `r`. More precisely, let `p` be the current point of `path` and let `h(t)` and `v(t)` be the cubic polynomials such that

     (h(0), v(0)) = p
     (h(1), v(1)) = s
     (h'(0), v'(0)) = 3 * (q - p)
     (h'(1), v'(1)) = 3 * (s - r)

(Where the primes denote differentiation with respect to `t`.) Then `CurveTo` adds the segment `(h(t), v(t))` for `t` between zero and one. (This is called the Bezier arc determined by `p`, `q`, `r`, and `s`.)

    PROCEDURE Close(path: T);

Add a linear segment to create a closed loop in `path`.

More precisely, let `p` be the current point of `path`, and let `q` be last point of `path` that was added by a call to `MoveTo` (Thus `q` is the startpoint of the first segment of the last subpath of `path`.) `Close` adds a linear segment from `p` to `q` and marks the sequence of segments from `q` to the end of the path as a closed subpath.

    PROCEDURE IsEmpty(p: T): BOOLEAN;

Returns `TRUE` if `p` is empty.

    PROCEDURE IsClosed(p: T): BOOLEAN;

Returns `TRUE` if `p` is empty or the last subpath of `p` is closed.

    PROCEDURE CurrentPoint(p: T): Point.T;

Returns the current point of `p`.

`LineTo`, `CurveTo`, `Close`, and `CurrentPoint` are checked runtime errors if the path has no current point.

    EXCEPTION Malformed;

The `Malformed` exception is raised when a procedure detects a malformed path.

    PROCEDURE Translate(p: T; READONLY delta: Point.T): T
      RAISES {Malformed};

The result of translating `p` by `delta`.

    TYPE
      MapObject = OBJECT METHODS
        move(READONLY pt: Point.T);
        line(READONLY pt1, pt2: Point.T);
        close(READONLY pt1, pt2: Point.T);
        curve(READONLY pt1, pt2, pt3, pt4: Point.T)
      END;

    PROCEDURE Map(path: T; map: MapObject) RAISES {Malformed};

Apply the appropriate method of `map` to each segment of `path`.

That is, for each segment `s` of `path`, in order, `Map` excecutes the following:

     IF s is a linear segment (p, q) THEN
       IF s was generated by MoveTo THEN
          (* p = q *)
          map.move(p)
       ELSIF s was generated by LineTo THEN
         map.line(p, q)
       ELSE (* s was generated by Close *)
         map.close(p, q)
       END
     ELSE (* s is a curved segment (p, q, r, s) *)
       map.curve(p, q, r, s)
     END

`Map` raises the exception if it is passed a malformed path.

    PROCEDURE Copy(p: T): T;

Returns a newly allocated path with the same contents as `p`.

    PROCEDURE Flatten(p: T): T RAISES {Malformed};

Return a path like `p` but with curved segments replaced by polygonal approximations.

    PROCEDURE BoundingBox(p: T): Rect.T RAISES {Malformed};

Return a rectangle that contains all points of `p`, and that is as small as convenient to compute.

    END Path.