diff --git a/masks2d.scad b/masks2d.scad
index 81071a6..f36a5d5 100644
--- a/masks2d.scad
+++ b/masks2d.scad
@@ -20,14 +20,16 @@
 // Topics: Shapes (2D), Paths (2D), Path Generators, Attachable, Masks (2D)
 // See Also: corner_profile(), edge_profile(), face_profile(), fillet()
 // Usage: As module
-//   mask2d_roundover(r|d=, [inset], [mask_angle], [excess]) [ATTACHMENTS];
+//   mask2d_roundover(r|d=|h=|cut=|joint=, [inset], [mask_angle], [excess], [flat_top=]) [ATTACHMENTS];
 // Usage: As function
-//   path = mask2d_roundover(r|d=, [inset], [mask_angle], [excess]);
+//   path = mask2d_roundover(r|d=|h=|cut=|joint=, [inset], [mask_angle], [excess], [flat_top=]);
 // Description:
 //   Creates a 2D roundover/bead mask shape that is useful for extruding into a 3D mask for an edge.
 //   Conversely, you can use that same extruded shape to make an interior fillet between two walls.
 //   As a 2D mask, this is designed to be differenced away from the edge of a shape that is in the first (X+Y+) quadrant.
 //   If called as a function, this just returns a 2D path of the outline of the mask shape.
+//   The roundover can be specified by radius, diameter, height, cut, or joint length.
+//   ![Types of Roundovers](images/rounding/section-types-of-roundovers_fig1.png)
 // Arguments:
 //   r = Radius of the roundover.
 //   inset = Optional bead inset size.  Default: 0
@@ -35,24 +37,34 @@
 //   excess = Extra amount of mask shape to creates on the X- and Y- sides of the shape.  Default: 0.01
 //   ---
 //   d = Diameter of the roundover.
+//   h = Mask height.  Given instead of r or d when you want a consistent mask height, no matter what the mask angle.
+//   cut = Cut distance.  IE: How much of the corner to cut off.  See [Types of Roundovers](rounding.scad#section-types-of-roundovers).
+//   joint = Joint distance.  IE: How far from the edge the roundover should start.  See [Types of Roundovers](rounding.scad#section-types-of-roundovers).
+//   flat_top = If true, the top inset of the mask will be horizontal instead of angled by the mask_angle.  Default: true.
 //   anchor = Translate so anchor point is at origin (0,0,0).  See [anchor](attachments.scad#subsection-anchor).  Default: `CENTER`
 //   spin = Rotate this many degrees around the Z axis after anchor.  See [spin](attachments.scad#subsection-spin).  Default: `0`
 // Side Effects:
 //  Tags the children with "remove" (and hence sets `$tag`) if no tag is already set.
 //
-// Example(2D): 2D Roundover Mask
+// Example(2D): 2D Roundover Mask by Radius
 //   mask2d_roundover(r=10);
 // Example(2D): 2D Bead Mask
 //   mask2d_roundover(r=10,inset=2);
+// Example(2D): 2D Bead Mask by Height
+//   mask2d_roundover(h=10,inset=2);
 // Example(2D): 2D Bead Mask for a Non-Right Edge.
 //   mask2d_roundover(r=10, inset=2, mask_angle=75);
+// Example(2D): Disabling flat_top=
+//   mask2d_roundover(r=10, inset=2, flat_top=false, mask_angle=75);
+// Example(2D): 2D Angled Bead Mask by Joint Length
+//   mask2d_roundover(joint=10, inset=2, mask_angle=75);
 // Example(2D): Increasing the Excess
 //   mask2d_roundover(r=10, inset=2, mask_angle=75, excess=2);
 // Example: Masking by Edge Attachment
 //   diff()
 //   cube([50,60,70],center=true)
 //       edge_profile([TOP,"Z"],except=[BACK,TOP+LEFT])
-//           mask2d_roundover(r=10, inset=2);
+//           mask2d_roundover(h=12, inset=2);
 // Example: Making an interior fillet
 //   %render() difference() {
 //       move(-[5,0,5]) cube(30, anchor=BOT+LEFT);
@@ -61,8 +73,8 @@
 //   xrot(90)
 //       linear_extrude(height=30, center=true)
 //           mask2d_roundover(r=10);
-module mask2d_roundover(r, inset=0, mask_angle=90, excess=0.01, d, anchor=CENTER,spin=0) {
-    path = mask2d_roundover(r=r, d=d, inset=inset, mask_angle=mask_angle, excess=excess);
+module mask2d_roundover(r, inset=0, mask_angle=90, excess=0.01, flat_top=true, d, h, cut, joint, anchor=CENTER,spin=0) {
+    path = mask2d_roundover(r=r, d=d, h=h, cut=cut, joint=joint, inset=inset, flat_top=flat_top, mask_angle=mask_angle, excess=excess);
     default_tag("remove") {
         attachable(anchor,spin, two_d=true, path=path) {
             polygon(path);
@@ -71,40 +83,127 @@ module mask2d_roundover(r, inset=0, mask_angle=90, excess=0.01, d, anchor=CENTER
     }
 }
 
-function mask2d_roundover(r, inset=0, mask_angle=90, excess=0.01, d, anchor=CENTER, spin=0) =
-    assert(is_finite(r)||is_finite(d))
+function mask2d_roundover(r, inset=0, mask_angle=90, excess=0.01, flat_top=true, d, h, cut, joint, anchor=CENTER, spin=0) =
+    assert(one_defined([r,d,h,cut,joint],"r,d,h,cut,joint"))
+    assert(is_undef(r) || is_finite(r))
+    assert(is_undef(d) || is_finite(d))
+    assert(is_undef(h) || is_finite(h))
+    assert(is_undef(cut) || is_finite(cut))
+    assert(is_undef(joint) || is_finite(joint))
     assert(is_finite(excess))
     assert(is_finite(mask_angle) && mask_angle>0 && mask_angle<180)
     assert(is_finite(inset)||(is_vector(inset)&&len(inset)==2))
+    assert(is_bool(flat_top))
     let(
         inset = is_list(inset)? inset : [inset,inset],
-        r = get_radius(r=r,d=d,dflt=1),
-        avec = polar_to_xy(inset.x,mask_angle-90),
-        line1 = [[0,inset.y], [100,inset.y]],
-        line2 = [avec, polar_to_xy(100,mask_angle)+avec],
-        corner = line_intersection(line1,line2),
-        arcpts = arc(r=r, corner=[line2.y, corner, line1.y]),
-        ipath = [
-            arcpts[0] + polar_to_xy(inset.x+excess, mask_angle+90),
+        r = is_finite(joint)? adj_ang_to_opp(joint, mask_angle/2) :
+            is_finite(h)? (
+                mask_angle==90? h-inset.y :
+                mask_angle < 90 ? adj_ang_to_opp(opp_ang_to_hyp(h-inset.y,mask_angle), mask_angle/2) :
+                adj_ang_to_opp(adj_ang_to_hyp(h-inset.y,mask_angle-90), mask_angle/2)
+            ) :
+            is_finite(cut)
+              ? let(
+                    o = adj_ang_to_opp(cut, mask_angle/2),
+                    h = adj_ang_to_hyp(cut, mask_angle/2)
+                ) adj_ang_to_opp(o+h, mask_angle/2)
+              : get_radius(r=r,d=d,dflt=undef),
+        pts = _inset_isect(inset,mask_angle,flat_top,excess,-r),
+        arcpts = arc(r=r, corner=[pts[4],pts[5],pts[0]]),
+        path = [
+            each select(pts, 1, 3),
             each arcpts,
-            last(arcpts) + polar_to_xy(inset.y+excess, -90),
-            [0,-excess],
-            [-excess,-excess],
-            [-excess,0]
-        ],
-        path = deduplicate(ipath)
+        ]
     ) reorient(anchor,spin, two_d=true, path=path, extent=false, p=path);
 
 
+function _inset_isect(inset,mask_angle,flat_top,excess,r,size) =
+    assert(one_defined([size,r],"size,r"))
+    let(
+        lft_n = polar_to_xy(1, mask_angle-90),
+        rgt_n = [1,0],
+        top_n = flat_top? [1,0] : lft_n,
+        bot_n = [0,1],
+
+        line_lft = [[0,0], polar_to_xy(100, mask_angle)],
+        line_bot = [[0,0], [100,0]],
+        ex_line_lft = move(-excess*lft_n, p=line_lft),
+        ex_line_bot = move(-excess*bot_n, p=line_bot),
+        in_line_lft = move(inset.x*top_n, p=line_lft),
+        in_line_bot = move(inset.y*bot_n, p=line_bot),
+
+        ex_pt = line_intersection(ex_line_lft, ex_line_bot),
+        in_pt = line_intersection(in_line_lft, in_line_bot),
+
+        pos_r = r==undef || r >= 0,
+        r = r==undef? undef : abs(r),
+        x = is_undef(size)? r : size.x,
+        y = is_undef(size)? r : size.y,
+        base_pt = !flat_top && is_num(r)? in_pt :
+            in_pt + [y*cos(mask_angle)/sin(mask_angle), 0],
+        line_top = !flat_top && is_num(r)
+          ? let( pt = in_pt + polar_to_xy(r/(pos_r?1:tan(mask_angle/2)), mask_angle) )
+            [pt, pt - top_n]
+          : [base_pt + [0,y], base_pt + [0,y] - top_n],
+        line_rgt = !flat_top && is_num(r)
+          ? pos_r
+            ? [in_pt + [r,0], in_pt + [r,1]]
+            : [in_pt + [r/tan(mask_angle/2),0], in_pt + [r/tan(mask_angle/2),1]]
+          : [base_pt + [x,0], base_pt + [x,1]],
+        top_pt = line_intersection(ex_line_lft, line_top),
+
+        path = is_vector(size)? [
+            // All size based
+            base_pt + [size.x,0],
+            [base_pt.x + size.x, -excess],
+            ex_pt,
+            top_pt,
+            base_pt + [0,size.y],
+            base_pt,
+            base_pt + size,
+        ] : flat_top? [
+            // flat_top radius
+            base_pt + [r,0],
+            [base_pt.x + r, -excess],
+            ex_pt,
+            top_pt,
+            base_pt + [0,r],
+            base_pt,
+            base_pt + [r,r],
+        ] : let(
+            cp_pt = line_intersection(line_rgt, line_top)
+        ) pos_r? [
+            // non-flat_top radius from inside
+            in_pt + [r,0],
+            [in_pt.x + r, -excess],
+            ex_pt,
+            top_pt,
+            in_pt + polar_to_xy(r,mask_angle),
+            in_pt,
+            cp_pt,
+        ] : [
+            // non-flat_top radius from outside
+            line_rgt[0],
+            [cp_pt.x, -excess],
+            ex_pt,
+            top_pt,
+            in_pt + polar_to_xy(r/tan(mask_angle/2),mask_angle),
+            in_pt,
+            cp_pt,
+        ]
+    ) path;
+
+
+
 // Function&Module: mask2d_cove()
 // Synopsis: Creates a 2D cove (quarter-round) mask shape.
 // SynTags: Geom, Path
 // Topics: Shapes (2D), Paths (2D), Path Generators, Attachable, Masks (2D)
 // See Also: corner_profile(), edge_profile(), face_profile()
 // Usage: As module
-//   mask2d_cove(r|d=, [inset], [mask_angle], [excess]) [ATTACHMENTS];
+//   mask2d_cove(r|d=|h=, [inset], [mask_angle], [excess], [flat_top=]) [ATTACHMENTS];
 // Usage: As function
-//   path = mask2d_cove(r|d=, [inset], [mask_angle], [excess]);
+//   path = mask2d_cove(r|d=|h=, [inset], [mask_angle], [excess], [flat_top=]);
 // Description:
 //   Creates a 2D cove mask shape that is useful for extruding into a 3D mask for an edge.
 //   Conversely, you can use that same extruded shape to make an interior rounded shelf decoration between two walls.
@@ -117,23 +216,31 @@ function mask2d_roundover(r, inset=0, mask_angle=90, excess=0.01, d, anchor=CENT
 //   excess = Extra amount of mask shape to creates on the X- and Y- sides of the shape.  Default: 0.01
 //   ---
 //   d = Diameter of the cove.
+//   h = Mask height.  Given instead of r or d when you want a consistent mask height, no matter what the mask angle.
+//   flat_top = If true, the top inset of the mask will be horizontal instead of angled by the mask_angle.  Default: true.
 //   anchor = Translate so anchor point is at origin (0,0,0).  See [anchor](attachments.scad#subsection-anchor).  Default: `CENTER`
 //   spin = Rotate this many degrees around the Z axis after anchor.  See [spin](attachments.scad#subsection-spin).  Default: `0`
 // Side Effects:
 //  Tags the children with "remove" (and hence sets `$tag`) if no tag is already set.
-// Example(2D): 2D Cove Mask
+// Example(2D): 2D Cove Mask by Radius
 //   mask2d_cove(r=10);
 // Example(2D): 2D Inset Cove Mask
 //   mask2d_cove(r=10,inset=3);
+// Example(2D): 2D Inset Cove Mask by Height
+//   mask2d_cove(h=10,inset=2);
 // Example(2D): 2D Inset Cove Mask for a Non-Right Edge
 //   mask2d_cove(r=10,inset=3,mask_angle=75);
+// Example(2D): Disabling flat_top=
+//   mask2d_cove(r=10, inset=3, flat_top=false, mask_angle=75);
+// Example(2D): 2D Angled Inset Cove Mask by Joint Length
+//   mask2d_cove(joint=10, inset=3, mask_angle=75);
 // Example(2D): Increasing the Excess
 //   mask2d_cove(r=10,inset=3,mask_angle=75, excess=2);
 // Example: Masking by Edge Attachment
 //   diff()
 //   cube([50,60,70],center=true)
 //       edge_profile([TOP,"Z"],except=[BACK,TOP+LEFT])
-//           mask2d_cove(r=10, inset=2);
+//           mask2d_cove(h=10, inset=3);
 // Example: Making an interior rounded shelf
 //   %render() difference() {
 //       move(-[5,0,5]) cube(30, anchor=BOT+LEFT);
@@ -142,8 +249,8 @@ function mask2d_roundover(r, inset=0, mask_angle=90, excess=0.01, d, anchor=CENT
 //   xrot(90)
 //       linear_extrude(height=30, center=true)
 //           mask2d_cove(r=5, inset=5);
-module mask2d_cove(r, inset=0, mask_angle=90, excess=0.01, d, anchor=CENTER, spin=0) {
-    path = mask2d_cove(r=r, d=d, inset=inset, mask_angle=mask_angle, excess=excess);
+module mask2d_cove(r, inset=0, mask_angle=90, excess=0.01, flat_top=true, d, h, anchor=CENTER, spin=0) {
+    path = mask2d_cove(r=r, d=d, h=h, flat_top=flat_top, inset=inset, mask_angle=mask_angle, excess=excess);
     default_tag("remove") {
         attachable(anchor,spin, two_d=true, path=path) {
             polygon(path);
@@ -152,26 +259,27 @@ module mask2d_cove(r, inset=0, mask_angle=90, excess=0.01, d, anchor=CENTER, spi
     }
 }
 
-function mask2d_cove(r, inset=0, mask_angle=90, excess=0.01, d, anchor=CENTER, spin=0) =
-    assert(is_finite(r)||is_finite(d))
+function mask2d_cove(r, inset=0, mask_angle=90, excess=0.01, flat_top=true, d, h, anchor=CENTER, spin=0) =
+    assert(one_defined([r,d,h],"r,d,h"))
+    assert(is_undef(r) || is_finite(r))
+    assert(is_undef(d) || is_finite(d))
+    assert(is_undef(h) || is_finite(h))
     assert(is_finite(mask_angle) && mask_angle>0 && mask_angle<180)
     assert(is_finite(excess))
     assert(is_finite(inset)||(is_vector(inset)&&len(inset)==2))
+    assert(is_bool(flat_top))
     let(
         inset = is_list(inset)? inset : [inset,inset],
-        r = get_radius(r=r,d=d,dflt=1),
-        avec = polar_to_xy(inset.x,mask_angle-90),
-        line1 = [[0,inset.y], [100,inset.y]],
-        line2 = [avec, polar_to_xy(100,mask_angle)+avec],
-        corner = line_intersection(line1,line2),
-        arcpts = arc(r=r, cp=corner, start=mask_angle, angle=-mask_angle),
+        r = is_finite(h)? (
+                mask_angle==90? h-inset.y :
+                mask_angle < 90 ? adj_ang_to_opp(opp_ang_to_hyp(h-inset.y,mask_angle), mask_angle/2) :
+                adj_ang_to_opp(adj_ang_to_hyp(h-inset.y,mask_angle-90), mask_angle/2)
+            ) : get_radius(r=r,d=d,dflt=undef),
+        pts = _inset_isect(inset,mask_angle,flat_top,excess,r),
+        arcpts = arc(r=r, corner=[pts[4],pts[6],pts[0]]),
         ipath = [
-            arcpts[0] + polar_to_xy(inset.x+excess, mask_angle+90),
+            each select(pts, 1, 3),
             each arcpts,
-            last(arcpts) + polar_to_xy(inset.y+excess, -90),
-            [0,-excess],
-            [-excess,-excess],
-            [-excess,0]
         ],
         path = deduplicate(ipath)
     ) reorient(anchor,spin, two_d=true, path=path, p=path);
@@ -201,10 +309,12 @@ function mask2d_cove(r, inset=0, mask_angle=90, excess=0.01, d, anchor=CENTER, s
 //   edge = The length of the edge of the chamfer.
 //   angle = The angle of the chamfer edge, away from vertical.  Default: 45.
 //   inset = Optional amount to inset code from corner.  Default: 0
+//   mask_angle = Number of degrees in the corner angle to mask.  Default: 90
 //   excess = Extra amount of mask shape to creates on the X- and Y- sides of the shape.  Default: 0.01
 //   ---
 //   x = The width of the chamfer.
 //   y = The height of the chamfer.
+//   flat_top = If true, the top inset of the mask will be horizontal instead of angled by the mask_angle.  Default: true.
 //   anchor = Translate so anchor point is at origin (0,0,0).  See [anchor](attachments.scad#subsection-anchor).  Default: `CENTER`
 //   spin = Rotate this many degrees around the Z axis after anchor.  See [spin](attachments.scad#subsection-spin).  Default: `0`
 // Side Effects:
@@ -230,8 +340,8 @@ function mask2d_cove(r, inset=0, mask_angle=90, excess=0.01, d, anchor=CENTER, s
 //   xrot(90)
 //       linear_extrude(height=30, center=true)
 //           mask2d_chamfer(edge=10);
-module mask2d_chamfer(edge, angle=45, inset=0, excess=0.01, x, y, anchor=CENTER,spin=0) {
-    path = mask2d_chamfer(x=x, y=y, edge=edge, angle=angle, excess=excess, inset=inset);
+module mask2d_chamfer(edge, angle=45, inset=0, excess=0.01, mask_angle=90, flat_top=true, x, y, anchor=CENTER,spin=0) {
+    path = mask2d_chamfer(x=x, y=y, edge=edge, angle=angle, excess=excess, inset=inset, mask_angle=mask_angle, flat_top=flat_top);
     default_tag("remove") {
         attachable(anchor,spin, two_d=true, path=path, extent=true) {
             polygon(path);
@@ -240,10 +350,11 @@ module mask2d_chamfer(edge, angle=45, inset=0, excess=0.01, x, y, anchor=CENTER,
     }
 }
 
-function mask2d_chamfer(edge, angle=45, inset=0, excess=0.01, x, y, anchor=CENTER,spin=0) =
+function mask2d_chamfer(edge, angle=45, inset=0, excess=0.01, mask_angle=90, flat_top=true, x, y, anchor=CENTER,spin=0) =
     let(dummy=one_defined([x,y,edge],["x","y","edge"]))
     assert(is_finite(angle))
     assert(is_finite(excess))
+    assert(is_finite(mask_angle) && mask_angle>0 && mask_angle<180)
     assert(is_finite(inset)||(is_vector(inset)&&len(inset)==2))
     let(
         inset = is_list(inset)? inset : [inset,inset],
@@ -251,12 +362,10 @@ function mask2d_chamfer(edge, angle=45, inset=0, excess=0.01, x, y, anchor=CENTE
             is_def(y)? adj_ang_to_opp(adj=y,ang=angle) :
             hyp_ang_to_opp(hyp=edge,ang=angle),
         y = opp_ang_to_adj(opp=x,ang=angle),
+        pts = _inset_isect(inset,mask_angle,flat_top,excess,size=[x,y]),
         path = [
-            [x+inset.x, -excess],
-            [-excess, -excess],
-            [-excess, y+inset.y],
-            [inset.x, y+inset.y],
-            [x+inset.x, inset.y]
+            each select(pts, 1, 4),
+            pts[0],
         ]
     ) reorient(anchor,spin, two_d=true, path=path, extent=true, p=path);
 
@@ -267,9 +376,9 @@ function mask2d_chamfer(edge, angle=45, inset=0, excess=0.01, x, y, anchor=CENTE
 // Topics: Shapes (2D), Paths (2D), Path Generators, Attachable, Masks (2D)
 // See Also: corner_profile(), edge_profile(), face_profile()
 // Usage: As Module
-//   mask2d_rabbet(size, [mask_angle], [excess]) [ATTACHMENTS];
+//   mask2d_rabbet(size, [mask_angle], [excess], [flat_top=]) [ATTACHMENTS];
 // Usage: As Function
-//   path = mask2d_rabbet(size, [mask_angle], [excess]);
+//   path = mask2d_rabbet(size, [mask_angle], [excess], [flat_top=]);
 // Description:
 //   Creates a 2D rabbet mask shape that is useful for extruding into a 3D mask for an edge.
 //   Conversely, you can use that same extruded shape to make an interior shelf decoration between two walls.
@@ -277,9 +386,11 @@ function mask2d_chamfer(edge, angle=45, inset=0, excess=0.01, x, y, anchor=CENTE
 //   If called as a function, this just returns a 2D path of the outline of the mask shape.
 // Arguments:
 //   size = The size of the rabbet, either as a scalar or an [X,Y] list.
+//   inset = Optional bead inset size.  Default: 0
 //   mask_angle = Number of degrees in the corner angle to mask.  Default: 90
 //   excess = Extra amount of mask shape to creates on the X- and Y- sides of the shape. Default: 0.01
 //   ---
+//   flat_top = If true, the top inset of the mask will be horizontal instead of angled by the mask_angle.  Default: true.
 //   anchor = Translate so anchor point is at origin (0,0,0).  See [anchor](attachments.scad#subsection-anchor).  Default: `CENTER`
 //   spin = Rotate this many degrees around the Z axis after anchor.  See [spin](attachments.scad#subsection-spin).  Default: `0`
 // Side Effects:
@@ -289,7 +400,9 @@ function mask2d_chamfer(edge, angle=45, inset=0, excess=0.01, x, y, anchor=CENTE
 // Example(2D): 2D Asymmetrical Rabbet Mask
 //   mask2d_rabbet(size=[5,10]);
 // Example(2D): 2D Mask for a Non-Right Edge
-//   mask2d_rabbet(size=10,mask_angle=75);
+//   mask2d_rabbet(size=10, mask_angle=75);
+// Example(2D): Disabling flat_top=
+//   mask2d_rabbet(size=10, flat_top=false, mask_angle=75);
 // Example: Masking by Edge Attachment
 //   diff()
 //   cube([50,60,70],center=true)
@@ -303,8 +416,8 @@ function mask2d_chamfer(edge, angle=45, inset=0, excess=0.01, x, y, anchor=CENTE
 //   xrot(90)
 //       linear_extrude(height=30, center=true)
 //           mask2d_rabbet(size=[5,10]);
-module mask2d_rabbet(size, mask_angle=90, excess=0.01, anchor=CTR, spin=0) {
-    path = mask2d_rabbet(size=size, mask_angle=mask_angle, excess=excess);
+module mask2d_rabbet(size, inset=[0,0], mask_angle=90, excess=0.01, flat_top=true, anchor=CTR, spin=0) {
+    path = mask2d_rabbet(size=size, inset=inset, mask_angle=mask_angle, excess=excess, flat_top=flat_top);
     default_tag("remove") {
         attachable(anchor,spin, two_d=true, path=path, extent=false) {
             polygon(path);
@@ -313,23 +426,18 @@ module mask2d_rabbet(size, mask_angle=90, excess=0.01, anchor=CTR, spin=0) {
     }
 }
 
-function mask2d_rabbet(size, mask_angle=90, excess=0.01, anchor=CTR, spin=0) =
+function mask2d_rabbet(size, inset=[0,0], mask_angle=90, excess=0.01, flat_top=true, anchor=CTR, spin=0) =
     assert(is_finite(size)||(is_vector(size)&&len(size)==2))
     assert(is_finite(mask_angle) && mask_angle>0 && mask_angle<180)
     assert(is_finite(excess))
+    assert(is_bool(flat_top))
     let(
         size = is_list(size)? size : [size,size],
-        avec = polar_to_xy(size.x,mask_angle-90),
-        line1 = [[0,size.y], [100,size.y]],
-        line2 = [avec, polar_to_xy(100,mask_angle)+avec],
-        cp = line_intersection(line1,line2),
+        pts = _inset_isect(inset,mask_angle,flat_top,excess,size=size),
         path = [
-            cp + polar_to_xy(size.x+excess, mask_angle+90),
-            cp,
-            cp + polar_to_xy(size.y+excess, -90),
-            [0,-excess],
-            [-excess,-excess],
-            [-excess,0]
+            each select(pts, 1, 4),
+            pts[6],
+            pts[0],
         ]
     ) reorient(anchor,spin, two_d=true, path=path, extent=false, p=path);
 
@@ -353,12 +461,14 @@ function mask2d_rabbet(size, mask_angle=90, excess=0.01, anchor=CTR, spin=0) =
 // Arguments:
 //   edge = The length of the edge of the dovetail.
 //   angle = The angle of the chamfer edge, away from vertical.  Default: 30.
-//   inset = Optional amount to inset code from corner.  Default: 0
 //   shelf = The extra height to add to the inside corner of the dovetail.  Default: 0
+//   inset = Optional amount to inset code from corner.  Default: 0
+//   mask_angle = Number of degrees in the corner angle to mask.  Default: 90
 //   excess = Extra amount of mask shape to creates on the X- and Y- sides of the shape.  Default: 0.01
 //   ---
 //   x = The width of the dovetail.
 //   y = The height of the dovetail.
+//   flat_top = If true, the top inset of the mask will be horizontal instead of angled by the mask_angle.  Default: true.
 //   anchor = Translate so anchor point is at origin (0,0,0).  See [anchor](attachments.scad#subsection-anchor).  Default: `CENTER`
 //   spin = Rotate this many degrees around the Z axis after anchor.  See [spin](attachments.scad#subsection-spin).  Default: `0`
 // Side Effects:
@@ -384,8 +494,8 @@ function mask2d_rabbet(size, mask_angle=90, excess=0.01, anchor=CTR, spin=0) =
 //   xrot(90)
 //       linear_extrude(height=30, center=true)
 //           mask2d_dovetail(x=10);
-module mask2d_dovetail(edge, angle=30, inset=0, shelf=0, excess=0.01, x, y, anchor=CENTER, spin=0) {
-    path = mask2d_dovetail(x=x, y=y, edge=edge, angle=angle, inset=inset, shelf=shelf, excess=excess);
+module mask2d_dovetail(edge, angle=30, shelf=0, inset=0, mask_angle=90, excess=0.01, flat_top=true, x, y, anchor=CENTER, spin=0) {
+    path = mask2d_dovetail(x=x, y=y, edge=edge, angle=angle, inset=inset, shelf=shelf, excess=excess, flat_top=flat_top, mask_angle=mask_angle);
     default_tag("remove") {
         attachable(anchor,spin, two_d=true, path=path) {
             polygon(path);
@@ -394,7 +504,7 @@ module mask2d_dovetail(edge, angle=30, inset=0, shelf=0, excess=0.01, x, y, anch
     }
 }
 
-function mask2d_dovetail(edge, angle=30, inset=0, shelf=0, excess=0.01, x, y, anchor=CENTER, spin=0) =
+function mask2d_dovetail(edge, angle=30, shelf=0, inset=0, mask_angle=90, excess=0.01, flat_top=true, x, y, anchor=CENTER, spin=0) =
     assert(num_defined([x,y,edge])==1)
     assert(is_finite(first_defined([x,y,edge])))
     assert(is_finite(angle))
@@ -406,13 +516,13 @@ function mask2d_dovetail(edge, angle=30, inset=0, shelf=0, excess=0.01, x, y, an
             !is_undef(y)? adj_ang_to_opp(adj=y,ang=angle) :
             hyp_ang_to_opp(hyp=edge,ang=angle),
         y = opp_ang_to_adj(opp=x,ang=angle),
+        pts = _inset_isect(inset,mask_angle,flat_top,excess,size=[x,y+shelf]),
         path = [
-            [inset.x,0],
-            [-excess, 0],
-            [-excess, y+inset.y+shelf],
-            inset+[x,y+shelf],
-            inset+[x,y],
-            inset
+            [max(0,pts[5].x),-excess],
+            each select(pts, 2, 4),
+            pts[6],
+            pts[6]-[0,shelf],
+            pts[5],
         ]
     ) reorient(anchor,spin, two_d=true, path=path, p=path);
 
@@ -432,13 +542,20 @@ function mask2d_dovetail(edge, angle=30, inset=0, shelf=0, excess=0.01, x, y, an
 //   As a 2D mask, this is designed to be differenced away from the edge of a shape that is in the first (X+Y+) quadrant.
 //   If called as a function, this just returns a 2D path of the outline of the mask shape.
 //   This is particularly useful to make partially rounded bottoms, that don't need support to print.
+//   The roundover can be specified by radius, diameter, height, cut, or joint length.
+//   ![Types of Roundovers](images/rounding/section-types-of-roundovers_fig1.png)
 // Arguments:
 //   r = Radius of the rounding.
 //   angle = The maximum angle from vertical.
+//   inset = Optional bead inset size.  Default: 0
 //   mask_angle = Number of degrees in the corner angle to mask.  Default: 90
 //   excess = Extra amount of mask shape to creates on the X- and Y- sides of the shape. Default: 0.01
 //   ---
 //   d = Diameter of the rounding.
+//   h = Mask height.  Given instead of r or d when you want a consistent mask height, no matter what the mask angle.
+//   cut = Cut distance.  IE: How much of the corner to cut off.  See [Types of Roundovers](rounding.scad#section-types-of-roundovers).
+//   joint = Joint distance.  IE: How far from the edge the roundover should start.  See [Types of Roundovers](rounding.scad#section-types-of-roundovers).
+//   flat_top = If true, the top inset of the mask will be horizontal instead of angled by the mask_angle.  Default: true.
 //   anchor = Translate so anchor point is at origin (0,0,0).  See [anchor](attachments.scad#subsection-anchor).  Default: `CENTER`
 //   spin = Rotate this many degrees around the Z axis after anchor.  See [spin](attachments.scad#subsection-spin).  Default: `0`
 // Side Effects:
@@ -464,34 +581,50 @@ function mask2d_dovetail(edge, angle=30, inset=0, shelf=0, excess=0.01, x, y, an
 //   xrot(90)
 //       linear_extrude(height=30, center=true)
 //           mask2d_teardrop(r=10);
-function mask2d_teardrop(r, angle=45, mask_angle=90, excess=0.01, d, anchor=CENTER, spin=0) =  
+function mask2d_teardrop(r, angle=45, inset=[0,0], mask_angle=90, excess=0.01, flat_top=true, d, h, cut, joint, anchor=CENTER, spin=0) =  
+    assert(one_defined([r,d,h,cut,joint],"r,d,h,cut,joint"))
+    assert(is_undef(r) || is_finite(r))
+    assert(is_undef(d) || is_finite(d))
+    assert(is_undef(h) || is_finite(h))
+    assert(is_undef(cut) || is_finite(cut))
+    assert(is_undef(joint) || is_finite(joint))
     assert(is_finite(angle))
     assert(angle>0 && angle<90)
     assert(is_finite(mask_angle) && mask_angle>0 && mask_angle<180)
     assert(is_finite(excess))
     let(
-        r = get_radius(r=r, d=d, dflt=1),
-        avec = polar_to_xy(r,mask_angle-90),
-        line1 = [[0,r], [100,r]],
-        line2 = [avec, polar_to_xy(100,mask_angle)+avec],
-        cp = line_intersection(line1,line2),
-        tp = cp + polar_to_xy(r,180+angle),
-        bp = [tp.x+adj_ang_to_opp(tp.y,angle), 0],
-        arcpts = arc(r=r, cp=cp, angle=[mask_angle+90,180+angle]),
-        ipath = [
-            arcpts[0] + polar_to_xy(excess, mask_angle+90),
-            each arcpts,
-            bp,
-            bp + [0,-excess],
-            [0,-excess],
-            [-excess,-excess],
-            [-excess,0]
+        r = is_finite(joint)? adj_ang_to_opp(joint, mask_angle/2) :
+            is_finite(h)? (
+                mask_angle==90? h :
+                mask_angle < 90 ? adj_ang_to_opp(opp_ang_to_hyp(h,mask_angle), mask_angle/2) :
+                adj_ang_to_opp(adj_ang_to_hyp(h,mask_angle-90), mask_angle/2)
+            ) :
+            is_finite(cut)
+              ? let(
+                    o = adj_ang_to_opp(cut, mask_angle/2),
+                    h = adj_ang_to_hyp(cut, mask_angle/2)
+                ) adj_ang_to_opp(o+h, mask_angle/2)
+              : get_radius(r=r,d=d,dflt=undef),
+        pts = _inset_isect(inset,mask_angle,flat_top,excess,-r),
+        arcpts = arc(r=r, corner=[pts[4],pts[5],pts[0]]),
+        arcpts2 = [
+            for (i = idx(arcpts))
+            if(i==0 || v_theta(arcpts[i]-arcpts[i-1]) <= angle-90)
+            arcpts[i]
         ],
-        path = deduplicate(ipath)
+        line1 = [last(arcpts2), last(arcpts2) + polar_to_xy(1, angle-90)],
+        line2 = [[0,inset.y], [100,inset.y]],
+        ipt = line_intersection(line1,line2),
+        path = [
+            [ipt.x, -excess],
+            each select(pts, 2, 3),
+            each arcpts2,
+            ipt,
+        ]
     ) reorient(anchor,spin, two_d=true, path=path, p=path);
 
-module mask2d_teardrop(r, angle=45, mask_angle=90, excess=0.01, d, anchor=CENTER, spin=0) {
-    path = mask2d_teardrop(r=r, d=d, angle=angle, mask_angle=mask_angle, excess=excess);
+module mask2d_teardrop(r, angle=45, mask_angle=90, excess=0.01, flat_top=true, d, h, cut, joint, anchor=CENTER, spin=0) {
+    path = mask2d_teardrop(r=r, d=d, h=h, cut=cut, joint=joint, angle=angle, mask_angle=mask_angle, excess=excess);
     default_tag("remove") {
         attachable(anchor,spin, two_d=true, path=path) {
             polygon(path);