LibGDX绘制弧线曲线

3

Libgdx的弧函数不是绘制弧形,而是绘制一个饼图(即有两条线连接到弧的起点)。

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

有没有解决方案可以让libgdx绘制类似于html5画布弧线函数的弧曲线?
4个回答

4
阅读源代码,这似乎是内置行为:
/** 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);           <b>&lt--- CENTER</b>
        renderer.color(colorBits);
        renderer.vertex(x + cx, y + cy, 0); <b><--- LINE TO START POINT</b>
        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); <b><-- LINE TO END POINT</b>
...

最简单的方法可能是复制整个函数,至少删除我标记的两行:CENTERLINE TO END POINT,以及每行前面的 renderer.color(.. 代码。
(您也可以删除 LINE TO START POINT - 它似乎设置曲线的起点,但实际上在循环内部也完成了这一点,因此是多余的。)
该函数有第二部分用于填充“弧形”(我同意,它应该被正确地命名为“饼图”或“楔形图”),但是您不需要在这里使用它,因为它会完全相同。
如果您使其正常工作,可以将其提交给libgdx的维护者,例如在libgdx贡献论坛上提出。

谢谢,这肯定比我之前考虑的贝塞尔曲线方法要容易得多。 - ejectamenta
1
似乎循环内的所有renderer.color(colorBits);语句都可能是不必要的,或者每个render.vertex之前需要有一个renderer.colour语句(奇怪)。 - ejectamenta
@ejectamenta: 是的,我也注意到了。如果存在某种缓存机制,可以使得绘制行的顺序错乱,那么这 可能 是必要的,但是要弄清楚这一点,你需要深入源代码进行研究。 - Jongware

3
最终,我子类化了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的问题。

2
请注意,您的类没有正确遵守 arc.setColor() 指令。color 的值应该通过调用 setColor() 进行更新。不过做得很好。我已经向您的源代码中添加了一个可工作的实现。 - EntangledLoops

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 一样),因此需要三个顶点才能绘制一个三角形。在这里,我们逐个绘制每个线段。每个线段由两个三角形组成,形成一个矩形。足够多的矩形会开始看起来像一个平滑的圆形。
内置 ShapeRenderer.arc 的绘制方式很像一个馅饼。如果你有足够多带有直边的片段,它就会开始看起来像一个完整的馅饼,而不是一堆三角形到中心的聚集。
我希望这使得其他人可以绘制自己的自定义形状并从中学习!

1

我认为你和其他回答者已经发布了当前最佳解决方案。对于任何人的另一种“hack”:

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

可以在绘制的两条线上方渲染相同颜色的背景线,以便在之后覆盖它们。是的,这很差劲,但是是快速而简单的小型代码解决方案。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接