This flowery shape has a sort of sacred significance to me, it symbolizes five vows I made to Jesus many years ago: one of Love (or one might say Growth), one of Honesty, one of Compassion, one of Ambition, and one of Humility.
The beauty in these diagrams comes from wanting circular arcs in a polyhedron which are tangent to the lines of that polyhedron (dotted lines). What makes the middle wonderful is that the inner arcs have the same radius as the bounding circle. That's actually also true for the top diagram, too.
Log in to post a comment.
// You can find the Turtle API reference here: https://turtletoy.net/syntax Canvas.setpenopacity(1); // Global code will be evaluated once. const turtle = new Turtle(); const deg = Math.PI/180; const sind = angle => Math.sin(deg * angle); const dotting = 0.125; const dotting_count = 20; function flower(R, num_sides, skip_nodes) { // to describe a regular n-gon a turtle has to turn by τ = 360° / n. const turn = 360 / num_sides; // side length of the polygon, necessary to walk between sublattices const len = R * 2 * sind(turn / 2); if (skip_nodes <= 0) { // degenerate case with infinitely large circles centered at infinity return i => { if (i >= num_sides) { // reset orientation and be done turtle.left(turn / 2); return false; } if (i === 0) { turtle.right(turn / 2); } const major = dotting * len / dotting_count; const minor = (1 - dotting) * len / dotting_count; for (let i = 0; i < dotting_count; i++) { turtle.forward(major/2); turtle.penup(); turtle.forward(minor); turtle.pendown(); turtle.forward(major/2); } turtle.right(turn); return true; } } /* Let s be the number of skipped nodes in the n-gon, with points G_1, ... G_n. We'll number these so the inner-circle arc begins at B = G_1 and ends at E = G_{s+2}. The arc is centered at some flower center F while the bounding circle is centered at some absolute center C. We first want to know how much angle the circular arc about F subtends, call that ψ, then we can use that to calculate the radius. We can calculate ψ with turtle power! Since we have tangency, a turtle which walks the arc must turn just as much as a turtle that walks from B to E along the G_i, and that one turns a total of s times by τ (defined above), so ψ = s · 360° / n. */ const arc_angle = skip_nodes * 360 / num_sides; /* Now for the radius, we have the angle ∠BFC = ψ/2 and we need ∠CFB. There are two ways to see this angle. The first is directly: an angle ∠G_iCG_{i+1} subtends 360° / n and walking backwards from G_1 = G_{n+1} back to G_{s+2} we have n − s − 1 arcs of that size, CF must bisect that angle. But I also like the turtle argument: think about the line tangent to the circle at B, and for clarity let's rotate or project G_2 onto a point L on this line so that we have some point on that line to talk about. We saw above that this angle ∠LBG_2 is τ / 2, that is how much the turtle has to turn to begin the inscribed n-gon after walking the circle that circumscribes it. But notice that you have two right angles which share B: ∠LBC = 90° and of course that from tangency, ∠FBG_2 = 90°, and you know that the rotation mapping the one onto the other is τ / 2. This rotation appears directly as also being ∠FBC = τ / 2. So since we are looking at this triangle FBC with angles s τ / 2 and τ / 2, the remaining angle is just ∠BCF = 180° − (s + 1) τ / 2. Once we have this triangle we can say that its altitude must be: R sin ∠BCF = r sin ∠CFB which gives us r/R. Since the sin(180° − x) = sin(x) we have just: */ const r = R * sind((skip_nodes + 1) * turn / 2) / sind(skip_nodes * turn / 2); return i => { if (i >= num_sides) { turtle.right(turn / 2); return false; } if (i === 0) { turtle.right(turn / 2); } else if ( i * (skip_nodes + 1) % num_sides === 0) { turtle.penup(); turtle.forward(len); turtle.right(turn); turtle.pendown(); } turtle.circle(r, arc_angle); turtle.right(turn); return true; }; } function doFlower(center_x, center_y, radius, sides, skip) { turtle.penup(); turtle.goto(center_x, center_y - radius); turtle.setheading(0); turtle.pendown(); turtle.circle(radius); const straights = flower(radius, sides, 0); for (let i = 0; straights(i); i++); const walker = flower(radius, sides, skip); for (let i = 0; walker(i); i++); } function walk(i) { switch (i) { case 0: doFlower(0, -80, 10, 3, 1); return true; case 1: doFlower(-40, -50, 10, 4, 1); return true; case 2: doFlower(40, -50, 10, 4, 2); return true; case 3: doFlower(-65, 2.5, 10, 5, 1); return true; case 4: doFlower(0, 2.5, 40, 5, 2); return true; case 5: doFlower(65, 2.5, 10, 5, 3); return true; case 6: doFlower(-65, 65, 10, 6, 1); return true; case 7: doFlower(-22.5, 65, 10, 6, 2); return true; case 8: doFlower(22.5, 65, 10, 6, 3); return true; case 9: doFlower(65, 65, 10, 6, 4); return true; default: return false; } }