All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jbox2d.particle.VoronoiDiagram Maven / Gradle / Ivy

The newest version!
/*
 * The MIT License (MIT)
 *
 * FXGL - JavaFX Game Library
 *
 * Copyright (c) 2015-2017 AlmasB ([email protected])
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package org.jbox2d.particle;

import com.almasb.fxgl.core.math.Vec2;
import org.jbox2d.common.JBoxUtils;
import org.jbox2d.pooling.normal.MutableStack;

public class VoronoiDiagram {
    public static class Generator {
        final Vec2 center = new Vec2();
        int tag;
    }

    public static class VoronoiDiagramTask {
        int m_x, m_y, m_i;
        Generator m_generator;

        public VoronoiDiagramTask() {
        }

        public VoronoiDiagramTask(int x, int y, int i, Generator g) {
            m_x = x;
            m_y = y;
            m_i = i;
            m_generator = g;
        }

        public VoronoiDiagramTask set(int x, int y, int i, Generator g) {
            m_x = x;
            m_y = y;
            m_i = i;
            m_generator = g;
            return this;
        }
    }

    public static interface VoronoiDiagramCallback {
        void callback(int aTag, int bTag, int cTag);
    }

    private Generator[] m_generatorBuffer;
    private int m_generatorCount;
    private int m_countX, m_countY;
    // The diagram is an array of "pointers".
    private Generator[] m_diagram;

    public VoronoiDiagram(int generatorCapacity) {
        m_generatorBuffer = new Generator[generatorCapacity];
        for (int i = 0; i < generatorCapacity; i++) {
            m_generatorBuffer[i] = new Generator();
        }
        m_generatorCount = 0;
        m_countX = 0;
        m_countY = 0;
        m_diagram = null;
    }

    public void getNodes(VoronoiDiagramCallback callback) {
        for (int y = 0; y < m_countY - 1; y++) {
            for (int x = 0; x < m_countX - 1; x++) {
                int i = x + y * m_countX;
                Generator a = m_diagram[i];
                Generator b = m_diagram[i + 1];
                Generator c = m_diagram[i + m_countX];
                Generator d = m_diagram[i + 1 + m_countX];
                if (b != c) {
                    if (a != b && a != c) {
                        callback.callback(a.tag, b.tag, c.tag);
                    }
                    if (d != b && d != c) {
                        callback.callback(b.tag, d.tag, c.tag);
                    }
                }
            }
        }
    }

    public void addGenerator(Vec2 center, int tag) {
        Generator g = m_generatorBuffer[m_generatorCount++];
        g.center.x = center.x;
        g.center.y = center.y;
        g.tag = tag;
    }

    private final Vec2 lower = new Vec2();
    private final Vec2 upper = new Vec2();
    private MutableStack taskPool =
            new MutableStack(50) {
                @Override
                protected VoronoiDiagramTask newInstance() {
                    return new VoronoiDiagramTask();
                }

                @Override
                protected VoronoiDiagramTask[] newArray(int size) {
                    return new VoronoiDiagramTask[size];
                }
            };
    private final StackQueue queue = new StackQueue();

    public void generate(float radius) {
        assert (m_diagram == null);
        float inverseRadius = 1 / radius;
        lower.x = Float.MAX_VALUE;
        lower.y = Float.MAX_VALUE;
        upper.x = -Float.MAX_VALUE;
        upper.y = -Float.MAX_VALUE;
        for (int k = 0; k < m_generatorCount; k++) {
            Generator g = m_generatorBuffer[k];
            Vec2.minToOut(lower, g.center, lower);
            Vec2.maxToOut(upper, g.center, upper);
        }
        m_countX = 1 + (int) (inverseRadius * (upper.x - lower.x));
        m_countY = 1 + (int) (inverseRadius * (upper.y - lower.y));
        m_diagram = new Generator[m_countX * m_countY];
        queue.reset(new VoronoiDiagramTask[4 * m_countX * m_countX]);
        for (int k = 0; k < m_generatorCount; k++) {
            Generator g = m_generatorBuffer[k];
            g.center.x = inverseRadius * (g.center.x - lower.x);
            g.center.y = inverseRadius * (g.center.y - lower.y);
            int x = JBoxUtils.max(0, JBoxUtils.min((int) g.center.x, m_countX - 1));
            int y = JBoxUtils.max(0, JBoxUtils.min((int) g.center.y, m_countY - 1));
            queue.push(taskPool.pop().set(x, y, x + y * m_countX, g));
        }
        while (!queue.empty()) {
            VoronoiDiagramTask front = queue.pop();
            int x = front.m_x;
            int y = front.m_y;
            int i = front.m_i;
            Generator g = front.m_generator;
            if (m_diagram[i] == null) {
                m_diagram[i] = g;
                if (x > 0) {
                    queue.push(taskPool.pop().set(x - 1, y, i - 1, g));
                }
                if (y > 0) {
                    queue.push(taskPool.pop().set(x, y - 1, i - m_countX, g));
                }
                if (x < m_countX - 1) {
                    queue.push(taskPool.pop().set(x + 1, y, i + 1, g));
                }
                if (y < m_countY - 1) {
                    queue.push(taskPool.pop().set(x, y + 1, i + m_countX, g));
                }
            }
            taskPool.push(front);
        }
        int maxIteration = m_countX + m_countY;
        for (int iteration = 0; iteration < maxIteration; iteration++) {
            for (int y = 0; y < m_countY; y++) {
                for (int x = 0; x < m_countX - 1; x++) {
                    int i = x + y * m_countX;
                    Generator a = m_diagram[i];
                    Generator b = m_diagram[i + 1];
                    if (a != b) {
                        queue.push(taskPool.pop().set(x, y, i, b));
                        queue.push(taskPool.pop().set(x + 1, y, i + 1, a));
                    }
                }
            }
            for (int y = 0; y < m_countY - 1; y++) {
                for (int x = 0; x < m_countX; x++) {
                    int i = x + y * m_countX;
                    Generator a = m_diagram[i];
                    Generator b = m_diagram[i + m_countX];
                    if (a != b) {
                        queue.push(taskPool.pop().set(x, y, i, b));
                        queue.push(taskPool.pop().set(x, y + 1, i + m_countX, a));
                    }
                }
            }
            boolean updated = false;
            while (!queue.empty()) {
                VoronoiDiagramTask front = queue.pop();
                int x = front.m_x;
                int y = front.m_y;
                int i = front.m_i;
                Generator k = front.m_generator;
                Generator a = m_diagram[i];
                Generator b = k;
                if (a != b) {
                    float ax = a.center.x - x;
                    float ay = a.center.y - y;
                    float bx = b.center.x - x;
                    float by = b.center.y - y;
                    float a2 = ax * ax + ay * ay;
                    float b2 = bx * bx + by * by;
                    if (a2 > b2) {
                        m_diagram[i] = b;
                        if (x > 0) {
                            queue.push(taskPool.pop().set(x - 1, y, i - 1, b));
                        }
                        if (y > 0) {
                            queue.push(taskPool.pop().set(x, y - 1, i - m_countX, b));
                        }
                        if (x < m_countX - 1) {
                            queue.push(taskPool.pop().set(x + 1, y, i + 1, b));
                        }
                        if (y < m_countY - 1) {
                            queue.push(taskPool.pop().set(x, y + 1, i + m_countX, b));
                        }
                        updated = true;
                    }
                }
                taskPool.push(front);
            }
            if (!updated) {
                break;
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy