The knot 10_123

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;
    }
}