diff --git a/joiners.scad b/joiners.scad index ad80d99..3609e2b 100644 --- a/joiners.scad +++ b/joiners.scad @@ -1139,7 +1139,7 @@ module snap_pin_socket(size, r, radius, l,length, d,diameter,nub_depth, snap, fi // tag("remove")zcyl(l=20,r=13.5, $fn=64); // } // Example: Clip that locks only on the right side -// rabbit_clip("double",length=8, width=7, snap=0.75, thickness=0.8, compression=0.2,depth=5,lock=RIGHT); +// rabbit_clip("pin",length=8, width=7, snap=0.75, thickness=0.8, compression=0.2,depth=5,lock=RIGHT); function rabbit_clip(type, length, width, snap, thickness, depth, compression=0.1, clearance=.1, lock=false, lock_clearance=0, splinesteps=8, anchor, orient, spin=0) = no_function("rabbit_clip"); diff --git a/rounding.scad b/rounding.scad index 50e5c6b..6cf45eb 100644 --- a/rounding.scad +++ b/rounding.scad @@ -4175,12 +4175,18 @@ function _prism_fillet_prism(name, basepoly, bot, top, d, k, N, overlap, uniform // Normally {{join_prism()}} will issue an error in this situation. The `debug` parameter is passed through to {{join_prism()}} and // tells that module to display invalid self-intersecting geometry to help you understand the problem. // . -// When connecting to an edge, artifacts may occur at the corners where the prism doesn't meet the object in the ideal fashion. +// When connecting to an unrounded edge, artifacts may occur at the corners where the prism doesn't meet the object in the ideal fashion. // Adjsting the points on your prism profile so that a point falls close to the corner will achieve the best result, and make sure // that `smooth_normals` is disabled (the default for edges) because it results in a completely incorrect fillet in this case. // If you connect to an extrusion object, the default value for `smooth_normals` is true, which generally works better when // for a uniformly sampled smooth object, but if your object has corners you may get better results by setting `smooth_normals=false`. // . +// When connecting to a rounded edge, the edge geometry must be circular or a smooth bezier rounding. In the circular case +// provide the `edge_r` parameter to specify the radius of the circular rounding. In the case of smooth bezier roundings you +// must give `edge_joint` to specify the size of the rounding, and you may optionally provide `edge_k` as required. The default +// of `edge_k=0.5` matches the default for {{rounded_prism()}}. These parameters are ignored for objects that are not edges. +// If you need to connect two differently rounded edges, you can used edge_r1, edge_joint2, etc. +// . // You can use two named anchors, "end1" and "end2", that provide anchors at the connecting points on the end faces of the connector prism. These anchors // point **along the prism axis**, which means they are not necessarily perpendicular to the end face of the prism. If you need // an anchor perpendicular to the end face of the prism you can use the anchor from the description at that end that you used @@ -4213,7 +4219,16 @@ function _prism_fillet_prism(name, basepoly, bot, top, d, k, N, overlap, uniform // overlap = amount of overlap of the prism fillet into both objects. Default: 1 // overlap1 = amount of overlap of the prism fillet into object1 // overlap2 = amount of overlap of the prism fillet into object2 -// smooth_normals = controls whether normals are smoothed when the object is a prism or edge; no effect otherwise. Default: false if object is an edge, true otherwise +// edge_r = when attaching to an edge, assume it has a circular rounding with this radius +// edge_joint = when attaching to an edge, assume it has a smooth bezier rounding with this joint length +// edge_k = when attaching to an edge with a bezier rounding, use this k parameter value. Default: 0.5 (matches rounded_prism) +// edge_r1 = when attaching to an edge on object1, assume it has a circular rounding with this radius +// edge_joint1 = when attaching to an edge on object1, assume it has a smooth bezier rounding with this joint length +// edge_k = when attaching to an edge on object1 with a bezier rounding, use this k parameter value. Default: 0.5 (matches rounded_prism) +// edge_r2 = when attaching to an edge, on object2 assume it has a circular rounding with this radius +// edge_joint2 = when attaching to an edge on object2, assume it has a smooth bezier rounding with this joint length +// edge_k2 = when attaching to an edge on object2 with a bezier rounding, use this k parameter value. Default: 0.5 (matches rounded_prism) +// smooth_normals = controls whether normals are smoothed when the object is a prism or edge; no effect otherwise. Default: false if object is an unrounded edge, true otherwise // smooth_normals1 = controls whether normals are smoothed when the object1 is a prism or edge; no effect otherwise. // smooth_normals2 = controls whether normals are smoothed when the object2 is a prism or edge; no effect otherwise. // debug = pass-through to the {{join_prism()}} debug parameter. If true then various cases where the fillet self intersects will be displayed instead of creating an error. Default: false @@ -4338,6 +4353,14 @@ function _prism_fillet_prism(name, basepoly, bot, top, d, k, N, overlap, uniform // cuboid(20) let(edge=parent()) // move([30,-30,-8]) zrot(-45) cuboid(20) let(extra=parent()) // #prism_connector(circ, edge, RIGHT+FWD, extra, LEFT, fillet=2); +// Example: If you specify the rounding parameters you can attach to rounded edges, which eliminates many of the difficulties listed above +// cuboid(10,rounding=4,edges=RIGHT+FWD) +// let(cube=parent()) +// move([20,-15]) sphere(r=5) +// prism_connector(circle(r=3,$fn=64), +// cube, RIGHT+FWD, +// parent(), CENTER, +// fillet=1.5, edge_r=4); // Example(3D,NoAxes): We can attach to the top surface of a shifted cone, but when attaching to the curved surface, only a right angle cylinder is supported. // $fn=32; // flower = scale(.6,[for(theta=lerpn(0,360,90,endpoint=false)) (15+1.3*sin(6*theta))*[cos(theta),sin(theta)]]); @@ -4492,8 +4515,8 @@ function _get_obj_type(ind,geom,anchor,prof,edge_r,edge_joint,edge_k) = x = y/tan(edge_angle/2), corner=[[x,-y],[0,0], [x,y]] ) - is_def(edge_r)? round_corners(corner, r=edge_r, closed=false) - : is_def(edge_joint) ? echo(jk=edge_joint,edge_k)round_corners(corner, method="smooth",joint=edge_joint, k=edge_k, closed=false) + is_def(edge_r) && edge_r>0 ? round_corners(corner, r=edge_r, closed=false) + : is_def(edge_joint) && edge_joint>0 ? round_corners(corner, method="smooth",joint=edge_joint, k=edge_k, closed=false) : corner : starts_with(geom[0], "extrusion") ? anchor==UP || anchor==DOWN || starts_with(anchor,"face") ? "plane" @@ -4694,7 +4717,10 @@ module prism_connector(profile, desc1, anchor1, desc2, anchor2, shift1, shift2, overlap, overlap1, overlap2, k, k1, k2, n, n1, n2, uniform, uniform1, uniform2, - smooth_normals, smooth_normals1, smooth_normals2, + smooth_normals, smooth_normals1, smooth_normals2, + edge_r, edge_joint, edge_k=0.5, + edge_r1, edge_joint1, edge_k1, + edge_r2, edge_joint2, edge_k2, debug=false, debug_pos=false) { shift1_input = first_defined([shift1,shift,0]); @@ -4710,17 +4736,30 @@ module prism_connector(profile, desc1, anchor1, desc2, anchor2, shift1, shift2, base_uniform = first_defined([uniform1, uniform, true]); aux_uniform = first_defined([uniform2, uniform, true]); - + base_k = first_defined([k1,k,0.7]); aux_k = first_defined([k2,k,0.7]); + edge_r1 = default(edge_r1,edge_r); + edge_r2 = default(edge_r2,edge_r); + edge_joint1 = default(edge_joint1, edge_joint); + edge_joint2 = default(edge_joint2, edge_joint); + edge_k1 = default(edge_k1,edge_k); + edge_k2 = default(edge_k2,edge_k); + profile = force_path(profile,"profile"); - dummy0 = assert(is_path(profile,2), "profile must be a 2d path"); + dummy0 = assert(is_path(profile,2), "profile must be a 2d path") + assert(num_defined([edge_r1,edge_joint1])<=1, "Cannot give both edge_r1 (edge_r) and edge_joint1 (edge_joint)") + assert(num_defined([edge_r2,edge_joint2])<=2, "Cannot give both edge_r2 (edge_r) and edge_joint2 (edge_joint)") + assert(is_undef(edge_r1) || all_nonnegative([edge_r1]), "edge_r1 (edge_r) must be nonnegative") + assert(is_undef(edge_r2) || all_nonnegative([edge_r2]), "edge_r2 (edge_r) must be nonnegative") + assert(is_undef(edge_joint1) || all_nonnegative([edge_joint1]), "edge_joint1 (edge_joint) must be nonnegative") + assert(is_undef(edge_joint2) || all_nonnegative([edge_joint2]), "edge_joint2 (edge_joint) must be nonnegative") + assert(all_nonnegative([edge_k1,edge_k2]), "edge_k1, edge_k2, and edge_k must be nonnegative"); corrected_base_anchor = is_vector(anchor1) && norm(anchor1)==0 ? _find_center_anchor(desc1,desc2,anchor2,true) : undef; corrected_aux_anchor = is_vector(anchor2) && norm(anchor2)==0 ? _find_center_anchor(desc2,desc1,anchor1,false) : undef; - base_anchor=is_string(anchor1) ? anchor1 : is_def(corrected_base_anchor) ? corrected_base_anchor[0] : point3d(anchor1); @@ -4739,7 +4778,7 @@ module prism_connector(profile, desc1, anchor1, desc2, anchor2, shift1, shift2, 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)"); - base_type = _get_obj_type(1,base[1],base_anchor,profile); + base_type = _get_obj_type(1,base[1],base_anchor,profile,edge_r1,edge_joint1,edge_k1); base_axis = base_type=="cyl" ? base[1][5] : RIGHT; base_edge = _is_geom_an_edge(base[1],base_anchor); base_r = in_list(base_type,["cyl","sphere"]) ? base[1][1] : 1; @@ -4750,7 +4789,7 @@ module prism_connector(profile, desc1, anchor1, desc2, anchor2, shift1, shift2, prelim_shift1 = _check_join_shift(1,base_type,shift1_input,true); 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,edge_r2,edge_joint2,edge_k2); aux_anch = _find_anchor(aux_anchor, aux[1]); aux_edge = _is_geom_an_edge(aux[1],aux_anchor); aux_r = in_list(aux_type,["cyl","sphere"]) ? aux[1][1] : 1; @@ -4763,8 +4802,8 @@ module prism_connector(profile, desc1, anchor1, desc2, anchor2, shift1, shift2, shift2 = corrected_aux_anchor ? corrected_aux_anchor[1] + prelim_shift2 : prelim_shift2; - base_smooth_normals = first_defined([smooth_normals1, smooth_normals,!base_edge]); - aux_smooth_normals = first_defined([smooth_normals2, smooth_normals,!aux_edge]); + base_smooth_normals = first_defined([smooth_normals1, smooth_normals,!base_edge || default(edge_r1,0)>0 || default(edge_joint1,0)>0]); + aux_smooth_normals = first_defined([smooth_normals2, smooth_normals,!aux_edge || default(edge_r2,0)>0 || default(edge_joint2,0)>0]); backdir = base_type=="cyl" ? base_axis : apply(rot(from=UP,to=base_anch_dir)*zrot(base_spin),BACK); @@ -4916,7 +4955,7 @@ module prism_connector(profile, desc1, anchor1, desc2, anchor2, shift1, shift2, // For this reason, the default is false when connecting to an edge that is not rounded. // . // When connecting to a rounded edge, the edge geometry must be circular or a smooth bezier rounding. In the circular case -// you must provide the `edge_r` parameter to specify the radius of the circular rounding. In the latter case you +// provide the `edge_r` parameter to specify the radius of the circular rounding. In the case of smooth bezier roundings you // must give `edge_joint` to specify the size of the rounding, and you may optionally provide `edge_k` as required. The default // of `edge_k=0.5` matches the default for {{rounded_prism()}}. // . diff --git a/shapes3d.scad b/shapes3d.scad index f957b96..7382a9d 100644 --- a/shapes3d.scad +++ b/shapes3d.scad @@ -858,7 +858,7 @@ function prismoid( // . // Anchors are based on the VNF of the prism. Especially for tapered or shifted prisms, this may give unexpected anchor positions, such as top side anchors // being located at the bottom of the shape, so confirm anchor positions before use. -// Additional named face and edge anchors are located on the side faces and vertical edges of the prism. +// Additional named face and edge anchors are located on the side faces and edges of the prism. // You can use `EDGE(i)`, `EDGE(TOP,i)` and `EDGE(BOT,i)` as a shorthand for accessing the named edge anchors, and `FACE(i)` for the face anchors. // The "edge0" anchor identifies an edge located along the X+ axis, and then edges // are labeled counting up in the clockwise direction. Similarly "face0" is the face immediately clockwise from "edge0", and face diff --git a/vnf.scad b/vnf.scad index 1349a7c..b92b15a 100644 --- a/vnf.scad +++ b/vnf.scad @@ -128,8 +128,8 @@ EMPTY_VNF = [[],[]]; // The standard empty VNF with no vertices or faces. // Example(3D): // vnf = vnf_vertex_array( // points=[ -// for (h = [0:5:180-EPSILON]) [ -// for (t = [0:5:360-EPSILON]) +// for (h = [0:5:179]) [ +// for (t = [0:5:359]) // cylindrical_to_xyz(100 + 12 * cos((h/2 + t)*6), t, h) // ] // ], @@ -171,7 +171,7 @@ EMPTY_VNF = [[],[]]; // The standard empty VNF with no vertices or faces. // Example(3D): Both `col_wrap` and `row_wrap` are true to make a torus. // vnf = vnf_vertex_array( // points=[ -// for (a=[0:5:360-EPSILON]) +// for (a=[0:5:359]) // apply( // zrot(a) * right(30) * xrot(90), // path3d(circle(d=20))