From b92b6eb420cd5097e8aa8319092e82af9a0039c1 Mon Sep 17 00:00:00 2001 From: github-actions Date: Tue, 18 May 2021 23:38:04 +0000 Subject: [PATCH 01/72] Bump release version. --- version.scad | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.scad b/version.scad index 5c37f18..4f3d75b 100644 --- a/version.scad +++ b/version.scad @@ -6,7 +6,7 @@ ////////////////////////////////////////////////////////////////////// -BOSL_VERSION = [2,0,628]; +BOSL_VERSION = [2,0,629]; // Section: BOSL Library Version Functions From bbfe46e1da0bedd202adf414b680dff36b529e92 Mon Sep 17 00:00:00 2001 From: Garth Minette Date: Thu, 20 May 2021 01:35:25 -0700 Subject: [PATCH 02/72] Fixed use of attach(overlap=) for 2D cases. --- attachments.scad | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/attachments.scad b/attachments.scad index 7858789..870b5f1 100644 --- a/attachments.scad +++ b/attachments.scad @@ -1057,11 +1057,12 @@ module attach(from, to, overlap, norot=false) $attach_to = to; $attach_anchor = anch; $attach_norot = norot; + olap = two_d? [0,-overlap,0] : [0,0,-overlap]; if (norot || (norm(anch[2]-UP)<1e-9 && anch[3]==0)) { - translate(anch[1]) translate([0,0,-overlap]) children(); + translate(anch[1]) translate(olap) children(); } else { fromvec = two_d? BACK : UP; - translate(anch[1]) rot(anch[3],from=fromvec,to=anch[2]) translate([0,0,-overlap]) children(); + translate(anch[1]) rot(anch[3],from=fromvec,to=anch[2]) translate(olap) children(); } } } @@ -1223,8 +1224,18 @@ module corner_profile(corners=CORNERS_ALL, except=[], r, d, convexity=10) { // Topics: Attachments // See Also: attachable(), position(), attach(), face_profile(), edge_profile(), corner_mask() // Description: -// Takes a 3D mask shape, and attaches it to the given edges, with the appropriate orientation to be `diff()`ed away. -// For a more step-by-step explanation of attachments, see the [[Attachments Tutorial|Tutorial-Attachments]]. +// Takes a 3D mask shape, and attaches it to the given edges, with the appropriate orientation to be +// `diff()`ed away. The mask shape should be vertically oriented (Z-aligned) with the back-right +// quadrant (X+Y+) shaped to be diffed away from the edge of parent attachable shape. For a more +// step-by-step explanation of attachments, see the [[Attachments Tutorial|Tutorial-Attachments]]. +// Figure: A Typical Edge Rounding Mask +// module roundit(l,r) difference() { +// translate([-1,-1,-l/2]) +// cube([r+1,r+1,l]); +// translate([r,r]) +// cylinder(h=l+1,r=r,center=true, $fn=quantup(segs(r),4)); +// } +// roundit(l=30,r=10); // Arguments: // edges = Edges to mask. See the docs for [`edges()`](edges.scad#edges) to see acceptable values. Default: All edges. // except = Edges to explicitly NOT mask. See the docs for [`edges()`](edges.scad#edges) to see acceptable values. Default: No edges. From 8603aa4bee7d612a09c22e7977022496f18f1768 Mon Sep 17 00:00:00 2001 From: Garth Minette Date: Thu, 20 May 2021 01:37:08 -0700 Subject: [PATCH 03/72] Expanded Attachments tutorial for 2D, recolor(), and masking. --- tutorials/Attachments.md | 236 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 228 insertions(+), 8 deletions(-) diff --git a/tutorials/Attachments.md b/tutorials/Attachments.md index b17b70a..98a0341 100644 --- a/tutorials/Attachments.md +++ b/tutorials/Attachments.md @@ -91,6 +91,23 @@ overrides the `anchor=` argument. A `center=true` argument is the same as `anch A `center=false` argument can mean `anchor=[-1,-1,-1]` for a cube, or `anchor=BOTTOM` for a cylinder. +Many 2D shapes provided by BOSL2 are also anchorable. Due to technical limitations of OpenSCAD, +however, `square()` and `circle()` are *not*. BOSL2 provides `rect()` and `oval()` as attachable +and anchorable equivalents. You can only anchor on the XY plane, of course, but you can use the +same `FRONT`, `BACK`, `LEFT`, `RIGHT`, and `CENTER` anchor constants. + +```openscad-2D +rect([40,30], anchor=BACK+LEFT); +``` + +```openscad-2D +oval(d=50, anchor=FRONT); +``` + +```openscad-2D +hexagon(d=50, anchor=BACK); +``` + ## Spin Attachable shapes also can be spun in place as you create them. You can do this by passing in @@ -107,6 +124,17 @@ vector, like [Xang,Yang,Zang]: cube([20,20,40], center=true, spin=[10,20,30]); ``` +You can also apply spin to 2D shapes from BOSL2. Again, you should use `rect()` and `oval()` +instead of `square()` and `circle()`: + +```openscad-2D +rect([40,30], spin=30); +``` + +```openscad-2D +oval(d=[40,30], spin=30); +``` + ## Orientation Another way to specify a rotation for an attachable shape, is to pass a 3D vector via the @@ -117,6 +145,9 @@ For example, you can make a cone that is tilted up and to the right like this: cylinder(h=100, r1=50, r2=20, orient=UP+RIGHT); ``` +You can *not* use `orient=` with 2D shapes. + + ## Mixing Anchoring, Spin, and Orientation When giving `anchor=`, `spin=`, and `orient=`, they are applied anchoring first, spin second, then orient last. For example, here's a cube: @@ -150,8 +181,14 @@ However, since spin is applied *after* anchoring, it can actually have a signifi cylinder(d=50, l=40, anchor=FWD, spin=-30); ``` +For 2D shapes, you can mix `anchor=` with `spin=`, but not with `orient=`. -## Attaching Children +```openscad-2D +rect([40,30], anchor=BACK+LEFT, spin=30); +``` + + +## Attaching 3D Children The reason attachables are called that, is because they can be attached to each other. You can do that by making one attachable shape be a child of another attachable shape. By default, the child of an attachable is attached to the center of the parent shape. @@ -178,13 +215,21 @@ cube(50,center=true) attach(TOP,TOP) cylinder(d1=50,d2=20,l=20); ``` -By default, `attach()` causes the child to overlap the parent by 0.01, to let CGAL correctly -join the parts. If you need the child to have no overlap, or a different overlap, you can use -the `overlap=` argument: +By default, `attach()` places the child exactly flush with the surface of the parent. Sometimes +it's useful to have the child overlap the parent by insetting a bit. You can do this with the +`overlap=` argument to `attach()`. A positive value will inset the child into the parent, and +a negative value will outset out from the parent: ```openscad cube(50,center=true) - attach(TOP,TOP,overlap=0) cylinder(d1=50,d2=20,l=20); + attach(TOP,overlap=10) + cylinder(d=20,l=20); +``` + +```openscad +cube(50,center=true) + attach(TOP,overlap=-20) + cylinder(d=20,l=20); ``` If you want to position the child at the parent's anchorpoint, without re-orienting, you can @@ -217,6 +262,23 @@ cube(50, center=true) position([TOP,RIGHT,FRONT]) cylinder(d1=50,d2=20,l=20); ``` +## Attaching 2D Children +You can use attachments in 2D as well, but only in the XY plane. Also, the built-in `square()` +and `circle()` 2D modules do not support attachments. Instead, you should use the `rect()` and +`oval()` modules: + +```openscad-2D +rect(50,center=true) + attach(RIGHT,FRONT) + trapezoid(w1=30,w2=0,h=30); +``` + +```openscad-2D +oval(d=50) + attach(BACK,FRONT,overlap=5) + trapezoid(w1=30,w2=0,h=30); +``` + ## Anchor Arrows One way that is useful to show the position and orientation of an anchorpoint is by attaching an anchor arrow to that anchor. @@ -259,6 +321,7 @@ cylinder(h=100, d=100, center=true) show_anchors(s=30); ``` + ## Tagged Operations BOSL2 introduces the concept of tags. Tags are names that can be given to attachables, so that you can refer to them when performing `diff()`, `intersect()`, and `hulling()` operations. @@ -372,12 +435,169 @@ cube(50, center=true, $tags="hull") { ``` -## Masking Children -TBW +## 3D Masking Attachments +To make it easier to mask away shapes from various edges of an attachable parent shape, there +are a few specialized alternatives to the `attach()` and `position()` modules. + +### `edge_mask()` +If you have a 3D mask shape that you want to difference away from various edges, you can use +the `edge_mask()` module. This module will take a vertically oriented shape, and will rotate +and move it such that the BACK, RIGHT (X+,Y+) side of the shape will be aligned with the given +edges. The shape will be tagged as a "mask" so that you can use `diff("mask")`. For example, +here's a shape for rounding an edge: + +```openscad +module round_edge(l,r) difference() { + translate([-1,-1,-l/2]) + cube([r+1,r+1,l]); + translate([r,r]) + cylinder(h=l+1,r=r,center=true, $fn=quantup(segs(r),4)); +} +round_edge(l=30, r=19); +``` + +You can use that mask to round various edges of a cube: + +```openscad +module round_edge(l,r) difference() { + translate([-1,-1,-l/2]) + cube([r+1,r+1,l]); + translate([r,r]) + cylinder(h=l+1,r=r,center=true, $fn=quantup(segs(r),4)); +} +diff("mask") +cube([50,60,70],center=true) + edge_mask([TOP,"Z"],except=[BACK,TOP+LEFT]) + round_edge(l=71,r=10); +``` + +### `corner_mask()` +If you have a 3D mask shape that you want to difference away from various corners, you can use +the `corner_mask()` module. This module will take a shape and rotate and move it such that the +BACK RIGHT TOP (X+,Y+,Z+) side of the shape will be aligned with the given corner. The shape +will be tagged as a "mask" so that you can use `diff("mask")`. For example, here's a shape for +rounding a corner: + +```openscad +module round_corner(r) difference() { + translate(-[1,1,1]) + cube(r+1); + translate([r,r,r]) + sphere(r=r, style="aligned", $fn=quantup(segs(r),4)); +} +round_corner(r=10); +``` + +You can use that mask to round various corners of a cube: + +```openscad +module round_corner(r) difference() { + translate(-[1,1,1]) + cube(r+1); + translate([r,r,r]) + sphere(r=r, style="aligned", $fn=quantup(segs(r),4)); +} +diff("mask") +cube([50,60,70],center=true) + corner_mask([TOP,FRONT],LEFT+FRONT+TOP) + round_corner(r=10); +``` + +### Mix and Match Masks +You can use `edge_mask()` and `corner_mask()` together as well: + +```openscad +module round_corner(r) difference() { + translate(-[1,1,1]) + cube(r+1); + translate([r,r,r]) + sphere(r=r, style="aligned", $fn=quantup(segs(r),4)); +} +module round_edge(l,r) difference() { + translate([-1,-1,-l/2]) + cube([r+1,r+1,l]); + translate([r,r]) + cylinder(h=l+1,r=r,center=true, $fn=quantup(segs(r),4)); +} +diff("mask") +cube([50,60,70],center=true) { + edge_mask("ALL") round_edge(l=71,r=10); + corner_mask("ALL") round_corner(r=10); +} +``` + +## 2D Profile Mask Attachments +While 3D mask shapes give you a great deal of control, you need to make sure they are correctly +sized, and you need to provide separate mask shapes for corners and edges. Often, a single 2D +profile could be used to describe the edge mask shape (via `linear_extrude()`), and the corner +mask shape (via `rotate_extrude()`). This is where `edge_profile()`, `corner_profile()`, and +`face_profile()` come in. + +### `edge_profile()` +Using the `edge_profile()` module, you can provide a 2D profile shape and it will be linearly +extruded to a mask of the apropriate length for each given edge. The resultant mask will be +tagged with "mask" so that you can difference it away with `diff("mask")`. The 2D profile is +assumed to be oriented with the BACK, RIGHT (X+,Y+) quadrant as the "cutter edge" that gets +re-oriented towards the edges of the parent shape. A typical mask profile for chamfering an +edge may look like: + +```openscad +mask2d_roundover(10); +``` + +Using that mask profile, you can mask the edges of a cube like: + +```openscad +diff("mask") +cube([50,60,70],center=true) + edge_profile("ALL") + mask2d_roundover(10); +``` + +### `corner_profile()` +You can use the same profile to make a rounded corner mask as well: + +```openscad +diff("mask") +cube([50,60,70],center=true) + corner_profile("ALL", r=10) + mask2d_roundover(10); +``` + +### `face_profile()` +As a simple shortcut to apply a profile mask to all edges and corners of a face, you can use the +`face_profile()` module: + +```openscad +diff("mask") +cube([50,60,70],center=true) + face_profile(TOP, r=10) + mask2d_roundover(10); +``` ## Coloring Attachables -TBW +Usually, when coloring a shape with the `color()` module, the parent color overrides the colors of +all children. This is often not what you want: + +```openscad +color("red") spheroid(d=3) { + attach(CENTER,BOT) color("white") cyl(h=10, d=1) { + attach(TOP,BOT) color("green") cyl(h=5, d1=3, d2=0); + } +} +``` + +If you use the `recolor()` module, however, the child's color overrides the color of the parent. +This is probably easier to understand by example: + +```openscad +recolor("red") spheroid(d=3) { + attach(CENTER,BOT) recolor("white") cyl(h=10, d=1) { + attach(TOP,BOT) recolor("green") cyl(h=5, d1=3, d2=0); + } +} +``` ## Making Attachables From f1b5f1d8f6b07ce0837f98808d5f8eda3661385f Mon Sep 17 00:00:00 2001 From: github-actions Date: Thu, 20 May 2021 08:41:05 +0000 Subject: [PATCH 04/72] Bump release version. --- version.scad | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.scad b/version.scad index 4f3d75b..bafb2a4 100644 --- a/version.scad +++ b/version.scad @@ -6,7 +6,7 @@ ////////////////////////////////////////////////////////////////////// -BOSL_VERSION = [2,0,629]; +BOSL_VERSION = [2,0,630]; // Section: BOSL Library Version Functions From 56d4f1d193dedaed331caa423cfa76dd40259613 Mon Sep 17 00:00:00 2001 From: Garth Minette Date: Thu, 20 May 2021 12:56:10 -0700 Subject: [PATCH 05/72] Fixed some images in the attachments tutorial. --- tutorials/Attachments.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tutorials/Attachments.md b/tutorials/Attachments.md index 98a0341..b33fd93 100644 --- a/tutorials/Attachments.md +++ b/tutorials/Attachments.md @@ -541,7 +541,7 @@ assumed to be oriented with the BACK, RIGHT (X+,Y+) quadrant as the "cutter edge re-oriented towards the edges of the parent shape. A typical mask profile for chamfering an edge may look like: -```openscad +```openscad-2D mask2d_roundover(10); ``` @@ -581,6 +581,7 @@ Usually, when coloring a shape with the `color()` module, the parent color overr all children. This is often not what you want: ```openscad +$fn = 24; color("red") spheroid(d=3) { attach(CENTER,BOT) color("white") cyl(h=10, d=1) { attach(TOP,BOT) color("green") cyl(h=5, d1=3, d2=0); @@ -592,6 +593,7 @@ If you use the `recolor()` module, however, the child's color overrides the colo This is probably easier to understand by example: ```openscad +$fn = 24; recolor("red") spheroid(d=3) { attach(CENTER,BOT) recolor("white") cyl(h=10, d=1) { attach(TOP,BOT) recolor("green") cyl(h=5, d1=3, d2=0); From f5edb803329cff235eb06cef5b5a046b96475c38 Mon Sep 17 00:00:00 2001 From: Garth Minette Date: Thu, 20 May 2021 16:36:26 -0700 Subject: [PATCH 06/72] Added named anchors section to attachments tutorial. --- tutorials/Attachments.md | 70 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 69 insertions(+), 1 deletion(-) diff --git a/tutorials/Attachments.md b/tutorials/Attachments.md index b33fd93..a5cce55 100644 --- a/tutorials/Attachments.md +++ b/tutorials/Attachments.md @@ -905,6 +905,74 @@ stellate_cube() show_anchors(50); ## Making Named Anchors -TBW +While vector anchors are often useful, sometimes there are logically extra attachment points that +aren't on the perimeter of the shape. This is what named string anchors are for. For example, +the `teardrop()` shape uses a cylindrical geometry for it's vector anchors, but it also provides +a named anchor "cap" that is at the tip of the hat of the teardrop shape. + +Named anchors are passed as an array of `anchorpt()`s to the `anchors=` argument of `attachable()`. +The `anchorpt()` call takes a name string, a positional point, an orientation vector, and a spin. +The name is the name of the anchor. The positional point is where the anchorpoint is at. The +orientation vector is the direction that a child attached at that anchorpoint should be oriented. +The spin is the number of degrees that an attached child should be rotated counter-clockwise around +the orientation vector. Spin is optional, and defaults to 0. + +To make a simple attachable shape similar to a `teardrop()` that provides a "cap" anchor, you may +define it like this: + +```openscad +module raindrop(r, thick, anchor=CENTER, spin=0, orient=UP) { + anchors = [ + anchorpt("cap", [0,r/sin(45),0], BACK, 0) + ]; + attachable(anchor,spin,orient, r=r, l=thick, anchors=anchors) { + linear_extrude(height=thick, center=true) { + circle(r=r); + back(r*sin(45)) zrot(45) square(r, center=true); + } + children(); + } +} +raindrop(r=25, thick=20, anchor="cap"); +``` + +If you want multiple named anchors, just add them to the list of anchors: + +```openscad-Spin +module raindrop(r, thick, anchor=CENTER, spin=0, orient=UP) { + anchors = [ + anchorpt("captop", [0,r/sin(45), thick/2], BACK+UP, 0), + anchorpt("cap", [0,r/sin(45), 0 ], BACK, 0), + anchorpt("capbot", [0,r/sin(45),-thick/2], BACK+DOWN, 0) + ]; + attachable(anchor,spin,orient, r=r, l=thick, anchors=anchors) { + linear_extrude(height=thick, center=true) { + circle(r=r); + back(r*sin(45)) zrot(45) square(r, center=true); + } + children(); + } +} +raindrop(r=15, thick=10) show_anchors(); +``` + +Sometimes the named anchor you want to add may be at a point that is reached through a complicated +set of translations and rotations. One quick way to calculate that point is to reproduce those +transformations in a transformation matrix chain. This is simplified by how you can use the +function forms of almost all the transformation modules to get the transformation matrices, and +chain them together with matrix multiplication. For example, if you have: + +``` +scale([1.1, 1.2, 1.3]) xrot(15) zrot(25) right(20) sphere(d=1); +``` + +and you want to calculate the centerpoint of the sphere, you can do it like: + +``` +sphere_pt = apply( + scale([1.1, 1.2, 1.3]) * xrot(15) * zrot(25) * right(20), + [0,0,0] +); +``` From 57b1d0412c49e0761cdccb63e382525a9055a860 Mon Sep 17 00:00:00 2001 From: github-actions Date: Thu, 20 May 2021 23:41:00 +0000 Subject: [PATCH 07/72] Bump release version. --- version.scad | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.scad b/version.scad index bafb2a4..bc5717c 100644 --- a/version.scad +++ b/version.scad @@ -6,7 +6,7 @@ ////////////////////////////////////////////////////////////////////// -BOSL_VERSION = [2,0,630]; +BOSL_VERSION = [2,0,631]; // Section: BOSL Library Version Functions From 0666f6482ca34dd569098003a44014255ebfb5bf Mon Sep 17 00:00:00 2001 From: Michael Diamond Date: Fri, 21 May 2021 00:46:17 -0700 Subject: [PATCH 08/72] Refactor and tidy BOSL's shell scripts * Fixed ShellCheck.net warnings, including * exit -1: https://github.com/koalaman/shellcheck/wiki/SC2242 * cd || exit: https://github.com/koalaman/shellcheck/wiki/SC2164 * "$foo": https://github.com/koalaman/shellcheck/wiki/SC2086 * $(( $foo )): https://github.com/koalaman/shellcheck/wiki/SC2004 * grep | wc -l: https://github.com/koalaman/shellcheck/wiki/SC2126 * Use arrays instead of unquoted strings for loops and command arguments * Use (( ... )) arithmetic context instead of string comparisons * Use bash's built-in regex support instead of grep+sed+awk * Write errors to stderr --- scripts/increment_version.sh | 21 ++++++++-------- scripts/linecount.sh | 21 ++++++---------- scripts/make_tutorials.sh | 46 +++++++++++++++++------------------ scripts/purge_wiki_history.sh | 7 +++--- scripts/run_tests.sh | 32 +++++++++++------------- 5 files changed, 58 insertions(+), 69 deletions(-) diff --git a/scripts/increment_version.sh b/scripts/increment_version.sh index 1109536..815486a 100755 --- a/scripts/increment_version.sh +++ b/scripts/increment_version.sh @@ -1,15 +1,16 @@ -#!/bin/sh +#!/bin/bash VERFILE="version.scad" -vernums=$(grep ^BOSL_VERSION "$VERFILE" | sed 's/^.*[[]\([0-9,]*\)[]].*$/\1/') -major=$(echo "$vernums" | awk -F, '{print $1}') -minor=$(echo "$vernums" | awk -F, '{print $2}') -revision=$(echo "$vernums" | awk -F, '{print $3}') +if [[ "$(cat "$VERFILE")" =~ BOSL_VERSION.*=.*\[([0-9]+),\ *([0-9]+),\ *([0-9]+)\]\; ]]; then + major=${BASH_REMATCH[1]} minor=${BASH_REMATCH[2]} revision=${BASH_REMATCH[3]} + new_revision=$(( revision+1 )) -newrev=$(($revision+1)) -echo "Current Version: $major.$minor.$revision" -echo "New Version: $major.$minor.$newrev" - -sed -i '' 's/^BOSL_VERSION = .*$/BOSL_VERSION = ['"$major,$minor,$newrev];/g" $VERFILE + echo "Current Version: $major.$minor.$revision" + echo "New Version: $major.$minor.$new_revision" + sed -i 's/^BOSL_VERSION = .*$/BOSL_VERSION = ['"$major,$minor,$new_revision];/g" "$VERFILE" +else + echo "Could not extract version number from $VERFILE" >&2 + exit 1 +fi diff --git a/scripts/linecount.sh b/scripts/linecount.sh index 234d8cd..16ca273 100755 --- a/scripts/linecount.sh +++ b/scripts/linecount.sh @@ -1,17 +1,12 @@ #!/bin/bash -lib_comment_lines=$(grep '^// ' *.scad | wc -l) -lib_code_lines=$(grep '^ *[^ /]' *.scad | wc -l) -script_code_lines=$(grep '^ *[^ /]' scripts/*.sh scripts/*.py | wc -l) -example_code_lines=$(grep '^ *[^ /]' examples/*.scad | wc -l) -test_code_lines=$(grep '^ *[^ /]' tests/*.scad | wc -l) -tutorial_lines=$(grep '^ *[^ /]' tutorials/*.md | wc -l) +lib_comment_lines=$(cat -- *.scad | grep -c '^// ') +tutorial_lines=$(cat tutorials/*.md | grep -c '^ *[^ /]') -y=$(printf "%06d" 13) - -printf "Documentation Lines : %6d\n" $(($lib_comment_lines+$tutorial_lines)) -printf "Example Code Lines : %6d\n" $example_code_lines -printf "Library Code Lines : %6d\n" $lib_code_lines -printf "Support Script Lines: %6d\n" $script_code_lines -printf "Test Code Lines : %6d\n" $test_code_lines +printf '%-20s: %6d\n' \ + 'Documentation Lines' "$(( lib_comment_lines + tutorial_lines ))" \ + 'Example Code Lines' "$(cat examples/*.scad | grep -c '^ *[^ /]')" \ + 'Library Code Lines' "$(cat -- *.scad | grep -c '^ *[^ /]')" \ + 'Support Script Lines' "$(cat scripts/*.sh scripts/*.py | grep -c '^ *[^ /]')" \ + 'Test Code Lines' "$(cat tests/*.scad | grep -c '^ *[^ /]')" diff --git a/scripts/make_tutorials.sh b/scripts/make_tutorials.sh index c092db5..b728607 100755 --- a/scripts/make_tutorials.sh +++ b/scripts/make_tutorials.sh @@ -1,40 +1,38 @@ #!/bin/bash -FORCED="" -FILES="" -DISPMD="" +DISPMD=0 +GEN_ARGS=() +FILES=() for opt in "$@" ; do - case $opt in - -f ) FORCED=$opt ;; - -d ) DISPMD=$opt ;; - -* ) echo "Unknown option $opt"; exit -1 ;; - * ) FILES="$FILES $opt" ;; + case "$opt" in + -f ) GEN_ARGS+=(-f) ;; + -d ) DISPMD=1 ;; + -* ) echo "Unknown option $opt" >&2; exit 1 ;; + * ) FILES+=("$opt") ;; esac done -if [[ "$FILES" != "" ]]; then - PREVIEW_LIBS="$FILES" -else - PREVIEW_LIBS="Shapes2d Shapes3d Transforms Distributors Mutators Attachments Paths FractalTree" +if (( ${#FILES[@]} == 0 )); then + FILES=(Shapes2d Shapes3d Transforms Distributors Mutators Attachments Paths FractalTree) fi -dir="$(basename $PWD)" -if [ "$dir" = "BOSL2" ]; then - cd BOSL2.wiki -elif [ "$dir" != "BOSL2.wiki" ]; then - echo "Must run this script from the BOSL2 or BOSL2/BOSL2.wiki directories." +# Try to cd to the BOSL2.wiki directory if run from the BOSL2 root +if [[ "$(basename "$PWD")" != "BOSL2.wiki" ]]; then + if ! cd BOSL2.wiki; then + echo "BOSL2.wiki directory not found, try running from the BOSL2 or BOSL2/BOSL2.wiki directory" >&2 exit 1 + fi fi rm -f tmp_*.scad -for base in $PREVIEW_LIBS; do - base="$(basename $base .md)" +for base in "${FILES[@]}"; do + base="$(basename "$base" .md)" mkdir -p images/tutorials - rm -f images/tutorials/${base}_*.png images/tutorials/${base}_*.gif - echo "$base.md" - ../scripts/tutorial_gen.py ../tutorials/$base.md -o Tutorial-$base.md $FORCED -I images/tutorials/ || exit 1 - if [ "$DISPMD" != "" ]; then - open -a Typora Tutorial-$base.md + rm -f "images/tutorials/${base}"_*.png "images/tutorials/${base}"_*.gif + echo "${base}.md" + ../scripts/tutorial_gen.py "../tutorials/${base}.md" -o "Tutorial-${base}.md" "${GEN_ARGS[@]}" -I images/tutorials/ || exit 1 + if (( DISPMD )); then + open -a Typora "Tutorial-${base}.md" fi done diff --git a/scripts/purge_wiki_history.sh b/scripts/purge_wiki_history.sh index 5392677..d53e62d 100755 --- a/scripts/purge_wiki_history.sh +++ b/scripts/purge_wiki_history.sh @@ -1,10 +1,11 @@ #!/bin/bash if [[ ! -d BOSL2.wiki/.git ]] ; then - echo "Must be run from the BOSL2 directory, with the BOSL2.wiki repo inside." - exit -1 + echo "Must be run from above the BOSL2.wiki repo." >&2 + exit 1 fi +set -e # short-circuit if any command fails cd BOSL2.wiki rm -rf .git git init @@ -12,5 +13,3 @@ git add . git commit -m "Purged wiki history." git remote add origin git@github.com:revarbat/BOSL2.wiki.git git push -u --force origin master -cd .. - diff --git a/scripts/run_tests.sh b/scripts/run_tests.sh index 5fc0099..69fe94a 100755 --- a/scripts/run_tests.sh +++ b/scripts/run_tests.sh @@ -1,35 +1,31 @@ #!/bin/bash -if [ "$(uname -s)" != "Darwin" ]; then - OPENSCAD=openscad -else +OPENSCAD=openscad +if [ "$(uname -s)" == "Darwin" ]; then OPENSCAD=/Applications/OpenSCAD.app/Contents/MacOS/OpenSCAD fi -if [ "$*" != "" ] ; then - INFILES="$*" -else - INFILES="tests/test_*.scad" +INFILES=("$@") +if (( ${#INFILES[@]} == 0 )); then + INFILES=(tests/test_*.scad) fi OUTCODE=0 -for testscript in $INFILES ; do - repname="$(basename $testscript | sed 's/^test_//')" - testfile="tests/test_$repname" - if [ -f "$testfile" ] ; then - ${OPENSCAD} -o out.echo --hardwarnings --check-parameters true --check-parameter-ranges true $testfile 2>&1 +for testfile in "${INFILES[@]}"; do + if [[ -f "$testfile" ]] ; then + repname="$(basename "$testfile" | sed 's/^test_//')" + "${OPENSCAD}" -o out.echo --hardwarnings --check-parameters true --check-parameter-ranges true "$testfile" 2>&1 retcode=$? - res=$(cat out.echo) - if [ $retcode -eq 0 ] && [ "$res" = "" ] ; then + output=$(cat out.echo) + if (( retcode == 0 )) && [[ "$output" = "" ]]; then echo "$repname: PASS" else echo "$repname: FAIL!" - cat out.echo - echo - OUTCODE=-1 + echo "$output" + OUTCODE="$retcode" fi rm -f out.echo fi done -exit $OUTCODE +exit "$OUTCODE" From 441f6a8b8790c9ccd33acf6921a99b59b3099711 Mon Sep 17 00:00:00 2001 From: Garth Minette Date: Fri, 21 May 2021 01:31:16 -0700 Subject: [PATCH 09/72] Added cubetruss_support(). --- cubetruss.scad | 60 ++++++++++++++++++++++++++++++++++++++++ tutorials/Attachments.md | 2 +- 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/cubetruss.scad b/cubetruss.scad index 880a301..b9bf68d 100644 --- a/cubetruss.scad +++ b/cubetruss.scad @@ -101,6 +101,66 @@ module cubetruss_segment(size, strut, bracing, anchor=CENTER, spin=0, orient=UP) } +// Module: cubetruss_support() +// Usage: +// cubetruss_support(, ); +// Description: +// Creates a single cubetruss support. +// Arguments: +// size = The length of each side of the cubetruss cubes. Default: `$cubetruss_size` (usually 30) +// strut = The width of the struts on the cubetruss cubes. Default: `$cubetruss_strut_size` (usually 3) +// extents = If given as an integer, specifies the number of vertical segments for the support. If given as a list of 3 integers, specifies the number of segments in the X, Y, and Z directions. Default: 1. +// --- +// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` +// spin = Rotate this many degrees around the Z axis. See [spin](attachments.scad#spin). Default: `0` +// orient = Vector to rotate top towards. See [orient](attachments.scad#orient). Default: `UP` +// Topics: Attachable, Trusses +// Example(VPT=[0,0,0],VPD=150): +// cubetruss_support(); +// Example(VPT=[0,0,0],VPD=200): +// cubetruss_support(extents=2); +// Example(VPT=[0,0,0],VPD=250): +// cubetruss_support(extents=3); +// Example(VPT=[0,0,0],VPD=350): +// cubetruss_support(extents=[2,2,3]); +// Example(VPT=[0,0,0],VPD=150): +// cubetruss_support(strut=4); +// Example(VPT=[0,0,0],VPD=260): +// cubetruss_support(extents=2) show_anchors(); +module cubetruss_support(size, strut, extents=1, anchor=CENTER, spin=0, orient=UP) { + extents = is_num(extents)? [1,1,extents] : extents; + size = is_undef(size)? $cubetruss_size : size; + strut = is_undef(strut)? $cubetruss_strut_size : strut; + assert(is_int(extents.x) && extents.x > 0); + assert(is_int(extents.y) && extents.y > 0); + assert(is_int(extents.z) && extents.z > 0); + w = (size-strut) * extents.x + strut; + l = (size-strut) * extents.y + strut; + h = (size-strut) * extents.z + strut; + attachable(anchor,spin,orient, size=[w,l,h], size2=[l,0], shift=[0,l/2], axis=DOWN) { + xcopies(size-strut, n=extents.x) { + difference() { + half_of(BACK/extents.y + UP/extents.z, s=size*(max(extents)+1)) + cube([size,l,h], center=true); + half_of(BACK/extents.y + UP/extents.z, cp=strut, s=size*(max(extents)+1)) { + ycopies(size-strut, n=extents.y) { + zcopies(size-strut, n=extents.z) { + cyl(h=size+1, d=size-2*strut, circum=true, realign=true, orient=RIGHT, $fn=8); + cyl(h=size+1, d=size-2*strut, circum=true, realign=true, $fn=8); + cube(size-2*strut, center=true); + } + } + } + zcopies(size-strut, n=extents.z) { + cyl(h=extents.y*size+1, d=size-2*strut, circum=true, realign=true, orient=BACK, $fn=8); + } + } + } + children(); + } +} + + // Module: cubetruss_clip() // Usage: // cubetruss_clip(extents, , , ); diff --git a/tutorials/Attachments.md b/tutorials/Attachments.md index a5cce55..dc94bbd 100644 --- a/tutorials/Attachments.md +++ b/tutorials/Attachments.md @@ -938,7 +938,7 @@ raindrop(r=25, thick=20, anchor="cap"); If you want multiple named anchors, just add them to the list of anchors: -```openscad-Spin +```openscad-FlatSpin,VPD=150 module raindrop(r, thick, anchor=CENTER, spin=0, orient=UP) { anchors = [ anchorpt("captop", [0,r/sin(45), thick/2], BACK+UP, 0), From 6afbc4637d6844a4743ab6cf22d9eb53896d5b40 Mon Sep 17 00:00:00 2001 From: github-actions Date: Fri, 21 May 2021 08:38:21 +0000 Subject: [PATCH 10/72] Bump release version. --- version.scad | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.scad b/version.scad index bc5717c..30d052e 100644 --- a/version.scad +++ b/version.scad @@ -6,7 +6,7 @@ ////////////////////////////////////////////////////////////////////// -BOSL_VERSION = [2,0,631]; +BOSL_VERSION = [2,0,632]; // Section: BOSL Library Version Functions From 4c53de802d948bdbb19eeea5a2fc04e3f80aefca Mon Sep 17 00:00:00 2001 From: Garth Minette Date: Fri, 21 May 2021 02:19:14 -0700 Subject: [PATCH 11/72] Fix for sed in increment_version.sh --- scripts/increment_version.sh | 3 ++- scripts/purge_wiki_history.sh | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/increment_version.sh b/scripts/increment_version.sh index 815486a..298ef3c 100755 --- a/scripts/increment_version.sh +++ b/scripts/increment_version.sh @@ -9,7 +9,8 @@ if [[ "$(cat "$VERFILE")" =~ BOSL_VERSION.*=.*\[([0-9]+),\ *([0-9]+),\ *([0-9]+ echo "Current Version: $major.$minor.$revision" echo "New Version: $major.$minor.$new_revision" - sed -i 's/^BOSL_VERSION = .*$/BOSL_VERSION = ['"$major,$minor,$new_revision];/g" "$VERFILE" + sed -i.bak -e 's/^BOSL_VERSION = .*$/BOSL_VERSION = ['"$major,$minor,$new_revision];/g" "$VERFILE" + rm "$VERFILE".bak else echo "Could not extract version number from $VERFILE" >&2 exit 1 diff --git a/scripts/purge_wiki_history.sh b/scripts/purge_wiki_history.sh index d53e62d..7cef346 100755 --- a/scripts/purge_wiki_history.sh +++ b/scripts/purge_wiki_history.sh @@ -11,5 +11,6 @@ rm -rf .git git init git add . git commit -m "Purged wiki history." +git config pull.rebase false git remote add origin git@github.com:revarbat/BOSL2.wiki.git git push -u --force origin master From 295a5a64245f0a4d32716b4c41aab3218bc8a362 Mon Sep 17 00:00:00 2001 From: github-actions Date: Fri, 21 May 2021 09:21:48 +0000 Subject: [PATCH 12/72] Bump release version. --- version.scad | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.scad b/version.scad index 30d052e..3ed1f66 100644 --- a/version.scad +++ b/version.scad @@ -6,7 +6,7 @@ ////////////////////////////////////////////////////////////////////// -BOSL_VERSION = [2,0,632]; +BOSL_VERSION = [2,0,633]; // Section: BOSL Library Version Functions From 18b0f48ca8edeff24c91e824976c30b8f474bc29 Mon Sep 17 00:00:00 2001 From: Garth Minette Date: Fri, 21 May 2021 02:39:02 -0700 Subject: [PATCH 13/72] Fixed some cubetruss example imagesd. --- cubetruss.scad | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cubetruss.scad b/cubetruss.scad index b9bf68d..0fbaadb 100644 --- a/cubetruss.scad +++ b/cubetruss.scad @@ -430,7 +430,7 @@ module cubetruss_uclip(dual=true, size, strut, clipthick, anchor=CENTER, spin=0, // spin = Rotate this many degrees around the Z axis. See [spin](attachments.scad#spin). Default: `0` // orient = Vector to rotate top towards. See [orient](attachments.scad#orient). Default: `UP` // Topics: Attachable, Trusses -// Examples(FlatSpin,VPD=444): +// Examples: // cubetruss(extents=3); // cubetruss(extents=3, clips=FRONT); // cubetruss(extents=3, clips=[FRONT,BACK]); @@ -500,7 +500,7 @@ module cubetruss(extents=6, clips=[], bracing, size, strut, clipthick, anchor=CE // spin = Rotate this many degrees around the Z axis. See [spin](attachments.scad#spin). Default: `0` // orient = Vector to rotate top towards. See [orient](attachments.scad#orient). Default: `UP` // Topics: Attachable, Trusses -// Examples(FlatSpin): +// Examples: // cubetruss_corner(extents=2); // cubetruss_corner(extents=2, h=2); // cubetruss_corner(extents=[3,3,0,0,2]); From a7888099aa97bf305f686b04ffa8fce44daf6050 Mon Sep 17 00:00:00 2001 From: github-actions Date: Fri, 21 May 2021 09:43:18 +0000 Subject: [PATCH 14/72] Bump release version. --- version.scad | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.scad b/version.scad index 3ed1f66..d7b4fe1 100644 --- a/version.scad +++ b/version.scad @@ -6,7 +6,7 @@ ////////////////////////////////////////////////////////////////////// -BOSL_VERSION = [2,0,633]; +BOSL_VERSION = [2,0,634]; // Section: BOSL Library Version Functions From 19c521913d06e6dce929ac25f9699fe559f6e788 Mon Sep 17 00:00:00 2001 From: Adrian Mariano Date: Fri, 21 May 2021 19:03:07 -0400 Subject: [PATCH 15/72] Added general root finder --- math.scad | 74 ++++++++++++++++++++++++++++++++++++++++++-- tests/test_math.scad | 32 +++++++++++++++++++ 2 files changed, 104 insertions(+), 2 deletions(-) diff --git a/math.scad b/math.scad index 73659ca..15fc927 100644 --- a/math.scad +++ b/math.scad @@ -1631,8 +1631,6 @@ function c_ident(n) = [for (i = [0:1:n-1]) [for (j = [0:1:n-1]) (i==j)?[1,0]:[0, // Compute the norm of a complex number or vector. function c_norm(z) = norm_fro(z); - - // Section: Polynomials // Function: quadratic_roots() @@ -1859,4 +1857,76 @@ function real_roots(p,eps=undef,tol=1e-14) = ? [for(z=roots) if (abs(z.y)/(1+norm(z)) Date: Fri, 21 May 2021 23:05:16 +0000 Subject: [PATCH 16/72] Bump release version. --- version.scad | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.scad b/version.scad index d7b4fe1..7193cf7 100644 --- a/version.scad +++ b/version.scad @@ -6,7 +6,7 @@ ////////////////////////////////////////////////////////////////////// -BOSL_VERSION = [2,0,634]; +BOSL_VERSION = [2,0,635]; // Section: BOSL Library Version Functions From 16ec2ecbd619a6e80800cf909650ea0c13f54dbd Mon Sep 17 00:00:00 2001 From: Adrian Mariano Date: Fri, 21 May 2021 20:21:50 -0400 Subject: [PATCH 17/72] doc addition --- math.scad | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/math.scad b/math.scad index 15fc927..4db010d 100644 --- a/math.scad +++ b/math.scad @@ -1838,12 +1838,19 @@ function _poly_roots(p, pderiv, s, z, tol, i=0) = // parts are zero. You can specify eps, in which case the test is // z.y/(1+norm(z)) < eps. Because // of poor convergence and higher error for repeated roots, such roots may -// be missed by the algorithm because their imaginary part is large. +// be missed by the algorithm because their imaginary part is large. // Arguments: // p = polynomial to solve as coefficient list, highest power term first // eps = used to determine whether imaginary parts of roots are zero // tol = tolerance for the complex polynomial root finder +// The algorithm is based on Brent's method and is a combination of +// bisection and inverse quadratic approximation, where bisection occurs +// at every step, with refinement using inverse quadratic approximation +// only when that approximation gives a good result. The detail +// of how to decide when to use the quadratic came from an article +// by Crenshaw on "The World's Best Root Finder". +// https://www.embedded.com/worlds-best-root-finder/ function real_roots(p,eps=undef,tol=1e-14) = assert( is_vector(p), "Invalid polynomial." ) let( p = _poly_trim(p,eps=0) ) From bcd275a9a67c88344c28c150c284aa397ebe33ce Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 22 May 2021 00:27:59 +0000 Subject: [PATCH 18/72] Bump release version. --- version.scad | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.scad b/version.scad index 7193cf7..6445528 100644 --- a/version.scad +++ b/version.scad @@ -6,7 +6,7 @@ ////////////////////////////////////////////////////////////////////// -BOSL_VERSION = [2,0,635]; +BOSL_VERSION = [2,0,636]; // Section: BOSL Library Version Functions From 70805ecfa53db9d676b6f24cd2f2d5bd0b8219b0 Mon Sep 17 00:00:00 2001 From: Garth Minette Date: Fri, 21 May 2021 23:09:14 -0700 Subject: [PATCH 19/72] Restrict pull merge automation. --- .github/workflows/docsgen.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/docsgen.yml b/.github/workflows/docsgen.yml index 9fa44bd..447de46 100644 --- a/.github/workflows/docsgen.yml +++ b/.github/workflows/docsgen.yml @@ -1,11 +1,13 @@ name: CI on: - push: + pull_request: branches: - master + types: [closed] jobs: GenerateDocs: + if: github.event.pull_request.merged == true runs-on: macos-10.15 steps: - name: Checkout From 9141cb6ec24355f5ad7eb7ae6693a088802ea901 Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 22 May 2021 06:13:31 +0000 Subject: [PATCH 20/72] Bump release version. --- version.scad | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.scad b/version.scad index d7b4fe1..7193cf7 100644 --- a/version.scad +++ b/version.scad @@ -6,7 +6,7 @@ ////////////////////////////////////////////////////////////////////// -BOSL_VERSION = [2,0,634]; +BOSL_VERSION = [2,0,635]; // Section: BOSL Library Version Functions From bf60136d403eb39b91a4c5294bf1a1b0d13ebc2f Mon Sep 17 00:00:00 2001 From: Garth Minette Date: Sat, 22 May 2021 00:50:20 -0700 Subject: [PATCH 21/72] Docs typo in turtle3d() --- turtle3d.scad | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/turtle3d.scad b/turtle3d.scad index 7f1e4c5..3ebad05 100644 --- a/turtle3d.scad +++ b/turtle3d.scad @@ -90,7 +90,7 @@ function _rotpart(T) = [for(i=[0:3]) [for(j=[0:3]) j<3 || i==3 ? T[i][j] : 0]]; // "xjump" | | x | Move the turtle's x position to the specified value // "yjump | | y | Move the turtle's y position to the specified value // "zjump | | y | Move the turtle's y position to the specified value -// "left" | [angle] | Turn turtle left by specified angle or default angle +// "left" | | [angle] | Turn turtle left by specified angle or default angle // "right" | | [angle] | Turn turtle to the right by specified angle or default angle // "up" | | [angle] | Turn turtle up by specified angle or default angle // "down" | | [angle] | Turn turtle down by specified angle or default angle From 650205c40267ebffbf5d7c980614d8b2a34a1e63 Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 22 May 2021 07:53:33 +0000 Subject: [PATCH 22/72] Bump release version. --- version.scad | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.scad b/version.scad index 7193cf7..6445528 100644 --- a/version.scad +++ b/version.scad @@ -6,7 +6,7 @@ ////////////////////////////////////////////////////////////////////// -BOSL_VERSION = [2,0,635]; +BOSL_VERSION = [2,0,636]; // Section: BOSL Library Version Functions From f8e829c3b7ba1ce030047f165c77d91d54d8c982 Mon Sep 17 00:00:00 2001 From: Garth Minette Date: Sat, 22 May 2021 01:41:25 -0700 Subject: [PATCH 23/72] Reformatted shapes.scad examples. --- shapes.scad | 107 +++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 86 insertions(+), 21 deletions(-) diff --git a/shapes.scad b/shapes.scad index f485b5a..0617e86 100644 --- a/shapes.scad +++ b/shapes.scad @@ -57,17 +57,41 @@ // Example: Rounded Edges, Untrimmed Corners // cuboid([30,40,50], rounding=10, trimcorners=false); // Example: Chamferring Selected Edges -// cuboid([30,40,50], chamfer=5, edges=[TOP+FRONT,TOP+RIGHT,FRONT+RIGHT], $fn=24); +// cuboid( +// [30,40,50], chamfer=5, +// edges=[TOP+FRONT,TOP+RIGHT,FRONT+RIGHT], +// $fn=24 +// ); // Example: Rounding Selected Edges -// cuboid([30,40,50], rounding=5, edges=[TOP+FRONT,TOP+RIGHT,FRONT+RIGHT], $fn=24); +// cuboid( +// [30,40,50], rounding=5, +// edges=[TOP+FRONT,TOP+RIGHT,FRONT+RIGHT], +// $fn=24 +// ); // Example: Negative Chamferring -// cuboid([30,40,50], chamfer=-5, edges=[TOP,BOT], except_edges=RIGHT, $fn=24); +// cuboid( +// [30,40,50], chamfer=-5, +// edges=[TOP,BOT], except_edges=RIGHT, +// $fn=24 +// ); // Example: Negative Chamferring, Untrimmed Corners -// cuboid([30,40,50], chamfer=-5, edges=[TOP,BOT], except_edges=RIGHT, trimcorners=false, $fn=24); +// cuboid( +// [30,40,50], chamfer=-5, +// edges=[TOP,BOT], except_edges=RIGHT, +// trimcorners=false, $fn=24 +// ); // Example: Negative Rounding -// cuboid([30,40,50], rounding=-5, edges=[TOP,BOT], except_edges=RIGHT, $fn=24); +// cuboid( +// [30,40,50], rounding=-5, +// edges=[TOP,BOT], except_edges=RIGHT, +// $fn=24 +// ); // Example: Negative Rounding, Untrimmed Corners -// cuboid([30,40,50], rounding=-5, edges=[TOP,BOT], except_edges=RIGHT, trimcorners=false, $fn=24); +// cuboid( +// [30,40,50], rounding=-5, +// edges=[TOP,BOT], except_edges=RIGHT, +// trimcorners=false, $fn=24 +// ); // Example: Standard Connectors // cuboid(40) show_anchors(); module cuboid( @@ -417,7 +441,11 @@ function cuboid( // prismoid(100, 80, chamfer=[0,5,10,15], h=30); // Example: Mixing Chamfer and Rounding // include -// prismoid(100, 80, chamfer=[0,5,0,10], rounding=[5,0,10,0], h=30); +// prismoid( +// 100, 80, h=30, +// chamfer=[0,5,0,10], +// rounding=[5,0,10,0] +// ); // Example: Really Mixing It Up // include // prismoid( @@ -588,7 +616,11 @@ function prismoid( // rect_tube(isize=[60,80], wall=5, h=30); // rect_tube(size=[100,60], isize=[90,50], h=30); // rect_tube(size1=[100,60], size2=[70,40], wall=5, h=30); -// rect_tube(size1=[100,60], size2=[70,40], isize1=[40,20], isize2=[65,35], h=15); +// Example: +// rect_tube( +// size1=[100,60], size2=[70,40], +// isize1=[40,20], isize2=[65,35], h=15 +// ); // Example: Outer Rounding Only // include // rect_tube(size=100, wall=5, rounding=10, irounding=0, h=30); @@ -603,16 +635,30 @@ function prismoid( // rect_tube(size=100, wall=5, chamfer=10, irounding=8, h=30); // Example: Gradiant Rounding // include -// rect_tube(size1=100, size2=80, wall=5, rounding1=10, rounding2=0, irounding1=8, irounding2=0, h=30); +// rect_tube( +// size1=100, size2=80, wall=5, h=30, +// rounding1=10, rounding2=0, +// irounding1=8, irounding2=0 +// ); // Example: Per Corner Rounding // include -// rect_tube(size=100, wall=10, rounding=[0,5,10,15], irounding=0, h=30); +// rect_tube( +// size=100, wall=10, h=30, +// rounding=[0,5,10,15], irounding=0 +// ); // Example: Per Corner Chamfer // include -// rect_tube(size=100, wall=10, chamfer=[0,5,10,15], ichamfer=0, h=30); +// rect_tube( +// size=100, wall=10, h=30, +// chamfer=[0,5,10,15], ichamfer=0 +// ); // Example: Mixing Chamfer and Rounding // include -// rect_tube(size=100, wall=10, chamfer=[0,5,0,10], ichamfer=0, rounding=[5,0,10,0], irounding=0, h=30); +// rect_tube( +// size=100, wall=10, h=30, +// chamfer=[0,5,0,10], ichamfer=0, +// rounding=[5,0,10,0], irounding=0 +// ); // Example: Really Mixing It Up // include // rect_tube( @@ -838,7 +884,11 @@ function right_triangle(size=[1,1,1], center, anchor, spin=0, orient=UP) = // } // // Example: Putting it all together -// cyl(l=40, d1=25, d2=15, chamfer1=10, chamfang1=30, from_end=true, rounding2=5); +// cyl( +// l=40, d1=25, d2=15, +// chamfer1=10, chamfang1=30, +// from_end=true, rounding2=5 +// ); // // Example: External Chamfers // cyl(l=50, r=30, chamfer=-5, chamfang=30, $fa=1, $fs=1); @@ -1702,9 +1752,15 @@ module pie_slice( // // Example: // union() { -// translate([0,2,-4]) cube([20, 4, 24], anchor=BOTTOM); -// translate([0,-10,-4]) cube([20, 20, 4], anchor=BOTTOM); -// color("green") interior_fillet(l=20, r=10, spin=180, orient=RIGHT); +// translate([0,2,-4]) +// cube([20, 4, 24], anchor=BOTTOM); +// translate([0,-10,-4]) +// cube([20, 20, 4], anchor=BOTTOM); +// color("green") +// interior_fillet( +// l=20, r=10, +// spin=180, orient=RIGHT +// ); // } // // Example: @@ -1762,21 +1818,30 @@ module interior_fillet(l=1.0, r, ang=90, overlap=0.01, d, anchor=FRONT+LEFT, spi // orient = Vector to rotate top towards. See [orient](attachments.scad#orient). Default: `UP` // Example: // heightfield(size=[100,100], bottom=-20, data=[ -// for (y=[-180:4:180]) [for(x=[-180:4:180]) 10*cos(3*norm([x,y]))] +// for (y=[-180:4:180]) [ +// for(x=[-180:4:180]) +// 10*cos(3*norm([x,y])) +// ] // ]); // Example: // intersection() { // heightfield(size=[100,100], data=[ -// for (y=[-180:5:180]) [for(x=[-180:5:180]) 10+5*cos(3*x)*sin(3*y)] +// for (y=[-180:5:180]) [ +// for(x=[-180:5:180]) +// 10+5*cos(3*x)*sin(3*y) +// ] // ]); // cylinder(h=50,d=100); // } -// Example(NORENDER): Heightfield by Function +// Example: Heightfield by Function // fn = function (x,y) 10*sin(x*360)*cos(y*360); // heightfield(size=[100,100], data=fn); -// Example(NORENDER): Heightfield by Function, with Specific Ranges +// Example: Heightfield by Function, with Specific Ranges // fn = function (x,y) 2*cos(5*norm([x,y])); -// heightfield(size=[100,100], bottom=-20, data=fn, xrange=[-180:2:180], yrange=[-180:2:180]); +// heightfield( +// size=[100,100], bottom=-20, data=fn, +// xrange=[-180:2:180], yrange=[-180:2:180] +// ); module heightfield(data, size=[100,100], bottom=-20, maxz=100, xrange=[-1:0.04:1], yrange=[-1:0.04:1], style="default", convexity=10, anchor=CENTER, spin=0, orient=UP) { size = is_num(size)? [size,size] : point2d(size); From 2221a6cf097dfa68f113ee3426eb9a9e6f020854 Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 22 May 2021 08:46:23 +0000 Subject: [PATCH 24/72] Bump release version. --- version.scad | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.scad b/version.scad index 6445528..fb909e5 100644 --- a/version.scad +++ b/version.scad @@ -6,7 +6,7 @@ ////////////////////////////////////////////////////////////////////// -BOSL_VERSION = [2,0,636]; +BOSL_VERSION = [2,0,637]; // Section: BOSL Library Version Functions From da2292c91aafe2dd9d0b65872676321a4d324a86 Mon Sep 17 00:00:00 2001 From: Garth Minette Date: Sun, 23 May 2021 17:51:14 -0700 Subject: [PATCH 25/72] Added hull.scad to std.scad. --- .openscad_gendocs_rc | 1 + math.scad | 18 ++++++++++++------ shapes.scad | 36 ++++++------------------------------ std.scad | 1 + 4 files changed, 20 insertions(+), 36 deletions(-) diff --git a/.openscad_gendocs_rc b/.openscad_gendocs_rc index 0d6030c..6591e1b 100644 --- a/.openscad_gendocs_rc +++ b/.openscad_gendocs_rc @@ -46,4 +46,5 @@ PrioritizeFiles: DefineHeader(BulletList): Side Effects DefineHeader(Table:Anchor Name|Position): Extra Anchors DefineHeader(Table:Name|Definition): Terminology +DefineHeader(BulletList): Requirements diff --git a/math.scad b/math.scad index 73659ca..df69f0e 100644 --- a/math.scad +++ b/math.scad @@ -1261,8 +1261,10 @@ function compare_lists(a, b) = // Function: any() // Usage: -// b = any(l); -// b = any(l,func); +// bool = any(l); +// bool = any(l,func); // Requires OpenSCAD 2021.01 or later. +// Requirements: +// Requires OpenSCAD 2021.01 or later to use the `func=` argument. // Description: // Returns true if any item in list `l` evaluates as true. // Arguments: @@ -1292,8 +1294,10 @@ function _any_bool(l, i=0, out=false) = // Function: all() // Usage: -// b = all(l); -// b = all(l,func); +// bool = all(l); +// bool = all(l,func); // Requires OpenSCAD 2021.01 or later. +// Requirements: +// Requires OpenSCAD 2021.01 or later to use the `func=` argument. // Description: // Returns true if all items in list `l` evaluate as true. If `func` is given a function liteal // of signature (x), returning bool, then that function literal is evaluated for each list item. @@ -1325,8 +1329,10 @@ function _all_bool(l, i=0, out=true) = // Function: count_true() // Usage: -// n = count_true(l,) -// n = count_true(l,func,) +// n = count_true(l,); +// n = count_true(l,func,); // Requires OpenSCAD 2021.01 or later. +// Requirements: +// Requires OpenSCAD 2021.01 or later to use the `func=` argument. // Description: // Returns the number of items in `l` that evaluate as true. // If `l` is a lists of lists, this is applied recursively to each diff --git a/shapes.scad b/shapes.scad index 0617e86..fd9b9cb 100644 --- a/shapes.scad +++ b/shapes.scad @@ -387,10 +387,6 @@ function cuboid( // Creates a rectangular prismoid shape with optional roundovers and chamfering. // You can only round or chamfer the vertical(ish) edges. For those edges, you can // specify rounding and/or chamferring per-edge, and for top and bottom separately. -// Note: if using chamfers or rounding, you **must** also include the hull.scad file: -// ``` -// include -// ``` // // Arguments: // size1 = [width, length] of the bottom end of the prism. @@ -398,12 +394,12 @@ function cuboid( // h|l = Height of the prism. // shift = [X,Y] amount to shift the center of the top end with respect to the center of the bottom end. // --- -// rounding = The roundover radius for the vertical-ish edges of the prismoid. Requires including hull.scad. If given as a list of four numbers, gives individual radii for each corner, in the order [X+Y+,X-Y+,X-Y-,X+Y-]. Default: 0 (no rounding) -// rounding1 = The roundover radius for the bottom of the vertical-ish edges of the prismoid. Requires including hull.scad. If given as a list of four numbers, gives individual radii for each corner, in the order [X+Y+,X-Y+,X-Y-,X+Y-]. -// rounding2 = The roundover radius for the top of the vertical-ish edges of the prismoid. Requires including hull.scad. If given as a list of four numbers, gives individual radii for each corner, in the order [X+Y+,X-Y+,X-Y-,X+Y-]. -// chamfer = The chamfer size for the vertical-ish edges of the prismoid. Requires including hull.scad. If given as a list of four numbers, gives individual chamfers for each corner, in the order [X+Y+,X-Y+,X-Y-,X+Y-]. Default: 0 (no chamfer) -// chamfer1 = The chamfer size for the bottom of the vertical-ish edges of the prismoid. Requires including hull.scad. If given as a list of four numbers, gives individual chamfers for each corner, in the order [X+Y+,X-Y+,X-Y-,X+Y-]. -// chamfer2 = The chamfer size for the top of the vertical-ish edges of the prismoid. Requires including hull.scad. If given as a list of four numbers, gives individual chamfers for each corner, in the order [X+Y+,X-Y+,X-Y-,X+Y-]. +// rounding = The roundover radius for the vertical-ish edges of the prismoid. If given as a list of four numbers, gives individual radii for each corner, in the order [X+Y+,X-Y+,X-Y-,X+Y-]. Default: 0 (no rounding) +// rounding1 = The roundover radius for the bottom of the vertical-ish edges of the prismoid. If given as a list of four numbers, gives individual radii for each corner, in the order [X+Y+,X-Y+,X-Y-,X+Y-]. +// rounding2 = The roundover radius for the top of the vertical-ish edges of the prismoid. If given as a list of four numbers, gives individual radii for each corner, in the order [X+Y+,X-Y+,X-Y-,X+Y-]. +// chamfer = The chamfer size for the vertical-ish edges of the prismoid. If given as a list of four numbers, gives individual chamfers for each corner, in the order [X+Y+,X-Y+,X-Y-,X+Y-]. Default: 0 (no chamfer) +// chamfer1 = The chamfer size for the bottom of the vertical-ish edges of the prismoid. If given as a list of four numbers, gives individual chamfers for each corner, in the order [X+Y+,X-Y+,X-Y-,X+Y-]. +// chamfer2 = The chamfer size for the top of the vertical-ish edges of the prismoid. If given as a list of four numbers, gives individual chamfers for each corner, in the order [X+Y+,X-Y+,X-Y-,X+Y-]. // anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` // spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0` // orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP` @@ -425,29 +421,22 @@ function cuboid( // Example(FlatSpin,VPD=160,VPT=[0,0,10]): Shifting/Skewing // prismoid(size1=[50,30], size2=[20,20], h=20, shift=[15,5]); // Example: Rounding -// include // prismoid(100, 80, rounding=10, h=30); // Example: Outer Chamfer Only -// include // prismoid(100, 80, chamfer=5, h=30); // Example: Gradiant Rounding -// include // prismoid(100, 80, rounding1=10, rounding2=0, h=30); // Example: Per Corner Rounding -// include // prismoid(100, 80, rounding=[0,5,10,15], h=30); // Example: Per Corner Chamfer -// include // prismoid(100, 80, chamfer=[0,5,10,15], h=30); // Example: Mixing Chamfer and Rounding -// include // prismoid( // 100, 80, h=30, // chamfer=[0,5,0,10], // rounding=[5,0,10,0] // ); // Example: Really Mixing It Up -// include // prismoid( // size1=[100,80], size2=[80,60], h=20, // chamfer1=[0,5,0,10], chamfer2=[5,0,10,0], @@ -579,10 +568,6 @@ function prismoid( // You can only round or chamfer the vertical(ish) edges. For those edges, you can // specify rounding and/or chamferring per-edge, and for top and bottom, inside and // outside separately. -// Note: if using chamfers or rounding, you **must** also include the hull.scad file: -// ``` -// include -// ``` // Arguments: // h|l = The height or length of the rectangular tube. Default: 1 // size = The outer [X,Y] size of the rectangular tube. @@ -622,45 +607,36 @@ function prismoid( // isize1=[40,20], isize2=[65,35], h=15 // ); // Example: Outer Rounding Only -// include // rect_tube(size=100, wall=5, rounding=10, irounding=0, h=30); // Example: Outer Chamfer Only -// include // rect_tube(size=100, wall=5, chamfer=5, ichamfer=0, h=30); // Example: Outer Rounding, Inner Chamfer -// include // rect_tube(size=100, wall=5, rounding=10, ichamfer=8, h=30); // Example: Inner Rounding, Outer Chamfer -// include // rect_tube(size=100, wall=5, chamfer=10, irounding=8, h=30); // Example: Gradiant Rounding -// include // rect_tube( // size1=100, size2=80, wall=5, h=30, // rounding1=10, rounding2=0, // irounding1=8, irounding2=0 // ); // Example: Per Corner Rounding -// include // rect_tube( // size=100, wall=10, h=30, // rounding=[0,5,10,15], irounding=0 // ); // Example: Per Corner Chamfer -// include // rect_tube( // size=100, wall=10, h=30, // chamfer=[0,5,10,15], ichamfer=0 // ); // Example: Mixing Chamfer and Rounding -// include // rect_tube( // size=100, wall=10, h=30, // chamfer=[0,5,0,10], ichamfer=0, // rounding=[5,0,10,0], irounding=0 // ); // Example: Really Mixing It Up -// include // rect_tube( // size1=[100,80], size2=[80,60], // isize1=[50,30], isize2=[70,50], h=20, diff --git a/std.scad b/std.scad index 72b5dc8..f99e984 100644 --- a/std.scad +++ b/std.scad @@ -27,6 +27,7 @@ include include include include +include include include include From a16ed0ac930c39e385f7d3170f96079f74fcb22d Mon Sep 17 00:00:00 2001 From: github-actions Date: Mon, 24 May 2021 00:57:31 +0000 Subject: [PATCH 26/72] Bump release version. --- version.scad | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.scad b/version.scad index 6445528..fb909e5 100644 --- a/version.scad +++ b/version.scad @@ -6,7 +6,7 @@ ////////////////////////////////////////////////////////////////////// -BOSL_VERSION = [2,0,636]; +BOSL_VERSION = [2,0,637]; // Section: BOSL Library Version Functions From 9afae8f1e4453866895c64a5a9d918043eda41bf Mon Sep 17 00:00:00 2001 From: Garth Minette Date: Sun, 23 May 2021 18:55:14 -0700 Subject: [PATCH 27/72] Fix for issue #512 --- attachments.scad | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/attachments.scad b/attachments.scad index 870b5f1..bd3137d 100644 --- a/attachments.scad +++ b/attachments.scad @@ -330,6 +330,9 @@ function attach_geom_size(geom) = let( r=geom[1] ) is_num(r)? [2,2,2]*r : vmul([2,2,2],point3d(r)) ) : type == "vnf_extent" || type=="vnf_isect"? ( //vnf + let( + vnf = geom[1] + ) vnf==EMPTY_VNF? [0,0,0] : let( mm = pointlist_bounds(geom[1][0]), delt = mm[1]-mm[0] @@ -425,7 +428,7 @@ function attach_transform(anchor, spin, orient, geom, p) = ) ) ) is_undef(p)? m : - is_vnf(p)? [apply(m, p[0]), p[1]] : + is_vnf(p)? [(p==EMPTY_VNF? p : apply(m, p[0])), p[1]] : apply(m, p); @@ -515,7 +518,9 @@ function find_anchor(anchor, geom) = ) [anchor, pos, vec, oang] ) : type == "vnf_isect"? ( //vnf let( - vnf=geom[1], + vnf=geom[1] + ) vnf==EMPTY_VNF? [anchor, [0,0,0], unit(anchor), 0] : + let( eps = 1/2048, points = vnf[0], faces = vnf[1], @@ -565,7 +570,9 @@ function find_anchor(anchor, geom) = [anchor, pos, n, oang] ) : type == "vnf_extent"? ( //vnf let( - vnf=geom[1], + vnf=geom[1] + ) vnf==EMPTY_VNF? [anchor, [0,0,0], unit(anchor), 0] : + let( rpts = apply(rot(from=anchor, to=RIGHT) * move(point3d(-cp)), vnf[0]), maxx = max(subindex(rpts,0)), idxs = [for (i = idx(rpts)) if (approx(rpts[i].x, maxx)) i], From 36e9120d0be1e67c55d19b32a1568209dde9fb35 Mon Sep 17 00:00:00 2001 From: github-actions Date: Mon, 24 May 2021 01:58:12 +0000 Subject: [PATCH 28/72] Bump release version. --- version.scad | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.scad b/version.scad index fb909e5..9564d71 100644 --- a/version.scad +++ b/version.scad @@ -6,7 +6,7 @@ ////////////////////////////////////////////////////////////////////// -BOSL_VERSION = [2,0,637]; +BOSL_VERSION = [2,0,638]; // Section: BOSL Library Version Functions From fc60867a5911802f7ca9d47b7ab2d39b80f4affd Mon Sep 17 00:00:00 2001 From: Garth Minette Date: Mon, 24 May 2021 12:39:23 -0700 Subject: [PATCH 29/72] Fix for issue #550. --- gears.scad | 1 - 1 file changed, 1 deletion(-) diff --git a/gears.scad b/gears.scad index aea28db..920b42e 100644 --- a/gears.scad +++ b/gears.scad @@ -800,7 +800,6 @@ module spur_gear( backlash = backlash, interior = interior ); - circle(d=shaft_diam+4); } if (shaft_diam > 0) { cylinder(h=2*thickness+1, r=shaft_diam/2, center=true, $fn=max(12,segs(shaft_diam/2))); From f1db6eadaeadf3d06cf11fe094f9a432fd6921f5 Mon Sep 17 00:00:00 2001 From: github-actions Date: Mon, 24 May 2021 19:44:29 +0000 Subject: [PATCH 30/72] Bump release version. --- version.scad | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.scad b/version.scad index 9564d71..6803c9c 100644 --- a/version.scad +++ b/version.scad @@ -6,7 +6,7 @@ ////////////////////////////////////////////////////////////////////// -BOSL_VERSION = [2,0,638]; +BOSL_VERSION = [2,0,639]; // Section: BOSL Library Version Functions From b59b05c04a2369b89f60a184909f7e30770c1474 Mon Sep 17 00:00:00 2001 From: Garth Minette Date: Wed, 26 May 2021 20:07:05 -0700 Subject: [PATCH 31/72] Fix for issue #421 --- shapes.scad | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/shapes.scad b/shapes.scad index fd9b9cb..4557432 100644 --- a/shapes.scad +++ b/shapes.scad @@ -154,6 +154,7 @@ module cuboid( size = scalar_vec3(size); edges = edges(edges, except=except_edges); assert(is_vector(size,3)); + assert(all_positive(size)); assert(is_undef(chamfer) || is_finite(chamfer)); assert(is_undef(rounding) || is_finite(rounding)); assert(is_undef(p1) || is_vector(p1)); @@ -465,6 +466,10 @@ module prismoid( eps = pow(2,-14); size1 = is_num(size1)? [size1,size1] : size1; size2 = is_num(size2)? [size2,size2] : size2; + assert(all_nonnegative(size1)); + assert(all_nonnegative(size2)); + assert(size1.x + size2.x > 0); + assert(size1.y + size2.y > 0); s1 = [max(size1.x, eps), max(size1.y, eps)]; s2 = [max(size2.x, eps), max(size2.y, eps)]; rounding1 = default(rounding1, rounding); From 5ec1fbd85beccdca8a5d9a7c96d19f562f1dc5f4 Mon Sep 17 00:00:00 2001 From: github-actions Date: Thu, 27 May 2021 03:12:10 +0000 Subject: [PATCH 32/72] Bump release version. --- version.scad | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.scad b/version.scad index 6803c9c..2073768 100644 --- a/version.scad +++ b/version.scad @@ -6,7 +6,7 @@ ////////////////////////////////////////////////////////////////////// -BOSL_VERSION = [2,0,639]; +BOSL_VERSION = [2,0,640]; // Section: BOSL Library Version Functions From d7ee1cee09c477dc0b008e57e868eca720171111 Mon Sep 17 00:00:00 2001 From: Garth Minette Date: Thu, 27 May 2021 23:26:31 -0700 Subject: [PATCH 33/72] Added atext() --- attachments.scad | 86 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/attachments.scad b/attachments.scad index bd3137d..55f22e4 100644 --- a/attachments.scad +++ b/attachments.scad @@ -993,6 +993,92 @@ module attachable( } +// Module: atext() +// Topics: Attachments, Text +// Usage: +// atext(text, , , ); +// Description: +// Creates a 3D text block that can be attached to other attachable objects. +// NOTE: This cannot have children attached to it. +// Arguments: +// text = The text string to instantiate as an object. +// h = The height to which the text should be extruded. Default: 1 +// size = The font size used to create the text block. Default: 10 +// font = The name of the font used to create the text block. Default: "Courier" +// --- +// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `"baseline"` +// spin = Rotate this many degrees around the Z axis. See [spin](attachments.scad#spin). Default: `0` +// orient = Vector to rotate top towards. See [orient](attachments.scad#orient). Default: `UP` +// See Also: attachable() +// Extra Anchors: +// "baseline" = Anchors at the baseline of the text, at the start of the string. +// str("baseline",VECTOR) = Anchors at the baseline of the text, modified by the X and Z components of the appended vector. +// Examples: +// atext("Foobar", h=3, size=10); +// atext("Foobar", h=2, size=12, font="Helvetica"); +// atext("Foobar", h=2, anchor=CENTER); +// atext("Foobar", h=2, anchor=str("baseline",CENTER)); +// atext("Foobar", h=2, anchor=str("baseline",BOTTOM+RIGHT)); +// Example: Using line_of() distributor +// txt = "This is the string."; +// line_of(spacing=[10,-5],n=len(txt)) +// atext(txt[$idx], size=10, anchor=CENTER); +// Example: Using arc_of() distributor +// txt = "This is the string"; +// arc_of(r=50, n=len(txt), sa=0, ea=180) +// atext(select(txt,-1-$idx), size=10, anchor=str("baseline",CENTER), spin=-90); +module atext(text, h=1, size=9, font="Courier", anchor="baseline", spin=0, orient=UP) { + no_children($children); + dummy1 = + assert(is_undef(anchor) || is_vector(anchor) || is_string(anchor), str("Got: ",anchor)) + assert(is_undef(spin) || is_vector(spin,3) || is_num(spin), str("Got: ",spin)) + assert(is_undef(orient) || is_vector(orient,3), str("Got: ",orient)); + anchor = default(anchor, CENTER); + spin = default(spin, 0); + orient = default(orient, UP); + geom = attach_geom(size=[size,size,h]); + anch = !any([for (c=anchor) c=="["])? anchor : + let( + parts = str_split(str_split(str_split(anchor,"]")[0],"[")[1],","), + vec = [for (p=parts) str_float(str_strip_leading(p," "))] + ) vec; + ha = anchor=="baseline"? "left" : + anchor==anch && is_string(anchor)? "center" : + anch.x<0? "left" : + anch.x>0? "right" : + "center"; + va = starts_with(anchor,"baseline")? "baseline" : + anchor==anch && is_string(anchor)? "center" : + anch.y<0? "bottom" : + anch.y>0? "top" : + "center"; + base = anchor=="baseline"? CENTER : + anchor==anch && is_string(anchor)? CENTER : + anch.z<0? BOTTOM : + anch.z>0? TOP : + CENTER; + m = attach_transform(base,spin,orient,geom); + multmatrix(m) { + $parent_anchor = anchor; + $parent_spin = spin; + $parent_orient = orient; + $parent_geom = geom; + $parent_size = attach_geom_size(geom); + $attach_to = undef; + do_show = attachment_is_shown($tags); + if (do_show) { + if (is_undef($color)) { + linear_extrude(height=h, center=true) + text(text=text, size=size, halign=ha, valign=va); + } else color($color) { + $color = undef; + linear_extrude(height=h, center=true) + text(text=text, size=size, halign=ha, valign=va); + } + } + } +} + // Section: Attachment Positioning From bf877f3459f4f5e4f087e564cc057a3b07f535fb Mon Sep 17 00:00:00 2001 From: github-actions Date: Fri, 28 May 2021 06:29:17 +0000 Subject: [PATCH 34/72] Bump release version. --- version.scad | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.scad b/version.scad index 2073768..cffc6cd 100644 --- a/version.scad +++ b/version.scad @@ -6,7 +6,7 @@ ////////////////////////////////////////////////////////////////////// -BOSL_VERSION = [2,0,640]; +BOSL_VERSION = [2,0,641]; // Section: BOSL Library Version Functions From 67f08e020bdc9a2871106d96b970a75144f0cccc Mon Sep 17 00:00:00 2001 From: Adrian Mariano Date: Sun, 30 May 2021 20:32:56 -0400 Subject: [PATCH 35/72] Added examples for vp tree --- vectors.scad | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/vectors.scad b/vectors.scad index a98594a..7e38321 100644 --- a/vectors.scad +++ b/vectors.scad @@ -291,6 +291,18 @@ function _vp_tree(ptlist, ind, leafsize) = // tree = vantage point tree from vp_tree // p = point to search for // r = search radius +// Example: A set of four queries to find points within 1 unit of the query. The circles show the search region and all have radius 1. +// $fn=32; +// k = 2000; +// points = array_group(rands(0,10,k*2,seed=13333),2); +// vp = vp_tree(points); +// queries = [for(i=[3,7],j=[3,7]) [i,j]]; +// search_ind = [for(q=queries) vp_search(points, vp, q, 1)]; +// move_copies(points) circle(r=.08); +// for(i=idx(queries)){ +// color("blue")stroke(move(queries[i],circle(r=1)), closed=true, width=.08); +// color("red")move_copies(select(points, search_ind[i])) circle(r=.08); +// } function _vp_search(points, tree, p, r) = is_list(tree[0]) ? [for(i=tree[0]) if (norm(points[i]-p)<=r) i] : @@ -324,6 +336,20 @@ function vp_search(points, tree, p, r) = // tree = vantage point tree from vp_tree // p = point to search for // k = number of neighbors to return +// Example: Four queries to find the 15 nearest points. The circles show the radius defined by the most distant query result. Note they are different for each query. +// $fn=32; +// k = 2000; +// points = array_group(rands(0,10,k*2,seed=13333),2); +// vp = vp_tree(points); +// queries = [for(i=[3,7],j=[3,7]) [i,j]]; +// search_ind = [for(q=queries) vp_nearest(points, vp, q, 15)]; +// move_copies(points) circle(r=.08); +// for(i=idx(queries)){ +// color("red")move_copies(select(points, search_ind[i])) circle(r=.08); +// color("blue")stroke(move(queries[i], +// circle(r=norm(points[last(search_ind[i])]-queries[i]))), +// closed=true, width=.08); +// } function _insert_sorted(list, k, new) = len(list)==k && new[1]>= last(list)[1] ? list : [ From 697230b7041c4538c3900028cfc0d8c67b82b132 Mon Sep 17 00:00:00 2001 From: Garth Minette Date: Sun, 30 May 2021 20:59:13 -0700 Subject: [PATCH 36/72] Try to fix bad docs image generation by moving to Ubuntu VM. --- .github/workflows/docsgen.yml | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/.github/workflows/docsgen.yml b/.github/workflows/docsgen.yml index 447de46..a61802a 100644 --- a/.github/workflows/docsgen.yml +++ b/.github/workflows/docsgen.yml @@ -1,14 +1,17 @@ -name: CI +name: DocsGen on: pull_request: branches: - master types: [closed] + workflow_dispatch: + branches: + - master jobs: GenerateDocs: if: github.event.pull_request.merged == true - runs-on: macos-10.15 + runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v2 @@ -19,27 +22,31 @@ jobs: repository: revarbat/BOSL2.wiki path: BOSL2.wiki - - name: Install gifsicle - run: brew install gifsicle + - name: Apt Update + run: sudo apt update - - name: Install Pillow - run: sudo pip3 install Pillow + - name: Install Packages + run: sudo apt-get install -y python3-pip python3-dev python3-setuptools python3-pil gifsicle - - name: Install Docsgen + - name: Install openscad-docsgen run: sudo pip3 install openscad_docsgen + - name: Install gifsicle + run: sudo pip3 install gifsicle + - name: Install OpenSCAD run: | - curl -L -o OpenSCAD.dmg https://files.openscad.org/snapshots/OpenSCAD-2021.05.07.dmg - hdiutil attach OpenSCAD.dmg - cp -a /Volumes/OpenSCAD/OpenSCAD.app /Applications/ + cd $GITHUB_WORKSPACE + wget https://files.openscad.org/OpenSCAD-2021.01-x86_64.AppImage + sudo mv OpenSCAD-2021.01*-x86_64.AppImage /usr/local/bin/openscad + sudo chmod +x /usr/local/bin/openscad - name: Generating Docs run: | cd $GITHUB_WORKSPACE export OPENSCADPATH=$(dirname $GITHUB_WORKSPACE) echo "::add-matcher::.github/openscad_docsgen.json" - openscad-docsgen -m -i -t -c -I *.scad + openscad-docsgen -ticmI *.scad cd BOSL2.wiki git config user.name github-actions git config user.email github-actions@github.com From 8f138be8aa0afa2b0c96532e7ba6b6b450bc6884 Mon Sep 17 00:00:00 2001 From: Garth Minette Date: Sun, 30 May 2021 21:10:17 -0700 Subject: [PATCH 37/72] Enable manual forced docsgen. --- .github/workflows/docsgen.yml | 8 ++--- .github/workflows/forced_docsgen.yml | 49 ++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/forced_docsgen.yml diff --git a/.github/workflows/docsgen.yml b/.github/workflows/docsgen.yml index a61802a..4ce201f 100644 --- a/.github/workflows/docsgen.yml +++ b/.github/workflows/docsgen.yml @@ -1,12 +1,8 @@ -name: DocsGen +name: Docs on: pull_request: - branches: - - master + branches: [master] types: [closed] - workflow_dispatch: - branches: - - master jobs: GenerateDocs: diff --git a/.github/workflows/forced_docsgen.yml b/.github/workflows/forced_docsgen.yml new file mode 100644 index 0000000..bf1d7b5 --- /dev/null +++ b/.github/workflows/forced_docsgen.yml @@ -0,0 +1,49 @@ +name: FDocs +on: + workflow_dispatch: + branches: [master] + +jobs: + GenerateDocs: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Checkout Wiki + uses: actions/checkout@v2 + with: + repository: revarbat/BOSL2.wiki + path: BOSL2.wiki + + - name: Apt Update + run: sudo apt update + + - name: Install Packages + run: sudo apt-get install -y python3-pip python3-dev python3-setuptools python3-pil gifsicle + + - name: Install openscad-docsgen + run: sudo pip3 install openscad_docsgen + + - name: Install gifsicle + run: sudo pip3 install gifsicle + + - name: Install OpenSCAD + run: | + cd $GITHUB_WORKSPACE + wget https://files.openscad.org/OpenSCAD-2021.01-x86_64.AppImage + sudo mv OpenSCAD-2021.01*-x86_64.AppImage /usr/local/bin/openscad + sudo chmod +x /usr/local/bin/openscad + + - name: Generating Docs + run: | + cd $GITHUB_WORKSPACE + export OPENSCADPATH=$(dirname $GITHUB_WORKSPACE) + echo "::add-matcher::.github/openscad_docsgen.json" + openscad-docsgen -ticmIf *.scad + cd BOSL2.wiki + git config user.name github-actions + git config user.email github-actions@github.com + git add --all + git commit -m "Wiki docs auto-regen." && git push || true + From b4d0034e1257ad452c6c76aee8ac4bbeaf415120 Mon Sep 17 00:00:00 2001 From: Garth Minette Date: Sun, 30 May 2021 21:15:29 -0700 Subject: [PATCH 38/72] Fix bug with pip installing pygifsicle. --- .github/workflows/docsgen.yml | 4 ++-- .github/workflows/forced_docsgen.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/docsgen.yml b/.github/workflows/docsgen.yml index 4ce201f..faa46ab 100644 --- a/.github/workflows/docsgen.yml +++ b/.github/workflows/docsgen.yml @@ -27,8 +27,8 @@ jobs: - name: Install openscad-docsgen run: sudo pip3 install openscad_docsgen - - name: Install gifsicle - run: sudo pip3 install gifsicle + - name: Install pygifsicle + run: sudo pip3 install pygifsicle - name: Install OpenSCAD run: | diff --git a/.github/workflows/forced_docsgen.yml b/.github/workflows/forced_docsgen.yml index bf1d7b5..323112b 100644 --- a/.github/workflows/forced_docsgen.yml +++ b/.github/workflows/forced_docsgen.yml @@ -25,8 +25,8 @@ jobs: - name: Install openscad-docsgen run: sudo pip3 install openscad_docsgen - - name: Install gifsicle - run: sudo pip3 install gifsicle + - name: Install pygifsicle + run: sudo pip3 install pygifsicle - name: Install OpenSCAD run: | From bf98ade8e00504cda63cef8f0e703e8dd5790a32 Mon Sep 17 00:00:00 2001 From: github-actions Date: Mon, 31 May 2021 04:17:30 +0000 Subject: [PATCH 39/72] Bump release version. --- version.scad | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.scad b/version.scad index cffc6cd..524ad96 100644 --- a/version.scad +++ b/version.scad @@ -6,7 +6,7 @@ ////////////////////////////////////////////////////////////////////// -BOSL_VERSION = [2,0,641]; +BOSL_VERSION = [2,0,642]; // Section: BOSL Library Version Functions From 409462c4caf26c2c320396323b5def776dee1938 Mon Sep 17 00:00:00 2001 From: Garth Minette Date: Mon, 31 May 2021 15:11:53 -0700 Subject: [PATCH 40/72] GitHub Actions Testing. --- .github/workflows/testwf.yml | 45 ++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 .github/workflows/testwf.yml diff --git a/.github/workflows/testwf.yml b/.github/workflows/testwf.yml new file mode 100644 index 0000000..ed82cf8 --- /dev/null +++ b/.github/workflows/testwf.yml @@ -0,0 +1,45 @@ +name: TestWorkflow +on: + workflow_dispatch: + branches: [master] + +jobs: + TestJob: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Checkout Wiki + uses: actions/checkout@v2 + with: + repository: revarbat/BOSL2.wiki + path: BOSL2.wiki + + - name: Apt Update + run: sudo apt update + + - name: Install Packages + run: sudo apt-get install -y python3-pip python3-dev python3-setuptools python3-pil gifsicle + + - name: Install openscad-docsgen + run: sudo pip3 install openscad_docsgen + + - name: Install pygifsicle + run: sudo pip3 install pygifsicle + + - name: Install OpenSCAD + run: | + cd $GITHUB_WORKSPACE + wget https://files.openscad.org/OpenSCAD-2021.01-x86_64.AppImage + sudo mv OpenSCAD-2021.01*-x86_64.AppImage /usr/local/bin/openscad + sudo chmod +x /usr/local/bin/openscad + + - name: TestScript + run: | + cd $GITHUB_WORKSPACE + export OPENSCADPATH=$(dirname $GITHUB_WORKSPACE) + echo "::add-matcher::.github/openscad_docsgen.json" + echo "cube(50, center=100);" > testwf.scad + openscad -o tmp_cube.png --imgsize=640,480 --view=axes,scales --projection=o --autocenter --viewall --preview '' --hardwarnings testwf.scad + From ed4dc1dbae03fcc2a98e4048fd6a138bbf43badb Mon Sep 17 00:00:00 2001 From: github-actions Date: Mon, 31 May 2021 22:13:15 +0000 Subject: [PATCH 41/72] Bump release version. --- version.scad | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.scad b/version.scad index 524ad96..8639cf2 100644 --- a/version.scad +++ b/version.scad @@ -6,7 +6,7 @@ ////////////////////////////////////////////////////////////////////// -BOSL_VERSION = [2,0,642]; +BOSL_VERSION = [2,0,643]; // Section: BOSL Library Version Functions From 6d84f055905e85c50869e142eda2350391d5e018 Mon Sep 17 00:00:00 2001 From: Garth Minette Date: Mon, 31 May 2021 15:32:56 -0700 Subject: [PATCH 42/72] More github action testing. --- .github/workflows/docsgen.yml | 5 +---- .github/workflows/forced_docsgen.yml | 5 +---- .github/workflows/testwf.yml | 5 +---- 3 files changed, 3 insertions(+), 12 deletions(-) diff --git a/.github/workflows/docsgen.yml b/.github/workflows/docsgen.yml index faa46ab..92deef1 100644 --- a/.github/workflows/docsgen.yml +++ b/.github/workflows/docsgen.yml @@ -22,14 +22,11 @@ jobs: run: sudo apt update - name: Install Packages - run: sudo apt-get install -y python3-pip python3-dev python3-setuptools python3-pil gifsicle + run: sudo apt-get install -y python3-pip python3-dev python3-setuptools python3-pil - name: Install openscad-docsgen run: sudo pip3 install openscad_docsgen - - name: Install pygifsicle - run: sudo pip3 install pygifsicle - - name: Install OpenSCAD run: | cd $GITHUB_WORKSPACE diff --git a/.github/workflows/forced_docsgen.yml b/.github/workflows/forced_docsgen.yml index 323112b..d9a7ebc 100644 --- a/.github/workflows/forced_docsgen.yml +++ b/.github/workflows/forced_docsgen.yml @@ -20,14 +20,11 @@ jobs: run: sudo apt update - name: Install Packages - run: sudo apt-get install -y python3-pip python3-dev python3-setuptools python3-pil gifsicle + run: sudo apt-get install -y python3-pip python3-dev python3-setuptools python3-pil - name: Install openscad-docsgen run: sudo pip3 install openscad_docsgen - - name: Install pygifsicle - run: sudo pip3 install pygifsicle - - name: Install OpenSCAD run: | cd $GITHUB_WORKSPACE diff --git a/.github/workflows/testwf.yml b/.github/workflows/testwf.yml index ed82cf8..af79a88 100644 --- a/.github/workflows/testwf.yml +++ b/.github/workflows/testwf.yml @@ -20,14 +20,11 @@ jobs: run: sudo apt update - name: Install Packages - run: sudo apt-get install -y python3-pip python3-dev python3-setuptools python3-pil gifsicle + run: sudo apt-get install -y python3-pip python3-dev python3-setuptools python3-pil - name: Install openscad-docsgen run: sudo pip3 install openscad_docsgen - - name: Install pygifsicle - run: sudo pip3 install pygifsicle - - name: Install OpenSCAD run: | cd $GITHUB_WORKSPACE From dcf11fe64a0a2f7bfcdd761238c901366e88644e Mon Sep 17 00:00:00 2001 From: github-actions Date: Mon, 31 May 2021 22:33:53 +0000 Subject: [PATCH 43/72] Bump release version. --- version.scad | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.scad b/version.scad index 8639cf2..2ec9a8e 100644 --- a/version.scad +++ b/version.scad @@ -6,7 +6,7 @@ ////////////////////////////////////////////////////////////////////// -BOSL_VERSION = [2,0,643]; +BOSL_VERSION = [2,0,644]; // Section: BOSL Library Version Functions From 7b36b2ad488bc7e066ad2513a8c523ab879bfdf8 Mon Sep 17 00:00:00 2001 From: Garth Minette Date: Mon, 31 May 2021 16:50:56 -0700 Subject: [PATCH 44/72] Rework workflows with xvfb. --- .github/workflows/docsgen.yml | 17 +++++++++++------ .github/workflows/forced_docsgen.yml | 17 +++++++++++------ .github/workflows/testwf.yml | 15 +++++++++------ 3 files changed, 31 insertions(+), 18 deletions(-) diff --git a/.github/workflows/docsgen.yml b/.github/workflows/docsgen.yml index 92deef1..627aeba 100644 --- a/.github/workflows/docsgen.yml +++ b/.github/workflows/docsgen.yml @@ -34,13 +34,18 @@ jobs: sudo mv OpenSCAD-2021.01*-x86_64.AppImage /usr/local/bin/openscad sudo chmod +x /usr/local/bin/openscad - - name: Generating Docs + - name: Generating Docs Headless + uses: GabrielBB/xvfb-action@v1 + with: + working-directory: $GITHUB_WORKSPACE + run: | + export OPENSCADPATH=$(dirname $GITHUB_WORKSPACE) + echo "::add-matcher::.github/openscad_docsgen.json" + openscad-docsgen -ticmI *.scad + + - name: Commit Wiki Docs run: | - cd $GITHUB_WORKSPACE - export OPENSCADPATH=$(dirname $GITHUB_WORKSPACE) - echo "::add-matcher::.github/openscad_docsgen.json" - openscad-docsgen -ticmI *.scad - cd BOSL2.wiki + cd $GITHUB_WORKSPACE/BOSL2.wiki git config user.name github-actions git config user.email github-actions@github.com git add --all diff --git a/.github/workflows/forced_docsgen.yml b/.github/workflows/forced_docsgen.yml index d9a7ebc..cc318f0 100644 --- a/.github/workflows/forced_docsgen.yml +++ b/.github/workflows/forced_docsgen.yml @@ -32,13 +32,18 @@ jobs: sudo mv OpenSCAD-2021.01*-x86_64.AppImage /usr/local/bin/openscad sudo chmod +x /usr/local/bin/openscad - - name: Generating Docs + - name: Generating Docs Headless + uses: GabrielBB/xvfb-action@v1 + with: + working-directory: $GITHUB_WORKSPACE + run: | + export OPENSCADPATH=$(dirname $GITHUB_WORKSPACE) + echo "::add-matcher::.github/openscad_docsgen.json" + openscad-docsgen -ticmIf *.scad + + - name: Commit Wiki Docs run: | - cd $GITHUB_WORKSPACE - export OPENSCADPATH=$(dirname $GITHUB_WORKSPACE) - echo "::add-matcher::.github/openscad_docsgen.json" - openscad-docsgen -ticmIf *.scad - cd BOSL2.wiki + cd $GITHUB_WORKSPACE/BOSL2.wiki git config user.name github-actions git config user.email github-actions@github.com git add --all diff --git a/.github/workflows/testwf.yml b/.github/workflows/testwf.yml index af79a88..a6d5774 100644 --- a/.github/workflows/testwf.yml +++ b/.github/workflows/testwf.yml @@ -33,10 +33,13 @@ jobs: sudo chmod +x /usr/local/bin/openscad - name: TestScript - run: | - cd $GITHUB_WORKSPACE - export OPENSCADPATH=$(dirname $GITHUB_WORKSPACE) - echo "::add-matcher::.github/openscad_docsgen.json" - echo "cube(50, center=100);" > testwf.scad - openscad -o tmp_cube.png --imgsize=640,480 --view=axes,scales --projection=o --autocenter --viewall --preview '' --hardwarnings testwf.scad + uses: GabrielBB/xvfb-action@v1 + with: + working-directory: $GITHUB_WORKSPACE + run: | + cd $GITHUB_WORKSPACE + export OPENSCADPATH=$(dirname $GITHUB_WORKSPACE) + echo "::add-matcher::.github/openscad_docsgen.json" + echo "cube(50, center=100);" > testwf.scad + openscad -o tmp_cube.png --imgsize=640,480 --view=axes,scales --projection=o --autocenter --viewall --preview '' --hardwarnings testwf.scad From 71e06f51911df9cfad3aa5a2faf7af39323c0928 Mon Sep 17 00:00:00 2001 From: Garth Minette Date: Mon, 31 May 2021 16:59:01 -0700 Subject: [PATCH 45/72] xvfb workflow debugging. --- .github/workflows/docsgen.yml | 5 ++--- .github/workflows/forced_docsgen.yml | 5 ++--- .github/workflows/testwf.yml | 12 +++++++----- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/docsgen.yml b/.github/workflows/docsgen.yml index 627aeba..435a0d0 100644 --- a/.github/workflows/docsgen.yml +++ b/.github/workflows/docsgen.yml @@ -33,15 +33,14 @@ jobs: wget https://files.openscad.org/OpenSCAD-2021.01-x86_64.AppImage sudo mv OpenSCAD-2021.01*-x86_64.AppImage /usr/local/bin/openscad sudo chmod +x /usr/local/bin/openscad + echo "::add-matcher::.github/openscad_docsgen.json" - name: Generating Docs Headless uses: GabrielBB/xvfb-action@v1 with: working-directory: $GITHUB_WORKSPACE run: | - export OPENSCADPATH=$(dirname $GITHUB_WORKSPACE) - echo "::add-matcher::.github/openscad_docsgen.json" - openscad-docsgen -ticmI *.scad + OPENSCADPATH=$(dirname $GITHUB_WORKSPACE) openscad-docsgen -ticmI *.scad - name: Commit Wiki Docs run: | diff --git a/.github/workflows/forced_docsgen.yml b/.github/workflows/forced_docsgen.yml index cc318f0..51aa1c0 100644 --- a/.github/workflows/forced_docsgen.yml +++ b/.github/workflows/forced_docsgen.yml @@ -31,15 +31,14 @@ jobs: wget https://files.openscad.org/OpenSCAD-2021.01-x86_64.AppImage sudo mv OpenSCAD-2021.01*-x86_64.AppImage /usr/local/bin/openscad sudo chmod +x /usr/local/bin/openscad + echo "::add-matcher::.github/openscad_docsgen.json" - name: Generating Docs Headless uses: GabrielBB/xvfb-action@v1 with: working-directory: $GITHUB_WORKSPACE run: | - export OPENSCADPATH=$(dirname $GITHUB_WORKSPACE) - echo "::add-matcher::.github/openscad_docsgen.json" - openscad-docsgen -ticmIf *.scad + OPENSCADPATH=$(dirname $GITHUB_WORKSPACE) openscad-docsgen -ticmIf *.scad - name: Commit Wiki Docs run: | diff --git a/.github/workflows/testwf.yml b/.github/workflows/testwf.yml index a6d5774..6af9c4a 100644 --- a/.github/workflows/testwf.yml +++ b/.github/workflows/testwf.yml @@ -31,15 +31,17 @@ jobs: wget https://files.openscad.org/OpenSCAD-2021.01-x86_64.AppImage sudo mv OpenSCAD-2021.01*-x86_64.AppImage /usr/local/bin/openscad sudo chmod +x /usr/local/bin/openscad + echo "::add-matcher::.github/openscad_docsgen.json" + + - name: Make SCAD File + run: | + cd $GITHUB_WORKSPACE + echo "cube(50, center=100);" > testwf.scad - name: TestScript uses: GabrielBB/xvfb-action@v1 with: working-directory: $GITHUB_WORKSPACE run: | - cd $GITHUB_WORKSPACE - export OPENSCADPATH=$(dirname $GITHUB_WORKSPACE) - echo "::add-matcher::.github/openscad_docsgen.json" - echo "cube(50, center=100);" > testwf.scad - openscad -o tmp_cube.png --imgsize=640,480 --view=axes,scales --projection=o --autocenter --viewall --preview '' --hardwarnings testwf.scad + OPENSCADPATH=$(dirname $GITHUB_WORKSPACE) openscad -o tmp_cube.png --imgsize=640,480 --view=axes,scales --projection=o --autocenter --viewall --preview '' --hardwarnings testwf.scad From 2e55947cf421223e51c28a3503b9d55fb415ad26 Mon Sep 17 00:00:00 2001 From: Garth Minette Date: Mon, 31 May 2021 17:09:18 -0700 Subject: [PATCH 46/72] Further xvfb workflow debugging. --- .github/workflows/docsgen.yml | 10 +++++----- .github/workflows/forced_docsgen.yml | 10 +++++----- .github/workflows/testwf.yml | 10 +++++----- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.github/workflows/docsgen.yml b/.github/workflows/docsgen.yml index 435a0d0..a35837a 100644 --- a/.github/workflows/docsgen.yml +++ b/.github/workflows/docsgen.yml @@ -36,11 +36,11 @@ jobs: echo "::add-matcher::.github/openscad_docsgen.json" - name: Generating Docs Headless - uses: GabrielBB/xvfb-action@v1 - with: - working-directory: $GITHUB_WORKSPACE - run: | - OPENSCADPATH=$(dirname $GITHUB_WORKSPACE) openscad-docsgen -ticmI *.scad + env: + OPENSCADPATH: $GITHUB_WORKSPACE/.. + run: | + xvfb-run --server-args="-screen 0, 1280x720x24" -a \ + openscad-docsgen -ticmI *.scad - name: Commit Wiki Docs run: | diff --git a/.github/workflows/forced_docsgen.yml b/.github/workflows/forced_docsgen.yml index 51aa1c0..d114ecc 100644 --- a/.github/workflows/forced_docsgen.yml +++ b/.github/workflows/forced_docsgen.yml @@ -34,11 +34,11 @@ jobs: echo "::add-matcher::.github/openscad_docsgen.json" - name: Generating Docs Headless - uses: GabrielBB/xvfb-action@v1 - with: - working-directory: $GITHUB_WORKSPACE - run: | - OPENSCADPATH=$(dirname $GITHUB_WORKSPACE) openscad-docsgen -ticmIf *.scad + env: + OPENSCADPATH: $GITHUB_WORKSPACE/.. + run: | + xvfb-run --server-args="-screen 0, 1280x720x24" -a \ + openscad-docsgen -ticmIf *.scad - name: Commit Wiki Docs run: | diff --git a/.github/workflows/testwf.yml b/.github/workflows/testwf.yml index 6af9c4a..9363a6d 100644 --- a/.github/workflows/testwf.yml +++ b/.github/workflows/testwf.yml @@ -39,9 +39,9 @@ jobs: echo "cube(50, center=100);" > testwf.scad - name: TestScript - uses: GabrielBB/xvfb-action@v1 - with: - working-directory: $GITHUB_WORKSPACE - run: | - OPENSCADPATH=$(dirname $GITHUB_WORKSPACE) openscad -o tmp_cube.png --imgsize=640,480 --view=axes,scales --projection=o --autocenter --viewall --preview '' --hardwarnings testwf.scad + env: + OPENSCADPATH: $GITHUB_WORKSPACE/.. + run: | + xvfb-run --server-args="-screen 0, 1280x720x24" -a \ + openscad -o tmp_cube.png --imgsize=640,480 --view=axes,scales --projection=o --autocenter --viewall --preview '' --hardwarnings testwf.scad From 669079360d0e6be2461228df7bda0c495c206ed3 Mon Sep 17 00:00:00 2001 From: github-actions Date: Tue, 1 Jun 2021 00:11:01 +0000 Subject: [PATCH 47/72] Bump release version. --- version.scad | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.scad b/version.scad index 2ec9a8e..095607e 100644 --- a/version.scad +++ b/version.scad @@ -6,7 +6,7 @@ ////////////////////////////////////////////////////////////////////// -BOSL_VERSION = [2,0,644]; +BOSL_VERSION = [2,0,645]; // Section: BOSL Library Version Functions From 986e908dba2eee1edc06d34fd69228d0ee83cdfa Mon Sep 17 00:00:00 2001 From: Garth Minette Date: Mon, 31 May 2021 17:34:29 -0700 Subject: [PATCH 48/72] Fix openscad library path --- .github/workflows/docsgen.yml | 3 +-- .github/workflows/forced_docsgen.yml | 3 +-- .github/workflows/testwf.yml | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/.github/workflows/docsgen.yml b/.github/workflows/docsgen.yml index a35837a..105f9b6 100644 --- a/.github/workflows/docsgen.yml +++ b/.github/workflows/docsgen.yml @@ -36,9 +36,8 @@ jobs: echo "::add-matcher::.github/openscad_docsgen.json" - name: Generating Docs Headless - env: - OPENSCADPATH: $GITHUB_WORKSPACE/.. run: | + export OPENSCADPATH=$(dirname $GITHUB_WORKSPACE) xvfb-run --server-args="-screen 0, 1280x720x24" -a \ openscad-docsgen -ticmI *.scad diff --git a/.github/workflows/forced_docsgen.yml b/.github/workflows/forced_docsgen.yml index d114ecc..0d70d94 100644 --- a/.github/workflows/forced_docsgen.yml +++ b/.github/workflows/forced_docsgen.yml @@ -34,9 +34,8 @@ jobs: echo "::add-matcher::.github/openscad_docsgen.json" - name: Generating Docs Headless - env: - OPENSCADPATH: $GITHUB_WORKSPACE/.. run: | + export OPENSCADPATH=$(dirname $GITHUB_WORKSPACE) xvfb-run --server-args="-screen 0, 1280x720x24" -a \ openscad-docsgen -ticmIf *.scad diff --git a/.github/workflows/testwf.yml b/.github/workflows/testwf.yml index 9363a6d..0c33682 100644 --- a/.github/workflows/testwf.yml +++ b/.github/workflows/testwf.yml @@ -39,9 +39,8 @@ jobs: echo "cube(50, center=100);" > testwf.scad - name: TestScript - env: - OPENSCADPATH: $GITHUB_WORKSPACE/.. run: | + export OPENSCADPATH=$(dirname $GITHUB_WORKSPACE) xvfb-run --server-args="-screen 0, 1280x720x24" -a \ openscad -o tmp_cube.png --imgsize=640,480 --view=axes,scales --projection=o --autocenter --viewall --preview '' --hardwarnings testwf.scad From 178f3b8c20bb6fb0651a6434c49596dca01d1246 Mon Sep 17 00:00:00 2001 From: github-actions Date: Tue, 1 Jun 2021 00:36:32 +0000 Subject: [PATCH 49/72] Bump release version. --- version.scad | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.scad b/version.scad index 095607e..4226732 100644 --- a/version.scad +++ b/version.scad @@ -6,7 +6,7 @@ ////////////////////////////////////////////////////////////////////// -BOSL_VERSION = [2,0,645]; +BOSL_VERSION = [2,0,646]; // Section: BOSL Library Version Functions From 536d21b46e4b6538c1301103c37efd84739d8f26 Mon Sep 17 00:00:00 2001 From: Garth Minette Date: Mon, 31 May 2021 17:42:01 -0700 Subject: [PATCH 50/72] Fix for installing gifsicle for workflows. --- .github/workflows/docsgen.yml | 2 +- .github/workflows/forced_docsgen.yml | 2 +- .github/workflows/testwf.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/docsgen.yml b/.github/workflows/docsgen.yml index 105f9b6..220a1da 100644 --- a/.github/workflows/docsgen.yml +++ b/.github/workflows/docsgen.yml @@ -22,7 +22,7 @@ jobs: run: sudo apt update - name: Install Packages - run: sudo apt-get install -y python3-pip python3-dev python3-setuptools python3-pil + run: sudo apt-get install -y python3-pip python3-dev python3-setuptools python3-pil gifsicle - name: Install openscad-docsgen run: sudo pip3 install openscad_docsgen diff --git a/.github/workflows/forced_docsgen.yml b/.github/workflows/forced_docsgen.yml index 0d70d94..6393b90 100644 --- a/.github/workflows/forced_docsgen.yml +++ b/.github/workflows/forced_docsgen.yml @@ -20,7 +20,7 @@ jobs: run: sudo apt update - name: Install Packages - run: sudo apt-get install -y python3-pip python3-dev python3-setuptools python3-pil + run: sudo apt-get install -y python3-pip python3-dev python3-setuptools python3-pil gifsicle - name: Install openscad-docsgen run: sudo pip3 install openscad_docsgen diff --git a/.github/workflows/testwf.yml b/.github/workflows/testwf.yml index 0c33682..18ee4e1 100644 --- a/.github/workflows/testwf.yml +++ b/.github/workflows/testwf.yml @@ -20,7 +20,7 @@ jobs: run: sudo apt update - name: Install Packages - run: sudo apt-get install -y python3-pip python3-dev python3-setuptools python3-pil + run: sudo apt-get install -y python3-pip python3-dev python3-setuptools python3-pil gifsicle - name: Install openscad-docsgen run: sudo pip3 install openscad_docsgen From af4d3c02acb9a6ce8b1e7ddf5739d76c624f8f2b Mon Sep 17 00:00:00 2001 From: github-actions Date: Tue, 1 Jun 2021 00:43:01 +0000 Subject: [PATCH 51/72] Bump release version. --- version.scad | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.scad b/version.scad index 4226732..4f7d766 100644 --- a/version.scad +++ b/version.scad @@ -6,7 +6,7 @@ ////////////////////////////////////////////////////////////////////// -BOSL_VERSION = [2,0,646]; +BOSL_VERSION = [2,0,647]; // Section: BOSL Library Version Functions From 5a69414f67662dca3bac3a2d970d9c446c26d980 Mon Sep 17 00:00:00 2001 From: Garth Minette Date: Tue, 1 Jun 2021 18:03:08 -0700 Subject: [PATCH 52/72] Test glxinfo in workflow. --- .github/workflows/testwf.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/testwf.yml b/.github/workflows/testwf.yml index 18ee4e1..e3bb817 100644 --- a/.github/workflows/testwf.yml +++ b/.github/workflows/testwf.yml @@ -42,5 +42,5 @@ jobs: run: | export OPENSCADPATH=$(dirname $GITHUB_WORKSPACE) xvfb-run --server-args="-screen 0, 1280x720x24" -a \ - openscad -o tmp_cube.png --imgsize=640,480 --view=axes,scales --projection=o --autocenter --viewall --preview '' --hardwarnings testwf.scad + glxinfo From 9f911c0644f05a825af571820719e854a42b8492 Mon Sep 17 00:00:00 2001 From: github-actions Date: Wed, 2 Jun 2021 01:04:12 +0000 Subject: [PATCH 53/72] Bump release version. --- version.scad | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.scad b/version.scad index 4f7d766..20acde6 100644 --- a/version.scad +++ b/version.scad @@ -6,7 +6,7 @@ ////////////////////////////////////////////////////////////////////// -BOSL_VERSION = [2,0,647]; +BOSL_VERSION = [2,0,648]; // Section: BOSL Library Version Functions From 3aabe3ffde85e546eef152b8db679b8d4e42ab88 Mon Sep 17 00:00:00 2001 From: Garth Minette Date: Tue, 1 Jun 2021 18:07:33 -0700 Subject: [PATCH 54/72] Setup test opengl worflow --- .github/workflows/testwf.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/testwf.yml b/.github/workflows/testwf.yml index e3bb817..f99e8f6 100644 --- a/.github/workflows/testwf.yml +++ b/.github/workflows/testwf.yml @@ -16,6 +16,9 @@ jobs: repository: revarbat/BOSL2.wiki path: BOSL2.wiki + - name: Setup OpenGL + uses: openrndr/setup-opengl@v1.1 + - name: Apt Update run: sudo apt update From a8ccf89839c2d135a2a0d20e0d87be2be0af98ff Mon Sep 17 00:00:00 2001 From: github-actions Date: Wed, 2 Jun 2021 01:08:39 +0000 Subject: [PATCH 55/72] Bump release version. --- version.scad | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.scad b/version.scad index 20acde6..424d29f 100644 --- a/version.scad +++ b/version.scad @@ -6,7 +6,7 @@ ////////////////////////////////////////////////////////////////////// -BOSL_VERSION = [2,0,648]; +BOSL_VERSION = [2,0,649]; // Section: BOSL Library Version Functions From c7073a47b23894c4dcb1c21eb4d30782d813980d Mon Sep 17 00:00:00 2001 From: Garth Minette Date: Mon, 7 Jun 2021 20:46:50 -0700 Subject: [PATCH 56/72] Fixed anchoring of cubetruss_corner() --- attachments.scad | 3 ++- cubetruss.scad | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/attachments.scad b/attachments.scad index 55f22e4..3dd12da 100644 --- a/attachments.scad +++ b/attachments.scad @@ -448,7 +448,8 @@ function attach_transform(anchor, spin, orient, geom, p) = function find_anchor(anchor, geom) = let( cp = select(geom,-3), - offset = anchor==CENTER? CENTER : select(geom,-2), + offset_raw = select(geom,-2), + offset = [for (i=[0:2]) anchor[i]==0? 0 : offset_raw[i]], // prevents bad centering. anchors = last(geom), type = geom[0] ) diff --git a/cubetruss.scad b/cubetruss.scad index 0fbaadb..35a85d0 100644 --- a/cubetruss.scad +++ b/cubetruss.scad @@ -512,12 +512,12 @@ module cubetruss_corner(h=1, extents=[1,1,0,0,1], bracing, size, strut, clipthic bracing = is_undef(bracing)? $cubetruss_bracing : bracing; clipthick = is_undef(clipthick)? $cubetruss_clip_thickness : clipthick; exts = is_vector(extents)? list_fit(extents,5,fill=0) : [extents, extents, 0, 0, extents]; - s = [cubetruss_dist(1+exts[0]+exts[2],1), cubetruss_dist(1+exts[1]+exts[3],1), cubetruss_dist(h+exts[4],1)]; - offset = [cubetruss_dist(exts[0]-exts[2],0), cubetruss_dist(exts[1]-exts[3],0), cubetruss_dist(exts[4],0)]/2; + s = [cubetruss_dist(exts[0]+1+exts[2],1), cubetruss_dist(exts[1]+1+exts[3],1), cubetruss_dist(h+exts[4],1)]; + offset = [cubetruss_dist(exts[0]-exts[2],0), cubetruss_dist(exts[1]-exts[3],0), cubetruss_dist(h+exts[4]-1,0)]/2; attachable(anchor,spin,orient, size=s, offset=offset) { union() { for (zcol = [0:h-1]) { - up((size-strut+0.01)*zcol) { + up((size-strut)*zcol) { cubetruss_segment(size=size, strut=strut, bracing=bracing); } } From 49a2d5125cf981c3991baea12f0ff3ade97ca7f4 Mon Sep 17 00:00:00 2001 From: github-actions Date: Tue, 8 Jun 2021 19:52:55 +0000 Subject: [PATCH 57/72] Bump release version. --- version.scad | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.scad b/version.scad index 424d29f..348a4b9 100644 --- a/version.scad +++ b/version.scad @@ -6,7 +6,7 @@ ////////////////////////////////////////////////////////////////////// -BOSL_VERSION = [2,0,649]; +BOSL_VERSION = [2,0,650]; // Section: BOSL Library Version Functions From 4ddb9031ce8600864aa555dbdb73a7cafede2e35 Mon Sep 17 00:00:00 2001 From: Garth Minette Date: Tue, 8 Jun 2021 18:08:21 -0700 Subject: [PATCH 58/72] Corrected assertion in trapezoidal_threaded_rod() --- threading.scad | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/threading.scad b/threading.scad index 50e1336..cd026f6 100644 --- a/threading.scad +++ b/threading.scad @@ -183,7 +183,7 @@ module trapezoidal_threaded_rod( higthr1 = ceil(higang1 / 360); higthr2 = ceil(higang2 / 360); pdepth = -min(subindex(profile,1)); - dummy1 = assert(_r1>2*pdepth) assert(_r2>2*pdepth); + dummy1 = assert(_r1>pdepth) assert(_r2>pdepth); skew_mat = affine3d_skew(sxz=(_r2-_r1)/l); side_mat = affine3d_xrot(90) * affine3d_mirror([-1,1,0]) * From 203023b03b90b3bc053a1e35b52970db1d75dc56 Mon Sep 17 00:00:00 2001 From: github-actions Date: Wed, 9 Jun 2021 01:11:52 +0000 Subject: [PATCH 59/72] Bump release version. --- version.scad | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.scad b/version.scad index 348a4b9..5a8e835 100644 --- a/version.scad +++ b/version.scad @@ -6,7 +6,7 @@ ////////////////////////////////////////////////////////////////////// -BOSL_VERSION = [2,0,650]; +BOSL_VERSION = [2,0,651]; // Section: BOSL Library Version Functions From 1670b0fa1657da8f97f8ceb7ee9d418db167bb78 Mon Sep 17 00:00:00 2001 From: Garth Minette Date: Wed, 9 Jun 2021 18:29:29 -0700 Subject: [PATCH 60/72] NPT threading fixes. --- threading.scad | 65 ++++++++++++++++++++++++++++++++----------------- transforms.scad | 4 +-- 2 files changed, 45 insertions(+), 24 deletions(-) diff --git a/threading.scad b/threading.scad index cd026f6..3a0ca7c 100644 --- a/threading.scad +++ b/threading.scad @@ -208,7 +208,7 @@ module trapezoidal_threaded_rod( prof = apply(side_mat, [ for (thread = [-threads/2:1:threads/2-1]) let( tang = (thread/starts) * 360 + ang, - hsc = + hsc = internal? 1 : abs(tang) > twist/2? 0 : (higang1==0 && higang2==0)? 1 : lookup(tang, hig_table), @@ -225,7 +225,7 @@ module trapezoidal_threaded_rod( ]; thread_vnfs = vnf_merge([ for (i=[0:1:starts-1]) - zrot(i*360/starts, p=vnf_vertex_array(thread_verts, reverse=left_handed)), + zrot(i*360/starts, p=vnf_vertex_array(thread_verts, reverse=left_handed, style="min_edge")), for (i=[0:1:starts-1]) let( rmat = zrot(i*360/starts), pts = deduplicate(list_head(thread_verts[0], len(prof3d)+1)), @@ -447,11 +447,15 @@ module threaded_nut( // Module: npt_threaded_rod() // Description: -// Constructs a standard NPT pipe threading. +// Constructs a standard NPT pipe end threading. If `internal=true`, creates a mask for making +// internal pipe threads. Tapers smaller upwards if `internal=false`. Tapers smaller downwards +// if `internal=true`. If `hollow=true` and `internal=false`, then the pipe threads will be +// hollowed out into a pipe with the apropriate internal diameter. // Arguments: -// d = Outer diameter of threaded rod. -// left_handed = if true, create left-handed threads. Default = false -// bevel = if true, bevel the thread ends. Default: false +// size = NPT standard pipe size in inches. 1/16", 1/8", 1/4", 3/8", 1/2", 3/4", 1", 1+1/4", 1+1/2", or 2". Default: 1/2" +// left_handed = If true, create left-handed threads. Default = false +// bevel = If true, bevel the thread ends. Default: false +// hollow = If true, create a pipe with the correct internal diameter. // internal = If true, make this a mask for making internal threads. // anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` // spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0` @@ -467,9 +471,16 @@ module npt_threaded_rod( size=1/2, left_handed=false, bevel=false, + hollow=false, internal=false, anchor, spin, orient ) { + assert(is_finite(size)); + assert(is_bool(left_handed)); + assert(is_bool(bevel)); + assert(is_bool(hollow)); + assert(is_bool(internal)); + assert(!(internal&&hollow), "Cannot created a hollow internal threads mask."); info_table = [ // Size OD len TPI [ 1/16, [ 0.3896, 0.308, 27 ]], @@ -488,8 +499,10 @@ module npt_threaded_rod( l = 25.4 * info[0]; d = 25.4 * info[1]; pitch = 25.4 / info[2]; - r1 = get_radius(d=d, dflt=0.84 * 25.4 / 2); - r2 = r1 - l/32; + rr = get_radius(d=d, dflt=0.84 * 25.4 / 2); + rr2 = rr - l/32; + r1 = internal? rr2 : rr; + r2 = internal? rr : rr2; depth = pitch * cos(30) * 5/8; profile = internal? [ [-6/16, -depth/pitch], @@ -506,20 +519,28 @@ module npt_threaded_rod( [ 6/16, -depth/pitch], [ 7/16, -depth/pitch*1.07] ]; - trapezoidal_threaded_rod( - d1=2*r1, d2=2*r2, l=l, - pitch=pitch, - thread_depth=depth, - thread_angle=30, - profile=profile, - left_handed=left_handed, - bevel=bevel, - internal=internal, - higbee=r1*PI/2, - anchor=anchor, - spin=spin, - orient=orient - ) children(); + attachable(anchor,spin,orient, l=l, r1=r1, r2=r2) { + difference() { + trapezoidal_threaded_rod( + d1=2*r1, d2=2*r2, l=l, + pitch=pitch, + thread_depth=depth, + thread_angle=30, + profile=profile, + left_handed=left_handed, + bevel=bevel, + internal=internal, + higbee=r1*PI/2, + anchor=anchor, + spin=spin, + orient=orient + ); + if (hollow) { + cylinder(l=l+1, d=size*INCH, center=true); + } else nil(); + } + children(); + } } diff --git a/transforms.scad b/transforms.scad index e5372de..2b1fe39 100644 --- a/transforms.scad +++ b/transforms.scad @@ -946,8 +946,8 @@ function yscale(y=1, p, cp=0, planar=false) = assert(is_bool(planar)) let( cp = is_num(cp)? [0,cp,0] : cp ) (planar || (!is_undef(p) && len(p)==2)) - ? scale([1,y],p=p) - : scale([1,y,1],p=p); + ? scale([1,y], cp=cp, p=p) + : scale([1,y,1], cp=cp, p=p); // Function&Module: zscale() From 9d9234513d4bed997cd170c4a09c81035d89f74a Mon Sep 17 00:00:00 2001 From: Garth Minette Date: Thu, 10 Jun 2021 15:25:34 -0700 Subject: [PATCH 61/72] Fix regressions return code. --- scripts/run_tests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/run_tests.sh b/scripts/run_tests.sh index 69fe94a..bcfc494 100755 --- a/scripts/run_tests.sh +++ b/scripts/run_tests.sh @@ -22,7 +22,7 @@ for testfile in "${INFILES[@]}"; do else echo "$repname: FAIL!" echo "$output" - OUTCODE="$retcode" + OUTCODE=1 fi rm -f out.echo fi From 5ee224f2d3c18f752bdb6d75457d8d326144e777 Mon Sep 17 00:00:00 2001 From: Garth Minette Date: Thu, 10 Jun 2021 15:49:44 -0700 Subject: [PATCH 62/72] Added point_segment_distance(), segment_distance(). Renamed distance_from_line() -> point_line_distance(). Renamed distance_from_plane() -> point_plane_distance() --- geometry.scad | 74 +++++++++++++++++++++++++++++++++++----- skin.scad | 36 +++++++++++-------- tests/test_geometry.scad | 50 ++++++++++++++++++++------- tests/test_shapes.scad | 22 ++++++------ 4 files changed, 135 insertions(+), 47 deletions(-) diff --git a/geometry.scad b/geometry.scad index 24fbac6..27adf7f 100644 --- a/geometry.scad +++ b/geometry.scad @@ -85,22 +85,80 @@ function collinear(a, b, c, eps=EPSILON) = : noncollinear_triple(points,error=false,eps=eps)==[]; -// Function: distance_from_line() +// Function: point_line_distance() // Usage: -// distance_from_line(line, pt); +// point_line_distance(pt, line); // Description: // Finds the perpendicular distance of a point `pt` from the line `line`. // Arguments: // line = A list of two points, defining a line that both are on. // pt = A point to find the distance of from the line. // Example: -// distance_from_line([[-10,0], [10,0]], [3,8]); // Returns: 8 -function distance_from_line(line, pt) = +// dist = point_line_distance([3,8], [[-10,0], [10,0]]); // Returns: 8 +function point_line_distance(pt, line) = assert( _valid_line(line) && is_vector(pt,len(line[0])), "Invalid line, invalid point or incompatible dimensions." ) _dist2line(pt-line[0],unit(line[1]-line[0])); +// Function: point_segment_distance() +// Usage: +// dist = point_segment_distance(pt, seg); +// Description: +// Returns the closest distance of the given point to the given line segment. +// Arguments: +// pt = The point to check the distance of. +// seg = The two points representing the line segment to check the distance of. +// Example: +// dist = point_segment_distance([3,8], [[-10,0], [10,0]]); // Returns: 8 +// dist2 = point_segment_distance([14,3], [[-10,0], [10,0]]); // Returns: 5 +function point_segment_distance(pt, seg) = + norm(seg[0]-seg[1]) < EPSILON ? norm(pt-seg[0]) : + norm(pt-segment_closest_point(seg,pt)); + + +// Function: segment_distance() +// Usage: +// dist = segment_distance(seg1, seg2); +// Description: +// Returns the closest distance of the two given line segments. +// Arguments: +// seg1 = The list of two points representing the first line segment to check the distance of. +// seg2 = The list of two points representing the second line segment to check the distance of. +// Example: +// dist = segment_distance([[-14,3], [-15,9]], [[-10,0], [10,0]]); // Returns: 5 +// dist2 = segment_distance([[-5,5], [5,-5]], [[-10,3], [10,-3]]); // Returns: 0 +function segment_distance(seg1, seg2) = + let( + dseg1 = seg1[1]-seg1[0], + dseg2 = seg2[1]-seg2[0], + A = [ [dseg1*dseg1, -dseg1*dseg2], + [-dseg2*dseg1, dseg2*dseg2] ], + b = -[ dseg1, -dseg2 ]*(seg1[0]-seg2[0]), + uv = linear_solve(A,b) + ) + !uv ? + norm(dseg1)=0 && uv[0]<=1 ? + let( p1 = seg1[0] + uv[0]*dseg1 ) + uv[1]>=0 && uv[1]<=1 + ? norm(p1 - (seg2[0] + uv[1]*dseg2) ) + : min(norm(p1-seg2[0]), norm(p1-seg2[1])) : + uv[1]>=0 && uv[1]<=1 + ? let( p2 = seg2[0] + uv[1]*dseg2 ) + min(norm(p2-seg1[0]), norm(p2-seg1[1])) + : min( + norm(seg1[0]-seg2[0]), + norm(seg1[0]-seg2[1]), + norm(seg1[1]-seg2[0]), + norm(seg1[1]-seg2[1]) + ); + + // Function: line_normal() // Usage: // line_normal([P1,P2]) @@ -1072,9 +1130,9 @@ function plane_point_nearest_origin(plane) = point3d(plane) * plane[3]; -// Function: distance_from_plane() +// Function: point_plane_distance() // Usage: -// distance_from_plane(plane, point) +// point_plane_distance(plane, point) // Description: // Given a plane as [A,B,C,D] where the cartesian equation for that plane // is Ax+By+Cz=D, determines how far from that plane the given point is. @@ -1085,7 +1143,7 @@ function plane_point_nearest_origin(plane) = // Arguments: // plane = The `[A,B,C,D]` plane definition where `Ax+By+Cz=D` is the formula of the plane. // point = The distance evaluation point. -function distance_from_plane(plane, point) = +function point_plane_distance(plane, point) = assert( _valid_plane(plane), "Invalid input plane." ) assert( is_vector(point,3), "The point should be a 3D point." ) let( plane = normalize_plane(plane) ) @@ -1310,7 +1368,7 @@ function points_on_plane(points, plane, eps=EPSILON) = // plane = The [A,B,C,D] coefficients for the first plane equation `Ax+By+Cz=D`. // point = The 3D point to test. function in_front_of_plane(plane, point) = - distance_from_plane(plane, point) > EPSILON; + point_plane_distance(plane, point) > EPSILON; diff --git a/skin.scad b/skin.scad index 4609556..1614fdb 100644 --- a/skin.scad +++ b/skin.scad @@ -812,24 +812,30 @@ function _skin_tangent_match(poly1, poly2) = newbig = polygon_shift(big, shift), repeat_counts = [for(i=[0:len(small)-1]) posmod(cutpts[i]-select(cutpts,i-1),len(big))], newsmall = repeat_entries(small,repeat_counts) - ) - assert(len(newsmall)==len(newbig), "Tangent alignment failed, probably because of insufficient points or a concave curve") - swap ? [newbig, newsmall] : [newsmall, newbig]; + ) + assert(len(newsmall)==len(newbig), "Tangent alignment failed, probably because of insufficient points or a concave curve") + swap ? [newbig, newsmall] : [newsmall, newbig]; function _find_one_tangent(curve, edge, curve_offset=[0,0,0], closed=true) = - let( - angles = - [for(i=[0:len(curve)-(closed?1:2)]) - let( - plane = plane3pt( edge[0], edge[1], curve[i]), - tangent = [curve[i], select(curve,i+1)] - ) - plane_line_angle(plane,tangent)], - zero_cross = [for(i=[0:len(curve)-(closed?1:2)]) if (sign(angles[i]) != sign(select(angles,i+1))) i], - d = [for(i=zero_cross) distance_from_line(edge, curve[i]+curve_offset)] - ) - zero_cross[min_index(d)]; + let( + angles = [ + for (i = [0:len(curve)-(closed?1:2)]) + let( + plane = plane3pt( edge[0], edge[1], curve[i]), + tangent = [curve[i], select(curve,i+1)] + ) plane_line_angle(plane,tangent) + ], + zero_cross = [ + for (i = [0:len(curve)-(closed?1:2)]) + if (sign(angles[i]) != sign(select(angles,i+1))) + i + ], + d = [ + for (i = zero_cross) + point_line_distance(curve[i]+curve_offset, edge) + ] + ) zero_cross[min_index(d)]; // Function: associate_vertices() diff --git a/tests/test_geometry.scad b/tests/test_geometry.scad index 04f5126..9cf7c40 100644 --- a/tests/test_geometry.scad +++ b/tests/test_geometry.scad @@ -9,7 +9,9 @@ include <../std.scad> test_point_on_segment2d(); test_point_left_of_line2d(); test_collinear(); -test_distance_from_line(); +test_point_line_distance(); +test_point_segment_distance(); +test_segment_distance(); test_line_normal(); test_line_intersection(); //test_line_ray_intersection(); @@ -44,7 +46,7 @@ test_plane_normal(); test_plane_offset(); test_projection_on_plane(); test_plane_point_nearest_origin(); -test_distance_from_plane(); +test_point_plane_distance(); test__general_plane_line_intersection(); test_plane_line_angle(); @@ -230,7 +232,7 @@ module test__general_plane_line_intersection() { interspoint = line1[0]+inters1[1]*(line1[1]-line1[0]); assert_approx(inters1[0],interspoint, info1); assert_approx(point3d(plane1)*inters1[0], plane1[3], info1); // interspoint on the plane - assert_approx(distance_from_plane(plane1, inters1[0]), 0, info1); // inters1[0] on the plane + assert_approx(point_plane_distance(plane1, inters1[0]), 0, info1); // inters1[0] on the plane } // line parallel to the plane @@ -351,13 +353,35 @@ module test_collinear() { *test_collinear(); -module test_distance_from_line() { - assert(abs(distance_from_line([[-10,-10,-10], [10,10,10]], [1,1,1])) < EPSILON); - assert(abs(distance_from_line([[-10,-10,-10], [10,10,10]], [-1,-1,-1])) < EPSILON); - assert(abs(distance_from_line([[-10,-10,-10], [10,10,10]], [1,-1,0]) - sqrt(2)) < EPSILON); - assert(abs(distance_from_line([[-10,-10,-10], [10,10,10]], [8,-8,0]) - 8*sqrt(2)) < EPSILON); +module test_point_line_distance() { + assert_approx(point_line_distance([1,1,1], [[-10,-10,-10], [10,10,10]]), 0); + assert_approx(point_line_distance([-1,-1,-1], [[-10,-10,-10], [10,10,10]]), 0); + assert_approx(point_line_distance([1,-1,0], [[-10,-10,-10], [10,10,10]]), sqrt(2)); + assert_approx(point_line_distance([8,-8,0], [[-10,-10,-10], [10,10,10]]), 8*sqrt(2)); } -*test_distance_from_line(); +*test_point_line_distance(); + + +module test_point_segment_distance() { + assert_approx(point_segment_distance([3,8], [[-10,0], [10,0]]), 8); + assert_approx(point_segment_distance([14,3], [[-10,0], [10,0]]), 5); +} +*test_point_segment_distance(); + + +module test_segment_distance() { + assert_approx(segment_distance([[-14,3], [-14,9]], [[-10,0], [10,0]]), 5); + assert_approx(segment_distance([[-14,3], [-15,9]], [[-10,0], [10,0]]), 5); + assert_approx(segment_distance([[14,3], [14,9]], [[-10,0], [10,0]]), 5); + assert_approx(segment_distance([[-14,-3], [-14,-9]], [[-10,0], [10,0]]), 5); + assert_approx(segment_distance([[-14,-3], [-15,-9]], [[-10,0], [10,0]]), 5); + assert_approx(segment_distance([[14,-3], [14,-9]], [[-10,0], [10,0]]), 5); + assert_approx(segment_distance([[14,3], [14,-3]], [[-10,0], [10,0]]), 4); + assert_approx(segment_distance([[-14,3], [-14,-3]], [[-10,0], [10,0]]), 4); + assert_approx(segment_distance([[-6,5], [4,-5]], [[-10,0], [10,0]]), 0); + assert_approx(segment_distance([[-5,5], [5,-5]], [[-10,3], [10,-3]]), 0); +} +*test_segment_distance(); module test_line_normal() { @@ -713,12 +737,12 @@ module test_plane_normal() { *test_plane_normal(); -module test_distance_from_plane() { +module test_point_plane_distance() { plane1 = plane3pt([-10,0,0], [0,10,0], [10,0,0]); - assert(distance_from_plane(plane1, [0,0,5]) == 5); - assert(distance_from_plane(plane1, [5,5,8]) == 8); + assert(point_plane_distance(plane1, [0,0,5]) == 5); + assert(point_plane_distance(plane1, [5,5,8]) == 8); } -*test_distance_from_plane(); +*test_point_plane_distance(); module test_polygon_line_intersection() { diff --git a/tests/test_shapes.scad b/tests/test_shapes.scad index d1e7570..ca904d7 100644 --- a/tests/test_shapes.scad +++ b/tests/test_shapes.scad @@ -9,17 +9,17 @@ module test_prismoid() { assert_approx(prismoid([100,80],[40,50],h=50,anchor=BOT), [[[20,25,50],[20,-25,50],[-20,-25,50],[-20,25,50],[50,40,0],[50,-40,0],[-50,-40,0],[-50,40,0]],[[0,1,2],[0,2,3],[0,4,5],[0,5,1],[1,5,6],[1,6,2],[2,6,7],[2,7,3],[3,7,4],[3,4,0],[4,7,6],[4,6,5]]]); assert_approx(prismoid([100,80],[40,50],h=50,anchor=TOP+RIGHT), [[[0,25,0],[0,-25,0],[-40,-25,0],[-40,25,0],[30,40,-50],[30,-40,-50],[-70,-40,-50],[-70,40,-50]],[[0,1,2],[0,2,3],[0,4,5],[0,5,1],[1,5,6],[1,6,2],[2,6,7],[2,7,3],[3,7,4],[3,4,0],[4,7,6],[4,6,5]]]); assert_approx(prismoid([100,80],[40,50],h=50,shift=[10,5]), [[[30,30,50],[30,-20,50],[-10,-20,50],[-10,30,50],[50,40,0],[50,-40,0],[-50,-40,0],[-50,40,0]],[[0,1,2],[0,2,3],[0,4,5],[0,5,1],[1,5,6],[1,6,2],[2,6,7],[2,7,3],[3,7,4],[3,4,0],[4,7,6],[4,6,5]]]); - assert_approx(prismoid([100,80],[40,50],h=50,shift=[10,5],chamfer=5), [[[50,-35,0],[45,-40,0],[-45,-40,0],[-50,-35,0],[-50,35,0],[-45,40,0],[45,40,0],[50,35,0],[30,-15,50],[25,-20,50],[-5,-20,50],[-10,-15,50],[-10,25,50],[-5,30,50],[25,30,50],[30,25,50]],[[4,1,0],[1,4,2],[2,4,3],[4,0,5],[14,5,6],[5,0,6],[14,6,7],[6,0,7],[0,1,8],[7,0,8],[1,2,9],[8,1,9],[14,8,9],[2,3,10],[9,2,10],[14,9,10],[14,10,11],[3,4,11],[10,3,11],[4,5,12],[14,11,12],[11,4,12],[5,14,13],[14,12,13],[12,5,13],[14,7,15],[7,8,15],[8,14,15]]]); - assert_approx(prismoid([100,80],[40,50],h=50,shift=[10,5],chamfer1=5),[[[50,-35,0],[45,-40,0],[-45,-40,0],[-50,-35,0],[-50,35,0],[-45,40,0],[45,40,0],[50,35,0],[30,-20,50],[-10,-20,50],[-10,30,50],[30,30,50]],[[4,1,0],[1,4,2],[2,4,3],[4,0,5],[11,5,6],[5,0,6],[0,11,7],[11,6,7],[6,0,7],[11,0,8],[0,1,8],[1,2,8],[3,4,9],[2,3,9],[8,2,9],[11,8,9],[4,5,10],[5,11,10],[11,9,10],[9,4,10]]]); - assert_approx(prismoid([100,80],[40,50],h=50,shift=[10,5],chamfer2=5), [[[50,-40,0],[-50,-40,0],[-50,40,0],[50,40,0],[30,-15,50],[25,-20,50],[-5,-20,50],[-10,-15,50],[-10,25,50],[-5,30,50],[25,30,50],[30,25,50]],[[2,1,0],[10,2,3],[2,0,3],[3,0,4],[10,4,5],[0,1,5],[4,0,5],[10,5,6],[5,1,6],[1,2,7],[6,1,7],[10,6,7],[10,7,8],[7,2,8],[2,10,9],[10,8,9],[8,2,9],[10,3,11],[3,4,11],[4,10,11]]]); - assert_approx(prismoid([100,80],[40,50],h=50,shift=[10,5],chamfer1=5,chamfer2=10), [[[50,-35,0],[45,-40,0],[-45,-40,0],[-50,-35,0],[-50,35,0],[-45,40,0],[45,40,0],[50,35,0],[30,-10,50],[20,-20,50],[0,-20,50],[-10,-10,50],[-10,20,50],[0,30,50],[20,30,50],[30,20,50]],[[4,1,0],[1,4,2],[2,4,3],[4,0,5],[14,5,6],[5,0,6],[14,6,7],[6,0,7],[0,1,8],[7,0,8],[1,2,9],[8,1,9],[14,8,9],[2,3,10],[9,2,10],[14,9,10],[14,10,11],[3,4,11],[10,3,11],[4,5,12],[14,11,12],[11,4,12],[5,14,13],[14,12,13],[12,5,13],[14,7,15],[7,8,15],[8,14,15]]]); - assert_approx(prismoid([100,80],[40,50],h=50,shift=[10,5],rounding=5), [[[50,-35,0],[49.8296291314,-36.2940952255,0],[49.3301270189,-37.5,0],[48.5355339059,-38.5355339059,0],[47.5,-39.3301270189,0],[46.2940952255,-39.8296291314,0],[45,-40,0],[-45,-40,0],[-46.2940952255,-39.8296291314,0],[-47.5,-39.3301270189,0],[-48.5355339059,-38.5355339059,0],[-49.3301270189,-37.5,0],[-49.8296291314,-36.2940952255,0],[-50,-35,0],[-50,35,0],[-49.8296291314,36.2940952255,0],[-49.3301270189,37.5,0],[-48.5355339059,38.5355339059,0],[-47.5,39.3301270189,0],[-46.2940952255,39.8296291314,0],[-45,40,0],[45,40,0],[46.2940952255,39.8296291314,0],[47.5,39.3301270189,0],[48.5355339059,38.5355339059,0],[49.3301270189,37.5,0],[49.8296291314,36.2940952255,0],[50,35,0],[30,-15,50],[29.8296291314,-16.2940952255,50],[29.3301270189,-17.5,50],[28.5355339059,-18.5355339059,50],[27.5,-19.3301270189,50],[26.2940952255,-19.8296291314,50],[25,-20,50],[-5,-20,50],[-6.29409522551,-19.8296291314,50],[-7.5,-19.3301270189,50],[-8.53553390593,-18.5355339059,50],[-9.33012701892,-17.5,50],[-9.82962913145,-16.2940952255,50],[-10,-15,50],[-10,25,50],[-9.82962913145,26.2940952255,50],[-9.33012701892,27.5,50],[-8.53553390593,28.5355339059,50],[-7.5,29.3301270189,50],[-6.29409522551,29.8296291314,50],[-5,30,50],[25,30,50],[26.2940952255,29.8296291314,50],[27.5,29.3301270189,50],[28.5355339059,28.5355339059,50],[29.3301270189,27.5,50],[29.8296291314,26.2940952255,50],[30,25,50]],[[16,1,0],[1,16,2],[2,16,3],[3,16,4],[4,16,5],[5,16,6],[6,16,7],[7,16,8],[8,16,9],[9,16,10],[10,16,11],[11,16,12],[12,16,13],[13,16,14],[14,16,15],[16,0,17],[17,0,18],[18,0,19],[19,0,20],[20,0,21],[21,0,22],[51,22,23],[22,0,23],[51,23,24],[23,0,24],[24,0,25],[25,0,26],[26,0,27],[0,1,28],[27,0,28],[1,2,29],[28,1,29],[51,28,29],[2,3,30],[29,2,30],[51,29,30],[3,4,31],[30,3,31],[51,30,31],[4,5,32],[31,4,32],[51,31,32],[5,6,33],[32,5,33],[51,32,33],[51,33,34],[6,7,34],[33,6,34],[51,34,35],[7,8,35],[34,7,35],[51,35,36],[8,9,36],[35,8,36],[51,36,37],[9,10,37],[36,9,37],[51,37,38],[10,11,38],[37,10,38],[51,38,39],[11,12,39],[38,11,39],[51,39,40],[12,13,40],[39,12,40],[51,40,41],[13,14,41],[40,13,41],[51,41,42],[14,15,42],[41,14,42],[51,42,43],[15,16,43],[42,15,43],[51,43,44],[16,17,44],[43,16,44],[17,18,45],[44,17,45],[51,44,45],[18,19,46],[45,18,46],[51,45,46],[19,20,47],[46,19,47],[51,46,47],[20,21,48],[47,20,48],[51,47,48],[21,22,49],[51,48,49],[48,21,49],[22,51,50],[51,49,50],[49,22,50],[51,24,52],[24,25,52],[28,51,52],[25,26,53],[52,25,53],[28,52,53],[26,27,54],[53,26,54],[28,53,54],[27,28,55],[28,54,55],[54,27,55]]]); - assert_approx(prismoid([100,80],[40,50],h=50,shift=[10,5],rounding1=5), [[[50,-35,0],[49.8296291314,-36.2940952255,0],[49.3301270189,-37.5,0],[48.5355339059,-38.5355339059,0],[47.5,-39.3301270189,0],[46.2940952255,-39.8296291314,0],[45,-40,0],[-45,-40,0],[-46.2940952255,-39.8296291314,0],[-47.5,-39.3301270189,0],[-48.5355339059,-38.5355339059,0],[-49.3301270189,-37.5,0],[-49.8296291314,-36.2940952255,0],[-50,-35,0],[-50,35,0],[-49.8296291314,36.2940952255,0],[-49.3301270189,37.5,0],[-48.5355339059,38.5355339059,0],[-47.5,39.3301270189,0],[-46.2940952255,39.8296291314,0],[-45,40,0],[45,40,0],[46.2940952255,39.8296291314,0],[47.5,39.3301270189,0],[48.5355339059,38.5355339059,0],[49.3301270189,37.5,0],[49.8296291314,36.2940952255,0],[50,35,0],[30,-20,50],[-10,-20,50],[-10,30,50],[30,30,50]],[[16,1,0],[1,16,2],[2,16,3],[3,16,4],[4,16,5],[5,16,6],[6,16,7],[7,16,8],[8,16,9],[9,16,10],[10,16,11],[11,16,12],[12,16,13],[13,16,14],[14,16,15],[16,0,17],[17,0,18],[18,0,19],[19,0,20],[31,20,21],[20,0,21],[31,21,22],[21,0,22],[31,22,23],[22,0,23],[31,23,24],[23,0,24],[31,24,25],[24,0,25],[31,25,26],[25,0,26],[0,31,27],[31,26,27],[26,0,27],[31,0,28],[0,1,28],[1,2,28],[2,3,28],[3,4,28],[4,5,28],[5,6,28],[6,7,28],[13,14,29],[7,8,29],[28,7,29],[8,9,29],[9,10,29],[10,11,29],[11,12,29],[12,13,29],[31,28,29],[17,18,30],[18,19,30],[19,20,30],[20,31,30],[15,16,30],[14,15,30],[29,14,30],[16,17,30],[31,29,30]]]); - assert_approx(prismoid([100,80],[40,50],h=50,shift=[10,5],rounding2=5), [[[50,-40,0],[-50,-40,0],[-50,40,0],[50,40,0],[30,-15,50],[29.8296291314,-16.2940952255,50],[29.3301270189,-17.5,50],[28.5355339059,-18.5355339059,50],[27.5,-19.3301270189,50],[26.2940952255,-19.8296291314,50],[25,-20,50],[-5,-20,50],[-6.29409522551,-19.8296291314,50],[-7.5,-19.3301270189,50],[-8.53553390593,-18.5355339059,50],[-9.33012701892,-17.5,50],[-9.82962913145,-16.2940952255,50],[-10,-15,50],[-10,25,50],[-9.82962913145,26.2940952255,50],[-9.33012701892,27.5,50],[-8.53553390593,28.5355339059,50],[-7.5,29.3301270189,50],[-6.29409522551,29.8296291314,50],[-5,30,50],[25,30,50],[26.2940952255,29.8296291314,50],[27.5,29.3301270189,50],[28.5355339059,28.5355339059,50],[29.3301270189,27.5,50],[29.8296291314,26.2940952255,50],[30,25,50]],[[2,1,0],[2,0,3],[3,0,4],[28,4,5],[4,0,5],[28,5,6],[5,0,6],[28,6,7],[6,0,7],[28,7,8],[7,0,8],[28,8,9],[8,0,9],[28,9,10],[0,1,10],[9,0,10],[10,1,11],[28,10,11],[11,1,12],[28,11,12],[12,1,13],[28,12,13],[13,1,14],[28,13,14],[14,1,15],[28,14,15],[15,1,16],[28,15,16],[1,2,17],[16,1,17],[28,16,17],[28,17,18],[17,2,18],[28,18,19],[18,2,19],[28,19,20],[19,2,20],[28,20,21],[20,2,21],[28,21,22],[21,2,22],[22,2,23],[28,22,23],[2,3,24],[23,2,24],[28,23,24],[28,24,25],[24,3,25],[28,25,26],[25,3,26],[3,28,27],[28,26,27],[26,3,27],[28,3,29],[4,28,29],[4,29,30],[29,3,30],[3,4,31],[4,30,31],[30,3,31]]]); - assert_approx(prismoid([100,80],[40,50],h=50,shift=[10,5],rounding1=5,rounding2=10), [[[50,-35,0],[49.8296291314,-36.2940952255,0],[49.3301270189,-37.5,0],[48.5355339059,-38.5355339059,0],[47.5,-39.3301270189,0],[46.2940952255,-39.8296291314,0],[45,-40,0],[-45,-40,0],[-46.2940952255,-39.8296291314,0],[-47.5,-39.3301270189,0],[-48.5355339059,-38.5355339059,0],[-49.3301270189,-37.5,0],[-49.8296291314,-36.2940952255,0],[-50,-35,0],[-50,35,0],[-49.8296291314,36.2940952255,0],[-49.3301270189,37.5,0],[-48.5355339059,38.5355339059,0],[-47.5,39.3301270189,0],[-46.2940952255,39.8296291314,0],[-45,40,0],[45,40,0],[46.2940952255,39.8296291314,0],[47.5,39.3301270189,0],[48.5355339059,38.5355339059,0],[49.3301270189,37.5,0],[49.8296291314,36.2940952255,0],[50,35,0],[30,-10,50],[29.6592582629,-12.588190451,50],[28.6602540378,-15,50],[27.0710678119,-17.0710678119,50],[25,-18.6602540378,50],[22.588190451,-19.6592582629,50],[20,-20,50],[0,-20,50],[-2.58819045103,-19.6592582629,50],[-5,-18.6602540378,50],[-7.07106781187,-17.0710678119,50],[-8.66025403784,-15,50],[-9.65925826289,-12.588190451,50],[-10,-10,50],[-10,20,50],[-9.65925826289,22.588190451,50],[-8.66025403784,25,50],[-7.07106781187,27.0710678119,50],[-5,28.6602540378,50],[-2.58819045103,29.6592582629,50],[0,30,50],[20,30,50],[22.588190451,29.6592582629,50],[25,28.6602540378,50],[27.0710678119,27.0710678119,50],[28.6602540378,25,50],[29.6592582629,22.588190451,50],[30,20,50]],[[16,1,0],[1,16,2],[2,16,3],[3,16,4],[4,16,5],[5,16,6],[6,16,7],[7,16,8],[8,16,9],[9,16,10],[10,16,11],[11,16,12],[12,16,13],[13,16,14],[14,16,15],[16,0,17],[17,0,18],[18,0,19],[19,0,20],[20,0,21],[21,0,22],[51,22,23],[22,0,23],[51,23,24],[23,0,24],[24,0,25],[25,0,26],[26,0,27],[0,1,28],[27,0,28],[1,2,29],[28,1,29],[51,28,29],[2,3,30],[29,2,30],[51,29,30],[3,4,31],[30,3,31],[51,30,31],[4,5,32],[31,4,32],[51,31,32],[5,6,33],[32,5,33],[51,32,33],[51,33,34],[6,7,34],[33,6,34],[51,34,35],[7,8,35],[34,7,35],[51,35,36],[8,9,36],[35,8,36],[51,36,37],[9,10,37],[36,9,37],[51,37,38],[10,11,38],[37,10,38],[51,38,39],[11,12,39],[38,11,39],[51,39,40],[12,13,40],[39,12,40],[51,40,41],[13,14,41],[40,13,41],[51,41,42],[14,15,42],[41,14,42],[51,42,43],[15,16,43],[42,15,43],[51,43,44],[16,17,44],[43,16,44],[51,44,45],[17,18,45],[44,17,45],[51,45,46],[18,19,46],[45,18,46],[19,20,47],[46,19,47],[51,46,47],[20,21,48],[47,20,48],[51,47,48],[21,22,49],[51,48,49],[48,21,49],[22,51,50],[51,49,50],[49,22,50],[51,24,52],[24,25,52],[28,51,52],[25,26,53],[52,25,53],[28,52,53],[26,27,54],[53,26,54],[28,53,54],[27,28,55],[28,54,55],[54,27,55]]]); - assert_approx(prismoid([100,80],[40,50],h=50,shift=[10,5],rounding1=5,chamfer2=10), [[[50,-35,0],[49.8296291314,-36.2940952255,0],[49.3301270189,-37.5,0],[48.5355339059,-38.5355339059,0],[47.5,-39.3301270189,0],[46.2940952255,-39.8296291314,0],[45,-40,0],[-45,-40,0],[-46.2940952255,-39.8296291314,0],[-47.5,-39.3301270189,0],[-48.5355339059,-38.5355339059,0],[-49.3301270189,-37.5,0],[-49.8296291314,-36.2940952255,0],[-50,-35,0],[-50,35,0],[-49.8296291314,36.2940952255,0],[-49.3301270189,37.5,0],[-48.5355339059,38.5355339059,0],[-47.5,39.3301270189,0],[-46.2940952255,39.8296291314,0],[-45,40,0],[45,40,0],[46.2940952255,39.8296291314,0],[47.5,39.3301270189,0],[48.5355339059,38.5355339059,0],[49.3301270189,37.5,0],[49.8296291314,36.2940952255,0],[50,35,0],[30,-10,50],[20,-20,50],[0,-20,50],[-10,-10,50],[-10,20,50],[0,30,50],[20,30,50],[30,20,50]],[[0,16,9],[28,0,1],[0,9,1],[28,1,2],[1,9,2],[28,2,3],[2,9,3],[3,9,4],[4,9,5],[5,9,6],[6,9,7],[7,9,8],[9,16,10],[10,16,11],[11,16,12],[12,16,13],[13,16,14],[14,16,15],[16,0,17],[17,0,18],[18,0,19],[19,0,20],[20,0,21],[21,0,22],[22,0,23],[23,0,24],[24,0,25],[25,0,26],[0,28,27],[26,0,27],[28,3,29],[3,4,29],[4,5,29],[5,6,29],[6,7,29],[8,9,30],[7,8,30],[29,7,30],[9,10,30],[28,29,30],[28,30,31],[10,11,31],[30,10,31],[11,12,31],[12,13,31],[13,14,31],[28,31,32],[15,16,32],[14,15,32],[31,14,32],[16,17,32],[20,21,33],[28,32,33],[19,20,33],[17,18,33],[32,17,33],[18,19,33],[23,24,34],[28,33,34],[21,22,34],[33,21,34],[22,23,34],[26,27,35],[27,28,35],[25,26,35],[28,34,35],[24,25,35],[34,24,35]]]); - assert_approx(prismoid([100,80],[40,50],h=50,shift=[10,5],chamfer=[0,5,10,15]), [[[50,-25,0],[35,-40,0],[-40,-40,0],[-50,-30,0],[-50,35,0],[-45,40,0],[50,40,0],[30,-5,50],[15,-20,50],[0,-20,50],[-10,-10,50],[-10,25,50],[-5,30,50],[30,30,50]],[[4,1,0],[1,4,2],[2,4,3],[4,0,5],[0,13,6],[13,5,6],[5,0,6],[13,0,7],[0,1,7],[1,2,8],[7,1,8],[13,7,8],[13,8,9],[2,3,9],[8,2,9],[13,9,10],[3,4,10],[9,3,10],[4,5,11],[13,10,11],[10,4,11],[5,13,12],[13,11,12],[11,5,12]]]); - assert_approx(prismoid([100,80],[40,50],h=50,shift=[10,5],chamfer1=[15,10,5,0], rounding2=[0,5,10,15]), [[[50,-40,0],[-45,-40,0],[-50,-35,0],[-50,30,0],[-40,40,0],[35,40,0],[50,25,0],[30,-5,50],[29.4888873943,-8.88228567654,50],[27.9903810568,-12.5,50],[25.6066017178,-15.6066017178,50],[22.5,-17.9903810568,50],[18.8822856765,-19.4888873943,50],[15,-20,50],[0,-20,50],[-2.58819045103,-19.6592582629,50],[-5,-18.6602540378,50],[-7.07106781187,-17.0710678119,50],[-8.66025403784,-15,50],[-9.65925826289,-12.588190451,50],[-10,-10,50],[-10,25,50],[-9.82962913145,26.2940952255,50],[-9.33012701892,27.5,50],[-8.53553390593,28.5355339059,50],[-7.5,29.3301270189,50],[-6.29409522551,29.8296291314,50],[-5,30,50],[30,30,50]],[[3,1,0],[1,3,2],[3,0,4],[28,4,5],[4,0,5],[0,28,6],[28,5,6],[5,0,6],[28,0,7],[7,0,8],[28,7,8],[28,8,9],[8,0,9],[28,9,10],[9,0,10],[28,10,11],[10,0,11],[28,11,12],[11,0,12],[28,12,13],[0,1,13],[12,0,13],[28,13,14],[13,1,14],[28,14,15],[14,1,15],[28,15,16],[15,1,16],[28,16,17],[1,2,17],[16,1,17],[28,17,18],[17,2,18],[28,18,19],[18,2,19],[28,19,20],[2,3,20],[19,2,20],[28,20,21],[20,3,21],[21,3,22],[28,21,22],[22,3,23],[28,22,23],[3,4,24],[23,3,24],[28,23,24],[28,24,25],[24,4,25],[28,25,26],[25,4,26],[4,28,27],[28,26,27],[26,4,27]]]); + assert_approx(prismoid([100,80],[40,50],h=50,shift=[10,5],chamfer=5), [[[50,-35,0],[45,-40,0],[-45,-40,0],[-50,-35,0],[-50,35,0],[-45,40,0],[45,40,0],[50,35,0],[30,-15,50],[25,-20,50],[-5,-20,50],[-10,-15,50],[-10,25,50],[-5,30,50],[25,30,50],[30,25,50]],[[14,7,15],[7,8,15],[8,14,15],[5,14,13],[14,12,13],[12,5,13],[14,11,12],[11,4,12],[4,5,12],[14,10,11],[3,4,11],[10,3,11],[2,3,10],[9,2,10],[14,9,10],[14,8,9],[1,2,9],[8,1,9],[7,0,8],[0,1,8],[14,6,7],[6,0,7],[14,5,6],[5,0,6],[4,0,5],[2,4,3],[1,4,2],[4,1,0]]]); + assert_approx(prismoid([100,80],[40,50],h=50,shift=[10,5],chamfer1=5), [[[50,-35,0],[45,-40,0],[-45,-40,0],[-50,-35,0],[-50,35,0],[-45,40,0],[45,40,0],[50,35,0],[30,-20,50],[-10,-20,50],[-10,30,50],[30,30,50]],[[11,9,10],[9,4,10],[4,5,10],[5,11,10],[2,3,9],[8,2,9],[11,8,9],[3,4,9],[1,2,8],[11,0,8],[0,1,8],[0,11,7],[11,6,7],[6,0,7],[11,5,6],[5,0,6],[4,0,5],[2,4,3],[1,4,2],[4,1,0]]]); + assert_approx(prismoid([100,80],[40,50],h=50,shift=[10,5],chamfer2=5), [[[50,-40,0],[-50,-40,0],[-50,40,0],[50,40,0],[30,-15,50],[25,-20,50],[-5,-20,50],[-10,-15,50],[-10,25,50],[-5,30,50],[25,30,50],[30,25,50]],[[10,3,11],[3,4,11],[4,10,11],[2,10,9],[10,8,9],[8,2,9],[10,7,8],[7,2,8],[1,2,7],[6,1,7],[10,6,7],[10,5,6],[5,1,6],[10,4,5],[0,1,5],[4,0,5],[3,0,4],[10,2,3],[2,0,3],[2,1,0]]]); + assert_approx(prismoid([100,80],[40,50],h=50,shift=[10,5],chamfer1=5,chamfer2=10), [[[50,-35,0],[45,-40,0],[-45,-40,0],[-50,-35,0],[-50,35,0],[-45,40,0],[45,40,0],[50,35,0],[30,-10,50],[20,-20,50],[0,-20,50],[-10,-10,50],[-10,20,50],[0,30,50],[20,30,50],[30,20,50]],[[14,7,15],[7,8,15],[8,14,15],[5,14,13],[14,12,13],[12,5,13],[14,11,12],[11,4,12],[4,5,12],[14,10,11],[3,4,11],[10,3,11],[2,3,10],[9,2,10],[14,9,10],[14,8,9],[1,2,9],[8,1,9],[7,0,8],[0,1,8],[14,6,7],[6,0,7],[14,5,6],[5,0,6],[4,0,5],[2,4,3],[1,4,2],[4,1,0]]]); + assert_approx(prismoid([100,80],[40,50],h=50,shift=[10,5],rounding=5), [[[50,-35,0],[49.8296291314,-36.2940952255,0],[49.3301270189,-37.5,0],[48.5355339059,-38.5355339059,0],[47.5,-39.3301270189,0],[46.2940952255,-39.8296291314,0],[45,-40,0],[-45,-40,0],[-46.2940952255,-39.8296291314,0],[-47.5,-39.3301270189,0],[-48.5355339059,-38.5355339059,0],[-49.3301270189,-37.5,0],[-49.8296291314,-36.2940952255,0],[-50,-35,0],[-50,35,0],[-49.8296291314,36.2940952255,0],[-49.3301270189,37.5,0],[-48.5355339059,38.5355339059,0],[-47.5,39.3301270189,0],[-46.2940952255,39.8296291314,0],[-45,40,0],[45,40,0],[46.2940952255,39.8296291314,0],[47.5,39.3301270189,0],[48.5355339059,38.5355339059,0],[49.3301270189,37.5,0],[49.8296291314,36.2940952255,0],[50,35,0],[30,-15,50],[29.8296291314,-16.2940952255,50],[29.3301270189,-17.5,50],[28.5355339059,-18.5355339059,50],[27.5,-19.3301270189,50],[26.2940952255,-19.8296291314,50],[25,-20,50],[-5,-20,50],[-6.29409522551,-19.8296291314,50],[-7.5,-19.3301270189,50],[-8.53553390593,-18.5355339059,50],[-9.33012701892,-17.5,50],[-9.82962913145,-16.2940952255,50],[-10,-15,50],[-10,25,50],[-9.82962913145,26.2940952255,50],[-9.33012701892,27.5,50],[-8.53553390593,28.5355339059,50],[-7.5,29.3301270189,50],[-6.29409522551,29.8296291314,50],[-5,30,50],[25,30,50],[26.2940952255,29.8296291314,50],[27.5,29.3301270189,50],[28.5355339059,28.5355339059,50],[29.3301270189,27.5,50],[29.8296291314,26.2940952255,50],[30,25,50]],[[27,28,55],[28,54,55],[54,27,55],[28,53,54],[26,27,54],[53,26,54],[28,52,53],[25,26,53],[52,25,53],[28,51,52],[51,24,52],[24,25,52],[22,51,50],[51,49,50],[49,22,50],[51,48,49],[48,21,49],[21,22,49],[20,21,48],[47,20,48],[51,47,48],[19,20,47],[46,19,47],[51,46,47],[51,45,46],[18,19,46],[45,18,46],[51,44,45],[17,18,45],[44,17,45],[16,17,44],[43,16,44],[51,43,44],[15,16,43],[42,15,43],[51,42,43],[14,15,42],[41,14,42],[51,41,42],[13,14,41],[40,13,41],[51,40,41],[12,13,40],[39,12,40],[51,39,40],[11,12,39],[38,11,39],[51,38,39],[10,11,38],[37,10,38],[51,37,38],[9,10,37],[36,9,37],[51,36,37],[8,9,36],[35,8,36],[51,35,36],[7,8,35],[34,7,35],[51,34,35],[6,7,34],[33,6,34],[51,33,34],[51,32,33],[5,6,33],[32,5,33],[51,31,32],[4,5,32],[31,4,32],[51,30,31],[3,4,31],[30,3,31],[51,29,30],[2,3,30],[29,2,30],[51,28,29],[1,2,29],[28,1,29],[27,0,28],[0,1,28],[26,0,27],[25,0,26],[24,0,25],[51,23,24],[23,0,24],[51,22,23],[22,0,23],[21,0,22],[20,0,21],[19,0,20],[18,0,19],[17,0,18],[16,0,17],[14,16,15],[13,16,14],[12,16,13],[11,16,12],[10,16,11],[9,16,10],[8,16,9],[7,16,8],[6,16,7],[5,16,6],[4,16,5],[3,16,4],[2,16,3],[1,16,2],[16,1,0]]]); + assert_approx(prismoid([100,80],[40,50],h=50,shift=[10,5],rounding1=5), [[[50,-35,0],[49.8296291314,-36.2940952255,0],[49.3301270189,-37.5,0],[48.5355339059,-38.5355339059,0],[47.5,-39.3301270189,0],[46.2940952255,-39.8296291314,0],[45,-40,0],[-45,-40,0],[-46.2940952255,-39.8296291314,0],[-47.5,-39.3301270189,0],[-48.5355339059,-38.5355339059,0],[-49.3301270189,-37.5,0],[-49.8296291314,-36.2940952255,0],[-50,-35,0],[-50,35,0],[-49.8296291314,36.2940952255,0],[-49.3301270189,37.5,0],[-48.5355339059,38.5355339059,0],[-47.5,39.3301270189,0],[-46.2940952255,39.8296291314,0],[-45,40,0],[45,40,0],[46.2940952255,39.8296291314,0],[47.5,39.3301270189,0],[48.5355339059,38.5355339059,0],[49.3301270189,37.5,0],[49.8296291314,36.2940952255,0],[50,35,0],[30,-20,50],[-10,-20,50],[-10,30,50],[30,30,50]],[[16,17,30],[31,29,30],[15,16,30],[14,15,30],[29,14,30],[19,20,30],[20,31,30],[18,19,30],[17,18,30],[12,13,29],[31,28,29],[11,12,29],[10,11,29],[9,10,29],[8,9,29],[7,8,29],[28,7,29],[13,14,29],[6,7,28],[5,6,28],[4,5,28],[3,4,28],[2,3,28],[1,2,28],[31,0,28],[0,1,28],[0,31,27],[31,26,27],[26,0,27],[31,25,26],[25,0,26],[31,24,25],[24,0,25],[31,23,24],[23,0,24],[31,22,23],[22,0,23],[31,21,22],[21,0,22],[31,20,21],[20,0,21],[19,0,20],[18,0,19],[17,0,18],[16,0,17],[14,16,15],[13,16,14],[12,16,13],[11,16,12],[10,16,11],[9,16,10],[8,16,9],[7,16,8],[6,16,7],[5,16,6],[4,16,5],[3,16,4],[2,16,3],[1,16,2],[16,1,0]]]); + assert_approx(prismoid([100,80],[40,50],h=50,shift=[10,5],rounding2=5), [[[50,-40,0],[-50,-40,0],[-50,40,0],[50,40,0],[30,-15,50],[29.8296291314,-16.2940952255,50],[29.3301270189,-17.5,50],[28.5355339059,-18.5355339059,50],[27.5,-19.3301270189,50],[26.2940952255,-19.8296291314,50],[25,-20,50],[-5,-20,50],[-6.29409522551,-19.8296291314,50],[-7.5,-19.3301270189,50],[-8.53553390593,-18.5355339059,50],[-9.33012701892,-17.5,50],[-9.82962913145,-16.2940952255,50],[-10,-15,50],[-10,25,50],[-9.82962913145,26.2940952255,50],[-9.33012701892,27.5,50],[-8.53553390593,28.5355339059,50],[-7.5,29.3301270189,50],[-6.29409522551,29.8296291314,50],[-5,30,50],[25,30,50],[26.2940952255,29.8296291314,50],[27.5,29.3301270189,50],[28.5355339059,28.5355339059,50],[29.3301270189,27.5,50],[29.8296291314,26.2940952255,50],[30,25,50]],[[3,4,31],[4,30,31],[30,3,31],[4,29,30],[29,3,30],[28,3,29],[4,28,29],[3,28,27],[28,26,27],[26,3,27],[28,25,26],[25,3,26],[28,24,25],[24,3,25],[2,3,24],[23,2,24],[28,23,24],[22,2,23],[28,22,23],[28,21,22],[21,2,22],[28,20,21],[20,2,21],[28,19,20],[19,2,20],[28,18,19],[18,2,19],[28,17,18],[17,2,18],[1,2,17],[16,1,17],[28,16,17],[15,1,16],[28,15,16],[14,1,15],[28,14,15],[13,1,14],[28,13,14],[12,1,13],[28,12,13],[11,1,12],[28,11,12],[10,1,11],[28,10,11],[0,1,10],[9,0,10],[28,9,10],[8,0,9],[28,8,9],[28,7,8],[7,0,8],[28,6,7],[6,0,7],[28,5,6],[5,0,6],[28,4,5],[4,0,5],[3,0,4],[2,0,3],[2,1,0]]]); + assert_approx(prismoid([100,80],[40,50],h=50,shift=[10,5],rounding1=5,rounding2=10), [[[50,-35,0],[49.8296291314,-36.2940952255,0],[49.3301270189,-37.5,0],[48.5355339059,-38.5355339059,0],[47.5,-39.3301270189,0],[46.2940952255,-39.8296291314,0],[45,-40,0],[-45,-40,0],[-46.2940952255,-39.8296291314,0],[-47.5,-39.3301270189,0],[-48.5355339059,-38.5355339059,0],[-49.3301270189,-37.5,0],[-49.8296291314,-36.2940952255,0],[-50,-35,0],[-50,35,0],[-49.8296291314,36.2940952255,0],[-49.3301270189,37.5,0],[-48.5355339059,38.5355339059,0],[-47.5,39.3301270189,0],[-46.2940952255,39.8296291314,0],[-45,40,0],[45,40,0],[46.2940952255,39.8296291314,0],[47.5,39.3301270189,0],[48.5355339059,38.5355339059,0],[49.3301270189,37.5,0],[49.8296291314,36.2940952255,0],[50,35,0],[30,-10,50],[29.6592582629,-12.588190451,50],[28.6602540378,-15,50],[27.0710678119,-17.0710678119,50],[25,-18.6602540378,50],[22.588190451,-19.6592582629,50],[20,-20,50],[0,-20,50],[-2.58819045103,-19.6592582629,50],[-5,-18.6602540378,50],[-7.07106781187,-17.0710678119,50],[-8.66025403784,-15,50],[-9.65925826289,-12.588190451,50],[-10,-10,50],[-10,20,50],[-9.65925826289,22.588190451,50],[-8.66025403784,25,50],[-7.07106781187,27.0710678119,50],[-5,28.6602540378,50],[-2.58819045103,29.6592582629,50],[0,30,50],[20,30,50],[22.588190451,29.6592582629,50],[25,28.6602540378,50],[27.0710678119,27.0710678119,50],[28.6602540378,25,50],[29.6592582629,22.588190451,50],[30,20,50]],[[27,28,55],[28,54,55],[54,27,55],[28,53,54],[26,27,54],[53,26,54],[28,52,53],[25,26,53],[52,25,53],[28,51,52],[51,24,52],[24,25,52],[22,51,50],[51,49,50],[49,22,50],[51,48,49],[48,21,49],[21,22,49],[20,21,48],[47,20,48],[51,47,48],[19,20,47],[46,19,47],[51,46,47],[18,19,46],[45,18,46],[51,45,46],[17,18,45],[44,17,45],[51,44,45],[16,17,44],[43,16,44],[51,43,44],[15,16,43],[42,15,43],[51,42,43],[14,15,42],[41,14,42],[51,41,42],[13,14,41],[40,13,41],[51,40,41],[12,13,40],[39,12,40],[51,39,40],[11,12,39],[38,11,39],[51,38,39],[10,11,38],[37,10,38],[51,37,38],[9,10,37],[36,9,37],[51,36,37],[8,9,36],[35,8,36],[51,35,36],[7,8,35],[34,7,35],[51,34,35],[6,7,34],[33,6,34],[51,33,34],[51,32,33],[5,6,33],[32,5,33],[51,31,32],[4,5,32],[31,4,32],[51,30,31],[3,4,31],[30,3,31],[51,29,30],[2,3,30],[29,2,30],[51,28,29],[1,2,29],[28,1,29],[27,0,28],[0,1,28],[26,0,27],[25,0,26],[24,0,25],[51,23,24],[23,0,24],[51,22,23],[22,0,23],[21,0,22],[20,0,21],[19,0,20],[18,0,19],[17,0,18],[16,0,17],[14,16,15],[13,16,14],[12,16,13],[11,16,12],[10,16,11],[9,16,10],[8,16,9],[7,16,8],[6,16,7],[5,16,6],[4,16,5],[3,16,4],[2,16,3],[1,16,2],[16,1,0]]]); + assert_approx(prismoid([100,80],[40,50],h=50,shift=[10,5],rounding1=5,chamfer2=10), [[[50,-35,0],[49.8296291314,-36.2940952255,0],[49.3301270189,-37.5,0],[48.5355339059,-38.5355339059,0],[47.5,-39.3301270189,0],[46.2940952255,-39.8296291314,0],[45,-40,0],[-45,-40,0],[-46.2940952255,-39.8296291314,0],[-47.5,-39.3301270189,0],[-48.5355339059,-38.5355339059,0],[-49.3301270189,-37.5,0],[-49.8296291314,-36.2940952255,0],[-50,-35,0],[-50,35,0],[-49.8296291314,36.2940952255,0],[-49.3301270189,37.5,0],[-48.5355339059,38.5355339059,0],[-47.5,39.3301270189,0],[-46.2940952255,39.8296291314,0],[-45,40,0],[45,40,0],[46.2940952255,39.8296291314,0],[47.5,39.3301270189,0],[48.5355339059,38.5355339059,0],[49.3301270189,37.5,0],[49.8296291314,36.2940952255,0],[50,35,0],[30,-10,50],[20,-20,50],[0,-20,50],[-10,-10,50],[-10,20,50],[0,30,50],[20,30,50],[30,20,50]],[[24,25,35],[34,24,35],[25,26,35],[28,34,35],[26,27,35],[27,28,35],[22,23,34],[21,22,34],[33,21,34],[28,33,34],[23,24,34],[18,19,33],[17,18,33],[32,17,33],[19,20,33],[28,32,33],[20,21,33],[16,17,32],[15,16,32],[14,15,32],[31,14,32],[28,31,32],[13,14,31],[12,13,31],[11,12,31],[10,11,31],[30,10,31],[28,30,31],[28,29,30],[9,10,30],[8,9,30],[7,8,30],[29,7,30],[6,7,29],[5,6,29],[4,5,29],[28,3,29],[3,4,29],[0,28,27],[26,0,27],[25,0,26],[24,0,25],[23,0,24],[22,0,23],[21,0,22],[20,0,21],[19,0,20],[18,0,19],[17,0,18],[16,0,17],[14,16,15],[13,16,14],[12,16,13],[11,16,12],[10,16,11],[9,16,10],[7,9,8],[6,9,7],[5,9,6],[4,9,5],[3,9,4],[28,2,3],[2,9,3],[28,1,2],[1,9,2],[28,0,1],[0,9,1],[0,16,9]]]); + assert_approx(prismoid([100,80],[40,50],h=50,shift=[10,5],chamfer=[0,5,10,15]), [[[50,-25,0],[35,-40,0],[-40,-40,0],[-50,-30,0],[-50,35,0],[-45,40,0],[50,40,0],[30,-5,50],[15,-20,50],[0,-20,50],[-10,-10,50],[-10,25,50],[-5,30,50],[30,30,50]],[[5,13,12],[13,11,12],[11,5,12],[13,10,11],[10,4,11],[4,5,11],[13,9,10],[3,4,10],[9,3,10],[2,3,9],[8,2,9],[13,8,9],[13,7,8],[1,2,8],[7,1,8],[13,0,7],[0,1,7],[0,13,6],[13,5,6],[5,0,6],[4,0,5],[2,4,3],[1,4,2],[4,1,0]]]); + assert_approx(prismoid([100,80],[40,50],h=50,shift=[10,5],chamfer1=[15,10,5,0], rounding2=[0,5,10,15]), [[[50,-40,0],[-45,-40,0],[-50,-35,0],[-50,30,0],[-40,40,0],[35,40,0],[50,25,0],[30,-5,50],[29.4888873943,-8.88228567654,50],[27.9903810568,-12.5,50],[25.6066017178,-15.6066017178,50],[22.5,-17.9903810568,50],[18.8822856765,-19.4888873943,50],[15,-20,50],[0,-20,50],[-2.58819045103,-19.6592582629,50],[-5,-18.6602540378,50],[-7.07106781187,-17.0710678119,50],[-8.66025403784,-15,50],[-9.65925826289,-12.588190451,50],[-10,-10,50],[-10,25,50],[-9.82962913145,26.2940952255,50],[-9.33012701892,27.5,50],[-8.53553390593,28.5355339059,50],[-7.5,29.3301270189,50],[-6.29409522551,29.8296291314,50],[-5,30,50],[30,30,50]],[[4,28,27],[28,26,27],[26,4,27],[28,25,26],[25,4,26],[28,24,25],[24,4,25],[3,4,24],[23,3,24],[28,23,24],[22,3,23],[28,22,23],[21,3,22],[28,21,22],[28,20,21],[20,3,21],[28,19,20],[2,3,20],[19,2,20],[28,18,19],[18,2,19],[28,17,18],[17,2,18],[1,2,17],[16,1,17],[28,16,17],[15,1,16],[28,15,16],[14,1,15],[28,14,15],[13,1,14],[28,13,14],[0,1,13],[12,0,13],[28,12,13],[11,0,12],[28,11,12],[10,0,11],[28,10,11],[9,0,10],[28,9,10],[8,0,9],[28,8,9],[28,7,8],[7,0,8],[28,0,7],[0,28,6],[28,5,6],[5,0,6],[28,4,5],[4,0,5],[3,0,4],[1,3,2],[3,1,0]]]); } test_prismoid(); From 3eb506e78ab6ed70b60340428658229c546af2ef Mon Sep 17 00:00:00 2001 From: Garth Minette Date: Sat, 12 Jun 2021 17:17:05 -0700 Subject: [PATCH 63/72] Rename quaternion functions to not have uppercase names. --- paths.scad | 14 +- quaternions.scad | 483 +++++++++++++++---------------- shapes2d.scad | 6 +- tests/test_quaternions.scad | 553 +++++++++++++++++------------------- 4 files changed, 519 insertions(+), 537 deletions(-) diff --git a/paths.scad b/paths.scad index 1dc1c2f..73ed488 100644 --- a/paths.scad +++ b/paths.scad @@ -1109,11 +1109,11 @@ module spiral_sweep(poly, h, r, twist=360, higbee, center, r1, r2, d, d1, d2, hi // path = [ [0, 0, 0], [33, 33, 33], [66, 33, 40], [100, 0, 0], [150,0,0] ]; // path_extrude(path) circle(r=10, $fn=6); module path_extrude(path, convexity=10, clipsize=100) { - function polyquats(path, q=Q_Ident(), v=[0,0,1], i=0) = let( + function polyquats(path, q=q_ident(), v=[0,0,1], i=0) = let( v2 = path[i+1] - path[i], ang = vector_angle(v,v2), axis = ang>0.001? unit(cross(v,v2)) : [0,0,1], - newq = Q_Mul(Quat(axis, ang), q), + newq = q_mul(quat(axis, ang), q), dist = norm(v2) ) i < (len(path)-2)? concat([[dist, newq, ang]], polyquats(path, newq, v2, i+1)) : @@ -1129,7 +1129,7 @@ module path_extrude(path, convexity=10, clipsize=100) { q = pquats[i][1]; difference() { translate(pt1) { - Qrot(q) { + q_rot(q) { down(clipsize/2/2) { if ((dist+clipsize/2) > 0) { linear_extrude(height=dist+clipsize/2, convexity=convexity) { @@ -1140,12 +1140,12 @@ module path_extrude(path, convexity=10, clipsize=100) { } } translate(pt1) { - hq = (i > 0)? Q_Slerp(q, pquats[i-1][1], 0.5) : q; - Qrot(hq) down(clipsize/2+epsilon) cube(clipsize, center=true); + hq = (i > 0)? q_slerp(q, pquats[i-1][1], 0.5) : q; + q_rot(hq) down(clipsize/2+epsilon) cube(clipsize, center=true); } translate(pt2) { - hq = (i < ptcount-2)? Q_Slerp(q, pquats[i+1][1], 0.5) : q; - Qrot(hq) up(clipsize/2+epsilon) cube(clipsize, center=true); + hq = (i < ptcount-2)? q_slerp(q, pquats[i+1][1], 0.5) : q; + q_rot(hq) up(clipsize/2+epsilon) cube(clipsize, center=true); } } } diff --git a/quaternions.scad b/quaternions.scad index 1dc7192..7598655 100644 --- a/quaternions.scad +++ b/quaternions.scad @@ -16,190 +16,190 @@ // Internal -function _Quat(a,s,w) = [a[0]*s, a[1]*s, a[2]*s, w]; +function _quat(a,s,w) = [a[0]*s, a[1]*s, a[2]*s, w]; -function _Qvec(q) = [q.x,q.y,q.z]; +function _qvec(q) = [q.x,q.y,q.z]; -function _Qreal(q) = q[3]; +function _qreal(q) = q[3]; -function _Qset(v,r) = concat( v, r ); +function _qset(v,r) = concat( v, r ); // normalizes without checking -function _Qnorm(q) = q/norm(q); +function _qnorm(q) = q/norm(q); -// Function: Q_is_quat() +// Function: is_quaternion() // Usage: -// if(Q_is_quat(q)) a=0; +// if(is_quaternion(q)) a=0; // Description: Return true if q is a valid non-zero quaternion. // Arguments: // q = object to check. -function Q_is_quat(q) = is_vector(q,4) && ! approx(norm(q),0) ; +function is_quaternion(q) = is_vector(q,4) && ! approx(norm(q),0) ; -// Function: Quat() +// Function: quat() // Usage: -// Quat(ax, ang); +// quat(ax, ang); // Description: Create a normalized Quaternion from axis and angle of rotation. // Arguments: // ax = Vector of axis of rotation. // ang = Number of degrees to rotate around the axis counter-clockwise, when facing the origin. -function Quat(ax=[0,0,1], ang=0) = +function quat(ax=[0,0,1], ang=0) = assert( is_vector(ax,3) && is_finite(ang), "Invalid input") let( n = norm(ax) ) approx(n,0) - ? _Quat([0,0,0], sin(ang/2), cos(ang/2)) - : _Quat(ax/n, sin(ang/2), cos(ang/2)); + ? _quat([0,0,0], sin(ang/2), cos(ang/2)) + : _quat(ax/n, sin(ang/2), cos(ang/2)); -// Function: QuatX() +// Function: quat_x() // Usage: -// QuatX(a); +// quat_x(a); // Description: Create a normalized Quaternion for rotating around the X axis [1,0,0]. // Arguments: // a = Number of degrees to rotate around the axis counter-clockwise, when facing the origin. -function QuatX(a=0) = +function quat_x(a=0) = assert( is_finite(a), "Invalid angle" ) - Quat([1,0,0],a); + quat([1,0,0],a); -// Function: QuatY() +// Function: quat_y() // Usage: -// QuatY(a); +// quat_y(a); // Description: Create a normalized Quaternion for rotating around the Y axis [0,1,0]. // Arguments: // a = Number of degrees to rotate around the axis counter-clockwise, when facing the origin. -function QuatY(a=0) = +function quat_y(a=0) = assert( is_finite(a), "Invalid angle" ) - Quat([0,1,0],a); + quat([0,1,0],a); -// Function: QuatZ() +// Function: quat_z() // Usage: -// QuatZ(a); +// quat_z(a); // Description: Create a normalized Quaternion for rotating around the Z axis [0,0,1]. // Arguments: // a = Number of degrees to rotate around the axis counter-clockwise, when facing the origin. -function QuatZ(a=0) = +function quat_z(a=0) = assert( is_finite(a), "Invalid angle" ) - Quat([0,0,1],a); + quat([0,0,1],a); -// Function: QuatXYZ() +// Function: quat_xyz() // Usage: -// QuatXYZ([X,Y,Z]) +// quat_xyz([X,Y,Z]) // Description: // Creates a normalized quaternion from standard [X,Y,Z] rotation angles in degrees. // Arguments: // a = The triplet of rotation angles, [X,Y,Z] -function QuatXYZ(a=[0,0,0]) = +function quat_xyz(a=[0,0,0]) = assert( is_vector(a,3), "Invalid angles") let( - qx = QuatX(a[0]), - qy = QuatY(a[1]), - qz = QuatZ(a[2]) + qx = quat_x(a[0]), + qy = quat_y(a[1]), + qz = quat_z(a[2]) ) - Q_Mul(qz, Q_Mul(qy, qx)); + q_mul(qz, q_mul(qy, qx)); -// Function: Q_From_to() +// Function: q_from_to() // Usage: -// q = Q_From_to(v1, v2); +// q = q_from_to(v1, v2); // Description: // Returns the normalized quaternion that rotates the non zero 3D vector v1 // to the non zero 3D vector v2. -function Q_From_to(v1, v2) = +function q_from_to(v1, v2) = assert( is_vector(v1,3) && is_vector(v2,3) && ! approx(norm(v1),0) && ! approx(norm(v2),0) , "Invalid vector(s)") let( ax = cross(v1,v2), n = norm(ax) ) approx(n, 0) - ? v1*v2>0 ? Q_Ident() : Quat([ v1.y, -v1.x, 0], 180) - : Quat(ax, atan2( n , v1*v2 )); + ? v1*v2>0 ? q_ident() : quat([ v1.y, -v1.x, 0], 180) + : quat(ax, atan2( n , v1*v2 )); -// Function: Q_Ident() +// Function: q_ident() // Description: Returns the "Identity" zero-rotation Quaternion. -function Q_Ident() = [0, 0, 0, 1]; +function q_ident() = [0, 0, 0, 1]; -// Function: Q_Add_S() +// Function: q_add_s() // Usage: -// Q_Add_S(q, s) +// q_add_s(q, s) // Description: // Adds a scalar value `s` to the W part of a quaternion `q`. // The returned quaternion is usually not normalized. -function Q_Add_S(q, s) = +function q_add_s(q, s) = assert( is_finite(s), "Invalid scalar" ) q+[0,0,0,s]; -// Function: Q_Sub_S() +// Function: q_sub_s() // Usage: -// Q_Sub_S(q, s) +// q_sub_s(q, s) // Description: // Subtracts a scalar value `s` from the W part of a quaternion `q`. // The returned quaternion is usually not normalized. -function Q_Sub_S(q, s) = +function q_sub_s(q, s) = assert( is_finite(s), "Invalid scalar" ) q-[0,0,0,s]; -// Function: Q_Mul_S() +// Function: q_mul_s() // Usage: -// Q_Mul_S(q, s) +// q_mul_s(q, s) // Description: // Multiplies each part of a quaternion `q` by a scalar value `s`. // The returned quaternion is usually not normalized. -function Q_Mul_S(q, s) = +function q_mul_s(q, s) = assert( is_finite(s), "Invalid scalar" ) q*s; -// Function: Q_Div_S() +// Function: q_div_s() // Usage: -// Q_Div_S(q, s) +// q_div_s(q, s) // Description: // Divides each part of a quaternion `q` by a scalar value `s`. // The returned quaternion is usually not normalized. -function Q_Div_S(q, s) = +function q_div_s(q, s) = assert( is_finite(s) && ! approx(s,0) , "Invalid scalar" ) q/s; -// Function: Q_Add() +// Function: q_add() // Usage: -// Q_Add(a, b) +// q_add(a, b) // Description: // Adds each part of two quaternions together. // The returned quaternion is usually not normalized. -function Q_Add(a, b) = - assert( Q_is_quat(a) && Q_is_quat(a), "Invalid quaternion(s)") +function q_add(a, b) = + assert( is_quaternion(a) && is_quaternion(a), "Invalid quaternion(s)") assert( ! approx(norm(a+b),0), "Quaternions cannot be opposed" ) a+b; -// Function: Q_Sub() +// Function: q_sub() // Usage: -// Q_Sub(a, b) +// q_sub(a, b) // Description: // Subtracts each part of quaternion `b` from quaternion `a`. // The returned quaternion is usually not normalized. -function Q_Sub(a, b) = - assert( Q_is_quat(a) && Q_is_quat(a), "Invalid quaternion(s)") +function q_sub(a, b) = + assert( is_quaternion(a) && is_quaternion(a), "Invalid quaternion(s)") assert( ! approx(a,b), "Quaternions cannot be equal" ) a-b; -// Function: Q_Mul() +// Function: q_mul() // Usage: -// Q_Mul(a, b) +// q_mul(a, b) // Description: // Multiplies quaternion `a` by quaternion `b`. // The returned quaternion is normalized if both `a` and `b` are normalized -function Q_Mul(a, b) = - assert( Q_is_quat(a) && Q_is_quat(b), "Invalid quaternion(s)") +function q_mul(a, b) = + assert( is_quaternion(a) && is_quaternion(b), "Invalid quaternion(s)") [ a[3]*b.x + a.x*b[3] + a.y*b.z - a.z*b.y, a[3]*b.y - a.x*b.z + a.y*b[3] + a.z*b.x, @@ -208,94 +208,94 @@ function Q_Mul(a, b) = ]; -// Function: Q_Cumulative() +// Function: q_cumulative() // Usage: -// Q_Cumulative(v); +// q_cumulative(v); // Description: // Given a list of Quaternions, cumulatively multiplies them, returning a list // of each cumulative Quaternion product. It starts with the first quaternion // given in the list, and applies successive quaternion rotations in list order. // The quaternion in the returned list are normalized if each quaternion in v // is normalized. -function Q_Cumulative(v, _i=0, _acc=[]) = +function q_cumulative(v, _i=0, _acc=[]) = _i==len(v) ? _acc : - Q_Cumulative( + q_cumulative( v, _i+1, concat( _acc, - [_i==0 ? v[_i] : Q_Mul(v[_i], last(_acc))] + [_i==0 ? v[_i] : q_mul(v[_i], last(_acc))] ) ); -// Function: Q_Dot() +// Function: q_dot() // Usage: -// Q_Dot(a, b) +// q_dot(a, b) // Description: Calculates the dot product between quaternions `a` and `b`. -function Q_Dot(a, b) = - assert( Q_is_quat(a) && Q_is_quat(b), "Invalid quaternion(s)" ) +function q_dot(a, b) = + assert( is_quaternion(a) && is_quaternion(b), "Invalid quaternion(s)" ) a*b; -// Function: Q_Neg() +// Function: q_neg() // Usage: -// Q_Neg(q) +// q_neg(q) // Description: Returns the negative of quaternion `q`. -function Q_Neg(q) = - assert( Q_is_quat(q), "Invalid quaternion" ) +function q_neg(q) = + assert( is_quaternion(q), "Invalid quaternion" ) -q; -// Function: Q_Conj() +// Function: q_conj() // Usage: -// Q_Conj(q) +// q_conj(q) // Description: Returns the conjugate of quaternion `q`. -function Q_Conj(q) = - assert( Q_is_quat(q), "Invalid quaternion" ) +function q_conj(q) = + assert( is_quaternion(q), "Invalid quaternion" ) [-q.x, -q.y, -q.z, q[3]]; -// Function: Q_Inverse() +// Function: q_inverse() // Usage: -// qc = Q_Inverse(q) +// qc = q_inverse(q) // Description: Returns the multiplication inverse of quaternion `q` that is normalized only if `q` is normalized. -function Q_Inverse(q) = - assert( Q_is_quat(q), "Invalid quaternion" ) - let(q = _Qnorm(q) ) +function q_inverse(q) = + assert( is_quaternion(q), "Invalid quaternion" ) + let(q = _qnorm(q) ) [-q.x, -q.y, -q.z, q[3]]; -// Function: Q_Norm() +// Function: q_norm() // Usage: -// Q_Norm(q) +// q_norm(q) // Description: // Returns the `norm()` "length" of quaternion `q`. // Normalized quaternions have unitary norm. -function Q_Norm(q) = - assert( Q_is_quat(q), "Invalid quaternion" ) +function q_norm(q) = + assert( is_quaternion(q), "Invalid quaternion" ) norm(q); -// Function: Q_Normalize() +// Function: q_normalize() // Usage: -// Q_Normalize(q) +// q_normalize(q) // Description: Normalizes quaternion `q`, so that norm([W,X,Y,Z]) == 1. -function Q_Normalize(q) = - assert( Q_is_quat(q) , "Invalid quaternion" ) +function q_normalize(q) = + assert( is_quaternion(q) , "Invalid quaternion" ) q/norm(q); -// Function: Q_Dist() +// Function: q_dist() // Usage: -// Q_Dist(q1, q2) +// q_dist(q1, q2) // Description: Returns the "distance" between two quaternions. -function Q_Dist(q1, q2) = - assert( Q_is_quat(q1) && Q_is_quat(q2), "Invalid quaternion(s)" ) +function q_dist(q1, q2) = + assert( is_quaternion(q1) && is_quaternion(q2), "Invalid quaternion(s)" ) norm(q2-q1); -// Function: Q_Slerp() +// Function: q_slerp() // Usage: -// Q_Slerp(q1, q2, u); +// q_slerp(q1, q2, u); // Description: // Returns a quaternion that is a spherical interpolation between two quaternions. // Arguments: @@ -303,45 +303,45 @@ function Q_Dist(q1, q2) = // q2 = The second quaternion. (u=1) // u = The proportional value, from 0 to 1, of what part of the interpolation to return. // Example(3D): Giving `u` as a Scalar -// a = QuatY(-135); -// b = QuatXYZ([0,-30,30]); +// a = quat_y(-135); +// b = quat_xyz([0,-30,30]); // for (u=[0:0.1:1]) -// Qrot(Q_Slerp(a, b, u)) +// q_rot(q_slerp(a, b, u)) // right(80) cube([10,10,1]); // #sphere(r=80); // Example(3D): Giving `u` as a Range -// a = QuatZ(-135); -// b = QuatXYZ([90,0,-45]); -// for (q = Q_Slerp(a, b, [0:0.1:1])) -// Qrot(q) right(80) cube([10,10,1]); +// a = quat_z(-135); +// b = quat_xyz([90,0,-45]); +// for (q = q_slerp(a, b, [0:0.1:1])) +// q_rot(q) right(80) cube([10,10,1]); // #sphere(r=80); -function Q_Slerp(q1, q2, u, _dot) = +function q_slerp(q1, q2, u, _dot) = is_undef(_dot) ? assert(is_finite(u) || is_range(u) || is_vector(u), "Invalid interpolation coefficient(s)") - assert(Q_is_quat(q1) && Q_is_quat(q2), "Invalid quaternion(s)" ) + assert(is_quaternion(q1) && is_quaternion(q2), "Invalid quaternion(s)" ) let( _dot = q1*q2, q1 = q1/norm(q1), q2 = _dot<0 ? -q2/norm(q2) : q2/norm(q2), dot = abs(_dot) ) - ! is_finite(u) ? [for (uu=u) Q_Slerp(q1, q2, uu, dot)] : - Q_Slerp(q1, q2, u, dot) + ! is_finite(u) ? [for (uu=u) q_slerp(q1, q2, uu, dot)] : + q_slerp(q1, q2, u, dot) : _dot>0.9995 - ? _Qnorm(q1 + u*(q2-q1)) + ? _qnorm(q1 + u*(q2-q1)) : let( theta = u*acos(_dot), - q3 = _Qnorm(q2 - _dot*q1) + q3 = _qnorm(q2 - _dot*q1) ) - _Qnorm(q1*cos(theta) + q3*sin(theta)); + _qnorm(q1*cos(theta) + q3*sin(theta)); -// Function: Q_Matrix3() +// Function: q_matrix3() // Usage: -// Q_Matrix3(q); +// q_matrix3(q); // Description: // Returns the 3x3 rotation matrix for the given normalized quaternion q. -function Q_Matrix3(q) = - let( q = Q_Normalize(q) ) +function q_matrix3(q) = + let( q = q_normalize(q) ) [ [1-2*q[1]*q[1]-2*q[2]*q[2], 2*q[0]*q[1]-2*q[2]*q[3], 2*q[0]*q[2]+2*q[1]*q[3]], [ 2*q[0]*q[1]+2*q[2]*q[3], 1-2*q[0]*q[0]-2*q[2]*q[2], 2*q[1]*q[2]-2*q[0]*q[3]], @@ -349,13 +349,13 @@ function Q_Matrix3(q) = ]; -// Function: Q_Matrix4() +// Function: q_matrix4() // Usage: -// Q_Matrix4(q); +// q_matrix4(q); // Description: // Returns the 4x4 rotation matrix for the given normalized quaternion q. -function Q_Matrix4(q) = - let( q = Q_Normalize(q) ) +function q_matrix4(q) = + let( q = q_normalize(q) ) [ [1-2*q[1]*q[1]-2*q[2]*q[2], 2*q[0]*q[1]-2*q[2]*q[3], 2*q[0]*q[2]+2*q[1]*q[3], 0], [ 2*q[0]*q[1]+2*q[2]*q[3], 1-2*q[0]*q[0]-2*q[2]*q[2], 2*q[1]*q[2]-2*q[0]*q[3], 0], @@ -364,115 +364,115 @@ function Q_Matrix4(q) = ]; -// Function: Q_Axis() +// Function: q_axis() // Usage: -// Q_Axis(q) +// q_axis(q) // Description: // Returns the axis of rotation of a normalized quaternion `q`. // The input doesn't need to be normalized. -function Q_Axis(q) = - assert( Q_is_quat(q) , "Invalid quaternion" ) - let( d = norm(_Qvec(q)) ) - approx(d,0)? [0,0,1] : _Qvec(q)/d; +function q_axis(q) = + assert( is_quaternion(q) , "Invalid quaternion" ) + let( d = norm(_qvec(q)) ) + approx(d,0)? [0,0,1] : _qvec(q)/d; -// Function: Q_Angle() +// Function: q_angle() // Usage: -// a = Q_Angle(q) -// a12 = Q_Angle(q1,q2); +// a = q_angle(q) +// a12 = q_angle(q1,q2); // Description: // If only q1 is given, returns the angle of rotation (in degrees) of that quaternion. // If both q1 and q2 are given, returns the angle (in degrees) between them. // The input quaternions don't need to be normalized. -function Q_Angle(q1,q2) = - assert(Q_is_quat(q1) && (is_undef(q2) || Q_is_quat(q2)), "Invalid quaternion(s)" ) - let( n1 = is_undef(q2)? norm(_Qvec(q1)): norm(q1) ) +function q_angle(q1,q2) = + assert(is_quaternion(q1) && (is_undef(q2) || is_quaternion(q2)), "Invalid quaternion(s)" ) + let( n1 = is_undef(q2)? norm(_qvec(q1)): norm(q1) ) is_undef(q2) - ? 2 * atan2(n1,_Qreal(q1)) + ? 2 * atan2(n1,_qreal(q1)) : let( q1 = q1/norm(q1), q2 = q2/norm(q2) ) 4 * atan2(norm(q1 - q2), norm(q1 + q2)); -// Function&Module: Qrot() +// Function&Module: q_rot() // Usage: As Module -// Qrot(q) ... +// q_rot(q) ... // Usage: As Function -// pts = Qrot(q,p); +// pts = q_rot(q,p); // Description: // When called as a module, rotates all children by the rotation stored in quaternion `q`. // When called as a function with a `p` argument, rotates the point or list of points in `p` by the rotation stored in quaternion `q`. // When called as a function without a `p` argument, returns the affine3d rotation matrix for the rotation stored in quaternion `q`. // Example(FlatSpin,VPD=225,VPT=[71,-26,16]): // module shape() translate([80,0,0]) cube([10,10,1]); -// q = QuatXYZ([90,-15,-45]); -// Qrot(q) shape(); +// q = quat_xyz([90,-15,-45]); +// q_rot(q) shape(); // #shape(); // Example(NORENDER): -// q = QuatXYZ([45,35,10]); -// mat4x4 = Qrot(q); +// q = quat_xyz([45,35,10]); +// mat4x4 = q_rot(q); // Example(NORENDER): -// q = QuatXYZ([45,35,10]); -// pt = Qrot(q, p=[4,5,6]); +// q = quat_xyz([45,35,10]); +// pt = q_rot(q, p=[4,5,6]); // Example(NORENDER): -// q = QuatXYZ([45,35,10]); -// pts = Qrot(q, p=[[2,3,4], [4,5,6], [9,2,3]]); -module Qrot(q) { - multmatrix(Q_Matrix4(q)) { +// q = quat_xyz([45,35,10]); +// pts = q_rot(q, p=[[2,3,4], [4,5,6], [9,2,3]]); +module q_rot(q) { + multmatrix(q_matrix4(q)) { children(); } } -function Qrot(q,p) = - is_undef(p)? Q_Matrix4(q) : - is_vector(p)? Qrot(q,[p])[0] : - apply(Q_Matrix4(q), p); +function q_rot(q,p) = + is_undef(p)? q_matrix4(q) : + is_vector(p)? q_rot(q,[p])[0] : + apply(q_matrix4(q), p); -// Module: Qrot_copies() +// Module: q_rot_copies() // Usage: -// Qrot_copies(quats) ... +// q_rot_copies(quats) ... // Description: // For each quaternion given in the list `quats`, rotates to that orientation and creates a copy -// of all children. This is equivalent to `for (q=quats) Qrot(q) ...`. +// of all children. This is equivalent to `for (q=quats) q_rot(q) ...`. // Arguments: // quats = A list containing all quaternions to rotate to and create copies of all children for. // Example: -// a = QuatZ(-135); -// b = QuatXYZ([0,-30,30]); -// Qrot_copies(Q_Slerp(a, b, [0:0.1:1])) +// a = quat_z(-135); +// b = quat_xyz([0,-30,30]); +// q_rot_copies(q_slerp(a, b, [0:0.1:1])) // right(80) cube([10,10,1]); // #sphere(r=80); -module Qrot_copies(quats) for (q=quats) Qrot(q) children(); +module q_rot_copies(quats) for (q=quats) q_rot(q) children(); -// Function: Q_Rotation() +// Function: q_rotation() // Usage: -// Q_Rotation(R) +// q_rotation(R) // Description: // Returns a normalized quaternion corresponding to the rotation matrix R. // R may be a 3x3 rotation matrix or a homogeneous 4x4 rotation matrix. // The last row and last column of R are ignored for 4x4 matrices. // It doesn't check whether R is in fact a rotation matrix. // If R is not a rotation, the returned quaternion is an unpredictable quaternion . -function Q_Rotation(R) = +function q_rotation(R) = assert( is_matrix(R,3,3) || is_matrix(R,4,4) , "Matrix is neither 3x3 nor 4x4") let( tr = R[0][0]+R[1][1]+R[2][2] ) // R trace tr>0 ? let( r = 1+tr ) - _Qnorm( _Qset([ R[1][2]-R[2][1], R[2][0]-R[0][2], R[0][1]-R[1][0] ], -r ) ) + _qnorm( _qset([ R[1][2]-R[2][1], R[2][0]-R[0][2], R[0][1]-R[1][0] ], -r ) ) : let( i = max_index([ R[0][0], R[1][1], R[2][2] ]), r = 1 + 2*R[i][i] -R[0][0] -R[1][1] -R[2][2] ) - i==0 ? _Qnorm( _Qset( [ 4*r, (R[1][0]+R[0][1]), (R[0][2]+R[2][0]) ], (R[2][1]-R[1][2])) ): - i==1 ? _Qnorm( _Qset( [ (R[1][0]+R[0][1]), 4*r, (R[2][1]+R[1][2]) ], (R[0][2]-R[2][0])) ): - _Qnorm( _Qset( [ (R[2][0]+R[0][2]), (R[1][2]+R[2][1]), 4*r ], (R[1][0]-R[0][1])) ) ; + i==0 ? _qnorm( _qset( [ 4*r, (R[1][0]+R[0][1]), (R[0][2]+R[2][0]) ], (R[2][1]-R[1][2])) ): + i==1 ? _qnorm( _qset( [ (R[1][0]+R[0][1]), 4*r, (R[2][1]+R[1][2]) ], (R[0][2]-R[2][0])) ): + _qnorm( _qset( [ (R[2][0]+R[0][2]), (R[1][2]+R[2][1]), 4*r ], (R[1][0]-R[0][1])) ) ; -// Function&Module: Q_Rotation_path() +// Function&Module: q_rotation_path() // Usage: As a function -// path = Q_Rotation_path(q1, n, q2); -// path = Q_Rotation_path(q1, n); +// path = q_rotation_path(q1, n, q2); +// path = q_rotation_path(q1, n); // Usage: As a module -// Q_Rotation_path(q1, n, q2) ... +// q_rotation_path(q1, n, q2) ... // Description: // If q2 is undef and it is called as a function, the path, with length n+1 (n>=1), will be the // cumulative multiplications of the matrix rotation of q1 by itself. @@ -488,50 +488,50 @@ function Q_Rotation(R) = // q2 = The quaternion of the last rotation. // n = An integer defining the path length ( path length = n+1). // Example(3D): as a function -// a = QuatY(-135); -// b = QuatXYZ([0,-30,30]); -// for (M=Q_Rotation_path(a, 10, b)) +// a = quat_y(-135); +// b = quat_xyz([0,-30,30]); +// for (M=q_rotation_path(a, 10, b)) // multmatrix(M) // right(80) cube([10,10,1]); // #sphere(r=80); // Example(3D): as a module -// a = QuatY(-135); -// b = QuatXYZ([0,-30,30]); -// Q_Rotation_path(a, 10, b) +// a = quat_y(-135); +// b = quat_xyz([0,-30,30]); +// q_rotation_path(a, 10, b) // right(80) cube([10,10,1]); // #sphere(r=80); // Example(3D): as a function -// a = QuatY(5); -// for (M=Q_Rotation_path(a, 10)) +// a = quat_y(5); +// for (M=q_rotation_path(a, 10)) // multmatrix(M) // right(80) cube([10,10,1]); // #sphere(r=80); // Example(3D): as a module -// a = QuatY(5); -// Q_Rotation_path(a, 10) +// a = quat_y(5); +// q_rotation_path(a, 10) // right(80) cube([10,10,1]); // #sphere(r=80); -function Q_Rotation_path(q1, n=1, q2) = - assert( Q_is_quat(q1) && (is_undef(q2) || Q_is_quat(q2) ), "Invalid quaternion(s)" ) +function q_rotation_path(q1, n=1, q2) = + assert( is_quaternion(q1) && (is_undef(q2) || is_quaternion(q2) ), "Invalid quaternion(s)" ) assert( is_finite(n) && n>=1 && n==floor(n), "Invalid integer" ) assert( is_undef(q2) || ! approx(norm(q1+q2),0), "Quaternions cannot be opposed" ) is_undef(q2) - ? [for( i=0, dR=Q_Matrix4(q1), R=dR; i<=n; i=i+1, R=dR*R ) R] - : let( q2 = Q_Normalize( q1*q2<0 ? -q2: q2 ), - dq = Q_pow( Q_Mul( q2, Q_Inverse(q1) ), 1/n ), - dR = Q_Matrix4(dq) ) - [for( i=0, R=Q_Matrix4(q1); i<=n; i=i+1, R=dR*R ) R]; + ? [for( i=0, dR=q_matrix4(q1), R=dR; i<=n; i=i+1, R=dR*R ) R] + : let( q2 = q_normalize( q1*q2<0 ? -q2: q2 ), + dq = q_pow( q_mul( q2, q_inverse(q1) ), 1/n ), + dR = q_matrix4(dq) ) + [for( i=0, R=q_matrix4(q1); i<=n; i=i+1, R=dR*R ) R]; -module Q_Rotation_path(q1, n=1, q2) { - for(Mi=Q_Rotation_path(q1, n, q2)) +module q_rotation_path(q1, n=1, q2) { + for(Mi=q_rotation_path(q1, n, q2)) multmatrix(Mi) children(); } -// Function: Q_Nlerp() +// Function: q_nlerp() // Usage: -// q = Q_Nlerp(q1, q2, u); +// q = q_nlerp(q1, q2, u); // Description: // Returns a quaternion that is a normalized linear interpolation between two quaternions // when u is a number. @@ -543,33 +543,33 @@ module Q_Rotation_path(q1, n=1, q2) { // q2 = The second quaternion. (u=1) // u = A value (or a list of values), between 0 and 1, of the proportion(s) of each quaternion in the interpolation. // Example(3D): Giving `u` as a Scalar -// a = QuatY(-135); -// b = QuatXYZ([0,-30,30]); +// a = quat_y(-135); +// b = quat_xyz([0,-30,30]); // for (u=[0:0.1:1]) -// Qrot(Q_Nlerp(a, b, u)) +// q_rot(q_nlerp(a, b, u)) // right(80) cube([10,10,1]); // #sphere(r=80); // Example(3D): Giving `u` as a Range -// a = QuatZ(-135); -// b = QuatXYZ([90,0,-45]); -// for (q = Q_Nlerp(a, b, [0:0.1:1])) -// Qrot(q) right(80) cube([10,10,1]); +// a = quat_z(-135); +// b = quat_xyz([90,0,-45]); +// for (q = q_nlerp(a, b, [0:0.1:1])) +// q_rot(q) right(80) cube([10,10,1]); // #sphere(r=80); -function Q_Nlerp(q1,q2,u) = +function q_nlerp(q1,q2,u) = assert(is_finite(u) || is_range(u) || is_vector(u) , "Invalid interpolation coefficient(s)" ) - assert(Q_is_quat(q1) && Q_is_quat(q2), "Invalid quaternion(s)" ) + assert(is_quaternion(q1) && is_quaternion(q2), "Invalid quaternion(s)" ) assert( ! approx(norm(q1+q2),0), "Quaternions cannot be opposed" ) - let( q1 = Q_Normalize(q1), - q2 = Q_Normalize(q2) ) + let( q1 = q_normalize(q1), + q2 = q_normalize(q2) ) is_num(u) - ? _Qnorm((1-u)*q1 + u*q2 ) - : [for (ui=u) _Qnorm((1-ui)*q1 + ui*q2 ) ]; + ? _qnorm((1-u)*q1 + u*q2 ) + : [for (ui=u) _qnorm((1-ui)*q1 + ui*q2 ) ]; -// Function: Q_Squad() +// Function: q_squad() // Usage: -// qn = Q_Squad(q1,q2,q3,q4,u); +// qn = q_squad(q1,q2,q3,q4,u); // Description: // Returns a quaternion that is a cubic spherical interpolation of the quaternions // q1 and q4 taking the other two quaternions, q2 and q3, as parameter of a cubic @@ -586,71 +586,72 @@ function Q_Nlerp(q1,q2,u) = // q4 = The end quaternion. (u=1) // u = A value (or a list of values), of the proportion(s) of each quaternion in the cubic interpolation. // Example(3D): Giving `u` as a Scalar -// a = QuatY(-135); -// b = QuatXYZ([-50,-50,120]); -// c = QuatXYZ([-50,-40,30]); -// d = QuatY(-45); +// a = quat_y(-135); +// b = quat_xyz([-50,-50,120]); +// c = quat_xyz([-50,-40,30]); +// d = quat_y(-45); // color("red"){ -// Qrot(b) right(80) cube([10,10,1]); -// Qrot(c) right(80) cube([10,10,1]); +// q_rot(b) right(80) cube([10,10,1]); +// q_rot(c) right(80) cube([10,10,1]); // } // for (u=[0:0.05:1]) -// Qrot(Q_Squad(a, b, c, d, u)) +// q_rot(q_squad(a, b, c, d, u)) // right(80) cube([10,10,1]); // #sphere(r=80); // Example(3D): Giving `u` as a Range -// a = QuatY(-135); -// b = QuatXYZ([-50,-50,120]); -// c = QuatXYZ([-50,-40,30]); -// d = QuatY(-45); -// for (q = Q_Squad(a, b, c, d, [0:0.05:1])) -// Qrot(q) right(80) cube([10,10,1]); +// a = quat_y(-135); +// b = quat_xyz([-50,-50,120]); +// c = quat_xyz([-50,-40,30]); +// d = quat_y(-45); +// for (q = q_squad(a, b, c, d, [0:0.05:1])) +// q_rot(q) right(80) cube([10,10,1]); // #sphere(r=80); -function Q_Squad(q1,q2,q3,q4,u) = +function q_squad(q1,q2,q3,q4,u) = assert(is_finite(u) || is_range(u) || is_vector(u) , "Invalid interpolation coefficient(s)" ) is_num(u) - ? Q_Slerp( Q_Slerp(q1,q4,u), Q_Slerp(q2,q3,u), 2*u*(1-u)) - : [for(ui=u) Q_Slerp( Q_Slerp(q1,q4,ui), Q_Slerp(q2,q3,ui), 2*ui*(1-ui) ) ]; + ? q_slerp( q_slerp(q1,q4,u), q_slerp(q2,q3,u), 2*u*(1-u)) + : [for(ui=u) q_slerp( q_slerp(q1,q4,ui), q_slerp(q2,q3,ui), 2*ui*(1-ui) ) ]; -// Function: Q_exp() +// Function: q_exp() // Usage: -// q2 = Q_exp(q); +// q2 = q_exp(q); // Description: // Returns the quaternion that is the exponential of the quaternion q in base e // The returned quaternion is usually not normalized. -function Q_exp(q) = +function q_exp(q) = assert( is_vector(q,4), "Input is not a valid quaternion") - let( nv = norm(_Qvec(q)) ) // q may be equal to zero here! - exp(_Qreal(q))*Quat(_Qvec(q),2*nv); + let( nv = norm(_qvec(q)) ) // q may be equal to zero here! + exp(_qreal(q))*quat(_qvec(q),2*nv); -// Function: Q_ln() +// Function: q_ln() // Usage: -// q2 = Q_ln(q); +// q2 = q_ln(q); // Description: // Returns the quaternion that is the natural logarithm of the quaternion q. // The returned quaternion is usually not normalized and may be zero. -function Q_ln(q) = - assert(Q_is_quat(q), "Input is not a valid quaternion") - let( nq = norm(q), - nv = norm(_Qvec(q)) ) - approx(nv,0) ? _Qset([0,0,0] , ln(nq) ) : - _Qset(_Qvec(q)*atan2(nv,_Qreal(q))/nv, ln(nq)); +function q_ln(q) = + assert(is_quaternion(q), "Input is not a valid quaternion") + let( + nq = norm(q), + nv = norm(_qvec(q)) + ) + approx(nv,0) ? _qset([0,0,0] , ln(nq) ) : + _qset(_qvec(q)*atan2(nv,_qreal(q))/nv, ln(nq)); -// Function: Q_pow() +// Function: q_pow() // Usage: -// q2 = Q_pow(q, r); +// q2 = q_pow(q, r); // Description: // Returns the quaternion that is the power of the quaternion q to the real exponent r. // The returned quaternion is normalized if `q` is normalized. -function Q_pow(q,r=1) = - assert( Q_is_quat(q) && is_finite(r), - "Invalid inputs") - let( theta = 2*atan2(norm(_Qvec(q)),_Qreal(q)) ) - Quat(_Qvec(q), r*theta); // Q_exp(r*Q_ln(q)); +function q_pow(q,r=1) = + assert( is_quaternion(q) && is_finite(r), "Invalid inputs") + let( theta = 2*atan2(norm(_qvec(q)),_qreal(q)) ) + quat(_qvec(q), r*theta); // q_exp(r*q_ln(q)); diff --git a/shapes2d.scad b/shapes2d.scad index f24fa0c..1fce85f 100644 --- a/shapes2d.scad +++ b/shapes2d.scad @@ -307,15 +307,15 @@ module stroke( multmatrix(mat) polygon(endcap_shape2); } } else { - quatsums = Q_Cumulative([ + quatsums = q_cumulative([ for (i = idx(path2,e=-2)) let( vec1 = i==0? UP : unit(path2[i]-path2[i-1], UP), vec2 = unit(path2[i+1]-path2[i], UP), axis = vector_axis(vec1,vec2), ang = vector_angle(vec1,vec2) - ) Quat(axis,ang) + ) quat(axis,ang) ]); - rotmats = [for (q=quatsums) Q_Matrix4(q)]; + rotmats = [for (q=quatsums) q_matrix4(q)]; sides = [ for (i = idx(path2,e=-2)) quantup(segs(max(widths[i],widths[i+1])/2),4) diff --git a/tests/test_quaternions.scad b/tests/test_quaternions.scad index b7c2f6f..fe95ebc 100644 --- a/tests/test_quaternions.scad +++ b/tests/test_quaternions.scad @@ -1,403 +1,384 @@ include <../std.scad> -include <../strings.scad> -function rec_cmp(a,b,eps=1e-9) = - typeof(a)!=typeof(b)? false : - is_num(a)? approx(a,b,eps=eps) : - is_list(a)? len(a)==len(b) && all([for (i=idx(a)) rec_cmp(a[i],b[i],eps=eps)]) : - a == b; -function Qstandard(q) = sign([for(qi=q) if( ! approx(qi,0)) qi,0 ][0])*q; +function _q_standard(q) = sign([for(qi=q) if( ! approx(qi,0)) qi,0 ][0])*q; -module verify_f(actual,expected) { - if (!rec_cmp(actual,expected)) { - echo(str("Expected: ",fmt_float(expected,10))); - echo(str(" : ",expected)); - echo(str("Actual : ",fmt_float(actual,10))); - echo(str(" : ",actual)); - echo(str("Delta : ",fmt_float(expected-actual,10))); - echo(str(" : ",expected-actual)); - assert(approx(expected,actual)); - } + +module test_is_quaternion() { + assert_approx(is_quaternion([0]),false); + assert_approx(is_quaternion([0,0,0,0]),false); + assert_approx(is_quaternion([1,0,2,0]),true); + assert_approx(is_quaternion([1,0,2,0,0]),false); } +test_is_quaternion(); -module test_Q_is_quat() { - verify_f(Q_is_quat([0]),false); - verify_f(Q_is_quat([0,0,0,0]),false); - verify_f(Q_is_quat([1,0,2,0]),true); - verify_f(Q_is_quat([1,0,2,0,0]),false); +module test_quat() { + assert_approx(quat(UP,0),[0,0,0,1]); + assert_approx(quat(FWD,0),[0,0,0,1]); + assert_approx(quat(LEFT,0),[0,0,0,1]); + assert_approx(quat(UP,45),[0,0,0.3826834324,0.9238795325]); + assert_approx(quat(LEFT,45),[-0.3826834324, 0, 0, 0.9238795325]); + assert_approx(quat(BACK,45),[0,0.3826834323,0,0.9238795325]); + assert_approx(quat(FWD+RIGHT,30),[0.1830127019, -0.1830127019, 0, 0.9659258263]); } -test_Q_is_quat(); +test_quat(); -module test_Quat() { - verify_f(Quat(UP,0),[0,0,0,1]); - verify_f(Quat(FWD,0),[0,0,0,1]); - verify_f(Quat(LEFT,0),[0,0,0,1]); - verify_f(Quat(UP,45),[0,0,0.3826834324,0.9238795325]); - verify_f(Quat(LEFT,45),[-0.3826834324, 0, 0, 0.9238795325]); - verify_f(Quat(BACK,45),[0,0.3826834323,0,0.9238795325]); - verify_f(Quat(FWD+RIGHT,30),[0.1830127019, -0.1830127019, 0, 0.9659258263]); +module test_quat_x() { + assert_approx(quat_x(0),[0,0,0,1]); + assert_approx(quat_x(35),[0.3007057995,0,0,0.9537169507]); + assert_approx(quat_x(45),[0.3826834324,0,0,0.9238795325]); } -test_Quat(); +test_quat_x(); -module test_QuatX() { - verify_f(QuatX(0),[0,0,0,1]); - verify_f(QuatX(35),[0.3007057995,0,0,0.9537169507]); - verify_f(QuatX(45),[0.3826834324,0,0,0.9238795325]); +module test_quat_y() { + assert_approx(quat_y(0),[0,0,0,1]); + assert_approx(quat_y(35),[0,0.3007057995,0,0.9537169507]); + assert_approx(quat_y(45),[0,0.3826834323,0,0.9238795325]); } -test_QuatX(); +test_quat_y(); -module test_QuatY() { - verify_f(QuatY(0),[0,0,0,1]); - verify_f(QuatY(35),[0,0.3007057995,0,0.9537169507]); - verify_f(QuatY(45),[0,0.3826834323,0,0.9238795325]); +module test_quat_z() { + assert_approx(quat_z(0),[0,0,0,1]); + assert_approx(quat_z(36),[0,0,0.3090169944,0.9510565163]); + assert_approx(quat_z(45),[0,0,0.3826834324,0.9238795325]); } -test_QuatY(); +test_quat_z(); -module test_QuatZ() { - verify_f(QuatZ(0),[0,0,0,1]); - verify_f(QuatZ(36),[0,0,0.3090169944,0.9510565163]); - verify_f(QuatZ(45),[0,0,0.3826834324,0.9238795325]); +module test_quat_xyz() { + assert_approx(quat_xyz([0,0,0]), [0,0,0,1]); + assert_approx(quat_xyz([30,0,0]), [0.2588190451, 0, 0, 0.9659258263]); + assert_approx(quat_xyz([90,0,0]), [0.7071067812, 0, 0, 0.7071067812]); + assert_approx(quat_xyz([-270,0,0]), [-0.7071067812, 0, 0, -0.7071067812]); + assert_approx(quat_xyz([180,0,0]), [1,0,0,0]); + assert_approx(quat_xyz([270,0,0]), [0.7071067812, 0, 0, -0.7071067812]); + assert_approx(quat_xyz([-90,0,0]), [-0.7071067812, 0, 0, 0.7071067812]); + assert_approx(quat_xyz([360,0,0]), [0,0,0,-1]); + + assert_approx(quat_xyz([0,0,0]), [0,0,0,1]); + assert_approx(quat_xyz([0,30,0]), [0, 0.2588190451, 0, 0.9659258263]); + assert_approx(quat_xyz([0,90,0]), [0, 0.7071067812, 0, 0.7071067812]); + assert_approx(quat_xyz([0,-270,0]), [0, -0.7071067812, 0, -0.7071067812]); + assert_approx(quat_xyz([0,180,0]), [0,1,0,0]); + assert_approx(quat_xyz([0,270,0]), [0, 0.7071067812, 0, -0.7071067812]); + assert_approx(quat_xyz([0,-90,0]), [0, -0.7071067812, 0, 0.7071067812]); + assert_approx(quat_xyz([0,360,0]), [0,0,0,-1]); + + assert_approx(quat_xyz([0,0,0]), [0,0,0,1]); + assert_approx(quat_xyz([0,0,30]), [0, 0, 0.2588190451, 0.9659258263]); + assert_approx(quat_xyz([0,0,90]), [0, 0, 0.7071067812, 0.7071067812]); + assert_approx(quat_xyz([0,0,-270]), [0, 0, -0.7071067812, -0.7071067812]); + assert_approx(quat_xyz([0,0,180]), [0,0,1,0]); + assert_approx(quat_xyz([0,0,270]), [0, 0, 0.7071067812, -0.7071067812]); + assert_approx(quat_xyz([0,0,-90]), [0, 0, -0.7071067812, 0.7071067812]); + assert_approx(quat_xyz([0,0,360]), [0,0,0,-1]); + + assert_approx(quat_xyz([30,30,30]), [0.1767766953, 0.3061862178, 0.1767766953, 0.9185586535]); + assert_approx(quat_xyz([12,34,56]), [-0.04824789229, 0.3036636044, 0.4195145429, 0.8540890495]); } -test_QuatZ(); +test_quat_xyz(); -module test_QuatXYZ() { - verify_f(QuatXYZ([0,0,0]), [0,0,0,1]); - verify_f(QuatXYZ([30,0,0]), [0.2588190451, 0, 0, 0.9659258263]); - verify_f(QuatXYZ([90,0,0]), [0.7071067812, 0, 0, 0.7071067812]); - verify_f(QuatXYZ([-270,0,0]), [-0.7071067812, 0, 0, -0.7071067812]); - verify_f(QuatXYZ([180,0,0]), [1,0,0,0]); - verify_f(QuatXYZ([270,0,0]), [0.7071067812, 0, 0, -0.7071067812]); - verify_f(QuatXYZ([-90,0,0]), [-0.7071067812, 0, 0, 0.7071067812]); - verify_f(QuatXYZ([360,0,0]), [0,0,0,-1]); - - verify_f(QuatXYZ([0,0,0]), [0,0,0,1]); - verify_f(QuatXYZ([0,30,0]), [0, 0.2588190451, 0, 0.9659258263]); - verify_f(QuatXYZ([0,90,0]), [0, 0.7071067812, 0, 0.7071067812]); - verify_f(QuatXYZ([0,-270,0]), [0, -0.7071067812, 0, -0.7071067812]); - verify_f(QuatXYZ([0,180,0]), [0,1,0,0]); - verify_f(QuatXYZ([0,270,0]), [0, 0.7071067812, 0, -0.7071067812]); - verify_f(QuatXYZ([0,-90,0]), [0, -0.7071067812, 0, 0.7071067812]); - verify_f(QuatXYZ([0,360,0]), [0,0,0,-1]); - - verify_f(QuatXYZ([0,0,0]), [0,0,0,1]); - verify_f(QuatXYZ([0,0,30]), [0, 0, 0.2588190451, 0.9659258263]); - verify_f(QuatXYZ([0,0,90]), [0, 0, 0.7071067812, 0.7071067812]); - verify_f(QuatXYZ([0,0,-270]), [0, 0, -0.7071067812, -0.7071067812]); - verify_f(QuatXYZ([0,0,180]), [0,0,1,0]); - verify_f(QuatXYZ([0,0,270]), [0, 0, 0.7071067812, -0.7071067812]); - verify_f(QuatXYZ([0,0,-90]), [0, 0, -0.7071067812, 0.7071067812]); - verify_f(QuatXYZ([0,0,360]), [0,0,0,-1]); - - verify_f(QuatXYZ([30,30,30]), [0.1767766953, 0.3061862178, 0.1767766953, 0.9185586535]); - verify_f(QuatXYZ([12,34,56]), [-0.04824789229, 0.3036636044, 0.4195145429, 0.8540890495]); +module test_q_from_to() { + assert_approx(q_mul(q_from_to([1,2,3], [4,5,2]),q_from_to([4,5,2], [1,2,3])), q_ident()); + assert_approx(q_matrix4(q_from_to([1,2,3], [4,5,2])), rot(from=[1,2,3],to=[4,5,2])); + assert_approx(q_rot(q_from_to([1,2,3], -[1,2,3]),[1,2,3]), -[1,2,3]); + assert_approx(unit(q_rot(q_from_to([1,2,3], [4,5,2]),[1,2,3])), unit([4,5,2])); } -test_QuatXYZ(); +test_q_from_to(); -module test_Q_From_to() { - verify_f(Q_Mul(Q_From_to([1,2,3], [4,5,2]),Q_From_to([4,5,2], [1,2,3])), Q_Ident()); - verify_f(Q_Matrix4(Q_From_to([1,2,3], [4,5,2])), rot(from=[1,2,3],to=[4,5,2])); - verify_f(Qrot(Q_From_to([1,2,3], -[1,2,3]),[1,2,3]), -[1,2,3]); - verify_f(unit(Qrot(Q_From_to([1,2,3], [4,5,2]),[1,2,3])), unit([4,5,2])); +module test_q_ident() { + assert_approx(q_ident(), [0,0,0,1]); } -test_Q_From_to(); +test_q_ident(); -module test_Q_Ident() { - verify_f(Q_Ident(), [0,0,0,1]); +module test_q_add_s() { + assert_approx(q_add_s([0,0,0,1],3),[0,0,0,4]); + assert_approx(q_add_s([0,0,1,0],3),[0,0,1,3]); + assert_approx(q_add_s([0,1,0,0],3),[0,1,0,3]); + assert_approx(q_add_s([1,0,0,0],3),[1,0,0,3]); + assert_approx(q_add_s(quat(LEFT+FWD,23),1),[-0.1409744184, -0.1409744184, 0, 1.979924705]); } -test_Q_Ident(); +test_q_add_s(); -module test_Q_Add_S() { - verify_f(Q_Add_S([0,0,0,1],3),[0,0,0,4]); - verify_f(Q_Add_S([0,0,1,0],3),[0,0,1,3]); - verify_f(Q_Add_S([0,1,0,0],3),[0,1,0,3]); - verify_f(Q_Add_S([1,0,0,0],3),[1,0,0,3]); - verify_f(Q_Add_S(Quat(LEFT+FWD,23),1),[-0.1409744184, -0.1409744184, 0, 1.979924705]); +module test_q_sub_s() { + assert_approx(q_sub_s([0,0,0,1],3),[0,0,0,-2]); + assert_approx(q_sub_s([0,0,1,0],3),[0,0,1,-3]); + assert_approx(q_sub_s([0,1,0,0],3),[0,1,0,-3]); + assert_approx(q_sub_s([1,0,0,0],3),[1,0,0,-3]); + assert_approx(q_sub_s(quat(LEFT+FWD,23),1),[-0.1409744184, -0.1409744184, 0, -0.02007529538]); } -test_Q_Add_S(); +test_q_sub_s(); -module test_Q_Sub_S() { - verify_f(Q_Sub_S([0,0,0,1],3),[0,0,0,-2]); - verify_f(Q_Sub_S([0,0,1,0],3),[0,0,1,-3]); - verify_f(Q_Sub_S([0,1,0,0],3),[0,1,0,-3]); - verify_f(Q_Sub_S([1,0,0,0],3),[1,0,0,-3]); - verify_f(Q_Sub_S(Quat(LEFT+FWD,23),1),[-0.1409744184, -0.1409744184, 0, -0.02007529538]); +module test_q_mul_s() { + assert_approx(q_mul_s([0,0,0,1],3),[0,0,0,3]); + assert_approx(q_mul_s([0,0,1,0],3),[0,0,3,0]); + assert_approx(q_mul_s([0,1,0,0],3),[0,3,0,0]); + assert_approx(q_mul_s([1,0,0,0],3),[3,0,0,0]); + assert_approx(q_mul_s([1,0,0,1],3),[3,0,0,3]); + assert_approx(q_mul_s(quat(LEFT+FWD,23),4),[-0.5638976735, -0.5638976735, 0, 3.919698818]); } -test_Q_Sub_S(); +test_q_mul_s(); -module test_Q_Mul_S() { - verify_f(Q_Mul_S([0,0,0,1],3),[0,0,0,3]); - verify_f(Q_Mul_S([0,0,1,0],3),[0,0,3,0]); - verify_f(Q_Mul_S([0,1,0,0],3),[0,3,0,0]); - verify_f(Q_Mul_S([1,0,0,0],3),[3,0,0,0]); - verify_f(Q_Mul_S([1,0,0,1],3),[3,0,0,3]); - verify_f(Q_Mul_S(Quat(LEFT+FWD,23),4),[-0.5638976735, -0.5638976735, 0, 3.919698818]); + +module test_q_div_s() { + assert_approx(q_div_s([0,0,0,1],3),[0,0,0,1/3]); + assert_approx(q_div_s([0,0,1,0],3),[0,0,1/3,0]); + assert_approx(q_div_s([0,1,0,0],3),[0,1/3,0,0]); + assert_approx(q_div_s([1,0,0,0],3),[1/3,0,0,0]); + assert_approx(q_div_s([1,0,0,1],3),[1/3,0,0,1/3]); + assert_approx(q_div_s(quat(LEFT+FWD,23),4),[-0.03524360459, -0.03524360459, 0, 0.2449811762]); } -test_Q_Mul_S(); +test_q_div_s(); - -module test_Q_Div_S() { - verify_f(Q_Div_S([0,0,0,1],3),[0,0,0,1/3]); - verify_f(Q_Div_S([0,0,1,0],3),[0,0,1/3,0]); - verify_f(Q_Div_S([0,1,0,0],3),[0,1/3,0,0]); - verify_f(Q_Div_S([1,0,0,0],3),[1/3,0,0,0]); - verify_f(Q_Div_S([1,0,0,1],3),[1/3,0,0,1/3]); - verify_f(Q_Div_S(Quat(LEFT+FWD,23),4),[-0.03524360459, -0.03524360459, 0, 0.2449811762]); +module test_q_add() { + assert_approx(q_add([2,3,4,5],[-1,-1,-1,-1]),[1,2,3,4]); + assert_approx(q_add([2,3,4,5],[-3,-3,-3,-3]),[-1,0,1,2]); + assert_approx(q_add([2,3,4,5],[0,0,0,0]),[2,3,4,5]); + assert_approx(q_add([2,3,4,5],[1,1,1,1]),[3,4,5,6]); + assert_approx(q_add([2,3,4,5],[1,0,0,0]),[3,3,4,5]); + assert_approx(q_add([2,3,4,5],[0,1,0,0]),[2,4,4,5]); + assert_approx(q_add([2,3,4,5],[0,0,1,0]),[2,3,5,5]); + assert_approx(q_add([2,3,4,5],[0,0,0,1]),[2,3,4,6]); + assert_approx(q_add([2,3,4,5],[2,1,2,1]),[4,4,6,6]); + assert_approx(q_add([2,3,4,5],[1,2,1,2]),[3,5,5,7]); } -test_Q_Div_S(); +test_q_add(); -module test_Q_Add() { - verify_f(Q_Add([2,3,4,5],[-1,-1,-1,-1]),[1,2,3,4]); - verify_f(Q_Add([2,3,4,5],[-3,-3,-3,-3]),[-1,0,1,2]); - verify_f(Q_Add([2,3,4,5],[0,0,0,0]),[2,3,4,5]); - verify_f(Q_Add([2,3,4,5],[1,1,1,1]),[3,4,5,6]); - verify_f(Q_Add([2,3,4,5],[1,0,0,0]),[3,3,4,5]); - verify_f(Q_Add([2,3,4,5],[0,1,0,0]),[2,4,4,5]); - verify_f(Q_Add([2,3,4,5],[0,0,1,0]),[2,3,5,5]); - verify_f(Q_Add([2,3,4,5],[0,0,0,1]),[2,3,4,6]); - verify_f(Q_Add([2,3,4,5],[2,1,2,1]),[4,4,6,6]); - verify_f(Q_Add([2,3,4,5],[1,2,1,2]),[3,5,5,7]); +module test_q_sub() { + assert_approx(q_sub([2,3,4,5],[-1,-1,-1,-1]),[3,4,5,6]); + assert_approx(q_sub([2,3,4,5],[-3,-3,-3,-3]),[5,6,7,8]); + assert_approx(q_sub([2,3,4,5],[0,0,0,0]),[2,3,4,5]); + assert_approx(q_sub([2,3,4,5],[1,1,1,1]),[1,2,3,4]); + assert_approx(q_sub([2,3,4,5],[1,0,0,0]),[1,3,4,5]); + assert_approx(q_sub([2,3,4,5],[0,1,0,0]),[2,2,4,5]); + assert_approx(q_sub([2,3,4,5],[0,0,1,0]),[2,3,3,5]); + assert_approx(q_sub([2,3,4,5],[0,0,0,1]),[2,3,4,4]); + assert_approx(q_sub([2,3,4,5],[2,1,2,1]),[0,2,2,4]); + assert_approx(q_sub([2,3,4,5],[1,2,1,2]),[1,1,3,3]); } -test_Q_Add(); +test_q_sub(); -module test_Q_Sub() { - verify_f(Q_Sub([2,3,4,5],[-1,-1,-1,-1]),[3,4,5,6]); - verify_f(Q_Sub([2,3,4,5],[-3,-3,-3,-3]),[5,6,7,8]); - verify_f(Q_Sub([2,3,4,5],[0,0,0,0]),[2,3,4,5]); - verify_f(Q_Sub([2,3,4,5],[1,1,1,1]),[1,2,3,4]); - verify_f(Q_Sub([2,3,4,5],[1,0,0,0]),[1,3,4,5]); - verify_f(Q_Sub([2,3,4,5],[0,1,0,0]),[2,2,4,5]); - verify_f(Q_Sub([2,3,4,5],[0,0,1,0]),[2,3,3,5]); - verify_f(Q_Sub([2,3,4,5],[0,0,0,1]),[2,3,4,4]); - verify_f(Q_Sub([2,3,4,5],[2,1,2,1]),[0,2,2,4]); - verify_f(Q_Sub([2,3,4,5],[1,2,1,2]),[1,1,3,3]); +module test_q_mul() { + assert_approx(q_mul(quat_z(30),quat_x(57)),[0.4608999698, 0.1234977747, 0.2274546059, 0.8488721457]); + assert_approx(q_mul(quat_y(30),quat_z(23)),[0.05160021841, 0.2536231763, 0.1925746368, 0.94653458]); } -test_Q_Sub(); +test_q_mul(); -module test_Q_Mul() { - verify_f(Q_Mul(QuatZ(30),QuatX(57)),[0.4608999698, 0.1234977747, 0.2274546059, 0.8488721457]); - verify_f(Q_Mul(QuatY(30),QuatZ(23)),[0.05160021841, 0.2536231763, 0.1925746368, 0.94653458]); +module test_q_cumulative() { + assert_approx(q_cumulative([quat_z(30),quat_x(57),quat_y(18)]),[[0, 0, 0.2588190451, 0.9659258263], [0.4608999698, -0.1234977747, 0.2274546059, 0.8488721457], [0.4908072659, 0.01081554785, 0.1525536221, 0.8577404293]]); } -test_Q_Mul(); +test_q_cumulative(); -module test_Q_Cumulative() { - verify_f(Q_Cumulative([QuatZ(30),QuatX(57),QuatY(18)]),[[0, 0, 0.2588190451, 0.9659258263], [0.4608999698, -0.1234977747, 0.2274546059, 0.8488721457], [0.4908072659, 0.01081554785, 0.1525536221, 0.8577404293]]); +module test_q_dot() { + assert_approx(q_dot(quat_z(30),quat_x(57)),0.8488721457); + assert_approx(q_dot(quat_y(30),quat_z(23)),0.94653458); } -test_Q_Cumulative(); +test_q_dot(); -module test_Q_Dot() { - verify_f(Q_Dot(QuatZ(30),QuatX(57)),0.8488721457); - verify_f(Q_Dot(QuatY(30),QuatZ(23)),0.94653458); +module test_q_neg() { + assert_approx(q_neg([1,0,0,1]),[-1,0,0,-1]); + assert_approx(q_neg([0,1,1,0]),[0,-1,-1,0]); + assert_approx(q_neg(quat_xyz([23,45,67])),[0.0533818345,-0.4143703268,-0.4360652669,-0.7970537592]); } -test_Q_Dot(); +test_q_neg(); -module test_Q_Neg() { - verify_f(Q_Neg([1,0,0,1]),[-1,0,0,-1]); - verify_f(Q_Neg([0,1,1,0]),[0,-1,-1,0]); - verify_f(Q_Neg(QuatXYZ([23,45,67])),[0.0533818345,-0.4143703268,-0.4360652669,-0.7970537592]); +module test_q_conj() { + assert_approx(q_conj([1,0,0,1]),[-1,0,0,1]); + assert_approx(q_conj([0,1,1,0]),[0,-1,-1,0]); + assert_approx(q_conj(quat_xyz([23,45,67])),[0.0533818345, -0.4143703268, -0.4360652669, 0.7970537592]); } -test_Q_Neg(); +test_q_conj(); -module test_Q_Conj() { - verify_f(Q_Conj([1,0,0,1]),[-1,0,0,1]); - verify_f(Q_Conj([0,1,1,0]),[0,-1,-1,0]); - verify_f(Q_Conj(QuatXYZ([23,45,67])),[0.0533818345, -0.4143703268, -0.4360652669, 0.7970537592]); +module test_q_inverse() { + + assert_approx(q_inverse([1,0,0,1]),[-1,0,0,1]/sqrt(2)); + assert_approx(q_inverse([0,1,1,0]),[0,-1,-1,0]/sqrt(2)); + assert_approx(q_inverse(quat_xyz([23,45,67])),q_conj(quat_xyz([23,45,67]))); + assert_approx(q_mul(q_inverse(quat_xyz([23,45,67])),quat_xyz([23,45,67])),q_ident()); } -test_Q_Conj(); +test_q_inverse(); -module test_Q_Inverse() { - - verify_f(Q_Inverse([1,0,0,1]),[-1,0,0,1]/sqrt(2)); - verify_f(Q_Inverse([0,1,1,0]),[0,-1,-1,0]/sqrt(2)); - verify_f(Q_Inverse(QuatXYZ([23,45,67])),Q_Conj(QuatXYZ([23,45,67]))); - verify_f(Q_Mul(Q_Inverse(QuatXYZ([23,45,67])),QuatXYZ([23,45,67])),Q_Ident()); +module test_q_Norm() { + assert_approx(q_norm([1,0,0,1]),1.414213562); + assert_approx(q_norm([0,1,1,0]),1.414213562); + assert_approx(q_norm(quat_xyz([23,45,67])),1); } -test_Q_Inverse(); +test_q_Norm(); -module test_Q_Norm() { - verify_f(Q_Norm([1,0,0,1]),1.414213562); - verify_f(Q_Norm([0,1,1,0]),1.414213562); - verify_f(Q_Norm(QuatXYZ([23,45,67])),1); +module test_q_normalize() { + assert_approx(q_normalize([1,0,0,1]),[0.7071067812, 0, 0, 0.7071067812]); + assert_approx(q_normalize([0,1,1,0]),[0, 0.7071067812, 0.7071067812, 0]); + assert_approx(q_normalize(quat_xyz([23,45,67])),[-0.0533818345, 0.4143703268, 0.4360652669, 0.7970537592]); } -test_Q_Norm(); +test_q_normalize(); -module test_Q_Normalize() { - verify_f(Q_Normalize([1,0,0,1]),[0.7071067812, 0, 0, 0.7071067812]); - verify_f(Q_Normalize([0,1,1,0]),[0, 0.7071067812, 0.7071067812, 0]); - verify_f(Q_Normalize(QuatXYZ([23,45,67])),[-0.0533818345, 0.4143703268, 0.4360652669, 0.7970537592]); +module test_q_dist() { + assert_approx(q_dist(quat_xyz([23,45,67]),quat_xyz([23,45,67])),0); + assert_approx(q_dist(quat_xyz([23,45,67]),quat_xyz([12,34,56])),0.1257349854); } -test_Q_Normalize(); +test_q_dist(); -module test_Q_Dist() { - verify_f(Q_Dist(QuatXYZ([23,45,67]),QuatXYZ([23,45,67])),0); - verify_f(Q_Dist(QuatXYZ([23,45,67]),QuatXYZ([12,34,56])),0.1257349854); +module test_q_slerp() { + assert_approx(q_slerp(quat_x(45),quat_y(30),0.0),quat_x(45)); + assert_approx(q_slerp(quat_x(45),quat_y(30),0.5),[0.1967063121, 0.1330377423, 0, 0.9713946602]); + assert_approx(q_slerp(quat_x(45),quat_y(30),1.0),quat_y(30)); } -test_Q_Dist(); +test_q_slerp(); -module test_Q_Slerp() { - verify_f(Q_Slerp(QuatX(45),QuatY(30),0.0),QuatX(45)); - verify_f(Q_Slerp(QuatX(45),QuatY(30),0.5),[0.1967063121, 0.1330377423, 0, 0.9713946602]); - verify_f(Q_Slerp(QuatX(45),QuatY(30),1.0),QuatY(30)); +module test_q_matrix3() { + assert_approx(q_matrix3(quat_z(37)),rot(37,planar=true)); + assert_approx(q_matrix3(quat_z(-49)),rot(-49,planar=true)); } -test_Q_Slerp(); +test_q_matrix3(); -module test_Q_Matrix3() { - verify_f(Q_Matrix3(QuatZ(37)),rot(37,planar=true)); - verify_f(Q_Matrix3(QuatZ(-49)),rot(-49,planar=true)); +module test_q_matrix4() { + assert_approx(q_matrix4(quat_z(37)),rot(37)); + assert_approx(q_matrix4(quat_z(-49)),rot(-49)); + assert_approx(q_matrix4(quat_x(37)),rot([37,0,0])); + assert_approx(q_matrix4(quat_y(37)),rot([0,37,0])); + assert_approx(q_matrix4(quat_xyz([12,34,56])),rot([12,34,56])); } -test_Q_Matrix3(); +test_q_matrix4(); -module test_Q_Matrix4() { - verify_f(Q_Matrix4(QuatZ(37)),rot(37)); - verify_f(Q_Matrix4(QuatZ(-49)),rot(-49)); - verify_f(Q_Matrix4(QuatX(37)),rot([37,0,0])); - verify_f(Q_Matrix4(QuatY(37)),rot([0,37,0])); - verify_f(Q_Matrix4(QuatXYZ([12,34,56])),rot([12,34,56])); +module test_q_axis() { + assert_approx(q_axis(quat_x(37)),RIGHT); + assert_approx(q_axis(quat_x(-37)),LEFT); + assert_approx(q_axis(quat_y(37)),BACK); + assert_approx(q_axis(quat_y(-37)),FWD); + assert_approx(q_axis(quat_z(37)),UP); + assert_approx(q_axis(quat_z(-37)),DOWN); } -test_Q_Matrix4(); +test_q_axis(); -module test_Q_Axis() { - verify_f(Q_Axis(QuatX(37)),RIGHT); - verify_f(Q_Axis(QuatX(-37)),LEFT); - verify_f(Q_Axis(QuatY(37)),BACK); - verify_f(Q_Axis(QuatY(-37)),FWD); - verify_f(Q_Axis(QuatZ(37)),UP); - verify_f(Q_Axis(QuatZ(-37)),DOWN); +module test_q_angle() { + assert_approx(q_angle(quat_x(0)),0); + assert_approx(q_angle(quat_y(0)),0); + assert_approx(q_angle(quat_z(0)),0); + assert_approx(q_angle(quat_x(37)),37); + assert_approx(q_angle(quat_x(-37)),37); + assert_approx(q_angle(quat_y(37)),37); + assert_approx(q_angle(quat_y(-37)),37); + assert_approx(q_angle(quat_z(37)),37); + assert_approx(q_angle(quat_z(-37)),37); + + assert_approx(q_angle(quat_z(-37),quat_z(-37)), 0); + assert_approx(q_angle(quat_z( 37.123),quat_z(-37.123)), 74.246); + assert_approx(q_angle(quat_x( 37),quat_y(-37)), 51.86293283); } -test_Q_Axis(); +test_q_angle(); -module test_Q_Angle() { - verify_f(Q_Angle(QuatX(0)),0); - verify_f(Q_Angle(QuatY(0)),0); - verify_f(Q_Angle(QuatZ(0)),0); - verify_f(Q_Angle(QuatX(37)),37); - verify_f(Q_Angle(QuatX(-37)),37); - verify_f(Q_Angle(QuatY(37)),37); - verify_f(Q_Angle(QuatY(-37)),37); - verify_f(Q_Angle(QuatZ(37)),37); - verify_f(Q_Angle(QuatZ(-37)),37); - - verify_f(Q_Angle(QuatZ(-37),QuatZ(-37)), 0); - verify_f(Q_Angle(QuatZ( 37.123),QuatZ(-37.123)), 74.246); - verify_f(Q_Angle(QuatX( 37),QuatY(-37)), 51.86293283); +module test_q_rot() { + assert_approx(q_rot(quat_xyz([12,34,56])),rot([12,34,56])); + assert_approx(q_rot(quat_xyz([12,34,56]),p=[2,3,4]),rot([12,34,56],p=[2,3,4])); + assert_approx(q_rot(quat_xyz([12,34,56]),p=[[2,3,4],[4,9,6]]),rot([12,34,56],p=[[2,3,4],[4,9,6]])); } -test_Q_Angle(); +test_q_rot(); -module test_Qrot() { - verify_f(Qrot(QuatXYZ([12,34,56])),rot([12,34,56])); - verify_f(Qrot(QuatXYZ([12,34,56]),p=[2,3,4]),rot([12,34,56],p=[2,3,4])); - verify_f(Qrot(QuatXYZ([12,34,56]),p=[[2,3,4],[4,9,6]]),rot([12,34,56],p=[[2,3,4],[4,9,6]])); +module test_q_rotation() { + assert_approx(_q_standard(q_rotation(q_matrix3(quat([12,34,56],33)))),_q_standard(quat([12,34,56],33))); + assert_approx(q_matrix3(q_rotation(q_matrix3(quat_xyz([12,34,56])))), + q_matrix3(quat_xyz([12,34,56]))); } -test_Qrot(); +test_q_rotation(); -module test_Q_Rotation() { - verify_f(Qstandard(Q_Rotation(Q_Matrix3(Quat([12,34,56],33)))),Qstandard(Quat([12,34,56],33))); - verify_f(Q_Matrix3(Q_Rotation(Q_Matrix3(QuatXYZ([12,34,56])))), - Q_Matrix3(QuatXYZ([12,34,56]))); -} -test_Q_Rotation(); +module test_q_rotation_path() { + assert_approx(q_rotation_path(quat_x(135), 5, quat_y(13.5))[0] , q_matrix4(quat_x(135))); + assert_approx(q_rotation_path(quat_x(135), 11, quat_y(13.5))[11] , yrot(13.5)); + assert_approx(q_rotation_path(quat_x(135), 16, quat_y(13.5))[8] , q_rotation_path(quat_x(135), 8, quat_y(13.5))[4]); + assert_approx(q_rotation_path(quat_x(135), 16, quat_y(13.5))[7] , + q_rotation_path(quat_y(13.5),16, quat_x(135))[9]); - -module test_Q_Rotation_path() { - - verify_f(Q_Rotation_path(QuatX(135), 5, QuatY(13.5))[0] , Q_Matrix4(QuatX(135))); - verify_f(Q_Rotation_path(QuatX(135), 11, QuatY(13.5))[11] , yrot(13.5)); - verify_f(Q_Rotation_path(QuatX(135), 16, QuatY(13.5))[8] , Q_Rotation_path(QuatX(135), 8, QuatY(13.5))[4]); - verify_f(Q_Rotation_path(QuatX(135), 16, QuatY(13.5))[7] , - Q_Rotation_path(QuatY(13.5),16, QuatX(135))[9]); - - verify_f(Q_Rotation_path(QuatX(11), 5)[0] , xrot(11)); - verify_f(Q_Rotation_path(QuatX(11), 5)[4] , xrot(55)); + assert_approx(q_rotation_path(quat_x(11), 5)[0] , xrot(11)); + assert_approx(q_rotation_path(quat_x(11), 5)[4] , xrot(55)); } -test_Q_Rotation_path(); +test_q_rotation_path(); -module test_Q_Nlerp() { - verify_f(Q_Nlerp(QuatX(45),QuatY(30),0.0),QuatX(45)); - verify_f(Q_Nlerp(QuatX(45),QuatY(30),0.5),[0.1967063121, 0.1330377423, 0, 0.9713946602]); - verify_f(Q_Rotation_path(QuatX(135), 16, QuatY(13.5))[8] , Q_Matrix4(Q_Nlerp(QuatX(135), QuatY(13.5),0.5))); - verify_f(Q_Nlerp(QuatX(45),QuatY(30),1.0),QuatY(30)); +module test_q_nlerp() { + assert_approx(q_nlerp(quat_x(45),quat_y(30),0.0),quat_x(45)); + assert_approx(q_nlerp(quat_x(45),quat_y(30),0.5),[0.1967063121, 0.1330377423, 0, 0.9713946602]); + assert_approx(q_rotation_path(quat_x(135), 16, quat_y(13.5))[8] , q_matrix4(q_nlerp(quat_x(135), quat_y(13.5),0.5))); + assert_approx(q_nlerp(quat_x(45),quat_y(30),1.0),quat_y(30)); } -test_Q_Nlerp(); +test_q_nlerp(); -module test_Q_Squad() { - verify_f(Q_Squad(QuatX(45),QuatZ(30),QuatX(90),QuatY(30),0.0),QuatX(45)); - verify_f(Q_Squad(QuatX(45),QuatZ(30),QuatX(90),QuatY(30),1.0),QuatY(30)); - verify_f(Q_Squad(QuatX(0),QuatX(30),QuatX(90),QuatX(120),0.5), - Q_Slerp(QuatX(0),QuatX(120),0.5)); - verify_f(Q_Squad(QuatY(0),QuatY(0),QuatX(120),QuatX(120),0.3), - Q_Slerp(QuatY(0),QuatX(120),0.3)); +module test_q_squad() { + assert_approx(q_squad(quat_x(45),quat_z(30),quat_x(90),quat_y(30),0.0),quat_x(45)); + assert_approx(q_squad(quat_x(45),quat_z(30),quat_x(90),quat_y(30),1.0),quat_y(30)); + assert_approx(q_squad(quat_x(0),quat_x(30),quat_x(90),quat_x(120),0.5), + q_slerp(quat_x(0),quat_x(120),0.5)); + assert_approx(q_squad(quat_y(0),quat_y(0),quat_x(120),quat_x(120),0.3), + q_slerp(quat_y(0),quat_x(120),0.3)); } -test_Q_Squad(); +test_q_squad(); -module test_Q_exp() { - verify_f(Q_exp(Q_Ident()), exp(1)*Q_Ident()); - verify_f(Q_exp([0,0,0,33.7]), exp(33.7)*Q_Ident()); - verify_f(Q_exp(Q_ln(Q_Ident())), Q_Ident()); - verify_f(Q_exp(Q_ln([1,2,3,0])), [1,2,3,0]); - verify_f(Q_exp(Q_ln(QuatXYZ([31,27,34]))), QuatXYZ([31,27,34])); - let(q=QuatXYZ([12,23,34])) - verify_f(Q_exp(q+Q_Inverse(q)),Q_Mul(Q_exp(q),Q_exp(Q_Inverse(q)))); +module test_q_exp() { + assert_approx(q_exp(q_ident()), exp(1)*q_ident()); + assert_approx(q_exp([0,0,0,33.7]), exp(33.7)*q_ident()); + assert_approx(q_exp(q_ln(q_ident())), q_ident()); + assert_approx(q_exp(q_ln([1,2,3,0])), [1,2,3,0]); + assert_approx(q_exp(q_ln(quat_xyz([31,27,34]))), quat_xyz([31,27,34])); + let(q=quat_xyz([12,23,34])) + assert_approx(q_exp(q+q_inverse(q)),q_mul(q_exp(q),q_exp(q_inverse(q)))); } -test_Q_exp(); +test_q_exp(); -module test_Q_ln() { - verify_f(Q_ln([1,2,3,0]), [24.0535117721, 48.1070235442, 72.1605353164, 1.31952866481]); - verify_f(Q_ln(Q_Ident()), [0,0,0,0]); - verify_f(Q_ln(5.5*Q_Ident()), [0,0,0,ln(5.5)]); - verify_f(Q_ln(Q_exp(QuatXYZ([13,37,43]))), QuatXYZ([13,37,43])); - verify_f(Q_ln(QuatXYZ([12,23,34]))+Q_ln(Q_Inverse(QuatXYZ([12,23,34]))), [0,0,0,0]); +module test_q_ln() { + assert_approx(q_ln([1,2,3,0]), [24.0535117721, 48.1070235442, 72.1605353164, 1.31952866481]); + assert_approx(q_ln(q_ident()), [0,0,0,0]); + assert_approx(q_ln(5.5*q_ident()), [0,0,0,ln(5.5)]); + assert_approx(q_ln(q_exp(quat_xyz([13,37,43]))), quat_xyz([13,37,43])); + assert_approx(q_ln(quat_xyz([12,23,34]))+q_ln(q_inverse(quat_xyz([12,23,34]))), [0,0,0,0]); } -test_Q_ln(); +test_q_ln(); -module test_Q_pow() { - q = Quat([1,2,3],77); - verify_f(Q_pow(q,1), q); - verify_f(Q_pow(q,0), Q_Ident()); - verify_f(Q_pow(q,-1), Q_Inverse(q)); - verify_f(Q_pow(q,2), Q_Mul(q,q)); - verify_f(Q_pow(q,3), Q_Mul(q,Q_pow(q,2))); - verify_f(Q_Mul(Q_pow(q,0.456),Q_pow(q,0.544)), q); - verify_f(Q_Mul(Q_pow(q,0.335),Q_Mul(Q_pow(q,.552),Q_pow(q,.113))), q); +module test_q_pow() { + q = quat([1,2,3],77); + assert_approx(q_pow(q,1), q); + assert_approx(q_pow(q,0), q_ident()); + assert_approx(q_pow(q,-1), q_inverse(q)); + assert_approx(q_pow(q,2), q_mul(q,q)); + assert_approx(q_pow(q,3), q_mul(q,q_pow(q,2))); + assert_approx(q_mul(q_pow(q,0.456),q_pow(q,0.544)), q); + assert_approx(q_mul(q_pow(q,0.335),q_mul(q_pow(q,.552),q_pow(q,.113))), q); } -test_Q_pow(); +test_q_pow(); From a748c77077d8309d2365e4dcaa4d0067a1b36274 Mon Sep 17 00:00:00 2001 From: Garth Minette Date: Mon, 14 Jun 2021 20:28:49 -0700 Subject: [PATCH 64/72] vmul() to v_mul(), etc. --- attachments.scad | 44 +++++++++---------- beziers.scad | 2 +- cubetruss.scad | 2 +- distributors.scad | 20 ++++----- edges.scad | 2 +- gears.scad | 4 +- math.scad | 8 ++-- mutators.scad | 2 +- partitions.scad | 4 +- polyhedra.scad | 2 +- primitives.scad | 2 +- shapes.scad | 22 +++++----- shapes2d.scad | 4 +- tests/test_transforms.scad | 6 +-- tests/test_vectors.scad | 89 +++++++++++++++++++------------------- transforms.scad | 4 +- turtle3d.scad | 2 +- vectors.scad | 43 +++++++++--------- 18 files changed, 130 insertions(+), 132 deletions(-) diff --git a/attachments.scad b/attachments.scad index 3dd12da..915b641 100644 --- a/attachments.scad +++ b/attachments.scad @@ -328,7 +328,7 @@ function attach_geom_size(geom) = [2*maxxr, 2*maxyr,l] ) : type == "spheroid"? ( //r let( r=geom[1] ) - is_num(r)? [2,2,2]*r : vmul([2,2,2],point3d(r)) + is_num(r)? [2,2,2]*r : v_mul([2,2,2],point3d(r)) ) : type == "vnf_extent" || type=="vnf_isect"? ( //vnf let( vnf = geom[1] @@ -344,7 +344,7 @@ function attach_geom_size(geom) = ) [maxx, size.y] ) : type == "circle"? ( //r let( r=geom[1] ) - is_num(r)? [2,2]*r : vmul([2,2],point2d(r)) + is_num(r)? [2,2]*r : v_mul([2,2],point2d(r)) ) : type == "path_isect" || type == "path_extent"? ( //path let( mm = pointlist_bounds(geom[1]), @@ -476,8 +476,8 @@ function find_anchor(anchor, geom) = h = size.z, u = (anch.z+1)/2, axy = point2d(anch), - bot = point3d(vmul(point2d(size)/2,axy),-h/2), - top = point3d(vmul(point2d(size2)/2,axy)+shift,h/2), + bot = point3d(v_mul(point2d(size)/2,axy),-h/2), + top = point3d(v_mul(point2d(size2)/2,axy)+shift,h/2), pos = point3d(cp) + lerp(bot,top,u) + offset, sidevec = unit(rot(from=UP, to=top-bot, p=point3d(axy)),UP), vvec = anch==CENTER? UP : unit([0,0,anch.z],UP), @@ -497,8 +497,8 @@ function find_anchor(anchor, geom) = anch = rot(from=axis, to=UP, p=anchor), u = (anch.z+1)/2, axy = unit(point2d(anch),[0,0]), - bot = point3d(vmul(r1,axy), -l/2), - top = point3d(vmul(r2,axy)+shift, l/2), + bot = point3d(v_mul(r1,axy), -l/2), + top = point3d(v_mul(r2,axy)+shift, l/2), pos = point3d(cp) + lerp(bot,top,u) + offset, sidevec = rot(from=UP, to=top-bot, p=point3d(axy)), vvec = anch==CENTER? UP : unit([0,0,anch.z],UP), @@ -514,8 +514,8 @@ function find_anchor(anchor, geom) = rr = geom[1], r = is_num(rr)? [rr,rr,rr] : point3d(rr), anchor = unit(point3d(anchor),CENTER), - pos = point3d(cp) + vmul(r,anchor) + point3d(offset), - vec = unit(vmul(r,anchor),UP) + pos = point3d(cp) + v_mul(r,anchor) + point3d(offset), + vec = unit(v_mul(r,anchor),UP) ) [anchor, pos, vec, oang] ) : type == "vnf_isect"? ( //vnf let( @@ -597,8 +597,8 @@ function find_anchor(anchor, geom) = rr = geom[1], r = is_num(rr)? [rr,rr] : point2d(rr), anchor = unit(point2d(anchor),[0,0]), - pos = point2d(cp) + vmul(r,anchor) + point2d(offset), - vec = unit(vmul(r,anchor),[0,1]) + pos = point2d(cp) + v_mul(r,anchor) + point2d(offset), + vec = unit(v_mul(r,anchor),[0,1]) ) [anchor, pos, vec, 0] ) : type == "path_isect"? ( //path let( @@ -1232,10 +1232,10 @@ module edge_profile(edges=EDGES_ALL, except=[], convexity=10) { psize = point3d($parent_size); length = [for (i=[0:2]) if(!vec[i]) psize[i]][0]+0.1; rotang = - vec.z<0? [90,0,180+vang(point2d(vec))] : - vec.z==0 && sign(vec.x)==sign(vec.y)? 135+vang(point2d(vec)) : - vec.z==0 && sign(vec.x)!=sign(vec.y)? [0,180,45+vang(point2d(vec))] : - [-90,0,180+vang(point2d(vec))]; + vec.z<0? [90,0,180+v_theta(vec)] : + vec.z==0 && sign(vec.x)==sign(vec.y)? 135+v_theta(vec) : + vec.z==0 && sign(vec.x)!=sign(vec.y)? [0,180,45+v_theta(vec)] : + [-90,0,180+v_theta(vec)]; translate(anch[1]) { rot(rotang) { linear_extrude(height=length, center=true, convexity=convexity) { @@ -1286,8 +1286,8 @@ module corner_profile(corners=CORNERS_ALL, except=[], r, d, convexity=10) { $attach_norot = true; $tags = "mask"; rotang = vec.z<0? - [ 0,0,180+vang(point2d(vec))-45] : - [180,0,-90+vang(point2d(vec))-45]; + [ 0,0,180+v_theta(vec)-45] : + [180,0,-90+v_theta(vec)-45]; translate(anch[1]) { rot(rotang) { render(convexity=convexity) @@ -1357,10 +1357,10 @@ module edge_mask(edges=EDGES_ALL, except=[]) { $attach_norot = true; $tags = "mask"; rotang = - vec.z<0? [90,0,180+vang(point2d(vec))] : - vec.z==0 && sign(vec.x)==sign(vec.y)? 135+vang(point2d(vec)) : - vec.z==0 && sign(vec.x)!=sign(vec.y)? [0,180,45+vang(point2d(vec))] : - [-90,0,180+vang(point2d(vec))]; + vec.z<0? [90,0,180+v_theta(vec)] : + vec.z==0 && sign(vec.x)==sign(vec.y)? 135+v_theta(vec) : + vec.z==0 && sign(vec.x)!=sign(vec.y)? [0,180,45+v_theta(vec)] : + [-90,0,180+v_theta(vec)]; translate(anch[1]) rot(rotang) children(); } } @@ -1401,8 +1401,8 @@ module corner_mask(corners=CORNERS_ALL, except=[]) { $attach_norot = true; $tags = "mask"; rotang = vec.z<0? - [ 0,0,180+vang(point2d(vec))-45] : - [180,0,-90+vang(point2d(vec))-45]; + [ 0,0,180+v_theta(vec)-45] : + [180,0,-90+v_theta(vec)-45]; translate(anch[1]) rot(rotang) children(); } } diff --git a/beziers.scad b/beziers.scad index feacb48..51808f8 100644 --- a/beziers.scad +++ b/beziers.scad @@ -1461,7 +1461,7 @@ function bezier_patch_flat(size=[100,100], N=4, spin=0, orient=UP, trans=[0,0,0] patch = [ for (x=[0:1:N]) [ for (y=[0:1:N]) - vmul(point3d(size), [x/N-0.5, 0.5-y/N, 0]) + v_mul(point3d(size), [x/N-0.5, 0.5-y/N, 0]) ] ], m = move(trans) * rot(a=spin, from=UP, to=orient) diff --git a/cubetruss.scad b/cubetruss.scad index 35a85d0..670a5c0 100644 --- a/cubetruss.scad +++ b/cubetruss.scad @@ -465,7 +465,7 @@ module cubetruss(extents=6, clips=[], bracing, size, strut, clipthick, anchor=CE } if (clipthick > 0) { for (vec = clips) { - exts = vabs(rot(from=FWD, to=vec, p=extents)); + exts = v_abs(rot(from=FWD, to=vec, p=extents)); rot(from=FWD,to=vec) { for (zrow = [0:1:exts.z-1]) { up((zrow-(exts.z-1)/2)*(size-strut)) { diff --git a/distributors.scad b/distributors.scad index 588acb8..6010b12 100644 --- a/distributors.scad +++ b/distributors.scad @@ -520,20 +520,20 @@ module grid2d(spacing, n, size, stagger=false, inside=undef) ) : is_vector(spacing)? assert(len(spacing)==2) spacing : size!=undef? ( - is_num(n)? vdiv(size,(n-1)*[1,1]) : - is_vector(n)? assert(len(n)==2) vdiv(size,n-[1,1]) : - vdiv(size,(stagger==false? [1,1] : [2,2])) + is_num(n)? v_div(size,(n-1)*[1,1]) : + is_vector(n)? assert(len(n)==2) v_div(size,n-[1,1]) : + v_div(size,(stagger==false? [1,1] : [2,2])) ) : undef; n = is_num(n)? [n,n] : is_vector(n)? assert(len(n)==2) n : - size!=undef && spacing!=undef? vfloor(vdiv(size,spacing))+[1,1] : + size!=undef && spacing!=undef? v_floor(v_div(size,spacing))+[1,1] : [2,2]; - offset = vmul(spacing, n-[1,1])/2; + offset = v_mul(spacing, n-[1,1])/2; if (stagger == false) { for (row = [0:1:n.y-1]) { for (col = [0:1:n.x-1]) { - pos = vmul([col,row],spacing) - offset; + pos = v_mul([col,row],spacing) - offset; if ( is_undef(inside) || (is_path(inside) && point_in_polygon(pos, inside)>=0) || @@ -556,7 +556,7 @@ module grid2d(spacing, n, size, stagger=false, inside=undef) if (rowcols > 0) { for (col = [0:1:rowcols-1]) { rowdx = (row%2 != staggermod)? spacing.x : 0; - pos = vmul([2*col,row],spacing) + [rowdx,0] - offset; + pos = v_mul([2*col,row],spacing) + [rowdx,0] - offset; if ( is_undef(inside) || (is_path(inside) && point_in_polygon(pos, inside)>=0) || @@ -616,7 +616,7 @@ module grid3d(xa=[0], ya=[0], za=[0], n=undef, spacing=undef) for (yi = [0:1:n.y-1]) { for (zi = [0:1:n.z-1]) { $idx = [xi,yi,zi]; - $pos = vmul(spacing, $idx - (n-[1,1,1])/2); + $pos = v_mul(spacing, $idx - (n-[1,1,1])/2); translate($pos) children(); } } @@ -989,7 +989,7 @@ module arc_of( // // Example: // ovoid_spread(n=500, d=100, cone_ang=180) -// color(unit(point3d(vabs($pos)))) +// color(unit(point3d(v_abs($pos)))) // cylinder(d=8, h=10, center=false); module ovoid_spread(r=undef, d=undef, n=100, cone_ang=90, scale=[1,1,1], perp=true) { @@ -1004,7 +1004,7 @@ module ovoid_spread(r=undef, d=undef, n=100, cone_ang=90, scale=[1,1,1], perp=tr for ($idx = idx(theta_phis)) { tp = theta_phis[$idx]; xyz = spherical_to_xyz(r, tp[0], tp[1]); - $pos = vmul(xyz,point3d(scale,1)); + $pos = v_mul(xyz,point3d(scale,1)); $theta = tp[0]; $phi = tp[1]; $rad = r; diff --git a/edges.scad b/edges.scad index 43d58c5..0ddc880 100644 --- a/edges.scad +++ b/edges.scad @@ -139,7 +139,7 @@ function _edge_set(v) = str(v, " must be a vector, edge array, or one of ", valid_values) ) v ) : - let(nonz = sum(vabs(v))) + let(nonz = sum(v_abs(v))) nonz==2? (v==v2) : // Edge: return matching edge. let( matches = count_true([ diff --git a/gears.scad b/gears.scad index 920b42e..8a35a42 100644 --- a/gears.scad +++ b/gears.scad @@ -928,7 +928,7 @@ function bevel_gear( radcp = [0, midpr] + polar_to_xy(cutter_radius, 180+spiral_angle), angC1 = law_of_cosines(a=cutter_radius, b=norm(radcp), c=ocone_rad), angC2 = law_of_cosines(a=cutter_radius, b=norm(radcp), c=icone_rad), - radcpang = vang(radcp), + radcpang = v_theta(radcp), sang = radcpang - (180-angC1), eang = radcpang - (180-angC2), profile = gear_tooth_profile( @@ -944,7 +944,7 @@ function bevel_gear( verts1 = [ for (v = lerpn(0,1,slices+1)) let( p = radcp + polar_to_xy(cutter_radius, lerp(sang,eang,v)), - ang = vang(p)-90, + ang = v_theta(p)-90, dist = norm(p) ) [ let( diff --git a/math.scad b/math.scad index 346b09e..96bd475 100644 --- a/math.scad +++ b/math.scad @@ -237,7 +237,7 @@ function u_sub(a,b) = is_undef(a) || is_undef(b)? undef : a - b; // b = Second value. function u_mul(a,b) = is_undef(a) || is_undef(b)? undef : - is_vector(a) && is_vector(b)? vmul(a,b) : + is_vector(a) && is_vector(b)? v_mul(a,b) : a * b; @@ -252,7 +252,7 @@ function u_mul(a,b) = // b = Second value. function u_div(a,b) = is_undef(a) || is_undef(b)? undef : - is_vector(a) && is_vector(b)? vdiv(a,b) : + is_vector(a) && is_vector(b)? v_div(a,b) : a / b; @@ -674,7 +674,7 @@ function _product(v, i=0, _tot) = i>=len(v) ? _tot : _product( v, i+1, - ( is_vector(v[i])? vmul(_tot,v[i]) : _tot*v[i] ) ); + ( is_vector(v[i])? v_mul(_tot,v[i]) : _tot*v[i] ) ); @@ -711,7 +711,7 @@ function _cumprod_vec(v,_i=0,_acc=[]) = v, _i+1, concat( _acc, - [_i==0 ? v[_i] : vmul(_acc[len(_acc)-1],v[_i])] + [_i==0 ? v[_i] : v_mul(_acc[len(_acc)-1],v[_i])] ) ); diff --git a/mutators.scad b/mutators.scad index 1f954a8..14e4b7a 100644 --- a/mutators.scad +++ b/mutators.scad @@ -515,7 +515,7 @@ module path_extrude2d(path, caps=true) { } } for (t=triplet(path)) { - ang = vang(t[2]-t[1]) - vang(t[1]-t[0]); + ang = v_theta(t[2]-t[1]) - v_theta(t[1]-t[0]); delt = t[2] - t[1]; translate(t[1]) { minkowski() { diff --git a/partitions.scad b/partitions.scad index 2e516a0..747279f 100644 --- a/partitions.scad +++ b/partitions.scad @@ -44,7 +44,7 @@ function _partition_cutpath(l, h, cutsize, cutpath, gap) = cplen = (cutsize.x+gap) * reps, path = deduplicate(concat( [[-l/2, cutpath[0].y*cutsize.y]], - [for (i=[0:1:reps-1], pt=cutpath) vmul(pt,cutsize)+[i*(cutsize.x+gap)+gap/2-cplen/2,0]], + [for (i=[0:1:reps-1], pt=cutpath) v_mul(pt,cutsize)+[i*(cutsize.x+gap)+gap/2-cplen/2,0]], [[ l/2, cutpath[len(cutpath)-1].y*cutsize.y]] )) ) path; @@ -164,7 +164,7 @@ module partition(size=100, spread=10, cutsize=10, cutpath=undef, gap=0, spin=0) { size = is_vector(size)? size : [size,size,size]; cutsize = is_vector(cutsize)? cutsize : [cutsize*2, cutsize]; - rsize = vabs(rot(spin,p=size)); + rsize = v_abs(rot(spin,p=size)); vec = rot(spin,p=BACK)*spread/2; move(vec) { intersection() { diff --git a/polyhedra.scad b/polyhedra.scad index 0914a60..be5f62a 100644 --- a/polyhedra.scad +++ b/polyhedra.scad @@ -377,7 +377,7 @@ function _point_ref(points, sign="both") = unique([ for(i=[-1,1],j=[-1,1],k=[-1,1]) if (sign=="both" || sign=="even" && i*j*k>0 || sign=="odd" && i*j*k<0) - each [for(point=points) vmul(point,[i,j,k])] + each [for(point=points) v_mul(point,[i,j,k])] ]); // _tribonacci=(1+4*cosh(acosh(2+3/8)/3))/3; diff --git a/primitives.scad b/primitives.scad index de96579..c6dc1f5 100644 --- a/primitives.scad +++ b/primitives.scad @@ -139,7 +139,7 @@ function cube(size=1, center, anchor, spin=0, orient=UP) = [-1,-1, 1],[1,-1, 1],[1,1, 1],[-1,1, 1], ]/2, verts = is_num(size)? unscaled * size : - is_vector(size,3)? [for (p=unscaled) vmul(p,size)] : + is_vector(size,3)? [for (p=unscaled) v_mul(p,size)] : assert(is_num(size) || is_vector(size,3)), faces = [ [0,1,2], [0,2,3], //BOTTOM diff --git a/shapes.scad b/shapes.scad index 4557432..4628061 100644 --- a/shapes.scad +++ b/shapes.scad @@ -111,9 +111,9 @@ module cuboid( cnt = sum(e); r = first_defined([chamfer, rounding, 0]); c = [min(r,size.x/2), min(r,size.y/2), min(r,size.z/2)]; - c2 = vmul(corner,c/2); + c2 = v_mul(corner,c/2); $fn = is_finite(chamfer)? 4 : segs(r); - translate(vmul(corner, size/2-c)) { + translate(v_mul(corner, size/2-c)) { if (cnt == 0 || approx(r,0)) { translate(c2) cube(c, center=true); } else if (cnt == 1) { @@ -163,7 +163,7 @@ module cuboid( if (!is_undef(p1)) { if (!is_undef(p2)) { translate(pointlist_bounds([p1,p2])[0]) { - cuboid(size=vabs(p2-p1), chamfer=chamfer, rounding=rounding, edges=edges, trimcorners=trimcorners, anchor=ALLNEG) children(); + cuboid(size=v_abs(p2-p1), chamfer=chamfer, rounding=rounding, edges=edges, trimcorners=trimcorners, anchor=ALLNEG) children(); } } else { translate(p1) { @@ -209,7 +209,7 @@ module cuboid( for (i = [0:3], axis=[0:1]) { if (edges[axis][i]>0) { vec = EDGE_OFFSETS[axis][i]; - translate(vmul(vec/2, size+[ach,ach,-ach])) { + translate(v_mul(vec/2, size+[ach,ach,-ach])) { rotate(majrots[axis]) { cube([ach, ach, size[axis]], center=true); } @@ -222,7 +222,7 @@ module cuboid( for (za=[-1,1], ya=[-1,1], xa=[-1,1]) { ce = corner_edges(edges, [xa,ya,za]); if (ce.x + ce.y > 1) { - translate(vmul([xa,ya,za]/2, size+[ach-0.01,ach-0.01,-ach])) { + translate(v_mul([xa,ya,za]/2, size+[ach-0.01,ach-0.01,-ach])) { cube([ach+0.01,ach+0.01,ach], center=true); } } @@ -234,7 +234,7 @@ module cuboid( for (i = [0:3], axis=[0:1]) { if (edges[axis][i]>0) { vec = EDGE_OFFSETS[axis][i]; - translate(vmul(vec/2, size+[2*ach,2*ach,-2*ach])) { + translate(v_mul(vec/2, size+[2*ach,2*ach,-2*ach])) { rotate(majrots[axis]) { zrot(45) cube([ach*sqrt(2), ach*sqrt(2), size[axis]+2.1*ach], center=true); } @@ -296,7 +296,7 @@ module cuboid( for (i = [0:3], axis=[0:1]) { if (edges[axis][i]>0) { vec = EDGE_OFFSETS[axis][i]; - translate(vmul(vec/2, size+[ard,ard,-ard])) { + translate(v_mul(vec/2, size+[ard,ard,-ard])) { rotate(majrots[axis]) { cube([ard, ard, size[axis]], center=true); } @@ -309,7 +309,7 @@ module cuboid( for (za=[-1,1], ya=[-1,1], xa=[-1,1]) { ce = corner_edges(edges, [xa,ya,za]); if (ce.x + ce.y > 1) { - translate(vmul([xa,ya,za]/2, size+[ard-0.01,ard-0.01,-ard])) { + translate(v_mul([xa,ya,za]/2, size+[ard-0.01,ard-0.01,-ard])) { cube([ard+0.01,ard+0.01,ard], center=true); } } @@ -321,7 +321,7 @@ module cuboid( for (i = [0:3], axis=[0:1]) { if (edges[axis][i]>0) { vec = EDGE_OFFSETS[axis][i]; - translate(vmul(vec/2, size+[2*ard,2*ard,-2*ard])) { + translate(v_mul(vec/2, size+[2*ard,2*ard,-2*ard])) { rotate(majrots[axis]) { cyl(l=size[axis]+2.1*ard, r=ard); } @@ -521,8 +521,8 @@ function prismoid( let( corners = [[1,1],[1,-1],[-1,-1],[-1,1]] * 0.5, points = [ - for (p=corners) point3d(vmul(s2,p), +h/2) + shiftby, - for (p=corners) point3d(vmul(s1,p), -h/2) + for (p=corners) point3d(v_mul(s2,p), +h/2) + shiftby, + for (p=corners) point3d(v_mul(s1,p), -h/2) ], faces=[ [0,1,2], [0,2,3], [0,4,5], [0,5,1], diff --git a/shapes2d.scad b/shapes2d.scad index 1fce85f..b54a64f 100644 --- a/shapes2d.scad +++ b/shapes2d.scad @@ -959,7 +959,7 @@ function rect(size=1, center, rounding=0, chamfer=0, anchor, spin=0) = [-size.x/2, size.y/2], [ size.x/2, size.y/2] ] - ) rot(spin, p=move(-vmul(anchor,size/2), p=path)) : + ) rot(spin, p=move(-v_mul(anchor,size/2), p=path)) : let( chamfer = is_list(chamfer)? chamfer : [for (i=[0:3]) chamfer], rounding = is_list(rounding)? rounding : [for (i=[0:3]) rounding], @@ -978,7 +978,7 @@ function rect(size=1, center, rounding=0, chamfer=0, anchor, spin=0) = quad = quadorder[i], inset = insets[quad], cverts = quant(segs(inset),4)/4, - cp = vmul(size/2-[inset,inset], quadpos[quad]), + cp = v_mul(size/2-[inset,inset], quadpos[quad]), step = 90/cverts, angs = chamfer[quad] > 0? [0,-90]-90*[i,i] : diff --git a/tests/test_transforms.scad b/tests/test_transforms.scad index b19d50b..20ab59c 100644 --- a/tests/test_transforms.scad +++ b/tests/test_transforms.scad @@ -111,7 +111,7 @@ module test_scale() { for (val=vals) { assert_equal(scale(point2d(val)), [[val.x,0,0],[0,val.y,0],[0,0,1]]); assert_equal(scale(val), [[val.x,0,0,0],[0,val.y,0,0],[0,0,val.z,0],[0,0,0,1]]); - assert_equal(scale(val, p=[1,2,3]), vmul([1,2,3], val)); + assert_equal(scale(val, p=[1,2,3]), v_mul([1,2,3], val)); scale(val) nil(); } assert_equal(scale(3), [[3,0,0,0],[0,3,0,0],[0,0,3,0],[0,0,0,1]]); @@ -122,7 +122,7 @@ module test_scale() { assert_equal(scale([2,3], p=square(1)), square([2,3])); assert_equal(scale([2,2], cp=[0.5,0.5], p=square(1)), move([-0.5,-0.5], p=square([2,2]))); assert_equal(scale([2,3,4], p=cb), cube([2,3,4])); - assert_equal(scale([-2,-3,-4], p=cb), [[for (p=cb[0]) vmul(p,[-2,-3,-4])], [for (f=cb[1]) reverse(f)]]); + assert_equal(scale([-2,-3,-4], p=cb), [[for (p=cb[0]) v_mul(p,[-2,-3,-4])], [for (f=cb[1]) reverse(f)]]); // Verify that module at least doesn't crash. scale(-5) scale(5) nil(); } @@ -289,7 +289,7 @@ module test_rot() { for (vec2 = vecs2d) { assert_equal( rot(from=vec1, to=vec2, p=pts2d, planar=true), - apply(affine2d_zrot(vang(vec2)-vang(vec1)), pts2d), + apply(affine2d_zrot(v_theta(vec2)-v_theta(vec1)), pts2d), info=str( "from = ", vec1, ", ", "to = ", vec2, ", ", diff --git a/tests/test_vectors.scad b/tests/test_vectors.scad index b23ccb0..f430524 100644 --- a/tests/test_vectors.scad +++ b/tests/test_vectors.scad @@ -32,66 +32,67 @@ module test_is_vector() { test_is_vector(); -module test_vfloor() { - assert_equal(vfloor([2.0, 3.14, 18.9, 7]), [2,3,18,7]); - assert_equal(vfloor([-2.0, -3.14, -18.9, -7]), [-2,-4,-19,-7]); +module test_v_floor() { + assert_equal(v_floor([2.0, 3.14, 18.9, 7]), [2,3,18,7]); + assert_equal(v_floor([-2.0, -3.14, -18.9, -7]), [-2,-4,-19,-7]); } -test_vfloor(); +test_v_floor(); -module test_vceil() { - assert_equal(vceil([2.0, 3.14, 18.9, 7]), [2,4,19,7]); - assert_equal(vceil([-2.0, -3.14, -18.9, -7]), [-2,-3,-18,-7]); +module test_v_ceil() { + assert_equal(v_ceil([2.0, 3.14, 18.9, 7]), [2,4,19,7]); + assert_equal(v_ceil([-2.0, -3.14, -18.9, -7]), [-2,-3,-18,-7]); } -test_vceil(); +test_v_ceil(); -module test_vmul() { - assert_equal(vmul([3,4,5], [8,7,6]), [24,28,30]); - assert_equal(vmul([1,2,3], [4,5,6]), [4,10,18]); - assert_equal(vmul([[1,2,3],[4,5,6],[7,8,9]], [[4,5,6],[3,2,1],[5,9,3]]), [32,28,134]); +module test_v_mul() { + assert_equal(v_mul([3,4,5], [8,7,6]), [24,28,30]); + assert_equal(v_mul([1,2,3], [4,5,6]), [4,10,18]); + assert_equal(v_mul([[1,2,3],[4,5,6],[7,8,9]], [[4,5,6],[3,2,1],[5,9,3]]), [32,28,134]); } -test_vmul(); +test_v_mul(); -module test_vdiv() { - assert(vdiv([24,28,30], [8,7,6]) == [3, 4, 5]); +module test_v_div() { + assert(v_div([24,28,30], [8,7,6]) == [3, 4, 5]); } -test_vdiv(); +test_v_div(); -module test_vabs() { - assert(vabs([2,4,8]) == [2,4,8]); - assert(vabs([-2,-4,-8]) == [2,4,8]); - assert(vabs([-2,4,8]) == [2,4,8]); - assert(vabs([2,-4,8]) == [2,4,8]); - assert(vabs([2,4,-8]) == [2,4,8]); +module test_v_abs() { + assert(v_abs([2,4,8]) == [2,4,8]); + assert(v_abs([-2,-4,-8]) == [2,4,8]); + assert(v_abs([-2,4,8]) == [2,4,8]); + assert(v_abs([2,-4,8]) == [2,4,8]); + assert(v_abs([2,4,-8]) == [2,4,8]); } -test_vabs(); +test_v_abs(); include <../strings.scad> -module test_vang() { - assert(vang([1,0])==0); - assert(vang([0,1])==90); - assert(vang([-1,0])==180); - assert(vang([0,-1])==-90); - assert(vang([1,1])==45); - assert(vang([-1,1])==135); - assert(vang([1,-1])==-45); - assert(vang([-1,-1])==-135); - assert(vang([0,0,1])==[0,90]); - assert(vang([0,1,1])==[90,45]); - assert(vang([0,1,-1])==[90,-45]); - assert(vang([1,0,0])==[0,0]); - assert(vang([0,1,0])==[90,0]); - assert(vang([0,-1,0])==[-90,0]); - assert(vang([-1,0,0])==[180,0]); - assert(vang([1,0,1])==[0,45]); - assert(vang([0,1,1])==[90,45]); - assert(vang([0,-1,1])==[-90,45]); - assert(approx(vang([1,1,1]),[45, 35.2643896828])); +module test_v_theta() { + assert_approx(v_theta([0,0]), 0); + assert_approx(v_theta([1,0]), 0); + assert_approx(v_theta([0,1]), 90); + assert_approx(v_theta([-1,0]), 180); + assert_approx(v_theta([0,-1]), -90); + assert_approx(v_theta([1,1]), 45); + assert_approx(v_theta([-1,1]), 135); + assert_approx(v_theta([1,-1]), -45); + assert_approx(v_theta([-1,-1]), -135); + assert_approx(v_theta([0,0,1]), 0); + assert_approx(v_theta([0,1,1]), 90); + assert_approx(v_theta([0,1,-1]), 90); + assert_approx(v_theta([1,0,0]), 0); + assert_approx(v_theta([0,1,0]), 90); + assert_approx(v_theta([0,-1,0]), -90); + assert_approx(v_theta([-1,0,0]), 180); + assert_approx(v_theta([1,0,1]), 0); + assert_approx(v_theta([0,1,1]), 90); + assert_approx(v_theta([0,-1,1]), -90); + assert_approx(v_theta([1,1,1]), 45); } -test_vang(); +test_v_theta(); module test_unit() { diff --git a/transforms.scad b/transforms.scad index 2b1fe39..9d4a9a9 100644 --- a/transforms.scad +++ b/transforms.scad @@ -414,8 +414,8 @@ function rot(a=0, v, cp, from, to, reverse=false, planar=false, p, _m) = assert(approx(point3d(from).z, 0), "'from' must be a 2D vector when 'planar' is true.") assert(approx(point3d(to).z, 0), "'to' must be a 2D vector when 'planar' is true.") affine2d_zrot( - vang(point2d(to)) - - vang(point2d(from)) + v_theta(to) - + v_theta(from) ), m2 = is_undef(cp)? m1 : (move(cp) * m1 * move(-cp)), m3 = reverse? matrix_inverse(m2) : m2 diff --git a/turtle3d.scad b/turtle3d.scad index 3ebad05..200c6e2 100644 --- a/turtle3d.scad +++ b/turtle3d.scad @@ -693,7 +693,7 @@ function _turtle3d_list_command(command,arcsteps,movescale, lastT,lastPre,index) assert(is_vector(grow,2), str("Parameter to \"grow\" must be a scalar or 2d vector at index ",index)) assert(is_vector(shrink,2), str("Parameter to \"shrink\" must be a scalar or 2d vector at index ",index)) let( - scaling = point3d(vdiv(grow,shrink),1), + scaling = point3d(v_div(grow,shrink),1), usersteps = struct_val(keys,"steps"), roll = struct_val(keys,"roll"), //////////////////////////////////////////////////////////////////////////////////////// diff --git a/vectors.scad b/vectors.scad index 7e38321..fcdacdf 100644 --- a/vectors.scad +++ b/vectors.scad @@ -11,7 +11,7 @@ // Function: is_vector() // Usage: -// is_vector(v, [length]); +// is_vector(v, , ...); // Description: // Returns true if v is a list of finite numbers. // Arguments: @@ -42,20 +42,17 @@ function is_vector(v, length, zero, all_nonzero=false, eps=EPSILON) = && (!all_nonzero || all_nonzero(v)) ; -// Function: vang() +// Function: v_theta() // Usage: -// theta = vang([X,Y]); -// theta_phi = vang([X,Y,Z]); +// theta = v_theta([X,Y]); // Description: -// Given a 2D vector, returns the angle in degrees counter-clockwise from X+ on the XY plane. -// Given a 3D vector, returns [THETA,PHI] where THETA is the number of degrees counter-clockwise from X+ on the XY plane, and PHI is the number of degrees up from the X+ axis along the XZ plane. -function vang(v) = +// Given a vector, returns the angle in degrees counter-clockwise from X+ on the XY plane. +function v_theta(v) = assert( is_vector(v,2) || is_vector(v,3) , "Invalid vector") - len(v)==2? atan2(v.y,v.x) : - let(res=xyz_to_spherical(v)) [res[1], 90-res[2]]; + atan2(v.y,v.x); -// Function: vmul() +// Function: v_mul() // Description: // Element-wise multiplication. Multiplies each element of `v1` by the corresponding element of `v2`. // Both `v1` and `v2` must be the same length. Returns a vector of the products. @@ -63,13 +60,13 @@ function vang(v) = // v1 = The first vector. // v2 = The second vector. // Example: -// vmul([3,4,5], [8,7,6]); // Returns [24, 28, 30] -function vmul(v1, v2) = +// v_mul([3,4,5], [8,7,6]); // Returns [24, 28, 30] +function v_mul(v1, v2) = assert( is_list(v1) && is_list(v2) && len(v1)==len(v2), "Incompatible input") [for (i = [0:1:len(v1)-1]) v1[i]*v2[i]]; -// Function: vdiv() +// Function: v_div() // Description: // Element-wise vector division. Divides each element of vector `v1` by // the corresponding element of vector `v2`. Returns a vector of the quotients. @@ -77,35 +74,35 @@ function vmul(v1, v2) = // v1 = The first vector. // v2 = The second vector. // Example: -// vdiv([24,28,30], [8,7,6]); // Returns [3, 4, 5] -function vdiv(v1, v2) = +// v_div([24,28,30], [8,7,6]); // Returns [3, 4, 5] +function v_div(v1, v2) = assert( is_vector(v1) && is_vector(v2,len(v1)), "Incompatible vectors") [for (i = [0:1:len(v1)-1]) v1[i]/v2[i]]; -// Function: vabs() +// Function: v_abs() // Description: Returns a vector of the absolute value of each element of vector `v`. // Arguments: // v = The vector to get the absolute values of. // Example: -// vabs([-1,3,-9]); // Returns: [1,3,9] -function vabs(v) = +// v_abs([-1,3,-9]); // Returns: [1,3,9] +function v_abs(v) = assert( is_vector(v), "Invalid vector" ) [for (x=v) abs(x)]; -// Function: vfloor() +// Function: v_floor() // Description: // Returns the given vector after performing a `floor()` on all items. -function vfloor(v) = +function v_floor(v) = assert( is_vector(v), "Invalid vector" ) [for (x=v) floor(x)]; -// Function: vceil() +// Function: v_ceil() // Description: // Returns the given vector after performing a `ceil()` on all items. -function vceil(v) = +function v_ceil(v) = assert( is_vector(v), "Invalid vector" ) [for (x=v) ceil(x)]; @@ -213,7 +210,7 @@ function vector_axis(v1,v2=undef,v3=undef) = w1 = point3d(v1/norm(v1)), w2 = point3d(v2/norm(v2)), w3 = (norm(w1-w2) > eps && norm(w1+w2) > eps) ? w2 - : (norm(vabs(w2)-UP) > eps)? UP + : (norm(v_abs(w2)-UP) > eps)? UP : RIGHT ) unit(cross(w1,w3)); From cf79fba26d4b05e8ab8aa97bdd187c1ca10f79b0 Mon Sep 17 00:00:00 2001 From: Garth Minette Date: Sat, 19 Jun 2021 21:24:37 -0700 Subject: [PATCH 65/72] Make labels on show_anchors() more legible. --- debug.scad | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/debug.scad b/debug.scad index 4bb8479..920eec9 100644 --- a/debug.scad +++ b/debug.scad @@ -387,13 +387,24 @@ module show_anchors(s=10, std=true, custom=true) { color("black") noop($tags="anchor-arrow") { xrot(two_d? 0 : 90) { - up(s/10) { - linear_extrude(height=0.01, convexity=12, center=true) { - text(text=anchor[0], size=s/4, halign="center", valign="center"); + back(s/3) { + yrot_copies(n=2) + up(s/30) { + linear_extrude(height=0.01, convexity=12, center=true) { + text(text=anchor[0], size=s/4, halign="center", valign="center"); + } } } } } + color([1, 1, 1, 0.4]) + noop($tags="anchor-arrow") { + xrot(two_d? 0 : 90) { + back(s/3) { + zcopies(s/21) cube([s/4.5*len(anchor[0]), s/3, 0.01], center=true); + } + } + } } } } From 34e5db205262659a297110c3d22a51429de678ad Mon Sep 17 00:00:00 2001 From: Garth Minette Date: Sat, 19 Jun 2021 21:27:17 -0700 Subject: [PATCH 66/72] Added conical ability to teardrop(). --- shapes.scad | 64 ++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 53 insertions(+), 11 deletions(-) diff --git a/shapes.scad b/shapes.scad index 4628061..899f292 100644 --- a/shapes.scad +++ b/shapes.scad @@ -1535,6 +1535,9 @@ function spheroid(r, style="aligned", d, circum=false, anchor=CENTER, spin=0, or // Usage: Typical // teardrop(h|l, r, , , ...); // teardrop(h|l, d=, , , ...); +// Usage: Psuedo-Conical +// teardrop(h|l, r1=, r2=, , , , ...); +// teardrop(h|l, d1=, d2=, , , , ...); // Usage: Attaching Children // teardrop(h|l, r, ...) ; // @@ -1544,33 +1547,72 @@ function spheroid(r, style="aligned", d, circum=false, anchor=CENTER, spin=0, or // ang = Angle of hat walls from the Z axis. Default: 45 degrees // cap_h = If given, height above center where the shape will be truncated. Default: `undef` (no truncation) // --- -// d = Diameter of circular portion of bottom. (Use instead of r) +// r1 = Radius of circular portion of the front end of the teardrop shape. +// r2 = Radius of circular portion of the back end of the teardrop shape. +// d = Diameter of circular portion of the teardrop shape. +// d1 = Diameter of circular portion of the front end of the teardrop shape. +// d2 = Diameter of circular portion of the back end of the teardrop shape. +// cap_h1 = If given, height above center where the shape will be truncated, on the front side. Default: `undef` (no truncation) +// cap_h2 = If given, height above center where the shape will be truncated, on the back side. Default: `undef` (no truncation) // anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` // spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0` // orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP` // +// Extra Anchors: +// cap = The center of the top of the cap, oriented with the cap face normal. +// cap_fwd = The front edge of the cap. +// cap_back = The back edge of the cap. +// // Example: Typical Shape // teardrop(r=30, h=10, ang=30); // Example: Crop Cap // teardrop(r=30, h=10, ang=30, cap_h=40); // Example: Close Crop // teardrop(r=30, h=10, ang=30, cap_h=20); -// Example: Standard Connectors -// teardrop(r=30, h=10, ang=30) show_anchors(); -module teardrop(h, r, ang=45, cap_h, d, l, anchor=CENTER, spin=0, orient=UP) +// Example: Psuedo-Conical +// teardrop(r1=20, r2=30, h=40, cap_h1=25, cap_h2=35); +// Example: Standard Conical Connectors +// teardrop(d1=20, d2=30, h=20, cap_h1=11, cap_h2=16) +// show_anchors(custom=false); +// Example(Spin,VPD=275): Named Conical Connectors +// teardrop(d1=20, d2=30, h=20, cap_h1=11, cap_h2=16) +// show_anchors(std=false); +module teardrop(h, r, ang=45, cap_h, r1, r2, d, d1, d2, cap_h1, cap_h2, l, anchor=CENTER, spin=0, orient=UP) { - r = get_radius(r=r, d=d, dflt=1); + r1 = get_radius(r=r, r1=r1, d=d, d1=d1, dflt=1); + r2 = get_radius(r=r, r1=r2, d=d, d1=d2, dflt=1); l = first_defined([l, h, 1]); - tip_y = adj_ang_to_hyp(r, 90-ang); - cap_h = min(default(cap_h,tip_y), tip_y); + tip_y1 = adj_ang_to_hyp(r1, 90-ang); + tip_y2 = adj_ang_to_hyp(r2, 90-ang); + cap_h1 = min(first_defined([cap_h1, cap_h, tip_y1]), tip_y1); + cap_h2 = min(first_defined([cap_h2, cap_h, tip_y2]), tip_y2); + capvec = unit([0, cap_h1-cap_h2, l]); anchors = [ - ["cap", [0,0,cap_h], UP, 0] + anchorpt("cap", [0,0,(cap_h1+cap_h2)/2], capvec), + anchorpt("cap_fwd", [0,-l/2,cap_h1], unit((capvec+FWD)/2)), + anchorpt("cap_back", [0,+l/2,cap_h2], unit((capvec+BACK)/2), 180), ]; - attachable(anchor,spin,orient, r=r, l=l, axis=BACK, anchors=anchors) { + attachable(anchor,spin,orient, r1=r1, r2=r2, l=l, axis=BACK, anchors=anchors) { + mat = geom_transform(anchor,spin,orient,$parent_geom) rot(from=UP,to=FWD) { if (l > 0) { - linear_extrude(height=l, center=true, slices=2) { - teardrop2d(r=r, ang=ang, cap_h=cap_h); + if (r1 == r2) { + linear_extrude(height=l, center=true, slices=2) { + teardrop2d(r=r1, ang=ang, cap_h=cap_h); + } + } else { + hull() { + up(l/2-0.001) { + linear_extrude(height=0.001, center=false) { + teardrop2d(r=r1, ang=ang, cap_h=cap_h1); + } + } + down(l/2) { + linear_extrude(height=0.001, center=false) { + teardrop2d(r=r2, ang=ang, cap_h=cap_h2); + } + } + } } } } From 6e7cdb932d375fd44c02e7ae670435c36465f27d Mon Sep 17 00:00:00 2001 From: Garth Minette Date: Sat, 19 Jun 2021 21:33:29 -0700 Subject: [PATCH 67/72] Cleanup of bottle threading adapters. --- bottlecaps.scad | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/bottlecaps.scad b/bottlecaps.scad index a65da94..49aa340 100644 --- a/bottlecaps.scad +++ b/bottlecaps.scad @@ -603,9 +603,9 @@ function generic_bottle_cap( ) = no_function("generic_bottle_cap"); -// Module: thread_adapter_NC() +// Module: bottle_adapter_neck_to_cap() // Usage: -// thread_adapter_NC(wall, ); +// bottle_adapter_neck_to_cap(wall, ); // Description: // Creates a threaded neck to cap adapter // Arguments: @@ -628,8 +628,8 @@ function generic_bottle_cap( // d = Distance between bottom of neck and top of cap // taper_lead_in = Length to leave straight before tapering on tube between neck and cap if exists. // Examples: -// thread_adapter_NC(); -module thread_adapter_NC( +// bottle_adapter_neck_to_cap(); +module bottle_adapter_neck_to_cap( wall, texture = "none", cap_wall = 2, @@ -708,17 +708,17 @@ module thread_adapter_NC( } } -function thread_adapter_NC( +function bottle_adapter_neck_to_cap( wall, texture, cap_wall, cap_h, cap_thread_od, tolerance, cap_neck_od, cap_neck_id, cap_thread_taper, cap_thread_pitch, neck_d, neck_id, neck_thread_od, neck_h, neck_thread_pitch, neck_support_od, d, taper_lead_in -) = no_fuction("thread_adapter_NC"); +) = no_fuction("bottle_adapter_neck_to_cap"); -// Module: thread_adapter_CC() +// Module: bottle_adapter_cap_to_cap() // Usage: -// thread_adapter_CC(wall, ); +// bottle_adapter_cap_to_cap(wall, ); // Description: // Creates a threaded cap to cap adapter. // Arguments: @@ -738,8 +738,8 @@ function thread_adapter_NC( // neck_id2 = Inner diameter of cutout in bottom cap. // taper_lead_in = Length to leave straight before tapering on tube between caps if exists. // Examples: -// thread_adapter_CC(); -module thread_adapter_CC( +// bottle_adapter_cap_to_cap(); +module bottle_adapter_cap_to_cap( wall = 2, texture = "none", cap_h1 = 11.2, @@ -822,16 +822,16 @@ module thread_adapter_CC( } } -function thread_adapter_CC( +function bottle_adapter_cap_to_cap( wall, texture, cap_h1, cap_thread_od1, tolerance, cap_neck_od1, cap_thread_pitch1, cap_h2, cap_thread_od2, cap_neck_od2, cap_thread_pitch2, d, neck_id1, neck_id2, taper_lead_in -) = no_function("thread_adapter_CC"); +) = no_function("bottle_adapter_cap_to_cap"); -// Module: thread_adapter_NN() +// Module: bottle_adapter_neck_to_neck() // Usage: -// thread_adapter_NN(); +// bottle_adapter_neck_to_neck(); // Description: // Creates a threaded neck to neck adapter. // Arguments: @@ -851,8 +851,8 @@ function thread_adapter_CC( // taper_lead_in = Length to leave straight before tapering on tube between necks if exists. // wall = Thickness of tube wall between necks. Leave undefined to match outer diameters with the neckODs/supportODs. // Examples: -// thread_adapter_NN(); -module thread_adapter_NN( +// bottle_adapter_neck_to_neck(); +module bottle_adapter_neck_to_neck( d = 0, neck_od1 = 25, neck_id1 = 21.4, @@ -939,12 +939,12 @@ module thread_adapter_NN( } } -function thread_adapter_NN( +function bottle_adapter_neck_to_neck( d, neck_od1, neck_id1, thread_od1, height1, support_od1, thread_pitch1, neck_od2, neck_id2, thread_od2, height2, support_od2, pitch2, taper_lead_in, wall -) = no_fuction("thread_adapter_NN"); +) = no_fuction("bottle_adapter_neck_to_neck"); From 9ccd21eaed3b09f165eb32d39e505e2a16cdd836 Mon Sep 17 00:00:00 2001 From: Garth Minette Date: Sat, 19 Jun 2021 21:37:31 -0700 Subject: [PATCH 68/72] Fixed erroronious like added to shapes.scad --- shapes.scad | 1 - 1 file changed, 1 deletion(-) diff --git a/shapes.scad b/shapes.scad index 899f292..388bcec 100644 --- a/shapes.scad +++ b/shapes.scad @@ -1593,7 +1593,6 @@ module teardrop(h, r, ang=45, cap_h, r1, r2, d, d1, d2, cap_h1, cap_h2, l, ancho anchorpt("cap_back", [0,+l/2,cap_h2], unit((capvec+BACK)/2), 180), ]; attachable(anchor,spin,orient, r1=r1, r2=r2, l=l, axis=BACK, anchors=anchors) { - mat = geom_transform(anchor,spin,orient,$parent_geom) rot(from=UP,to=FWD) { if (l > 0) { if (r1 == r2) { From fee3cb0d80f5d195c99520d193757a6cf092c6b8 Mon Sep 17 00:00:00 2001 From: Garth Minette Date: Sat, 19 Jun 2021 23:21:52 -0700 Subject: [PATCH 69/72] Fix for issue #429 --- paths.scad | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/paths.scad b/paths.scad index 73ed488..2c3a2b3 100644 --- a/paths.scad +++ b/paths.scad @@ -661,7 +661,7 @@ function path_self_intersections(path, closed=true, eps=EPSILON) = path = cleanup_path(path, eps=eps), plen = len(path) ) [ - for (i = [0:1:plen-(closed?2:3)], j=[i+1:1:plen-(closed?1:2)]) let( + for (i = [0:1:plen-(closed?2:3)], j=[i+2:1:plen-(closed?1:2)]) let( a1 = path[i], a2 = path[(i+1)%plen], b1 = path[j], @@ -675,15 +675,16 @@ function path_self_intersections(path, closed=true, eps=EPSILON) = c = a1-a2, d = b1-b2, denom = (c.x*d.y)-(c.y*d.x) - ) abs(denom)eps && isect[1]<=1+eps && - isect[2]>eps && isect[2]<=1+eps + isect[1]>=-eps && isect[1]<=1+eps && + isect[2]>=-eps && isect[2]<=1+eps ) [isect[0], i, isect[1], j, isect[2]] ]; From 44feeab7e68d94dcbd1dd0abe459029238d2249b Mon Sep 17 00:00:00 2001 From: Garth Minette Date: Mon, 21 Jun 2021 00:37:31 -0700 Subject: [PATCH 70/72] Bugfix for path_self_intersections() with closed paths. --- paths.scad | 1 + 1 file changed, 1 insertion(+) diff --git a/paths.scad b/paths.scad index 2c3a2b3..88d9ec7 100644 --- a/paths.scad +++ b/paths.scad @@ -682,6 +682,7 @@ function path_self_intersections(path, closed=true, eps=EPSILON) = u = ((e.x*c.y)-(e.y*c.x)) / denom ) [a1+t*(a2-a1), t, u] ) if ( + (!closed || i!=0 || j!=plen-1) && isect != undef && isect[1]>=-eps && isect[1]<=1+eps && isect[2]>=-eps && isect[2]<=1+eps From a6656e475a74bab0b580a535d01f85f9c38582b7 Mon Sep 17 00:00:00 2001 From: github-actions Date: Mon, 21 Jun 2021 07:52:42 +0000 Subject: [PATCH 71/72] Bump release version. --- version.scad | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.scad b/version.scad index 5a8e835..0de9998 100644 --- a/version.scad +++ b/version.scad @@ -6,7 +6,7 @@ ////////////////////////////////////////////////////////////////////// -BOSL_VERSION = [2,0,651]; +BOSL_VERSION = [2,0,652]; // Section: BOSL Library Version Functions From 4d09b78771e3a69aa1b7983248cd362989d41290 Mon Sep 17 00:00:00 2001 From: RonaldoCMP Date: Mon, 21 Jun 2021 19:57:28 +0100 Subject: [PATCH 72/72] minor doc correction --- geometry.scad | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/geometry.scad b/geometry.scad index e2569b9..46cff38 100644 --- a/geometry.scad +++ b/geometry.scad @@ -2257,7 +2257,7 @@ function is_convex_polygon(poly,eps=EPSILON) = // convex_distance(pts1, pts2,); // See also: // convex_collision -// Descrition: +// Description: // Returns the smallest distance between a point in convex hull of `points1` // and a point in the convex hull of `points2`. All the points in the lists // should have the same dimension, either 2D or 3D. @@ -2317,7 +2317,7 @@ function _GJK_distance(points1, points2, eps=EPSILON, lbd, d, simplex=[]) = // convex_collision(pts1, pts2,); // See also: // convex_distance -// Descrition: +// Description: // Returns `true` if the convex hull of `points1` intercepts the convex hull of `points2` // otherwise, `false`. // All the points in the lists should have the same dimension, either 2D or 3D.