Fix tab indents.

This commit is contained in:
Garth Minette 2021-06-22 17:20:08 -07:00
parent 7a3720a812
commit 48c5139099
6 changed files with 320 additions and 267 deletions

View file

@ -951,28 +951,34 @@ function shuffle(list,seed) =
// idx should be an index of the arrays l[i]
function _group_sort_by_index(l,idx) =
len(l) == 0 ? [] :
len(l) == 1 ? [l] :
let( pivot = l[floor(len(l)/2)][idx],
equal = [ for(li=l) if( li[idx]==pivot) li ] ,
lesser = [ for(li=l) if( li[idx]< pivot) li ] ,
greater = [ for(li=l) if( li[idx]> pivot) li ]
)
concat( _group_sort_by_index(lesser,idx),
[equal],
_group_sort_by_index(greater,idx) ) ;
len(l) == 0 ? [] :
len(l) == 1 ? [l] :
let(
pivot = l[floor(len(l)/2)][idx],
equal = [ for(li=l) if( li[idx]==pivot) li ],
lesser = [ for(li=l) if( li[idx]< pivot) li ],
greater = [ for(li=l) if( li[idx]> pivot) li ]
)
concat(
_group_sort_by_index(lesser,idx),
[equal],
_group_sort_by_index(greater,idx)
);
function _group_sort(l) =
len(l) == 0 ? [] :
len(l) == 1 ? [l] :
let( pivot = l[floor(len(l)/2)],
equal = [ for(li=l) if( li==pivot) li ] ,
lesser = [ for(li=l) if( li< pivot) li ] ,
greater = [ for(li=l) if( li> pivot) li ]
)
concat( _group_sort(lesser),
[equal],
_group_sort(greater) ) ;
len(l) == 0 ? [] :
len(l) == 1 ? [l] :
let(
pivot = l[floor(len(l)/2)],
equal = [ for(li=l) if( li==pivot) li ] ,
lesser = [ for(li=l) if( li< pivot) li ] ,
greater = [ for(li=l) if( li> pivot) li ]
)
concat(
_group_sort(lesser),
[equal],
_group_sort(greater)
);
// Sort a vector of scalar values with the native comparison operator
@ -1171,11 +1177,11 @@ function group_sort(list, idx) =
assert(is_list(list), "Input should be a list." )
assert(is_undef(idx) || (is_finite(idx) && idx>=0) , "Invalid index." )
len(list)<=1 ? [list] :
is_vector(list)? _group_sort(list) :
let( idx = is_undef(idx) ? 0 : idx )
assert( [for(entry=list) if(!is_list(entry) || len(entry)<idx || !is_num(entry[idx]) ) 1]==[],
"Some entry of the list is a list shorter than `idx` or the indexed entry of it is not a number." )
_group_sort_by_index(list,idx);
is_vector(list)? _group_sort(list) :
let( idx = is_undef(idx) ? 0 : idx )
assert( [for(entry=list) if(!is_list(entry) || len(entry)<idx || !is_num(entry[idx]) ) 1]==[],
"Some entry of the list is a list shorter than `idx` or the indexed entry of it is not a number.")
_group_sort_by_index(list,idx);
// Function: unique()
@ -1197,23 +1203,27 @@ function unique(list) =
is_string(list)? str_join(unique([for (x = list) x])) :
len(list)<=1? list :
is_homogeneous(list,1) && ! is_list(list[0])
? _unique_sort(list)
? _unique_sort(list)
: let( sorted = sort(list))
[ for (i=[0:1:len(sorted)-1])
if (i==0 || (sorted[i] != sorted[i-1]))
sorted[i]
];
[
for (i=[0:1:len(sorted)-1])
if (i==0 || (sorted[i] != sorted[i-1]))
sorted[i]
];
function _unique_sort(l) =
len(l) <= 1 ? l :
let( pivot = l[floor(len(l)/2)],
equal = [ for(li=l) if( li==pivot) li ] ,
lesser = [ for(li=l) if( li<pivot ) li ] ,
greater = [ for(li=l) if( li>pivot) li ]
)
concat( _unique_sort(lesser),
equal[0],
_unique_sort(greater) ) ;
len(l) <= 1 ? l :
let(
pivot = l[floor(len(l)/2)],
equal = [ for(li=l) if( li==pivot) li ] ,
lesser = [ for(li=l) if( li<pivot ) li ] ,
greater = [ for(li=l) if( li>pivot) li ]
)
concat(
_unique_sort(lesser),
equal[0],
_unique_sort(greater)
);
// Function: unique_count()
@ -1232,11 +1242,21 @@ function unique_count(list) =
assert(is_list(list) || is_string(list), "Invalid input." )
list == [] ? [[],[]] :
is_homogeneous(list,1) && ! is_list(list[0])
? let( sorted = _group_sort(list) )
[ [for(s=sorted) s[0] ], [for(s=sorted) len(s) ] ]
: let( list=sort(list) )
let( ind = [0, for(i=[1:1:len(list)-1]) if (list[i]!=list[i-1]) i] )
[ select(list,ind), deltas( concat(ind,[len(list)]) ) ];
? let( sorted = _group_sort(list) ) [
[for(s=sorted) s[0] ],
[for(s=sorted) len(s) ]
]
: let(
list=sort(list),
ind = [
0,
for(i=[1:1:len(list)-1])
if (list[i]!=list[i-1]) i
]
) [
select(list,ind),
deltas( concat(ind,[len(list)]) )
];
// Section: List Iteration Helpers

View file

@ -502,7 +502,7 @@ module generic_bottle_neck(
}
function generic_bottle_neck(
neck_d,
neck_d,
id,
thread_od,
height,

View file

@ -31,8 +31,8 @@ function _dist2line(d,n) = norm(d-(d * n) * n);
function _point_above_below_segment(point, edge) =
let( edge = edge - [point, point] )
edge[0].y <= 0
? (edge[1].y > 0 && cross(edge[0], edge[1]-edge[0]) > 0) ? 1 : 0
: (edge[1].y <= 0 && cross(edge[0], edge[1]-edge[0]) < 0) ? -1 : 0 ;
? (edge[1].y > 0 && cross(edge[0], edge[1]-edge[0]) > 0) ? 1 : 0
: (edge[1].y <= 0 && cross(edge[0], edge[1]-edge[0]) < 0) ? -1 : 0;
//Internal
function _valid_line(line,dim,eps=EPSILON) =
@ -74,8 +74,8 @@ function collinear(a, b, c, eps=EPSILON) =
"Input should be 3 points or a list of points with same dimension.")
assert( is_finite(eps) && (eps>=0), "The tolerance should be a non-negative value." )
let( points = is_def(c) ? [a,b,c]: a )
len(points)<3 ? true
: noncollinear_triple(points,error=false,eps=eps)==[];
len(points)<3 ? true :
noncollinear_triple(points,error=false,eps=eps) == [];
// Function: point_line_distance()
@ -124,8 +124,7 @@ function point_segment_distance(pt, seg) =
// dist = segment_distance([[-14,3], [-15,9]], [[-10,0], [10,0]]); // Returns: 5
// dist2 = segment_distance([[-5,5], [5,-5]], [[-10,3], [10,-3]]); // Returns: 0
function segment_distance(seg1, seg2) =
assert( is_matrix(concat(seg1,seg2),4),
"Inputs should be two valid segments." )
assert( is_matrix(concat(seg1,seg2),4), "Inputs should be two valid segments." )
convex_distance(seg1,seg2);
@ -147,9 +146,9 @@ function segment_distance(seg1, seg2) =
// color("blue") move_copies([p1,p2]) circle(d=2, $fn=12);
function line_normal(p1,p2) =
is_undef(p2)
? assert( len(p1)==2 && !is_undef(p1[1]) , "Invalid input." )
? assert( len(p1)==2 && !is_undef(p1[1]) , "Invalid input." )
line_normal(p1[0],p1[1])
: assert( _valid_line([p1,p2],dim=2), "Invalid line." )
: assert( _valid_line([p1,p2],dim=2), "Invalid line." )
unit([p1.y-p2.y,p2.x-p1.x]);
@ -164,7 +163,8 @@ function line_normal(p1,p2) =
function _general_line_intersection(s1,s2,eps=EPSILON) =
let(
denominator = det2([s1[0],s2[0]]-[s1[1],s2[1]])
) approx(denominator,0,eps=eps)? [undef,undef,undef] : let(
) approx(denominator,0,eps=eps)? [undef,undef,undef] :
let(
t = det2([s1[0],s2[0]]-s2) / denominator,
u = det2([s1[0],s1[0]]-[s2[0],s1[1]]) / denominator
) [s1[0]+t*(s1[1]-s1[0]), t, u];
@ -201,11 +201,10 @@ function line_intersection(l1,l2,eps=EPSILON) =
function line_ray_intersection(line,ray,eps=EPSILON) =
assert( is_finite(eps) && (eps>=0), "The tolerance should be a non-negative value." )
assert( _valid_line(line,dim=2,eps=eps) && _valid_line(ray,dim=2,eps=eps), "Invalid line or ray." )
let(
isect = _general_line_intersection(line,ray,eps=eps)
)
let( isect = _general_line_intersection(line,ray,eps=eps) )
is_undef(isect[0]) ? undef :
(isect[2]<0-eps) ? undef : isect[0];
(isect[2]<0-eps) ? undef :
isect[0];
// Function: line_segment_intersection()
@ -221,9 +220,7 @@ function line_ray_intersection(line,ray,eps=EPSILON) =
function line_segment_intersection(line,segment,eps=EPSILON) =
assert( is_finite(eps) && (eps>=0), "The tolerance should be a non-negative value." )
assert( _valid_line(line, dim=2,eps=eps) &&_valid_line(segment,dim=2,eps=eps), "Invalid line or segment." )
let(
isect = _general_line_intersection(line,segment,eps=eps)
)
let( isect = _general_line_intersection(line,segment,eps=eps) )
is_undef(isect[0]) ? undef :
isect[2]<0-eps || isect[2]>1+eps ? undef :
isect[0];
@ -242,11 +239,10 @@ function line_segment_intersection(line,segment,eps=EPSILON) =
function ray_intersection(r1,r2,eps=EPSILON) =
assert( is_finite(eps) && (eps>=0), "The tolerance should be a non-negative value." )
assert( _valid_line(r1,dim=2,eps=eps) && _valid_line(r2,dim=2,eps=eps), "Invalid ray(s)." )
let(
isect = _general_line_intersection(r1,r2,eps=eps)
)
let( isect = _general_line_intersection(r1,r2,eps=eps) )
is_undef(isect[0]) ? undef :
isect[1]<0-eps || isect[2]<0-eps ? undef : isect[0];
isect[1]<0-eps || isect[2]<0-eps ? undef :
isect[0];
// Function: ray_segment_intersection()
@ -262,9 +258,7 @@ function ray_intersection(r1,r2,eps=EPSILON) =
function ray_segment_intersection(ray,segment,eps=EPSILON) =
assert( _valid_line(ray,dim=2,eps=eps) && _valid_line(segment,dim=2,eps=eps), "Invalid ray or segment." )
assert( is_finite(eps) && (eps>=0), "The tolerance should be a non-negative value." )
let(
isect = _general_line_intersection(ray,segment,eps=eps)
)
let( isect = _general_line_intersection(ray,segment,eps=eps) )
is_undef(isect[0]) ? undef :
isect[1]<0-eps || isect[2]<0-eps || isect[2]>1+eps ? undef :
isect[0];
@ -283,9 +277,7 @@ function ray_segment_intersection(ray,segment,eps=EPSILON) =
function segment_intersection(s1,s2,eps=EPSILON) =
assert( _valid_line(s1,dim=2,eps=eps) && _valid_line(s2,dim=2,eps=eps), "Invalid segment(s)." )
assert( is_finite(eps) && (eps>=0), "The tolerance should be a non-negative value." )
let(
isect = _general_line_intersection(s1,s2,eps=eps)
)
let( isect = _general_line_intersection(s1,s2,eps=eps) )
is_undef(isect[0]) ? undef :
isect[1]<0-eps || isect[1]>1+eps || isect[2]<0-eps || isect[2]>1+eps ? undef :
isect[0];
@ -346,7 +338,7 @@ function line_closest_point(line,pt) =
assert(_valid_line(line), "Invalid line." )
assert( is_vector(pt,len(line[0])), "Invalid point or incompatible dimensions." )
let( n = unit( line[0]- line[1]) )
line[1]+((pt- line[1]) * n) * n;
line[1] + ((pt- line[1]) * n) * n;
// Function: ray_closest_point()
@ -485,7 +477,9 @@ function line_from_points(points, fast=false, eps=EPSILON) =
assert( is_finite(eps) && (eps>=0), "The tolerance should be a non-negative value." )
let( pb = furthest_point(points[0],points) )
norm(points[pb]-points[0])<eps*max(norm(points[pb]),norm(points[0])) ? undef :
fast || collinear(points) ? [points[pb], points[0]] : undef;
fast || collinear(points)
? [points[pb], points[0]]
: undef;
@ -556,7 +550,8 @@ function law_of_sines(a, A, b, B) =
// a/sin(A) = b/sin(B) = c/sin(C)
assert(num_defined([b,B]) == 1, "Must give exactly one of b= or B=.")
let( r = a/sin(A) )
is_undef(b) ? r*sin(B) : asin(constrain(b/r, -1, 1));
is_undef(b) ? r*sin(B) :
asin(constrain(b/r, -1, 1));
// Function: tri_calc()
@ -626,11 +621,11 @@ function tri_calc(ang,ang2,adj,opp,hyp) =
hyp
: (adj!=undef? (adj/cos(ang))
: (opp/sin(ang)))
)
[adj, opp, hyp, ang, ang2];
) [adj, opp, hyp, ang, ang2];
// Function: hyp_opp_to_adj()
// Alias: opp_hyp_to_adj()
// Usage:
// adj = hyp_opp_to_adj(hyp,opp);
// Description:
@ -646,8 +641,11 @@ function hyp_opp_to_adj(hyp,opp) =
"Triangle side lengths should be a positive numbers." )
sqrt(hyp*hyp-opp*opp);
function opp_hyp_to_adj(opp,hyp) = hyp_opp_to_adj(hyp,opp);
// Function: hyp_ang_to_adj()
// Alias: ang_hyp_to_adj()
// Usage:
// adj = hyp_ang_to_adj(hyp,ang);
// Description:
@ -663,8 +661,11 @@ function hyp_ang_to_adj(hyp,ang) =
assert(is_finite(ang) && ang>-90 && ang<90, "The angle should be an acute angle." )
hyp*cos(ang);
function ang_hyp_to_adj(ang,hyp) = hyp_ang_to_adj(hyp, ang);
// Function: opp_ang_to_adj()
// Alias: ang_opp_to_adj()
// Usage:
// adj = opp_ang_to_adj(opp,ang);
// Description:
@ -680,8 +681,11 @@ function opp_ang_to_adj(opp,ang) =
assert(is_finite(ang) && ang>-90 && ang<90, "The angle should be an acute angle." )
opp/tan(ang);
function ang_opp_to_adj(ang,opp) = opp_ang_to_adj(opp,ang);
// Function: hyp_adj_to_opp()
// Alias: adj_hyp_to_opp()
// Usage:
// opp = hyp_adj_to_opp(hyp,adj);
// Description:
@ -696,8 +700,11 @@ function hyp_adj_to_opp(hyp,adj) =
"Triangle side lengths should be a positive numbers." )
sqrt(hyp*hyp-adj*adj);
function adj_hyp_to_opp(adj,hyp) = hyp_adj_to_opp(hyp,adj);
// Function: hyp_ang_to_opp()
// Alias: ang_hyp_to_opp()
// Usage:
// opp = hyp_ang_to_opp(hyp,adj);
// Description:
@ -712,8 +719,11 @@ function hyp_ang_to_opp(hyp,ang) =
assert(is_finite(ang) && ang>-90 && ang<90, "The angle should be an acute angle." )
hyp*sin(ang);
function ang_hyp_to_opp(ang,hyp) = hyp_ang_to_opp(hyp,ang);
// Function: adj_ang_to_opp()
// Alias: ang_adj_to_opp()
// Usage:
// opp = adj_ang_to_opp(adj,ang);
// Description:
@ -728,8 +738,11 @@ function adj_ang_to_opp(adj,ang) =
assert(is_finite(ang) && ang>-90 && ang<90, "The angle should be an acute angle." )
adj*tan(ang);
function ang_adj_to_opp(ang,adj) = adj_ang_to_opp(adj,ang);
// Function: adj_opp_to_hyp()
// Alias: opp_adj_to_hyp()
// Usage:
// hyp = adj_opp_to_hyp(adj,opp);
// Description:
@ -744,8 +757,11 @@ function adj_opp_to_hyp(adj,opp) =
"Triangle side lengths should be a positive numbers." )
norm([opp,adj]);
function opp_adj_to_hyp(opp,adj) = adj_opp_to_hyp(adj,opp);
// Function: adj_ang_to_hyp()
// Alias: ang_adj_to_hyp()
// Usage:
// hyp = adj_ang_to_hyp(adj,ang);
// Description:
@ -760,8 +776,11 @@ function adj_ang_to_hyp(adj,ang) =
assert(is_finite(ang) && ang>-90 && ang<90, "The angle should be an acute angle." )
adj/cos(ang);
function ang_adj_to_hyp(ang,adj) = adj_ang_to_hyp(adj,ang);
// Function: opp_ang_to_hyp()
// Alias: ang_opp_to_hyp()
// Usage:
// hyp = opp_ang_to_hyp(opp,ang);
// Description:
@ -776,8 +795,11 @@ function opp_ang_to_hyp(opp,ang) =
assert(is_finite(ang) && ang>-90 && ang<90, "The angle should be an acute angle." )
opp/sin(ang);
function ang_opp_to_hyp(ang,opp) = opp_ang_to_hyp(opp,ang);
// Function: hyp_adj_to_ang()
// Alias: adj_hyp_to_ang()
// Usage:
// ang = hyp_adj_to_ang(hyp,adj);
// Description:
@ -792,8 +814,11 @@ function hyp_adj_to_ang(hyp,adj) =
"Triangle side lengths should be positive numbers." )
acos(adj/hyp);
function adj_hyp_to_ang(adj,hyp) = hyp_adj_to_ang(hyp,adj);
// Function: hyp_opp_to_ang()
// Alias: opp_hyp_to_ang()
// Usage:
// ang = hyp_opp_to_ang(hyp,opp);
// Description:
@ -808,8 +833,11 @@ function hyp_opp_to_ang(hyp,opp) =
"Triangle side lengths should be positive numbers." )
asin(opp/hyp);
function opp_hyp_to_ang(opp,hyp) = hyp_opp_to_ang(hyp,opp);
// Function: adj_opp_to_ang()
// Alias: opp_adj_to_ang()
// Usage:
// ang = adj_opp_to_ang(adj,opp);
// Description:
@ -824,6 +852,8 @@ function adj_opp_to_ang(adj,opp) =
"Triangle side lengths should be positive numbers." )
atan2(opp,adj);
function opp_adj_to_ang(opp,adj) = adj_opp_to_ang(adj,opp);
// Function: triangle_area()
// Usage:
@ -866,8 +896,7 @@ function plane3pt(p1, p2, p3) =
let(
crx = cross(p3-p1, p2-p1),
nrm = norm(crx)
)
approx(nrm,0) ? [] :
) approx(nrm,0) ? [] :
concat(crx, crx*p1)/nrm;
@ -893,8 +922,7 @@ function plane3pt_indexed(points, i1, i2, i3) =
p1 = points[i1],
p2 = points[i2],
p3 = points[i3]
)
plane3pt(p1,p2,p3);
) plane3pt(p1,p2,p3);
// Function: plane_from_normal()
@ -940,7 +968,7 @@ function _eigenvec_symm_3(M,evals,i=0) =
I = ident(3),
A = (M - evals[(i+1)%3]*I) * (M - evals[(i+2)%3]*I) ,
k = max_index( [for(i=[0:2]) norm(A[i]) ])
)
)
norm(A[k])<EPSILON ? I[k] : A[k]/norm(A[k]);
@ -977,14 +1005,15 @@ function plane_from_points(points, fast=false, eps=EPSILON) =
assert( is_path(points,dim=3), "Improper 3d point list." )
assert( is_finite(eps) && (eps>=0), "The tolerance should be a non-negative value." )
len(points) == 3
? let( plane = plane3pt(points[0],points[1],points[2]) )
? let( plane = plane3pt(points[0],points[1],points[2]) )
plane==[] ? [] : plane
: let(
: let(
covmix = _covariance_evec_eval(points),
pm = covmix[0],
evec = covmix[1],
eval0 = covmix[2],
plane = [ each evec, pm*evec] )
plane = [ each evec, pm*evec]
)
!fast && _pointlist_greatest_distance(points,plane)>eps*eval0 ? undef :
plane ;
@ -1121,10 +1150,10 @@ function _general_plane_line_intersection(plane, line, eps=EPSILON) =
b = plane*[each(line[1]-line[0]),0] // difference between the plane expression evaluation at line[1] and at line[0]
)
approx(b,0,eps) // is (line[1]-line[0]) "parallel" to the plane ?
? approx(a,0,eps) // is line[0] on the plane ?
? [line,undef] // line is on the plane
: undef // line is parallel but not on the plane
: [ line[0]-a/b*(line[1]-line[0]), -a/b ];
? approx(a,0,eps) // is line[0] on the plane ?
? [line,undef] // line is on the plane
: undef // line is parallel but not on the plane
: [ line[0]-a/b*(line[1]-line[0]), -a/b ];
// Function: normalize_plane()
@ -1152,8 +1181,7 @@ function plane_line_angle(plane, line) =
normal = plane_normal(plane),
sin_angle = linedir*normal,
cos_angle = norm(cross(linedir,normal))
)
atan2(sin_angle,cos_angle);
) atan2(sin_angle,cos_angle);
// Function: plane_line_intersection()
@ -1176,8 +1204,7 @@ function plane_line_intersection(plane, line, bounded=false, eps=EPSILON) =
let(
bounded = is_list(bounded)? bounded : [bounded, bounded],
res = _general_plane_line_intersection(plane, line, eps=eps)
)
is_undef(res) ? undef :
) is_undef(res) ? undef :
is_undef(res[1]) ? res[0] :
bounded[0] && res[1]<0 ? undef :
bounded[1] && res[1]>1 ? undef :
@ -1207,41 +1234,37 @@ function polygon_line_intersection(poly, line, bounded=false, eps=EPSILON) =
bounded = is_list(bounded)? bounded : [bounded, bounded],
poly = deduplicate(poly),
indices = noncollinear_triple(poly)
)
indices==[] ? undef :
) indices==[] ? undef :
let(
p1 = poly[indices[0]],
p2 = poly[indices[1]],
p3 = poly[indices[2]],
plane = plane3pt(p1,p2,p3),
res = _general_plane_line_intersection(plane, line, eps=eps)
)
is_undef(res)? undef :
is_undef(res[1])
? ( let(// Line is on polygon plane.
) is_undef(res)? undef :
is_undef(res[1]) ? (
let(// Line is on polygon plane.
linevec = unit(line[1] - line[0]),
lp1 = line[0] + (bounded[0]? 0 : -1000000) * linevec,
lp2 = line[1] + (bounded[1]? 0 : 1000000) * linevec,
poly2d = clockwise_polygon(project_plane(plane, poly)),
line2d = project_plane(plane, [lp1,lp2]),
parts = split_path_at_region_crossings(line2d, [poly2d], closed=false),
inside = [for (part = parts)
if (point_in_polygon(mean(part), poly2d)>0) part
]
)
!inside? undef :
let(
isegs = [for (seg = inside) lift_plane(plane, seg) ]
)
inside = [
for (part = parts)
if (point_in_polygon(mean(part), poly2d)>0) part
]
) !inside? undef :
let( isegs = [for (seg = inside) lift_plane(plane, seg) ] )
isegs
)
: bounded[0] && res[1]<0? undef :
bounded[1] && res[1]>1? undef :
let(
proj = clockwise_polygon(project_plane([p1, p2, p3], poly)),
pt = project_plane([p1, p2, p3], res[0])
)
point_in_polygon(pt, proj) < 0 ? undef : res[0];
) :
bounded[0] && res[1]<0? undef :
bounded[1] && res[1]>1? undef :
let(
proj = clockwise_polygon(project_plane([p1, p2, p3], poly)),
pt = project_plane([p1, p2, p3], res[0])
) point_in_polygon(pt, proj) < 0 ? undef :
res[0];
// Function: plane_intersection()
@ -1260,19 +1283,20 @@ function plane_intersection(plane1,plane2,plane3) =
assert( _valid_plane(plane1) && _valid_plane(plane2) && (is_undef(plane3) ||_valid_plane(plane3)),
"The input must be 2 or 3 planes." )
is_def(plane3)
? let(
matrix = [for(p=[plane1,plane2,plane3]) point3d(p)],
rhs = [for(p=[plane1,plane2,plane3]) p[3]]
? let(
matrix = [for(p=[plane1,plane2,plane3]) point3d(p)],
rhs = [for(p=[plane1,plane2,plane3]) p[3]]
)
linear_solve(matrix,rhs)
: let( normal = cross(plane_normal(plane1), plane_normal(plane2)) )
: let( normal = cross(plane_normal(plane1), plane_normal(plane2)) )
approx(norm(normal),0) ? undef :
let(
matrix = [for(p=[plane1,plane2]) point3d(p)],
rhs = [plane1[3], plane2[3]],
point = linear_solve(matrix,rhs)
)
point==[]? undef: [point, point+normal];
point==[]? undef:
[point, point+normal];
// Function: coplanar()
@ -1287,7 +1311,7 @@ function coplanar(points, eps=EPSILON) =
assert( is_path(points,dim=3) , "Input should be a list of 3D points." )
assert( is_finite(eps) && eps>=0, "The tolerance should be a non-negative value." )
len(points)<=2 ? false
: let( ip = noncollinear_triple(points,error=false,eps=eps) )
: let( ip = noncollinear_triple(points,error=false,eps=eps) )
ip == [] ? false :
let( plane = plane3pt(points[ip[0]],points[ip[1]],points[ip[2]]) )
_pointlist_greatest_distance(points,plane) < eps;
@ -1298,7 +1322,7 @@ function _pointlist_greatest_distance(points,plane) =
let(
normal = point3d(plane),
pt_nrm = points*normal
)
)
abs(max( max(pt_nrm) - plane[3], -min(pt_nrm) + plane[3])) / norm(normal);
@ -1429,6 +1453,7 @@ function circle_2tangents(pt1, pt2, pt3, r, d, tangents=false) =
)
[cp, n, tp1, tp2, dang1, dang2];
module circle_2tangents(pt1, pt2, pt3, r, d, h, center=false) {
c = circle_2tangents(pt1=pt1, pt2=pt2, pt3=pt3, r=r, d=d);
assert(!is_undef(c), "Cannot find circle when both rays are collinear.");
@ -1445,6 +1470,7 @@ module circle_2tangents(pt1, pt2, pt3, r, d, h, center=false) {
}
}
// Function&Module: circle_3points()
// Usage: As Function
// circ = circle_3points(pt1, pt2, pt3);
@ -1699,18 +1725,16 @@ function noncollinear_triple(points,error=true,eps=EPSILON) =
b = furthest_point(pa, points),
pb = points[b],
nrm = norm(pa-pb)
)
nrm <= eps*max(norm(pa),norm(pb))
? assert(!error, "Cannot find three noncollinear points in pointlist.")
[]
: let(
n = (pb-pa)/nrm,
distlist = [for(i=[0:len(points)-1]) _dist2line(points[i]-pa, n)]
)
max(distlist) < eps*nrm
? assert(!error, "Cannot find three noncollinear points in pointlist.")
[]
: [0,b,max_index(distlist)];
)
nrm <= eps*max(norm(pa),norm(pb)) ?
assert(!error, "Cannot find three noncollinear points in pointlist.") [] :
let(
n = (pb-pa)/nrm,
distlist = [for(i=[0:len(points)-1]) _dist2line(points[i]-pa, n)]
)
max(distlist) < eps*nrm ?
assert(!error, "Cannot find three noncollinear points in pointlist.") [] :
[0, b, max_index(distlist)];
// Function: pointlist_bounds()
@ -1726,10 +1750,12 @@ function pointlist_bounds(pts) =
assert(is_path(pts,dim=undef,fast=true) , "Invalid pointlist." )
let(
select = ident(len(pts[0])),
spread = [for(i=[0:len(pts[0])-1])
let( spreadi = pts*select[i] )
[min(spreadi), max(spreadi)] ] )
transpose(spread);
spread = [
for(i=[0:len(pts[0])-1])
let( spreadi = pts*select[i] )
[ min(spreadi), max(spreadi) ]
]
) transpose(spread);
// Function: closest_point()
@ -1783,10 +1809,10 @@ function polygon_area(poly, signed=false) =
plane==[]? undef :
let(
n = plane_normal(plane),
total =
sum([ for(i=[1:1:len(poly)-2])
cross(poly[i]-poly[0], poly[i+1]-poly[0])
]) * n/2
total = sum([
for(i=[1:1:len(poly)-2])
cross(poly[i]-poly[0], poly[i+1]-poly[0])
]) * n/2
)
signed ? total : abs(total);
@ -1869,7 +1895,7 @@ function reindex_polygon(reference, poly, return_error=false) =
[for(i=[0:N-1])
(reference[i]*poly[(i+k)%N]) ] ]*I,
optimal_poly = polygon_shift(fixpoly, max_index(val))
)
)
return_error? [optimal_poly, min(poly*(I*poly)-2*val)] :
optimal_poly;
@ -1901,7 +1927,8 @@ function align_polygon(reference, poly, angles, cp) =
"The `angle` parameter must be a range or a non void list of numbers.")
let( // alignments is a vector of entries of the form: [polygon, error]
alignments = [
for(angle=angles) reindex_polygon(
for(angle=angles)
reindex_polygon(
reference,
zrot(angle,p=poly,cp=cp),
return_error=true
@ -1927,22 +1954,21 @@ function centroid(poly, eps=EPSILON) =
assert( is_finite(eps) && (eps>=0), "The tolerance should be a non-negative value." )
let(
n = len(poly[0])==2 ? 1 :
let(
plane = plane_from_points(poly, fast=true) )
let( plane = plane_from_points(poly, fast=true) )
assert( !is_undef(plane), "The polygon must be planar." )
plane_normal(plane),
v0 = poly[0] ,
val = sum([for(i=[1:len(poly)-2])
let(
v1 = poly[i],
v2 = poly[i+1],
area = cross(v2-v0,v1-v0)*n
)
[ area, (v0+v1+v2)*area ]
] )
)
assert(!approx(val[0],0, eps), "The polygon is self-intersecting or its points are collinear.")
val[1]/val[0]/3;
val = sum([
for(i=[1:len(poly)-2])
let(
v1 = poly[i],
v2 = poly[i+1],
area = cross(v2-v0,v1-v0)*n
) [ area, (v0+v1+v2)*area ]
])
)
assert(!approx(val[0],0, eps), "The polygon is self-intersecting or its points are collinear.")
val[1]/val[0]/3;
@ -1972,38 +1998,39 @@ function point_in_polygon(point, poly, nonzero=true, eps=EPSILON) =
assert( is_finite(eps) && (eps>=0), "The tolerance should be a non-negative value." )
// Does the point lie on any edges? If so return 0.
let(
on_brd = [for(i=[0:1:len(poly)-1])
let( seg = select(poly,i,i+1) )
if( !approx(seg[0],seg[1],eps) )
point_on_segment2d(point, seg, eps=eps)? 1:0 ]
)
sum(on_brd) > 0
? 0
: nonzero
? // Compute winding number and return 1 for interior, -1 for exterior
let(
windchk = [for(i=[0:1:len(poly)-1])
let(seg=select(poly,i,i+1))
if(!approx(seg[0],seg[1],eps=eps))
_point_above_below_segment(point, seg)
]
on_brd = [
for (i = [0:1:len(poly)-1])
let( seg = select(poly,i,i+1) )
if (!approx(seg[0],seg[1],eps) )
point_on_segment2d(point, seg, eps=eps)? 1:0
]
)
sum(on_brd) > 0? 0 :
nonzero
? // Compute winding number and return 1 for interior, -1 for exterior
let(
windchk = [
for(i=[0:1:len(poly)-1])
let( seg=select(poly,i,i+1) )
if (!approx(seg[0],seg[1],eps=eps))
_point_above_below_segment(point, seg)
]
) sum(windchk) != 0 ? 1 : -1
: // or compute the crossings with the ray [point, point+[1,0]]
let(
n = len(poly),
cross = [
for(i=[0:n-1])
let(
p0 = poly[i]-point,
p1 = poly[(i+1)%n]-point
)
sum(windchk) != 0 ? 1 : -1
: // or compute the crossings with the ray [point, point+[1,0]]
let(
n = len(poly),
cross =
[for(i=[0:n-1])
let(
p0 = poly[i]-point,
p1 = poly[(i+1)%n]-point
)
if( ( (p1.y>eps && p0.y<=eps) || (p1.y<=eps && p0.y>eps) )
&& -eps < p0.x - p0.y *(p1.x - p0.x)/(p1.y - p0.y) )
1
]
)
2*(len(cross)%2)-1;
if (
( (p1.y>eps && p0.y<=eps) || (p1.y<=eps && p0.y>eps) )
&& -eps < p0.x - p0.y *(p1.x - p0.x)/(p1.y - p0.y)
) 1
]
) 2*(len(cross)%2)-1;
// Function: polygon_is_clockwise()
@ -2052,7 +2079,7 @@ function ccw_polygon(poly) =
// poly = The list of the path points for the perimeter of the polygon.
function reverse_polygon(poly) =
assert(is_path(poly), "Input should be a polygon")
[poly[0], for(i=[len(poly)-1:-1:1]) poly[i] ];
[ poly[0], for(i=[len(poly)-1:-1:1]) poly[i] ];
// Function: polygon_normal()
@ -2244,14 +2271,16 @@ function split_polygons_at_each_z(polys, zs, _i=0) =
// is_convex_polygon(spiral); // Returns: false
function is_convex_polygon(poly,eps=EPSILON) =
assert(is_path(poly), "The input should be a 2D or 3D polygon." )
let( lp = len(poly),
p0 = poly[0] )
let(
lp = len(poly),
p0 = poly[0]
)
assert( lp>=3 , "A polygon must have at least 3 points" )
let( crosses = [for(i=[0:1:lp-1]) cross(poly[(i+1)%lp]-poly[i], poly[(i+2)%lp]-poly[(i+1)%lp]) ] )
len(p0)==2
? assert( !approx(sqrt(max(max(crosses),-min(crosses))),eps), "The points are collinear" )
? assert( !approx(sqrt(max(max(crosses),-min(crosses))),eps), "The points are collinear" )
min(crosses) >=0 || max(crosses)<=0
: let( prod = crosses*sum(crosses),
: let( prod = crosses*sum(crosses),
minc = min(prod),
maxc = max(prod) )
assert( !approx(sqrt(max(maxc,-minc)),eps), "The points are collinear" )
@ -2439,12 +2468,12 @@ function _closest_s3(s,eps=EPSILON) =
let( nr = cross(s[1]-s[0],s[2]-s[0]),
sz = [ norm(s[1]-s[0]), norm(s[1]-s[2]), norm(s[2]-s[0]) ] )
norm(nr)<eps*max(sz)
? let( i = max_index(sz) )
? let( i = max_index(sz) )
_closest_s2([ s[i], s[(i+1)%3], s[3] ], eps) // degenerate case
// considering that s[3] was the last inserted vertex in s,
// the only possible outcomes will be:
// s or some of the 3 triangles of s containing s[3]
: let(
// considering that s[3] was the last inserted vertex in s,
// the only possible outcomes will be:
// s or some of the 3 triangles of s containing s[3]
: let(
tris = [ [s[0], s[1], s[3]],
[s[1], s[2], s[3]],
[s[2], s[0], s[3]] ],

View file

@ -791,13 +791,17 @@ function _med3(a,b,c) =
// d = convolve([[1,1],[2,2],[3,1]],[[1,2],[2,1]])); // Returns: [3,9,11,7]
function convolve(p,q) =
p==[] || q==[] ? [] :
assert( (is_vector(p) || is_matrix(p))
&& ( is_vector(q) || (is_matrix(q) && ( !is_vector(p[0]) || (len(p[0])==len(q[0])) ) ) ) ,
"The inputs should be vectors or paths all of the same dimension.")
let( n = len(p),
m = len(q))
[for(i=[0:n+m-2], k1 = max(0,i-n+1), k2 = min(i,m-1) )
sum([for(j=[k1:k2]) p[i-j]*q[j] ])
assert(
(is_vector(p) || is_matrix(p))
&& ( is_vector(q) || (is_matrix(q) && ( !is_vector(p[0]) || (len(p[0])==len(q[0])) ) ) ) ,
"The inputs should be vectors or paths all of the same dimension."
)
let(
n = len(p),
m = len(q)
) [
for (i=[0:n+m-2], k1 = max(0,i-n+1), k2 = min(i,m-1) )
sum([for(j=[k1:k2]) p[i-j]*q[j] ])
];

View file

@ -1010,7 +1010,7 @@ module jittered_poly(path, dist=1/512) {
// xcopies(3) circle(3, $fn=32);
// }
module extrude_from_to(pt1, pt2, convexity, twist, scale, slices) {
assert( is_path([pt1,pt2],3), "The points should be 3d points");
assert( is_path([pt1,pt2],3), "The points should be 3d points");
rtp = xyz_to_spherical(pt2-pt1);
translate(pt1) {
rotate([0, rtp[2], rtp[1]]) {

View file

@ -1434,10 +1434,10 @@ function trapezoid(h, w1, w2, angle, shift=0, chamfer=0, rounding=0, anchor=CENT
assert(is_undef(angle) || is_finite(angle))
assert(num_defined([h, w1, w2, angle]) == 3, "Must give exactly 3 of the arguments h, w1, w2, and angle.")
assert(is_finite(shift))
assert(is_finite(chamfer) || is_vector(chamfer,4))
assert(is_finite(rounding) || is_vector(rounding,4))
assert(is_finite(chamfer) || is_vector(chamfer,4))
assert(is_finite(rounding) || is_vector(rounding,4))
let(
simple = chamfer==0 && rounding==0,
simple = chamfer==0 && rounding==0,
h = !is_undef(h)? h : opp_ang_to_adj(abs(w2-w1)/2, abs(angle)),
w1 = !is_undef(w1)? w1 : w2 + 2*(adj_ang_to_opp(h, angle) + shift),
w2 = !is_undef(w2)? w2 : w1 - 2*(adj_ang_to_opp(h, angle) + shift)
@ -1451,23 +1451,23 @@ function trapezoid(h, w1, w2, angle, shift=0, chamfer=0, rounding=0, anchor=CENT
[-w1/2,-h/2],
[w1/2,-h/2],
],
cpath = simple? base_path :
path_chamfer_and_rounding(
base_path, closed=true,
chamfer=chamfer,
rounding=rounding
),
path = reverse(cpath)
) simple?
reorient(anchor,spin, two_d=true, size=[w1,h], size2=w2, shift=shift, p=path) :
reorient(anchor,spin, two_d=true, path=path, p=path);
cpath = simple? base_path :
path_chamfer_and_rounding(
base_path, closed=true,
chamfer=chamfer,
rounding=rounding
),
path = reverse(cpath)
) simple
? reorient(anchor,spin, two_d=true, size=[w1,h], size2=w2, shift=shift, p=path)
: reorient(anchor,spin, two_d=true, path=path, p=path);
module trapezoid(h, w1, w2, angle, shift=0, chamfer=0, rounding=0, anchor=CENTER, spin=0) {
path = trapezoid(h=h, w1=w1, w2=w2, angle=angle, shift=shift, chamfer=chamfer, rounding=rounding);
path = trapezoid(h=h, w1=w1, w2=w2, angle=angle, shift=shift, chamfer=chamfer, rounding=rounding);
union() {
simple = chamfer==0 && rounding==0;
simple = chamfer==0 && rounding==0;
h = !is_undef(h)? h : opp_ang_to_adj(abs(w2-w1)/2, abs(angle));
w1 = !is_undef(w1)? w1 : w2 + 2*(adj_ang_to_opp(h, angle) + shift);
w2 = !is_undef(w2)? w2 : w1 - 2*(adj_ang_to_opp(h, angle) + shift);