绘制圆弧以适应 html canvas 中的角度,圆缠结已知半径 2D 的点
Drawing arc to fit angle in html canvas by Circle tangles point with known radius 2D
这个问题很难解释。但我会尽力而为。
首先我画了 2 条线,一条线包含一个起点和一个终点,像这样
line = {
startPoint{x: , y:}
endPoint{x: , y:}
}
然后我在 canvas 上画两条线,形成像这样的三角形的角。
我现在将线彼此分开,长度为 Radius*2,如下所示
那么我现在如何使用两个端点作为切点来绘制圆弧,如下所示
我需要为此使用 arc 还是可以使用 arcto 来完成?如果它是弧形的;然后我如何给它开始绘图和结束点,以便它像上一张图中的图像所示那样绘制它。感谢您的宝贵时间,任何意见都有帮助。再次对问题的错误描述表示抱歉
- 更新 -
看来我没有完全解释我的问题。所以这里有一点更新。使用此处给出的示例。我最终得到一个椭圆形圆圈。我想要得到的是线条之间的圆圈。
给定在公共点相交的 2 条线段,您可以使用三次贝塞尔曲线对它们应用圆形交点:
方法如下...
给定点 p1 和从 p1 延伸到 p0 (P10) 和从 p1 延伸到 p2 (P12) 的线段:
var p0={x:50,y:50};
var p1={x:100,y:150};
var p2={x:250,y:100};
计算 P10 和 P12 上的点,这些点是从公共点 (p1) 返回到各自起点 (p0 和 p2) 的指定路径百分比:
var lerp=function(a,b,x){ return(a+x*(b-a)); };
var dx,dy,length;
var offsetPct=0.15;
// calc a point on P10 that is 15% of the way from p1 to p0
dx=p1.x-p0.x;
dy=p1.y-p0.y;
p00={ x:lerp(p1.x,p0.x,offsetPct), y:lerp(p1.y,p0.y,offsetPct) }
// calc a point on P12 that is 15% of the way from p1 to p2
dx=p1.x-p2.x;
dy=p1.y-p2.y;
p22={ x:lerp(p1.x,p2.x,offsetPct), y:lerp(p1.y,p2.y,offsetPct) }
然后您可以使用缩短的线段和三次贝塞尔曲线绘制圆角交叉点:
示例代码和演示:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var p0={x:50,y:50};
var p1={x:100,y:150};
var p2={x:150,y:50};
roundedIntersection(p0,p1,p2,0.15);
function roundedIntersection(p0,p1,p2,offsetPct){
var lerp=function(a,b,x){ return(a+x*(b-a)); };
var dx,dy,length;
dx=p1.x-p0.x;
dy=p1.y-p0.y;
p00={ x:lerp(p1.x,p0.x,offsetPct), y:lerp(p1.y,p0.y,offsetPct) }
dx=p1.x-p2.x;
dy=p1.y-p2.y;
p22={ x:lerp(p1.x,p2.x,offsetPct), y:lerp(p1.y,p2.y,offsetPct) }
ctx.beginPath();
ctx.moveTo(p0.x,p0.y);
ctx.lineTo(p00.x,p00.y);
ctx.bezierCurveTo( p1.x,p1.y, p1.x,p1.y ,p22.x,p22.y);
ctx.lineTo(p2.x,p2.y);
ctx.stroke();
dot(p0.x,p0.y);
dot(p1.x,p1.y);
dot(p2.x,p2.y);
dot(p00.x,p00.y);
dot(p22.x,p22.y);
function dot(x,y){
ctx.beginPath();
ctx.arc(x,y,2,0,Math.PI*2);
ctx.closePath();
ctx.fill();
}
}
body{ background-color: ivory; }
#canvas{border:1px solid red; margin:0 auto; }
<canvas id="canvas" width=300 height=300></canvas>
是的,你可以使用 arcTo()
:
- 设置你的第一行起点
moveTo()
- 那么两条线之间的交点作为第一对
- 然后第二行的终点(你说的"startpoint line 2")最后一对。
- 提供半径
- 要实际绘制最后一条线(它仅用于
arcTo()
的计算)为弧中的最后一对点添加一个lineTo()
,stroke/fill.
如果您想将线分开但不知道交点,您必须 manually calculate(请参阅该答案中的 getIntersection()
),方法是首先对线进行插值以使其超出其原始长度。
var ctx = document.querySelector("canvas").getContext("2d");
ctx.moveTo(0, 0); // start point
ctx.arcTo(50, 150, 100, 0, 20); // intersection, outpoint, radius
ctx.lineTo(100, 0); // line from arc-end to outpoint
ctx.translate(130, 0);
ctx.moveTo(0, 0); // start point
ctx.arcTo(50, 150, 80, 50, 8); // intersection, outpoint, radius
ctx.lineTo(80, 50); // line from arc-end to outpoint
ctx.stroke();
<canvas></canvas>
如果您在不知道交点的情况下将线分开,则可以通过以下方式扩展线以找到交点:
function extendLine(line, scale) {
var sx = line.startPoint.x,
sy = line.startPoint.y,
ex = line.endPoint.x,
ey = line.endPoint.y;
return {
startPoint: {x: sx, y: sy},
endPoint: {
x: sx + (ex - sx) * scale,
y: sy + (ey - sy) * scale
}
}
}
比例可能是一个荒谬的值,因为我们需要确保在非常陡峭的角度下线条会在某处相交。不影响计算速度。
然后用两条线(确保第二条线从第一条线的终点继续,这意味着你可能必须反转坐标 - 如果你想动态地这样做,你可以测量每条线的距离第二行的点到第一行的终点,距离最短的在前作为startPoint):
执行步骤将是:
var line1 = ...,
line2 = ...,
line1tmp = extendLine(line1, 10000),
line2tmp = extendLine(line2, 10000),
ipoint = getIntersection(line1, line2); // see link above
// define the line + arcTo
ctx.moveTo(line1.startPoint.x, line1.startPoint.y);
ctx.arcTo(ipoint.x, ipoint.y,
line2.endPoint.x, line2.endPoint.y,
Math.abs(line2.startPoint.x - line1.endPoint.x) / 2);
ctx.lineTo(line2.endPoint.x, line2.endPoint.y);
ctx.stroke();
这个问题很难解释。但我会尽力而为。 首先我画了 2 条线,一条线包含一个起点和一个终点,像这样
line = {
startPoint{x: , y:}
endPoint{x: , y:}
}
然后我在 canvas 上画两条线,形成像这样的三角形的角。
我现在将线彼此分开,长度为 Radius*2,如下所示
那么我现在如何使用两个端点作为切点来绘制圆弧,如下所示
我需要为此使用 arc 还是可以使用 arcto 来完成?如果它是弧形的;然后我如何给它开始绘图和结束点,以便它像上一张图中的图像所示那样绘制它。感谢您的宝贵时间,任何意见都有帮助。再次对问题的错误描述表示抱歉
- 更新 - 看来我没有完全解释我的问题。所以这里有一点更新。使用此处给出的示例。我最终得到一个椭圆形圆圈。我想要得到的是线条之间的圆圈。
给定在公共点相交的 2 条线段,您可以使用三次贝塞尔曲线对它们应用圆形交点:
方法如下...
给定点 p1 和从 p1 延伸到 p0 (P10) 和从 p1 延伸到 p2 (P12) 的线段:
var p0={x:50,y:50}; var p1={x:100,y:150}; var p2={x:250,y:100};
计算 P10 和 P12 上的点,这些点是从公共点 (p1) 返回到各自起点 (p0 和 p2) 的指定路径百分比:
var lerp=function(a,b,x){ return(a+x*(b-a)); }; var dx,dy,length; var offsetPct=0.15; // calc a point on P10 that is 15% of the way from p1 to p0 dx=p1.x-p0.x; dy=p1.y-p0.y; p00={ x:lerp(p1.x,p0.x,offsetPct), y:lerp(p1.y,p0.y,offsetPct) } // calc a point on P12 that is 15% of the way from p1 to p2 dx=p1.x-p2.x; dy=p1.y-p2.y; p22={ x:lerp(p1.x,p2.x,offsetPct), y:lerp(p1.y,p2.y,offsetPct) }
然后您可以使用缩短的线段和三次贝塞尔曲线绘制圆角交叉点:
示例代码和演示:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var p0={x:50,y:50};
var p1={x:100,y:150};
var p2={x:150,y:50};
roundedIntersection(p0,p1,p2,0.15);
function roundedIntersection(p0,p1,p2,offsetPct){
var lerp=function(a,b,x){ return(a+x*(b-a)); };
var dx,dy,length;
dx=p1.x-p0.x;
dy=p1.y-p0.y;
p00={ x:lerp(p1.x,p0.x,offsetPct), y:lerp(p1.y,p0.y,offsetPct) }
dx=p1.x-p2.x;
dy=p1.y-p2.y;
p22={ x:lerp(p1.x,p2.x,offsetPct), y:lerp(p1.y,p2.y,offsetPct) }
ctx.beginPath();
ctx.moveTo(p0.x,p0.y);
ctx.lineTo(p00.x,p00.y);
ctx.bezierCurveTo( p1.x,p1.y, p1.x,p1.y ,p22.x,p22.y);
ctx.lineTo(p2.x,p2.y);
ctx.stroke();
dot(p0.x,p0.y);
dot(p1.x,p1.y);
dot(p2.x,p2.y);
dot(p00.x,p00.y);
dot(p22.x,p22.y);
function dot(x,y){
ctx.beginPath();
ctx.arc(x,y,2,0,Math.PI*2);
ctx.closePath();
ctx.fill();
}
}
body{ background-color: ivory; }
#canvas{border:1px solid red; margin:0 auto; }
<canvas id="canvas" width=300 height=300></canvas>
是的,你可以使用 arcTo()
:
- 设置你的第一行起点
moveTo()
- 那么两条线之间的交点作为第一对
- 然后第二行的终点(你说的"startpoint line 2")最后一对。
- 提供半径
- 要实际绘制最后一条线(它仅用于
arcTo()
的计算)为弧中的最后一对点添加一个lineTo()
,stroke/fill.
如果您想将线分开但不知道交点,您必须 manually calculate(请参阅该答案中的 getIntersection()
),方法是首先对线进行插值以使其超出其原始长度。
var ctx = document.querySelector("canvas").getContext("2d");
ctx.moveTo(0, 0); // start point
ctx.arcTo(50, 150, 100, 0, 20); // intersection, outpoint, radius
ctx.lineTo(100, 0); // line from arc-end to outpoint
ctx.translate(130, 0);
ctx.moveTo(0, 0); // start point
ctx.arcTo(50, 150, 80, 50, 8); // intersection, outpoint, radius
ctx.lineTo(80, 50); // line from arc-end to outpoint
ctx.stroke();
<canvas></canvas>
如果您在不知道交点的情况下将线分开,则可以通过以下方式扩展线以找到交点:
function extendLine(line, scale) {
var sx = line.startPoint.x,
sy = line.startPoint.y,
ex = line.endPoint.x,
ey = line.endPoint.y;
return {
startPoint: {x: sx, y: sy},
endPoint: {
x: sx + (ex - sx) * scale,
y: sy + (ey - sy) * scale
}
}
}
比例可能是一个荒谬的值,因为我们需要确保在非常陡峭的角度下线条会在某处相交。不影响计算速度。
然后用两条线(确保第二条线从第一条线的终点继续,这意味着你可能必须反转坐标 - 如果你想动态地这样做,你可以测量每条线的距离第二行的点到第一行的终点,距离最短的在前作为startPoint):
执行步骤将是:
var line1 = ...,
line2 = ...,
line1tmp = extendLine(line1, 10000),
line2tmp = extendLine(line2, 10000),
ipoint = getIntersection(line1, line2); // see link above
// define the line + arcTo
ctx.moveTo(line1.startPoint.x, line1.startPoint.y);
ctx.arcTo(ipoint.x, ipoint.y,
line2.endPoint.x, line2.endPoint.y,
Math.abs(line2.startPoint.x - line1.endPoint.x) / 2);
ctx.lineTo(line2.endPoint.x, line2.endPoint.y);
ctx.stroke();