JeffProd
JeffProd

Reputation: 3148

Simple equilateral triangle with rounded corners in canvas 2D

I can't find the right values to draw a simple equilateral triangle with rounded corners with a given radius. Thanks for your help.

var canvas = document.getElementById('arch');
var context = canvas.getContext('2d');

// Vertices of the equilateral triangle :
// (0,0) (200,0) (100, h*100)

const radius = 15
const h = Math.sqrt(3) / 2

context.beginPath();
context.moveTo(radius, 0);
context.lineTo(200 - radius, 0);
context.quadraticCurveTo(200, 0, 200, radius*h);
context.lineTo(100+radius, (h*200)-radius);
context.quadraticCurveTo(100, h*200, 100-radius, h*200);
context.lineTo(radius, radius);
context.quadraticCurveTo(0, 0, 0, 0);

context.lineWidth = 2
context.strokeStyle = 'black';
context.stroke();
<canvas id="arch" width=500px height=500px>
</canvas>

Upvotes: 1

Views: 211

Answers (2)

Blindman67
Blindman67

Reputation: 54026

Use arcTo

Though the existing answer works well it requires the calculation of the start and end angles of each arc (which is not mentioned?). There is also an additional check per corner, and that is to make sure that the arc direction is clockwise or anticlockwise.

The 2D canvas API has arcTo that eliminates the need to calculate any angles or winding directions.

There is a need to calculate line segment centers however the math is a lot simpler.

Example

The example below uses the function pathRounded to draw rounded corners on a given set of points. The path can be concave or convex, must have at least 3 or more points, and the radius must fit the corners.

If you want corners to fit any set of lines then you need to get the min length of either line entering a corner and ensure that the radius is not greater than half that length.

const ctx = canvas.getContext('2d');
const P2 = (x, y) => ({x,y});
const path = [P2(140, 0), P2(280, 140), P2(0, 140)];

pathRounded(ctx, path, 20);


function pathRounded(ctx, points, radius) {
    const len = points.length;
    ctx.lineWidth = 8;
    ctx.beginPath();
    var i = 0, p1 = points[i++], p2 = points[i++];
    ctx.moveTo((p1.x + p2.x) * 0.5, (p1.y + p2.y) * 0.5);
    while (i <= points.length + 1) {
        p1 = p2;
        p2 = points[i++ % len];
        const x = (p1.x + p2.x) * 0.5;
        const y = (p1.y + p2.y) * 0.5;
        ctx.arcTo(p1.x, p1.y, x, y, radius);
    }
    ctx.closePath();
    ctx.stroke();
}
<canvas id="canvas" height="150" width="300"></canvas>

Upvotes: 1

Helder Sepulveda
Helder Sepulveda

Reputation: 17584

I don't think that quadraticCurveTo will get your expected result, try using arc instead:
https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/arc

Here is a quick example:

var canvas = document.getElementById('arch');
var context = canvas.getContext('2d');

const radius = 18
context.lineWidth = 8
context.strokeStyle = 'black';

context.beginPath();
context.arc(30, 30, radius, 2.2, -1.4);
context.arc(90, 30, radius, 4.6, 1.0);
context.arc(60, 60, radius, 0.6, 2.6);
context.arc(30, 30, radius, 2.2, -1.4);
context.stroke();
<canvas id="arch"></canvas>

Upvotes: 1

Related Questions