diff --git a/beziers.scad b/beziers.scad index 4162a17..9b02948 100644 --- a/beziers.scad +++ b/beziers.scad @@ -324,8 +324,6 @@ function bezier_segment_closest_point(curve, pt, max_err=0.01, u=0, end_u=1) = bezier_segment_closest_point(curve, pt, max_err=max_err, u=minima[0], end_u=minima[1]); - - // Function: bezier_segment_length() // Usage: // pathlen = bezier_segment_length(curve, , , ); @@ -361,6 +359,28 @@ function bezier_segment_length(curve, start_u=0, end_u=1, max_deflect=0.01) = +// Function: bezier_line_intersection() +// Usage: +// u = bezier_line_intersection(curve, line); +// Description: +// Finds the parameter(s) of the 2d curve whose Bezier control points are `curve` +// corresponding to its intersection points with the line through +// the pair of distinct 2d points in `line`. +// Arguments: +// curve = List of 2d control points for the Bezier curve +// line = a list of two distinct 2d points defining a line +function bezier_line_intersection(curve, line) = + assert(is_path(curve,2), "The input ´curve´ must be a 2d bezier") + assert(_valid_line(line,2), "The input `line` is not a valid 2d line") + let( + a = _bezier_matrix(len(curve)-1)*curve, // curve algebraic coeffs. + n = [-line[1].y+line[0].y, line[1].x-line[0].x], // line normal + q = [for(i=[len(a)-1:-1:1]) a[i]*n, (a[0]-line[0])*n] // curve SDF to line + ) + [for(u=real_roots(q)) if (u>=0 && u<=1) u]; + + + // Function: fillet3pts() // Usage: // bez_path_pts = fillet3pts(p0, p1, p2, r); diff --git a/common.scad b/common.scad index 2a51a3b..bf7b79c 100644 --- a/common.scad +++ b/common.scad @@ -222,6 +222,18 @@ function _list_pattern(list) = function same_shape(a,b) = _list_pattern(a) == b*0; +// Function: is_bool_list() +// Usage: +// check = is_bool_list(list,) +// Description: +// Tests whether input is a list containing only booleans, and optionally checks its length. +// Arguments: +// list = list to test +// length = if given, list must be this length +function is_bool_list(list, length) = + is_list(list) && (is_undef(length) || len(list)==length) && []==[for(entry=list) if (!is_bool(entry)) 1]; + + // Section: Handling `undef`s. diff --git a/geometry.scad b/geometry.scad index 280a4fd..9dd7774 100644 --- a/geometry.scad +++ b/geometry.scad @@ -1139,7 +1139,7 @@ function plane_line_angle(plane, line) = function plane_line_intersection(plane, line, bounded=false, eps=EPSILON) = assert( is_finite(eps) && eps>=0, "The tolerance should be a positive number." ) assert(_valid_plane(plane,eps=eps) && _valid_line(line,dim=3,eps=eps), "Invalid plane and/or line.") - assert(is_bool(bounded) || (is_list(bounded) && len(bounded)==2), "Invalid bound condition(s).") + assert(is_bool(bounded) || is_bool_list(bounded,2), "Invalid bound condition.") let( bounded = is_list(bounded)? bounded : [bounded, bounded], res = _general_plane_line_intersection(plane, line, eps=eps) @@ -1591,6 +1591,47 @@ function circle_circle_tangents(c1,r1,c2,r2,d1,d2) = +// Function: circle_line_intersection() +// Usage: +// isect = circle_line_intersection(c,r,line,,); +// isect = circle_line_intersection(c,d,line,,); +// Description: +// Find intersection points between a 2d circle and a line, ray or segment specified by two points. +// By default the line is unbounded. +// Arguments: +// c = center of circle +// r = radius of circle +// line = two points defining the unbounded line +// bounded = false for unbounded line, true for a segment, or a vector [false,true] or [true,false] to specify a ray with the first or second end unbounded. Default: false +// eps = epsilon used for identifying the case with one solution. Default: 1e-9 +// --- +// d = diameter of circle +function circle_line_intersection(c,r,line,d,bounded=false,eps=EPSILON) = + let(r=get_radius(r=r,d=d,dflt=undef)) + assert(_valid_line(line,2), "Input 'line' is not a valid 2d line.") + assert(is_vector(c,2), "Circle center must be a 2-vector") + assert(is_num(r) && r>0, "Radius must be positive") + assert(is_bool(bounded) || is_bool_list(bounded,2), "Invalid bound condition") + let( + bounded = force_list(bounded,2), + closest = line_closest_point(line,c), + d = norm(closest-c) + ) + d > r ? [] : + let( + isect = approx(d,r,eps) ? [closest] : + let( offset = sqrt(r*r-d*d), + uvec=unit(line[1]-line[0]) + ) [closest-offset*uvec, closest+offset*uvec] + + ) + [for(p=isect) + if ((!bounded[0] || (p-line[0])*(line[1]-line[0])>=0) + && (!bounded[1] || (p-line[1])*(line[0]-line[1])>=0)) p]; + + + + // Section: Pointlists diff --git a/modular_hose.scad b/modular_hose.scad index fdf2861..e7e71d1 100644 --- a/modular_hose.scad +++ b/modular_hose.scad @@ -126,9 +126,11 @@ _hose_waist = [1.7698, 1.8251, 3.95998]; // or you can make modular hose segments. To make assembly possible with printed // parts you can add clearances that make the ball end smaller and the socket end // larger. These work by simply increasing the radius of the whole end by the specified -// amount. On a Prusa printer with PETG clearance values around .1 work, but you -// will have to experiment with your machine and materials. And note clearance values -// are different for the different sizes. +// amount. On a Prusa printer with PETG, a clearance of 0.05 allows the 3/4" hose parts to mate +// with standard modular hose or itself. A clearance of 0.1 allows the 3/4" parts to mate with +// standard hose, and with clearance 0 the 1/4" parts will mate with standard hose. And note clearance values +// are different for the different sizes. You will have to experiment with your machine and materials. Small +// adjustments will change the stiffness of the connection. // Arguments: // size = size of modular hose part, must be 1/4, 1/2 or 3/4. // type = type of part to make, either "segment", "socket" (or "big"), or "ball" (or "small") @@ -174,7 +176,7 @@ module modular_hose(size, type, clearance=0, waist_len, anchor=BOTTOM, spin=0,or center = mean(bounds); attachable(anchor,spin,orient,l=bounds[1].y-bounds[0].y, r=bounds[1].x) { - rotate_extrude() + rotate_extrude(convexity=4) polygon(fwd(center.y,p=shape)); children(); }