【问题标题】:Polylines outline construction / Drawing thick polylines多段线轮廓构造/绘制粗多段线
【发布时间】:2016-04-08 00:26:45
【问题描述】:

我面临一项任务,我必须使用多边形绘制折线。 作为输入参数,我有一个点数组和厚度值。见下图。

我有形成黑色折线和厚度的点,例如10像素。现在我需要计算点并构造一条蓝色折线以形成一个多边形,然后进行渲染。

有一些与此相关的文章:

但我发现它们有点复杂且难以理解。 没有任何现有的库或更简单的算法来实现这一点。不需要圆形接头。我正在使用 java 和 libGDX。

【问题讨论】:

  • 你不能简单地这样:对于每个黑点:首先,创建一个伪法线,它是该点的入射线的法线的平均值。其次,将伪法线归一化并乘以厚度。三、将其与黑点相加,得到对应的蓝点。
  • 蓝线和黑线是什么关系?
  • @gpasch 我使用颜色只是为了显示给定的黑点数组,并根据它们和厚度我需要确定蓝点数组才能形成多边形
  • 好的,蓝点是如何定义的:你能用文字描述吗?我们只是将黑线向上移动吗?我们旋转吗?什么?我无法想象蓝点一定有数学描述
  • @gpasch 蓝色点应绘制在黑色点上方(线段应平行)。这样多边形的厚度将是恒定的,并且可以正确绘制线段的关节

标签: java algorithm graphics libgdx graphics2d


【解决方案1】:

算法如下:

for each line:
  find the parallel line upwards:
    find the perpendicular: has a slope m2 in approximate
    check which side is right (compare angles)
    find the two points of the parallel line by solving a small equation problem (A, B, C)
  if this line is the first one keep it (l1)
  else find the intersection with the previous line (l1, l2): this will give the next articulation point

黄线是你想要的;红线是一般的平行线。 关节点为绿色。您可以在某些组件中打印此缓冲图像。

注意:多边形的宽度不能像您意识到的那样固定,因为在关节点处距离会更大。保证的是线段之间的距离是恒定的。

int[] approximate(int[] p, int[] p2, double dr, int l) { // l is the distance, dr is 0 for the beginning of the segment and 1 for the end
          double d=Math.sqrt(Math.pow(p[0]-p2[0], 2)+Math.pow(p[1]-p2[1], 2));
          double ix=p[0]+dr*(p2[0]-p[0]), iy=p[1]+dr*(p2[1]-p[1]);
          double x1=0, x2=0, y1=0, y2=0;
          if(p2[0]==p[0]) {
            x1=ix+l; x2=ix-l; y1=iy; y2=iy;
          }
          else {
          double m=1.0*(p2[1]-p[1])/(p2[0]-p[0]);
          if(Math.abs(m)==0) {
            x1=ix; x2=ix; y1=iy+l; y2=iy-l;
          }
          else {
            double m2=-1/m;
            double c=iy-m2*ix;
            double A=1+m2*m2, B=-2*(ix-m2*c+m2*iy), C=ix*ix+iy*iy+c*c-2*c*iy-l*l;
            x1=(-B+Math.sqrt(B*B-4*A*C))/(2*A); x2=(-B-Math.sqrt(B*B-4*A*C))/(2*A); y1=m2*x1+c; y2=m2*x2+c;
          }
          }
          int[] cp1={p2[0]-p[0], p2[1]-p[1]}, cp2={(int)x1-p[0], (int)y1-p[1]}, xy=new int[2];
          int cpp=compareAngles(cp1, cp2);
          if(cpp>0) { xy[0]=(int)x1; xy[1]=(int)y1; } else { xy[0]=(int)x2; xy[1]=(int)y2; }
          return xy;
  }

  void poly() {
    int[][] p={{100, 400}, {110, 440}, {250, 300}, {350, 400}, {300, 310}};
    BufferedImage bim=new BufferedImage(500, 500, BufferedImage.TYPE_INT_RGB);
    Graphics2D g=(Graphics2D)bim.getGraphics();
    g.setColor(Color.white);
    g.fillRect(0, 0, 500, 500);
    g.setStroke(new BasicStroke(5f));
    g.setColor(Color.black);
    Line2D.Double l1=new Line2D.Double(), l2=new Line2D.Double();
    int[] currentp=new int[2], lastp=new int[2];
    for(int i=0; i<p.length-1; i++) {
      g.setColor(Color.black);
      g.drawLine(p[i][0], p[i][1], p[i+1][0], p[i+1][1]);
      int[] p1=approximate(p[i], p[i+1], 0, 10), p2=approximate(p[i], p[i+1], 1, 10);
      g.setColor(Color.red);
      g.drawLine(p1[0], p1[1], p2[0], p2[1]);
      if(i==0) { l1=new Line2D.Double(p1[0], p1[1], p2[0], p2[1]); currentp[0]=p1[0]; currentp[1]=p1[1]; }
      else {
        l2=new Line2D.Double(p1[0], p1[1], p2[0], p2[1]);
        int[] pi=intersectionPoint(l1, l2);
        g.setColor(Color.green);
        g.fillOval(pi[0], pi[1], 5, 5);
        g.setColor(Color.yellow);
        g.drawLine(currentp[0], currentp[1], pi[0], pi[1]);
        currentp[0]=pi[0]; currentp[1]=pi[1];
        l1.setLine(l2);
      }
      if(i==p.length-2) { lastp[0]=p2[0]; lastp[1]=p2[1]; }
    }
    g.setColor(Color.yellow);
    g.drawLine(currentp[0], currentp[1], lastp[0], lastp[1]);
  }

  public int[] intersectionPoint(Line2D.Double l1, Line2D.Double l2) {
    return intersectionPoint((int)l1.getX1(), (int)l1.getY1(), (int)l1.getX2(), (int)l1.getY2(), (int)l2.getX1(), (int)l2.getY1(), (int)l2.getX2(), (int)l2.getY2());
  }

  public int[] intersectionPoint(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) {
    int[] xy={(int)(1.0*((x1*y2-y1*x2)*(x3-x4)-(x1-x2)*(x3*y4-y3*x4))/((x1-x2)*(y3-y4)-(y1-y2)*(x3-x4))),
                (int)(1.0*((x1*y2-y1*x2)*(y3-y4)-(y1-y2)*(x3*y4-y3*x4))/((x1-x2)*(y3-y4)-(y1-y2)*(x3-x4)))};
    return xy;
  }


  public int compareAngles(int[] a, int[] b) {
    int cp=a[0]*b[1]-a[1]*b[0];
    return -cp;
  }

【讨论】:

  • 太棒了!这就是我所需要的。感谢您的宝贵时间!
【解决方案2】:

我不太确定你为什么要在框架中实现一些高级图形算法,而首要任务是轻松渲染事物:)

Libgdx 具有内置的ShapeRenderer,可让您绘制简单的形状。您所要做的就是根据thickness 计算新顶点并将它们传递给ShapeRenderer 以绘制圆和连接这些圆的线。

为了让它变得超级简单,你甚至可以使用

    rectLine(float x1, float y1, float x2, float y2, float width)

允许您绘制给定粗细线的方法。因此,您只需要遍历点并绘制所有线条,就像在这个伪代码中一样:

    for point in points:
        if thereIsANextPoint():
            next = getNextPoint()

            sr.rectLine(point.x, point.y, next.x, next .y, thickness)

我在上面附上的reference 中包含了有关如何使用 ShapeRenderer 的精彩描述


我想点之间的连接可能会有一点问题(例如,因为不同的角度)我认为在这个连接上方渲染圆圈将是一个很好的解决方法:)

【讨论】:

  • 我不能使用 ShapeRenderer.rectLine(),因为线段的关节有问题,而且我需要用渐变为顶点着色并对其应用纹理。所以我决定使用网格,现在我正在寻找一种算法来计算这些点以形成一个多边形。
猜你喜欢
  • 2021-07-26
  • 2013-08-16
  • 2015-03-23
  • 1970-01-01
  • 2019-06-27
  • 2013-06-05
  • 1970-01-01
  • 1970-01-01
  • 2021-12-14
相关资源
最近更新 更多