如何计算沿圆柱体切片的两点之间的 SVG 路径弧
How to calculate SVG path arc between two points along the slice of a cylinder
我正在尝试利用 SVG 在圆柱形表面上动态绘制线条。因为它是圆柱体,所以两点之间的任何直线实际上都会沿着圆柱体的椭圆切片,因此需要渲染为椭圆弧的一部分。
作为docs状态,SVG弧定义为:"A rx ry x-axis-rotation large-arc-flag sweep-flag x y"
请告诉我在推导这个弧 (AB) 时哪里出错了。
- 显然我知道我的起点和终点 (AB)。
- 我假设柱面和切片中的 ry 都相等。
- rx 是切片斜边的一半。
θ是x轴旋转...我觉得?
我认为这就是我遇到麻烦的地方。正如你在第二张图片中看到的那样,当我尝试将我所有的直线路径转换为弧形时,一些弧形在不旋转时变得完美 (它们平行于矩形平面中的 x 轴,这意味着它们遵循主圆柱椭圆的圆弧路径), 然而,旋转过程中发生了一些有趣的事情。当我打开 large-arc-flag 时,很明显绘制的椭圆根本没有与我的圆柱对齐。
我非常有信心斜边的计算是正确的,因为将 θ 设置为零可以得到圆柱体的直径,所以我对哪里出错有点困惑。
TLDR: 鉴于我在图 1 中提供的数据,您将如何绘制圆弧 AB。
编辑 1: 这是一个圆柱体的 SVG 和一条可以玩的线。同样,我试图将直线变成弧形以适合圆柱体表面,使其与通过圆柱体切片形成的椭圆弧相匹配。
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path id="cylinder" fill="none" stroke="#000000" stroke-width="2" d="M 0 32 a148 32 0 0 0 296 0 a148 32 0 0 0 -296 0 v185 a148 32 0 0 0 296 0 v-185"/>
<path id="line_should_become_arc" fill="none" stroke="#000000" stroke-width="2" d="M 37 106 L 259 148"/>
</svg>
也许你想要这样的东西
(本人对JS不熟悉,不知道如何提供SVG曲线的计算参数)
AB 在直角坐标系中的角度为 15 度,1/cos(15)=1.035
- rx 的系数。 Y-coordinates 的蓝色圆弧有意移动了 10 个像素
<svg width="400" height="400" xmlns="http://www.w3.org/2000/svg">
<path d="M50 50 L50 250" stroke="black" fill="transparent"/>
<path d="M50 250 A100 40 0 0 0 250 250" stroke="black" fill="transparent"/>
<path d="M250 250 L250 50 A100 40 0 0 0 50 50 A100 40 0 0 0 250 50" stroke="black" fill="transparent"/>
<path d="M50 150 A103.5 40 15 0 0 250 200 A103.5 40 15 0 0 50 150" stroke="black" fill="transparent"/>
<path d="M100 210 A103.5 40 15 0 0 200 232" stroke="blue" fill="transparent"/>
</svg>
几乎是即插即用的。唯一需要一些努力的是 Arc 命令中的两个标志。
我从您的示例 SVG 中的两个路径端点开始,并从形成圆柱体顶部的弧中获得 rx 和 ry。但是你没有提供任何 theta,所以我选择了一个,并且不得不调整端点,使切片与圆柱壁对齐。
var arc = document.getElementById("line_should_become_arc");
var slice = document.getElementById("slice");
var Ax = 57, Ay = 126;
var Bx = 279, By = 168;
var rx = 148;
var ry = 32;
var theta = 14; // 14 deg
var slice_rx = rx / Math.cos(theta * Math.PI / 180);
arc.setAttribute("d", ['M', Ax,Ay, 'A', slice_rx, ry, theta, 0, 0, Bx,By].join(' '));
slice.setAttribute("d", ['M', Ax,Ay, 'A', slice_rx, ry, theta, 1, 1, Bx,By].join(' '));
<svg width="400" height="400">
<path id="cylinder" fill="none" stroke="#000000" stroke-width="2" d="M 0 32 a148 32 0 0 0 296 0 a148 32 0 0 0 -296 0 v185 a148 32 0 0 0 296 0 v-185"/>
<path id="slice" fill="none" stroke="#000000" stroke-width="2" d="M 0,0"/>
<path id="line_should_become_arc" fill="none" stroke="#f00" stroke-width="2" d="M 0,0"/>
</svg>
我假设如下:你有一个 3D 坐标系 Oxyz 和一个半径为 a
的直圆柱体,其轴 运行 沿着圆柱体的中间,与 Oy 轴重合.然后圆柱体的圆形底面垂直于轴 Oy,因此平行于(或重合)坐标平面 Oxz。在这个坐标系中,圆柱体可以用属性
描述为所有的3D点
[x; y; z] such that x^2 + z^2 = a^2, while y is arbitrary or D <= y < = U.
我假设圆柱体是从 3D Oxyz 坐标系投影到 Oxy 坐标平面上的,这样通过圆柱体与坐标平面 Oxz 的交点获得的圆被投影为长轴椭圆长度 a
,与轴 Ox 对齐,短轴长度 b
,与轴 Oz 对齐。在你的照片上 a = cylinder rx
和 b = ry
。
此信息使我们能够确定投影方向:
direction = [0; b; a]
即对于任何点 P = [x_3D; y_3D; z_3D]在3D系统Oxyz中,我们取过P并平行于向量direction
的直线,它与Oxy的交点就是P在Oxy上的投影。这个公式是
[x_3D; y_3D; z_3D] ---> [x_3D; y_3D - (b/a)*z_3D]
i.e.
x = x_3D
y = y_3D - (b/a)*z_3D
(方向还有另一个选项:direction = [0; - b; a]
如果投影完成 "from under" Oxz 轴而不是 "over",但让我们坚持使用 "over")
相反,如果给定一个点 [x; y] 在 Oxy 2D 坐标平面上,可以恢复圆柱表面上的两个点,投影到 [x; y]:
[x; y] ---> [x; y + (b/a)*sqrt(a^2 - x^2); sqrt(a^2 - x^2)]
这是半个圆柱体上的点 space 轴 Oz 为正,并且
[x; y] ---> [x; y - (b/a)*sqrt(a^2 - x^2); - sqrt(a^2 - x^2)]
在半个圆柱体上 space 其中轴 )z 为负。
圆柱体的表面可以通过取一个平面矩形进行参数化,并通过将其两个平行边粘合在一起以形成直圆柱体来在 3D 中弯曲它。这个变换可以写成
[s; y] ---> [a*cos(s/a); y; a*sin(s/a)]
i.e.
x = a*cos(s/a)
y = y
z = a*sin(s/a)
然后是平面正方形上的一条通用直线
y = y0 + m*(s - s0)
变成躺在圆柱体表面的3D曲线
x = a*cos(s/a)
y = y0 + m*(s - s0)
z = a*sin(s/a)
这是一个螺旋。
现在,您将作为输入
a, b, A = [xA; yA], B = [xB; yB]
你的目标是找到 Oxy 中通过 A 和 B 的曲线的方程,它是螺旋线在 3D 圆柱体上的投影。
Step1:恢复圆柱体上分别投影到A和B的3D点A_3D和B_3D。使用上面的公式(假设 A_3D 和 B_3D 在 Oz 的正侧)
A_3D = [xA; yA + (b/a)*sqrt(a^2 - xA^2); sqrt(a^2 - xA^2)];
B_3D = [xB; yB + (b/a)*sqrt(a^2 - xB^2); sqrt(a^2 - xB^2)];
Step2:在[s;中分别表示A_3D和B_3D; y]圆柱面坐标:
s_A = a*arccos(xA);
y_A = yA + (b/a)*sqrt(a^2 - xA^2);
s_B = a*arccos(xB);
y_B = yB + (b/a)*sqrt(a^2 - xB^2);
第三步:在[s;中构造直线; y]坐标:
m = (y_B - y_A) / (s_B - s_A)
= (yB + (b/a)*sqrt(a^2-xB^2) - yA - (b/a)*sqrt(a^2-xA^2)) / (a*arccos(xB) - a*arccos(xA))
= ((yB - yA) + (b/a)*(sqrt(a^2-xB^2) - sqrt(a^2-xA^2))) / (a*arccos(xB) - a*arccos(xA));
y = y_A + m*(s - s_A);
第 4 步: 在 3D 中将其表示为螺旋线:
x_3D = a*cos(s/a)
y_3D = y_A + m*(s - s_A)
z_3D = a*sin(s/a)
第 5 步: 将圆柱体的螺旋线沿 direction = [0; b; a]
:
投影到坐标 Oxy 平面上
x = a*cos(s/a)
y = y_A + m*(s - s_A) - b*sin(s/a)
我正在尝试利用 SVG 在圆柱形表面上动态绘制线条。因为它是圆柱体,所以两点之间的任何直线实际上都会沿着圆柱体的椭圆切片,因此需要渲染为椭圆弧的一部分。
作为docs状态,SVG弧定义为:"A rx ry x-axis-rotation large-arc-flag sweep-flag x y"
请告诉我在推导这个弧 (AB) 时哪里出错了。
- 显然我知道我的起点和终点 (AB)。
- 我假设柱面和切片中的 ry 都相等。
- rx 是切片斜边的一半。
θ是x轴旋转...我觉得?
我认为这就是我遇到麻烦的地方。正如你在第二张图片中看到的那样,当我尝试将我所有的直线路径转换为弧形时,一些弧形在不旋转时变得完美 (它们平行于矩形平面中的 x 轴,这意味着它们遵循主圆柱椭圆的圆弧路径), 然而,旋转过程中发生了一些有趣的事情。当我打开 large-arc-flag 时,很明显绘制的椭圆根本没有与我的圆柱对齐。
我非常有信心斜边的计算是正确的,因为将 θ 设置为零可以得到圆柱体的直径,所以我对哪里出错有点困惑。
TLDR: 鉴于我在图 1 中提供的数据,您将如何绘制圆弧 AB。
编辑 1: 这是一个圆柱体的 SVG 和一条可以玩的线。同样,我试图将直线变成弧形以适合圆柱体表面,使其与通过圆柱体切片形成的椭圆弧相匹配。
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path id="cylinder" fill="none" stroke="#000000" stroke-width="2" d="M 0 32 a148 32 0 0 0 296 0 a148 32 0 0 0 -296 0 v185 a148 32 0 0 0 296 0 v-185"/>
<path id="line_should_become_arc" fill="none" stroke="#000000" stroke-width="2" d="M 37 106 L 259 148"/>
</svg>
也许你想要这样的东西
(本人对JS不熟悉,不知道如何提供SVG曲线的计算参数)
AB 在直角坐标系中的角度为 15 度,1/cos(15)=1.035
- rx 的系数。 Y-coordinates 的蓝色圆弧有意移动了 10 个像素
<svg width="400" height="400" xmlns="http://www.w3.org/2000/svg">
<path d="M50 50 L50 250" stroke="black" fill="transparent"/>
<path d="M50 250 A100 40 0 0 0 250 250" stroke="black" fill="transparent"/>
<path d="M250 250 L250 50 A100 40 0 0 0 50 50 A100 40 0 0 0 250 50" stroke="black" fill="transparent"/>
<path d="M50 150 A103.5 40 15 0 0 250 200 A103.5 40 15 0 0 50 150" stroke="black" fill="transparent"/>
<path d="M100 210 A103.5 40 15 0 0 200 232" stroke="blue" fill="transparent"/>
</svg>
几乎是即插即用的。唯一需要一些努力的是 Arc 命令中的两个标志。
我从您的示例 SVG 中的两个路径端点开始,并从形成圆柱体顶部的弧中获得 rx 和 ry。但是你没有提供任何 theta,所以我选择了一个,并且不得不调整端点,使切片与圆柱壁对齐。
var arc = document.getElementById("line_should_become_arc");
var slice = document.getElementById("slice");
var Ax = 57, Ay = 126;
var Bx = 279, By = 168;
var rx = 148;
var ry = 32;
var theta = 14; // 14 deg
var slice_rx = rx / Math.cos(theta * Math.PI / 180);
arc.setAttribute("d", ['M', Ax,Ay, 'A', slice_rx, ry, theta, 0, 0, Bx,By].join(' '));
slice.setAttribute("d", ['M', Ax,Ay, 'A', slice_rx, ry, theta, 1, 1, Bx,By].join(' '));
<svg width="400" height="400">
<path id="cylinder" fill="none" stroke="#000000" stroke-width="2" d="M 0 32 a148 32 0 0 0 296 0 a148 32 0 0 0 -296 0 v185 a148 32 0 0 0 296 0 v-185"/>
<path id="slice" fill="none" stroke="#000000" stroke-width="2" d="M 0,0"/>
<path id="line_should_become_arc" fill="none" stroke="#f00" stroke-width="2" d="M 0,0"/>
</svg>
我假设如下:你有一个 3D 坐标系 Oxyz 和一个半径为 a
的直圆柱体,其轴 运行 沿着圆柱体的中间,与 Oy 轴重合.然后圆柱体的圆形底面垂直于轴 Oy,因此平行于(或重合)坐标平面 Oxz。在这个坐标系中,圆柱体可以用属性
[x; y; z] such that x^2 + z^2 = a^2, while y is arbitrary or D <= y < = U.
我假设圆柱体是从 3D Oxyz 坐标系投影到 Oxy 坐标平面上的,这样通过圆柱体与坐标平面 Oxz 的交点获得的圆被投影为长轴椭圆长度 a
,与轴 Ox 对齐,短轴长度 b
,与轴 Oz 对齐。在你的照片上 a = cylinder rx
和 b = ry
。
此信息使我们能够确定投影方向:
direction = [0; b; a]
即对于任何点 P = [x_3D; y_3D; z_3D]在3D系统Oxyz中,我们取过P并平行于向量direction
的直线,它与Oxy的交点就是P在Oxy上的投影。这个公式是
[x_3D; y_3D; z_3D] ---> [x_3D; y_3D - (b/a)*z_3D]
i.e.
x = x_3D
y = y_3D - (b/a)*z_3D
(方向还有另一个选项:direction = [0; - b; a]
如果投影完成 "from under" Oxz 轴而不是 "over",但让我们坚持使用 "over")
相反,如果给定一个点 [x; y] 在 Oxy 2D 坐标平面上,可以恢复圆柱表面上的两个点,投影到 [x; y]:
[x; y] ---> [x; y + (b/a)*sqrt(a^2 - x^2); sqrt(a^2 - x^2)]
这是半个圆柱体上的点 space 轴 Oz 为正,并且
[x; y] ---> [x; y - (b/a)*sqrt(a^2 - x^2); - sqrt(a^2 - x^2)]
在半个圆柱体上 space 其中轴 )z 为负。
圆柱体的表面可以通过取一个平面矩形进行参数化,并通过将其两个平行边粘合在一起以形成直圆柱体来在 3D 中弯曲它。这个变换可以写成
[s; y] ---> [a*cos(s/a); y; a*sin(s/a)]
i.e.
x = a*cos(s/a)
y = y
z = a*sin(s/a)
然后是平面正方形上的一条通用直线
y = y0 + m*(s - s0)
变成躺在圆柱体表面的3D曲线
x = a*cos(s/a)
y = y0 + m*(s - s0)
z = a*sin(s/a)
这是一个螺旋。
现在,您将作为输入
a, b, A = [xA; yA], B = [xB; yB]
你的目标是找到 Oxy 中通过 A 和 B 的曲线的方程,它是螺旋线在 3D 圆柱体上的投影。
Step1:恢复圆柱体上分别投影到A和B的3D点A_3D和B_3D。使用上面的公式(假设 A_3D 和 B_3D 在 Oz 的正侧)
A_3D = [xA; yA + (b/a)*sqrt(a^2 - xA^2); sqrt(a^2 - xA^2)];
B_3D = [xB; yB + (b/a)*sqrt(a^2 - xB^2); sqrt(a^2 - xB^2)];
Step2:在[s;中分别表示A_3D和B_3D; y]圆柱面坐标:
s_A = a*arccos(xA);
y_A = yA + (b/a)*sqrt(a^2 - xA^2);
s_B = a*arccos(xB);
y_B = yB + (b/a)*sqrt(a^2 - xB^2);
第三步:在[s;中构造直线; y]坐标:
m = (y_B - y_A) / (s_B - s_A)
= (yB + (b/a)*sqrt(a^2-xB^2) - yA - (b/a)*sqrt(a^2-xA^2)) / (a*arccos(xB) - a*arccos(xA))
= ((yB - yA) + (b/a)*(sqrt(a^2-xB^2) - sqrt(a^2-xA^2))) / (a*arccos(xB) - a*arccos(xA));
y = y_A + m*(s - s_A);
第 4 步: 在 3D 中将其表示为螺旋线:
x_3D = a*cos(s/a)
y_3D = y_A + m*(s - s_A)
z_3D = a*sin(s/a)
第 5 步: 将圆柱体的螺旋线沿 direction = [0; b; a]
:
x = a*cos(s/a)
y = y_A + m*(s - s_A) - b*sin(s/a)