mirror of
https://github.com/BelfrySCAD/BOSL2.git
synced 2024-12-29 16:29:40 +00:00
Moved path functions from geometry.scad to paths.scad
This commit is contained in:
parent
28114b49b5
commit
c6ec9c8820
3 changed files with 159 additions and 122 deletions
125
geometry.scad
125
geometry.scad
|
@ -665,12 +665,11 @@ function _general_plane_line_intersection(plane, line, eps=EPSILON) =
|
||||||
// The resulting angle is signed, with the sign positive if the vector p2-p1 lies on
|
// The resulting angle is signed, with the sign positive if the vector p2-p1 lies on
|
||||||
// the same side of the plane as the plane's normal vector.
|
// the same side of the plane as the plane's normal vector.
|
||||||
function plane_line_angle(plane, line) =
|
function plane_line_angle(plane, line) =
|
||||||
let(
|
let(
|
||||||
vect = line[1]-line[0],
|
vect = line[1]-line[0],
|
||||||
zplane = plane_normal(plane),
|
zplane = plane_normal(plane),
|
||||||
sin_angle = vect*zplane/norm(zplane)/norm(vect)
|
sin_angle = vect*zplane/norm(zplane)/norm(vect)
|
||||||
)
|
) asin(constrain(sin_angle,-1,1));
|
||||||
asin(constrain(sin_angle,-1,1));
|
|
||||||
|
|
||||||
|
|
||||||
// Function: plane_line_intersection()
|
// Function: plane_line_intersection()
|
||||||
|
@ -1056,20 +1055,30 @@ function polygon_shift_to_closest_point(path, pt) =
|
||||||
// color("red") place_copies([pent[0],circ[0]]) circle(r=.1,$fn=32);
|
// color("red") place_copies([pent[0],circ[0]]) circle(r=.1,$fn=32);
|
||||||
// color("blue") translate(reindexed[0])circle(r=.1,$fn=32);
|
// color("blue") translate(reindexed[0])circle(r=.1,$fn=32);
|
||||||
function reindex_polygon(reference, poly, return_error=false) =
|
function reindex_polygon(reference, poly, return_error=false) =
|
||||||
assert(is_path(reference) && is_path(poly))
|
assert(is_path(reference) && is_path(poly))
|
||||||
assert(len(reference)==len(poly), "Polygons must be the same length in reindex_polygon")
|
assert(len(reference)==len(poly), "Polygons must be the same length in reindex_polygon")
|
||||||
let(
|
let(
|
||||||
dim = len(reference[0]),
|
dim = len(reference[0]),
|
||||||
N = len(reference),
|
N = len(reference),
|
||||||
fixpoly = dim != 2 ? poly :
|
fixpoly = dim != 2? poly :
|
||||||
polygon_is_clockwise(reference) ? clockwise_polygon(poly) : ccw_polygon(poly),
|
polygon_is_clockwise(reference)? clockwise_polygon(poly) :
|
||||||
dist = [for (p1=reference) [for (p2=fixpoly) norm(p1-p2)]], // Matrix of all pairwise distances
|
ccw_polygon(poly),
|
||||||
// Compute the sum of all distance pairs for a each shift
|
dist = [
|
||||||
sums = [for(shift=[0:N-1])
|
// Matrix of all pairwise distances
|
||||||
sum([for(i=[0:N-1]) dist[i][(i+shift)%N]])],
|
for (p1=reference) [
|
||||||
optimal_poly = polygon_shift(fixpoly,min_index(sums))
|
for (p2=fixpoly) norm(p1-p2)
|
||||||
)
|
]
|
||||||
return_error ? [optimal_poly, min(sums)] : optimal_poly;
|
],
|
||||||
|
// Compute the sum of all distance pairs for a each shift
|
||||||
|
sums = [
|
||||||
|
for(shift=[0:1:N-1]) sum([
|
||||||
|
for(i=[0:1:N-1]) dist[i][(i+shift)%N]
|
||||||
|
])
|
||||||
|
],
|
||||||
|
optimal_poly = polygon_shift(fixpoly,min_index(sums))
|
||||||
|
)
|
||||||
|
return_error? [optimal_poly, min(sums)] :
|
||||||
|
optimal_poly;
|
||||||
|
|
||||||
|
|
||||||
// Function: align_polygon()
|
// Function: align_polygon()
|
||||||
|
@ -1092,14 +1101,19 @@ function reindex_polygon(reference, poly, return_error=false) =
|
||||||
// color("red") place_copies(scale(1.4,p=align_polygon(pentagon,hexagon,[0:10:359]))) circle(r=.1);
|
// color("red") place_copies(scale(1.4,p=align_polygon(pentagon,hexagon,[0:10:359]))) circle(r=.1);
|
||||||
// place_copies(concat(pentagon,hexagon))circle(r=.1);
|
// place_copies(concat(pentagon,hexagon))circle(r=.1);
|
||||||
function align_polygon(reference, poly, angles, cp) =
|
function align_polygon(reference, poly, angles, cp) =
|
||||||
assert(is_path(reference) && is_path(poly))
|
assert(is_path(reference) && is_path(poly))
|
||||||
assert(len(reference)==len(poly), "Polygons must be the same length to be aligned in align_polygon")
|
assert(len(reference)==len(poly), "Polygons must be the same length to be aligned in align_polygon")
|
||||||
assert(is_num(angles[0]), "The `angle` parameter to align_polygon must be a range or vector")
|
assert(is_num(angles[0]), "The `angle` parameter to align_polygon must be a range or vector")
|
||||||
let( // alignments is a vector of entries of the form: [polygon, error]
|
let( // alignments is a vector of entries of the form: [polygon, error]
|
||||||
alignments = [for(angle=angles) reindex_polygon(reference, zrot(angle,p=poly,cp=cp),return_error=true)],
|
alignments = [
|
||||||
best = min_index(subindex(alignments,1))
|
for(angle=angles) reindex_polygon(
|
||||||
)
|
reference,
|
||||||
alignments[best][0];
|
zrot(angle,p=poly,cp=cp),
|
||||||
|
return_error=true
|
||||||
|
)
|
||||||
|
],
|
||||||
|
best = min_index(subindex(alignments,1))
|
||||||
|
) alignments[best][0];
|
||||||
|
|
||||||
|
|
||||||
// Function: centroid()
|
// Function: centroid()
|
||||||
|
@ -1162,7 +1176,7 @@ function point_in_polygon(point, path, eps=EPSILON) =
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// path = The list of 2D path points for the perimeter of the polygon.
|
// path = The list of 2D path points for the perimeter of the polygon.
|
||||||
function polygon_is_clockwise(path) =
|
function polygon_is_clockwise(path) =
|
||||||
assert(is_path(path) && len(path[0])==2, "Input must be a 2d path")
|
assert(is_path(path) && len(path[0])==2, "Input must be a 2d path")
|
||||||
let(
|
let(
|
||||||
minx = min(subindex(path,0)),
|
minx = min(subindex(path,0)),
|
||||||
lowind = search(minx, path, 0, 0),
|
lowind = search(minx, path, 0, 0),
|
||||||
|
@ -1201,57 +1215,4 @@ function reverse_polygon(poly) =
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Function: path_tangents()
|
|
||||||
// Usage: path_tangents(path, [closed])
|
|
||||||
// Description:
|
|
||||||
// Compute the tangent vector to the input path. The derivative approximation is described in deriv().
|
|
||||||
// The returns vectors will be normalized to length 1.
|
|
||||||
function path_tangents(path, closed=false) =
|
|
||||||
assert(is_path(path))
|
|
||||||
[for(t=deriv(path)) normalize(t)];
|
|
||||||
|
|
||||||
// Function: path_normals()
|
|
||||||
// Usage: path_normals(path, [tangents], [closed])
|
|
||||||
// Description:
|
|
||||||
// Compute the normal vector to the input path. This vector is perpendicular to the
|
|
||||||
// path tangent and lies in the plane of the curve. When there are collinear points,
|
|
||||||
// the curve does not define a unique plane and the normal is not uniquely defined.
|
|
||||||
function path_normals(path, tangents, closed=false) =
|
|
||||||
assert(is_path(path))
|
|
||||||
assert(is_bool(closed))
|
|
||||||
let( tangents = default(tangents, path_tangents(path,closed)))
|
|
||||||
assert(is_path(tangents))
|
|
||||||
[for(i=idx(path))
|
|
||||||
let( pts = i==0 ? (closed ? select(path,-1,1) : select(path,0,2)) :
|
|
||||||
i==len(path)-1 ? (closed ? select(path,i-1,i+1) : select(path,i-2,i)) :
|
|
||||||
select(path,i-1,i+1)
|
|
||||||
)
|
|
||||||
normalize( cross(cross(pts[1]-pts[0], pts[2]-pts[0]),tangents[i]))];
|
|
||||||
|
|
||||||
|
|
||||||
// Function: path_curvature()
|
|
||||||
// Usage: path_curvature(path, [closed])
|
|
||||||
// Description:
|
|
||||||
// Numerically estimate the curvature of the path (in any dimension).
|
|
||||||
function path_curvature(path, closed=false) =
|
|
||||||
let(
|
|
||||||
d1 = deriv(path, closed=closed),
|
|
||||||
d2 = deriv2(path, closed=closed)
|
|
||||||
)
|
|
||||||
[for(i=idx(path)) sqrt(sqr(norm(d1[i])*norm(d2[i])) - sqr(d1[i]*d2[i]))/ pow(norm(d1[i]),3)];
|
|
||||||
|
|
||||||
|
|
||||||
// Function: path_torsion()
|
|
||||||
// Usage: path_torsion(path, [closed])
|
|
||||||
// Description:
|
|
||||||
// Numerically estimate the torsion of a 3d path.
|
|
||||||
function path_torsion(path, closed=false) =
|
|
||||||
let(
|
|
||||||
d1 = deriv(path,closed=closed),
|
|
||||||
d2 = deriv2(path,closed=closed),
|
|
||||||
d3 = deriv3(path,closed=closed)
|
|
||||||
)
|
|
||||||
[for(i=idx(path)) let(crossterm = cross(d1[i],d2[i])) crossterm * d3[i] / sqr(norm(crossterm))];
|
|
||||||
|
|
||||||
|
|
||||||
// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap
|
// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap
|
||||||
|
|
154
paths.scad
154
paths.scad
|
@ -252,6 +252,71 @@ function path_closest_point(path, pt) =
|
||||||
) [min_seg, pts[min_seg]];
|
) [min_seg, pts[min_seg]];
|
||||||
|
|
||||||
|
|
||||||
|
// Function: path_tangents()
|
||||||
|
// Usage: path_tangents(path, [closed])
|
||||||
|
// Description:
|
||||||
|
// Compute the tangent vector to the input path. The derivative approximation is described in deriv().
|
||||||
|
// The returns vectors will be normalized to length 1.
|
||||||
|
function path_tangents(path, closed=false) =
|
||||||
|
assert(is_path(path))
|
||||||
|
[for(t=deriv(path)) normalize(t)];
|
||||||
|
|
||||||
|
|
||||||
|
// Function: path_normals()
|
||||||
|
// Usage: path_normals(path, [tangents], [closed])
|
||||||
|
// Description:
|
||||||
|
// Compute the normal vector to the input path. This vector is perpendicular to the
|
||||||
|
// path tangent and lies in the plane of the curve. When there are collinear points,
|
||||||
|
// the curve does not define a unique plane and the normal is not uniquely defined.
|
||||||
|
function path_normals(path, tangents, closed=false) =
|
||||||
|
assert(is_path(path))
|
||||||
|
assert(is_bool(closed))
|
||||||
|
let( tangents = default(tangents, path_tangents(path,closed)) )
|
||||||
|
assert(is_path(tangents))
|
||||||
|
[
|
||||||
|
for(i=idx(path)) let(
|
||||||
|
pts = i==0? (closed? select(path,-1,1) : select(path,0,2)) :
|
||||||
|
i==len(path)-1? (closed? select(path,i-1,i+1) : select(path,i-2,i)) :
|
||||||
|
select(path,i-1,i+1)
|
||||||
|
) normalize(cross(
|
||||||
|
cross(pts[1]-pts[0], pts[2]-pts[0]),
|
||||||
|
tangents[i]
|
||||||
|
))
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
// Function: path_curvature()
|
||||||
|
// Usage: path_curvature(path, [closed])
|
||||||
|
// Description:
|
||||||
|
// Numerically estimate the curvature of the path (in any dimension).
|
||||||
|
function path_curvature(path, closed=false) =
|
||||||
|
let(
|
||||||
|
d1 = deriv(path, closed=closed),
|
||||||
|
d2 = deriv2(path, closed=closed)
|
||||||
|
) [
|
||||||
|
for(i=idx(path))
|
||||||
|
sqrt(
|
||||||
|
sqr(norm(d1[i])*norm(d2[i])) -
|
||||||
|
sqr(d1[i]*d2[i])
|
||||||
|
) / pow(norm(d1[i]),3)
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
// Function: path_torsion()
|
||||||
|
// Usage: path_torsion(path, [closed])
|
||||||
|
// Description:
|
||||||
|
// Numerically estimate the torsion of a 3d path.
|
||||||
|
function path_torsion(path, closed=false) =
|
||||||
|
let(
|
||||||
|
d1 = deriv(path,closed=closed),
|
||||||
|
d2 = deriv2(path,closed=closed),
|
||||||
|
d3 = deriv3(path,closed=closed)
|
||||||
|
) [
|
||||||
|
for (i=idx(path)) let(
|
||||||
|
crossterm = cross(d1[i],d2[i])
|
||||||
|
) crossterm * d3[i] / sqr(norm(crossterm))
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
// Function: path3d_spiral()
|
// Function: path3d_spiral()
|
||||||
// Description:
|
// Description:
|
||||||
|
@ -1091,12 +1156,14 @@ function _path_cuts_dir(path, cuts, closed=false, eps=1e-2) =
|
||||||
// and passing the rounding error forward to the next entry.
|
// and passing the rounding error forward to the next entry.
|
||||||
// This will generally distribute the error in a uniform manner.
|
// This will generally distribute the error in a uniform manner.
|
||||||
function _sum_preserving_round(data, index=0) =
|
function _sum_preserving_round(data, index=0) =
|
||||||
index == len(data)-1 ? list_set(data, len(data)-1, round(data[len(data)-1])) :
|
index == len(data)-1 ? list_set(data, len(data)-1, round(data[len(data)-1])) :
|
||||||
let(
|
let(
|
||||||
newval = round(data[index]),
|
newval = round(data[index]),
|
||||||
error = newval - data[index]
|
error = newval - data[index]
|
||||||
)
|
) _sum_preserving_round(
|
||||||
_sum_preserving_round(list_set(data, [index,index+1], [newval, data[index+1]-error]), index+1);
|
list_set(data, [index,index+1], [newval, data[index+1]-error]),
|
||||||
|
index+1
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
// Function: subdivide_path()
|
// Function: subdivide_path()
|
||||||
|
@ -1155,31 +1222,37 @@ function _sum_preserving_round(data, index=0) =
|
||||||
// mypath = subdivide_path([[0,0,0],[2,0,1],[2,3,2]], 12);
|
// mypath = subdivide_path([[0,0,0],[2,0,1],[2,3,2]], 12);
|
||||||
// place_copies(mypath)sphere(r=.1,$fn=32);
|
// place_copies(mypath)sphere(r=.1,$fn=32);
|
||||||
function subdivide_path(path, N, closed=true, exact=true, method="length") =
|
function subdivide_path(path, N, closed=true, exact=true, method="length") =
|
||||||
assert(is_path(path))
|
assert(is_path(path))
|
||||||
assert(method=="length" || method=="segment")
|
assert(method=="length" || method=="segment")
|
||||||
assert((is_num(N) && N>0) || is_vector(N),"Parameter N to subdivide_path must be postive number or vector")
|
assert((is_num(N) && N>0) || is_vector(N),"Parameter N to subdivide_path must be postive number or vector")
|
||||||
let(
|
let(
|
||||||
count = len(path) - (closed?0:1),
|
count = len(path) - (closed?0:1),
|
||||||
add_guess =
|
add_guess = method=="segment"? (
|
||||||
method=="segment" ?
|
is_list(N)? (
|
||||||
(is_list(N) ? assert(len(N)==count,"Vector parameter N to subdivide_path has the wrong length")
|
assert(len(N)==count,"Vector parameter N to subdivide_path has the wrong length")
|
||||||
add_scalar(N,-1)
|
add_scalar(N,-1)
|
||||||
: replist((N-len(path)) / count, count))
|
) : replist((N-len(path)) / count, count)
|
||||||
: // method=="length"
|
) : // method=="length"
|
||||||
assert(is_num(N),"Parameter N to subdivide path must be a number when method=\"length\"")
|
assert(is_num(N),"Parameter N to subdivide path must be a number when method=\"length\"")
|
||||||
let(
|
let(
|
||||||
path_lens = concat([for (i = [0:1:len(path)-2]) norm(path[i+1]-path[i])],
|
path_lens = concat(
|
||||||
closed?[norm(path[len(path)-1]-path[0])]:[]),
|
[ for (i = [0:1:len(path)-2]) norm(path[i+1]-path[i]) ],
|
||||||
add_density = (N - len(path)) / sum(path_lens)
|
closed? [norm(path[len(path)-1]-path[0])] : []
|
||||||
)
|
),
|
||||||
path_lens * add_density,
|
add_density = (N - len(path)) / sum(path_lens)
|
||||||
add = exact ? _sum_preserving_round(add_guess) : [for (val=add_guess) round(val)]
|
)
|
||||||
)
|
path_lens * add_density,
|
||||||
concat(
|
add = exact? _sum_preserving_round(add_guess) :
|
||||||
[for (i=[0:1:count])
|
[for (val=add_guess) round(val)]
|
||||||
each [for(j=[0:1:add[i]]) lerp(path[i],select(path,i+1), j/(add[i]+1))]],
|
) concat(
|
||||||
closed ? [] : [select(path,-1)]
|
[
|
||||||
);
|
for (i=[0:1:count]) each [
|
||||||
|
for(j=[0:1:add[i]])
|
||||||
|
lerp(path[i],select(path,i+1), j/(add[i]+1))
|
||||||
|
]
|
||||||
|
],
|
||||||
|
closed? [] : [select(path,-1)]
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
// Function: path_length_fractions()
|
// Function: path_length_fractions()
|
||||||
|
@ -1190,14 +1263,17 @@ function subdivide_path(path, N, closed=true, exact=true, method="length") =
|
||||||
// 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.
|
||||||
function path_length_fractions(path, closed=false) =
|
function path_length_fractions(path, closed=false) =
|
||||||
assert(is_path(path))
|
assert(is_path(path))
|
||||||
assert(is_bool(closed))
|
assert(is_bool(closed))
|
||||||
let(
|
let(
|
||||||
lengths = [0, for(i=[0:1:len(path)-(closed?1:2)]) norm(select(path,i+1)-path[i])],
|
lengths = [
|
||||||
partial_len = cumsum(lengths),
|
0,
|
||||||
total_len = select(partial_len,-1)
|
for (i=[0:1:len(path)-(closed?1:2)])
|
||||||
)
|
norm(select(path,i+1)-path[i])
|
||||||
partial_len / total_len;
|
],
|
||||||
|
partial_len = cumsum(lengths),
|
||||||
|
total_len = select(partial_len,-1)
|
||||||
|
) partial_len / total_len;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
BOSL_VERSION = [2,0,141];
|
BOSL_VERSION = [2,0,142];
|
||||||
|
|
||||||
|
|
||||||
// Section: BOSL Library Version Functions
|
// Section: BOSL Library Version Functions
|
||||||
|
|
Loading…
Reference in a new issue