Perlin Noise is not Gaining Depth with More Octaves - java

I am new to Perlin noise and I have hit a roadblock. The perlin noise function I have translated from C++ seems to work correctly for one octave, however I have found that the lower octaves of noise aren't added to the original Perlin Noise. Here is my code:
public class Perlin {
float[][] generateWhiteNoise(int width, int height) {
Random random = new Random(0);
float[][] noise = new float[width][height];
for (int i = 0; i < noise.length; i++) {
for (int j = 0; j < noise[i].length; j++){
noise[i][j] = (float)random.nextDouble();
}
}
return noise;
}
float[][] generateSmoothNoise(float[][] baseNoise, int octave){
int width = baseNoise.length;
int height = baseNoise[0].length;
float[][] smoothNoise = baseNoise;
int samplePeriod = (int) Math.pow(2,octave); // calculates 2 ^ k
float sampleFrequency = 1.0f / samplePeriod;
for (int i = 0; i < width; i++) {
//calculate the horizontal sampling indices
int sample_i0 = (i / samplePeriod) * samplePeriod;
int sample_i1 = (sample_i0 + samplePeriod) % width; //wrap around
float horizontal_blend = (i - sample_i0) * sampleFrequency;
for (int j = 0; j < height; j++){
//calculate the vertical sampling indices
int sample_j0 = (j / samplePeriod) * samplePeriod;
int sample_j1 = (sample_j0 + samplePeriod) % height; //wrap around
float vertical_blend = (j - sample_j0) * sampleFrequency;
//blend the top two corners
float top = interpolate(baseNoise[sample_i0][sample_j0],
baseNoise[sample_i1][sample_j0], horizontal_blend);
//blend the bottom two corners
float bottom = interpolate(baseNoise[sample_i0][sample_j1],
baseNoise[sample_i1][sample_j1], horizontal_blend);
//final blend
smoothNoise[i][j] = interpolate(top, bottom, vertical_blend);
}
}
return smoothNoise;
}
float interpolate(float x0, float x1, float alpha){
return (float) ((float)(x0) * (float)(1 - alpha) + (float)(alpha * x1));
}
float[][] generatePerlinNoise(float[][] baseNoise, int octaveCount) {
int width = baseNoise.length;
int height = baseNoise[0].length;
float[][][] smoothNoise = new float[octaveCount][][]; //an array of 2D arrays containing
float persistance = .5f;
//generate smooth noise
for (int i = 0; i<octaveCount; i++) {
System.out.println("Generating Smooth Noise: " + i);
smoothNoise[i] = generateSmoothNoise(baseNoise, i);
}
float[][] perlinNoise = new float[width][height];
float amplitude = 1.0f;
float totalAmplitude = 0.0f;
//blend noise together
for (int octave = octaveCount - 1; octave >= 0; octave--) {
amplitude *= persistance;
totalAmplitude += amplitude;
System.out.println("Adding smooth noise for octave: " + octave + " at amplitude: " + amplitude);
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
perlinNoise[i][j] += smoothNoise[octave][i][j] * amplitude;
}
}
}
//normalization
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
perlinNoise[i][j] /= totalAmplitude;
}
}
return perlinNoise;
}
public float[][] printVals(float[][] baseNoise){
baseNoise = generatePerlinNoise(generateWhiteNoise(800,800),6);
for(int i = 0; i<baseNoise.length; i++){
String row = "";
for(int j = 0; j<baseNoise[i].length;j++){
row+= (int)(baseNoise[i][j]*255) + " ";
}
System.out.println(row);
}
return baseNoise;
}
}
Here is the code in that I use to get values:
baseNoise = generatePerlinNoise(generateWhiteNoise(800,800),6);
Here are the outputs at octaves 1,2,3, and 4
Any help would be appreciated!
Edit:
Through trial and error, I have found that the most likely area for there to be problems is in the generatePerlinNoise() function. If I change the octaves, I get the desired noise level, which is what I want. That also means that the generateWhiteNoise() and generateSmoothNoise() works. So, somewhere within the generatePerlinNoise() blending there is a problem, but it seems like it should work.

I have found an answer. I had thought that the generateSmoothNoise() command was going to give me multi-octave perlin noise. However, I realized that I needed to build another command similar to what happens with the blending of the smooth noise to blend my different octaves of perlin noise.
public float[][] generateMultiOctavePerlinNoise(int octaves, double persistence, double dropoff, int width, int height){
float[][][]noise = new float[octaves][width][height];
for(int i = octaves - 1; i > 0;i--){
noise[i] = generatePerlinNoise(generateWhiteNoise(width,height),octaves - i);
}
float[][] multiOctave = new float[width][height];
for(int a= 0; a<noise.length; a++){
persistence*= dropoff;
for(int i = 0; i<multiOctave.length; i++){
for(int j = 0; j<multiOctave[i].length; j++){
multiOctave[i][j] += noise[a][i][j]*persistence;
}
}
}
return multiOctave;
}

Related

Rotate 2D array by alpha degrees

I wrote a function which takes two parameters:
JPG image as 3D array
rotation degrees given by alpha
My approach was:
public static int[][] rotate(int[][] img, double alpha) {
double rad = Math.toRadians(alpha);
double sin = Math.sin(rad);
double cos = Math.cos(rad);
int height = img.length;
int width = img[0].length;
int[][] rotate = new int[height][width];
for(int i = 0; i < height; i++) {
for(int j = height - i - 1; j < width; j++) {
if(j < height && i < width) {
double i_new = Math.floor(cos * (img[i].length - i) - sin * (img[j].length - j)) + i;
double j_new = Math.floor(sin * (img[i].length - i) + cos * (img[j].length - j)) + j;
rotate[i][j] = img[(int)j_new][(int)i_new];
}
}
}
return rotate;
}
While fixing the index range, the output is a black image. What am I missing?
After a while I got to a solution.
Caution: Its not using any special pre-defined libraries.
The global function which run`s over the matrice:
public static int[][] rotate(int[][] img, double alpha) {
double rad = Math.toRadians(alpha); //construct of the relevant angles
double sin = Math.sin(rad);
double cos = Math.cos(rad);
int height = img.length;
int width = img[0].length;
int[][] rotate = new int[height][width];
int a = height / 2; //we will use the area of a and b to compare coordinates by the formula given
int b = width / 2;
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
double i_new = Math.floor(cos * (i - a) - sin * (j - b)) + a; // following the conversion function
double j_new = Math.floor(sin * (i - a) + cos * (j - b)) + b;
if (i_new >= rotate.length || i_new < 0 || j_new >= rotate[0].length || j_new < rotate[0][0]) { // if out of scope of the conversion --> print none
System.out.print(""); //mainly cause 'continue' statements are not necessary in java and JS
} else {
rotate[(int) i_new][(int) j_new] = img[i][j]; //convert
}
}
}
return rotate;
}
The global function which rotates each 2D matrice:
public static int[][][] rotate_alpha(int[][][] img, double alpha) {
int height = img[0].length;
int width = img[0][0].length;
int[][][] rotated = new int[3][height][width];
for (int k = 0; k < 3; k++) {
rotated[k] = rotate(img[k], alpha);
}
return rotated;
}
Hope this topic is solved by now, and stands by all the standards of the clean code.

processing sketch throwing java.lang.AssertionError only when fill() is used

The study project is about warping text along bezier curves(recursive polynomial form) on processing using the geomerative library to get shape from a .ttf type font file.(It needs a ttf file in the data directory to function.) currently, the sketch seems to throws errors when
fill(any color); is used at the part where the code draws the character shapes and the bezier curve's length gets shorter than a certain length. If fill(); is not used, the sketch seems to function okay without any errors.
The goal is to use the fill(); function to fill the characters without errors.
I've tried;
1) getting rid of the beginContour(); and endContour(); because I thought it hasn't been written properly.(I thought it was wrong because the contours should only be drawn when the shape is the inner side of a letter but currently, it draws contours when it's not the first or last shape) But the sketch throws errors even when the contour function was not used (fill(); was used).
2) thought it had something to do with the length of the curve, so tried to add a if statement to the part where it draws the letters. So far, I've tried using the width of the RGroup generated from the initial font size and string in void setup(){}, and the length of the bezier curve. The condition examples within the if statement was as follows;
-draw letters when the RGroup shape's width is smaller than the length of the curve
-draw letters when the "indent"(a variable to calculate the position on the curve) value is smaller than the length of the curve. (this case made the sketch to draw letters only when the letters were placed within the curve, but the error still occurred)
-draw letters when the "indent"(a variable to calculate the position on the curve) value is smaller than the width of the RGroup.
I've failed to see where exactly the problem is occurring, so I'm sharing the entire code within the sketch, but I marked the spot where I presume the error is happening with "//*******".
This study was based on the following link.
The geomerative library documentation can be seen from the following link.
//build a bezier curve using the recursive polynomial form
//number of control points(num+1 points)
int num = 4;
//arraylist to store the picked values
ArrayList<Point> pt;
float nfac;
import geomerative.*;
RFont fnt;
RGroup rg;
RPoint [][]rp;
String str = "(O_o)/ Oooh";
FloatList X;
FloatList Y;
FloatList SUM;
void setup(){
size(1000,1000,P2D);
RG.init(this);
pt = new ArrayList<Point>();
//pick a number of points with random positions and store x,y values in them
for(int i=0; i<=num; i++){
float x = random(0,width);
float y = random(0,height);
pt.add(new Point(x,y));
}
fnt = new RFont("Zapfino.ttf",100);
//RCommand.setSegmentAngle(random(0,HALF_PI));
//RCommand.setSegmentator(RCommand.ADAPTATIVE);
RCommand.setSegmentLength(3);
RCommand.setSegmentator(RCommand.UNIFORMLENGTH);
rg = fnt.toGroup(str);
rp = rg.getPointsInPaths();
X = new FloatList();
Y = new FloatList();
SUM = new FloatList();
}
void draw(){
background(255);
noFill();
strokeCap(ROUND);
strokeWeight(2);
//draw points
beginShape();
for(int i=0; i<=num; i++){
vertex(pt.get(i).x,pt.get(i).y);
}
endShape();
for(int i=0; i<=num; i++){
ellipse(pt.get(i).x,pt.get(i).y,10,10);
}
//draw curve
nfac = 1;
for(int i=0; i<num; i++){
nfac *= (i+1);
}
strokeWeight(2);
beginShape();
int Bzindex = 0;
for(float t=0; t<=1; t+=0.01){
float x = 0;
float y = 0;
Bzindex++;
for(int i=0; i<=num; i++){
float coef = 1;
float kfac = 1;
float k_nfac = 1;
for(int k=i; k>0; k--){
kfac *= k;
}
for(int k=(num-i); k>0; k--){
k_nfac *= k;
}
coef = nfac/(kfac*k_nfac);
x += coef*(pow((1-t),num-i)*pow(t,i)*pt.get(i).x);
y += coef*(pow((1-t),num-i)*pow(t,i)*pt.get(i).y);
}
vertex(x,y);
X.set(Bzindex,x);
Y.set(Bzindex,y);
}
endShape();
//get arclength by pulling points from a floatlist
int numberOfDivisions = X.size()-2;
int maxPoint = numberOfDivisions+1;
float sum = 0;
float prevPointX = X.get(0);
float prevPointY = Y.get(0);
for(int i=1; i<=maxPoint; i++){
float pointX = X.get(i);
float pointY = Y.get(i);
sum += dist(pointX,pointY,prevPointX,prevPointY);
SUM.set(i-1,sum);
prevPointX = pointX;
prevPointY = pointY;
}
//
//*******************************************************
noFill();
//creates error only when using fill();
fill(0,255,0);
stroke(0);
//noStroke();
strokeWeight(4);
float indent = SUM.get(0);
beginShape();
for(int i=0; i<rp.length; i++){
if(i>0){
beginContour();
}
for(int j=0; j<rp[i].length; j++){
float t = 0;
indent = rp[i][j].x+SUM.get(0);
//
float targetArcLength = indent;
int index = IndexOfLargestValueSmallerThan(targetArcLength);
if(SUM.get(index)==targetArcLength){
t = index/(SUM.size()-1);
}else{
float lengthBefore = SUM.get(index);
float lengthAfter = SUM.get(index+1);
float segmentLength = lengthAfter - lengthBefore;
float segmentFraction = (targetArcLength - lengthBefore)/segmentLength;
t = (index+segmentFraction)/(SUM.size()-1);
}
float x = 0;
float y = 0;
float vx = 0;
float vy = 0;
for(int l=0; l<=num; l++){
float coef = 1;
float kfac = 1;
float k_nfac = 1;
for(int k=l; k>0; k--){
kfac *= k;
}
for(int k=(num-l); k>0; k--){
k_nfac *= k;
}
coef = nfac/(kfac*k_nfac);
x += coef*(pow((1-t),num-l)*pow(t,l)*pt.get(l).x);
y += coef*(pow((1-t),num-l)*pow(t,l)*pt.get(l).y);
float v = (pow(1-t,num-l)*l*pow(t,l-1))+((num-l)*pow(1-t,num-l-1)*(-1)*pow(t,l));
vx += coef*pt.get(l).x*(v);
vy += coef*pt.get(l).y*(v);
}
PVector P = new PVector(x,rp[i][j].y+y);
PVector ldir = new PVector(P.x-x,P.y-y);
PVector dir = new PVector(vy,-vx);
//
ldir.rotate(dir.heading()+PI/2);
vertex(x+ldir.x,y+ldir.y);
}
if(i<rp.length&&i>0){
endContour();
}
}
endShape();
//**************************************************************
}
int IndexOfLargestValueSmallerThan(float _targetArcLength){
int index = 0;
for(int i=0; i<SUM.size()-1; i++){
if(SUM.get(i)<=_targetArcLength){
index = i;
}
}
return index;
}
void mouseDragged(){
int which = -1;
if((mouseX<width)&&(mouseX>0)&&(mouseY<height)&&(mouseY>0)){
for(int i=0; i<=num; i++){
if(dist(mouseX,mouseY,pt.get(i).x,pt.get(i).y)<50){
which = i;
pt.get(which).update(mouseX,mouseY);
}
}
}
}
class Point{
float x,y;
Point(float _x, float _y){
x = _x;
y = _y;
}
void update(float _newx, float _newy){
x = _newx;
y = _newy;
}
}
Sometimes the error happens when the sketch is loaded. Most of the time, it loads okay but throws an error when you drag the point around a bit. The error code sometimes refers to the point where the control points of the curve are updated by mouse position, but because error somtimes occurs when the sketch is loaded as well, I didn't think it was a problem connected to the updated positions.
The error code is as follows;
a.lang.AssertionError
at processing.opengl.PSurfaceJOGL$2.run(PSurfaceJOGL.java:412)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.AssertionError
at jogamp.opengl.glu.tessellator.Sweep.AddRightEdges(Sweep.java:358)
at jogamp.opengl.glu.tessellator.Sweep.SweepEvent(Sweep.java:1105)
at jogamp.opengl.glu.tessellator.Sweep.__gl_computeInterior(Sweep.java:1339)
at jogamp.opengl.glu.tessellator.GLUtessellatorImpl.gluTessEndPolygon(GLUtessellatorImpl.java:526)
at com.jogamp.opengl.glu.GLU.gluTessEndPolygon(GLU.java:896)
at processing.opengl.PJOGL$Tessellator.endPolygon(PJOGL.java:641)
at processing.opengl.PGraphicsOpenGL$Tessellator.tessellatePolygon(PGraphicsOpenGL.java:12621)
at processing.opengl.PGraphicsOpenGL.tessellate(PGraphicsOpenGL.java:2255)
at processing.opengl.PGraphicsOpenGL.endShape(PGraphicsOpenGL.java:1965)
at processing.core.PGraphics.endShape(PGraphics.java:1707)
at processing.core.PApplet.endShape(PApplet.java:11641)
at bezier_polynomial_recursive_text_03.draw(bezier_polynomial_recursive_text_03.java:218)
at processing.core.PApplet.handleDraw(PApplet.java:2475)
at processing.opengl.PSurfaceJOGL$DrawListener.display(PSurfaceJOGL.java:866)
at jogamp.opengl.GLDrawableHelper.displayImpl(GLDrawableHelper.java:692)
at jogamp.opengl.GLDrawableHelper.display(GLDrawableHelper.java:674)
RuntimeException: java.lang.AssertionError
at jogamp.opengl.GLAutoDrawableBase$2.run(GLAutoDrawableBase.java:443)
at jogamp.opengl.GLDrawableHelper.invokeGLImpl(GLDrawableHelper.java:1293)
at jogamp.opengl.GLDrawableHelper.invokeGL(GLDrawableHelper.java:1147)
at com.jogamp.newt.opengl.GLWindow.display(GLWindow.java:759)
at com.jogamp.opengl.util.AWTAnimatorImpl.display(AWTAnimatorImpl.java:81)
at com.jogamp.opengl.util.AnimatorBase.display(AnimatorBase.java:452)
at com.jogamp.opengl.util.FPSAnimator$MainTask.run(FPSAnimator.java:178)
at java.util.TimerThread.mainLoop(Timer.java:555)
at java.util.TimerThread.run(Timer.java:505)
I don't think this would be a direct answer to my question, but it did stop the errors from occurring while using both fill() and the P2D renderer. The main problem, as pointed out by laancelot above, indeed seems to have been connected with stack overflow. So I approached the problem in two ways written below; Conclusion: The direct reason was a poorly expressed math formula.
1) switching the RPoints inside a class.
-I don't think this was the direct reason the errors were occurring, because at the stage where only this part of rewriting the code was done, the errors were still there. But maybe it was part of the problem. I'm not sure.
2) rewriting the part where the code expresses the formula to evaluate bezier curves at a specific point.
-Previously, the formula was made by using the explicit definition of a bezier curve with degree n. And, as a result, the formula had to be calculated(more like made) for every point in the RPoint points. As is mentioned on the wikipedia page about bezier curves, this way of computing is not recommended.
-on the revised code, the formula used to warp text was expressed in the polynomial form. Thus, it was able to pre-calculate the coefficients of the polynomial before the RPoint points were iterated. This seems to have solved the problem.
I'm still not really confident about what actually caused the problem and why it has been solved, and which part of the code I should show to explain this to others, so I'll share the entire code that has been rewritten. You need processing, the geomerative library, and a ttf type font file in a data folder to test the code. I have marked the place where the revised version of the formula is implicated. (It's still really messy....)
//n number of points
int num = 4;
//arraylist to store the picked values
ArrayList<cntrlPoint> pt;
//import the geomerative library
import geomerative.*;
//string
String str = "(O_o)/ Oooh";
FloatList X;
FloatList Y;
FloatList SUM;
RClass rc;
void setup() {
size(1000, 1000, P2D);
pt = new ArrayList<cntrlPoint>();
//pick a number of points with random positions
for (int i=0; i<=num; i++) {
float x = random(0, width);
float y = random(0, height);
pt.add(new cntrlPoint(x, y));
}
RG.init(this);
rc = new RClass();
X = new FloatList();
Y = new FloatList();
SUM = new FloatList();
}
void draw() {
background(255);
noFill();
strokeWeight(2);
drwCntrlPoints();
drwCurve();
gtArcLength();
fill(0,255,0);
rc.crtPoly(pt);
rc.drwText();
}
void drwCntrlPoints() {
//draw points
beginShape();
for (int i=0; i<=num; i++) {
vertex(pt.get(i).x, pt.get(i).y);
}
endShape();
for (int i=0; i<=num; i++) {
ellipse(pt.get(i).x, pt.get(i).y, 10, 10);
}
}
void drwCurve() {
//draw curve
float curveDetail = 0.01;
float nfac = 1;
for (int i=0; i<num; i++) {
nfac *= (i+1);
}
int arcIndex = 0;
strokeWeight(2);
beginShape();
for (float t=0; t<=1; t+=curveDetail) {
float x = 0;
float y = 0;
arcIndex++;
for (int i=0; i<=num; i++) {
float coef = 1;
float kfac = 1;
float k_nfac = 1;
for (int k=i; k>0; k--) {
kfac *= k;
}
for (int k=(num-i); k>0; k--) {
k_nfac *= k;
}
coef = nfac/(kfac*k_nfac);
x += coef*(pow((1-t), num-i)*pow(t, i)*pt.get(i).x);
y += coef*(pow((1-t), num-i)*pow(t, i)*pt.get(i).y);
}
vertex(x, y);
X.set(arcIndex, x);
Y.set(arcIndex, y);
}
endShape();
}
void gtArcLength() {
//get arclength by pulling points from a floatlist
int numberOfDivisions = X.size()-2;
int maxPoint = numberOfDivisions+1;
float sum = 0;
float prevPointX = X.get(0);
float prevPointY = Y.get(0);
for (int i=1; i<=maxPoint; i++) {
float pointX = X.get(i);
float pointY = Y.get(i);
sum += dist(pointX, pointY, prevPointX, prevPointY);
SUM.set(i-1, sum);
prevPointX = pointX;
prevPointY = pointY;
}
}
//*******factorial
int fact(int fa){
if(fa==1){
return 1;
}
if(fa==0){
return 1;
}
else{
return fa*fact(fa-1);
}
}
//********************
int IndexOfLargestValueSmallerThan(float _targetArcLength) {
int index = 0;
for (int i=0; i<SUM.size()-1; i++) {
if (SUM.get(i)<=_targetArcLength) {
index = i;
}
}
return index;
}
void mouseDragged() {
int which = -1;
if ((mouseX<width)&&(mouseX>0)&&(mouseY<height)&&(mouseY>0)) {
for (int i=0; i<=num; i++) {
if (dist(mouseX, mouseY, pt.get(i).x, pt.get(i).y)<80) {
which = i;
}
}
if (which>-1) {
pt.get(which).update(mouseX, mouseY);
}
}
}
class RClass {
//get ttf file
//create rfont
RFont fnt;
//turn rfont to rgroup to get points
RGroup rg;
//going to get point in path, so that the characters in the string can be seperated
RPoint [][]rp;
//floatlist to store coefficients
FloatList Cx;
FloatList Cy;
RClass() {
fnt = new RFont("Zapfino.ttf", 100);
rg = fnt.toGroup(str);
rp = rg.getPointsInPaths();
//RCommand.setSegmentAngle(random(0,HALF_PI));
//RCommand.setSegmentator(RCommand.ADAPTATIVE);
RCommand.setSegmentLength(3);
RCommand.setSegmentator(RCommand.UNIFORMLENGTH);
Cx = new FloatList();
Cy = new FloatList();
}
//**********************************here
void crtPoly(ArrayList<cntrlPoint> _pt){
float ptsize = _pt.size();
for(int j=0; j<ptsize; j++){
float coefx = 0;
float coefy = 0;
float pi = 1;
float sigx = 0;
float sigy = 0;
for(int m=0; m<=j-1; m++){
pi *= (ptsize-1-m);
}
for(int i=0; i<=j; i++){
sigx += (pow(-1,i+j)*pt.get(i).x)/(fact(i)*fact(j-i));
sigy += (pow(-1,i+j)*pt.get(i).y)/(fact(i)*fact(j-i));
}
coefx = pi*sigx;
coefy = pi*sigy;
Cx.set(j,coefx);
Cy.set(j,coefy);
}
}
//**************************************
void drwText() {
float indent = SUM.get(0);
beginShape();
for (int i=0; i<rp.length; i++) {
if(i>0){
beginContour();
}
for (int j=0; j<rp[i].length; j++) {
float t = 0;
indent = rp[i][j].x+SUM.get(0);
float targetArcLength = indent;
int index = IndexOfLargestValueSmallerThan(targetArcLength);
if (SUM.get(index)==targetArcLength) {
t = index/(SUM.size()-1);
} else {
float lengthBefore = SUM.get(index);
float lengthAfter = SUM.get(index+1);
float segmentLength = lengthAfter - lengthBefore;
float segmentFraction = (targetArcLength - lengthBefore)/segmentLength;
t = (index+segmentFraction)/(SUM.size()-1);
}
//***************************here
float x = 0;
float y = 0;
float vx = 0;
float vy = 0;
for(int l=0; l<=num; l++){
x += Cx.get(l)*pow(t,l);
y += Cy.get(l)*pow(t,l);
}
for(int l=1; l<=num; l++){
vx += l*Cx.get(l)*pow(t,l-1);
vy += l*Cy.get(l)*pow(t,l-1);
}
//**************************************
PVector P = new PVector(x, rp[i][j].y+y);
PVector ldir = new PVector(P.x-x, P.y-y);
PVector dir = new PVector(vy, -vx);
//
ldir.rotate(dir.heading()+PI/2);
vertex(x+ldir.x, y+ldir.y);
}
if(i>0&&i<rp.length){
endContour();
}
}
endShape();
}
}
class cntrlPoint{
float x,y;
cntrlPoint(float _x, float _y){
x = _x;
y = _y;
}
void update(float _newx, float _newy){
x = _newx;
y = _newy;
}
}

Java 2D Polygon - Polygon Collision Detection

Recently I have been using the Polygon class to create asteroids as well as bullets and a spaceship. I am currently trying to create the collision detection for the program however it appears that the collision detection only works around 1/5 of the time (no pattern appears as to why it works).
Here's the code..
Creating the Polygon:
void renderPoly() {
int j;
int s = sides;
double r, angle;
int x, y;
for (j = 0; j < s; j++) {
angle = 2 * Math.PI / s * j;
r = MIN_ROCK_SIZE + (int) (Math.random() * (MAX_ROCK_SIZE - MIN_ROCK_SIZE));
x = (int) (r * Math.cos(angle));
y = (int) (r * -Math.sin(angle));
cOM[0] += x;
cOM[1] += y;
pointData[j][0] = x;
pointData[j][1] = y;
}
cOM[0] /= asteroidShape.npoints;
cOM[1] /= asteroidShape.npoints;
for (int i = 0; i < asteroidShape.npoints; i++) {
pointData[i][0] += cOM[0];
pointData[i][1] += cOM[1];
}
}
rotating and moving the polygon:
void move() {
int x, y, i;
//change rotation
theta += rotVel;
//change x
asteroidData[0] += deltaX;
//change y
asteroidData[1] += deltaY;
for (i = 0; i < asteroidShape.npoints; i++) {
x = (int) (pointData[i][0] * Math.cos(theta) - pointData[i][1] * Math.sin(theta) );
y = (int) (pointData[i][0] * Math.sin(theta) + pointData[i][1] * Math.cos(theta) );
asteroidShape.xpoints[i] = x + asteroidData[0];
asteroidShape.ypoints[i] = y + asteroidData[1];
asteroidShape.invalidate();
}
}
check if touching bullet:
boolean hitBullet(Bullet b) {
this.asteroidShape.invalidate();
for (int i = 0; i < b.bulletShape.npoints; i++)
if (this.asteroidShape.contains(b.bulletShape.xpoints[i], b.bulletShape.ypoints[i]) )
return true;
for (int j = 0; j < this.asteroidShape.npoints; j++)
if (b.bulletShape.contains(this.asteroidShape.xpoints[j], this.asteroidShape.ypoints[j]) )
return true;
return false;
}
(the ship method is the same except the constructor requires a ship object)
as well as the loop that calls it in the 'game' class:
for (int i = 0; i < aArray.length-1; i++) {
if (aArray[i] != null) {
for (int j = 0; j < bArray.length-1; j++) {
if (bArray[j] != null) {
if (aArray[i].hitBullet(bArray[j])) {
aArray[i] = null;
bArray[j] = null;
i = aArray.length-1;
j = bArray.length-1;
}
}
else {
i = aArray.length-1;
j = bArray.length-1;
}
}
}
else {
i = aArray.length-1;
}
}
I have been looking around at alternative solutions such as the Separating Axis Theorem however I do have convex polygons at times and since this method (.contains()) already exists I would like to use it.
Any help would be appreciated, thanks!
The easy way to solve this that I've found is to convert Shapes (in your case Polygon(2D?)) into Areas. You can use Area.intersect(Area) to see if two Areas have collided

Issues while generating Perlin Noise

I've created a function to generate a 2-dimension array of Perlin Noise data, starting with a base noise array of random values between 0 and 1.
The base white noise array looks like this:
From there, I use the smoothNoise() function to interpolate values to create good looking image (which I don't have because it doesn't work)
The smooth noise function is called once every octave. The images of what data the smooth noise function returns is as follows (octaves lowest to highest)
After all the smoothing is done the image outputted turns black. I would upload an image but it is just black so there is no need.
My Code is:
import java.util.Random;
public class Noise2D {
int width;
int height;
public Noise2D(int width, int height){
this.width = width;
this.height = height;
}
public double[][] generateWhiteNoise(int width,int height){
Random r = new Random(0);
double[][] whiteNoise = new double[width][height];
for(int i = 0; i<whiteNoise.length; i++){
for(int j = 0; j<whiteNoise[0].length;j++){
double rNum = r.nextDouble()%1;
whiteNoise[i][j] = (double)rNum;
}
}
ImageWriter.writeImage(whiteNoise, "WhiteNoise");
return whiteNoise;
}
public double interpolate(double x0, double x1, double alpha){
return x0 * (1 - alpha) + alpha * x1;
}
public double[][] generateSmoothNoise(double[][] baseNoise, int octave){
int width = baseNoise.length;
int height = baseNoise[0].length;
double[][] smoothNoise = new double[width][height];
int period = 1<< octave; //2^i
double frequency = 1.0/period;
for(int x = 0; x<width; x++){
int x0 = (x/period)*period; //7/3 = 2 *3 = 6
int x1 = (x0+period)%width;
double hBlend = (x-x0)*frequency;
for(int y = 0; y<height; y++){
int y0 = (y/period)*period;
int y1 = (y0 + period)%height;
double vBlend = (y - y0)*frequency;
double top = interpolate(baseNoise[x0][y0],baseNoise[x1][y1],hBlend);
double bottom = interpolate(baseNoise[x0][y1],baseNoise[x1][y0],hBlend);
smoothNoise[x][y] = interpolate(top,bottom,vBlend);
}
}
ImageWriter.writeImage(smoothNoise,"Smooth"+Integer.toString(octave));
return smoothNoise;
}
public double[][] generatePerlinNoise(double baseNoise[][], int octaves){
int width = baseNoise.length;
int height = baseNoise[0].length;
double persistence = 0.5;
double[][][] smoothNoise = new double[octaves][][];
for(int i = 0; i<octaves; i++){
smoothNoise[i] = generateSmoothNoise(baseNoise,i);
}
double[][] perlinNoise = new double[width][height];
double amplitude = 1;
double totalAmplitude = 0;
for(int octave = octaves-1; octave>=0; octave--){
amplitude*=persistence;
totalAmplitude+=amplitude;
for(int x = 0; x<width;x++){
for(int y = 0; y<height; y++){
perlinNoise[x][y] = smoothNoise[octave][x][y] * amplitude;
}
}
}
ImageWriter.writeImage(perlinNoise,"files");
for(int i = 0; i<width; i++){
for(int j = 0; j<height; j++){
perlinNoise[i][j] /= totalAmplitude;
}
}
return perlinNoise;
}
}
The Class is defined and methods are called by:
Perlin p = new Perlin(256,256);
writeImage(p.smoothNoise(p.makeNoise(256,256), 1, 16), "Perlin");
1 is frequency, 16 is octave count
Where the data is written in the function writeImage(2d array, "name") using the following code (I don't think there is a problem with this part but I'll post it anyway):
public static void writeImage(double[][] data,String name){
BufferedImage img = new BufferedImage(data.length,data[0].length,BufferedImage.TYPE_INT_RGB);
for(int y = 0; y<data.length; y++){
for(int x = 0; x<data[y].length; x++){
if(data[y][x]>1){
data[y][x] = 1;
}
if(data[y][x]<0){
data[y][x] = 0;
}
Color c = new Color((float)data[y][x],(float)data[y][x],(float)data[y][x]);
img.setRGB(x,y,c.getRGB());
}
}
try{
File file = new File(name+".png");
file.createNewFile();
ImageIO.write(img,"png",file);
}catch(IOException e){
e.printStackTrace();
}
}
public static void writeExistingImage(BufferedImage img){
try{
File file = new File("noise2.png");
file.createNewFile();
ImageIO.write(img,"png",file);
}catch(IOException e){
e.printStackTrace();
}
}
To conclude, I believe the problem lies within the smoothNoise method, although I could quite possibly be wrong because my knowledge of Perlin Noise is not extensive. If the problem does not lie here, I assume it lies in the generatePerlinNoise() method. Any help would be greatly appreciated. I will be so happy with any suggestion, I have been trying to solve this problem for an extremely long time.
To clarify:
My problem is that the generatePerlinNoise method returns a set of data(2d) array that makes a black image (rather than a cool noise image)
I also think that the smooth noise images are not supposed to look like they are split up into squares

Perlin Noise Generation not functioning properly

I have been working with perlin noise recently and when implementing it into a tile engine I am using, I have noticed that the perlin noise function produced "blocks" as seen in the picture below. Each pixel is another different location in a 500 by 500 array that is returned from the perlin noise function.
in this example the persistence is 0.5 with an octave count of 5
When playing with it further, the more octaves I have, the larger the block chunks.
Here is the Code that I am using to call the perlin noise function:
PerlinNoise p = new PerlinNoise();
//returns a float[][] array of 500 by 500
p.GeneratePerlinNoise(p.genWhiteNoise(500, 500), 5, (float) 0.1);
PerlinNoise class
import java.util.Random;
public class PerlinNoise {
Random r;
public PerlinNoise() {
r = new Random();
}
public void setSeed(long seed) {
r.setSeed(seed);
}
public void printOutArray(float[][] arr) {
for(int i = 0; i < arr.length; i++) {
for(int n = 0; n < arr[0].length; n++) {
System.out.print(arr[i][n] + ", ");
}
System.out.print("\n");
}
}
public void printOutTerrain(float[][] arr) {
for(int i = 0; i < arr.length; i++) {
for(int n = 0; n < arr[0].length; n++) {
float a = arr[i][n];
if(a < 0.4) {
System.out.print("W");
} else {
System.out.print("L");
}
}
System.out.print("\n");
}
}
//-------------------------------------------------------------//
float[][] genWhiteNoise(int width, int height) {
float[][] noise = new float[height][width];
for(int y = 0; y < height; y++) {
for(int x = 0; x < width; x++) {
noise[y][x] = r.nextFloat();
}
}
return noise;
}
float[][] genSmoothNoise(float[][] baseNoise, int octave) {
int height = baseNoise.length;
int width = baseNoise[0].length;
float[][] smoothNoise = new float[height][width];
int samplePeriod = 1 << octave; //calculates 2^k
float sampleFrequency = (float) (1.0/samplePeriod);
for(int i = 0; i < height; i++) {
int sample_i0 = (i / samplePeriod) * samplePeriod;
int sample_i1 = (sample_i0 + samplePeriod) % height; //wrap around
float vertical_blend = (i - sample_i0) * sampleFrequency;
for(int n = 0; n < width; n++) {
int sample_n0 = (n / samplePeriod) * samplePeriod;
int sample_n1 = (sample_n0 + samplePeriod) % width; //wrap around
float horizontal_blend = (n - sample_n0) * sampleFrequency;
//blend the top two corners
float top = Interpolate(baseNoise[sample_i0][sample_n0],
baseNoise[sample_i1][sample_n0], horizontal_blend);
//blend the bottom two corners
float bottom = Interpolate(baseNoise[sample_i0][sample_n1],
baseNoise[sample_i1][sample_n1], horizontal_blend);
//final blend
smoothNoise[i][n] = Interpolate(top, bottom, vertical_blend);
}
}
return smoothNoise;
}
float[][] GeneratePerlinNoise(float[][] baseNoise, int octaveCount, float persistance)
{
int height = baseNoise.length;
int width = baseNoise[0].length;
float[][][] smoothNoise = new float[octaveCount][][]; //an array of 2D arrays containing
//generate smooth noise
for (int i = 0; i < octaveCount; i++)
{
smoothNoise[i] = genSmoothNoise(baseNoise, i);
}
float[][] perlinNoise = new float[height][width];
float amplitude = 1.0f;
float totalAmplitude = 0.0f;
//blend noise together
for (int octave = octaveCount - 1; octave >= 0; octave--)
{
amplitude *= persistance;
totalAmplitude += amplitude;
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
perlinNoise[i][j] += smoothNoise[octave][i][j] * amplitude;
}
}
}
//normalisation
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
perlinNoise[i][j] /= totalAmplitude;
}
}
return perlinNoise;
}
//linear average between two points
float Interpolate(float x0, float x1, float alpha)
{
return Cosine_Interpolate(x0, x1, alpha);
}
//Linear Interpolation
float Linear_Interpolate(float x0, float x1, float alpha)
{
return x0 * (1 - alpha) + alpha * x1;
}
//Cosine interpolation (much smoother)
float Cosine_Interpolate(float x0, float x1, float alpha)
{
float ft = (float) (alpha * 3.141592653589);
float f = (float) ((1 - Math.cos(ft)) * 0.5);
return x0*(1-f) + x1*f;
}
}
So to reiterate my question: Why is my perlin noise function behaving the way it does, as in only generating space in chunks?
So to fix this, all I had to do is swap the vertical_blend and horizontal_blend variables in the genSmoothNoise() method. It's amazing what you notice after a break

Categories