From c034c954875f3fb9228b826dcb0ee717b5d5c391 Mon Sep 17 00:00:00 2001 From: Revar Desmera Date: Thu, 2 Nov 2023 19:05:45 -0700 Subject: [PATCH 01/16] Added catenary_path(). --- .github/workflows/main.yml | 11 ++++++ .github/workflows/pr_merge.yml | 36 ------------------ drawing.scad | 69 ++++++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+), 36 deletions(-) delete mode 100644 .github/workflows/pr_merge.yml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8a5cab9..a5e62a3 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -2,6 +2,17 @@ name: Checks on: [pull_request] jobs: + VersionCheck: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Verify Version Bump + run: | + git fetch origin master + git diff --summary FETCH_HEAD version.scad | grep -q '^BOSL_VERSION' + Regressions: runs-on: ubuntu-latest steps: diff --git a/.github/workflows/pr_merge.yml b/.github/workflows/pr_merge.yml deleted file mode 100644 index 9ce7eca..0000000 --- a/.github/workflows/pr_merge.yml +++ /dev/null @@ -1,36 +0,0 @@ -name: VersionBump -on: - pull_request: - types: - - closed - -jobs: - VersionBump: - if: github.event.pull_request.merged == true - runs-on: ubuntu-latest - permissions: - contents: write - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Apt Update - run: sudo apt update - - - name: Bump Version - id: commit - env: - GITHUB_TOKEN: ${{ secrets.GH_PAT }} - run: | - cd $GITHUB_WORKSPACE - ./scripts/increment_version.sh - - - name: Push changes - uses: stefanzweifel/git-auto-commit-action@v5 - with: - branch: master - commit_user_email: github+actions@gmail.com - commit_message: Version Bump - - - diff --git a/drawing.scad b/drawing.scad index 3c33f7b..3e2f6f9 100644 --- a/drawing.scad +++ b/drawing.scad @@ -863,6 +863,75 @@ module arc(n, r, angle, d, cp, points, corner, width, thickness, start, wedge=fa } +// Function: catenary_path() +// Synopsis: Returns a 2D Catenary chain or arch path. +// SynTags: Path +// Topics: Paths +// See Also: circle(), stroke() +// Usage: +// path = catenary_path(width, droop=|angle=, n=); +// Description: +// Returns a 2D Catenary path, which is the path a chain held at both ends will take. +// The path will have the endpoints at `[±width/2, 0]`, and the middle of the path will droop +// towards Y- if the given droop= or angle= is positive. It will droop towards Y+ if the +// droop= or angle= is negative. You *must* specify one of droop= or angle=. +// Arguments: +// width = The straight-line distance between the endpoints of the path. +// droop = If given, specifies the height difference between the endpoints and the hanging middle of the path. If given a negative value, returns an arch *above* the Y axis. +// n = The number of points to return in the path. Default: 100 +// --- +// angle = If given, specifies the angle that the path will droop by at the endpoints. If given a negative value, returns an arch *above* the Y axis. +// Example(2D): By Droop +// stroke(catenary_path(100, droop=30)); +// Example(2D): By Angle +// stroke(catenary_path(100, angle=30)); +// Example(2D): Upwards Arch by Angle +// stroke(catenary_path(100, angle=30)); +// Example(2D): Upwards Arch by Height Delta +// stroke(catenary_path(100, droop=-30)); +// Example(2D): Specifying Vertex Count +// stroke(catenary_path(100, angle=-85, n=11), dots="dot"); +// Example: Sweeping a Catenary Path +// path = xrot(90, p=path3d(catenary_path(100, droop=20, n=41))); +// path_sweep(circle(r=1.5, $fn=24), path); +function catenary_path(width, droop, n=100, angle) = + assert(one_defined([droop, angle],"droop,angle")) + let( + sgn = is_undef(droop)? sign(angle) : sign(droop), + droop = droop==undef? undef : abs(droop), + angle = angle==undef? undef : abs(angle) + ) + assert(is_finite(width) && width>0, "Bad width= value.") + assert(is_integer(n) && n>0, "Bad n= value. Must be a positive integer.") + assert(is_undef(droop) || is_finite(droop), "Bad droop= value.") + assert(is_undef(angle) || (is_finite(angle) && angle != 0 && abs(angle) < 90), "Bad angle= value.") + let( + lup = is_undef(droop) + ? [ + for (x=[0:0.01:10]) + let( + p1 = [x-0.001, cosh(x-0.001)-1], + p2 = [x+0.001, cosh(x+0.001)-1], + delta = p2-p1, + ang = atan2(delta.y, delta.x) + ) + [ang, x] + ] + : [ for (x=[0.001:0.1:10]) [(cosh(x)-1)/x, x] ], + lval = is_undef(droop) + ? angle + : droop / (width/2), + scx = lookup(lval, lup), + droop = !is_undef(droop)? droop : + (cosh(scx)-1) * width/2 / scx, + path = [ + for (x = lerpn(-scx,scx,n)) + [x, cosh(x)-1] * width/2 / scx - [0,droop] + ], + out = sgn>0? path : yflip(p=path) + ) out; + + // Function: helix() // Synopsis: Creates a 2d spiral or 3d helical path. // SynTags: Path From 3ad7b40576e8fad9c0b90d953d8ba84a3ce1d09f Mon Sep 17 00:00:00 2001 From: Revar Desmera Date: Thu, 2 Nov 2023 19:09:42 -0700 Subject: [PATCH 02/16] Bump version. --- version.scad | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.scad b/version.scad index df6cd31..aaa9ef0 100644 --- a/version.scad +++ b/version.scad @@ -9,7 +9,7 @@ ////////////////////////////////////////////////////////////////////// -BOSL_VERSION = [2,0,654]; +BOSL_VERSION = [2,0,655]; // Section: BOSL Library Version Functions From 74c96eec79728ddba3907d167b08d427b279e6ab Mon Sep 17 00:00:00 2001 From: Revar Desmera Date: Thu, 2 Nov 2023 19:20:44 -0700 Subject: [PATCH 03/16] Version checking, try 2 --- .github/workflows/main.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a5e62a3..c371df3 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -8,10 +8,16 @@ jobs: - name: Checkout uses: actions/checkout@v3 - - name: Verify Version Bump + - name: Fetch Origin run: | + cd $GITHUB_WORKSPACE git fetch origin master - git diff --summary FETCH_HEAD version.scad | grep -q '^BOSL_VERSION' + + - name: Verify Version Change + run: | + cd $GITHUB_WORKSPACE + verstr=$(git diff --summary FETCH_HEAD version.scad | grep '^BOSL_VERSION') + [ "$verstr" != "" ] Regressions: runs-on: ubuntu-latest From 19e064fea33ef855b6650478f61b4ce379174c1f Mon Sep 17 00:00:00 2001 From: Revar Desmera Date: Thu, 2 Nov 2023 19:39:22 -0700 Subject: [PATCH 04/16] catenary_path() renamed to catenary() --- .github/workflows/main.yml | 19 ++++++------------- drawing.scad | 18 +++++++++--------- 2 files changed, 15 insertions(+), 22 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c371df3..ec3658d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -5,19 +5,12 @@ jobs: VersionCheck: runs-on: ubuntu-latest steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Fetch Origin - run: | - cd $GITHUB_WORKSPACE - git fetch origin master - - - name: Verify Version Change - run: | - cd $GITHUB_WORKSPACE - verstr=$(git diff --summary FETCH_HEAD version.scad | grep '^BOSL_VERSION') - [ "$verstr" != "" ] + - name: Check Version + uses: JJ/github-pr-contains-action@releases/v11 + with: + github-token: ${{github.token}} + diffContains: '^BOSL_VERSION' + waivedUsers: ["dependabot[bot]"] Regressions: runs-on: ubuntu-latest diff --git a/drawing.scad b/drawing.scad index 3e2f6f9..bb75ecf 100644 --- a/drawing.scad +++ b/drawing.scad @@ -863,13 +863,13 @@ module arc(n, r, angle, d, cp, points, corner, width, thickness, start, wedge=fa } -// Function: catenary_path() +// Function: catenary() // Synopsis: Returns a 2D Catenary chain or arch path. // SynTags: Path // Topics: Paths // See Also: circle(), stroke() // Usage: -// path = catenary_path(width, droop=|angle=, n=); +// path = catenary(width, droop=|angle=, n=); // Description: // Returns a 2D Catenary path, which is the path a chain held at both ends will take. // The path will have the endpoints at `[±width/2, 0]`, and the middle of the path will droop @@ -882,19 +882,19 @@ module arc(n, r, angle, d, cp, points, corner, width, thickness, start, wedge=fa // --- // angle = If given, specifies the angle that the path will droop by at the endpoints. If given a negative value, returns an arch *above* the Y axis. // Example(2D): By Droop -// stroke(catenary_path(100, droop=30)); +// stroke(catenary(100, droop=30)); // Example(2D): By Angle -// stroke(catenary_path(100, angle=30)); +// stroke(catenary(100, angle=30)); // Example(2D): Upwards Arch by Angle -// stroke(catenary_path(100, angle=30)); +// stroke(catenary(100, angle=30)); // Example(2D): Upwards Arch by Height Delta -// stroke(catenary_path(100, droop=-30)); +// stroke(catenary(100, droop=-30)); // Example(2D): Specifying Vertex Count -// stroke(catenary_path(100, angle=-85, n=11), dots="dot"); +// stroke(catenary(100, angle=-85, n=11), dots="dot"); // Example: Sweeping a Catenary Path -// path = xrot(90, p=path3d(catenary_path(100, droop=20, n=41))); +// path = xrot(90, p=path3d(catenary(100, droop=20, n=41))); // path_sweep(circle(r=1.5, $fn=24), path); -function catenary_path(width, droop, n=100, angle) = +function catenary(width, droop, n=100, angle) = assert(one_defined([droop, angle],"droop,angle")) let( sgn = is_undef(droop)? sign(angle) : sign(droop), From 21180e2c54520db465e1ecabcb25b054f8e90a46 Mon Sep 17 00:00:00 2001 From: Revar Desmera Date: Fri, 3 Nov 2023 01:06:22 -0700 Subject: [PATCH 05/16] Version checking, try 4 --- .github/workflows/main.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ec3658d..6baa57d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -10,7 +10,6 @@ jobs: with: github-token: ${{github.token}} diffContains: '^BOSL_VERSION' - waivedUsers: ["dependabot[bot]"] Regressions: runs-on: ubuntu-latest From 22e2fe35c8314a09d7511825bceda126a3ec4739 Mon Sep 17 00:00:00 2001 From: Revar Desmera Date: Sat, 4 Nov 2023 22:07:13 -0700 Subject: [PATCH 06/16] Fixed tetrahedron hinge angle. --- hinges.scad | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hinges.scad b/hinges.scad index dce4503..20400ba 100644 --- a/hinges.scad +++ b/hinges.scad @@ -415,7 +415,7 @@ module folding_hinge_mask(l, thick, layerheight=0.2, foldangle=90, hingegap=unde // Example(Med): // size=100; // apply_folding_hinges_and_snaps( -// thick=3, foldangle=54.74, +// thick=3, foldangle=acos(1/3), // hinges=[ // for (a=[0,120,240], b=[-size/2,size/4]) each [ // [200, polar_to_xy(b,a), a+90] From 0fcca5437eddcd31db5c3d3d7a3a1673b5016409 Mon Sep 17 00:00:00 2001 From: Revar Desmera Date: Sat, 4 Nov 2023 22:08:27 -0700 Subject: [PATCH 07/16] Added module catenary() --- drawing.scad | 54 +++++++++++++++++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 20 deletions(-) diff --git a/drawing.scad b/drawing.scad index bb75ecf..90ac553 100644 --- a/drawing.scad +++ b/drawing.scad @@ -881,6 +881,8 @@ module arc(n, r, angle, d, cp, points, corner, width, thickness, start, wedge=fa // n = The number of points to return in the path. Default: 100 // --- // angle = If given, specifies the angle that the path will droop by at the endpoints. If given a negative value, returns an arch *above* the Y axis. +// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). (Module only) Default: `CENTER` +// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). (Module only) Default: `0` // Example(2D): By Droop // stroke(catenary(100, droop=30)); // Example(2D): By Angle @@ -902,36 +904,48 @@ function catenary(width, droop, n=100, angle) = angle = angle==undef? undef : abs(angle) ) assert(is_finite(width) && width>0, "Bad width= value.") - assert(is_integer(n) && n>0, "Bad n= value. Must be a positive integer.") + assert(is_integer(n) && n>0, "Bad n= value. Must be a positive integer.") assert(is_undef(droop) || is_finite(droop), "Bad droop= value.") assert(is_undef(angle) || (is_finite(angle) && angle != 0 && abs(angle) < 90), "Bad angle= value.") let( - lup = is_undef(droop) - ? [ - for (x=[0:0.01:10]) - let( - p1 = [x-0.001, cosh(x-0.001)-1], - p2 = [x+0.001, cosh(x+0.001)-1], - delta = p2-p1, - ang = atan2(delta.y, delta.x) - ) - [ang, x] - ] - : [ for (x=[0.001:0.1:10]) [(cosh(x)-1)/x, x] ], - lval = is_undef(droop) - ? angle - : droop / (width/2), - scx = lookup(lval, lup), - droop = !is_undef(droop)? droop : - (cosh(scx)-1) * width/2 / scx, + catlup_fn = is_undef(droop) + ? function(x) let( + p1 = [x-0.001, cosh(x-0.001)-1], + p2 = [x+0.001, cosh(x+0.001)-1], + delta = p2-p1, + ang = atan2(delta.y, delta.x) + ) ang + : function(x) (cosh(x)-1)/x, + binsearch_fn = function(targ,x=0,inc=4) + inc < 1e-9? lookup(targ,[[catlup_fn(x),x],[catlup_fn(x+inc),x+inc]]) : + catlup_fn(x+inc) > targ? binsearch_fn(targ,x,inc/2) : + binsearch_fn(targ,x+inc,inc), + scx = is_undef(droop)? binsearch_fn(angle) : + binsearch_fn(droop / (width/2)), + sc = width/2 / scx, + droop = !is_undef(droop)? droop : (cosh(scx)-1) * sc, path = [ for (x = lerpn(-scx,scx,n)) - [x, cosh(x)-1] * width/2 / scx - [0,droop] + let( + xval = x * sc, + yval = approx(abs(x),scx)? 0 : + (cosh(x)-1) * sc - droop + ) + [xval, yval] ], out = sgn>0? path : yflip(p=path) ) out; +module catenary(width, droop, n=100, angle, anchor=CTR, spin=0) { + path = catenary(width=width, droop=droop, n=n, angle=angle); + attachable(anchor,spin, two_d=true, path=path, extent=true) { + polygon(path); + children(); + } +} + + // Function: helix() // Synopsis: Creates a 2d spiral or 3d helical path. // SynTags: Path From 3d38866692917889e5504e73676ab69c3d9e1c70 Mon Sep 17 00:00:00 2001 From: Revar Desmera Date: Fri, 17 Nov 2023 22:11:06 -0800 Subject: [PATCH 08/16] Document FPS= in examples. --- WRITING_DOCS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/WRITING_DOCS.md b/WRITING_DOCS.md index f704d21..3a97bc1 100644 --- a/WRITING_DOCS.md +++ b/WRITING_DOCS.md @@ -633,6 +633,7 @@ metadata directives: - `Anim`: Make an animation where `$t` varies from `0.0` to almost `1.0`. - `Frames=36`: Number of animation frames to make. - `FrameMS=250`: Sets the number of milliseconds per frame for spins and animation. +- `FPS=8`: Sets the number of frames per second for spins and animation. - `Small`: Make the image small sized. - `Med`: Make the image medium sized. - `Big`: Make the image big sized. From 87d8e6a3d4b54e896a6394480770605fdaacd317 Mon Sep 17 00:00:00 2001 From: Revar Desmera Date: Fri, 17 Nov 2023 22:11:59 -0800 Subject: [PATCH 09/16] Added top/bottom rounding example to prismoid() --- shapes3d.scad | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/shapes3d.scad b/shapes3d.scad index b56178e..d3af6c6 100644 --- a/shapes3d.scad +++ b/shapes3d.scad @@ -574,7 +574,7 @@ function cuboid( // Synopsis: Creates a rectangular prismoid shape with optional roundovers and chamfering. // SynTags: Geom, VNF // Topics: Shapes (3D), Attachable, VNF Generators -// See Also: cuboid(), rounded_prism(), trapezoid() +// See Also: cuboid(), rounded_prism(), trapezoid(), edge_profile() // Usage: // prismoid(size1, size2, [h|l|height|length], [shift], [xang=], [yang=], ...) [ATTACHMENTS]; // Usage: Chamfered and/or Rounded Prismoids @@ -659,6 +659,13 @@ function cuboid( // chamfer1=[0,5,0,10], chamfer2=[5,0,10,0], // rounding1=[5,0,10,0], rounding2=[0,5,0,10] // ); +// Example: How to Round a Top or Bottom Edge +// diff() +// prismoid([50,30], [30,20], shift=[3,6], h=15, rounding=[5,0,5,0]) { +// edge_profile([TOP+RIGHT, BOT+FRONT], excess=10, convexity=20) { +// mask2d_roundover(h=5,mask_angle=$edge_angle); +// } +// } // Example(Spin,VPD=160,VPT=[0,0,10]): Standard Connectors // prismoid(size1=[50,30], size2=[20,20], h=20, shift=[15,5]) // show_anchors(); From a03d47d5cc3b97c4de2da5abdaff94f17e6947ae Mon Sep 17 00:00:00 2001 From: Revar Desmera Date: Fri, 17 Nov 2023 22:12:47 -0800 Subject: [PATCH 10/16] Refactored 2D masks to support mask_angle and flat_top --- masks2d.scad | 323 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 228 insertions(+), 95 deletions(-) diff --git a/masks2d.scad b/masks2d.scad index 81071a6..f36a5d5 100644 --- a/masks2d.scad +++ b/masks2d.scad @@ -20,14 +20,16 @@ // Topics: Shapes (2D), Paths (2D), Path Generators, Attachable, Masks (2D) // See Also: corner_profile(), edge_profile(), face_profile(), fillet() // Usage: As module -// mask2d_roundover(r|d=, [inset], [mask_angle], [excess]) [ATTACHMENTS]; +// mask2d_roundover(r|d=|h=|cut=|joint=, [inset], [mask_angle], [excess], [flat_top=]) [ATTACHMENTS]; // Usage: As function -// path = mask2d_roundover(r|d=, [inset], [mask_angle], [excess]); +// path = mask2d_roundover(r|d=|h=|cut=|joint=, [inset], [mask_angle], [excess], [flat_top=]); // Description: // Creates a 2D roundover/bead mask shape that is useful for extruding into a 3D mask for an edge. // Conversely, you can use that same extruded shape to make an interior fillet between two walls. // As a 2D mask, this is designed to be differenced away from the edge of a shape that is in the first (X+Y+) quadrant. // If called as a function, this just returns a 2D path of the outline of the mask shape. +// The roundover can be specified by radius, diameter, height, cut, or joint length. +// ![Types of Roundovers](images/rounding/section-types-of-roundovers_fig1.png) // Arguments: // r = Radius of the roundover. // inset = Optional bead inset size. Default: 0 @@ -35,24 +37,34 @@ // excess = Extra amount of mask shape to creates on the X- and Y- sides of the shape. Default: 0.01 // --- // d = Diameter of the roundover. +// h = Mask height. Given instead of r or d when you want a consistent mask height, no matter what the mask angle. +// cut = Cut distance. IE: How much of the corner to cut off. See [Types of Roundovers](rounding.scad#section-types-of-roundovers). +// joint = Joint distance. IE: How far from the edge the roundover should start. See [Types of Roundovers](rounding.scad#section-types-of-roundovers). +// flat_top = If true, the top inset of the mask will be horizontal instead of angled by the mask_angle. Default: true. // anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER` // spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` // Side Effects: // Tags the children with "remove" (and hence sets `$tag`) if no tag is already set. // -// Example(2D): 2D Roundover Mask +// Example(2D): 2D Roundover Mask by Radius // mask2d_roundover(r=10); // Example(2D): 2D Bead Mask // mask2d_roundover(r=10,inset=2); +// Example(2D): 2D Bead Mask by Height +// mask2d_roundover(h=10,inset=2); // Example(2D): 2D Bead Mask for a Non-Right Edge. // mask2d_roundover(r=10, inset=2, mask_angle=75); +// Example(2D): Disabling flat_top= +// mask2d_roundover(r=10, inset=2, flat_top=false, mask_angle=75); +// Example(2D): 2D Angled Bead Mask by Joint Length +// mask2d_roundover(joint=10, inset=2, mask_angle=75); // Example(2D): Increasing the Excess // mask2d_roundover(r=10, inset=2, mask_angle=75, excess=2); // Example: Masking by Edge Attachment // diff() // cube([50,60,70],center=true) // edge_profile([TOP,"Z"],except=[BACK,TOP+LEFT]) -// mask2d_roundover(r=10, inset=2); +// mask2d_roundover(h=12, inset=2); // Example: Making an interior fillet // %render() difference() { // move(-[5,0,5]) cube(30, anchor=BOT+LEFT); @@ -61,8 +73,8 @@ // xrot(90) // linear_extrude(height=30, center=true) // mask2d_roundover(r=10); -module mask2d_roundover(r, inset=0, mask_angle=90, excess=0.01, d, anchor=CENTER,spin=0) { - path = mask2d_roundover(r=r, d=d, inset=inset, mask_angle=mask_angle, excess=excess); +module mask2d_roundover(r, inset=0, mask_angle=90, excess=0.01, flat_top=true, d, h, cut, joint, anchor=CENTER,spin=0) { + path = mask2d_roundover(r=r, d=d, h=h, cut=cut, joint=joint, inset=inset, flat_top=flat_top, mask_angle=mask_angle, excess=excess); default_tag("remove") { attachable(anchor,spin, two_d=true, path=path) { polygon(path); @@ -71,40 +83,127 @@ module mask2d_roundover(r, inset=0, mask_angle=90, excess=0.01, d, anchor=CENTER } } -function mask2d_roundover(r, inset=0, mask_angle=90, excess=0.01, d, anchor=CENTER, spin=0) = - assert(is_finite(r)||is_finite(d)) +function mask2d_roundover(r, inset=0, mask_angle=90, excess=0.01, flat_top=true, d, h, cut, joint, anchor=CENTER, spin=0) = + assert(one_defined([r,d,h,cut,joint],"r,d,h,cut,joint")) + assert(is_undef(r) || is_finite(r)) + assert(is_undef(d) || is_finite(d)) + assert(is_undef(h) || is_finite(h)) + assert(is_undef(cut) || is_finite(cut)) + assert(is_undef(joint) || is_finite(joint)) assert(is_finite(excess)) assert(is_finite(mask_angle) && mask_angle>0 && mask_angle<180) assert(is_finite(inset)||(is_vector(inset)&&len(inset)==2)) + assert(is_bool(flat_top)) let( inset = is_list(inset)? inset : [inset,inset], - r = get_radius(r=r,d=d,dflt=1), - avec = polar_to_xy(inset.x,mask_angle-90), - line1 = [[0,inset.y], [100,inset.y]], - line2 = [avec, polar_to_xy(100,mask_angle)+avec], - corner = line_intersection(line1,line2), - arcpts = arc(r=r, corner=[line2.y, corner, line1.y]), - ipath = [ - arcpts[0] + polar_to_xy(inset.x+excess, mask_angle+90), + r = is_finite(joint)? adj_ang_to_opp(joint, mask_angle/2) : + is_finite(h)? ( + mask_angle==90? h-inset.y : + mask_angle < 90 ? adj_ang_to_opp(opp_ang_to_hyp(h-inset.y,mask_angle), mask_angle/2) : + adj_ang_to_opp(adj_ang_to_hyp(h-inset.y,mask_angle-90), mask_angle/2) + ) : + is_finite(cut) + ? let( + o = adj_ang_to_opp(cut, mask_angle/2), + h = adj_ang_to_hyp(cut, mask_angle/2) + ) adj_ang_to_opp(o+h, mask_angle/2) + : get_radius(r=r,d=d,dflt=undef), + pts = _inset_isect(inset,mask_angle,flat_top,excess,-r), + arcpts = arc(r=r, corner=[pts[4],pts[5],pts[0]]), + path = [ + each select(pts, 1, 3), each arcpts, - last(arcpts) + polar_to_xy(inset.y+excess, -90), - [0,-excess], - [-excess,-excess], - [-excess,0] - ], - path = deduplicate(ipath) + ] ) reorient(anchor,spin, two_d=true, path=path, extent=false, p=path); +function _inset_isect(inset,mask_angle,flat_top,excess,r,size) = + assert(one_defined([size,r],"size,r")) + let( + lft_n = polar_to_xy(1, mask_angle-90), + rgt_n = [1,0], + top_n = flat_top? [1,0] : lft_n, + bot_n = [0,1], + + line_lft = [[0,0], polar_to_xy(100, mask_angle)], + line_bot = [[0,0], [100,0]], + ex_line_lft = move(-excess*lft_n, p=line_lft), + ex_line_bot = move(-excess*bot_n, p=line_bot), + in_line_lft = move(inset.x*top_n, p=line_lft), + in_line_bot = move(inset.y*bot_n, p=line_bot), + + ex_pt = line_intersection(ex_line_lft, ex_line_bot), + in_pt = line_intersection(in_line_lft, in_line_bot), + + pos_r = r==undef || r >= 0, + r = r==undef? undef : abs(r), + x = is_undef(size)? r : size.x, + y = is_undef(size)? r : size.y, + base_pt = !flat_top && is_num(r)? in_pt : + in_pt + [y*cos(mask_angle)/sin(mask_angle), 0], + line_top = !flat_top && is_num(r) + ? let( pt = in_pt + polar_to_xy(r/(pos_r?1:tan(mask_angle/2)), mask_angle) ) + [pt, pt - top_n] + : [base_pt + [0,y], base_pt + [0,y] - top_n], + line_rgt = !flat_top && is_num(r) + ? pos_r + ? [in_pt + [r,0], in_pt + [r,1]] + : [in_pt + [r/tan(mask_angle/2),0], in_pt + [r/tan(mask_angle/2),1]] + : [base_pt + [x,0], base_pt + [x,1]], + top_pt = line_intersection(ex_line_lft, line_top), + + path = is_vector(size)? [ + // All size based + base_pt + [size.x,0], + [base_pt.x + size.x, -excess], + ex_pt, + top_pt, + base_pt + [0,size.y], + base_pt, + base_pt + size, + ] : flat_top? [ + // flat_top radius + base_pt + [r,0], + [base_pt.x + r, -excess], + ex_pt, + top_pt, + base_pt + [0,r], + base_pt, + base_pt + [r,r], + ] : let( + cp_pt = line_intersection(line_rgt, line_top) + ) pos_r? [ + // non-flat_top radius from inside + in_pt + [r,0], + [in_pt.x + r, -excess], + ex_pt, + top_pt, + in_pt + polar_to_xy(r,mask_angle), + in_pt, + cp_pt, + ] : [ + // non-flat_top radius from outside + line_rgt[0], + [cp_pt.x, -excess], + ex_pt, + top_pt, + in_pt + polar_to_xy(r/tan(mask_angle/2),mask_angle), + in_pt, + cp_pt, + ] + ) path; + + + // Function&Module: mask2d_cove() // Synopsis: Creates a 2D cove (quarter-round) mask shape. // SynTags: Geom, Path // Topics: Shapes (2D), Paths (2D), Path Generators, Attachable, Masks (2D) // See Also: corner_profile(), edge_profile(), face_profile() // Usage: As module -// mask2d_cove(r|d=, [inset], [mask_angle], [excess]) [ATTACHMENTS]; +// mask2d_cove(r|d=|h=, [inset], [mask_angle], [excess], [flat_top=]) [ATTACHMENTS]; // Usage: As function -// path = mask2d_cove(r|d=, [inset], [mask_angle], [excess]); +// path = mask2d_cove(r|d=|h=, [inset], [mask_angle], [excess], [flat_top=]); // Description: // Creates a 2D cove mask shape that is useful for extruding into a 3D mask for an edge. // Conversely, you can use that same extruded shape to make an interior rounded shelf decoration between two walls. @@ -117,23 +216,31 @@ function mask2d_roundover(r, inset=0, mask_angle=90, excess=0.01, d, anchor=CENT // excess = Extra amount of mask shape to creates on the X- and Y- sides of the shape. Default: 0.01 // --- // d = Diameter of the cove. +// h = Mask height. Given instead of r or d when you want a consistent mask height, no matter what the mask angle. +// flat_top = If true, the top inset of the mask will be horizontal instead of angled by the mask_angle. Default: true. // anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER` // spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` // Side Effects: // Tags the children with "remove" (and hence sets `$tag`) if no tag is already set. -// Example(2D): 2D Cove Mask +// Example(2D): 2D Cove Mask by Radius // mask2d_cove(r=10); // Example(2D): 2D Inset Cove Mask // mask2d_cove(r=10,inset=3); +// Example(2D): 2D Inset Cove Mask by Height +// mask2d_cove(h=10,inset=2); // Example(2D): 2D Inset Cove Mask for a Non-Right Edge // mask2d_cove(r=10,inset=3,mask_angle=75); +// Example(2D): Disabling flat_top= +// mask2d_cove(r=10, inset=3, flat_top=false, mask_angle=75); +// Example(2D): 2D Angled Inset Cove Mask by Joint Length +// mask2d_cove(joint=10, inset=3, mask_angle=75); // Example(2D): Increasing the Excess // mask2d_cove(r=10,inset=3,mask_angle=75, excess=2); // Example: Masking by Edge Attachment // diff() // cube([50,60,70],center=true) // edge_profile([TOP,"Z"],except=[BACK,TOP+LEFT]) -// mask2d_cove(r=10, inset=2); +// mask2d_cove(h=10, inset=3); // Example: Making an interior rounded shelf // %render() difference() { // move(-[5,0,5]) cube(30, anchor=BOT+LEFT); @@ -142,8 +249,8 @@ function mask2d_roundover(r, inset=0, mask_angle=90, excess=0.01, d, anchor=CENT // xrot(90) // linear_extrude(height=30, center=true) // mask2d_cove(r=5, inset=5); -module mask2d_cove(r, inset=0, mask_angle=90, excess=0.01, d, anchor=CENTER, spin=0) { - path = mask2d_cove(r=r, d=d, inset=inset, mask_angle=mask_angle, excess=excess); +module mask2d_cove(r, inset=0, mask_angle=90, excess=0.01, flat_top=true, d, h, anchor=CENTER, spin=0) { + path = mask2d_cove(r=r, d=d, h=h, flat_top=flat_top, inset=inset, mask_angle=mask_angle, excess=excess); default_tag("remove") { attachable(anchor,spin, two_d=true, path=path) { polygon(path); @@ -152,26 +259,27 @@ module mask2d_cove(r, inset=0, mask_angle=90, excess=0.01, d, anchor=CENTER, spi } } -function mask2d_cove(r, inset=0, mask_angle=90, excess=0.01, d, anchor=CENTER, spin=0) = - assert(is_finite(r)||is_finite(d)) +function mask2d_cove(r, inset=0, mask_angle=90, excess=0.01, flat_top=true, d, h, anchor=CENTER, spin=0) = + assert(one_defined([r,d,h],"r,d,h")) + assert(is_undef(r) || is_finite(r)) + assert(is_undef(d) || is_finite(d)) + assert(is_undef(h) || is_finite(h)) assert(is_finite(mask_angle) && mask_angle>0 && mask_angle<180) assert(is_finite(excess)) assert(is_finite(inset)||(is_vector(inset)&&len(inset)==2)) + assert(is_bool(flat_top)) let( inset = is_list(inset)? inset : [inset,inset], - r = get_radius(r=r,d=d,dflt=1), - avec = polar_to_xy(inset.x,mask_angle-90), - line1 = [[0,inset.y], [100,inset.y]], - line2 = [avec, polar_to_xy(100,mask_angle)+avec], - corner = line_intersection(line1,line2), - arcpts = arc(r=r, cp=corner, start=mask_angle, angle=-mask_angle), + r = is_finite(h)? ( + mask_angle==90? h-inset.y : + mask_angle < 90 ? adj_ang_to_opp(opp_ang_to_hyp(h-inset.y,mask_angle), mask_angle/2) : + adj_ang_to_opp(adj_ang_to_hyp(h-inset.y,mask_angle-90), mask_angle/2) + ) : get_radius(r=r,d=d,dflt=undef), + pts = _inset_isect(inset,mask_angle,flat_top,excess,r), + arcpts = arc(r=r, corner=[pts[4],pts[6],pts[0]]), ipath = [ - arcpts[0] + polar_to_xy(inset.x+excess, mask_angle+90), + each select(pts, 1, 3), each arcpts, - last(arcpts) + polar_to_xy(inset.y+excess, -90), - [0,-excess], - [-excess,-excess], - [-excess,0] ], path = deduplicate(ipath) ) reorient(anchor,spin, two_d=true, path=path, p=path); @@ -201,10 +309,12 @@ function mask2d_cove(r, inset=0, mask_angle=90, excess=0.01, d, anchor=CENTER, s // edge = The length of the edge of the chamfer. // angle = The angle of the chamfer edge, away from vertical. Default: 45. // inset = Optional amount to inset code from corner. Default: 0 +// mask_angle = Number of degrees in the corner angle to mask. Default: 90 // excess = Extra amount of mask shape to creates on the X- and Y- sides of the shape. Default: 0.01 // --- // x = The width of the chamfer. // y = The height of the chamfer. +// flat_top = If true, the top inset of the mask will be horizontal instead of angled by the mask_angle. Default: true. // anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER` // spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` // Side Effects: @@ -230,8 +340,8 @@ function mask2d_cove(r, inset=0, mask_angle=90, excess=0.01, d, anchor=CENTER, s // xrot(90) // linear_extrude(height=30, center=true) // mask2d_chamfer(edge=10); -module mask2d_chamfer(edge, angle=45, inset=0, excess=0.01, x, y, anchor=CENTER,spin=0) { - path = mask2d_chamfer(x=x, y=y, edge=edge, angle=angle, excess=excess, inset=inset); +module mask2d_chamfer(edge, angle=45, inset=0, excess=0.01, mask_angle=90, flat_top=true, x, y, anchor=CENTER,spin=0) { + path = mask2d_chamfer(x=x, y=y, edge=edge, angle=angle, excess=excess, inset=inset, mask_angle=mask_angle, flat_top=flat_top); default_tag("remove") { attachable(anchor,spin, two_d=true, path=path, extent=true) { polygon(path); @@ -240,10 +350,11 @@ module mask2d_chamfer(edge, angle=45, inset=0, excess=0.01, x, y, anchor=CENTER, } } -function mask2d_chamfer(edge, angle=45, inset=0, excess=0.01, x, y, anchor=CENTER,spin=0) = +function mask2d_chamfer(edge, angle=45, inset=0, excess=0.01, mask_angle=90, flat_top=true, x, y, anchor=CENTER,spin=0) = let(dummy=one_defined([x,y,edge],["x","y","edge"])) assert(is_finite(angle)) assert(is_finite(excess)) + assert(is_finite(mask_angle) && mask_angle>0 && mask_angle<180) assert(is_finite(inset)||(is_vector(inset)&&len(inset)==2)) let( inset = is_list(inset)? inset : [inset,inset], @@ -251,12 +362,10 @@ function mask2d_chamfer(edge, angle=45, inset=0, excess=0.01, x, y, anchor=CENTE is_def(y)? adj_ang_to_opp(adj=y,ang=angle) : hyp_ang_to_opp(hyp=edge,ang=angle), y = opp_ang_to_adj(opp=x,ang=angle), + pts = _inset_isect(inset,mask_angle,flat_top,excess,size=[x,y]), path = [ - [x+inset.x, -excess], - [-excess, -excess], - [-excess, y+inset.y], - [inset.x, y+inset.y], - [x+inset.x, inset.y] + each select(pts, 1, 4), + pts[0], ] ) reorient(anchor,spin, two_d=true, path=path, extent=true, p=path); @@ -267,9 +376,9 @@ function mask2d_chamfer(edge, angle=45, inset=0, excess=0.01, x, y, anchor=CENTE // Topics: Shapes (2D), Paths (2D), Path Generators, Attachable, Masks (2D) // See Also: corner_profile(), edge_profile(), face_profile() // Usage: As Module -// mask2d_rabbet(size, [mask_angle], [excess]) [ATTACHMENTS]; +// mask2d_rabbet(size, [mask_angle], [excess], [flat_top=]) [ATTACHMENTS]; // Usage: As Function -// path = mask2d_rabbet(size, [mask_angle], [excess]); +// path = mask2d_rabbet(size, [mask_angle], [excess], [flat_top=]); // Description: // Creates a 2D rabbet mask shape that is useful for extruding into a 3D mask for an edge. // Conversely, you can use that same extruded shape to make an interior shelf decoration between two walls. @@ -277,9 +386,11 @@ function mask2d_chamfer(edge, angle=45, inset=0, excess=0.01, x, y, anchor=CENTE // If called as a function, this just returns a 2D path of the outline of the mask shape. // Arguments: // size = The size of the rabbet, either as a scalar or an [X,Y] list. +// inset = Optional bead inset size. Default: 0 // mask_angle = Number of degrees in the corner angle to mask. Default: 90 // excess = Extra amount of mask shape to creates on the X- and Y- sides of the shape. Default: 0.01 // --- +// flat_top = If true, the top inset of the mask will be horizontal instead of angled by the mask_angle. Default: true. // anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER` // spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` // Side Effects: @@ -289,7 +400,9 @@ function mask2d_chamfer(edge, angle=45, inset=0, excess=0.01, x, y, anchor=CENTE // Example(2D): 2D Asymmetrical Rabbet Mask // mask2d_rabbet(size=[5,10]); // Example(2D): 2D Mask for a Non-Right Edge -// mask2d_rabbet(size=10,mask_angle=75); +// mask2d_rabbet(size=10, mask_angle=75); +// Example(2D): Disabling flat_top= +// mask2d_rabbet(size=10, flat_top=false, mask_angle=75); // Example: Masking by Edge Attachment // diff() // cube([50,60,70],center=true) @@ -303,8 +416,8 @@ function mask2d_chamfer(edge, angle=45, inset=0, excess=0.01, x, y, anchor=CENTE // xrot(90) // linear_extrude(height=30, center=true) // mask2d_rabbet(size=[5,10]); -module mask2d_rabbet(size, mask_angle=90, excess=0.01, anchor=CTR, spin=0) { - path = mask2d_rabbet(size=size, mask_angle=mask_angle, excess=excess); +module mask2d_rabbet(size, inset=[0,0], mask_angle=90, excess=0.01, flat_top=true, anchor=CTR, spin=0) { + path = mask2d_rabbet(size=size, inset=inset, mask_angle=mask_angle, excess=excess, flat_top=flat_top); default_tag("remove") { attachable(anchor,spin, two_d=true, path=path, extent=false) { polygon(path); @@ -313,23 +426,18 @@ module mask2d_rabbet(size, mask_angle=90, excess=0.01, anchor=CTR, spin=0) { } } -function mask2d_rabbet(size, mask_angle=90, excess=0.01, anchor=CTR, spin=0) = +function mask2d_rabbet(size, inset=[0,0], mask_angle=90, excess=0.01, flat_top=true, anchor=CTR, spin=0) = assert(is_finite(size)||(is_vector(size)&&len(size)==2)) assert(is_finite(mask_angle) && mask_angle>0 && mask_angle<180) assert(is_finite(excess)) + assert(is_bool(flat_top)) let( size = is_list(size)? size : [size,size], - avec = polar_to_xy(size.x,mask_angle-90), - line1 = [[0,size.y], [100,size.y]], - line2 = [avec, polar_to_xy(100,mask_angle)+avec], - cp = line_intersection(line1,line2), + pts = _inset_isect(inset,mask_angle,flat_top,excess,size=size), path = [ - cp + polar_to_xy(size.x+excess, mask_angle+90), - cp, - cp + polar_to_xy(size.y+excess, -90), - [0,-excess], - [-excess,-excess], - [-excess,0] + each select(pts, 1, 4), + pts[6], + pts[0], ] ) reorient(anchor,spin, two_d=true, path=path, extent=false, p=path); @@ -353,12 +461,14 @@ function mask2d_rabbet(size, mask_angle=90, excess=0.01, anchor=CTR, spin=0) = // Arguments: // edge = The length of the edge of the dovetail. // angle = The angle of the chamfer edge, away from vertical. Default: 30. -// inset = Optional amount to inset code from corner. Default: 0 // shelf = The extra height to add to the inside corner of the dovetail. Default: 0 +// inset = Optional amount to inset code from corner. Default: 0 +// mask_angle = Number of degrees in the corner angle to mask. Default: 90 // excess = Extra amount of mask shape to creates on the X- and Y- sides of the shape. Default: 0.01 // --- // x = The width of the dovetail. // y = The height of the dovetail. +// flat_top = If true, the top inset of the mask will be horizontal instead of angled by the mask_angle. Default: true. // anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER` // spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` // Side Effects: @@ -384,8 +494,8 @@ function mask2d_rabbet(size, mask_angle=90, excess=0.01, anchor=CTR, spin=0) = // xrot(90) // linear_extrude(height=30, center=true) // mask2d_dovetail(x=10); -module mask2d_dovetail(edge, angle=30, inset=0, shelf=0, excess=0.01, x, y, anchor=CENTER, spin=0) { - path = mask2d_dovetail(x=x, y=y, edge=edge, angle=angle, inset=inset, shelf=shelf, excess=excess); +module mask2d_dovetail(edge, angle=30, shelf=0, inset=0, mask_angle=90, excess=0.01, flat_top=true, x, y, anchor=CENTER, spin=0) { + path = mask2d_dovetail(x=x, y=y, edge=edge, angle=angle, inset=inset, shelf=shelf, excess=excess, flat_top=flat_top, mask_angle=mask_angle); default_tag("remove") { attachable(anchor,spin, two_d=true, path=path) { polygon(path); @@ -394,7 +504,7 @@ module mask2d_dovetail(edge, angle=30, inset=0, shelf=0, excess=0.01, x, y, anch } } -function mask2d_dovetail(edge, angle=30, inset=0, shelf=0, excess=0.01, x, y, anchor=CENTER, spin=0) = +function mask2d_dovetail(edge, angle=30, shelf=0, inset=0, mask_angle=90, excess=0.01, flat_top=true, x, y, anchor=CENTER, spin=0) = assert(num_defined([x,y,edge])==1) assert(is_finite(first_defined([x,y,edge]))) assert(is_finite(angle)) @@ -406,13 +516,13 @@ function mask2d_dovetail(edge, angle=30, inset=0, shelf=0, excess=0.01, x, y, an !is_undef(y)? adj_ang_to_opp(adj=y,ang=angle) : hyp_ang_to_opp(hyp=edge,ang=angle), y = opp_ang_to_adj(opp=x,ang=angle), + pts = _inset_isect(inset,mask_angle,flat_top,excess,size=[x,y+shelf]), path = [ - [inset.x,0], - [-excess, 0], - [-excess, y+inset.y+shelf], - inset+[x,y+shelf], - inset+[x,y], - inset + [max(0,pts[5].x),-excess], + each select(pts, 2, 4), + pts[6], + pts[6]-[0,shelf], + pts[5], ] ) reorient(anchor,spin, two_d=true, path=path, p=path); @@ -432,13 +542,20 @@ function mask2d_dovetail(edge, angle=30, inset=0, shelf=0, excess=0.01, x, y, an // As a 2D mask, this is designed to be differenced away from the edge of a shape that is in the first (X+Y+) quadrant. // If called as a function, this just returns a 2D path of the outline of the mask shape. // This is particularly useful to make partially rounded bottoms, that don't need support to print. +// The roundover can be specified by radius, diameter, height, cut, or joint length. +// ![Types of Roundovers](images/rounding/section-types-of-roundovers_fig1.png) // Arguments: // r = Radius of the rounding. // angle = The maximum angle from vertical. +// inset = Optional bead inset size. Default: 0 // mask_angle = Number of degrees in the corner angle to mask. Default: 90 // excess = Extra amount of mask shape to creates on the X- and Y- sides of the shape. Default: 0.01 // --- // d = Diameter of the rounding. +// h = Mask height. Given instead of r or d when you want a consistent mask height, no matter what the mask angle. +// cut = Cut distance. IE: How much of the corner to cut off. See [Types of Roundovers](rounding.scad#section-types-of-roundovers). +// joint = Joint distance. IE: How far from the edge the roundover should start. See [Types of Roundovers](rounding.scad#section-types-of-roundovers). +// flat_top = If true, the top inset of the mask will be horizontal instead of angled by the mask_angle. Default: true. // anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER` // spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` // Side Effects: @@ -464,34 +581,50 @@ function mask2d_dovetail(edge, angle=30, inset=0, shelf=0, excess=0.01, x, y, an // xrot(90) // linear_extrude(height=30, center=true) // mask2d_teardrop(r=10); -function mask2d_teardrop(r, angle=45, mask_angle=90, excess=0.01, d, anchor=CENTER, spin=0) = +function mask2d_teardrop(r, angle=45, inset=[0,0], mask_angle=90, excess=0.01, flat_top=true, d, h, cut, joint, anchor=CENTER, spin=0) = + assert(one_defined([r,d,h,cut,joint],"r,d,h,cut,joint")) + assert(is_undef(r) || is_finite(r)) + assert(is_undef(d) || is_finite(d)) + assert(is_undef(h) || is_finite(h)) + assert(is_undef(cut) || is_finite(cut)) + assert(is_undef(joint) || is_finite(joint)) assert(is_finite(angle)) assert(angle>0 && angle<90) assert(is_finite(mask_angle) && mask_angle>0 && mask_angle<180) assert(is_finite(excess)) let( - r = get_radius(r=r, d=d, dflt=1), - avec = polar_to_xy(r,mask_angle-90), - line1 = [[0,r], [100,r]], - line2 = [avec, polar_to_xy(100,mask_angle)+avec], - cp = line_intersection(line1,line2), - tp = cp + polar_to_xy(r,180+angle), - bp = [tp.x+adj_ang_to_opp(tp.y,angle), 0], - arcpts = arc(r=r, cp=cp, angle=[mask_angle+90,180+angle]), - ipath = [ - arcpts[0] + polar_to_xy(excess, mask_angle+90), - each arcpts, - bp, - bp + [0,-excess], - [0,-excess], - [-excess,-excess], - [-excess,0] + r = is_finite(joint)? adj_ang_to_opp(joint, mask_angle/2) : + is_finite(h)? ( + mask_angle==90? h : + mask_angle < 90 ? adj_ang_to_opp(opp_ang_to_hyp(h,mask_angle), mask_angle/2) : + adj_ang_to_opp(adj_ang_to_hyp(h,mask_angle-90), mask_angle/2) + ) : + is_finite(cut) + ? let( + o = adj_ang_to_opp(cut, mask_angle/2), + h = adj_ang_to_hyp(cut, mask_angle/2) + ) adj_ang_to_opp(o+h, mask_angle/2) + : get_radius(r=r,d=d,dflt=undef), + pts = _inset_isect(inset,mask_angle,flat_top,excess,-r), + arcpts = arc(r=r, corner=[pts[4],pts[5],pts[0]]), + arcpts2 = [ + for (i = idx(arcpts)) + if(i==0 || v_theta(arcpts[i]-arcpts[i-1]) <= angle-90) + arcpts[i] ], - path = deduplicate(ipath) + line1 = [last(arcpts2), last(arcpts2) + polar_to_xy(1, angle-90)], + line2 = [[0,inset.y], [100,inset.y]], + ipt = line_intersection(line1,line2), + path = [ + [ipt.x, -excess], + each select(pts, 2, 3), + each arcpts2, + ipt, + ] ) reorient(anchor,spin, two_d=true, path=path, p=path); -module mask2d_teardrop(r, angle=45, mask_angle=90, excess=0.01, d, anchor=CENTER, spin=0) { - path = mask2d_teardrop(r=r, d=d, angle=angle, mask_angle=mask_angle, excess=excess); +module mask2d_teardrop(r, angle=45, mask_angle=90, excess=0.01, flat_top=true, d, h, cut, joint, anchor=CENTER, spin=0) { + path = mask2d_teardrop(r=r, d=d, h=h, cut=cut, joint=joint, angle=angle, mask_angle=mask_angle, excess=excess); default_tag("remove") { attachable(anchor,spin, two_d=true, path=path) { polygon(path); From 18e2ca789591b43592a2049e5af38ff3ff184195 Mon Sep 17 00:00:00 2001 From: Revar Desmera Date: Sat, 18 Nov 2023 00:11:39 -0800 Subject: [PATCH 11/16] Mask Regressions Fixes. --- scripts/check_for_tabs.sh | 2 +- tests/test_masks2d.scad | 64 +++++++++++++++++++-------------------- 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/scripts/check_for_tabs.sh b/scripts/check_for_tabs.sh index a075703..21b86d1 100755 --- a/scripts/check_for_tabs.sh +++ b/scripts/check_for_tabs.sh @@ -1,6 +1,6 @@ #!/bin/bash -if grep -H -n -P '\t' *.scad ; then +if grep -H -n '\t' *.scad ; then echo "Tabs found in source code." 2>&1 exit 1 fi diff --git a/tests/test_masks2d.scad b/tests/test_masks2d.scad index ca4ccd4..2925349 100644 --- a/tests/test_masks2d.scad +++ b/tests/test_masks2d.scad @@ -19,60 +19,60 @@ test_mask2d_chamfer(); module test_mask2d_cove() { $fn = 24; - assert_approx(mask2d_cove(r=10),[[-0.01,10],[0,10],[3.09016994375,9.51056516295],[5.87785252292,8.09016994375],[8.09016994375,5.87785252292],[9.51056516295,3.09016994375],[10,0],[10,-0.01],[0,-0.01],[-0.01,-0.01],[-0.01,0]]); - assert_approx(mask2d_cove(d=20),[[-0.01,10],[0,10],[3.09016994375,9.51056516295],[5.87785252292,8.09016994375],[8.09016994375,5.87785252292],[9.51056516295,3.09016994375],[10,0],[10,-0.01],[0,-0.01],[-0.01,-0.01],[-0.01,0]]); - assert_approx(mask2d_cove(r=10,inset=1),[[-0.01,11],[1,11],[4.09016994375,10.510565163],[6.87785252292,9.09016994375],[9.09016994375,6.87785252292],[10.510565163,4.09016994375],[11,1],[11,-0.01],[0,-0.01],[-0.01,-0.01],[-0.01,0]]); - assert_approx(mask2d_cove(d=20,inset=1),[[-0.01,11],[1,11],[4.09016994375,10.510565163],[6.87785252292,9.09016994375],[9.09016994375,6.87785252292],[10.510565163,4.09016994375],[11,1],[11,-0.01],[0,-0.01],[-0.01,-0.01],[-0.01,0]]); - assert_approx(mask2d_cove(r=10,inset=1,excess=1),[[-1,11],[1,11],[4.09016994375,10.510565163],[6.87785252292,9.09016994375],[9.09016994375,6.87785252292],[10.510565163,4.09016994375],[11,1],[11,-1],[0,-1],[-1,-1],[-1,0]]); - assert_approx(mask2d_cove(d=20,inset=1,excess=1),[[-1,11],[1,11],[4.09016994375,10.510565163],[6.87785252292,9.09016994375],[9.09016994375,6.87785252292],[10.510565163,4.09016994375],[11,1],[11,-1],[0,-1],[-1,-1],[-1,0]]); + assert_approx(mask2d_cove(r=10),[[10,-0.01],[-0.01,-0.01],[-0.01,10],[1.7763568394e-15,10],[3.09016994375,9.51056516295],[5.87785252292,8.09016994375],[8.09016994375,5.87785252292],[9.51056516295,3.09016994375],[10,1.7763568394e-15]]); + assert_approx(mask2d_cove(d=20),[[10,-0.01],[-0.01,-0.01],[-0.01,10],[1.7763568394e-15,10],[3.09016994375,9.51056516295],[5.87785252292,8.09016994375],[8.09016994375,5.87785252292],[9.51056516295,3.09016994375],[10,1.7763568394e-15]]); + assert_approx(mask2d_cove(r=10,inset=1),[[11,-0.01],[-0.01,-0.01],[-0.01,11],[1,11],[4.09016994375,10.510565163],[6.87785252292,9.09016994375],[9.09016994375,6.87785252292],[10.510565163,4.09016994375],[11,1]]); + assert_approx(mask2d_cove(d=20,inset=1),[[11,-0.01],[-0.01,-0.01],[-0.01,11],[1,11],[4.09016994375,10.510565163],[6.87785252292,9.09016994375],[9.09016994375,6.87785252292],[10.510565163,4.09016994375],[11,1]]); + assert_approx(mask2d_cove(r=10,inset=1,excess=1),[[11,-1],[-1,-1],[-1,11],[1,11],[4.09016994375,10.510565163],[6.87785252292,9.09016994375],[9.09016994375,6.87785252292],[10.510565163,4.09016994375],[11,1]]); + assert_approx(mask2d_cove(d=20,inset=1,excess=1),[[11,-1],[-1,-1],[-1,11],[1,11],[4.09016994375,10.510565163],[6.87785252292,9.09016994375],[9.09016994375,6.87785252292],[10.510565163,4.09016994375],[11,1]]); } test_mask2d_cove(); module test_mask2d_roundover() { $fn = 24; - assert_approx(mask2d_roundover(r=10),[[-0.01,10],[-1.7763568394e-15,10],[0.489434837048,6.90983005625],[1.90983005625,4.12214747708],[4.12214747708,1.90983005625],[6.90983005625,0.489434837048],[10,-1.7763568394e-15],[10,-0.01],[0,-0.01],[-0.01,-0.01],[-0.01,0]]); - assert_approx(mask2d_roundover(d=20),[[-0.01,10],[-1.7763568394e-15,10],[0.489434837048,6.90983005625],[1.90983005625,4.12214747708],[4.12214747708,1.90983005625],[6.90983005625,0.489434837048],[10,-1.7763568394e-15],[10,-0.01],[0,-0.01],[-0.01,-0.01],[-0.01,0]]); - assert_approx(mask2d_roundover(r=10,inset=1),[[-0.01,11],[1,11],[1.48943483705,7.90983005625],[2.90983005625,5.12214747708],[5.12214747708,2.90983005625],[7.90983005625,1.48943483705],[11,1],[11,-0.01],[0,-0.01],[-0.01,-0.01],[-0.01,0]]); - assert_approx(mask2d_roundover(d=20,inset=1),[[-0.01,11],[1,11],[1.48943483705,7.90983005625],[2.90983005625,5.12214747708],[5.12214747708,2.90983005625],[7.90983005625,1.48943483705],[11,1],[11,-0.01],[0,-0.01],[-0.01,-0.01],[-0.01,0]]); - assert_approx(mask2d_roundover(r=10,inset=1,excess=1),[[-1,11],[1,11],[1.48943483705,7.90983005625],[2.90983005625,5.12214747708],[5.12214747708,2.90983005625],[7.90983005625,1.48943483705],[11,1],[11,-1],[0,-1],[-1,-1],[-1,0]]); - assert_approx(mask2d_roundover(d=20,inset=1,excess=1),[[-1,11],[1,11],[1.48943483705,7.90983005625],[2.90983005625,5.12214747708],[5.12214747708,2.90983005625],[7.90983005625,1.48943483705],[11,1],[11,-1],[0,-1],[-1,-1],[-1,0]]); + assert_approx(mask2d_roundover(r=10),[[10,-0.01],[-0.01,-0.01],[-0.01,10],[-1.7763568394e-15,10],[0.489434837048,6.90983005625],[1.90983005625,4.12214747708],[4.12214747708,1.90983005625],[6.90983005625,0.489434837048],[10,-1.7763568394e-15]]); + assert_approx(mask2d_roundover(d=20),[[10,-0.01],[-0.01,-0.01],[-0.01,10],[-1.7763568394e-15,10],[0.489434837048,6.90983005625],[1.90983005625,4.12214747708],[4.12214747708,1.90983005625],[6.90983005625,0.489434837048],[10,-1.7763568394e-15]]); + assert_approx(mask2d_roundover(r=10,inset=1),[[11,-0.01],[-0.01,-0.01],[-0.01,11],[1,11],[1.48943483705,7.90983005625],[2.90983005625,5.12214747708],[5.12214747708,2.90983005625],[7.90983005625,1.48943483705],[11,1]]); + assert_approx(mask2d_roundover(d=20,inset=1),[[11,-0.01],[-0.01,-0.01],[-0.01,11],[1,11],[1.48943483705,7.90983005625],[2.90983005625,5.12214747708],[5.12214747708,2.90983005625],[7.90983005625,1.48943483705],[11,1]]); + assert_approx(mask2d_roundover(r=10,inset=1,excess=1),[[11,-1],[-1,-1],[-1,11],[1,11],[1.48943483705,7.90983005625],[2.90983005625,5.12214747708],[5.12214747708,2.90983005625],[7.90983005625,1.48943483705],[11,1]]); + assert_approx(mask2d_roundover(d=20,inset=1,excess=1),[[11,-1],[-1,-1],[-1,11],[1,11],[1.48943483705,7.90983005625],[2.90983005625,5.12214747708],[5.12214747708,2.90983005625],[7.90983005625,1.48943483705],[11,1]]); } test_mask2d_roundover(); module test_mask2d_dovetail() { - assert_approx(mask2d_dovetail(x=10),[[0,0],[-0.01,0],[-0.01,17.3205080757],[10,17.3205080757],[10,17.3205080757],[0,0]]); - assert_approx(mask2d_dovetail(y=10),[[0,0],[-0.01,0],[-0.01,10],[5.7735026919,10],[5.7735026919,10],[0,0]]); - assert_approx(mask2d_dovetail(edge=10),[[0,0],[-0.01,0],[-0.01,8.66025403784],[5,8.66025403784],[5,8.66025403784],[0,0]]); - assert_approx(mask2d_dovetail(x=10,angle=30),[[0,0],[-0.01,0],[-0.01,17.3205080757],[10,17.3205080757],[10,17.3205080757],[0,0]]); - assert_approx(mask2d_dovetail(y=10,angle=30),[[0,0],[-0.01,0],[-0.01,10],[5.7735026919,10],[5.7735026919,10],[0,0]]); - assert_approx(mask2d_dovetail(edge=10,angle=30),[[0,0],[-0.01,0],[-0.01,8.66025403784],[5,8.66025403784],[5,8.66025403784],[0,0]]); - assert_approx(mask2d_dovetail(x=10,angle=30,inset=1),[[1,0],[-0.01,0],[-0.01,18.3205080757],[11,18.3205080757],[11,18.3205080757],[1,1]]); - assert_approx(mask2d_dovetail(y=10,angle=30,inset=1),[[1,0],[-0.01,0],[-0.01,11],[6.7735026919,11],[6.7735026919,11],[1,1]]); - assert_approx(mask2d_dovetail(edge=10,angle=30,inset=1),[[1,0],[-0.01,0],[-0.01,9.66025403784],[6,9.66025403784],[6,9.66025403784],[1,1]]); - assert_approx(mask2d_dovetail(x=10,angle=30,inset=1,excess=1),[[1,0],[-1,0],[-1,18.3205080757],[11,18.3205080757],[11,18.3205080757],[1,1]]); - assert_approx(mask2d_dovetail(y=10,angle=30,inset=1,excess=1),[[1,0],[-1,0],[-1,11],[6.7735026919,11],[6.7735026919,11],[1,1]]); - assert_approx(mask2d_dovetail(edge=10,angle=30,inset=1,excess=1),[[1,0],[-1,0],[-1,9.66025403784],[6,9.66025403784],[6,9.66025403784],[1,1]]); + assert_approx(mask2d_dovetail(x=10),[[0,-0.01],[-0.01,-0.01],[-0.01,17.3205080757],[0,17.3205080757],[10,17.3205080757],[10,17.3205080757],[0,0]]); + assert_approx(mask2d_dovetail(y=10),[[0,-0.01],[-0.01,-0.01],[-0.01,10],[0,10],[5.7735026919,10],[5.7735026919,10],[0,0]]); + assert_approx(mask2d_dovetail(edge=10),[[0,-0.01],[-0.01,-0.01],[-0.01,8.66025403784],[0,8.66025403784],[5,8.66025403784],[5,8.66025403784],[0,0]]); + assert_approx(mask2d_dovetail(x=10,angle=30),[[0,-0.01],[-0.01,-0.01],[-0.01,17.3205080757],[0,17.3205080757],[10,17.3205080757],[10,17.3205080757],[0,0]]); + assert_approx(mask2d_dovetail(y=10,angle=30),[[0,-0.01],[-0.01,-0.01],[-0.01,10],[0,10],[5.7735026919,10],[5.7735026919,10],[0,0]]); + assert_approx(mask2d_dovetail(edge=10,angle=30),[[0,-0.01],[-0.01,-0.01],[-0.01,8.66025403784],[0,8.66025403784],[5,8.66025403784],[5,8.66025403784],[0,0]]); + assert_approx(mask2d_dovetail(x=10,angle=30,inset=1),[[1,-0.01],[-0.01,-0.01],[-0.01,18.3205080757],[1,18.3205080757],[11,18.3205080757],[11,18.3205080757],[1,1]]); + assert_approx(mask2d_dovetail(y=10,angle=30,inset=1),[[1,-0.01],[-0.01,-0.01],[-0.01,11],[1,11],[6.7735026919,11],[6.7735026919,11],[1,1]]); + assert_approx(mask2d_dovetail(edge=10,angle=30,inset=1),[[1,-0.01],[-0.01,-0.01],[-0.01,9.66025403784],[1,9.66025403784],[6,9.66025403784],[6,9.66025403784],[1,1]]); + assert_approx(mask2d_dovetail(x=10,angle=30,inset=1,excess=1),[[1,-1],[-1,-1],[-1,18.3205080757],[1,18.3205080757],[11,18.3205080757],[11,18.3205080757],[1,1]]); + assert_approx(mask2d_dovetail(y=10,angle=30,inset=1,excess=1),[[1,-1],[-1,-1],[-1,11],[1,11],[6.7735026919,11],[6.7735026919,11],[1,1]]); + assert_approx(mask2d_dovetail(edge=10,angle=30,inset=1,excess=1),[[1,-1],[-1,-1],[-1,9.66025403784],[1,9.66025403784],[6,9.66025403784],[6,9.66025403784],[1,1]]); } test_mask2d_dovetail(); module test_mask2d_rabbet() { - assert_approx(mask2d_rabbet(10), [[-0.01,10],[10,10],[10,-0.01],[0,-0.01],[-0.01,-0.01],[-0.01,0]]); - assert_approx(mask2d_rabbet(size=10), [[-0.01,10],[10,10],[10,-0.01],[0,-0.01],[-0.01,-0.01],[-0.01,0]]); - assert_approx(mask2d_rabbet(size=[10,15]), [[-0.01,15],[10,15],[10,-0.01],[0,-0.01],[-0.01,-0.01],[-0.01,0]]); - assert_approx(mask2d_rabbet(size=[10,15],excess=1), [[-1,15],[10,15],[10,-1],[0,-1],[-1,-1],[-1,0]]); + assert_approx(mask2d_rabbet(10), [[10,-0.01],[-0.01,-0.01],[-0.01,10],[0,10],[10,10],[10,0]]); + assert_approx(mask2d_rabbet(size=10), [[10,-0.01],[-0.01,-0.01],[-0.01,10],[0,10],[10,10],[10,0]]); + assert_approx(mask2d_rabbet(size=[10,15]), [[10,-0.01],[-0.01,-0.01],[-0.01,15],[0,15],[10,15],[10,0]]); + assert_approx(mask2d_rabbet(size=[10,15],excess=1), [[10,-1],[-1,-1],[-1,15],[0,15],[10,15],[10,0]]); } test_mask2d_rabbet(); module test_mask2d_teardrop() { $fn=24; - assert_approx(mask2d_teardrop(r=10), [[-0.01,10],[0,10],[0.761204674887,6.17316567635],[2.92893218813,2.92893218813],[5.85786437627,0],[5.85786437627,-0.01],[0,-0.01],[-0.01,-0.01],[-0.01,0]]); - assert_approx(mask2d_teardrop(d=20), [[-0.01,10],[0,10],[0.761204674887,6.17316567635],[2.92893218813,2.92893218813],[5.85786437627,0],[5.85786437627,-0.01],[0,-0.01],[-0.01,-0.01],[-0.01,0]]); - assert_approx(mask2d_teardrop(r=10,angle=30), [[-0.01,10],[0,10],[0.340741737109,7.41180954897],[1.33974596216,5],[4.2264973081,0],[4.2264973081,-0.01],[0,-0.01],[-0.01,-0.01],[-0.01,0]]); - assert_approx(mask2d_teardrop(r=10,angle=30,excess=1), [[-1,10],[0,10],[0.340741737109,7.41180954897],[1.33974596216,5],[4.2264973081,0],[4.2264973081,-1],[0,-1],[-1,-1],[-1,0]]); + assert_approx(mask2d_teardrop(r=10), [[6.03197753333,-0.01],[-0.01,-0.01],[-0.01,10],[-1.7763568394e-15,10],[0.489434837048,6.90983005625],[1.90983005625,4.12214747708],[4.12214747708,1.90983005625],[6.03197753333,-4.4408920985e-16]]); + assert_approx(mask2d_teardrop(d=20), [[6.03197753333,-0.01],[-0.01,-0.01],[-0.01,10],[-1.7763568394e-15,10],[0.489434837048,6.90983005625],[1.90983005625,4.12214747708],[4.12214747708,1.90983005625],[6.03197753333,-4.4408920985e-16]]); + assert_approx(mask2d_teardrop(r=10,angle=30), [[4.28975301178,-0.01],[-0.01,-0.01],[-0.01,10],[-1.7763568394e-15,10],[0.489434837048,6.90983005625],[1.90983005625,4.12214747708],[4.28975301178,0]]); + assert_approx(mask2d_teardrop(r=10,angle=30,excess=1), [[4.28975301178,-1],[-1,-1],[-1,10],[-1.7763568394e-15,10],[0.489434837048,6.90983005625],[1.90983005625,4.12214747708],[4.28975301178,0]]); } test_mask2d_teardrop(); From 210f0c1164c038e04387f76453f293c2906098d9 Mon Sep 17 00:00:00 2001 From: Revar Desmera Date: Sat, 18 Nov 2023 01:34:19 -0800 Subject: [PATCH 12/16] Removed bad cove mask example. --- masks2d.scad | 3 +-- scripts/check_for_tabs.sh | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/masks2d.scad b/masks2d.scad index f36a5d5..75ffca5 100644 --- a/masks2d.scad +++ b/masks2d.scad @@ -232,8 +232,6 @@ function _inset_isect(inset,mask_angle,flat_top,excess,r,size) = // mask2d_cove(r=10,inset=3,mask_angle=75); // Example(2D): Disabling flat_top= // mask2d_cove(r=10, inset=3, flat_top=false, mask_angle=75); -// Example(2D): 2D Angled Inset Cove Mask by Joint Length -// mask2d_cove(joint=10, inset=3, mask_angle=75); // Example(2D): Increasing the Excess // mask2d_cove(r=10,inset=3,mask_angle=75, excess=2); // Example: Masking by Edge Attachment @@ -633,6 +631,7 @@ module mask2d_teardrop(r, angle=45, mask_angle=90, excess=0.01, flat_top=true, d } } + // Function&Module: mask2d_ogee() // Synopsis: Creates a 2D ogee mask shape. // SynTags: Geom, Path diff --git a/scripts/check_for_tabs.sh b/scripts/check_for_tabs.sh index 21b86d1..a075703 100755 --- a/scripts/check_for_tabs.sh +++ b/scripts/check_for_tabs.sh @@ -1,6 +1,6 @@ #!/bin/bash -if grep -H -n '\t' *.scad ; then +if grep -H -n -P '\t' *.scad ; then echo "Tabs found in source code." 2>&1 exit 1 fi From 84a302b5824a6eb87d25c827ae87c7dc36f6e1ee Mon Sep 17 00:00:00 2001 From: Revar Desmera Date: Mon, 27 Nov 2023 16:41:38 -0800 Subject: [PATCH 13/16] Triangulate big faces in spiral_sweep(). Fixes #1313 --- skin.scad | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/skin.scad b/skin.scad index e01deac..fc9ed38 100644 --- a/skin.scad +++ b/skin.scad @@ -1258,9 +1258,10 @@ function spiral_sweep(poly, h, r, turns=1, taper, r1, r2, d, d1, d2, internal=fa points, col_wrap=true, caps=true, reverse=dir>0, // style=higbee1>0 || higbee2>0 ? "quincunx" : "alt" style="convex" - ) + ), + vnf2 = vnf_triangulate(vnf) ) - reorient(anchor,spin,orient, vnf=vnf, r1=r1, r2=r2, l=h, p=vnf); + reorient(anchor,spin,orient, vnf=vnf2, r1=r1, r2=r2, l=h, p=vnf2); From e94365556c75df0fe3ea2b2d2dcc659c735268a6 Mon Sep 17 00:00:00 2001 From: Revar Desmera Date: Tue, 28 Nov 2023 21:44:35 -0800 Subject: [PATCH 14/16] Added triangulate= option to vnf_vertex_array() --- vnf.scad | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/vnf.scad b/vnf.scad index cf762cb..c6cbc4f 100644 --- a/vnf.scad +++ b/vnf.scad @@ -33,7 +33,7 @@ EMPTY_VNF = [[],[]]; // The standard empty VNF with no vertices or faces. // Topics: VNF Generators, Lists // See Also: vnf_tri_array(), vnf_join(), vnf_from_polygons(), vnf_from_region() // Usage: -// vnf = vnf_vertex_array(points, [caps=], [cap1=], [cap2=], [style=], [reverse=], [col_wrap=], [row_wrap=]); +// vnf = vnf_vertex_array(points, [caps=], [cap1=], [cap2=], [style=], [reverse=], [col_wrap=], [row_wrap=], [triangulate=]); // Description: // Creates a VNF structure from a rectangular vertex list, by dividing the vertices into columns and rows, // adding faces to tile the surface. You can optionally have faces added to wrap the last column @@ -55,6 +55,7 @@ EMPTY_VNF = [[],[]]; // The standard empty VNF with no vertices or faces. // row_wrap = If true, add faces to connect the last row to the first. // reverse = If true, reverse all face normals. // style = The style of subdividing the quads into faces. Valid options are "default", "alt", "min_edge", "quincunx", "convex" and "concave". +// triangulate = If true, triangulates endcaps to resolve possible CGAL issues. This can be an expensive operation if the endcaps are complex. Default: false // Example(3D): // vnf = vnf_vertex_array( // points=[ @@ -131,13 +132,15 @@ function vnf_vertex_array( col_wrap=false, row_wrap=false, reverse=false, - style="default" + style="default", + triangulate = false ) = assert(!(any([caps,cap1,cap2]) && !col_wrap), "col_wrap must be true if caps are requested") assert(!(any([caps,cap1,cap2]) && row_wrap), "Cannot combine caps with row_wrap") assert(in_list(style,["default","alt","quincunx", "convex","concave", "min_edge","min_area"])) assert(is_matrix(points[0], n=3),"Point array has the wrong shape or points are not 3d") assert(is_consistent(points), "Non-rectangular or invalid point array") + assert(is_bool(triangulate)) let( pts = flatten(points), pcnt = len(pts), @@ -226,9 +229,9 @@ function vnf_vertex_array( rfaces = reverse? [for (face=culled_faces) reverse(face)] : culled_faces ) rfaces, - ] - ) - [verts,allfaces]; + ], + vnf = [verts, allfaces] + ) triangulate? vnf_triangulate(vnf) : vnf; // Function: vnf_tri_array() From 0dece30ff82a30348e54bb621305135da22ac9e6 Mon Sep 17 00:00:00 2001 From: Revar Desmera Date: Tue, 28 Nov 2023 22:12:22 -0800 Subject: [PATCH 15/16] Version Bumping on PR merge --- .github/workflows/main.yml | 9 --------- .github/workflows/pr_merge.yml | 27 +++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 9 deletions(-) create mode 100644 .github/workflows/pr_merge.yml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6baa57d..8a5cab9 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -2,15 +2,6 @@ name: Checks on: [pull_request] jobs: - VersionCheck: - runs-on: ubuntu-latest - steps: - - name: Check Version - uses: JJ/github-pr-contains-action@releases/v11 - with: - github-token: ${{github.token}} - diffContains: '^BOSL_VERSION' - Regressions: runs-on: ubuntu-latest steps: diff --git a/.github/workflows/pr_merge.yml b/.github/workflows/pr_merge.yml new file mode 100644 index 0000000..bd17ed5 --- /dev/null +++ b/.github/workflows/pr_merge.yml @@ -0,0 +1,27 @@ +name: VersionBump +on: + pull_request: + types: [closed] + +jobs: + VersionCheck: + runs-on: ubuntu-latest + if: github.event.pull_request.merged == true + permissions: + contents: write + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + ref: ${{ github.head_ref }} + + - name: Bump Version + run: ./scripts/increment_version.sh + + - name: Checkin + uses: stefanzweifel/git-auto-commit-action@v5 + with: + commit_message: Version Bump + file_pattern: version.scad + From fb2cfba370ce0f0b799c3b435a21cf55e0f76a77 Mon Sep 17 00:00:00 2001 From: Revar Desmera Date: Tue, 28 Nov 2023 22:20:44 -0800 Subject: [PATCH 16/16] GitHub action job title fix. --- .github/workflows/pr_merge.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr_merge.yml b/.github/workflows/pr_merge.yml index bd17ed5..5d6592e 100644 --- a/.github/workflows/pr_merge.yml +++ b/.github/workflows/pr_merge.yml @@ -4,7 +4,7 @@ on: types: [closed] jobs: - VersionCheck: + VersionBumpJob: runs-on: ubuntu-latest if: github.event.pull_request.merged == true permissions: