/* 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 perlin_noise extends PApplet {

final int columns = 16;
final int rows = 9;
int cellWidth, cellHeight;

boolean[][] chunkState = new boolean[columns][rows];

float horizontalPixelFactor, verticalPixelFactor;

int iMax, jMax;

PVector[][] vectors = new PVector[columns + 1][rows + 1];

int theme = 0;

public void setup()
{
  /* size commented out by preprocessor */;
  frameRate(1000);
  /* noSmooth commented out by preprocessor */;
  fill(0xFF00FFFF);
  stroke(0xFF00FFFF);

  cellWidth = width / columns;
  cellHeight = height / rows;

  iMax = columns * cellWidth;
  jMax = rows * cellHeight;

  horizontalPixelFactor = 1.0f / cellWidth;
  verticalPixelFactor = 1.0f / cellHeight;

  for (int i = 0; i <= columns; i++) for (int j = 0; j <= rows; j++) vectors[i][j] = PVector.random2D();

  activateAllChunks();

  loadPixels();
}

public void draw()
{
  if (mousePressed) updateVectors();

  drawNoise();

  updateChunks();

  //drawGrid();

  drawVectors();

  text(frameRate, 20, height - 20);
}
public void updateChunks()
{
  for (int i = 0; i < columns; i++) for (int j = 0; j < rows; j++) chunkState[i][j] = false;
}

public void activateAllChunks()
{
  for (int i = 0; i < columns; i++) for (int j = 0; j < rows; j++) chunkState[i][j] = true;
}
public int colorize(float x)
{
  switch(theme)
  {
  case 0:
    return 0xFF << 24 | PApplet.parseInt((x + 1) * 0x7F) << 16 | PApplet.parseInt((x + 1) * 0x7F) << 8 | PApplet.parseInt((x + 1) * 0x7F);
  case 1:
    return 0xFF << 24 | cap((x + 1) * 0xFF) << 16 | cap(0xFF - x * 0xFF) << 8 | cap(0xFF - x * 0xFF);
  case 2:
    return 0xFF << 24 | cap(0xFF - x * 0xFF) << 16 | cap((x + 1) * 0xFF) << 8 | cap(0xFF - x * 0xFF);
  case 3:
    return 0xFF << 24 | cap(0xFF - x * 0xFF) << 16 | cap(0xFF - x * 0xFF) << 8 | cap((x + 1) * 0xFF);
  case 4:
    return x < 0 ? 0xFF000000 : 0xFFFFFFFF;
  case 5:
    return x < 0 ? 0xFFFF0000 : 0xFF00FFFF;
  case 6:
    return x < 0 ? 0xFF00FF00 : 0xFFFF00FF;
  case 7:
    return x < 0 ? 0xFF0000FF : 0xFFFFFF00;
  case 8:
    return x < 0 ? x < -.2f ? 0xFF000000 : 0xFF555555 : x > .2f ? 0xFFFFFFFF : 0xFFAAAAAA;
  case 9:
    return x < 0 ? x < -.2f ? 0xFF000000 : 0xFF551000 : x > .2f ? 0xFFFF0000 : 0xFFAA0800;
  case 10:
    return x < 0 ? x < -.2f ? 0xFF000000 : 0xFF456500 : x > .2f ? 0xFFFFFF00 : 0xFFA2B200;
  case 11:
    return x < 0 ? x < -.2f ? 0xFF000000 : 0xFF005510 : x > .2f ? 0xFF00FF00 : 0xFF00AA08;
  case 12:
    return x < 0 ? x < -.2f ? 0xFF000000 : 0xFF004565 : x > .2f ? 0xFF00FFFF : 0xFF00A2B2;
  case 13:
    return x < 0 ? x < -.2f ? 0xFF000000 : 0xFF100055 : x > .2f ? 0xFF0000FF : 0xFF0800AA;
  case 14:
    return x < 0 ? x < -.2f ? 0xFF000000 : 0xFF650045 : x > .2f ? 0xFFFF00FF : 0xFFB200A2;
  case 15:
    return x > -.1f && x < .1f ? 0xFFFFFFFF : 0xFF000000;
  case 16:
    return 0xFF << 24 | PApplet.parseInt(abs(x) * 0xFF) << 16 | PApplet.parseInt(abs(x) * 0xFF) << 8 | PApplet.parseInt(abs(x) * 0xFF);
  case 17:
    return 0xFF << 24 | 0xFF - PApplet.parseInt(abs(x) * 0xFF) << 16 | 0xFF - PApplet.parseInt(abs(x) * 0xFF) << 8 | 0xFF - PApplet.parseInt(abs(x) * 0xFF);
  }
  return 0;
}

public int cap(float x)
{
  if (x > 255) return 255;
  return PApplet.parseInt(x);
}
public void drawGrid()
{
  for (int i = 0; i < columns; i++) line(i * cellWidth, 0, i * cellWidth, height);
  for (int i = 0; i < rows; i++) line(0, i * cellHeight, width, i * cellHeight);
}
public void drawNoise()
{
  float horizontalOffset = 0;
  for (int i = 0; i < cellWidth; i++)
  {
    float verticalOffset = 0;
    for (int j = 0; j < cellHeight; j++)
    {
      final float smoothstepFactorX = smoothstep(horizontalOffset);
      final float smoothstepFactorY = smoothstep(verticalOffset);

      final PVector offsetTopLeft = new PVector(horizontalOffset, verticalOffset);
      final PVector offsetTopRight = new PVector(horizontalOffset - 1, verticalOffset);
      final PVector offsetBottomLeft = new PVector(horizontalOffset, verticalOffset - 1);
      final PVector offsetBottomRight = new PVector(horizontalOffset - 1, verticalOffset - 1);

      int horizontalPosition = i;
      for (int currentColumn = 0; currentColumn < columns; currentColumn++)
      {
        int verticalPosition = j;
        for (int currentRow = 0; currentRow < rows; currentRow++)
        {
          if (chunkState[currentColumn][currentRow])
          {
            final float dotTopLeft = dotProduct(vectors[currentColumn][currentRow], offsetTopLeft);
            final float dotTopRight = dotProduct(vectors[currentColumn + 1][currentRow], offsetTopRight);
            final float dotBottomLeft = dotProduct(vectors[currentColumn][currentRow + 1], offsetBottomLeft);
            final float dotBottomRight = dotProduct(vectors[currentColumn + 1][currentRow + 1], offsetBottomRight);

            final float value = (dotBottomRight * smoothstepFactorX + dotBottomLeft * (1 - smoothstepFactorX)) * smoothstepFactorY + (dotTopRight * smoothstepFactorX + dotTopLeft * (1 - smoothstepFactorX)) * (1 - smoothstepFactorY);

            pixels[horizontalPosition + verticalPosition * width] = colorize(value);
          }
          verticalPosition += cellHeight;
        }
        horizontalPosition += cellWidth;
      }
      verticalOffset += verticalPixelFactor;
    }
    horizontalOffset += horizontalPixelFactor;
  }
  updatePixels();
}

public float dotProduct(PVector a, PVector b)
{
  return a.x * b.x + a.y * b.y;
}

public float smoothstep(float x)
{
  return x * x * (3 - 2 * x);
}
public void drawVectors()
{
  for (int i = 0; i <= columns; i++) for (int j = 0; j <= rows; j++) line(i * cellWidth, j * cellHeight, i * cellWidth + vectors[i][j].x * 30, j * cellHeight + vectors[i][j].y * 30);
}
public void updateVectors()
{
  int mouseXGrid = (mouseX + cellWidth / 2) / cellWidth;
  int mouseYGrid = (mouseY + cellHeight / 2) / cellHeight;

  vectors[mouseXGrid][mouseYGrid] = new PVector(mouseX - (mouseXGrid) * cellWidth, mouseY - (mouseYGrid) * cellHeight).normalize();

  if (mouseXGrid != columns && mouseYGrid != rows) chunkState[mouseXGrid][mouseYGrid] = true;
  if (mouseXGrid != 0 && mouseYGrid != rows) chunkState[mouseXGrid - 1][mouseYGrid] = true;
  if (mouseYGrid != 0 && mouseXGrid != columns) chunkState[mouseXGrid][mouseYGrid - 1] = true;
  if (mouseXGrid != 0 && mouseYGrid != 0)chunkState[mouseXGrid - 1][mouseYGrid - 1] = true;
}

public void keyPressed()
{
  if (key == ' ')
  {
    theme++;
    if (theme > 17) theme = 0;

    activateAllChunks();

    switch(theme)
    {
    case 0:
      fill(0xFF00FFFF);
      stroke(0xFF00FFFF);
      break;
    case 1:
      fill(0xFF000000);
      stroke(0xFF000000);
      break;
    case 4:
      fill(0xFFFF00FF);
      stroke(0xFFFF00FF);
      break;
    case 5:
      fill(0xFF000000);
      stroke(0xFF000000);
      break;
    case 8:
      fill(0xFFFF00FF);
      stroke(0xFFFF00FF);
      break;
    case 9:
      fill(0xFFFFFFFF);
      stroke(0xFFFFFFFF);
      break;
    case 15:
      fill(0xFFFF00FF);
      stroke(0xFFFF00FF);
    }
  }
}


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

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