Как нарисовать точные границы вокруг заполненных многоугольников в libGDX? - PullRequest
0 голосов
/ 25 октября 2018

Я пытаюсь нарисовать меньший заполненный многоугольник (который в моем случае всегда является выпуклым, а не самопересекающимся четырехугольником) поверх другого заполненного многоугольника, чтобы получить впечатление границы вокруг этого заполненного многоугольника.Для этого я узнал, как рассчитать центроид четырехугольника - это будет точка, вокруг которой будет уменьшен заполненный меньший многоугольник.Я скопировал алгоритм отсюда: https://math.stackexchange.com/a/2878092

И вот полный код класса:

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.
    }
}

Кажется, правильно вычислить центроид, я проверил егоздесь : http://eguruchela.com/math/Calculator/polygon-centroid-point но результирующие границы не выглядят хорошо .Я даже думал, что есть некоторые арифметические проблемы с плавающей точкой в ​​libGDX при вычислении позиций вершин, но я не смог их найти, поэтому я не знаю, в чем проблема.Также есть проблема с вращением, потому что теперь я рисую с точкой начала координат в центре четырехугольника, поэтому он не рендерится должным образом (перед попыткой нарисовать границу, originX и originY были 0, что сделало правильный рендеринг повернутых тел правильным, теперь он не выравнивается так, как должен).

Знаете ли вы, как правильно отрисовать эту границу и с правильным вращением многоугольника?Или, может быть, весь подход к этой проблеме неверен, и есть лучший способ сделать это?

1 Ответ

0 голосов
/ 26 октября 2018

Хорошо, у меня сейчас работает.Я использовал этот алгоритм: https://math.stackexchange.com/a/2824263 (метод 2). Вот как это выглядит сейчас .Это не на 100% стабильно, хотя.Некоторые очень плоские четырехугольники содержат ошибки, это редко, если граница не толстая, но если она равна , она может выглядеть так и даже хуже, если толщина границы установлена ​​выше.Но для моих тонких границ это достаточно хорошо.Вот код класса сейчас:

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);
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...