Merge pull request #1607 from adrianVmariano/master

Final prism_connector fixes
This commit is contained in:
adrianVmariano
2025-03-26 12:41:36 -04:00
committed by GitHub
2 changed files with 35 additions and 28 deletions

View File

@@ -5270,6 +5270,13 @@ function transform_desc(T,desc) =
// See the last examples in {{prism_connector()}} for examples using this module. // See the last examples in {{prism_connector()}} for examples using this module.
// Arguments: // Arguments:
// transforms = list of transformation matrices to apply to the children // transforms = list of transformation matrices to apply to the children
// Side Effects:
// `$count` is set to the number of transformations
// `$idx` is set to the index number of the current transformation
// `$is_last` is set to true if this is the last copy and false otherwise
// `$next()` is set to a function literal that produces the next description (see above)
// `$prev()` is set to a function literal that produces the previous description (see above)
// `$desc()` is set to a function literal that produces the description at a specified index (see above)
module desc_copies(transforms) module desc_copies(transforms)
{ {

View File

@@ -2860,7 +2860,8 @@ Access to the derivative smoothing parameter?
// For the cylinder and spherical objects you may wish to joint a prism to the concave surface. You can do this by setting a negative // For the cylinder and spherical objects you may wish to joint a prism to the concave surface. You can do this by setting a negative
// radius for the base or auxiliary object. When `base_r` is negative, and the joiner prism axis is vertical, the prism root is **below** the // radius for the base or auxiliary object. When `base_r` is negative, and the joiner prism axis is vertical, the prism root is **below** the
// XY plane. In this case it is actually possible to use the same object for base and aux and you can get a joiner prism that crosses a cylindrical // XY plane. In this case it is actually possible to use the same object for base and aux and you can get a joiner prism that crosses a cylindrical
// or spherical hole. // or spherical hole. You can also attach to the inside of a prism object by setting the corresponding radius to a negative value. Only the sign
// matters in this case.
// . // .
// When placing prisms inside a hole, an ambiguity can arise about how to identify the root and end of the joiner prism. The prism axis has // When placing prisms inside a hole, an ambiguity can arise about how to identify the root and end of the joiner prism. The prism axis has
// two intersections with a cylinder and both are potentially valid roots. When the auxiliary object is entirely inside the hole, or the auxiliary // two intersections with a cylinder and both are potentially valid roots. When the auxiliary object is entirely inside the hole, or the auxiliary
@@ -3559,7 +3560,8 @@ function join_prism(polygon, base, base_r, base_d, base_T=IDENT,
assert(!in_list(aux,["sphere","cyl","cylinder"]) || (is_num(aux_r) && !approx(aux_r,0)), str("Must give nonzero aux_r with base ",base)) assert(!in_list(aux,["sphere","cyl","cylinder"]) || (is_num(aux_r) && !approx(aux_r,0)), str("Must give nonzero aux_r with base ",base))
assert(!short || (in_list(base,["sphere","cyl","cylinder"]) && base_r<0), "You can only set short to true if the base is a sphere or cylinder with radius<0") assert(!short || (in_list(base,["sphere","cyl","cylinder"]) && base_r<0), "You can only set short to true if the base is a sphere or cylinder with radius<0")
let( let(
base_r=default(base_r,0), base_r=default(base_r,1),
aux_r=default(aux_r,1),
polygon=clockwise_polygon(polygon), polygon=clockwise_polygon(polygon),
start_center = CENTER, start_center = CENTER,
aux_T_horiz = submatrix(aux_T,[0:2],[0:2]) == ident(3) && aux_T[2][3]==0, aux_T_horiz = submatrix(aux_T,[0:2],[0:2]) == ident(3) && aux_T[2][3]==0,
@@ -3586,7 +3588,7 @@ function join_prism(polygon, base, base_r, base_d, base_T=IDENT,
: is_path(base) ? : is_path(base) ?
let( let(
mapped = apply(yrot(-90),axisline), mapped = apply(yrot(-90),axisline),
answer = _prism_line_isect(pair(base,wrap=true),mapped,mapped[1])[0] answer = _prism_line_isect(pair(base,wrap=true),mapped,sign(base_r)*mapped[1])[0]
) )
assert(answer,"Prism center doesn't intersect prism (base)") assert(answer,"Prism center doesn't intersect prism (base)")
apply(yrot(90),answer) apply(yrot(90),answer)
@@ -3596,7 +3598,7 @@ function join_prism(polygon, base, base_r, base_d, base_T=IDENT,
prism_end_T = aux=="none" ? IDENT : prism_end_T, prism_end_T = aux=="none" ? IDENT : prism_end_T,
aux = aux=="none" && aux_fillet!=0 ? "plane" : aux, aux = aux=="none" && aux_fillet!=0 ? "plane" : aux,
end_center = apply(aux_T,CENTER), end_center = apply(aux_T,CENTER),
ndir = base_r<0 ? unit(start_center-start) : unit(end_center-start_center,UP), ndir = base_r<0 && in_list(base,["cylinder","cyl","sphere"]) ? unit(start_center-start) : unit(end_center-start_center,UP),
end_prelim = is_def(end) ? end end_prelim = is_def(end) ? end
:apply(move(start)*prism_end_T*move(-start), :apply(move(start)*prism_end_T*move(-start),
aux=="sphere" ? aux=="sphere" ?
@@ -3613,7 +3615,7 @@ function join_prism(polygon, base, base_r, base_d, base_T=IDENT,
: is_path(aux) ? : is_path(aux) ?
let( let(
mapped = apply(yrot(90),[start,start+ndir]), mapped = apply(yrot(90),[start,start+ndir]),
answer = _prism_line_isect(pair(aux,wrap=true),mapped,mapped[0]-mapped[1])[0] answer = _prism_line_isect(pair(aux,wrap=true),mapped,sign(aux_r)*(mapped[0]-mapped[1]))[0] //!!!
) )
assert(answer,"Prism center doesn't intersect prism (aux)") assert(answer,"Prism center doesn't intersect prism (aux)")
apply(aux_T*yrot(-90),answer) apply(aux_T*yrot(-90),answer)
@@ -3634,7 +3636,7 @@ function join_prism(polygon, base, base_r, base_d, base_T=IDENT,
: is_path(aux) ? : is_path(aux) ?
let( let(
mapped = apply(yrot(90)*move(-end_center),[start,end_prelim]), mapped = apply(yrot(90)*move(-end_center),[start,end_prelim]),
answer = _prism_line_isect(pair(aux,wrap=true),mapped,mapped[0]-mapped[1])[0] answer = _prism_line_isect(pair(aux,wrap=true),mapped,sign(aux_r)*(mapped[0]-mapped[1]))[0]
) )
assert(answer,"Prism center doesn't intersect prism (aux)") assert(answer,"Prism center doesn't intersect prism (aux)")
apply(move(end_center)*yrot(-90),answer) apply(move(end_center)*yrot(-90),answer)
@@ -3652,7 +3654,7 @@ function join_prism(polygon, base, base_r, base_d, base_T=IDENT,
topmesh_reversed = _prism_fillet(_name2,aux, aux_r, aux_top, aux_bot, aux_fillet, aux_k, aux_n, aux_overlap,aux_uniform,aux_smooth_normals,debug), topmesh_reversed = _prism_fillet(_name2,aux, aux_r, aux_top, aux_bot, aux_fillet, aux_k, aux_n, aux_overlap,aux_uniform,aux_smooth_normals,debug),
topmesh = apply(aux_T,[for(i=[len(topmesh_reversed)-1:-1:0]) reverse_polygon(topmesh_reversed[i])]), topmesh = apply(aux_T,[for(i=[len(topmesh_reversed)-1:-1:0]) reverse_polygon(topmesh_reversed[i])]),
round_dir = select(topmesh,-1)-botmesh[0], round_dir = select(topmesh,-1)-botmesh[0],
roundings_cross = [for(i=idx(truetop)) if (round_dir[i]*(truetop[i]-truebot[i])<0) echo(truetop[i],truebot[i],round_dir[i]) i], roundings_cross = [for(i=idx(truetop)) if (round_dir[i]*(truetop[i]-truebot[i])<0) i],
vnf = vnf_vertex_array(concat(topmesh,botmesh),col_wrap=true, caps=true, reverse=true) vnf = vnf_vertex_array(concat(topmesh,botmesh),col_wrap=true, caps=true, reverse=true)
) )
assert(debug || roundings_cross==[],"Roundings from the two ends cross on the prism: decrease size of roundings") assert(debug || roundings_cross==[],"Roundings from the two ends cross on the prism: decrease size of roundings")
@@ -3730,7 +3732,7 @@ function _prism_fillet(name, base, R, bot, top, d, k, N, overlap,uniform,smooth_
: base=="plane" ? _prism_fillet_plane(name,bot, top, d, k, N, overlap,debug) : base=="plane" ? _prism_fillet_plane(name,bot, top, d, k, N, overlap,debug)
: base=="cyl" || base=="cylinder" ? _prism_fillet_cyl(name, R, bot, top, d, k, N, overlap,uniform,debug) : base=="cyl" || base=="cylinder" ? _prism_fillet_cyl(name, R, bot, top, d, k, N, overlap,uniform,debug)
: base=="sphere" ? _prism_fillet_sphere(name, R, bot, top, d, k, N, overlap,uniform,debug) : base=="sphere" ? _prism_fillet_sphere(name, R, bot, top, d, k, N, overlap,uniform,debug)
: is_path(base,2) ? _prism_fillet_prism(name, base, bot, top, d, k, N, overlap,uniform,smooth_normals, debug) : is_path(base,2) ? _prism_fillet_prism(name, base, bot, top, d, k, N, overlap,uniform,smooth_normals, R, debug)
: assert(false,"Unknown base type"); : assert(false,"Unknown base type");
@@ -3739,7 +3741,10 @@ function _prism_fillet_plane(name, bot, top, d, k, N, overlap,debug) =
dir = sign(top[0].z-bot[0].z), // Negative if we are upside down, with "top" below "bot" dir = sign(top[0].z-bot[0].z), // Negative if we are upside down, with "top" below "bot"
isect = [for (i=idx(top)) plane_line_intersection([0,0,1,0], [top[i],bot[i]])] isect = [for (i=idx(top)) plane_line_intersection([0,0,1,0], [top[i],bot[i]])]
) )
d==0 ? [isect, if (overlap!=0) move(overlap*dir*DOWN,isect)] : d==0 ? [isect,
if (overlap!=0) isect,
if (overlap!=0) move(overlap*dir*DOWN,isect),
] :
let( let(
base_normal = -path3d(path_normals(path2d(isect), closed=true)), base_normal = -path3d(path_normals(path2d(isect), closed=true)),
mesh = transpose([for(i=idx(top)) mesh = transpose([for(i=idx(top))
@@ -3774,6 +3779,7 @@ function _prism_fillet_cyl(name, R, bot, top, d, k, N, overlap, uniform, debug)
) )
d==0 ? yrot(90,[ d==0 ? yrot(90,[
isect, isect,
if (overlap!=0) isect,
if (overlap!=0) [for(p=isect) point3d(unit(point2d(p))*(norm(point2d(p))-sign(R)*overlap),p.z)] if (overlap!=0) [for(p=isect) point3d(unit(point2d(p))*(norm(point2d(p))-sign(R)*overlap),p.z)]
]) ])
: :
@@ -3821,7 +3827,8 @@ function _prism_fillet_sphere(name, R,bot, top, d, k, N, overlap, uniform, debug
] ]
) )
d==0 ? [isect, d==0 ? [isect,
if (overlap!=0) [for(p=isect) echo(p=p, overlap=overlap, R=R) p - 0*overlap*sign(R)*unit(p)] if (overlap!=0) isect,
if (overlap!=0) [for(p=isect) p - overlap*sign(R)*unit(p)]
] : ] :
let( let(
tangent = path_tangents(isect,closed=true), tangent = path_tangents(isect,closed=true),
@@ -3896,14 +3903,15 @@ function _polygon_step(poly, ind, u, dir, length) =
// This function needs more error checking? // This function needs more error checking?
// Needs check for zero overlap case and zero joint case // Needs check for zero overlap case and zero joint case
function _prism_fillet_prism(name, basepoly, bot, top, d, k, N, overlap, uniform, smooth_normals, debug)= function _prism_fillet_prism(name, basepoly, bot, top, d, k, N, overlap, uniform, smooth_normals,inside, debug)=
let( let(
inside=sign(inside),
top = yrot(-90,top), top = yrot(-90,top),
bot = yrot(-90,bot), bot = yrot(-90,bot),
basepoly = clockwise_polygon(basepoly), basepoly = clockwise_polygon(basepoly),
segpairs = pair(basepoly,wrap=true), segpairs = pair(basepoly,wrap=true),
isect_ind = [for (i=idx(top)) isect_ind = [for (i=idx(top))
let(isect = _prism_line_isect(segpairs, [top[i], bot[i]], top[i])) let(isect = _prism_line_isect(segpairs, [top[i], bot[i]], inside*top[i]))
assert(isect, str("Prism doesn't fully intersect prism (",name,")")) assert(isect, str("Prism doesn't fully intersect prism (",name,")"))
isect isect
], ],
@@ -3914,6 +3922,7 @@ function _prism_fillet_prism(name, basepoly, bot, top, d, k, N, overlap, uniform
) )
d==0 ? yrot(90,[ d==0 ? yrot(90,[
isect, isect,
if (overlap!=0) isect,
if (overlap!=0) if (overlap!=0)
[for(i=idx(isect)) [for(i=idx(isect))
let(normal = point3d(_getnormal(basepoly,index[i],uval[i],smooth_normals))) let(normal = point3d(_getnormal(basepoly,index[i],uval[i],smooth_normals)))
@@ -3924,7 +3933,7 @@ function _prism_fillet_prism(name, basepoly, bot, top, d, k, N, overlap, uniform
mesh = transpose([for(i=idx(top)) mesh = transpose([for(i=idx(top))
let( let(
normal = point3d(_getnormal(basepoly,index[i],uval[i],smooth_normals)), normal = point3d(_getnormal(basepoly,index[i],uval[i],smooth_normals)),
dir = unit(cross(normal,tangent[i])), dir = inside*unit(cross(normal,tangent[i])),
zpart = d*dir.z, zpart = d*dir.z,
length_needed = d*norm(point2d(dir)), length_needed = d*norm(point2d(dir)),
edgept2d = _polygon_step(basepoly, index[i], uval[i], sign(cross(point2d(dir),point2d(normal))), length_needed), edgept2d = _polygon_step(basepoly, index[i], uval[i], sign(cross(point2d(dir),point2d(normal))), length_needed),
@@ -3949,7 +3958,7 @@ function _prism_fillet_prism(name, basepoly, bot, top, d, k, N, overlap, uniform
) )
[ [
each bezier_curve(bez, N, endpoint=true), each bezier_curve(bez, N, endpoint=true),
if (overlap!=0) edgepoint-unit(point3d(normal))*overlap if (overlap!=0) edgepoint-unit(point3d(normal))*overlap*inside
] ]
]) ])
) )
@@ -3964,7 +3973,6 @@ function _prism_fillet_prism(name, basepoly, bot, top, d, k, N, overlap, uniform
// See Also: parent(), join_prism(), linear_sweep() // See Also: parent(), join_prism(), linear_sweep()
// Usage: // Usage:
// prism_connector(desc1, anchor1, desc2, anchor2, [spin_align=]); // prism_connector(desc1, anchor1, desc2, anchor2, [spin_align=]);
// Description: // Description:
// Given descriptions and anchors for two objects, construct a filleted prism that connects the // Given descriptions and anchors for two objects, construct a filleted prism that connects the
// anchor points on those objects, with a filleted joint at each end. This is an alternative interface // anchor points on those objects, with a filleted joint at each end. This is an alternative interface
@@ -4496,21 +4504,22 @@ module prism_connector(profile, desc1, anchor1, desc2, anchor2, shift1=0, shift2
dummy = assert(is_vector(base_anchor) || is_string(base_anchor), "anchor1 must be a string or a 3-vector") dummy = assert(is_vector(base_anchor) || is_string(base_anchor), "anchor1 must be a string or a 3-vector")
assert(is_vector(aux_anchor) || is_string(aux_anchor), "anchor2 must be a string or a 3-vector") assert(is_vector(aux_anchor) || is_string(aux_anchor), "anchor2 must be a string or a 3-vector")
assert(is_rotation(auxmap), "desc1 and desc2 are not related to each other by a rotation (and translation)"); assert(is_rotation(auxmap), "desc1 and desc2 are not related to each other by a rotation (and translation)");
base_type = _get_obj_type(1,base[1],base_anchor,profile); base_type = _get_obj_type(1,base[1],base_anchor,profile);
base_axis = base_type=="cyl" ? base[1][5] : RIGHT; base_axis = base_type=="cyl" ? base[1][5] : RIGHT;
base_edge = _is_geom_an_edge(base[1],base_anchor); base_edge = _is_geom_an_edge(base[1],base_anchor);
base_r = in_list(base_type,["cyl","sphere"]) ? base[1][1] : undef; base_r = in_list(base_type,["cyl","sphere"]) ? base[1][1] : 1;
base_anch = _find_anchor(base_anchor, base[1]); base_anch = _find_anchor(base_anchor, base[1]);
base_spin = base_anch[3]; base_spin = base_anch[3];
base_anch_pos = base_anch[1]; base_anch_pos = base_anch[1];
base_anch_dir = base_anch[2]; base_anch_dir = base_anch[2];
prelim_shift1 = _check_join_shift(1,base_type,shift1,true); prelim_shift1 = _check_join_shift(1,base_type,shift1,true);
shift1 = corrected_base_anchor ? corrected_base_anchor[1] + prelim_shift1 : prelim_shift1; shift1 = corrected_base_anchor ? corrected_base_anchor[1] + prelim_shift1 : prelim_shift1;
aux_type = _get_obj_type(2,aux[1],aux_anchor,profile); aux_type = _get_obj_type(2,aux[1],aux_anchor,profile);
aux_anch = _find_anchor(aux_anchor, aux[1]); aux_anch = _find_anchor(aux_anchor, aux[1]);
aux_edge = _is_geom_an_edge(aux[1],aux_anchor); aux_edge = _is_geom_an_edge(aux[1],aux_anchor);
aux_r = aux_type=="plane" ? undef : aux[1][1]; aux_r = in_list(aux_type,["cyl","sphere"]) ? aux[1][1] : 1;
aux_anch_pos = aux_anch[1]; aux_anch_pos = aux_anch[1];
aux_anch_dir = aux_anch[2]; aux_anch_dir = aux_anch[2];
aux_spin = aux_anch[3]; aux_spin = aux_anch[3];
@@ -4551,6 +4560,7 @@ module prism_connector(profile, desc1, anchor1, desc2, anchor2, shift1=0, shift2
: apply(aux_T * matrix_inverse(aux_to_canonical), aux_anch_pos); : apply(aux_T * matrix_inverse(aux_to_canonical), aux_anch_pos);
base_root = base_type=="plane" || is_list(base_type) || base_anchor==CTR ? CENTER : base_r*UP; base_root = base_type=="plane" || is_list(base_type) || base_anchor==CTR ? CENTER : base_r*UP;
prism_axis = aux_root-base_root; prism_axis = aux_root-base_root;
base_inside = prism_axis.z<0 ? -1 : 1; base_inside = prism_axis.z<0 ? -1 : 1;
@@ -4592,8 +4602,6 @@ module prism_connector(profile, desc1, anchor1, desc2, anchor2, shift1=0, shift2
move(base_root)rot(from=UP,to=prism_axis) move(base_root)rot(from=UP,to=prism_axis)
linear_extrude(height=norm(base_root-aux_root))zrot(base_spin-spin)polygon(profile); linear_extrude(height=norm(base_root-aux_root))zrot(base_spin-spin)polygon(profile);
else{ else{
// aux_type = is_string(aux_type) ? aux_type : _flip(aux_type);
// base_type = is_string(base_type) ? base_type : _flip(base_type);
join_prism(zrot(base_spin-spin,profile), join_prism(zrot(base_spin-spin,profile),
base=base_type, base_r=u_mul(base_r,base_inside), base=base_type, base_r=u_mul(base_r,base_inside),
aux=aux_type, aux_T=aux_T, aux_r=u_mul(aux_r,aux_inside), aux=aux_type, aux_T=aux_T, aux_r=u_mul(aux_r,aux_inside),
@@ -4608,13 +4616,5 @@ module prism_connector(profile, desc1, anchor1, desc2, anchor2, shift1=0, shift2
} }
} }
function _flip(prof) =
[
prof[0],
each xflip(prof),
last(prof)
];
// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap // vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap