Please find below the explanation of the process. I won’t share the full code as it is a huge mess but I’ll try to share relevant links.
The first step is to create grid of circles. You can play with the size and spacing of the circles but for simplicity I kept them the same size and on a strict grid as it is in the example that you shared:
Then, the idea is to select some circles that will act as pins to wrap a thread around. In that step, it is also key to define the order in which the thread will wrap around:
Once the pins and their orders are known, we can start to define in which orientation the thread will need to wrap around the pins.
The way to do it is to check the position of the previous pin, the current pin and the next pin. They form a triangle and we need to define if we traverse the triangle clockwise or counterclockwise.
An example with the pin 0. We can see that if we go from pin 10 to pin 0 to pin 1 and back to pin 10 again, we move in the clockwise orientation. We can then set our pin 0 to be a clockwise pin:
Some times, when the are aligned, there is no really defined orientation. In that case will leave the orientation and undefined for the time being:
In practice, you can use the determinant of the triangle to determine it’s orientation. If A, B, C are the 3 points, in order, of your triangle, then you can define the orientation matrix as followed:
if det(O) is negative then the triangle is oriented clockwise, and counterclockwise if positive.
Keep in mind that it works for a 2D space where x is pointing right and left pointing top. In processing y is pointing down so the results are reversed: clockwise when positive and counterclockwise when negative.
After doing it for all pins, we have the following result:
The next step would be to find how to connect each pin between them.
First let’s find the different tangents. You can write a piece of code to take care of all possible scenarios but since I know the circles have the same size and never cross each other, I know that only 4 tangents exists and simplified the code as much as possible.
My code is based on the second figure of this page to find the 2 green tangents in the following figure.
For the 2 red tangents, you can obtain them by computing the vector u (in blue) with a magnitude equal to the radius of the circles and with the direction from the center of the first circle to the center of the second circle. Then by rotating it -90° or +90°, it can give you the start and end points of the 2 tangents:
Now we know how to find the tangents, we need a way to select the one(s) that work(s). For this we will use the pin orientation that we determined earlier. The idea is that the tangent should go in the same direction than the pin.
If we look at the example of the link between the pin 0 and the pin 1, it is quite intuitive that only the green tangent would work:
In code, we can verify that the tangent is correctly oriented by creating a vector v (in purple) perpendicular to the vector (in blue) that goes from the center of the circle to the point of the tangent on that circle:
By construction (rotation of -90° of the blue vector), we know that this vector v is in the clockwise direction. If the pin is in the clockwise direction, then we know the tangent goes in the right direction if it is pointing the same way of v. If the pin is in the counterclockwise direction, then we know the tangent goes in the right direction if it is pointing in the opposite direction of v.
We can check if 2 vectors are heading in the same direction by taking their dot product. If it is positive, they have the same direction, if negative, the go opposite direction. It is equal to 0 if the are orthogonal.
Repeat for the end point of the tangent to check if the tangent is also valid with the direction of the second pin.
For pin without defined orientation, it means that several tangents are possible. In that case, I put them in a stack of possible tangents and picked one at random. That’s the case between pin 4 and pin 5, 2 options are possible (in green):
But once selected, it also now define the orientation of the pin. In the example below, pin 5 must now be oriented counterclockwise:
By repeating this process over each pair of pins, you end up with the final set of tangents:
All left to do is to connect the dots to create the shape. Sadly ther is no
arcVertex() function so I had to create mine by approximating a circle arc with the
I used PM 2ring’s answer for the math. What was left to do was to determine the full angle span of the arc and if higher than 90° split it in as many several parts as needed to apply the approximation on each arc section.
One thing I haven’t done is to check for the shape intersecting itself. If done you could keep generating new shapes until a proper one pop up.
Hope this help. Do not hesitate if you have any questions =)