Skip to content

Instantly share code, notes, and snippets.

@ouroborus
Last active January 13, 2025 08:46
2D chamfer/fillet of point in list of points
// 2d-chamfer.scad by ouroborus@ouroborus.org is marked with CC0 1.0. To view a copy of this license, visit https://creativecommons.org/publicdomain/zero/1.0/
/* Chamfer by length of new edge
* chamfer([[?,?],...], ?, ?)
* chamfer(points=[[?,?],...], idx=?, length=?)
* Chamfer by lengths of two adjacent edges
* chamfer([[?,?],...], ?, ?, ?)
* chamfer(points=[[?,?],...], idx=?, length=?, length2=?)
* Chamfer by distance from point
* chamfer([[?,?],...], ?, depth=?)
* chamfer(points=[[?,?],...], idx=?, depth=?)
* points: A list of 2d points representing a polygon
* idx: The index of the point to chamfer
* returns: The chamfered list of 2d points
*/
function chamfer(points, idx, length, length2, depth) = let (
l = len(points),
last = l-1,
head = idx > 0 ? [for(i=[0:idx-1]) points[i]] : [],
tail = idx < last ? [for(i=[idx+1:last]) points[i]] : [],
target = [points[(idx-1+l)%l], points[idx], points[(idx+1)%l]],
vector_a = target[0] - target[1],
vector_b = target[2] - target[1],
norm_a = vector_a / norm(vector_a),
norm_b = vector_b / norm(vector_b),
chamfer1 = function() let (
hyp = length / sqrt(2 - 2 * (norm_a * norm_b)),
) [
norm_a * hyp,
norm_b * hyp
],
chamfer2 = function() [
norm_a * length,
norm_b * length2,
],
chamfer3 = function() let (
leg = sqrt(2) * depth / sqrt(norm_a * norm_b + 1)
) [
norm_a * leg,
norm_b * leg
],
p = is_undef(depth) ? is_undef(length2) ? chamfer1() : chamfer2() : chamfer3()
) [each head, p[0] + target[1], p[1] + target[1], each tail];
// 2d-fillet.scad by ouroborus@ouroborus.org is marked with CC0 1.0. To view a copy of this license, visit https://creativecommons.org/publicdomain/zero/1.0/
/* Fillet a 2d point in a list of 2d points
* fillet([[?,?],...], ?, ?)
* fillet(points=[[?,?],...], idx=?, radius=?)
* points: A list of 2d points representing a polygon
* idx: The index of the point to fillet
* $fn and $fa work the same as in native shapes.
* returns: The filleted list of 2d points
*/
function fillet(points, idx, radius, $fn=$fn, $fa=$fa) = let (
lerp = function(a, b, t) a + t * (b - a),
get_angle = function(a, b) atan2(b.y - a.y, b.x - a.x),
l = len(points),
last = l-1,
head = idx > 0 ? [for(i=[0:idx-1]) points[i]] : [],
tail = idx < last ? [for(i=[idx+1:last]) points[i]] : [],
target = [points[(idx-1+l)%l], points[idx], points[(idx+1)%l]],
vector_a = target[0] - target[1],
vector_b = target[2] - target[1],
norm_a = vector_a / norm(vector_a),
norm_b = vector_b / norm(vector_b),
vector_bisect = norm_a + norm_b,
norm_bisect = vector_bisect / norm(vector_bisect),
dotp = norm_a * norm_b,
crossp = cross(norm_a, norm_b),
divisor = sqrt(1 - dotp),
leg = sqrt(1 + dotp) * radius / divisor,
hyp = sqrt(2) * radius / divisor,
p_start = target[1] + norm_a * leg,
p_stop = target[1] + norm_b * leg,
center = target[1] + hyp * norm_bisect,
start_angle = get_angle(center, p_start),
stop_angle = get_angle(center, p_stop),
stop_angle_up = stop_angle < start_angle ? stop_angle + 360 : stop_angle,
a_start = crossp >= 0 ? start_angle + 360 : start_angle,
a_stop = stop_angle_up,
spread = abs(a_stop - a_start),
step_t = 1 / ($fn > 0 ? $fn : ceil(spread / $fa)),
curve = [for(i=[step_t:step_t:1-step_t]) let(
l = (lerp(a_start, a_stop, i) + 360) % 360
) center + [cos(l) * radius, sin(l) * radius]
],
) [each head, p_start, each curve, p_stop, each tail];
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment