Added edge_profile_asym()

This commit is contained in:
Revar Desmera 2023-06-08 02:36:00 -07:00
parent 7c2a85ef51
commit 7f13560181

View file

@ -1585,7 +1585,7 @@ module face_profile(faces=[], r, d, excess=0.01, convexity=10) {
// Synopsis: Extrudes a 2d edge profile into a mask on the given edges of the parent.
// SynTags: Geom
// Topics: Attachments, Masking
// See Also: attachable(), position(), attach(), face_profile(), corner_profile(), edge_mask(), face_mask(), corner_mask()
// See Also: attachable(), position(), attach(), face_profile(), edge_profile_asym(), corner_profile(), edge_mask(), face_mask(), corner_mask()
// Usage:
// PARENT() edge_profile([edges], [except], [convexity]) CHILDREN;
// Description:
@ -1610,19 +1610,20 @@ module face_profile(faces=[], r, d, excess=0.01, convexity=10) {
// cube([50,60,70],center=true)
// edge_profile([TOP,"Z"],except=[BACK,TOP+LEFT])
// mask2d_roundover(r=10, inset=2);
module edge_profile(edges=EDGES_ALL, except=[], excess=0.01, convexity=10) {
req_children($children);
assert($parent_geom != undef, "No object to attach to!");
check1 = assert($parent_geom != undef, "No object to attach to!");
edges = _edges(edges, except=except);
vecs = [
for (i = [0:3], axis=[0:2])
if (edges[axis][i]>0)
EDGE_OFFSETS[axis][i]
];
all_vecs_are_edges = all([for (vec = vecs) sum(v_abs(vec))==2]);
check2 = assert(all_vecs_are_edges, "All vectors must be edges.");
for ($idx = idx(vecs)) {
vec = vecs[$idx];
vcount = (vec.x?1:0) + (vec.y?1:0) + (vec.z?1:0);
dummy=assert(vcount == 2, "Not an edge vector!");
anch = _find_anchor(vec, $parent_geom);
$attach_to = undef;
$attach_anchor = anch;
@ -1646,6 +1647,292 @@ module edge_profile(edges=EDGES_ALL, except=[], excess=0.01, convexity=10) {
}
}
// Module: edge_profile_asym()
// Synopsis: Extrudes an asymmetric 2D profile into a mask on the given edges and corners of the parent.
// SynTags: Geom
// Topics: Attachments, Masking
// See Also: attachable(), position(), attach(), face_profile(), edge_profile(), corner_profile(), edge_mask(), face_mask(), corner_mask()
// Usage:
// PARENT() edge_profile([edges], [except=], [convexity=], [flip=], [corner_type=]) CHILDREN;
// Description:
// Takes an asymmetric 2D mask shape and attaches it to the selected edges and corners, with the appropriate
// orientation and extruded length to be `diff()`ed away, to give the edges and corners a matching profile.
// If no tag is set then `edge_profile_asym()` sets the tag for children to "remove" so that it will work
// with the default {{diff()}} tag. For details on specifying the edges to mask see [Specifying Edges](attachments.scad#subsection-specifying-edges).
// For a step-by-step explanation of attachments, see the [Attachments Tutorial](Tutorial-Attachments).
// Profile orientation will be made consistent for all connected edges and corners. This prohibits having three
// edges meeting at any one corner. You can intert the orientations of all edges with `flip=true`.
// Arguments:
// edges = Edges to mask. See [Specifying Edges](attachments.scad#subsection-specifying-edges). Default: All edges.
// except = Edges to explicitly NOT mask. See [Specifying Edges](attachments.scad#subsection-specifying-edges). Default: No edges.
// excess = Excess length to extrude the profile to make edge masks. Default: 0.01
// convexity = Max number of times a line could intersect the perimeter of the mask shape. Default: 10
// flip = If true, reverses the orientation of any external profile parts at each edge. Default false
// corner_type = Specifies how exterior corners should be formed. Must be one of `"none"`, `"chamfer"`, `"round"`, or `"sharp"`. Default: `"none"`
// Side Effects:
// Tags the children with "remove" (and hence sets `$tag`) if no tag is already set.
// `$idx` is set to the index number of each edge.
// `$attach_anchor` is set for each edge given, to the `[ANCHOR, POSITION, ORIENT, SPIN]` information for that anchor.
// `$profile_type` is set to `"edge"`.
// Example:
// ogee = [
// "xstep",1, "ystep",1, // Starting shoulder.
// "fillet",5, "round",5, // S-curve.
// "ystep",1, "xstep",1 // Ending shoulder.
// ];
// diff()
// cuboid(50) {
// edge_profile_asym(FRONT)
// mask2d_ogee(ogee);
// }
// Example: Flipped
// ogee = [
// "xstep",1, "ystep",1, // Starting shoulder.
// "fillet",5, "round",5, // S-curve.
// "ystep",1, "xstep",1 // Ending shoulder.
// ];
// diff()
// cuboid(50) {
// edge_profile_asym(FRONT, flip=true)
// mask2d_ogee(ogee);
// }
// Example: Negative Chamfering
// cuboid(50) {
// edge_profile_asym(FWD, flip=false)
// xflip() mask2d_chamfer(10);
// edge_profile_asym(BACK, flip=true, corner_type="sharp")
// xflip() mask2d_chamfer(10);
// }
// Example: Negative Roundings
// cuboid(50) {
// edge_profile_asym(FWD, flip=false)
// xflip() mask2d_roundover(10);
// edge_profile_asym(BACK, flip=true, corner_type="round")
// xflip() mask2d_roundover(10);
// }
// Example: Cornerless
// cuboid(50) {
// edge_profile_asym(
// "ALL", except=[TOP+FWD+RIGHT, BOT+BACK+LEFT]
// ) xflip() mask2d_roundover(10);
// }
// Example: More complicated edge sets
// cuboid(50) {
// edge_profile_asym(
// "ALL", except=[TOP+FWD+RIGHT, BOT+BACK+LEFT],
// corner_type="chamfer"
// ) xflip() mask2d_roundover(10);
// }
module edge_profile_asym(edges=EDGES_ALL, except=[], excess=0.01, convexity=10, flip=false, corner_type="none") {
function _corner_orientation(pos,pvec) =
let(
j = [for (i=[0:2]) if (pvec[i]) i][0],
T =
(pos.x>0? xflip() : ident(4)) *
(pos.y>0? yflip() : ident(4)) *
(pos.z>0? zflip() : ident(4)) *
rot(-120*(2-j), v=[1,1,1]),
) T;
function _default_edge_orientation(edge) =
edge.z < 0? [[-edge.x,-edge.y,0], UP] :
edge.z > 0? [[-edge.x,-edge.y,0], DOWN] :
edge.y < 0? [[-edge.x,0,0], BACK] :
[[-edge.x,0,0], FWD] ;
function _edge_transition_needs_flip(from,to) =
let(
flip_edges = [
[BOT+FWD, [FWD+LEFT, FWD+RIGHT]],
[BOT+BACK, [BACK+LEFT, BACK+RIGHT]],
[BOT+LEFT, []],
[BOT+RIGHT, []],
[TOP+FWD, [FWD+LEFT, FWD+RIGHT]],
[TOP+BACK, [BACK+LEFT, BACK+RIGHT]],
[TOP+LEFT, []],
[TOP+RIGHT, []],
[FWD+LEFT, [TOP+FWD, BOT+FWD]],
[FWD+RIGHT, [TOP+FWD, BOT+FWD]],
[BACK+LEFT, [TOP+BACK, BOT+BACK]],
[BACK+RIGHT, [TOP+BACK, BOT+BACK]],
],
i = search([from], flip_edges, num_returns_per_match=1)[0],
check = assert(i!=[], "Bad edge vector.")
) in_list(to,flip_edges[i][1]);
function _edge_corner_numbers(vec) =
let(
v2 = [for (i=idx(vec)) vec[i]? (vec[i]+1)/2*pow(2,i) : 0],
off = v2.x + v2.y + v2.z,
xs = [0, if (!vec.x) 1],
ys = [0, if (!vec.y) 2],
zs = [0, if (!vec.z) 4]
) [for (x=xs, y=ys, z=zs) x+y+z + off];
function _gather_contiguous_edges(edge_corners) =
let(
no_tri_corners = all([for(cn = [0:7]) len([for (ec=edge_corners) if(in_list(cn,ec[1])) 1])<3]),
check = assert(no_tri_corners, "Cannot have three edges that meet at the same corner.")
)
_gather_contiguous_edges_r(
[for (i=idx(edge_corners)) if(i) edge_corners[i]],
edge_corners[0][1],
[edge_corners[0][0]], []);
function _gather_contiguous_edges_r(edge_corners, ecns, curr, out) =
len(edge_corners)==0? [each out, curr] :
let(
i1 = [
for (i = idx(edge_corners))
if (in_list(ecns[0], edge_corners[i][1]))
i
],
i2 = [
for (i = idx(edge_corners))
if (in_list(ecns[1], edge_corners[i][1]))
i
]
) !i1 && !i2? _gather_contiguous_edges_r(
[for (i=idx(edge_corners)) if(i) edge_corners[i]],
edge_corners[0][1],
[edge_corners[0][0]],
[each out, curr]
) : let(
nu_curr = [
if (i1) edge_corners[i1[0]][0],
each curr,
if (i2) edge_corners[i2[0]][0],
],
nu_ecns = [
if (!i1) ecns[0] else [
for (ecn = edge_corners[i1[0]][1])
if (ecn != ecns[0]) ecn
][0],
if (!i2) ecns[1] else [
for (ecn = edge_corners[i2[0]][1])
if (ecn != ecns[1]) ecn
][0],
],
rem = [
for (i = idx(edge_corners))
if (i != i1[0] && i != i2[0])
edge_corners[i]
]
)
_gather_contiguous_edges_r(rem, nu_ecns, nu_curr, out);
function _edge_transition_inversions(edge_string) =
let(
// boolean cumulative sum
bcs = function(list, i=0, inv=false, out=[])
i>=len(list)? out :
let( nu_inv = list[i]? !inv : inv )
bcs(list, i+1, nu_inv, [each out, nu_inv]),
inverts = bcs([
false,
for(i = idx(edge_string)) if (i)
_edge_transition_needs_flip(
edge_string[i-1],
edge_string[i]
)
]),
boti = [for(i = idx(edge_string)) if (edge_string[i].z<0) i],
topi = [for(i = idx(edge_string)) if (edge_string[i].z>0) i],
lfti = [for(i = idx(edge_string)) if (edge_string[i].x<0) i],
rgti = [for(i = idx(edge_string)) if (edge_string[i].x>0) i],
idx = [for (m = [boti, topi, lfti, rgti]) if(m) m[0]][0],
rinverts = inverts[idx] == false? inverts : [for (x = inverts) !x]
) rinverts;
function _is_closed_edge_loop(edge_string) =
let(
e1 = edge_string[0],
e2 = last(edge_string)
)
len([for (i=[0:2]) if (abs(e1[i])==1 && e1[i]==e2[i]) 1]) == 1 &&
len([for (i=[0:2]) if (e1[i]==0 && abs(e2[i])==1) 1]) == 1 &&
len([for (i=[0:2]) if (e2[i]==0 && abs(e1[i])==1) 1]) == 1;
function _edge_pair_perp_vec(e1,e2) =
[for (i=[0:2]) if (abs(e1[i])==1 && e1[i]==e2[i]) -e1[i] else 0];
req_children($children);
check1 = assert($parent_geom != undef, "No object to attach to!")
assert(in_list(corner_type, ["none", "round", "chamfer", "sharp"]))
assert(is_bool(flip));
edges = _edges(edges, except=except);
vecs = [
for (i = [0:3], axis=[0:2])
if (edges[axis][i]>0)
EDGE_OFFSETS[axis][i]
];
all_vecs_are_edges = all([for (vec = vecs) sum(v_abs(vec))==2]);
check2 = assert(all_vecs_are_edges, "All vectors must be edges.");
edge_corners = [for (vec = vecs) [vec, _edge_corner_numbers(vec)]];
edge_strings = _gather_contiguous_edges(edge_corners);
for (edge_string = edge_strings) {
inverts = _edge_transition_inversions(edge_string);
flipverts = [for (x = inverts) flip? !x : x];
vecpairs = [
for (i = idx(edge_string))
let (p = _default_edge_orientation(edge_string[i]))
flipverts[i]? [p.y,p.x] : p
];
is_loop = _is_closed_edge_loop(edge_string);
for (i = idx(edge_string)) {
if (corner_type!="none" && (i || is_loop)) {
e1 = select(edge_string,i-1);
e2 = select(edge_string,i);
vp1 = select(vecpairs,i-1);
vp2 = select(vecpairs,i);
pvec = _edge_pair_perp_vec(e1,e2);
pos = [for (i=[0:2]) e1[i]? e1[i] : e2[i]];
if (vp1.y == vp2.y) {
default_tag("remove")
position(pos) {
mirT = _corner_orientation(pos, pvec);
multmatrix(mirT) {
if (corner_type=="chamfer") {
fn = $fn;
rotate_extrude(angle=90, $fn=4)
right_half(planar=true, $fn=fn)
children();
rotate_extrude(angle=90, $fn=4)
left_half(planar=true, $fn=fn)
children();
} else if (corner_type=="round") {
rotate_extrude(angle=90)
right_half(planar=true)
children();
rotate_extrude(angle=90)
left_half(planar=true)
children();
} else { //corner_type == "sharp"
intersection() {
rot([90,0, 0]) linear_extrude(height=100,center=true,convexity=convexity) children();
rot([90,0,90]) linear_extrude(height=100,center=true,convexity=convexity) children();
}
}
}
}
}
}
}
for (i = idx(edge_string)) {
edge_profile(edge_string[i], excess=excess, convexity=convexity) {
if (flipverts[i]) {
mirror([-1,1]) children();
} else {
children();
}
}
}
}
}
// Module: corner_profile()
// Synopsis: Rotationally extrudes a 2d edge profile into corner mask on the given corners of the parent.
// SynTags: Geom
@ -1678,15 +1965,15 @@ module edge_profile(edges=EDGES_ALL, except=[], excess=0.01, convexity=10) {
// mask2d_teardrop(r=10, angle=40);
// }
module corner_profile(corners=CORNERS_ALL, except=[], r, d, convexity=10) {
assert($parent_geom != undef, "No object to attach to!");
check1 = assert($parent_geom != undef, "No object to attach to!");
r = max(0.01, get_radius(r=r, d=d, dflt=undef));
assert(is_num(r));
check2 = assert(is_num(r), "Bad r/d argument.");
corners = _corners(corners, except=except);
vecs = [for (i = [0:7]) if (corners[i]>0) CORNER_OFFSETS[i]];
all_vecs_are_corners = all([for (vec = vecs) sum(v_abs(vec))==3]);
check3 = assert(all_vecs_are_corners, "All vectors must be corners.");
for ($idx = idx(vecs)) {
vec = vecs[$idx];
vcount = (vec.x?1:0) + (vec.y?1:0) + (vec.z?1:0);
dummy=assert(vcount == 3, "Not an edge vector!");
anch = _find_anchor(vec, $parent_geom);
$attach_to = undef;
$attach_anchor = anch;