2
votes

I've been trying to understand arc svg since it seems I need them in plotly -- my goal is to plot circle intersections.

My original idea was something like this:

enter image description here

for every intersection, to find the start and end coordinates as well as the height - but I am not very sure of where to go from here. It seems I am lacking the rotation and Large Arc Flag / Sweep parameters, and I am not sure how I would go about retrieving them. If anyone could point me into the right direction here, that would be great!

2

2 Answers

8
votes

Circles and intercepting points

Don't know much about SVG arcTo. MDN gives "A rx,ry xAxisRotate LargeArcFlag,SweepFlag x,y". as an arc in the path element. What rx and ry are??? I would guess radius for x,y.

I am guessing you would use it as

// x,y start position
// rx,ry radius x and y
// x1,y1 end position
<path d="M x,y A rx, ry, 0 1 1 x1, y1"/>

Below is the problem solved as javascript. I have Commented the part you need for the SVG. The two end points (intercepts)

There is a lot of redundancy but its not clear what you want so the code provides how to find other parts of two intersecting circles.

Law of Cosines

The math to solve the problem is called the law of cosines that is used to solve triangles.

In this case the triangle is created from 3 lengths. One each of the circle radius and one is the distance between circle centers. The image gives more details

enter image description here

With the angle c you can find the lengths GE, DE, and EF. If you want the angle for the other side at point f just swap B and C.

Example

Move mouse to check intercept.

const ctx = canvas.getContext("2d");
const m = {
  x: 0,
  y: 0
};
document.addEventListener("mousemove", e => {
  var b = canvas.getBoundingClientRect();
  m.x = e.pageX - b.left - scrollX;
  m.y = e.pageY - b.top - scrollY;
});
const PI = Math.PI;
const PI2 = Math.PI * 2;
const circles = [];

function circle(x, y, r, col, f = 0, t = PI2, w = 2) {
  var c;
  circles.push(c = { x, y,r, col, f, t, w});
  return c;
};

function drawCircle(A) {
  ctx.strokeStyle = A.col;
  ctx.lineWidth = A.w;
  ctx.beginPath();
  ctx.arc(A.x, A.y, A.r, A.f, A.t);
  ctx.stroke();
}

function mark(x, y, r, c) {
  ctx.strokeStyle = c;
  ctx.lineWidth = 2;
  ctx.beginPath();
  ctx.arc(x, y, r, 0, PI2);
  ctx.stroke();
}

function line(A, B, c) {
  ctx.strokeStyle = c;
  ctx.lineWidth = 2;
  ctx.beginPath();
  ctx.lineTo(A.x, A.y);
  ctx.lineTo(B.x, B.y);
  ctx.stroke();
}

// note I am sharing calc results between function  


function circleIntercept(A, B) {
  var vx, vy, dist, c, d, x, y, x1, y1, x2, y2, dir, a1, a2;
  // Vec from A to B
  vx = B.x - A.x;
  vy = B.y - A.y;
  // Distance between
  dist = Math.sqrt(vx * vx + vy * vy);
  // Are the intercepting
  if (dist < A.r + B.r && dist > B.r - A.r) {
    c = (B.r * B.r - (dist * dist + A.r * A.r)) / (-2 * dist);

    // Find mid point on cord
    x = A.x + vx * (c / dist);
    y = A.y + vy * (c / dist);
    mark(x, y, 5, "blue");

    // Find circumference intercepts

    //#################################################################
    //=================================================================
    // SVG path 
    // Use x1,y1 and x2,y2 as the start and end angles of the ArcTo SVG
    d = Math.sqrt(A.r * A.r - c * c);
    x1 = x - vy * (d / dist);
    y1 = y + vx * (d / dist);
    x2 = x + vy * (d / dist);
    y2 = y - vx * (d / dist);
    // SVG path from above coords
    // d = `M ${x1}, ${y1} A ${A.r}, ${A,r1} 0, 1, 1, ${x2}, ${y2}`;
    //=================================================================

    // draw the chord
    line({x: x1,y: y1}, {x: x2,y: y2}, "red");
    
    // mark the intercepts
    mark(x1, y1, 5, "Green");
    mark(x2, y2, 5, "Orange");

    // Get direction from A to B
    dir = Math.atan2(vy, vx);

    // Get half inside sweep
    a1 = Math.acos(c / A.r);

    // Draw arc for A
    A.col = "black";
    A.w = 4;
    A.f = dir - a1;
    A.t = dir + a1;
    drawCircle(A);
    A.col = "#aaa";
    A.w = 2;
    A.f = 0;
    A.t = PI2;

    // inside sweep for B
    a2 = Math.asin(d / B.r);

    // Draw arc for B
    B.col = "black";
    B.w = 4;
    if (dist < c) {
      B.t = dir - a2;
      B.f = dir + a2;
    } else {

      B.f = dir + PI - a2;
      B.t = dir + PI + a2;
    }
    drawCircle(B);
    B.col = "#aaa";
    B.w = 2;
    B.f = 0;
    B.t = PI2;

  }

}

var w = canvas.width;
var h = canvas.height;
var cw = w / 2; // center 
var ch = h / 2;
var C1 = circle(cw, ch, ch * 0.5, "#aaa");
var C2 = circle(cw, ch, ch * 0.8, "#aaa");

function update(timer) {
  ctx.setTransform(1, 0, 0, 1, 0, 0);
  ctx.globalAlpha = 1;
  if (w !== innerWidth || h !== innerHeight) {
    cw = (w = canvas.width = innerWidth) / 2;
    ch = (h = canvas.height = innerHeight) / 2;
    C1.x = cw;
    C1.y = ch;
    C1.r = ch * 0.5;
    ctx.lineCap = "round";
  }
  C2.x = m.x;
  C2.y = m.y;
  ctx.clearRect(0, 0, w, h);


  drawCircle(C1);
  drawCircle(C2);
  circleIntercept(C1, C2);






  requestAnimationFrame(update);
}
requestAnimationFrame(update);
canvas {
  position: absolute;
  top: 0px;
  left: 0px;
}
<canvas id="canvas"></canvas>
1
votes

Let's start with some terminology to clear up what is a clockwise direction (remember the y axis of SVG goes down): the first circle has radius r1, the second r2.

  • If the center of the first circle is lower than that of the second (cy1 > cy2), then name the intersection point with the smaller x coordinate (x1, y1), and the other (x2, y2).
  • If the center of the first circle is higher than that of the second (cy1 < cy2), then name the intersection point with the greater x coordinate (x1, y1), and the other (x2, y2).
  • Else, name the intersection point with the smaller y coordinate (x1, y1), and the other (x2, y2).

Now we will draw an arc from the first to the second intersection point with the radius of the first circle. The first two arc parameters are the horizontal and vertical radius. Since we are drawing a circle, both are identical. For the same reason, rotating the radii does not make sense and the third parameter is 0.

The intersection of two circles always uses the small arc (the large arc would be used for the union), therefore the large arc flag is 0. We are drawing the arc clockwise, therefore the sweep flag is 1.

Still unclear? The spec uses this picture for explaining the flags:

enter image description here

The second arc is going from the second to the first intersection point with the radius of the second circle. The flags remain the same.

The result looks like this:

M x1, y1 A r1 r1 0 0 1 x2, y2 A r2 r2 0 0 1 x1, y1 Z