Arc SVG 参数
Arc SVG Parameters
我一直在努力理解 arc svg,因为我似乎需要它们在 plotly 中——我的目标是绘制圆的交叉点。
我最初的想法是这样的:
对于每个路口,找到起点和终点坐标以及高度 - 但我不太确定从这里到哪里去。看来我缺少旋转和大弧形标志/扫描参数,而且我不确定我将如何检索它们。如果有人能在这里指出我正确的方向,那就太好了!
圆圈和截取点
对 SVG arcTo 了解不多。 MDN给出了"A rx,ry xAxisRotate LargeArcFlag,SweepFlag x,y"
。作为路径元素中的弧。 rx 和 ry 是什么???我猜 x,y 的半径。
我猜你会用它作为
// x,y start position
// rx,ry radius x and y
// x1,y1 end position
<path d="M x,y A rx, ry, 0 1 1 x1, y1"/>
下面是解决的问题javascript。我已经评论了您需要的 SVG 部分。两个端点(截距)
有很多冗余但不清楚你想要什么所以代码提供了如何找到两个相交圆的其他部分。
余弦定律
解决问题的数学称为law of cosines,用于解决三角形。
在这种情况下,三角形是由 3 个长度创建的。一个是圆半径,一个是圆心之间的距离。图片提供了更多细节
用角 c 可以求出 GE、DE 和 EF 的长度。如果你想要另一边在 f 点的角度,只需交换 B 和 C。
例子
移动鼠标检查拦截。
const ctx = canvas.getContext("2d");
const m = {
x: 0,
y: 0
};
document.addEventListener("mousemove", e => {
var b = canvas.getBoundingClientRect();
m.x = e.pageX - b.left - scrollX;
m.y = e.pageY - b.top - scrollY;
});
const PI = Math.PI;
const PI2 = Math.PI * 2;
const circles = [];
function circle(x, y, r, col, f = 0, t = PI2, w = 2) {
var c;
circles.push(c = { x, y,r, col, f, t, w});
return c;
};
function drawCircle(A) {
ctx.strokeStyle = A.col;
ctx.lineWidth = A.w;
ctx.beginPath();
ctx.arc(A.x, A.y, A.r, A.f, A.t);
ctx.stroke();
}
function mark(x, y, r, c) {
ctx.strokeStyle = c;
ctx.lineWidth = 2;
ctx.beginPath();
ctx.arc(x, y, r, 0, PI2);
ctx.stroke();
}
function line(A, B, c) {
ctx.strokeStyle = c;
ctx.lineWidth = 2;
ctx.beginPath();
ctx.lineTo(A.x, A.y);
ctx.lineTo(B.x, B.y);
ctx.stroke();
}
// note I am sharing calc results between function
function circleIntercept(A, B) {
var vx, vy, dist, c, d, x, y, x1, y1, x2, y2, dir, a1, a2;
// Vec from A to B
vx = B.x - A.x;
vy = B.y - A.y;
// Distance between
dist = Math.sqrt(vx * vx + vy * vy);
// Are the intercepting
if (dist < A.r + B.r && dist > B.r - A.r) {
c = (B.r * B.r - (dist * dist + A.r * A.r)) / (-2 * dist);
// Find mid point on cord
x = A.x + vx * (c / dist);
y = A.y + vy * (c / dist);
mark(x, y, 5, "blue");
// Find circumference intercepts
//#################################################################
//=================================================================
// SVG path
// Use x1,y1 and x2,y2 as the start and end angles of the ArcTo SVG
d = Math.sqrt(A.r * A.r - c * c);
x1 = x - vy * (d / dist);
y1 = y + vx * (d / dist);
x2 = x + vy * (d / dist);
y2 = y - vx * (d / dist);
// SVG path from above coords
// d = `M ${x1}, ${y1} A ${A.r}, ${A,r1} 0, 1, 1, ${x2}, ${y2}`;
//=================================================================
// draw the chord
line({x: x1,y: y1}, {x: x2,y: y2}, "red");
// mark the intercepts
mark(x1, y1, 5, "Green");
mark(x2, y2, 5, "Orange");
// Get direction from A to B
dir = Math.atan2(vy, vx);
// Get half inside sweep
a1 = Math.acos(c / A.r);
// Draw arc for A
A.col = "black";
A.w = 4;
A.f = dir - a1;
A.t = dir + a1;
drawCircle(A);
A.col = "#aaa";
A.w = 2;
A.f = 0;
A.t = PI2;
// inside sweep for B
a2 = Math.asin(d / B.r);
// Draw arc for B
B.col = "black";
B.w = 4;
if (dist < c) {
B.t = dir - a2;
B.f = dir + a2;
} else {
B.f = dir + PI - a2;
B.t = dir + PI + a2;
}
drawCircle(B);
B.col = "#aaa";
B.w = 2;
B.f = 0;
B.t = PI2;
}
}
var w = canvas.width;
var h = canvas.height;
var cw = w / 2; // center
var ch = h / 2;
var C1 = circle(cw, ch, ch * 0.5, "#aaa");
var C2 = circle(cw, ch, ch * 0.8, "#aaa");
function update(timer) {
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.globalAlpha = 1;
if (w !== innerWidth || h !== innerHeight) {
cw = (w = canvas.width = innerWidth) / 2;
ch = (h = canvas.height = innerHeight) / 2;
C1.x = cw;
C1.y = ch;
C1.r = ch * 0.5;
ctx.lineCap = "round";
}
C2.x = m.x;
C2.y = m.y;
ctx.clearRect(0, 0, w, h);
drawCircle(C1);
drawCircle(C2);
circleIntercept(C1, C2);
requestAnimationFrame(update);
}
requestAnimationFrame(update);
canvas {
position: absolute;
top: 0px;
left: 0px;
}
<canvas id="canvas"></canvas>
让我们从一些术语开始,弄清楚什么是顺时针方向(记住 SVG 的 y 轴是 向下 ):第一个圆的半径是 r1
,第二个 r2
.
- 如果第一个圆的圆心比第二个圆心低(
cy1 > cy2
),则用较小的x坐标命名交点(x1, y1)
,另一个(x2, y2)
.
- 如果第一个圆的圆心比第二个圆心高(
cy1 < cy2
),则用较大的x坐标命名交点(x1, y1)
,另一个(x2, y2)
.
- 否则,用较小的y坐标命名交点
(x1, y1)
,另一个(x2, y2)
。
现在我们将以第一个圆的半径从第一个交点到第二个交点绘制圆弧。前两个圆弧参数是水平和垂直半径。由于我们正在画一个圆,所以两者是相同的。同理,旋转半径没有意义,第三个参数为0。
两个圆的交点总是使用小圆弧(大圆弧用于并集),因此大圆弧标志为0。我们是顺时针绘制圆弧,因此扫描标志为1.
还不清楚? spec 使用这张图片来解释标志:
第二个圆弧从第二个到第一个交点与第二个圆的半径相交。标志保持不变。
结果如下所示:
M x1, y1 A r1 r1 0 0 1 x2, y2 A r2 r2 0 0 1 x1, y1 Z
我一直在努力理解 arc svg,因为我似乎需要它们在 plotly 中——我的目标是绘制圆的交叉点。
我最初的想法是这样的:
对于每个路口,找到起点和终点坐标以及高度 - 但我不太确定从这里到哪里去。看来我缺少旋转和大弧形标志/扫描参数,而且我不确定我将如何检索它们。如果有人能在这里指出我正确的方向,那就太好了!
圆圈和截取点
对 SVG arcTo 了解不多。 MDN给出了"A rx,ry xAxisRotate LargeArcFlag,SweepFlag x,y"
。作为路径元素中的弧。 rx 和 ry 是什么???我猜 x,y 的半径。
我猜你会用它作为
// x,y start position
// rx,ry radius x and y
// x1,y1 end position
<path d="M x,y A rx, ry, 0 1 1 x1, y1"/>
下面是解决的问题javascript。我已经评论了您需要的 SVG 部分。两个端点(截距)
有很多冗余但不清楚你想要什么所以代码提供了如何找到两个相交圆的其他部分。
余弦定律
解决问题的数学称为law of cosines,用于解决三角形。
在这种情况下,三角形是由 3 个长度创建的。一个是圆半径,一个是圆心之间的距离。图片提供了更多细节
用角 c 可以求出 GE、DE 和 EF 的长度。如果你想要另一边在 f 点的角度,只需交换 B 和 C。
例子
移动鼠标检查拦截。
const ctx = canvas.getContext("2d");
const m = {
x: 0,
y: 0
};
document.addEventListener("mousemove", e => {
var b = canvas.getBoundingClientRect();
m.x = e.pageX - b.left - scrollX;
m.y = e.pageY - b.top - scrollY;
});
const PI = Math.PI;
const PI2 = Math.PI * 2;
const circles = [];
function circle(x, y, r, col, f = 0, t = PI2, w = 2) {
var c;
circles.push(c = { x, y,r, col, f, t, w});
return c;
};
function drawCircle(A) {
ctx.strokeStyle = A.col;
ctx.lineWidth = A.w;
ctx.beginPath();
ctx.arc(A.x, A.y, A.r, A.f, A.t);
ctx.stroke();
}
function mark(x, y, r, c) {
ctx.strokeStyle = c;
ctx.lineWidth = 2;
ctx.beginPath();
ctx.arc(x, y, r, 0, PI2);
ctx.stroke();
}
function line(A, B, c) {
ctx.strokeStyle = c;
ctx.lineWidth = 2;
ctx.beginPath();
ctx.lineTo(A.x, A.y);
ctx.lineTo(B.x, B.y);
ctx.stroke();
}
// note I am sharing calc results between function
function circleIntercept(A, B) {
var vx, vy, dist, c, d, x, y, x1, y1, x2, y2, dir, a1, a2;
// Vec from A to B
vx = B.x - A.x;
vy = B.y - A.y;
// Distance between
dist = Math.sqrt(vx * vx + vy * vy);
// Are the intercepting
if (dist < A.r + B.r && dist > B.r - A.r) {
c = (B.r * B.r - (dist * dist + A.r * A.r)) / (-2 * dist);
// Find mid point on cord
x = A.x + vx * (c / dist);
y = A.y + vy * (c / dist);
mark(x, y, 5, "blue");
// Find circumference intercepts
//#################################################################
//=================================================================
// SVG path
// Use x1,y1 and x2,y2 as the start and end angles of the ArcTo SVG
d = Math.sqrt(A.r * A.r - c * c);
x1 = x - vy * (d / dist);
y1 = y + vx * (d / dist);
x2 = x + vy * (d / dist);
y2 = y - vx * (d / dist);
// SVG path from above coords
// d = `M ${x1}, ${y1} A ${A.r}, ${A,r1} 0, 1, 1, ${x2}, ${y2}`;
//=================================================================
// draw the chord
line({x: x1,y: y1}, {x: x2,y: y2}, "red");
// mark the intercepts
mark(x1, y1, 5, "Green");
mark(x2, y2, 5, "Orange");
// Get direction from A to B
dir = Math.atan2(vy, vx);
// Get half inside sweep
a1 = Math.acos(c / A.r);
// Draw arc for A
A.col = "black";
A.w = 4;
A.f = dir - a1;
A.t = dir + a1;
drawCircle(A);
A.col = "#aaa";
A.w = 2;
A.f = 0;
A.t = PI2;
// inside sweep for B
a2 = Math.asin(d / B.r);
// Draw arc for B
B.col = "black";
B.w = 4;
if (dist < c) {
B.t = dir - a2;
B.f = dir + a2;
} else {
B.f = dir + PI - a2;
B.t = dir + PI + a2;
}
drawCircle(B);
B.col = "#aaa";
B.w = 2;
B.f = 0;
B.t = PI2;
}
}
var w = canvas.width;
var h = canvas.height;
var cw = w / 2; // center
var ch = h / 2;
var C1 = circle(cw, ch, ch * 0.5, "#aaa");
var C2 = circle(cw, ch, ch * 0.8, "#aaa");
function update(timer) {
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.globalAlpha = 1;
if (w !== innerWidth || h !== innerHeight) {
cw = (w = canvas.width = innerWidth) / 2;
ch = (h = canvas.height = innerHeight) / 2;
C1.x = cw;
C1.y = ch;
C1.r = ch * 0.5;
ctx.lineCap = "round";
}
C2.x = m.x;
C2.y = m.y;
ctx.clearRect(0, 0, w, h);
drawCircle(C1);
drawCircle(C2);
circleIntercept(C1, C2);
requestAnimationFrame(update);
}
requestAnimationFrame(update);
canvas {
position: absolute;
top: 0px;
left: 0px;
}
<canvas id="canvas"></canvas>
让我们从一些术语开始,弄清楚什么是顺时针方向(记住 SVG 的 y 轴是 向下 ):第一个圆的半径是 r1
,第二个 r2
.
- 如果第一个圆的圆心比第二个圆心低(
cy1 > cy2
),则用较小的x坐标命名交点(x1, y1)
,另一个(x2, y2)
. - 如果第一个圆的圆心比第二个圆心高(
cy1 < cy2
),则用较大的x坐标命名交点(x1, y1)
,另一个(x2, y2)
. - 否则,用较小的y坐标命名交点
(x1, y1)
,另一个(x2, y2)
。
现在我们将以第一个圆的半径从第一个交点到第二个交点绘制圆弧。前两个圆弧参数是水平和垂直半径。由于我们正在画一个圆,所以两者是相同的。同理,旋转半径没有意义,第三个参数为0。
两个圆的交点总是使用小圆弧(大圆弧用于并集),因此大圆弧标志为0。我们是顺时针绘制圆弧,因此扫描标志为1.
还不清楚? spec 使用这张图片来解释标志:
第二个圆弧从第二个到第一个交点与第二个圆的半径相交。标志保持不变。
结果如下所示:
M x1, y1 A r1 r1 0 0 1 x2, y2 A r2 r2 0 0 1 x1, y1 Z