Perlin Noise in Java - java

For a cellular automata project I'm working on I need to generate two dimensional boolean arrays randomly using different algorithms and techniques. At the moment I have just one type of randomization in the application - looping through every cell in the array and generating a random double variable, then if the random number is higher than 0.5 then I set that cell to true, if not it gets set to false.
I would like to look into generating these boolean matrices using more interesting algorithms such as Perlin Noise or something like that. Noise generators that are used in terrain generation or something like that might be good if you know of any other than Perlin Noise (Minecraft's world generation gave me this idea).
The only problem is I have no idea where to start (any ideas?) :)

The first thing I thought of was a random displacement fractal. It is also used to generate terrain and is easier than Perlin Noise.
package so;
import java.util.Random;
public class Noise {
/** Source of entropy */
private Random rand_;
/** Amount of roughness */
float roughness_;
/** Plasma fractal grid */
private float[][] grid_;
/** Generate a noise source based upon the midpoint displacement fractal.
*
* #param rand The random number generator
* #param roughness a roughness parameter
* #param width the width of the grid
* #param height the height of the grid
*/
public Noise(Random rand, float roughness, int width, int height) {
roughness_ = roughness / width;
grid_ = new float[width][height];
rand_ = (rand == null) ? new Random() : rand;
}
public void initialise() {
int xh = grid_.length - 1;
int yh = grid_[0].length - 1;
// set the corner points
grid_[0][0] = rand_.nextFloat() - 0.5f;
grid_[0][yh] = rand_.nextFloat() - 0.5f;
grid_[xh][0] = rand_.nextFloat() - 0.5f;
grid_[xh][yh] = rand_.nextFloat() - 0.5f;
// generate the fractal
generate(0, 0, xh, yh);
}
// Add a suitable amount of random displacement to a point
private float roughen(float v, int l, int h) {
return v + roughness_ * (float) (rand_.nextGaussian() * (h - l));
}
// generate the fractal
private void generate(int xl, int yl, int xh, int yh) {
int xm = (xl + xh) / 2;
int ym = (yl + yh) / 2;
if ((xl == xm) && (yl == ym)) return;
grid_[xm][yl] = 0.5f * (grid_[xl][yl] + grid_[xh][yl]);
grid_[xm][yh] = 0.5f * (grid_[xl][yh] + grid_[xh][yh]);
grid_[xl][ym] = 0.5f * (grid_[xl][yl] + grid_[xl][yh]);
grid_[xh][ym] = 0.5f * (grid_[xh][yl] + grid_[xh][yh]);
float v = roughen(0.5f * (grid_[xm][yl] + grid_[xm][yh]), xl + yl, yh
+ xh);
grid_[xm][ym] = v;
grid_[xm][yl] = roughen(grid_[xm][yl], xl, xh);
grid_[xm][yh] = roughen(grid_[xm][yh], xl, xh);
grid_[xl][ym] = roughen(grid_[xl][ym], yl, yh);
grid_[xh][ym] = roughen(grid_[xh][ym], yl, yh);
generate(xl, yl, xm, ym);
generate(xm, yl, xh, ym);
generate(xl, ym, xm, yh);
generate(xm, ym, xh, yh);
}
/**
* Dump out as a CSV
*/
public void printAsCSV() {
for(int i = 0;i < grid_.length;i++) {
for(int j = 0;j < grid_[0].length;j++) {
System.out.print(grid_[i][j]);
System.out.print(",");
}
System.out.println();
}
}
/**
* Convert to a Boolean array
* #return the boolean array
*/
public boolean[][] toBooleans() {
int w = grid_.length;
int h = grid_[0].length;
boolean[][] ret = new boolean[w][h];
for(int i = 0;i < w;i++) {
for(int j = 0;j < h;j++) {
ret[i][j] = grid_[i][j] < 0;
}
}
return ret;
}
/** For testing */
public static void main(String[] args) {
Noise n = new Noise(null, 1.0f, 250, 250);
n.initialise();
n.printAsCSV();
}
}

I have some perlin noise implementations and some other noise generation functions in my library code:
http://code.google.com/p/mikeralib/source/browse/#svn%2Ftrunk%2FMikera%2Fsrc%2Fmain%2Fjava%2Fmikera%2Fmath
Feel free to explore / use (code is open source GPL, based on the J3D code).

Related

Implementation of Floor Casting Producing Unexpected Errors

I have been following and translating a YouTube tutorial about how to create a raycasting engine as the person who made the video created theirs in C++ and I am making mine in Java.
Link To The Tutorial: https://www.youtube.com/watch?v=PC1RaETIx3Y
For the most part, following the creator's tutorial has turned out exactly as it is supposed to be. However when I try to implement the floor casting that they used I constantly get the following error:
java.lang.ArrayIndexOutOfBoundsException: https://i.stack.imgur.com/lxF27.png
Following the stack trace, it says that it has an error with I try to get the color value from the map position of the floor and ceiling arrays:
int mpF = mapF[(int)(texY/32.0)*mapX+(int)(texX/32.0)]*32*32;
int mpC = mapC[(int)(texY/32.0)*mapX+(int)(texX/32.0)]*32*32;
I have tried implementing other solutions to floor casting but I have a hard time grasping what it is doing and those solutions don't fit the engine that I am building. I was wondering if anyone knows what could be wrong with my implementation as my calculations should be functionally the same as the creator's. I would also accept answers that provide a different solution that makes use of how the engine is implemeted.
Their Code:
for(y=lineOff+lineH;y<320;y++)
{
float dy=y-(320/2.0), deg=degToRad(ra), raFix=cos(degToRad(FixAng(pa-ra)));
tx=px/2 + cos(deg)*158*32/dy/raFix;
ty=py/2 - sin(deg)*158*32/dy/raFix;
int mp=mapF[(int)(ty/32.0)*mapX+(int)(tx/32.0)]*32*32;
float c=All_Textures[((int)(ty)&31)*32 + ((int)(tx)&31)+mp]*0.7;
glColor3f(c/1.3,c/1.3,c);glPointSize(8);glBegin(GL_POINTS);glVertex2i(r*8+530,y);glEnd();
mp=mapC[(int)(ty/32.0)*mapX+(int)(tx/32.0)]*32*32;
c=All_Textures[((int)(ty)&31)*32 + ((int)(tx)&31)+mp]*0.7;
glColor3f(c/2.0,c/1.2,c/2.0);glPointSize(8);glBegin(GL_POINTS);glVertex2i(r*8+530,320-y);glEnd();
}
My Code:
//Draws Floor And Ceiling Tiles
for(int y = (int)(lineO+lineH); y<HEIGHT; y++) {
float dy = y - ((float)HEIGHT/2f), raFix = (float) Math.cos(ca);
texX = px/2 + (float)Math.cos(ra)*(HEIGHT/2)*32/dy/raFix;
texY = py/2 - (float)Math.sin(ra)*(HEIGHT/2)*32/dy/raFix;
int mpF = mapF[(int)(texY/32.0)*mapX+(int)(texX/32.0)]*32*32;
int mpC = mapC[(int)(texY/32.0)*mapX+(int)(texX/32.0)]*32*32;
Color c = colors[textures[((int)(texY)&31)*32 + ((int)(texX)&31)+mpF]];
g.setColor(getShade(c, ratio));
g.drawLine(drawX, y, drawX, y);
c = colors[textures[((int)(texY)&31)*32 + ((int)(texX)&31)+mpC]];
g.setColor(getShade(c, ratio));
g.drawLine(drawX, HEIGHT-y, drawX, HEIGHT-y);
}
Notes:
My engine makes use of "textures" through the use of an int array and gets the java.awt.Color object that corresponds to the value from the texture array within a java.awt.Color array
My engine does not make the use of x-planes, y-planes, or z-buffers like other ray casters do as the engine only needs the ray's hit position, the ray's angle, the player's position, and the distance between the player position and ray's position to properly calculate what it needs to draw on screen.
My engine uses three int arrays, the first being mapW, which represents the wall layouts and textures for the walls. The second being mapF, which is the floor textures for the walkable spaces. And the third being mapC which is the same in concept as the floor int array, but for the ceiling.
Any potential solutions need to allow me easy access to the value of the floor/ceiling tile that I am getting the texture from as doing so will allow me to later implement more complex lighting/shading techniques.
If it would be more helpful, here is the entire java file that I am doing the raycasting in:
package launcher;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import SwingUI.ComputerController;
import SwingUI.Program;
import world.Texture;
//This class extends a class I made in another Java Project, which I exported as a JAR file
//The class Program, is a JFrame subclass that implements the game loop
public class Game extends Program{
private static final long serialVersionUID = 1L;
private int mapX = 16, mapY = 16, mapS = 64, mapLen = mapX*mapY;
private int door = 4;
private int[] mapW = {
1,1,1,1,1,1,1,1, 2,2,2,2,2,2,2,2, //16
1,0,0,0,0,0,0,1, 2,0,0,0,0,0,0,2, //32
1,0,0,0,0,0,0,1, 2,0,0,0,0,0,0,2, //48
1,0,0,0,0,0,0,4, 0,0,0,0,0,0,0,2, //64
1,0,0,0,0,0,0,1, 2,0,0,0,0,0,0,2, //80
1,0,0,0,0,0,0,1, 2,0,0,0,0,0,0,2, //96
1,0,0,0,0,0,0,1, 2,0,0,0,0,0,0,2, //112
1,1,1,1,1,1,1,1, 2,2,2,4,2,2,2,2, //128
1,1,1,1,1,1,1,1, 3,3,3,0,3,3,3,3, //144
1,0,0,0,0,0,0,1, 3,0,0,0,0,0,0,3, //160
1,0,0,0,0,0,0,1, 3,0,0,0,0,0,0,3, //176
1,0,0,0,0,0,0,1, 3,0,0,0,0,0,0,3, //192
1,0,0,0,0,0,0,0, 4,0,0,0,0,0,0,3, //208
1,0,0,0,0,0,0,1, 3,0,0,0,0,0,0,3, //224
1,0,0,0,0,0,0,1, 3,0,0,0,0,0,0,3, //240
1,1,1,1,1,1,1,1, 3,3,3,3,3,3,3,3, //256
};
private int[] mapF = {
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, //16
0,1,1,1,1,1,1,0, 0,1,1,1,1,1,1,0, //32
0,1,1,1,1,1,1,0, 0,1,1,1,1,1,1,0, //48
0,1,1,1,1,1,1,0, 1,1,1,1,1,1,1,0, //64
0,1,1,1,1,1,1,0, 0,1,1,1,1,1,1,0, //80
0,1,1,1,1,1,1,0, 0,1,1,1,1,1,1,0, //96
0,1,1,1,1,1,1,0, 0,1,1,1,1,1,1,0, //112
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, //128
0,0,0,0,0,0,0,0, 0,0,0,1,0,0,0,0, //144
0,1,1,1,1,1,1,0, 0,1,1,1,1,1,1,0, //160
0,1,1,1,1,1,1,0, 0,1,1,1,1,1,1,0, //176
0,1,1,1,1,1,1,0, 0,1,1,1,1,1,1,0, //192
0,1,1,1,1,1,1,1, 0,1,1,1,1,1,1,0, //208
0,1,1,1,1,1,1,0, 0,1,1,1,1,1,1,0, //224
0,1,1,1,1,1,1,0, 0,1,1,1,1,1,1,0, //240
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, //256
};
private int[] mapC = {
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, //16
0,2,2,2,2,2,2,0, 0,2,2,2,2,2,2,0, //32
0,2,2,2,2,2,2,0, 0,2,2,2,2,2,2,0, //48
0,2,2,2,2,2,2,0, 2,2,2,2,2,2,2,0, //64
0,2,2,2,2,2,2,0, 0,2,2,2,2,2,2,0, //80
0,2,2,2,2,2,2,0, 0,2,2,2,2,2,2,0, //96
0,2,2,2,2,2,2,0, 0,2,2,2,2,2,2,0, //112
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, //128
0,0,0,0,0,0,0,0, 0,0,0,2,0,0,0,0, //144
0,2,2,2,2,2,2,0, 0,2,2,2,2,2,2,0, //160
0,2,2,2,2,2,2,0, 0,2,2,2,2,2,2,0, //176
0,2,2,2,2,2,2,0, 0,2,2,2,2,2,2,0, //192
0,2,2,2,2,2,2,2, 0,2,2,2,2,2,2,0, //208
0,2,2,2,2,2,2,0, 0,2,2,2,2,2,2,0, //224
0,2,2,2,2,2,2,0, 0,2,2,2,2,2,2,0, //240
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, //256
};
private int[] textures = Texture.textures;
private Color[] colors = Texture.colors;
public Game(String title) {
super(title);
}
#Override
public void init() {
setResizable(false);
px = 100;
py = 100;
}
float px,centX,py,centY,pdx,pdy,pa;
float velX, velY;
boolean forward,backward,left,right;
boolean tl,tr;
/**
* Updates all game objects
* In this case, it handles the camera
*/
#Override
public void updateObjects() {
forward = ComputerController.keys[KeyEvent.VK_W];
left = ComputerController.keys[KeyEvent.VK_A];
backward = ComputerController.keys[KeyEvent.VK_S];
right = ComputerController.keys[KeyEvent.VK_D];
tl = ComputerController.keys[KeyEvent.VK_LEFT];
tr = ComputerController.keys[KeyEvent.VK_RIGHT];
if(tl)
pa -= 0.05;
if(tr)
pa += 0.05;
if(pa > PI2)
pa -= PI2;
if(pa < 0)
pa += PI2;
pdx = (float)Math.cos(pa)*5;
pdy = (float)Math.sin(pa)*5;
velX = 0;
velY = 0;
if(forward) {
velX += pdx;
velY += pdy;
}
else if(backward) {
velX -= pdx;
velY -= pdy;
}
if(left) {
velX += pdy;
velY -= pdx;
}
else if(right) {
velX -= pdy;
velY += pdx;
}
int mpx = (int)(centX/mapS), mpy = (int)(centY/mapS), mpxO = (int)((centX+velX)/mapS), mpyO = (int)((centY+velY)/mapS);
if(mapW[mpy*mapX + mpxO] == 0)
px+=velX;
if(mapW[mpyO*mapX + mpx] == 0)
py+=velY;
if(ComputerController.keys[KeyEvent.VK_E]) {
mpxO = (int)((centX+pdx*10)/mapS);
mpyO = (int)((py+pdy*10)/mapS);
if(mapW[mpyO*mapX + mpxO] == door)
mapW[mpyO*mapX + mpxO] = 0;
}
centX = px+3;
centY = py+3;
}
BasicStroke brushSize = new BasicStroke(16);
BasicStroke reset = new BasicStroke(1);
/**
* Draws everything to the screen
*/
#Override
public void renderObjects(Graphics g) {
Graphics2D g2d = (Graphics2D)g;
g2d.setStroke(reset);
g2d.setColor(Color.GREEN);
g2d.fillRect(0, 0, WIDTH, HEIGHT);
castRays(g2d);
}
int fov = 64, fineness=4, totalRays = (int) (fov*fineness*(16/brushSize.getLineWidth())), renderDistance = 8;
float PI = (float) Math.PI, PI2 = (float) (2*Math.PI), PIHalf = (float) (Math.PI/2), PI3Half = (float) (3*Math.PI/2);
float DR = (float) Math.toRadians(1), DROffset = (float) (Math.toRadians(1)/fineness), drawXOffset = brushSize.getLineWidth()/fineness;
/**
* Implements Raycasting rendering
*/
private void castRays(Graphics2D g) {
int r,mxH,myH,mxV,myV,mpH,mpV,dofH,dofV;
float ra=fixAngle(pa-(DR*fov/2)),xo1=0,yo1=0, xo2=0,yo2=0;
g.setStroke(new BasicStroke(4));
for(r=0; r < totalRays; r++) {
dofH=0; dofV=0;
float aTan = (float) (-1/Math.tan(ra)), nTan = (float) -Math.tan(ra);
float distH = 1000000000,hx=centX,hy=centY,distV = 1000000000,vx=centX,vy=centY;
// Horizontal Line Check!!!!
if(ra>PI) {hy = (float) (((int)centY/mapS)*mapS-0.0001); hx = (centY-hy)*aTan+centX; yo1 = -mapS; xo1 = -yo1*aTan;}
else if(ra<PI) {hy = ((int)centY/mapS)*mapS+mapS; hx = (centY-hy)*aTan+centX; yo1 = mapS; xo1 = -yo1*aTan;}
else if(ra==PI || ra==0) {hx=centX;hy=centY;dofH=8;}
for(dofH=0; dofH < mapX; dofH++) {
mxH=(int)hx/mapS; myH=(int)hy/mapS; mpH=myH*mapX+mxH;
boolean withinMapH = hitWall(mpH);
if(withinMapH) {distH=getDistance(hx,hy,centX,centY);dofH=mapX;}
else {hx+=xo1;hy+=yo1;}
}
// Vertical Line Check!!!!
if(ra>PIHalf&&ra<PI3Half) {vx = (float) (((int)centX/mapS)*mapS-0.0001); vy = (centX-vx)*nTan+centY; xo2 = -mapS; yo2 = -xo2*nTan;}
else if(ra<PIHalf||ra>PI3Half) {vx = ((int)centX/mapS)*mapS+mapS; vy = (centX-vx)*nTan+centY; xo2 = mapS; yo2 = -xo2*nTan;}
else if(ra==PI||ra==0) {vy=centX;vx=centY;dofV=8;}
for(dofV=0; dofV < mapY; dofV++) {
mxV=(int)vx/mapS; myV=(int)vy/mapS; mpV=myV*mapX+mxV;
boolean withinMapV = hitWall(mpV);
if(withinMapV) {distV=getDistance(vx,vy,centX,centY);dofV=mapY;}
else {vx+=xo2;vy+=yo2;}
}
//Get Shortest Distance
float distT = 0, rx = 0, ry = 0;
if(distH<distV) {distT=distH;rx=hx;ry=hy;}
else if(distV<distH) {distT=distV;rx=vx;ry=vy;}
//Fix Fish-eye effect
float ca = fixAngle(pa-ra);
distT*=Math.cos(ca);
//Prepare for drawing Psuedo 3D Environment
float lineH = (mapS*WIDTH)/distT;
int drawX = r*(int)drawXOffset;
float stepY=32/lineH;
float texYOff=0;
if(lineH > HEIGHT) {
texYOff = (lineH-HEIGHT)/2;
lineH = HEIGHT;
}
float lineO = HEIGHT/2-lineH/2;
int mx=(int)rx/mapS, my=(int)ry/mapS, mp=my*mapX+mx, mt = mapW[mp]-1;
float texY=texYOff*stepY+mt*32, texX=0;
if(rx==vx) {
texX=(int)(ry/2)%32;
if(ra>PIHalf && ra<PI3Half) {texX = 31-texX;}
}
else {
texX=(int)(rx/2)%32;
if(ra<PI) {texX = 31-texX;}
}
//Draws Walls
float ratio = distT/(mapS*renderDistance);
for(int y = 0; y < (int)lineH; y++) {
Color c = colors[textures[(int)texY*32+(int)texX]];
g.setColor(getShade(c, ratio));
g.drawLine(drawX, y+(int)(lineO), drawX, y+(int)(lineO));
texY+=stepY;
}
//Draws Floor And Ceiling Tiles
for(int y = (int)(lineO+lineH); y<HEIGHT; y++)
{
float dy = y - ((float)HEIGHT/2f), raFix = (float) Math.cos(ca);
texX = px/2 + (float)Math.cos(ra)*(HEIGHT/2)*32/dy/raFix;
texY = py/2 - (float)Math.sin(ra)*(HEIGHT/2)*32/dy/raFix;
int mpF = mapF[(int)(texY/32.0)*mapX+(int)(texX/32.0)]*32*32;
int mpC = mapC[(int)(texY/32.0)*mapX+(int)(texX/32.0)]*32*32;
Color c = colors[textures[((int)(texY)&31)*32 + ((int)(texX)&31)+mpF]];
g.setColor(c);
g.drawLine(drawX, y, drawX, y);
c = colors[textures[((int)(texY)&31)*32 + ((int)(texX)&31)+mpC]];
g.setColor(c);
g.drawLine(drawX, HEIGHT-y, drawX, HEIGHT-y);
}
//Get Next Angle
ra = fixAngle(ra+DROffset);
}
}
/**
* Detects if a ray has hit a wall
* #param mp
* The world-position of the ray's end point
* #return
* True if the position is within bounds and is a wall tile
*/
public boolean hitWall(int mp) {
return (mp >= 0 && mp < mapLen && mapW[mp] > 0);
}
/**
* Resets angles to the correct value if they are above 2PI radians (360 degrees) or below 0 radians (0 degrees)
* #param angle
* The angle we are fixing
* #return
* The fixed angle
*/
public float fixAngle(float angle) {
if(angle > PI2) {
angle -= PI2;
}
if(angle < 0) {
angle += PI2;
}
return angle;
}
/**
*
* Gets the distance between two points
* #param ax
* the X Coordinate of the first point
* #param ay
* the Y Coordinate of the first point
*
* #param bx
* the X Coordinate of the second point
* #param by
* the Y Coordinate of the second point
* #return
* The distance between the two points
*/
public float getDistance(float ax, float ay, float bx, float by) {
return (float)Math.sqrt( (bx-ax)*(bx-ax) + (by-ay)*(by-ay) );
}
/**
*
* Gets a shade of a given color relative to the ratio given
* #param color
* The color we want to change
* #param ratio
* What percent it is close to the color black
* #return
* The new shade
*/
private Color getShade(Color color, float ratio){
int r = (int) Math.round(Math.max(0, color.getRed() - 255 * ratio));
int g = (int) Math.round(Math.max(0, color.getGreen() - 255 * ratio));
int b = (int) Math.round(Math.max(0, color.getBlue() - 255 * ratio));
int rgb = (r << 16) | (g << 8) | b;
return new Color(rgb);
}
/**
* Creates GUI on launch
*/
public static void main(String[] args) {
new Game("RAYCASTER");
}
}
The fixAngle method resets angles to the correct value if they are above 2PI radians (360 degrees) or below 0 radians (0 degrees). For example, an angle of 5PI/2 would return PI/2, and an angle of -3PI/2 would return PI/2
The getShade method returns a shade of a given color relative to the ratio given. The ratio is what percent (in decimal form) the desired color is close to the color black
I'll be a bit more helpful about answering your question. I recently had the same problem and was coincidentally working from the same tutorial as you were. I ran into the same problem as you and it took me 3 days to figure out the solution. I apologize for not understanding the maths behind it, as I am still in high school and I have not taken triginometry. However, after trial and error, I have found a solution. I really hope that this helps you with your project and good luck with future projects!
for (int y=(int)(lineO+lineH); y<height; y++)
{
//------------------------draw floor------------------------\\
float dy=y-(320/2.0), raFix=cos(ca);
tx=px/2 + cos(ra)*158*32/dy/raFix;
ty=py/2 + sin(ra)*158*32/dy/raFix;
int MP=mapF[(int)(ty/32.0)*mapX+(int)(tx/32.0)]*32*32;
float c=All_Textures[((int)(ty)&31)*32 + ((int)(tx)&31)+MP]*0.7;
fill(c/1.3*255, c/1.3*255, c*255);//code to change the color of the boxes
rect(r*8+530, y, 8, 8);//code to draw the boxes
//------------------------draw ceiling------------------------\\
mp=mapC[(int)(ty/32.0)*mapX+(int)(tx/32.0)]*32*32;
c=All_Textures[((int)(ty)&31)*32 + ((int)(tx)&31)+mp]*0.7;
fill(c/2.0*255, c/1.2*255, c/2.0*255); //code to change the color of the boxes
rect(r*8+530, 320-y, 8, 8); //code to draw the boxes
}

SUNFLOW: Paint only contour edges of 3D mesh

This is literal copy of WireframeShader from really old and long time abandoned java app called Sunflow:
package org.sunflow.core.shader;
import org.sunflow.SunflowAPI;
import org.sunflow.core.ParameterList;
import org.sunflow.core.Shader;
import org.sunflow.core.ShadingState;
import org.sunflow.image.Color;
import org.sunflow.math.Matrix4;
import org.sunflow.math.Point3;
public class WireframeShader implements Shader {
private Color lineColor;
private Color fillColor;
private float width;
private float cosWidth;
public WireframeShader() {
lineColor = Color.BLACK;
fillColor = Color.WHITE;
// pick a very small angle - should be roughly the half the angular width of a pixel
width = (float) (Math.PI * 0.5 / 4096);
cosWidth = (float) Math.cos(width);
}
public boolean update(ParameterList pl, SunflowAPI api) {
lineColor = pl.getColor("line", lineColor);
fillColor = pl.getColor("fill", fillColor);
width = pl.getFloat("width", width);
cosWidth = (float) Math.cos(width);
return true;
}
public Color getMaterialColor() {
return lineColor;
}
public Color getFillColor(ShadingState state) {
return fillColor;
}
public Color getLineColor(ShadingState state) {
return lineColor;
}
public Color getRadiance(ShadingState state) {
Point3[] p = new Point3[3];
if (!state.getTrianglePoints(p)) {
return getFillColor(state);
}
// transform points into camera space
Point3 center = state.getPoint();
Matrix4 w2c = state.getWorldToCamera();
center = w2c.transformP(center);
for (int i = 0; i < 3; i++) {
p[i] = w2c.transformP(state.getInstance().transformObjectToWorld(p[i]));
}
float cn = 1.0f / (float) Math.sqrt(center.x * center.x + center.y * center.y + center.z * center.z);
for (int i = 0, i2 = 2; i < 3; i2 = i, i++) {
// compute orthogonal projection of the shading point onto each triangle edge as in:
// http://mathworld.wolfram.com/Point-LineDistance3-Dimensional.html
float t = (center.x - p[i].x) * (p[i2].x - p[i].x);
t += (center.y - p[i].y) * (p[i2].y - p[i].y);
t += (center.z - p[i].z) * (p[i2].z - p[i].z);
t /= p[i].distanceToSquared(p[i2]);
float projx = (1 - t) * p[i].x + t * p[i2].x;
float projy = (1 - t) * p[i].y + t * p[i2].y;
float projz = (1 - t) * p[i].z + t * p[i2].z;
float n = 1.0f / (float) Math.sqrt(projx * projx + projy * projy + projz * projz);
// check angular width
float dot = projx * center.x + projy * center.y + projz * center.z;
if (dot * n * cn >= cosWidth) {
return getLineColor(state);
}
}
return getFillColor(state);
}
public void scatterPhoton(ShadingState state, Color power) {
}
#Override
public float getReflectionValue() {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
}
It would render any 3D mesh so that every edge of mesh triangle would be painted thus creating a wireframe-like visual (see pic below)
My question is: does anybody know how to change/update the code (specifically getRadiance() method) so it would only paint contour edges of mesh so it would look like in the pic below?
This is harder than you think, because it cannot be done by using information from just a single triangle. You need to check all edges in the mesh and for each edge take the two faces which contain it. You draw the edge if and only if the normals of these two faces are not the same (differ enough).

Draw equilateral triangles using a nested for loop? [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 4 years ago.
Improve this question
I am trying to figure out how to use a nested for loop to produce this pattern below:
Thus far, I have
public class Triangle {
public static final int SIZE = 600;
public static final int INITIAL_X = 300;
public static final int INITIAL_Y = 50;
public static final int SIDE = 100;
/**
*
*
* #param args command line arguments
*/
public static void main(String[] args) {
Scanner console = new Scanner(System.in);
DrawingPanel panel = new DrawingPanel(SIZE, SIZE);
panel.setBackground(Color.WHITE);
System.out.print("Enter number of rows (1-5): " );
int row = console.nextInt();
if(row < 1) {
row = 1;
} else if(row > 5) {
row = 5;
}
System.out.print("Specify red value (0-255): ");
int red = console.nextInt();
if(red < 0) {
red = 0;
} else if(red > 255) {
red = 255;
System.out.print("Specify green value (0-255): ");
int green = console.nextInt();
if(green < 0) {
green = 0;
} else if(green > 255) {
green = 255;
}
System.out.print("Specify blue value (0-255): ");
int blue = console.nextInt();
if(blue < 0) {
blue = 0;
} else if(blue > 255) {
blue = 255;
}
Graphics g = panel.getGraphics(); //Initializes graphics panel.
g.setColor(new Color(red, green, blue));
for (int i = 1; i <= row; i++) {
for (int x = 1; x <= row; x++) {
int lx = sx - SIDE/2;
int rx = sx + SIDE/2;
int ly = (int) (sy + SIDE*Math.sqrt(3)/2);
int ry = ly;
System.out.println("\n*CLOSE the Drawing Panel to exit the program*");
}
/**
* Draws an equilateral triangle with topmost point at (x, y) with the
* given side length and color.
*
* #param g
* #param color
* #param x
* #param y
* #param sideLength
*/
public static void drawTriangle(Graphics g, int sx, int sy, int rows) {
g.drawLine(sx, sy, lx, ly);
g.drawLine(sx, sy, rx, ry);
g.drawLine(lx, ly, rx, ry);
}
This code is definitely not finished. I only want to use the java.awt.* and java.util.* packages to solve this. My trouble is mainly with creating a nested for loop in the main method, but I believe I have a grasp on the scanner part of this assignment. I have used the equilateral triangle formulas below that are derived from the Pythagorean theorem. Each row is supposed to have that many triangles (for instance, the 5th row is supposed to have 5 triangles). The triangles are contiguous and the lower left/right points of each triangle form the uppermost points of the triangles in the row below. The (x, y) coordinates of the topmost point of the triangle in the first row must be (300, 50). Please let me know if anyone can help.
A recursive approach sounds easier than iterative so it could look like this:
private void drawTree(double x, double y, int depth, int maxDepth, Graphics graphics, double sideLength) {
if (depth >= maxDepth) {
return;
}
double leftX = x - sideLength / 2;
double leftY = y + Math.sqrt(sideLength * 3) / 2;
double rightX = x + sideLength / 2;
double rightY = leftY;
//draw line from (x,y) -> (leftX, leftY)
graphics.drawLine(x, y, leftX, leftY);
//draw line from (x,y) -> (rightX, rightY)
graphics.drawLine(x, y, rightX, rightY);
//draw line from (leftX, leftY) -> (rightX, rightY)
graphics.drawLine(leftX, leftX, rightX, rightY);
drawTree(leftX, leftY, depth + 1, maxDepth, graphics, sideLength);
drawTree(rightX, rightY, depth + 1, maxDepth, graphics, sideLength);
}

Java - Adjacency matrix based on distance between vertices

What I'm trying to do is come up with a way to generate n random points on a graph (displaying it isn't necessary). A point is randomly selected and connected to the point closest to it (or the next closest if it's already connected to the best option) in a way so that no two lines intersect. This repeats until no more connections are possible. The vertices are meant to represent regions on a map, and connections represent adjacency. The following code I have thus far is as follows, taken from http://javaingrab.blogspot.com/2012/12/m-way-graph-coloring-with-backtracking.html:
public class MWayGrColor{
/*G is graph's adjacency matrix and x is solution vector */
private int G[][],x[],n,m,soln;
public void mColoring(int k){ //backtracking function
for(int i=1;i<=n;i++){
next_color(k); //coloring kth vertex
if(x[k]==0)
return; //if unsuccessful then backtrack
if(k==n) //if all colored then show
write();
else
mColoring(k+1); /* successful but still left to color */
}
}
private void next_color(int k){
do{
int i=1;
x[k]=(x[k]+1)%(m+1);
if(x[k]==0)
return;
for(i=1;i<=n;i++)
if(G[i][k]!=0 && x[k]==x[i]) /* checking adjacency and not same color */
break;
if(i==n+1) return; //new color found
}while(true);
}
private void write(){
System.out.print("\nColoring(V C) # "+(++soln)+"-->");
for(int i=1;i<=n;i++)
System.out.print("\t("+i+" "+x[i]+")"); //solution vector
}
public void input(){
java.util.Scanner sc=new java.util.Scanner(System.in);
System.out.print("Enter no. of vertices : ");
n=sc.nextInt();
G=new int[n+1][n+1];
x=new int[n+1];
System.out.print("Enter no. of colors : ");
m=sc.nextInt();
System.out.println("Enter adjacency matrix-->");
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
G[i][j]=sc.nextInt();
}
public static void main (String[] args) {
MWayGrColor obj=new MWayGrColor();
obj.input();
obj.mColoring(1);
if(obj.soln==0)
System.out.println("\nNeed more than "+obj.m+" colors");
else
System.out.print("\nTOTAL SOLN : "+obj.soln);
}
}
As noted, the map doesn't need to be visually represented, as the current method of display is adequate. I'm aware of the Point2D.Double class and the Line2D class, and I was originally going to just start generating points and use the lines to create the adjacency matrix already shown in the code, but the methods for connecting points and avoiding repetition are extremely confusing to me in how they should be implemented. How can I accomplish this generation of an adjacency matrix?
It's still not clear what the actual question is. It sounds like "this is so complicated, I don't get it done". However, unless there are strict requirements about the approach and its running time etc., one can pragmatically write down what has to be done:
do
{
V v0 = randomVertex();
V v1 = findClosestUnconnected(v0);
if (line(v0,v1).intersectsNoOtherLine())
{
insert(line(v0,v1));
}
} while (insertedNewLine);
Of course, this implies some searching. For large graphs there may be some sophisticated data structures to accelerate this. Particularly the search for the nearest (unconnected) neighbor may be accelerated with the classical structures like KD-trees etc. But this seems to be unrelated to the original question.
The handing of the adjacency matrix can be made a bit more convenient with a wrapper that offers methods that allow a more "natural" description:
class Graph
{
private final boolean matrix[][];
void addEdge(V v0, V v1)
{
matrix[v0.index][v1.index] = true;
matrix[v1.index][v0.index] = true;
}
boolean hasEdge(V v0, V v1)
{
return matrix[v0.index][v1.index];
}
}
But in this case, this is only a minor, syntactical simplification.
An example, only as a VERY q&d sketch:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class NonIntersectingAdjacencies
{
public static void main(String[] args)
{
Random random = new Random(0);
int numVertices = 25;
List<AdjacencyVertex> vertices =
createRandomVertices(numVertices, random);
final AdjacencyGraph adjacencyGraph =
new AdjacencyGraph(vertices);
boolean createdNewLine = true;
while (createdNewLine)
{
createdNewLine = false;
List<Integer> indices =
createShuffledList(numVertices, random);
for (int i=0; i<numVertices; i++)
{
int randomIndex = indices.get(i);
AdjacencyVertex randomVertex = vertices.get(randomIndex);
AdjacencyVertex closest =
findClosestUnconnected(randomVertex, adjacencyGraph);
if (closest != null)
{
if (!intersectsOtherLine(
randomVertex, closest, adjacencyGraph))
{
adjacencyGraph.addEdge(randomVertex, closest);
createdNewLine = true;
}
}
}
}
AdjacencyGraphPanel.show(adjacencyGraph);
}
private static List<AdjacencyVertex> createRandomVertices(
int numVertices, Random random)
{
List<AdjacencyVertex> vertices = new ArrayList<AdjacencyVertex>();
for (int i=0; i<numVertices; i++)
{
AdjacencyVertex v = new AdjacencyVertex();
v.index = i;
v.x = random.nextDouble();
v.y = random.nextDouble();
vertices.add(v);
}
return vertices;
}
private static List<Integer> createShuffledList(
int maxValue, Random random)
{
List<Integer> list = new ArrayList<Integer>();
for (int i=0; i<maxValue; i++)
{
list.add(i);
}
Collections.shuffle(list, random);
return list;
}
private static boolean intersectsOtherLine(
AdjacencyVertex v0, AdjacencyVertex v1,
AdjacencyGraph adjacencyGraph)
{
Line2D newLine = new Line2D.Double(
v0.x, v0.y, v1.x, v1.y);
List<AdjacencyVertex> vertices = adjacencyGraph.getVertices();
for (int i=0; i<vertices.size(); i++)
{
for (int j=0; j<vertices.size(); j++)
{
if (i == j)
{
continue;
}
AdjacencyVertex oldV0 = vertices.get(i);
AdjacencyVertex oldV1 = vertices.get(j);
if (adjacencyGraph.hasEdge(oldV0, oldV1))
{
Line2D oldLine = new Line2D.Double(
oldV0.x, oldV0.y, oldV1.x, oldV1.y);
if (Intersection.intersect(oldLine, newLine))
{
return true;
}
}
}
}
return false;
}
private static AdjacencyVertex findClosestUnconnected(
AdjacencyVertex v,
AdjacencyGraph adjacencyGraph)
{
double minDistanceSquared = Double.MAX_VALUE;
AdjacencyVertex closest = null;
List<AdjacencyVertex> vertices = adjacencyGraph.getVertices();
for (int i=0; i<vertices.size(); i++)
{
AdjacencyVertex other = vertices.get(i);
if (other.index == v.index)
{
continue;
}
if (adjacencyGraph.hasEdge(v, other))
{
continue;
}
double dx = other.x - v.x;
double dy = other.y - v.y;
double distanceSquared = Math.hypot(dx, dy);
if (distanceSquared < minDistanceSquared)
{
minDistanceSquared = distanceSquared;
closest = other;
}
}
return closest;
}
}
class AdjacencyVertex
{
double x;
double y;
int index;
}
class AdjacencyGraph
{
private final boolean matrix[][];
private final List<AdjacencyVertex> vertices;
AdjacencyGraph(List<AdjacencyVertex> vertices)
{
this.vertices = vertices;
this.matrix = new boolean[vertices.size()][vertices.size()];
}
List<AdjacencyVertex> getVertices()
{
return vertices;
}
void addEdge(AdjacencyVertex v0, AdjacencyVertex v1)
{
matrix[v0.index][v1.index] = true;
matrix[v1.index][v0.index] = true;
}
boolean hasEdge(AdjacencyVertex v0, AdjacencyVertex v1)
{
return matrix[v0.index][v1.index];
}
}
//============================================================================
// Only helper stuff below this line...
class AdjacencyGraphPanel extends JPanel
{
public static void show(final AdjacencyGraph adjacencyGraph)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
createAndShowGUI(adjacencyGraph);
}
});
}
private static void createAndShowGUI(AdjacencyGraph adjacencyGraph)
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(new AdjacencyGraphPanel(adjacencyGraph));
f.setSize(600,600);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
private final AdjacencyGraph adjacencyGraph;
public AdjacencyGraphPanel(AdjacencyGraph adjacencyGraph)
{
this.adjacencyGraph = adjacencyGraph;
}
#Override
protected void paintComponent(Graphics gr)
{
super.paintComponent(gr);
Graphics2D g = (Graphics2D)gr;
int offsetX = 30;
int offsetY = 30;
int w = getWidth() - offsetX - offsetX;
int h = getHeight() - offsetY - offsetY;
g.setColor(Color.BLACK);
List<AdjacencyVertex> vertices = adjacencyGraph.getVertices();
for (int i=0; i<vertices.size(); i++)
{
for (int j=0; j<vertices.size(); j++)
{
if (i == j)
{
continue;
}
AdjacencyVertex v0 = vertices.get(i);
AdjacencyVertex v1 = vertices.get(j);
if (adjacencyGraph.hasEdge(v0, v1))
{
Line2D newLine = new Line2D.Double(
offsetX + v0.x*w,
offsetY + v0.y*h,
offsetX + v1.x*w,
offsetY + v1.y*h);
g.draw(newLine);
}
}
}
g.setColor(Color.BLUE);
for (int i=0; i<vertices.size(); i++)
{
AdjacencyVertex v = vertices.get(i);
int ix = (int)(offsetX + v.x * w);
int iy = (int)(offsetY + v.y * h);
g.fill(new Ellipse2D.Double(
ix - 5, iy - 5, 10, 10));
g.drawString(String.valueOf(i), ix, iy+16);
}
}
}
class Intersection
{
static boolean intersect(Line2D line0, Line2D line1)
{
Point2D location = new Point2D.Double();
Point2D intersection =
Intersection.computeIntersectionSegmentSegment(
line0, line1, location);
if (intersection == null)
{
return false;
}
return !isAtLineAnd(location);
}
private static boolean isAtLineAnd(Point2D location)
{
double EPSILON = 0.05;
if (Math.abs(location.getX()) < EPSILON)
{
return true;
}
if (Math.abs(location.getX()-1) < EPSILON)
{
return true;
}
if (Math.abs(location.getY()) < EPSILON)
{
return true;
}
if (Math.abs(location.getY()-1) < EPSILON)
{
return true;
}
return false;
}
/**
* Epsilon for floating point computations
*/
private static final double epsilon = 1e-6f;
/**
* Computes the intersection of the specified line segments and returns
* the intersection point, or <code>null</code> if the line segments do
* not intersect.
*
* #param line0 The first line segment
* #param line1 The second line segment
* #param location Optional location that stores the
* relative location of the intersection point on
* the given line segments
* #return The intersection point, or <code>null</code> if
* there is no intersection.
*/
public static Point2D computeIntersectionSegmentSegment(
Line2D line0, Line2D line1, Point2D location)
{
return computeIntersectionSegmentSegment(
line0.getX1(), line0.getY1(), line0.getX2(), line0.getY2(),
line1.getX1(), line1.getY1(), line1.getX2(), line1.getY2(),
location);
}
/**
* Computes the intersection of the specified line segments and returns
* the intersection point, or <code>null</code> if the line segments do
* not intersect.
*
* #param s0x0 x-coordinate of point 0 of line segment 0
* #param s0y0 y-coordinate of point 0 of line segment 0
* #param s0x1 x-coordinate of point 1 of line segment 0
* #param s0y1 y-coordinate of point 1 of line segment 0
* #param s1x0 x-coordinate of point 0 of line segment 1
* #param s1y0 y-coordinate of point 0 of line segment 1
* #param s1x1 x-coordinate of point 1 of line segment 1
* #param s1y1 y-coordinate of point 1 of line segment 1
* #param location Optional location that stores the
* relative location of the intersection point on
* the given line segments
* #return The intersection point, or <code>null</code> if
* there is no intersection.
*/
public static Point2D computeIntersectionSegmentSegment(
double s0x0, double s0y0,
double s0x1, double s0y1,
double s1x0, double s1y0,
double s1x1, double s1y1,
Point2D location)
{
if (location == null)
{
location = new Point2D.Double();
}
Point2D result = computeIntersectionLineLine(
s0x0, s0y0, s0x1, s0y1, s1x0, s1y0, s1x1, s1y1, location);
if (location.getX() >= 0 && location.getX() <= 1.0 &&
location.getY() >= 0 && location.getY() <= 1.0)
{
return result;
}
return null;
}
/**
* Computes the intersection of the specified lines and returns the
* intersection point, or <code>null</code> if the lines do not
* intersect.
*
* Ported from
* http://www.geometrictools.com/LibMathematics/Intersection/
* Wm5IntrSegment2Segment2.cpp
*
* #param s0x0 x-coordinate of point 0 of line segment 0
* #param s0y0 y-coordinate of point 0 of line segment 0
* #param s0x1 x-coordinate of point 1 of line segment 0
* #param s0y1 y-coordinate of point 1 of line segment 0
* #param s1x0 x-coordinate of point 0 of line segment 1
* #param s1y0 y-coordinate of point 0 of line segment 1
* #param s1x1 x-coordinate of point 1 of line segment 1
* #param s1y1 y-coordinate of point 1 of line segment 1
* #param location Optional location that stores the
* relative location of the intersection point on
* the given line segments
* #return The intersection point, or <code>null</code> if
* there is no intersection.
*/
public static Point2D computeIntersectionLineLine(
double s0x0, double s0y0,
double s0x1, double s0y1,
double s1x0, double s1y0,
double s1x1, double s1y1,
Point2D location)
{
double dx0 = s0x1 - s0x0;
double dy0 = s0y1 - s0y0;
double dx1 = s1x1 - s1x0;
double dy1 = s1y1 - s1y0;
double len0 = Math.sqrt(dx0*dx0+dy0*dy0);
double len1 = Math.sqrt(dx1*dx1+dy1*dy1);
double dir0x = dx0 / len0;
double dir0y = dy0 / len0;
double dir1x = dx1 / len1;
double dir1y = dy1 / len1;
double c0x = s0x0 + dx0 * 0.5;
double c0y = s0y0 + dy0 * 0.5;
double c1x = s1x0 + dx1 * 0.5;
double c1y = s1y0 + dy1 * 0.5;
double cdx = c1x - c0x;
double cdy = c1y - c0y;
double dot = dotPerp(dir0x, dir0y, dir1x, dir1y);
if (Math.abs(dot) > epsilon)
{
double dot0 = dotPerp(cdx, cdy, dir0x, dir0y);
double dot1 = dotPerp(cdx, cdy, dir1x, dir1y);
double invDot = 1.0/dot;
double s0 = dot1*invDot;
double s1 = dot0*invDot;
if (location != null)
{
double n0 = (s0 / len0) + 0.5;
double n1 = (s1 / len1) + 0.5;
location.setLocation(n0, n1);
}
double x = c0x + s0 * dir0x;
double y = c0y + s0 * dir0y;
return new Point2D.Double(x,y);
}
return null;
}
/**
* Returns the perpendicular dot product, i.e. the length
* of the vector (x0,y0,0)x(x1,y1,0).
*
* #param x0 Coordinate x0
* #param y0 Coordinate y0
* #param x1 Coordinate x1
* #param y1 Coordinate y1
* #return The length of the cross product vector
*/
private static double dotPerp(double x0, double y0, double x1, double y1)
{
return x0*y1 - y0*x1;
}
}

Calculating the Moment Of Inertia for a concave 2D polygon relative to its orgin

I want to compute the moment of inertia of a (2D) concave polygon. I found this on the internet. But I'm not very sure how to interpret the formula...
Formula http://img101.imageshack.us/img101/8141/92175941c14cadeeb956d8f.gif
1) Is this formula correct?
2) If so, is my convertion to C++ correct?
float sum (0);
for (int i = 0; i < N; i++) // N = number of vertices
{
int j = (i + 1) % N;
sum += (p[j].y - p[i].y) * (p[j].x + p[i].x) * (pow(p[j].x, 2) + pow(p[i].x, 2)) - (p[j].x - p[i].x) * (p[j].y + p[i].y) * (pow(p[j].y, 2) + pow(p[i].y, 2));
}
float inertia = (1.f / 12.f * sum) * density;
Martijn
#include <math.h> //for abs
float dot (vec a, vec b) {
return (a.x*b.x + a.y*b.y);
}
float lengthcross (vec a, vec b) {
return (abs(a.x*b.y - a.y*b.x));
}
...
do stuff
...
float sum1=0;
float sum2=0;
for (int n=0;n<N;++n) { //equivalent of the Σ
sum1 += lengthcross(P[n+1],P[n])*
(dot(P[n+1],P[n+1]) + dot(P[n+1],P[n]) + dot(P[n],P[n]));
sum2 += lengthcross(P[n+1],P[n]);
}
return (m/6*sum1/sum2);
Edit: Lots of small math changes
I think you have more work to do that merely translating formulas into code. You need to understand exactly what this formula means.
When you have a 2D polygon, you have three moments of inertia you can calculate relative to a given coordinate system: moment about x, moment about y, and polar moment of inertia. There's a parallel axis theorem that allows you to translate from one coordinate system to another.
Do you know precisely which moment and coordinate system this formula applies to?
Here's some code that might help you, along with a JUnit test to prove that it works:
import java.awt.geom.Point2D;
/**
* PolygonInertiaCalculator
* User: Michael
* Date: Jul 25, 2010
* Time: 9:51:47 AM
*/
public class PolygonInertiaCalculator
{
private static final int MIN_POINTS = 2;
public static double dot(Point2D u, Point2D v)
{
return u.getX()*v.getX() + u.getY()*v.getY();
}
public static double cross(Point2D u, Point2D v)
{
return u.getX()*v.getY() - u.getY()*v.getX();
}
/**
* Calculate moment of inertia about x-axis
* #param poly of 2D points defining a closed polygon
* #return moment of inertia about x-axis
*/
public static double ix(Point2D [] poly)
{
double ix = 0.0;
if ((poly != null) && (poly.length > MIN_POINTS))
{
double sum = 0.0;
for (int n = 0; n < (poly.length-1); ++n)
{
double twiceArea = poly[n].getX()*poly[n+1].getY() - poly[n+1].getX()*poly[n].getY();
sum += (poly[n].getY()*poly[n].getY() + poly[n].getY()*poly[n+1].getY() + poly[n+1].getY()*poly[n+1].getY())*twiceArea;
}
ix = sum/12.0;
}
return ix;
}
/**
* Calculate moment of inertia about y-axis
* #param poly of 2D points defining a closed polygon
* #return moment of inertia about y-axis
* #link http://en.wikipedia.org/wiki/Second_moment_of_area
*/
public static double iy(Point2D [] poly)
{
double iy = 0.0;
if ((poly != null) && (poly.length > MIN_POINTS))
{
double sum = 0.0;
for (int n = 0; n < (poly.length-1); ++n)
{
double twiceArea = poly[n].getX()*poly[n+1].getY() - poly[n+1].getX()*poly[n].getY();
sum += (poly[n].getX()*poly[n].getX() + poly[n].getX()*poly[n+1].getX() + poly[n+1].getX()*poly[n+1].getX())*twiceArea;
}
iy = sum/12.0;
}
return iy;
}
/**
* Calculate polar moment of inertia xy
* #param poly of 2D points defining a closed polygon
* #return polar moment of inertia xy
* #link http://en.wikipedia.org/wiki/Second_moment_of_area
*/
public static double ixy(Point2D [] poly)
{
double ixy = 0.0;
if ((poly != null) && (poly.length > MIN_POINTS))
{
double sum = 0.0;
for (int n = 0; n < (poly.length-1); ++n)
{
double twiceArea = poly[n].getX()*poly[n+1].getY() - poly[n+1].getX()*poly[n].getY();
sum += (poly[n].getX()*poly[n+1].getY() + 2.0*poly[n].getX()*poly[n].getY() + 2.0*poly[n+1].getX()*poly[n+1].getY() + poly[n+1].getX()*poly[n].getY())*twiceArea;
}
ixy = sum/24.0;
}
return ixy;
}
/**
* Calculate the moment of inertia of a 2D concave polygon
* #param poly array of 2D points defining the perimeter of the polygon
* #return moment of inertia
* #link http://www.physicsforums.com/showthread.php?t=43071
* #link http://www.physicsforums.com/showthread.php?t=25293
* #link http://stackoverflow.com/questions/3329383/help-me-with-converting-latex-formula-to-code
*/
public static double inertia(Point2D[] poly)
{
double inertia = 0.0;
if ((poly != null) && (poly.length > MIN_POINTS))
{
double numer = 0.0;
double denom = 0.0;
double scale;
double mag;
for (int n = 0; n < (poly.length-1); ++n)
{
scale = dot(poly[n + 1], poly[n + 1]) + dot(poly[n + 1], poly[n]) + dot(poly[n], poly[n]);
mag = Math.sqrt(cross(poly[n], poly[n+1]));
numer += mag * scale;
denom += mag;
}
inertia = numer / denom / 6.0;
}
return inertia;
}
}
Here's the JUnit test to accompany it:
import org.junit.Test;
import java.awt.geom.Point2D;
import static org.junit.Assert.assertEquals;
/**
* PolygonInertiaCalculatorTest
* User: Michael
* Date: Jul 25, 2010
* Time: 10:16:04 AM
*/
public class PolygonInertiaCalculatorTest
{
#Test
public void testTriangle()
{
Point2D[] poly =
{
new Point2D.Double(0.0, 0.0),
new Point2D.Double(1.0, 0.0),
new Point2D.Double(0.0, 1.0)
};
// Moment of inertia about the y1 axis
// http://www.efunda.com/math/areas/triangle.cfm
double expected = 1.0/3.0;
double actual = PolygonInertiaCalculator.inertia(poly);
assertEquals(expected, actual, 1.0e-6);
}
#Test
public void testSquare()
{
Point2D[] poly =
{
new Point2D.Double(0.0, 0.0),
new Point2D.Double(1.0, 0.0),
new Point2D.Double(1.0, 1.0),
new Point2D.Double(0.0, 1.0)
};
// Polar moment of inertia about z axis
// http://www.efunda.com/math/areas/Rectangle.cfm
double expected = 2.0/3.0;
double actual = PolygonInertiaCalculator.inertia(poly);
assertEquals(expected, actual, 1.0e-6);
}
#Test
public void testRectangle()
{
// This gives the moment of inertia about the y axis for a coordinate system
// through the centroid of the rectangle
Point2D[] poly =
{
new Point2D.Double(0.0, 0.0),
new Point2D.Double(4.0, 0.0),
new Point2D.Double(4.0, 1.0),
new Point2D.Double(0.0, 1.0)
};
double expected = 5.0 + 2.0/3.0;
double actual = PolygonInertiaCalculator.inertia(poly);
assertEquals(expected, actual, 1.0e-6);
double ix = PolygonInertiaCalculator.ix(poly);
double iy = PolygonInertiaCalculator.iy(poly);
double ixy = PolygonInertiaCalculator.ixy(poly);
assertEquals(ix, (1.0 + 1.0/3.0), 1.0e-6);
assertEquals(iy, (21.0 + 1.0/3.0), 1.0e-6);
assertEquals(ixy, 4.0, 1.0e-6);
}
}
For reference, here's a mutable 2D org.gcs.kinetic.Vector implementation and a more versatile, immutable org.jscience.mathematics.vector implementation. This article on Calculating a 2D Vector’s Cross Product is helpful, too.
I did it with Tesselation. And take the MOI's all together.

Categories