/* autogenerated by Processing revision 1293 on 2024-04-28 */
import processing.core.*;
import processing.data.*;
import processing.event.*;
import processing.opengl.*;

import java.util.HashMap;
import java.util.ArrayList;
import java.io.File;
import java.io.BufferedReader;
import java.io.PrintWriter;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;

public class sandbox extends PApplet {

int[] particles;
boolean[] pixelUpdates;
int[][] chunkState;
int widthChunks, heightChunks;
int chunkSize = 32;
boolean paused = false;
boolean drawChunks = false;
boolean[] pressed = new boolean[3];
int paintSize = 100;
int paintSizeOffset = 64;
int xorshift = 3;
int widthheight;
int element = 1;
float airDensity = 1.239f;
float sandDensity = 1500;
float waterDensity = 997;
float[] density = {airDensity, sandDensity, waterDensity};
float airViscosity = 1;
float sandviscosity = 63;
float waterViscosity = 31;
float[] viscosity = {airViscosity, sandviscosity, waterViscosity};

float totalMillisStart, totalMillisTotal, loadPixelsMillisStart, loadPixelsMillisTotal, mouseParticlesMillisStart, mouseParticlesMillisTotal, particlePhysicsMillisStart, particlePhysicsMillisTotal, drawParticlesMillisStart, drawParticlesMillisTotal, updatePixelsMillisStart, updatePixelsMillisTotal, userInterfaceMillisStart, userInterfaceMillisTotal, updateChunksMillisStart, updateChunksMillisTotal;

public void setup() {
  /* size commented out by preprocessor */;
  frameRate(144);
  /* noSmooth commented out by preprocessor */;
  
  widthheight = width * height;
  
  particles = new int[widthheight];
  pixelUpdates = new boolean[widthheight];
  
  widthChunks = ceil(PApplet.parseFloat(width) / chunkSize);
  heightChunks = ceil(PApplet.parseFloat(height) / chunkSize);
  chunkState = new int[widthChunks][heightChunks];
}

public void draw() {
  totalMillisStart = millis();
  
  loadPixelsMillisStart = millis();
  loadPixels();
  loadPixelsMillisTotal = loadPixelsMillisTotal *.99f + (millis() - loadPixelsMillisStart) * .01f;
  
  mouseParticlesMillisStart = millis();
  mouseParticles();
  mouseParticlesMillisTotal = mouseParticlesMillisTotal *.99f + (millis() - mouseParticlesMillisStart) * .01f;

  particlePhysicsMillisStart = millis();
  particlePhysics();
  particlePhysicsMillisTotal = particlePhysicsMillisTotal *.99f + (millis() - particlePhysicsMillisStart) * .01f;
  
  drawParticlesMillisStart = millis();
  drawParticles();
  drawParticlesMillisTotal = drawParticlesMillisTotal *.99f + (millis() - drawParticlesMillisStart) * .01f;
  
  updatePixelsMillisStart = millis();
  updatePixels();
  updatePixelsMillisTotal = updatePixelsMillisTotal *.99f + (millis() - updatePixelsMillisStart) * .01f;
  
  userInterfaceMillisStart = millis();
  userInterface();
  userInterfaceMillisTotal = userInterfaceMillisTotal *.99f + (millis() - userInterfaceMillisStart) * .01f;
  
  updateChunksMillisStart = millis();
  updateChunks();
  updateChunksMillisTotal = updateChunksMillisTotal *.99f + (millis() - updateChunksMillisStart) * .01f;
  
  totalMillisTotal = totalMillisTotal *.99f + (millis() - totalMillisStart) * .01f;
}
public void drawParticles() {
  for (int i = 0; i < widthheight; i++) {
    switch(particles[i]) {
    case 0:
      pixels[i] = 0xFF000000;
      break;
    case 1:
      pixels[i] = 0xFFC2B280;
      break;
    case 2:
      pixels[i] = 0xFFD4F1F9;
    }
    if (pixelUpdates[i]) pixels[i] = 0xFFFF0000;
    
    float distance = sq(i % width - mouseX) + sq(i / width - mouseY);
    if (distance < paintSize && distance > paintSizeOffset) pixels[i] = 0xFFFFFFFF;
  }
}
public boolean inBoundsCheck(int i, int j) {
  if (i >= 0 && j >= 0 && i < width && j < height) return true;
  return false;
}
public void mouseParticles() {
  if (mousePressed) for (int i = 0; i < width; i++) for (int j = 0; j < height; j++) {
    float distance = sq(i - mouseX) + sq(j - mouseY);
    if (distance < paintSize) {
      if (mouseButton == LEFT && particles[i + j * width] == 0) {
        particles[i + j * width] = element;
        chunkState[i / chunkSize][j /chunkSize] = 1;
      } else if (mouseButton == RIGHT && particles[i + j * width] != 0) {
        particles[i + j * width] = 0;
        chunkState[i / chunkSize][j /chunkSize] = 1;
      }
    }
  }
  if (keyPressed) {
    if (!pressed[0] && key == ' ') {
      paused = !paused;
      pressed[0] = true;
    }
    if (!pressed[1] && key == 'c') {
      drawChunks = !drawChunks;
      pressed[1] = true;
    }
    if (!pressed[2] && key == 'e') {
      element++;
      if (element > 2) element = 1;
      pressed[2] = true;
    }
  }
}

public void keyReleased() {
  if (key == ' ') pressed[0] = false;
  if (key == 'c') pressed[1] = false;
  if (key == 'e') pressed[2] = false;
}

public void mouseWheel(MouseEvent event) {
  if (abs(event.getCount()) == event.getCount()) paintSize = (int)sq(sqrt(paintSize) - 10);
  else paintSize = (int)sq(sqrt(paintSize) + 10);
  if (paintSize < 100) paintSize = 100;
  paintSizeOffset = (int)sq(sqrt(paintSize) - 2);
}
public void particlePhysics() {
  if (!paused) {
    final boolean random = abs(xorshift()) >> 30 == 0;
    int g, h, i, j;
    if (random) {
      for (h = heightChunks - 1; h > -1; h--) for (g = 0; g < widthChunks; g++) if (chunkState[g][h] != 0) {
        int gTimesChunkSize = g * chunkSize;
        int hTimesChunkSize = h * chunkSize;
        for (i = gTimesChunkSize; i < gTimesChunkSize + chunkSize; i++) for (j = hTimesChunkSize + chunkSize - 1; j > hTimesChunkSize - 1; j--) if (j < height && i < width) updateParticles(i, j);
      }
    } else for (h = heightChunks - 1; h > -1; h--) for (g = widthChunks - 1; g > -1; g--) if (chunkState[g][h] != 0) {
      int gTimesChunkSize = g * chunkSize;
      int hTimesChunkSize = h * chunkSize;
      for (i = gTimesChunkSize + chunkSize - 1; i > gTimesChunkSize - 1; i--) for (j = hTimesChunkSize + chunkSize - 1; j > hTimesChunkSize - 1; j--) if (j < height && i < width) updateParticles(i, j);
    }
  }
}

public void updateParticles(int i, int j) {
  switch(particles[i + j * width]) {
  case 1:
    updateSand(i, j);
    break;
  case 2:
    updateWater(i, j);
  }
}

public void updateSand(int i, int j) {
  int[] options = new int[3];
  int index = 0;

  final int jAdd1MultWidth = (j + 1) * width;
  final int down = i + jAdd1MultWidth;
  final int left = i - 1 + jAdd1MultWidth;
  final int right = i + 1 + jAdd1MultWidth;

  if (inBoundsCheck(i, j + 1) && density[particles[down]] < sandDensity) {
    options[0] = 0;
    index++;
  }
  if (inBoundsCheck(i - 1, j + 1) && density[particles[left]] < sandDensity) {
    options[index] = 1;
    index++;
  }
  if (inBoundsCheck(i + 1, j + 1) && density[particles[right]] < sandDensity) {
    options[index] = 2;
    index++;
  }

  if (index != 0) {
    switch(options[abs(xorshift()) % index]) {
    case 0:
      if (abs(xorshift) >> 25 > viscosity[particles[down]]) {
        particles[i + j * width] = particles[down];
        particles[down] = 1;
        chunkState[i / chunkSize][(j + 1) / chunkSize] = 2;
      }
      break;
    case 1:
      if (abs(xorshift) >> 25 > viscosity[particles[left]]) {
        particles[i + j * width] = particles[left];
        particles[left] = 1;
        chunkState[(i - 1) / chunkSize][(j + 1) / chunkSize] = 2;
      }
      break;
    case 2:
      if (abs(xorshift) >> 25 > viscosity[particles[right]]) {
        particles[i + j * width] = particles[right];
        particles[right] = 1;
        chunkState[(i + 1) / chunkSize][(j + 1) / chunkSize] = 2;
      }
    }
    int iDivChunkSize = i / chunkSize;
    int iModChunkSize = i % chunkSize;
    chunkState[iDivChunkSize][(j - 1) / chunkSize] = 2;
    if (iModChunkSize == 0 && i != 0) chunkState[iDivChunkSize - 1][j / chunkSize] = 2;
    else if (iModChunkSize == chunkSize - 1 && i != width - 1) chunkState[iDivChunkSize + 1][j / chunkSize] = 2;
  }
}

public void updateWater(int i, int j) {
  int[] options = new int[5];
  int index = 0;

  final int jAdd1MultWidth = (j + 1) * width;
  final int down = i + jAdd1MultWidth;
  final int leftdown = i - 1 + jAdd1MultWidth;
  final int rightdown = i + 1 + jAdd1MultWidth;
  final int left = i - 1 + j * width;
  final int right = i + 1 + j * width;

  if (inBoundsCheck(i, j + 1) && density[particles[down]] < waterDensity) {
    options[0] = 0;
    index++;
  }
  if (inBoundsCheck(i - 1, j + 1) && density[particles[leftdown]] < waterDensity) {
    options[index] = 1;
    index++;
  }
  if (inBoundsCheck(i + 1, j + 1) && density[particles[rightdown]] < waterDensity) {
    options[index] = 2;
    index++;
  }
  if (inBoundsCheck(i - 1, j) && density[particles[left]] < waterDensity) {
    options[index] = 3;
    index++;
  }
  if (inBoundsCheck(i + 1, j) && density[particles[right]] < waterDensity) {
    options[index] = 4;
    index++;
  }

  if (index != 0) {
    switch(options[abs(xorshift()) % index]) {
    case 0:
      if (abs(xorshift) >> 25 > viscosity[particles[down]]) {
        particles[i + j * width] = particles[down];
        particles[down] = 2;
        chunkState[i / chunkSize][(j + 1) / chunkSize] = 2;
      }
      break;
    case 1:
      if (abs(xorshift) >> 25 > viscosity[particles[leftdown]]) {
        particles[i + j * width] = particles[leftdown];
        particles[leftdown] = 2;
        chunkState[(i - 1) / chunkSize][(j + 1) / chunkSize] = 2;
      }
      break;
    case 2:
      if (abs(xorshift) >> 25 > viscosity[particles[rightdown]]) {
        particles[i + j * width] = particles[rightdown];
        particles[rightdown] = 2;
        chunkState[(i + 1) / chunkSize][(j + 1) / chunkSize] = 2;
      }
      break;
    case 3:
      if (abs(xorshift) >> 25 > viscosity[particles[left]]) {
        particles[i + j * width] = particles[left];
        particles[left] = 2;
        chunkState[(i - 1) / chunkSize][j / chunkSize] = 2;
      }
      break;
    case 4:
      if (abs(xorshift) >> 25 > viscosity[particles[right]]) {
        particles[i + j * width] = particles[right];
        particles[right] = 2;
        chunkState[(i + 1) / chunkSize][j / chunkSize] = 2;
      }
    }
    int iDivChunkSize = i / chunkSize;
    int iModChunkSize = i % chunkSize;
    chunkState[iDivChunkSize][(j - 1) / chunkSize] = 2;
    if (iModChunkSize == 0 && i != 0) chunkState[iDivChunkSize - 1][j / chunkSize] = 2;
    else if (iModChunkSize == chunkSize - 1 && i != width - 1) chunkState[iDivChunkSize + 1][j / chunkSize] = 2;
  }
}
public void updateChunks() {
  if (!paused) for (int i = 0; i < widthChunks; i++) for (int j = 0; j < heightChunks; j++) if (chunkState[i][j] != 0) chunkState[i][j]--;
}
public void userInterface() {
  if (drawChunks) for (int i = 0; i < widthChunks; i++) for (int j = 0; j < heightChunks; j++) {
    if (chunkState[i][j] == 2) stroke(0, 255, 0);
    else if (chunkState[i][j] == 1) stroke(255, 0, 255);
    if (chunkState[i][j] != 0) {
      noFill();
      rect(i * chunkSize, j * chunkSize, chunkSize - 1, chunkSize - 1);
    }
  }
  
  fill(0, 255, 255);
  if (paused) text("PAUSED", width - 60, height - 20);
  text(frameRate, 20, height - 20);
  text(totalMillisTotal, 120, height - 20);
  text(loadPixelsMillisTotal, 220, height - 20);
  text(mouseParticlesMillisTotal, 320, height - 20);
  text(particlePhysicsMillisTotal, 420, height - 20);
  text(drawParticlesMillisTotal, 520, height - 20);
  text(updatePixelsMillisTotal, 620, height - 20);
  text(userInterfaceMillisTotal, 720, height - 20);
  text(updateChunksMillisTotal, 820, height - 20);
  text("frameRate", 20, height - 40);
  text("total", 120, height - 40);
  text("loadPixels", 220, height - 40);
  text("mouseParticles", 320, height - 40);
  text("particlePhysics", 420, height - 40);
  text("drawParticles", 520, height - 40);
  text("updatePixels", 620, height - 40);
  text("userInterface", 720, height - 40);
  text("updateChunks", 820, height - 40);
}
public int xorshift() {
  xorshift ^= xorshift << 13;
  xorshift ^= xorshift >> 17;
  xorshift ^= xorshift << 5;
  return xorshift;
}


  public void settings() { fullScreen(JAVA2D);
noSmooth(); }

  static public void main(String[] passedArgs) {
    String[] appletArgs = new String[] { "--full-screen", "--bgcolor=#666666", "--stop-color=#cccccc", "sandbox" };
    if (passedArgs != null) {
      PApplet.main(concat(appletArgs, passedArgs));
    } else {
      PApplet.main(appletArgs);
    }
  }
}
