mirror of
https://github.com/BelfrySCAD/BOSL2.git
synced 2024-12-29 16:29:40 +00:00
fix vnf_halfspace bug
fix doc errors
This commit is contained in:
parent
869a764815
commit
9a01e15f3f
1 changed files with 264 additions and 257 deletions
521
vnf.scad
521
vnf.scad
|
@ -206,35 +206,35 @@ function vnf_vertex_array(
|
||||||
// points = List of point lists for each row
|
// points = List of point lists for each row
|
||||||
// row_wrap = If true then add faces connecting the first row and last row. These rows must differ by at most 2 in length.
|
// row_wrap = If true then add faces connecting the first row and last row. These rows must differ by at most 2 in length.
|
||||||
// reverse = Set this to reverse the direction of the faces
|
// reverse = Set this to reverse the direction of the faces
|
||||||
// Example: Each row has one more point than the preceeding one.
|
// Example(3D): Each row has one more point than the preceeding one.
|
||||||
// pts = [for(y=[1:1:10]) [for(x=[0:y-1]) [x,y,y]]];
|
// pts = [for(y=[1:1:10]) [for(x=[0:y-1]) [x,y,y]]];
|
||||||
// vnf = vnf_tri_array(pts);
|
// vnf = vnf_tri_array(pts);
|
||||||
// vnf_wireframe(vnf,d=.1);
|
// vnf_wireframe(vnf,width=0.1);
|
||||||
// color("red")move_copies(flatten(pts)) sphere(r=.15,$fn=9);
|
// color("red")move_copies(flatten(pts)) sphere(r=.15,$fn=9);
|
||||||
// Example: Each row has one more point than the preceeding one.
|
// Example(3D): Each row has one more point than the preceeding one.
|
||||||
// pts = [for(y=[0:2:10]) [for(x=[-y/2:y/2]) [x,y,y]]];
|
// pts = [for(y=[0:2:10]) [for(x=[-y/2:y/2]) [x,y,y]]];
|
||||||
// vnf = vnf_tri_array(pts);
|
// vnf = vnf_tri_array(pts);
|
||||||
// vnf_wireframe(vnf,d=.1);
|
// vnf_wireframe(vnf,width=0.1);
|
||||||
// color("red")move_copies(flatten(pts)) sphere(r=.15,$fn=9);
|
// color("red")move_copies(flatten(pts)) sphere(r=.15,$fn=9);
|
||||||
// Example: Chaining two VNFs to construct a cone with one point length change between rows.
|
// Example(3D): Chaining two VNFs to construct a cone with one point length change between rows.
|
||||||
// pts1 = [for(z=[0:10]) path3d(arc(3+z,r=z/2+1, angle=[0,180]),10-z)];
|
// pts1 = [for(z=[0:10]) path3d(arc(3+z,r=z/2+1, angle=[0,180]),10-z)];
|
||||||
// pts2 = [for(z=[0:10]) path3d(arc(3+z,r=z/2+1, angle=[180,360]),10-z)];
|
// pts2 = [for(z=[0:10]) path3d(arc(3+z,r=z/2+1, angle=[180,360]),10-z)];
|
||||||
// vnf = vnf_tri_array(pts1,
|
// vnf = vnf_tri_array(pts1,
|
||||||
// vnf=vnf_tri_array(pts2));
|
// vnf=vnf_tri_array(pts2));
|
||||||
// color("green")vnf_wireframe(vnf,d=.1);
|
// color("green")vnf_wireframe(vnf,width=0.1);
|
||||||
// vnf_polyhedron(vnf);
|
// vnf_polyhedron(vnf);
|
||||||
// Example: Cone with length change two between rows
|
// Example(3D): Cone with length change two between rows
|
||||||
// pts1 = [for(z=[0:1:10]) path3d(arc(3+2*z,r=z/2+1, angle=[0,180]),10-z)];
|
// pts1 = [for(z=[0:1:10]) path3d(arc(3+2*z,r=z/2+1, angle=[0,180]),10-z)];
|
||||||
// pts2 = [for(z=[0:1:10]) path3d(arc(3+2*z,r=z/2+1, angle=[180,360]),10-z)];
|
// pts2 = [for(z=[0:1:10]) path3d(arc(3+2*z,r=z/2+1, angle=[180,360]),10-z)];
|
||||||
// vnf = vnf_tri_array(pts1,
|
// vnf = vnf_tri_array(pts1,
|
||||||
// vnf=vnf_tri_array(pts2));
|
// vnf=vnf_tri_array(pts2));
|
||||||
// color("green")vnf_wireframe(vnf,d=.1);
|
// color("green")vnf_wireframe(vnf,width=0.1);
|
||||||
// vnf_polyhedron(vnf);
|
// vnf_polyhedron(vnf);
|
||||||
// Example: Point count can change irregularly
|
// Example(3D): Point count can change irregularly
|
||||||
// lens = [10,9,7,5,6,8,8,10];
|
// lens = [10,9,7,5,6,8,8,10];
|
||||||
// pts = [for(y=idx(lens)) lerpn([-lens[y],y,y],[lens[y],y,y],lens[y])];
|
// pts = [for(y=idx(lens)) lerpn([-lens[y],y,y],[lens[y],y,y],lens[y])];
|
||||||
// vnf = vnf_tri_array(pts);
|
// vnf = vnf_tri_array(pts);
|
||||||
// vnf_wireframe(vnf,d=.1);
|
// vnf_wireframe(vnf,width=0.1);
|
||||||
// color("red")move_copies(flatten(pts)) sphere(r=.15,$fn=9);
|
// color("red")move_copies(flatten(pts)) sphere(r=.15,$fn=9);
|
||||||
function vnf_tri_array(points, row_wrap=false, reverse=false, vnf=EMPTY_VNF) =
|
function vnf_tri_array(points, row_wrap=false, reverse=false, vnf=EMPTY_VNF) =
|
||||||
let(
|
let(
|
||||||
|
@ -422,234 +422,6 @@ function vnf_triangulate(vnf) =
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Section: Turning a VNF into geometry
|
|
||||||
|
|
||||||
|
|
||||||
// Module: vnf_polyhedron()
|
|
||||||
// Usage:
|
|
||||||
// vnf_polyhedron(vnf);
|
|
||||||
// vnf_polyhedron([VNF, VNF, VNF, ...]);
|
|
||||||
// Description:
|
|
||||||
// Given a VNF structure, or a list of VNF structures, creates a polyhedron from them.
|
|
||||||
// Arguments:
|
|
||||||
// vnf = A VNF structure, or list of VNF structures.
|
|
||||||
// convexity = Max number of times a line could intersect a wall of the shape.
|
|
||||||
// extent = If true, calculate anchors by extents, rather than intersection. Default: true.
|
|
||||||
// cp = Centerpoint of VNF to use for anchoring when `extent` is false. Default: `[0, 0, 0]`
|
|
||||||
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `"origin"`
|
|
||||||
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0`
|
|
||||||
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP`
|
|
||||||
module vnf_polyhedron(vnf, convexity=2, extent=true, cp=[0,0,0], anchor="origin", spin=0, orient=UP) {
|
|
||||||
vnf = is_vnf_list(vnf)? vnf_merge(vnf) : vnf;
|
|
||||||
cp = is_def(cp) ? cp : vnf_centroid(vnf);
|
|
||||||
attachable(anchor,spin,orient, vnf=vnf, extent=extent, cp=cp) {
|
|
||||||
polyhedron(vnf[0], vnf[1], convexity=convexity);
|
|
||||||
children();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Module: vnf_wireframe()
|
|
||||||
// Usage:
|
|
||||||
// vnf_wireframe(vnf, <r|d>);
|
|
||||||
// Description:
|
|
||||||
// Given a VNF, creates a wire frame ball-and-stick model of the polyhedron with a cylinder for
|
|
||||||
// each edge and a sphere at each vertex. The width parameter specifies the width of the sticks
|
|
||||||
// that form the wire frame.
|
|
||||||
// Arguments:
|
|
||||||
// vnf = A vnf structure
|
|
||||||
// width = width of the cylinders forming the wire frame. Default: 1
|
|
||||||
// Example:
|
|
||||||
// $fn=32;
|
|
||||||
// ball = sphere(r=20, $fn=6);
|
|
||||||
// vnf_wireframe(ball,width=1);
|
|
||||||
// Example:
|
|
||||||
// include <BOSL2/polyhedra.scad>
|
|
||||||
// $fn=32;
|
|
||||||
// cube_oct = regular_polyhedron_info("vnf", name="cuboctahedron", or=20);
|
|
||||||
// vnf_wireframe(cube_oct);
|
|
||||||
// Example: The spheres at the vertex are imperfect at aligning with the cylinders, so especially at low $fn things look prety ugly. This is normal.
|
|
||||||
// include <BOSL2/polyhedra.scad>
|
|
||||||
// $fn=8;
|
|
||||||
// octahedron = regular_polyhedron_info("vnf", name="octahedron", or=20);
|
|
||||||
// vnf_wireframe(octahedron,width=5);
|
|
||||||
module vnf_wireframe(vnf, width=1)
|
|
||||||
{
|
|
||||||
vertex = vnf[0];
|
|
||||||
edges = unique([for (face=vnf[1], i=idx(face))
|
|
||||||
sort([face[i], select(face,i+1)])
|
|
||||||
]);
|
|
||||||
for (e=edges) extrude_from_to(vertex[e[0]],vertex[e[1]]) circle(d=width);
|
|
||||||
move_copies(vertex) sphere(d=width);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Section: Operations on VNFs
|
|
||||||
|
|
||||||
// Function: vnf_volume()
|
|
||||||
// Usage:
|
|
||||||
// vol = vnf_volume(vnf);
|
|
||||||
// Description:
|
|
||||||
// Returns the volume enclosed by the given manifold VNF. The VNF must describe a valid polyhedron with consistent face direction and
|
|
||||||
// no holes; otherwise the results are undefined. Returns a positive volume if face direction is clockwise and a negative volume
|
|
||||||
// if face direction is counter-clockwise.
|
|
||||||
|
|
||||||
// Divide the polyhedron into tetrahedra with the origin as one vertex and sum up the signed volume.
|
|
||||||
function vnf_volume(vnf) =
|
|
||||||
let(verts = vnf[0])
|
|
||||||
sum([
|
|
||||||
for(face=vnf[1], j=[1:1:len(face)-2])
|
|
||||||
cross(verts[face[j+1]], verts[face[j]]) * verts[face[0]]
|
|
||||||
])/6;
|
|
||||||
|
|
||||||
|
|
||||||
// Function: vnf_area()
|
|
||||||
// Usage:
|
|
||||||
// area = vnf_area(vnf);
|
|
||||||
// Description:
|
|
||||||
// Returns the surface area in any VNF by adding up the area of all its faces. The VNF need not be a manifold.
|
|
||||||
function vnf_area(vnf) =
|
|
||||||
let(verts=vnf[0])
|
|
||||||
sum([for(face=vnf[1]) polygon_area(select(verts,face))]);
|
|
||||||
|
|
||||||
|
|
||||||
// Function: vnf_centroid()
|
|
||||||
// Usage:
|
|
||||||
// vol = vnf_centroid(vnf);
|
|
||||||
// Description:
|
|
||||||
// Returns the centroid of the given manifold VNF. The VNF must describe a valid polyhedron with consistent face direction and
|
|
||||||
// no holes; otherwise the results are undefined.
|
|
||||||
|
|
||||||
// Divide the solid up into tetrahedra with the origin as one vertex.
|
|
||||||
// The centroid of a tetrahedron is the average of its vertices.
|
|
||||||
// The centroid of the total is the volume weighted average.
|
|
||||||
function vnf_centroid(vnf) =
|
|
||||||
assert(is_vnf(vnf) && len(vnf[0])!=0 )
|
|
||||||
let(
|
|
||||||
verts = vnf[0],
|
|
||||||
pos = sum([
|
|
||||||
for(face=vnf[1], j=[1:1:len(face)-2]) let(
|
|
||||||
v0 = verts[face[0]],
|
|
||||||
v1 = verts[face[j]],
|
|
||||||
v2 = verts[face[j+1]],
|
|
||||||
vol = cross(v2,v1)*v0
|
|
||||||
)
|
|
||||||
[ vol, (v0+v1+v2)*vol ]
|
|
||||||
])
|
|
||||||
)
|
|
||||||
assert(!approx(pos[0],0, EPSILON), "The vnf has self-intersections.")
|
|
||||||
pos[1]/pos[0]/4;
|
|
||||||
|
|
||||||
|
|
||||||
// Function: vnf_halfspace()
|
|
||||||
// Usage:
|
|
||||||
// newvnf = vnf_halfspace(plane, vnf, [closed]);
|
|
||||||
// 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.
|
|
||||||
// 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
|
|
||||||
// Example:
|
|
||||||
// vnf = cube(10,center=true);
|
|
||||||
// cutvnf = vnf_halfspace([-1,1,-1,0], vnf);
|
|
||||||
// vnf_polyhedron(cutvnf);
|
|
||||||
// Example: Cut face has 2 components
|
|
||||||
// vnf = path_sweep(circle(r=4, $fn=16),
|
|
||||||
// circle(r=20, $fn=64),closed=true);
|
|
||||||
// cutvnf = vnf_halfspace([-1,1,-4,0], vnf);
|
|
||||||
// vnf_polyhedron(cutvnf);
|
|
||||||
// Example: Cut face is not simply connected
|
|
||||||
// vnf = path_sweep(circle(r=4, $fn=16),
|
|
||||||
// circle(r=20, $fn=64),closed=true);
|
|
||||||
// cutvnf = vnf_halfspace([0,0.7,-4,0], vnf);
|
|
||||||
// vnf_polyhedron(cutvnf);
|
|
||||||
// Example: Cut object has multiple components
|
|
||||||
// function knot(a,b,t) = // rolling knot
|
|
||||||
// [ a * cos (3 * t) / (1 - b* sin (2 *t)),
|
|
||||||
// a * sin( 3 * t) / (1 - b* sin (2 *t)),
|
|
||||||
// 1.8 * b * cos (2 * t) /(1 - b* sin (2 *t))];
|
|
||||||
// a = 0.8; b = sqrt (1 - a * a);
|
|
||||||
// ksteps = 400;
|
|
||||||
// knot_path = [for (i=[0:ksteps-1]) 50 * knot(a,b,(i/ksteps)*360)];
|
|
||||||
// ushape = [[-10, 0],[-10, 10],[ -7, 10],[ -7, 2],[ 7, 2],[ 7, 7],[ 10, 7],[ 10, 0]];
|
|
||||||
// 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) =
|
|
||||||
let(
|
|
||||||
inside = [for(x=vnf[0]) plane*[each x,-1] >= 0 ? 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(
|
|
||||||
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(
|
|
||||||
faceregion = project_plane(plane, newpaths),
|
|
||||||
facevnf = region_faces(faceregion,reverse=true)
|
|
||||||
)
|
|
||||||
vnf_merge([[newvert, faces_edges_vertices[0]], lift_plane(plane, facevnf)]);
|
|
||||||
|
|
||||||
|
|
||||||
function _assemble_paths(vertices, edges, paths=[],i=0) =
|
|
||||||
i==len(edges) ? paths :
|
|
||||||
norm(vertices[edges[i][0]]-vertices[edges[i][1]])<EPSILON ? echo(degen=i)_assemble_paths(vertices,edges,paths,i+1) :
|
|
||||||
let( // Find paths that connects on left side and right side of the edges (if one exists)
|
|
||||||
left = [for(j=idx(paths)) if (approx(vertices[last(paths[j])],vertices[edges[i][0]])) j],
|
|
||||||
right = [for(j=idx(paths)) if (approx(vertices[edges[i][1]],vertices[paths[j][0]])) j]
|
|
||||||
)
|
|
||||||
assert(len(left)<=1 && len(right)<=1)
|
|
||||||
let(
|
|
||||||
keep_path = list_remove(paths,concat(left,right)),
|
|
||||||
update_path = left==[] && right==[] ? edges[i]
|
|
||||||
: left==[] ? concat([edges[i][0]],paths[right[0]])
|
|
||||||
: right==[] ? concat(paths[left[0]],[edges[i][1]])
|
|
||||||
: left != right ? concat(paths[left[0]], paths[right[0]])
|
|
||||||
: paths[left[0]]
|
|
||||||
)
|
|
||||||
_assemble_paths(vertices, edges, concat(keep_path, [update_path]), i+1);
|
|
||||||
|
|
||||||
|
|
||||||
function _vnfcut(plane, vertices, vertexmap, inside, faces, vertcount, newfaces=[], newedges=[], newvertices=[], i=0) =
|
|
||||||
i==len(faces) ? [newfaces, newedges, newvertices] :
|
|
||||||
let(
|
|
||||||
pts_inside = select(inside,faces[i])
|
|
||||||
)
|
|
||||||
all(pts_inside) ? _vnfcut(plane, vertices, vertexmap, inside, faces, vertcount,
|
|
||||||
concat(newfaces, [select(vertexmap,faces[i])]), newedges, newvertices, i+1):
|
|
||||||
!any(pts_inside) ? _vnfcut(plane, vertices, vertexmap,inside, faces, vertcount, newfaces, newedges, newvertices, i+1):
|
|
||||||
let(
|
|
||||||
first = search([[1,0]],pair(pts_inside,wrap=true),0)[0],
|
|
||||||
second = search([[0,1]],pair(pts_inside,wrap=true),0)[0]
|
|
||||||
)
|
|
||||||
assert(len(first)==1 && len(second)==1, "Found concave face in VNF. Run vnf_triangulate first to ensure convex faces.")
|
|
||||||
let(
|
|
||||||
newface = [each select(vertexmap,select(faces[i],second[0]+1,first[0])),vertcount, vertcount+1],
|
|
||||||
newvert = [plane_line_intersection(plane, select(vertices,select(faces[i],first[0],first[0]+1)),eps=0),
|
|
||||||
plane_line_intersection(plane, select(vertices,select(faces[i],second[0],second[0]+1)),eps=0)]
|
|
||||||
)
|
|
||||||
true //!approx(newvert[0],newvert[1])
|
|
||||||
? _vnfcut(plane, vertices, vertexmap, inside, faces, vertcount+2,
|
|
||||||
concat(newfaces, [newface]), concat(newedges,[[vertcount+1,vertcount]]),concat(newvertices,newvert),i+1)
|
|
||||||
:len(newface)>3
|
|
||||||
? _vnfcut(plane, vertices, vertexmap, inside, faces, vertcount+1,
|
|
||||||
concat(newfaces, [list_head(newface)]), newedges,concat(newvertices,[newvert[0]]),i+1)
|
|
||||||
:
|
|
||||||
_vnfcut(plane, vertices, vertexmap, inside, faces, vertcount,newfaces, newedges, newvert, i+1);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Function: vnf_slice()
|
// Function: vnf_slice()
|
||||||
// Usage:
|
// Usage:
|
||||||
// sliced = vnf_slice(vnf, dir, cuts);
|
// sliced = vnf_slice(vnf, dir, cuts);
|
||||||
|
@ -657,7 +429,7 @@ function _vnfcut(plane, vertices, vertexmap, inside, faces, vertcount, newfaces=
|
||||||
// Slice the faces of a VNF along a specified axis direction at a given list
|
// Slice the faces of a VNF along a specified axis direction at a given list
|
||||||
// of cut points. You can use this to refine the faces of a VNF before applying
|
// of cut points. You can use this to refine the faces of a VNF before applying
|
||||||
// a nonlinear transformation to its vertex set.
|
// a nonlinear transformation to its vertex set.
|
||||||
// Example:
|
// Example(3D):
|
||||||
// include <BOSL2-fork/polyhedra.scad>
|
// include <BOSL2-fork/polyhedra.scad>
|
||||||
// vnf = regular_polyhedron_info("vnf", "dodecahedron", side=12);
|
// vnf = regular_polyhedron_info("vnf", "dodecahedron", side=12);
|
||||||
// vnf_polyhedron(vnf);
|
// vnf_polyhedron(vnf);
|
||||||
|
@ -753,6 +525,240 @@ function _slice_3dpolygons(polys, dir, cuts) =
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Section: Turning a VNF into geometry
|
||||||
|
|
||||||
|
|
||||||
|
// Module: vnf_polyhedron()
|
||||||
|
// Usage:
|
||||||
|
// vnf_polyhedron(vnf);
|
||||||
|
// vnf_polyhedron([VNF, VNF, VNF, ...]);
|
||||||
|
// Description:
|
||||||
|
// Given a VNF structure, or a list of VNF structures, creates a polyhedron from them.
|
||||||
|
// Arguments:
|
||||||
|
// vnf = A VNF structure, or list of VNF structures.
|
||||||
|
// convexity = Max number of times a line could intersect a wall of the shape.
|
||||||
|
// extent = If true, calculate anchors by extents, rather than intersection. Default: true.
|
||||||
|
// cp = Centerpoint of VNF to use for anchoring when `extent` is false. Default: `[0, 0, 0]`
|
||||||
|
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `"origin"`
|
||||||
|
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0`
|
||||||
|
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP`
|
||||||
|
module vnf_polyhedron(vnf, convexity=2, extent=true, cp=[0,0,0], anchor="origin", spin=0, orient=UP) {
|
||||||
|
vnf = is_vnf_list(vnf)? vnf_merge(vnf) : vnf;
|
||||||
|
cp = is_def(cp) ? cp : vnf_centroid(vnf);
|
||||||
|
attachable(anchor,spin,orient, vnf=vnf, extent=extent, cp=cp) {
|
||||||
|
polyhedron(vnf[0], vnf[1], convexity=convexity);
|
||||||
|
children();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Module: vnf_wireframe()
|
||||||
|
// Usage:
|
||||||
|
// vnf_wireframe(vnf, <r|d>);
|
||||||
|
// Description:
|
||||||
|
// Given a VNF, creates a wire frame ball-and-stick model of the polyhedron with a cylinder for
|
||||||
|
// each edge and a sphere at each vertex. The width parameter specifies the width of the sticks
|
||||||
|
// that form the wire frame.
|
||||||
|
// Arguments:
|
||||||
|
// vnf = A vnf structure
|
||||||
|
// width = width of the cylinders forming the wire frame. Default: 1
|
||||||
|
// Example:
|
||||||
|
// $fn=32;
|
||||||
|
// ball = sphere(r=20, $fn=6);
|
||||||
|
// vnf_wireframe(ball,width=1);
|
||||||
|
// Example:
|
||||||
|
// include <BOSL2/polyhedra.scad>
|
||||||
|
// $fn=32;
|
||||||
|
// cube_oct = regular_polyhedron_info("vnf",
|
||||||
|
// name="cuboctahedron", or=20);
|
||||||
|
// vnf_wireframe(cube_oct);
|
||||||
|
// Example: The spheres at the vertex are imperfect at aligning with the cylinders, so especially at low $fn things look prety ugly. This is normal.
|
||||||
|
// include <BOSL2/polyhedra.scad>
|
||||||
|
// $fn=8;
|
||||||
|
// octahedron = regular_polyhedron_info("vnf",
|
||||||
|
// name="octahedron", or=20);
|
||||||
|
// vnf_wireframe(octahedron,width=5);
|
||||||
|
module vnf_wireframe(vnf, width=1)
|
||||||
|
{
|
||||||
|
vertex = vnf[0];
|
||||||
|
edges = unique([for (face=vnf[1], i=idx(face))
|
||||||
|
sort([face[i], select(face,i+1)])
|
||||||
|
]);
|
||||||
|
for (e=edges) extrude_from_to(vertex[e[0]],vertex[e[1]]) circle(d=width);
|
||||||
|
move_copies(vertex) sphere(d=width);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Section: Operations on VNFs
|
||||||
|
|
||||||
|
// Function: vnf_volume()
|
||||||
|
// Usage:
|
||||||
|
// vol = vnf_volume(vnf);
|
||||||
|
// Description:
|
||||||
|
// Returns the volume enclosed by the given manifold VNF. The VNF must describe a valid polyhedron with consistent face direction and
|
||||||
|
// no holes; otherwise the results are undefined. Returns a positive volume if face direction is clockwise and a negative volume
|
||||||
|
// if face direction is counter-clockwise.
|
||||||
|
|
||||||
|
// Divide the polyhedron into tetrahedra with the origin as one vertex and sum up the signed volume.
|
||||||
|
function vnf_volume(vnf) =
|
||||||
|
let(verts = vnf[0])
|
||||||
|
sum([
|
||||||
|
for(face=vnf[1], j=[1:1:len(face)-2])
|
||||||
|
cross(verts[face[j+1]], verts[face[j]]) * verts[face[0]]
|
||||||
|
])/6;
|
||||||
|
|
||||||
|
|
||||||
|
// Function: vnf_area()
|
||||||
|
// Usage:
|
||||||
|
// area = vnf_area(vnf);
|
||||||
|
// Description:
|
||||||
|
// Returns the surface area in any VNF by adding up the area of all its faces. The VNF need not be a manifold.
|
||||||
|
function vnf_area(vnf) =
|
||||||
|
let(verts=vnf[0])
|
||||||
|
sum([for(face=vnf[1]) polygon_area(select(verts,face))]);
|
||||||
|
|
||||||
|
|
||||||
|
// Function: vnf_centroid()
|
||||||
|
// Usage:
|
||||||
|
// vol = vnf_centroid(vnf);
|
||||||
|
// Description:
|
||||||
|
// Returns the centroid of the given manifold VNF. The VNF must describe a valid polyhedron with consistent face direction and
|
||||||
|
// no holes; otherwise the results are undefined.
|
||||||
|
|
||||||
|
// Divide the solid up into tetrahedra with the origin as one vertex.
|
||||||
|
// The centroid of a tetrahedron is the average of its vertices.
|
||||||
|
// The centroid of the total is the volume weighted average.
|
||||||
|
function vnf_centroid(vnf) =
|
||||||
|
assert(is_vnf(vnf) && len(vnf[0])!=0 )
|
||||||
|
let(
|
||||||
|
verts = vnf[0],
|
||||||
|
pos = sum([
|
||||||
|
for(face=vnf[1], j=[1:1:len(face)-2]) let(
|
||||||
|
v0 = verts[face[0]],
|
||||||
|
v1 = verts[face[j]],
|
||||||
|
v2 = verts[face[j+1]],
|
||||||
|
vol = cross(v2,v1)*v0
|
||||||
|
)
|
||||||
|
[ vol, (v0+v1+v2)*vol ]
|
||||||
|
])
|
||||||
|
)
|
||||||
|
assert(!approx(pos[0],0, EPSILON), "The vnf has self-intersections.")
|
||||||
|
pos[1]/pos[0]/4;
|
||||||
|
|
||||||
|
|
||||||
|
// Function: vnf_halfspace()
|
||||||
|
// Usage:
|
||||||
|
// newvnf = vnf_halfspace(plane, vnf, [closed]);
|
||||||
|
// 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.
|
||||||
|
// 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
|
||||||
|
// Example(3D):
|
||||||
|
// vnf = cube(10,center=true);
|
||||||
|
// cutvnf = vnf_halfspace([-1,1,-1,0], vnf);
|
||||||
|
// vnf_polyhedron(cutvnf);
|
||||||
|
// Example(3D): Cut face has 2 components
|
||||||
|
// vnf = path_sweep(circle(r=4, $fn=16),
|
||||||
|
// circle(r=20, $fn=64),closed=true);
|
||||||
|
// cutvnf = vnf_halfspace([-1,1,-4,0], vnf);
|
||||||
|
// vnf_polyhedron(cutvnf);
|
||||||
|
// Example(3D): Cut face is not simply connected
|
||||||
|
// vnf = path_sweep(circle(r=4, $fn=16),
|
||||||
|
// circle(r=20, $fn=64),closed=true);
|
||||||
|
// cutvnf = vnf_halfspace([0,0.7,-4,0], vnf);
|
||||||
|
// vnf_polyhedron(cutvnf);
|
||||||
|
// Example(3D): Cut object has multiple components
|
||||||
|
// function knot(a,b,t) = // rolling knot
|
||||||
|
// [ a * cos (3 * t) / (1 - b* sin (2 *t)),
|
||||||
|
// a * sin( 3 * t) / (1 - b* sin (2 *t)),
|
||||||
|
// 1.8 * b * cos (2 * t) /(1 - b* sin (2 *t))];
|
||||||
|
// a = 0.8; b = sqrt (1 - a * a);
|
||||||
|
// ksteps = 400;
|
||||||
|
// knot_path = [for (i=[0:ksteps-1]) 50 * knot(a,b,(i/ksteps)*360)];
|
||||||
|
// ushape = [[-10, 0],[-10, 10],[ -7, 10],[ -7, 2],[ 7, 2],[ 7, 7],[ 10, 7],[ 10, 0]];
|
||||||
|
// 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) =
|
||||||
|
let(
|
||||||
|
inside = [for(x=vnf[0]) plane*[each x,-1] >= 0 ? 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(
|
||||||
|
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(
|
||||||
|
M = project_plane(plane),
|
||||||
|
faceregion = [for(path=newpaths) path2d(project_plane(plane, select(newvert,path)))],
|
||||||
|
facevnf = region_faces(faceregion,transform=rot_inverse(M),reverse=true)
|
||||||
|
)
|
||||||
|
vnf_merge([[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) :
|
||||||
|
let( // Find paths that connects on left side and right side of the edges (if one exists)
|
||||||
|
left = [for(j=idx(paths)) if (approx(vertices[last(paths[j])],vertices[edges[i][0]])) j],
|
||||||
|
right = [for(j=idx(paths)) if (approx(vertices[edges[i][1]],vertices[paths[j][0]])) j]
|
||||||
|
)
|
||||||
|
assert(len(left)<=1 && len(right)<=1)
|
||||||
|
let(
|
||||||
|
keep_path = list_remove(paths,concat(left,right)),
|
||||||
|
update_path = left==[] && right==[] ? edges[i]
|
||||||
|
: left==[] ? concat([edges[i][0]],paths[right[0]])
|
||||||
|
: right==[] ? concat(paths[left[0]],[edges[i][1]])
|
||||||
|
: left != right ? concat(paths[left[0]], paths[right[0]])
|
||||||
|
: paths[left[0]]
|
||||||
|
)
|
||||||
|
_assemble_paths(vertices, edges, concat(keep_path, [update_path]), i+1);
|
||||||
|
|
||||||
|
|
||||||
|
function _vnfcut(plane, vertices, vertexmap, inside, faces, vertcount, newfaces=[], newedges=[], newvertices=[], i=0) =
|
||||||
|
i==len(faces) ? [newfaces, newedges, newvertices] :
|
||||||
|
let(
|
||||||
|
pts_inside = select(inside,faces[i])
|
||||||
|
)
|
||||||
|
all(pts_inside) ? _vnfcut(plane, vertices, vertexmap, inside, faces, vertcount,
|
||||||
|
concat(newfaces, [select(vertexmap,faces[i])]), newedges, newvertices, i+1):
|
||||||
|
!any(pts_inside) ? _vnfcut(plane, vertices, vertexmap,inside, faces, vertcount, newfaces, newedges, newvertices, i+1):
|
||||||
|
let(
|
||||||
|
first = search([[1,0]],pair(pts_inside,wrap=true),0)[0],
|
||||||
|
second = search([[0,1]],pair(pts_inside,wrap=true),0)[0]
|
||||||
|
)
|
||||||
|
assert(len(first)==1 && len(second)==1, "Found concave face in VNF. Run vnf_triangulate first to ensure convex faces.")
|
||||||
|
let(
|
||||||
|
newface = [each select(vertexmap,select(faces[i],second[0]+1,first[0])),vertcount, vertcount+1],
|
||||||
|
newvert = [plane_line_intersection(plane, select(vertices,select(faces[i],first[0],first[0]+1)),eps=0),
|
||||||
|
plane_line_intersection(plane, select(vertices,select(faces[i],second[0],second[0]+1)),eps=0)]
|
||||||
|
)
|
||||||
|
true //!approx(newvert[0],newvert[1])
|
||||||
|
? _vnfcut(plane, vertices, vertexmap, inside, faces, vertcount+2,
|
||||||
|
concat(newfaces, [newface]), concat(newedges,[[vertcount+1,vertcount]]),concat(newvertices,newvert),i+1)
|
||||||
|
:len(newface)>3
|
||||||
|
? _vnfcut(plane, vertices, vertexmap, inside, faces, vertcount+1,
|
||||||
|
concat(newfaces, [list_head(newface)]), newedges,concat(newvertices,[newvert[0]]),i+1)
|
||||||
|
:
|
||||||
|
_vnfcut(plane, vertices, vertexmap, inside, faces, vertcount,newfaces, newedges, newvert, i+1);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function _triangulate_planar_convex_polygons(polys) =
|
function _triangulate_planar_convex_polygons(polys) =
|
||||||
polys==[]? [] :
|
polys==[]? [] :
|
||||||
let(
|
let(
|
||||||
|
@ -809,7 +815,8 @@ function _triangulate_planar_convex_polygons(polys) =
|
||||||
// bent2 = vnf_bend(vnf2, axis="Y");
|
// bent2 = vnf_bend(vnf2, axis="Y");
|
||||||
// vnf_polyhedron([bent1,bent2]);
|
// vnf_polyhedron([bent1,bent2]);
|
||||||
// Example(3D):
|
// Example(3D):
|
||||||
// rgn = union(rect([100,20],center=true), rect([20,100],center=true));
|
// rgn = union(rect([100,20],center=true),
|
||||||
|
// rect([20,100],center=true));
|
||||||
// vnf0 = linear_sweep(zrot(45,p=rgn), height=10);
|
// vnf0 = linear_sweep(zrot(45,p=rgn), height=10);
|
||||||
// vnf1 = up(50, p=vnf0);
|
// vnf1 = up(50, p=vnf0);
|
||||||
// vnf2 = down(50, p=vnf0);
|
// vnf2 = down(50, p=vnf0);
|
||||||
|
@ -889,22 +896,22 @@ function vnf_bend(vnf,r,d,axis="Z") =
|
||||||
|
|
||||||
// Section: Debugging Polyhedrons
|
// Section: Debugging Polyhedrons
|
||||||
|
|
||||||
// Module: _show_vertices()
|
/// Internal Module: _show_vertices()
|
||||||
// Usage:
|
/// Usage:
|
||||||
// _show_vertices(vertices, [size])
|
/// _show_vertices(vertices, [size])
|
||||||
// Description:
|
/// Description:
|
||||||
// Draws all the vertices in an array, at their 3D position, numbered by their
|
/// Draws all the vertices in an array, at their 3D position, numbered by their
|
||||||
// position in the vertex array. Also draws any children of this module with
|
/// position in the vertex array. Also draws any children of this module with
|
||||||
// transparency.
|
/// transparency.
|
||||||
// Arguments:
|
/// Arguments:
|
||||||
// vertices = Array of point vertices.
|
/// vertices = Array of point vertices.
|
||||||
// size = The size of the text used to label the vertices. Default: 1
|
/// size = The size of the text used to label the vertices. Default: 1
|
||||||
// Example:
|
/// Example:
|
||||||
// verts = [for (z=[-10,10], y=[-10,10], x=[-10,10]) [x,y,z]];
|
/// verts = [for (z=[-10,10], y=[-10,10], x=[-10,10]) [x,y,z]];
|
||||||
// faces = [[0,1,2], [1,3,2], [0,4,5], [0,5,1], [1,5,7], [1,7,3], [3,7,6], [3,6,2], [2,6,4], [2,4,0], [4,6,7], [4,7,5]];
|
/// faces = [[0,1,2], [1,3,2], [0,4,5], [0,5,1], [1,5,7], [1,7,3], [3,7,6], [3,6,2], [2,6,4], [2,4,0], [4,6,7], [4,7,5]];
|
||||||
// _show_vertices(vertices=verts, size=2) {
|
/// _show_vertices(vertices=verts, size=2) {
|
||||||
// polyhedron(points=verts, faces=faces);
|
/// polyhedron(points=verts, faces=faces);
|
||||||
// }
|
/// }
|
||||||
module _show_vertices(vertices, size=1) {
|
module _show_vertices(vertices, size=1) {
|
||||||
color("blue") {
|
color("blue") {
|
||||||
dups = vector_search(vertices, EPSILON, vertices);
|
dups = vector_search(vertices, EPSILON, vertices);
|
||||||
|
@ -924,7 +931,7 @@ module _show_vertices(vertices, size=1) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Module: _show_faces()
|
/// Internal Module: _show_faces()
|
||||||
/// Usage:
|
/// Usage:
|
||||||
/// _show_faces(vertices, faces, [size=]);
|
/// _show_faces(vertices, faces, [size=]);
|
||||||
/// Description:
|
/// Description:
|
||||||
|
|
Loading…
Reference in a new issue