mirror of
https://github.com/BelfrySCAD/BOSL2.git
synced 2024-12-29 16:29:40 +00:00
Added edge_profile_asym()
This commit is contained in:
parent
7c2a85ef51
commit
7f13560181
1 changed files with 295 additions and 8 deletions
303
attachments.scad
303
attachments.scad
|
@ -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.
|
// Synopsis: Extrudes a 2d edge profile into a mask on the given edges of the parent.
|
||||||
// SynTags: Geom
|
// SynTags: Geom
|
||||||
// Topics: Attachments, Masking
|
// 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:
|
// Usage:
|
||||||
// PARENT() edge_profile([edges], [except], [convexity]) CHILDREN;
|
// PARENT() edge_profile([edges], [except], [convexity]) CHILDREN;
|
||||||
// Description:
|
// Description:
|
||||||
|
@ -1610,19 +1610,20 @@ module face_profile(faces=[], r, d, excess=0.01, convexity=10) {
|
||||||
// cube([50,60,70],center=true)
|
// cube([50,60,70],center=true)
|
||||||
// edge_profile([TOP,"Z"],except=[BACK,TOP+LEFT])
|
// edge_profile([TOP,"Z"],except=[BACK,TOP+LEFT])
|
||||||
// mask2d_roundover(r=10, inset=2);
|
// mask2d_roundover(r=10, inset=2);
|
||||||
|
|
||||||
module edge_profile(edges=EDGES_ALL, except=[], excess=0.01, convexity=10) {
|
module edge_profile(edges=EDGES_ALL, except=[], excess=0.01, convexity=10) {
|
||||||
req_children($children);
|
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);
|
edges = _edges(edges, except=except);
|
||||||
vecs = [
|
vecs = [
|
||||||
for (i = [0:3], axis=[0:2])
|
for (i = [0:3], axis=[0:2])
|
||||||
if (edges[axis][i]>0)
|
if (edges[axis][i]>0)
|
||||||
EDGE_OFFSETS[axis][i]
|
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)) {
|
for ($idx = idx(vecs)) {
|
||||||
vec = vecs[$idx];
|
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);
|
anch = _find_anchor(vec, $parent_geom);
|
||||||
$attach_to = undef;
|
$attach_to = undef;
|
||||||
$attach_anchor = anch;
|
$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()
|
// Module: corner_profile()
|
||||||
// Synopsis: Rotationally extrudes a 2d edge profile into corner mask on the given corners of the parent.
|
// Synopsis: Rotationally extrudes a 2d edge profile into corner mask on the given corners of the parent.
|
||||||
// SynTags: Geom
|
// SynTags: Geom
|
||||||
|
@ -1678,15 +1965,15 @@ module edge_profile(edges=EDGES_ALL, except=[], excess=0.01, convexity=10) {
|
||||||
// mask2d_teardrop(r=10, angle=40);
|
// mask2d_teardrop(r=10, angle=40);
|
||||||
// }
|
// }
|
||||||
module corner_profile(corners=CORNERS_ALL, except=[], r, d, convexity=10) {
|
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));
|
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);
|
corners = _corners(corners, except=except);
|
||||||
vecs = [for (i = [0:7]) if (corners[i]>0) CORNER_OFFSETS[i]];
|
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)) {
|
for ($idx = idx(vecs)) {
|
||||||
vec = vecs[$idx];
|
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);
|
anch = _find_anchor(vec, $parent_geom);
|
||||||
$attach_to = undef;
|
$attach_to = undef;
|
||||||
$attach_anchor = anch;
|
$attach_anchor = anch;
|
||||||
|
|
Loading…
Reference in a new issue