001package squidpony.squidmath; 002 003 004import squidpony.annotation.Beta; 005 006import java.io.Serializable; 007import java.util.LinkedList; 008import java.util.List; 009 010/** 011 * Creates a field of particles that tend to form a neuron image type 012 * distribution. The distribution tends to reach towards the largest area of 013 * empty space, but features many nice branches and curls as well. 014 * 015 * If no points are added before the populate method is run, the center of the 016 * area is chosen as the single pre-populated point. 017 * 018 * Based on work by Nolithius 019 * 020 * http://www.nolithius.com/game-development/neural-particle-deposition 021 * 022 * Source code is available on GitHub: 023 * https://github.com/Nolithius/neural-particle as well as Google Code 024 * (now archived): http://code.google.com/p/neural-particle/ 025 * 026 * @author @author Eben Howard - http://squidpony.com - howard@squidpony.com 027 */ 028@Beta 029public class NeuralParticle implements Serializable{ 030 private static final long serialVersionUID = -3742942580678517149L; 031 032 private final RNG rng; 033 private final int maxDistance, minDistance, width, height; 034 private final LinkedList<Coord> distribution = new LinkedList<>(); 035 036 public NeuralParticle(int width, int height, int maxDistance, RNG rng) { 037 this.rng = rng; 038 this.maxDistance = maxDistance; 039 this.width = width; 040 this.height = height; 041 minDistance = 1; 042 } 043 044 /** 045 * Populates the field with given number of points. 046 * 047 * @param quantity the number of points to insert 048 */ 049 public void populate(int quantity) { 050 for (int i = 0; i < quantity; i++) { 051 add(createPoint()); 052 } 053 } 054 055 /** 056 * Returns a list of the current distribution. 057 * 058 * @return the distribution as a List of Coord 059 */ 060 public List<Coord> asList() { 061 return new LinkedList<>(distribution); 062 } 063 064 /** 065 * Returns an integer mapping of the current distribution. 066 * 067 * @param scale the value that active points will hold 068 * @return a 2D int array, with all elements equal to either 0 or scale 069 */ 070 public int[][] asIntMap(int scale) { 071 int ret[][] = new int[width][height]; 072 for (Coord p : distribution) { 073 ret[p.x][p.y] = scale; 074 } 075 return ret; 076 } 077 078 /** 079 * Adds a single specific point to the distribution. 080 * 081 * @param point the Coord, also called a pip here, to insert 082 */ 083 public void add(Coord point) { 084 distribution.add(point); 085 } 086 087 /** 088 * Creates a pip that falls within the required distance from the current 089 * distribution. Does not add the pip to the distribution. 090 * 091 * @return the created pip 092 */ 093 public Coord createPoint() { 094 Coord randomPoint = randomPoint(); 095 Coord nearestPoint = nearestPoint(randomPoint); 096 double pointDistance = randomPoint.distance(nearestPoint); 097 // Too close, toss 098 while (pointDistance < minDistance) { 099 randomPoint = randomPoint(); 100 nearestPoint = nearestPoint(randomPoint); 101 pointDistance = randomPoint.distance(nearestPoint); 102 } 103 // Adjust if we're too far 104 if (pointDistance > maxDistance) { 105 // Calculate unit vector 106 double unitX = (randomPoint.x - nearestPoint.x) / pointDistance; 107 double unitY = (randomPoint.y - nearestPoint.y) / pointDistance; 108 randomPoint = Coord.get( (int) (rng.between(minDistance, maxDistance + 1) * unitX + nearestPoint.x) 109 , (int) (rng.between(minDistance, maxDistance + 1) * unitY + nearestPoint.y)); 110 } 111 return randomPoint; 112 } 113 114 private Coord nearestPoint(Coord point) { 115 if (distribution.isEmpty()) { 116 Coord center = Coord.get(width / 2, height / 2); 117 distribution.add(center); 118 return center; 119 } 120 121 Coord nearestPoint = distribution.getFirst(); 122 double nearestDistance = point.distance(nearestPoint); 123 for (Coord candidatePoint : distribution) { 124 double candidateDistance = point.distance(candidatePoint); 125 if (candidateDistance > 0 && candidateDistance <= maxDistance) { 126 return candidatePoint; 127 } 128 129 if (candidateDistance < nearestDistance) { 130 nearestPoint = candidatePoint; 131 nearestDistance = candidateDistance; 132 } 133 } 134 return nearestPoint; 135 } 136 137 private Coord randomPoint() { 138 return Coord.get(rng.nextInt(width), rng.nextInt(height)); 139 } 140 141}