Skip to content

Instantly share code, notes, and snippets.

@GeekAndDad
Last active November 7, 2023 11:41
Show Gist options
  • Save GeekAndDad/121caef1462ff591802bbb4cc0b47e58 to your computer and use it in GitHub Desktop.
Save GeekAndDad/121caef1462ff591802bbb4cc0b47e58 to your computer and use it in GitHub Desktop.
An answer to "distribute 3 circles evenly inside another" for OpenSCAD for Pete.
// For Pete
// v3.0.2
// by @GeekAndDad
// Define the outer circle radius and desired inner circle radius
outer_circle_radius = 100;
desired_inner_radius = 20;
// Call our function to calulate the centers of the three evenly spaced circles
// and
info = threeCenters(outer_radius = outer_circle_radius, inner_radius = desired_inner_radius, space = 1);
echo( info );
// ECHO: [[[-46.4102, -26.314], [46.4102, -26.314], [0, 53.5898]], 20]
// ---------
// Sample 1:
// Can extract and use each falue individually.
// First extract each center from the list of centers (info[0]):
circle1_center = info[0][0];
circle2_center = info[0][1];
circle3_center = info[0][2];
// Then extract the clamped radius that fits:
clamped_radius = info[1];
// Finally, make a circular plate and cut out three holes in the locations calculated:
translate([125, 200, 0]) // move it out of the way so we can do a second one
difference() {
// make the plate - here we are using the outer_circle_radius, but it could
// be a square plate or a larger circle - just has to have a circle with
// radius outer_circle_radius or larger space in it's center (0,0) so
// the inner holes we'll cut out next fit.
cylinder(h = 20, r1 = outer_circle_radius, r2 = outer_circle_radius);
// Put the three holes in it.
// translating each one to the center returned by the function,
// making them 2 pixels longer and offsetting them one pixel in z axis
// so they cut through the plate
translate([circle1_center[0], circle1_center[1], -1])
color("red")
cylinder(h = 22, r1 = clamped_radius, r2 = clamped_radius);
translate([circle2_center[0], circle2_center[1], -1])
color("blue")
cylinder(h = 22, r1 = clamped_radius, r2 = clamped_radius);
translate([circle3_center[0], circle3_center[1], -1])
color("green")
cylinder(h = 22, r1 = clamped_radius, r2 = clamped_radius);
}
// ---------
// Sample 2:
// Same result as above, but use a loop to show why the centers are
// returned as a list separate from the clamped radius.
translate([-125, 200, 0]) { // move it out of the way
difference() {
cylinder(h = 20, r1 = outer_circle_radius, r2 = outer_circle_radius);
// and put the holes in it
color("orange")
for (center = info[0])
{
translate([center[0], center[1], -1])
cylinder(h = 22, r1 = clamped_radius, r2 = clamped_radius);
}
}
color("red")
cylinder(22, 2, 2, $fn = 100);
}
echo(clamped_radius);
// ---------
// Sample 3:
// Use a rectangular plate that is larger than outer_circle_radius.
translate([0, -100, 0]) // move it out of the way
difference() {
// unlike cylinders, cubes are made with one corner on the origin
// since our three holes are distributed around the origin,
// move the plate to be overlapping the origin the correct amount
translate([-outer_circle_radius, - (2 * outer_circle_radius), 0])
cube([outer_circle_radius * 2, outer_circle_radius * 3, 10]);
// and put the holes in it
color("orange")
for (center = info[0])
{
translate([center[0], center[1], -1])
cylinder(h = 22, r1 = clamped_radius, r2 = clamped_radius);
}
}
// -----------------------------------------------
// The function that does the calculations and returns
// a list of centers and the desired inner circle radius clamped to
// the maximum size that will allow all three inner circles to fit
// within the outer circle radius with very thin space between them.
// The third parameter 'space' lets you tune the padding that is used
// to calculate the centers and the resulting clamped_radus, default is 1.
// You can pass smaller values for this value but this will override the
// "fits within the outer circle" restriction and may produce undesirable results.
//
// Result:
// [[[center1_x, center1_y], [center2_x, center2_y], [center3_x, center3_y]], clamped_radius]
function threeCenters(outer_radius, inner_radius, space = 1) =
// calculate the largest inner circle radius that will fit inside the outer circle
// (will touch each other and the outer circle).
// Do *not* take into account the space parameter here because we want to calculate the
// points without the spacing so that they are equally close to the edges and center of
// the outer circle. We subtract the spacing when we clamp the radius below.
let (max_inner_radius = (outer_radius) / (1 + 2 / sqrt(3)) )
let (p1 = [ -(max_inner_radius), -( (sin(30) / sin(60)) * (max_inner_radius) )])
let (p2 = [ (max_inner_radius), -( (sin(30) / sin(60)) * (max_inner_radius) )])
let (p3 = [ 0, (max_inner_radius) / sin(60) ])
// Calculate the largest inner circle radius that will fit based on the outer
// circle radius passed in and the "space" parameter.
let (clamped_inner_radius = min(inner_radius, max_inner_radius - (space * 2)))
// return the results as a list
[[p1, p2, p3], clamped_inner_radius];
@GeekAndDad
Copy link
Author

GeekAndDad commented Nov 7, 2023

Version 3.0.2 fixes the space parameter and makes the layout actually correct in all cases that I've tested.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment