From ef7acd1c10bf3507e3fed3c046d9353e71233983 Mon Sep 17 00:00:00 2001
From: Revar Desmera <revarbat@gmail.com>
Date: Wed, 6 May 2020 17:17:45 -0700
Subject: [PATCH] Added "sideN" named anchors for Ngons.

---
 attachments.scad          | 11 +++++------
 shapes2d.scad             | 32 +++++++++++++++++++++++++++++---
 tutorials/Basic_Shapes.md | 14 +++++++-------
 version.scad              |  2 +-
 4 files changed, 42 insertions(+), 17 deletions(-)

diff --git a/attachments.scad b/attachments.scad
index 78c7807..a411128 100644
--- a/attachments.scad
+++ b/attachments.scad
@@ -328,10 +328,8 @@ function attach_transform(anchor=CENTER, spin=0, orient=UP, geom, p) =
 				pos = anch[1]
 			) two_d? (
 				assert(two_d && is_num(spin))
-				let(
-					ang = vector_angle(anch[2], BACK)
-				)
-				affine3d_zrot(ang+spin) *
+				affine3d_zrot(spin) *
+				rot(to=FWD, from=point3d(anch[2])) *
 				affine3d_translate(point3d(-pos))
 			) : (
 				assert(is_num(spin) || is_vector(spin,3))
@@ -633,7 +631,7 @@ function reorient(
 	anchors=[],
 	two_d=false,
 	p=undef
-) = let(
+) = (anchor==CENTER && spin==0 && orient==UP && p!=undef)? p : let(
 	geom = attach_geom(
 		size=size, size2=size2, shift=shift,
 		r=r, r1=r1, r2=r2, h=h,
@@ -641,7 +639,8 @@ function reorient(
 		vnf=vnf, path=path, extent=extent,
 		offset=offset, anchors=anchors,
 		two_d=two_d
-	)
+	),
+	$attach_to = undef
 ) attach_transform(anchor,spin,orient,geom,p);
 
 
diff --git a/shapes2d.scad b/shapes2d.scad
index 20ffb32..de33739 100644
--- a/shapes2d.scad
+++ b/shapes2d.scad
@@ -878,6 +878,8 @@ function oval(r, d, realign=false, circum=false, anchor=CENTER, spin=0) =
 //   realign = If false, a tip is aligned with the Y+ axis.  If true, the midpoint of a side is aligned with the Y+ axis.  Default: false
 //   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`
+// Extra Anchors:
+//   "side0", "side1", etc. = The center of each side has an anchor, pointing outwards.
 // Example(2D): by Outer Size
 //   regular_ngon(n=5, or=30);
 //   regular_ngon(n=5, od=60);
@@ -914,8 +916,17 @@ function regular_ngon(n=6, r, d, or, od, ir, id, side, rounding=0, realign=false
 				maxx_idx = max_index(subindex(path2,0)),
 				path3 = polygon_shift(path2,maxx_idx)
 			) path3
-		)
-	) reorient(anchor,spin, two_d=true, path=path, extent=false, p=path);
+		),
+		anchors = !is_string(anchor)? [] : [
+			for (i = [0:1:n-1]) let(
+				a1 = 360 - i*360/n - (realign? 180/n : 0),
+				a2 = a1 - 360/n,
+				p1 = polar_to_xy(r,a1),
+				p2 = polar_to_xy(r,a2),
+				pos = (p1+p2)/2
+			) anchorpt(str("side",i), pos, unit(pos), 0)
+		]
+	) reorient(anchor,spin, two_d=true, path=path, extent=false, p=path, anchors=anchors);
 
 
 module regular_ngon(n=6, r, d, or, od, ir, id, side, rounding=0, realign=false, anchor=CENTER, spin=0) {
@@ -923,7 +934,16 @@ module regular_ngon(n=6, r, d, or, od, ir, id, side, rounding=0, realign=false,
 	r = get_radius(r1=ir*sc, r2=or, r=r, d1=id*sc, d2=od, d=d, dflt=side/2/sin(180/n));
 	assert(!is_undef(r), "regular_ngon(): need to specify one of r, d, or, od, ir, id, side.");
 	path = regular_ngon(n=n, r=r, rounding=rounding, realign=realign);
-	attachable(anchor,spin, two_d=true, path=path, extent=false) {
+	anchors = [
+		for (i = [0:1:n-1]) let(
+			a1 = 360 - i*360/n - (realign? 180/n : 0),
+			a2 = a1 - 360/n,
+			p1 = polar_to_xy(r,a1),
+			p2 = polar_to_xy(r,a2),
+			pos = (p1+p2)/2
+		) anchorpt(str("side",i), pos, unit(pos), 0)
+	];
+	attachable(anchor,spin, two_d=true, path=path, extent=false, anchors=anchors) {
 		polygon(path);
 		children();
 	}
@@ -950,6 +970,8 @@ module regular_ngon(n=6, r, d, or, od, ir, id, side, rounding=0, realign=false,
 //   realign = If false, a tip is aligned with the Y+ axis.  If true, the midpoint of a side is aligned with the Y+ axis.  Default: false
 //   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`
+// Extra Anchors:
+//   "side0" ... "side4" = The center of each side has an anchor, pointing outwards.
 // Example(2D): by Outer Size
 //   pentagon(or=30);
 //   pentagon(od=60);
@@ -990,6 +1012,8 @@ module pentagon(r, d, or, od, ir, id, side, rounding=0, realign=false, anchor=CE
 //   realign = If false, a tip is aligned with the Y+ axis.  If true, the midpoint of a side is aligned with the Y+ axis.  Default: false
 //   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`
+// Extra Anchors:
+//   "side0" ... "side5" = The center of each side has an anchor, pointing outwards.
 // Example(2D): by Outer Size
 //   hexagon(or=30);
 //   hexagon(od=60);
@@ -1030,6 +1054,8 @@ module hexagon(r, d, or, od, ir, id, side, rounding=0, realign=false, anchor=CEN
 //   realign = If false, a tip is aligned with the Y+ axis.  If true, the midpoint of a side is aligned with the Y+ axis.  Default: false
 //   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`
+// Extra Anchors:
+//   "side0" ... "side7" = The center of each side has an anchor, pointing outwards.
 // Example(2D): by Outer Size
 //   octagon(or=30);
 //   octagon(od=60);
diff --git a/tutorials/Basic_Shapes.md b/tutorials/Basic_Shapes.md
index 426ba3a..3e46666 100644
--- a/tutorials/Basic_Shapes.md
+++ b/tutorials/Basic_Shapes.md
@@ -103,13 +103,13 @@ constants defined:
 
 Constant | Direction | Value
 -------- | --------- | -----------
-`LEFT`   | X-        | `[-1,0,0]`
-`RIGHT`  | X+        | `[1,0,0]`
-`FRONT`/`FORWARD`/`FWD` | Y- | `[0,-1,0]`
-`BACK`   | Y+        | `[0,1,0]`
-`BOTTOM`/`BOT`/`BTM`/`DOWN` | Z- | `[0,0,-1]` (3D only.)
-`TOP`/`UP` | Z+      | `[0,0,1]` (3D only.)
-`CENTER`/`CTR` | Centered | `[0,0,0]`
+`LEFT`   | X-        | `[-1, 0, 0]`
+`RIGHT`  | X+        | `[ 1, 0, 0]`
+`FRONT`/`FORWARD`/`FWD` | Y- | `[ 0,-1, 0]`
+`BACK`   | Y+        | `[ 0, 1, 0]`
+`BOTTOM`/`BOT`/`BTM`/`DOWN` | Z- | `[ 0, 0,-1]` (3D only.)
+`TOP`/`UP` | Z+      | `[ 0, 0, 1]` (3D only.)
+`CENTER`/`CTR` | Centered | `[ 0, 0, 0]`
 
 Note that even though these are 3D vectors, you can use most of them,
 (except `UP`/`DOWN`, of course) for anchors in 2D shapes:
diff --git a/version.scad b/version.scad
index b20e50e..21a5c6f 100644
--- a/version.scad
+++ b/version.scad
@@ -8,7 +8,7 @@
 //////////////////////////////////////////////////////////////////////
 
 
-BOSL_VERSION = [2,0,294];
+BOSL_VERSION = [2,0,295];
 
 
 // Section: BOSL Library Version Functions