Skip to content
Advertisement

Create a curve with degree between two lines intersection

I want to make a maple leaf like in the canadian’s flag,but I have a problem to create a curve when the lines intersect so that the curve is only in the intersection and create certain degree between the lines. what I mean is shown in arc A,B,C,etc in the picture I bring down here:

canada flag curve

This is the function I created so far:

function drawMapleLeaf (ctx,x,y,width,height) {
    let rx = width;
    let ry = height;
    let xc = x + rx / 2;
    let yc = y + height;

    let xPoints = new Array(26).fill(0);
    let yPoints = new Array(26).fill(0);
    xPoints [0] = (xc + rx * 0.021423);
    yPoints [0] = (yc - ry * 0.215686);
    xPoints [1] = (xc + rx * 0.270780);
    yPoints [1] = (yc - ry * 0.203804);
    xPoints [2] = (xc + rx * 0.271820);
    yPoints [2] = (yc - ry * 0.295752);
    xPoints [3] = (xc + rx * 0.482015);
    yPoints [3] = (yc - ry * 0.411765);
    xPoints [4] = (xc + rx * 0.443046);
    yPoints [4] = (yc - ry * 0.483267);
    xPoints [5] = (xc + rx * 0.500000);
    yPoints [5] = (yc - ry * 0.587435);
    xPoints [6] = (xc + rx * 0.363353);
    yPoints [6] = (yc - ry * 0.619576);
    xPoints [7] = (xc + rx * 0.342287);
    yPoints [7] = (yc - ry * 0.693849);
    xPoints [8] = (xc + rx * 0.153596);
    yPoints [8] = (yc - ry * 0.612537);
    xPoints [9] = (xc + rx * 0.201601);
    yPoints [9] = (yc - ry * 0.918462);
    xPoints [10] = (xc + rx * 0.093001);
    yPoints [10] = (yc - ry * 0.894514);
    xPoints [11] = xc;
    yPoints [11] = (yc - ry);
    xPoints [12] = (xc - rx * 0.093001);
    yPoints [12] = yPoints [10];
    xPoints [13] = (xc - rx * 0.201601);
    yPoints [13] = yPoints [9];
    xPoints [14] = (xc - rx * 0.153596);
    yPoints [14] = yPoints [8];
    xPoints [15] = (xc - rx * 0.342287);
    yPoints [15] = yPoints [7];
    xPoints [16] = (xc - rx * 0.363353);
    yPoints [16] = yPoints [6];
    xPoints [17] = (xc - rx * 0.500000);
    yPoints [17] = yPoints [5];
    xPoints [18] = (xc - rx * 0.443046);
    yPoints [18] = yPoints [4];
    xPoints [19] = (xc - rx * 0.482015);
    yPoints [19] = yPoints [3];
    xPoints [20] = (xc - rx * 0.271820);
    yPoints [20] = yPoints [2];
    xPoints [21] =  (xc - rx * .2707796);
    yPoints [21] = yPoints [1];
    xPoints [22] = (xc - rx * 0.021423);
    yPoints [22] = yPoints [0];
    xPoints [23] = xPoints [22];
    yPoints [23] = yc;
    xPoints [24] = xPoints [0];
    yPoints [24] = yPoints [23];
    xPoints [25] = xPoints [0];
    yPoints [25] = yPoints [0];
    ctx.beginPath();
    ctx.moveTo(xPoints[0],yPoints[0]);
    for(let i=1;i<xPoints.length;i++){
        ctx.lineTo(xPoints[i],yPoints[i]);
    }
    //ctx.stroke();
    ctx.fillStyle='red';
    ctx.fill();
  }  
  

Advertisement

Answer

I am going to try to make this as simple as possible but it may take some playing around with to understand. (Final code in last snippet)

First take a look at how arcTo() works https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-arcto-dev

You’ll notice you have a start point and two control points (CP). CP1 is where the lines intersect. CP2 is where the second line starts.

With that said if we take two lines and run an intersect formula on them we can get CP1. CP2 will just be where the second line starts.

In the below snippet the top (first points) of line1 is the top of the leaf. line2’s first points are the tip of the second leaf. For each line x2 and y2 were derived by using the function getCoords(). Since your diagram has a different coordinate system you’ll have to take the angle it gives and subtract it from 360. So line1 has an angle of 63 degrees and line2 is 150 degrees.

Just plug in the numbers and once you get the console.log() for x2 and y2 copy and paste them into the line object. The length doesn;t matter as long as the lines cross.

let canvas = document.getElementById("canvas");
let ctx = canvas.getContext("2d");
canvas.width = 800;
canvas.height = 800;

let line1 = {x1: 200, y1: 50, x2: 290.79809994790935, y2: 228.20130483767355}
let line2 = {x1: 275, y1: 99, x2: 101.79491924311225, y2: 199}

function getCoords(x1, y1, length, angle) {
    let rads = angle * (Math.PI/180);
    x2 = x1 + length * Math.cos(rads);
    y2 = y1 + length * Math.sin(rads);
    console.log(x2, y2);
}
//line2 x, y, length, angle
getCoords(275, 99, 200, 150)

function drawLines(line1, line2) {
  ctx.beginPath()
  ctx.moveTo(line1.x1, line1.y1)
  ctx.lineTo(line1.x2, line1.y2)
  ctx.moveTo(line2.x1, line2.y1)
  ctx.lineTo(line2.x2, line2.y2)
  ctx.stroke()
}
drawLines(line1, line2)
<canvas id='canvas'></canvas>

After you have accurate x1, y1, x2, y2 coordinates for both line we can then run them through the line intersect function to get CP1.

let canvas = document.getElementById("canvas");
let ctx = canvas.getContext("2d");
canvas.width = 800;
canvas.height = 800;

let line1 = {x1: 200, y1: 50, x2: 290.79809994790935, y2: 228.20130483767355}
let line2 = {x1: 275, y1: 99, x2: 101.79491924311225, y2: 199}

function getCoords(x1, y1, length, angle) {
    let rads = angle * (Math.PI/180);
    x2 = x1 + length * Math.cos(rads);
    y2 = y1 + length * Math.sin(rads);
    console.log(x2, y2);
}
//line2 x, y, length, angle
getCoords(275, 99, 200, 150)

function lineSegmentsIntersect(line1, line2) {
  let a_dx = line1.x2 - line1.x1;
  let a_dy = line1.y2 - line1.y1;
  let b_dx = line2.x2 - line2.x1;
  let b_dy = line2.y2 - line2.y1;
  let s =
    (-a_dy * (line1.x1 - line2.x1) + a_dx * (line1.y1 - line2.y1)) /
    (-b_dx * a_dy + a_dx * b_dy);
  let t =
    (+b_dx * (line1.y1 - line2.y1) - b_dy * (line1.x1 - line2.x1)) /
    (-b_dx * a_dy + a_dx * b_dy);
  if (s >= 0 && s <= 1 && t >= 0 && t <= 1) {
      console.log('control x: '+Math.round(line1.x1 + t * (line1.x2 - line1.x1)));
      console.log('control y: '+Math.round( line1.y1 + t * (line1.y2 - line1.y1)));
  }
}
//once both lines have acurate starting and end points plug them in here
//in the console you will see CP1 for acrTo()
lineSegmentsIntersect(line1, line2)

function drawLines(line1, line2) {
  ctx.beginPath()
  ctx.moveTo(line1.x1, line1.y1)
  ctx.lineTo(line1.x2, line1.y2)
  ctx.moveTo(line2.x1, line2.y1)
  ctx.lineTo(line2.x2, line2.y2)
  ctx.stroke()
}
drawLines(line1, line2)
<canvas id='canvas'></canvas>

As you see now the function is just console logging the coordinate for you to use as CP1 in the arcTo(). Now you can properly draw you lines. Be aware that arcTo() will draw a straight line connecting to the previous point in the drawing. This means that we don’t need to use ‘lineTo()’ from the previous point to the start of the arc, just from the end of the arc to the next point.

let canvas = document.getElementById("canvas");
let ctx = canvas.getContext("2d");
canvas.width = 800;
canvas.height = 800;

//let line1 = {x1: 200, y1: 50, x2: 290.79809994790935, y2: 228.20130483767355}
//let line2 = {x1: 275, y1: 99, x2: 101.79491924311225, y2: 199}

function drawLines() {
  ctx.beginPath()
  ctx.moveTo(200, 50) //Just a starting point
  ctx.arcTo(236, 121, 275, 99, 13) //(control x, control y, start of line2 x, start of line2 y, radius)
  ctx.lineTo(275, 99) //(start of line2)
  ctx.stroke()
}
drawLines()
<canvas id='canvas'></canvas>

And the final product will be something like this once you go through the whole leaf. The helper functions have been removed as they are not needed now that I have the numbers. Creating your drawing this way will help reduce issues when trying to fill it in.

let canvas = document.getElementById("canvas");
let ctx = canvas.getContext("2d");
canvas.width = 800;
canvas.height = 800;

function drawShape() {
  ctx.fillStyle = 'red';
  ctx.beginPath()
  ctx.moveTo(200, 50)
  ctx.arcTo(243, 134, 275, 99, 13); 
  ctx.lineTo(275, 99)
  ctx.arcTo(250, 226, 308, 164.5, 13)
  ctx.lineTo(308, 164.5)
  ctx.arcTo(321, 194, 380, 181.5, 13)
  ctx.lineTo(380, 181.5)
  ctx.arcTo(360, 244, 386, 256.5, 13)
  ctx.lineTo(386, 256.5)
  ctx.arcTo(288, 336, 301.5, 372, 13)
  ctx.lineTo(301.5, 372)
  ctx.arcTo(204, 355, 209, 453, 19)
  ctx.lineTo(209, 453)
  ctx.lineTo(191, 453)
  //..you need to finish
  ctx.closePath()
  ctx.fill()
  ctx.stroke()
}
drawShape()
<canvas id='canvas'></canvas>
User contributions licensed under: CC BY-SA
9 People found this is helpful
Advertisement