修剪包含闭环

时间:2015-06-06 18:11:29

标签: algorithm geometry

我在matplotlib中绘制数据,用户可以通过套索进行交互,套索内部表示为构成多边形链的顶点列表。我想要做的是修剪套索,如这个高度专业的图片所示: enter image description here

形式上,我在欧几里德平面上有一个顶点列表,我想在列表中找到两个不相邻的顶点,以便

  • 它们在x和y方向上的差异都低于某个阈值(我想的是在我正在套索的图中可见的轴范围的1%)

  • 列表中位于它们之间的顶点数量最大化

如果不存在这样的对,则套索将被完全丢弃。

天真的方法是O(n^2),其中n是列表的长度:对于每个顶点数1 ≤ i ≤ n - 2,请确切地检查每对顶点i索引,对于存在合适对的最高i,选择找到的第一对。

这甚至可以容忍复杂性,因为一些基本测试表明套索很少超过500个顶点,并且可能完全排除超过大约1000个顶点。不过,在我看来,我应该能做得比这更好。

是否存在小于O(n ^ 2)的算法并找到所需对的索引(如果存在)?

1 个答案:

答案 0 :(得分:1)

  1. 您可以测试线路交叉点

    这是O(n^2)如果你将线条细分为细分,那么复杂性变得更好看

    对于更复杂的这种方法和其他方法,然后O(n^2)

  2. 您可以利用循环周期

    闭环类似于圆圈,因此x轴经过两次相同的点,y轴也是如此。所以请记住,开始x,y循环遍历所有行,当xy靠近起点时(只有单轴而不是最先出的一个展位)然后停止。

    这是O(n)现在只测试您从起点到某个阈值距离停止和前进的线周围的交点。如果没有找到交叉点再次与另一个轴停止点一起执行此操作。测试仍为O(平方公尺),但这次是m<<n

  3. 您可以整合角度

    计算中心作为平均点O(n)现在循环开始和计算角度的线,它在套索圆(从中心)覆盖,将其添加到某个变量,并在达到360度时停止。从最后做同样的事。

    现在你选择了循环的内部部分,所以只测试从停止点到套索的开始/停止的行

    example

  4. [edit1]今天有点时间/心情,所以这里有更详细的子弹2

    1. 计算套索的边界框

      需要min,maxx,y坐标让我们调用x0<=x1,y0<=y1这可以通过O(n)

    2. 中的单个循环轻松完成
    3. 为每个坐标分配和计算行计数器

      如果浮动坐标截断为某个网格/比例,则需要2个数组int cntx[x1-x0+1],cnty[y1-y0+1]。不要忘记将值清零。对于套索的任何渲染pixel(x,y),计算都很容易。

      增量计数器cntx[x-x0]++; cnty[y-y0]++;不会在端点上递增(以避免重复增量)。这也是O(n)

    4. 从头开始找到每个坐标超过2行的第一行

      让它为i0

    5. 找到行数较少或相等的下一行,然后找到每个坐标2行

      让它为i1

    6. 从结尾处执行相同操作并调用索引j0,j1

    7. 找到交叉点

      现在您需要检查来自<i0,i1>的任意行与来自<j0,j1>的任何行之间的交叉点O(n^2)

    8. 这是一个例子:

      example

      我手绘的套索 svg 编辑器(遗憾的是 SO 不支持 svg 图片)。在左侧,您会看到蓝线(行数&gt; 2),在侧面有cntx,cnty内容的图表。右侧是红色

      的计算选择(i0,i1),(j0,j1)

      C ++中的一些源代码:

      int i,i0,i1,j0,j1;
      int a,x,y,x0,y0,x1,y1;
      int *cntx,*cnty;
      List<int> pnt;  // laso points (x,y,flag)
      
      // bounding box O(n) and reset flags
      x=pnt[0]; x0=x; x1=x;
      y=pnt[1]; y0=y; y1=y;
      for (i=0;i<pnt.num;)
          {
          x=pnt[i]; i++;
          y=pnt[i]; i++;
          pnt[i]=0; i++;
          if (x0>x) x0=x;
          if (x1<x) x1=x;
          if (y0>y) y0=y;
          if (y1<y) y1=y;
          }
      // allocate and compute counter buffers (count how many line at x or y coordinate)
      cntx=new int[x1-x0+1];
      cnty=new int[y1-y0+1];
      for (x=x0;x<=x1;x++) cntx[x-x0]=0;
      for (y=y0;y<=y1;y++) cnty[y-y0]=0;
      x=pnt[0];
      y=pnt[1];
      for (i=0;i<pnt.num;)
          {
          a=pnt[i]; i++;
          for (;x<a;x++) cntx[x-x0]++;
          for (;x>a;x--) cntx[x-x0]++; x=a;
          a=pnt[i]; i++; i++;
          for (;y<a;y++) cnty[y-y0]++;
          for (;y>a;y--) cnty[y-y0]++; y=a;
          }
      // select sections with 3 lines (trimable)
      for (i=0;i<pnt.num;)
          {
          x=pnt[i]; i++;
          y=pnt[i]; i++;
          if ((cntx[x-x0]>2)||(cnty[y-y0]>2)) pnt[i]=1; i++;
          }
      // select trim areas from start (i0,i1) and from end (j0,j1)
      for (i=0      ;i<pnt.num;) { i0=i; i++; i++; a=pnt[i]; i++; if ( a) break; }
      for (         ;i<pnt.num;) { i1=i; i++; i++; a=pnt[i]; i++; if (!a) break; }
      for (i=pnt.num;i>0      ;) { i--; a=pnt[i]; i--; i--; j1=i; if ( a) break; }
      for (         ;i>0      ;) { i--; a=pnt[i]; i--; i--; j0=i; if (!a) break; }
      delete[] cntx;
      delete[] cnty;
      
      • pnt[pnt.num]是我的动态数组,包含每个顶点的laso坐标和标志
      • pnt[0]=x(0),pnt[1]=y(0),pnt[2]=flag(0),pnt[3]=x(1),...

        i0,i1,j0,j1的末尾包含可选择的点/线选择,因此它是至少存在3条平行线的部分,并且从开始和结束的第一条线中选择。希望现在已经足够清楚了。