简体   繁体   English

如何在libGDX中的填充多边形周围绘制精确的边框?

[英]How to draw precise borders around filled polygons in libGDX?

I'm trying to draw a smaller filled polygon (which are always convex not self-intersecting quadrangles in my case) over another filled polygon to get an impression of a border around that filled polygon. 我试图在另一个填充的多边形上绘制一个较小的填充的多边形(在我的情况下,它们总是凸的而不是自相交的四边形),以使该填充的多边形周围有边框的印象。 To do that I've found out how to calculate the centroid of quadrangle - it will be a point around which the smaller filled polygon will be scaled down. 为此,我发现了如何计算四边形的质心-将围绕该点缩小较小的填充多边形。 I've copied the algorithm from here: https://math.stackexchange.com/a/2878092 我已经从此处复制了算法: https : //math.stackexchange.com/a/2878092

And here's a full code of the class: 这是该类的完整代码:

public class BodyPartActor extends Actor {
    private static TextureRegion textureRegion = Globals.getTextureRegion();
    private static Color borderColor = Assets.getSkin().getColor("pink");
    private static Color bodyPartColor = Assets.getSkin().getColor("green");
    private static short[] triangles = new short[] {
            0, 1, 2,         // Two triangles using vertex indices.
            0, 2, 3          // Take care of the counter-clockwise direction.
    };
    private PolygonRegion polygonRegion;
    private BodyPart bodyPart;
    private Vector2 centroid;

    public BodyPartActor(BodyPart bodyPart) {
        this.bodyPart = bodyPart;
        polygonRegion = new PolygonRegion(textureRegion, this.bodyPart.getVertices(), triangles);
        centroid = getCentroidOfQuadrangle();
    }

    private Vector2 getCentroidOfQuadrangle() {
        float[] v = bodyPart.getVertices();
        Vector2 centroidA = getCentroidOfTriangle(new float[]{v[0], v[1], v[2], v[3], v[4], v[5]});
        Vector2 centroidB = getCentroidOfTriangle(new float[]{v[2], v[3], v[4], v[5], v[6], v[7]});
        Vector2 centroidC = getCentroidOfTriangle(new float[]{v[4], v[5], v[6], v[7], v[0], v[1]});
        Vector2 centroidD = getCentroidOfTriangle(new float[]{v[6], v[7], v[0], v[1], v[2], v[3]});
        return getPointOfLinesIntersection(centroidA, centroidC, centroidB, centroidD);
    }

    private Vector2 getCentroidOfTriangle(float[] vertices) {
        return new Vector2 (
                (vertices[0] + vertices[2] + vertices[4]) / 3,
                (vertices[1] + vertices[3] + vertices[5]) / 3
        );
    }

    private Vector2 getPointOfLinesIntersection(Vector2 startLineA, Vector2 endLineA, Vector2 startLineB,
                                                Vector2 endLineB) {
        float detOfLineA = det2x2(startLineA.x, startLineA.y, endLineA.x, endLineA.y);
        float detOfLineB = det2x2(startLineB.x, startLineB.y, endLineB.x, endLineB.y);

        float xDeltaOfLineA = startLineA.x - endLineA.x;
        float xDeltaOfLineB = startLineB.x - endLineB.x;
        float yDeltaOfLineA = startLineA.y - endLineA.y;
        float yDeltaOfLineB = startLineB.y - endLineB.y;

        float xNom = det2x2(detOfLineA, xDeltaOfLineA, detOfLineB, xDeltaOfLineB);
        float yNom = det2x2(detOfLineA, yDeltaOfLineA, detOfLineB, yDeltaOfLineB);
        float deNom = det2x2(xDeltaOfLineA, yDeltaOfLineA, xDeltaOfLineB, yDeltaOfLineB);

        return new Vector2(xNom / deNom, yNom / deNom);
    }

    /**
     * Calculate determinant of a 2x2 matrix:
     * |a b|
     * |c d|
     */
    private float det2x2(float a, float b, float c, float d) {
        return (a * d) - (b * c);
    }

    @Override
    public void draw(Batch batch, float parentAlpha) {
        float x = bodyPart.getBody().getPosition().x;
        float y = bodyPart.getBody().getPosition().y;
        float width = polygonRegion.getRegion().getRegionWidth();
        float height = polygonRegion.getRegion().getRegionHeight();
        float originX = centroid.x;
        float originY = centroid.y;
        float rotation = calculateRotation(originX, originY);

        PolygonSpriteBatch polygonSpriteBatch = (PolygonSpriteBatch) batch;
        polygonSpriteBatch.setColor(borderColor.r, borderColor.g, borderColor.b, borderColor.a * parentAlpha);
        polygonSpriteBatch.draw(polygonRegion, x, y, originX, originY, width, height, 1f, 1f, rotation);
        polygonSpriteBatch.setColor(bodyPartColor.r, bodyPartColor.g, bodyPartColor.b, bodyPartColor.a * parentAlpha);
        polygonSpriteBatch.draw(polygonRegion, x, y, originX, originY, width, height, 0.9f, 0.9f, rotation);
    }

    private float calculateRotation(float originX, float originY) {
    // How to calculate when originX and originY are the center of quadrangle???
        return bodyPart.getBody().getAngle() * MathUtils.radiansToDegrees; // Works only if originX and originY are 0.
    }
}

It seems to the calculate centroid correctly, I checked it here : http://eguruchela.com/math/Calculator/polygon-centroid-point but the resulting borders doesn't look okay . 看来正确计算了质心,我在这里检查了一下http : //eguruchela.com/math/Calculator/polygon-centroid-point,但是生成的边框看起来不太好 I even thought there's some floating-point arithmetic issues in libGDX when calculating vertices positions but I couldn't found any, so I don't know where's the problem. 我什至以为libGDX在计算顶点位置时存在一些浮点算术问题,但我找不到任何问题,所以我不知道问题出在哪里。 There's also an issue with rotation, because now I'm drawing with origin point at the centroid of quadrangle, so it doesn't render properly (before trying to draw a border, originX and originY were 0, which made correct rendering rotated bodies correct, now it doesn't align how it should). 旋转还有一个问题,因为现在我将原点绘制在四边形的质心处,所以它无法正确渲染(在尝试绘制边界之前,originX和originY为0,这使得正确渲染旋转的物体正确) ,现在不符合应有的状态)。

Do you know how to render properly this border and with proper rotation of the polygon? 您知道如何正确渲染此边框以及如何正确旋转多边形吗? Or maybe the whole approach to this problem is wrong and there's a better way to do that? 也许解决此问题的整个方法是错误的,并且有更好的方法可以做到这一点?

Ok, I have it working now. 好的,我现在可以正常工作了。 I used this algorithm: https://math.stackexchange.com/a/2824263 (Method 2). 我使用了以下算法: https : //math.stackexchange.com/a/2824263 (方法2)。 Here's how it looks now . 这是现在的样子 It's not 100% stable though. 但是它不是100%稳定的。 Some very flat quadrangles are buggy, it's rare if border isn't thick, but if it is it can look like this and even worse when border thickness is setted higher. 一些非常平坦的四边形是越野车,如果边框不粗则很少见,但如果边框不粗则看起来像这样 ,而将边框粗度设置得更高则更糟。 But for my thin borders it's good enough. 但是对于我的薄边框来说,这已经足够了。 Here's the code of the class now: 现在是该类的代码:

public class BodyPartActor extends Actor {
    private static TextureRegion textureRegion = Globals.getTextureRegion();
    private static Color borderColor = Assets.getSkin().getColor("bodyPartBorder");
    private static Color fillColor = Assets.getSkin().getColor("bodyPartFill");
    private static short[] triangles = new short[] {
            0, 1, 2,         // Two triangles using vertex indices.
            0, 2, 3          // Take care of the counter-clockwise direction.
    };
    private static float borderThickness = 0.1f;
    private PolygonRegion polygonRegionBorder;
    private PolygonRegion polygonRegionFill;
    private BodyPart bodyPart;

    public BodyPartActor(BodyPart bodyPart) {
        this.bodyPart = bodyPart;
        float[] vertices = this.bodyPart.getVertices();
        polygonRegionBorder = new PolygonRegion(textureRegion, vertices, triangles);
        polygonRegionFill = new PolygonRegion(textureRegion, calculateInnerVertices(vertices), triangles);
    }

    /**
     * @param v vertices of the outer quadrangle
     */
    private float[] calculateInnerVertices(float[] v) {
        Vector2 vA = calculateInnerVertex(v[6], v[7], v[0], v[1], v[2], v[3]);
        Vector2 vB = calculateInnerVertex(v[0], v[1], v[2], v[3], v[4], v[5]);
        Vector2 vC = calculateInnerVertex(v[2], v[3], v[4], v[5], v[6], v[7]);
        Vector2 vD = calculateInnerVertex(v[4], v[5], v[6], v[7], v[0], v[1]);
        return new float[]{vA.x, vA.y, vB.x, vB.y, vC.x, vC.y, vD.x, vD.y};
    }

    /**
     * Positive borderThickness value will produce vertex that is offseted to the inner side of the provided quadrangle.
     * Negative borderThickness value will produce vertex that is on the outer side of the quadrangle.
     */
    private Vector2 calculateInnerVertex(float prevX, float prevY, float curX, float curY, float nextX, float nextY) {
        Vector2 v = new Vector2(nextX - curX, nextY - curY);
        Vector2 w = new Vector2(prevX - curX, prevY - curY);
        Vector2 u = v.cpy().scl(w.len()).add(w.cpy().scl(v.len()));
        float absDetVW = Math.abs((v.x * w.y) - (v.y * w.x));
        return new Vector2(curX, curY).add(u.scl(borderThickness / absDetVW));
    }

    @Override
    public void draw(Batch batch, float parentAlpha) {
        float x = bodyPart.getBody().getPosition().x;
        float y = bodyPart.getBody().getPosition().y;
        float rotation = bodyPart.getBody().getAngle() * MathUtils.radiansToDegrees;

        PolygonSpriteBatch polygonSpriteBatch = (PolygonSpriteBatch) batch;
        polygonSpriteBatch.setColor(borderColor.r, borderColor.g, borderColor.b, borderColor.a * parentAlpha);
        polygonSpriteBatch.draw(polygonRegionBorder, x, y, 0f, 0f, 1f, 1f, 1f, 1f, rotation);
        polygonSpriteBatch.setColor(fillColor.r, fillColor.g, fillColor.b, fillColor.a * parentAlpha);
        polygonSpriteBatch.draw(polygonRegionFill, x, y, 0f, 0f, 1f, 1f, 1f, 1f, rotation);
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM