如何计算沿圆柱体切片的两点之间的 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) 时哪里出错了。

  1. 显然我知道我的起点和终点 (AB)。
  2. 我假设柱面和切片中的 ry 都相等。
  3. rx 是切片斜边的一半。
  4. θ是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曲线的计算参数)

A​​B 在直角坐标系中的角度为 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 rxb = 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)