【问题标题】:libgdx drawing arc curvelibgdx 绘制圆弧曲线
【发布时间】:2015-06-07 22:58:15
【问题描述】:

libgdx 的 arc 函数不是画圆弧而是画一个饼段(即有 2 条线连接到圆弧的原点)

shapeRenderer.begin(ShapeType.Line);
shapeRenderer.arc(x, y, radius, 30, 120);
shapeRenderer.end();

有没有办法解决这个问题,让libgdx可以画出类似于html5 canvas arc函数的圆弧曲线?

【问题讨论】:

    标签: java libgdx


    【解决方案1】:

    最后我将 ShapeRenderer 类归为子类

    public class Arc extends ShapeRenderer{
    
        private final ImmediateModeRenderer renderer;
        private final Color color = new Color(1, 1, 1, 1);
    
        public Arc(){
            renderer = super.getRenderer();
        }
    
        /** Draws an arc using {@link ShapeType#Line} or {@link ShapeType#Filled}. */
        public void arc (float x, float y, float radius, float start, float degrees) {
        int segments = (int)(6 * (float)Math.cbrt(radius) * (degrees / 360.0f));
    
        if (segments <= 0) throw new IllegalArgumentException("segments must be > 0.");
        float colorBits = color.toFloatBits();
        float theta = (2 * MathUtils.PI * (degrees / 360.0f)) / segments;
        float cos = MathUtils.cos(theta);
        float sin = MathUtils.sin(theta);
        float cx = radius * MathUtils.cos(start * MathUtils.degreesToRadians);
        float cy = radius * MathUtils.sin(start * MathUtils.degreesToRadians);
    
        for (int i = 0; i < segments; i++) {
            renderer.color(colorBits);
            renderer.vertex(x + cx, y + cy, 0);
            float temp = cx;
            cx = cos * cx - sin * cy;
            cy = sin * temp + cos * cy;
            renderer.color(colorBits);
            renderer.vertex(x + cx, y + cy, 0);
        }
      }
    }
    

    然后这样称呼它

        Arc a = new Arc();
        a.setProjectionMatrix(cam.combined);
        a.begin(ShapeType.Line);
        a.arc(10, 10, 10, 30, 120);
        a.end();
    

    正确的角度很奇怪,不确定这是我的代码还是 libGDX

    【讨论】:

    • 请注意,您的班级没有正确遵守 arc.setColor() 指令。 color 的值应该通过调用setColor() 来更新。不过,干得好。刚刚添加了一个工作实现。到您的来源。
    【解决方案2】:

    这是一个使用 kotlin 扩展的简单解决方案。

    /** Draws an arc with 'stroke' of given width  */
    fun ShapeRenderer.strokeArc(strokeWidth: Float, x: Float, y: Float, radius: Float, start: Float, degrees: Float, sampling: Float = 2f, color: Color = Color.WHITE) {
        val segments = ((6 * Math.cbrt(radius.toDouble()) * (Math.abs(degrees) / 360.0f)) * sampling).toInt()
        val colorBits = color.toFloatBits()
    
        for (i in 0 until segments) {
            val x1 = radius * MathUtils.cosDeg(start + (degrees / segments) * i)
            val y1 = radius * MathUtils.sinDeg(start + (degrees / segments) * i)
    
            val x2 = (radius - strokeWidth) * MathUtils.cosDeg(start + (degrees / segments) * i)
            val y2 = (radius - strokeWidth) * MathUtils.sinDeg(start + (degrees / segments) * i)
    
            val x3 = radius * MathUtils.cosDeg(start + (degrees / segments) * (i + 1))
            val y3 = radius * MathUtils.sinDeg(start + (degrees / segments) * (i + 1))
    
            val x4 = (radius - strokeWidth) * MathUtils.cosDeg(start + (degrees / segments) * (i + 1))
            val y4 = (radius - strokeWidth) * MathUtils.sinDeg(start + (degrees / segments) * (i + 1))
    
            renderer.color(colorBits)
            renderer.vertex(x + x1, y + y1, 0f)
            renderer.color(colorBits)
            renderer.vertex(x + x3, y + y3, 0f)
            renderer.color(colorBits)
            renderer.vertex(x + x2, y + y2, 0f)
    
            renderer.color(colorBits)
            renderer.vertex(x + x3, y + y3, 0f)
            renderer.color(colorBits)
            renderer.vertex(x + x2, y + y2, 0f)
            renderer.color(colorBits)
            renderer.vertex(x + x4, y + y4, 0f)
        }
    }
    

    为了更好地理解这一点,以及为什么内置解决方案能做到这一点。 ShapeRenderer 绘制三角形(就像 OpenGL 一样)所以你需要 3 个顶点来绘制一个三角形。在这里,我们一次绘制一个片段。每段由 2 个三角形组成一个矩形。足够多的矩形开始看起来像一个平滑的圆形。

    内置 ShapeRenderer.arc 的绘制方式很像一个馅饼。如果你有足够多的直边切片,它开始看起来像一个完整的馅饼,而不是中心的一堆三角形。

    我希望这有一定的意义,并允许其他人绘制自己的自定义形状并为未来学习!

    【讨论】:

      【解决方案3】:

      阅读源代码,这似乎是内置行为:

      /** Draws an arc using {@link ShapeType#Line} or {@link ShapeType#Filled}. */
      public void arc (float x, float y, float radius, float start, float degrees, int segments) {
          if (segments <= 0) throw new IllegalArgumentException("segments must be > 0.");
          float colorBits = color.toFloatBits();
          float theta = (2 * MathUtils.PI * (degrees / 360.0f)) / segments;
          float cos = MathUtils.cos(theta);
          float sin = MathUtils.sin(theta);
          float cx = radius * MathUtils.cos(start * MathUtils.degreesToRadians);
          float cy = radius * MathUtils.sin(start * MathUtils.degreesToRadians);
      
          if (shapeType == ShapeType.Line) {
              check(ShapeType.Line, ShapeType.Filled, segments * 2 + 2);
      
              renderer.color(colorBits);
              renderer.vertex(x, y, 0);           &lt--- CENTER
              renderer.color(colorBits);
              renderer.vertex(x + cx, y + cy, 0); <--- LINE TO START POINT
              for (int i = 0; i < segments; i++) {
                  renderer.color(colorBits);
                  renderer.vertex(x + cx, y + cy, 0);
                  float temp = cx;
                  cx = cos * cx - sin * cy;
                  cy = sin * temp + cos * cy;
                  renderer.color(colorBits);
                  renderer.vertex(x + cx, y + cy, 0);
              }
              renderer.color(colorBits);
              renderer.vertex(x + cx, y + cy, 0); <-- LINE TO END POINT
      ...

      最简单的可能是复制整个函数,并且至少去掉我标记的两行:CENTERLINE TO END POINT,以及随附的 @ 987654325@ 各行上方。

      (您也可以删除 LINE TO START POINT - 它看起来是设置曲线的起点,但实际上也是在循环内部完成的,所以它是多余的.)

      该函数有一个填充“弧”的第二部分(我同意,它应该被正确命名为“饼”或“楔”),但你不需要在这里,因为它会完全一样。

      如果你让它工作,你可以将它推荐给 libgdx 的维护者,例如libgdx's Contributions Forum

      【讨论】:

      • 谢谢,肯定比我考虑的贝塞尔曲线方法容易得多
      • 似乎所有这些 renderer.color(colorBits);循环中的语句也可能是不必要的,或者每个 render.vertex 之前都需要一个 renderer.colour 语句(奇怪)
      • @ejectamenta:是的,我也注意到了这一点。这可能是必要的,如果涉及某种缓存,其中可能会乱序绘制线条,但要弄清楚这一点,您需要更深入地研究源代码。
      【解决方案4】:

      我认为您和其他响应者已经发布了当前的最佳解决方案。任何人的替代“黑客”

      1. 不太关心性能
      2. 具有静态背景

      将用相同的背景色线绘制 2 条线,以在之后覆盖它们。是的,这是蹩脚但又快又脏的小代码解决方案。

      【讨论】: