Full Code of v3ga/Processing for AI

master f756857dd47d cached
38 files
69.9 KB
22.8k tokens
1 requests
Download .txt
Repository: v3ga/Processing
Branch: master
Commit: f756857dd47d
Files: 38
Total size: 69.9 KB

Directory structure:
gitextract_73h8wnpj/

├── Animation/
│   └── OmbroCinema/
│       ├── OmbroCinema.pde
│       ├── Scanimation.pde
│       └── Utils.pde
├── Forms/
│   ├── CellularAutomaton/
│   │   ├── Automaton.pde
│   │   ├── CellularAutomaton.pde
│   │   └── Grid.pde
│   ├── Chladni/
│   │   └── Chladni.pde
│   ├── Circles_recursion/
│   │   ├── Circle.pde
│   │   └── Circles_recursion.pde
│   ├── HilbertCurve/
│   │   └── HilbertCurve.pde
│   ├── L_System/
│   │   ├── LInterpreter.pde
│   │   ├── LRule.pde
│   │   ├── LSystem.pde
│   │   └── L_System.pde
│   ├── LangtonAnt/
│   │   ├── Ant.pde
│   │   ├── Grid.pde
│   │   └── LangtonAnt.pde
│   ├── LangtonAntMultiple/
│   │   ├── Ant.pde
│   │   ├── Grid.pde
│   │   └── LangtonAntMultiple.pde
│   ├── Lorenz/
│   │   └── Lorenz.pde
│   ├── MazeGenerator/
│   │   ├── Maze.pde
│   │   └── MazeGenerator.pde
│   ├── Penrose/
│   │   ├── Penrose.pde
│   │   ├── Sun.pde
│   │   └── Triangle.pde
│   ├── RuttEtra/
│   │   ├── ImageSize.pde
│   │   ├── RuttEtra.pde
│   │   └── RuttEtraizer.pde
│   ├── Sunflower/
│   │   └── Sunflower.pde
│   ├── Torus/
│   │   ├── Torus.pde
│   │   └── TorusKnot.pde
│   └── Turmites/
│       ├── Rules.pde
│       ├── Turmite.pde
│       └── Turmites.pde
├── Image/
│   ├── CameraPropagation/
│   │   └── CameraPropagation.pde
│   └── CameraSlitScanVertical/
│       └── CameraSlitScanVertical.pde
└── README.md

================================================
FILE CONTENTS
================================================

================================================
FILE: Animation/OmbroCinema/OmbroCinema.pde
================================================
/*
  OmbroCinema

  —
  Developped and tested on : 
    - Processing 2.1.1 on MacOSX (10.10.1)

  —
  Julien @v3ga Gachadoat
  www.v3ga.net
  www.2roqs.com

  —
  Keyboard : 
    - 'i' draws the composition
    - 'c' draws the composition with mask
    - 'a' draws animation frames
    - 'e' exports composition (with timestamp) + mask to .pdf format

*/

// ------------------------------------------------------
import processing.pdf.*;

// ------------------------------------------------------
Scanimation scanimation;
int mode = 0;

// ------------------------------------------------------
void setup()
{
  size(600, 600);
  // Create the Scanimation instance, which will be made of 6 frames
  scanimation = new Scanimation(this, 6);
  // Compose the final frame (this is calling "drawScanimationFrame" for each frame)
  scanimation.composeFinalFrame();
  // Set the animation period in seconds (use 'a' on keyboard)
  scanimation.setTimerPeriod(0.5);
}

// ------------------------------------------------------
void draw()
{
  background(255);
  
  // Draws the composition
  if (mode == 0)
  {
    scanimation.draw();
  }
  // Draws the composition with mask
  else if (mode == 1)
  {
    scanimation.drawWithMask();
  }
  // Draws the animation
  else if (mode == 2)
  {
    scanimation.animate();
  }
}

// ------------------------------------------------------
// Automatically called by composeFinalFrame
void drawScanimationFrame(PGraphics pg, int frame, int nbFrames)
{
    pg.translate(pg.width/2, pg.height/2);
    pg.rotate( map(frame, 0, nbFrames, 0, radians(90)) );
    pg.noStroke();
    pg.rectMode(CENTER);
    pg.rect(0,0,400,100);
    pg.ellipse(30,60,100,100);
  }


// ------------------------------------------------------
void keyPressed()
{
  if (key == 'i')
  {
    mode = 0;
  }
  else if (key == 'c')
  {
    mode = 1;
  }
  else if (key == 'a')
  {
    mode = 2;
  }
  else if (key == 'e')
  {
    scanimation.exportPDF();
  }
}



================================================
FILE: Animation/OmbroCinema/Scanimation.pde
================================================
import java.lang.reflect.*;

// ------------------------------------------------------
class Scanimation
{
  int nbFrames;
  PApplet applet;
  Method methodFrame;

  ArrayList<PGraphics> listFrames;
  ArrayList<PGraphics> listMaskedFrames;
  PGraphics compositionFrame;
  PGraphics maskFrame, maskFrameScreen, currentFrame;

  Timer timer;
  float periodChangeFrame = 0.25f; // seconds
  float timeChangeFrame=0.0f;  
  int framePlaying = 0;

  boolean exportCompo = false;


  // ------------------------------------------------------
  Scanimation(PApplet applet, int nbFrames)
  {
    this.applet = applet;
    this.nbFrames = nbFrames;
    createFrames();
    findMethodFrame();
    createTimer();
  }

  // ------------------------------------------------------
  Scanimation(PApplet applet)
  {
    this.applet = applet;
    this.nbFrames = 6;
    createFrames();
    findMethodFrame();
    createTimer();
  }

  // ------------------------------------------------------
  void createFrames()
  {
    listFrames = new ArrayList<PGraphics>();
    listMaskedFrames = new ArrayList<PGraphics>(); 
    for (int i=0;i<nbFrames;i++) {
      listFrames.add( createGraphics(applet.width, applet.height) );
      listMaskedFrames.add( createGraphics(applet.width, applet.height) );
    }

    compositionFrame = createGraphics(applet.width, applet.height);
    maskFrame = createGraphics(applet.width, applet.height);
    maskFrameScreen = createGraphics(applet.width, applet.height);

    maskFrame.beginDraw();
    maskFrame.background(255);
    drawMaskStripes(maskFrame);
    maskFrame.endDraw();

    maskFrameScreen.beginDraw();
    drawMaskStripes(maskFrameScreen);
    maskFrameScreen.endDraw();
  }

  void drawMaskStripes(PGraphics maskFrame)
  {
    maskFrame.rectMode(CORNER);
    maskFrame.noStroke();
    maskFrame.fill(0);

    int x = 0;
    while (x<applet.width) {
      maskFrame.rect(x, 0, nbFrames-1, applet.height);
      x+=nbFrames;
    }
  }

  // ------------------------------------------------------
  void createTimer()
  {
    timer = new Timer();
  }

  // ------------------------------------------------------
  void resetTimer()
  {
    timeChangeFrame = 0.0f;
  }
  
  // ------------------------------------------------------
  void setTimerPeriod(float period)
  {
    periodChangeFrame = period;
  }

  // ------------------------------------------------------
  void animate()
  {
    float dt = timer.update();
    timeChangeFrame+=dt;
    if (timeChangeFrame>=periodChangeFrame)
    {
      framePlaying = (framePlaying+1)%getNumberFrames();
      resetTimer();
    }
    PGraphics frame = getFrame(framePlaying); 
    image(frame, 0, 0, width, height);
  }

  // ------------------------------------------------------
  int getNumberFrames()
  {
    return nbFrames;
  }

  // ------------------------------------------------------
  PGraphics getFrame(int i)
  {
    return listFrames.get(i);
  }

  // ------------------------------------------------------
  PGraphics getFrameMasked(int i)
  {
    return listMaskedFrames.get(i);
  }

  // ------------------------------------------------------
  PGraphics getCompositionFrame()
  {
    return compositionFrame;
  }

  // ------------------------------------------------------
  PGraphics getMaskFrame()
  {
    return maskFrame;
  }

  // ------------------------------------------------------
  PGraphics getMaskFrameScreen()
  {
    return maskFrameScreen;
  }

  // ------------------------------------------------------
  void findMethodFrame()
  {
    try 
    {
      this.methodFrame = applet.getClass().getMethod("drawScanimationFrame", new Class[] {
        PGraphics.class, Integer.TYPE, Integer.TYPE
      }
      );
      System.out.println("- \"findMethodFrame\" found.");
    } 
    catch (Exception e) 
    {
      System.out.println("- no \"findMethodFrame\" found.");
    }
  }

  // ------------------------------------------------------
  void composeFinalFrame()
  {
    if (methodFrame != null)
    {

      // Draw Each Frame
      PGraphics frame, frameMasked;

      for (int i=0;i<nbFrames;i++) {
        beginFrame(i);
        try {
          frame = listFrames.get(i);
          frameMasked = listMaskedFrames.get(i);

          frame.pushMatrix();
          frame.background(255);
          frame.noStroke();
          frame.fill(0);  
          methodFrame.invoke( applet, new Object[] { 
            frame, i, nbFrames
          } 
          );
          frame.popMatrix();

          PImage frameImg = frame.get();
          frameImg.mask(maskFrame);

          frameMasked.beginDraw();
          frameMasked.image(frameImg, 0, 0, frame.width, frame.height);
          frameMasked.endDraw();

          //frameMasked.get().mask(maskFrame);
        }
        catch (Exception e) {
        }
        endFrame();
      }

      // Compose
      compositionFrame.beginDraw();
      compositionFrame.background(255);
      for (int i=0;i<nbFrames;i++) {
        frameMasked = listMaskedFrames.get(i);
        compositionFrame.blend(frameMasked, 0, 0, frameMasked.width, frameMasked.height, i, 0, frameMasked.width, frameMasked.height, BLEND);
      }
      compositionFrame.endDraw();
    }
  }

  // ------------------------------------------------------
  void beginFrame(int i)
  {
    if (i>=nbFrames) return;
    currentFrame = listFrames.get(i);
    currentFrame.beginDraw();
  }

  // ------------------------------------------------------
  void endFrame()
  {
    if (currentFrame!=null)
      currentFrame.endDraw();
  }

  // ------------------------------------------------------
  void draw()
  {
    if (exportCompo)
    {
      exportCompo = false;

      String time = timestamp();
      beginRecord(PDF, "exports/"+time+"_export.pdf");
      strokeCap(SQUARE);
      stroke(0, 255);
      strokeWeight(2);

      compositionFrame.loadPixels();
      boolean isBeginLine = false;
      int r = 0, i=0, j=0, jbegin=0;

      for (i=0;i<compositionFrame.width;i++)
      {
        isBeginLine = false;
        for (j=0;j<compositionFrame.height;j++)
        {
          r = (int)red( compositionFrame.get(i, j) );
          if (isBeginLine)
          {
            if (r>=254) {
              isBeginLine = false;
              line(i, jbegin, i, j-1);
              println("colonne end "+i+";j="+j);
            }
          }
          else
          {
            if (r<=5) {
              isBeginLine = true;
              jbegin = j;
              println("colonne begin "+i+";j="+j);
            }
          }
        }

        if (isBeginLine)
        {
          line(i, jbegin, i, compositionFrame.height);
        }
      }
      endRecord();
    }
    image(compositionFrame, 0, 0, width, height);
  }

  // ------------------------------------------------------
  void drawWithMask()
  {
    image(getCompositionFrame(),0,0);
    blend(getMaskFrameScreen(), 0, 0, width, height, mouseX-width/2, 0, width, height, BLEND);
  }
  
  // ------------------------------------------------------
  void exportPDF()
  {
    exportCompo = true;
  }
};



================================================
FILE: Animation/OmbroCinema/Utils.pde
================================================
import java.util.Calendar;

// ------------------------------------------------------
class Timer
{
  float now, before;
  Timer()
  {
    now = before = millis();
  }

  float update()
  {
    now = millis();
    float dt = now-before;
    before = now;
    
    return dt/1000.0f;
  }
}

// ------------------------------------------------------
String timestamp() 
{
  Calendar now = Calendar.getInstance();
  return String.format("%1$ty%1$tm%1$td_%1$tH%1$tM%1$tS", now);
}


================================================
FILE: Forms/CellularAutomaton/Automaton.pde
================================================
class Automaton
{
  Grid grid;
  int line=0;
  boolean done = false;
  int frameStep = 2;  
  int rule = RULE_30;

  final static int RANDOM = 0;
  final static int CENTER = 1;
  
  final static int RULE_30   = 30;
  final static int RULE_110  = 110;

  Automaton(int rule, int initMode, int resx, int resy)
  {
    this.grid     = new Grid(resx, resy);
    this.rule     = rule;
    if (initMode == RANDOM)
    {
      for (int i=0; i<resx; i++)
        if (random(1.0)>0.5)
          this.grid.setState(i, 0, 1);
    }
    else if (initMode == CENTER)
    {
      this.grid.setState(resx/2, 0, 1);
    }
  }

  void setFrameStep(int n)
  {
    this.frameStep = max(1,n);
  }

  void setRule(int which)
  {
    this.rule = which;
  }

  boolean applyRule(int which, int s_1, int s0, int s1)
  {
    if (which == 30)
    {
      return 
        ((s_1==1 && s0==0 && s1==0) ||
        (s_1==0 && s0==1 && s1==1) ||
        (s_1==0 && s0==1 && s1==0) ||
        (s_1==0 && s0==0 && s1==1));
    } else if (which == 110)
    {
      return 
        ((s_1==1 && s0==1 && s1==0) ||
        (s_1==1 && s0==0 && s1==1) ||
        (s_1==0 && s0==1 && s1==1) ||
        (s_1==0 && s0==1 && s1==0) ||
        (s_1==0 && s0==0 && s1==1));
    }
    return false;
  }


  void run()
  {
    if (this.done || frameCount%frameStep!=0) return;

    int i, j = line;
    int s_1, s0, s1;
    for (i=1; i<this.grid.resx-1; i++)
    {
      s_1 = this.grid.getState(i-1, j);
      s0  = this.grid.getState(i+0, j);
      s1  = this.grid.getState(i+1, j);
      if (applyRule(this.rule, s_1, s0, s1)) 
      {
        this.grid.setState(i, j+1, 1);
      } else
      {
        this.grid.setState(i, j+1, 0);
      }
    }

    this.line++;
    if (this.line >= this.grid.resy-1)
      this.done = true;
  }

  void draw()
  {
    // this.grid.draw();
    this.grid.drawState();
  }
}


================================================
FILE: Forms/CellularAutomaton/CellularAutomaton.pde
================================================
/*
  CellularAutomaton
 —
An implementation of Rule 30 / 110 cellular automata described here:
https://en.wikipedia.org/wiki/Rule_30
https://en.wikipedia.org/wiki/Rule_110
—
 Developped and tested on : 
 - Processing 3.5.3 on Windows 10
 
 —
 Julien @v3ga Gachadoat
 www.v3ga.net
 www.2roqs.com
 */

Automaton automaton;

void setup()
{
  size(500,500); 
  surface.setTitle("Rule 30 / 110");

  automaton = new Automaton(Automaton.RULE_110,Automaton.RANDOM,200,200);
  automaton.setFrameStep(2); // every "n" frames
}

void draw()
{
  background(255);
  automaton.run();
  automaton.draw();
}


================================================
FILE: Forms/CellularAutomaton/Grid.pde
================================================
class Grid
{
  int resx, resy;
  int[] state;
  float cellw, cellh;
  
  Grid(int resx, int resy)
  {
    this.resx = resx;
    this.resy = resy;
    this.state = new int[resx*resy];
    this.cellw = float(width) / resx;
    this.cellh = float(height) / resy;
  }
  
  void setState(int i, int j, int state)
  {
    this.state[i+j*this.resx] = state;
  }
  
  int getState(int i, int j)
  {
    return this.state[i+j*this.resx];
  }

  void draw()
  {
    pushStyle();
    stroke(220);
    float x=0.0, y=0.0;
    for (int i=0; i<=this.resy; i++)
    {
       x = i*this.cellw;
       line(x,0,x,height);
    }
    for (int i=0; i<=this.resy; i++)
    {
       y = i*this.cellh;
       line(0,y,width,y);
    }
    popStyle();
  }

  void drawState()
  {

    pushStyle();
    noStroke();
    fill(0);
    int i, j;
    float x=0.0, y=0.0;
    int c = 0;
    for (j=0; j<this.resy; j++)
    {
      x = 0.0;
      for (i=0; i<this.resx; i++)
      {
        c = this.state[i+j*this.resx];
        if (c==1) 
          rect(x, y, cellw, cellh);
        x+=this.cellw;
      }
      y+=this.cellh;
    }
    popStyle();
  }
}


================================================
FILE: Forms/Chladni/Chladni.pde
================================================
/*
  Chladni plate interference surfaces
 
 —
 Based on :
 http://paulbourke.net/geometry/chladni/
 
 —
 Developped and tested on : 
 - Processing 2.1.1 on MacOSX (10.9.2)
  
 —
 Julien @v3ga Gachadoat
 www.v3ga.net
 www.2roqs.com
 
 */


// ------------------------------------------------------------------------------------------------
float m=10, n=2;
float L = 500;
float epsilon = 0.05;
boolean recompute = true;

// ------------------------------------------------------------------------------------------------
void settings()
{
  size((int)L, (int)L);
}

// ------------------------------------------------------------------------------------------------
void draw()
{
  if (recompute) {
    recompute = false;

    background(255);
    loadPixels();

    float chladni = 0.0f;
    int offset = 0;
    for (float y=0; y<height; y++) {
      for (float x=0; x<width; x++) {
        chladni = cos(n*PI*x/L)*cos(m*PI*y/L) - cos(m*PI*x/L)*cos(n*PI*y/L);
        if (abs(chladni)<=epsilon) {
          offset = (int)x+(int)y*(int)L;
          pixels[offset] = color(0, 0, 0);
        }
      }
    }    
    updatePixels();

    /*
    String infos = "m="+(int)m+";n="+(int)n+"\nepsilon="+nf(epsilon,1,4);
    fill(255,0,0);
    text(infos,4,16);
    */
  }
}

// ------------------------------------------------------------------------------------------------
void keyPressed()
{
  if (key == CODED)
  {
    if (keyCode == UP) { 
      m+=1; 
      recompute = true;
    }
    if (keyCode == DOWN) { 
      m-=1; 
      recompute = true;
    }
    if (keyCode == LEFT) { 
      n-=1; 
      recompute = true;
    }
    if (keyCode == RIGHT) { 
      n+=1; 
      recompute = true;
    }
  } else
  {
    if (key == '+') { 
      epsilon += 0.01; 
      recompute= true;
    }
    if (key == '-') { 
      epsilon -= 0.01; 
      recompute= true;
    }
  }

  m = constrain(m, 1, 20);
  n = constrain(n, 1, 20);
}


================================================
FILE: Forms/Circles_recursion/Circle.pde
================================================
class Circle
{
  float radius;
  float angle = 0.0;
  float angleSpeed = 0.0;
  int depth = 0;
  boolean bPause = false;

  Circle parent;
  Circle child;

  PVector pos = new PVector();

  Circle(Circle parent_, float radius_)
  {
    this.angleSpeed = random(angle_speed_min, angle_speed_max);
    this.parent = parent_;
    this.radius = radius_;
    if (this.parent != null)
    {
      this.angleSpeed = this.parent.angleSpeed + angle_speed_child;
      this.depth = ++this.parent.depth;
    }
    if (this.radius > radius_min)
      this.child = new Circle(this, this.radius * radius_size_factor);
  }

  void setRadius(float radius_)
  {
    this.radius = radius_;
    if (this.child != null)
      this.child.setRadius(radius_size_factor * this.radius);
  }

  void modifyAngleSpeed()
  {
    if (parent == null)
    {
      this.angleSpeed = random(angle_speed_min, angle_speed_max);
      if (this.child != null)
        this.child.modifyAngleSpeed();
    } else
    {
      this.angleSpeed = this.parent.angleSpeed + angle_speed_child;
    }
  }

  void setPos(float x, float y)
  {
    this.pos.set(x, y);
  }

  void setPause(boolean bPause_)
  {
    bPause = bPause_;
    if (this.child != null) 
      this.child.setPause(bPause);
  }

  void update()
  {
    if (this.parent != null)
    {
      this.pos.x = this.parent.pos.x + (this.parent.radius - this.radius)*cos( radians(angle) ); 
      this.pos.y = this.parent.pos.y + (this.parent.radius - this.radius)*sin( radians(angle) ); 

      if (bPause == false)
        this.angle += this.angleSpeed;
    }

    if (this.child != null)  
      this.child.update();
  }


  void draw()
  {
    noFill();
    if (bDrawFilled)
      fill( depth%2 == 0 ? color(255) : color(0) );
    else
      stroke(0, 200);

    ellipse(pos.x, pos.y, 2*radius, 2*radius);

    if (this.child != null)  
      this.child.draw();
  }
}

================================================
FILE: Forms/Circles_recursion/Circles_recursion.pde
================================================
/*
  Circles_recursion

  —
  Developped and tested on : 
    - Processing 3.2.1 on MacOSX (10.12.5)
    
  —
  Julien @v3ga Gachadoat
  www.v3ga.net
  www.2roqs.com

*/


// --------------------------------------------------
float radius_min = 5;
float radius_size_factor = 0.75;
float radius_factor = 1.0;
float angle_speed_min = 1, angle_speed_max = 3;
float angle_speed_child = 0.5;
boolean bDrawFilled = true;

Circle circle;


// --------------------------------------------------
void setup()
{
  size(500,500);
  circle = new Circle(null,0.5*0.95*min(width,height));
  circle.setPos(width/2, height/2);
}

// --------------------------------------------------
void draw()
{
  background(255);
  circle.update();
  circle.draw();
}

================================================
FILE: Forms/HilbertCurve/HilbertCurve.pde
================================================
/*
  Hilbert Curve
  —
  Based on explanations found here :
  https://www.youtube.com/watch?v=3s7h2MHQtxc
  —
  Developped and tested on : 
    - Processing 3.2.1 on MacOSX (10.12.5)
    
  —
  Julien @v3ga Gachadoat
  www.v3ga.net
  www.2roqs.com
*/

// ------------------------------------------------------------------------------------------------
import java.util.Collections;

// ------------------------------------------------------------------------------------------------
ArrayList<PVector> hilbertPoints;
int margin = 5;

// ------------------------------------------------------------------------------------------------
void setup()
{
  size(500, 500);
  hilbertPoints = getHilbertPoints(6, margin, margin, width-2*margin, height-2*margin);
}

// ------------------------------------------------------------------------------------------------
void draw()
{
  background(255);
  stroke(0);
  strokeWeight(2);
  int nbPoints = hilbertPoints.size();
  PVector A = hilbertPoints.get(0);
  PVector B = null;
  for (int i=0; i<nbPoints-1; i++)
  {
    B = hilbertPoints.get(i+1);
    line(A.x, A.y, B.x, B.y);

    A = B;
  }
}

// ------------------------------------------------------------------------------------------------
void keyPressed()
{
  if (key == 's') saveFrame("Hilbert.png");
}

// ------------------------------------------------------------------------------------------------
ArrayList<PVector> getHilbertPoints(int order, float x, float y, float w, float h)
{
  ArrayList<PVector> points = new ArrayList<PVector>();

  float w2 = w/2;
  float h2 = h/2;

  if (order == 1)
  {
    points.add(new PVector(x+w2/2, y+3*h2/2)); // SW
    points.add(new PVector(x+w2/2, y+h2/2)); // NW
    points.add(new PVector(x+3*w2/2, y+h2/2)); // NE
    points.add(new PVector(x+3*w2/2, y+3*h2/2)); // SE
  } else
  {
    ArrayList<PVector> pointsNW = getHilbertPoints(order-1, x,     y,     w2, h2);
    ArrayList<PVector> pointsNE = getHilbertPoints(order-1, x+w2,  y,     w2, h2);
    ArrayList<PVector> pointsSW = getHilbertPoints(order-1, x,     y+h2,  w2, h2);
    ArrayList<PVector> pointsSE = getHilbertPoints(order-1, x+w2,   y+h2, w2, h2);

    flipPoints(90,   pointsSW, x, y+h2, w2, h2);
    flipPoints(-90,   pointsSE, x+w2, y+h2, w2, h2);

    points.addAll(pointsSW);
    points.addAll(pointsNW);
    points.addAll(pointsNE);
    points.addAll(pointsSE);
  }

  return points;
}

// ------------------------------------------------------------------------------------------------
void flipPoints(int which, ArrayList<PVector> source, float x, float y, float w, float h)
{
  if (which == 90)
  {
    for (PVector p : source)
      p.set( x + w - (p.y-y), y + p.x-x );
  } else if (which == -90)
  {
    for (PVector p : source)
      p.set( x + (p.y-y), y + h-(p.x-x) );
  }

  Collections.reverse(source);
}


================================================
FILE: Forms/L_System/LInterpreter.pde
================================================
interface LInterpreter
{
  void run(String s);
}


================================================
FILE: Forms/L_System/LRule.pde
================================================
class LRule
{
  char variable;
  String replacement;
  
  LRule(char v, String r)
  {
    this.variable = v;
    this.replacement = r;
  }
  
  String apply()
  {
    return replacement;
  }
}


================================================
FILE: Forms/L_System/LSystem.pde
================================================
class LSystem
{
  String seed = "";  
  int generation = 0;
  ArrayList<String> listGenerations;
  ArrayList<LRule> listRules;
  String str="";
  LInterpreter interpreter;
  String infos="";
  
  LSystem(String seed)
  {
    init(seed);
  }

  LSystem(String seed,LInterpreter interpreter)
  {
    init(seed);
    setInterpreter(interpreter);
  }
  
  void init(String seed)
  {
    this.seed = this.str = seed;
    this.listGenerations = new ArrayList<String>(); 
    this.listRules = new ArrayList<LRule>();
  
    this.listGenerations.add(seed);
  }

  void setInterpreter(LInterpreter interpreter)
  {
    this.interpreter = interpreter;
  }
  void addRule(char c, String r)
  {
    addRule( new LRule(c,r) );
  }
  
  void addRule(LRule r)
  {
    this.listRules.add(r);
  }
  
  void grow(int generations)
  {
    for (int i=0;i<generations;i++) 
      grow();
  }

  void grow()
  {
    String newstr = applyRules();
    listGenerations.add(newstr);
    str = newstr;
    generation++;
  }
  
  String applyRules()
  {
    String newstr = "";
    for (int i=0;i<str.length();i++){
      char c = str.charAt(i);
      LRule rule = getRuleFor(c);
      if (rule != null){
        newstr+=rule.apply();
      }
      else{
        newstr+=c;
      }
    }
    return newstr;
  }
  
  LRule getRuleFor(char c)
  {
    for (LRule rule:listRules){
      if (rule.variable == c)
        return rule;
    }    
    return null;
  }
  
  int getLastGeneration()
  {
    if (generation>0)  
      return generation;
    return -1;
  }
  
  void draw()
  {
    draw(interpreter,getLastGeneration());
  }
  
  void draw(LInterpreter interpreter, int generation)
  {
    if (interpreter!=null && generation!=-1)
    {
      interpreter.run( listGenerations.get(generation) );
    }
  }
  
  void drawInfos()
  {
    if (infos.equals("")){
      infos += seed+"\n";
      for (LRule rule:listRules)
      {
        infos+=rule.variable+" to "+rule.replacement+"\n";
      }
    }
    fill(0);
    textSize(11);
    text(infos,4,12);
  }
  
  String toString()
  {
    String s = generation+" generations\n";
    s+="----";
    int i=0;
    for (String str:listGenerations)
    {
      s+= i+"-"+str+"["+str.length()+" chars]\n";
      i++;
    }
    return s;
  }

}


================================================
FILE: Forms/L_System/L_System.pde
================================================
/*
  L_system

  —
  Developped and tested on : 
    - Processing 2.1.1 on MacOSX (10.9.2)
    
  —
  Julien @v3ga Gachadoat
  www.v3ga.net
  www.2roqs.com

*/

// ------------------------------------------------------------------------------------------------
LSystem ls2;

// ------------------------------------------------------------------------------------------------
void setup()
{
  ls2 = new LSystem("X", new LInterpreterF());
  ls2.addRule('X', "F-[[X]+X]+FF[+FX]-X");
  ls2.addRule('F', "FF");

  ls2.grow(6);
  println(ls2);
  
  size(500,500);
  smooth();
}


// ------------------------------------------------------------------------------------------------
void draw()
{
  background(255);

  pushMatrix();
  translate(width/2, height);
  ls2.draw();
  popMatrix();
  ls2.drawInfos();
}

// ------------------------------------------------------------------------------------------------
void mousePressed()
{
  saveFrame("lsystem.png");
}


// ------------------------------------------------------------------------------------------------
class LInterpreterF implements LInterpreter
{
  float angle = PI/10;
  float length = 2.1;
  
  LInterpreterF()
  {
  }
  
  void run(String s)
  {
    length=map(mouseY,0,height,1,3);
    angle=map(mouseX,0,width,0,PI);

    stroke(0);
    noFill();
    int nbChars = s.length();
    char c;
    for (int i=0;i<nbChars;i++){
      c = s.charAt(i);
      switch(c)
      {
        case 'F':
          line(0,0,0,-length);
          translate(0,-length);
        break;

        case '+':
          rotate(angle);
        break;

        case '-':
          rotate(-angle);
        break;

        case '[':
          pushMatrix();
        break;

        case ']':
          popMatrix();
        break;
      }
    }
    
  }
}

================================================
FILE: Forms/LangtonAnt/Ant.pde
================================================
class Ant
{
  Grid grid;
  int i, j; // cell position on grid
  int turn=0; // 0 -> 0, 1->90, 2->180, 3->270
  Ant(Grid grid, int i, int j)
  {
    this.grid = grid;
    this.i = i%this.grid.resx;
    this.j = j%this.grid.resy;
    this.turn=0;
  }

  void run()
  {
    //if (frameCount % 2 == 0)
    {
      // check the state of the grid
      int c = this.grid.getState(this.i, this.j);
      // White square
      if (c == 0)
      {
        this.grid.setState(this.i, this.j, 1);
        this.turn90CW();
        this.move();
      }
      // Black square
      else if (c == 1)
      {
        this.grid.setState(this.i, this.j, 0);
        this.turn90CCW();
        this.move();
      }
    }
  }

  void turn90CW()
  {
    turn = (turn+1)%4;
  }

  void turn90CCW()
  {
    turn = turn-1;
    if (turn<0) turn = 3;
  }

  void move()
  {
    if (turn == 0)
      this.i +=1;
    else if (turn == 1)
      this.j +=1;
    else if (turn == 2)
      this.i -=1;
    else if (turn == 3)
      this.j -=1;

    if (this.i >= this.grid.resx) this.i=0;
    else if (this.i < 0) this.i=this.grid.resx-1;
    if (this.j >= this.grid.resy) this.j=0;
    else if (this.j < 0) this.j=this.grid.resy-1;
  }
}


================================================
FILE: Forms/LangtonAnt/Grid.pde
================================================
class Grid
{
  int resx, resy;
  int[] state;
  float cellw, cellh;
  
  Grid(int resx, int resy)
  {
    this.resx = resx;
    this.resy = resy;
    this.state = new int[resx*resy];
    this.cellw = float(width) / resx;
    this.cellh = float(height) / resy;
  }
  
  void setState(int i, int j, int state)
  {
    this.state[i+j*this.resx] = state;
  }
  
  int getState(int i, int j)
  {
    return this.state[i+j*this.resx];
  }

  void draw()
  {
    pushStyle();
    stroke(220);
    float x=0.0, y=0.0;
    for (int i=0; i<=this.resy; i++)
    {
       x = i*this.cellw;
       line(x,0,x,height);
    }
    for (int i=0; i<=this.resy; i++)
    {
       y = i*this.cellh;
       line(0,y,width,y);
    }
    popStyle();
  }

  void drawState()
  {

    pushStyle();
    noStroke();
    fill(0);
    int i, j;
    float x=0.0, y=0.0;
    int c = 0;
    for (j=0; j<this.resy; j++)
    {
      x = 0.0;
      for (i=0; i<this.resx; i++)
      {
        c = this.state[i+j*this.resx];
        if (c==1) 
          rect(x, y, cellw, cellh);
        x+=this.cellw;
      }
      y+=this.cellh;
    }
    popStyle();
  }
}


================================================
FILE: Forms/LangtonAnt/LangtonAnt.pde
================================================
/*
  LangtonAnt
 —
 An implementation of Langton's ant described here:
https://en.wikipedia.org/wiki/Langton%27s_ant
—
 Developped and tested on : 
 - Processing 3.5.3 on Windows 10
 
 —
 Julien @v3ga Gachadoat
 www.v3ga.net
 www.2roqs.com
 */

Grid grid; 
Ant ant;
void setup()
{
  size(500,500); 
  grid = new Grid(80,80);
  ant = new Ant(grid,40,40);
}

void draw()
{
  background(255);
  ant.run();
  grid.draw();
  grid.drawState();
}


================================================
FILE: Forms/LangtonAntMultiple/Ant.pde
================================================
class Ant
{
  Grid grid;
  int i, j; // cell position on grid
  int turn=0; // 0 -> 0, 1->90, 2->180, 3->270
  Ant(Grid grid, int i, int j)
  {
    this.grid = grid;
    this.i = i%this.grid.resx;
    this.j = j%this.grid.resy;
    this.turn=0;
  }

  void run()
  {
    //if (frameCount % 2 == 0)
    {
      // check the state of the grid
      int c = this.grid.getState(this.i, this.j);
      // White square
      if (c == 0)
      {
        this.grid.setState(this.i, this.j, 1);
        this.turn90CW();
        this.move();
      }
      // Black square
      else if (c == 1)
      {
        this.grid.setState(this.i, this.j, 0);
        this.turn90CCW();
        this.move();
      }
    }
  }

  void turn90CW()
  {
    turn = (turn+1)%4;
  }

  void turn90CCW()
  {
    turn = turn-1;
    if (turn<0) turn = 3;
  }

  void move()
  {
    if (turn == 0)
      this.i +=1;
    else if (turn == 1)
      this.j +=1;
    else if (turn == 2)
      this.i -=1;
    else if (turn == 3)
      this.j -=1;

    if (this.i >= this.grid.resx) this.i=0;
    else if (this.i < 0) this.i=this.grid.resx-1;
    if (this.j >= this.grid.resy) this.j=0;
    else if (this.j < 0) this.j=this.grid.resy-1;
  }
}


================================================
FILE: Forms/LangtonAntMultiple/Grid.pde
================================================
class Grid
{
  int resx, resy;
  int[] state;
  float cellw, cellh;
  
  Grid(int resx, int resy)
  {
    this.resx = resx;
    this.resy = resy;
    this.state = new int[resx*resy];
    this.cellw = float(width) / resx;
    this.cellh = float(height) / resy;
  }
  
  void setState(int i, int j, int state)
  {
    this.state[i+j*this.resx] = state;
  }
  
  int getState(int i, int j)
  {
    return this.state[i+j*this.resx];
  }

  void draw()
  {
    pushStyle();
    stroke(220);
    float x=0.0, y=0.0;
    for (int i=0; i<=this.resy; i++)
    {
       x = i*this.cellw;
       line(x,0,x,height);
    }
    for (int i=0; i<=this.resy; i++)
    {
       y = i*this.cellh;
       line(0,y,width,y);
    }
    popStyle();
  }

  void drawState()
  {

    pushStyle();
    noStroke();
    fill(0);
    int i, j;
    float x=0.0, y=0.0;
    int c = 0;
    for (j=0; j<this.resy; j++)
    {
      x = 0.0;
      for (i=0; i<this.resx; i++)
      {
        c = this.state[i+j*this.resx];
        if (c==1) 
          rect(x, y, cellw, cellh);
        x+=this.cellw;
      }
      y+=this.cellh;
    }
    popStyle();
  }
}


================================================
FILE: Forms/LangtonAntMultiple/LangtonAntMultiple.pde
================================================
/*
  LangtonAntMultiple
 —
 An implementation of Langton's ant described here:
https://en.wikipedia.org/wiki/Langton%27s_ant
—
 Developped and tested on : 
 - Processing 3.5.3 on Windows 10
 
 —
 Julien @v3ga Gachadoat
 www.v3ga.net
 www.2roqs.com
 */

Grid grid; 
ArrayList<Ant> ants = new ArrayList<Ant>();
boolean bDrawGrid = false;

void setup()
{
  size(500,500); 
  grid = new Grid(100,100);
  addAnt(50,50);
}

void draw()
{
  background(255);
  for (Ant ant : ants)
    ant.run();
  if (bDrawGrid)
    grid.draw();
  grid.drawState();
}

void mousePressed()
{
  addAnt(floor(mouseX/grid.cellw), floor(mouseY/grid.cellh));
}

void addAnt(int i, int j)
{
  ants.add( new Ant(grid,i,j) );
}


================================================
FILE: Forms/Lorenz/Lorenz.pde
================================================
/*
  Lorenz attractors

  —
  Developped and tested on : 
    - Processing 2.1.1 on MacOSX (10.9.4)
    
  —
  Julien @v3ga Gachadoat
  www.v3ga.net
  www.2roqs.com

*/

// ------------------------------------------------------------------------------------------------
import controlP5.*;

// ------------------------------------------------------------------------------------------------
PGraphics offscreen;

// ------------------------------------------------------------------------------------------------
float x=1.0, x1=0.0;
float y=1.0, y1=0.0;
float z=1.0, z1=0.0;

// ------------------------------------------------------------------------------------------------
float s = 5.0;
float p = 15.0;
float b = 1.0;
float dt = 0.005;
float scale = 6;

// ------------------------------------------------------------------------------------------------
ControlP5 controls;

// ------------------------------------------------------------------------------------------------
void setup()
{
  size(800, 400, P3D);

  controls = new ControlP5(this);
  controls.begin(5,5);
  controls.addSlider("s", 1.0,30.0).linebreak();
  controls.addSlider("p", 1.0,30.0).linebreak();
  controls.addSlider("b", 1.0,30.0).linebreak();
  controls.addSlider("scale", 1.0,10.0).linebreak();
  controls.addButton("reset").setSize(30,14).linebreak();
  controls.end();

  offscreen = createGraphics(width, height,P3D);
  reset();
}

// ------------------------------------------------------------------------------------------------
void draw()
{
  drawOffscreen();
  image(offscreen,0,0);
}

// ------------------------------------------------------------------------------------------------
void clearOffscreen()
{
  offscreen.beginDraw();
  offscreen.background(0);
  offscreen.endDraw();
}

// ------------------------------------------------------------------------------------------------
void drawOffscreen()
{
  x1 = x + dt*s*(y-x);
  y1 = y + dt*(p*x - y - x*z);
  z1 = z + dt*(x*y - b*z);

  offscreen.beginDraw();
  offscreen.translate(width/2, height/2);
  offscreen.stroke(255);
  offscreen.line(scale*x1, scale*y1, scale*z1, scale*x, scale*y, scale*z);
  offscreen.endDraw();

  x = x1;
  y = y1;
  z = z1;
}

// ------------------------------------------------------------------------------------------------
void reset()
{
  x=random(-5,5); x1=0.0;
  y=random(-5,5); y1=0.0;
  z=random(-5,5); z1=0.0;

  clearOffscreen();
}



================================================
FILE: Forms/MazeGenerator/Maze.pde
================================================
class Maze
{
  // Maze structure
  MazeCell[] cells;
  int resx, resy, nbCells;

  // Iteration variables
  ArrayList<MazeCell> stack;
  MazeCell cellCurrent;
  int nbCellsVisited=0;
  int cellStartx = 0, cellStarty = 0;
  boolean computed = false;

  // Step by step variables
  boolean stepByStep = false;
  float time = 0.0f;
  float timeStep = 0.0015f;

  // Solution
  ArrayList<MazeCell> solution;

  Maze(int resx, int resy)
  {
    this.resx = resx;
    this.resy = resy;
    this.nbCells = resx * resy;

    resetCells();
  }

  void setCellStart(int x, int y)
  {
    if (x>=resx) x=0;
    if (y>=resy) y=0;

    this.cellStartx = x;
    this.cellStarty = y;
  }

  void setTimeStepByStep(float t)
  {
    this.timeStep = t;
  }

  boolean isComputed()
  {
    return computed;
  }

  void resetCells()
  {
    this.cells = new MazeCell[nbCells];
    int k=0;
    for (int j=0; j<resy; j++)
      for (int i=0; i<resx; i++)
      {
        this.cells[k++] = new MazeCell(i, j, this);
      }
    nbCells = resx*resy;
    nbCellsVisited = 0;
  }

  MazeCell getCell(int x, int y)
  {
    if ((x>=0 && x<resx) && (y>=0 && y<resy)) 
      return this.cells[x+resx*y];
    return null;
  }

  void setCellVisited(MazeCell c)
  {
    if (!c.visited)
    {
      c.visited = true;
      nbCellsVisited++;
    }
  }

  boolean hasCellNeightborUnvisited(MazeCell c)
  {
    MazeCell north = getCell(c.x, c.y-1);
    if (north != null && !north.visited) return true;
    MazeCell east = getCell(c.x+1, c.y);
    if (east != null && !east.visited) return true;
    MazeCell south = getCell(c.x, c.y+1);
    if (south != null && !south.visited) return true;
    MazeCell west = getCell(c.x-1, c.y);
    if (west != null && !west.visited) return true;

    return false;
  }

  int[][] dir = new int[][]
    {
    {0, 1}, 
    {0, -1}, 
    {1, 0}, 
    {-1, 0}
  };
  MazeCell findCellNeightborUnvisited(MazeCell c)
  {
    MazeCell n=null;
    do
    {
      int rnd = (int) random(0, 3.99); // not sure about this

      n = getCell(c.x+dir[rnd][0], c.y+dir[rnd][1]);
    }
    while (n==null || n.visited);
    return n;
  }

  void removeCellsWalls(MazeCell cellCurrent, MazeCell cellNeighbor)
  {
    if (cellCurrent.x == cellNeighbor.x)
    {
      if (cellCurrent.y > cellNeighbor.y)
      {
        cellCurrent.hasNorth = false;
        cellNeighbor.hasSouth = false;
      } else
      {
        cellCurrent.hasSouth = false;
        cellNeighbor.hasNorth = false;
      }
    } else if (cellCurrent.y == cellNeighbor.y)
    {
      if (cellCurrent.x > cellNeighbor.x)
      {
        cellCurrent.hasWest = false;
        cellNeighbor.hasEast = false;
      } else
      {
        cellCurrent.hasEast = false;
        cellNeighbor.hasWest = false;
      }
    }
  }

  void reset()
  {
    resetCells();

    cellCurrent = getCell(cellStartx, cellStarty);
    setCellVisited(cellCurrent);

    stack = new ArrayList<MazeCell>();
  }

  void compute()
  {
    reset();
    stepByStep = false;
    computed = false;

    while (nbCellsVisited<nbCells)
    {
      step();
    }
    computed = true;
  }

  void beginComputeStepByStep()
  {
    time = millis();
    reset();
    stepByStep = true;
  }

  void computeStepByStep()
  {
    if (nbCellsVisited<nbCells)
    {
      float timeNow = millis();
      if (timeNow - time >= timeStep*1000)
      {
        step();
        time = timeNow;
      }
    } else
      computed = true;
  }


  void step()
  {
    if (hasCellNeightborUnvisited(cellCurrent))
    {
      stack.add( cellCurrent );

      MazeCell cellNeighbor = findCellNeightborUnvisited(cellCurrent);
      removeCellsWalls(cellCurrent, cellNeighbor);

      cellCurrent = cellNeighbor;
      setCellVisited(cellCurrent);
    } else
    {
      if (stack.size()>0)
      {
        cellCurrent = stack.remove( stack.size()-1 );
      }
    }
  }

  void draw()
  {
    for (int i=0; i<nbCells; i++)
      this.cells[i].draw();
  }

  void findSolution(int cellStartx, int cellStarty, int cellEndx, int cellEndy)
  {
    if (isComputed())
    {
      solution = new ArrayList<MazeCell>();
      MazeCell cellCurrent = getCell(cellStartx, cellStarty);
      
    }
  }
}

class MazeCell
{
  int x, y;
  float w, h;
  boolean hasNorth = true;
  boolean hasEast = true;
  boolean hasSouth = true;
  boolean hasWest = true;
  boolean visited = false;

  Maze parent;

  MazeCell(int x, int y, Maze parent)
  {
    this.x = x;
    this.y = y;
    this.parent = parent;
    this.w = (float)width/(float)parent.resx;
    this.h = (float)height/(float)parent.resy;
  }

  void draw()
  {
    float xx = (float)x;
    float yy = (float)y;

    float x1 = xx*w;
    float x2 = (xx+1)*w;
    float y1 = yy*h;
    float y2 = (yy+1)*h;
    if (x2>=width) x2=width-1;    
    if (y2>=height) y2=height-1;    

    if (visited && parent.stepByStep) {
      pushStyle();
      noStroke();
      fill(200);
      rectMode(CORNERS);
      rect(x1, y1, x2, y2);
      popStyle();
    }
    if (hasNorth) line(x1, y1, x2, y1);
    if (hasEast) line(x2, y1, x2, y2);
    if (hasSouth) line(x1, y2, x2, y2);
    if (hasWest) line(x1, y1, x1, y2);
  }
}

================================================
FILE: Forms/MazeGenerator/MazeGenerator.pde
================================================
/*
  MazeGenerator
 —
 An implementation of recursive backtracker algorithm described here:
 https://en.wikipedia.org/wiki/Maze_generation_algorithm
 —
 Developped and tested on : 
 - Processing 3.0b4 on MacOSX (10.10.5)
 
 —
 Julien @v3ga Gachadoat
 www.v3ga.net
 www.2roqs.com
 */

Maze maze;

void setup()
{
  size(500, 500);
  maze = new Maze(25, 25);
  maze.setCellStart(0, 0);
  //maze.compute();
  maze.beginComputeStepByStep();
}

void draw()
{
  background(255);
  stroke(0);
  strokeWeight(1);
  maze.computeStepByStep();
  maze.draw();
}

void mousePressed()
{
  maze.compute();
}

void keyPressed()
{
  if (key == 'e')
    saveFrame("maze.png");
}

================================================
FILE: Forms/Penrose/Penrose.pde
================================================
/*
  Penrose
  —
  Based on explanations found here :
  https://en.wikipedia.org/wiki/Penrose_tiling
  http://preshing.com/20110831/penrose-tiling-explained/
  —
  Developped and tested on : 
    - Processing 3.2.1 on MacOSX (10.12.5)
    
  —
  Julien @v3ga Gachadoat
  www.v3ga.net
  www.2roqs.com
*/

// ------------------------------------------------------------------------------------------------
Sun sun;

// ------------------------------------------------------------------------------------------------
void setup()
{
  size(500, 500,P2D);
  sun = new Sun(7, 0.48*height);
}

// ------------------------------------------------------------------------------------------------
void draw()
{
  translate(width/2, height/2);

  background(0);
  noStroke();
  sun.draw();
}

// ------------------------------------------------------------------------------------------------
void keyPressed()
{
  if (key == 's')
    saveFrame("Penrose.png");
}


================================================
FILE: Forms/Penrose/Sun.pde
================================================
class Sun
{
  int nbTriangles = 10;
  Triangle[] triangles = new Triangle[nbTriangles];


  Sun(int subdivide, float r)
  {
    for (int i = 0; i < nbTriangles; i++)
    {
      float start = (2*i-1) * PI/nbTriangles;
      float end = (2*i+1) * PI/nbTriangles;

      PVector A = new PVector(0, 0);
      PVector B = new PVector(r*cos(start), r*sin(start));
      PVector C = new PVector(r*cos(end), r*sin(end));

      triangles[i] = new Triangle(0, A, i%2 == 0 ? B : C, i%2 == 0 ? C : B);
      triangles[i].subdivide(subdivide);
    }
  }

  void draw()
  {
    for (int i = 0; i < nbTriangles; i++)
      triangles[i].draw();
  }
}


================================================
FILE: Forms/Penrose/Triangle.pde
================================================
class Triangle
{
  final static float phi = 1.61803398875;

  int type = 0;
  int level = 0;

  PVector A = new PVector();
  PVector B = new PVector();
  PVector C = new PVector();

  Triangle[] children;


  Triangle(int type, PVector A, PVector B, PVector C)
  {
    this.type = type;
    this.level = 0;
    this.A = A;
    this.B = B;
    this.C = C;
  }

  Triangle(int type, PVector A, PVector B, PVector C, int level)
  {
    this.type = type;
    this.level = level;
    this.A = A;
    this.B = B;
    this.C = C;
  }

  void draw()
  {
    if (children != null)
    {
      for (int i=0; i<children.length; i++)
        children[i].draw();
    } else
    {
      fill(type==0 ? 0 : 255);

      beginShape(TRIANGLES);
      vertex(A.x, A.y);
      vertex(B.x, B.y);
      vertex(C.x, C.y);
      endShape();
    }
  }

  void subdivide(int levelMax)
  {
    if (level == levelMax) return;

    if (type == 0)
    {
      PVector P = new PVector(A.x + (B.x-A.x)/phi, A.y + (B.y-A.y)/phi);

      children = new Triangle[2];
      children[0] = new Triangle(0, C, P, B, level+1);
      children[1] = new Triangle(1, P, C, A, level+1);
    } else if (type == 1)
    {
      PVector Q = new PVector(B.x + (A.x-B.x)/phi, B.y + (A.y-B.y)/phi);
      PVector R = new PVector(B.x + (C.x-B.x)/phi, B.y + (C.y-B.y)/phi);

      children = new Triangle[3];
      children[0] = new Triangle(1, R, C, A, level+1);
      children[1] = new Triangle(1, Q, R, B, level+1);
      children[2] = new Triangle(0, R, Q, A, level+1);
    }

    for (int i=0; i<children.length; i++)
      children[i].subdivide(levelMax);
  }
}


================================================
FILE: Forms/RuttEtra/ImageSize.pde
================================================
class ImageResize
{
  PImage src;
  PImage resized;
  
  ImageResize(PImage src, int divider)
  {
    this.resized = new PImage(src.width/divider, src.height/divider);
    this.src = src;
    println("ImageResize("+resized.width+","+resized.height+")");
  }
  
  void update()
  {
    resized.copy(src,0,0,src.width,src.height,0,0,resized.width,resized.height);
  }
  
  PImage image()
  {
    return resized;
  }
  
  int width()
  {
    return resized.width;
  }

  int height()
  {
    return resized.height;
  }
  
};



================================================
FILE: Forms/RuttEtra/RuttEtra.pde
================================================
/*
  RuttEtra
  —
  Developped and tested on : 
    - Processing 2.1.1 on MacOSX (10.10.1)
  —
  Julien @v3ga Gachadoat
  www.v3ga.net
  www.2roqs.com
  —
  Keyboard : 
   - '-' & '+' respectively decreases and increases blur level
   - '1' to '6' : change draw mode 
*/

// ------------------------------------------------------
import processing.video.*;

// ------------------------------------------------------
Capture video;
RuttEtraizer re;

// ------------------------------------------------------
void setup()
{
  size(600, 600, P3D);
  video = new Capture(this, 160, 120);
  video.start();
}

// ------------------------------------------------------
void draw()
{
  background(0);
  stroke(255);
  if (video.available())
  {
    video.read();

    if (re == null)
      re = new RuttEtraizer(video, 2);
    if (re!=null)
      re.apply();
  }

  if (re!=null)
  {
    pushMatrix();
    translate(width/2, height/2, 300);
    rotateX( map(mouseY,0,height,-PI,PI) );
    rotateY( map(mouseX,0,width,-PI,PI) );
    re.draw(this, 100);
    popMatrix();
    image(re.getImageResized(),0,0);
  }
}

// ------------------------------------------------------
void keyPressed()
{
  if (key == '-') re.decreaseBlur();
  if (key == '+') re.increaseBlur();
  if (key >= '1' && key <='6') re.setDrawMode(key-'0');
}



================================================
FILE: Forms/RuttEtra/RuttEtraizer.pde
================================================
class RuttEtraizer
{
  PGraphics imgSmall;
  PImage imgRef;
  int blurImgSmall = 1;
  int drawMode = 1;
  int s = 8;
  float filterTh = 0.5;

  // ------------------------------------------------------
  // RuttEtra
  // ------------------------------------------------------
  RuttEtraizer(PImage img, int s)
  {
    imgRef = img;
    setScale(s);
    resizeImageSmall();
  }

  // ------------------------------------------------------
  // setImgRef
  // ------------------------------------------------------
  void setImgRef(PImage img)
  {
    this.imgRef = img;
    resizeImageSmall();    
  }

  // ------------------------------------------------------
  // resizeImg
  // ------------------------------------------------------
  PImage getImageResized()
  {
    return imgSmall;
  }

  // ------------------------------------------------------
  // setDrawMode
  // ------------------------------------------------------
  void setDrawMode(int i)
  {
    drawMode = i;
  }

  // ------------------------------------------------------
  // setBlur
  // ------------------------------------------------------
  void setBlur(int b)
  {
    blurImgSmall = b;
    blurImgSmall=constrain(blurImgSmall, 0, 10);
  }

  // ------------------------------------------------------
  // increaseBlur
  // ------------------------------------------------------
  void increaseBlur()
  {
    setBlur(++blurImgSmall);
  }

  // ------------------------------------------------------
  // decreaseBlur
  // ------------------------------------------------------
  void decreaseBlur()
  {
    setBlur(--blurImgSmall);
  }

  // ------------------------------------------------------
  // setScale
  // ------------------------------------------------------
  void setScale(int s)
  {
    this.s = s;
    this.s = constrain(s, 2, 8);
    resizeImageSmall();
  }


  // ------------------------------------------------------
  // apply
  // ------------------------------------------------------
  void apply()
  {
    imgSmall.beginDraw();
    imgRef.filter(THRESHOLD, filterTh);
    imgRef.filter(BLUR, blurImgSmall);
    imgSmall.image(imgRef, 0, 0, imgSmall.width, imgSmall.height);
    imgSmall.endDraw();
  }

  // ------------------------------------------------------
  // resizeImageSmall
  // ------------------------------------------------------
  void resizeImageSmall()
  {
    imgSmall  = createGraphics(imgRef.width/s, imgRef.height/s);
    apply();
  }

  // ------------------------------------------------------
  // draw
  // ------------------------------------------------------
  void draw(PApplet applet, float h)
  {
    draw(applet.g, h);
  }

  // ------------------------------------------------------
  // draw
  // ------------------------------------------------------
  void draw(PGraphics g, float h)
  {
    if (drawMode == 1)
      drawLines(g,imgSmall, h);
    else if (drawMode == 2)
      drawPoints(g,imgSmall);
    else if (drawMode == 3)
      drawTriangles(g,imgSmall, h);
    else if (drawMode == 4)
      drawLines2(g,imgSmall, 280);
    else if (drawMode == 5)
    {
      drawLines(g,imgSmall, h);
      drawLines2(g,imgSmall, h);
    }
    else if (drawMode == 6)
    {
      drawMesh(g,imgSmall, h);
      g.stroke(255,255);
      g.pushMatrix();
      g.translate(0,0,2);
      drawLines(g, imgSmall, h);
      g.popMatrix();
    }
  }


  // ------------------------------------------------------
  // drawPoints
  // ------------------------------------------------------
  void drawPoints(PGraphics g,PImage img)
  {
    int i, j, offset;
    int w = img.width;
    int h = img.height;
    int gg = 0;

    g.pushMatrix();
    g.translate(-s*img.width/2, -s*img.height/2);
    img.loadPixels();
    for (j=0;j<h;j++)
    {
      for (i=0;i<w;i++)
      {
        offset = i+j*w;
        gg = img.pixels[offset] & 0x000000FF;
        g.point(i*s, j*s, float(gg)/255.0*100);
      }
    }
    g.popMatrix();
  }

  // ------------------------------------------------------
  // drawLines
  // ------------------------------------------------------
  void drawLines(PGraphics g,PImage img, float hh)
  {
    int i, j, offset1, offset2;
    int w = img.width;
    int h = img.height;
    int p1, p2;
    int r1, g1, b1;
    int r2, g2, b2;
    float i1, i2;

    g.pushMatrix();
    g.translate(-s*img.width/2, -s*img.height/2);
    img.loadPixels();
    for (j=0;j<h;j++)
    {
      for (i=0;i<w-1;i++)
      {
        offset1 = i+j*w;
        offset2 = i+1+j*w;
        p1 = img.pixels[offset1];
        p2 = img.pixels[offset2];

        r1 = p1 & 0x000000FF;
        r2 = p2 & 0x000000FF;

        i1 = float(r1)/(255.0);
        i2 = float(r2)/(255.0);

//        g.stroke( float(j)/float(h)*255 );
        g.line(i*s, j*s, i1*hh, (i+1)*s, j*s, i2*hh);
      }
    }
    g.popMatrix();
  }

  // ------------------------------------------------------
  // drawLines2
  // ------------------------------------------------------
  void drawLines2(PGraphics g, PImage img, float hh)
  {
    int i, j, offset1, offset2;
    int w = img.width;
    int h = img.height;
    int p1, p2;
    int r1, g1, b1;
    int r2, g2, b2;
    float i1, i2;

    g.pushMatrix();
    g.translate(-s*img.width/2, -s*img.height/2);
    img.loadPixels();
    for (j=0;j<h-1;j++)
    {
      for (i=0;i<w;i++)
      {
        offset1 = i+j*w;
        offset2 = i+(j+1)*w;
        p1 = img.pixels[offset1];
        p2 = img.pixels[offset2];

        r1 = p1 & 0x000000FF;
        r2 = p2 & 0x000000FF;

        i1 = float(r1)/(255.0);
        i2 = float(r2)/(255.0);

        g.line(i*s, j*s, i1*hh, i*s, (j+1)*s, i2*hh);
      }
    }
    g.popMatrix();
  }


  // ------------------------------------------------------
  // drawTriangles
  // ------------------------------------------------------
  void drawTriangles(PGraphics g, PImage img, float hh)
  {
    int i, j, offset1, offset2;
    int w = img.width;
    int h = img.height;
    int g1 = 0;
    int g2 = 0;
    float m =1.0;

    g.pushMatrix();
    g.translate(-s*img.width/2, -s*img.height/2);
    img.loadPixels();
    fill(0);
    for (j=0;j<h;j++)
    {
      m = sin(float(j)/float(h)*PI);
      g.beginShape();
      for (i=0;i<w;i++)
      {
        offset1 = i+j*w;
        g1 = img.pixels[offset1] & 0x000000FF;
        g.vertex(i*s, j*s, m*float(g1)/255.0*hh);
      }
      g.endShape(CLOSE);
    }
    g.popMatrix();
  }

  // ------------------------------------------------------
  // drawMesh
  // ------------------------------------------------------
  void drawMesh(PGraphics g, PImage img, float hh)
  {
    int i, j, offset1, offset2, offset3, offset4;
    int w = img.width;
    int h = img.height;
    int g1 = 0;
    int g2 = 0;
    int g3 = 0;
    int g4 = 0;
    float m =1.0;

    g.pushMatrix();
    g.translate(-s*img.width/2, -s*img.height/2);
    img.loadPixels();
    g.fill(0);
//    stroke(255);
    g.noStroke();
    for (j=0;j<h-1;j++)
    {
      m = 1;//sin(float(j)/float(h)*PI);
      g.beginShape(QUAD_STRIP);
      for (i=0;i<w;i++)
      {
        offset1 = i+j*w;
        offset2 = i+(j+1)*w;
        g1 = img.pixels[offset1] & 0x000000FF;
        g2 = img.pixels[offset2] & 0x000000FF;
        g.vertex(i*s, j*s, m*float(g1)/255.0*hh);
        g.vertex(i*s, (j+1)*s, m*float(g2)/255.0*hh);
      }
      g.endShape();
    }
    g.popMatrix();
  }
  
  // ------------------------------------------------------
  // toMesh
  // ------------------------------------------------------
/*
  TriangleMesh toMesh(float ground, float hMax)
  {
    Terrain terrain = new Terrain(imgSmall.width,imgSmall.height,1);
    float[] elevation = new float[imgSmall.width*imgSmall.height];
    imgSmall.loadPixels();
    int offset=0;
    for (int i=0;i<imgSmall.width;i++)
      for (int j=0;j<imgSmall.height;j++)
    {
      offset = i+j*imgSmall.width;
      elevation[offset] = float(imgSmall.pixels[offset] & 0x000000FF)/255.0f*hMax;
    }
    terrain.setElevation(elevation);
    return (TriangleMesh)terrain.toMesh(ground);
  }

  // ------------------------------------------------------
  // toMeshSTL
  // ------------------------------------------------------
  void toMeshSTL(String filename,float ground, float hMax)
  {
    toMesh(ground,hMax).saveAsSTL(sketchPath(timestamp()+"_"+filename+".stl")); 
  }
*/

}





================================================
FILE: Forms/Sunflower/Sunflower.pde
================================================
/*
  Sunflower
 
  —
  Not sure where this code comes from.
  
  —
  Julien @v3ga Gachadoat
  www.v3ga.net
  www.2roqs.com
 
*/


float r, x1, y1, golden=radians(180*(3-sqrt(5)));

size(500, 500);
background(255);
smooth();
noStroke();
fill(0);
for (int n=1; n<=520; n++) 
{
  r = 5*sqrt(n);
  x1 = width/2+2*r*cos(golden*n);
  y1 = height/2+2*r*sin(golden*n);
  ellipse(x1, y1, 10, 10);
}

// saveFrame("sunflower.jpg");



================================================
FILE: Forms/Torus/Torus.pde
================================================
/*
 
 Torus Knot
  
 —
 Julien @v3ga Gachadoat
 www.v3ga.net
 www.2roqs.com
 
 */

// ----------------------------------------------------------------
import toxi.geom.*;
import toxi.geom.mesh.*;
import toxi.processing.*;

// ----------------------------------------------------------------
ToxiclibsSupport gfx;
TorusKnot knot;

// ----------------------------------------------------------------
void setup()
{
  size(800, 800,P3D);
  gfx=new ToxiclibsSupport(this);
  knot = new TorusKnot(140, 70, 4,5, 700, 50, 3); // global radius, inner radius, P,Q, res, res2 (around), render mode
}

// ----------------------------------------------------------------
void draw()
{
  background(0);

  translate(width/2, height/2);
  rotateX( map(mouseY, 0, height, -PI, PI) );
  rotateY( map(mouseX, 0, width, -PI, PI) );

  fill(0);
  noStroke();
  gfx.mesh(knot);
  noFill();
  strokeWeight(2);
  stroke(255);
  scale(1.003);
  knot.renderLines();
}

// ----------------------------------------------------------------
void keyPressed()
{
  if (key == 's')
  {
    saveFrame("Knot.png"); 
  }
}


================================================
FILE: Forms/Torus/TorusKnot.pde
================================================
// --------------------------------------------------------
// TorusKnot code is dirty ... sorry.
class TorusKnot extends TriangleMesh
{
  public float R, P, Q;

//  TriangleMesh mesh;

  Vec3D points[][];
  Vec3D normals[][];
  ArrayList<TorusKnotLine> lines;

  int res = 500;
  int res2 = 60;
  float r2 = 100.0;

  int renderMode = 0;

  // Constructor
  TorusKnot(float R_, float R2_, float P_, float Q_, int res_, int res2_, int renderMode_)
  {
    R = R_;
    r2 = R2_;
    P = P_;
    Q = Q_;
    res = res_;
    res2 = res2_;
    renderMode = renderMode_;

//    mesh = new TriangleMesh();
    lines = new ArrayList<TorusKnotLine>();


    PVector A, B;
    float[] b;
    Matrix4x4 transfo = new Matrix4x4();
    Matrix4x4 basisM = new Matrix4x4();
    float th  = 0.0;
    float dth = 360.0/(res) ;

    points = new Vec3D[res][res2];
    normals = new Vec3D[res][res2];

    for (int i=0; i<res; i++)
    {
      A = getPointAt(th);
      b = getTangentBasis(th);

      transfo.identity();
      transfo.translateSelf(A.x, A.y, A.z);

      basisM.set(
        b[0], b[3], b[6], 0.0, 
        b[1], b[4], b[7], 0.0, 
        b[2], b[5], b[8], 0.0, 
        0.0, 0.0, 0.0, 1.0);

      transfo.multiplySelf(basisM);


      points[i] = new Vec3D[res2];      
      normals[i] = new Vec3D[res2];

      Vec3D A2 = new Vec3D(A.x, A.y, A.z);

      float a = 0.0f;
      for (int j=0; j<res2; j++)
      {
        points[i][j] = transfo.applyTo( new Vec3D( r2*cos(a), r2*sin(a), 0.0 ) );
        normals[i][j] = points[i][j].sub(A2).normalize();

        a+=TWO_PI/(res2);
      }
      th+=dth;
    }

    Vec3D n1 = new Vec3D(), n2 = new Vec3D();
    Vec3D aa, bb, cc, dd;
    for (int i=0; i<points.length; i++) {

      for (int j=0; j<points[i].length; j++) {

        n1 = ( points[i][(j+1)%res2].sub(points[i][j]) ).cross( points[(i+1)%res][j].sub(points[i][j]) );
        n2 = ( points[(i+1)%res][(j+1)%res2].sub(points[i][(j+1)%res2]) ).cross( points[(i+1)%res][j].sub(points[(i+1)%res][(j+1)%res2]) );

        n1 = n1.normalize();
        n2 = n2.normalize();

        aa = points[i][j];
        bb = points[i][(j+1)%res2];
        cc = points[(i+1)%res][(j+1)%res2];
        dd = points[(i+1)%res][j];

        addFace(aa, bb, dd, n1);
        addFace(bb, cc, dd, n2);


        if (renderMode == 0)
        {
          //          lines.add( new TorusKnotLine(aa,bb) );
          lines.add( new TorusKnotLine(aa, cc) );
        } else
          if (renderMode == 1)
          {
            float rnd = random(1);
            if (rnd<0.25) lines.add( new TorusKnotLine(aa, cc) );
            else if (rnd < 0.5) lines.add( new TorusKnotLine(bb, dd) );
          } else
            if (renderMode == 2)
            {
              float rnd = random(1);
              if (rnd<0.25)           lines.add( new TorusKnotLine(aa, bb) );
              else if (rnd < 0.5)     lines.add( new TorusKnotLine(aa, dd) );
              else if (rnd < 0.75)    lines.add( new TorusKnotLine(dd, cc) );
              else                    lines.add( new TorusKnotLine(cc, bb) );
            }
        if (renderMode == 3)
        {
          lines.add( new TorusKnotLine(aa, bb) );
        }
      }
    }

    computeVertexNormals();
  }


  // Methods
  void renderLines()
  {
    for (TorusKnotLine l : lines)
      line(l.A.x, l.A.y, l.A.z, l.B.x, l.B.y, l.B.z);
  }

  PVector getPointAt(float th)
  {
    PVector p = new PVector();
    float   rA	= 0.5*(2.0+sin(radians(Q*th)));
    p.x	= R*rA*cos(radians(P*th));
    p.y	= R*rA*cos(radians(Q*th));
    p.z	= R*rA*sin(radians(P*th));

    return p;
  }

  PVector getTangentAt(float th)
  {
    float dth = 360.0 / 10000.0f;

    PVector A = this.getPointAt(th);
    PVector B = this.getPointAt(th+dth);
    PVector T = new PVector(B.x - A.x, B.y - A.y, B.z - A.z);

    T.normalize();

    return T;
  }

  float[] getTangentBasis(float th)
  {
    float[] b = new float[9];
    float dth = 360.0/10000.0;

    PVector T  = new PVector();
    PVector N  = new PVector();
    PVector B_ = new PVector();

    PVector A = getPointAt(th);
    PVector B = getPointAt(th+dth);

    T.set(B.x - A.x, B.y - A.y, B.z - A.z);
    N.set(B.x + A.x, B.y + A.y, B.z + A.z);

    B_ = T.cross(N);
    N  = B_.cross(T);

    N.normalize();
    B_.normalize();
    T.normalize();

    b[0] = N.x ; 
    b[1] = N.y ; 
    b[2] = N.z ; 

    b[3] = B_.x ; 
    b[4] = B_.y ; 
    b[5] = B_.z ; 

    b[6] = T.x ;
    b[7] = T.y ;
    b[8] = T.z ;

    return b;
  }
};

class TorusKnotLine
{
  Vec3D A, B;

  TorusKnotLine(Vec3D A_, Vec3D B_)
  {
    A = A_;
    B = B_;
  }
}


================================================
FILE: Forms/Turmites/Rules.pde
================================================
int[][] TurmiteTurn = 
{
  // 0 (//)
  {-1,-1,-1,-1},
  // 1 (No turn)
  { 0, 1, 2, 3},
  // 2  (Right)
  { 1, 2, 3, 0},
  // 3  (//)
  {-1,-1,-1,-1},
  // 4  (U-turn)
  { 2, 3, 0, 1},
  // 5  (//)
  {-1,-1,-1,-1},
  // 6  (//)
  {-1,-1,-1,-1},
  // 7  (//)
  {-1,-1,-1,-1},
  // 8  (Left)
  { 3, 0, 1, 2}
};

// I know.
int[][][][] r  = 
{
{{{1, 2, 0}, {0, 8, 0}}},  // 1: Langton's ant
{{{1, 2, 0}, {0, 1, 0}}},  // 2: binary counter
{{{0, 8, 1}, {1, 2, 1}}, {{1, 1, 0}, {1, 1, 1}}}, // 3: (filled triangle)
{{{0, 1, 1}, {0, 8, 1}}, {{1, 2, 0}, {0, 1, 1}}}, // 4: spiral in a box
{{{0, 2, 1}, {0, 8, 0}}, {{1, 8, 1}, {0, 2, 0}}}, // 5: stripe-filled spiral
{{{0, 2, 1}, {0, 8, 0}}, {{1, 8, 1}, {1, 1, 0}}}, // 6: stepped pyramid
{{{0, 2, 1}, {0, 1, 1}}, {{1, 2, 1}, {1, 8, 0}}}, // 7: contoured island
{{{0, 2, 1}, {0, 2, 1}}, {{1, 1, 0}, {0, 2, 1}}}, // 8: woven placemat
{{{0, 2, 1}, {1, 2, 1}}, {{1, 8, 1}, {1, 8, 0}}}, // 9: snowflake-ish
{{{1, 8, 0}, {0, 1, 1}}, {{0, 8, 0}, {0, 8, 1}}}, // 10: slow city builder
{{{1, 8, 0}, {1, 2, 1}}, {{0, 2, 0}, {0, 8, 1}}}, // 11: framed computer art
{{{1, 8, 0}, {1, 2, 1}}, {{0, 2, 1}, {1, 8, 0}}}, // 12: balloon bursting (makes a spreading highway) 
{{{1, 8, 1}, {0, 8, 0}}, {{1, 1, 0}, {0, 1, 0}}}, // 13: makes a horizontal highway
{{{1, 8, 1}, {0, 8, 0}}, {{1, 2, 1}, {1, 2, 0}}}, // 14: makes a 45 degree highway
{{{1, 8, 1}, {0, 8, 1}}, {{1, 2, 1}, {0, 8, 0}}}, // 15: makes a 45 degree highway
{{{1, 8, 1}, {0, 1, 0}}, {{1, 1, 0}, {1, 2, 0}}}, // 16: spiral in a filled box
{{{1, 8, 1}, {0, 2, 0}}, {{0, 8, 0}, {0, 8, 0}}}, // 17: glaciers
{{{1, 8, 1}, {1, 8, 1}}, {{1, 2, 1}, {0, 1, 0}}}, // 18: golden rectangle!
{{{1, 8, 1}, {1, 2, 0}}, {{0, 8, 0}, {0, 8, 0}}}, // 19: fizzy spill
{{{1, 8, 1}, {1, 2, 1}}, {{1, 1, 0}, {0, 1, 1}}}, // 20: nested cabinets
{{{1, 1, 1}, {0, 8, 1}}, {{1, 2, 0}, {1, 1, 1}}}, // 21: (cross)
{{{1, 1, 1}, {0, 1, 0}}, {{0, 2, 0}, {1, 8, 0}}}, // 22: saw-tipped growth
{{{1, 1, 1}, {0, 1, 1}}, {{1, 2, 1}, {0, 1, 0}}}, // 23: curves in blocks growth
{{{1, 1, 1}, {0, 2, 0}}, {{0, 8, 0}, {0, 8, 0}}}, // 24: textured growth
{{{1, 1, 1}, {0, 2, 1}}, {{1, 8, 0}, {1, 2, 0}}}, // 25: (diamond growth)
{{{1, 1, 1}, {1, 8, 0}}, {{1, 2, 1}, {0, 1, 0}}}, // 26: coiled rope
{{{1, 2, 0}, {0, 8, 1}}, {{1, 8, 0}, {0, 1, 1}}}, // 27: (growth)
{{{1, 2, 0}, {0, 8, 1}}, {{1, 8, 0}, {0, 2, 1}}}, // 28: (square spiral)
{{{1, 2, 0}, {1, 2, 1}}, {{0, 1, 0}, {0, 1, 1}}}, // 29: loopy growth with holes
{{{1, 2, 1}, {0, 8, 1}}, {{1, 1, 0}, {0, 1, 0}}}, // 30: Lanton's Ant drawn with squares
{{{1, 2, 1}, {0, 2, 0}}, {{0, 8, 1}, {1, 8, 0}}}, // 31: growth with curves and blocks
{{{1, 2, 1}, {0, 2, 0}}, {{0, 1, 0}, {1, 2, 1}}}, // 32: distracted spiral builder
{{{1, 2, 1}, {0, 2, 1}}, {{1, 1, 0}, {1, 1, 1}}}, // 33: cauliflower stalk (45 deg highway)
{{{1, 2, 1}, {1, 8, 1}}, {{1, 2, 1}, {0, 2, 0}}}, // 34: worm trails (eventually turns cyclic!)
{{{1, 2, 1}, {1, 1, 0}}, {{1, 1, 0}, {0, 1, 1}}}, // 35: eventually makes a two-way highway!
{{{1, 2, 1}, {1, 2, 0}}, {{0, 1, 0}, {0, 1, 0}}}, // 36: almost symmetric mould bloom
{{{1, 2, 1}, {1, 2, 0}}, {{0, 2, 0}, {1, 1, 1}}}, // 37: makes a 1 in 2 gradient highway
{{{1, 2, 1}, {1, 2, 1}}, {{1, 8, 1}, {0, 2, 0}}}, // 38: immediately makes a 1 in 3 highway
{{{0, 2, 1}, {1, 2, 1}}, {{0, 8, 2}, {0, 8, 0}}, {{1, 2, 2}, {1, 8, 0}}}, // 39: squares and diagonals growth
{{{1, 8, 1}, {0, 1, 0}}, {{0, 2, 2}, {1, 8, 0}}, {{1, 2, 1}, {1, 1, 0}}}, // 40: streak at approx. an 8.1 in 1 gradient
{{{1, 8, 1}, {0, 1, 2}}, {{0, 2, 2}, {1, 1, 1}}, {{1, 2, 1}, {1, 1, 0}}}, // 41: streak at approx. a 1.14 in 1 gradient
{{{1, 8, 1}, {1, 8, 1}}, {{1, 1, 0}, {0, 1, 2}}, {{0, 8, 1}, {1, 1, 1}}}, // 42: maze-like growth
{{{1, 8, 2}, {0, 2, 0}}, {{1, 8, 0}, {0, 2, 0}}, {{0, 8, 0}, {0, 8, 1}}}, // 43: growth by cornices 
{{{1, 2, 0}, {0, 2, 2}}, {{0, 8, 0}, {0, 2, 0}}, {{0, 1, 1}, {1, 8, 0}}}, // 44: makes a 1 in 7 highway
{{{1, 2, 1}, {0, 8, 0}}, {{1, 2, 2}, {0, 1, 0}}, {{1, 8, 0}, {0, 8, 0}}} // 45: makes a 4 in 1 highway
};


================================================
FILE: Forms/Turmites/Turmite.pde
================================================
class Turmite
{
  PImage grid;
  int offset;
  int x, y;
  int state = 0;
  int direction = 0;
  int ruleIndex=0;
  int iteration=0;

  Turmite(PImage grid, int ruleIndex)
  {
    this.grid = grid;
    setRule(ruleIndex);
    reset();
  }
  
  void reset()
  {
    grid.loadPixels();
    for (int i=0;i<grid.pixels.length;i++) grid.pixels[i] = color(255);
    grid.updatePixels();
    
    this.state = 0;
    this.x = this.grid.width/2;
    this.y = this.grid.height/2;
  }
  
  void setRule(int which)
  {
    if (which >= r.length) which = 0;
    this.ruleIndex = which;
  }

  boolean color0(color c)
  {
    return (red(c) == 255);
  }

  boolean color1(color c)
  {
    return (red(c) == 0);
  }
  
  int colorIndex(color pixel)
  {
    if (color0(pixel)) return 0;
    if (color1(pixel)) return 1;
    return -1;
  }
  
  void colorCell(int colorIndex)
  {
    offset = this.x + grid.width*this.y;
    grid.pixels[offset] = color(colorIndex == 0 ? 255 : 0);  
  }
  
  void turn(int which)
  {
    this.direction = TurmiteTurn[which][direction];
  }

  void state_(int which)
  {
    this.state = which;
  }
  
  void move()
  {
    switch(direction)
    {
    case 0:
      y = y-1;
      if (y<0) y=grid.height-1;
      break;
    case 1:
      x = (x+1)%grid.width;
      break;
    case 2:
      y = (y+1)%grid.height;
      break;
    case 3:
      x = x-1;
      if (x<0) x=grid.width-1;
      break;
    }

  }

  void run()
  {
    grid.loadPixels(); 
    color pixel = grid.get(x, y);

    // Rule
    int c = colorIndex(pixel);
    if (c>=0)
    {
      int[] next = r[ruleIndex][state][c];
      int nextColorIndex = next[0];
      int nextTurn = next[1];
      int nextState = next[2];

      // Action
      colorCell(nextColorIndex);
      turn(nextTurn);
      state_(nextState);

      // Move
      move();
    
      // Iteration
      iteration++;
    }

    grid.updatePixels();
  }
}


================================================
FILE: Forms/Turmites/Turmites.pde
================================================
/*
  Turmites

  —
  Based on :
    - http://en.wikipedia.org/wiki/Turmite
    - can't remember where I grabbed the table in Rules tab though ...

  —
  Developped and tested on : 
    - Processing 2.1.1 on MacOSX (10.9.2)

  —
  Instructions : 
    - click to generate new turmite.
      
  —
  Julien @v3ga Gachadoat
  www.v3ga.net
  www.2roqs.com

*/


// ------------------------------------------------------------------------------------------------
PImage grid;
Turmite turmite;
int div = 20;
int ruleIndex = 0;

// ------------------------------------------------------------------------------------------------
void setup()
{
  size(800, 800, P2D);
  grid = createImage(width/div, height/div, RGB);
  turmite = new Turmite(grid, 0);
  setWindowTitle();
}

// ------------------------------------------------------------------------------------------------
void draw()
{
  turmite.run();

  background(255);
  fill(0);
  grid.loadPixels();	
  int i, j, off;
  int x, y;
  color c;
  stroke(0);
  fill(0);
  noStroke();
  rectMode(CENTER);

  for (i=0;i<grid.width;i++)
  {
    x = div*i+div/2;
    for (j=0;j<grid.height;j++)
    {
      y = div*j+div/2;
      if ( red( grid.pixels[i+grid.width*j] ) == 0) {
        rect(x, y, div, div);
      }
    }
  }
  grid.updatePixels();
  filter(INVERT);
}

// ------------------------------------------------------------------------------------------------
void mousePressed()
{
  ruleIndex = (ruleIndex+1)%r.length;
  turmite.reset();
  turmite.setRule(ruleIndex);
}

// ------------------------------------------------------------------------------------------------
void keyPressed()
{
  if (key == CODED) 
  {	
    if (keyCode == RIGHT) { 
      ruleIndex = (ruleIndex+1)%r.length;
    }
    if (keyCode == LEFT) { 
      ruleIndex = ruleIndex-1;
      if (ruleIndex<0) ruleIndex = r.length-1;
    }

    turmite.reset();
    turmite.setRule(ruleIndex);
    setWindowTitle();
  }
  else
  if (key == 's')
  {
    saveFrame("Turmites.png");
  }
}

// ------------------------------------------------------------------------------------------------
void setWindowTitle()
{
  frame.setTitle("Turmites - rule "+ruleIndex);
}


================================================
FILE: Image/CameraPropagation/CameraPropagation.pde
================================================
/*
  CameraPropagation

  —
  Developped and tested on : 
    - Processing 2.1.1 on MacOSX (10.10.2)

  —
  Julien @v3ga Gachadoat
  www.v3ga.net
  www.2roqs.com

*/

// ------------------------------------------------------
import processing.video.*;

// ------------------------------------------------------
Capture camera;

// ------------------------------------------------------
int nbColumns = 10;
int nbRows = 10;

// ------------------------------------------------------
int wImage = 0;
int hImage = 0;

// ------------------------------------------------------
PImage[] images;
int indexImageCurrent = 0;


// ------------------------------------------------------
void setup()
{
  size(1024, 768);
  wImage = width / nbColumns;
  hImage = height / nbRows;

  images = new PImage[nbColumns*nbRows];
  for (int i=0; i<images.length ; i++)
  {
    images[i] = createImage(wImage, hImage, RGB);
  }
  
  camera = new Capture(this, 320, 180, 30); // largeur, hauteur, nombre images par seconde
  camera.start();
}

// ------------------------------------------
void draw()
{
  if (camera.available() == true)
  {
    camera.read();
    
    images[indexImageCurrent].copy(camera, 0, 0, 320, 180, 0, 0, wImage, hImage);
    indexImageCurrent = indexImageCurrent+1; 
    if (indexImageCurrent > images.length-1)
    {
      indexImageCurrent=0;
    }
  }

  // Dessin de notre grille d'images
  // (Identique à ce que l'on a fait sur le dessin de «patterns»)
  for (int j=0; j<nbRows ; j++)
  {
    for (int i=0;i<nbColumns; i++)
    {
      // dessin de l'image
      int indexImageLoop = (indexImageCurrent+(i+j*nbColumns))%images.length;
      image( images[indexImageLoop],  i*wImage, j*hImage, wImage, hImage);
    }
  }

}



================================================
FILE: Image/CameraSlitScanVertical/CameraSlitScanVertical.pde
================================================
/*
  OmbroCinema

  —
  Developped and tested on : 
    - Processing 2.1.1 on MacOSX (10.10.2)

  —
  Julien @v3ga Gachadoat
  www.v3ga.net
  www.2roqs.com

*/

// ------------------------------------------------------
import processing.video.*;

// ------------------------------------------------------
Capture camera;

// ------------------------------------------------------
int wImage = 0;
int hImage = 0;

// ------------------------------------------------------
PImage[] images;

// ------------------------------------------------------
int indexImageCurrent = 0;

// ------------------------------------------------------
void setup()
{
  size(320, 180);

  wImage = width;
  hImage = height;

  images = new PImage[hImage];
  for (int i=0; i<images.length ; i++)
  {
    images[i] = createImage(wImage, hImage, RGB);
  }

  camera = new Capture(this, wImage, hImage, 30);
  camera.start();
}

// ------------------------------------------------------
void draw()
{
  if (camera.available() == true)
  {
    camera.read();

    images[indexImageCurrent].copy(camera, 0, 0, wImage, hImage, 0, 0, wImage, hImage);

    // Pour chaque ligne, on copie une ligne d'une image sauvegardée ...!
    int indexImage = indexImageCurrent;
    for (int y=0;y<hImage;y++)
    {
      copy(images[indexImage], 0, y, wImage, 1, 0, y, wImage, 1);     
      indexImage = (indexImage+1)%hImage;
    }


    indexImageCurrent = (indexImageCurrent+1)%images.length; 
  }
}

// ------------------------------------------
void mousePressed()
{
  saveFrame("slitscan.png");
}

================================================
FILE: README.md
================================================
Processing
==========
A collection of sketches using various libraries I use during courses.  

## Forms
* **Chladni plate interference surfaces**
* **L_System**
* **Turmites** - according to wikipedia, a turmite is a Turing machine which has an orientation as well as a current state and a "tape" that consists of an infinite two-dimensional grid of cells.
* **Sunflower**
* **Torus** - made with Processing 1.5.1.
* **RuttEtra**
* **MazeGenerator** - using recursive backtracker algorithm.
* **Circles**
* **HilbertCurve**
* **Penrose**
* **Langton ant**
* **Cellular automaton with rule 30 and 110**


![Chladni](http://v3ga.github.io/Images/Processing/Forms/Thumbnails/Chandli-250.png)
![L_System](http://v3ga.github.io/Images/Processing/Forms/Thumbnails/L_system-250.png)
![Turmites](http://v3ga.github.io/Images/Processing/Forms/Thumbnails/Turmites-250.png)
![Sunflower](http://v3ga.github.io/Images/Processing/Forms/Thumbnails/Sunflower-250.png)
![Torus](http://v3ga.github.io/Images/Processing/Forms/Thumbnails/Torus-250.png)
![RuttEtra](http://v3ga.github.io/Images/Processing/Forms/Thumbnails/RuttEtra-250.png)
![Maze](http://v3ga.github.io/Images/Processing/Forms/Thumbnails/Maze-250.png)
![Circles recursion](http://v3ga.github.io/Images/Processing/Forms/Thumbnails/Circles_recursion-250.png)
![Hilbert curve](http://v3ga.github.io/Images/Processing/Forms/Thumbnails/HilbertCurve-250.png)
![Penrose](http://v3ga.github.io/Images/Processing/Forms/Thumbnails/Penrose-250.png)
![LangtonAnt](http://v3ga.github.io/Images/Processing/Forms/Thumbnails/LangtonAntMultiple-250.png)
![Rule110](http://v3ga.github.io/Images/Processing/Forms/Thumbnails/Rule110-250.png)

## Animation
* **OmbroCinema**
![OmbroCinema](http://v3ga.github.io/Images/Processing/Animation/Thumbnails/OmbroCinema-250.png)
Download .txt
gitextract_73h8wnpj/

├── Animation/
│   └── OmbroCinema/
│       ├── OmbroCinema.pde
│       ├── Scanimation.pde
│       └── Utils.pde
├── Forms/
│   ├── CellularAutomaton/
│   │   ├── Automaton.pde
│   │   ├── CellularAutomaton.pde
│   │   └── Grid.pde
│   ├── Chladni/
│   │   └── Chladni.pde
│   ├── Circles_recursion/
│   │   ├── Circle.pde
│   │   └── Circles_recursion.pde
│   ├── HilbertCurve/
│   │   └── HilbertCurve.pde
│   ├── L_System/
│   │   ├── LInterpreter.pde
│   │   ├── LRule.pde
│   │   ├── LSystem.pde
│   │   └── L_System.pde
│   ├── LangtonAnt/
│   │   ├── Ant.pde
│   │   ├── Grid.pde
│   │   └── LangtonAnt.pde
│   ├── LangtonAntMultiple/
│   │   ├── Ant.pde
│   │   ├── Grid.pde
│   │   └── LangtonAntMultiple.pde
│   ├── Lorenz/
│   │   └── Lorenz.pde
│   ├── MazeGenerator/
│   │   ├── Maze.pde
│   │   └── MazeGenerator.pde
│   ├── Penrose/
│   │   ├── Penrose.pde
│   │   ├── Sun.pde
│   │   └── Triangle.pde
│   ├── RuttEtra/
│   │   ├── ImageSize.pde
│   │   ├── RuttEtra.pde
│   │   └── RuttEtraizer.pde
│   ├── Sunflower/
│   │   └── Sunflower.pde
│   ├── Torus/
│   │   ├── Torus.pde
│   │   └── TorusKnot.pde
│   └── Turmites/
│       ├── Rules.pde
│       ├── Turmite.pde
│       └── Turmites.pde
├── Image/
│   ├── CameraPropagation/
│   │   └── CameraPropagation.pde
│   └── CameraSlitScanVertical/
│       └── CameraSlitScanVertical.pde
└── README.md
Condensed preview — 38 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (77K chars).
[
  {
    "path": "Animation/OmbroCinema/OmbroCinema.pde",
    "chars": 1964,
    "preview": "/*\n  OmbroCinema\n\n  —\n  Developped and tested on : \n    - Processing 2.1.1 on MacOSX (10.10.1)\n\n  —\n  Julien @v3ga Gacha"
  },
  {
    "path": "Animation/OmbroCinema/Scanimation.pde",
    "chars": 7089,
    "preview": "import java.lang.reflect.*;\n\n// ------------------------------------------------------\nclass Scanimation\n{\n  int nbFrame"
  },
  {
    "path": "Animation/OmbroCinema/Utils.pde",
    "chars": 477,
    "preview": "import java.util.Calendar;\n\n// ------------------------------------------------------\nclass Timer\n{\n  float now, before;"
  },
  {
    "path": "Forms/CellularAutomaton/Automaton.pde",
    "chars": 1866,
    "preview": "class Automaton\n{\n  Grid grid;\n  int line=0;\n  boolean done = false;\n  int frameStep = 2;  \n  int rule = RULE_30;\n\n  fin"
  },
  {
    "path": "Forms/CellularAutomaton/CellularAutomaton.pde",
    "chars": 593,
    "preview": "/*\n  CellularAutomaton\n —\nAn implementation of Rule 30 / 110 cellular automata described here:\nhttps://en.wikipedia.org/"
  },
  {
    "path": "Forms/CellularAutomaton/Grid.pde",
    "chars": 1124,
    "preview": "class Grid\n{\n  int resx, resy;\n  int[] state;\n  float cellw, cellh;\n  \n  Grid(int resx, int resy)\n  {\n    this.resx = re"
  },
  {
    "path": "Forms/Chladni/Chladni.pde",
    "chars": 1919,
    "preview": "/*\n  Chladni plate interference surfaces\n \n —\n Based on :\n http://paulbourke.net/geometry/chladni/\n \n —\n Developped and "
  },
  {
    "path": "Forms/Circles_recursion/Circle.pde",
    "chars": 1884,
    "preview": "class Circle\n{\n  float radius;\n  float angle = 0.0;\n  float angleSpeed = 0.0;\n  int depth = 0;\n  boolean bPause = false;"
  },
  {
    "path": "Forms/Circles_recursion/Circles_recursion.pde",
    "chars": 738,
    "preview": "/*\n  Circles_recursion\n\n  —\n  Developped and tested on : \n    - Processing 3.2.1 on MacOSX (10.12.5)\n    \n  —\n  Julien @"
  },
  {
    "path": "Forms/HilbertCurve/HilbertCurve.pde",
    "chars": 2836,
    "preview": "/*\n  Hilbert Curve\n  —\n  Based on explanations found here :\n  https://www.youtube.com/watch?v=3s7h2MHQtxc\n  —\n  Developp"
  },
  {
    "path": "Forms/L_System/LInterpreter.pde",
    "chars": 49,
    "preview": "interface LInterpreter\n{\n  void run(String s);\n}\n"
  },
  {
    "path": "Forms/L_System/LRule.pde",
    "chars": 193,
    "preview": "class LRule\n{\n  char variable;\n  String replacement;\n  \n  LRule(char v, String r)\n  {\n    this.variable = v;\n    this.re"
  },
  {
    "path": "Forms/L_System/LSystem.pde",
    "chars": 2260,
    "preview": "class LSystem\n{\n  String seed = \"\";  \n  int generation = 0;\n  ArrayList<String> listGenerations;\n  ArrayList<LRule> list"
  },
  {
    "path": "Forms/L_System/L_System.pde",
    "chars": 1786,
    "preview": "/*\n  L_system\n\n  —\n  Developped and tested on : \n    - Processing 2.1.1 on MacOSX (10.9.2)\n    \n  —\n  Julien @v3ga Gacha"
  },
  {
    "path": "Forms/LangtonAnt/Ant.pde",
    "chars": 1205,
    "preview": "class Ant\n{\n  Grid grid;\n  int i, j; // cell position on grid\n  int turn=0; // 0 -> 0, 1->90, 2->180, 3->270\n  Ant(Grid "
  },
  {
    "path": "Forms/LangtonAnt/Grid.pde",
    "chars": 1124,
    "preview": "class Grid\n{\n  int resx, resy;\n  int[] state;\n  float cellw, cellh;\n  \n  Grid(int resx, int resy)\n  {\n    this.resx = re"
  },
  {
    "path": "Forms/LangtonAnt/LangtonAnt.pde",
    "chars": 440,
    "preview": "/*\n  LangtonAnt\n —\n An implementation of Langton's ant described here:\nhttps://en.wikipedia.org/wiki/Langton%27s_ant\n—\n "
  },
  {
    "path": "Forms/LangtonAntMultiple/Ant.pde",
    "chars": 1205,
    "preview": "class Ant\n{\n  Grid grid;\n  int i, j; // cell position on grid\n  int turn=0; // 0 -> 0, 1->90, 2->180, 3->270\n  Ant(Grid "
  },
  {
    "path": "Forms/LangtonAntMultiple/Grid.pde",
    "chars": 1124,
    "preview": "class Grid\n{\n  int resx, resy;\n  int[] state;\n  float cellw, cellh;\n  \n  Grid(int resx, int resy)\n  {\n    this.resx = re"
  },
  {
    "path": "Forms/LangtonAntMultiple/LangtonAntMultiple.pde",
    "chars": 696,
    "preview": "/*\n  LangtonAntMultiple\n —\n An implementation of Langton's ant described here:\nhttps://en.wikipedia.org/wiki/Langton%27s"
  },
  {
    "path": "Forms/Lorenz/Lorenz.pde",
    "chars": 2424,
    "preview": "/*\n  Lorenz attractors\n\n  —\n  Developped and tested on : \n    - Processing 2.1.1 on MacOSX (10.9.4)\n    \n  —\n  Julien @v"
  },
  {
    "path": "Forms/MazeGenerator/Maze.pde",
    "chars": 5151,
    "preview": "class Maze\n{\n  // Maze structure\n  MazeCell[] cells;\n  int resx, resy, nbCells;\n\n  // Iteration variables\n  ArrayList<Ma"
  },
  {
    "path": "Forms/MazeGenerator/MazeGenerator.pde",
    "chars": 659,
    "preview": "/*\n  MazeGenerator\n —\n An implementation of recursive backtracker algorithm described here:\n https://en.wikipedia.org/wi"
  },
  {
    "path": "Forms/Penrose/Penrose.pde",
    "chars": 952,
    "preview": "/*\n  Penrose\n  —\n  Based on explanations found here :\n  https://en.wikipedia.org/wiki/Penrose_tiling\n  http://preshing.c"
  },
  {
    "path": "Forms/Penrose/Sun.pde",
    "chars": 637,
    "preview": "class Sun\n{\n  int nbTriangles = 10;\n  Triangle[] triangles = new Triangle[nbTriangles];\n\n\n  Sun(int subdivide, float r)\n"
  },
  {
    "path": "Forms/Penrose/Triangle.pde",
    "chars": 1615,
    "preview": "class Triangle\n{\n  final static float phi = 1.61803398875;\n\n  int type = 0;\n  int level = 0;\n\n  PVector A = new PVector("
  },
  {
    "path": "Forms/RuttEtra/ImageSize.pde",
    "chars": 523,
    "preview": "class ImageResize\n{\n  PImage src;\n  PImage resized;\n  \n  ImageResize(PImage src, int divider)\n  {\n    this.resized = new"
  },
  {
    "path": "Forms/RuttEtra/RuttEtra.pde",
    "chars": 1316,
    "preview": "/*\n  RuttEtra\n  —\n  Developped and tested on : \n    - Processing 2.1.1 on MacOSX (10.10.1)\n  —\n  Julien @v3ga Gachadoat\n"
  },
  {
    "path": "Forms/RuttEtra/RuttEtraizer.pde",
    "chars": 8338,
    "preview": "class RuttEtraizer\n{\n  PGraphics imgSmall;\n  PImage imgRef;\n  int blurImgSmall = 1;\n  int drawMode = 1;\n  int s = 8;\n  f"
  },
  {
    "path": "Forms/Sunflower/Sunflower.pde",
    "chars": 423,
    "preview": "/*\n  Sunflower\n \n  —\n  Not sure where this code comes from.\n  \n  —\n  Julien @v3ga Gachadoat\n  www.v3ga.net\n  www.2roqs.c"
  },
  {
    "path": "Forms/Torus/Torus.pde",
    "chars": 1089,
    "preview": "/*\n \n Torus Knot\n  \n —\n Julien @v3ga Gachadoat\n www.v3ga.net\n www.2roqs.com\n \n */\n\n// ----------------------------------"
  },
  {
    "path": "Forms/Torus/TorusKnot.pde",
    "chars": 4624,
    "preview": "// --------------------------------------------------------\n// TorusKnot code is dirty ... sorry.\nclass TorusKnot extend"
  },
  {
    "path": "Forms/Turmites/Rules.pde",
    "chars": 4036,
    "preview": "int[][] TurmiteTurn = \n{\n  // 0 (//)\n  {-1,-1,-1,-1},\n  // 1 (No turn)\n  { 0, 1, 2, 3},\n  // 2  (Right)\n  { 1, 2, 3, 0},"
  },
  {
    "path": "Forms/Turmites/Turmite.pde",
    "chars": 1912,
    "preview": "class Turmite\n{\n  PImage grid;\n  int offset;\n  int x, y;\n  int state = 0;\n  int direction = 0;\n  int ruleIndex=0;\n  int "
  },
  {
    "path": "Forms/Turmites/Turmites.pde",
    "chars": 2177,
    "preview": "/*\n  Turmites\n\n  —\n  Based on :\n    - http://en.wikipedia.org/wiki/Turmite\n    - can't remember where I grabbed the tabl"
  },
  {
    "path": "Image/CameraPropagation/CameraPropagation.pde",
    "chars": 1736,
    "preview": "/*\n  CameraPropagation\n\n  —\n  Developped and tested on : \n    - Processing 2.1.1 on MacOSX (10.10.2)\n\n  —\n  Julien @v3ga"
  },
  {
    "path": "Image/CameraSlitScanVertical/CameraSlitScanVertical.pde",
    "chars": 1563,
    "preview": "/*\n  OmbroCinema\n\n  —\n  Developped and tested on : \n    - Processing 2.1.1 on MacOSX (10.10.2)\n\n  —\n  Julien @v3ga Gacha"
  },
  {
    "path": "README.md",
    "chars": 1799,
    "preview": "Processing\n==========\nA collection of sketches using various libraries I use during courses.  \n\n## Forms\n* **Chladni pla"
  }
]

About this extraction

This page contains the full source code of the v3ga/Processing GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 38 files (69.9 KB), approximately 22.8k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!