【问题标题】:Extract vertices from an ARC in generic way以通用方式从 ARC 中提取顶点
【发布时间】:2015-04-25 10:55:37
【问题描述】:

我想从 ARC 中获取所有顶点。我有用于绘制弧线的所有数据(例如:起点、终点、起点角度、终点角度、半径),但我需要从弧线数据中生成所有顶点。

我已经尝试了一种或两种算法,但我未能从弧数据中获取确切的顶点。

我使用了 Bresenham 的算法,但失败了。

现在我正在使用下面的代码,但它不起作用..

            double theta = 2 * 3.1415926 / 100; 
            double c = Math.cos(theta);
            double s = Math.sin(theta);
            double t;

            double x = ((ArcTo) element).getRadius();//we start at angle = 0 
            double y = 0; 

            for(int ii = 0; ii < 100; ii++) { 
              coordinates.add(new Coordinate(x + element.getCenterPoint().getX(), y + element.getCenterPoint().getY()));//output vertex 

              //apply the rotation matrix
              t = x;
              x = c * x - s * y;
              y = s * t + c * y;
            }

请帮助我。谢谢。

【问题讨论】:

  • 最简单的方法是使用 Bresenham 算法。
  • @EgorSkriptunoff 感谢您的回复。我已经尝试过了,但它不适用于 arc。
  • 发布您的代码并实现 Bresenham 算法。说出究竟是什么不起作用。
  • @EgorSkriptunoff 你可以在这里找到我的代码 - jsfiddle.net/shiladittya/qb4528cq。它画了一个圆,但我想画一个圆弧而不是创建一个圆。我拥有绘制弧线所需的所有变量。

标签: javascript algorithm geometry bresenham rasterize


【解决方案1】:
  1. 首先澄清一下

    我假设 顶点 是指像素,而 ARC 是标准 2D 圆弧 (不是椭圆弧!!! ) 并且您的输入数据是:

    int (x0,y0),(x1,y1) // star/end points on curve !!!
    float a0,a1         // start end angles [rad]
    int (xc,yc)         // center of circle
    int r               // radius
    
  2. 不要使用布雷森汉姆

    因为您需要从零角度开始计算所有像素,直到到达起点。然后翻转绘制标志,以便您从该点开始填充像素并在端点命中时停止。您还需要处理绕组以匹配 ARC 方向。

  3. 你可以使用圆参数方程

    // just some arc data to test with
    float r=25.0;
    float a0= 45.0*M_PI/180.0;
    float a1=270.0*M_PI/180.0;
    int xc=100,x0=xc+floor(r*cos(a0)),x1=xc+floor(r*cos(a1));
    int yc=100,y0=yc+floor(r*sin(a0)),y1=yc+floor(r*sin(a1));
    // arc rasterize code
    int x,y;
    float a,da;
    // here draw pixels x0,y0 and x1,y1 to avoid rounding holes ...
    if (r) da=0.75/float(r); else da=0.1; // step slightly less then pixel to avoid holes
    for (a=a0;;a+=da)
     {
     x=xc+int(floor(r*cos(a)));
     y=yc+int(floor(r*sin(a)));
     // here draw pixel x,y
     if ((x==x1)&&(y==y1)) // stop if endpoint reach
      if (fabs(a-a1)<da)   // but ignore stop if not at end angle (full or empty circle arc)
       break;
     }
    

    可能是round 而不是floor 会有更少的像素位置误差。如果您的端点不匹配,那么这将无限循环。如果你稍微调整一下结束条件,你甚至可以避免这种情况,或者像我一样从a1 重新计算x1,y1 ...

  4. 您可以使用公式(x-xc)^2+(y-yc)^2=r^2

    您需要将 ARC 划分为象限,并将每个象限作为单独的弧循环处理,循环通过 xy 并计算另一个坐标。循环遍历变化更大的坐标

    蓝色区域循环y,红色循环x。例如红色区号可以是这样的:

    int x,y;
    for (x=_x0;;x++)
     {
     y=sqrt((r*r)-((x-xc)*(x-xc)));
     // here draw pixel x,y
     if (x==_x1) // stop if endpoint reach
      break;
     }
    

    您需要计算象限内ARC切割部分的(_x0,_y0),(_x1,_y1)起点终点,并制作_x0&lt;=_x1


    _x 循环起点/终点坐标的值将是 xc +/- sqrt(r)x0x1
    _y 循环起点/终点坐标的值将是 yc +/- sqrt(r)y0y1

    蓝色部分以类似的方式完成(只需交换/替换 xy)。由于切割,这种方法有点复杂,但只能在整数上完成。 sqrt 可以通过LUT(限制最大半径)加速,^2 也可以进一步优化。

[备注]

所以如果我概括一下参数方程是最容易实现但最慢的。然后是 sqrt 方法,它可以像 Bresenham 一样快(使用 LUT 可能更快),但需要将 ARC 切割成需要很少的象限的代码渲染前ifs。

所有代码都在 C++ 中,并且可以进一步改进,例如避免一些 int/float 转换、在循环之前预先计算一些值等等......

最后一个去布雷森纳姆,但你需要改变里面的一些东西,当你不知道自己在做什么时,你很容易迷路。它还需要削减到八分圆,因此更改的复杂性远大于sqrt 方法

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-06-19
    • 2020-03-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-08
    • 2013-08-03
    • 2013-04-25
    相关资源
    最近更新 更多