Fixed interior_fillet(). Refactored circle_* functions.

This commit is contained in:
Garth Minette 2022-04-06 19:37:00 -07:00
parent 65615dc07d
commit d9691b3b4a
8 changed files with 145 additions and 139 deletions

View file

@ -710,7 +710,7 @@ function arc(n, r, angle, d, cp, points, corner, width, thickness, start, wedge=
let( r = get_radius(r=r, d=d) ) let( r = get_radius(r=r, d=d) )
assert(is_finite(r) && r>0, "Must specify r= or d= when corner= is given.") assert(is_finite(r) && r>0, "Must specify r= or d= when corner= is given.")
let( let(
ci = circle_2tangents(corner[0], corner[1], corner[2], r=r, tangents=true), ci = circle_2tangents(r, corner[0], corner[1], corner[2], tangents=true),
cp = ci[0], nrm = ci[1], tp1 = ci[2], tp2 = ci[3], cp = ci[0], nrm = ci[1], tp1 = ci[2], tp2 = ci[3],
dir = det2([corner[1]-corner[0],corner[2]-corner[1]]) > 0, dir = det2([corner[1]-corner[0],corner[2]-corner[1]]) > 0,
corner = dir? [tp1,tp2] : [tp2,tp1], corner = dir? [tp1,tp2] : [tp2,tp1],

View file

@ -861,14 +861,14 @@ function _is_point_above_plane(plane, point) =
// Function: circle_line_intersection() // Function: circle_line_intersection()
// Usage: // Usage:
// isect = circle_line_intersection(c, r|d=, line, [bounded], [eps=]); // pts = circle_line_intersection(r|d=, cp, line, [bounded], [eps=]);
// Topics: Geometry, Circles, Lines, Intersection // Topics: Geometry, Circles, Lines, Intersection
// Description: // Description:
// Find intersection points between a 2d circle and a line, ray or segment specified by two points. // Find intersection points between a 2D circle and a line, ray or segment specified by two points.
// By default the line is unbounded. Returns the list of zero or more intersection points. // By default the line is unbounded. Returns the list of zero or more intersection points.
// Arguments: // Arguments:
// c = Center of circle
// r = Radius of circle // r = Radius of circle
// cp = Center of circle
// line = Two points defining the line // line = Two points defining the 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 // 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
// --- // ---
@ -879,14 +879,14 @@ function _is_point_above_plane(plane, point) =
// cp = [1,2]; r = 10; // cp = [1,2]; r = 10;
// translate(cp) circle(r=r); // translate(cp) circle(r=r);
// color("black") stroke(line, endcaps="arrow2", width=0.5); // color("black") stroke(line, endcaps="arrow2", width=0.5);
// isects = circle_line_intersection(c=cp, r=r, line=line); // isects = circle_line_intersection(r=r, cp=cp, line=line);
// color("red") move_copies(isects) circle(d=1); // color("red") move_copies(isects) circle(d=1);
// Example(2D): Tangent intersection returns one point. // Example(2D): Tangent intersection returns one point.
// line = [[-10,12], [10,12]]; // line = [[-10,12], [10,12]];
// cp = [1,2]; r = 10; // cp = [1,2]; r = 10;
// translate(cp) circle(r=r); // translate(cp) circle(r=r);
// color("black") stroke(line, endcaps="arrow2", width=0.5); // color("black") stroke(line, endcaps="arrow2", width=0.5);
// isects = circle_line_intersection(c=cp, r=r, line=line); // isects = circle_line_intersection(r=r, cp=cp, line=line);
// color("#f44") move_copies(isects) circle(d=1); // color("#f44") move_copies(isects) circle(d=1);
// Example(2D): A bounded ray might only intersect in one direction. // Example(2D): A bounded ray might only intersect in one direction.
// line = [[-5,2], [5,7]]; // line = [[-5,2], [5,7]];
@ -895,30 +895,30 @@ function _is_point_above_plane(plane, point) =
// translate(cp) circle(r=r); // translate(cp) circle(r=r);
// color("gray") dashed_stroke(extended, width=0.2); // color("gray") dashed_stroke(extended, width=0.2);
// color("black") stroke(line, endcap2="arrow2", width=0.5); // color("black") stroke(line, endcap2="arrow2", width=0.5);
// isects = circle_line_intersection(c=cp, r=r, line=line, bounded=[true,false]); // isects = circle_line_intersection(r=r, cp=cp, line=line, bounded=[true,false]);
// color("#f44") move_copies(isects) circle(d=1); // color("#f44") move_copies(isects) circle(d=1);
// Example(2D): If they don't intersect at all, then an empty list is returned. // Example(2D): If they don't intersect at all, then an empty list is returned.
// line = [[-12,12], [12,8]]; // line = [[-12,12], [12,8]];
// cp = [-5,-2]; r = 10; // cp = [-5,-2]; r = 10;
// translate(cp) circle(r=r); // translate(cp) circle(r=r);
// color("black") stroke(line, endcaps="arrow2", width=0.5); // color("black") stroke(line, endcaps="arrow2", width=0.5);
// isects = circle_line_intersection(c=cp, r=r, line=line); // isects = circle_line_intersection(r=r, cp=cp, line=line);
// color("#f44") move_copies(isects) circle(d=1); // color("#f44") move_copies(isects) circle(d=1);
function circle_line_intersection(c,r,line,bounded=false,d,eps=EPSILON) = function circle_line_intersection(r, cp, line, bounded=false, d, eps=EPSILON) =
assert(_valid_line(line,2), "Invalid 2d line.") assert(_valid_line(line,2), "Invalid 2d line.")
assert(is_vector(c,2), "Circle center must be a 2-vector") assert(is_vector(cp,2), "Circle center must be a 2-vector")
_circle_or_sphere_line_intersection(c,r,line,bounded,d,eps); _circle_or_sphere_line_intersection(r, cp, line, bounded, d, eps);
function _circle_or_sphere_line_intersection(c,r,line,bounded=false,d,eps=EPSILON) = function _circle_or_sphere_line_intersection(r, cp, line, bounded=false, d, eps=EPSILON) =
let(r=get_radius(r=r,d=d,dflt=undef)) let(r=get_radius(r=r,d=d,dflt=undef))
assert(is_num(r) && r>0, "Radius must be positive") assert(is_num(r) && r>0, "Radius must be positive")
assert(is_bool(bounded) || is_bool_list(bounded,2), "Invalid bound condition") assert(is_bool(bounded) || is_bool_list(bounded,2), "Invalid bound condition")
let( let(
bounded = force_list(bounded,2), bounded = force_list(bounded,2),
closest = line_closest_point(line,c), closest = line_closest_point(line,cp),
d = norm(closest-c) d = norm(closest-cp)
) )
d > r ? [] : d > r ? [] :
let( let(
@ -934,73 +934,74 @@ function _circle_or_sphere_line_intersection(c,r,line,bounded=false,d,eps=EPSILO
// Function: circle_circle_intersection() // Function: circle_circle_intersection()
// Usage: // Usage:
// pts = circle_circle_tangents(c1, r1|d1=, c2, r2|d2=, [eps]); // pts = circle_circle_intersection(r1|d1=, cp1, r2|d2=, cp2, [eps]);
// Topics: Geometry, Circles // Topics: Geometry, Circles
// Description: // Description:
// Compute the intersection points of two circles. Returns a list of the intersection points, which // Compute the intersection points of two circles. Returns a list of the intersection points, which
// will contain two points in the general case, one point for tangent circles, or will be empty // will contain two points in the general case, one point for tangent circles, or will be empty
// if the circles do not intersect. // if the circles do not intersect.
// Arguments: // Arguments:
// c1 = Center of the first circle.
// r1 = Radius of the first circle. // r1 = Radius of the first circle.
// c2 = Center of the second circle. // cp1 = Centerpoint of the first circle.
// r2 = Radius of the second circle. // r2 = Radius of the second circle.
// cp2 = Centerpoint of the second circle.
// eps = Tolerance for detecting tangent circles. Default: EPSILON // eps = Tolerance for detecting tangent circles. Default: EPSILON
// --- // ---
// d1 = Diameter of the first circle. // d1 = Diameter of the first circle.
// d2 = Diameter of the second circle. // d2 = Diameter of the second circle.
// Example(2D,NoAxes): Circles intersect in two points. // Example(2D,NoAxes): Circles intersect in two points.
// $fn=32; // $fn=32;
// c1 = [4,4]; r1 = 3; // cp1 = [4,4]; r1 = 3;
// c2 = [7,7]; r2 = 2; // cp2 = [7,7]; r2 = 2;
// pts = circle_circle_intersection(c1,r1,c2,r2); // pts = circle_circle_intersection(r1, cp1, r2, cp2);
// move(c1) stroke(circle(r=r1), width=0.2, closed=true); // move(cp1) stroke(circle(r=r1), width=0.2, closed=true);
// move(c2) stroke(circle(r=r2), width=0.2, closed=true); // move(cp2) stroke(circle(r=r2), width=0.2, closed=true);
// color("red")move_copies(pts) circle(r=.3); // color("red") move_copies(pts) circle(r=.3);
// Example(2D,NoAxes): Circles are tangent, so one intersection point: // Example(2D,NoAxes): Circles are tangent, so one intersection point:
// $fn=32; // $fn=32;
// c1 = [4,4]; r1 = 4; // cp1 = [4,4]; r1 = 4;
// c2 = [4,10]; r2 = 2; // cp2 = [4,10]; r2 = 2;
// pts = circle_circle_intersection(c1,r1,c2,r2); // pts = circle_circle_intersection(r1, cp1, r2, cp2);
// move(c1) stroke(circle(r=r1), width=0.2, closed=true); // move(cp1) stroke(circle(r=r1), width=0.2, closed=true);
// move(c2) stroke(circle(r=r2), width=0.2, closed=true); // move(cp2) stroke(circle(r=r2), width=0.2, closed=true);
// color("red")move_copies(pts) circle(r=.3); // color("red") move_copies(pts) circle(r=.3);
// Example(2D,NoAxes): Another tangent example: // Example(2D,NoAxes): Another tangent example:
// $fn=32; // $fn=32;
// c1 = [4,4]; r1 = 4; // cp1 = [4,4]; r1 = 4;
// c2 = [5,5]; r2 = 4-sqrt(2); // cp2 = [5,5]; r2 = 4-sqrt(2);
// pts = circle_circle_intersection(c1,r1,c2,r2); // pts = circle_circle_intersection(r1, cp1, r2, cp2);
// move(c1) stroke(circle(r=r1), width=0.2, closed=true); // move(cp1) stroke(circle(r=r1), width=0.2, closed=true);
// move(c2) stroke(circle(r=r2), width=0.2, closed=true); // move(cp2) stroke(circle(r=r2), width=0.2, closed=true);
// color("red")move_copies(pts) circle(r=.3); // color("red") move_copies(pts) circle(r=.3);
// Example(2D,NoAxes): Circles do not intersect. Returns empty list. // Example(2D,NoAxes): Circles do not intersect. Returns empty list.
// $fn=32; // $fn=32;
// c1 = [3,4]; r1 = 2; // cp1 = [3,4]; r1 = 2;
// c2 = [7,10]; r2 = 3; // cp2 = [7,10]; r2 = 3;
// pts = circle_circle_intersection(c1,r1,c2,r2); // pts = circle_circle_intersection(r1, cp1, r2, cp2);
// move(c1) stroke(circle(r=r1), width=0.2, closed=true); // move(cp1) stroke(circle(r=r1), width=0.2, closed=true);
// move(c2) stroke(circle(r=r2), width=0.2, closed=true); // move(cp2) stroke(circle(r=r2), width=0.2, closed=true);
// color("red")move_copies(pts) circle(r=.2); // pts is [] // color("red") move_copies(pts) circle(r=.3);
function circle_circle_intersection(c1,r1,c2,r2,eps=EPSILON,d1,d2) = function circle_circle_intersection(r1, cp1, r2, cp2, eps=EPSILON, d1, d2) =
assert( is_path([c1,c2],dim=2), "Invalid center point(s)." ) assert( is_path([cp1,cp2],dim=2), "Invalid center point(s)." )
let( let(
r1 = get_radius(r1=r1,d1=d1), r1 = get_radius(r1=r1,d1=d1),
r2 = get_radius(r1=r2,d1=d2), r2 = get_radius(r1=r2,d1=d2),
d = norm(c2-c1), d = norm(cp2-cp1),
a = (c2-c1)/d, a = (cp2-cp1)/d,
b = [-a.y,a.x], b = [-a.y,a.x],
L = (r1^2-r2^2+d^2)/2/d, L = (r1^2-r2^2+d^2)/2/d,
hsqr = r1^2-L^2 hsqr = r1^2-L^2
) )
approx(hsqr,0,eps) ? [L*a+c1] approx(hsqr,0,eps) ? [L*a+cp1]
: hsqr<0 ? [] : hsqr<0 ? []
: let(h=sqrt(hsqr)) : let(h=sqrt(hsqr))
[L*a+h*b+c1, L*a-h*b+c1]; [L*a+h*b+cp1, L*a-h*b+cp1];
// Function: circle_2tangents() // Function: circle_2tangents()
// Usage: // Usage:
// circ = circle_2tangents(pt1, pt2, pt3, r|d=, [tangents=]); // circ = circle_2tangents(r|d=, pt1, pt2, pt3, [tangents=]);
// circ = circle_2tangents(r|d=, [PT1, PT2, PT3], [tangents=]);
// Topics: Geometry, Circles, Tangents // Topics: Geometry, Circles, Tangents
// Description: // Description:
// Given a pair of rays with a common origin, and a known circle radius/diameter, finds // Given a pair of rays with a common origin, and a known circle radius/diameter, finds
@ -1017,7 +1018,7 @@ function circle_circle_intersection(c1,r1,c2,r2,eps=EPSILON,d1,d2) =
// Figure(3D,Med,NoAxes,VPD=130,VPT=[29,19,3],VPR=[55,0,25]): // Figure(3D,Med,NoAxes,VPD=130,VPT=[29,19,3],VPR=[55,0,25]):
// pts = [[45,10,-5], [10,5,10], [15,40,5]]; // pts = [[45,10,-5], [10,5,10], [15,40,5]];
// rad = 15; // rad = 15;
// circ = circle_2tangents(pt1=pts[0], pt2=pts[1], pt3=pts[2], r=rad, tangents=true); // circ = circle_2tangents(r=rad, pt1=pts[0], pt2=pts[1], pt3=pts[2], tangents=true);
// cp = circ[0]; n = circ[1]; tp1 = circ[2]; tp2 = circ[3]; // cp = circ[0]; n = circ[1]; tp1 = circ[2]; tp2 = circ[3];
// color("yellow") stroke(pts, endcaps="arrow2"); // color("yellow") stroke(pts, endcaps="arrow2");
// color("purple") move_copies([cp,tp1,tp2]) sphere(d=2, $fn=12); // color("purple") move_copies([cp,tp1,tp2]) sphere(d=2, $fn=12);
@ -1034,32 +1035,32 @@ function circle_circle_intersection(c1,r1,c2,r2,eps=EPSILON,d1,d2) =
// ["TanPt2", "brown", 2.0, [-5, 0, 2], tp2], // ["TanPt2", "brown", 2.0, [-5, 0, 2], tp2],
// ]; // ];
// for(l=labels) // for(l=labels)
// color(l[1]) move(l[4]+l[3]) rot($vpr) // color(l[1]) move(l[4]+l[3]) rot([55,0,25])
// linear_extrude(height=0.1) // linear_extrude(height=0.1)
// text(text=l[0], size=l[2], halign="center", valign="center"); // text(text=l[0], size=l[2], halign="center", valign="center");
// color("green",0.5) move(cp) cyl(h=0.1, r=rad, orient=n, $fn=36); // color("green",0.5) move(cp) cyl(h=0.1, r=rad, orient=n, $fn=36);
// Arguments: // Arguments:
// r = The radius of the circle to find.
// pt1 = A point that the first ray passes though. // pt1 = A point that the first ray passes though.
// pt2 = The starting point of both rays. // pt2 = The starting point of both rays.
// pt3 = A point that the second ray passes though. // pt3 = A point that the second ray passes though.
// r = The radius of the circle to find.
// --- // ---
// d = The diameter of the circle to find. // d = The diameter of the circle to find.
// tangents = If true, extended information about the tangent points is calculated and returned. Default: false // tangents = If true, extended information about the tangent points is calculated and returned. Default: false
// Example(2D): // Example(2D):
// pts = [[40,40], [10,10], [55,5]]; rad = 10; // pts = [[40,40], [10,10], [55,5]]; rad = 10;
// circ = circle_2tangents(pt1=pts[0], pt2=pts[1], pt3=pts[2], r=rad); // circ = circle_2tangents(r=rad, pt1=pts[0], pt2=pts[1], pt3=pts[2]);
// stroke(pts, endcaps="arrow2"); // stroke(pts, endcaps="arrow2");
// color("red") move(circ[0]) circle(r=rad); // color("red") move(circ[0]) circle(r=rad);
// Example(2D): // Example(2D):
// pts = [[20,40], [10,10], [55,20]]; rad = 10; // pts = [[20,40], [10,10], [55,20]]; rad = 10;
// circ = circle_2tangents(pt1=pts[0], pt2=pts[1], pt3=pts[2], r=rad, tangents=true); // circ = circle_2tangents(r=rad, pt1=pts[0], pt2=pts[1], pt3=pts[2], tangents=true);
// stroke(pts, endcaps="arrow2"); // stroke(pts, endcaps="arrow2");
// color("red") move(circ[0]) circle(r=rad); // color("red") move(circ[0]) circle(r=rad);
// color("blue") move_copies(select(circ,2,3)) circle(d=2); // color("blue") move_copies(select(circ,2,3)) circle(d=2);
// Example(3D): Fit into 3D path corner. // Example(3D): Fit into 3D path corner.
// pts = [[45,5,10], [10,10,15], [30,40,30]]; rad = 10; // pts = [[45,5,10], [10,10,15], [30,40,30]]; rad = 10;
// circ = circle_2tangents(pt1=pts[0], pt2=pts[1], pt3=pts[2], r=rad); // circ = circle_2tangents(r=rad, pt1=pts[0], pt2=pts[1], pt3=pts[2]);
// stroke(pts, endcaps="arrow2"); // stroke(pts, endcaps="arrow2");
// color("red") move(circ[0]) cyl(h=10, r=rad, orient=circ[1]); // color("red") move(circ[0]) cyl(h=10, r=rad, orient=circ[1]);
// Example(3D): // Example(3D):
@ -1067,17 +1068,17 @@ function circle_circle_intersection(c1,r1,c2,r2,eps=EPSILON,d1,d2) =
// stroke(path, closed=true); // stroke(path, closed=true);
// for (i = [0:1:5]) { // for (i = [0:1:5]) {
// crn = select(path, i*2-1, i*2+1); // crn = select(path, i*2-1, i*2+1);
// ci = circle_2tangents(crn[0], crn[1], crn[2], r=5); // ci = circle_2tangents(r=5, crn[0], crn[1], crn[2]);
// move(ci[0]) cyl(h=10,r=5,,orient=ci[1]); // move(ci[0]) cyl(h=10,r=5,,orient=ci[1]);
// } // }
function circle_2tangents(pt1, pt2, pt3, r, d, tangents=false) = function circle_2tangents(r, pt1, pt2, pt3, tangents=false, d) =
let(r = get_radius(r=r, d=d, dflt=undef)) let(r = get_radius(r=r, d=d, dflt=undef))
assert(r!=undef, "Must specify either r or d.") assert(r!=undef, "Must specify either r or d.")
assert( ( is_path(pt1) && len(pt1)==3 && is_undef(pt2) && is_undef(pt3)) assert( ( is_path(pt1) && len(pt1)==3 && is_undef(pt2) && is_undef(pt3))
|| (is_matrix([pt1,pt2,pt3]) && (len(pt1)==2 || len(pt1)==3) ), || (is_matrix([pt1,pt2,pt3]) && (len(pt1)==2 || len(pt1)==3) ),
"Invalid input points." ) "Invalid input points." )
is_undef(pt2) is_undef(pt2)
? circle_2tangents(pt1[0], pt1[1], pt1[2], r=r, tangents=tangents) ? circle_2tangents(r, pt1[0], pt1[1], pt1[2], tangents=tangents)
: is_collinear(pt1, pt2, pt3)? undef : : is_collinear(pt1, pt2, pt3)? undef :
let( let(
v1 = unit(pt1 - pt2), v1 = unit(pt1 - pt2),
@ -1100,7 +1101,7 @@ function circle_2tangents(pt1, pt2, pt3, r, d, tangents=false) =
// Function: circle_3points() // Function: circle_3points()
// Usage: // Usage:
// circ = circle_3points(pt1, pt2, pt3); // circ = circle_3points(pt1, pt2, pt3);
// circ = circle_3points([pt1, pt2, pt3]); // circ = circle_3points([PT1, PT2, PT3]);
// Topics: Geometry, Circles // Topics: Geometry, Circles
// Description: // Description:
// Returns the [CENTERPOINT, RADIUS, NORMAL] of the circle that passes through three non-collinear // Returns the [CENTERPOINT, RADIUS, NORMAL] of the circle that passes through three non-collinear
@ -1109,8 +1110,8 @@ function circle_2tangents(pt1, pt2, pt3, r, d, tangents=false) =
// points are 2D, then the resulting centerpoint will be 2D, and the normal will be UP ([0,0,1]). // points are 2D, then the resulting centerpoint will be 2D, and the normal will be UP ([0,0,1]).
// If any of the points are 3D, then the resulting centerpoint will be 3D. If the three points are // If any of the points are 3D, then the resulting centerpoint will be 3D. If the three points are
// collinear, then `[undef,undef,undef]` will be returned. The normal will be a normalized 3D // collinear, then `[undef,undef,undef]` will be returned. The normal will be a normalized 3D
// vector with a non-negative Z axis. // vector with a non-negative Z axis. Instead of 3 arguments, it is acceptable to input the 3 points
// Instead of 3 arguments, it is acceptable to input the 3 points in a list `pt1`, leaving `pt2`and `pt3` as undef. // as a list given in `pt1`, leaving `pt2`and `pt3` as undef.
// Arguments: // Arguments:
// pt1 = The first point. // pt1 = The first point.
// pt2 = The second point. // pt2 = The second point.
@ -1186,7 +1187,7 @@ function circle_point_tangents(r, cp, pt, d) =
// Function: circle_circle_tangents() // Function: circle_circle_tangents()
// Usage: // Usage:
// segs = circle_circle_tangents(c1, r1|d1=, c2, r2|d2=); // segs = circle_circle_tangents(r1|d1=, cp1, r2|d2=, cp2);
// Topics: Geometry, Circles, Tangents // Topics: Geometry, Circles, Tangents
// Description: // Description:
// Computes 2d lines tangents to a pair of circles in 2d. Returns a list of line endpoints [p1,p2] where // Computes 2d lines tangents to a pair of circles in 2d. Returns a list of line endpoints [p1,p2] where
@ -1200,54 +1201,54 @@ function circle_point_tangents(r, cp, pt, d) =
// so the function returns the empty set. When the circles are tangent a degenerate tangent line // so the function returns the empty set. When the circles are tangent a degenerate tangent line
// passes through the point of tangency of the two circles: this degenerate line is NOT returned. // passes through the point of tangency of the two circles: this degenerate line is NOT returned.
// Arguments: // Arguments:
// c1 = Center of the first circle.
// r1 = Radius of the first circle. // r1 = Radius of the first circle.
// c2 = Center of the second circle. // cp1 = Centerpoint of the first circle.
// r2 = Radius of the second circle. // r2 = Radius of the second circle.
// cp2 = Centerpoint of the second circle.
// --- // ---
// d1 = Diameter of the first circle. // d1 = Diameter of the first circle.
// d2 = Diameter of the second circle. // d2 = Diameter of the second circle.
// Example(2D,NoAxes): Four tangents, first in green, second in black, third in blue, last in red. // Example(2D,NoAxes): Four tangents, first in green, second in black, third in blue, last in red.
// $fn=32; // $fn=32;
// c1 = [3,4]; r1 = 2; // cp1 = [3,4]; r1 = 2;
// c2 = [7,10]; r2 = 3; // cp2 = [7,10]; r2 = 3;
// pts = circle_circle_tangents(c1,r1,c2,r2); // pts = circle_circle_tangents(r1, cp1, r2, cp2);
// move(c1) stroke(circle(r=r1), width=0.2, closed=true); // move(cp1) stroke(circle(r=r1), width=0.2, closed=true);
// move(c2) stroke(circle(r=r2), width=0.2, closed=true); // move(cp2) stroke(circle(r=r2), width=0.2, closed=true);
// colors = ["green","black","blue","red"]; // colors = ["green","black","blue","red"];
// for(i=[0:len(pts)-1]) color(colors[i]) stroke(pts[i],width=0.2); // for(i=[0:len(pts)-1]) color(colors[i]) stroke(pts[i],width=0.2);
// Example(2D,NoAxes): Circles overlap so only exterior tangents exist. // Example(2D,NoAxes): Circles overlap so only exterior tangents exist.
// $fn=32; // $fn=32;
// c1 = [4,4]; r1 = 3; // cp1 = [4,4]; r1 = 3;
// c2 = [7,7]; r2 = 2; // cp2 = [7,7]; r2 = 2;
// pts = circle_circle_tangents(c1,r1,c2,r2); // pts = circle_circle_tangents(r1, cp1, r2, cp2);
// move(c1) stroke(circle(r=r1), width=0.2, closed=true); // move(cp1) stroke(circle(r=r1), width=0.2, closed=true);
// move(c2) stroke(circle(r=r2), width=0.2, closed=true); // move(cp2) stroke(circle(r=r2), width=0.2, closed=true);
// colors = ["green","black","blue","red"]; // colors = ["green","black","blue","red"];
// for(i=[0:len(pts)-1]) color(colors[i]) stroke(pts[i],width=0.2); // for(i=[0:len(pts)-1]) color(colors[i]) stroke(pts[i],width=0.2);
// Example(2D,NoAxes): Circles are tangent. Only exterior tangents are returned. The degenerate internal tangent is not returned. // Example(2D,NoAxes): Circles are tangent. Only exterior tangents are returned. The degenerate internal tangent is not returned.
// $fn=32; // $fn=32;
// c1 = [4,4]; r1 = 4; // cp1 = [4,4]; r1 = 4;
// c2 = [4,10]; r2 = 2; // cp2 = [4,10]; r2 = 2;
// pts = circle_circle_tangents(c1,r1,c2,r2); // pts = circle_circle_tangents(r1, cp1, r2, cp2);
// move(c1) stroke(circle(r=r1), width=0.2, closed=true); // move(cp1) stroke(circle(r=r1), width=0.2, closed=true);
// move(c2) stroke(circle(r=r2), width=0.2, closed=true); // move(cp2) stroke(circle(r=r2), width=0.2, closed=true);
// colors = ["green","black","blue","red"]; // colors = ["green","black","blue","red"];
// for(i=[0:1:len(pts)-1]) color(colors[i]) stroke(pts[i],width=0.2); // for(i=[0:1:len(pts)-1]) color(colors[i]) stroke(pts[i],width=0.2);
// Example(2D,NoAxes): One circle is inside the other: no tangents exist. If the interior circle is tangent the single degenerate tangent will not be returned. // Example(2D,NoAxes): One circle is inside the other: no tangents exist. If the interior circle is tangent the single degenerate tangent will not be returned.
// $fn=32; // $fn=32;
// c1 = [4,4]; r1 = 4; // cp1 = [4,4]; r1 = 4;
// c2 = [5,5]; r2 = 2; // cp2 = [5,5]; r2 = 2;
// pts = circle_circle_tangents(c1,r1,c2,r2); // pts = circle_circle_tangents(r1, cp1, r2, cp2);
// move(c1) stroke(circle(r=r1), width=0.2, closed=true); // move(cp1) stroke(circle(r=r1), width=0.2, closed=true);
// move(c2) stroke(circle(r=r2), width=0.2, closed=true); // move(cp2) stroke(circle(r=r2), width=0.2, closed=true);
// echo(pts); // Returns [] // echo(pts); // Returns []
function circle_circle_tangents(c1,r1,c2,r2,d1,d2) = function circle_circle_tangents(r1, cp1, r2, cp2, d1, d2) =
assert( is_path([c1,c2],dim=2), "Invalid center point(s)." ) assert( is_path([cp1,cp2],dim=2), "Invalid center point(s)." )
let( let(
r1 = get_radius(r1=r1,d1=d1), r1 = get_radius(r1=r1,d1=d1),
r2 = get_radius(r1=r2,d1=d2), r2 = get_radius(r1=r2,d1=d2),
Rvals = [r2-r1, r2-r1, -r2-r1, -r2-r1]/norm(c1-c2), Rvals = [r2-r1, r2-r1, -r2-r1, -r2-r1]/norm(cp1-cp2),
kvals = [-1,1,-1,1], kvals = [-1,1,-1,1],
ext = [1,1,-1,-1], ext = [1,1,-1,-1],
N = 1-sqr(Rvals[2])>=0 ? 4 : N = 1-sqr(Rvals[2])>=0 ? 4 :
@ -1256,13 +1257,13 @@ function circle_circle_tangents(c1,r1,c2,r2,d1,d2) =
for(i=[0:1:N-1]) [ for(i=[0:1:N-1]) [
[Rvals[i], -kvals[i]*sqrt(1-sqr(Rvals[i]))], [Rvals[i], -kvals[i]*sqrt(1-sqr(Rvals[i]))],
[kvals[i]*sqrt(1-sqr(Rvals[i])), Rvals[i]] [kvals[i]*sqrt(1-sqr(Rvals[i])), Rvals[i]]
] * unit(c2-c1) ] * unit(cp2-cp1)
] ]
) [ ) [
for(i=[0:1:N-1]) let( for(i=[0:1:N-1]) let(
pt = [ pt = [
c1-r1*coef[i], cp1-r1*coef[i],
c2-ext[i]*r2*coef[i] cp2-ext[i]*r2*coef[i]
] ]
) if (pt[0]!=pt[1]) pt ) if (pt[0]!=pt[1]) pt
]; ];
@ -1311,15 +1312,15 @@ function _noncollinear_triple(points,error=true,eps=EPSILON) =
// Function: sphere_line_intersection() // Function: sphere_line_intersection()
// Usage: // Usage:
// isect = sphere_line_intersection(c,r|d=,line,[bounded],[eps=]); // isect = sphere_line_intersection(r|d=, cp, line, [bounded], [eps=]);
// Topics: Geometry, Spheres, Lines, Intersection // Topics: Geometry, Spheres, Lines, Intersection
// Description: // Description:
// Find intersection points between a sphere and a line, ray or segment specified by two points. // Find intersection points between a sphere and a line, ray or segment specified by two points.
// By default the line is unbounded. // By default the line is unbounded.
// Arguments: // Arguments:
// c = center of sphere // r = Radius of sphere
// r = radius of sphere // cp = Centerpoint of sphere
// line = two points defining the line // line = Two points defining the 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 // 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
// --- // ---
// d = diameter of sphere // d = diameter of sphere
@ -1327,14 +1328,14 @@ function _noncollinear_triple(points,error=true,eps=EPSILON) =
// Example(3D): // Example(3D):
// cp = [10,20,5]; r = 40; // cp = [10,20,5]; r = 40;
// line = [[-50,-10,25], [70,0,40]]; // line = [[-50,-10,25], [70,0,40]];
// isects = sphere_line_intersection(c=cp, r=r, line=line); // isects = sphere_line_intersection(r=r, cp=cp, line=line);
// color("cyan") stroke(line); // color("cyan") stroke(line);
// move(cp) sphere(r=r, $fn=72); // move(cp) sphere(r=r, $fn=72);
// color("red") move_copies(isects) sphere(d=3, $fn=12); // color("red") move_copies(isects) sphere(d=3, $fn=12);
function sphere_line_intersection(c,r,line,bounded=false,d,eps=EPSILON) = function sphere_line_intersection(r, cp, line, bounded=false, d, eps=EPSILON) =
assert(_valid_line(line,3), "Invalid 3d line.") assert(_valid_line(line,3), "Invalid 3d line.")
assert(is_vector(c,3), "Sphere center must be a 3-vector") assert(is_vector(cp,3), "Sphere center must be a 3-vector")
_circle_or_sphere_line_intersection(c,r,line,bounded,d,eps); _circle_or_sphere_line_intersection(r, cp, line, bounded, d, eps);

View file

@ -963,7 +963,7 @@ module rabbit_clip(type, length, width, snap, thickness, depth, compression=0.1
scaled_len = length - 0.5 * (earwidth * snap + point_length * length) / sqrt(sqr(snap)+sqr(length/2)); scaled_len = length - 0.5 * (earwidth * snap + point_length * length) / sqrt(sqr(snap)+sqr(length/2));
bottom_pt = [0,max(scaled_len*0.15+thickness, 2*thickness)]; bottom_pt = [0,max(scaled_len*0.15+thickness, 2*thickness)];
ctr = [width/2,scaled_len] + line_normal([width/2-snap, scaled_len/2], [width/2, scaled_len]) * earwidth/2; ctr = [width/2,scaled_len] + line_normal([width/2-snap, scaled_len/2], [width/2, scaled_len]) * earwidth/2;
inside_pt = circle_circle_tangents(bottom_pt, 0, ctr, earwidth/2)[0][1]; inside_pt = circle_circle_tangents(0, bottom_pt, earwidth/2, ctr)[0][1];
sidepath =[ sidepath =[
[width/2,0], [width/2,0],
[width/2-snap,scaled_len/2], [width/2-snap,scaled_len/2],

View file

@ -3281,7 +3281,7 @@ function _fix_angle_list(list,ind=0, result=[]) =
function _cyl_line_intersection(R, line, ref) = function _cyl_line_intersection(R, line, ref) =
let( let(
line2d = path2d(line), line2d = path2d(line),
cisect = circle_line_intersection([0,0], r=R, line= line2d) cisect = circle_line_intersection(r=R, cp=[0,0], line=line2d)
) )
len(cisect)<2 ? [] : len(cisect)<2 ? [] :
let( let(
@ -3300,7 +3300,7 @@ function _cyl_line_intersection(R, line, ref) =
function _sphere_line_isect_best(R, line, ref) = function _sphere_line_isect_best(R, line, ref) =
let( let(
pts = sphere_line_intersection([0,0,0],abs(R), line=line) pts = sphere_line_intersection(abs(R), [0,0,0], line=line)
) )
len(pts)<2 ? [] : len(pts)<2 ? [] :
let( let(

View file

@ -254,7 +254,7 @@ function circle(r, d, points, corner, anchor=CENTER, spin=0) =
assert(is_undef(points), "Cannot specify points= when corner= is given.") assert(is_undef(points), "Cannot specify points= when corner= is given.")
let( let(
r = get_radius(r=r, d=d, dflt=1), r = get_radius(r=r, d=d, dflt=1),
c = circle_2tangents(pt1=corner[0], pt2=corner[1], pt3=corner[2], r=r) c = circle_2tangents(r=r, pt1=corner[0], pt2=corner[1], pt3=corner[2])
) )
assert(c!=undef, "Corner path cannot be collinear.") assert(c!=undef, "Corner path cannot be collinear.")
let( cp = c[0] ) let( cp = c[0] )
@ -283,7 +283,7 @@ module circle(r, d, points, corner, anchor=CENTER, spin=0) {
} }
} else if (is_path(corner)) { } else if (is_path(corner)) {
r = get_radius(r=r, d=d, dflt=1); r = get_radius(r=r, d=d, dflt=1);
c = circle_2tangents(pt1=corner[0], pt2=corner[1], pt3=corner[2], r=r); c = circle_2tangents(r=r, pt1=corner[0], pt2=corner[1], pt3=corner[2]);
check = assert(c != undef && c[0] != undef, "Points must not be collinear."); check = assert(c != undef && c[0] != undef, "Points must not be collinear.");
cp = c[0]; cp = c[0];
translate(cp) { translate(cp) {
@ -1298,7 +1298,7 @@ function egg(length, r1, r2, R, d1, d2, D, anchor=CENTER, spin=0) =
c1 = [-length/2+r1,0], c1 = [-length/2+r1,0],
c2 = [length/2-r2,0], c2 = [length/2-r2,0],
Rmin = (r1+r2+norm(c1-c2))/2, Rmin = (r1+r2+norm(c1-c2))/2,
Mlist = circle_circle_intersection(c1,R-r1,c2,R-r2), Mlist = circle_circle_intersection(R-r1, c1, R-r2, c2),
arcparms = reverse([for(M=Mlist) [M, c1+r1*unit(c1-M), c2+r2*unit(c2-M)]]), arcparms = reverse([for(M=Mlist) [M, c1+r1*unit(c1-M), c2+r2*unit(c2-M)]]),
path = concat( path = concat(
arc(r=r2, cp=c2, points=[[length/2,0],arcparms[0][2]],endpoint=false), arc(r=r2, cp=c2, points=[[length/2,0],arcparms[0][2]],endpoint=false),

View file

@ -1223,7 +1223,7 @@ module cyl(
) [p1,p2] ) [p1,p2]
) : !is_undef(fil2)? ( ) : !is_undef(fil2)? (
let( let(
cn = circle_2tangents([r2-fil2,l/2], [r2,l/2], [r1,-l/2], r=abs(fil2)), cn = circle_2tangents(r=abs(fil2), [r2-fil2,l/2], [r2,l/2], [r1,-l/2]),
ang = fil2<0? phi : phi-180, ang = fil2<0? phi : phi-180,
steps = ceil(abs(ang)/360*segs(abs(fil2))), steps = ceil(abs(ang)/360*segs(abs(fil2))),
step = ang/steps, step = ang/steps,
@ -1238,7 +1238,7 @@ module cyl(
) [p1,p2] ) [p1,p2]
) : !is_undef(fil1)? ( ) : !is_undef(fil1)? (
let( let(
cn = circle_2tangents([r1-fil1,-l/2], [r1,-l/2], [r2,l/2], r=abs(fil1)), cn = circle_2tangents(r=abs(fil1), [r1-fil1,-l/2], [r1,-l/2], [r2,l/2]),
ang = fil1<0? 180-phi : -phi, ang = fil1<0? 180-phi : -phi,
steps = ceil(abs(ang)/360*segs(abs(fil1))), steps = ceil(abs(ang)/360*segs(abs(fil1))),
step = ang/steps, step = ang/steps,
@ -2706,19 +2706,21 @@ module path_text(path, text, font, size, thickness, lettersize, offset=0, revers
// //
// Example: // Example:
// union() { // union() {
// translate([0,2,-4]) // translate([0,2,-4])
// cube([20, 4, 24], anchor=BOTTOM); // cube([20, 4, 24], anchor=BOTTOM);
// translate([0,-10,-4]) // translate([0,-10,-4])
// cube([20, 20, 4], anchor=BOTTOM); // cube([20, 20, 4], anchor=BOTTOM);
// color("green") // color("green")
// interior_fillet( // interior_fillet(
// l=20, r=10, // l=20, r=10,
// spin=180, orient=RIGHT // spin=180, orient=RIGHT
// ); // );
// } // }
// //
// Example: // Examples:
// interior_fillet(l=40, r=10, spin=-90); // interior_fillet(l=10, r=20, ang=60);
// interior_fillet(l=10, r=20, ang=90);
// interior_fillet(l=10, r=20, ang=120);
// //
// Example: Using with Attachments // Example: Using with Attachments
// cube(50,center=true) { // cube(50,center=true) {
@ -2727,19 +2729,22 @@ module path_text(path, text, font, size, thickness, lettersize, offset=0, revers
// position(BOT+FRONT) // position(BOT+FRONT)
// interior_fillet(l=50, r=10, spin=180, orient=RIGHT); // interior_fillet(l=50, r=10, spin=180, orient=RIGHT);
// } // }
module interior_fillet(l=1.0, r, ang=90, overlap=0.01, d, anchor=FRONT+LEFT, spin=0, orient=UP) { module interior_fillet(l=1.0, r, ang=90, overlap=0.01, d, anchor=CENTER, spin=0, orient=UP) {
r = get_radius(r=r, d=d, dflt=1); r = get_radius(r=r, d=d, dflt=1);
dy = r/tan(ang/2); steps = ceil(segs(r)*(180-ang)/360);
steps = ceil(segs(r)*ang/360); arc = arc(n=steps+1, r=r, corner=[polar_to_xy(r,ang),[0,0],[r,0]]);
step = ang/steps; maxx = last(arc).x;
attachable(anchor,spin,orient, size=[r,r,l]) { maxy = arc[0].y;
path = [
[maxx, -overlap],
polar_to_xy(overlap, 180+ang/2),
arc[0] + polar_to_xy(overlap, 90+ang),
each arc
];
attachable(anchor,spin,orient, size=[2*maxx,2*maxy,l]) {
if (l > 0) { if (l > 0) {
linear_extrude(height=l, convexity=4, center=true) { linear_extrude(height=l, convexity=4, center=true) {
path = concat( polygon(path);
[[0,0]],
[for (i=[0:1:steps]) let(a=270-i*step) r*[cos(a),sin(a)]+[dy,r]]
);
translate(-[r,r]/2) polygon(path);
} }
} }
children(); children();

View file

@ -424,15 +424,15 @@ module test_line_closest_point() {
module test_circle_2tangents() { module test_circle_2tangents() {
//** missing tests with arg tangent=true //** missing tests with arg tangent=true
assert(approx(circle_2tangents([10,10],[0,0],[10,-10],r=10/sqrt(2))[0],[10,0])); assert(approx(circle_2tangents(r=10/sqrt(2),[10,10],[0,0],[10,-10])[0], [10,0]));
assert(approx(circle_2tangents([-10,10],[0,0],[-10,-10],r=10/sqrt(2))[0],[-10,0])); assert(approx(circle_2tangents(r=10/sqrt(2),[-10,10],[0,0],[-10,-10])[0], [-10,0]));
assert(approx(circle_2tangents([-10,10],[0,0],[10,10],r=10/sqrt(2))[0],[0,10])); assert(approx(circle_2tangents(r=10/sqrt(2),[-10,10],[0,0],[10,10])[0], [0,10]));
assert(approx(circle_2tangents([-10,-10],[0,0],[10,-10],r=10/sqrt(2))[0],[0,-10])); assert(approx(circle_2tangents(r=10/sqrt(2),[-10,-10],[0,0],[10,-10])[0], [0,-10]));
assert(approx(circle_2tangents([0,10],[0,0],[10,0],r=10)[0],[10,10])); assert(approx(circle_2tangents(r=10,[0,10],[0,0],[10,0])[0], [10,10]));
assert(approx(circle_2tangents([10,0],[0,0],[0,-10],r=10)[0],[10,-10])); assert(approx(circle_2tangents(r=10,[10,0],[0,0],[0,-10])[0], [10,-10]));
assert(approx(circle_2tangents([0,-10],[0,0],[-10,0],r=10)[0],[-10,-10])); assert(approx(circle_2tangents(r=10,[0,-10],[0,0],[-10,0])[0], [-10,-10]));
assert(approx(circle_2tangents([-10,0],[0,0],[0,10],r=10)[0],[-10,10])); assert(approx(circle_2tangents(r=10,[-10,0],[0,0],[0,10])[0], [-10,10]));
assert_approx(circle_2tangents(polar_to_xy(10,60),[0,0],[10,0],r=10)[0],polar_to_xy(20,30)); assert_approx(circle_2tangents(r=10,polar_to_xy(10,60),[0,0],[10,0])[0], polar_to_xy(20,30));
} }
*test_circle_2tangents(); *test_circle_2tangents();

View file

@ -183,10 +183,10 @@ module thinning_wall(h=50, l=100, thick=5, ang=30, braces=false, strut, wall, an
wall = is_num(wall)? wall : thick/2; wall = is_num(wall)? wall : thick/2;
bevel_h = strut + (thick-wall)/2/tan(ang); bevel_h = strut + (thick-wall)/2/tan(ang);
cp1 = circle_2tangents([0,0,+h/2], [l2/2,0,+h/2], [l1/2,0,-h/2], r=strut)[0]; cp1 = circle_2tangents(r=strut, [0,0,+h/2], [l2/2,0,+h/2], [l1/2,0,-h/2])[0];
cp2 = circle_2tangents([0,0,+h/2], [l2/2,0,+h/2], [l1/2,0,-h/2], r=bevel_h)[0]; cp2 = circle_2tangents(r=bevel_h, [0,0,+h/2], [l2/2,0,+h/2], [l1/2,0,-h/2])[0];
cp3 = circle_2tangents([0,0,-h/2], [l1/2,0,-h/2], [l2/2,0,+h/2], r=bevel_h)[0]; cp3 = circle_2tangents(r=bevel_h, [0,0,-h/2], [l1/2,0,-h/2], [l2/2,0,+h/2])[0];
cp4 = circle_2tangents([0,0,-h/2], [l1/2,0,-h/2], [l2/2,0,+h/2], r=strut)[0]; cp4 = circle_2tangents(r=strut, [0,0,-h/2], [l1/2,0,-h/2], [l2/2,0,+h/2])[0];
z1 = h/2; z1 = h/2;
z2 = cp1.z; z2 = cp1.z;