mirror of
https://github.com/BelfrySCAD/BOSL2.git
synced 2024-12-29 00:09:41 +00:00
add regular_prism(), fix anchors to prismoid()
This commit is contained in:
parent
fa58f33e6d
commit
989007a4bc
3 changed files with 4204 additions and 3806 deletions
142
attachments.scad
142
attachments.scad
|
@ -909,6 +909,7 @@ module attach(parent, child, overlap, align, spin=0, norot, inset=0, shiftout=0,
|
|||
dummy2=assert(align_list==[undef] || is_def(child), "Cannot use 'align' without 'child'")
|
||||
assert(!inside || is_def(child), "Cannot use 'inside' without 'child'")
|
||||
assert(inset==0 || is_def(child), "Cannot specify 'inset' without 'child'")
|
||||
assert(inset==0 || is_def(align), "Cannot specify 'inset' without 'align'")
|
||||
assert(shiftout==0 || is_def(child), "Cannot specify 'shiftout' without 'child'");
|
||||
factor = inside?-1:1;
|
||||
$attach_to = child;
|
||||
|
@ -934,29 +935,37 @@ module attach(parent, child, overlap, align, spin=0, norot, inset=0, shiftout=0,
|
|||
$align=align;
|
||||
dummy=assert(is_undef(align) || all_zero(v_mul(anchor,align)),
|
||||
str("Invalid alignment: align value (",align,") includes component parallel to parent anchor (",anchor,")"));
|
||||
// Now compute position on the parent (including alignment but not inset) where the child will be anchored
|
||||
pos = is_undef(align) ? anchor_data[1] : _find_anchor(anchor+align, $parent_geom)[1];
|
||||
$attach_anchor = list_set(anchor_data, 1, pos); ///
|
||||
$attach_anchor = list_set(anchor_data, 1, pos); // Never used; For user informational use? Should this be set at all?
|
||||
startdir = two_d || is_undef(align)? undef
|
||||
: anchor==UP || anchor==DOWN ? BACK
|
||||
: UP - (anchor*UP)*anchor/(anchor*anchor);
|
||||
: UP - (anchor*UP)*anchor/(anchor*anchor); // Component of UP perpendicular to anchor
|
||||
enddir = is_undef(child) || child.z==0 ? UP : BACK;
|
||||
// Compute adjustment to the child anchor for position purposes. This adjustment
|
||||
// accounts for the change in the anchor needed to to alignment.
|
||||
child_adjustment = is_undef(align)? CTR
|
||||
: two_d ? rot(to=child,from=-factor*anchor,p=align)
|
||||
: apply( frame_map(x=child, z=enddir)
|
||||
*frame_map(x=-factor*anchor, z=startdir, reverse=true)
|
||||
*frame_map(x=-factor*anchor, z=startdir, reverse=true)
|
||||
*rot(v=anchor,-spin), align);
|
||||
// The $anchor_override anchor value forces an override of the *position* only for the anchor
|
||||
// used when attachable() places the child
|
||||
$anchor_override = all_zero(child_adjustment)? inside?child:undef
|
||||
: child+child_adjustment;
|
||||
reference = two_d? BACK : UP;
|
||||
// inset_dir is the direction for insetting when alignment is in effect
|
||||
inset_dir = is_undef(align) ? CTR
|
||||
: two_d ? rot(to=reference, from=anchor,p=align)
|
||||
: apply(zrot(-factor*spin)*frame_map(x=reference, z=BACK)*frame_map(x=factor*anchor, z=startdir, reverse=true),
|
||||
align);
|
||||
|
||||
spinaxis = two_d? UP : anchor_dir;
|
||||
olap = - overlap * reference - inset*inset_dir + shiftout * (inset_dir + factor*reference);
|
||||
|
||||
if (norot || (approx(anchor_dir,reference) && anchor_spin==0)) {
|
||||
translate(pos) rot(v=spinaxis,a=factor*spin) translate(olap) default_tag("remove",inside) children();
|
||||
} else {
|
||||
} else {
|
||||
translate(pos)
|
||||
rot(v=spinaxis,a=factor*spin)
|
||||
rot(anchor_spin,from=reference,to=anchor_dir){
|
||||
|
@ -2044,7 +2053,7 @@ module edge_profile(edges=EDGES_ALL, except=[], excess=0.01, convexity=10) {
|
|||
// 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;
|
||||
// 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.
|
||||
|
@ -2064,6 +2073,7 @@ module edge_profile(edges=EDGES_ALL, except=[], excess=0.01, convexity=10) {
|
|||
// 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
|
||||
|
@ -2818,9 +2828,9 @@ module attachable(
|
|||
) {
|
||||
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("Got: ",anchor))
|
||||
assert(is_undef(spin) || is_vector(spin,3) || is_num(spin), str("Got: ",spin))
|
||||
assert(is_undef(orient) || is_vector(orient,3), str("Got: ",orient));
|
||||
assert(is_undef(anchor) || is_vector(anchor) || is_string(anchor), str("Invalid anchor: ",anchor))
|
||||
assert(is_undef(spin) || is_vector(spin,3) || is_num(spin), str("Invalid spin: ",spin))
|
||||
assert(is_undef(orient) || is_vector(orient,3), str("Invalid orient: ",orient));
|
||||
anchor = first_defined([anchor, CENTER]);
|
||||
spin = default(spin, 0);
|
||||
orient = is_def($anchor_override)? UP : default(orient, UP);
|
||||
|
@ -3031,12 +3041,6 @@ function named_anchor(name, pos, orient, spin, rot, flip) =
|
|||
[name, pos, dir, spin];
|
||||
|
||||
|
||||
function _force_rot(T) =
|
||||
[for(i=[0:3])
|
||||
[for(j=[0:3]) j<3 ? T[i][j] :
|
||||
i==3 ? 1
|
||||
: 0]];
|
||||
|
||||
// Function: attach_geom()
|
||||
// Synopsis: Returns the internal geometry description of an attachable object.
|
||||
// Topics: Attachments
|
||||
|
@ -3157,11 +3161,6 @@ function _force_rot(T) =
|
|||
// geom = attach_geom(region=region, l=length, extent=false);
|
||||
//
|
||||
|
||||
function _local_struct_val(struct, key)=
|
||||
assert(is_def(key),"key is missing")
|
||||
let(ind = search([key],struct)[0])
|
||||
ind == [] ? undef : struct[ind][1];
|
||||
|
||||
|
||||
function attach_geom(
|
||||
size, size2,
|
||||
|
@ -3460,17 +3459,19 @@ function _attach_geom_edge_path(geom, edge) =
|
|||
/// p = If given as a VNF, path, or point, applies the affine3d transformation matrix to it and returns the result.
|
||||
|
||||
function _attach_transform(anchor, spin, orient, geom, p) =
|
||||
assert(is_undef(anchor) || is_vector(anchor) || is_string(anchor), str("Got: ",anchor))
|
||||
assert(is_undef(spin) || is_vector(spin,3) || is_num(spin), str("Got: ",spin))
|
||||
assert(is_undef(orient) || is_vector(orient,3), str("Got: ",orient))
|
||||
assert(is_undef(anchor) || is_vector(anchor) || is_string(anchor), str("Invalid anchor: ",anchor))
|
||||
assert(is_undef(spin) || is_vector(spin,3) || is_num(spin), str("Invalid spin: ",spin))
|
||||
assert(is_undef(orient) || is_vector(orient,3), str("Invalid orient: ",orient))
|
||||
let(
|
||||
anchor = default(anchor, CENTER),
|
||||
spin = default(spin, 0),
|
||||
orient = default(orient, UP),
|
||||
two_d = _attach_geom_2d(geom),
|
||||
m = ($attach_to != undef)? (
|
||||
let(
|
||||
m = ($attach_to != undef)? ( // $attach_to is the attachment point on this object
|
||||
let( // which will attach to the parent
|
||||
anch = _find_anchor($attach_to, geom),
|
||||
// if $anchor_override is set it defines the object position anchor (but note not direction or spin).
|
||||
// Otherwise we use the provided anchor for the object.
|
||||
pos = is_undef($anchor_override) ? anch[1]
|
||||
: _find_anchor(_make_anchor_legal($anchor_override,geom),geom)[1]
|
||||
)
|
||||
|
@ -3480,9 +3481,8 @@ function _attach_transform(anchor, spin, orient, geom, p) =
|
|||
rot(to=FWD, from=point3d(anch[2]))
|
||||
* affine3d_translate(point3d(-pos))
|
||||
:
|
||||
assert(is_num(spin) || is_vector(spin,3))
|
||||
let(
|
||||
ang = vector_angle(anch[2], DOWN),
|
||||
ang = vector_angle(anch[2], DOWN), // anch[2] is the anchor direction vector
|
||||
axis = vector_axis(anch[2], DOWN),
|
||||
ang2 = (anch[2]==UP || anch[2]==DOWN)? 0 : 180-anch[3],
|
||||
axis2 = rot(p=axis,[0,0,ang2])
|
||||
|
@ -3505,7 +3505,7 @@ function _attach_transform(anchor, spin, orient, geom, p) =
|
|||
:
|
||||
assert(is_num(spin) || is_vector(spin,3))
|
||||
let(
|
||||
axis = vector_axis(UP,orient),
|
||||
axis = vector_axis(UP,orient), // Returns BACK if orient is UP
|
||||
ang = vector_angle(UP,orient)
|
||||
)
|
||||
affine3d_rot_by_axis(axis,ang)
|
||||
|
@ -3557,12 +3557,6 @@ function _get_cp(geom) =
|
|||
|
||||
|
||||
|
||||
function _force_anchor_2d(anchor) =
|
||||
is_undef(anchor) || len(anchor)==2 ? anchor :
|
||||
assert(anchor.y==0 || anchor.z==0, "Anchor for a 2D shape cannot be fully 3D. It must have either Y or Z component equal to zero.")
|
||||
anchor.y==0 ? [anchor.x,anchor.z] : point2d(anchor);
|
||||
|
||||
|
||||
/// Internal Function: _find_anchor()
|
||||
/// Usage:
|
||||
/// anchorinfo = _find_anchor(anchor, geom);
|
||||
|
@ -3618,28 +3612,37 @@ function _find_anchor(anchor, geom) =
|
|||
axy = point2d(anch),
|
||||
bot = point3d(v_mul(point2d(size )/2, axy), -h/2),
|
||||
top = point3d(v_mul(point2d(size2)/2, axy) + shift, h/2),
|
||||
edge = top-bot,
|
||||
pos = point3d(cp) + lerp(bot,top,u) + offset,
|
||||
vecs = anchor==CENTER? [UP]
|
||||
: [
|
||||
if (anch.x!=0) unit(rot(from=UP, to=[(top-bot).x,0,max(0.01,h)], p=[axy.x,0,0]), UP),
|
||||
if (anch.y!=0) unit(rot(from=UP, to=[0,(top-bot).y,max(0.01,h)], p=[0,axy.y,0]), UP),
|
||||
// Find vectors of the faces involved in the anchor
|
||||
facevecs =
|
||||
[
|
||||
if (anch.x!=0) unit(rot(from=UP, to=[edge.x,0,max(0.01,h)], p=[axy.x,0,0]), UP),
|
||||
if (anch.y!=0) unit(rot(from=UP, to=[0,edge.y,max(0.01,h)], p=[0,axy.y,0]), UP),
|
||||
if (anch.z!=0) unit([0,0,anch.z],UP)
|
||||
],
|
||||
vec2 = anchor==CENTER? UP
|
||||
: len(vecs)==1? unit(vecs[0],UP)
|
||||
: len(vecs)==2? vector_bisect(vecs[0],vecs[1])
|
||||
: let(
|
||||
v1 = vector_bisect(vecs[0],vecs[2]),
|
||||
v2 = vector_bisect(vecs[1],vecs[2]),
|
||||
p1 = plane_from_normal(yrot(90,p=v1)),
|
||||
p2 = plane_from_normal(xrot(-90,p=v2)),
|
||||
line = plane_intersection(p1,p2),
|
||||
v3 = unit(line[1]-line[0],UP) * anch.z
|
||||
)
|
||||
unit(v3,UP),
|
||||
vec = default(override[1],rot(from=UP, to=axis, p=vec2)),
|
||||
pos2 = default(override[0],rot(from=UP, to=axis, p=pos))
|
||||
) [anchor, pos2, vec, default(override[2],oang)]
|
||||
dir = anchor==CENTER? UP
|
||||
: len(facevecs)==1? unit(facevecs[0],UP)
|
||||
: len(facevecs)==2? vector_bisect(facevecs[0],facevecs[1])
|
||||
: let(
|
||||
v1 = vector_bisect(facevecs[0],facevecs[2]),
|
||||
v2 = vector_bisect(facevecs[1],facevecs[2]),
|
||||
p1 = plane_from_normal(yrot(90,p=v1)),
|
||||
p2 = plane_from_normal(xrot(-90,p=v2)),
|
||||
line = plane_intersection(p1,p2),
|
||||
v3 = unit(line[1]-line[0],UP) * anch.z
|
||||
)
|
||||
unit(v3,UP),
|
||||
final_dir = default(override[1],rot(from=UP, to=axis, p=dir)),
|
||||
final_pos = default(override[0],rot(from=UP, to=axis, p=pos)),
|
||||
// If the anchor is on a face or horizontal edge we take the oang value for spin
|
||||
// If the anchor is on a vertical or sloped edge or corner we want to align the spin to point upward along the edge
|
||||
// The "native" spin direction is the rotation of UP to the anchor direction
|
||||
// The desired spin direction is the edge vector
|
||||
// The axis of rotation is the direction vector, so we need component of edge perpendicular to dir
|
||||
spin = anchor.x!=0 && anchor.y!=0 ? _compute_spin(dir, edge) //sign(anchor.x)*vector_angle(edge - (edge*dir)*dir/(dir*dir), rot(from=UP,to=dir,p=BACK))
|
||||
: oang
|
||||
) [anchor, final_pos, final_dir, default(override[2],spin)]
|
||||
) : type == "conoid"? ( //r1, r2, l, shift
|
||||
assert(anchor.z == sign(anchor.z), "The Z component of an anchor for a cylinder/cone must be -1, 0, or 1")
|
||||
let(
|
||||
|
@ -4568,4 +4571,39 @@ module _show_cube_faces(faces, size=20, toplabel,botlabel) {
|
|||
color("yellow",0.7) cuboid(size=size);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Internal utility function
|
||||
|
||||
function _force_rot(T) =
|
||||
[for(i=[0:3])
|
||||
[for(j=[0:3]) j<3 ? T[i][j] :
|
||||
i==3 ? 1
|
||||
: 0]];
|
||||
|
||||
function _local_struct_val(struct, key)=
|
||||
assert(is_def(key),"key is missing")
|
||||
let(ind = search([key],struct)[0])
|
||||
ind == [] ? undef : struct[ind][1];
|
||||
|
||||
|
||||
function _force_anchor_2d(anchor) =
|
||||
is_undef(anchor) || len(anchor)==2 ? anchor :
|
||||
assert(anchor.y==0 || anchor.z==0, "Anchor for a 2D shape cannot be fully 3D. It must have either Y or Z component equal to zero.")
|
||||
anchor.y==0 ? [anchor.x,anchor.z] : point2d(anchor);
|
||||
|
||||
// Compute spin angle based on a anchor direction and desired spin direction
|
||||
// anchor_dir assumed to be a unit vector; no assumption on spin_dir
|
||||
// Takes the component of the spin direction perpendicular to the anchor
|
||||
// direction and gives the spin angle that achieves it.
|
||||
function _compute_spin(anchor_dir, spin_dir) =
|
||||
let(
|
||||
native_dir = rot(from=UP, to=anchor_dir, p=BACK),
|
||||
spin_dir = spin_dir - (spin_dir*anchor_dir)*anchor_dir, // component of spin_dir perpendicular to anchor_dir
|
||||
angle = vector_angle(native_dir,spin_dir),
|
||||
sign = cross(native_dir,spin_dir)*anchor_dir<0 ? -1 : 1
|
||||
)
|
||||
sign*angle;
|
||||
|
||||
|
||||
// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap
|
||||
|
|
|
@ -437,7 +437,7 @@ function _partition_cutpath(l, h, cutsize, cutpath, gap) =
|
|||
// Creates a mask that you can use to difference or intersect with an object to remove half of it,
|
||||
// leaving behind a side designed to allow assembly of the sub-parts.
|
||||
// Arguments:
|
||||
// l = The length of the cut axis.
|
||||
// l = The length of the cut axis.
|
||||
// w = The width of the part to be masked, back from the cut plane.
|
||||
// h = The height of the part to be masked.
|
||||
// cutsize = The width of the cut pattern to be used.
|
||||
|
@ -487,16 +487,15 @@ module partition_mask(l=100, w=100, h=100, cutsize=10, cutpath="jigsaw", gap=0,
|
|||
// Topics: Partitions, Masking, Paths
|
||||
// See Also: partition_mask(), partition()
|
||||
// Usage:
|
||||
// partition_cut_mask(l, w, h, [cutsize], [cutpath], [gap], [inverse], [$slop=], [anchor=], [spin=], [orient=]) [ATTACHMENTS];
|
||||
// partition_cut_mask(l, [cutsize], [cutpath], [gap], [inverse], [$slop=], [anchor=], [spin=], [orient=]) [ATTACHMENTS];
|
||||
// Description:
|
||||
// Creates a mask that you can use to difference with an object to cut it into two sub-parts that can be assembled.
|
||||
// The `$slop` value is important to get the proper fit and should probably be smaller than 0.2. The examples below
|
||||
// use larger values to make the mask easier to see.
|
||||
// Arguments:
|
||||
// l = The length of the cut axis.
|
||||
// w = The width of the part to be masked, back from the cut plane.
|
||||
// h = The height of the part to be masked.
|
||||
// cutsize = The width of the cut pattern to be used.
|
||||
// cutsize = The width of the cut pattern to be used. Default: 10
|
||||
// cutpath = The cutpath to use. Standard named paths are "flat", "sawtooth", "sinewave", "comb", "finger", "dovetail", "hammerhead", and "jigsaw". Alternatively, you can give a cutpath as a 2D path, where X is between 0 and 1, and Y is between -0.5 and 0.5. Default: "jigsaw"
|
||||
// gap = Empty gaps between cutpath iterations. Default: 0
|
||||
// spin = Rotate this many degrees around the Z axis. See [spin](attachments.scad#subsection-spin). Default: `0`
|
||||
|
|
7861
shapes3d.scad
7861
shapes3d.scad
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue