From 65ec17f4b33d9545b091c41832fabba1fbbc9933 Mon Sep 17 00:00:00 2001 From: Alex Matulich Date: Wed, 4 Dec 2024 23:26:54 -0800 Subject: [PATCH 1/4] added squircle to shapes2d.scad --- shapes2d.scad | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/shapes2d.scad b/shapes2d.scad index 45fda07..d87f58f 100644 --- a/shapes2d.scad +++ b/shapes2d.scad @@ -1929,7 +1929,7 @@ function _superformula(theta,m1,m2,n1,n2=1,n3=1,a=1,b=1) = // Usage: As Function // path = reuleaux_polygon(n, r|d=, ...); // Description: -// When called as a module, reates a 2D Reuleaux Polygon; a constant width shape that is not circular. Uses "intersect" type anchoring. +// When called as a module, creates a 2D Reuleaux Polygon; a constant width shape that is not circular. Uses "intersect" type anchoring. // When called as a function, returns a 2D path for a Reulaux Polygon. // Arguments: // n = Number of "sides" to the Reuleaux Polygon. Must be an odd positive number. Default: 3 @@ -1988,6 +1988,73 @@ function reuleaux_polygon(n=3, r, d, anchor=CENTER, spin=0) = +// Function&Module: squircle() +// Synopsis: Creates a shape between a circle and a square, centered on the origin. +// SynTags: Geom, Path +// Topics: Shapes (2D), Paths (2D), Path Generators, Attachable +// See Also: circle(), square() +// Usage: As Module +// squircle(squareness, size) [ATTACHMENTS]; +// Usage: As Function +// path = squircle(squareness, size); +// Description: +// A squircle is a shape intermediate between a square/rectangle and a circle/ellipse. Squircles are sometimes used to make dinner plates (more area for the same radius as a circle), keyboard buttons, and smartphone icons. Old CRT television screens also resembled squircles. +// When called as a module, creates a 2D squircle with the desired squareness. Uses "intersect" type anchoring. +// When called as a function, returns a 2D path for a squircle. +// Arguments: +// squareness = Value between 0 and 1. Controls the shape of the squircle. When `squareness=0` the shape is a circle, and when `squareness=1` the shape is a square. Default: 0.8 +// size = Bounding box of the squircle, same as the `size` parameter in `square()`, can be a single number or an `[xsize,ysize]` vector. Default: [10,10] +// $fn = Number of points. Special variables `$fs` and `$fa` are ignored. If set, `$fn` must be 12 or greater, and is rounded to the nearest multiple of 4. Points are generated non-uniformly around the squircle so they are more dense sharper curves. Default if not set: 40 +// Examples(2D): +// squircle(squareness=0.5, size=50); +// squircle(0.95, [80,60], $fn=64); +// Examples(2D): Standard vector anchors are based on extents +// squircle(0.8, 50) show_anchors(custom=false); +// Examples(2D): Named anchors exist for the sides and corners +// squircle(0.8, 50) show_anchors(std=false); +module squircle(squareness=0.8, size=[10,10], anchor=CENTER, spin=0) { + check = assert(squareness >= 0 && squareness <= 1); + bbox = is_num(size) ? [size,size] : point2d(size); + assert(all_positive(bbox), "All components of size must be positive."); + path = squircle(squareness, size); + anchors = [ + for (i = [0:1:3]) let( + ca = 360 - i*90, + cp = polar_to_xy(squircle_radius(squareness, bbox[0], ca), ca) + ) named_anchor(str("side",i), cp, unit(cp,BACK), 0), + for (i = [0:1:3]) let( + ca = 360-45 - i*90, + cp = polar_to_xy(squircle_radius(squareness, bbox[0], ca), ca) + ) named_anchor(str("corner",i), cp, unit(cp,BACK), 0) + ]; + attachable(anchor,spin, two_d=true, path=path, extent=false, anchors=anchors) { + polygon(path); + children(); + } +} + + +function squircle(squareness=0.8, size=[10,10]) = + assert(squareness >= 0 && squareness <= 1) [ + let( + sq = sqrt(squareness), // somewhat linearize the squareness response + bbox = is_num(size) ? [size,size] : point2d(size), + aspect = bbox[1] / bbox[0], + r = 0.5 * bbox[0], + astep = $fn>=12 ? 90/round($fn/4) : 9 + ) for(a=[360:-astep:0.01]) let( + theta = a + sq * sin(4*a) * 30/PI, // tighter angle steps at corners + p = squircle_radius(sq, r, theta) + ) [p*cos(theta), aspect*p*sin(theta)] +]; + + +function squircle_radius(squareness, r, angle) = let( + s2a = abs(squareness*sin(2*angle)) + ) s2a>0 ? r*sqrt(2)/s2a * sqrt(1 - sqrt(1 - s2a*s2a)) : r; + + + // Section: Text // Module: text() From dd96a30c92731c60cf94f82395596b617b7098fa Mon Sep 17 00:00:00 2001 From: Alex Matulich Date: Thu, 5 Dec 2024 10:18:10 -0800 Subject: [PATCH 2/4] Expanded documentation to explain differences from supershape() --- shapes2d.scad | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/shapes2d.scad b/shapes2d.scad index d87f58f..cb63af3 100644 --- a/shapes2d.scad +++ b/shapes2d.scad @@ -1992,14 +1992,15 @@ function reuleaux_polygon(n=3, r, d, anchor=CENTER, spin=0) = // Synopsis: Creates a shape between a circle and a square, centered on the origin. // SynTags: Geom, Path // Topics: Shapes (2D), Paths (2D), Path Generators, Attachable -// See Also: circle(), square() +// See Also: circle(), square(), supershape() // Usage: As Module // squircle(squareness, size) [ATTACHMENTS]; // Usage: As Function // path = squircle(squareness, size); // Description: -// A squircle is a shape intermediate between a square/rectangle and a circle/ellipse. Squircles are sometimes used to make dinner plates (more area for the same radius as a circle), keyboard buttons, and smartphone icons. Old CRT television screens also resembled squircles. -// When called as a module, creates a 2D squircle with the desired squareness. Uses "intersect" type anchoring. +// A squircle is a shape intermediate between a square/rectangle and a circle/ellipse. A squircle is a special case of supershape (shown in `supershape()` example 3), but this implementation uses a different method that requires just two parameters, and the vertex distribution is optimized for smoothness. +// Squircles are sometimes used to make dinner plates (more area for the same radius as a circle), keyboard buttons, and smartphone icons. Old CRT television screens also resembled squircles. +// When called as a module, creates a 2D squircle with the desired squareness. Uses "intersect" type anchoring. // When called as a function, returns a 2D path for a squircle. // Arguments: // squareness = Value between 0 and 1. Controls the shape of the squircle. When `squareness=0` the shape is a circle, and when `squareness=1` the shape is a square. Default: 0.8 From 495ebbefd85025111cc889f2787e11dce536757b Mon Sep 17 00:00:00 2001 From: Alex Matulich Date: Thu, 5 Dec 2024 10:18:10 -0800 Subject: [PATCH 3/4] Expanded documentation to explain differences from supershape() --- shapes2d.scad | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/shapes2d.scad b/shapes2d.scad index d87f58f..3de6bcb 100644 --- a/shapes2d.scad +++ b/shapes2d.scad @@ -1992,27 +1992,31 @@ function reuleaux_polygon(n=3, r, d, anchor=CENTER, spin=0) = // Synopsis: Creates a shape between a circle and a square, centered on the origin. // SynTags: Geom, Path // Topics: Shapes (2D), Paths (2D), Path Generators, Attachable -// See Also: circle(), square() +// See Also: circle(), square(), supershape() // Usage: As Module // squircle(squareness, size) [ATTACHMENTS]; // Usage: As Function // path = squircle(squareness, size); // Description: -// A squircle is a shape intermediate between a square/rectangle and a circle/ellipse. Squircles are sometimes used to make dinner plates (more area for the same radius as a circle), keyboard buttons, and smartphone icons. Old CRT television screens also resembled squircles. -// When called as a module, creates a 2D squircle with the desired squareness. Uses "intersect" type anchoring. +// A squircle is a shape intermediate between a square/rectangle and a circle/ellipse. A squircle is a special case of supershape (shown in `supershape()` example 3), but this implementation uses a different method that requires just two parameters, and the vertex distribution is optimized for smoothness. +// Squircles are sometimes used to make dinner plates (more area for the same radius as a circle), keyboard buttons, and smartphone icons. Old CRT television screens also resembled squircles. +// When called as a module, creates a 2D squircle with the desired squareness. Uses "intersect" type anchoring. // When called as a function, returns a 2D path for a squircle. // Arguments: -// squareness = Value between 0 and 1. Controls the shape of the squircle. When `squareness=0` the shape is a circle, and when `squareness=1` the shape is a square. Default: 0.8 +// squareness = Value between 0 and 1. Controls the shape of the squircle. When `squareness=0` the shape is a circle, and when `squareness=1` the shape is a square. Default: 0.7 // size = Bounding box of the squircle, same as the `size` parameter in `square()`, can be a single number or an `[xsize,ysize]` vector. Default: [10,10] // $fn = Number of points. Special variables `$fs` and `$fa` are ignored. If set, `$fn` must be 12 or greater, and is rounded to the nearest multiple of 4. Points are generated non-uniformly around the squircle so they are more dense sharper curves. Default if not set: 40 // Examples(2D): -// squircle(squareness=0.5, size=50); -// squircle(0.95, [80,60], $fn=64); +// squircle(squareness=0.4, size=50); +// squircle(0.8, [80,60], $fn=64); +// Examples(2D): Ten increments of squareness parameter +// for(sq=[0:0.1:1]) +// stroke(squircle(sq, 100, $fn=128), closed=true, width=0.5); // Examples(2D): Standard vector anchors are based on extents // squircle(0.8, 50) show_anchors(custom=false); // Examples(2D): Named anchors exist for the sides and corners // squircle(0.8, 50) show_anchors(std=false); -module squircle(squareness=0.8, size=[10,10], anchor=CENTER, spin=0) { +module squircle(squareness=0.7, size=[10,10], anchor=CENTER, spin=0) { check = assert(squareness >= 0 && squareness <= 1); bbox = is_num(size) ? [size,size] : point2d(size); assert(all_positive(bbox), "All components of size must be positive."); @@ -2034,10 +2038,11 @@ module squircle(squareness=0.8, size=[10,10], anchor=CENTER, spin=0) { } -function squircle(squareness=0.8, size=[10,10]) = +function squircle(squareness=0.7, size=[10,10]) = assert(squareness >= 0 && squareness <= 1) [ let( - sq = sqrt(squareness), // somewhat linearize the squareness response + sqlim = max(0, min(1, squareness)), + sq = sqrt(sqlim*(2-sqlim)), // somewhat linearize squareness response bbox = is_num(size) ? [size,size] : point2d(size), aspect = bbox[1] / bbox[0], r = 0.5 * bbox[0], From 504c92bba9c1eb4a0260a3f0abfcae6d3e9dc4d3 Mon Sep 17 00:00:00 2001 From: Alex Matulich Date: Thu, 5 Dec 2024 16:59:58 -0800 Subject: [PATCH 4/4] Corrected anchor positions, improved linear response of squareness, added example --- shapes2d.scad | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shapes2d.scad b/shapes2d.scad index 3de6bcb..9a9f5a8 100644 --- a/shapes2d.scad +++ b/shapes2d.scad @@ -2024,11 +2024,11 @@ module squircle(squareness=0.7, size=[10,10], anchor=CENTER, spin=0) { anchors = [ for (i = [0:1:3]) let( ca = 360 - i*90, - cp = polar_to_xy(squircle_radius(squareness, bbox[0], ca), ca) + cp = polar_to_xy(squircle_radius(squareness, bbox[0]/2, ca), ca) ) named_anchor(str("side",i), cp, unit(cp,BACK), 0), for (i = [0:1:3]) let( ca = 360-45 - i*90, - cp = polar_to_xy(squircle_radius(squareness, bbox[0], ca), ca) + cp = polar_to_xy(squircle_radius(squareness, bbox[0]/2, ca), ca) ) named_anchor(str("corner",i), cp, unit(cp,BACK), 0) ]; attachable(anchor,spin, two_d=true, path=path, extent=false, anchors=anchors) {