在两条线相交之间创建具有度数的曲线

Create a curve with degree between two lines intersection

我想像加拿大国旗一样制作枫叶,但是当线条相交时创建曲线时遇到问题,因此曲线仅在相交处并在线条之间产生一定的度数。 我的意思是在我在这里放的图片中的弧 A、B、C 等中显示:

canada flag curve

这是我目前创建的函数:

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();
  }  
  

我会尽量使它变得简单,但可能需要一些尝试才能理解。 (最后一段中的最终代码)

先来看看如何arcTo()works https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-arcto-dev

您会注意到您有一个起点和两个控制点 (CP)。 CP1 是线相交的地方。 CP2 是第二行开始的地方。

话虽如此,如果我们取两条线,运行 在它们上面求一个相交公式,我们就可以得到 CP1。 CP2 将是第二行开始的地方。

在下面的代码片段中,第 1 行的顶部(第一个点)是叶子的顶部。 line2 的第一个点是第二片叶子的尖端。对于每一行,x2 和 y2 都是使用函数 getCoords() 导出的。由于您的图表具有不同的坐标系,因此您必须采用它给出的角度并将其从 360 中减去。因此 line1 的角度为 63 度,line2 的角度为 150 度。

只需插入数字,一旦获得 x2 和 y2 的 console.log(),将它们复制并粘贴到线对象中。只要线条交叉,长度并不重要。

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>

在两条线都有准确的 x1、y1、x2、y2 坐标后,我们就可以 运行 通过线相交函数得到 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>

如您所见,该功能只是控制台记录坐标,供您在 arcTo() 中用作 CP1。现在你可以正确地画线了。请注意 arcTo() 将绘制一条直线连接到绘图中的前一个点。这意味着我们不需要使用'lineTo()'从上一个点到弧的起点,只需要从弧的终点到下一个点。

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>

当你翻遍整片叶子后,最终的作品会是这样的。辅助函数已被删除,因为现在我已经有了数字,所以不需要它们。以这种方式创建绘图将有助于减少尝试填充时出现的问题。

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>