From 67697fe1396f3c178663cf0f1aa75305eae2d4d1 Mon Sep 17 00:00:00 2001 From: Adrian Mariano Date: Thu, 20 Jun 2024 17:20:10 -0400 Subject: [PATCH 1/3] add projection() --- vnf.scad | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/vnf.scad b/vnf.scad index 8a2e7c0..dfe8724 100644 --- a/vnf.scad +++ b/vnf.scad @@ -1188,6 +1188,70 @@ function _vnf_centroid(vnf,eps=EPSILON) = pos[1]/pos[0]/4; +// Function: projection() +// Synopsis: Returns projection or intersection of vnf with XY plane +// SynTags: VNF: +// Topics: VNF Manipulation +// See Also: vnf_halfspace() +// Usage: +// region = projection(vnf, [cut]); +// Description: +// When `cut=false`, which is the default, projects the input VNF +// onto the XY plane, returning a region. Note that as currently implemented, this operation +// involves the 2D union of all the projected faces and can be very +// slow if the VNF has many faces. Minimize the face count of the VNF for best performance. +// . +// When `cut=true`, returns the intersection of the VNF with the +// XY plane, which is again a region. If the VNF does not intersect +// the XY plane then returns the empty set. This operation is +// much faster than `cut=false`. +// Example: Here's a VNF with two linked toruses and a small cube +// vnf = vnf_join([ +// xrot(90,torus(id=15,od=24,$fn=5)), +// right(12,torus(id=15,od=24,$fn=4)), +// up(13,right(15,cube(3,center=true))) +// ]); +// vnf_polyhedron(vnf); +// Example(2D): Projection of above VNF with default behavior, `cut=false` +// vnf = vnf_join([ +// xrot(90,torus(id=15,od=24,$fn=5)), +// right(12,torus(id=15,od=24,$fn=4)), +// up(13,right(15,cube(3,center=true))) +// ]); +// reg = projection(vnf); +// region(reg); +// Example: Tilted torus +// vnf = xrot(35,torus(id=4,od=12,$fn=32)); +// vnf_polyhedron(vnf); +// Example(2D): Projection of tilted torus using `cut=true` +// vnf = xrot(35,torus(id=4,od=12,$fn=32)); +// reg = projection(vnf,cut=true); +// region(reg); + +function projection(vnf,cut=false,eps=EPSILON) = + assert(is_vnf(vnf)) + cut ? + let( + vnf_bdy = vnf_halfspace([0,0,1,0],vnf, boundary=true), + ind = vnf_bdy[1], + pts = path2d(vnf_bdy[0][0]) + ) + ind==[] ? [] + : [for (path=ind) select(pts, path)] + : + let( + pts = vnf[0], + faces = vnf[1], + facets = [for(face=faces) + let(projface = path2d(select(pts,face))) + if (!approx(polygon_area(projface),0,eps=eps)) + projface + ] + ) + union(facets); + + + // Function: vnf_halfspace() // Synopsis: Returns the intersection of the vnf with a half space. // SynTags: VNF From 683bab1d6ae0908485690a621a43716eb9a5961e Mon Sep 17 00:00:00 2001 From: Adrian Mariano Date: Thu, 20 Jun 2024 17:29:14 -0400 Subject: [PATCH 2/3] fix typo --- vnf.scad | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vnf.scad b/vnf.scad index dfe8724..937431c 100644 --- a/vnf.scad +++ b/vnf.scad @@ -1190,7 +1190,7 @@ function _vnf_centroid(vnf,eps=EPSILON) = // Function: projection() // Synopsis: Returns projection or intersection of vnf with XY plane -// SynTags: VNF: +// SynTags: VNF // Topics: VNF Manipulation // See Also: vnf_halfspace() // Usage: From e0c6280b0941093b9f026de58c57888ca3807ad8 Mon Sep 17 00:00:00 2001 From: Adrian Mariano Date: Fri, 21 Jun 2024 17:51:18 -0400 Subject: [PATCH 3/3] cylindrical_extrude bugfix --- miscellaneous.scad | 41 ++++++++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/miscellaneous.scad b/miscellaneous.scad index a5a6f4b..2dcc35b 100644 --- a/miscellaneous.scad +++ b/miscellaneous.scad @@ -251,19 +251,30 @@ module path_extrude(path, convexity=10, clipsize=100) { // Usage: // cylindrical_extrude(ir|id=, or|od=, [size=], [convexity=], [spin=], [orient=]) 2D-CHILDREN; // Description: -// Extrudes its 2D children outwards, curved around a cylindrical shape. Uses $fn/$fa/$fs to -// control the faceting of the extrusion. +// Chops the 2D children into rectangles and extrudes each rectangle as a facet around an +// approximate cylindrical shape. Uses $fn/$fa/$fs to control the number of facets. +// By default the calculation assumes that the children occupy in the X direction one revolution of the +// cylinder of specified radius/diameter and are not more than 1000 units tall (in the Y direction). +// If the children are in fact much smaller in width then this assumption is inefficient. If the children +// are wider then they will be truncated at one revolution. To address either of these problems you can set +// the `size` parameter. Note that the specified height isn't very important: it just needs to be larger than +// the actual height of the children, which is why it defaults to 1000. If you set `size` to a scalar then +// that only changes the X value and the Y value remains at the default of 1000. +// . +// When performing the wrap, the X=0 line of the children maps to the Y- axis and the facets are centered on the Y- axis. +// This is not consistent with how cylinder() creates its facets. If `$fn` is a multiple of 4 then the facets will line +// up with a cylinder. Otherwise you must rotate a cylinder by 90 deg in the case of `$fn` even or `90-360/$fn/2` if `$fn` is odd. // Arguments: // ir = The inner radius to extrude from. // or = The outer radius to extrude to. // --- // od = The outer diameter to extrude to. // id = The inner diameter to extrude from. -// size = The [X,Y] size of the 2D children to extrude. Default: [1000,1000] +// size = If a scalar, the width of the 2D children. If a vector, the [X,Y] size of the 2D children. Default: [2*PI*or,1000] // convexity = The max number of times a line could pass though a wall. Default: 10 // spin = Amount in degrees to spin around cylindrical axis. Default: 0 // orient = The orientation of the cylinder to wrap around, given as a vector. Default: UP -// Example: +// Example: Basic example with defaults. This will run faster with large facet counts if you set `size=100` // cylindrical_extrude(or=50, ir=45) // text(text="Hello World!", size=10, halign="center", valign="center"); // Example: Spin Around the Cylindrical Axis @@ -272,29 +283,33 @@ module path_extrude(path, convexity=10, clipsize=100) { // Example: Orient to the Y Axis. // cylindrical_extrude(or=40, ir=35, orient=BACK) // text(text="Hello World!", size=10, halign="center", valign="center"); -module cylindrical_extrude(ir, or, od, id, size=1000, convexity=10, spin=0, orient=UP) { +// Example(Med): You must give a size argument for this example where the child wraps fully around the cylinder +// cylindrical_extrude(or=27, ir=25, size=300, spin=-85) +// zrot(-10)text(text="This long text wraps around the cylinder.", size=10, halign="center", valign="center"); +module cylindrical_extrude(ir, or, od, id, size, convexity=10, spin=0, orient=UP) { req_children($children); - check1 = assert(is_num(size) || is_vector(size,2)); - size = is_num(size)? [size,size] : size; ir = get_radius(r=ir,d=id); or = get_radius(r=or,d=od); check2 = assert(all_positive([ir,or]), "Must supply positive inner and outer radius or diameter"); - index_r = or; - circumf = 2 * PI * index_r; - width = min(size.x, circumf); - check3 = assert(width <= circumf, "Shape would more than completely wrap around."); + circumf = 2 * PI * or; + size = is_undef(size) ? [circumf, 1000] + : is_num(size) ? [size, 1000] + : size; + check1 = assert(is_vector(size,2) && all_positive(size), "Size must be a positive number or 2-vector"); sides = segs(or); step = circumf / sides; - steps = ceil(width / step); + steps = ceil(size.x / step); + scalefactor = sides/PI*sin(180/sides); // Scale from circle to polygon, which has shorter length attachable() { rot(from=UP, to=orient) rot(spin) { - for (i=[0:1:steps-2]) { + for (i=[0:1:steps-1]) { x = (i+0.5-steps/2) * step; zrot(360 * x / circumf) { fwd(or*cos(180/sides)) { xrot(-90) { linear_extrude(height=or-ir, scale=[ir/or,1], center=false, convexity=convexity) { yflip() + xscale(scalefactor) intersection() { left(x) children(); rect([quantup(step,pow(2,-15)),size.y]);