Trying to calculate sunrise...ain't getting the right answer - java

This is my current code:
public class Sunpos {
final private double Pi = Math.PI;
final private double eul = 2.71828182845904523552 ;
final private double sonauf = 90;
final private double RAD = 0.017453292519943295769236907684886;
public double sunrisefinal (double Breitengrad, double Laengengrad, int tagzahl, int sommerzeit, int nacht) {
double lngHour = Laengengrad/15;
double t = tagzahl + ((6 - lngHour)/24);
// double ab = tagzahl + ((18 - lngHour)/24);
double M = (0.9856 * t) - 3.289;
double L = M + (1.916 * Math.sin(M)) + (0.020 * Math.sin(2 * M)) + 282.634;
if (L >= 359) { L -= 360; }
else if (L < 0) { L += 360; }
double RA = (Math.atan(0.91764 * Math.tan(Pi/180)*L));
if (RA >= 359) { RA -= 360; }
else if (RA < 0) { RA += 360; }
double Lquadrant = (Math.floor(L/90)*90);
double RAquadrant = (Math.floor(RA/90))*90;
RA = RA + (Lquadrant - RAquadrant);
RA = RA/15;
double sinDec = 0.39782 * Math.sin((Pi/180)*L);
double cosDec = (180/Pi)*(Math.cos(Math.asin(sinDec)));
double cosH = (Math.cos((Pi/180)*sonauf)-(sinDec*Math.sin((Pi/180)*Breitengrad)))/(cosDec * Math.cos((Pi/180)*Breitengrad));
double H = 360 - Math.acos(cosH);
H /= 15;
double T = H + RA -(0.06571 * t) - 6.622;
double UTC = T - lngHour;
if (UTC >= 23) { UTC -= 24; }
else if (UTC < 0) { UTC += 24; }
double locTime = UTC; // Fuer die schweiz!
System.out.println(locTime);
return(0);
}
The inputs are the following: ( 50, 10, 294, 1, 0). The last 2 can be ignored.
Now I am basing this on the following page:
http://williams.best.vwh.net/sunrise_sunset_algorithm.htm
The code should be complete according to the site, but I don't get anywhere near the supposed results. I should get around 7.5 for today but I'm getting a 9.358.
Now, that might be because something with radiants/degrees? I can't quite get my Mind into that, as I've been trying to insert those converters (Pi/180) into the code, without any usable result.
Can anyone tell me where to put them or point me in the right direction? I've spent waaaay too much time on this already, and now I'm so close.

I'll just post my implementation here in case people need it (ported from the same source as yours)
https://gist.github.com/zhong-j-yu/2232343b14a5b5ef5b9d
public class SunRiseSetAlgo
{
static double calcSunrise(int dayOfYear, double localOffset, double latitude, double longitude)
{
return calc(dayOfYear, localOffset, latitude, longitude, true);
}
static double calcSunset(int dayOfYear, double localOffset, double latitude, double longitude)
{
return calc(dayOfYear, localOffset, latitude, longitude, false);
}
// http://williams.best.vwh.net/sunrise_sunset_algorithm.htm
static double calc(int dayOfYear, double localOffset, double latitude, double longitude, boolean rise)
{
//1. first calculate the day of the year
// int N1 = floor(275 * month / 9.0);
// int N2 = floor((month + 9) / 12.0);
// int N3 = (1 + floor((year - 4 * floor(year / 4.0) + 2) / 3.0));
// int N = N1 - (N2 * N3) + day - 30;
int N = dayOfYear;
//2. convert the longitude to hour value and calculate an approximate time
double lngHour = longitude / 15;
double t = rise?
N + (( 6 - lngHour) / 24) :
N + ((18 - lngHour) / 24);
//3. calculate the Sun's mean anomaly
double M = (0.9856 * t) - 3.289;
//4. calculate the Sun's true longitude
double L = M + (1.916 * sin(M)) + (0.020 * sin(2 * M)) + 282.634;
L = mod(L, 360);
//5a. calculate the Sun's right ascension
double RA = atan(0.91764 * tan(L));
RA = mod(RA, 360);
//5b. right ascension value needs to be in the same quadrant as L
double Lquadrant = (floor( L/90)) * 90;
double RAquadrant = (floor(RA/90)) * 90;
RA = RA + (Lquadrant - RAquadrant);
//5c. right ascension value needs to be converted into hours
RA = RA / 15;
//6. calculate the Sun's declination
double sinDec = 0.39782 * sin(L);
double cosDec = cos(asin(sinDec));
//7a. calculate the Sun's local hour angle
double zenith = 90 + 50.0/60;
double cosH = (cos(zenith) - (sinDec * sin(latitude))) / (cosDec * cos(latitude));
if (cosH > 1)
throw new Error("the sun never rises on this location (on the specified date");
if (cosH < -1)
throw new Error("the sun never sets on this location (on the specified date");
//7b. finish calculating H and convert into hours
double H = rise?
360 - acos(cosH) :
acos(cosH);
H = H / 15;
//8. calculate local mean time of rising/setting
double T = H + RA - (0.06571 * t) - 6.622;
//9. adjust back to UTC
double UT = T - lngHour;
//10. convert UT value to local time zone of latitude/longitude
double localT = UT + localOffset;
localT = mod(localT, 24);
return localT;
}
static int floor(double d){ return (int)Math.floor(d); }
static double sin(double degree)
{
return Math.sin(degree*Math.PI/180);
}
static double cos(double degree)
{
return Math.cos(degree*Math.PI/180);
}
static double tan(double degree)
{
return Math.tan(degree*Math.PI/180);
}
static double atan(double x)
{
return Math.atan(x) *180/Math.PI;
}
static double asin(double x)
{
return Math.asin(x) *180/Math.PI;
}
static double acos(double x)
{
return Math.acos(x) *180/Math.PI;
}
static double mod(double x, double lim)
{
return x - lim * floor(x/lim);
}
}

Everone seems to link to this http://williams.best.vwh.net/sunrise_sunset_algorithm.htm
which doesn't exist anymore. Why not try something that gets updated once in a while like https://en.wikipedia.org/wiki/Sunrise_equation
Then if you like you could help edit it to make it better.

Related

How to solve sine mathematic equation in java?

How to solve following mathematic equation in java?
Equation:
x + sin(x) = constant, where x is variable. I encountered this equation after 18 years. I forgot this basic concept. Please help me on this basic high school question.
I tried to code above equation x + sin(x) = constant as following, however, it is giving wrong answer. Please let me know where i am wrong.
public double balanceLength(double total_weight) {
// 5.00 assume inical value of x
return newtonRaphson( 5.00, total_weight);
}
private static double derivFunc(double x)
{
return sin(x) + x;
}
private static double func(double x, double weight)
{
return sin(x) + x - weight;
}
static double newtonRaphson(double x, double weight)
{
double h = func(x, weight) / derivFunc(x);
while (abs(h) >= EPSILON)
{
h = func(x, weight) / derivFunc(x);
x = x - h;
}
return round(x * 100.0) / 100.0 ;
}
This is a very basic implementation, only partially tested. It reruns x in radians, which satisfies y=six(x) +x for a given y :
//returned value in radians
static double evaluateSinxPlusx(double y){
double delta = y>0 ? 0.01 : -0.01 ;//change constants
double epsilon = 0.01; //to change
int iterations = 100; //accuracy
double x = 0;
double sum = 1;
while(Math.abs(y - sum) > epsilon) {
x+=delta;
//based Taylor series approximation
double term = 1.0;
sum = x;
double d = 1;
for (int i = 1; i< iterations; i++) {
term = Math.pow(x, i);
d*=i;
if (i % 4 == 1) {
sum += term/d;
}
if (i % 4 == 3) {
sum -= term/d;
}
}
}
return x;
}
//test it
public static void main(String[] args) throws Exception{
double y = 0.979;
//expected x = 0.5 radians
System.out.println("for x="+ evaluateSinxPlusx(y)+"(radians), sin(x)+x = "+ y);
y = -0.979;
//expected x = - 0.5 radians
System.out.println("for x="+ evaluateSinxPlusx(y)+"(radians), sin(x)+x = "+ y);
y = 0.33256;
//expected x = 0.16666 radians
System.out.println("for x="+ evaluateSinxPlusx(y)+"(radians), sin(x)+x = "+ y);
}
This is not a robust implementation and should be used as demo only.

No convergence with batch gradient descent

I make my first steps in implementation of batch and stochastic gradient descent.
Here is my implementation:
package ch.learning;
import java.util.*;
import org.jzy3d.analysis.AbstractAnalysis;
import org.jzy3d.analysis.AnalysisLauncher;
import org.jzy3d.chart.factories.AWTChartComponentFactory;
import org.jzy3d.colors.Color;
import org.jzy3d.colors.ColorMapper;
import org.jzy3d.colors.colormaps.ColorMapRainbow;
import org.jzy3d.maths.Coord3d;
import org.jzy3d.maths.Range;
import org.jzy3d.plot3d.builder.*;
import org.jzy3d.plot3d.builder.concrete.*;
import org.jzy3d.plot3d.primitives.Scatter;
import org.jzy3d.plot3d.primitives.Shape;
import org.jzy3d.plot3d.rendering.canvas.Quality;
import org.apache.commons.math3.analysis.function.Sigmoid;
public class LogisticReg_GradientDescent {
private List<double[]> trainingExamples = new LinkedList<double[]>();
private static final int sizeTrainingset = 1000;
private volatile double[] theta = {10, 10, 10, 10 };
// Configurable compoenent of step size during theata update
private final double alpha = 0.01;
// Amount of iteration in Batch Gradient Descent
private static final int iterations = 10000;
private static final int printsAtStartAndEnd = 5;
private void buildTrainingExample(int amount) {
// Area of the house
double areaMin = 80;
double areaMax = 1000;
double areaRange = areaMax - areaMin;
// Distance to center
double distanceMin = 10;
double distanceMax = 10000;
double distanceRange = distanceMax - distanceMin;
// Generate training examples with prices
for (int i = 0; i < amount; i++) {
double[] example = new double[5];
example[0] = 1.0;
example[1] = areaMin + Math.random() * areaRange;
example[2] = distanceMin + Math.random() * distanceRange;
// Price is a feature as well in this logistic regression example
double price = 0;
price += _priceComponent(example[1], areaRange);
price += _priceComponent(example[2], distanceRange);
// price += _priceComponent(example[3], yocRange);
example[3] = price;
example[4] = (price>200000)?0:1;
trainingExamples.add(example);
}
}
// Random price according with some range constraints
private double _priceComponent(double value, double range) {
if (value <= range / 3)
return 50000 + 50000 * Math.random() * 0.1;
if (value <= (range / 3 * 2))
return 100000 + 100000 * Math.random() * 0.1;
return 150000 + 150000 * Math.random() * 0.1;
}
private double classificationByHypothesis(double[] features) {
// Scaling
double scalingF0 = features[0];
double scalingF1 = (features[1] - 80) / (920);
double scalingF2 = (features[2] - 10) / (9990);
double scalingF3 = (features[3] - 50000) / (400000);
double z = this.theta[0] * scalingF0 + this.theta[1] * scalingF1 + this.theta[2] * scalingF2
+ this.theta[3] * scalingF3;
double ret = 1 / (1 + Math.pow(Math.E, -z));
return ret;
}
// Costfunction: Mean squared error function
private double gradientBatch_costs() {
double costs = this.trainingExamples.stream().mapToDouble(l -> {
double costsint;
if (l[4] == 0) {
costsint = -Math.log(1 - classificationByHypothesis(l));
} else {
costsint = -Math.log(classificationByHypothesis(l));
}
return costsint;
}).sum();
return costs / this.trainingExamples.size();
}
// Theta Update with Batch Gradient Descent
private void gradientBatch_thetaUpdate(int amount) {
for (int i = 0; i < amount; i++) {
double partialDerivative0 = this.trainingExamples.stream()
.mapToDouble(l -> (classificationByHypothesis(l) - l[4]) * l[0]).sum();
double tmpTheta0 = this.theta[0] - (this.alpha * partialDerivative0 / this.trainingExamples.size());
double partialDerivative1 = this.trainingExamples.stream()
.mapToDouble(l -> (classificationByHypothesis(l) - l[4]) * l[1]).sum();
double tmpTheta1 = this.theta[1] - (this.alpha * partialDerivative1 / this.trainingExamples.size());
double partialDerivative2 = this.trainingExamples.stream()
.mapToDouble(l -> (classificationByHypothesis(l) - l[4]) * l[2]).sum();
double tmpTheta2 = this.theta[2] - (this.alpha * partialDerivative2 / this.trainingExamples.size());
double partialDerivative3 = this.trainingExamples.stream()
.mapToDouble(l -> (classificationByHypothesis(l) - l[4]) * l[3]).sum();
double tmpTheta3 = this.theta[3] - (this.alpha * partialDerivative3 / this.trainingExamples.size());
this.theta = new double[] { tmpTheta0, tmpTheta1, tmpTheta2, tmpTheta3 };
}
}
// Theta update with Stochastic Gradient Descent
private void gradientStochastic_thetaUpdate(double[] feature) {
double tmpTheta0 = this.theta[0] - this.alpha * (classificationByHypothesis(feature) - feature[4]) * feature[0];
double tmpTheta1 = this.theta[1] - this.alpha * (classificationByHypothesis(feature) - feature[4]) * feature[1];
double tmpTheta2 = this.theta[2] - this.alpha * (classificationByHypothesis(feature) - feature[4]) * feature[2];
double tmpTheta3 = this.theta[3] - this.alpha * (classificationByHypothesis(feature) - feature[4]) * feature[3];
this.theta = new double[] { tmpTheta0, tmpTheta1, tmpTheta2, tmpTheta3 };
}
private void resetTheta() {
this.theta = new double[] {0.00001, 0.00001, 0.00001, 0.00001};
}
private void printSummary(int iteration) {
System.out.println(String.format("%s \t\t Theta: %f \t %f \t %f \t %f \t Costs: %f", iteration, this.theta[0],
this.theta[1], this.theta[2], this.theta[3], this.gradientBatch_costs()));
}
public static void main(String[] args) {
LogisticReg_GradientDescent d = new LogisticReg_GradientDescent();
// Batch and Stochastic Gradient Descent use the same training example
d.buildTrainingExample(sizeTrainingset);
System.out.println("Batch Gradient Descent");
d.printSummary(0);
System.out.println(String.format("First %s iterations", printsAtStartAndEnd));
for (int i = 1; i <= iterations; i++) {
d.gradientBatch_thetaUpdate(1);
d.printSummary(i);
}
System.out.println("Some examples are:");
System.out.println(String.format("The 1:%s, Area:%s, Distance:%s, Price:%s, Classification:%s", d.trainingExamples.get(0)[0],d.trainingExamples.get(0)[1],d.trainingExamples.get(0)[2],d.trainingExamples.get(0)[3],d.trainingExamples.get(0)[4]));
System.out.println(String.format("The 1:%s, Area:%s, Distance:%s, Price:%s, Classification:%s", d.trainingExamples.get(500)[0],d.trainingExamples.get(500)[1],d.trainingExamples.get(500)[2],d.trainingExamples.get(500)[3],d.trainingExamples.get(500)[4]));
try {
AnalysisLauncher.open(d.new SurfaceDemo());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
class SurfaceDemo extends AbstractAnalysis{
#Override
public void init(){
double x;
double y;
double z;
float a;
Coord3d[] points = new Coord3d[trainingExamples.size()];
Color[] colors = new Color[trainingExamples.size()];
for(int i=0; i<trainingExamples.size(); i++){
x = trainingExamples.get(i)[1]; // Area
y = trainingExamples.get(i)[2]; // Distance to center
z = trainingExamples.get(i)[3]; // price
points[i] = new Coord3d(x, y, z);
a = 1f;
if(trainingExamples.get(i)[4]==1){
colors[i] =new Color(0,0,0,a);
}else{
colors[i]= new Color(250,0,0,a);
}
}
Scatter scatter = new Scatter(points, colors);
scatter.setWidth(4);
Mapper mapper = new Mapper() {
#Override
public double f(double x, double y) {
return (-theta[0]-theta[1]*x-theta[2]*y)/theta[3];
}
};
// Create the object to represent the function over the given range.
Range rangeX = new Range(0, 1000);
Range rangeY = new Range(0, 10000);
int steps = 10;
final Shape surface = Builder.buildOrthonormal(new OrthonormalGrid(rangeX, steps, rangeY, steps), mapper);
surface.setColorMapper(new ColorMapper(new ColorMapRainbow(), surface.getBounds().getZmin(), surface.getBounds().getZmax(), new Color(1, 1, 1, .5f)));
surface.setFaceDisplayed(true);
surface.setWireframeDisplayed(false);
chart = AWTChartComponentFactory.chart(Quality.Advanced, getCanvasType());
chart.getScene().add(scatter);
chart.getScene().add(surface);
}
}
}
A graphical representation looks like
So i plot the generated training instances with org.jzy3d.plot3d.
We see x(the area of the house), y(distance to town center) and z(price).
The classification makes red (negative class -> not sold) and black (positive class -> sold).
In the generated trainings instances the classification depends just at the price, you see it here:
example[4] = (price>200000)?0:1;
The problem, the thing I don't understand is
I would like to plot the decision boundary of my classificator.
The decision bounday depends on the optimized components from Theta. (Using batch gradient descent).
So i try to plot the decision boundary plane with this code:
Mapper mapper = new Mapper() {
#Override
public double f(double x, double y) {
return (-theta[0]-theta[1]*x-theta[2]*y)/theta[3];
}
};
Because
theta[0]*1 + theta[1 ]*x + theta[2]*y + theta[3]*z = 0
so
z = -(theta[0]*1 + theta[1 ]*x + theta[2]*y)/theta[3]
I would expect my decision boundary plane between the red- and blackarea.
Instead it hangs around by z=0.
I didn't know, either I'm not able to plot this decision boundary plane in a proper way, or my optimized parameters are shit.
Further I don't know how to choose a good initial theta vector.
Right now i use
private volatile double[] theta = {1, 1, 1, 1 };
I set alpha to 0.0001
private final double alpha = 0.0001;
It was the biggest possible Alpha, where my cost function doesn't jump around and the sigmoid implementation doesn't return infinity.
I already make feature scaling at
private double classificationByHypothesis(double[] features) {
// Scaling
double scalingF0 = features[0];
double scalingF1 = (features[1] - 80) / (920);
double scalingF2 = (features[2] - 10) / (9990);
double scalingF3 = (features[3] - 50000) / (400000);
double z = this.theta[0] * scalingF0 + this.theta[1] * scalingF1 + this.theta[2] * scalingF2
+ this.theta[3] * scalingF3;
double ret = 1 / (1 + Math.pow(Math.E, -z));
return ret;
}
The last five iteration with given initial theta and alpha equals 0.0001 are
9996,Theta: 1.057554,-6.340981,-6.242139,8.145087,Costs: 0.359108
9997,Theta: 1.057560,-6.341234,-6.242345,8.145576,Costs: 0.359109
9998,Theta: 1.057565,-6.341487,-6.242552,8.146065,Costs: 0.359110
9999,Theta: 1.057571,-6.341740,-6.242758,8.146553,Costs: 0.359112
10000,Theta: 1.057576,-6.341993,-6.242965,8.147042,Costs: 0.359113
Some example of the generated training instances are
Area: 431.50139030510206, Distance: 8591.341686012887,
Price: 255049.1280388437, Classification:0.0
Area: 727.4042972310916, Distance: 4364.710136408952,
Price: 258385.59452489938, Classification:0.0
Thanks for any hint!

What is the best method of scanning nearby locations?

Quick note, I am new to programming Android applications so feel free to say I'm going about this the entirely wrong way or even to explain things to me like I'm an idiot.
My application is very location dependent. I am using the Google Play Location Services to provide the users current location. Users can store locations in their device which I store using a hashmap. In order to make it more convenient I am trying to make it so the user only has to be within a range of that stored location. My current method to create this range is to essentially use a for loop to scan the user's current location to see if it is in the hashmap.
public String scan(Location local) {
String fLoc = "";
List<String> pLoc = new ArrayList<String>();
String loc = "";
double lat = local.getLatitude();
double lon = local.getLongitude();
lat = (double) Math.round(lat * decimal) / decimal;
lon = (double) Math.round(lon * decimal) / decimal;
for (double x = -8; x < 9; x++) {
double tlat = lat;
double tlon = lon;
tlat = tlat + (x / decimal);
tlat = (double) Math.round(tlat * decimal) / decimal;
for (double y = -8; y < 9; y++) {
tlon = lon;
tlon = tlon + (y / decimal);
tlon = (double) Math.round(tlon * decimal) / decimal;
loc = ("Latitude: " + tlat + " Longitude: " + tlon);
if (locMap.containsKey(loc)) {
pLoc.add(loc);
}
}
}
if (pLoc.isEmpty()) {
lat = local.getLatitude();
lon = local.getLongitude();
lat = (double) Math.round(lat * decimal) / decimal;
lon = (double) Math.round(lon * decimal) / decimal;
loc = ("Latitude: " + lat + " Longitude: " + lon);
fLoc = loc;
} else if (pLoc.size() == 1) {
fLoc = (String) pLoc.toArray()[0];
} else {
Double diff = 0.0;
Double tdiff = 10.0;
for (String l : pLoc) {
diff = 10.0;
String[] parts = l.split(" ");
double dlat = Double.parseDouble(parts[1]) - lat;
if (dlat < 0) {
dlat = dlat * -1;
}
double dlon = Double.parseDouble(parts[5]) - lon;
if (dlon < 0) {
dlon = dlon * -1;
}
diff = dlon + dlat;
if (diff < tdiff) {
tdiff = diff;
fLoc = l;
}
}
}
return fLoc;
}
If no location is close, the user's current location is returned. I feel this is a crude method that is wasteful. What is a better way to find nearby locations? Maybe a for loop through saved location to see if the difference between it and the nearby location is small enough, but this could get costly when the user starts save lots of locations.
Any suggestions?
PS Any tips on adding accurate elevation without barometers?

basic math equation math to java code

so i have a math equation that i need to use in java but for some reason my code is giving me small errors :(
the math equation is describe on this web page in the section extra credit
my current code outpouts 4000 and the answere is 4005 what am i duing wrong ?
my test class lookes like this
public class MainActivity {
public static void main(String[] args) throws Exception{
double baseMaterial =556;
int me =5;
int ml = 10;
int extraMaterial = 3444;
System.out.println(""+calculateMiniralTotal(baseMaterial,me,ml,extraMaterial));
}
public static double calculateMiniralTotal(double perfekt,int me,int ml,int extraMaterial) {
double s = (perfekt + (perfekt * (10 / (ml + 1)) / 100));
s = Math.round(s);
double r = s + (perfekt * (0.25 - (0.05 * me)));
r = Math.round(r);
double q = extraMaterial + (extraMaterial * (0.25 - (0.05 * me)));
q = Math.round(q);
//double r=q;
r = r + q;
return Math.round(r);
}
}
You are performing integer division with (10 / (ml + 1)) / 100, which in Java must result in another int. Your ml is 10, and in Java, 10 / 11 is 0, not 0.909..., and nothing is added to s.
Use a double literal or cast to double to force floating-point computations.
double s = (perfekt + (perfekt * (10.0 / (ml + 1)) / 100));
or
double s = (perfekt + (perfekt * ( (double) 10 / (ml + 1)) / 100));
Making either change makes the output:
4005.0
When you multiply a double by an int you get an int back.
public class Main
{
public static void main(String[] args)
throws Exception
{
double baseMaterial = 556;
int me = 5;
int ml = 10;
int extraMaterial = 3444;
System.out.println("" + calculateMiniralTotal(baseMaterial, me, ml, extraMaterial));
}
public static double calculateMiniralTotal(double perfekt, int me, int ml, int extraMaterial)
{
double s = (perfekt + (perfekt * (10.0 / (ml + 1)) / 100.0)); // <-- changed from 10 to 10.0 and 100 to 100.0. This way they are doubles too
s = Math.round(s);
double r = s + (perfekt * (0.25 - (0.05 * me)));
r = Math.round(r);
double q = extraMaterial + (extraMaterial * (0.25 - (0.05 * me)));
q = Math.round(q);
// double r=q;
r = r + q;
return Math.round(r);
}
}

Perlin noise value range

I used perlin noise to generate a 2D height map. At first i tried some parameters manually and found a good combination of amplitude, persistence,... for my job.
Now that i'm developing the program, i added the feature for user to change the map parameters and make a new map for himself but now i see that for certain parameters (Mostly octaves and frequency) the values are not in the range i used to see. I thought that if a set Amplitude = 20, the values(heights) i get from it will be in e.g [0,20] or [-10,10] or [-20,20] ranges but now i see that Amplitude is not the only parameter that controls output range.
My question is: Is there an exact mathematical formula (a function of Amplitude, Octaves, Frequency and persistence) to compute the range or i should take a lot of samples (like 100,000) and check minimum and maximum values of them to guess the aproximate range?
Note: The following code is an implementation of perlin noise that one of stackoverflow guys worte it in C and i ported it to java.
PerlinNoiseParameters.java
public class PerlinNoiseParameters {
public double persistence;
public double frequency;
public double amplitude;
public int octaves;
public int randomseed;
public PerlinNoiseParameters(double persistence, double frequency, double amplitude, int octaves, int randomseed) {
this.ChangeParameters(persistence, frequency, amplitude, octaves, randomseed);
}
public void ChangeParameters(double persistence, double frequency, double amplitude, int octaves, int randomseed) {
this.persistence = persistence;
this.frequency = frequency;
this.amplitude = amplitude;
this.octaves = octaves;
this.randomseed = 2 + randomseed * randomseed;
}
}
PerlinNoiseGenerator.java
public class PerlinNoiseGenerator {
PerlinNoiseParameters parameters;
public PerlinNoiseGenerator() {
}
public PerlinNoiseGenerator(PerlinNoiseParameters parameters) {
this.parameters = parameters;
}
public void ChangeParameters(double persistence, double frequency, double amplitude, int octaves, int randomseed) {
parameters.ChangeParameters(persistence, frequency, amplitude, octaves, randomseed);
}
public void ChangeParameters(PerlinNoiseParameters newParams) {
parameters = newParams;
}
public double get(double x, double y) {
return parameters.amplitude * Total(x, y);
}
private double Total(double i, double j) {
double t = 0.0f;
double _amplitude = 1;
double freq = parameters.frequency;
for (int k = 0; k < parameters.octaves; k++) {
t += GetValue(j * freq + parameters.randomseed, i * freq + parameters.randomseed)
* _amplitude;
_amplitude *= parameters.persistence;
freq *= 2;
}
return t;
}
private double GetValue(double x, double y) {
int Xint = (int) x;
int Yint = (int) y;
double Xfrac = x - Xint;
double Yfrac = y - Yint;
double n01 = Noise(Xint - 1, Yint - 1);
double n02 = Noise(Xint + 1, Yint - 1);
double n03 = Noise(Xint - 1, Yint + 1);
double n04 = Noise(Xint + 1, Yint + 1);
double n05 = Noise(Xint - 1, Yint);
double n06 = Noise(Xint + 1, Yint);
double n07 = Noise(Xint, Yint - 1);
double n08 = Noise(Xint, Yint + 1);
double n09 = Noise(Xint, Yint);
double n12 = Noise(Xint + 2, Yint - 1);
double n14 = Noise(Xint + 2, Yint + 1);
double n16 = Noise(Xint + 2, Yint);
double n23 = Noise(Xint - 1, Yint + 2);
double n24 = Noise(Xint + 1, Yint + 2);
double n28 = Noise(Xint, Yint + 2);
double n34 = Noise(Xint + 2, Yint + 2);
double x0y0 = 0.0625 * (n01 + n02 + n03 + n04) + 0.1250
* (n05 + n06 + n07 + n08) + 0.2500 * n09;
double x1y0 = 0.0625 * (n07 + n12 + n08 + n14) + 0.1250
* (n09 + n16 + n02 + n04) + 0.2500 * n06;
double x0y1 = 0.0625 * (n05 + n06 + n23 + n24) + 0.1250
* (n03 + n04 + n09 + n28) + 0.2500 * n08;
double x1y1 = 0.0625 * (n09 + n16 + n28 + n34) + 0.1250
* (n08 + n14 + n06 + n24) + 0.2500 * n04;
double v1 = Interpolate(x0y0, x1y0, Xfrac);
double v2 = Interpolate(x0y1, x1y1, Xfrac);
double fin = Interpolate(v1, v2, Yfrac);
return fin;
}
private double Interpolate(double x, double y, double a) {
double negA = 1.0 - a;
double negASqr = negA * negA;
double fac1 = 3.0 * (negASqr) - 2.0 * (negASqr * negA);
double aSqr = a * a;
double fac2 = 3.0 * aSqr - 2.0 * (aSqr * a);
return x * fac1 + y * fac2;
}
private double Noise(int x, int y) {
int n = x + y * 57;
n = (n << 13) ^ n;
int t = (n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff;
return 1.0 - (double) t * 0.931322574615478515625e-9;
}
}
The range of a single perlin noise step is:
http://digitalfreepen.com/2017/06/20/range-perlin-noise.html
-sqrt(N/4), sqrt(N/4)
With N being the amount of dimensions. 2 in your case.
Octaves, persistence and amplitude add on top of that:
double range = 0.0;
double _amplitude = parameters.;
for (int k = 0; k < parameters.octaves; k++) {
range += sqrt(N/4) * _amplitude;
_amplitude *= parameters.persistence;
}
return range;
There might be some way to do this as a single mathematical expression. Involving pow(), but by brain fails me right now.
This is not a problem with octaves and frequency affecting amplitude, not directly at least. It is a problem with integer overflow. Because you introduce your random seed by adding it to the the x and y co-ordinates (which is unusual, I don't think this is the usual implimentation)
t += GetValue(j * freq + parameters.randomseed, i * freq + parameters.randomseed)* _amplitude;
And random seed could be huge (possibly the near full size of the int) because
this.randomseed = 2 + randomseed * randomseed;
So if you input large values for j and i you end up with the doubles that are passed through at GetValue(double x, double y) being larger than the maximum size of int, at that point when you call
int Xint = (int) x;
int Yint = (int) y;
Xint and YInt won't be anything like x and y (because x and y could be huge!) and so
double Xfrac = x - Xint;
double Yfrac = y - Yint;
could be much much larger that 1, allowing values not between -1 and 1 to be returned.
Using reasonable and small values my ranges using your code are between -1 and 1 (for amplitude 1)
As an asside, in java usually method names are methodName, not MethodName
If its useful please find annother java implimentation of perlin noise here:
http://mrl.nyu.edu/~perlin/noise/

Categories

Resources