mirror of
https://github.com/BelfrySCAD/BOSL2.git
synced 2025-01-04 03:09:45 +00:00
Merge pull request #983 from adrianVmariano/master
bug fixes (vnf_halfspace, bselect)
This commit is contained in:
commit
0af2edfcf2
3 changed files with 73 additions and 21 deletions
15
lists.scad
15
lists.scad
|
@ -336,20 +336,17 @@ function list_tail(list, from=1) =
|
|||
// Topics: List Handling
|
||||
// See Also: list_bset()
|
||||
// Description:
|
||||
// Returns the items in `list` whose matching element in `index` is true.
|
||||
// Returns the items in `list` whose matching element in `index` evaluates as true.
|
||||
// Arguments:
|
||||
// list = Initial list to extract items from.
|
||||
// index = List of booleans.
|
||||
// list = Initial list (or string) to extract items from.
|
||||
// index = List of values that will be evaluated as boolean, same length as `list`.
|
||||
// Example:
|
||||
// a = bselect([3,4,5,6,7], [false,true,true,false,true]); // Returns: [4,5,7]
|
||||
function bselect(list,index) =
|
||||
assert(is_list(list)||is_string(list), "Improper list." )
|
||||
assert(is_list(index) && len(index)>=len(list) , "Improper index list." )
|
||||
assert(is_list(list)||is_string(list), "First argument must be a list or string." )
|
||||
assert(is_list(index) && len(index)==len(list) , "Second argument must have same length as the first." )
|
||||
is_string(list)? str_join(bselect( [for (x=list) x], index)) :
|
||||
[for(i=[0:len(list)-1]) if (index[i]) list[i]];
|
||||
|
||||
|
||||
|
||||
[for(i=idx(list)) if (index[i]) list[i]];
|
||||
|
||||
|
||||
// Section: List Construction
|
||||
|
|
|
@ -1372,6 +1372,11 @@ function is_2d_transform(t) = // z-parameters are zero, except we allow t[2][
|
|||
// .
|
||||
// Any other combination of matrices will produce an error, including acting with a 2D matrix (3x3) on 3D data.
|
||||
// The output of apply is always the same dimension as the input—projections are not supported.
|
||||
// .
|
||||
// Note that a matrix with a negative determinant such as any mirror reflection flips the orientation of faces.
|
||||
// If the transform matrix is square then apply() checks the determinant and if it is negative, apply() reverses the face order so that
|
||||
// the transformed VNF has faces with the same winding direction as the original VNF. This adjustment applies
|
||||
// only to VNFs, not to beziers or point lists.
|
||||
// Arguments:
|
||||
// transform = The 2D (3x3 or 2x3) or 3D (4x4 or 3x4) transformation matrix to apply.
|
||||
// points = The point, point list, bezier patch, or VNF to apply the transformation to.
|
||||
|
@ -1395,7 +1400,7 @@ function is_2d_transform(t) = // z-parameters are zero, except we allow t[2][
|
|||
// stroke(path2,closed=true);
|
||||
function apply(transform,points) =
|
||||
points==[] ? []
|
||||
: is_vector(points) ? _apply(transform, [points])[0] // point
|
||||
: is_vector(points) ? _apply(transform, [points])[0] // point
|
||||
: is_vnf(points) ? // vnf
|
||||
let(
|
||||
newvnf = [_apply(transform, points[0]), points[1]],
|
||||
|
|
72
vnf.scad
72
vnf.scad
|
@ -1009,16 +1009,26 @@ function _vnf_centroid(vnf,eps=EPSILON) =
|
|||
|
||||
// Function: vnf_halfspace()
|
||||
// Usage:
|
||||
// newvnf = vnf_halfspace(plane, vnf, [closed]);
|
||||
// newvnf = vnf_halfspace(plane, vnf, [closed], [boundary]);
|
||||
// Description:
|
||||
// Returns the intersection of the vnf with a half space. The half space is defined by
|
||||
// plane = [A,B,C,D], taking the side where the normal [A,B,C] points: Ax+By+Cz≥D.
|
||||
// If closed is set to false then the cut face is not included in the vnf. This could
|
||||
// allow further extension of the vnf by merging with other vnfs.
|
||||
// allow further extension of the vnf by join with other vnfs using {{vnf_join()}}.
|
||||
// Note that if your given VNF has holes (missing faces) or is not a complete polyhedron
|
||||
// then closed=true is may produce invalid results when it tries to construct closing faces
|
||||
// on the cut plane. Set closed=false for such inputs.
|
||||
// .
|
||||
// If you set boundary to true then the return will be the pair [vnf,boundary] where vnf is the
|
||||
// vnf as usual (with closed=false) and boundary is a list giving each connected component of the cut
|
||||
// boundary surface. Each entry in boundary is a list of index values that index into the vnf vertex list (vnf[0]).
|
||||
// This makes it possible to construct mating shapes, e.g. with {{skin()}} or {{vnf_vertex_array()}} that
|
||||
// can be combined using {{vnf_join()}} to make a valid polyhedron.
|
||||
// Arguments:
|
||||
// plane = plane defining the boundary of the half space
|
||||
// vnf = vnf to cut
|
||||
// closed = if false do not return include cut face(s). Default: true
|
||||
// closed = if false do not return the cut face(s) in the returned VNF. Default: true
|
||||
// boundary = if true return a pair [vnf,boundary] where boundary is a list of paths on the cut boundary indexed into the VNF vertex list. If boundary is true, then closed is set to false. Default: false
|
||||
// Example(3D):
|
||||
// vnf = cube(10,center=true);
|
||||
// cutvnf = vnf_halfspace([-1,1,-1,0], vnf);
|
||||
|
@ -1045,32 +1055,72 @@ function _vnf_centroid(vnf,eps=EPSILON) =
|
|||
// knot=path_sweep(ushape, knot_path, closed=true, method="incremental");
|
||||
// cut_knot = vnf_halfspace([1,0,0,0], knot);
|
||||
// vnf_polyhedron(cut_knot);
|
||||
function vnf_halfspace(plane, vnf, closed=true) =
|
||||
// Example(VPR=[80,0,15]): Cut a sphere with an arbitrary plane
|
||||
// vnf1=sphere(r=50, style="icosa", $fn=16);
|
||||
// vnf2=vnf_halfspace([.8,1,-1.5,0], vnf1);
|
||||
// vnf_polyhedron(vnf2);
|
||||
// Example(VPR=[80,0,15]): Cut it again, but with closed=false to leave an open boundary.
|
||||
// vnf1=sphere(r=50, style="icosa", $fn=16);
|
||||
// vnf2=vnf_halfspace([.8,1,-1.5,0], vnf1);
|
||||
// vnf3=vnf_halfspace([0,0,-1,0], vnf2, closed=false);
|
||||
// vnf_polyhedron(vnf3);
|
||||
// Example(VPR=[80,0,15]): Use {vnf_join()} to combine with a mating vnf, in this case a reflection of the part we made.
|
||||
// vnf1=sphere(r=50, style="icosa", $fn=16);
|
||||
// vnf2=vnf_halfspace([.8,1,-1.5,0], vnf1);
|
||||
// vnf3=vnf_halfspace([0,0,-1,0], vnf2, closed=false);
|
||||
// vnf4=vnf_join([vnf3, zflip(vnf3,1)]);
|
||||
// vnf_polyhedron(vnf4);
|
||||
// Example: When the input VNF is a surface with a boundary, if you use the default setting closed=true, then vnf_halfspace() tries to construct closing faces from the edges created by the cut. These faces may be invalid, for example if the cut points are collinear. In this example the constructed face is a valid face.
|
||||
// include <BOSL2/beziers.scad>
|
||||
// patch=[
|
||||
// [[10,-10,0],[1,-1,0],[-1,-1,0],[-10,-10,0]],
|
||||
// [[10,-10,20],[1,-1,20],[-1,-1,20],[-10,-10,20]]
|
||||
// ];
|
||||
// vnf=bezier_vnf(patch);
|
||||
// vnfcut = vnf_halfspace([-.8,0,-1,-14],vnf);
|
||||
// vnf_polyhedron(vnfcut);
|
||||
// Example: Setting closed to false eliminates this (possibly invalid) face:
|
||||
// include <BOSL2/beziers.scad>
|
||||
// patch=[
|
||||
// [[10,-10,0],[1,-1,0],[-1,-1,0],[-10,-10,0]],
|
||||
// [[10,-10,20],[1,-1,20],[-1,-1,20],[-10,-10,20]]
|
||||
// ];
|
||||
// vnf=bezier_vnf(patch);
|
||||
// vnfcut = vnf_halfspace([-.8,0,-1,-14],vnf,closed=false);
|
||||
// vnf_polyhedron(vnfcut);
|
||||
// Example: If boundary=true then the return is a list with the VNF and boundary data.
|
||||
// vnf = path_sweep(circle(r=4, $fn=16),
|
||||
// circle(r=20, $fn=64),closed=true);
|
||||
// cut_bnd = vnf_halfspace([-1,1,-4,0], vnf, boundary=true);*/
|
||||
// cutvnf = cut_bnd[0];
|
||||
// boundary = [for(b=cut_bnd[1]) select(cutvnf[0],b)];
|
||||
// vnf_polyhedron(cutvnf);
|
||||
// stroke(boundary,color="red");
|
||||
function vnf_halfspace(plane, vnf, closed=true, boundary=false) =
|
||||
assert(_valid_plane(plane), "Invalid plane")
|
||||
assert(is_vnf(vnf), "Invalid vnf")
|
||||
let(
|
||||
inside = [for(x=vnf[0]) plane*[each x,-1] >= 0 ? 1 : 0],
|
||||
inside = [for(x=vnf[0]) plane*[each x,-1] >= -EPSILON ? 1 : 0],
|
||||
vertexmap = [0,each cumsum(inside)],
|
||||
faces_edges_vertices = _vnfcut(plane, vnf[0],vertexmap,inside, vnf[1], last(vertexmap)),
|
||||
newvert = concat(bselect(vnf[0],inside), faces_edges_vertices[2])
|
||||
)
|
||||
closed==false ? [newvert, faces_edges_vertices[0]] :
|
||||
let(
|
||||
closed==false && !boundary ? [newvert, faces_edges_vertices[0]]
|
||||
: let(
|
||||
allpaths = _assemble_paths(newvert, faces_edges_vertices[1]),
|
||||
newpaths = [for(p=allpaths) if (len(p)>=3) p
|
||||
else assert(approx(p[0],p[1]),"Orphan edge found when assembling cut edges.")
|
||||
]
|
||||
)
|
||||
len(newpaths)<=1 ? [newvert, concat(faces_edges_vertices[0], newpaths)]
|
||||
:
|
||||
let(
|
||||
boundary ? [[newvert, faces_edges_vertices[0]], newpaths]
|
||||
: len(newpaths)<=1 ? [newvert, concat(faces_edges_vertices[0], newpaths)]
|
||||
: let(
|
||||
M = project_plane(plane),
|
||||
faceregion = [for(path=newpaths) path2d(apply(M,select(newvert,path)))],
|
||||
facevnf = vnf_from_region(faceregion,transform=rot_inverse(M),reverse=true)
|
||||
)
|
||||
vnf_join([[newvert, faces_edges_vertices[0]], facevnf]);
|
||||
|
||||
|
||||
function _assemble_paths(vertices, edges, paths=[],i=0) =
|
||||
i==len(edges) ? paths :
|
||||
norm(vertices[edges[i][0]]-vertices[edges[i][1]])<EPSILON ? _assemble_paths(vertices,edges,paths,i+1) :
|
||||
|
|
Loading…
Reference in a new issue