0

I want to draw an arc. A fairly wide round arc, between two points, on the canvas in android.

Of course I exhaustively searched here and found several people asking the same question (such as), but try as I might, I couldn't make heads or tails of the answers, and I couldn't get the drawArc function to do what I needed!

Then I finally found this very detailed breakdown of how to do exactly that.

private static void arcBetween(PointF e1, PointF e2, Paint paint, Canvas canvas) {

    float a1Degrees = 36.0f;
    double a1 = Math.toRadians(a1Degrees);
    // l1 is half the length of the line from e1 to e2
    double dx = e2.x - e1.x, dy = e2.y - e1.y;
    double l = Math.sqrt((dx * dx) + (dy * dy));
    double l1 = l / 2.0;
    // h is length of the line from the middle of the connecting line to the
    //  center of the circle.
    double h = l1 / (Math.tan(a1 / 2.0));

    // r is the radius of the circle
    double r = l1 / (Math.sin(a1 / 2.0));
    // a2 is the angle at which L intersects the x axis
    double a2 = Math.atan2(dy, dx);
    // a3 is the angle at which H intersects the x axis
    double a3 = (Math.PI / 2.0) - a2;
    // m is the midpoint of the line from e1 to e2
    double mX = (e1.x + e2.x) / 2.0;
    double mY = (e1.y + e2.y) / 2.0;

    // c is the the center of the circle
    double cY = mY + (h * Math.sin(a3));
    double cX = mX - (h * Math.cos(a3));
    // rect is the square RectF that bounds the "oval"
    RectF oval =
            new RectF((float) (cX - r), (float) (cY - r), (float) (cX + r), (float) (cY + r));
    // a4 is the starting sweep angle
    double rawA4 = Math.atan2(e1.y - cY, e1.x - cX);
    float a4 = (float) Math.toDegrees(rawA4);

    canvas.drawArc(oval, a4, a1Degrees, false, paint);
}

Alas, the math in it is beyond me, but I managed to copy/paste it into code, and it does produce an arc (as you can see in the image).

However, there are three problems with this arc for my purposes.

  1. Why is there also a straight line with the enclosed area filled? I need just a, curved line, not a dome. I thought that's what the false in drawArc() did, but it's turned off and doesn't seem to do it.

  2. Though the upper corner of the arc is in the correct location, I've also placed (and super triple checked) that the lower point is on the edge of the blue element, and not on the bottom of the screen. Yet, still the corner of the arc is there.

  3. One very important part though is that I need the arc to be shaped differently, to have more of a bulge in the middle. Really, I need to be able to adjust that bulge programmaticly, so I need it passed somehow as a variable to that function.

That's it, I've been trying, but haven't studied this math and am baffled. Your solutions are deeply appreciation.

Community
  • 1
  • 1
Joshua
  • 15
  • 5
  • See, when I tried it without that function, I figured the oval needed to be set to something like: new RectF(e1.x - bulge, e2.y - bulge, e2.x, e1.y); (with bulge being arbitrary and adjustable by me). But when I do that, I just get a weird little sliver in a totally other place. I just don't get what the oval is supposed to define, and I just don't get either startAngle OR sweepAngle! ARGH! – Joshua May 19 '16 at 04:39
  • `"Really, I need to be able to adjust that bulge programmaticly"` so use `Path#quadTo` – pskink May 19 '16 at 06:49
  • 1
    I marked that answered because the style issue is solved and because you comment solved the remaining issues with quadTo. D'oh! :) – Joshua May 19 '16 at 06:51
  • honestly i dont know why to make that complex math stuff (sin, cos, atan) if you can just use [this](http://pastebin.com/XwsPCeUu), use weight: -1, 0, 1 etc – pskink May 19 '16 at 20:54
  • Can you write the working answer? – MathankumarK Oct 03 '18 at 09:25

1 Answers1

0

Your false' parameteris responding for radius lines from arc ends to the circle center.
To remove filling - set

 paint.setStyle(Paint.Style.STROKE);

Your calculations seem right before a2, a3 determination - it is easy to miss errors with these angles and their signs, so center might be erroneous. I prefer vector approach. You already have direction vector (dx, dy). Make normalized one

udx = dx / l
udy = dy / l

left perpendicular to this vector in left-handed coordinate system (Y axis down)

px = udy
py = -udx

Now circle center

cx = mx + px * h
cy = my + py * h

I have not Android device and Java, but quick check in Windows shows correct drawing (in my case Arc function has bounding rect parameter too, but no angles)

var
  e1, e2: TPoint;
  a1degrees, a1, a2, a3, a4, dx, dy, l, l1, h, mx, my, udx, udy, px, py: Double;
  r, cx, cy: Integer;
  rct: TRect;
begin
    e1 := Point(300, 100);
    e2 := Point(200, 200);
    Canvas.Brush.Color := clLime;
    Canvas.Ellipse(e1.X - 3, e1.Y - 3, e1.X + 4, e1.Y + 4);
    Canvas.Ellipse(e2.X - 3, e2.Y - 3, e2.X + 4, e2.Y + 4);
    a1Degrees := 36;
    a1 := DegToRad(a1Degrees);
    dx := e2.x - e1.x;
    dy := e2.y - e1.y;
    mx := (e2.x + e1.x) / 2;
    my := (e2.y + e1.y) / 2;
    l := Sqrt((dx * dx) + (dy * dy));
    l1 := l / 2.0;
    // h is length of the line from the middle of the connecting line to the
    //  center of the circle.
    h := l1 / tan(a1 / 2.0);
    // r is the radius of the circle
    r := Round(l1 / sin(a1 / 2.0));

    udx := dx / l;
    udy := dy / l;
    px := udy;  // for right-handed coordinate system
    py := -udx; // inverse signs
    cx := Round(mx + px * h);
    cy := Round(my + py * h);

    Canvas.Brush.Color := clRed;   //draw center red
    Canvas.Ellipse(cx - 5, cy - 5, cx + 6, cy + 6);

    Canvas.Arc(cX - r, cY - r, cX + r, cY + r, e1.X, e1.Y, e2.X, e2.Y);

Examples for arc angles 36, 120 and 270 degrees: enter image description here

MBo
  • 77,366
  • 5
  • 53
  • 86