从贝塞尔曲线重建圆

时间:2012-10-16 01:02:08

标签: svg postscript bezier

我正在尝试从Postscript / SVG路径重建原始图形基元。因此,原始圆圈(在SVG标记中)呈现为:

   <path stroke-width="0.5" d="M159.679 141.309 
        C159.679 141.793 159.286 142.186 158.801 142.186 
        C158.318 142.186 157.925 141.793 157.925 141.309 
        C157.925 140.825 158.318 140.432 158.801 140.432 
        C159.286 140.432 159.679 140.825 159.679 141.309" />

这是使用4条贝塞尔曲线创建圆的近似值。在其他地方,圆弧由链接的贝塞尔曲线近似。

我的问题是,是否有一种算法可用于识别此构造并重建“最佳”圆。我不介意小错误 - 最糟糕的情况是二阶。

更新:请注意,我不知道这是一个圆圈或圆弧 - 它可能是任何东西。曲线上可能有2,3,4或甚至更多的点。所以我真的很喜欢这种类型的功能:

error = getCircleFromPath(path)

其中error会尽早表明这是否可能是一个圆圈。

[我同意,如果我知道这是一个圆圈,这是一个更容易的问题。]

更新:@george在某种程度上回答了我的问题,但我不认为这是整个故事。

在翻译到原点和标准化后,我似乎在曲线上有以下四点:

point [0, 1] with control point at [+-d,1] // horizontal tangent
point [1, 0] with control point at [1,+-d] // vertical tangent
point [0, -1] with control point at [+-d,-1] // horizontal tangent
point [-1, 0] with control point at [-1,+-d] // vertical tangent

这保证了每个点处的切线与该点处的路径方向“平行”。它还保证了对称性(带有反射的4倍轴。但它不能保证圆形。例如,d的大值将给出圆形框,小值为圆形钻石。

d的值似乎约为0.57。这可能是1 / sqrt(3。)或者它可能是别的。这就是我要求的这种关系。

@george给出弧的中点为;

{p1,(p1 + 3 (p2 + p3) + p4)/8,p4}

所以在我的例子中(对于1,0到0,1),这将是:     [[1,0]+3[1,d]+3[d,1]+[0,1]] / 8 即。

[0.5+3d/8, 3d/8+0.5]

如果d = 0.57,则得到0.71,所以d可能是

(sqrt(0.5)-0.5)*8./3.

这适用于方形钻石,但对于圆弧,公式必须更加通用,如果有人拥有它,我将不胜感激。例如,我不熟悉Bezier数学,所以@george的公式对我来说是新的

enter code here

4 个答案:

答案 0 :(得分:5)

没有为你做所有的数学运算......这可能会有所帮助:

bezier上总有4个控制点。 您的曲线是4个贝塞尔曲线,它们与控制点1-4,4-7,7-10和10-13相连 对于每个部分。点1,4,7和10(&amp; 13 == 1)恰好位于曲线上。要查看你是否有一个漂亮的圆计算:

center =   ( p1+p7 )/2  =(  {159.679, 141.309} +  {157.925, 141.309} ) / 2
       = {158.802, 141.309}

使用积分4 + 10验证您获得相同的结果 - &gt; {158.801,141.309}

一旦你知道了中心,就可以沿着曲线采样点,看看你是否有一个恒定的距离。

如果你只有一个带有4个点的bezier弧,则有用的公式是中点为 (p1 + 3(p2 + p3)+ p4)/ 8。所以你可以找到通过三个点的圆圈:

{p1,(p1 + 3 (p2 + p3) + p4)/8,p4}

再次对曲线上的其他点进行采样,以确定您是否确实有一个近圆弧。

编辑 bezier公式是这样的:

x=(1-t)^3 p1 + 3 (1-t)^2 t p2 + 3 (1-t) t^2 p3 + t^3 p4    with  parameter 0 < t < 1

所以例如在t = 1/4你有

x=( 27 p1 + 27 p2 + 9 p3 + 1 p4 ) / 64

因此,一旦找到中心,您可以轻松检查几个点并计算它们的距离。

我怀疑如果你只想检测几乎精确的圆弧,那么用一个严格的公差检查两个额外的点就可以了。如果你想检测近似圆形的东西,我会计算一堆点,并使用平均误差作为标准。

答案 1 :(得分:3)

如果您的所有元素都是圆形的,那么您可以通过path.getBBox()获取尺寸并从那里生成一个圆。在这种情况下,我正在考虑省略号,但您可以轻松地将其转换为实际的circle元素:

var path = document.getElementById("circle_path");
var bbox = path.getBBox();

var rx = bbox.width/2;
var ry = bbox.height/2;
var cx = bbox.x + rx;
var cy = bbox.y + ry;

var ellipse = document.createElementNS(xmlns, "ellipse");
ellipse.setAttribute("fill", "none");
ellipse.setAttribute("stroke", "red");
ellipse.setAttribute("stroke-width", 0.1);
ellipse.setAttribute("cx", cx);
ellipse.setAttribute("cy", cy);
ellipse.setAttribute("rx", rx);
ellipse.setAttribute("ry", ry);

svg.appendChild(ellipse);

你可以在这里看到一个演示:

http://jsfiddle.net/nwHm6/

答案 2 :(得分:1)

Bézier曲线的端点可能 on 圆圈。如果是这样,重建原始圆圈很容易。

另一种可能性是将控制点的重心作为圆的中心,因为控制点可能围绕中心对称布局。从中心,您可以得到半径作为最靠近中心的四个控制点的平均距离。

答案 3 :(得分:0)

可以将椭圆定义为以(0,0)为中心,平移(2个参数),缩放(2个参数)和旋转(1个参数)的单位圆。因此,在每个弧上取五个点(t =0¼½¾1)并求解这五个参数。接下来取四个点之间(t =⅛⅛⅜⅝),并测试它们是否位于同一个变换圆上。如果是的话,哇哇哇!,这是一个变形的圆圈(的一部分)。

之前和之后可能是另一个arcarcn。这些椭圆是一样的吗?如果是,并且对接角度接触,则将各部分的描述连接在一起。