Problems calculating an average angle

I try to calculate an average angle, but it flips on the left side.
(And if I allow negative angles then it flips on the right side).

(It’s about the purple line)

How can I correctly create the average angle?

float[][] points_reference = new float[][] {{0.251, 0.289}, {0.282, 0.289}, {0.306, 0.289}, {0.34, 0.283}, {0.36, 0.275}, {0.382, 0.264}, {0.392, 0.257}, {0.407, 0.239}, {0.412, 0.224}, {0.414, 0.211}, {0.416, 0.188}, {0.419, 0.175}, {0.425, 0.163}, {0.442, 0.145}, {0.452, 0.141}, {0.465, 0.137}, {0.478, 0.135}, {0.497, 0.135}, {0.513, 0.135}, {0.527, 0.135}, {0.55, 0.141}, {0.572, 0.149}, {0.591, 0.156}, {0.605, 0.163}, {0.628, 0.175}, {0.638, 0.182}, {0.646, 0.19}, {0.655, 0.202}, {0.661, 0.215}, {0.666, 0.232}, {0.668, 0.247}, {0.669, 0.269}, {0.666, 0.294}, {0.643, 0.333}, {0.61, 0.378}, {0.591, 0.398}, {0.577, 0.412}, {0.57, 0.42}, {0.561, 0.432}, {0.559, 0.449}, {0.56, 0.462}, {0.563, 0.473}, {0.574, 0.478}, {0.593, 0.487}, {0.61, 0.495}, {0.636, 0.504}, {0.645, 0.518}, {0.655, 0.531}, {0.666, 0.543}, {0.673, 0.551}, {0.68, 0.559}, {0.687, 0.571}, {0.692, 0.591}, {0.693, 0.605}, {0.692, 0.621}, {0.688, 0.631}, {0.682, 0.645}, {0.674, 0.659}, {0.668, 0.67}, {0.661, 0.68}, {0.652, 0.692}, {0.641, 0.7}, {0.631, 0.705}, {0.619, 0.709}, {0.607, 0.713}, {0.594, 0.717}, {0.575, 0.72}, {0.555, 0.721}, {0.535, 0.721}, {0.515, 0.717}, {0.498, 0.709}, {0.471, 0.697}, {0.455, 0.688}, {0.439, 0.681}, {0.424, 0.673}, {0.409, 0.665}, {0.394, 0.657}, {0.38, 0.649}, {0.37, 0.642}, {0.36, 0.634}, {0.351, 0.628}, {0.339, 0.621}, {0.329, 0.618}, {0.318, 0.618}, {0.31, 0.626}, {0.3, 0.635}, {0.292, 0.643}, {0.281, 0.654}, {0.272, 0.668}, {0.264, 0.679}, {0.261, 0.689}, {0.261, 0.7}, {0.248, 0.717}, {0.238, 0.726}, {0.226, 0.734}, {0.213, 0.739}, {0.196, 0.744}, {0.186, 0.747}, {0.175, 0.749}, {0.16, 0.75}, {0.146, 0.749}, {0.132, 0.737}, {0.125, 0.724}, {0.119, 0.71}, {0.114, 0.701}, {0.11, 0.69}, {0.105, 0.677}, {0.103, 0.659}, {0.107, 0.64}, {0.116, 0.622}, {0.126, 0.609}, {0.134, 0.602}, {0.143, 0.595}, {0.151, 0.588}, {0.165, 0.577}, {0.177, 0.564}, {0.19, 0.551}, {0.203, 0.54}, {0.212, 0.534}, {0.221, 0.528}, {0.208, 0.522}, {0.194, 0.516}, {0.182, 0.51}, {0.173, 0.503}, {0.164, 0.495}, {0.152, 0.489}, {0.141, 0.484}, {0.132, 0.476}, {0.122, 0.464}, {0.116, 0.455}, {0.111, 0.442}, {0.109, 0.428}, {0.109, 0.415}, {0.115, 0.406}, {0.125, 0.397}, {0.136, 0.391}, {0.15, 0.39}, {0.16, 0.388}, {0.174, 0.387}, {0.188, 0.387}, {0.187, 0.374}, {0.175, 0.359}, {0.165, 0.347}, {0.159, 0.335}, {0.151, 0.319}, {0.145, 0.306}, {0.14, 0.295}, {0.137, 0.281}, {0.132, 0.27}, {0.125, 0.251}, {0.12, 0.234}, {0.119, 0.22}, {0.119, 0.199}, {0.12, 0.181}, {0.128, 0.166}, {0.141, 0.148}, {0.153, 0.138}, {0.166, 0.131}, {0.178, 0.127}, {0.192, 0.127}, {0.206, 0.133}, {0.218, 0.144}, {0.227, 0.157}, {0.232, 0.169}, {0.237, 0.187}, {0.239, 0.2}, {0.239, 0.212}, {0.239, 0.225}, {0.239, 0.236}, {0.236, 0.246}, {0.236, 0.257}, {0.238, 0.267}, {0.243, 0.276}, {0.251, 0.289}, {0.282, 0.289}, {0.306, 0.289}, {0.34, 0.283}, {0.36, 0.275}, {0.382, 0.264}, {0.392, 0.257}, {0.407, 0.239}, {0.412, 0.224}, {0.414, 0.211}, {0.416, 0.188}, {0.419, 0.175}, {0.425, 0.163}, {0.442, 0.145}, {0.452, 0.141}, {0.465, 0.137}, {0.478, 0.135}, {0.497, 0.135}, {0.513, 0.135}, {0.527, 0.135}, {0.55, 0.141}, {0.572, 0.149}, {0.591, 0.156}, {0.605, 0.163}, {0.628, 0.175}, {0.638, 0.182}, {0.646, 0.19}, {0.655, 0.202}, {0.661, 0.215}, {0.666, 0.232}, {0.668, 0.247}, {0.669, 0.269}, {0.666, 0.294}, {0.643, 0.333}, {0.61, 0.378}, {0.591, 0.398}, {0.577, 0.412}, {0.57, 0.42}, {0.561, 0.432}, {0.559, 0.449}, {0.56, 0.462}, {0.563, 0.473}, {0.574, 0.478}, {0.593, 0.487}, {0.61, 0.495}, {0.636, 0.504}, {0.645, 0.518}, {0.655, 0.531}, {0.666, 0.543}, {0.673, 0.551}, {0.68, 0.559}, {0.687, 0.571}, {0.692, 0.591}, {0.693, 0.605}, {0.692, 0.621}, {0.688, 0.631}, {0.682, 0.645}, {0.674, 0.659}, {0.668, 0.67}, {0.661, 0.68}, {0.652, 0.692}, {0.641, 0.7}, {0.631, 0.705}, {0.619, 0.709}, {0.607, 0.713}, {0.594, 0.717}, {0.575, 0.72}, {0.555, 0.721}, {0.535, 0.721}, {0.515, 0.717}, {0.498, 0.709}, {0.471, 0.697}, {0.455, 0.688}, {0.439, 0.681}, {0.424, 0.673}, {0.409, 0.665}, {0.394, 0.657}, {0.38, 0.649}, {0.37, 0.642}, {0.36, 0.634}, {0.351, 0.628}, {0.339, 0.621}, {0.329, 0.618}, {0.318, 0.618}, {0.31, 0.626}, {0.3, 0.635}, {0.292, 0.643}, {0.281, 0.654}, {0.272, 0.668}, {0.264, 0.679}, {0.261, 0.689}, {0.261, 0.7}, {0.248, 0.717}, {0.238, 0.726}, {0.226, 0.734}, {0.213, 0.739}, {0.196, 0.744}, {0.186, 0.747}, {0.175, 0.749}, {0.16, 0.75}, {0.146, 0.749}, {0.132, 0.737}, {0.125, 0.724}, {0.119, 0.71}, {0.114, 0.701}, {0.11, 0.69}, {0.105, 0.677}, {0.103, 0.659}, {0.107, 0.64}, {0.116, 0.622}, {0.126, 0.609}, {0.134, 0.602}, {0.143, 0.595}, {0.151, 0.588}, {0.165, 0.577}, {0.177, 0.564}, {0.19, 0.551}, {0.203, 0.54}, {0.212, 0.534}, {0.221, 0.528}, {0.208, 0.522}, {0.194, 0.516}, {0.182, 0.51}, {0.173, 0.503}, {0.164, 0.495}, {0.152, 0.489}, {0.141, 0.484}, {0.132, 0.476}, {0.122, 0.464}, {0.116, 0.455}, {0.111, 0.442}, {0.109, 0.428}, {0.109, 0.415}, {0.115, 0.406}, {0.125, 0.397}, {0.136, 0.391}, {0.15, 0.39}, {0.16, 0.388}, {0.174, 0.387}, {0.188, 0.387}, {0.187, 0.374}, {0.175, 0.359}, {0.165, 0.347}, {0.159, 0.335}, {0.151, 0.319}, {0.145, 0.306}, {0.14, 0.295}, {0.137, 0.281}, {0.132, 0.27}, {0.125, 0.251}, {0.12, 0.234}, {0.119, 0.22}, {0.119, 0.199}, {0.12, 0.181}, {0.128, 0.166}, {0.141, 0.148}, {0.153, 0.138}, {0.166, 0.131}, {0.178, 0.127}, {0.192, 0.127}, {0.206, 0.133}, {0.218, 0.144}, {0.227, 0.157}, {0.232, 0.169}, {0.237, 0.187}, {0.239, 0.2}, {0.239, 0.212}, {0.239, 0.225}, {0.239, 0.236}, {0.236, 0.246}, {0.236, 0.257}, {0.238, 0.267}, {0.243, 0.276}};
float[][] points_source;

int X = 0;
int Y = 1;

void setup() {
  size(900, 900);
  pixelDensity(2);

  for (int i = 0; i < points_reference.length; i++) {
    points_reference[i][X] = map(points_reference[i][X], 0, 1, width * 0.3, width * 0.7);
    points_reference[i][Y] = map(points_reference[i][Y], 0, 1, height * 0.3, height * 0.7);
  }

  points_source = new float[points_reference.length][2];

  for (int i = 0; i < points_reference.length; i++) {
    points_source[i][X] = points_reference[i][X];
    points_source[i][Y] = points_reference[i][Y];
  }
}



void draw() {

  // reposition the source points
  float tx = map(mouseX, 0, width, -450, 450);
  float ty = map(mouseY, 0, height, -450, 450);
  for (int i = 0; i < points_reference.length; i++) {
    points_source[i][X] = points_reference[i][X] + tx;
    points_source[i][Y] = points_reference[i][Y] + ty;
  }


  background(30);

  noStroke();
  for (float[] xy : points_reference) {
    fill(255, 0, 0, 100);
    ellipse(xy[X], xy[Y], 15, 15);
  }



  noStroke();
  for (float[] xy : points_source) {
    fill(0, 255, 0, 100);
    ellipse(xy[X], xy[Y], 15, 15);
  }


  float avg_angle = 0;
  float avg_mag = 0;

  float cx = 0, cy = 0;

  for (float[] xy : points_source) {

    float x = xy[X];
    float y = xy[Y];

    cx += x;
    cy += y;

    // find the closest point on the reference
    float closest_d = MAX_FLOAT;
    float[] closest = null;
    for (float[] xy_ref : points_reference) {
      float d = dist(x, y, xy_ref[X], xy_ref[Y]);
      if (d < closest_d) {
        closest_d = d;
        closest = xy_ref;
        if (d == 0) break;
      }
    }

    float a = atan2(closest[Y] - y, closest[X] - x);
    // HERE IT STARTS FLIPPING!!
    a = (a >= 0) ? a : TWO_PI + a; 
    
    pushMatrix();
    translate(x, y);
    rotate(a);
    stroke(255, 255, 0);
    line(0, 0, 30, 0);
    popMatrix();

    avg_angle += a;
    avg_mag += closest_d;
  }


  avg_angle /= points_source.length;
  avg_mag /= points_source.length;
  cx /= points_source.length;
  cy /= points_source.length;

  fill(255);
  text("avg_angle: "+avg_angle, 50, 50);
  text("avg_mag: "+avg_mag, 50, 70);

  ellipse(cx, cy, 5, 5);
  pushMatrix();
  translate(cx, cy);
  rotate(avg_angle);
  stroke(255, 0, 255);
  line(0, 0, avg_mag, 0);
  popMatrix();
}
1 Like

Can you elaborate what the yellow lines are doing? You average their angles, right? What is the role of the red and green points in setting the direction of the yellow lines?

Kf

1 Like