1
votes

I need to draw the arc from 3 points in specific direction.

Lets say i have 3 vec2 points P1, P2, P3;

I've been manage to find arc center:

circleCenter: function (b, c, d) {
    var temp = Math.pow(c.x, 2) + Math.pow(c.y, 2);
    var bc = (Math.pow(b.x, 2) + Math.pow(b.y, 2) - temp) / 2.;
    var cd = (temp - Math.pow(d.x, 2) - Math.pow(d.y, 2)) / 2.;
    var det = (b.x - c.x) * (c.y - d.y) - (c.x - d.x) * (b.y - c.y);

    if (Math.abs(det) < 1e-14)
        return false;

    var circ = new THREE.Vector2((bc * (c.y - d.y) - cd * (b.y - c.y)) / det,
    ((b.x - c.x) * cd - (c.x - d.x) * bc) / det
    );

    return circ;
},

and radius...

    var startPoint = P1;
    var endPoint = P3;
    var centerPoint = P2;

    var centerPoint = this.circle(startPoint, centerPoint, endPoint);

    var r = Math.sqrt((startPoint.x - centerPoint.x) * (startPoint.x - centerPoint.x) + (startPoint.y - centerPoint.y) * (startPoint.y - centerPoint.y));

third step is finding angles which is the place I've been stuck. I'm calculating angles this way for each of point I have:

angleFromOrigin: function (c, p) {
    var x = p.x - c.x;
    var y = p.y - c.y;
    var theta = (180 / Math.PI * Math.atan2(y, x));
    return theta;
},

But this approach does not give me a) direction, b) it does not always include 3rd point (shows opposite arc on the circle)

so I need to correct those angles I have, using rotation direction (clockwise, counterclockwise) and 3rth angle i need to include in arc.

2

2 Answers

0
votes

heh was there too while convert some specific vector data elliptic arcs to SVG ...

few if should do it. I see your case like this:

arc

try this angle correction:

if (a2-a1>+180.0) a2-=360.0;
if (a2-a1<-180.0) a2+=360.0;
if (a3-a2>+180.0) a3-=360.0;
if (a3-a2<-180.0) a3+=360.0;

and after this is direction easy:

if (a2-a1< 0) dir = CW;
if (a2-a1> 0) dir = CCW;
if (a2-a1==0) dir = none;

The only problem is when your arc cover whole 360 degree circle or more ...

  • then this is not enough

draw can be done like this:

for (i=0,a=a1;i<100;i++,a+=(a3-a1)/99.0) // 100 lines per arc or use GDI arc ...
 {
 x=C.x + R*cos(a*PI/180.0);
 y=C.y + R*sin(a*PI/180.0); // or - R*...  if your render device has opposite Y direction
 if (!i) ...->Canvas->MoveTo(x,y);
 else    ...->Canvas->LineTo(x,y);
 }
  • R = |P1-C|
0
votes

I'm working on a similar problem, and it looks like you can calculate both direction and sweep angle by comparing the angles you've calculated. If you have to go further back from the end angle to get to the start angle than to get to the middle angle, you're going clockwise. You can regularize the angles for comparison by using the modulo operator (%), which solves the problem of crossing 360. Something like this:

function determineDirection(start, middle, end) {
    endToStart = (end - start + 360) % 360;
    endToMiddle = (end - middle + 360) % 360;
    if (endToMiddle < endToStart) {
        return {direction: 'cw', sweepAngle: endToStart};
    } else {
        return {direction: 'ccw', sweepAngle: (360 - endToStart)};
    }
}

angleStart = angleFromOrigin(centerOfCircle, P1);
angleMiddle = angleFromOrigin(centerOfCircle, P2);
angleEnd = angleFromOrigin(centerOfCircle, P3);

direction = determineDirection(angleStart, angleMiddle, angleEnd);

Note that this function assumes that angles increase in the clockwise direction. If you're using it for the typical case where angles increase in the counterclockwise direction, exchange the positions of 'cw' and 'ccw'.

Trying it out:

determineDirection(0, 1, 2)
> {direction: "cw", sweepAngle: 2}

determineDirection(2, 1, 0)
> {direction: "ccw", sweepAngle: 2}

determineDirection(0, 2, 1)
> {direction: "ccw", sweepAngle: 359}

determineDirection(1, 2, 0)
> {direction: "cw", sweepAngle: 359}

determineDirection(359, 0, 1)
> {direction: "cw", sweepAngle: 2}

determineDirection(359, 1, 0)
> {direction: "ccw", sweepAngle: 359}

determineDirection(0, 1, 359)
> {direction: "cw", sweepAngle: 359}

determineDirection(1, 0, 359)
> {direction: "ccw", sweepAngle: 2}

determineDirection(0, 359, 1)
> {direction: "ccw", sweepAngle: 359}

determineDirection(1, 359, 0)
> {direction: "cw", sweepAngle: 359}

determineDirection(0, 90, 180)
> {direction: "cw", sweepAngle: 180}

determineDirection(180, 90, 0)
> {direction: "ccw", sweepAngle: 180}

determineDirection(180, 0, 90)
> {direction: "cw", sweepAngle: 270}

I noticed that you're using "centerPoint" for both the center of the circle and the middle point in your arc, so I changed the variable names to avoid confusion.