Merge branch 'master' into master

This commit is contained in:
Revar Desmera 2020-03-17 19:51:36 -07:00 committed by GitHub
commit 8999338d63
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 137 additions and 45 deletions

View file

@ -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(

View file

@ -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);

View file

@ -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))],

View file

@ -8,7 +8,7 @@
//////////////////////////////////////////////////////////////////////
BOSL_VERSION = [2,0,196];
BOSL_VERSION = [2,0,200];
// Section: BOSL Library Version Functions

View file

@ -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];