Link more circles
Flashback»
In my last blog post I wrote about the smooth connection of three arcs and you are looking into part two of this experiment.
You might still wonder, why we are looking into this at all. Be assured, it will make sense in the end.
More circles»
We are getting closer, because next we will make a smooth onnection of multiple arcs.
Imagine the blue circle is the same size as the red circle. And then there are even more of the red circles, and also more of the green circles connecting the others.
You get a chain of two types of circles defined by their radius of both types of circles and the distance between the red ones. Feel free to play with the rulers and see the resulting shape of the line.
And because of the symmetry, the equations are now a bit simpler and the distances are repeated again and again.
let count = 4;
let ra = 50;
let rc = 20;
let dist = 20;
$: ac = ra + rc;
$: ab = dist + 2*ra;
$: x = (ab*ab) / (2*ab);
$: y = Math.sqrt(ac*ac - x*x);
$: aPoints = nums(count).map(i => point(offset.x + i*ab, offset.y));
$: cPoints = nums(count-1).map(i => point(offset.x + x + i*ab, offset.y - y));
After that we can have another iteration over the connection point array (cPoints) to get our waypoints along the path of connected arcs.
$: path =
cPoints.map((pc, i) => {
const pa = aPoints[i];
const pb = aPoints[i+1];
const pac = plerp(pa,pc,ra/(ra+rc));
const pbc = plerp(pb,pc,ra/(ra+rc));
return (
(i === 0 ? moveTo(point(pa.x-ra, pa.y)) : "") + " " +
arcTo(pac, ra) + " " +
arcTo(pbc, rc, 0) + " " +
(i+2 === count ? arcTo(point(pb.x+ra, pb.y), ra) : "")
);
}).join(" ")
;
The target shape»
Now, instead of using reactive assignments from Svelte, I putted everything into a function which produces such a wavy path by just defining the number of circles, radius 1 and radius 2 and the distance between the circles.
And out of four of these wavy pathes I could build the following shape.
$: path1 = getWavyPath(count, ra, rc, dist, offset);
$: path2 = getWavyPath(count, ra-thick, rc+thick, dist+thick*2, offset);
$: path3 = getWavyPath(count, ra, rc, dist, offset, true);
$: path4 = getWavyPath(count, ra-thick, rc+thick, dist+thick*2, offset, true);
I won’t go much more into the implementation details for now, but instead I’ll tell you, what I needed this shape for. You can still have a look at
components source
<script>
import RangeSlider from '$lib/shared/range-slider.svelte';
import SvgContainer from '$lib/shared/svg-container.svelte';
import { nums } from '$lib/utils/math-utils.js';
import { point, plerp } from '$lib/utils/path-utils.js';
import { moveTo, arcTo, pathToFixed } from '$lib/utils/svg-utils.js';
const offset = point(50,60);
let count = 4;
let ra = 40;
let rc = 16;
let dist = 8;
let thick = 8;
export let showPath = false;
const getWavyPath = (count, ra, rc, dist, offset, neg) => {
const ac = ra + rc;
const ab = dist + 2*ra;
const x = (ab*ab) / (2*ab);
const y = Math.sqrt(ac*ac - x*x);
const aPoints = nums(count).map(i => point(offset.x + i*ab, offset.y));
const cPoints = nums(count-1).map(i => point(offset.x + x + i*ab, offset.y + (neg ? y : -y)));
return cPoints.map((jp, i) => {
const pa = aPoints[i];
const pb = aPoints[i+1];
const pac = plerp(pa,jp,ra/(ra+rc));
const pbc = plerp(pb,jp,ra/(ra+rc));
return (
(i === 0 ? moveTo(point(pa.x-ra, pa.y)) : "") + " " +
arcTo(pac, ra, neg?0:1) + " " +
arcTo(pbc, rc, neg?1:0) + " " +
(i+2 === count ? arcTo(point(pb.x+ra, pb.y), ra, neg?0:1) : "")
);
}).join(" ");
};
$: path1 = getWavyPath(count, ra, rc, dist, offset);
$: path2 = getWavyPath(count, ra-thick, rc+thick, dist+thick*2, offset);
$: path3 = getWavyPath(count, ra, rc, dist, offset, true);
$: path4 = getWavyPath(count, ra-thick, rc+thick, dist+thick*2, offset, true);
</script>
<style>
.controls {
display: inline-block;
vertical-align: top;
}
#path {
font-family: monospace;
}
</style>
<div class="controls">
<RangeSlider label="radius 1" bind:value={ra} min={0} max={50} color="#7f7f7f" />
<RangeSlider label="radius 2" bind:value={rc} min={dist/2} max={50} color="#7f7f7f" />
<RangeSlider label="distance" bind:value={dist} min={0} max={2*rc} color="#7f7f7f" />
</div>
<div class="controls">
<RangeSlider label="count" bind:value={count} min={2} max={8} color="#7f7f7f" />
<RangeSlider label="thickness" bind:value={thick} min={0} max={10} color="#7f7f7f" />
</div>
<SvgContainer width="400" height="120">
<g fill="red">
<path d={path1} stroke-width="2" stroke="#aaaaaa" fill="none" />
<path d={path2} stroke-width="1" stroke="#7f7f7f" fill="none" />
<path d={path3} stroke-width="2" stroke="#aaaaaa" fill="none" />
<path d={path4} stroke-width="1" stroke="#7f7f7f" fill="none" />
</g>
</SvgContainer>
{#if showPath}
<div id="path">
{pathToFixed(path1)}
</div>
{/if}
Adding the 3rd dimension»
Maybe you already guessed what can be done with this shape, maybe not.
Lets add a third dimension and put some height to this shape.
Most recently I’ve got into 3D printing and I started to create models on my own. But instead of using point and click 3D modelling Tools like TinkerCad, FreeCAD or even Blender, I immediately felt more comfortable with OpenSCAD where I write code to construct the 3D model.
There are lot’s of different spice holder models at thingiverse, for example this one. So I asked myself how I could do something like that myself with OpenSCAD.
And because I’m still a newbie to OpenSCAD, I used Svelte again to construct this more complex shape. Then in OpenSCAD I could just linear extrude the shape to get more close to building another spice rack.
I do not switch to OpenSCAD code in full detail here, just a snippet for illustration.
use <get_wavy_path.scad>
$fn=240;
outer_color = "#9999cc";
inner_color = "#666699";
count = 3;
ra = 20;
rc = 15;
dist = 10;
h = 36;
thick = 2.75;
difference() {
// outer shape
color(outer_color)
linear_extrude(height = h)
get_wavy_path(count, ra, rc, dist, [ra,0]);
// inner shape
color(inner_color)
translate([0,0,thick])
linear_extrude(height = h+2*thick)
get_wavy_path(count, ra-thick, rc+thick, dist+2*thick, [ra,0]);
}
I should also mention, that because I wasn’t yet aware how to draw arcs in OpenSCAD by myself, I really used SVG and a nice OpenSCAD library called pathbuilder to translate SVG strings into 2D shapes.
But that’s another story, and maybe another blog post. I also plan to release my OpenSCAD code at Github sometime soon.