I tried to come up with my solution. First, I created a

**two_connected_circles** module:

include <rotate_p.scad>;

include <bezier_curve.scad>;

include <rounded_extrude.scad>;

$fn = 36;

radius = 10;

leng = 40;

t_step = 0.1;

tangent_angle = 40;

module two_connected_circles(radius, dist, tangent_angle, t_step) {

half_dist = dist / 2;

p1 = rotate_p([0, -radius], tangent_angle) + [-half_dist, 0];

p2 = rotate_p([radius * tan(tangent_angle), -radius], tangent_angle) + [-half_dist, 0];

p3 = [-p1[0], p1[1]];

p4 = [-p2[0], p2[1]];

curve_pts = bezier_curve(t_step, [p1, p2, p4, p3]);

leng_pts = len(curve_pts);

upper_curve_pts =

[

for(i = [0:leng_pts - 1])

curve_pts[leng_pts - i - 1]

];

lower_curve_pts =

[

for(pt = curve_pts)

[pt[0], -pt[1]]

];

translate([-half_dist, 0])

circle(radius);

translate([half_dist, 0])

circle(radius);

polygon(

concat(

upper_curve_pts,

lower_curve_pts

)

);

}

two_connected_circles(

radius, leng, tangent_angle, t_step

);

After that, I created a

**tri_connected_circles** module:

module tri_connected_circles(radius, leng, tangent_angle, t_step) {

angle = 60;

half_leng = leng / 2;

module two_circles() {

two_connected_circles(radius, leng, tangent_angle, t_step, round_r);

}

translate([0, -half_leng * tan(30), 0]) union() {

two_circles();

translate([-half_leng, 0, 0])

rotate(angle)

translate([half_leng, 0, 0])

two_circles();

translate([half_leng, 0, 0])

rotate(-angle)

translate([-half_leng, 0, 0])

two_circles();

}

}

tri_connected_circles(radius, leng, tangent_angle, t_step);

Then, I can rounded it by the following code:

round_r = 3;

rounded_extrude([leng + 2 * radius, leng * tan(60) + 2 * radius], round_r, 180)

union() {

circle(radius * 1.5);

tri_connected_circles(radius, leng, tangent_angle, t_step);

}

The complete code:

include <rotate_p.scad>;

include <bezier_curve.scad>;

include <rounded_extrude.scad>;

$fn = 36;

radius = 10;

leng = 40;

t_step = 0.1;

tangent_angle = 40;

round_r = 3;

module two_connected_circles(radius, dist, tangent_angle, t_step) {

half_dist = dist / 2;

p1 = rotate_p([0, -radius], tangent_angle) + [-half_dist, 0];

p2 = rotate_p([radius * tan(tangent_angle), -radius], tangent_angle) + [-half_dist, 0];

p3 = [-p1[0], p1[1]];

p4 = [-p2[0], p2[1]];

curve_pts = bezier_curve(t_step, [p1, p2, p4, p3]);

leng_pts = len(curve_pts);

upper_curve_pts =

[

for(i = [0:leng_pts - 1])

curve_pts[leng_pts - i - 1]

];

lower_curve_pts =

[

for(pt = curve_pts)

[pt[0], -pt[1]]

];

translate([-half_dist, 0])

circle(radius);

translate([half_dist, 0])

circle(radius);

polygon(

concat(

upper_curve_pts,

lower_curve_pts

)

);

}

module tri_connected_circles(radius, leng, tangent_angle, t_step) {

angle = 60;

half_leng = leng / 2;

module two_circles() {

two_connected_circles(radius, leng, tangent_angle, t_step, round_r);

}

translate([0, -half_leng * tan(30), 0]) union() {

two_circles();

translate([-half_leng, 0, 0])

rotate(angle)

translate([half_leng, 0, 0])

two_circles();

translate([half_leng, 0, 0])

rotate(-angle)

translate([-half_leng, 0, 0])

two_circles();

}

}

* two_connected_circles(

radius, leng, tangent_angle, t_step

);

* tri_connected_circles(radius, leng, tangent_angle, t_step);

rounded_extrude([leng + 2 * radius, leng * tan(60) + 2 * radius], round_r, 180)

union() {

circle(radius * 1.5);

tri_connected_circles(radius, leng, tangent_angle, t_step);

}

You can find rotate_p.scad, bezier_curve.scad, rounded_extrude.scad here:

https://justinsdk.github.io/dotSCAD/