ok, so i've been working on this project for a long time now, a falling sand game, on and off. it works almost the way i want it too... 2 problems, one i don't think this community will be able to help me with, the other, the optimization, i have higher hopes for.

the first one, less likely to have a solution is that heat doesn't spread right, at the moment i am averaging two heat values, the current and a neighboring pixel's, to get the new current heat, i believe this is done in the doHeat() method, but my code is sloppy and it has been a while since i did anything too it.

the second, is that it runs slowly, especially with a lot of stuff on the screen. there are a lot of loops, and i cannot think of a way to reduce the loop count, or could accessing arrayLists also be limiting the speed?

i apologize for the large size of this code, and the lack of useful comments. the entirety of the source is attached in a zip (including a main class, an class that adds to the array lists in this class, and a class that contains an editor for the physics of the sand), to find out more about the project my site has a page on it...

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.awt.image.*;
import javax.swing.*;
/**
* Workings of Project.
* @author SciWizEH
* @version 3
/
public class SandEngine extends JPanel implements Runnable,
MouseListener, MouseMotionListener {
//check neighbor interactions?
boolean doNeighbor = true;
//calculate heat?
boolean doHeat = true;
//in bounds?
boolean inBounds = false;
//paused?
static boolean pause = false;
//width
static int pWIDTH;
//height
static int pHEIGHT;
//calculation thread
Thread runner;
//mouse position keepers
int mousex;
int mousey;
int mousex2;
int mousey2;
//image used to draw sand on
BufferedImage buffer = new BufferedImage(pWIDTH, pHEIGHT + 50,
BufferedImage.TYPE_INT_RGB);
//carrier variable
Graphics g;
//brush sice
int bsize = 4;
//random generators *some of which may be unused

Random generator = new Random();
Random generator2 = new Random();
Random generator3 = new Random();
Random generator4 = new Random();
Random generator5 = new Random();
//elemental data in 2D plane
int[][] pixelData = new int[pWIDTH + 1][pHEIGHT + 1];
//heat data in 2D plane
int[][] heatData = new int[pWIDTH + 1][pHEIGHT + 1];
//draw this 2D pixel?
boolean[][] drawData = new boolean[pWIDTH + 1][pHEIGHT + 1];
//check this 2D pixels neighbor
boolean[][] checkN = new boolean[pWIDTH + 1][pHEIGHT + 1];
//do this 2D pixels heat
boolean[][] checkH = new boolean[pWIDTH + 1][pHEIGHT + 1];
//is there a heat source on screen
boolean hSource = false;
//left 1
int leftD = 1;
//right 1
int rightD = -1;
//current element to draw
int eLEMENT = 1;
/names of elements.
*
*/
public static ArrayList < String > nameS = new ArrayList < String > ();
/
colors of elements.
*
*/
public static ArrayList < Color > elements = new ArrayList < Color > ();
/slip of elements non zero slip
* denotates an element that will move only sideways.
/
public static ArrayList < Integer > slip = new ArrayList < Integer > ();
/
gravity of elements.*/
public static ArrayList < Integer > gravity =
new ArrayList < Integer > ();
/density of elements.*/
public static ArrayList < Double > density = new ArrayList < Double > ();
/
heat of elements > 100 heats things
* < 100 cools things otherwhise nothing.*/
public static ArrayList < Integer > heat = new ArrayList < Integer > ();
/first element of interaction.
*
*/
public static ArrayList < Integer > eLM1 = new ArrayList < Integer > ();
/
second element of interaction.
*
*/
public static ArrayList < Integer > eLM2 = new ArrayList < Integer > ();
/possibility of happening.*/
public static ArrayList < Integer > cHANCE = new ArrayList < Integer > ();
/
first new element of interaction.
*
*/
public static ArrayList < Integer > nELM1 = new ArrayList < Integer > ();
/**
* does this interaction explode.
*/
public static ArrayList < Boolean > boom = new ArrayList < Boolean > ();
/second new elelemtn of interaction.
*
*/
public static ArrayList < Integer > nELM2 = new ArrayList < Integer > ();
/

* heat needed to interact.
*/
public static ArrayList < Integer > reqHeat =
new ArrayList < Integer > ();
/**
* size of explostion.
*/
public static ArrayList < Integer > eSIZE = new ArrayList < Integer > ();
/possibility that a neighbor will happen to
*one of 8 directions or not fall through to next choice.
*/
public static double avper;
/
max heat.*/
public static final int MAX_HEAT = 100;
/min heat.*/
public static final int MIN_HEAT = -100;
//draw border?
boolean bottom = false;
//has the border been drawn?
boolean borderDrawn = false;
/
makes a color based on Red Green Blue first makes
* sure they are in the 0-255 range.*/
public static Color makeCol(int r, int g, int b) {
if (r > 255) {
r = 255;
}
else if (r < 0) {
r = 0;
}
if (g > 255) {
g = 255;
}
else if (g < 0) {
g = 0;
}
if (b > 255) {
b = 255;
}
else if (b < 0) {
b = 0;
}
Color c = new Color(r, g, b);
return c;
}
/checks if a value is zero.*/
public static boolean isZero(int n) {
if (n == 0) {
return true;
}
else {
return false;
}
}
/
checks if a value is positive.*/
public static boolean isPositive(int n) {
if (n > 0) {
return true;
}
else {
return false;
}
}
/averages an indiscriminate number of double arguments into an int.*/
public static int average(double... nS ) {
int numberOfNumbers = nS.length;
int total = 0;
for (int i = 0; i < numberOfNumbers; i++ ) {
total += nS[i];
}
return (int)total / numberOfNumbers;
}
/
returns a true with a percent chance of the input \n
* ex \n*input of 10 has about a 10% chance of returning true./
public boolean pc(double chance) {
Random rand = new Random();
double r1;
if (chance <= 0) {
return false;
}
else if (chance >= 100) {
return true;
}
else {
r1 = rand.nextInt(101);
if (r1 <= chance) {
return true;
}
else {
return false;
}
}
}
/
absolute valueof input.*/
public static int absV(int n) {
return Math.abs(n);
}
/start a sand engine.*/
public SandEngine() {
setSize(pWIDTH, pHEIGHT + 50);
setBackground(new Color(236, 236, 236));
addMouseListener(this);
addMouseMotionListener(this);
startThread();
}
/
start runner.*/
private void startThread() {
if (runner == null) {
runner = new Thread(this);
runner.start();
}
}
/overwrite run method.*/
public void run() {
while (runner != null) {
computeAndDrawSand();
repaint();
try {
Thread.sleep(10);
}
catch (Exception e) {
//do nothing
}
}
}
/
calls methods for computing and drawing sand.*/
private void computeAndDrawSand() {
g = buffer.getGraphics();
if (!pause) {

        if (!bottom) {
            for (int i = 0;  i < pWIDTH;  i++) {
                pixelData[i][pHEIGHT - 2] = 0; 
                drawData[i][pHEIGHT - 2] = true; 
            }
            for (int i = 0;  i < pWIDTH;  i++) {
                pixelData[i][1] = 0; 
                drawData[i][1] = true; 
            }
        } 
        else {
        }
        moveSand(); 
        if (doHeat) {
            limitHeat(); 
        }
        g.setColor(Color.black); 
        g.fillRect(0, 0, pWIDTH, pHEIGHT + 50); 
    } 
    else {
        g.setColor(Color.black); 
        g.fillRect(0, pHEIGHT, pWIDTH, pHEIGHT + 50); 
    }
    drawSand(g); 
}
/**eAM stands for Element At Mouse simply prints the name and heat 
* of the element at the current mouse coordinate
*\n works closely with the mousemoved method, which 
*grabs the coordinates of every movement of the mouse.
**/
private void eAM(Graphics g3) {
    String cN; 
    int cH; 
    int cE; 
    g3.setColor(new Color(2, 255, 255)); 
    try {
        cE = pixelData[mousex2][mousey2]; 
        cN = nameS.get(cE); 
        cH = heatData[mousex2][mousey2]; 
    }
    catch (ArrayIndexOutOfBoundsException e) {
        cN = "Not In Bounds"; 
        cH = 0; 
    }
    g3.drawString("Element: " + cN + " Heat: " + cH, 5, pHEIGHT + 15); 
    g3.drawString("X: " + mousex2 + " Y: " + mousey2, 5, pHEIGHT + 30); 
}

/**computes the movements of the sand
and checks for heat and neighbor interactions.
**/
private void moveSand() {
//variable declaration
int thisE;
int thisH;
int up;
int upH;
int upleft;
int upleftH;
int upright;
int uprightH;
int down;
int downH;
int left;
int leftH;
int right;
int rightH;
int downleft;
int downleftH;
int downright;
int downrightH;
int r1;
int r2;
int slip2;
int gravity2;
int cH;
int h;
double density2;
boolean r3;
//end variable declaration
for (int y = 1; y < pHEIGHT - 1; y++ ) {
for (int x = 1; x < pWIDTH - 1; x++ ) {
if (!drawData[x][y]) {
//variable assighnment
thisE = pixelData[x][y];
thisH = heatData[x][y];
up = pixelData[x - 1][y];
upH = heatData[x - 1][y];
upright = pixelData[x - 1][y + rightD];
uprightH = heatData[x - 1][y + rightD];
upleft = pixelData[x - 1][y + leftD];
upleftH = heatData[x - 1][y + leftD];
gravity2 = gravity.get(thisE);
down = pixelData[x][y + gravity2];
left = pixelData[x + 1][y];
right = pixelData[x - 1][y];
downleft = pixelData[x + 1][y + gravity2];
downright = pixelData[x - 1][y + gravity2];
downH = heatData[x][y + gravity2];
leftH = heatData[x + 1][y];
rightH = heatData[x - 1][y];
downleftH = heatData[x + 1][y + gravity2];
downrightH = heatData[x - 1][y + gravity2];
slip2 = slip.get(thisE);
density2 = density.get(thisE);
r1 = generator.nextInt(11);
//end variable assighnment
if (thisE != 0) {
if (gravity2 != 0) {
if (r1 < 3) {
if (downleft == 0 /
&& slip2 != 0/) {
pixelData[x][y] = 0;
heatData[x][y] = 0;
pixelData[x + 1][y + gravity2] = thisE;
heatData[x + 1][y + gravity2] = thisH;
drawData[x][y] = true;
drawData[x + 1][y + gravity2] = true;
}
else if (density.get(downleft) < density2 //&&
/
slip2 != 0/) {
pixelData[x + 1][y + gravity2] = thisE;
heatData[x + 1][y + gravity2] = thisH;
pixelData[x][y] = downleft;
heatData[x][y] = downleftH;
drawData[x + 1][y + gravity2] = true;
drawData[x][y] = true;
}
else if (slip2 != 0) {
if (left == 0) {
pixelData[x][y] = 0;
heatData[x][y] = 0;
pixelData[x + 1][y] = thisE;
heatData[x + 1][y] = thisH;
drawData[x + 1][y] = true;
drawData[x][y] = true;
}
else {
pixelData[x][y] = thisE;
heatData[x][y] = thisH;
drawData[x][y] = true;
}
}
else {
pixelData[x][y] = thisE;
heatData[x][y] = thisH;
drawData[x][y] = true;
}
}
else if (r1 > 7) {
if (downright == 0 /
&& slip2 != 0/) {
pixelData[x][y] = 0;
heatData[x][y] = 0;
pixelData[x - 1][y + gravity2] = thisE;
heatData[x - 1][y + gravity2] = thisH;
drawData[x][y] = true;
drawData[x - 1][y + gravity2] = true;
}
else if (density.get(downright) < density2 //&&
/
slip2 != 0*/) {
pixelData[x - 1][y + gravity2] = thisE;
heatData[x - 1][y + gravity2] = thisH;
pixelData[x][y] = downright;
heatData[x][y] = downrightH;
drawData[x - 1][y + gravity2] = true;
drawData[x][y] = true;
}
else if (right == 0) {
if (slip2 != 0) {
pixelData[x - 1][y] = thisE;
heatData[x - 1][y] = thisH;
pixelData[x][y] = 0;
heatData[x][y] = 0;
drawData[x - 1][y] = true;
drawData[x][y] = true;
}
else {
pixelData[x][y] = pixelData[x][y];
heatData[x][y] = heatData[x][y];
drawData[x][y] = true;
}
}
else {
pixelData[x][y] = pixelData[x][y];
heatData[x][y] = heatData[x][y];
drawData[x][y] = true;
}
}
else {
if (down == 0) {
pixelData[x][y] = 0;
heatData[x][y] = 0;
pixelData[x][y + gravity2] = thisE;
heatData[x][y + gravity2] = thisH;
drawData[x][y] = true;
drawData[x][y + gravity2] = true;
}
else if (density.get(down) < density2) {
pixelData[x][y + gravity2] = thisE;
heatData[x][y + gravity2] = thisH;
pixelData[x][y] = down;
heatData[x][y] = downH;
drawData[x][y + gravity2] = true;
drawData[x][y] = true;
}
else {
pixelData[x][y] = pixelData[x][y];
heatData[x][y] = heatData[x][y];
drawData[x][y] = true;
}
}
}
else {
pixelData[x][y] = pixelData[x][y];
heatData[x][y] = heatData[x][y];
drawData[x][y] = true;
}
}
else {
pixelData[x][y] = pixelData[x][y];
drawData[x][y] = true;
checkN[x][y] = false;
heatData[x][y] = 0;
}

                if (doHeat && checkH[x][y]) {
                    heatEx(x, y); 
                }
                if (doNeighbor) {
                    if (checkN[x][y] /*true*/) {
                        if (thisE != 0) {
                            int iT = 0; 
                            int iT2 = 0;
                            double curChance; 
                            double curChance2;
                            for (Integer e : eLM1) {
                                if (e == pixelData[x][y]) {
                                    curChance = cHANCE.get(iT); 
                                    if (pc(curChance)) {
                                        if (!boom.get(iT)) {
                                            if (doNieghbor(x, y,
                                                eLM2.get(iT), iT)) {
                                                break; 
                                            }
                                        }
                                        else {
                                            if (doExplosion(x, y, 
                                                eLM2.get(iT), iT)) {
                                                break;
                                            }
                                        }
                                    }
                                }
                                iT += 1; 
                            }
                        }
                    }
                }       
            }
        }
    }
}

/**
exchange heat computation for inputted location /n
// doing much better now, though still not perfect.
*/
private void heatEx(int x, int y) {
int thisE = pixelData[x][y];
if (thisE != 0) {
int thisH = heatData[x][y];
int h = heat.get(thisE);
if ( h > 100) {
hSource = true;
heatData[x][y] = 100;
if (heat.get(pixelData[x + 1][y]) != 0) {
heatData[x + 1][y] = heatData[x + 1][y] +
heat.get(pixelData[x][y]) - 100;
checkH[x + 1][y] = false;
}
if (heat.get(pixelData[x - 1][y]) != 0) {
heatData[x - 1][y] = heatData[x - 1][y] +
heat.get(pixelData[x][y]) - 100;
checkH[x - 1][y] = false;
}
if (heat.get(pixelData[x + 1][y + rightD]) != 0) {
heatData[x + 1][y + rightD] = heatData[x + 1][y + rightD] +
heat.get(pixelData[x][y]) - 100;
checkH[x + 1][y + rightD] = false;
}
if (heat.get(pixelData[x + 1][y + leftD]) != 0) {
heatData[x + 1][y + leftD] = heatData[x + 1][y + leftD] +
heat.get(pixelData[x][y]) - 100;
checkH[x + 1][y + leftD] = false;
}
if (heat.get(pixelData[x - 1][y + rightD]) != 0) {
heatData[x - 1][y + rightD] = heatData[x - 1][y + rightD] +
heat.get(pixelData[x][y]) - 100;
checkH[x - 1][y + rightD] = false;
}
if (heat.get(pixelData[x - 1][y]) != 0) {
heatData[x - 1][y + leftD] = heatData[x - 1][y + leftD] +
heat.get(pixelData[x][y]) - 100;
checkH[x - 1][y + leftD] = false;
}
if (heat.get(pixelData[x][y + leftD]) != 0) {
heatData[x][y + leftD] = heatData[x][y + leftD] +
heat.get(pixelData[x][y]) - 100;
checkH[x][y + leftD] = false;
}
if (heat.get(pixelData[x][y + rightD]) != 0) {
heatData[x][y + rightD] = heatData[x][y + rightD] +
heat.get(pixelData[x][y]) - 100;
checkH[x][y + rightD] = false;
}
}
else if ( h < -100) {
hSource = true;
heatData[x][y] = -100;
if (heat.get(pixelData[x + 1][y]) != 0) {
heatData[x + 1][y] = heatData[x + 1][y] +
heat.get(pixelData[x][y]) + 100;
checkH[x + 1][y] = false;
}
if (heat.get(pixelData[x - 1][y]) != 0) {
heatData[x - 1][y] = heatData[x - 1][y] +
heat.get(pixelData[x][y]) + 100;
checkH[x - 1][y] = false;
}
if (heat.get(pixelData[x + 1][y + rightD]) != 0) {
heatData[x + 1][y + rightD] = heatData[x + 1][y + rightD] +
heat.get(pixelData[x][y]) + 100;
checkH[x + 1][y + rightD] = false;
}
if (heat.get(pixelData[x + 1][y + leftD]) != 0) {
heatData[x + 1][y + leftD] = heatData[x + 1][y + leftD] +
heat.get(pixelData[x][y]) + 100;
checkH[x + 1][y + leftD] = false;
}
if (heat.get(pixelData[x - 1][y + rightD]) != 0) {
heatData[x - 1][y + rightD] = heatData[x - 1][y + rightD] +
heat.get(pixelData[x][y]) + 100;
checkH[x - 1][y + rightD] = false;
}
if (heat.get(pixelData[x - 1][y]) != 0) {
heatData[x - 1][y + leftD] = heatData[x - 1][y + leftD] +
heat.get(pixelData[x][y]) + 100;
checkH[x - 1][y + leftD] = false;
}
if (heat.get(pixelData[x][y + leftD]) != 0) {
heatData[x][y + leftD] = heatData[x][y + leftD] +
heat.get(pixelData[x][y]) + 100;
checkH[x][y + leftD] = false;
}
if (heat.get(pixelData[x][y + rightD]) != 0) {
heatData[x][y + rightD] = heatData[x][y + rightD] +
heat.get(pixelData[x][y]) + 100;
checkH[x][y + rightD] = false;
}
}
else if (isZero(h)) {
//do nothing because if heat is
//zero there is nothing to exchange
}
else if ((heatData[x][y] > 0) && thisE != 0) {
if (heat.get(pixelData[x + 1][y]) != 0) {
heatData[x + 1][y] = 1 * absV(average(heatData[x + 1][y],
heatData[x][y]));
checkH[x + 1][y] = false;
}
if (heat.get(pixelData[x - 1][y]) != 0) {
checkH[x - 1][y] = false;
heatData[x - 1][y] = 1 * absV(average(heatData[x - 1][y],
heatData[x][y]));
}
if (heat.get(pixelData[x + 1][y + rightD]) != 0) {
checkH[x + 1][y + rightD] = false;
heatData[x + 1][y + rightD] = 1 * absV(average(
heatData[x + 1][y + rightD], heatData[x][y]));
}
if (heat.get(pixelData[x + 1][y + leftD]) != 0) {
checkH[x + 1][y + leftD] = false;
heatData[x + 1][y + leftD] = 1 * absV(average(
heatData[x + 1][y + leftD], heatData[x][y]));
}
if (heat.get(pixelData[x - 1][y + rightD]) != 0) {
checkH[x - 1][y + rightD] = false;
heatData[x - 1][y + rightD] = 1 * absV(average(
heatData[x - 1][y + rightD], heatData[x][y]));
}
if (heat.get(pixelData[x - 1][y]) != 0) {
checkH[x - 1][y + leftD] = false;
heatData[x - 1][y + leftD] = 1 * absV(average(
heatData[x - 1][y + leftD], heatData[x][y]));
}
if (heat.get(pixelData[x][y + leftD]) != 0) {
checkH[x][y + leftD] = false;
heatData[x][y + leftD] = 1 * absV(average(
heatData[x][y + leftD], heatData[x][y]));
}
if (heat.get(pixelData[x][y + rightD]) != 0) {
checkH[x][y + rightD] = false;
heatData[x][y + rightD] = 1 * absV(average(
heatData[x][y + rightD], heatData[x][y]));
}
}
else if ((heatData[x][y] < 0) && thisE != 0) {
if (heat.get(pixelData[x + 1][y]) != 0) {
heatData[x + 1][y] = -1 * absV(average(
heatData[x + 1][y], heatData[x][y]));
checkH[x + 1][y] = false;
}
if (heat.get(pixelData[x - 1][y]) != 0) {
checkH[x - 1][y] = false;
heatData[x - 1][y] = -1 * absV(average(
heatData[x - 1][y], heatData[x][y]));
}
if (heat.get(pixelData[x + 1][y + rightD]) != 0) {
checkH[x + 1][y + rightD] = false;
heatData[x + 1][y + rightD] = -1 * absV(average(
heatData[x + 1][y + rightD], heatData[x][y]));
}
if (heat.get(pixelData[x + 1][y + leftD]) != 0) {
checkH[x + 1][y + leftD] = false;
heatData[x + 1][y + leftD] = -1 * absV(average(
heatData[x + 1][y + leftD], heatData[x][y]));
}
if (heat.get(pixelData[x - 1][y + rightD]) != 0) {
checkH[x - 1][y + rightD] = false;
heatData[x - 1][y + rightD] = -1 * absV(average(
heatData[x - 1][y + rightD], heatData[x][y]));
}
if (heat.get(pixelData[x - 1][y]) != 0) {
checkH[x - 1][y + leftD] = false;
heatData[x - 1][y + leftD] = -1 * absV(average(
heatData[x - 1][y + leftD], heatData[x][y]));
}
if (heat.get(pixelData[x][y + leftD]) != 0) {
checkH[x][y + leftD] = false;
heatData[x][y + leftD] = -1 * absV(average(
heatData[x][y + leftD], heatData[x][y]));
}
if (heat.get(pixelData[x][y + rightD]) != 0) {
checkH[x][y + rightD] = false;
heatData[x][y + rightD] = -1 * absV(
average(heatData[x][y + rightD], heatData[x][y]));
}
}
}
}
/limits the heat of the entire sandbox and
* lowers/raises heat when hSource is false. */
private void limitHeat() {
for (int x = 0; x < pWIDTH - 1; x++ ) {
for (int y = 0; y < pHEIGHT - 1; y++ ) {
try {
if (pixelData[x][y] == 0) {
heatData[x][y] = 0;
}
if (heatData[x][y] > 100) {
heatData[x][y] = 100;
}
if (heatData[x][y] < -100) {
heatData[x][y] = -100;
}
if (heat.get(pixelData[x][y]) == 0) {
heatData[x][y] = 0;
}
if (!hSource && pixelData[x][y] != 0 &&
heatData[x][y] != 0) {
if (heatData[x][y] > 0) {
heatData[x][y] -= heat.get(pixelData[x][y]);
if (heatData[x][y] < 0) {
heatData[x][y] = 0;
}
}
if (heatData[x][y] < 0) {
heatData[x][y] += heat.get(pixelData[x][y]);
if (heatData[x][y] > 0) {
heatData[x][y] = 0;
}
}
}
}
catch (IndexOutOfBoundsException ex) {
//do nothing
}
}
}
}
/
draws sand darkening cold / brightening hot spots.
/
private void drawSand(Graphics g3) {
Color tC;
int c;
int c2;
int c3;
hSource = false;
for (int x = 1; x < pWIDTH - 1; x++ ) {
for (int y = 1; y < pHEIGHT - 1; y++ ) {
if ((pixelData[x][y] != 0 || pause) && drawData[x][y] == true) {
if (heatData[x][y] == 0) {
tC = elements.get(pixelData[x][y]);
}
else if (isPositive(heatData[x][y])) {
if (heat.get(pixelData[x][y]) > 100) {
tC = elements.get(pixelData[x][y]);
}
else {
c = elements.get(pixelData[x][y]).getRed() +
heatData[x][y];
c2 = elements.get(pixelData[x][y]).getGreen() +
heatData[x][y];
c3 = elements.get(pixelData[x][y]).getBlue() +
heatData[x][y];
tC = makeCol(c, c2, c3);
}
}
else {
if (heat.get(pixelData[x][y]) < -100) {
tC = elements.get(pixelData[x][y]);
}
else {
c = elements.get(pixelData[x][y]).getRed() +
heatData[x][y];
c2 = elements.get(pixelData[x][y]).getGreen() +
heatData[x][y];
c3 = elements.get(pixelData[x][y]).getBlue() +
heatData[x][y];
tC = makeCol(c, c2, c3);
}
}
g3.setColor(tC);
g3.fillRect(x, y, 1, 1);
drawData[x][y] = false;
checkN[x][y] = true;
checkH[x][y] = true;
}
}
}
eAM(g3);
}
/**overwrite paintcomponent method.*/
public void paintComponent(Graphics g4) {
super.paintComponent(g4);
if (buffer != null) {
g4.drawImage(buffer, 0, 0, null);

    }
}
//the following methods overwrite mouse event methods
//some add sand one - mouse moved -finds mouse position for eAM
//most do nothing
/**
* mousePressed.
*/
public void mousePressed(MouseEvent e) {
    if (inBounds) {
        mousex = e.getX(); 
        mousey = e.getY(); 
        doCircle(mousex,mousey,bsize,eLEMENT);
    } 
}
/**
 * nothing.
 */
public void mouseClicked(MouseEvent e) {
}
/**
 * mouse dragged.
 */
public void mouseDragged(MouseEvent e) {
    if (inBounds) {
        mousex = e.getX(); 
        mousey = e.getY(); 
        doCircle(mousex,mousey,bsize,eLEMENT);
    }
}

/**
 * nothing.
 */
public void mouseEntered(MouseEvent e) {
    inBounds = true;
}
/**
 * nothing.
 */
public void mouseExited(MouseEvent e) {
    inBounds = false;
}
/**
 * nothing.
 */
public void mouseReleased(MouseEvent e) {
}
/**
 * mouse moved.
 */
public void mouseMoved(MouseEvent e) {
    mousex2 = e.getX(); 
    mousey2 = e.getY(); 
}

/switch element to draw.*/
public void changeElement(int nE) {
eLEMENT = nE;
}
/
change brush size.*/
public void changebsize(int nS) {
bsize = nS;
}
/change whether there is a border.*/
public void changeBottom(boolean nB) {
bottom = nB;
borderDrawn = false;
}
/
chane wheter heat is calculated.*/
public void changeHeat(boolean nD) {
doHeat = nD;
}
/change whether neihgbors are done.*/
public void changeNeighbor(boolean nN) {
doNeighbor = nN;
}
/
toggle whether the game is paused.*/
public static void togglePause() {
if (pause) {
pause = false;
}
else {
pause = true;
}
}
/clear sandbox.*/
public void clearS() {
for (int y = 0; y < pHEIGHT; y++ ) {
for (int x = 0; x < pWIDTH; x++ ) {
pixelData[x][y] = 0;
heatData[x][y] = 0;
drawData[x][y] = true;
}
}
}
/
do neighbor interactions.*/
private boolean doNieghbor(int x, int y, int eLM, int iT) {
int thisE = pixelData[x][y];
int grav = gravity.get(thisE);
int down = pixelData[x][y + 1];
int up = pixelData[x][y - 1];
int left = pixelData[x + leftD][y];
int right = pixelData[x + rightD][y];
int downleft = pixelData[x + leftD][y + 1];
int upleft = pixelData[x + leftD][y - 1];
int downright = pixelData[x + rightD][y + 1];
int upright = pixelData[x + rightD][y - 1];
Random gen = new Random();
int tELM1 = nELM1.get(iT);
int tELM2 = nELM2.get(iT);
boolean returnd;
if (reqHeat.get(iT) == 0) {
if (pc(avper) && upleft == eLM) {
pixelData[x][y] = tELM1;
pixelData[x + leftD][y - 1] = tELM2;
checkN[x][y] = false;
checkN[x + leftD][y - 1] = false;
return true;
}
else if (pc(avper) && up == eLM) {
pixelData[x][y] = tELM1;
pixelData[x][y - 1] = tELM2;
checkN[x][y] = false;
checkN[x][y - 1] = false;
return true;
}
else if (pc(avper) && left == eLM) {
pixelData[x][y] = tELM1;
pixelData[x + leftD][y] = tELM2;
checkN[x][y] = false;
checkN[x + leftD][y] = false;
return true;
}
else if (pc(avper) && right == eLM) {
pixelData[x][y] = tELM1;
pixelData[x + rightD][y] = tELM2;
checkN[x][y] = false;
checkN[x + rightD][y] = false;
return true;
}
else if (pc(avper) && downleft == eLM) {
pixelData[x + leftD][y + 1] = tELM2;
pixelData[x][y] = tELM1;
checkN[x][y] = false;
checkN[x + leftD][y + 1] = false;
return true;
}
else if (pc(avper) && downright == eLM) {
pixelData[x + rightD][y + 1] = tELM2;
pixelData[x][y] = tELM1;
checkN[x][y] = false;
checkN[x + rightD][y + 1] = false;
return true;
}
else if (pc(avper) && upright == eLM) {
pixelData[x][y] = tELM1;
pixelData[x + rightD][y - 1] = tELM2;
checkN[x][y] = false;
checkN[x + rightD][y - 1] = false;
return true;
}
else if (down == eLM) {
pixelData[x][y] = tELM1;
pixelData[x][y + 1] = tELM2;
checkN[x][y] = false;
checkN[x][y + 1] = false;
return true;
}
else {
return false;
}
}
else if (isPositive(reqHeat.get(iT))) {
if (heatData[x][y] >= reqHeat.get(iT)) {
if (pc(avper) && upleft == eLM) {
pixelData[x][y] = tELM1;
pixelData[x + leftD][y - 1] = tELM2;
checkN[x][y] = false;
checkN[x + leftD][y - 1] = false;
return true;
}
else if (pc(avper) && up == eLM) {
pixelData[x][y] = tELM1;
pixelData[x][y - 1] = tELM2;
checkN[x][y] = false;
checkN[x][y - 1] = false;
return true;
}
else if (pc(avper) && left == eLM) {
pixelData[x][y] = tELM1;
pixelData[x + leftD][y] = tELM2;
checkN[x][y] = false;
checkN[x + leftD][y] = false;
return true;
}
else if (pc(avper) && right == eLM) {
pixelData[x][y] = tELM1;
pixelData[x + rightD][y] = tELM2;
checkN[x][y] = false;
checkN[x + rightD][y] = false;
return true;
}
else if (pc(avper) && downleft == eLM) {
pixelData[x + leftD][y + 1] = tELM2;
pixelData[x][y] = tELM1;
checkN[x][y] = false;
checkN[x + leftD][y + 1] = false;
return true;
}
else if (pc(avper) && downright == eLM) {
pixelData[x + rightD][y + 1] = tELM2;
pixelData[x][y] = tELM1;
checkN[x][y] = false;
checkN[x + rightD][y + 1] = false;
return true;
}
else if (pc(avper) && upright == eLM) {
pixelData[x][y] = tELM1;
pixelData[x + rightD][y - 1] = tELM2;
checkN[x][y] = false;
checkN[x + rightD][y - 1] = false;
return true;
}
else if (down == eLM) {
pixelData[x][y] = tELM1;
pixelData[x][y + 1] = tELM2;
checkN[x][y] = false;
checkN[x][y + 1] = false;
return true;
}
else {
return false;
}
}
else {
return false;
}
}
else {
if (heatData[x][y] <= reqHeat.get(iT)) {
if (pc(avper) && upleft == eLM) {
pixelData[x][y] = tELM1;
pixelData[x + leftD][y - 1] = tELM2;
checkN[x][y] = false;
checkN[x + leftD][y - 1] = false;
return true;
}
else if (pc(avper) && up == eLM) {
pixelData[x][y] = tELM1;
pixelData[x][y - 1] = tELM2;
checkN[x][y] = false;
checkN[x][y - 1] = false;
return true;
}
else if (pc(avper) && left == eLM) {
pixelData[x][y] = tELM1;
pixelData[x + leftD][y] = tELM2;
checkN[x][y] = false;
checkN[x + leftD][y] = false;
return true;
}
else if (pc(avper) && right == eLM) {
pixelData[x][y] = tELM1;
pixelData[x + rightD][y] = tELM2;
checkN[x][y] = false;
checkN[x + rightD][y] = false;
return true;
}
else if (pc(avper) && downleft == eLM) {
pixelData[x + leftD][y + 1] = tELM2;
pixelData[x][y] = tELM1;
checkN[x][y] = false;
checkN[x + leftD][y + 1] = false;
return true;
}
else if (pc(avper) && downright == eLM) {
pixelData[x + rightD][y + 1] = tELM2;
pixelData[x][y] = tELM1;
checkN[x][y] = false;
checkN[x + rightD][y + 1] = false;
return true;
}
else if (pc(avper) && upright == eLM) {
pixelData[x][y] = tELM1;
pixelData[x + rightD][y - 1] = tELM2;
checkN[x][y] = false;
checkN[x + rightD][y - 1] = false;
return true;
}
else if (down == eLM) {
pixelData[x][y] = tELM1;
pixelData[x][y + 1] = tELM2;
checkN[x][y] = false;
checkN[x][y + 1] = false;
return true;
}
else {
return false;
}
}
else {
return false;
}
}
}
/**
* do explosion.
*/
private boolean doExplosion(int x, int y, int eLM, int iT) {
int thisE = pixelData[x][y];
int grav = gravity.get(thisE);
int down = pixelData[x][y + 1];
int up = pixelData[x][y - 1];
int left = pixelData[x + leftD][y];
int right = pixelData[x + rightD][y];
int downleft = pixelData[x + leftD][y + 1];
int upleft = pixelData[x + leftD][y - 1];
int downright = pixelData[x + rightD][y + 1];
int upright = pixelData[x + rightD][y - 1];
int tELM1 = nELM1.get(iT);
Random gen = new Random();
boolean returnS = false;
if (reqHeat.get(iT) == 0) {
if (pc(avper) && upleft == eLM) {
doEx(x, y, eSIZE.get(iT), tELM1);
return true;
}
else if (pc(avper) && up == eLM) {
doEx(x, y, eSIZE.get(iT), tELM1);
return true;
}
else if (pc(avper) && left == eLM) {
doEx(x, y, eSIZE.get(iT), tELM1);
return true;
}
else if (pc(avper) && right == eLM) {
doEx(x, y, eSIZE.get(iT), tELM1);
return true;
}
else if (pc(avper) && downleft == eLM) {
doEx(x, y, eSIZE.get(iT), tELM1);
return true;
}
else if (pc(avper) && downright == eLM) {
doEx(x, y, eSIZE.get(iT), tELM1);
return true;
}
else if (pc(avper) && upright == eLM) {
doEx(x, y, eSIZE.get(iT), tELM1);
return true;
}
else if (down == eLM) {
doEx(x, y, eSIZE.get(iT), tELM1);
return true;
}
}
else if (isPositive(reqHeat.get(iT))) {
if (heatData[x][y] >= reqHeat.get(iT)) {
if (pc(avper) && upleft == eLM) {
doEx(x, y, eSIZE.get(iT), tELM1);
return true;
}
else if (pc(avper) && up == eLM) {
doEx(x, y, eSIZE.get(iT), tELM1);
return true;
}
else if (pc(avper) && left == eLM) {
doEx(x, y, eSIZE.get(iT), tELM1);
return true;
}
else if (pc(avper) && right == eLM) {
doEx(x, y, eSIZE.get(iT), tELM1);
return true;
}
else if (pc(avper) && downleft == eLM) {
doEx(x, y, eSIZE.get(iT), tELM1);
return true;
}
else if (pc(avper) && downright == eLM) {
doEx(x, y, eSIZE.get(iT), tELM1);
return true;
}
else if (pc(avper) && upright == eLM) {
doEx(x, y, eSIZE.get(iT), tELM1);
return true;
}
else if (down == eLM) {
doEx(x, y, eSIZE.get(iT), tELM1);
return true;
}
}
}
else {
if (heatData[x][y] <= reqHeat.get(iT)) {
if (pc(avper) && upleft == eLM) {
doEx(x, y, eSIZE.get(iT), tELM1);
return true;
}
else if (pc(avper) && up == eLM) {
doEx(x, y, eSIZE.get(iT), tELM1);
return true;
}
else if (pc(avper) && left == eLM) {
doEx(x, y, eSIZE.get(iT), tELM1);
return true;
}
else if (pc(avper) && right == eLM) {
doEx(x, y, eSIZE.get(iT), tELM1);
return true;
}
else if (pc(avper) && downleft == eLM) {
doEx(x, y, eSIZE.get(iT), tELM1);
return true;
}
else if (pc(avper) && downright == eLM) {
doEx(x, y, eSIZE.get(iT), tELM1);
return true;
}
else if (pc(avper) && upright == eLM) {
doEx(x, y, eSIZE.get(iT), tELM1);
return true;
}
else if (down == eLM) {
doEx(x, y, eSIZE.get(iT), tELM1);
return true;
}
}
}
return returnS;
}
/**
* make explosion occur.
*/
private void doEx (int x, int y, int size, int eLM) {
// int itter = 0;
// for (int i = 0; i < size; i++ ) {
// for (int j = 0; j < size; j++ ) {
// try {
// pixelData[(x - (size / 2)) + i][(y -
// (size / 2)) + j] = eLM;
// heatData[(x - (size / 2)) + i][(y -
// (size / 2)) + j] = 0;
// drawData[(x - (size / 2)) + i][(y -
// (size / 2)) + j] = true;
// }
// catch (ArrayIndexOutOfBoundsException ex) {
// //do nothing
// }
// }
// }
doCircle(x,y,size,eLM);
}
private void doCircle(int x, int y, int size, int elm){
for(int i = x-(size);i<=x+(size);i++){
for(int j = y-(size);j<=y+(size);j++){
if(Math.sqrt(((x-i)*(x-i))+((y-j)*(y-j)))<=(size)){
try {
pixelData[i][j] = elm;
heatData[i][j] = 0;
drawData[i][j] = true;
}
catch (ArrayIndexOutOfBoundsException ex) {
//do nothing
}
}
}
}
}
}

any advise is welcome
SourceZip.zip

Comments
Very Professional implementation of Physics into Java!

First and foremost I'd like to say that I'm damn impressed!

If I remember correctly, using switch/case statements yields much faster selection than if-else statements.

I don't think that's the main issue though - it may be the operations done in the for loops or possibly the amount of threads you're using for calculations. I've only briefly glanced at your code while testing it (enjoyably!) =)

Edit: This may sound redundant, but considering a post made by Ezzaral in the past your problem may have something to do with your Thread performing operations vs the Event Dispatching Thread performing operations.

It may be a good idea to (somehow) add the event you need to run to the EDT to be dispatched sometime in the future. It may or may not be in your best interests, so I won't post the way to do it until a professional can support this claim (I'm currently still learning the mechanics of the EDT so I do not want to make this a concrete claim).

Comments
quite a nice reply pretty infimative, and nice rep comment too :)

First and foremost I'd like to say that I'm damn impressed!

If I remember correctly, using switch/case statements yields much faster selection than if-else statements.

I don't think that's the main issue though - it may be the operations done in the for loops or possibly the amount of threads you're using for calculations. I've only briefly glanced at your code while testing it (enjoyably!) =)

Edit: This may sound redundant, but considering a post made by Ezzaral in the past your problem may have something to do with your Thread performing operations vs the Event Dispatching Thread performing operations.

It may be a good idea to (somehow) add the event you need to run to the EDT to be dispatched sometime in the future. It may or may not be in your best interests, so I won't post the way to do it until a professional can support this claim (I'm currently still learning the mechanics of the EDT so I do not want to make this a concrete claim).

first, thanks, and thanks for the reply.
i may overhaul the code into using switch's, but it will likely take a while.

i don't know much about the EDT either, i just know that this whole thing should be running in a completely different thread than the rest of the program.

i really think the answer is in the removal of loops, but as far as i can tell all are essential.

any professionals have any opinion? any amateurs? advice still welcome

Neat program. I don't have a great deal of time to go through it at the moment, but here are some things to consider from the start.

First, you'll want to set up a repeatable test state of the program. In order to get any reliable information on the impact of any changes, you need to get your simulation running the same for each iteration. The easiest way to do that is probably to capture some of the commands you use to add elements to System.out (with their current parameters) and paste these into a test() method that you can call to rebuild the running conditions each time you want to test any changes.

Secondly, you need to capture the execution time for each invocation of computeAndDrawSand() so you have concreate numbers to work from. That is of course the roughest measure you can get. If you have a profiler to use, all the better, so you can drill down through methods calls (Netbeans has one build in and I think Eclipse may as well).

Here is a profiler snapshot from Netbeans after letting your code for several minutes with a lot of elements on the screen:


The bulk of your computation time is occurring in the moveSand() method, with doNeighbor() as the heaviest offender. The pc() method is pulling a lot of time due to a very large number of invocations (it also creates a new Random for each invocation). Are you sure you need the random chance check quite that often?

I think you can cut a good amount of execution time out by minimizing redundant array access. Many of your calcs re-access by index values that you have already captured to a variable. You may also be able to eliminate some neighbor checks that have already been performed or not needed. It's hard to say without going through it all in detail. Also, as already mentioned, you have a lot of if() braches within the tightest loops and anything you can do to eliminate or combine those could definitely help. Many of the operations they perform are similar and differ only by array position, which is often a good clue that you could rearrange them or reduce them to fewer statements with a couple of new variable calculations for the relative positions.

That's all I have time to offer at the moment, but perhaps it might help get you started.

Attachments sandProfile.png 26.61 KB

thanks, i will try to eliminate making a new random, and yes, i do need to call that method a lot, because it determines what pixel will change in an interaction. but i can use an instance random variable, again i can make an overhaul to switch statements

any more advise.

ok, i replaced the random creation with an instance variable, i haven't re-profiled it yet, although it didn't make a noticeable frame rate difference, i didn't expect it too... but overall it will probably all adds up though.

changing to switch statements just won't work in an easy way, i use too many range comparisons (rand<3 for example) and not enough equalities (==) to make using switch statements work at all.

i notice that because of the (possibly unnecessary) loops, but any time i try to add the same code to a pre-existing loop, doesn't to the same thing that it does on it's own, and i cannot figure out why, and it is frustrating me, i tried putting it in the draw sand method right after the for statements, but it just doesn't do it right anymore.

any suggestions or ideas on that? or anything else?

ok, i replaced the random creation with an instance variable, i haven't re-profiled it yet, although it didn't make a noticeable frame rate difference, i didn't expect it too... but overall it will probably all adds up though.

Correct. It's not a huge overhead, but given that it is unnecessary and the number of invocations of pc(), it is a worthwhile change to make.

changing to switch statements just won't work in an easy way, i use too many range comparisons (rand<3 for example) and not enough equalities (==) to make using switch statements work at all.

Changing to switch statement is unlikely to reap large improvements anyway, so I wouldn't sweat that. The number of array accesses and calculations per pixel is overwhelmingly the bulk of the workload.

can you point out a few line numbers where i do access the array unnecessarily please, i just don't see it :(

thanks for your help!

Well, these stand out pretty clearly

pixelData[x][y] = pixelData[x][y];
heatData[x][y] = heatData[x][y];

(around line 445 in your posting above)
and these

c = elements.get(pixelData[x][y]).getRed() + 
    heatData[x][y]; 
c2 = elements.get(pixelData[x][y]).getGreen() + 
    heatData[x][y]; 
c3 = elements.get(pixelData[x][y]).getBlue() + 
    heatData[x][y];

(~line 739)
The compiler may be able to consolidate some of those if it recognizes the invariance, but short of looking at the bytecode I can't say for certain. Those are just a few places where there is redundancy in an inner loop.

Other unnecessary access may be occurring from checks or calculations that have already been performed by a neighboring pixel or not needed at all because they are not near any active elements of the display. Not knowing nor having time to intimately know the code, I can't really say if that is the case or if it can't be dealt with easily. You know the logic of those loops and calculations and your intent far better and I am just pointing out some possibilities.

previous checks by neighbors do get counted and are not repeated (in theory :P ) whether it really helps i don't know.

the first line in every loop set is typically if(!drawData[x][y]){ which surrounds the rest of the loop body, it is set to true whenever something is done to a pixel or a neighboring pixel

then after all of the variable declarations another if statement that surrounds the rest of the body checks to make sure that the element at the current point is not nothing.

i deleted a few of those redundant lines.

i don't know about some stuff, though but without someone getting payed to read through every line of my code i won't get as specific help as may be necessary, but still thanks for your advice, and more is always appreciated

Statements like this--

if (r > 255) {
			r = 255;
		}
		else if (r < 0) {		
			r = 0;		
		}

Can be rolled into Ternary statements like so--

r = (r > 255) ? 255 : ( (r < 0) ? 0: r);

Not really sure if this would cause a performance boost, it would just reduce the amount of lines of code some.

i wrote this test class to see about the speed of the conditional statement:

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package test;

/**
 *
 * @author Bill
 */
public class Main {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        String s = "";
        for(int i=0;i<100000;i++){
            s = ifelse(i);
            System.out.println(s);
            s=condit(i);
            System.out.println(s);
        }
    }
    public static String ifelse(int i){
        if(i<50000){
            return "less";
        } else {
            return "greater";
        }
    }
    public static String condit(int i){
        return (i<50000) ? "less" : "greater";
    }
}

and i ran it through netbeans' profiler and got this the if else combo was faster than the conditional in the long run... imagine that

Attachments testifelse.png 8.84 KB
This article has been dead for over six months. Start a new discussion instead.