From d986db2e1e50c76854892fa85bdbee735be18d91 Mon Sep 17 00:00:00 2001 From: Adrian Mariano <avm4@cornell.edu> Date: Sun, 3 Oct 2021 22:37:57 -0400 Subject: [PATCH] vnf_debug update path self intersection speedup --- paths.scad | 47 ++++++------ vnf.scad | 204 ++++++++++++++++++++++++++--------------------------- 2 files changed, 120 insertions(+), 131 deletions(-) diff --git a/paths.scad b/paths.scad index eaf40d2..db2c279 100644 --- a/paths.scad +++ b/paths.scad @@ -210,36 +210,31 @@ function path_length_fractions(path, closed=false) = /// for (isect=isects) translate(isect[0]) color("blue") sphere(d=10); function _path_self_intersections(path, closed=true, eps=EPSILON) = let( - path = cleanup_path(path, eps=eps), + path = closed ? close_path(path,eps=eps) : path, plen = len(path) ) - [ - for (i = [0:1:plen-(closed?2:3)]) - let( - a1 = path[i], - a2 = path[(i+1)%plen], - maxax = max(a1.x,a2.x), - minax = min(a1.x,a2.x), - maxay = max(a1.y,a2.y), - minay = min(a1.y,a2.y) - ) - for(j=[i+2:1:plen-(closed?1:2)]) + [ for (i = [0:1:plen-3]) let( + a1 = path[i], + a2 = path[i+1], + // The sign of signals is positive if the segment is one one side of + // the line defined by [a1,a2] and negative on the other side. + seg_normal = unit([-(a2-a1).y, (a2-a1).x]), + signals = [for(j=[i+2:1:plen-(i==0 && closed? 2: 1)]) path[j]-a1 ]*seg_normal + ) + for(j=[i+2:1:plen-(i==0 && closed? 3: 2)]) + // The signals test requires the two signals to have different signs, + // otherwise b1 and b2 are on the same side of the line defined by [a1,a2] + // and hence intersection is impossible + if( signals[j-i-2]*signals[j-i-1] <= 0 ) let( b1 = path[j], - b2 = path[(j+1)%plen], - isect = - maxax < b1.x && maxax < b2.x || - minax > b1.x && minax > b2.x || - maxay < b1.y && maxay < b2.y || - minay > b1.y && minay > b2.y - ? undef - : _general_line_intersection([a1,a2],[b1,b2]) + b2 = path[j+1] ) - if ((!closed || i!=0 || j!=plen-1) - && isect != undef - && isect[1]>=-eps && isect[1]<=1+eps - && isect[2]>=-eps && isect[2]<=1+eps) - [isect[0], i, isect[1], j, isect[2]] + // This test checks that a1 and a2 are on opposite sides of the + // line defined by [b1,b2]. + if( cross(b2-b1, a1-b1)*cross(b2-b1, a2-b1) <= 0 ) + let(isect = _general_line_intersection([a1,a2],[b1,b2],eps=eps)) + if (isect) [isect[0], i, isect[1], j, isect[2]] ]; @@ -266,7 +261,7 @@ function _sum_preserving_round(data, index=0) = // Function: subdivide_path() // Usage: -// newpath = subdivide_path(path, [N|refine], method); +// newpath = subdivide_path(path, [N|refine], method, [closed], [exact]); // Description: // Takes a path as input (closed or open) and subdivides the path to produce a more // finely sampled path. The new points can be distributed proportional to length diff --git a/vnf.scad b/vnf.scad index db9ca92..5db8f27 100644 --- a/vnf.scad +++ b/vnf.scad @@ -486,33 +486,34 @@ module vnf_polyhedron(vnf, convexity=2, extent=true, cp=[0,0,0], anchor="origin" // 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. +// 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 -// r|d = radius or diameter of the cylinders forming the wire frame. Default: r=1 +// width = width of the cylinders forming the wire frame. Default: 1 // Example: // $fn=32; // ball = sphere(r=20, $fn=6); -// vnf_wireframe(ball,d=1); +// 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); +// 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,r=5); -module vnf_wireframe(vnf, r, d) +// 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) { - r = get_radius(r=r,d=d,dflt=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(r=r); - move_copies(vertex) sphere(r=r); + for (e=edges) extrude_from_to(vertex[e[0]],vertex[e[1]]) circle(d=width); + move_copies(vertex) sphere(d=width); } @@ -931,9 +932,9 @@ function _split_polygons_at_each_y(polys, ys, _i=0) = // Section: Debugging Polyhedrons -// Module: debug_vertices() +// Module: _show_vertices() // Usage: -// debug_vertices(vertices, [size], [disabled=]); +// _show_vertices(vertices, [size], [disabled=]); // Description: // 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 @@ -946,87 +947,75 @@ function _split_polygons_at_each_y(polys, ys, _i=0) = // Example: // 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]]; -// debug_vertices(vertices=verts, size=2) { +// _show_vertices(vertices=verts, size=2) { // polyhedron(points=verts, faces=faces); // } -module debug_vertices(vertices, size=1, disabled=false) { - if (!disabled) { - color("blue") { - dups = vector_search(vertices, EPSILON, vertices); - for (ind = dups){ - numstr = str_join([for(i=ind) str(i)],","); - v = vertices[ind[0]]; - translate(v) { - up(size/8) zrot($vpr[2]) xrot(90) { - linear_extrude(height=size/10, center=true, convexity=10) { - text(text=numstr, size=size, halign="center"); - } - } - sphere(size/10); +module _show_vertices(vertices, size=1) { + color("blue") { + dups = vector_search(vertices, EPSILON, vertices); + for (ind = dups){ + numstr = str_join([for(i=ind) str(i)],","); + v = vertices[ind[0]]; + translate(v) { + rot($vpr) back(size/8){ + linear_extrude(height=size/10, center=true, convexity=10) { + text(text=numstr, size=size, halign="center"); + } } + sphere(size/10); } } } - if ($children > 0) { - if (!disabled) { - color([0.2, 1.0, 0, 0.5]) children(); - } else { - children(); - } - } } - -// Module: debug_faces() -// Usage: -// debug_faces(vertices, faces, [size=], [disabled=]); -// Description: -// Draws all the vertices at their 3D position, numbered in blue by their -// position in the vertex array. Each face will have their face number drawn -// in red, aligned with the center of face. All children of this module are drawn -// with transparency. -// Arguments: -// vertices = Array of point vertices. -// faces = Array of faces by vertex numbers. -// --- -// size = The size of the text used to label the faces and vertices. Default: 1 -// disabled = If true, don't draw numbers, and draw children without transparency. Default: false. -// Example(EdgesMed): -// 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]]; -// debug_faces(vertices=verts, faces=faces, size=2) { -// polyhedron(points=verts, faces=faces); -// } -module debug_faces(vertices, faces, size=1, disabled=false) { - if (!disabled) { - vlen = len(vertices); - color("red") { - for (i = [0:1:len(faces)-1]) { - face = faces[i]; - if (face[0] < 0 || face[1] < 0 || face[2] < 0 || face[0] >= vlen || face[1] >= vlen || face[2] >= vlen) { - echo("BAD FACE: ", vlen=vlen, face=face); - } else { - verts = select(vertices,face); - c = mean(verts); - v0 = verts[0]; - v1 = verts[1]; - v2 = verts[2]; - dv0 = unit(v1 - v0); - dv1 = unit(v2 - v0); - nrm0 = cross(dv0, dv1); - nrm1 = UP; - axis = vector_axis(nrm0, nrm1); - ang = vector_angle(nrm0, nrm1); - theta = atan2(nrm0[1], nrm0[0]); - translate(c) { - rotate(a=180-ang, v=axis) { - zrot(theta-90) - linear_extrude(height=size/10, center=true, convexity=10) { - union() { - text(text=str(i), size=size, halign="center"); - text(text=str("_"), size=size, halign="center"); - } +/// Module: _show_faces() +/// Usage: +/// _show_faces(vertices, faces, [size=], [disabled=]); +/// Description: +/// Draws all the vertices at their 3D position, numbered in blue by their +/// position in the vertex array. Each face will have their face number drawn +/// in red, aligned with the center of face. All children of this module are drawn +/// with transparency. +/// Arguments: +/// vertices = Array of point vertices. +/// faces = Array of faces by vertex numbers. +/// --- +/// size = The size of the text used to label the faces and vertices. Default: 1 +/// disabled = If true, don't draw numbers, and draw children without transparency. Default: false. +/// Example(EdgesMed): +/// 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]]; +/// _show_faces(vertices=verts, faces=faces, size=2) { +/// polyhedron(points=verts, faces=faces); +/// } +module _show_faces(vertices, faces, size=1) { + vlen = len(vertices); + color("red") { + for (i = [0:1:len(faces)-1]) { + face = faces[i]; + if (face[0] < 0 || face[1] < 0 || face[2] < 0 || face[0] >= vlen || face[1] >= vlen || face[2] >= vlen) { + echo("BAD FACE: ", vlen=vlen, face=face); + } else { + verts = select(vertices,face); + c = mean(verts); + v0 = verts[0]; + v1 = verts[1]; + v2 = verts[2]; + dv0 = unit(v1 - v0); + dv1 = unit(v2 - v0); + nrm0 = cross(dv0, dv1); + nrm1 = UP; + axis = vector_axis(nrm0, nrm1); + ang = vector_angle(nrm0, nrm1); + theta = atan2(nrm0[1], nrm0[0]); + translate(c) { + rotate(a=180-ang, v=axis) { + zrot(theta-90) + linear_extrude(height=size/10, center=true, convexity=10) { + union() { + text(text=str(i), size=size, halign="center"); + text(text=str("_"), size=size, halign="center"); } } } @@ -1034,44 +1023,49 @@ module debug_faces(vertices, faces, size=1, disabled=false) { } } } - debug_vertices(vertices, size=size, disabled=disabled) { - children(); - } - if (!disabled) { - echo(faces=faces); - } } -// Module: debug_vnf() +// Module: vnf_debug() // Usage: -// debug_vnf(vnfs, [convexity=], [txtsize=], [disabled=]); +// vnf_debug(vnfs, [faces=], [vertices=], [convexity=], [txtsize=]); // Description: -// A drop-in module to replace `vnf_polyhedron()` and help debug vertices and faces. +// A drop-in module to replace `vnf_polyhedron()` to help debug vertices and faces. // Draws all the vertices at their 3D position, numbered in blue by their // position in the vertex array. Each face will have its face number drawn // in red, aligned with the center of face. All given faces are drawn with // transparency. All children of this module are drawn with transparency. // Works best with Thrown-Together preview mode, to see reversed faces. +// You can set opacity to 0 if you want to disable the display of the polyhedron faces. +// . +// The vertex numbers are shown rotated to face you. As you rotate your polyhedron you +// can rerun the preview to display them oriented for viewing from a different viewpoint. +// Topics: Polyhedra, Debugging // Arguments: // vnf = vnf to display // --- +// faces = if true display face numbers. Default: true +// vertices = if true display vertex numbers. Default: true +// opacity = Opacity of the polyhedron faces. Default: 0.5 // convexity = The max number of walls a ray can pass through the given polygon paths. -// txtsize = The size of the text used to label the faces and vertices. +// size = The size of the text used to label the faces and vertices. Default: 1 // disabled = If true, act exactly like `polyhedron()`. Default = false. // Example(EdgesMed): // verts = [for (z=[-10,10], a=[0:120:359.9]) [10*cos(a),10*sin(a),z]]; // faces = [[0,1,2], [5,4,3], [0,3,4], [0,4,1], [1,4,5], [1,5,2], [2,5,3], [2,3,0]]; -// debug_vnf([verts,faces], txtsize=2); -module debug_vnf(vnf, convexity=6, txtsize=1, disabled=false) { - debug_faces(vertices=vnf[0], faces=vnf[1], size=txtsize, disabled=disabled) { - vnf_polyhedron(vnf, convexity=convexity); - } +// vnf_debug([verts,faces], txtsize=2); +module vnf_debug(vnf, convexity=6, size=1, faces=true, vertices=true, opacity=0.5) { + no_children($children); + if (faces) + _show_faces(vertices=vnf[0], faces=vnf[1], size=size); + if (vertices) + _show_vertices(vertices=vnf[0], size=size); + color([0.2, 1.0, 0, opacity]) + vnf_polyhedron(vnf,convexity=convexity); } - // Function&Module: vnf_validate() // Usage: As Function // fails = vnf_validate(vnf);