Module Hugin_gg.Path
Paths.
A path is a sequence of subpaths, each a start point followed by line and cubic Bézier segments, optionally closed. Build one segment by segment from empty, or take a shape: rect, circle, and polyline and polygon, which store their points in two float arrays without a block per point. Quadratic Béziers and circular arcs are converted to cubic Béziers as they are added, exactly for quadratics and within a stated error for arcs, so every renderer draws the same curves.
fold visits what a path draws, segment by segment, with its gaps applied; flatten does the same with curves replaced by chords. Renderers draw paths through them.
Types
type tThe type for paths.
is_empty p is equal p empty. A path of gaps only is not empty; bounds p = None tells whether p draws anything.
Building
A subpath is open until closed. Filling closes open subpaths implicitly; stroking does not. A line or curve added to a path without a current point, that is to empty or right after close, starts a subpath at its end point instead; an arc, which carries its own start, starts a subpath there and is drawn.
quad_to c pt p is p with a quadratic Bézier from the current point to pt with control point c, stored as the cubic of the same curve.
cubic_to c c' pt p is p with a cubic Bézier from the current point to pt with control points c and c'.
arc c r ~start ~sweep p is p with the circular arc of centre c and radius r from angle start through sweep radians, positive sweeps turning from the positive x axis towards the positive y axis. If p has a current point, a line joins it to the arc's start; otherwise the arc starts a subpath. The arc is stored as cubic Béziers, one per quarter turn or less, which depart from the circle by at most 0.03% of r. A sweep beyond a full turn retraces the circle.
Raises Invalid_argument if r is negative, if r, start or sweep is not finite, or if sweep exceeds a thousand turns in magnitude.
close p is p with its last subpath closed by a line back to its start, after which p has no current point. It is p if p is empty or its last subpath is closed or has no segment.
append q p is the subpaths of p followed by those of q. The order is the opposite of List.append, so that p |> append q appends q.
transform m p is p with every point mapped through m. Since curves are cubic Béziers, the image of a curve is exact.
crop b p is what p draws within b, its edges included, subpath by subpath:
- An open subpath keeps its pieces within
b, each an open subpath of its own. A segment that crosses an edge ofbis cut where it crosses, a curve into the cubic of its part withinb. - A closed subpath becomes a closed subpath whose region under either fill rule is its own region within
b: its pieces outsidebare replaced by segments along the edges ofb, where a stroke of the result runs. It ends with its close, with no line back to its start, and it is dropped if what remains of it has no area, its points on one line.
A cut adds no segment of zero length. So cropping before filling draws what filling then clipping to b draws, and cropping an open path before stroking keeps the pen's ink whole where the path lies within b. A point that a cut puts on an edge of b has that edge's coordinate exactly. Gaps are applied first, so every point of crop b p is finite; if everything fold visits in p is within b, crop b p is p.
Shapes
rect b is the closed subpath around b from its corner (minx b, miny b) along the x axis first, clockwise on screen.
polyline xs ys is the open subpath through the points (xs.(i), ys.(i)) in order, or empty for fewer than two points. The arrays are copied.
Raises Invalid_argument if xs and ys differ in length.
Traversing
Gaps
The points of a segment are the points it was built with: its end point and, for a curve, its control points. The point it starts from is not one of them. A segment with a non-finite point is a gap, and so is a subpath start at a non-finite point. The subpath a gap would extend ends there, unclosed, and the next segment whose points are finite starts a subpath at its end point. A close closes the subpath current at that moment, if that subpath has a segment. After a gap, that is the subpath the gap's successor started, and the close does nothing if no such subpath has started.
val fold :
move:('a -> float -> float -> 'a) ->
line:('a -> float -> float -> 'a) ->
cubic:('a -> float -> float -> float -> float -> float -> float -> 'a) ->
close:('a -> 'a) ->
'a ->
t ->
'afold ~move ~line ~cubic ~close acc p folds over what p draws, in drawing order: move acc x y starts a subpath, line acc x y and cubic acc c1x c1y c2x c2y x y extend it and close acc closes it. Gaps are applied, so every number given is finite: a segment after a gap is given as move to its end point. Polylines and polygons are visited point by point, quadratics and arcs as the cubics that store them.
val flatten :
?tolerance:float ->
Affine.t ->
move:('a -> float -> float -> 'a) ->
line:('a -> float -> float -> 'a) ->
close:('a -> 'a) ->
'a ->
t ->
'aflatten ~tolerance m ~move ~line ~close acc p is fold over transform m p, with every cubic replaced by line segments that stay within tolerance of it. tolerance defaults to 0.1. Gaps are those of transform m p, so every number given is finite whatever m. A cubic is cut into at most 65536 chords, which keeps it within tolerance if its control points lie within 1e9 *. tolerance of each other.
Raises Invalid_argument if tolerance is not positive.