While working on SVG implementation for Internet Explorer to be based on its own VML format I came to a problem of translation of an SVG elliptical arc to an VML elliptical arc.
在为Internet Explorer编写SVG实现时,基于它自己的VML格式,我遇到了一个将SVG椭圆弧线转换为VML椭圆弧线的问题。
In VML an arc is given by: two angles for two points on ellipse and lengths of radiuses, In SVG an arc is given by: two pairs of coordinates for two points on ellipse and sizes of ellipse boundary box
在VML中给出了一个圆弧:两个角在椭圆和半径上的两个角,在SVG中给出了一个圆弧:两个在椭圆和椭圆边界框上的两个点的坐标。
So, the question is: How to express angles of two points on ellipse to two pairs of their coordinates. An intermediate question could be: How to find the center of an ellipse by coordinates of a pair of points on its curve.
因此,问题是:如何将两个点的角度在椭圆上表示成两对坐标。一个中间的问题可以是:如何通过曲线上一对点的坐标找到椭圆的中心。
Update: Let's have a precondition saying that an ellipse is normally placed (its radiuses are parallel to linear coordinate system axis), thus no rotation is applied.
更新:我们有一个前提条件,即椭圆是正常放置的(它的半径与线性坐标系的轴平行),因此不应用旋转。
Update: This question is not related to svg:ellipse element, rather to "a" elliptical arc command in svg:path element (SVG Paths: The elliptical arc curve commands)
更新:这个问题与svg无关:椭圆元素,而不是svg中的椭圆弧线命令:path元素(svg路径:椭圆弧线命令)
4 个解决方案
#1
21
So the solution is here:
所以解决方法是:
The parametrized formula of an ellipse:
椭圆的参数化公式:
x = x0 + a * cos(t) y = y0 + b * sin(t)
Let's put known coordinates of two points to it:
让我们把已知的两个点的坐标,
x1 = x0 + a * cos(t1) x2 = x0 + a * cos(t2) y1 = y0 + b * sin(t1) y2 = y0 + b * sin(t2)
Now we have a system of equations with 4 variables: center of ellipse (x0/y0) and two angles t1, t2
现在我们有一个有4个变量的方程组:椭圆的中心(x0/y0)和两个角t1, t2。
Let's subtract equations in order to get rid of center coordinates:
为了去掉中心坐标,让我们减去方程:
x1 - x2 = a * (cos(t1) - cos(t2)) y1 - y2 = b * (sin(t1) - sin(t2))
This can be rewritten (with product-to-sum identities formulas) as:
这可以重写为:
(x1 - x2) / (2 * a) = sin((t1 + t2) / 2) * sin((t1 - t2) / 2) (y2 - y1) / (2 * b) = cos((t1 + t2) / 2) * sin((t1 - t2) / 2)
Let's replace some of the equations:
我们来替换一些方程:
r1: (x1 - x2) / (2 * a) r2: (y2 - y1) / (2 * b) a1: (t1 + t2) / 2 a2: (t1 - t2) / 2
Then we get simple equations system:
然后我们得到简单的方程组:
r1 = sin(a1) * sin(a2) r2 = cos(a1) * sin(a2)
Dividing first equation by second produces:
将第一个方程除以二产生:
a1 = arctan(r1/r2)
Adding this result to the first equation gives:
将这个结果加到第一个方程中:
a2 = arcsin(r2 / cos(arctan(r1/r2)))
Or, simple (using compositions of trig and inverse trig functions):
或者,简单(使用三角函数和逆三角函数的组合):
a2 = arcsin(r2 / (1 / sqrt(1 + (r1/r2)^2)))
or even more simple:
甚至更简单:
a2 = arcsin(sqrt(r1^2 + r2^2))
Now the initial four-equations system can be resolved with easy and all angles as well as eclipse center coordinates can be found.
现在,初始的四方程系统可以轻松地解决,所有角度和eclipse中心坐标都可以找到。
#2
4
The elliptical curve arc link you posted includes a link to elliptical arc implementation notes.
您发布的椭圆曲线弧链接包括一个椭圆弧线实现注释的链接。
In there, you will find the equations for conversion from endpoint to centre parameterisation.
在那里,你会发现从端点到中心参数化的等式。
Here is my JavaScript implementation of those equations, taken from an interactive demo of elliptical arc paths, using Sylvester.js to perform the matrix and vector calculations.
这是我的JavaScript实现的那些方程,从一个交互式演示的椭圆弧线路径,使用西尔维斯特。执行矩阵和向量计算。
// Calculate the centre of the ellipse
// Based on http://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter
var x1 = 150; // Starting x-point of the arc
var y1 = 150; // Starting y-point of the arc
var x2 = 400; // End x-point of the arc
var y2 = 300; // End y-point of the arc
var fA = 1; // Large arc flag
var fS = 1; // Sweep flag
var rx = 100; // Horizontal radius of ellipse
var ry = 50; // Vertical radius of ellipse
var phi = 0; // Angle between co-ord system and ellipse x-axes
var Cx, Cy;
// Step 1: Compute (x1′, y1′)
var M = $M([
[ Math.cos(phi), Math.sin(phi)],
[-Math.sin(phi), Math.cos(phi)]
]);
var V = $V( [ (x1-x2)/2, (y1-y2)/2 ] );
var P = M.multiply(V);
var x1p = P.e(1); // x1 prime
var y1p = P.e(2); // y1 prime
// Ensure radii are large enough
// Based on http://www.w3.org/TR/SVG/implnote.html#ArcOutOfRangeParameters
// Step (a): Ensure radii are non-zero
// Step (b): Ensure radii are positive
rx = Math.abs(rx);
ry = Math.abs(ry);
// Step (c): Ensure radii are large enough
var lambda = ( (x1p * x1p) / (rx * rx) ) + ( (y1p * y1p) / (ry * ry) );
if(lambda > 1)
{
rx = Math.sqrt(lambda) * rx;
ry = Math.sqrt(lambda) * ry;
}
// Step 2: Compute (cx′, cy′)
var sign = (fA == fS)? -1 : 1;
// Bit of a hack, as presumably rounding errors were making his negative inside the square root!
if((( (rx*rx*ry*ry) - (rx*rx*y1p*y1p) - (ry*ry*x1p*x1p) ) / ( (rx*rx*y1p*y1p) + (ry*ry*x1p*x1p) )) < 1e-7)
var co = 0;
else
var co = sign * Math.sqrt( ( (rx*rx*ry*ry) - (rx*rx*y1p*y1p) - (ry*ry*x1p*x1p) ) / ( (rx*rx*y1p*y1p) + (ry*ry*x1p*x1p) ) );
var V = $V( [rx*y1p/ry, -ry*x1p/rx] );
var Cp = V.multiply(co);
// Step 3: Compute (cx, cy) from (cx′, cy′)
var M = $M([
[ Math.cos(phi), -Math.sin(phi)],
[ Math.sin(phi), Math.cos(phi)]
]);
var V = $V( [ (x1+x2)/2, (y1+y2)/2 ] );
var C = M.multiply(Cp).add(V);
Cx = C.e(1);
Cy = C.e(2);
#3
1
An ellipse cannot be defined by only two points. Even a circle (a special cased ellipse) is defined by three points.
一个椭圆不能只定义两个点。甚至一个圆(一个特殊的外壳椭圆)由3个点定义。
Even with three points, you would have infinite ellipses passing through these three points (think: rotation).
即使有三个点,你也会有无限的椭圆通过这三个点(思考:旋转)。
Note that a bounding box suggests a center for the ellipse, and most probably assumes that its major and minor axes are parallel to the x,y (or y,x) axes.
注意,一个边界框建议一个椭圆的中心,并且很可能假设它的主轴和副轴平行于x,y(或y,x)轴。
#4
1
The intermediate question is fairly easy... you don't. You work out the centre of an ellipse from the bounding box (namely, the centre of the box is the centre of the ellipse, as long as the ellipse is centred in the box).
中间的问题相当简单……你不。你从包围盒中算出一个椭圆的中心(也就是说,盒子的中心是椭圆的中心,只要椭圆的中心在盒子里)。
For your first question, I'd look at the polar form of the ellipse equation, which is available on Wikipedia. You would need to work out the eccentricity of the ellipse as well.
对于第一个问题,我将看一下椭圆方程的极坐标形式,它在*上是可用的。你也需要计算出椭圆的离心率。
Or you could brute force the values from the bounding box... work out if a point lies on the ellipse and matches the angle, and iterate through every point in the bounding box.
或者你可以用蛮力从包围盒中强制值…如果一个点位于椭圆上并与角度匹配,并遍历边界框中的每一个点,就可以算出结果。
#1
21
So the solution is here:
所以解决方法是:
The parametrized formula of an ellipse:
椭圆的参数化公式:
x = x0 + a * cos(t) y = y0 + b * sin(t)
Let's put known coordinates of two points to it:
让我们把已知的两个点的坐标,
x1 = x0 + a * cos(t1) x2 = x0 + a * cos(t2) y1 = y0 + b * sin(t1) y2 = y0 + b * sin(t2)
Now we have a system of equations with 4 variables: center of ellipse (x0/y0) and two angles t1, t2
现在我们有一个有4个变量的方程组:椭圆的中心(x0/y0)和两个角t1, t2。
Let's subtract equations in order to get rid of center coordinates:
为了去掉中心坐标,让我们减去方程:
x1 - x2 = a * (cos(t1) - cos(t2)) y1 - y2 = b * (sin(t1) - sin(t2))
This can be rewritten (with product-to-sum identities formulas) as:
这可以重写为:
(x1 - x2) / (2 * a) = sin((t1 + t2) / 2) * sin((t1 - t2) / 2) (y2 - y1) / (2 * b) = cos((t1 + t2) / 2) * sin((t1 - t2) / 2)
Let's replace some of the equations:
我们来替换一些方程:
r1: (x1 - x2) / (2 * a) r2: (y2 - y1) / (2 * b) a1: (t1 + t2) / 2 a2: (t1 - t2) / 2
Then we get simple equations system:
然后我们得到简单的方程组:
r1 = sin(a1) * sin(a2) r2 = cos(a1) * sin(a2)
Dividing first equation by second produces:
将第一个方程除以二产生:
a1 = arctan(r1/r2)
Adding this result to the first equation gives:
将这个结果加到第一个方程中:
a2 = arcsin(r2 / cos(arctan(r1/r2)))
Or, simple (using compositions of trig and inverse trig functions):
或者,简单(使用三角函数和逆三角函数的组合):
a2 = arcsin(r2 / (1 / sqrt(1 + (r1/r2)^2)))
or even more simple:
甚至更简单:
a2 = arcsin(sqrt(r1^2 + r2^2))
Now the initial four-equations system can be resolved with easy and all angles as well as eclipse center coordinates can be found.
现在,初始的四方程系统可以轻松地解决,所有角度和eclipse中心坐标都可以找到。
#2
4
The elliptical curve arc link you posted includes a link to elliptical arc implementation notes.
您发布的椭圆曲线弧链接包括一个椭圆弧线实现注释的链接。
In there, you will find the equations for conversion from endpoint to centre parameterisation.
在那里,你会发现从端点到中心参数化的等式。
Here is my JavaScript implementation of those equations, taken from an interactive demo of elliptical arc paths, using Sylvester.js to perform the matrix and vector calculations.
这是我的JavaScript实现的那些方程,从一个交互式演示的椭圆弧线路径,使用西尔维斯特。执行矩阵和向量计算。
// Calculate the centre of the ellipse
// Based on http://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter
var x1 = 150; // Starting x-point of the arc
var y1 = 150; // Starting y-point of the arc
var x2 = 400; // End x-point of the arc
var y2 = 300; // End y-point of the arc
var fA = 1; // Large arc flag
var fS = 1; // Sweep flag
var rx = 100; // Horizontal radius of ellipse
var ry = 50; // Vertical radius of ellipse
var phi = 0; // Angle between co-ord system and ellipse x-axes
var Cx, Cy;
// Step 1: Compute (x1′, y1′)
var M = $M([
[ Math.cos(phi), Math.sin(phi)],
[-Math.sin(phi), Math.cos(phi)]
]);
var V = $V( [ (x1-x2)/2, (y1-y2)/2 ] );
var P = M.multiply(V);
var x1p = P.e(1); // x1 prime
var y1p = P.e(2); // y1 prime
// Ensure radii are large enough
// Based on http://www.w3.org/TR/SVG/implnote.html#ArcOutOfRangeParameters
// Step (a): Ensure radii are non-zero
// Step (b): Ensure radii are positive
rx = Math.abs(rx);
ry = Math.abs(ry);
// Step (c): Ensure radii are large enough
var lambda = ( (x1p * x1p) / (rx * rx) ) + ( (y1p * y1p) / (ry * ry) );
if(lambda > 1)
{
rx = Math.sqrt(lambda) * rx;
ry = Math.sqrt(lambda) * ry;
}
// Step 2: Compute (cx′, cy′)
var sign = (fA == fS)? -1 : 1;
// Bit of a hack, as presumably rounding errors were making his negative inside the square root!
if((( (rx*rx*ry*ry) - (rx*rx*y1p*y1p) - (ry*ry*x1p*x1p) ) / ( (rx*rx*y1p*y1p) + (ry*ry*x1p*x1p) )) < 1e-7)
var co = 0;
else
var co = sign * Math.sqrt( ( (rx*rx*ry*ry) - (rx*rx*y1p*y1p) - (ry*ry*x1p*x1p) ) / ( (rx*rx*y1p*y1p) + (ry*ry*x1p*x1p) ) );
var V = $V( [rx*y1p/ry, -ry*x1p/rx] );
var Cp = V.multiply(co);
// Step 3: Compute (cx, cy) from (cx′, cy′)
var M = $M([
[ Math.cos(phi), -Math.sin(phi)],
[ Math.sin(phi), Math.cos(phi)]
]);
var V = $V( [ (x1+x2)/2, (y1+y2)/2 ] );
var C = M.multiply(Cp).add(V);
Cx = C.e(1);
Cy = C.e(2);
#3
1
An ellipse cannot be defined by only two points. Even a circle (a special cased ellipse) is defined by three points.
一个椭圆不能只定义两个点。甚至一个圆(一个特殊的外壳椭圆)由3个点定义。
Even with three points, you would have infinite ellipses passing through these three points (think: rotation).
即使有三个点,你也会有无限的椭圆通过这三个点(思考:旋转)。
Note that a bounding box suggests a center for the ellipse, and most probably assumes that its major and minor axes are parallel to the x,y (or y,x) axes.
注意,一个边界框建议一个椭圆的中心,并且很可能假设它的主轴和副轴平行于x,y(或y,x)轴。
#4
1
The intermediate question is fairly easy... you don't. You work out the centre of an ellipse from the bounding box (namely, the centre of the box is the centre of the ellipse, as long as the ellipse is centred in the box).
中间的问题相当简单……你不。你从包围盒中算出一个椭圆的中心(也就是说,盒子的中心是椭圆的中心,只要椭圆的中心在盒子里)。
For your first question, I'd look at the polar form of the ellipse equation, which is available on Wikipedia. You would need to work out the eccentricity of the ellipse as well.
对于第一个问题,我将看一下椭圆方程的极坐标形式,它在*上是可用的。你也需要计算出椭圆的离心率。
Or you could brute force the values from the bounding box... work out if a point lies on the ellipse and matches the angle, and iterate through every point in the bounding box.
或者你可以用蛮力从包围盒中强制值…如果一个点位于椭圆上并与角度匹配,并遍历边界框中的每一个点,就可以算出结果。