12

I have tried a lot of different approaches from examples around the web, but I can't seem to get this to work. I am trying to make a method that draws a curved line between 2 points on a canvas. The curve should be defined by a radius parameter.

Below is my current code.

public OverlayBuilder drawCurvedArrow(int startX, int startY, int endX, int endY, int curveRadius, int padding, int color) {
    PointF mPoint1 = new PointF(startX, startY);
    PointF mPoint2 = new PointF(endX, endY);
    Paint paint  = new Paint();
    paint.setAntiAlias(true);
    paint.setStyle(Paint.Style.STROKE);
    paint.setStrokeWidth(12);
    paint.setColor(color);
    Path myPath = new Path();

    myPath.moveTo(startX, startY);
    myPath.quadTo(mPoint1.x, mPoint1.y, mPoint2.x, mPoint2.y);
    canvas.drawPath(myPath, paint);

    return this;
}

Edit

The problem is that I can't figure out how to curve the line that is drawn on the canvas.

halfer
  • 19,824
  • 17
  • 99
  • 186
Langkiller
  • 3,377
  • 13
  • 43
  • 72

3 Answers3

35

I found a solution to my problem myself. Even though there were some great answers, they weren't an exact solution to my particular problem.

Here is what I did:

  • Found the point in between the 2 given points
  • Calculated the angle 90 degrees between the 2 points
  • Calculated the point X pixels from the middle point using the calculated degree from before.
  • Used "path.cubicTo" with these 3 points (Takes both negative and positive values to determine which way the line should curve).

Here is my code if anyone else should run into the same problem:

public OverlayBuilder drawCurvedArrow(int x1, int y1, int x2, int y2, int curveRadius, int color, int lineWidth) {

    Paint paint  = new Paint();
    paint.setAntiAlias(true);
    paint.setStyle(Paint.Style.STROKE);
    paint.setStrokeWidth(lineWidth);
    paint.setColor(ContextCompat.getColor(context, color));

    final Path path = new Path();
    int midX            = x1 + ((x2 - x1) / 2);
    int midY            = y1 + ((y2 - y1) / 2);
    float xDiff         = midX - x1;
    float yDiff         = midY - y1;
    double angle        = (Math.atan2(yDiff, xDiff) * (180 / Math.PI)) - 90;
    double angleRadians = Math.toRadians(angle);
    float pointX        = (float) (midX + curveRadius * Math.cos(angleRadians));
    float pointY        = (float) (midY + curveRadius * Math.sin(angleRadians));

    path.moveTo(x1, y1);
    path.cubicTo(x1,y1,pointX, pointY, x2, y2);
    canvas.drawPath(path, paint);

    return this;
}

And here is an example of how the implementation looks like:

enter image description here

Ryan Cha
  • 7
  • 3
Langkiller
  • 3,377
  • 13
  • 43
  • 72
  • 1
    Thanks a lot for providing a working method out of the box, without having to adjust 100 variables first – Big_Chair Apr 07 '19 at 17:38
1

Suppose you have two points mPoint1 and mPoint2

int w=canvas.getWidth();
int h=canvas.getHeight();
int w_2= (w / 2);
int h_2= (h / 2);
PointF mPoint1 = new PointF(0, 0); //starts at canvas left top
PointF mPoint2 = new PointF(w_2, h_2);//mid of the canvas
Path drawPath1 =drawCurve(mPoint1, mPoint2);
canvas.drawPath(drawPath1, paint);

Method to draw the line

private Path drawCurve(PointF mPointa, PointF mPointb) {
            Path myPath = new Path();
            myPath.moveTo(mPointa.x, mPointa.y);
            final float x2 = (mPointb.x + mPointa.x) / 3;
            final float y2 = (mPointb.y + mPointa.y) / 3;
            myPath.quadTo(x2, y2, mPointb.x, mPointb.y);
            return myPath;
}

enter image description here

Sreehari
  • 5,621
  • 2
  • 25
  • 59
  • Thanks for your answer @Stallion. In the code you've provided, where is the value that defines the radius? – Langkiller May 25 '16 at 09:44
  • I had done this long back, Can you try changing the 3 to different value (2,3 or 4) ? (mPointb.x + mPointa.x) / VARIABLE – Sreehari May 25 '16 at 09:51
  • I tried that. if set to 2 it is just a straight line. if set to higher values, a peaking shape is drawn. – Langkiller May 25 '16 at 10:06
  • hmm yeay well.. the problem is that I want it to "curve out from the middle of the line. And then I want to determine how much it should curve. Like this: http://pasteboard.co/1bt6SF4x.png – Langkiller May 25 '16 at 10:45
  • Tanks a lot for your help, much appreciated.. However, I found the solution to my particular problem. – Langkiller May 25 '16 at 19:43
0

I think you are using wrong method for this purpose, one of the solutions that I can suggest is below

float radius = 20;
final RectF oval = new RectF();
oval.set(point1.x - radius, point1.y - radius, point1.x + radius, point1.y+   radius);
Path myPath = new Path();
myPath.arcTo(oval, startAngle, -(float) sweepAngle, true);

and for calculation of startAngle you will need

int startAngle = (int) (180 / Math.PI * Math.atan2(point.y - point1.y, point.x - point1.x));

for sweepAngle you can find detailed description here.

Eric
  • 5,323
  • 6
  • 30
  • 35
Atif Rehman
  • 325
  • 1
  • 12
  • Thanks! I have seen this example in another post. What is sweepAngle? Should I calculate its value somehow? – Langkiller May 25 '16 at 09:41
  • Okay I'll look into to that.. hmm in the calculation of startAngle, what is the variable point? And what is point1 int the first snippet of code? Which one is start point and which is end point? – Langkiller May 25 '16 at 09:59
  • Tanks a lot for your help, much appreciated.. However, I found the solution to my particular problem. – Langkiller May 25 '16 at 19:43