Arc function is unpredictable!

hello all,

I am writing a code with a bunch of circles and lines intersecting and I would like to draw the arcs based on the points of intersection.
I isolated some parts of the code to illustrate my problem. (this is why the code may look overcomplicated for my exemple)
all I am lookin for is a predictable way to draw the arcs from a point A to B clockwise.
for my exemple , I have to add TWO_PI when one an angle the line is under the circle center aka the angle<PI in this case. but this is not working of every situation I have, and I can not study every situation. what am I missing ?

 if ( d<0) {
 arc(Circle2.center.x, Circle2.center.y, 500, 500, Angle1, TAU+Angle2);
  }

this should be easy but it is driving me crazy!
any suggestion ?

the code is a circle class and line class and a bunch of intersection detection function, between two circles and between a line and a circle.

import java.util.Arrays;
import java.util.Comparator;
import processing.pdf.*;
Line Line;
Circle Circle1, Circle2;
boolean show= true;

void setup() {
  size(800, 800);
  smooth();
  strokeWeight(1);
}


void draw() {
  background(#2D4D83);
  /// one line and two circles
  Line = new Line(0, mouseY, width, mouseY);
  Circle1 = new Circle(300, 400, 500);
  Circle2 = new Circle(500, 400, 500);
  Line.show();
  /// intersection points 
  PVector C1L=intersectLC(Circle1, Line, 0, 1);
  PVector C2L=intersectLC(Circle2, Line, 0, 0);
  PVector C1C2=intersectCC(Circle1.center, Circle2.center, 500, 500, 1, 0);
  
 //////////////////////////////////////////
 
 ///// drawing the arcs 
 
 //////////////////////////////////////////
 
 
  if (C1L!=null && C2L !=null && C1C2 != null) {
    float Angle1 = atan2(C2L.y-Circle2.center.y, C2L.x-Circle2.center.x);
    float Angle2  = atan2(C1C2.y-Circle2.center.y, C1C2.x-Circle2.center.x);

    //println(Angle1+" "+Angle2);
    pushStyle();
    strokeWeight(4);
    stroke(#FFA805);
    noFill();

    arc(Circle2.center.x, Circle2.center.y, 500, 500, Angle1, Angle2);
   
    // is the line above or under the circles center
    float d= Circle1.center.y - C2L.y ;
    
    //// uncomment to make it work correctly 
    
    // if ( d<0) {
    //arc(Circle2.center.x, Circle2.center.y, 500, 500, Angle1, TAU+Angle2);
    //}
    
    float Angle3 = atan2(C1C2.y-Circle1.center.y, C1C2.x-Circle1.center.x);
    float Angle4  = atan2(C1L.y-Circle1.center.y, C1L.x-Circle1.center.x);

   
     arc(Circle1.center.x, Circle1.center.y, 500, 500, Angle3, Angle4);
    popStyle();
  
  
  pushStyle();
  noStroke();
  fill(255, 0, 0);
  circle(C1L.x, C1L.y, 10);
  circle(C2L.x, C2L.y, 10);
  circle(C1C2.x, C1C2.y, 10);
  circle(Circle1.center.x, Circle1.center.y, 10);
  circle(Circle2.center.x, Circle2.center.y, 10);
  popStyle();
 
  pushStyle();
  noStroke();
  fill(255);
 text("C1L",C1L.x-10, C1L.y-10);
  text("C2L",C2L.x-10, C2L.y-10);
  text("C1C2",C1C2.x, C1C2.y-10);
  text("Circle1 center",Circle1.center.x, Circle1.center.y+20);
   text("Circle2 center",Circle2.center.x, Circle2.center.y+20);
  popStyle();
  }
}



//// coparators to sort an array of PVectors by x or y
static final Comparator<PVector> VEC_CMPX = new Comparator<PVector>() {
  @ Override final int compare(final PVector a, final PVector b) {
    int cmp;
    return
      (cmp = Float.compare(a.y, b.y)) != 0? cmp :
      Float.compare(a.x, b.x);
  }
};
static final Comparator<PVector> VEC_CMPY = new Comparator<PVector>() {
  @ Override final int compare(final PVector a, final PVector b) {
    int cmp;
    return
      (cmp = Float.compare(a.x, b.x)) != 0? cmp :
      Float.compare(a.y, b.y);
  }
};


//// class Circle
class Circle {
  PVector center;
  float radius; // diametre
  float x, y;
  Circle(float _x, float _y, float _radius) {
    center = new PVector(_x, _y);
    radius = _radius;
  }
  PVector circleCenter() {
    return center;
  }
  float radius() {
    return radius/2;
  }
}

//// class Line

class Line {
  float x1;
  float y1;
  float x2;
  float y2;
  public Line(float _x1, float _y1, float _x2, float _y2) {
    x1 = _x1;
    y1 = _y1;
    x2 = _x2;
    y2 = _y2;
  }
  PVector Start() {
    PVector start = new PVector(x1, y1);
    return start;
  }
  PVector End() {
    PVector end = new PVector(x2, y2);
    return end;
  }
  void show() {

    line(x1, y1, x2, y2);
  }
}

PVector intersectCC(PVector CC1, PVector CC2, float r1, float r2, int direction, int mode) {
  //horizontal =0 vertical 1 min=0 max 1
  PVector inter= new PVector();
  pushStyle();
  stroke(255);
  noFill();
  //if (show) {
  //  ellipse(CC1.x, CC1.y, r1, r1);
  //  ellipse(CC2.x, CC2.y, r2, r2);
  //}
  popStyle();
  PVector[] sh1sh2 = CCinter(CC1, CC2, r1, r2);
  if (sh1sh2 != null) {
    PVector[] Points=new PVector[sh1sh2.length];

    for (int i=0; i<sh1sh2.length; i++) {
      pushStyle();
      fill(255, 0, 0);
      Points[i]=new PVector(sh1sh2[i].x, sh1sh2[i].y);
      popStyle();
    }

    if (direction == 1 && mode==0) {
      Arrays.sort(Points, VEC_CMPY);
      inter = new PVector(Points[0].x, Points[0].y);
    }
    if (direction == 1 && mode==1) {
      Arrays.sort(Points, VEC_CMPY.reversed());
      inter = new PVector(Points[0].x, Points[0].y);
    }
    if (direction == 0 && mode==0) {
      Arrays.sort(Points, VEC_CMPX);
      inter = new PVector(Points[0].x, Points[0].y);
    }
    if (direction == 0 && mode==1) {
      Arrays.sort(Points, VEC_CMPX.reversed());
      inter = new PVector(Points[0].x, Points[0].y);
    }
  }
  return inter;
}
void lineV(PVector A, PVector B) {
  if (A != null && B!= null) {
    line(A.x, A.y, B.x, B.y);
  }
}



PVector[] CCinter(PVector CC1, PVector CC2, float R1, float R2) {
  float x1, x2, y1, y2;
  float r1=R1/2;
  float r2=R2/2;
  x1=CC1.x;
  x2=CC2.x;
  y1=CC1.y;
  y2=CC2.y;

  float a = x2 - x1;
  float b = y2 - y1;
  float ds = a*a + b*b;
  float d = sqrt( ds );
  if (r1 + r2 <= d)
    return null;

  if (d <= Math.abs( r1 - r2 ))
    return null;
  float t = sqrt( (d + r1 + r2) * (d + r1 - r2) * (d - r1 + r2) * (-d + r1 + r2) );
  float sx1 = 0.5 * (a + (a*(r1*r1 - r2*r2) + b*t)/ds);
  float sx2 = 0.5 * (a + (a*(r1*r1 - r2*r2) - b*t)/ds);
  float sy1 = 0.5 * (b + (b*(r1*r1 - r2*r2) - a*t)/ds);
  float sy2 = 0.5 * (b + (b*(r1*r1 - r2*r2) + a*t)/ds);
  sx1 += x1;
  sy1 += y1;
  sx2 += x1;
  sy2 += y1;
  PVector Sx1= new PVector(sx1, sy1);
  PVector Sx2= new PVector(sx2, sy2);
  PVector[] intersect=new PVector[2];
  intersect[0]=Sx1;
  intersect[1]=Sx2;
  if (show) {
    pushStyle();
    noStroke();
    fill(255, 0, 0);
    // ellipse(Sx1.x, Sx1.y, 10, 10);
    popStyle();
  }
  return intersect;
}

ArrayList<PVector> intersectionsLC(Circle C, Line L) {
  PVector start, end, circleCenter;
  float radius;
  start = new PVector(L.Start().x, L.Start().y);
  end= new PVector(L.End().x, L.End().y);
  circleCenter = new PVector(C.circleCenter().x, C.circleCenter().y);
  radius = C.radius();
  PVector d = PVector.sub(end, start);
  PVector f = PVector.sub(start, circleCenter);
  ArrayList<PVector> result = new ArrayList<PVector>();
  float a = d.dot(d);
  float b = 2 * f.dot( d );
  float c = f.dot(f) - radius * radius;
  float discriminant = b * b - 4 * a * c;
  if ( discriminant < 0 ) {
    return result;
  }
  discriminant = sqrt(discriminant);
  float t1 = (-b - discriminant) / (2 * a);
  float t2 = (-b + discriminant) / (2 * a);
  if (t1 >= 0 && t1 <= 1) {
    result.add(PVector.lerp(start, end, t1));
  }
  if (t2 >= 0 && t2 <= 1) {
    result.add(PVector.lerp(start, end, t2));
  }
  return result;
}

PVector intersectLC(Circle C, Line L, int direction, int mode) {
  //horizontal =0 vertical 1 min=0 max 1
  PVector CC1=new PVector();
  CC1=C.circleCenter();
  float radius=C.radius();

  //intersectionsLC
  PVector inter= new PVector();
  pushStyle();
  stroke(255);
  noFill();
  if (show) {
    ellipse(CC1.x, CC1.y, radius*2, radius*2);
  }
  popStyle();
  ArrayList<PVector> sh1sh2 = intersectionsLC(C, L);
  if (sh1sh2 != null) {
    int Len = sh1sh2.size();
    PVector[] Points=new PVector[Len];
    if (Len == 1) {
      Points[0]= new PVector(sh1sh2.get(0).x, sh1sh2.get(0).y);
      inter = new PVector(Points[0].x, Points[0].y);
      return inter;
    }
    if (Len==2) {
      for (int i=0; i<Len; i++) {
        pushStyle();
        fill(255, 0, 0);
        if (show) {
        }

        Points[i]=new PVector(sh1sh2.get(i).x, sh1sh2.get(i).y);
        popStyle();
      }
      if (direction == 1 && mode==0) {
        Arrays.sort(Points, VEC_CMPY);
        inter = new PVector(Points[0].x, Points[0].y);
      }
      if (direction == 1 && mode==1) {
        Arrays.sort(Points, VEC_CMPY.reversed());
        inter = new PVector(Points[0].x, Points[0].y);
      }
      if (direction == 0 && mode==0) {
        Arrays.sort(Points, VEC_CMPX);
        inter = new PVector(Points[0].x, Points[0].y);
      }
      if (direction == 0 && mode==1) {
        Arrays.sort(Points, VEC_CMPX.reversed());
        inter = new PVector(Points[0].x, Points[0].y);
      }
    }
  }
  return inter;
}

Hello @jeykech,

This was an initial exploration to just get the arcs correct…

  PVector C1L=intersectLC(Circle1, Line, 0, 1);
  PVector C2L=intersectLC(Circle2, Line, 0, 0);
  
  PVector C1C2=intersectCC(Circle1.center, Circle2.center, 500, 500, 1, 0);
  PVector C1C2_2 = new PVector(C1C2.x, C1C2.y+2*(400-C1C2.y));
  
  float Angle1 = atan2(C1C2.y-Circle2.center.y, C1C2.x-Circle2.center.x);
  float Angle2 = atan2(C1C2_2.y-Circle2.center.y, C1C2_2.x-Circle2.center.x);
  println("A1", degrees(Angle1), "A2", degrees(Angle2));
  
  noFill();
  strokeWeight(2);
  stroke(0, 255, 255);
  if(Angle1<TAU/2) Angle1+=TAU;  
  arc(Circle2.center.x, Circle2.center.y, 500, 500, Angle2, Angle1);

  float Angle3 = atan2(C1C2.y-Circle1.center.y, C1C2.x-Circle1.center.x);
  float Angle4 = atan2(C1C2_2.y-Circle1.center.y, C1C2_2.x-Circle1.center.x);
  println("A3", degrees(Angle3), "A4", degrees(Angle4));

  noFill();
  strokeWeight(2);
  stroke(255, 0, 255);
  //if(Angle3<TAU/2) Angle3+=TAU;  
  arc(Circle1.center.x, Circle1.center.y, 500, 500, Angle3, Angle4);  

:)

Hello @jeykech,

Example:

// Arcs with atan2()
// Author: glv
// Ver: 1.0.2 (slimmed down for PF post)
// Date: 2023-08-06

int x0, y0, x1, y1;
float w, h;

void setup()
  {
  size(500, 500);
  w = width;
  h = height;

  textSize(20);
  textAlign(CENTER, CENTER);
  }

void draw()
  {
  background(255);
  translate(w/2, h/2);

  // axes
  strokeWeight(2);
  stroke(128);
  line(-w/2, 0, w/2, 0);
  line(0, -h/2, 0, h/2);

  float ts = 1.2;                                  // Text offset

  float a0 = atan2(y0, x0);                        // Angle from x-axis
  stroke(255, 0, 0);                                
  line(0, 0, x0, y0);
  fill(0);  // Text
  text(str(x0) + ", " + str(y0) + ", " + str( int(degrees(a0)) ) + "°", x0*ts, y0*ts);

  float a1 = atan2(y1, x1);                        // Angle from x-axis
  stroke(0, 255, 0);                              
  line(0, 0, x1, y1);
  fill(0);                                         // Text
  text(str(x1) + ", " + str(y1) + ", " + str( int(degrees(a1)) ) + "°", x1*ts, y1*ts);

  // For drawing arcs
  arcs(a0, a1);
 
  }

void arcs(float a0, float a1)
  {
  println("a0", degrees(a0), "a1", degrees(a1)); //Before  
  noFill();
  
  if (a0-a1<0)
    a0 += TAU;

  //println("a1", degrees(a1), "a0", degrees(a0)); // After continuous
  
  stroke(0, 255, 255);            // magenta a1 to a0
  arc(0, 0, 100, 100, a1, a0);

  if (a1-a0<0)
    a1 += TAU;

  println("a0", degrees(a0), "a1", degrees(a1)); // After continuous
  println();

  stroke(0, 0, 255);              // blue magenta a0 to a1
  arc(0, 0, 50, 50, a0, a1);
  }

// Use right or left mouse button to move lines

void mouseDragged()
  {
  int x = mouseX;
  int y = mouseY;

  if (mousePressed)
    {
    if (mouseButton == LEFT)
      {
      x0 = x - width/2;
      y0 = y - width/2;
      }
    if (mouseButton == RIGHT)
      {
      x1 = x - width/2;
      y1 = y - width/2;
      }
    }
  }

I wrote this a while ago and slimmed it down for this post.
My arcs() function and print() statements may need some work.
It is not complete (in my notes) and you should scrutinize code.

:)

@glv thanx for your code, I will give it a try and see if it works for all the different cases I will come across!
have a good one! :slightly_smiling_face:

2 Likes

@glv your code is working flawlessly by itself but when I implemented the function in my code it started to behave differently, I am starting to loose the last two hairs on y bald head !!

import java.util.Arrays;
import java.util.Comparator;
import processing.pdf.*;
Line Line;
Circle Circle1, Circle2;
boolean show= true;

void setup() {
  size(1200, 1200);
  smooth();
  strokeWeight(1);
}


void draw() {
  background(#2D4D83);
  /// one line and two circles
  Line = new Line(0, mouseY, width, mouseY);
  Circle1 = new Circle(300, 400, 500);
  Circle2 = new Circle(500, 400, 500);
  Line.show();
  /// intersection points
  PVector C1L=intersectLC(Circle1, Line, 0, 1);
  PVector C2L=intersectLC(Circle2, Line, 0, 0);
  PVector C3L=intersectLC(Circle1, Line, 0, 0);
  PVector C1C2=intersectCC(Circle1.center, Circle2.center, 500, 500, 1, 0);

  //////////////////////////////////////////

  ///// drawing the arcs

  //////////////////////////////////////////


  if (C1L!=null && C2L !=null && C1C2 != null && C3L!=null) {
    float Angle1 = atan2(C2L.y-Circle2.center.y, C2L.x-Circle2.center.x);
    float Angle2  = atan2(C1C2.y-Circle2.center.y, C1C2.x-Circle2.center.x);

    //println(Angle1+" "+Angle2);
    pushStyle();
    strokeWeight(4);
    stroke(#FFA805);
    noFill();
    fill(255, 20);

    arcs(Circle2.center.x, Circle2.center.y, 500, 500, Angle1, Angle2);
    //float d= Circle1.center.y - C2L.y ;

    float Angle3 = atan2(C1C2.y-Circle1.center.y, C1C2.x-Circle1.center.x);
    float Angle4  = atan2(C1L.y-Circle1.center.y, C1L.x-Circle1.center.x);


    arcs(Circle1.center.x, Circle1.center.y, 500, 500, Angle3, Angle4);

    popStyle();


    pushStyle();
    noStroke();
    fill(255, 0, 0);
    circle(C1L.x, C1L.y, 10);
    circle(C2L.x, C2L.y, 10);
    circle(C1C2.x, C1C2.y, 10);
    circle(Circle1.center.x, Circle1.center.y, 10);
    circle(Circle2.center.x, Circle2.center.y, 10);
    popStyle();

    pushStyle();
    noStroke();
    fill(255);
    text("C1L", C1L.x-10, C1L.y-10);
    text("C2L", C2L.x-10, C2L.y-10);
    text("C1C2", C1C2.x, C1C2.y-10);
    text("Circle1 center", Circle1.center.x, Circle1.center.y+20);
    text("Circle2 center", Circle2.center.x, Circle2.center.y+20);
    popStyle();
  }
}



//// coparators to sort an array of PVectors by x or y
static final Comparator<PVector> VEC_CMPX = new Comparator<PVector>() {
  @ Override final int compare(final PVector a, final PVector b) {
    int cmp;
    return
      (cmp = Float.compare(a.y, b.y)) != 0? cmp :
      Float.compare(a.x, b.x);
  }
};
static final Comparator<PVector> VEC_CMPY = new Comparator<PVector>() {
  @ Override final int compare(final PVector a, final PVector b) {
    int cmp;
    return
      (cmp = Float.compare(a.x, b.x)) != 0? cmp :
      Float.compare(a.y, b.y);
  }
};


//// class Circle
class Circle {
  PVector center;
  float radius; // diametre
  float x, y;
  Circle(float _x, float _y, float _radius) {
    center = new PVector(_x, _y);
    radius = _radius;
  }
  PVector circleCenter() {
    return center;
  }
  float radius() {
    return radius/2;
  }
}

//// class Line

class Line {
  float x1;
  float y1;
  float x2;
  float y2;
  public Line(float _x1, float _y1, float _x2, float _y2) {
    x1 = _x1;
    y1 = _y1;
    x2 = _x2;
    y2 = _y2;
  }
  PVector Start() {
    PVector start = new PVector(x1, y1);
    return start;
  }
  PVector End() {
    PVector end = new PVector(x2, y2);
    return end;
  }
  void show() {

    line(x1, y1, x2, y2);
  }
}

PVector intersectCC(PVector CC1, PVector CC2, float r1, float r2, int direction, int mode) {
  //horizontal =0 vertical 1 min=0 max 1
  PVector inter= new PVector();
  pushStyle();
  stroke(255);
  noFill();
  //if (show) {
  //  ellipse(CC1.x, CC1.y, r1, r1);
  //  ellipse(CC2.x, CC2.y, r2, r2);
  //}
  popStyle();
  PVector[] sh1sh2 = CCinter(CC1, CC2, r1, r2);
  if (sh1sh2 != null) {
    PVector[] Points=new PVector[sh1sh2.length];

    for (int i=0; i<sh1sh2.length; i++) {
      pushStyle();
      fill(255, 0, 0);
      Points[i]=new PVector(sh1sh2[i].x, sh1sh2[i].y);
      popStyle();
    }

    if (direction == 1 && mode==0) {
      Arrays.sort(Points, VEC_CMPY);
      inter = new PVector(Points[0].x, Points[0].y);
    }
    if (direction == 1 && mode==1) {
      Arrays.sort(Points, VEC_CMPY.reversed());
      inter = new PVector(Points[0].x, Points[0].y);
    }
    if (direction == 0 && mode==0) {
      Arrays.sort(Points, VEC_CMPX);
      inter = new PVector(Points[0].x, Points[0].y);
    }
    if (direction == 0 && mode==1) {
      Arrays.sort(Points, VEC_CMPX.reversed());
      inter = new PVector(Points[0].x, Points[0].y);
    }
  }
  return inter;
}
void lineV(PVector A, PVector B) {
  if (A != null && B!= null) {
    line(A.x, A.y, B.x, B.y);
  }
}



PVector[] CCinter(PVector CC1, PVector CC2, float R1, float R2) {
  float x1, x2, y1, y2;
  float r1=R1/2;
  float r2=R2/2;
  x1=CC1.x;
  x2=CC2.x;
  y1=CC1.y;
  y2=CC2.y;

  float a = x2 - x1;
  float b = y2 - y1;
  float ds = a*a + b*b;
  float d = sqrt( ds );
  if (r1 + r2 <= d)
    return null;

  if (d <= Math.abs( r1 - r2 ))
    return null;
  float t = sqrt( (d + r1 + r2) * (d + r1 - r2) * (d - r1 + r2) * (-d + r1 + r2) );
  float sx1 = 0.5 * (a + (a*(r1*r1 - r2*r2) + b*t)/ds);
  float sx2 = 0.5 * (a + (a*(r1*r1 - r2*r2) - b*t)/ds);
  float sy1 = 0.5 * (b + (b*(r1*r1 - r2*r2) - a*t)/ds);
  float sy2 = 0.5 * (b + (b*(r1*r1 - r2*r2) + a*t)/ds);
  sx1 += x1;
  sy1 += y1;
  sx2 += x1;
  sy2 += y1;
  PVector Sx1= new PVector(sx1, sy1);
  PVector Sx2= new PVector(sx2, sy2);
  PVector[] intersect=new PVector[2];
  intersect[0]=Sx1;
  intersect[1]=Sx2;
  if (show) {
    pushStyle();
    noStroke();
    fill(255, 0, 0);
    // ellipse(Sx1.x, Sx1.y, 10, 10);
    popStyle();
  }
  return intersect;
}

ArrayList<PVector> intersectionsLC(Circle C, Line L) {
  PVector start, end, circleCenter;
  float radius;
  start = new PVector(L.Start().x, L.Start().y);
  end= new PVector(L.End().x, L.End().y);
  circleCenter = new PVector(C.circleCenter().x, C.circleCenter().y);
  radius = C.radius();
  PVector d = PVector.sub(end, start);
  PVector f = PVector.sub(start, circleCenter);
  ArrayList<PVector> result = new ArrayList<PVector>();
  float a = d.dot(d);
  float b = 2 * f.dot( d );
  float c = f.dot(f) - radius * radius;
  float discriminant = b * b - 4 * a * c;
  if ( discriminant < 0 ) {
    return result;
  }
  discriminant = sqrt(discriminant);
  float t1 = (-b - discriminant) / (2 * a);
  float t2 = (-b + discriminant) / (2 * a);
  if (t1 >= 0 && t1 <= 1) {
    result.add(PVector.lerp(start, end, t1));
  }
  if (t2 >= 0 && t2 <= 1) {
    result.add(PVector.lerp(start, end, t2));
  }
  return result;
}

PVector intersectLC(Circle C, Line L, int direction, int mode) {
  //horizontal =0 vertical 1 min=0 max 1
  PVector CC1=new PVector();
  CC1=C.circleCenter();
  float radius=C.radius();

  //intersectionsLC
  PVector inter= new PVector();
  pushStyle();
  stroke(255);
  noFill();
  if (show) {
    ellipse(CC1.x, CC1.y, radius*2, radius*2);
  }
  popStyle();
  ArrayList<PVector> sh1sh2 = intersectionsLC(C, L);
  if (sh1sh2 != null) {
    int Len = sh1sh2.size();
    PVector[] Points=new PVector[Len];
    if (Len == 1) {
      Points[0]= new PVector(sh1sh2.get(0).x, sh1sh2.get(0).y);
      inter = new PVector(Points[0].x, Points[0].y);
      return inter;
    }
    if (Len==2) {
      for (int i=0; i<Len; i++) {
        pushStyle();
        fill(255, 0, 0);
        if (show) {
        }

        Points[i]=new PVector(sh1sh2.get(i).x, sh1sh2.get(i).y);
        popStyle();
      }
      if (direction == 1 && mode==0) {
        Arrays.sort(Points, VEC_CMPY);
        inter = new PVector(Points[0].x, Points[0].y);
      }
      if (direction == 1 && mode==1) {
        Arrays.sort(Points, VEC_CMPY.reversed());
        inter = new PVector(Points[0].x, Points[0].y);
      }
      if (direction == 0 && mode==0) {
        Arrays.sort(Points, VEC_CMPX);
        inter = new PVector(Points[0].x, Points[0].y);
      }
      if (direction == 0 && mode==1) {
        Arrays.sort(Points, VEC_CMPX.reversed());
        inter = new PVector(Points[0].x, Points[0].y);
      }
    }
  }
  return inter;
}

void arcs(float x, float y, float R1, float R2, float a0, float a1)
{
  //println("a0", degrees(a0), "a1", degrees(a1)); //Before
  //noFill();

  if (a0-a1<0) {
    a0 += TAU;

    //println("a1", degrees(a1), "a0", degrees(a0)); // After continuous

    //stroke(0, 255, 255);            // magenta a1 to a0
    arc(x, y, R1, R2, a1, a0);
  }

  if (a1-a0<0) {
    a1 += TAU;

    //println("a0", degrees(a0), "a1", degrees(a1)); // After continuous
    //println();

    //stroke(0, 0, 255);              // blue magenta a0 to a1
    arc(x, y, R1, R2, a0, a1);
  }
}

Consider two circles that intersect then there will be 4 arcs, 2 per circle.
Now consider the two arcs on a single circle

  • their subtended angles will add up to 360^\circ but it is very unlikely they will both be 180^\circ which means that one will be big (\gt180^\circ) and the other small (\lt180^\circ) so we can categorize them as BIG or SMALL.
  • only one of the two arcs can be inside the second circle so we can categorize that arc as INSIDE and the second arc as OUTSIDE

We can see the possibilities here

Just because an arc is SMALL doesn’t mean it must be INSIDE. :thinking:

The following sketch was used to create the image above, the big circle moves with the mouse and you can select which arcs to show with the keyboard. You will notice a class called CircleArc which is used to represent the arcs, it makes it easier to manage them in the code. It akso takes care of the situation when the arc includes the 0^\circ position.

final int NONE = 0;
final int BIG = 1;
final int SMALL = 2;
final int INSIDE = 3;
final int OUTSIDE = 4;

String[] status = {"Intersections only", "Show big arcs", "Show small arcs", "Show inside arcs", "Show outside arcs" };
Circle c0, c1;
int mode = NONE;

// Create empty arrays to avoid NPEs
PVector[] intersects = new PVector[0];
CircleArc[] ca0 = new CircleArc[0], ca1 = new CircleArc[0];

void setup() {
  size(200, 280);
  cursor(CROSS);
  //noCursor();
  textSize(14);
  textAlign(CENTER, CENTER);
  c0 = new Circle(70, 60, 40);
  c1 = new Circle(150, 150, 55);
}

void draw() {
  background(96);
  // Show arcs
  noFill();
  stroke(255, 0, 255, 128);
  strokeWeight(8);
  for (CircleArc a : ca0)
    if (isVisible(a, c1))
      a.paint();
  for (CircleArc a : ca1)
    if (isVisible(a, c0))
      a.paint();
  // Show circles
  fill(0, 255, 0, 10);
  stroke(0, 255, 0);
  strokeWeight(1.5);
  c0.paint();
  c1.paint();
  // Show intersections
  fill(255, 0, 0);
  noStroke();
  for (int i = 0; i< intersects.length; i++)
    ellipse(intersects[i].x, intersects[i].y, 6, 6);
  showMode();
}

void showMode() {
  // Show status
  fill(255, 255, 0);
  noStroke();
  rect(0, height-60, width, 60);
  fill(0);
  text(status[mode], 10, height - 60, width - 20, 20);
  text("Select mode with keyboard", 10, height-24, width - 20, 20);
  for (int i = 0; i < 5; i++) {
    fill(i == mode?color(255, 0, 255):0);
    text("["+i+"]", 16 + i * 30, height-44, 50, 20);
  }
}

void keyTyped() {
  switch(key) {
  case '0': mode = NONE; break;
  case '1': mode = BIG; break;
  case '2': mode = SMALL; break;
  case '3': mode = INSIDE;break;
  case '4': mode = OUTSIDE; break;
  }
}

boolean isVisible(CircleArc a, Circle other) {
  switch(mode) {
  case BIG: return a.arcAngle() >= PI;
  case SMALL: return a.arcAngle() < PI;
  case INSIDE: return other.containsArc(a);
  case OUTSIDE: return !other.containsArc(a);
  default: return false;
  }
}

void mouseMoved() {
  c1.cx = mouseX; c1.cy = mouseY;
  intersects = circle_circle(c0, c1);
  ca0 = c0.getArcs(intersects);
  ca1 = c1.getArcs(intersects);
}

class Circle {
  float cx, cy;
  float rad;

  Circle(float centerX, float centerY, float radius) {
    cx = centerX;
    cy = centerY;
    rad = radius;
  }

  CircleArc[] getArcs(PVector[] isects) {
    CircleArc[] result = new CircleArc[0];
    if (isects.length == 2) {
      float a0 = atan2(isects[0].y - cy, isects[0].x - cx);
      float a1 = atan2(isects[1].y - cy, isects[1].x - cx);
      result = new CircleArc []{ new CircleArc(this, a0, a1), new CircleArc(this, a1, a0)};
    }
    return result;
  }

  boolean containsArc(CircleArc arc) {
    return isInside(arc.midX, arc.midY);
  }

  boolean isInside(float px, float py) {
    return (px - cx)*(px - cx) + (py - cy)*(py - cy) < rad * rad;
  }

  void paint() {
    push();
    translate(cx, cy);
    ellipse(0, 0, 2 * rad, 2 * rad);
    pop();
  }
}

class CircleArc {
  Circle c;
  float sa, ea;
  float midX, midY; // used to check if inner arc

  CircleArc(Circle circle, float startAngle, float endAngle) {
    c = circle;
    sa = normAngle(startAngle);
    ea = normAngle(endAngle);
    if (sa > ea) ea += TWO_PI;
    float ma = (sa+ea)/2;
    midX = c.cx + c.rad * cos(ma);
    midY = c.cy + c.rad * sin(ma);
  }

  // Get the angle subtended by the arc (radians)
  float arcAngle() {
    return ea - sa;
  }

  // Force angle to corresponding value in range >=0 and < 2Pi
  float normAngle(float a) {
    while (a < 0) a += TWO_PI;
    while (a > TWO_PI) a -= TWO_PI;
    return a;
  }

  void paint() {
    push();
    translate(c.cx, c.cy);
    arc(0, 0, 2*c.rad, 2*c.rad, sa, ea);
    pop();
  }
}

final float ACCY = 1E-9;

/**
 * Calculate the intersection points between two circles.
 * If the array is of length:
 * 0 then there is no intersection
 * 1 there is just one intersection (the circles are touching)
 * 2 there are two intersections <br>
 */
public PVector[] circle_circle(Circle c0, Circle c1) {
  PVector[] result = new PVector[0];
  float dx = c1.cx - c0.cx;
  float dy = c1.cy - c0.cy;
  float distSQ = dx*dx + dy*dy;
  if (distSQ > ACCY) {
    float r0SQ = c0.rad * c0.rad;
    float r1SQ = c1.rad * c1.rad;
    float diffRSQ = (r1SQ - r0SQ);
    float root = 2 * (r1SQ + r0SQ) * distSQ - distSQ * distSQ - diffRSQ * diffRSQ;
    if (root > -ACCY) {
      float distINV = 0.5f/ distSQ;
      float scl = 0.5f - diffRSQ * distINV;
      float x = dx * scl + c0.cx;
      float y = dy * scl + c0.cy;
      if (root < ACCY) {
        result = new PVector[] { new PVector(x, y) };
      } else {
        root = distINV * sqrt(root);
        float xfac = dx * root;
        float yfac = dy * root;
        result = new PVector[] { new PVector(x-yfac, y+xfac), new PVector(x+yfac, y-xfac) };
      }
    }
  }
  return result;
}
1 Like

@quark thank you for your answer, it is really detailed. the code will help me for sure.
I finally understood the source of my confusion and of course it is the simplest thing: when using the atan2 to calculate the angles, once the Angle is > PI (180°) , the result is negative. 3*QUARTER_PI is -QUARTER_PI or 270° is -90°. so when taking this in consideration all the errors make sens.
thank you all for your help!

1 Like