> show canvas only <


/* built with Studio Sketchpad: 
 *   https://sketchpad.cc
 * 
 * observe the evolution of this sketch: 
 *   https://studio.sketchpad.cc/sp/pad/view/ro.gj5C5JCcJUd/rev.791
 * 
 * authors: 
 *   Zach Denton

 * license (unless otherwise specified): 
 *   creative commons attribution-share alike 3.0 license.
 *   https://creativecommons.org/licenses/by-sa/3.0/ 
 */ 



// WASD to move. Spacebar to cycle fractal type.

RayMarcher raymarcher;
float moveX = 0.3;
float moveZ = 0.4;
int t = 0;

void setup() {
  size(400, 400);
  raymarcher = new RayMarcher(new PVector(0, 0, -4.0), 0.0001, 8.0, 200);
  colorMode(HSB);
}


void draw() {
  background(0);
  loadPixels();
  for (int i = 0; i < pixels.length; i++) {
    float intensity = raymarcher.structure[i];
    if (intensity > 0)
      pixels[i] = color(130 + (sin(t * PI/50) * intensity * 10), 255, intensity * 255);
  }
  updatePixels();
  t++;
}

void keyPressed() {
  switch (key) {
  case 'w':
    raymarcher.pos.add(new PVector(0, 0, moveZ));
    break;
  case 'a':
    raymarcher.pos.add(new PVector(-moveX, 0, 0));
    break;
  case 's':
    raymarcher.pos.add(new PVector(0, 0, -moveZ));
    break;
  case 'd':
    raymarcher.pos.add(new PVector(moveX, 0, 0));
    break;
  case ' ':
    raymarcher.function++;
    break;
  }
  raymarcher.recalculate();
}

class RayMarcher {
  private float minThreshold, maxDepth;
  private int maxSteps;
  public PVector pos;
  public float[] structure;
  public int function = 0;

  public RayMarcher(PVector cameraPos, float threshold, float depth, int steps) {
    pos = cameraPos;
    minThreshold = threshold;
    maxDepth = depth;
    maxSteps = steps;
    recalculate();
  }

  public void recalculate() {
    structure = new float[width * height];
    for (int i = 0; i < width; i++) {
      for (int j = 0; j < height; j++) {
        PVector direction = new PVector((float) i / width - 0.5, (float) j / height - 0.5, 1.0);
        direction.normalize();

        float totalDistance = 0.0;
        int steps;
        for (steps = 0; steps < maxSteps; steps++) {
          PVector ray = PVector.add(pos, PVector.mult(direction, totalDistance));
          float distance = distance_estimation(ray);
          totalDistance += distance;
          if (distance < minThreshold || totalDistance > maxDepth) break;
        }
        if (totalDistance < maxDepth) {
          structure[i + j*width] = 1.0 - (float) steps / maxSteps;
        } 
        else {
          structure[i + j*width] = 0;
        }
      }
    }
  }

  private float distance_estimation(PVector pos) {
    if (function % 2 == 0) {
      return menger2(pos.x, pos.y, pos.z);
    } 
    else {
      return menger_sponge(pos.x, pos.y, pos.z);
    }
  }

  private float menger_sponge(float x, float y, float z) {
    int iters=6;
    float t;
    for (int n=0;n<iters;n++) {
      x=abs(x); 
      y=abs(y); 
      z=abs(z);
      if (x<y) {
        t=x;
        x=y;
        y=t;
      }
      if (y<z) {
        t=y;
        y=z;
        z=t;
      }
      if (x<y) {
        t=x;
        x=y;
        y=t;
      }
      x=x*3.0-2.0;
      y=y*3.0-2.0;
      z=z*3.0-2.0;
      if (z<-1.0)z+=2.0;
    }
    return (sqrt(x*x+y*y+z*z)-1.5)*pow(3.0, -iters);
  }

  float menger2(float x, float y, float z) {
    int n, iters=12;
    float xx, yy, zz, DEfactor, r2, mr, scaleb;
    float fr, fr2, mr2;
    scaleb=3.2;
    mr2=0.5;
    fr2=1.2;
    xx=0.;
    yy=0.;
    zz=0.;
    r2=x*x+y*y+z*z;
    DEfactor=1.;

    for (n=0;n<iters;n++) {
      if (x>1) x=2-x;
      else if (x<-1) x=-2-x;
      if (y>1) y=2-y;
      else if (y<-1) y=-2-y;
      if (z>1) z=2-z;
      else if (z<-1) z=-2-z;

      r2=x*x+y*y+z*z;
      if (r2<mr2) {
        x=x*fr2/mr2;
        y=y*fr2/mr2;
        z=z*fr2/mr2;
        DEfactor=DEfactor*fr2/mr2;
      } 
      else if (r2<fr2) {
        x=x*fr2/r2;
        y=y*fr2/r2;
        z=z*fr2/r2;
        DEfactor=DEfactor*fr2/r2;
      }
      x=x*scaleb+xx;
      y=y*scaleb+yy;
      z=z*scaleb+zz;
      DEfactor=DEfactor*abs(scaleb)+1;
      r2=x*x+y*y+z*z;
    }
    return sqrt(r2)/DEfactor;
  }
}