Implementation of rotation in chaos games

71 Views Asked by At

I am trying to implement rotation in chaos games. I have the following implementation for determining the next point:

function w(x, attractor) {
    let compression_ratio = attractor.compression_ratio;
    
    if ('rotation' in attractor) {
        let unrotated_new_x = 
          compression_ratio * x[0] + 
            (1.0 - compression_ratio) * attractor['point'][0];
        let unrotated_new_y = 
          compression_ratio * x[1] + 
            (1.0 - compression_ratio) * attractor['point'][1];
        let dx = attractor['point'][0] - unrotated_new_x;
        let dy = attractor['point'][1] - unrotated_new_y;
        let d = Math.sqrt(dx**2 + dy**2);
        let theta_1 = Math.atan2(dy, dx);
        let theta_2 = attractor['rotation'] * Math.PI / 180.0;
        let theta = theta_1 + theta_2;
        let new_dx = d * Math.cos(theta);
        let new_dy = d * Math.sin(theta);

        return [attractor['point'][0] + new_dx, attractor['point'][1] + new_dy];
    } else {
        return [compression_ratio * x[0] + 
                  (1.0 - compression_ratio) * attractor['point'][0], 
                compression_ratio * x[1] + 
                  (1.0 - compression_ratio) * attractor['point'][1]];
    }
}

If I use these attractors (without rotation):

function get_sierpinski_triangle_attractors() {
  v_1 = [-1.0, 0.0];
  v_2 = [1.0, 0.0];
  v_3 = [0.0, Math.sqrt(3.0)];
  vs = [v_1, v_2, v_3];

  let attractors = 
    [{"point": vs[0], "compression_ratio": 0.5, 
      "probability": 1.0 / 3.0, "color": "red"}, 
     {"point": vs[1], "compression_ratio": 0.5, 
      "probability": 1.0 / 3.0, "color": "yellow"}, 
     {"point": vs[2], "compression_ratio": 0.5, 
      "probability": 1.0 / 3.0, "color": "blue"}];

  return attractors;
}

it works and I get this:

no rotation

However, if I set a rotation on the blue vertex:

  let attractors = 
    [{"point": vs[0], "compression_ratio": 0.5, 
      "probability": 1.0 / 3.0, "color": "red"}, 
     {"point": vs[1], "compression_ratio": 0.5, 
      "probability": 1.0 / 3.0, "color": "yellow"}, 
     {"point": vs[2], "compression_ratio": 0.5, 
      "probability": 1.0 / 3.0, "color": "blue", "rotation": 90}];

I get this:

actual

but I'm expecting something similar to this (except flipped vertically):

expected

Note that this is from "Chaos Rules!" by Robert L. Devaney of Boston University.

Moreover, if I set the rotation to 0:

  let attractors = 
    [{"point": vs[0], "compression_ratio": 0.5, 
      "probability": 1.0 / 3.0, "color": "red"}, 
     {"point": vs[1], "compression_ratio": 0.5, 
      "probability": 1.0 / 3.0, "color": "yellow"}, 
     {"point": vs[2], "compression_ratio": 0.5, 
      "probability": 1.0 / 3.0, "color": "blue", "rotation": 0}];

I expect to get the usual Sierpinski triangle. However, I get this:

actual with 0 rotation

What am I doing wrong here?

Note that my entire code base is available here.

1

There are 1 best solutions below

2
On
    let unrotated_new_x = 
      compression_ratio * x[0] + 
        (1.0 - compression_ratio) * attractor['point'][0];
    let unrotated_new_y = 
      compression_ratio * x[1] + 
        (1.0 - compression_ratio) * attractor['point'][1];
    let dx = unrotated_new_x - attractor['point'][0];
    let dy = unrotated_new_y - attractor['point'][1];
    let d = Math.sqrt(dx**2 + dy**2);
    let theta_1 = Math.atan2(dy, dx);
    let theta_2 = attractor['rotation'] * Math.PI / 180.0;
    let theta = theta_1 + theta_2;
    let new_dx = d * Math.cos(theta);
    let new_dy = d * Math.sin(theta);

    return [attractor['point'][0] + new_dx, attractor['point'][1] + new_dy];