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

squidpony.squidmath.NeuralParticle Maven / Gradle / Ivy

Go to download

SquidLib platform-independent logic and utility code. Please refer to https://github.com/SquidPony/SquidLib .

There is a newer version: 3.0.6
Show newest version
package squidpony.squidmath;


import squidpony.annotation.Beta;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

/**
 * Creates a field of particles that tend to form a neuron image type
 * distribution. The distribution tends to reach towards the largest area of
 * empty space, but features many nice branches and curls as well.
 *
 * If no points are added before the populate method is run, the center of the
 * area is chosen as the single pre-populated point.
 *
 * Based on work by Nolithius
 *
 * http://www.nolithius.com/game-development/neural-particle-deposition
 *
 * Source code is available on GitHub:
 * https://github.com/Nolithius/neural-particle as well as Google Code
 * (now archived): http://code.google.com/p/neural-particle/
 *
 * @author @author Eben Howard - http://squidpony.com - [email protected]
 */
@Beta
public class NeuralParticle implements Serializable{
    private static final long serialVersionUID = -3742942580678517149L;

    private final IRNG rng;
    private final int maxDistance, minDistance, width, height;
    private final ArrayList distribution = new ArrayList<>();

    public NeuralParticle(int width, int height, int maxDistance, IRNG rng) {
        this.rng = rng;
        this.maxDistance = maxDistance;
        this.width = width;
        this.height = height;
        minDistance = 1;
    }

    /**
     * Populates the field with given number of points.
     *
     * @param quantity the number of points to insert
     */
    public void populate(int quantity) {
        for (int i = 0; i < quantity; i++) {
            add(createPoint());
        }
    }

    /**
     * Returns a list of the current distribution.
     *
     * @return the distribution as a List of Coord
     */
    public List asList() {
        return new ArrayList<>(distribution);
    }

    /**
     * Returns an integer mapping of the current distribution.
     *
     * @param scale the value that active points will hold
     * @return a 2D int array, with all elements equal to either 0 or scale
     */
    public int[][] asIntMap(int scale) {
        int[][] ret = new int[width][height];
        for (Coord p : distribution) {
            ret[p.x][p.y] = scale;
        }
        return ret;
    }

    /**
     * Adds a single specific point to the distribution.
     *
     * @param point the Coord, also called a pip here, to insert
     */
    public void add(Coord point) {
        distribution.add(point);
    }

    /**
     * Creates a pip that falls within the required distance from the current
     * distribution. Does not add the pip to the distribution.
     *
     * @return the created pip
     */
    public Coord createPoint() {
        Coord randomPoint = randomPoint();
        Coord nearestPoint = nearestPoint(randomPoint);
        double pointDistance = randomPoint.distance(nearestPoint);
        // Too close, toss
        while (pointDistance < minDistance) {
            randomPoint = randomPoint();
            nearestPoint = nearestPoint(randomPoint);
            pointDistance = randomPoint.distance(nearestPoint);
        }
        // Adjust if we're too far
        if (pointDistance > maxDistance) {
            // Calculate unit vector
            double unitX = (randomPoint.x - nearestPoint.x) / pointDistance;
            double unitY = (randomPoint.y - nearestPoint.y) / pointDistance;
            randomPoint = Coord.get( (int) (rng.between(minDistance, maxDistance + 1) * unitX + nearestPoint.x)
                                   , (int) (rng.between(minDistance, maxDistance + 1) * unitY + nearestPoint.y));
        }
        return randomPoint;
    }

    private Coord nearestPoint(Coord point) {
        if (distribution.isEmpty()) {
            Coord center = Coord.get(width / 2, height / 2);
            distribution.add(center);
            return center;
        }

        Coord nearestPoint = distribution.get(0);
        double nearestDistance = point.distance(nearestPoint);
        for (Coord candidatePoint : distribution) {
            double candidateDistance = point.distance(candidatePoint);
            if (candidateDistance > 0 && candidateDistance <= maxDistance) {
                return candidatePoint;
            }

            if (candidateDistance < nearestDistance) {
                nearestPoint = candidatePoint;
                nearestDistance = candidateDistance;
            }
        }
        return nearestPoint;
    }

    private Coord randomPoint() {
        return Coord.get(rng.nextInt(width), rng.nextInt(height));
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy