From 7c737fd0a335630c5b72f89380f3f0a55b4d2d5e Mon Sep 17 00:00:00 2001
From: Adrian Mariano <avm4@cornell.edu>
Date: Sat, 27 Jul 2024 09:36:51 -0400
Subject: [PATCH 1/2] doc tweaks

---
 beziers.scad      | 2 ++
 distributors.scad | 8 ++++++--
 vnf.scad          | 2 +-
 3 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/beziers.scad b/beziers.scad
index 3b597e0..aa222b8 100644
--- a/beziers.scad
+++ b/beziers.scad
@@ -1444,6 +1444,8 @@ function bezier_patch_normals(patch, u, v) =
 
 
 // Function: bezier_sheet()
+// Synopsis: Creates a thin sheet from a bezier patch by extruding in normal to the patch
+// SynTags: VNF
 // Topics: Bezier Patches
 // See Also: bezier_patch_normals(), vnf_sheet()
 // Description:
diff --git a/distributors.scad b/distributors.scad
index 143dc14..8b072aa 100644
--- a/distributors.scad
+++ b/distributors.scad
@@ -852,21 +852,25 @@ function grid_copies(spacing, n, size, stagger=false, inside=undef, nonzero, p=_
 //       yrot(90) cylinder(h=20, r1=5, r2=0);
 //   color("red",0.333) yrot(90) cylinder(h=20, r1=5, r2=0);
 module rot_copies(rots=[], v, cp=[0,0,0], n, sa=0, offset=0, delta=[0,0,0], subrot=true)
-{
+{  echo("hi");
     req_children($children);  
     sang = sa + offset;
+    echo(sang=sang);
     angs = !is_undef(n)?
         (n<=0? [] : [for (i=[0:1:n-1]) i/n*360+sang]) :
         rots==[]? [] :
         assert(!is_string(rots), "Argument rots must be an angle, a list of angles, or a range of angles.")
         assert(!is_undef(rots[0]), "Argument rots must be an angle, a list of angles, or a range of angles.")
         [for (a=rots) a];
+    echo(angs=angs);
+    echo(subrot=subrot);
     for ($idx = idx(angs)) {
         $ang = angs[$idx];
         $axis = v;
         translate(cp) {
+            echo(rotang=$ang);
             rotate(a=$ang, v=v) {
-                translate(delta) {
+                translate(delta) { echo(sang=sang);
                     rot(a=(subrot? sang : $ang), v=v, reverse=true) {
                         translate(-cp) {
                             children();
diff --git a/vnf.scad b/vnf.scad
index 8a8664b..5b9106f 100644
--- a/vnf.scad
+++ b/vnf.scad
@@ -1762,7 +1762,7 @@ function vnf_small_offset(vnf, delta, merge=true) =
     [offset,faces];
 
 // Function: vnf_sheet()
-// Synopsis: Extends a VNF into a thin sheet by forming a small offset
+// Synopsis: Extends a VNF into a thin sheet by extruding normal to the VNF
 // SynTags: VNF
 // Topics: VNF Manipulation
 // See Also: vnf_small_offset(), vnf_boundary(), vnf_merge_points()

From f96e521e9bc92f54e99830ecb6d5eef46ff7ab56 Mon Sep 17 00:00:00 2001
From: Adrian Mariano <avm4@cornell.edu>
Date: Sat, 27 Jul 2024 10:04:58 -0400
Subject: [PATCH 2/2] bugfix for rot_copies: sa parameter was ignored

---
 distributors.scad | 34 +++++++++++++++++++++++-----------
 1 file changed, 23 insertions(+), 11 deletions(-)

diff --git a/distributors.scad b/distributors.scad
index 8b072aa..915e445 100644
--- a/distributors.scad
+++ b/distributors.scad
@@ -814,7 +814,7 @@ function grid_copies(spacing, n, size, stagger=false, inside=undef, nonzero, p=_
 //   n = Optional number of evenly distributed copies, rotated around the axis.
 //   sa = Starting angle, in degrees.  For use with `n`.  Angle is in degrees counter-clockwise.  Default: 0
 //   delta = [X,Y,Z] amount to move away from cp before rotating.  Makes rings of copies.  Default: `[0,0,0]`
-//   subrot = If false, don't sub-rotate children as they are copied around the ring.  Only makes sense when used with `delta`.  Default: `true`
+//   subrot = If false, don't sub-rotate children as they are copied around the ring.  Instead maintain their native orientation.  The false setting is only allowed when `delta` is given.  Default: `true`
 //   p = Either a point, pointlist, VNF or Bezier patch to be translated when used as a function.
 //
 // Side Effects:
@@ -852,26 +852,23 @@ function grid_copies(spacing, n, size, stagger=false, inside=undef, nonzero, p=_
 //       yrot(90) cylinder(h=20, r1=5, r2=0);
 //   color("red",0.333) yrot(90) cylinder(h=20, r1=5, r2=0);
 module rot_copies(rots=[], v, cp=[0,0,0], n, sa=0, offset=0, delta=[0,0,0], subrot=true)
-{  echo("hi");
+{
+    assert(subrot || norm(delta)>0, "subrot can only be false if delta is not zero");
     req_children($children);  
     sang = sa + offset;
-    echo(sang=sang);
     angs = !is_undef(n)?
         (n<=0? [] : [for (i=[0:1:n-1]) i/n*360+sang]) :
         rots==[]? [] :
         assert(!is_string(rots), "Argument rots must be an angle, a list of angles, or a range of angles.")
         assert(!is_undef(rots[0]), "Argument rots must be an angle, a list of angles, or a range of angles.")
         [for (a=rots) a];
-    echo(angs=angs);
-    echo(subrot=subrot);
     for ($idx = idx(angs)) {
         $ang = angs[$idx];
         $axis = v;
         translate(cp) {
-            echo(rotang=$ang);
             rotate(a=$ang, v=v) {
-                translate(delta) { echo(sang=sang);
-                    rot(a=(subrot? sang : $ang), v=v, reverse=true) {
+                translate(delta) { 
+                    rot(a=subrot? 0 : $ang, v=v, reverse=true) {
                         translate(-cp) {
                             children();
                         }
@@ -884,6 +881,7 @@ module rot_copies(rots=[], v, cp=[0,0,0], n, sa=0, offset=0, delta=[0,0,0], subr
 
 
 function rot_copies(rots=[], v, cp=[0,0,0], n, sa=0, offset=0, delta=[0,0,0], subrot=true, p=_NO_ARG) =
+    assert(subrot || norm(delta)>0, "subrot can only be false if delta is not zero")
     let(
         sang = sa + offset,
         angs = !is_undef(n)?
@@ -897,7 +895,7 @@ function rot_copies(rots=[], v, cp=[0,0,0], n, sa=0, offset=0, delta=[0,0,0], su
             translate(cp) *
                 rot(a=ang, v=v) *
                 translate(delta) *
-                rot(a=(subrot? sang : ang), v=v, reverse=true) *
+                rot(a=subrot? 0 : ang, v=v, reverse=true) *
                 translate(-cp)
         ]
     )
@@ -939,6 +937,7 @@ function rot_copies(rots=[], v, cp=[0,0,0], n, sa=0, offset=0, delta=[0,0,0], su
 //   sa = Starting angle, in degrees.  For use with `n`.  Angle is in degrees counter-clockwise from Y+, when facing the origin from X+.  First unrotated copy is placed at that angle.
 //   r = If given, makes a ring of child copies around the X axis, at the given radius.  Default: 0
 //   d = If given, makes a ring of child copies around the X axis, at the given diameter.
+//   subrot = If false, don't sub-rotate children as they are copied around the ring.  Instead maintain their native orientation.  The false setting is only allowed when `d` or `r` is given.  Default: `true`
 //   subrot = If false, don't sub-rotate children as they are copied around the ring.
 //   p = Either a point, pointlist, VNF or Bezier patch to be translated when used as a function.
 //
@@ -976,12 +975,16 @@ module xrot_copies(rots=[], cp=[0,0,0], n, sa=0, r, d, subrot=true)
 {
     req_children($children);  
     r = get_radius(r=r, d=d, dflt=0);
+    assert(all_nonnegative([r]), "d/r must be nonnegative");
+    assert(subrot || r>0, "subrot can only be false if d or r is given");
     rot_copies(rots=rots, v=RIGHT, cp=cp, n=n, sa=sa, delta=[0, r, 0], subrot=subrot) children();
 }
 
 
 function xrot_copies(rots=[], cp=[0,0,0], n, sa=0, r, d, subrot=true, p=_NO_ARG) =
     let( r = get_radius(r=r, d=d, dflt=0) )
+    assert(all_nonnegative([r]), "d/r must be nonnegative")
+    assert(subrot || r>0, "subrot can only be false if d or r is given")
     rot_copies(rots=rots, v=RIGHT, cp=cp, n=n, sa=sa, delta=[0, r, 0], subrot=subrot, p=p);
 
 
@@ -1020,7 +1023,7 @@ function xrot_copies(rots=[], cp=[0,0,0], n, sa=0, r, d, subrot=true, p=_NO_ARG)
 //   sa = Starting angle, in degrees.  For use with `n`.  Angle is in degrees counter-clockwise from X-, when facing the origin from Y+.
 //   r = If given, makes a ring of child copies around the Y axis, at the given radius.  Default: 0
 //   d = If given, makes a ring of child copies around the Y axis, at the given diameter.
-//   subrot = If false, don't sub-rotate children as they are copied around the ring.
+//   subrot = If false, don't sub-rotate children as they are copied around the ring.  Instead maintain their native orientation.  The false setting is only allowed when `d` or `r` is given.  Default: `true`
 //   p = Either a point, pointlist, VNF or Bezier patch to be translated when used as a function.
 //
 // Side Effects:
@@ -1057,12 +1060,16 @@ module yrot_copies(rots=[], cp=[0,0,0], n, sa=0, r, d, subrot=true)
 {
     req_children($children);
     r = get_radius(r=r, d=d, dflt=0);
+    assert(all_nonnegative([r]), "d/r must be nonnegative");
+    assert(subrot || r>0, "subrot can only be false if d or r is given");
     rot_copies(rots=rots, v=BACK, cp=cp, n=n, sa=sa, delta=[-r, 0, 0], subrot=subrot) children();
 }
 
 
 function yrot_copies(rots=[], cp=[0,0,0], n, sa=0, r, d, subrot=true, p=_NO_ARG) =
     let( r = get_radius(r=r, d=d, dflt=0) )
+    assert(all_nonnegative([r]), "d/r must be nonnegative")
+    assert(subrot || r>0, "subrot can only be false if d or r is given")
     rot_copies(rots=rots, v=BACK, cp=cp, n=n, sa=sa, delta=[-r, 0, 0], subrot=subrot, p=p);
 
 
@@ -1102,7 +1109,7 @@ function yrot_copies(rots=[], cp=[0,0,0], n, sa=0, r, d, subrot=true, p=_NO_ARG)
 //   sa = Starting angle, in degrees.  For use with `n`.  Angle is in degrees counter-clockwise from X+, when facing the origin from Z+.  Default: 0
 //   r = If given, makes a ring of child copies around the Z axis, at the given radius.  Default: 0
 //   d = If given, makes a ring of child copies around the Z axis, at the given diameter.
-//   subrot = If false, don't sub-rotate children as they are copied around the ring.  Default: true
+//   subrot = If false, don't sub-rotate children as they are copied around the ring.  Instead maintain their native orientation.  The false setting is only allowed when `d` or `r` is given.  Default: `true`
 //   p = Either a point, pointlist, VNF or Bezier patch to be translated when used as a function.
 //
 // Side Effects:
@@ -1137,13 +1144,18 @@ function yrot_copies(rots=[], cp=[0,0,0], n, sa=0, r, d, subrot=true, p=_NO_ARG)
 //   color("red",0.333) yrot(-90) cylinder(h=20, r1=5, r2=0, center=true);
 module zrot_copies(rots=[], cp=[0,0,0], n, sa=0, r, d, subrot=true)
 {
+    req_children($children);
     r = get_radius(r=r, d=d, dflt=0);
+    assert(all_nonnegative([r]), "d/r must be nonnegative");
+    assert(subrot || r>0, "subrot can only be false if d or r is given");
     rot_copies(rots=rots, v=UP, cp=cp, n=n, sa=sa, delta=[r, 0, 0], subrot=subrot) children();
 }
 
 
 function zrot_copies(rots=[], cp=[0,0,0], n, sa=0, r, d, subrot=true, p=_NO_ARG) =
     let( r = get_radius(r=r, d=d, dflt=0) )
+    assert(all_nonnegative([r]), "d/r must be nonnegative")
+    assert(subrot || r>0, "subrot can only be false if d or r is given")
     rot_copies(rots=rots, v=UP, cp=cp, n=n, sa=sa, delta=[r, 0, 0], subrot=subrot, p=p);