My project library #5! Gallery megathread

Here are some of my projects! I hope you get some ideas/inspiration from it : P

There are 4 previous megathreads with 60 projects in total!

If you have any questions regarding the projects, use the #[project number] while referencing them!
Any suggestions/ideas are very welcome! Enjoy!

2 Likes

#62 4 point Bézier curve

Code
p po[] = new p[10]; //4 primary, 3 V% across, 2 V$ across secondary, 1 V% across terciary
ln l[] = new ln[6];
float V = 0.5;
boolean pediting = false;

void setup() {
  size(600, 600);
  stroke(255);
  float offX = 0, offY = 0;
  po[0] = new p(100 + offX, 100 + offY, true);
  po[1] = new p(500 + offX, 100 + offY, true);
  po[2] = new p(500 + offX, 500 + offY, true);
  po[3] = new p(100 + offX, 500 + offY, true);

  for (int i = 4; i < po.length; i++) po[i] = new p(0, 0, false);

  l[0] = new ln(0, 1);
  l[1] = new ln(1, 2);
  l[2] = new ln(2, 3);

  l[3] = new ln(4, 5);
  l[4] = new ln(5, 6);

  l[5] = new ln(7, 8);
}
void draw() {
  background(0); 
  if (pediting == false && mousePressed) V = constrain(map(mouseX, 0, width, 0, 1),0,1);

  po[4].setValue( l[0].pos(V) );
  po[5].setValue( l[1].pos(V) );
  po[6].setValue( l[2].pos(V) );

  po[7].setValue( l[3].pos(V) );
  po[8].setValue( l[4].pos(V) );

  po[9].setValue( l[5].pos(V) );
  noStroke();
  for (int i = 0; i < po.length; i++) {
    po[i].display();
  }
  stroke(255);
  for (int i = 0; i < 6 && 0 != l.length; i++) {
    l[i].drawLN();
  }

  drawCurve(0.001, 0,V);
}

void drawCurve(float increment, float start, float stop) {
  noFill();
  stroke(0,0,255);
  beginShape();
  for (float vv = start; vv < stop; vv+=increment) {
    PVector cpoint = getPos(vv);
    vertex(cpoint.x, cpoint.y);
  }
  endShape();
}

void keyPressed() {
  if (key == ' ') pediting = !pediting;
}

void mouseDragged() {
  if (pediting) {
    int maxRadius = 50;
    int closest = -1;
    float closestDist = maxRadius;
    for (int i = 0; i < po.length; i++) if (po[i].canMove==true) {
      float dst = dist(mouseX, mouseY, po[i].pv.x, po[i].pv.y);
      if (dst < closestDist) {
        closestDist = dst;
        closest = i;
      }
    }
    if(closest>=0) {
      po[closest].pv = new PVector(mouseX,mouseY);
    }
  }
}

PVector getPos(float V) {
  po[4].setValue( l[0].pos(V) );
  po[5].setValue( l[1].pos(V) );
  po[6].setValue( l[2].pos(V) );

  po[7].setValue( l[3].pos(V) );
  po[8].setValue( l[4].pos(V) );

  po[9].setValue( l[5].pos(V) );

  return l[5].pos(V);
}


class ln {
  int p1, p2;
  ln(int p1, int p2) {
    this.p1 = p1;
    this.p2 = p2;
  }
  void drawLN() {
    stroke(255);
    line(po[p1].pv.x, po[p1].pv.y, po[p2].pv.x, po[p2].pv.y);
  }
  PVector pos (float v) {
    return new PVector( lerp(po[p1].pv.x, po[p2].pv.x, v), lerp(po[p1].pv.y, po[p2].pv.y, v) );
  }
}

class p {
  PVector pv;
  boolean canMove = false;
  p(float x, float y, boolean canMove) {
    pv = new PVector(x, y);
    this.canMove = canMove;
  }
  void setValue(PVector pv) {
    this.pv = pv;
  }
  void display() {
    fill( (pediting? #00FF00 : #FFFFFF) );
    circle(pv.x, pv.y, (pediting && canMove? 15 : 5));
  }
}

Press space to edit point location. Hold mouse to change the completion of the curve.

#62 B | N - point Bézier

Code
int n = 4; //N-point brazier curve
int prevN = n;
p po[] = new p[0];
l ln[] = new l[0];
float v = 0.5;
boolean pediting = false;
boolean displayLines = true;
boolean displayDots = true;
void setup() {
  updateStructure(true);
  size(600, 600);
}
void draw() {
  if (mousePressed && pediting == false) v = map(mouseX, 0, width, 0, 1);
  background(0);
  updatePoints(v);
  if(displayDots) for (int i = 0; i < po.length; i++) po[i].display();
  if(displayLines) for (int i = 0; i < ln.length; i++) ln[i].display();
  drawCurve(0.01, 0, 1);
}

void keyPressed() {
  if (key == ' ') {
    n++;
    updateStructure(false,new p(mouseX,mouseY,true));
  }
  if (key == 'c') {
    n++;
    updateStructure(true);
  }
  if (key == 'e') {
    pediting = !pediting;
  }
  if(key == 'h') displayLines = !displayLines;
  if(key == 'd') displayDots = !displayDots;
}

void mouseDragged() {
  if (pediting) {
    int maxRadius = 50;
    int closest = -1;
    float closestDist = maxRadius;
    for (int i = 0; i < po.length; i++) if (po[i].canMove==true) {
      float dst = dist(mouseX, mouseY, po[i].pv.x, po[i].pv.y);
      if (dst < closestDist) {
        closestDist = dst;
        closest = i;
      }
    }
    if (closest>=0) {
      po[closest].pv = new PVector(mouseX, mouseY);
    }
  }
}

void drawCurve(float increment, float start, float stop) {
  noFill();
  stroke(0, 0, 255);
  beginShape();
  for (float vv = start; vv < stop; vv+=increment) {
    PVector cpoint = getPos(vv);
    vertex(cpoint.x, cpoint.y);
  }
  endShape();
}

PVector getPos(float V) {
  updatePoints(V);
  return po[n*(n+1)/2-1].pv;
}

void updatePoints(float V) {
  for (int i = n; i < po.length; i++) {
    po[i].setValue( ln[i-n].pos(V));
  }
}

void updateStructure(boolean createPoints, p... addPoint) {
  if (n < 3) n = 3;

  if (addPoint.length>0) n+=addPoint.length-1;

  p newPO[] = new p[n*(n+1)/2];
  ln = new l[n*(n-1)/2];
  for(int i = 0; i < newPO.length; i++) newPO[i] = new p(150,150,true);
  for (int i = 0; i < prevN && i < po.length; i++) newPO[i] = po[i];
  if (addPoint.length>0) for (int i = 0; i < addPoint.length; i++) newPO[i+prevN] = addPoint[i];
  po = newPO;

  if (createPoints) { //creating PRIMARY POINTS
    float cx = width*0.5, cy = height*0.5, r = 250, a = TWO_PI/n;
    for (int i = 0; i < n; i++) {
      po[i] = new p( cx + cos(a*i)*r, cy + sin(a*i)*r, true);
    }
  }
  for (int i = n; i < po.length; i++) po[i] = new p(-1, -1, false); //creating all NON-PRIMARY points

  int counter = 0, counter2 = 0;
  for (int i = 1; i < n; i++) {
    for (int j = i; j < n; j++) {
      ln[counter2] = new l(counter, counter+1);
      counter++;
      counter2++;
    }
    counter++;
  }
  println(prevN,n,addPoint.length);
  prevN = n;
}

class p { //point
  PVector pv;
  boolean canMove;
  p (float x, float y, boolean canMove) {
    pv = new PVector(x, y);
    this.canMove = canMove;
  }
  void setValue(PVector pv) {
    this.pv = pv;
  }
  void display() {
    fill( (pediting? #00FF00 : #FFFFFF) ); 
    noStroke();
    circle(pv.x, pv.y, (pediting && canMove? 15 : 5 ));
  }
}
class l { //line
  int p1, p2;
  l (int p1, int p2) {
    this.p1 = p1;
    this.p2 = p2;
  }
  PVector pos(float v) {
    if(p1>=po.length-1 || p2>=po.length-1) println(p1,p2);
    return new PVector( lerp(po[p1].pv.x,po[p2].pv.x, v),lerp(po[p1].pv.y,po[p2].pv.y, v) );
  }
  void display() {
    stroke(255);
    line(po[p1].pv.x, po[p1].pv.y, po[p2].pv.x, po[p2].pv.y);
  }
}

Instructions:

  • when dots are white, drag the mouse across the screen to change V. Only mouseX is counted.
  • press ‘e’ to enter editing mode - all dots will turn green and the closest Large dot (primary), if in range of 50px, will be moved to mouse position.
  • press ‘c’ to add 1 point and arrange all points into a circle ish shape.
  • press ’ ’ to add a point where the cursor is without affecting the others
  • press ‘h’ to toggle lines
  • press ‘d’ to toggle dots.

#63 Snowflake generator
Creates a random snowflake-ish shape. Press ‘space’ to reset and use mouse to (un)pause. Scroll mouse wheel to adjust complexity (speed of changes). Press ‘c’ to clear stage and reset.


How to adjust things inside of the program?

  • Change the number of sides? - N, default = 6
  • Change step length? - L, default 5
  • Circle size? - cd, default L*0.5;
  • Update rate? - frameRate( speed );
    Everything else is explained inside of the code.

Just felt like this was a project worth sharing. It is simple and has a fun result. I first saw the idea about a year ago. I don’t remember the whole story, but I saw this and it surprised me. I couldn’t understand the code at all. I remember staring at it for minutes, yet coming up with nothing. Anyway, have fun.

Code
int N = 6; //sides
int a = 0, modulo = 3; //editing has no use, besides changing initial value
float l = 5, cd = l*0.5; //step length, circle diameter
final float A = TWO_PI/N; //just felt like using constants
mover m[] = new mover[2*N];
boolean active = true; //state? Looping / not looping
color bgColor = color(0), circleColor = color(255);
void setup() {
  size(600, 600);
  background(bgColor);
  fill(255); 
  noStroke();
  //frameRate(10); //update rate
  for (int i = 0; i < N; i++) {
    m[2*i]   = new mover(300, 300,  1, i); //adding pairs of points. 
    m[2*i+1] = new mover(300, 300, -1, i);
  }
}
void draw() {
  if (active) {
    fill(bgColor, 3); //fading speed. Set 2nd value to something between 0 and 255. 0 means no clearing trail, while anything >0 means that it is slowly erased. 255 means instant erasure. 
    rect(0, 0, width, height);
    fill(circleColor);
    if (frameCount%modulo==0) a+=round(random(-1, 1)); //changing direction 
    a%=N; //a is always between 0 and N
    a%=-N;

    for (int i = 0; i < 2*N; i++) m[i].move();
    for (int i = 0; i < 2*N; i++) m[i].display();
  }
}
void reset() {
  for (int i = 0; i < N; i++) {
    m[2*i]   = new mover(300, 300, 1, i);
    m[2*i+1] = new mover(300, 300, -1, i);
  }
}
void keyPressed() {
  if (key == ' ') reset();
  if (key == 'c') {
    background(bgColor);
    reset();
  }
}
void mousePressed() {
  active=!active;
}
void mouseWheel(MouseEvent e) {
  int n = round(e.getCount());
  modulo-=n;
  if(modulo<1) modulo=1; //if modulo = 0, program crashes
  println(modulo); 
}

class mover {
  float x, y;
  int am, ao;
  mover(float x, float y, int am, int ao) {
    this.x=x;
    this.y=y;
    this.am=am;
    this.ao=ao;
  }
  void move() {
    int la = (a+ao)*am;
    x+= cos(la*A)*l;
    y+= sin(la*A)*l;
  }
  void display() {
    circle(x, y, cd);
  }
}


1 Like

#64 Pythagoras tree


image
image
image

image

Controls:

  • Press any key to generate the next branch
  • Left click to change the triangle shape
  • Drag right click - change base square

This is a proof of concept, so it has some issues.

Code
float rot = -QUARTER_PI, move = sqrt(2)/2, baseLen=100;
squ base = getSqu(new PVector[]{new PVector(300-baseLen*0.5, 500), new PVector(300+baseLen*0.5, 500)});
ArrayList<squ> ss = new ArrayList<squ>();
float sx=0,sy=0;

void setup() {
  size(600, 600);
  stroke(255);
  ss.add(base);
}
void draw() {
  background(0);
  //base = getSqu(new PVector[]{new PVector(300,300), new PVector(mouseX,mouseY)});
  for (int i = 0; i < ss.size(); i++) ss.get(i).display();
  ss.get(0).displayPoint();
}
void keyPressed() { 
  step();
}
void mousePressed() {
  if (mouseButton==LEFT) {
    float dir = atan2(base.points[3].y-mouseY, base.points[3].x-mouseX);
    float dst = -dist (base.points[3].x, base.points[3].y, mouseX, mouseY)/baseLen;
    rot = dir;
    move=dst;
    base.updatePrefs();
    ss.clear();
    ss.add(base);
  } else if(mouseButton==RIGHT) {
    sx=mouseX;
    sy=mouseY;
  }
}
void mouseReleased() {
  if(mouseButton==RIGHT) {
    base = getSqu(new PVector[]{new PVector(sx,sy),new PVector(mouseX,mouseY)});
    baseLen=dist(sx,sy,mouseX,mouseY);
    base.updatePrefs();
    ss.clear();
    ss.add(base);
  }
}
void step() {
  for (int i = 0, m = ss.size(); i < m; i++) ss.get(i).dupe();
}

class squ {
  PVector points[], third;
  boolean canDupe = true;
  squ(PVector[] points) {
    this.points = points;
    third = getThirdPoint(new PVector[]{points[3], points[2]});
  }
  void updatePrefs() {
    canDupe=true;
    third = getThirdPoint(new PVector[]{points[3], points[2]});
  }
  void display() {
    for (int i = 0; i < 4; i++) line(points[i].x, points[i].y, points[(i+1)%4].x, points[(i+1)%4].y);
  }
  void displayPoint() {
    circle(third.x, third.y, 5);
  }
  void dupe() {
    if (canDupe) {
      ss.add(getSqu(new PVector[]{points[3], third}));
      ss.add(getSqu(new PVector[]{third, points[2]}));
      canDupe=false;
    }
  }
}

PVector getThirdPoint(PVector[] points) {
  float dir = atan2(points[1].y-points[0].y, points[1].x-points[0].x);
  float len = dist(points[0].x, points[0].y, points[1].x, points[1].y);

  return new PVector(points[0].x+ cos(dir+rot)*len*move, points[0].y + sin(dir+rot)*len*move);
}

squ getSqu(PVector[] points) {
  PVector output[] = {points[0], points[1], new PVector(), new PVector()};
  float dir = atan2(points[1].y-points[0].y, points[1].x-points[0].x);
  float dir2 = dir - HALF_PI;
  float len = dist(points[0].x, points[0].y, points[1].x, points[1].y);
  float kx = cos(dir2), ky = sin(dir2);

  output[3] = new PVector(points[0].x+kx*len, points[0].y+ky*len);
  output[2] = new PVector(points[1].x+kx*len, points[1].y+ky*len);

  return new squ(output);
}


#65 Noise flow field
It’s been a while… a few months actually…

Images are here: couldn’t upload for some reason

A flow field is a field of vectors, which tell the agents operating on it in which direction to move towards. A noise flow field is a flow field that uses Perlin noise to generate the directions and so the values near each other are similar.

Features:

  • Basic flow field functionalities
  • Screenshot key bind (ENTER)
  • Built in presets and examples (of all the images shown above)
  • Ability to edit everything about the presets without needing to modify the code (inside the running program)
  • Console popups with lots of features
  • A lot of customizations
  • A lot of color options (and logic settings)
  • Comments
  • And much much more!

Warning: the code is far more complex that the typical flow field program. It is because I added so many features. Just the basic program wouldn’t even be 30 lines. Not to mention 500…

Code
/*
Better noise flow field, with more settings!
*/
import javax.swing.JOptionPane; //library to get the popups working

String commandTutorialText = //'help' command text
"Commands:\n"+
"'clear': clears the window.\n"+
"'pause': toggle pause\n"+
"'load': enter the load sequence\n"+
"'savep': save the current settings/color manager to presets\n"+
"'print': prints active settings and color-manager, in the form of code\n"+
"'list': get the number of settings and color-manager presets\n"+
"'set': enter the sequence to modify current active preset\n"+
"'rush': iterate N frames, without updating the screen\n"+
"'teleport': teleport all agents to random locations\n"+
"\n"+
"'exit': terminate the program\n"+
"'back': return to the simulation\n"
;

ColorManager cm; //active color-manager
ArrayList<ColorManager> colorPresets = new ArrayList<ColorManager>();// color-manager presets
Settings s; //active settings
ArrayList<Settings> settingsPresets = new ArrayList<Settings>(); //Settings settingsPresets[];
Particle p[]; //agents / particles
Console console; //console
boolean paused = false;

void setup() {
  fullScreen();
  initialize(); //create presets & create console
  loadSettingPreset(1);
  loadColorPreset(0);
  //printPresetCode(colorPresets.get(1)); //this will output the code required to implement a preset created within the program
  
  //if you are annoyed when at having to close the popup window everytime on launch, comment out this line
  console.printMessage("Instructions:\nWhen focused on the window, the following actions can be taken:\nPress space: bring out command console\nPress enter: screenshot the screen (will be saved into the sketch's folder)\nEnjoy!");
  
}
void draw() {
  //if not paused -> do stuff : P
  if(paused == false) doStuff();
}

void doStuff() {
  //update color. not needed for some presets, but necessary for others
  cm.updateColor();
  //set stroke to updated color 
  stroke(cm.getColor());
  for(int i = 0; i < s.n; i++) {
    //updating and drawing agents
    p[i].update();
    p[i].display();
  }
}

void keyPressed() {
  char k = (""+key).toLowerCase().charAt(0); //turning char into lowercase
  if(k == ' ') {
    console.enterCommandWindow("enter 'help' to see command list");
    return;
  }
  if(k == ENTER) {
    saveFrame("screenshots/screenshot_########.png");
  }
}

void reset() {
    noiseSeed(int(random(100000))); //create new noise flow field
    cm.clearBackground();
    loadSettingsPreset(s); //reload active presets
    loadColorPreset(cm);
}
class ColorManager {
    boolean use_rgb; //true -> rgb, false -> hsb
    boolean bounce; //true -> if(vx > 255 || vx < 0) cx *= -1; //false -> if(vx > 255) vx = 0. if(vx<0) vx = 255
    
    //original color -> useful when printing preset code (if i didn't add this, it would only show the final color, not the origin)
    float ogv1, ogv2, ogv3, ogv4;
    
    //color components. I am not saying red, green, blue, alpha values since it could also be HSBA, so it is easier to say components
    float v1, v2, v3, v4;
    //rate of change of color components
    float c1, c2, c3, c4;
    //background components
    float b1, b2, b3;
    
    ColorManager(boolean usingRGB, boolean bounceColor, float startColor[], float colorChange[], float background[] ) {
      
      //i could add rules on what to do if there are not enough elements in the color arrays, but I can't be bothered right now.
      if(startColor.length != 4 || colorChange.length != 4 || background.length != 3) {
        println("\nERROR\nA color preset doesn't have the right amount of color information");
        console.printMessage("\nERROR\nA color preset doesn't have the right amount of color information");
        exit();
      }
      
      use_rgb = usingRGB;
      setColorMode();
      bounce = bounceColor;
      v1 = startColor[0];
      v2 = startColor[1];
      v3 = startColor[2];
      v4 = startColor[3];
      
      c1 = colorChange[0];
      c2 = colorChange[1];
      c3 = colorChange[2];
      c4 = colorChange[3];
      
      b1 = background[0];
      b2 = background[1];
      b3 = background[2];
      
      ogv1 = v1;
      ogv2 = v2;
      ogv3 = v3;
      ogv4 = v4;
    }
    void setColorMode() {
      if(use_rgb) colorMode(RGB,255,255,255,255); //the 255s decide the range. don't change it
      else colorMode(HSB,255,255,255,255);
    }
    
    
    void updateColor() {
      v1 += c1;
      v2 += c2;
      v3 += c3;
      v4 += c4;
      if(bounce) {
        if(v1 > 255 || v1 < 0) {c1 *= -1;}
        if(v2 > 255 || v2 < 0) {c2 *= -1;}
        if(v3 > 255 || v3 < 0) {c3 *= -1;}
        if(v4 > 255 || v4 < 0) {c4 *= -1;}
        return;
      }
      //loop color mode:
      if(v1 > 255) v1 = 0;
      if(v2 > 255) v2 = 0;
      if(v3 > 255) v3 = 0;
      if(v4 > 255) v4 = 0;
      
      if(v1 < 0) v1 = 255;
      if(v2 < 0) v2 = 255;
      if(v3 < 0) v3 = 255;
      if(v4 < 0) v4 = 255;
    }    
    void clearBackground() {
      background(b1,b2,b3);
    }
    color getColor() {
      return color(v1,v2,v3,v4); 
    }
}

class Particle {
  float x, y; //position
  boolean crossedBorder; //if it has crossed the border in the last frame
  boolean exists; //if it exists
  Particle(float x, float y) {
    setPosition(x,y);
    crossedBorder = false;
    exists = true;
  }
  void setPosition(float x, float y) {
    this.x = x;
    this.y = y;
  }
  void update() {
    if(exists == false) return;
    //interpret the field data of 0-1 as rotation
    float a = noise(x*s.noiseM, y*s.noiseM)*TWO_PI;
    //add a random number between 0 and randomDiviation
    a += random(s.randomDiviation);
    //move 1 pixel in the direction the agent is rotated in
    x+= cos(a);
    y+= sin(a);
    //if it is offscreen, trigger the function
    if(x>width||x<0||y>height||y<0) outOfScreen();
  }
  void display() {
    if(crossedBorder == true || exists == false) { crossedBorder = false; return;  };
    //display it only if hasn't crossed the border since last update & exists
    
    point(x,y);
  }
  void outOfScreen() {
      crossedBorder = false;
      switch(s.actionOnLeave) {
          case 0: //stop existing
          exists = false;
          break;
          case 1: //teleport randomly
          setPosition(random(width),random(height));
          break;
          case 2: //come out the other side
          if(x > width) x = 0;
          if(y > height) y = 0;
          if(x < 0) x = width;
          if(y < 0) y = height;          
          break;
          case 3: //mirror location
          if(x > width) {
            x = 0;
            y = height-y;
          }
          if(x < 0) {
            x = width;
            y = height-y;
          }
          //y
          if(y > height) {
            y = 0;
            x = width-x;
          }
          if(y < 0) {
            y = height;
            x = width-x;
          }
          break;
      }
  }
  
}

class Settings {
  int n; //number of agents - higher the number, more lines
  float noiseM; //scale of perlin noise map used to create the flow field
  float randomDiviation; //add a random number when moving. '0' results in sharp lines, '6.28' results in thicker lines.
  int actionOnLeave; //0 -> vanish, 1 -> teleport randomly, 2 -> x > w -> x = 0 ,3 -> x > w -> {x = 0 && y = height-y }
  Settings(int n, float noiseMultiplier, float randomDiviation, int actionOnOffscreen) {
    this.n = n;
    noiseM = noiseMultiplier;
    this.randomDiviation = randomDiviation;    
    actionOnLeave = actionOnOffscreen;
  }
}
void initialize() {
  console = new Console(); //create the console
  
  
  //addPreset is an overloaded function that can recieve either a Settings obj or ColorManager obj
  addPreset(new Settings(
    100000, //number of agents
    0.005, //noise scale multiplier (should be a low number)
    0, //random diviation (agent angle = noise() + random( randomDiviation). Setting it to TWO_PI*n will create a random flow field
    2 //action on agent crossing the border
  ));
  
  addPreset(new ColorManager(
    true, //using RGB (false -> HSB)
    true, //bouncing colors (what to do when clr > 255 || clr < 0)? (true : colr change speed *= -1 | false: loop around)
    new float[]{0,0,0,20}, //starting color
    new float[]{0,0,0,0}, //color change speed
    new float[]{255,255,255} //background color
  ));
  
  addPreset(new Settings( 100, 0.005, 0, 1 ));
  
  
  //bg = background, static = non changing, dynamic = changing
  
  //white bg with static black lines
  addPreset(new ColorManager( true, true, new float[]{0,0,0,20}, new float[]{0,0,0,0}, new float[]{255,255,255}));
  //black bg with static white lines
  addPreset(new ColorManager( true, true, new float[]{255,255,255,20}, new float[]{0,0,0,0}, new float[]{0,0,0}));
  //black bg with static electic blue lines
  addPreset(new ColorManager( true, true, new float[]{0,200,255,20}, new float[]{0,0,0,0}, new float[]{0,0,0}));
  //black bg with static red lines
  addPreset(new ColorManager( true, true, new float[]{255,0,0,20}, new float[]{0,0,0,0}, new float[]{0,0,0}));
  //black bg with static magenta lines
  addPreset(new ColorManager( true, true, new float[]{255,0,255,20}, new float[]{0,0,0,0}, new float[]{0,0,0}));
  //black bg with static yellow lines
  addPreset(new ColorManager( true, true, new float[]{255,255,0,20}, new float[]{0,0,0,0}, new float[]{0,0,0}));
  //white bg with dynamic gray lines
  addPreset(new ColorManager( true, true, new float[]{0,0,0,0}, new float[]{1,1,1,1}, new float[]{255,255,255}));
  
  
  addPreset(new ColorManager( false, true, new float[]{42,255,255,20}, new float[]{1,0,0,0}, new float[]{0,0,0}));
  
  //black bg with bright dynamic lines, that start with red -> orange -> yellow -> green ->blue -> violet -> red
  //change the '0.1' to something larger if you want a faster change
  addPreset(new ColorManager( false, false, new float[]{0,255,255,20}, new float[]{0.1,0,0,0}, new float[]{0,0,0}));
  
}
void addPreset(ColorManager cl) { //overloaded function that can accept ColorManager & Settings class
  colorPresets.add(cl);  
}
void addPreset(Settings st) {
  settingsPresets.add(st);  
}
void loadColorPreset(ColorManager cl) {
  cm = cl;
  cm.setColorMode();
  cm.clearBackground();
}
boolean loadColorPreset(int id) {
  if(id<0 || colorPresets.size()<= id) return false;
  loadColorPreset(colorPresets.get(id));
  return true;
}
void loadSettingsPreset(Settings st) {
  s = st;
  if(cm != null) cm.clearBackground();
  p = new Particle[s.n];
  for(int i = 0; i < s.n; i++) {
    p[i] = new Particle(random(width),random(height));  
  }
}
boolean loadSettingPreset(int id) {
  if(id<0 || settingsPresets.size()<= id) return false;
  loadSettingsPreset(settingsPresets.get(id));  
  return true;
}
void printPresetCode(Settings st) {
  println("addPreset(new Settings("+st.n,",",st.noiseM,",",st.randomDiviation,",",st.actionOnLeave+"));");
}
void printPresetCode(ColorManager cl) {
  println("addPreset(new ColorManager(",
  cl.use_rgb,",",
  cl.bounce,",",
  "new float[]{", cl.ogv1,",", cl.ogv2,",", cl.ogv3,",", cl.ogv4, "},",
  "new float[]{", cl.c1,",", cl.c2,",", cl.c3,",", cl.c4, "},",
  "new float[]{", cl.b1,",", cl.b2,",", cl.b3,"}",
  "));");
}






class Console {
  void printMessage(String message) { //create a popup with the message
     JOptionPane.showMessageDialog(null, message);  
  }
  void enterCommandWindow(String text) { //run the command the user said
    String userInput = JOptionPane.showInputDialog(null,text,"Enter a command");
    if(userInput == null) userInput = "";
    handleUserInput(userInput.toLowerCase());
  }
  String getUserInput(String text) { //get user input (from popup window)
    String userInput = JOptionPane.showInputDialog(null,text);
    if(userInput == null) userInput = "";
    return userInput.toLowerCase();
    
  }
  void handleUserInput(String userInput) { //decide what to do with the user's input
  
    /*
    From here on out, the code becomes pretty weird.
    If you don't understand it, just know that
    it is just a bunch of if-else statements that determine what to do
    
    Nothing amazing. And if it is hard to read... imagine what i had to go through to write it : P
    it took 2x as long to make it than it took to make everything else
    */
  
    userInput = userInput.toLowerCase();
    if(userInput.equals("help")) {
      enterCommandWindow(commandTutorialText);
      return;
    }
    if(userInput.equals("clear")) reset();
    if(userInput.equals("back")) return;
    if(userInput.equals("exit")) exit();
    
    if(userInput.equals("pause")) paused = !paused;
    if(userInput.equals("teleport")) {
      for(int i = 0; i < s.n; i++) p[i].setPosition(random(width),random(height));
    }
    
    if(userInput.equals("rush")) {
      int N = int(getUserInput("Enter the number of frames to be iterated"));
      for(int i = 0; i < N; i++) doStuff();
    }
    
    if(userInput.equals("list")) printMessage("There are currently " + colorPresets.size() + " color presets and " + settingsPresets.size() + " settings presets");
    if(userInput.equals("print")) {
      print("Settings & Color presets: \n"); 
      printPresetCode(s);
      println();
      printPresetCode(cm);
    }
    if(userInput.equals("load")) {
      String ui = getUserInput("Choose preset type: \n'color': load color preset\n'settings': load settings preset");
      if(ui.equals("color")) {
        int id = int(getUserInput("Which preset to load?\nAvailable: 0-"+(colorPresets.size()-1) ));
        loadColorPreset(id);
      }
      if(ui.equals("settings")) {
        int id = int(getUserInput("Which preset to load?\nAvailable: 0-"+(settingsPresets.size()-1) ));
        loadSettingPreset(id);
      }
      return;
    }
    if(userInput.equals("savep")) {
      String ui = getUserInput("Which active preset to save?\n'color'\n'settings'\n'both'");
      boolean saveSettings = ui.equals("settings");
      boolean saveColor = ui.equals("color");
      if(ui.equals("both")) {
        saveSettings = true;
        saveColor = true;
      }
      if(saveSettings) addPreset(s);
      if(saveColor) addPreset(cm);
    }
    if(userInput.equals("set")) {
       String ui = getUserInput("Properties of what to modify?\n'color'\n'settings'");
       boolean modifySettings = ui.equals("settings");
       boolean modifyColor = ui.equals("color");
       if(modifySettings) {
         ui = getUserInput("Which value to modify?\nAvaiable:\n'n': number of agents\n'm': noise scale\n'r': randomness\n'a': what action to do when agent leaves the screen");
         if(ui.equals("n")) {
            int newN = int(getUserInput("Enter the new number of agents"));           
            s.n = newN;
         }
         if(ui.equals("m")) {
            int newM = int(getUserInput("Enter the new noise scale (0.005 by default)"));           
            s.noiseM = newM;
         }
         if(ui.equals("r")) {
           int newR = int(getUserInput("Enter the new random diviation (0 by default)"));           
            s.randomDiviation = newR;
         }
         if(ui.equals("a")) {
           int newA = int(getUserInput("Enter the new action when an agent leaves the screen\nAvailable:\n"+
           "'0': they disappear\n'1': they are teleported randomly on the screen\n'2': they loop around\n'3': thier location is mirrorer"));           
           if(newA>=0 && newA<=3) s.actionOnLeave = newA;
           else printMessage("Invalid action type");
         }
         loadSettingsPreset(s);
         return;
       }
       //modify color
       if(modifyColor) {
         ui = getUserInput("Which color-manager property to change?"+
         "\n'mode':Color mode"+"\n'background': Background color\n'color': starting color\n'change': speed of change for color properties\n'bounce': what to do when reaching values 0/255");
         if(ui.equals("mode")) {
           ui = getUserInput("Enter new mode.\nAvailable:'rgb': rgba mode\n'hsb': hsba/hsva mode");
           if(ui.equals("rgb")) cm.use_rgb = true;
           else if(ui.equals("hsb")) cm.use_rgb = false;
           else printMessage("Invalid mode type");
         }         
         
         if(ui.equals("background")) {
           ui = getUserInput("Enter the "+((cm.use_rgb)? "RGB" : "HSB")+" values, separated by space.\nExample: 1.0 2.0 3.0");
           String splitUI[] = split(ui," ");
           if(splitUI.length != 3) {
             printMessage("incorrect amount of data");
             return;
           }
           cm.b1 = float(splitUI[0]);
           cm.b2 = float(splitUI[1]);
           cm.b3 = float(splitUI[2]);
         }
         if(ui.equals("color")) {
           ui = getUserInput("Enter the "+((cm.use_rgb)? "RGBA" : "HSBA")+" values, separated by space.\nExample: 1.0 2.0 3.0 4.0");
           String splitUI[] = split(ui," ");
           if(splitUI.length != 4) {
             printMessage("incorrect amount of data");
             return;
           }
           cm.v1 = float(splitUI[0]);
           cm.v2 = float(splitUI[1]);
           cm.v3 = float(splitUI[2]);
           cm.v4 = float(splitUI[3]);
           
           cm.ogv1 = cm.v1;
           cm.ogv2 = cm.v2;
           cm.ogv3 = cm.v3;
           cm.ogv4 = cm.v4;
         }
         if(ui.equals("change")) {
           ui = getUserInput("Enter the "+((cm.use_rgb)? "RGBA" : "HSBA")+" change values, separated by space.\nThis tells how much the colors will change each frame\nExample: 1.0 2.0 3.0 4.0");
           String splitUI[] = split(ui," ");
           if(splitUI.length != 4) {
             printMessage("incorrect amount of data");
             return;
           }
           cm.c1 = float(splitUI[0]);
           cm.c2 = float(splitUI[1]);
           cm.c3 = float(splitUI[2]);
           cm.c4 = float(splitUI[3]);
         }
         if(ui.equals("bounce")) {
           ui = getUserInput("What to do when color values hit 0 or 255?\nAvailable:\n'bounce': start going in the other direction\n'loop': go to the other extreme (0->255, 255->0)");
          if(ui.equals("loop")) cm.bounce = false;
          if(ui.equals("bounce")) cm.bounce = true;
         }
         loadColorPreset(cm);
         return;
       }      
       
    }    
  }
}
2 Likes