package OCADml

  1. Overview
  2. Docs

2d path generation (including arcs and basic shapes), manipulation (including offset and roundovers (see Round), and measurement.

type t = V2.t list

General Path Utilities

val length : ?closed:bool -> t -> float

length ?closed path

Calculate the length (total travel distance) of the path. If closed is true, include the distance between the endpoints (default = false).

val cummulative_length : ?closed:bool -> t -> float list

cummulative_length ?closed path

Calculate the cummulative length (distance travelled by each point) along the path. If closed is true, include the distance between the endpoints (default = false).

val to_continuous : ?closed:bool -> t -> float -> V2.t

to_continuous path

Return a continuous function from values over the range of 0. to 1. to positions along path (like a bezier function), treated as open (closed = false) by default.

val resample : freq:[< `N of int | `Spacing of float ] -> t -> t

resample ~freq path

Resample path with the given frequency (either a flat number of points, or a target point spacing). Note that the only points guaranteed to appear in the output are the start and end points of path. For upsampling that preserves input points, see subdivide.

val subdivide : ?closed:bool -> freq: [ `N of int * [ `ByLen | `BySeg ] | `RoughN of int * [ `ByLen | `BySeg ] | `Refine of int * [ `ByLen | `BySeg ] | `RoughRefine of int * [ `ByLen | `BySeg ] | `Spacing of float ] -> V2.t list -> V2.t list

subdivide ?closed ~freq path

Subdivides path with given frequency, including each of the original points in the output (unlike resample). This can be a flat number of points provided directly with `N (n, by), or as a multiple of number of points in path with `Refine (factor, by). The strategy for distribution of points for these count based methods is set with the second parameter by, which can be either `BySeg (same number of additional points per segment) and `ByLen (segments gain new points proportional to their length). Alternatively, a maximum `Spacing dist can be specified instead. The `Rough point sampling variants will favour sampling uniformity, at the expense of not adhering exactly to the requested point count.

val cut : ?closed:bool -> distances:[ `Abs of float list | `Rel of float list ] -> t -> t list

cut ?closed ~distances path

Cut path at a list of increasing distances (`Absolute or `Relative) along it from the start. If closed is true, the segment between the end and beginning of path will be considered, and the first point will be the last of the second path returned. Negative `Abs distance will start from the end to find the split point. Raises Invalid_argument if distance is an endpoint, or further than the end of the path.

val split : ?closed:bool -> distance:[ `Abs of float | `Rel of float ] -> t -> t * t

split ?closed ~distance path

Split path into two at the position distance (`Absolute or `Relative) along path from the start. Otherwise the behaviour is the same as cut.

val noncollinear_triple : ?eps:float -> t -> ((int * int * int) * (V2.t * V2.t * V2.t)) option

noncollinear_triple ?eps path

Returns a pair of triples of non-collinear indices and the corresponding points from path (if the path is not completely collinear). Two well separated points are selected, and the third point is the furthest off the line drawn by the first two points.

val is_collinear : ?eps:float -> t -> bool

is_collinear ?eps path

Returns true if all points in path are collinear (fall within eps distance of the same line).

val prune_collinear : ?closed:bool -> t -> t

prune_collinear path

Remove collinear points from path. If closed is true the last point can be pruned as collinear with the first. The first point is never pruned.

val deduplicate_consecutive : ?closed:bool -> ?keep:[ `First | `Last | `FirstAndEnds | `LastAndEnds ] -> ?eq:(V2.t -> V2.t -> bool) -> t -> t

deduplicate_consecutive ?closed ?keep ?eq path

Remove consecutive duplicate points as determined by the (approximate) equality function eq (V.approx ~eps:1e-9 by default) from path. By default keep is `First, which includes the first point of each run of duplicates in the output. This can be instead be set to keep the `Last, or to `FirstAndEnds or `LastAndEnds, which follow their respective simpler rules with the caveat of preserving the endpoints (first and last points) of the path. The path is treated as open (closed = false) by default, if closed is true the last point of the path may be dropped (even if keep is `FirstAndEnds | `LastAndEnds).

val deriv : ?closed:bool -> ?h:float -> t -> t

deriv ?closed ?h path

Computes a numerical derivative of path, with h (default 1.) giving the step size of the sampling of path, so that the derivative can be scaled correctly. Setting closed to true will include computation of the derivative between the last and first point of the path (default false).

val deriv_nonuniform : ?closed:bool -> h:float list -> t -> t

deriv_nonuniform ?closed ?h path

Computes a numerical derivative of path, with h giving the non-uniform step sizes of the sampling of path, so that the derivative can be scaled correctly. Setting closed to true will include computation of the derivative between the last and first point of the path (default false). As h provides scaling factors for each segment of the path, it must have a length of one less than path if it's unclosed, and the same length if closed is true.

val tangents : ?uniform:bool -> ?closed:bool -> t -> t

tangents ?uniform ?closed path

Compute tangent unit vectors of path. Set closed to true to indicate that tangents should include between the end and beginning of the path (default = false). Sampling of path is assumed to be uniform unless the parameter is set to false, in which case the derivatives will be adjusted to correct for non-uniform sampling of points.

val continuous_closest_point : ?closed:bool -> ?n_steps:int -> ?max_err:float -> (float -> V2.t) -> V2.t -> float

continuous_closest_point ?closed ?n_steps ?max_err f p

Find the closest position (from 0. to 1.) along the path function f to the point p.

  • n_steps sets the granularity of search at each stage.
  • max_err the maximum distance the solution can be from the target p
val segment : ?closed:bool -> t -> V2.line list

segment ?closed path

Break path into line segments. If closed is true, include a segment between the last and first points of path (default false).

val reindex_polygon : t -> t -> t

reindex_polygon reference poly

Rotate the polygonal (closed / coplanar) path poly to optimize its pairwise point association with the reference polygon. Paths should have the same clockwise winding direction (not checked / corrected).

val lerp : t -> t -> float -> t

lerp a b u

Linearly interpolate between the paths a and b. Raises Invalid_argument if the paths are of unequal length.

val nearby_idxs : ?min_tree_size:int -> ?radius:float -> V2.t list -> V2.t -> int list

nearby_idxs ?min_tree_size ?radius path p

Find the indices of points within radius (default = 1e-9) distance from the target point p in path. Match indices will be returned in arbitrary order (unsorted). When path is provided (eagerly on partial application), the length will be checked and a function to perform the search will be generated. If path is shorter than min_tree_size, it will be a simple direct search otherwise a BallTree2.t will be constructed. Thus, if you plan to search for more than one target point, take care to apply this function in two steps to avoid repeated length checks and closure/tree generations.

val nearby_points : ?min_tree_size:int -> ?radius:float -> V2.t list -> V2.t -> V2.t list

nearby_points ?min_tree_size ?radius path

Find the points within radius (default = 1e-9) distance from the target point p in path. Matched points will be returned in arbitrary order (unsorted). When path is provided (eagerly on partial application), the length will be checked and a function to perform the search will be generated. If path is shorter than min_tree_size, it will be a simple direct search otherwise a BallTree2.t will be constructed. Thus, if you plan to search for more than one target point, take care to apply this function in two steps to avoid repeated length checks and closure/tree generations.

val closest_tangent : ?closed:bool -> ?offset:V2.t -> line:V2.line -> t -> int * V2.line

closest_tangent ?closed ?offset ~line t

Find the tangent segment (and its index) on the curved path t closest to line after offset (default = V2.zero) is applied to the points of t (can be used to centre the path relative to line to help in choosing the desired tangent).

Creation and 2d-3d conversion

val of_tups : (float * float) list -> t

of_tups ps

Create a 2d path from a list of xy coordinate tuples.

val of_path3 : ?plane:Plane.t -> V3.t list -> t

of_path3 p

Project the 3d path p onto the given plane (default = Plane.xy).

val to_path3 : ?plane:Plane.t -> t -> V3.t list

to_path3 t

Lift the 2d path p onto the given plane (default = Plane.xy).

val lift : Plane.t -> t -> V3.t list

lift plane t

Lift the 2d path t onto the 3d plane.

Basic Shapes

val circle : ?fn:int -> ?fa:float -> ?fs:float -> float -> t

circle ?fn ?fa ?fs r

Create a circular path of radius r.

val square : ?center:bool -> V2.t -> t

square ?center dims

Create a rectangular path with xy dims (e.g. width and height). If center is true then the path will be centred around the origin (default = false).

val ellipse : ?fn:int -> ?fa:float -> ?fs:float -> V2.t -> t

ellipse ?fn ?fa ?fs radii

Draw an ellipse with xy radii. The greater of the two radii is used for fragment/resolution calculation.

val star : r1:float -> r2:float -> int -> t

star ~r1 ~r2 n

Draw an n pointed star with inner radius r1 and outer radius r2.

Drawing Arcs and Splines

val arc : ?rev:bool -> ?fn:int -> ?fa:float -> ?fs:float -> ?wedge:bool -> centre:V2.t -> radius:float -> start:float -> float -> t

arc ?rev ?fn ?fa ?fs ?wedge ~centre ~radius ~start a

Draw an arc of a radians with radius around the point centre, beginning with the angle start. If wedge is true, centre will be included as the last point of the returned path (default = false). If rev is true, the arc will end at start, rather than begin there.

val arc_about_centre : ?rev:bool -> ?fn:int -> ?fa:float -> ?fs:float -> ?dir:[ `CW | `CCW ] -> ?wedge:bool -> centre:V2.t -> V2.t -> V2.t -> t

arc_about_centre ?rev ?fn ?fa ?fs ?dir ?wedge ~centre p1 p2

Draw an arc between the points p1 and p2, about centre. dir can be provided to enforce clockwise or counter-clockwise winding direction. By default, the direction is computed automatically, though if centre, p1, and p2 do not form a valid triangle (they're collinear), an Invalid_argument exception will be raised if dir is not provided.

  • See arc for notes on rev and wedge.
val arc_through : ?rev:bool -> ?fn:int -> ?fa:float -> ?fs:float -> ?wedge:bool -> V2.t -> V2.t -> V2.t -> t

arc_through ?rev ?fn ?fa ?fs ?wedge p1 p2 p3

Draw an arc through the points p1, p2, and p3. If the points do not form a valid triangle (they're collinear), an Invalid_argument exception will be raised.

  • See arc for notes on rev, and wedge.
val cubic_spline : ?boundary:CubicSpline.boundary -> fn:int -> t -> t

cubic_spline ?boundary ~fn ps

Calculate a cubic spline with the given boundary condition (defaults to `Natural) for the 2-dimensional control points ps, and immediately interpolate a path of fn points along it. See the CubicSpline module for more details.

Roundovers

Outline offsets with optional rounding/chamfering as found in OpenSCADs 2d sub-system, as well as specification and application of non-offseting roundovers (circular, chamfer, and bezier (continuous curvature)) to 2d paths.

Based on the BOSL2 rounding module.

val offset : ?fn:int -> ?fs:float -> ?fa:float -> ?closed:bool -> ?check_valid:[ `Quality of int | `No ] -> ?mode:[< `Chamfer | `Delta | `Radius Delta ] -> float -> t -> t

offset ?fn ?fs ?fa ?closed ?check_valid ?mode d path

Offset a 2d path (treated as closed by default) by the specified distance d.The mode governs how d is used to create the new corners.

  • `Delta will create a new outline whose sides are a fixed distance d (+ve out, -ve in) from the original outline (this is the default behaviour).
  • `Chamfer fixed distance offset by d as with delta, but with corners chamfered.
  • `Radius creates a new outline as if a circle of some radius d is rotated around the exterior (d > 0) or interior (d < 0) original outline. fn, fs, and fa parameters govern the number of points that will be used for these arcs (they are ignored for delta and chamfer modes).
  • The check_valid default of `Quality 1 will check the validity of shifted line segments by checking whether their ends and n additional points spaced throughout are far enough from the original path. If there are no points that have been offset by the target d, a Failure exception will be raised. Checking can be turned off by setting this to `No.
module Round : sig ... end

Configuration module with types and helpers for specifying path roundovers.

val roundover : ?fn:int -> ?fa:float -> ?fs:float -> ?overrun:[ `Fail | `Warn | `Fix | `NoCheck ] -> Round.t -> V2.t list

roundover ?fn ?fa ?fs ?overrun path_spec

Apply the roundover specifictions in path_spec on the bundled path/shape, with quality set by the fn, fa, and fs parameters. Collinear points are ignored (included in output without roundover applied).

When overrun is set to `Fail (as it is by default) this function will raise Failure if computed joint distances would lead to point insertion that causes the path to reverse/double back on itself. Alternatively:

  • `Warn will print the detected overruns to stdout rather than raising Failure (useful for debuggin)
  • `Fix will automatically reduce the corner joints involved in each overrun proportional to their lengths.
  • `NoCheck skips overrun detection

Geometry

val clockwise_sign : ?eps:float -> t -> float

clockwise_sign path

Returns the rotational ordering of path as a signed float, -1. for clockwise, and 1. for counter-clockwise. If all points are collinear (within the tolerance of eps), 0. is returned.

val is_clockwise : t -> bool

is_clockwise path

Returns true if the rotational ordering of path is clockwise.

val self_intersections : ?eps:float -> ?closed:bool -> t -> t

self_intersection ?eps ?closed path

Find the points at which path intersects itself (within the tolerance of eps). If closed is true, a line segment between the last and first points will be considered (default = false).

val is_simple : ?eps:float -> ?closed:bool -> t -> bool

is_simple ?eps ?closed path

Return true if path is simple, e.g. contains no (paralell) reversals or self-intersections (within the tolerance eps). If closed is true, a line segment between the last and first points will be considered (default = false).

val bbox : t -> V2.bbox

bbox t

Compute the 2d bounding box of the path t.

val centroid : ?eps:float -> t -> V2.t

centroid ?eps t

Compute the centroid of the path t. If t is collinear or self-intersecting (within eps tolerance), an Invalid_argument exception is raised.

val area : ?signed:bool -> t -> float

area ?signed t

Compute the signed or unsigned area of the path t (unsigned by default).

val point_inside : ?eps:float -> ?nonzero:bool -> t -> V2.t -> [> `Inside | `OnBorder | `Outside ]

point_inside ?eps ?nonzero t p

Determine whether the point p is inside, on the border of, or outside the closed path t (may be non-simple / contain self-intersections). If nonzero is true, the Nonzero rule is followed, wherein a point is considered inside the polygon formed by t regardless of the number of times the containing regions overlap, by default this is false, and the Even-Odd rule is followed (as with in OpenSCAD).

val hull : ?all:bool -> t -> t

hull ?all t

Compute the convex hull polygon that encloses the points in the path t. When all is true, the output path will include the non-vertex points resting on the edges of the hull (default = false).

Path Matching / Vertex Association

Point duplicating strategies for associating vertices between incommensurate closed polygonal paths/profiles. Primarily for use in conjunction with Mesh.skin and Mesh.morphing_sweep, where commensurate profiles are required to draw edges between.

Ported from the skin module of the BOSL2 OpenSCAD library.

val distance_match : V2.t list -> V2.t list -> V2.t list * V2.t list

distance_match a b

If the closed polygonal paths a and b have incommensurate lengths, points on the smaller path are duplicated and the larger path is shifted (list rotated) in such a way that the total length of the edges between the associated vertices (same index/position) is minimized. The replacement paths, now having the same lengths, are returned as a pair (with the same order). This algorithm generally produces a good result when both a and b are discrete profiles with a small number of vertices.

This is computationally intensive ( O(N3) ), if the profiles are already known to be lined up, with their zeroth indices corresponding, then aligned_distance_match provides a ( O(N2) ) solution.

val aligned_distance_match : V2.t list -> V2.t list -> V2.t list * V2.t list

aligned_distance_match a b

Like distance_match, but the paths a and b are assumed to already be "lined up", with the zeroth indices in each corresponding to one another.

val tangent_match : V2.t list -> V2.t list -> V2.t list * V2.t list

tangent_match a b

If the closed polygonal paths a and b have incommensurate lengths, points on the larger (ideally convex, curved) path are grouped by association of their tangents with the edges of the smaller (ideally discrete) polygonal path. The points of the smaller path are then duplicated to associate with their corresponding spans of tangents on the curve, and the larger path is rotated to line up the indices. The profiles, now having the same length are returned as a pair in the order that they were applied returned.

This algorithm generally produces good results when connecting a discrete polygon to a convex finely sampled curve. It may fail if the curved profile is non-convex, or doesn't have enough points to distinguish all of the tangent points from each other.

Basic Transfomations

val translate : V2.t -> t -> t
val xtrans : float -> t -> t
val ytrans : float -> t -> t
val rotate : ?about:V2.t -> float -> t -> t
val zrot : ?about:V2.t -> float -> t -> t
val affine : Affine2.t -> t -> t
val affine3 : Affine3.t -> t -> V3.t list
val quaternion : ?about:V3.t -> Quaternion.t -> t -> V3.t list
val axis_rotate : ?about:V3.t -> V3.t -> float -> t -> V3.t list
val scale : V2.t -> t -> t
val xscale : float -> t -> t
val yscale : float -> t -> t
val mirror : V2.t -> t -> t