libm3/src/geometry/Path.i3


 Copyright (C) 1992, Digital Equipment Corporation                         
 All rights reserved.                                                      
 See the file COPYRIGHT for a full description.                            
                                                                           
 Path.def, by Greg Nelson, Wed Nov 12 00:05:53 1986                        
 Last modified on Wed Apr  6 11:35:35 PDT 1994 by heydon                   
      modified on Tue Feb 11 16:21:42 PST 1992 by muller                   
      modified on Mon Nov 11  0:57:57 PST 1991 by gnelson                  
      modified on Fri May 11 15:18:51 PDT 1990 by steveg                   
      modified on Tue Aug  4 10:28:16 1987 by mkent                        
<*PRAGMA LL*>
A Path.T is a sequence of straight and curved line segments, suitable for stroking or filling.

A {\it 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 {\it start} at (h(0), v(0)) and {\it end} at (h(1), v(1)). If h and v are linear functions of t, then the segment is {\it linear}: it consists of a line segment. If h and v are constant functions of t, then the segment is {\it degenerate}: it consists of a single point.

The segments of a path are grouped into contiguous {\it subpaths}, which can be {\it open} or {\it 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 {\it 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 {\it 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.