mirror of
https://github.com/BelfrySCAD/BOSL2.git
synced 2025-02-15 08:09:38 +00:00
Merge branch 'master' into master
This commit is contained in:
commit
8999338d63
5 changed files with 137 additions and 45 deletions
|
@ -592,23 +592,19 @@ function plane3pt_indexed(points, i1, i2, i3) =
|
|||
// is returned as a list of two points on the line of intersection. If any of the input planes are parallel
|
||||
// then returns undef.
|
||||
function plane_intersection(plane1,plane2,plane3) =
|
||||
is_def(plane3) ?
|
||||
let (
|
||||
matrix = [for(p=[plane1,plane2,plane3]) select(p,0,2)],
|
||||
rhs = [for(p=[plane1,plane2,plane3]) p[3]]
|
||||
)
|
||||
linear_solve(matrix,rhs)
|
||||
:
|
||||
let(
|
||||
normal = cross(plane_normal(plane1), plane_normal(plane2))
|
||||
)
|
||||
approx(normal,0) ? undef :
|
||||
let(
|
||||
matrix = [for(p=[plane1,plane2]) select(p,0,2)],
|
||||
rhs = [for(p=[plane1,plane2]) p[3]],
|
||||
point = linear_solve(matrix,rhs)
|
||||
)
|
||||
[point, point+normal];
|
||||
is_def(plane3)? let(
|
||||
matrix = [for(p=[plane1,plane2,plane3]) select(p,0,2)],
|
||||
rhs = [for(p=[plane1,plane2,plane3]) p[3]]
|
||||
) linear_solve(matrix,rhs) :
|
||||
let(
|
||||
normal = cross(plane_normal(plane1), plane_normal(plane2))
|
||||
) approx(norm(normal),0) ? undef :
|
||||
let(
|
||||
matrix = [for(p=[plane1,plane2]) select(p,0,2)],
|
||||
rhs = [for(p=[plane1,plane2]) p[3]],
|
||||
point = linear_solve(matrix,rhs)
|
||||
) is_undef(point)? undef :
|
||||
[point, point+normal];
|
||||
|
||||
|
||||
// Function: plane_from_normal()
|
||||
|
@ -653,8 +649,8 @@ function plane_from_pointslist(points, fast=false, eps=EPSILON) =
|
|||
// Usage:
|
||||
// plane_normal(plane);
|
||||
// Description:
|
||||
// Returns the normal vector for the given plane.
|
||||
function plane_normal(plane) = [for (i=[0:2]) plane[i]];
|
||||
// Returns the unit length normal vector for the given plane.
|
||||
function plane_normal(plane) = unit([for (i=[0:2]) plane[i]]);
|
||||
|
||||
|
||||
// Function: distance_from_plane()
|
||||
|
@ -690,6 +686,9 @@ function closest_point_on_plane(plane, point) =
|
|||
) point - n*d;
|
||||
|
||||
|
||||
// Returns [POINT, U] if line intersects plane at one point.
|
||||
// Returns [LINE, undef] if the line is on the plane.
|
||||
// Returns undef if line is parallel to, but not on the given plane.
|
||||
function _general_plane_line_intersection(plane, line, eps=EPSILON) =
|
||||
let(
|
||||
p0 = line[0],
|
||||
|
@ -698,6 +697,7 @@ function _general_plane_line_intersection(plane, line, eps=EPSILON) =
|
|||
u = p1 - p0,
|
||||
d = n * u
|
||||
) abs(d)<eps? (
|
||||
coplanar(plane, p0)? [line,undef] : // Line on plane
|
||||
undef // Line parallel to plane
|
||||
) : let(
|
||||
v0 = closest_point_on_plane(plane, [0,0,0]),
|
||||
|
@ -726,20 +726,24 @@ function plane_line_angle(plane, line) =
|
|||
// pt = plane_line_intersection(plane, line, [eps]);
|
||||
// Description:
|
||||
// Takes a line, and a plane [A,B,C,D] where the equation of that plane is `Ax+By+Cz=D`.
|
||||
// Returns the coordinates of the where the given `line` intersects the given `plane`.
|
||||
// Returns `undef` if the line is parallel to the plane.
|
||||
// If `line` intersects `plane` at one point, then that intersection point is returned.
|
||||
// If `line` lies on `plane`, then the original given `line` is returned.
|
||||
// If `line` is parallel to, but not on `plane`, then `undef` is returned.
|
||||
// Arguments:
|
||||
// plane = The [A,B,C,D] values for the equation of the plane.
|
||||
// line = A list of two 3D points that are on the line.
|
||||
// bounded = If false, the line is considered unbounded. If true, it is treated as a bounded line segment. If given as `[true, false]` or `[false, true]`, the boundedness of the points are specified individually, allowing the line to be treated as a half-bounded ray. Default: false (unbounded)
|
||||
// eps = The epsilon error value to determine whether the line is too close to parallel to the plane. Default: `EPSILON` (1e-9)
|
||||
function plane_line_intersection(plane, line, bounded=false, eps=EPSILON) =
|
||||
assert(is_vector(plane)&&len(plane)==4)
|
||||
assert(is_path(line)&&len(line)==2)
|
||||
assert(is_vector(plane)&&len(plane)==4, "Invalid plane value.")
|
||||
assert(is_path(line)&&len(line)==2, "Invalid line value.")
|
||||
assert(!approx(line[0],line[1]), "The two points defining the line must not be the same point.")
|
||||
let(
|
||||
bounded = is_list(bounded)? bounded : [bounded, bounded],
|
||||
res = _general_plane_line_intersection(plane, line, eps=eps)
|
||||
)
|
||||
is_undef(res)? undef :
|
||||
is_undef(res[1])? res[0] :
|
||||
bounded[0]&&res[1]<0? undef :
|
||||
bounded[1]&&res[1]>1? undef :
|
||||
res[0];
|
||||
|
@ -750,7 +754,10 @@ function plane_line_intersection(plane, line, bounded=false, eps=EPSILON) =
|
|||
// pt = polygon_line_intersection(poly, line, [bounded], [eps]);
|
||||
// Description:
|
||||
// Takes a possibly bounded line, and a 3D planar polygon, and finds their intersection point.
|
||||
// Returns the 3D coordinates of the intersection point, or `undef` if they do not intersect.
|
||||
// If the line is on the plane as the polygon, and intersects, then a list of 3D line
|
||||
// segments is returned, one for each section of the line that is inside the polygon.
|
||||
// If the line is not on the plane of the polygon, but intersects, then the 3D intersection
|
||||
// point is returned. If the line does not intersect the polygon, then `undef` is returned.
|
||||
// Arguments:
|
||||
// poly = The 3D planar polygon to find the intersection with.
|
||||
// line = A list of two 3D points that are on the line.
|
||||
|
@ -770,6 +777,27 @@ function polygon_line_intersection(poly, line, bounded=false, eps=EPSILON) =
|
|||
res = _general_plane_line_intersection(plane, line, eps=eps)
|
||||
)
|
||||
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(poly, p1, p2, p3)),
|
||||
line2d = project_plane([lp1,lp2], p1, p2, p3),
|
||||
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(seg, p1, p2, p3)
|
||||
]
|
||||
) isegs
|
||||
) :
|
||||
bounded[0]&&res[1]<0? undef :
|
||||
bounded[1]&&res[1]>1? undef :
|
||||
let(
|
||||
|
|
47
math.scad
47
math.scad
|
@ -106,9 +106,9 @@ function factorial(n,d=1) = product([for (i=[n:-1:d]) i]);
|
|||
// // Points colored in ROYGBIV order.
|
||||
// rainbow(pts) translate($item) circle(d=3,$fn=8);
|
||||
function lerp(a,b,u) =
|
||||
assert(same_shape(a,b), "Bad or inconsistent inputs to lerp")
|
||||
assert(same_shape(a,b), "Bad or inconsistent inputs to lerp")
|
||||
is_num(u)? (1-u)*a + u*b :
|
||||
assert(is_vector(u), "Input u to lerp must be number or vector")
|
||||
assert(!is_undef(u)&&!is_bool(u)&&!is_string(u), "Input u to lerp must be a number, vector, or range.")
|
||||
[for (v = u) lerp(a,b,v)];
|
||||
|
||||
|
||||
|
@ -435,8 +435,8 @@ function lcm(a,b=[]) =
|
|||
// sum([1,2,3]); // returns 6.
|
||||
// sum([[1,2,3], [3,4,5], [5,6,7]]); // returns [9, 12, 15]
|
||||
function sum(v, dflt=0) =
|
||||
assert(is_consistent(v), "Input to sum is non-numeric or inconsistent")
|
||||
len(v) == 0 ? dflt : _sum(v,v[0]*0);
|
||||
assert(is_consistent(v), "Input to sum is non-numeric or inconsistent")
|
||||
len(v) == 0 ? dflt : _sum(v,v[0]*0);
|
||||
|
||||
function _sum(v,_total,_i=0) = _i>=len(v) ? _total : _sum(v,_total+v[_i], _i+1);
|
||||
|
||||
|
@ -521,8 +521,8 @@ function product(v, i=0, tot=undef) = i>=len(v)? tot : product(v, i+1, ((tot==un
|
|||
|
||||
// Function: mean()
|
||||
// Description:
|
||||
// Returns the mean of all entries in the given array.
|
||||
// If passed an array of vectors, returns a vector of mean of each part.
|
||||
// Returns the arithmatic mean/average of all entries in the given array.
|
||||
// If passed a list of vectors, returns a vector of the mean of each part.
|
||||
// Arguments:
|
||||
// v = The list of values to get the mean of.
|
||||
// Example:
|
||||
|
@ -531,6 +531,25 @@ function product(v, i=0, tot=undef) = i>=len(v)? tot : product(v, i+1, ((tot==un
|
|||
function mean(v) = sum(v)/len(v);
|
||||
|
||||
|
||||
// Function: median()
|
||||
// Usage:
|
||||
// x = median(v);
|
||||
// Description:
|
||||
// Given a list of numbers or vectors, finds the median value or midpoint.
|
||||
// If passed a list of vectors, returns the vector of the median of each part.
|
||||
function median(v) =
|
||||
assert(is_list(v))
|
||||
assert(len(v)>0)
|
||||
is_vector(v[0])? (
|
||||
assert(is_consistent(v))
|
||||
[
|
||||
for (i=idx(v[0]))
|
||||
let(vals = subindex(v,i))
|
||||
(min(vals)+max(vals))/2
|
||||
]
|
||||
) : (min(v)+max(v))/2;
|
||||
|
||||
|
||||
// Section: Matrix math
|
||||
|
||||
// Function: linear_solve()
|
||||
|
@ -550,7 +569,7 @@ function linear_solve(A,b) =
|
|||
)
|
||||
assert(is_vector(b,m) || is_matrix(b,m),"Incompatible matrix and right hand side")
|
||||
let (
|
||||
qr = m<n ? qr_factor(transpose(A)) : qr_factor(A),
|
||||
qr = m<n? qr_factor(transpose(A)) : qr_factor(A),
|
||||
maxdim = max(n,m),
|
||||
mindim = min(n,m),
|
||||
Q = submatrix(qr[0],[0:maxdim-1], [0:mindim-1]),
|
||||
|
@ -587,7 +606,7 @@ function submatrix(M,ind1,ind2) = [for(i=ind1) [for(j=ind2) M[i][j] ] ];
|
|||
// Calculates the QR factorization of the input matrix A and returns it as the list [Q,R]. This factorization can be
|
||||
// used to solve linear systems of equations.
|
||||
function qr_factor(A) =
|
||||
assert(is_matrix(A))
|
||||
assert(is_matrix(A))
|
||||
let(
|
||||
m = len(A),
|
||||
n = len(A[0])
|
||||
|
@ -708,12 +727,12 @@ function determinant(M) =
|
|||
// n = optional width of matrix
|
||||
// square = set to true to require a square matrix. Default: false
|
||||
function is_matrix(A,m,n, square=false) =
|
||||
is_list(A) && len(A)>0 &&
|
||||
(is_undef(m) || len(A)==m) &&
|
||||
is_vector(A[0]) &&
|
||||
(is_undef(n) || len(A[0])==n) &&
|
||||
(!square || n==m) &&
|
||||
is_consistent(A);
|
||||
is_list(A) && len(A)>0 &&
|
||||
(is_undef(m) || len(A)==m) &&
|
||||
is_vector(A[0]) &&
|
||||
(is_undef(n) || len(A[0])==n) &&
|
||||
(!square || n==m) &&
|
||||
is_consistent(A);
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -691,7 +691,6 @@ function _skin_tangent_match(poly1, poly2) =
|
|||
small = swap ? poly2 : poly1,
|
||||
curve_offset = centroid(small)-centroid(big),
|
||||
cutpts = [for(i=[0:len(small)-1]) _find_one_tangent(big, select(small,i,i+1),curve_offset=curve_offset)],
|
||||
d=echo(cutpts = cutpts),
|
||||
shift = select(cutpts,-1)+1,
|
||||
newbig = polygon_shift(big, shift),
|
||||
repeat_counts = [for(i=[0:len(small)-1]) posmod(cutpts[i]-select(cutpts,i-1),len(big))],
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
BOSL_VERSION = [2,0,196];
|
||||
BOSL_VERSION = [2,0,200];
|
||||
|
||||
|
||||
// Section: BOSL Library Version Functions
|
||||
|
|
56
vnf.scad
56
vnf.scad
|
@ -344,14 +344,15 @@ module vnf_polyhedron(vnf, convexity=2) {
|
|||
// ERROR | Orange | OVRPOP_EDGE | Too many faces attached at edge
|
||||
// ERROR | Violet | REVERSAL | Faces reverse across edge
|
||||
// ERROR | Red | T_JUNCTION | Vertex is mid-edge on another Face
|
||||
// ERROR | Blue | FACE_ISECT | Faces intersect
|
||||
// ERROR | Magenta | HOLE_EDGE | Edge bounds Hole
|
||||
//
|
||||
// Still to implement:
|
||||
// - Face intersections.
|
||||
// - Overlapping coplanar faces.
|
||||
// Arguments:
|
||||
// vnf = The VNF to validate.
|
||||
// size = The width of the lines and diameter of points used to highlight edges and vertices. Module only. Default: 1
|
||||
// check_isects = If true, performs slow checks for intersecting faces. Default: false
|
||||
// Example: BIG_FACE Warnings; Faces with More Than 3 Vertices. CGAL often will fail to accept that a face is planar after a rotation, if it has more than 3 vertices.
|
||||
// vnf = skin([
|
||||
// path3d(regular_ngon(n=3, d=100),0),
|
||||
|
@ -397,13 +398,19 @@ module vnf_polyhedron(vnf, convexity=2) {
|
|||
// [[0,-50,100], [50,50,100], [50,-50,100]],
|
||||
// ]);
|
||||
// vnf_validate(vnf);
|
||||
// Example: FACE_ISECT Errors; Faces Intersect
|
||||
// vnf = vnf_merge([
|
||||
// vnf_triangulate(linear_sweep(square(100,center=true), height=100)),
|
||||
// move([75,35,30],p=vnf_triangulate(linear_sweep(square(100,center=true), height=100)))
|
||||
// ]);
|
||||
// vnf_validate(vnf,size=2,check_isects=true);
|
||||
// Example: HOLE_EDGE Errors; Edges Adjacent to Holes.
|
||||
// vnf = skin([
|
||||
// path3d(regular_ngon(n=4, d=100),0),
|
||||
// path3d(regular_ngon(n=5, d=100),100)
|
||||
// ], slices=0, caps=false);
|
||||
// vnf_validate(vnf);
|
||||
function vnf_validate(vnf) =
|
||||
// vnf_validate(vnf,size=2);
|
||||
function vnf_validate(vnf, check_isects=false) =
|
||||
let(
|
||||
vnf = vnf_compact(vnf),
|
||||
edges = sort([
|
||||
|
@ -472,10 +479,48 @@ function vnf_validate(vnf) =
|
|||
"red"
|
||||
]
|
||||
]),
|
||||
isect_faces = !check_isects? [] : unique([
|
||||
for (i = [0:1:len(vnf[1])-2])
|
||||
for (j = [i+1:1:len(vnf[1])-1]) let(
|
||||
f1 = vnf[1][i],
|
||||
f2 = vnf[1][j],
|
||||
shared_edges = [
|
||||
for (edge1 = pair_wrap(f1), edge2 = pair_wrap(f2)) let(
|
||||
e1 = edge1[0]<edge1[1]? edge1 : [edge1[1],edge1[0]],
|
||||
e2 = edge2[0]<edge2[1]? edge2 : [edge2[1],edge2[0]]
|
||||
) if (e1==e2) 1
|
||||
]
|
||||
)
|
||||
if (!shared_edges) let(
|
||||
plane1 = plane3pt_indexed(vnf[0], f1[0], f1[1], f1[2]),
|
||||
plane2 = plane3pt_indexed(vnf[0], f2[0], f2[1], f2[2]),
|
||||
line = plane_intersection(plane1, plane2)
|
||||
)
|
||||
if (!is_undef(line)) let(
|
||||
poly1 = select(vnf[0],f1),
|
||||
isects = polygon_line_intersection(poly1,line)
|
||||
)
|
||||
if (!is_undef(isects))
|
||||
for (isect=isects)
|
||||
if (len(isect)>1) let(
|
||||
poly2 = select(vnf[0],f2),
|
||||
isects2 = polygon_line_intersection(poly2,isect,bounded=true)
|
||||
)
|
||||
if (!is_undef(isects2))
|
||||
for (seg=isects2)
|
||||
if (!approx(seg[0],seg[1])) [
|
||||
"ERROR",
|
||||
"FACE_ISECT",
|
||||
"Faces intersect",
|
||||
seg,
|
||||
"blue"
|
||||
]
|
||||
]),
|
||||
hole_edges = unique([
|
||||
for (i=idx(uniq_edges))
|
||||
if (edgecnts[1][i]<2)
|
||||
if (_pts_not_reported(uniq_edges[i], vnf, t_juncts))
|
||||
if (_pts_not_reported(uniq_edges[i], vnf, isect_faces))
|
||||
[
|
||||
"ERROR",
|
||||
"HOLE_EDGE",
|
||||
|
@ -490,6 +535,7 @@ function vnf_validate(vnf) =
|
|||
overpop_edges,
|
||||
reversals,
|
||||
t_juncts,
|
||||
isect_faces,
|
||||
hole_edges
|
||||
);
|
||||
|
||||
|
@ -511,8 +557,8 @@ function _edge_not_reported(edge, vnf, reports) =
|
|||
] == [];
|
||||
|
||||
|
||||
module vnf_validate(vnf, size=1) {
|
||||
faults = vnf_validate(vnf);
|
||||
module vnf_validate(vnf, size=1, check_isects=false) {
|
||||
faults = vnf_validate(vnf, check_isects=check_isects);
|
||||
for (fault = faults) {
|
||||
typ = fault[0];
|
||||
err = fault[1];
|
||||
|
|
Loading…
Reference in a new issue