mirror of
https://github.com/BelfrySCAD/BOSL2.git
synced 2024-12-29 16:29:40 +00:00
Fix docs for path functions with 1-regions, change is_path_region to is_1region
This commit is contained in:
parent
94033a0bfd
commit
14804421b7
2 changed files with 90 additions and 82 deletions
166
paths.scad
166
paths.scad
|
@ -1,14 +1,16 @@
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
// LibFile: paths.scad
|
// LibFile: paths.scad
|
||||||
// Support for polygons and paths.
|
// A `path` is a list of points of the same dimensions, usually 2D or 3D, that can
|
||||||
|
// be connected together to form a sequence of line segments or a polygon.
|
||||||
|
// The functions in this file work on paths and also 1-regions, which are regions
|
||||||
|
// that include exactly one path. Capabilities include computing length of paths, computing
|
||||||
|
// path tangents and normals, resampling of paths, and cutting paths up into smaller paths.
|
||||||
// Includes:
|
// Includes:
|
||||||
// include <BOSL2/std.scad>
|
// include <BOSL2/std.scad>
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
// Section: Utility Functions
|
// Section: Utility Functions
|
||||||
|
|
||||||
|
|
||||||
// Function: is_path()
|
// Function: is_path()
|
||||||
// Usage:
|
// Usage:
|
||||||
// is_path(list, [dim], [fast])
|
// is_path(list, [dim], [fast])
|
||||||
|
@ -16,7 +18,8 @@
|
||||||
// Returns true if `list` is a path. A path is a list of two or more numeric vectors (AKA points).
|
// Returns true if `list` is a path. A path is a list of two or more numeric vectors (AKA points).
|
||||||
// All vectors must of the same size, and may only contain numbers that are not inf or nan.
|
// All vectors must of the same size, and may only contain numbers that are not inf or nan.
|
||||||
// By default the vectors in a path must be 2d or 3d. Set the `dim` parameter to specify a list
|
// By default the vectors in a path must be 2d or 3d. Set the `dim` parameter to specify a list
|
||||||
// of allowed dimensions, or set it to `undef` to allow any dimension.
|
// of allowed dimensions, or set it to `undef` to allow any dimension. (Note that this function
|
||||||
|
// returns `false` on 1-regions.)
|
||||||
// Example:
|
// Example:
|
||||||
// bool1 = is_path([[3,4],[5,6]]); // Returns true
|
// bool1 = is_path([[3,4],[5,6]]); // Returns true
|
||||||
// bool2 = is_path([[3,4]]); // Returns false
|
// bool2 = is_path([[3,4]]); // Returns false
|
||||||
|
@ -45,18 +48,17 @@ function is_path(list, dim=[2,3], fast=false) =
|
||||||
&& len(list[0])>0
|
&& len(list[0])>0
|
||||||
&& (is_undef(dim) || in_list(len(list[0]), force_list(dim)));
|
&& (is_undef(dim) || in_list(len(list[0]), force_list(dim)));
|
||||||
|
|
||||||
// Function: is_path_region()
|
// Function: is_1region()
|
||||||
// Usage:
|
// Usage:
|
||||||
// bool = is_path_region(path, [name])
|
// bool = is_1region(path, [name])
|
||||||
// Description:
|
// Description:
|
||||||
// If `path` is a region with one component then return true. If path is a region with more components
|
// If `path` is a region with one component (a 1-region) then return true. If path is a region with more components
|
||||||
// then display an error message about the parameter `name` requiring a path or a single component region. If the input
|
// then display an error message about the parameter `name` requiring a path or a single component region. If the input
|
||||||
// is not a region then return false. This function helps accept singleton regions in functions that
|
// is not a region then return false. This function helps path functions accept 1-regions.
|
||||||
// operate on a path.
|
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// path = input to process
|
// path = input to process
|
||||||
// name = name of parameter to use in error message. Default: "path"
|
// name = name of parameter to use in error message. Default: "path"
|
||||||
function is_path_region(path, name="path") =
|
function is_1region(path, name="path") =
|
||||||
!is_region(path)? false
|
!is_region(path)? false
|
||||||
:assert(len(path)==1,str("Parameter \"",name,"\" must be a path or singleton region, but is a multicomponent region"))
|
:assert(len(path)==1,str("Parameter \"",name,"\" must be a path or singleton region, but is a multicomponent region"))
|
||||||
true;
|
true;
|
||||||
|
@ -65,10 +67,9 @@ function is_path_region(path, name="path") =
|
||||||
// Usage:
|
// Usage:
|
||||||
// outpath = force_path(path, [name])
|
// outpath = force_path(path, [name])
|
||||||
// Description:
|
// Description:
|
||||||
// If `path` is a region with one component then return that component as a path. If path is a region with more components
|
// If `path` is a region with one component (a 1-region) then return that component as a path. If path is a region with more components
|
||||||
// then display an error message about the parameter `name` requiring a path or a single component region. If the input
|
// then display an error message about the parameter `name` requiring a path or a single component region. If the input
|
||||||
// is not a region then return the input without any checks. This function helps accept singleton regions in functions that
|
// is not a region then return the input without any checks. This function helps path functions accept 1-regions.
|
||||||
// operate on a path.
|
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// path = input to process
|
// path = input to process
|
||||||
// name = name of parameter to use in error message. Default: "path"
|
// name = name of parameter to use in error message. Default: "path"
|
||||||
|
@ -142,11 +143,11 @@ function _path_select(path, s1, u1, s2, u2, closed=false) =
|
||||||
// Usage:
|
// Usage:
|
||||||
// path_merge_collinear(path, [eps])
|
// path_merge_collinear(path, [eps])
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// path = A list of path points of any dimension.
|
// path = A path of any dimension or a 1-region
|
||||||
// closed = treat as closed polygon. Default: false
|
// closed = treat as closed polygon. Default: false
|
||||||
// eps = Largest positional variance allowed. Default: `EPSILON` (1-e9)
|
// eps = Largest positional variance allowed. Default: `EPSILON` (1-e9)
|
||||||
function path_merge_collinear(path, closed, eps=EPSILON) =
|
function path_merge_collinear(path, closed, eps=EPSILON) =
|
||||||
is_path_region(path) ? path_merge_collinear(path[0], default(closed,true), eps) :
|
is_1region(path) ? path_merge_collinear(path[0], default(closed,true), eps) :
|
||||||
let(closed=default(closed,false))
|
let(closed=default(closed,false))
|
||||||
assert(is_bool(closed))
|
assert(is_bool(closed))
|
||||||
assert( is_path(path), "Invalid path in path_merge_collinear." )
|
assert( is_path(path), "Invalid path in path_merge_collinear." )
|
||||||
|
@ -172,13 +173,13 @@ function path_merge_collinear(path, closed, eps=EPSILON) =
|
||||||
// Description:
|
// Description:
|
||||||
// Returns the length of the path.
|
// Returns the length of the path.
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// path = The list of points of the path to measure.
|
// path = Path of any dimension or 1-region.
|
||||||
// closed = true if the path is closed. Default: false
|
// closed = true if the path is closed. Default: false
|
||||||
// Example:
|
// Example:
|
||||||
// path = [[0,0], [5,35], [60,-25], [80,0]];
|
// path = [[0,0], [5,35], [60,-25], [80,0]];
|
||||||
// echo(path_length(path));
|
// echo(path_length(path));
|
||||||
function path_length(path,closed) =
|
function path_length(path,closed) =
|
||||||
is_path_region(path) ? path_length(path[0], default(closed,true)) :
|
is_1region(path) ? path_length(path[0], default(closed,true)) :
|
||||||
assert(is_path(path), "Invalid path in path_length")
|
assert(is_path(path), "Invalid path in path_length")
|
||||||
let(closed=default(closed,false))
|
let(closed=default(closed,false))
|
||||||
assert(is_bool(closed))
|
assert(is_bool(closed))
|
||||||
|
@ -192,10 +193,10 @@ function path_length(path,closed) =
|
||||||
// Description:
|
// Description:
|
||||||
// Returns list of the length of each segment in a path
|
// Returns list of the length of each segment in a path
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// path = path to measure
|
// path = path in any dimension or 1-region
|
||||||
// closed = true if the path is closed. Default: false
|
// closed = true if the path is closed. Default: false
|
||||||
function path_segment_lengths(path, closed) =
|
function path_segment_lengths(path, closed) =
|
||||||
is_path_region(path) ? path_segment_lengths(path[0], default(closed,true)) :
|
is_1region(path) ? path_segment_lengths(path[0], default(closed,true)) :
|
||||||
let(closed=default(closed,false))
|
let(closed=default(closed,false))
|
||||||
assert(is_path(path),"Invalid path in path_segment_lengths.")
|
assert(is_path(path),"Invalid path in path_segment_lengths.")
|
||||||
assert(is_bool(closed))
|
assert(is_bool(closed))
|
||||||
|
@ -214,10 +215,10 @@ function path_segment_lengths(path, closed) =
|
||||||
// will have one extra point because of the final connecting segment that connects the last
|
// will have one extra point because of the final connecting segment that connects the last
|
||||||
// point of the path to the first point.
|
// point of the path to the first point.
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// path = path to operate on
|
// path = path in any dimension or a 1-region
|
||||||
// closed = set to true if path is closed. Default: false
|
// closed = set to true if path is closed. Default: false
|
||||||
function path_length_fractions(path, closed) =
|
function path_length_fractions(path, closed) =
|
||||||
is_path_region(path) ? path_length_fractions(path[0], default(closed,true)):
|
is_1region(path) ? path_length_fractions(path[0], default(closed,true)):
|
||||||
let(closed=default(closed, false))
|
let(closed=default(closed, false))
|
||||||
assert(is_path(path))
|
assert(is_path(path))
|
||||||
assert(is_bool(closed))
|
assert(is_bool(closed))
|
||||||
|
@ -337,7 +338,7 @@ function _sum_preserving_round(data, index=0) =
|
||||||
// a closed polygon the total number of points will be sum(N). Note that with an open
|
// a closed polygon the total number of points will be sum(N). Note that with an open
|
||||||
// path there is an extra point at the end, so the number of points will be sum(N)+1.
|
// path there is an extra point at the end, so the number of points will be sum(N)+1.
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// path = path to subdivide
|
// path = path in any dimension or a 1-region
|
||||||
// N = scalar total number of points desired or with `method="segment"` can be a vector requesting `N[i]-1` points on segment i.
|
// N = scalar total number of points desired or with `method="segment"` can be a vector requesting `N[i]-1` points on segment i.
|
||||||
// refine = number of points to add each segment.
|
// refine = number of points to add each segment.
|
||||||
// closed = set to false if the path is open. Default: True
|
// closed = set to false if the path is open. Default: True
|
||||||
|
@ -423,7 +424,7 @@ function subdivide_path(path, N, refine, closed=true, exact=true, method="length
|
||||||
// Description:
|
// Description:
|
||||||
// Evenly subdivides long `path` segments until they are all shorter than `maxlen`.
|
// Evenly subdivides long `path` segments until they are all shorter than `maxlen`.
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// path = The path to subdivide.
|
// path = path in any dimension or a 1-region
|
||||||
// maxlen = The maximum allowed path segment length.
|
// maxlen = The maximum allowed path segment length.
|
||||||
// ---
|
// ---
|
||||||
// closed = If true, treat path like a closed polygon. Default: true
|
// closed = If true, treat path like a closed polygon. Default: true
|
||||||
|
@ -459,7 +460,7 @@ function subdivide_long_segments(path, maxlen, closed=true) =
|
||||||
// Note that because this function operates on a discrete input path the quality of the output depends on
|
// Note that because this function operates on a discrete input path the quality of the output depends on
|
||||||
// the sampling of the input. If you want very accurate output, use a lot of points for the input.
|
// the sampling of the input. If you want very accurate output, use a lot of points for the input.
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// path = path to resample
|
// path = path in any dimension or a 1-region
|
||||||
// N = Number of points in output
|
// N = Number of points in output
|
||||||
// spacing = Approximate spacing desired
|
// spacing = Approximate spacing desired
|
||||||
// closed = set to true if path is closed. Default: true
|
// closed = set to true if path is closed. Default: true
|
||||||
|
@ -493,11 +494,11 @@ function resample_path(path, N, spacing, closed=true) =
|
||||||
// still be simple.
|
// still be simple.
|
||||||
// If closed is set to true then treat the path as a polygon.
|
// If closed is set to true then treat the path as a polygon.
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// path = path to check
|
// path = 2D path or 1-region
|
||||||
// closed = set to true to treat path as a polygon. Default: false
|
// closed = set to true to treat path as a polygon. Default: false
|
||||||
// eps = Epsilon error value used for determine if points coincide. Default: `EPSILON` (1e-9)
|
// eps = Epsilon error value used for determine if points coincide. Default: `EPSILON` (1e-9)
|
||||||
function is_path_simple(path, closed, eps=EPSILON) =
|
function is_path_simple(path, closed, eps=EPSILON) =
|
||||||
is_path_region(path) ? is_path_simple(path[0], default(closed,true), eps) :
|
is_1region(path) ? is_path_simple(path[0], default(closed,true), eps) :
|
||||||
let(closed=default(closed,false))
|
let(closed=default(closed,false))
|
||||||
assert(is_path(path, 2),"Must give a 2D path")
|
assert(is_path(path, 2),"Must give a 2D path")
|
||||||
assert(is_bool(closed))
|
assert(is_bool(closed))
|
||||||
|
@ -551,7 +552,7 @@ function path_closest_point(path, pt, closed=true) =
|
||||||
// assumed to be non-uniform and the derivative is computed with adjustments to produce corrected
|
// assumed to be non-uniform and the derivative is computed with adjustments to produce corrected
|
||||||
// values.
|
// values.
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// path = path to find the tagent vectors for
|
// path = path of any dimension or a 1-region
|
||||||
// closed = set to true of the path is closed. Default: false
|
// closed = set to true of the path is closed. Default: false
|
||||||
// uniform = set to false to correct for non-uniform sampling. Default: true
|
// uniform = set to false to correct for non-uniform sampling. Default: true
|
||||||
// Example(2D): A shape with non-uniform sampling gives distorted derivatives that may be undesirable. Note that derivatives tilt towards the long edges of the rectangle.
|
// Example(2D): A shape with non-uniform sampling gives distorted derivatives that may be undesirable. Note that derivatives tilt towards the long edges of the rectangle.
|
||||||
|
@ -569,7 +570,7 @@ function path_closest_point(path, pt, closed=true) =
|
||||||
// for(i=[0:len(tangents)-1])
|
// for(i=[0:len(tangents)-1])
|
||||||
// stroke([rect[i]-tangents[i], rect[i]+tangents[i]],width=.25, endcap2="arrow2");
|
// stroke([rect[i]-tangents[i], rect[i]+tangents[i]],width=.25, endcap2="arrow2");
|
||||||
function path_tangents(path, closed, uniform=true) =
|
function path_tangents(path, closed, uniform=true) =
|
||||||
is_path_region(path) ? path_tangents(path[0], default(closed,true), uniform) :
|
is_1region(path) ? path_tangents(path[0], default(closed,true), uniform) :
|
||||||
let(closed=default(closed,false))
|
let(closed=default(closed,false))
|
||||||
assert(is_bool(closed))
|
assert(is_bool(closed))
|
||||||
assert(is_path(path))
|
assert(is_path(path))
|
||||||
|
@ -592,11 +593,11 @@ function path_tangents(path, closed, uniform=true) =
|
||||||
// For 2d paths the plane is always defined so the normal fails to exist only
|
// For 2d paths the plane is always defined so the normal fails to exist only
|
||||||
// when the derivative is zero (in the case of repeated points).
|
// when the derivative is zero (in the case of repeated points).
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// path = path to compute the normals to
|
// path = 2D or 3D path or a 1-region
|
||||||
// tangents = path tangents optionally supplied
|
// tangents = path tangents optionally supplied
|
||||||
// closed = if true path is treated as a polygon. Default: false
|
// closed = if true path is treated as a polygon. Default: false
|
||||||
function path_normals(path, tangents, closed) =
|
function path_normals(path, tangents, closed) =
|
||||||
is_path_region(path) ? path_normals(path[0], tangents, default(closed,true)) :
|
is_1region(path) ? path_normals(path[0], tangents, default(closed,true)) :
|
||||||
let(closed=default(closed,false))
|
let(closed=default(closed,false))
|
||||||
assert(is_path(path,[2,3]))
|
assert(is_path(path,[2,3]))
|
||||||
assert(is_bool(closed))
|
assert(is_bool(closed))
|
||||||
|
@ -623,9 +624,12 @@ function path_normals(path, tangents, closed) =
|
||||||
// Usage:
|
// Usage:
|
||||||
// curvs = path_curvature(path, [closed]);
|
// curvs = path_curvature(path, [closed]);
|
||||||
// Description:
|
// Description:
|
||||||
// Numerically estimate the curvature of the path (in any dimension).
|
// Numerically estimate the curvature of the path (in any dimension).
|
||||||
|
// Arguments:
|
||||||
|
// path = path in any dimension or a 1-region
|
||||||
|
// closed = if true then treat the path as a polygon. Default: false
|
||||||
function path_curvature(path, closed) =
|
function path_curvature(path, closed) =
|
||||||
is_path_region(path) ? path_curvature(path[0], default(closed,true)) :
|
is_1region(path) ? path_curvature(path[0], default(closed,true)) :
|
||||||
let(closed=default(closed,false))
|
let(closed=default(closed,false))
|
||||||
assert(is_bool(closed))
|
assert(is_bool(closed))
|
||||||
assert(is_path(path))
|
assert(is_path(path))
|
||||||
|
@ -643,9 +647,12 @@ function path_curvature(path, closed) =
|
||||||
|
|
||||||
// Function: path_torsion()
|
// Function: path_torsion()
|
||||||
// Usage:
|
// Usage:
|
||||||
// tortions = path_torsion(path, [closed]);
|
// torsions = path_torsion(path, [closed]);
|
||||||
// Description:
|
// Description:
|
||||||
// Numerically estimate the torsion of a 3d path.
|
// Numerically estimate the torsion of a 3d path.
|
||||||
|
// Arguments:
|
||||||
|
// path = 3D path
|
||||||
|
// closed = if true then treat path as a polygon. Default: false
|
||||||
function path_torsion(path, closed=false) =
|
function path_torsion(path, closed=false) =
|
||||||
assert(is_path(path,3), "Input path must be a 3d path")
|
assert(is_path(path,3), "Input path must be a 3d path")
|
||||||
assert(is_bool(closed))
|
assert(is_bool(closed))
|
||||||
|
@ -939,19 +946,19 @@ function _path_cuts_dir(path, cuts, closed=false, eps=1e-2) =
|
||||||
// Topics: Paths
|
// Topics: Paths
|
||||||
// See Also: split_path_at_self_crossings()
|
// See Also: split_path_at_self_crossings()
|
||||||
// Usage:
|
// Usage:
|
||||||
// path_list = path_cut(path, cutdist, [closed=]);
|
// path_list = path_cut(path, cutdist, [closed=]);
|
||||||
// Description:
|
// Description:
|
||||||
// Given a list of distances in `cutdist`, cut the path into
|
// Given a list of distances in `cutdist`, cut the path into
|
||||||
// subpaths at those lengths, returning a list of paths.
|
// subpaths at those lengths, returning a list of paths.
|
||||||
// If the input path is closed then the final path will include the
|
// If the input path is closed then the final path will include the
|
||||||
// original starting point. The list of cut distances must be
|
// original starting point. The list of cut distances must be
|
||||||
// in ascending order and should not include the endpoints: 0
|
// in ascending order and should not include the endpoints: 0
|
||||||
// or len(path). If you repeat a distance you will get an
|
// or len(path). If you repeat a distance you will get an
|
||||||
// empty list in that position in the output. If you give an
|
// empty list in that position in the output. If you give an
|
||||||
// empty cutdist array you will get the input path as output
|
// empty cutdist array you will get the input path as output
|
||||||
// (without the final vertex doubled in the case of a closed path).
|
// (without the final vertex doubled in the case of a closed path).
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// path = The original path to split.
|
// path = path of any dimension or a 1-region
|
||||||
// cutdist = Distance or list of distances where path is cut
|
// cutdist = Distance or list of distances where path is cut
|
||||||
// closed = If true, treat the path as a closed polygon. Default: false
|
// closed = If true, treat the path as a closed polygon. Default: false
|
||||||
// Example(2D,NoAxes):
|
// Example(2D,NoAxes):
|
||||||
|
@ -960,7 +967,7 @@ function _path_cuts_dir(path, cuts, closed=false, eps=1e-2) =
|
||||||
// rainbow(segs) stroke($item, endcaps="butt", width=3);
|
// rainbow(segs) stroke($item, endcaps="butt", width=3);
|
||||||
function path_cut(path,cutdist,closed) =
|
function path_cut(path,cutdist,closed) =
|
||||||
is_num(cutdist) ? path_cut(path,[cutdist],closed) :
|
is_num(cutdist) ? path_cut(path,[cutdist],closed) :
|
||||||
is_path_region(path) ? path_cut(path[0], cutdist, default(closed,true)):
|
is_1region(path) ? path_cut(path[0], cutdist, default(closed,true)):
|
||||||
let(closed=default(closed,false))
|
let(closed=default(closed,false))
|
||||||
assert(is_bool(closed))
|
assert(is_bool(closed))
|
||||||
assert(is_vector(cutdist))
|
assert(is_vector(cutdist))
|
||||||
|
@ -1017,10 +1024,11 @@ function _cut_to_seg_u_form(pathcut, path, closed) =
|
||||||
// Usage:
|
// Usage:
|
||||||
// paths = split_path_at_self_crossings(path, [closed], [eps]);
|
// paths = split_path_at_self_crossings(path, [closed], [eps]);
|
||||||
// Description:
|
// Description:
|
||||||
// Splits a path into sub-paths wherever the original path crosses itself.
|
// Splits a 2D path into sub-paths wherever the original path crosses itself.
|
||||||
// Splits may occur mid-segment, so new vertices will be created at the intersection points.
|
// Splits may occur mid-segment, so new vertices will be created at the intersection points.
|
||||||
|
// Returns a list of the resulting subpaths.
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// path = The path to split up.
|
// path = A 2D path or a 1-region.
|
||||||
// closed = If true, treat path as a closed polygon. Default: true
|
// closed = If true, treat path as a closed polygon. Default: true
|
||||||
// eps = Acceptable variance. Default: `EPSILON` (1e-9)
|
// eps = Acceptable variance. Default: `EPSILON` (1e-9)
|
||||||
// Example(2D,NoAxes):
|
// Example(2D,NoAxes):
|
||||||
|
@ -1081,22 +1089,22 @@ function _tag_self_crossing_subpaths(path, nonzero, closed=true, eps=EPSILON) =
|
||||||
|
|
||||||
// Function: polygon_parts()
|
// Function: polygon_parts()
|
||||||
// Usage:
|
// Usage:
|
||||||
// splitpaths = polygon_parts(path, [nonzero], [eps]);
|
// splitpolys = polygon_parts(poly, [nonzero], [eps]);
|
||||||
// Description:
|
// Description:
|
||||||
// Given a possibly self-intersecting polygon, constructs a representation of the original polygon as a list of
|
// Given a possibly self-intersecting 2d polygon, constructs a representation of the original polygon as a list of
|
||||||
// non-intersecting simple polygons. If nonzero is set to true then it uses the nonzero method for defining polygon membership, which
|
// non-intersecting simple polygons. If nonzero is set to true then it uses the nonzero method for defining polygon membership.
|
||||||
// means it will produce the outer perimeter.
|
// For simple cases, such as the pentagram, this will produce the outer perimeter of a self-intersecting polygon.
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// path = The path to split up.
|
// poly = a 2D polygon or 1-region
|
||||||
// nonzero = If true use the nonzero method for checking if a point is in a polygon. Otherwise use the even-odd method. Default: false
|
// nonzero = If true use the nonzero method for checking if a point is in a polygon. Otherwise use the even-odd method. Default: false
|
||||||
// eps = The epsilon error value to determine whether two points coincide. Default: `EPSILON` (1e-9)
|
// eps = The epsilon error value to determine whether two points coincide. Default: `EPSILON` (1e-9)
|
||||||
// Example(2D,NoAxes): This cross-crossing polygon breaks up into its 3 components (regardless of the value of nonzero).
|
// Example(2D,NoAxes): This cross-crossing polygon breaks up into its 3 components (regardless of the value of nonzero).
|
||||||
// path = [
|
// poly = [
|
||||||
// [-100,100], [0,-50], [100,100],
|
// [-100,100], [0,-50], [100,100],
|
||||||
// [100,-100], [0,50], [-100,-100]
|
// [100,-100], [0,50], [-100,-100]
|
||||||
// ];
|
// ];
|
||||||
// splitpaths = polygon_parts(path);
|
// splitpolys = polygon_parts(poly);
|
||||||
// rainbow(splitpaths) stroke($item, closed=true, width=3);
|
// rainbow(splitpolys) stroke($item, closed=true, width=3);
|
||||||
// Example(2D,NoAxes): With nonzero=false you get even-odd mode which matches OpenSCAD, so the pentagram breaks apart into its five points.
|
// Example(2D,NoAxes): With nonzero=false you get even-odd mode which matches OpenSCAD, so the pentagram breaks apart into its five points.
|
||||||
// pentagram = turtle(["move",100,"left",144], repeat=4);
|
// pentagram = turtle(["move",100,"left",144], repeat=4);
|
||||||
// left(100)polygon(pentagram);
|
// left(100)polygon(pentagram);
|
||||||
|
@ -1112,39 +1120,39 @@ function _tag_self_crossing_subpaths(path, nonzero, closed=true, eps=EPSILON) =
|
||||||
// N=12;
|
// N=12;
|
||||||
// ang=360/N;
|
// ang=360/N;
|
||||||
// sr=10;
|
// sr=10;
|
||||||
// path = turtle(["angle", 90+ang/2,
|
// poly = turtle(["angle", 90+ang/2,
|
||||||
// "move", sr, "left",
|
// "move", sr, "left",
|
||||||
// "move", 2*sr*sin(ang/2), "left",
|
// "move", 2*sr*sin(ang/2), "left",
|
||||||
// "repeat", 4,
|
// "repeat", 4,
|
||||||
// ["move", 2*sr, "left",
|
// ["move", 2*sr, "left",
|
||||||
// "move", 2*sr*sin(ang/2), "left"],
|
// "move", 2*sr*sin(ang/2), "left"],
|
||||||
// "move", sr]);
|
// "move", sr]);
|
||||||
// stroke(path, width=.3);
|
// stroke(poly, width=.3);
|
||||||
// right(20)rainbow(polygon_parts(path)) polygon($item);
|
// right(20)rainbow(polygon_parts(poly)) polygon($item);
|
||||||
// Example(2D,NoAxes): overlapping path segments disappear
|
// Example(2D,NoAxes): overlapping poly segments disappear
|
||||||
// path = [[0,0], [10,0], [10,10], [0,10],[0,20], [20,10],[10,10], [0,10],[0,0]];
|
// poly = [[0,0], [10,0], [10,10], [0,10],[0,20], [20,10],[10,10], [0,10],[0,0]];
|
||||||
// stroke(path,width=0.3);
|
// stroke(poly,width=0.3);
|
||||||
// right(22)stroke(polygon_parts(path)[0], width=0.3, closed=true);
|
// right(22)stroke(polygon_parts(poly)[0], width=0.3, closed=true);
|
||||||
// Example(2D,NoAxes): Path segments disappear outside as well
|
// Example(2D,NoAxes): Poly segments disappear outside as well
|
||||||
// path = turtle(["repeat", 3, ["move", 17, "left", "move", 10, "left", "move", 7, "left", "move", 10, "left"]]);
|
// poly = turtle(["repeat", 3, ["move", 17, "left", "move", 10, "left", "move", 7, "left", "move", 10, "left"]]);
|
||||||
// back(2)stroke(path,width=.5);
|
// back(2)stroke(poly,width=.5);
|
||||||
// fwd(12)rainbow(polygon_parts(path)) stroke($item, closed=true, width=0.5);
|
// fwd(12)rainbow(polygon_parts(poly)) stroke($item, closed=true, width=0.5);
|
||||||
// Example(2D,NoAxes): This shape has six components
|
// Example(2D,NoAxes): This shape has six components
|
||||||
// path = turtle(["repeat", 3, ["move", 15, "left", "move", 7, "left", "move", 10, "left", "move", 17, "left"]]);
|
// poly = turtle(["repeat", 3, ["move", 15, "left", "move", 7, "left", "move", 10, "left", "move", 17, "left"]]);
|
||||||
// polygon(path);
|
// polygon(poly);
|
||||||
// right(22)rainbow(polygon_parts(path)) polygon($item);
|
// right(22)rainbow(polygon_parts(poly)) polygon($item);
|
||||||
// Example(2D,NoAxes): When the loops of the shape overlap then nonzero gives a different result than the even-odd method.
|
// Example(2D,NoAxes): When the loops of the shape overlap then nonzero gives a different result than the even-odd method.
|
||||||
// path = turtle(["repeat", 3, ["move", 15, "left", "move", 7, "left", "move", 10, "left", "move", 10, "left"]]);
|
// poly = turtle(["repeat", 3, ["move", 15, "left", "move", 7, "left", "move", 10, "left", "move", 10, "left"]]);
|
||||||
// polygon(path);
|
// polygon(poly);
|
||||||
// right(27)rainbow(polygon_parts(path)) polygon($item);
|
// right(27)rainbow(polygon_parts(poly)) polygon($item);
|
||||||
// move([16,-14])rainbow(polygon_parts(path,nonzero=true)) polygon($item);
|
// move([16,-14])rainbow(polygon_parts(poly,nonzero=true)) polygon($item);
|
||||||
function polygon_parts(path, nonzero=false, eps=EPSILON) =
|
function polygon_parts(poly, nonzero=false, eps=EPSILON) =
|
||||||
let(path = force_path(path))
|
let(poly = force_path(poly))
|
||||||
assert(is_path(path,2), "Must give 2D path")
|
assert(is_path(poly,2), "Must give 2D polygon")
|
||||||
assert(is_bool(nonzero))
|
assert(is_bool(nonzero))
|
||||||
let(
|
let(
|
||||||
path = cleanup_path(path, eps=eps),
|
poly = cleanup_path(poly, eps=eps),
|
||||||
tagged = _tag_self_crossing_subpaths(path, nonzero=nonzero, closed=true, eps=eps),
|
tagged = _tag_self_crossing_subpaths(poly, nonzero=nonzero, closed=true, eps=eps),
|
||||||
kept = [for (sub = tagged) if(sub[0] == "O") sub[1]],
|
kept = [for (sub = tagged) if(sub[0] == "O") sub[1]],
|
||||||
outregion = _assemble_path_fragments(kept, eps=eps)
|
outregion = _assemble_path_fragments(kept, eps=eps)
|
||||||
) outregion;
|
) outregion;
|
||||||
|
|
|
@ -3,8 +3,7 @@
|
||||||
// This file provides 2D boolean geometry operations on paths, where you can
|
// This file provides 2D boolean geometry operations on paths, where you can
|
||||||
// compute the intersection or union of the shape defined by point lists, producing
|
// compute the intersection or union of the shape defined by point lists, producing
|
||||||
// a new point list. Of course, boolean operations may produce shapes with multiple
|
// a new point list. Of course, boolean operations may produce shapes with multiple
|
||||||
// components. To handle that, we use "regions" which are defined by sets of
|
// components. To handle that, we use "regions" which are defined as lists of paths.
|
||||||
// multiple paths.
|
|
||||||
// Includes:
|
// Includes:
|
||||||
// include <BOSL2/std.scad>
|
// include <BOSL2/std.scad>
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
@ -23,7 +22,8 @@
|
||||||
// Checking that the polygons on a list are simple and non-crossing can be a time consuming test,
|
// Checking that the polygons on a list are simple and non-crossing can be a time consuming test,
|
||||||
// so it is not done automatically. It is your responsibility to ensure that your regions are
|
// so it is not done automatically. It is your responsibility to ensure that your regions are
|
||||||
// compliant. You can construct regions by making a list of polygons, or by using
|
// compliant. You can construct regions by making a list of polygons, or by using
|
||||||
// boolean function operations such as union() or difference(). And if you must you
|
// boolean function operations such as union() or difference(), which all except paths, as
|
||||||
|
// well as regions, as their inputs. And if you must you
|
||||||
// can clean up an ill-formed region using sanitize_region().
|
// can clean up an ill-formed region using sanitize_region().
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue