From f982b96d12a3bc37e36ab91ac69cc57678bd96c7 Mon Sep 17 00:00:00 2001
From: Adrian Mariano <avm4@cornell.edu>
Date: Wed, 27 Nov 2024 20:19:48 -0500
Subject: [PATCH 1/4] check that rows is an integer for grid2d

---
 distributors.scad | 1 +
 1 file changed, 1 insertion(+)

diff --git a/distributors.scad b/distributors.scad
index 915e445..e706adc 100644
--- a/distributors.scad
+++ b/distributors.scad
@@ -654,6 +654,7 @@ module grid_copies(spacing, n, size, stagger=false, inside=undef, nonzero)
         is_vector(n)? assert(len(n)==2) n :
         size!=undef && spacing!=undef? v_floor(v_div(size,spacing))+[1,1] :
         [2,2];
+    dummy2 = assert(is_int(n[0]) && is_int(n[1]), "The number of rows/columns must be an integer");
     offset = v_mul(spacing, n-[1,1])/2;
 
     poslist = 

From 1930d5deee86c7ba4f525c3a1f1d180c6ea97c5a Mon Sep 17 00:00:00 2001
From: Adrian Mariano <avm4@cornell.edu>
Date: Wed, 27 Nov 2024 20:20:41 -0500
Subject: [PATCH 2/4] add ghosting

---
 attachments.scad | 51 +++++++++++++++++++++++++-----------------------
 color.scad       | 44 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 71 insertions(+), 24 deletions(-)

diff --git a/attachments.scad b/attachments.scad
index 89bd08c..20d0bb6 100644
--- a/attachments.scad
+++ b/attachments.scad
@@ -39,7 +39,9 @@ $edge_length = undef;
 $tags_shown = "ALL";
 $tags_hidden = [];
 
-
+$ghost_this=false;
+$ghost=false;
+$ghosting=false;    // Ghosting is in effect, so don't apply it again
 
 _ANCHOR_TYPES = ["intersect","hull"];
 
@@ -3070,7 +3072,7 @@ module attachable(
     axis=UP,override,
     geom,
     expose_tags=false, keep_color=false
-) { 
+) {
     dummy1 =
         assert($children==2, "attachable() expects exactly two children; the shape to manage, and the union of all attachment candidates.")
         assert(is_undef(anchor) || is_vector(anchor) || is_string(anchor), str("Invalid anchor: ",anchor))
@@ -3104,31 +3106,32 @@ module attachable(
         $attach_alignment=undef;
         if (expose_tags || _is_shown()){
             if (!keep_color)
-                _color($color) children(0);
+                _color($color)
+                    if (($ghost || $ghost_this) && !$ghosting)
+                        %union(){
+                           $ghosting=true;
+                           children(0);
+                        }
+                    else children(0);
             else {
                 $save_color=undef; // Force color_this() color in effect to persist for the entire object
-                children(0); 
+                    if (($ghost || $ghost_this) && !$ghosting)
+                        %union(){
+                           $ghosting=true;
+                           children(0);
+                        }
+                    else children(0);
             }
         }
-        if (is_def($save_tag) && is_def($save_color)){
-            $tag=$save_tag;
-            $save_tag=undef;
-            $color=$save_color;    // Revert to the color before color_this() call
-            $save_color=undef;
-            children(1);
-        }
-        else if (is_def($save_color)) {
-            $color=$save_color;    // Revert to the color before color_this() call
-            $save_color=undef;
-            children(1);
-        }
-        else if (is_def($save_tag)) {
-            $tag=$save_tag;
-            $save_tag=undef;
-            children(1);
-        }
-        else children(1);
-    }
+        let(
+            $ghost_this=false,
+            $tag=default($save_tag,$tag),
+            $save_tag=undef,
+            $color=default($save_color,$color),
+            $save_color=undef
+        )
+        children(1);
+   }
 }
 
 // Function: reorient()
@@ -3926,7 +3929,7 @@ function _find_anchor(anchor, geom)=
                  : oang        // face anchors point UP/BACK
         ) [anchor, final_pos, final_dir, default(override[2],spin),
            if (is_def(edgeang)) [["edge_angle",edgeang],["edge_length",edgelen], ["vec", endvecs]]]
-    ) : type == "conoid"? ( //r1, r2, l, shift
+    ) : type == "conoid"? ( //r1, r2, l, shift, axis
         let(
             rr1=geom[1],
             rr2=geom[2],
diff --git a/color.scad b/color.scad
index 3c6a51b..5582c07 100644
--- a/color.scad
+++ b/color.scad
@@ -161,6 +161,50 @@ module color_overlaps(color="red") {
     %children();
 }
 
+// Module: ghost()
+// Synopsis: Sets transparency for attachable children and their descendents.
+// SynTags: Trans
+// Topics: Attachments
+// See Also: ghost_this(), recolor(), color_this()
+// Usage:
+//   ghost([ghost]) CHILDREN;
+// Description:
+//   Sets the transparency for the attachable children and their descendents until another {{ghost()}} or {{ghost_this()}}.
+//   By default, turns transparency on.  Give the `false` parameter to disable transparency.
+//   Do not mix this with user supplied `%` operators anywhere in the geometry tree.  
+// Arguments:
+//   ghost = If true set the descendents to be transparent; if false, disable transparency.  Default: true
+// Example(3D):
+//   ghost() cuboid(10)
+//     ghost(false) cuboid(5);
+function ghost(ghost) = no_function("ghost");
+module ghost(ghost=true)
+{
+   $ghost=ghost;
+   children();
+}
+
+
+// Module: ghost_this()
+// Synopsis: Makes the children at a single level transparent. 
+// SynTags: Trans
+// Topics: Attachments
+// See Also: ghost(), recolor(), color_this()
+// Usage:
+//   ghost_this() CHILDREN;
+// Description:
+//   Makes the children transparent for one level, reverting to the previous transparency state for further descendents.  
+//   This works only with attachables and you cannot give the `%` operator anywhere in the geometry tree.  
+// Example(3D):
+//   ghost_this() cuboid(10)
+//      cuboid(5);
+function ghost_this() = no_function("ghost_this");
+module ghost_this()
+{
+   $ghost_this=true;
+   children();
+}
+
 
 // Section: Colorspace Conversion
 

From 8b90ef14d6968aa5b7367dec5906ea9556ccda95 Mon Sep 17 00:00:00 2001
From: Adrian Mariano <avm4@cornell.edu>
Date: Wed, 27 Nov 2024 20:20:56 -0500
Subject: [PATCH 3/4] doc fix for list_head

---
 lists.scad | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lists.scad b/lists.scad
index a343723..6ccb8a6 100644
--- a/lists.scad
+++ b/lists.scad
@@ -304,7 +304,7 @@ function last(list) =
 // Example:
 //   hlist1 = list_head(["foo", "bar", "baz"]);  // Returns: ["foo", "bar"]
 //   hlist2 = list_head(["foo", "bar", "baz"], -3); // Returns: ["foo"]
-//   hlist3 = list_head(["foo", "bar", "baz"], 2);  // Returns: ["foo","bar"]
+//   hlist3 = list_head(["foo", "bar", "baz"], 1);  // Returns: ["foo","bar"]
 //   hlist4 = list_head(["foo", "bar", "baz"], -5); // Returns: []
 //   hlist5 = list_head(["foo", "bar", "baz"], 5);  // Returns: ["foo","bar","baz"]
 function list_head(list, to=-2) =

From b56ab4755ee813cd5d6f0d23145b6ff2f329ad20 Mon Sep 17 00:00:00 2001
From: Adrian Mariano <avm4@cornell.edu>
Date: Wed, 27 Nov 2024 20:21:27 -0500
Subject: [PATCH 4/4] add "shear" as synonym for skew

---
 tutorials/Transforms.md | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/tutorials/Transforms.md b/tutorials/Transforms.md
index 142d385..3d1a882 100644
--- a/tutorials/Transforms.md
+++ b/tutorials/Transforms.md
@@ -219,8 +219,9 @@ color("blue",0.25) down(20) cube([40,40,0.1], center=true);
 ```
 
 
-## Skewing
-One transform that OpenSCAD does not perform natively is skewing.
+## Skewing / Shearing
+One transform that OpenSCAD does not perform natively is skewing, also
+known as shearing.  
 BOSL2 provides the `skew()` command for that.  You give it multipliers
 for the skews you want to perform.  The arguments used all start with `s`,
 followed by the axis you want to skew along, followed by the axis that