mirror of
https://github.com/BelfrySCAD/BOSL2.git
synced 2024-12-29 00:09:41 +00:00
Added corner= and points= to circle(), arc().
This commit is contained in:
parent
4404dfde45
commit
837358d44c
3 changed files with 258 additions and 143 deletions
99
drawing.scad
99
drawing.scad
|
@ -606,6 +606,8 @@ module dashed_stroke(path, dashpat=[3,3], width=1, closed=false) {
|
|||
// path=arc(n, cp=, points=[P0,P1], [long=], [cw=], [ccw=]);
|
||||
// Usage: 2D or 3D arc, starting at `P0`, passing through `P1` and ending at `P2`.
|
||||
// path=arc(n, points=[P0,P1,P2]);
|
||||
// Usage: 2D or 3D arc, fron tangent point on segment `[P0,P1]` to the tangent point on segment `[P1,P2]`.
|
||||
// path=arc(n, corner=[P0,P1,P2], r=);
|
||||
// Usage: as module
|
||||
// arc(...) [ATTACHMENTS];
|
||||
// Topics: Paths (2D), Paths (3D), Shapes (2D), Path Generators
|
||||
|
@ -620,6 +622,7 @@ module dashed_stroke(path, dashpat=[3,3], width=1, closed=false) {
|
|||
// d = Diameter of the arc.
|
||||
// cp = Centerpoint of arc.
|
||||
// points = Points on the arc.
|
||||
// corner = A path of two segments to fit an arc tangent to.
|
||||
// long = if given with cp and points takes the long arc instead of the default short arc. Default: false
|
||||
// cw = if given with cp and 2 points takes the arc in the clockwise direction. Default: false
|
||||
// ccw = if given with cp and 2 points takes the arc in the counter-clockwise direction. Default: false
|
||||
|
@ -641,6 +644,7 @@ module dashed_stroke(path, dashpat=[3,3], width=1, closed=false) {
|
|||
// arc(width=60, thickness=20);
|
||||
// arc(cp=[-10,5], points=[[20,10],[0,35]], wedge=true);
|
||||
// arc(points=[[30,-5],[20,10],[-10,20]], wedge=true);
|
||||
// Example(2D): Fit to three points.
|
||||
// arc(points=[[5,30],[-10,-10],[30,5]], wedge=true);
|
||||
// Example(2D):
|
||||
// path = arc(points=[[5,30],[-10,-10],[30,5]], wedge=true);
|
||||
|
@ -648,18 +652,25 @@ module dashed_stroke(path, dashpat=[3,3], width=1, closed=false) {
|
|||
// Example(FlatSpin,VPD=175):
|
||||
// path = arc(points=[[0,30,0],[0,0,30],[30,0,0]]);
|
||||
// stroke(path, dots=true, dots_color="blue");
|
||||
function arc(n, r, angle, d, cp, points, width, thickness, start, wedge=false, long=false, cw=false, ccw=false, endpoint=true) =
|
||||
// Example(2D): Fit to a corner.
|
||||
// pts = [[0,40], [-40,-10], [30,0]];
|
||||
// path = arc(corner=pts, r=20);
|
||||
// stroke(pts, endcaps="arrow2");
|
||||
// stroke(path, endcap2="arrow2", color="blue");
|
||||
function arc(n, r, angle, d, cp, points, corner, width, thickness, start, wedge=false, long=false, cw=false, ccw=false, endpoint=true) =
|
||||
assert(is_bool(endpoint))
|
||||
!endpoint ? assert(!wedge, "endpoint cannot be false if wedge is true")
|
||||
list_head(arc(u_add(n,1),r,angle,d,cp,points,width,thickness,start,wedge,long,cw,ccw,true)) :
|
||||
!endpoint ?
|
||||
assert(!wedge, "endpoint cannot be false if wedge is true")
|
||||
list_head(arc(u_add(n,1),r,angle,d,cp,points,corner,width,thickness,start,wedge,long,cw,ccw,true)) :
|
||||
assert(is_undef(n) || (is_integer(n) && n>=2), "Number of points must be an integer 2 or larger")
|
||||
// First try for 2D arc specified by width and thickness
|
||||
is_def(width) && is_def(thickness)? (
|
||||
assert(!any_defined([r,cp,points]) && !any([cw,ccw,long]),"Conflicting or invalid parameters to arc")
|
||||
assert(width>0, "Width must be postive")
|
||||
assert(thickness>0, "Thickness must be positive")
|
||||
assert(!any_defined([r,cp,points]) && !any([cw,ccw,long]),"Conflicting or invalid parameters to arc")
|
||||
assert(width>0, "Width must be postive")
|
||||
assert(thickness>0, "Thickness must be positive")
|
||||
arc(n,points=[[width/2,0], [0,thickness], [-width/2,0]],wedge=wedge)
|
||||
) : is_def(angle)? (
|
||||
) :
|
||||
is_def(angle)? (
|
||||
let(
|
||||
parmok = !any_defined([points,width,thickness]) &&
|
||||
((is_vector(angle,2) && is_undef(start)) || is_num(angle))
|
||||
|
@ -680,39 +691,71 @@ function arc(n, r, angle, d, cp, points, width, thickness, start, wedge=false, l
|
|||
extra = wedge? [cp] : []
|
||||
)
|
||||
concat(extra,arcpoints)
|
||||
) : is_def(corner)? (
|
||||
assert(is_path(corner,[2,3]),"Point list is invalid")
|
||||
// Arc is 3D, so transform corner to 2D and make a recursive call, then remap back to 3D
|
||||
len(corner[0]) == 3? (
|
||||
assert(!(cw || ccw), "(Counter)clockwise isn't meaningful in 3d, so `cw` and `ccw` must be false")
|
||||
assert(is_undef(cp) || is_vector(cp,3),"corner are 3d so cp must be 3d")
|
||||
let(
|
||||
plane = [is_def(cp) ? cp : corner[2], corner[0], corner[1]],
|
||||
center2d = is_def(cp) ? project_plane(plane,cp) : undef,
|
||||
points2d = project_plane(plane, corner)
|
||||
)
|
||||
lift_plane(plane,arc(n,cp=center2d,corner=points2d,wedge=wedge,long=long))
|
||||
) :
|
||||
assert(is_path(corner) && len(corner) == 3)
|
||||
let(col = is_collinear(corner[0],corner[1],corner[2]))
|
||||
assert(!col, "Collinear inputs do not define an arc")
|
||||
let( r = get_radius(r=r, d=d) )
|
||||
assert(is_finite(r) && r>0, "Must specify r= or d= when corner= is given.")
|
||||
let(
|
||||
ci = circle_2tangents(corner[0], corner[1], corner[2], r=r, tangents=true),
|
||||
cp = ci[0], nrm = ci[1], tp1 = ci[2], tp2 = ci[3],
|
||||
dir = det2([corner[1]-corner[0],corner[2]-corner[1]]) > 0,
|
||||
corner = dir? [tp1,tp2] : [tp2,tp1],
|
||||
theta_start = atan2(corner[0].y-cp.y, corner[0].x-cp.x),
|
||||
theta_end = atan2(corner[1].y-cp.y, corner[1].x-cp.x),
|
||||
angle = posmod(theta_end-theta_start, 360),
|
||||
arcpts = arc(n,cp=cp,r=r,start=theta_start,angle=angle,wedge=wedge)
|
||||
)
|
||||
dir ? arcpts : reverse(arcpts)
|
||||
) :
|
||||
assert(is_path(points,[2,3]),"Point list is invalid")
|
||||
// Arc is 3D, so transform points to 2D and make a recursive call, then remap back to 3D
|
||||
len(points[0])==3? (
|
||||
assert(!(cw || ccw), "(Counter)clockwise isn't meaningful in 3d, so `cw` and `ccw` must be false")
|
||||
assert(is_undef(cp) || is_vector(cp,3),"points are 3d so cp must be 3d")
|
||||
assert(is_path(points,[2,3]),"Point list is invalid")
|
||||
// Arc is 3D, so transform points to 2D and make a recursive call, then remap back to 3D
|
||||
len(points[0]) == 3? (
|
||||
assert(!(cw || ccw), "(Counter)clockwise isn't meaningful in 3d, so `cw` and `ccw` must be false")
|
||||
assert(is_undef(cp) || is_vector(cp,3),"points are 3d so cp must be 3d")
|
||||
let(
|
||||
plane = [is_def(cp) ? cp : points[2], points[0], points[1]],
|
||||
center2d = is_def(cp) ? project_plane(plane,cp) : undef,
|
||||
points2d = project_plane(plane, points)
|
||||
)
|
||||
lift_plane(plane,arc(n,cp=center2d,points=points2d,wedge=wedge,long=long))
|
||||
) : is_def(cp)? (
|
||||
) :
|
||||
is_def(cp)? (
|
||||
// Arc defined by center plus two points, will have radius defined by center and points[0]
|
||||
// and extent defined by direction of point[1] from the center
|
||||
assert(is_vector(cp,2), "Centerpoint must be a 2d vector")
|
||||
assert(len(points)==2, "When pointlist has length 3 centerpoint is not allowed")
|
||||
assert(points[0]!=points[1], "Arc endpoints are equal")
|
||||
assert(cp!=points[0]&&cp!=points[1], "Centerpoint equals an arc endpoint")
|
||||
assert(count_true([long,cw,ccw])<=1, str("Only one of `long`, `cw` and `ccw` can be true",cw,ccw,long))
|
||||
assert(is_vector(cp,2), "Centerpoint must be a 2d vector")
|
||||
assert(len(points)==2, "When pointlist has length 3 centerpoint is not allowed")
|
||||
assert(points[0]!=points[1], "Arc endpoints are equal")
|
||||
assert(cp!=points[0]&&cp!=points[1], "Centerpoint equals an arc endpoint")
|
||||
assert(count_true([long,cw,ccw])<=1, str("Only one of `long`, `cw` and `ccw` can be true",cw,ccw,long))
|
||||
let(
|
||||
angle = vector_angle(points[0], cp, points[1]),
|
||||
v1 = points[0]-cp,
|
||||
v2 = points[1]-cp,
|
||||
prelim_dir = sign(det2([v1,v2])), // z component of cross product
|
||||
dir = prelim_dir != 0
|
||||
? prelim_dir
|
||||
: assert(cw || ccw, "Collinear inputs don't define a unique arc")
|
||||
1,
|
||||
r=norm(v1),
|
||||
final_angle = long || (ccw && dir<0) || (cw && dir>0) ? -dir*(360-angle) : dir*angle
|
||||
prelim_dir = sign(det2([v1,v2])), // z component of cross product
|
||||
dir = prelim_dir != 0 ? prelim_dir :
|
||||
assert(cw || ccw, "Collinear inputs don't define a unique arc")
|
||||
1,
|
||||
r = norm(v1),
|
||||
final_angle = long || (ccw && dir<0) || (cw && dir>0) ?
|
||||
-dir*(360-angle) :
|
||||
dir*angle,
|
||||
sa = atan2(v1.y,v1.x)
|
||||
)
|
||||
arc(n,cp=cp,r=r,start=atan2(v1.y,v1.x),angle=final_angle,wedge=wedge)
|
||||
arc(n,cp=cp,r=r,start=sa,angle=final_angle,wedge=wedge)
|
||||
) : (
|
||||
// Final case is arc passing through three points, starting at point[0] and ending at point[3]
|
||||
let(col = is_collinear(points[0],points[1],points[2]))
|
||||
|
@ -732,9 +775,9 @@ function arc(n, r, angle, d, cp, points, width, thickness, start, wedge=false, l
|
|||
);
|
||||
|
||||
|
||||
module arc(n, r, angle, d, cp, points, width, thickness, start, wedge=false, anchor=CENTER, spin=0)
|
||||
module arc(n, r, angle, d, cp, points, corner, width, thickness, start, wedge=false, anchor=CENTER, spin=0)
|
||||
{
|
||||
path = arc(n=n, r=r, angle=angle, d=d, cp=cp, points=points, width=width, thickness=thickness, start=start, wedge=wedge);
|
||||
path = arc(n=n, r=r, angle=angle, d=d, cp=cp, points=points, corner=corner, width=width, thickness=thickness, start=start, wedge=wedge);
|
||||
attachable(anchor,spin, two_d=true, path=path, extent=false) {
|
||||
polygon(path);
|
||||
children();
|
||||
|
|
220
geometry.scad
220
geometry.scad
|
@ -865,15 +865,45 @@ function _is_point_above_plane(plane, point) =
|
|||
// Topics: Geometry, Circles, Lines, Intersection
|
||||
// Description:
|
||||
// Find intersection points between a 2d circle and a line, ray or segment specified by two points.
|
||||
// By default the line is unbounded.
|
||||
// By default the line is unbounded. Returns the list of zero or more intersection points.
|
||||
// Arguments:
|
||||
// c = center of circle
|
||||
// r = radius of circle
|
||||
// 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
|
||||
// c = Center of circle
|
||||
// r = Radius of circle
|
||||
// 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
|
||||
// ---
|
||||
// d = diameter of circle
|
||||
// eps = epsilon used for identifying the case with one solution. Default: 1e-9
|
||||
// d = Diameter of circle
|
||||
// eps = Epsilon used for identifying the case with one solution. Default: `1e-9`
|
||||
// Example(2D): Standard intersection returns two points.
|
||||
// line = [[-15,2], [15,7]];
|
||||
// cp = [1,2]; r = 10;
|
||||
// translate(cp) circle(r=r);
|
||||
// color("black") stroke(line, endcaps="arrow2", width=0.5);
|
||||
// isects = circle_line_intersection(c=cp, r=r, line=line);
|
||||
// color("red") move_copies(isects) circle(d=1);
|
||||
// Example(2D): Tangent intersection returns one point.
|
||||
// line = [[-10,12], [10,12]];
|
||||
// cp = [1,2]; r = 10;
|
||||
// translate(cp) circle(r=r);
|
||||
// color("black") stroke(line, endcaps="arrow2", width=0.5);
|
||||
// isects = circle_line_intersection(c=cp, r=r, line=line);
|
||||
// color("#f44") move_copies(isects) circle(d=1);
|
||||
// Example(2D): A bounded ray might only intersect in one direction.
|
||||
// line = [[-5,2], [5,7]];
|
||||
// extended = [line[0], line[0]+22*unit(line[1]-line[0])];
|
||||
// cp = [1,2]; r = 10;
|
||||
// translate(cp) circle(r=r);
|
||||
// color("gray") dashed_stroke(extended, width=0.2);
|
||||
// color("black") stroke(line, endcap2="arrow2", width=0.5);
|
||||
// isects = circle_line_intersection(c=cp, r=r, line=line, bounded=[true,false]);
|
||||
// color("#f44") move_copies(isects) circle(d=1);
|
||||
// Example(2D): If they don't intersect at all, then an empty list is returned.
|
||||
// line = [[-12,12], [12,8]];
|
||||
// cp = [-5,-2]; r = 10;
|
||||
// translate(cp) circle(r=r);
|
||||
// color("black") stroke(line, endcaps="arrow2", width=0.5);
|
||||
// isects = circle_line_intersection(c=cp, r=r, line=line);
|
||||
// color("#f44") move_copies(isects) circle(d=1);
|
||||
function circle_line_intersection(c,r,line,bounded=false,d,eps=EPSILON) =
|
||||
assert(_valid_line(line,2), "Invalid 2d line.")
|
||||
assert(is_vector(c,2), "Circle center must be a 2-vector")
|
||||
|
@ -968,32 +998,46 @@ function circle_circle_intersection(c1,r1,c2,r2,eps=EPSILON,d1,d2) =
|
|||
[L*a+h*b+c1, L*a-h*b+c1];
|
||||
|
||||
|
||||
// Function&Module: circle_2tangents()
|
||||
// Usage: As Function
|
||||
// Function: circle_2tangents()
|
||||
// Usage:
|
||||
// circ = circle_2tangents(pt1, pt2, pt3, r|d=, [tangents=]);
|
||||
// Topics: Geometry, Circles, Tangents
|
||||
// Usage: As Module
|
||||
// circle_2tangents(pt1, pt2, pt3, r|d=, [h=], [center=]);
|
||||
// Description:
|
||||
// Given a pair of rays with a common origin, and a known circle radius/diameter, finds
|
||||
// the centerpoint for the circle of that size that touches both rays tangentally.
|
||||
// Both rays start at `pt2`, one passing through `pt1`, and the other through `pt3`.
|
||||
// .
|
||||
// When called as a module with an `h` height argument, creates a 3D cylinder of `h`
|
||||
// length at the found centerpoint, aligned with the found normal.
|
||||
// .
|
||||
// When called as a module with 2D data and no `h` argument, creates a 2D circle of
|
||||
// the given radius/diameter, tangentially touching both rays.
|
||||
// .
|
||||
// When called as a function with collinear rays, returns `undef`.
|
||||
// Otherwise, when called as a function with `tangents=false`, returns `[CP,NORMAL]`.
|
||||
// Otherwise, when called as a function with `tangents=true`, returns `[CP,NORMAL,TANPT1,TANPT2,ANG1,ANG2]`.
|
||||
// When called with collinear rays, returns `undef`.
|
||||
// Otherwise, when called with `tangents=false`, returns `[CP,NORMAL]`.
|
||||
// Otherwise, when called with `tangents=true`, returns `[CP,NORMAL,TANPT1,TANPT2]`.
|
||||
// - CP is the centerpoint of the circle.
|
||||
// - NORMAL is the normal vector of the plane that the circle is on (UP or DOWN if the points are 2D).
|
||||
// - TANPT1 is the point where the circle is tangent to the ray `[pt2,pt1]`.
|
||||
// - TANPT2 is the point where the circle is tangent to the ray `[pt2,pt3]`.
|
||||
// - ANG1 is the angle from the ray `[CP,pt2]` to the ray `[CP,TANPT1]`
|
||||
// - ANG2 is the angle from the ray `[CP,pt2]` to the ray `[CP,TANPT2]`
|
||||
// Figure(Med,NoAxes):
|
||||
// pts = [[45,10,-5], [10,5,10], [15,40,5]];
|
||||
// rad = 15;
|
||||
// circ = circle_2tangents(pt1=pts[0], pt2=pts[1], pt3=pts[2], r=rad, tangents=true);
|
||||
// cp = circ[0]; n = circ[1]; tp1 = circ[2]; tp2 = circ[3];
|
||||
// color("yellow") stroke(pts, endcaps="arrow2");
|
||||
// color("purple") move_copies([cp,tp1,tp2]) sphere(d=2, $fn=12);
|
||||
// color("lightgray") stroke([cp,tp2], width=0.5);
|
||||
// stroke([cp,cp+n*20], endcap2="arrow2");
|
||||
// labels = [
|
||||
// ["pt1", "blue", 2.5, [ 4, 0, 1], pts[0]],
|
||||
// ["pt2", "blue", 2.5, [-4, 0,-3], pts[1]],
|
||||
// ["pt3", "blue", 2.5, [ 4, 0, 1], pts[2]],
|
||||
// ["r", "blue", 2.5, [ 0,-2, 2], (cp+tp2)/2],
|
||||
// ["CP", "brown", 2.5, [ 6,-4, 3], cp],
|
||||
// ["Normal", "brown", 2.0, [ 5, 2, 1], cp+20*n],
|
||||
// ["TanPt1", "brown", 2.0, [-5,-4, 0], tp1],
|
||||
// ["TanPt2", "brown", 2.0, [-5, 0, 2], tp2],
|
||||
// ];
|
||||
// for(l=labels)
|
||||
// color(l[1]) move(l[4]+l[3]) rot($vpr)
|
||||
// linear_extrude(height=0.1)
|
||||
// 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);
|
||||
// Arguments:
|
||||
// pt1 = A point that the first ray passes though.
|
||||
// pt2 = The starting point of both rays.
|
||||
|
@ -1001,40 +1045,31 @@ function circle_circle_intersection(c1,r1,c2,r2,eps=EPSILON,d1,d2) =
|
|||
// r = The radius of the circle to find.
|
||||
// ---
|
||||
// d = The diameter of the circle to find.
|
||||
// h = Height of the cylinder to create, when called as a module.
|
||||
// center = When called as a module, center the cylinder if true, Default: false
|
||||
// tangents = If true, extended information about the tangent points is calculated and returned. Default: false
|
||||
// Example(2D):
|
||||
// pts = [[60,40], [10,10], [65,5]];
|
||||
// rad = 10;
|
||||
// stroke([pts[1],pts[0]], endcap2="arrow2");
|
||||
// stroke([pts[1],pts[2]], endcap2="arrow2");
|
||||
// pts = [[40,40], [10,10], [55,5]]; rad = 10;
|
||||
// circ = circle_2tangents(pt1=pts[0], pt2=pts[1], pt3=pts[2], r=rad);
|
||||
// translate(circ[0]) {
|
||||
// color("green") {
|
||||
// stroke(circle(r=rad),closed=true);
|
||||
// stroke([[0,0],rad*[cos(315),sin(315)]]);
|
||||
// }
|
||||
// }
|
||||
// move_copies(pts) color("blue") circle(d=2, $fn=12);
|
||||
// translate(circ[0]) color("red") circle(d=2, $fn=12);
|
||||
// labels = [[pts[0], "pt1"], [pts[1],"pt2"], [pts[2],"pt3"], [circ[0], "CP"], [circ[0]+[cos(315),sin(315)]*rad*0.7, "r"]];
|
||||
// for(l=labels) translate(l[0]+[0,2]) color("black") text(text=l[1], size=2.5, halign="center");
|
||||
// stroke(pts, endcaps="arrow2");
|
||||
// color("red") move(circ[0]) circle(r=rad);
|
||||
// Example(2D):
|
||||
// pts = [[-5,25], [5,-25], [45,15]];
|
||||
// rad = 12;
|
||||
// color("blue") stroke(pts, width=0.75, endcaps="arrow2");
|
||||
// circle_2tangents(pt1=pts[0], pt2=pts[1], pt3=pts[2], r=rad);
|
||||
// Example: Non-centered Cylinder
|
||||
// pts = [[45,15,10], [5,-25,5], [-5,25,20]];
|
||||
// rad = 12;
|
||||
// color("blue") stroke(pts, width=0.75, endcaps="arrow2");
|
||||
// circle_2tangents(pt1=pts[0], pt2=pts[1], pt3=pts[2], r=rad, h=10, center=false);
|
||||
// Example: Non-centered Cylinder
|
||||
// pts = [[45,15,10], [5,-25,5], [-5,25,20]];
|
||||
// rad = 12;
|
||||
// color("blue") stroke(pts, width=0.75, endcaps="arrow2");
|
||||
// circle_2tangents(pt1=pts[0], pt2=pts[1], pt3=pts[2], r=rad, h=10, center=true);
|
||||
// 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);
|
||||
// stroke(pts, endcaps="arrow2");
|
||||
// color("red") move(circ[0]) circle(r=rad);
|
||||
// color("blue") move_copies(select(circ,2,3)) circle(d=2);
|
||||
// Example: Fit into 3D path corner.
|
||||
// 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);
|
||||
// stroke(pts, endcaps="arrow2");
|
||||
// color("red") move(circ[0]) cyl(h=10, r=rad, orient=circ[1]);
|
||||
// Example:
|
||||
// path = yrot(20, p=path3d(star(d=100, n=5, step=2)));
|
||||
// stroke(path, closed=true);
|
||||
// for (i = [0:1:5]) {
|
||||
// crn = select(path, i*2-1, i*2+1);
|
||||
// ci = circle_2tangents(crn[0], crn[1], crn[2], r=5);
|
||||
// move(ci[0]) cyl(h=10,r=5,,orient=ci[1]);
|
||||
// }
|
||||
function circle_2tangents(pt1, pt2, pt3, r, d, tangents=false) =
|
||||
let(r = get_radius(r=r, d=d, dflt=undef))
|
||||
assert(r!=undef, "Must specify either r or d.")
|
||||
|
@ -1057,39 +1092,16 @@ function circle_2tangents(pt1, pt2, pt3, r, d, tangents=false) =
|
|||
let(
|
||||
x = hyp * cos(a/2),
|
||||
tp1 = pt2 + x * v1,
|
||||
tp2 = pt2 + x * v2,
|
||||
dang1 = vector_angle(tp1-cp,pt2-cp),
|
||||
dang2 = vector_angle(tp2-cp,pt2-cp)
|
||||
tp2 = pt2 + x * v2
|
||||
)
|
||||
[cp, n, tp1, tp2, dang1, dang2];
|
||||
[cp, n, tp1, tp2];
|
||||
|
||||
|
||||
module circle_2tangents(pt1, pt2, pt3, r, d, h, center=false) {
|
||||
no_children($children);
|
||||
c = circle_2tangents(pt1=pt1, pt2=pt2, pt3=pt3, r=r, d=d);
|
||||
assert(!is_undef(c), "Cannot find circle when both rays are collinear.");
|
||||
cp = c[0]; n = c[1];
|
||||
if (approx(point3d(cp).z,0) && approx(point2d(n),[0,0]) && is_undef(h)) {
|
||||
translate(cp) circle(r=r, d=d);
|
||||
} else {
|
||||
assert(is_finite(h), "h argument required when result is not flat on the XY plane.");
|
||||
translate(cp) {
|
||||
rot(from=UP, to=n) {
|
||||
cylinder(r=r, d=d, h=h, center=center);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Function&Module: circle_3points()
|
||||
// Usage: As Function
|
||||
// Function: circle_3points()
|
||||
// Usage:
|
||||
// circ = circle_3points(pt1, pt2, pt3);
|
||||
// circ = circle_3points([pt1, pt2, pt3]);
|
||||
// Topics: Geometry, Circles
|
||||
// Usage: As Module
|
||||
// circle_3points(pt1, pt2, pt3, [h], [center]);
|
||||
// circle_3points([pt1, pt2, pt3], [h], [center]);
|
||||
// Description:
|
||||
// Returns the [CENTERPOINT, RADIUS, NORMAL] of the circle that passes through three non-collinear
|
||||
// points where NORMAL is the normal vector of the plane that the circle is on (UP or DOWN if the points are 2D).
|
||||
|
@ -1103,26 +1115,12 @@ module circle_2tangents(pt1, pt2, pt3, r, d, h, center=false) {
|
|||
// pt1 = The first point.
|
||||
// pt2 = The second point.
|
||||
// pt3 = The third point.
|
||||
// h = Height of the cylinder to create, when called as a module.
|
||||
// center = When called as a module, center the cylinder if true, Default: false
|
||||
// Example(2D):
|
||||
// pts = [[60,40], [10,10], [65,5]];
|
||||
// circ = circle_3points(pts[0], pts[1], pts[2]);
|
||||
// translate(circ[0]) color("green") stroke(circle(r=circ[1]),closed=true,$fn=72);
|
||||
// translate(circ[0]) color("red") circle(d=3, $fn=12);
|
||||
// move_copies(pts) color("blue") circle(d=3, $fn=12);
|
||||
// Example(2D):
|
||||
// pts = [[30,40], [10,20], [55,30]];
|
||||
// circle_3points(pts[0], pts[1], pts[2]);
|
||||
// move_copies(pts) color("blue") circle(d=3, $fn=12);
|
||||
// Example: Non-Centered Cylinder
|
||||
// pts = [[30,15,30], [10,20,15], [55,25,25]];
|
||||
// circle_3points(pts[0], pts[1], pts[2], h=10, center=false);
|
||||
// move_copies(pts) color("cyan") sphere(d=3, $fn=12);
|
||||
// Example: Centered Cylinder
|
||||
// pts = [[30,15,30], [10,20,15], [55,25,25]];
|
||||
// circle_3points(pts[0], pts[1], pts[2], h=10, center=true);
|
||||
// move_copies(pts) color("cyan") sphere(d=3, $fn=12);
|
||||
function circle_3points(pt1, pt2, pt3) =
|
||||
(is_undef(pt2) && is_undef(pt3) && is_list(pt1))
|
||||
? circle_3points(pt1[0], pt1[1], pt1[2])
|
||||
|
@ -1149,19 +1147,6 @@ function circle_3points(pt1, pt2, pt3) =
|
|||
) [ cp, r, n ];
|
||||
|
||||
|
||||
module circle_3points(pt1, pt2, pt3, h, center=false) {
|
||||
no_children($children);
|
||||
c = circle_3points(pt1, pt2, pt3);
|
||||
assert(!is_undef(c[0]), "Points cannot be collinear.");
|
||||
cp = c[0]; r = c[1]; n = c[2];
|
||||
if (approx(point3d(cp).z,0) && approx(point2d(n),[0,0]) && is_undef(h)) {
|
||||
translate(cp) circle(r=r);
|
||||
} else {
|
||||
assert(is_finite(h));
|
||||
translate(cp) rot(from=UP,to=n) cylinder(r=r, h=h, center=center);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Function: circle_point_tangents()
|
||||
// Usage:
|
||||
|
@ -1339,6 +1324,13 @@ function _noncollinear_triple(points,error=true,eps=EPSILON) =
|
|||
// ---
|
||||
// d = diameter of sphere
|
||||
// eps = epsilon used for identifying the case with one solution. Default: 1e-9
|
||||
// Example:
|
||||
// cp = [10,20,5]; r = 40;
|
||||
// line = [[-50,-10,25], [70,0,40]];
|
||||
// isects = sphere_line_intersection(c=cp, r=r, line=line);
|
||||
// color("cyan") stroke(line);
|
||||
// move(cp) sphere(r=r, $fn=72);
|
||||
// color("red") move_copies(isects) sphere(d=3, $fn=12);
|
||||
function sphere_line_intersection(c,r,line,bounded=false,d,eps=EPSILON) =
|
||||
assert(_valid_line(line,3), "Invalid 3d line.")
|
||||
assert(is_vector(c,3), "Sphere center must be a 3-vector")
|
||||
|
@ -1396,6 +1388,15 @@ function polygon_area(poly, signed=false) =
|
|||
// Arguments:
|
||||
// object = object to compute the centroid of
|
||||
// eps = epsilon value for identifying degenerate cases
|
||||
// Example(2D):
|
||||
// path = [
|
||||
// [-10,10], [-5,15], [15,15], [20,0],
|
||||
// [15,-5], [25,-20], [25,-27], [15,-20],
|
||||
// [0,-30], [-15,-25], [-5,-5]
|
||||
// ];
|
||||
// linear_extrude(height=0.01) polygon(path);
|
||||
// cp = centroid(path);
|
||||
// color("red") move(cp) sphere(d=2);
|
||||
function centroid(object,eps=EPSILON) =
|
||||
assert(is_finite(eps) && (eps>=0), "The tolerance should a non-negative value." )
|
||||
is_vnf(object) ? _vnf_centroid(object,eps)
|
||||
|
@ -1465,6 +1466,13 @@ function _polygon_centroid(poly, eps=EPSILON) =
|
|||
// the the result is undefined. It doesn't check for coplanarity.
|
||||
// Arguments:
|
||||
// poly = The list of 3D path points for the perimeter of the polygon.
|
||||
// Example:
|
||||
// path = rot([0,30,15], p=path3d(star(n=5, d=100, step=2)));
|
||||
// stroke(path, closed=true);
|
||||
// n = polygon_normal(path);
|
||||
// rot(from=UP, to=n)
|
||||
// color("red")
|
||||
// stroke([[0,0,0], [0,0,20]], endcap2="arrow2");
|
||||
function polygon_normal(poly) =
|
||||
assert(is_path(poly,dim=3), "Invalid 3D polygon." )
|
||||
let(
|
||||
|
|
|
@ -204,12 +204,18 @@ function rect(size=1, rounding=0, chamfer=0, atype="box", anchor=CENTER, spin=0)
|
|||
// Topics: Shapes (2D), Path Generators (2D)
|
||||
// Usage: As a Module
|
||||
// circle(r|d=, ...) [ATTACHMENTS];
|
||||
// circle(points=) [ATTACHMENTS];
|
||||
// circle(r|d=, corner=) [ATTACHMENTS];
|
||||
// Usage: As a Function
|
||||
// path = circle(r|d=, ...);
|
||||
// path = circle(points=);
|
||||
// path = circle(r|d=, corner=);
|
||||
// See Also: ellipse(), circle_2tangents(), circle_3points()
|
||||
// Description:
|
||||
// When called as the builtin module, creates a 2D polygon that approximates a circle of the given size.
|
||||
// When called as a function, returns a 2D list of points (path) for a polygon that approximates a circle of the given size.
|
||||
// If `corner=` is given three 2D points, centers the circle so that it will be tangent to both segments of the path, on the inside corner.
|
||||
// If `points=` is given three 2D points, centers and sizes the circle so that it passes through all three points.
|
||||
// Arguments:
|
||||
// r = The radius of the circle to create.
|
||||
// d = The diameter of the circle to create.
|
||||
|
@ -220,20 +226,78 @@ function rect(size=1, rounding=0, chamfer=0, atype="box", anchor=CENTER, spin=0)
|
|||
// circle(r=25);
|
||||
// Example(2D): By Diameter
|
||||
// circle(d=50);
|
||||
// Example(NORENDER): Called as Function
|
||||
// Example(2D): Fit to Three Points
|
||||
// pts = [[50,25], [25,-25], [-10,0]];
|
||||
// circle(points=pts);
|
||||
// color("red") move_copies(pts) circle();
|
||||
// Example(2D): Fit Tangent to Inside Corner of Two Segments
|
||||
// path = [[50,25], [-10,0], [25,-25]];
|
||||
// circle(corner=path, r=15);
|
||||
// color("red") stroke(path);
|
||||
// Example(2D): Called as Function
|
||||
// path = circle(d=50, anchor=FRONT, spin=45);
|
||||
function circle(r, d, anchor=CENTER, spin=0) =
|
||||
// stroke(path);
|
||||
function circle(r, d, points, corner, anchor=CENTER, spin=0) =
|
||||
assert(is_undef(corner) || (is_path(corner,[2]) && len(corner) == 3))
|
||||
assert(is_undef(points) || is_undef(corner), "Cannot specify both points and corner.")
|
||||
let(
|
||||
r = get_radius(r=r, d=d, dflt=1),
|
||||
data = is_def(points)?
|
||||
assert(is_path(points,[2]) && len(points) == 3)
|
||||
assert(is_undef(corner), "Cannot specify corner= when points= is given.")
|
||||
assert(is_undef(r) && is_undef(d), "Cannot specify r= or d= when points= is given.")
|
||||
let( c = circle_3points(points) )
|
||||
assert(!is_undef(c[0]), "Points cannot be collinear.")
|
||||
let( cp = c[0], r = c[1] )
|
||||
[cp, r] :
|
||||
is_def(corner)?
|
||||
assert(is_path(corner,[2]) && len(corner) == 3)
|
||||
assert(is_undef(points), "Cannot specify points= when corner= is given.")
|
||||
let(
|
||||
r = get_radius(r=r, d=d, dflt=1),
|
||||
c = circle_2tangents(pt1=corner[0], pt2=corner[1], pt3=corner[2], r=r)
|
||||
)
|
||||
assert(c!=undef, "Corner path cannot be collinear.")
|
||||
let( cp = c[0] )
|
||||
[cp, r] :
|
||||
let(
|
||||
cp = [0, 0],
|
||||
r = get_radius(r=r, d=d, dflt=1)
|
||||
) [cp, r],
|
||||
cp = data[0],
|
||||
r = data[1],
|
||||
sides = segs(r),
|
||||
path = [for (i=[0:1:sides-1]) let(a=360-i*360/sides) r*[cos(a),sin(a)]]
|
||||
path = [for (i=[0:1:sides-1]) let(a=360-i*360/sides) r*[cos(a),sin(a)]+cp]
|
||||
) reorient(anchor,spin, two_d=true, r=r, p=path);
|
||||
|
||||
module circle(r, d, anchor=CENTER, spin=0) {
|
||||
r = get_radius(r=r, d=d, dflt=1);
|
||||
attachable(anchor,spin, two_d=true, r=r) {
|
||||
_circle(r=r);
|
||||
children();
|
||||
module circle(r, d, points, corner, anchor=CENTER, spin=0) {
|
||||
if (is_path(points)) {
|
||||
c = circle_3points(points);
|
||||
check = assert(c!=undef && c[0] != undef, "Points must not be collinear.");
|
||||
cp = c[0];
|
||||
r = c[1];
|
||||
translate(cp) {
|
||||
attachable(anchor,spin, two_d=true, r=r) {
|
||||
_circle(r=r);
|
||||
children();
|
||||
}
|
||||
}
|
||||
} else if (is_path(corner)) {
|
||||
r = get_radius(r=r, d=d, dflt=1);
|
||||
c = circle_2tangents(pt1=corner[0], pt2=corner[1], pt3=corner[2], r=r);
|
||||
check = assert(c != undef && c[0] != undef, "Points must not be collinear.");
|
||||
cp = c[0];
|
||||
translate(cp) {
|
||||
attachable(anchor,spin, two_d=true, r=r) {
|
||||
_circle(r=r);
|
||||
children();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
r = get_radius(r=r, d=d, dflt=1);
|
||||
attachable(anchor,spin, two_d=true, r=r) {
|
||||
_circle(r=r);
|
||||
children();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue