San
San

Reputation: 2088

Drawing a Heart with Sharp Corner in Android

The following question might sound a bit stupid but I guess stupidity has no limit so here it goes. I am drawing a Heart using Canvas in Android and have got no issues in drawing the heart but I am not able to make the heart sharp in the meeting point. My heart looks like enter image description here

CODE :

            left_x_moveto = 200;
            left_y_moveto = 45;

            left_x1 = 197;
            left_y1 = -35;
            left_x2 = 60;
            left_y2 = 20;
            left_x3 = 193;
            left_y3 = 130;

            right_x_moveto = 200;
            right_y_moveto = 45;

            right_x1 = 197;
            right_y1 = -35;
            right_x2 = 345;
            right_y2 = 20;
            right_x3 = 193;
            right_y3 = 130;



           heart_outline_paint.setColor(getResources().getColor(R.color.heart_outline_color)); // Change the boundary color
        heart_outline_paint.setStrokeWidth(15);
        heart_outline_paint.setStyle(Paint.Style.STROKE);

        path.moveTo(left_x_moveto, left_y_moveto);
        path.cubicTo(left_x1, left_y1, left_x2, left_y2, left_x3, left_y3);

        path.moveTo(right_x_moveto, right_y_moveto);
        path.cubicTo(right_x1, right_y1, right_x2, right_y2, right_x3, right_y3);
        canvas.drawPath(path, heart_outline_paint);

What have I tried so far :

  1. Reducing or increasing the points of left_x_moveto,left_y_moveto and vice versa but the heart is completely disfigured for which I am unable to find the reason.

When the right_x_moveto = 198 and right_y_moveto = 45, the heart looks like

enter image description here

I am not sure why this is happening.

  1. Reducing the width of the heart_outline_paint would give me what I want but I want the thickness of the heart to be the same so reducing the setStrokeWidth is not an option.

In short, I want both the curves to MEET AND MERGE and not just MEET. Any help will be much appreciated. Thanks in advance.

Upvotes: 4

Views: 2655

Answers (2)

Henry
Henry

Reputation: 17851

You need to perform a couple of this.

  1. Close the path via path.close().
  2. You need to set the stroke join via heart_outline_paint.setStrokeJoin(Paint.Join.MITER);

A path can be closed only if it's drawn continuously. Hence I have modified the code so that path.close() can be done properly. Below is the code.

protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);

    heart_outline_paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    heart_outline_paint.setStrokeJoin(Paint.Join.MITER);
    path = new Path();

    int left_x_moveto = 200;
    int left_y_moveto = 45;

    int left_x1 = 180;
    int left_y1 = -20;
    int left_x2 = 30;
    int left_y2 = 20;
    int left_x3 = 193;
    int left_y3 = 130;

    int right_x_moveto = 200;
    int right_y_moveto = 45;

    int right_x1 = 214;
    int right_y1 = -20;
    int right_x2 = 375;
    int right_y2 = 20;
    int right_x3 = 193;
    int right_y3 = 130;

    heart_outline_paint.setColor(Color.RED); // Change the boundary color
    heart_outline_paint.setStrokeWidth(15);
    heart_outline_paint.setStyle(Paint.Style.STROKE);

    path.moveTo(left_x_moveto, left_y_moveto);
    path.cubicTo(left_x1, left_y1, left_x2, left_y2, left_x3, left_y3);
    path.cubicTo(right_x2, right_y2, right_x1, right_y1, right_x_moveto, right_y_moveto);

    path.close();

    canvas.drawPath(path, heart_outline_paint);
}

Paint.Join.MITER is the one that does what you want.

The outer edges of a join meet at a sharp angle

Now this MITER join works only when the angle is <= 90 degree. But here, based on the values that you have provided, the angle is 90 degree and hence the MITER join doesn't work. I have modified the values to get the following image. The image is not exact, but you need to play around with the value the get the right one.
enter image description here


You could set the ROUND join method to get the following.
enter image description here

The problem is with Path.cubicTo(). It's very hard to get the MITTER join work without getting the heart shape squished. So instead of cubicTo, I tried with lineTo and arcTo to create a simple heart. The below is the code for that. You will notice that I have rotated the canvas to 45 degrees and then drew the heart shape. This is purely for convenience, so that the coordinates are simple and does not involve pythagoras theorem.

protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);

    heart_outline_paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    heart_outline_paint.setStrokeJoin(Paint.Join.MITER);
    heart_outline_paint.setColor(Color.RED); // Change the boundary color
    heart_outline_paint.setStrokeWidth(15);
    heart_outline_paint.setStyle(Paint.Style.STROKE);
    path = new Path();

    float length = 100;
    float x = canvas.getWidth()/2;
    float y = canvas.getHeight()/2;

    canvas.rotate(45,x,y);

    path.moveTo(x,y);
    path.lineTo(x-length, y);
    path.arcTo(new RectF(x-length-(length/2),y-length,x-(length/2),y),90,180);
    path.arcTo(new RectF(x-length,y-length-(length/2),x,y-(length/2)),180,180);
    path.lineTo(x,y);
    path.close();

    canvas.drawPath(path, heart_outline_paint);
}

The final rendered image of this code is below:
enter image description here

Upvotes: 6

Dani&#235;l van den Berg
Dani&#235;l van den Berg

Reputation: 2365

What's happening is that the thickness of the line is being drawn on one side, not on both. In true MS paint fashion:

(The left is what's happening, the right is what you want. Black is the actual position of the line would width be 1px, red is the "padding", the 2nd, 3rd through 15th pixel, heart_outline_paint.setStrokeWidth(15);) enter image description here

To fix this, try to subtract half the width of the line from the x of right line, and add it to the x of the left line. Doing so will work around this problem, not fix it

Upvotes: 0

Related Questions