Skip to content
37 changes: 23 additions & 14 deletions drawing.scad
Original file line number Diff line number Diff line change
Expand Up @@ -696,13 +696,20 @@ module dashed_stroke(path, dashpat=[3,3], width=1, closed=false, fit=true, round
// Usage: as module
// arc(...) [ATTACHMENTS];
// Description:
// If called as a function, returns a 2D or 3D path forming an arc. If `wedge` is true, the centerpoint of the arc appears as the first point in the result.
// If called as a module, creates a 2D arc polygon or pie slice shape. Numerous methods are available to specify the arc.
// If called as a function, returns a 2D or 3D path forming an arc. Numerous methods are available to specify the arc as listed in the Arguments section.
// If `wedge` is true, the centerpoint of the arc appears as the first point in the result.
// If called as a module, the arc must be 2D and the module creates the 2D arc polygon or pie slice shape.
// .
// The `rounding` parameter is permitted only when `wedge=true` and applies specified radius roundings at each of the corners, with `rounding[0]` giving
// the rounding at the center point, and then the other two the two outer corners in the direction that the arc travels. If you don't need to control
// the exact point count, you should use `$fs` and `$fa` to control the number of points on the roundings and arc. If you give `n` then each arc
// section in your curve uses `n` points, so the total number of points is `n` times one plus the number of non-zero roundings you specified.
// If `endpoint=false`, which is only accepted by the functional form, then the arc stops one step before the final point.
// The `rounding` parameter, which is permitted only when `wedge=true`, applies specified radius roundings at each of the corners, with `rounding[0]` giving
// the rounding at the center point, and then the other two the two outer corners in the direction that the arc travels.
// .
// If you give `n` for an arc without roundings then the curve of the arc will use `n` points. If `endpoint=false` the arc still has `n` points, but
// they are closer together. When wedge is true the output has an extra point. If you use rounding on a wedge then each rounding will have the specified
// number of points, but note that the points of adjacent rounded areas overlap. It may be easier to think that each curve in the output has n-1 facets. With rounding,
// the total number of points on a wedge will be $n + 1 + (n-1) k$ where $k$ is the number of roundings that are nonzero.
// To get enough points on corner roundings you may need an `n` that is very large and produces more points than needed on the main curved portion of the arc.
// Using `$fs` and `$fa` makes it possible to have differing numbers of points on the various roundings in your arc based on their radius and angle.
// Arguments:
// n = Number of vertices to use in the arc. If `wedge=true` you will get `n+1` points.
// r = Radius of the arc.
Expand Down Expand Up @@ -791,7 +798,7 @@ function arc(n, r, angle, d, cp, points, corner, width, thickness, start, wedge=
is_def(rounding) ? assert(wedge,"rounding is only supportd with wedge=true") move(cp,zrot(start,_rounded_arc(r, rounding, angle, n)))
:
let(
n = is_def(n) ? n : max(3, ceil(segs(r)*abs(angle)/360)),
n = is_def(n) ? n : max(wedge?2:3,1+segs(r,angle)), //max(3, ceil(segs(r)*abs(angle)/360)),
arcpoints = [for(i=[0:n-1]) let(theta = start + i*angle/(n-1)) r*[cos(theta),sin(theta)]+cp]
)
[
Expand Down Expand Up @@ -892,14 +899,15 @@ function arc(n, r, angle, d, cp, points, corner, width, thickness, start, wedge=
module arc(n, r, angle, d, cp, points, corner, width, thickness, start, wedge=false, rounding, anchor=CENTER, spin=0)
{
path = arc(n=n, r=r, angle=angle, d=d, cp=cp, points=points, corner=corner, width=width, thickness=thickness, start=start, wedge=wedge, rounding=rounding);
attachable(anchor,spin, two_d=true, path=path, extent=false) {
polygon(path);
assert(len(path[0])==2 || sum(v_abs(column(path,2)))==0, "Module form of arc() only works with 2D inputs.");
path2d = path2d(path);
attachable(anchor,spin, two_d=true, path=path2d, extent=false) {
polygon(path2d);
children();
}
}




function _rounded_arc(radius, rounding=0, angle, n) =
assert(is_finite(angle) && abs(angle)<360, "angle must be strictly between -360 and 360")
Expand Down Expand Up @@ -928,7 +936,6 @@ function _rounded_arc(radius, rounding=0, angle, n) =

edge_gap1=radius-arc1_cut-radius_of_ctrpt_edge,
edge_gap2=radius-arc2_cut-radius_of_ctrpt_edge,

angle_span1 = rounding[1]>0 ? [-dir*90, dir*arc1_angle] : -[dir*90, dir*180 - arc1_angle],
angle_span2 = [angle-dir*arc2_angle + (rounding[2]<0 ? dir*180 : 0), angle+dir*90]
)
Expand All @@ -942,10 +949,12 @@ function _rounded_arc(radius, rounding=0, angle, n) =
polar_to_xy(r=radius_of_ctrpt_edge, theta=0)],
endpoint=edge_gap1!=0,n=n)
else repeat([0,0],rounding[0]>0 && abs(angle)==180 && is_def(n) ? n : 1),
each if (rounding[1]!=0) arc(r=abs(rounding[1]),cp=pt1,angle=angle_span1,endpoint=dir*arc1_angle==angle,n=n), // first corner
each if (rounding[1]!=0) arc(r=abs(rounding[1]),cp=pt1,angle=angle_span1,
endpoint=dir*arc1_angle==angle,n=u_add(n,dir*arc1_angle==angle?0:-1)), // first corner
each if (arc1_angle+arc2_angle<abs(angle))
arc(r=radius, angle=[dir*arc1_angle,angle - dir*arc2_angle], endpoint=rounding[2]==0, n=n), // main arc section
each if (rounding[2]!=0) arc(r=abs(rounding[2]),cp=pt3, angle=angle_span2, endpoint=edge_gap2!=0, n=n) // second corner
arc(r=radius, angle=[dir*arc1_angle,angle - dir*arc2_angle], endpoint=rounding[2]==0, // main arc section
n=u_add(n,rounding[2]==0?0:-1)),
each if (rounding[2]!=0) arc(r=abs(rounding[2]),cp=pt3, angle=angle_span2, endpoint=edge_gap2!=0, n=n) // second corner
];


Expand Down
35 changes: 27 additions & 8 deletions gears.scad
Original file line number Diff line number Diff line change
Expand Up @@ -934,6 +934,8 @@ function spur_gear(
let(
profile_shift = auto_profile_shift(teeth,PA,helical,profile_shift=profile_shift),
pr = pitch_radius(circ_pitch, teeth, helical),
feee=echo(pr_spur = pr, circ_pitch, teeth, helical),

or = outer_radius(circ_pitch, teeth, helical=helical, profile_shift=profile_shift, internal=internal,shorten=shorten),
rr = _root_radius_basic(circ_pitch, teeth, clearance, profile_shift=profile_shift, internal=internal),
anchor_rad = atype=="pitch" ? pr
Expand Down Expand Up @@ -2772,10 +2774,21 @@ function worm(
profile_shift=0
), 1, -2)
),

rack_profile = [
for (t = xcopies(trans_pitch, n=2*ceil(l/trans_pitch)+1))
each apply(t, tooth)
],
nrp = select(rack2d(
pitch=circ_pitch,
teeth=2*ceil(l/trans_pitch)+1,
pressure_angle=PA,
clearance=clearance,
backlash=backlash,
helical=helical,
profile_shift=0
), 1, -2),
ff=echo(tooth=tooth, rack_profile=rack_profile,nrp=nrp),
steps = max(36, segs(d/2)),
step = 360 / steps,
zsteps = ceil(l / trans_pitch / starts * steps),
Expand Down Expand Up @@ -3139,43 +3152,49 @@ function worm_gear(
let(
gear_arc = 2 * PA,
helical = asin(worm_starts * circ_pitch / PI / worm_diam),
//fee=echo(helical=helical),
full_tooth = apply(
zrot(90) * scale(0.99),
_gear_tooth_profile(
circ_pitch, teeth=teeth,
pressure_angle=PA,
profile_shift=-profile_shift,
clearance=clearance,
helical=helical,
helical=helical, internal=false,
center=true
)
),
ftl = len(full_tooth),
tooth_half1 = (select(full_tooth, 0, ftl/2-1)),
tooth_half2 = (select(full_tooth, ftl/2, -1)),
tang = 360 / teeth,
rteeth = quantdn(teeth * gear_arc / 360, 2) / 2 + 0.5,
rteeth = (quantdn(teeth * gear_arc / 360, 2) / 2 + 0.5),
pr = pitch_radius(circ_pitch, teeth, helical=helical),
//feee=echo(pr_worm = pr, circ_pitch, teeth, helical),
circum = 2*PI*pr,
tan_helical = tan(helical),
oslices = slices * 4,
rows = [
for (data = [[tooth_half1,1], [tooth_half2,-1]])
let (
tooth_half = data[0],
tooth_half = (data[0]),
dir = data[1]
)
for (pt = tooth_half) [
for (i = [0:1:oslices])
let (
u = i / oslices,
w_ang = worm_arc * (u - 0.5),
g_ang_delta = w_ang/360 * tang * worm_starts * (left_handed?1:-1),
m = zrot(dir*rteeth*tang+g_ang_delta, cp=[worm_diam/2+pr,0,0]) *
g_ang_delta = w_ang/360 * tang * worm_starts * (left_handed?1:-1) *0 ,
m = //zrot(dir*rteeth*tang+g_ang_delta, cp=[worm_diam/2+pr,0,0]) *
left(crowning) *
yrot(w_ang) *
right(worm_diam/2+crowning) *
zrot(-dir*rteeth*tang+g_ang_delta, cp=[pr,0,0]) *
xrot(180)
) apply(m, point3d(pt))
//zrot(-dir*rteeth*tang+g_ang_delta, cp=[pr,0,0]) *
xrot(180),
pt = apply(m, point3d(pt)),
angled_pt = zrot((left_handed?-1:1)*360*pt.z*tan_helical/circum,pt,cp=[worm_diam/2+pr,0,0])
) angled_pt
]
],
midrow = len(rows)/2,
Expand Down
2 changes: 1 addition & 1 deletion masks3d.scad
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ module rounding_edge_mask(l, r, ang, r1, r2, excess=0.01, d1, d2,d,r,length, h,
assert(all_positive([length]), "length/l/h/height must be a positive value")
assert(is_finite(ang) && ang>0 && ang<180, "ang must be a number between 0 and 180")
assert(all_nonnegative([chamfer1,chamfer2,rounding1,rounding2]), "chamfers and roundings must be nonnegative");
steps = ceil(segs(max(r1,r2))*(180-ang)/360);
steps = max(2,segs(max(r1,r2), 180-ang));
function make_path(r) =
r==0 ? repeat([0,0],steps+1)
: arc(n=steps+1, r=r, corner=[polar_to_xy(r,ang),[0,0],[r,0]]);
Expand Down
6 changes: 3 additions & 3 deletions regions.scad
Original file line number Diff line number Diff line change
Expand Up @@ -1016,7 +1016,7 @@ function offset(
path, r=undef, delta=undef, chamfer=false,
closed=true, check_valid=true,
quality=1, error=true, return_faces=false, firstface_index=0,
flip_faces=false, same_length=false
flip_faces=false, same_length=false, _reduce_output_count=false
) =
assert(!(same_length && return_faces), "\nCannot combine return_faces with same_length.")
is_region(path)?
Expand Down Expand Up @@ -1097,7 +1097,7 @@ function offset(
goodsegs[i][0]-goodpath[i]))
assert(!outsidecorner[i] || vang!=0, // If outsidecorner[i] is true then vang>0 needed to give valid step count
"\nOffset computation failed, probably because validity check mistakenly removed a valid segment. Increasing quality might fix this.")
1+floor(segs(r)*vang/360)
segs(r,vang)+(_reduce_output_count ? 0 : 1)
],
// newcorners is a list where each entry is a list of the points that correspond to a single point in the sharpcorners
// list: newcorners[i] is the point list that replaces goodpath[i]. Without rounding or chamfering (or reversals),
Expand Down Expand Up @@ -1134,7 +1134,7 @@ function offset(
)
arc(cp=goodpath[i], cw=cw, ccw=ccw,
points=basepts,
n=steps[i]+3)
n=steps[i])
],
pointcount = [for(entry=newcorners) len(entry)],
edges = flatten(newcorners),
Expand Down
11 changes: 7 additions & 4 deletions rounding.scad
Original file line number Diff line number Diff line change
Expand Up @@ -1679,7 +1679,7 @@ function _make_offset_polyhedron(path,offsets, offset_type, flip_faces, quality,
check_valid=check_valid, quality=quality,
return_faces=true,
firstface_index=vertexcount,
flip_faces=flip_faces
flip_faces=flip_faces, _reduce_output_count=true
)
)
_make_offset_polyhedron(
Expand Down Expand Up @@ -4180,6 +4180,7 @@ function _prism_fillet_prism(name, basepoly, bot, top, d, k, N, overlap, uniform
// ---
// shift1 = shift connection point on object1, a scalar for cylinders, extrusions, or edges, a 2-vector for faces, not permitted for spheres
// shift2 = shift connection point on object2, a scalar for cylinders, extrusions, or edges, a 2-vector for faces, not permitted for spheres
// shift = shift connection point on both objects; the shift must be valid for both object types
// spin_align = align the spin of the connecting prism to specified object (1 or 2) or if you give 12 or 21, the average of the two spins. Default: 1
// scale = scale the profile by this factor at anchor2. Default: 1
// n = number of facets to use for the fillets. Default: 15
Expand Down Expand Up @@ -4658,7 +4659,7 @@ function _find_center_anchor(desc1, desc2, anchor2, flip) =



module prism_connector(profile, desc1, anchor1, desc2, anchor2, shift1=0, shift2=0, spin_align=1,
module prism_connector(profile, desc1, anchor1, desc2, anchor2, shift1, shift2, shift, spin_align=1,
scale=1,
fillet, fillet1, fillet2,
overlap, overlap1, overlap2,
Expand All @@ -4667,6 +4668,8 @@ module prism_connector(profile, desc1, anchor1, desc2, anchor2, shift1=0, shift2
smooth_normals, smooth_normals1, smooth_normals2,
debug=false, debug_pos=false)
{
shift1_input = first_defined([shift1,shift,0]);
shift2_input = first_defined([shift2,shift,0]);
base_fillet = first_defined([fillet1,fillet,0]);
aux_fillet = first_defined([fillet2,fillet,0]);

Expand Down Expand Up @@ -4716,7 +4719,7 @@ module prism_connector(profile, desc1, anchor1, desc2, anchor2, shift1=0, shift2
base_anch_pos = base_anch[1];
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_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_anch = _find_anchor(aux_anchor, aux[1]);
Expand All @@ -4727,7 +4730,7 @@ module prism_connector(profile, desc1, anchor1, desc2, anchor2, shift1=0, shift2
aux_spin = aux_anch[3];
aux_spin_dir = apply(rot(from=UP,to=aux_anch[2])*zrot(aux_spin),BACK);
aux_axis = aux_type=="cyl" ? aux[1][5] : RIGHT;
prelim_shift2 = _check_join_shift(2,aux_type,shift2,false);
prelim_shift2 = _check_join_shift(2,aux_type,shift2_input,false);
shift2 = corrected_aux_anchor ? corrected_aux_anchor[1] + prelim_shift2 : prelim_shift2;


Expand Down
2 changes: 1 addition & 1 deletion tests/test_drawing.scad
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ module test_arc() {
assert_approx(arc(n=8, width=100, thickness=30), [[50,-3.5527136788e-15],[39.5300788555,13.9348601124],[25.3202618476,24.0284558904],[8.71492362453,29.3258437015],[-8.71492362453,29.3258437015],[-25.3202618476,24.0284558904],[-39.5300788555,13.9348601124],[-50,-1.42108547152e-14]]);
assert_approx(arc(n=8, cp=[10,10], points=[[45,45],[-25,45]]), [[45,45],[36.3342442379,51.9107096148],[26.3479795075,56.7198412457],[15.5419588213,59.1862449514],[4.45804117867,59.1862449514],[-6.34797950747,56.7198412457],[-16.3342442379,51.9107096148],[-25,45]]);
assert_approx(arc(n=24, cp=[10,10], points=[[45,45],[-25,45]], long=true), [[45,45],[51.3889035257,37.146982612],[56.0464336973,28.1583574081],[58.7777575294,18.4101349813],[59.4686187624,8.31010126292],[58.0901174104,-1.71924090789],[54.6999187001,-11.2583458482],[49.4398408296,-19.9081753929],[42.5299224539,-27.3068913894],[34.2592180667,-33.1449920477],[24.9737063235,-37.1782589647],[15.0618171232,-39.2379732261],[4.93818287676,-39.2379732261],[-4.97370632349,-37.1782589647],[-14.2592180667,-33.1449920477],[-22.5299224539,-27.3068913894],[-29.4398408296,-19.9081753929],[-34.6999187001,-11.2583458482],[-38.0901174104,-1.71924090789],[-39.4686187624,8.31010126292],[-38.7777575294,18.4101349813],[-36.0464336973,28.1583574081],[-31.3889035257,37.146982612],[-25,45]]);
assert_approx(arc($fn=24, cp=[10,10], points=[[45,45],[-25,45]], long=true), [[45,45],[53.2421021636,34.0856928585],[58.1827254512,21.3324740498],[59.4446596304,7.71403542491],[56.9315576496,-5.72987274525],[50.8352916125,-17.9728253654],[41.6213035891,-28.0800887515],[29.9930697126,-35.2799863457],[16.8383906815,-39.0228152281],[3.16160931847,-39.0228152281],[-9.9930697126,-35.2799863457],[-21.6213035891,-28.0800887515],[-30.8352916125,-17.9728253654],[-36.9315576496,-5.72987274525],[-39.4446596304,7.71403542491],[-38.1827254512,21.3324740498],[-33.2421021636,34.0856928585],[-25,45]]);
assert_approx(arc($fn=24, cp=[10,10], points=[[45,45],[-25,45]], long=true),[[45,45],[52.8660704987,34.7487373415],[57.8108891325,22.8108891325],[59.4974746831,10],[57.8108891325,-2.81088913246],[52.8660704987,-14.7487373415],[45,-25],[34.7487373415,-32.8660704987],[22.8108891325,-37.8108891325],[10,-39.4974746831],[-2.81088913246,-37.8108891325],[-14.7487373415,-32.8660704987],[-25,-25],[-32.8660704987,-14.7487373415],[-37.8108891325,-2.81088913246],[-39.4974746831,10],[-37.8108891325,22.8108891325],[-32.8660704987,34.7487373415],[-25,45]]);
}
test_arc();

Expand Down
Loading