Recursive Sierpinski triangle (just a bit of fun)

Curious about Jeremy’s animated version I found no jar in the lib, but here is an abstract of the code.

/**
 * RecursorTriangle
 * 2020-03-20 Jeremy Douglass - Processing 3.4
 * Progressively animate a recursive Sierpinski Triangle.
 * Press space to restart.
 */

import java.util.Random;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;

void setup() {
  size(600, 550);
  smooth();
  noStroke();
  colorMode(HSB, 360, 100, 100);
  // seed
  rq.add(new CallST(0, 450, 400, 0, 500, 550, 8));
  background(0, 0, 40);
}

void draw() {
  rq.stepUntil(3, 0);
}

void keyReleased() {  
  if (key==' ') {  // reset
    rq.clear();
    frameCount=-1;
  }
}

RecursorQueue rq = new RecursorQueue<CallST>() {
  public ArrayList<CallST> recurse(CallST call) {
    return call.recurse();
  }
};

class CallST {
  float x1, y1, x2, y2, x3, y3;
  int n;
  
  CallST(float x1, float y1, float x2, 
    float y2, float x3, float y3, int n) {
    this.x1 = x1;
    this.y1 = y1;
    this.x2 = x2;
    this.y2 = y2;
    this.x3 = x3;
    this.y3 = y3;
    this.n = n;
  }
  
  ArrayList<CallST> recurse() {
    ArrayList<CallST> calls = new ArrayList<CallST>();
    if ( n > 0 ) {
      int h = (110+n*(360/8))%360;
      fill(h, 100, 100);
      triangle(x1, y1, x2, y2, x3, y3);
      // calculate segment midpoints
      float h1 = (x1+x2)/2.0;
      float w1 = (y1+y2)/2.0;
      float h2 = (x2+x3)/2.0;
      float w2 = (y2+y3)/2.0;
      float h3 = (x3+x1)/2.0;
      float w3 = (y3+y1)/2.0;
      // call three subtriangles at corners
      calls.add(new CallST(x1, y1, h1, w1, h3, w3, n-1));
      calls.add(new CallST(h1, w1, x2, y2, h2, w2, n-1));
      calls.add(new CallST(h3, w3, h2, w2, x3, y3, n-1));
    }
    return calls;
  }
}

public enum Mode {
  FIRST, LAST, EITHER;
  
  private static final Mode[] VALUES = values();
  private static final Random RANDOM = new Random();
  /**
   * @return Returns either FIRST or LAST, randomly.
   */
  public static Mode either() {
    return VALUES[RANDOM.nextInt(2)];
  }
  /**
   * @return Returns any one of FIRST, LAST, EITHER.
   */
  public static Mode random() {
    return VALUES[RANDOM.nextInt(VALUES.length)];
  }
}

public class RecursorQueue<E> extends LinkedList<E> {
  private static final long serialVersionUID = -3584631587915680024L;
  /** Running count of calls added to the queue since initialize or reset(). */
  private int adds;
  /** Running count of calls popped off the queue since initialize or reset(). */
  private int pops;
  private int addsLast;
  private int popsLast;
  /** Mode for add operations: FIRST, LAST, EITHER. */
  public Mode addMode;
  /** Mode for pop operations: FIRST, LAST, EITHER. */
  public Mode popMode;
  
   public RecursorQueue() {
    super();
    this.popMode = Mode.FIRST;
    this.addMode = Mode.LAST;
  }
  
  public RecursorQueue( Mode addMode, Mode popMode) {
    super();
    this.addMode = addMode;
    this.popMode = popMode;
  }
  
  public ArrayList<E> recurse(E call) {
    ArrayList<E> result = new ArrayList<E>();
    // result.add(call);
    return result;
  }

  public int getAdds() {
    return adds;
  }

  public int getPops() {
    return pops;
  }
  
  public void resetCounts() {
    pops = 0;
    adds = 0;
    popsLast = 0;
    addsLast = 0;
  }
 
  public void shuffle() {
    Collections.shuffle(this);
  }

  public E step() {
    return step(this.popMode, this.addMode);
  }
 
  public E step(Mode popMode, Mode addMode) {
    // pop from first or last end of deque
    if(popMode==Mode.EITHER) popMode = Mode.either();
    E call = (popMode==Mode.FIRST) ? this.removeFirst() : this.removeLast();
    pops++;
    ArrayList<E> calls = recurse(call);  
    
    // add to first or last end of deque
    if(addMode==Mode.EITHER) addMode = Mode.either();
    if(addMode==Mode.FIRST) {
      for (int i=0; i<calls.size(); i++) {
        this.addFirst(calls.get(i));
      }
    } else {
      for (int i=0; i<calls.size(); i++) {
        this.addLast(calls.get(i));
      }      
    }
    adds += calls.size();
    return call;
  }

  public int[] stepAll() {
    return stepUntil(0, 0, popMode, addMode);
  }
  
  public int[] stepUntil(int popMax, int addCheck) {
    return stepUntil(popMax, addCheck, popMode, addMode);
  }
  
  public int[] stepUntil(int popMax, int addCheck, Mode popMode, Mode addMode) {
    // offset current counts
    this.popsLast = this.pops;
    this.addsLast = this.adds;
    int popsStop = popMax + popsLast;
    int addsStop = addCheck + addsLast;
    //System.out.println(popMax +" "+ pops +" "+ addCheck +" "+ adds +" "+ this.isEmpty());
    while ((popMax==0 || pops<popsStop) && (addCheck==0 || adds<addsStop) && !this.isEmpty()) {
      step(popMode, addMode);
    }
    return new int[] {getPopsLast(), getAddsLast()};
  }
  
  public int getAddsLast() {
    return adds-addsLast;
  }
  
  public int getPopsLast() {
    return pops-popsLast;
  }

  public String status() {
    return "pop,add[ " + popMode + "," + addMode + " ]  cnt[ " + pops + "," + adds + " ]  chg[ " + getPopsLast() + "," + getAddsLast() + " ]  size[" + size() + "]";
  }

  public String toString() {
    return getClass().getName() + "" + status();
  }
}
1 Like