Library
Module
Module type
Parameter
Class
Class type
3d path generation (including arcs and basic shapes), manipulation (including roundovers (see Round
), and conversion to sweeping transformations with to_transforms
), and measurement.
type t = V3.t list
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
).
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.
resample ~freq path
Resample path
with the given freq
uency (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 ] ->
V3.t list ->
V3.t list
subdivide ?closed ~freq path
Subdivides path
with given freq
uency, 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.
cut ?closed ~distances path
Cut path
at a list of increasing distances
(`Abs
olute or `Rel
ative) 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
.
split ?closed ~distance path
Split path
into two at the position distance
(`Abs
olute or `Rel
ative) along path
from the start. Otherwise the behaviour is the same as cut
.
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).
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:(V3.t -> V3.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
).
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
).
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
.
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 -> V3.t) ->
V3.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
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
).
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).
lerp a b u
Linearly interpolate between the paths a
and b
. Raises Invalid_argument
if the paths are of unequal length.
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 BallTree3.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.
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 BallTree3.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.
closest_tangent ?closed ?offset ~line t
Find the tangent segment (and its index) on the curved path t
closest to line
after offset
(default = V3.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).
val of_tups : (float * float * float) list -> t
of_tups ps
Create a 3d path from a list of xyz coordinate triples.
of_path2 ?plane path
Lift a 2d path
onto plane
(default = Plane.xy
).
to_path2 ?plane t
Project the 3d path t
onto plane
(default = Plane.xy
).
to_plane ?no_check ?eps t
Compute the normalized cartesian equation of the plane that the path t
resides on. If there are fewer than three points in t
, or they are not coplanar within the tolerance eps
(unless no_check = true
), an Invalid_argument
exception is raised.
circle ?fn ?fa ?fs ?plane radius
Draw a circular path of radius r
onto plane
(default = Plane.xy
).
square ?center ?plane dims
Draw a rectangular path with xy dims
(e.g. width and height) onto plane
(default = Plane.xy
). If center
is true
then the path will be centred around the origin (default = false
).
ellipse ?fn ?fa ?fs ?plane radii
Draw an ellipse with xy radii
onto plane
(default = Plane.xy
). The greater of the two radii is used for fragment/resolution calculation.
star ?plane ~r1 ~r2 n
Draw an n
pointed star with inner radius r1
and outer radius r2
onto plane
(default = Plane.xy
).
val arc_about_centre :
?rev:bool ->
?fn:int ->
?fa:float ->
?fs:float ->
?dir:[ `CW | `CCW ] ->
?wedge:bool ->
centre:V3.t ->
V3.t ->
V3.t ->
t
arc_about_centre ?rev ?fn ?fa ?fs ?dir ?wedge ~centre p1 p2
Draw an arc between p1
and p2
, about centre
, on the 3d plane occupied by the three points. See Path2.arc_about_centre
.
val arc_through :
?rev:bool ->
?fn:int ->
?fa:float ->
?fs:float ->
?wedge:bool ->
V3.t ->
V3.t ->
V3.t ->
t
arc_through ?rev ?fn ?fa ?fs ?wedge p1 p2 p3
Draw an arc through the points p1
, p2
, and p3
. See Path2.arc_through
.
val helix :
?fn:int ->
?fa:float ->
?fs:float ->
?left:bool ->
n_turns:int ->
pitch:float ->
?r2:float ->
float ->
t
helix ?fn ?fa ?fs ?left ~n_turns ~pitch ?r2 r1
Draw a 3d helical path around a cylinder/cone with start radius r1
and end radius r2
(default = r1
).
n_turns
sets the number of revolutions around the z-axispitch
describes the height of one complete turnleft
is used to set handedness (default = true
)fn
, fa
, and fs
parameters govern quality as they do in OpenSCADSpecification and application of circular, chamfer, and bezier (continuous curvature) roundovers to 3d paths.
Based on the BOSL2 rounding module.
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 ->
V3.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 detectionnormal t
Calculate the normal vector of the path t
. An Invalid_argument
exception is raised if there are fewer than three points in 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
Calculate the area of the co-planar path (describing a polygon) t
. If signed
is true
, the signed area is returned.
val coplanar : ?eps:float -> t -> bool
coplanar ?eps t
Returns true
if all points in t
are coplanar, within the tolerance eps
. If there are fewer than 3 points, or the path is collinear, this returns false
.
val bbox : t -> Gg.Box3.t
bbox t
Compute the 3d bounding box of the path t
.
scaler ?ez scale
Create a lookup from relative position (0.
to 1.
) to scaling transformation matrix for interpolating from {x = 1.; y = 1.}
at 0.
to scale
by 1.
. If provided, the pair of handle points ez
will be used to ease the scaling (see Easing.make
).
twister ?ez angle
Create a lookup from relative position (0.
to 1.
) to rotation transformation matrix for interpolating from 0.
(no rotation) at 0.
to angle
by 1.
. If provided, the pair of handle points ez
will be used to ease the scaling (see Easing.make
).
val to_transforms :
?mode:[ `Auto | `Align of V3.t | `NoAlign | `Euler ] ->
?scale_ez:(V2.t * V2.t) ->
?twist_ez:(V2.t * V2.t) ->
?scale:V2.t ->
?twist:float ->
t ->
Affine3.t list
to_transforms ?mode ?scale_ez ?twist_ez ?scale ?twist t
Generate list of transformations that can be applied to three-dimensional vectors (V3.t
via Affine3.transform
) or shapes, to move them along the path t
(intended to be applied to the vector/shape from its original position each time). Note that the transforms are generated assuming the shape is centred on the origin, and for the purposes of alignment to the beginning of the path and scaling/twisting, laying flat on the XY plane.
When mode
is `Auto | `Align _ | `NoAlign
(default it `Auto
), tangents are used to estimate appropriate rotations for each translation, using quaternion alignment from tangent to tangent, accumulating rotation along the way. In the case of `Auto
, some effort is made to automatically apply an alignment rotation towards the first tangent of the path before each transformation such that the orientation of the shape being swept will be consistent and sensible, though some manual rotation of the shape before sweeping along the generated transforms may be necessary to get the desired result.
`Align v
and `NoAlign
follow the same quaternion accumulation strategy along the path t
, however they differ from `Auto
in their handling of pre-alignment transformations. With `Align v
, the vector v
will be used to align the assumed shape normal of (v3 0. 0. 1.)
to, while `NoAlign
will skip the alignment step entirely (likely desirable if the intention is to sweep a shape not laying on the XY plane).
Setting mode = `Euler
will use euler rotations instead, which can have results more in line with expectations in some scenarios (helical-like paths for example, though Mesh.helix_extrude
may be a better fit in that case), but fail in others. For instance, `Euler
can generate an abrupt when the path tangent is exactly vertical.
If provided, scale
and twist
, specify scaling and rotation to be applied along the path increasing up to the provided value by the end. Scaling/twisting proceeds linearly by default, unless the corresponding _ez
parameters are provided to describe the desired eased transition (see scaler
and twister
).
val helical_transforms :
?fn:int ->
?fa:float ->
?fs:float ->
?scale_ez:(V2.t * V2.t) ->
?twist_ez:(V2.t * V2.t) ->
?scale:V2.t ->
?twist:float ->
?left:bool ->
n_turns:int ->
pitch:float ->
?r2:float ->
float ->
Affine3.t list
helical_transforms ?fn ?fs ?fa ?scale_ez ?twist_ez ?scale ?twist
?left ~n_turns ~pitch ?r2 r1
Affine transformations following a helical path. This can be thought of like a special case of to_transforms
, but using a path generated with helix
, and with rotational calculations taking into account the helical trajectory.
val prune_transforms :
?min_dist:float ->
shape:(int -> t) ->
Affine3.t list ->
(int * Affine3.t) list
prune_transforms ?min_dist ~shape transforms
Filter transforms
into (idx, m)
pairs for which the polygon computed by applying m
to the corresponding shape (result of shape idx
, allowing for scaling operations etc) is at least min_dist
(default 0.05
) above the plane of the previous polygon. This can be useful for avoiding self-intersections in the output of Mesh.sweep
. Note that all polygons returned by shape
must be planar, else Failure
will be raised.
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.
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.
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.
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.
val quaternion : ?about:V3.t -> Quaternion.t -> t -> t