/*
* Copyright 2014 MovingBlocks
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.terasology.utilities.procedural;
/**
* Computes Brownian noise based on some noise generator.
* Originally, Brown integrates white noise, but using other noises can be sometimes useful, too.
*/
public class BrownianNoise extends AbstractNoise {
/**
* Default persistence value
*/
public static final double DEFAULT_PERSISTENCE = 0.836281;
/**
* Default lacunarity value
*/
public static final double DEFAULT_LACUNARITY = 2.1379201;
private double lacunarity = DEFAULT_LACUNARITY;
private double persistence = DEFAULT_PERSISTENCE;
private int octaves;
private float[] spectralWeights;
private float scale; // 1/sum of all weights
private final Noise other;
/**
* Initialize with 9 octaves - <b>this is quite expensive, but backwards compatible</b>
* @param other the noise to use as a basis
*/
public BrownianNoise(Noise other) {
this(other, 9);
}
/**
* @param other other the noise to use as a basis
* @param octaves the number of octaves to use
*/
public BrownianNoise(Noise other, int octaves) {
this.other = other;
setOctaves(octaves);
}
/**
* Returns Fractional Brownian Motion at the given position.
*
* @param x Position on the x-axis
* @param y Position on the y-axis
* @return The noise value in the range of the base noise function
*/
@Override
public float noise(float x, float y) {
float result = 0.0f;
float workingX = x;
float workingY = y;
for (int i = 0; i < getOctaves(); i++) {
result += other.noise(workingX, workingY) * spectralWeights[i];
workingX *= getLacunarity();
workingY *= getLacunarity();
}
return result * scale;
}
/**
* Returns Fractional Brownian Motion at the given position.
*
* @param x Position on the x-axis
* @param y Position on the y-axis
* @param z Position on the z-axis
* @return The noise value in the range of the base noise function
*/
@Override
public float noise(float x, float y, float z) {
float result = 0.0f;
float workingX = x;
float workingY = y;
float workingZ = z;
for (int i = 0; i < getOctaves(); i++) {
result += other.noise(workingX, workingY, workingZ) * spectralWeights[i];
workingX *= getLacunarity();
workingY *= getLacunarity();
workingZ *= getLacunarity();
}
return result * scale;
}
private static float computeScale(float[] spectralWeights) {
float sum = 0;
for (float weight : spectralWeights) {
sum += weight;
}
return 1.0f / sum;
}
/**
* @param octaves the number of octaves used for computation
*/
public void setOctaves(int octaves) {
this.octaves = octaves;
updateWeights();
}
/**
* @return the number of octaves
*/
public int getOctaves() {
return octaves;
}
/**
* Lacunarity is what makes the frequency grow. Each octave
* the frequency is multiplied by the lacunarity.
* @return the lacunarity
*/
public double getLacunarity() {
return this.lacunarity;
}
/**
* Lacunarity is what makes the frequency grow. Each octave
* the frequency is multiplied by the lacunarity.
* @param lacunarity the lacunarity
*/
public void setLacunarity(double lacunarity) {
this.lacunarity = lacunarity;
}
/**
* Persistence is what makes the amplitude shrink.
* More precicely the amplitude of octave i = lacunarity^(-persistence * i)
* @return the persistance
*/
public double getPersistance() {
return this.persistence;
}
/**
* Persistence is what makes the amplitude shrink.
* More precisely the amplitude of octave i = lacunarity^(-persistence * i)
* @param persistence the persistence to set
*/
public void setPersistence(double persistence) {
this.persistence = persistence;
updateWeights();
}
private void updateWeights() {
// recompute weights eagerly
spectralWeights = new float[octaves];
for (int i = 0; i < octaves; i++) {
spectralWeights[i] = (float) Math.pow(lacunarity, -persistence * i);
}
scale = computeScale(spectralWeights);
}
}