diff --git a/Others/PerlinNoise.java b/Others/PerlinNoise.java new file mode 100644 index 000000000..1d0f795a3 --- /dev/null +++ b/Others/PerlinNoise.java @@ -0,0 +1,164 @@ +import java.util.Random; +import java.util.Scanner; + +/** + * For detailed info and implementation see: Perlin-Noise + */ +public class PerlinNoise { + /** + * @param width width of noise array + * @param height height of noise array + * @param octaveCount numbers of layers used for blending noise + * @param persistence value of impact each layer get while blending + * @param seed used for randomizer + * @return float array containing calculated "Perlin-Noise" values + */ + static float[][] generatePerlinNoise(int width, int height, int octaveCount, float persistence, long seed) { + final float[][] base = new float[width][height]; + final float[][] perlinNoise = new float[width][height]; + final float[][][] noiseLayers = new float[octaveCount][][]; + + Random random = new Random(seed); + //fill base array with random values as base for noise + for(int x = 0; x < width; x++) { + for(int y = 0; y < height; y++) { + base[x][y] = random.nextFloat(); + } + } + + //calculate octaves with different roughness + for(int octave = 0; octave < octaveCount; octave++) { + noiseLayers[octave] = generatePerlinNoiseLayer(base, width, height, octave); + } + + float amplitude = 1f; + float totalAmplitude = 0f; + + //calculate perlin noise by blending each layer together with specific persistence + for(int octave = octaveCount - 1; octave >= 0; octave--) { + amplitude *= persistence; + totalAmplitude += amplitude; + + for(int x = 0; x < width; x++) { + for(int y = 0; y < height; y++) { + //adding each value of the noise layer to the noise + //by increasing amplitude the rougher noises will have more impact + perlinNoise[x][y] += noiseLayers[octave][x][y] * amplitude; + } + } + } + + //normalize values so that they stay between 0..1 + for(int x = 0; x < width; x++) { + for (int y = 0; y < height; y++) { + perlinNoise[x][y] /= totalAmplitude; + } + } + + return perlinNoise; + } + + /** + * @param base base random float array + * @param width width of noise array + * @param height height of noise array + * @param octave current layer + * @return float array containing calculated "Perlin-Noise-Layer" values + */ + static float[][] generatePerlinNoiseLayer(float[][] base, int width, int height, int octave) { + float[][] perlinNoiseLayer = new float[width][height]; + + //calculate period (wavelength) for different shapes + int period = 1 << octave; //2^k + float frequency = 1f / period; // 1/2^k + + for(int x = 0; x < width; x++) { + //calculates the horizontal sampling indices + int x0 = (x / period) * period; + int x1 = (x0 + period) % width; + float horizintalBlend = (x - x0) * frequency; + + for(int y = 0; y < height; y++) { + //calculates the vertical sampling indices + int y0 = (y / period) * period; + int y1 = (y0 + period) % height; + float verticalBlend = (y - y0) * frequency; + + //blend top corners + float top = interpolate(base[x0][y0], base[x1][y0], horizintalBlend); + + //blend bottom corners + float bottom = interpolate(base[x0][y1], base[x1][y1], horizintalBlend); + + //blend top and bottom interpolation to get the final blend value for this cell + perlinNoiseLayer[x][y] = interpolate(top, bottom, verticalBlend); + } + } + + return perlinNoiseLayer; + } + + /** + * @param a value of point a + * @param b value of point b + * @param alpha determine which value has more impact (closer to 0 -> a, closer to 1 -> b) + * @return interpolated value + */ + static float interpolate(float a, float b, float alpha) { + return a * (1 - alpha) + alpha * b; + } + + public static void main(String[] args) { + Scanner in = new Scanner(System.in); + + final int width; + final int height; + final int octaveCount; + final float persistence; + final long seed; + final String charset; + final float[][] perlinNoise; + + System.out.println("Width (int): "); + width = in.nextInt(); + + System.out.println("Height (int): "); + height = in.nextInt(); + + System.out.println("Octave count (int): "); + octaveCount = in.nextInt(); + + System.out.println("Persistence (float): "); + persistence = in.nextFloat(); + + System.out.println("Seed (long): "); + seed = in.nextLong(); + + System.out.println("Charset (String): "); + charset = in.next(); + + + perlinNoise = generatePerlinNoise(width, height, octaveCount, persistence, seed); + final char[] chars = charset.toCharArray(); + final int length = chars.length; + final float step = 1f / length; + //output based on charset + for(int x = 0; x < width; x++) { + for(int y = 0; y < height; y++) { + float value = step; + float noiseValue = perlinNoise[x][y]; + + for (char c : chars) { + if (noiseValue <= value) { + System.out.print(c); + break; + } + + value += step; + } + } + + System.out.println(); + } + } +}