• fullscreen
  • ColorFunction.pde
  • Entocycloid.pde
  • Epicycloid.pde
  • Hypercycloid.pde
  • Hypocycloid.pde
  • Spirograph.pde
  • spirograph_creator.pde
  • public class ColorFunction
    {
      // 1: Minimum, 2: Maximum, 3: rotMulipiler, 4: rotAdder
      
      float[] rData = new float[4];
      float[] gData = new float[4];
      float[] bData = new float[4]; 
      
      public ColorFunction(float[] _rData, float[] _gData, float[] _bData)
      {
        rData = _rData;
        gData = _gData;
        bData = _bData;
      }
      
      public float getR(float rot)
      {
        return (rData[0] + rData[1]*0.5f*(sin(rot*rData[2] + rData[3])+1));
      }
      
      public float getB(float rot)
      {
        return (bData[0] + bData[1]*0.5f*(sin(rot*bData[2] + bData[3])+1));
      }
      
      public float getG(float rot)
      {
        return (gData[0] + gData[1]*0.5f*(sin(rot*gData[2] + gData[3])+1));
      }
      
      public void SetStroke(float rot)
      {
        stroke(getR(rot), getG(rot), getB(rot));
      }
      
      public float[] GetColor(float rot)
      {
        return new float[]{getR(rot), getG(rot), getB(rot)};
      }
    }
    
    // A cycloid that points inwards but travel on the INSIDE of the main circle
    public class Entocycloid extends Spirograph
    {
      public Entocycloid(float _mainRadius, float _smallRadius, float _positionRadius, float _offsetx, float _offsety)
      {
        mainRadius = _mainRadius;
        smallRadius = _smallRadius;
        positionRadius = _positionRadius;
        offsetx = _offsetx;
        offsety = _offsety;
      }  
      
      public Entocycloid(float _mainRadius, float _smallRadius, float _positionRadius, float _offsetx, float _offsety, float _rot, float _rotSpeed)
      {
        mainRadius = _mainRadius;
        smallRadius = _smallRadius;
        positionRadius = _positionRadius;
        offsetx = _offsetx;
        offsety = _offsety;
        rot = _rot;
        rotSpeed = _rotSpeed;
      }
      
      public Entocycloid(float _mainRadius, float _smallRadius, float _positionRadius, float _offsetx, float _offsety, float _rot, float _rotSpeed, ColorFunction _colorFunction)
      {
        mainRadius = _mainRadius;
        smallRadius = _smallRadius;
        positionRadius = _positionRadius;
        offsetx = _offsetx;
        offsety = _offsety;
        rot = _rot;
        rotSpeed = _rotSpeed;
        colorFunction = _colorFunction;
      }
      
      public void draw()
      {
        float currx = offsetx + (mainRadius - smallRadius) * cos(rot) + positionRadius * cos(rot*(mainRadius - smallRadius)/smallRadius);
        float curry = offsety + (mainRadius - smallRadius) * sin(rot) + positionRadius * sin(rot*(mainRadius - smallRadius)/smallRadius);
      
        if(prevx != Float.MIN_VALUE)
        {
          colorFunction.SetStroke(rot);
          line(prevx, prevy, currx, curry);
        }
      
        prevx = currx;
        prevy = curry;
      
        rot += rotSpeed;
      }
    }
    
    // A cycloid that points inwards but travel on the OUTSIDE of the main circle
    public class Epicycloid extends Spirograph
    {
      public Epicycloid(float _mainRadius, float _smallRadius, float _positionRadius, float _offsetx, float _offsety)
      {
        mainRadius = _mainRadius;
        smallRadius = _smallRadius;
        positionRadius = _positionRadius;
        offsetx = _offsetx;
        offsety = _offsety;
      }  
      
      public Epicycloid(float _mainRadius, float _smallRadius, float _positionRadius, float _offsetx, float _offsety, float _rot, float _rotSpeed)
      {
        mainRadius = _mainRadius;
        smallRadius = _smallRadius;
        positionRadius = _positionRadius;
        offsetx = _offsetx;
        offsety = _offsety;
        rot = _rot;
        rotSpeed = _rotSpeed;
      }
      
      public Epicycloid(float _mainRadius, float _smallRadius, float _positionRadius, float _offsetx, float _offsety, float _rot, float _rotSpeed, ColorFunction _colorFunction)
      {
        mainRadius = _mainRadius;
        smallRadius = _smallRadius;
        positionRadius = _positionRadius;
        offsetx = _offsetx;
        offsety = _offsety;
        rot = _rot;
        rotSpeed = _rotSpeed;
        colorFunction = _colorFunction;
      }
      
      public void draw()
      {
        float currx = offsetx + (mainRadius + smallRadius) * cos(rot) - positionRadius * cos(rot*(mainRadius + smallRadius)/smallRadius);
        float curry = offsety + (mainRadius + smallRadius) * sin(rot) - positionRadius * sin(rot*(mainRadius + smallRadius)/smallRadius);
      
        if(prevx != Float.MIN_VALUE)
        {
          colorFunction.SetStroke(rot);
          line(prevx, prevy, currx, curry);
        }
      
        prevx = currx;
        prevy = curry;
      
        rot += rotSpeed;
      }
    }
    
    // A cycloid that points outwards but travel on the OUTSIDE of the main circle
    public class Hypercycloid extends Spirograph
    {
      public Hypercycloid(float _mainRadius, float _smallRadius, float _positionRadius, float _offsetx, float _offsety)
      {
        mainRadius = _mainRadius;
        smallRadius = _smallRadius;
        positionRadius = _positionRadius;
        offsetx = _offsetx;
        offsety = _offsety;
      }  
      
      public Hypercycloid(float _mainRadius, float _smallRadius, float _positionRadius, float _offsetx, float _offsety, float _rot, float _rotSpeed)
      {
        mainRadius = _mainRadius;
        smallRadius = _smallRadius;
        positionRadius = _positionRadius;
        offsetx = _offsetx;
        offsety = _offsety;
        rot = _rot;
        rotSpeed = _rotSpeed;
      }
      
      public Hypercycloid(float _mainRadius, float _smallRadius, float _positionRadius, float _offsetx, float _offsety, float _rot, float _rotSpeed, ColorFunction _colorFunction)
      {
        mainRadius = _mainRadius;
        smallRadius = _smallRadius;
        positionRadius = _positionRadius;
        offsetx = _offsetx;
        offsety = _offsety;
        rot = _rot;
        rotSpeed = _rotSpeed;
        colorFunction = _colorFunction;
      }
      
      public void draw()
      {
        float currx = offsetx + (mainRadius + smallRadius) * cos(rot) - positionRadius * cos(rot*(mainRadius + smallRadius)/smallRadius);
        float curry = offsety + (mainRadius + smallRadius) * sin(rot) + positionRadius * sin(rot*(mainRadius + smallRadius)/smallRadius);
      
        if(prevx != Float.MIN_VALUE)
        {
          colorFunction.SetStroke(rot);
          line(prevx, prevy, currx, curry);
        }
      
        prevx = currx;
        prevy = curry;
      
        rot += rotSpeed;
      }
    }
    
    // A cycloid that points outwards but travel on the INSIDE of the main circle
    public class Hypocycloid extends Spirograph
    {
      public Hypocycloid(float _mainRadius, float _smallRadius, float _positionRadius, float _offsetx, float _offsety)
      {
        mainRadius = _mainRadius;
        smallRadius = _smallRadius;
        positionRadius = _positionRadius;
        offsetx = _offsetx;
        offsety = _offsety;
      }  
      
      public Hypocycloid(float _mainRadius, float _smallRadius, float _positionRadius, float _offsetx, float _offsety, float _rot, float _rotSpeed)
      {
        mainRadius = _mainRadius;
        smallRadius = _smallRadius;
        positionRadius = _positionRadius;
        offsetx = _offsetx;
        offsety = _offsety;
        rot = _rot;
        rotSpeed = _rotSpeed;
      }
      
      public Hypocycloid(float _mainRadius, float _smallRadius, float _positionRadius, float _offsetx, float _offsety, float _rot, float _rotSpeed, ColorFunction _colorFunction)
      {
        mainRadius = _mainRadius;
        smallRadius = _smallRadius;
        positionRadius = _positionRadius;
        offsetx = _offsetx;
        offsety = _offsety;
        rot = _rot;
        rotSpeed = _rotSpeed;
        colorFunction = _colorFunction;
      }
      
      public void draw()
      {
        float currx = offsetx + (mainRadius - smallRadius) * cos(rot) + positionRadius * cos(rot*(mainRadius - smallRadius)/smallRadius);
        float curry = offsety + (mainRadius - smallRadius) * sin(rot) - positionRadius * sin(rot*(mainRadius - smallRadius)/smallRadius);
      
        if(prevx != Float.MIN_VALUE)
        {
          colorFunction.SetStroke(rot);
          line(prevx, prevy, currx, curry);
        }
      
        prevx = currx;
        prevy = curry;
      
        rot += rotSpeed;
      }
    }
    
    public abstract class Spirograph
    {
      float rot = 0f;
      float rotSpeed = PI / 30;
      float mainRadius;
      float smallRadius;
      float positionRadius;
      float offsetx, offsety;
      float prevx = Float.MIN_VALUE;
      float prevy = Float.MIN_VALUE;
      ColorFunction colorFunction = new ColorFunction(new float[]{ 0, 240, 1f/5f, 0 }, new float[]{ 0, 210, 1f/11f, PI }, new float[]{ 0, 230, 1f/9f, PI/3 }); 
      
      public abstract void draw();
    }
    
    /* SPIROGRAPH CREATOR by CALLUM ROGERS
     * 
     * This code is licensed under the GNU GPL, the terms of which can be found here: http://creativecommons.org/licenses/GPL/2.0/
     * You are free to modify, distrbute and share this source code and binaries as long as you abide by the terms of the GPL
     *
     */
    
    Spirograph s;
    
    boolean paused = false;
    boolean speededUp = false;
    boolean superSpeedUp = false;
    boolean mouseDown = false;
    PFont font;
    boolean createOwn = false;
    int createOwnStage = -1;
    float[] createOwnData = new float[8];
    int ct = -1;
    int[] lastMousePos = new int[]{ Integer.MIN_VALUE, Integer.MIN_VALUE };
    
    public static final int CYCLOID_ENTO = 0;
    public static final int CYCLOID_EPI = 1;
    public static final int CYCLOID_HYPO = 2;
    public static final int CYCLOID_HYPER = 3;
    
    boolean cfDesigner = false;
    float[] lastStroke = new float[3];
    ColorFunction colorFunction = new ColorFunction(new float[]{ 0, 240, 1f/5f, 0 }, new float[]{ 0, 210, 1f/11f, PI }, new float[]{ 0, 230, 1f/9f, PI/3 });
    float crot = 0f;
    
    boolean infoScreen = true;
    
    void setup()
    {
      size(600,600);
      smooth();
      frameRate(60);
      noFill();
      stroke(100);
      background(255);
      ellipseMode(CENTER);
      
      font = loadFont("Calibri-16.vlw");
      textFont(font, 16);
      textAlign(LEFT);
      
      s = CreateRandomSpirograph();
    }
    
    Spirograph CreateRandomSpirograph()
    {
      Spirograph sp = s;
      switch((int)random(4))
      {
        case 0: sp = new Entocycloid(width/2 - 20 - random(20), random(width/9,width/5) + random(-7,7), random(width/9, width/5), width/2, height/2, 0f, PI/30, colorFunction); break;
        case 1: sp = new Epicycloid(width/5 - random(20), random(width/9,width/5) + random(-7,7), random(width/9, width/5), width/2, height/2, 0f, PI/30, colorFunction); break;
        case 2: sp = new Hypocycloid(width/2 - 20 - random(20), random(width/9,width/5) + random(-7,7), random(width/9, width/5), width/2, height/2, 0f, PI/30, colorFunction); break;
        case 3: sp = new Hypercycloid(width/5 - random(20), random(width/9,width/5) + random(-7,7), random(width/9, width/5), width/2, height/2, 0f, PI/30, colorFunction); break;
      }
      return sp;
    }
    
    ColorFunction CreateRandomColorFunction()
    {
      return new ColorFunction(new float[]{ random(0,100), random(160,230), 1f/random(3,11), random(0,TWO_PI) }, new float[]{ random(0,100), random(160,230), 1f/random(3,11), random(0,TWO_PI) }, new float[]{ random(0,100), random(160,230), 1f/random(3,11), random(0,TWO_PI) });
    }
    
    void draw()
    {
      if(infoScreen)
      {
        if(mouseDown)
        {
          background(255);
          infoScreen = false;
        }
        else
        {
          background(255);
          fill(0);
          text("Spirograph creator - made by Callum Rogers",10,20);
          text("Buttons: ",20,60);
          text("N: Create your own custom spirograph!",120,60);
          text("R: Create a random spirograph",120,80);
          text("F: Randomize the colours",120,100);
          text("C: Create your own custom colours!",120,120);
          text("P: Pause the drawing",120,140);
          text("S: Speed up drawing 2x",120,160);
          text("Z: Speed up drawing 4x",120,180);
          text("I: Show this screen",120,200);
          textAlign(CENTER);
          text("Click the mouse or press a button to continue!",width/2,height/2);
          textAlign(LEFT);
          noFill();
        }
      }
      else if(createOwn)
      {
        background(255);
        if(createOwnStage == -1)
        {
          if(ct != -1)
          {
            createOwnStage++;
          }
          else
          {
            fill(0);
            text("Please select the type of spirography you want: ",10,20);
            text("Outward Pointing (Hypercycloid) - Press H",20,40);
            text("Inward Pointing (Epicycloid) - Press E",20,60);
            noFill();
          }
        }
        else if(createOwnStage == 0)
        {
          if(mouseDown)
          {
            mouseDown = false;
            createOwnData[0] = mouseX;
            createOwnData[1] = mouseY;
            createOwnStage++;
            if(lastMousePos[0] != Integer.MIN_VALUE)
            {
              mouseX = lastMousePos[0];
              mouseY = lastMousePos[1];
            }
            lastMousePos = new int[]{ Integer.MIN_VALUE, Integer.MIN_VALUE };
          }
          else
          {
            fill(0);
            text("Select the center of the main circle (Press m to select the exact middle)",10,20);
            noFill();
            ellipse(mouseX, mouseY, 2, 2);
          }
        }
        else if(createOwnStage == 1)
        {
          if(mouseDown)
          {
            mouseDown = false;
            float radius = dist(createOwnData[0],createOwnData[1],mouseX,mouseY);
            createOwnData[2] = radius;
            createOwnStage++;
          }
          else
          {
            fill(0);
            text("Select the size of the main circle (move your mouse first, don't click it straight away!)",10,20);
            noFill();
            float diameter = dist(createOwnData[0],createOwnData[1],mouseX,mouseY)*2;
            ellipse(createOwnData[0], createOwnData[1], diameter, diameter);
          }
        }
        else if(createOwnStage == 2)
        {
          if(mouseDown)
          {
            mouseDown = false;
            createOwnData[3] = mouseX;
            createOwnData[4] = mouseY;
            createOwnData[5] = abs(createOwnData[2]-dist(mouseX, mouseY, createOwnData[0],createOwnData[1]));
            if(dist(mouseX, mouseY, createOwnData[0],createOwnData[1]) > createOwnData[2])
            {
              if(ct == CYCLOID_ENTO) ct = CYCLOID_EPI;
              else if(ct == CYCLOID_HYPO) ct = CYCLOID_HYPER;
            }
            createOwnStage++;
          }
          else
          {
            fill(0);
            text("Select the center of the moving circle (it can be on the outside of the main circle)",10,20);
            noFill();
            ellipse(createOwnData[0], createOwnData[1], createOwnData[2]*2, createOwnData[2]*2);
            float diameter = (createOwnData[2]-dist(mouseX, mouseY, createOwnData[0],createOwnData[1]))*2;
            ellipse(mouseX, mouseY, diameter, diameter);
          }
        }
        else if(createOwnStage == 3)
        {
          float distFromCircle = dist(mouseX, mouseY, createOwnData[3], createOwnData[4]);
          if(mouseDown)
          {
            mouseDown = false;
            createOwnData[6] = distFromCircle;
            createOwnData[7] = atan((mouseY - createOwnData[4])/(mouseX - createOwnData[3]));
            createOwnStage++;
          }
          else
          {
            fill(0);
            text("Select the point to draw from on (or off!) the moving circle",10,20);
            noFill();
            ellipse(createOwnData[0], createOwnData[1], createOwnData[2]*2, createOwnData[2]*2);
            ellipse(createOwnData[3], createOwnData[4], createOwnData[5]*2, createOwnData[5]*2);
            stroke(150);
            line(createOwnData[3],createOwnData[4],mouseX,mouseY);
            stroke(lastStroke[0], lastStroke[1], lastStroke[2]);
            ellipse(mouseX, mouseY, 2, 2);
          }
        }
        else if(createOwnStage >= 4)
        {
          createOwn = false;
          infoScreen = false;
          //PrintArray(createOwnData);
          switch(ct)
          {
            case CYCLOID_ENTO: s = new Entocycloid(createOwnData[2], createOwnData[5], createOwnData[6], createOwnData[0], createOwnData[1], createOwnData[7], PI / 30, colorFunction); break;
            case CYCLOID_EPI: s = new Epicycloid(createOwnData[2], createOwnData[5], createOwnData[6], createOwnData[0], createOwnData[1], createOwnData[7], PI / 30, colorFunction); break;
            case CYCLOID_HYPO: s = new Hypocycloid(createOwnData[2], createOwnData[5], createOwnData[6], createOwnData[0], createOwnData[1], createOwnData[7], PI / 30, colorFunction); break;
            case CYCLOID_HYPER: s = new Hypercycloid(createOwnData[2], createOwnData[5], createOwnData[6], createOwnData[0], createOwnData[1], createOwnData[7], PI / 30, colorFunction); break;
          }      
        }
      }
      else if(cfDesigner)
      {
        background(255);
        
        float[] sections = new float[]{ 40, 40+(width-80)/6, 40+(width-80)*2/6, 40+(width-80)*3/6, 40+(width-80)*4/6  };
        fill(0);
        text("Red:",10,sections[0]);
        text("Green:",10,sections[1]);
        text("Blue:",10,sections[2]);
        text("Range:",60,sections[0]);
        text("Range:",60,sections[1]);
        text("Range:",60,sections[2]);
        DrawGradient((int)140, (int)sections[0]-15, width-170, 30, color(0,0,0), color(255,0,0), X_AXIS);
        DrawGradient((int)140, (int)sections[1]-15, width-170, 30, color(0,0,0), color(0,255,0), X_AXIS);
        DrawGradient((int)140, (int)sections[2]-15, width-170, 30, color(0,0,0), color(0,0,255), X_AXIS);
        stroke(0);
        line(140+colorFunction.rData[0]/255*(width-170), sections[0]-19, 140+colorFunction.rData[0]/255*(width-170), sections[0]+19);
        line(140+colorFunction.rData[1]/255*(width-170), sections[0]-19, 140+colorFunction.rData[1]/255*(width-170), sections[0]+19);
        text("Min",130+colorFunction.rData[0]/255*(width-170), sections[0]-21);
        text("Max",130+colorFunction.rData[1]/255*(width-170), sections[0]-21);
        
        line(140+colorFunction.gData[0]/255*(width-170), sections[1]-19, 140+colorFunction.gData[0]/255*(width-170), sections[1]+19);
        line(140+colorFunction.gData[1]/255*(width-170), sections[1]-19, 140+colorFunction.gData[1]/255*(width-170), sections[1]+19);
        text("Min",130+colorFunction.gData[0]/255*(width-170), sections[1]-21);
        text("Max",130+colorFunction.gData[1]/255*(width-170), sections[1]-21);
        
        line(140+colorFunction.bData[0]/255*(width-170), sections[2]-19, 140+colorFunction.bData[0]/255*(width-170), sections[2]+19);
        line(140+colorFunction.bData[1]/255*(width-170), sections[2]-19, 140+colorFunction.bData[1]/255*(width-170), sections[2]+19);
        text("Min",130+colorFunction.bData[0]/255*(width-170), sections[2]-21);
        text("Max",130+colorFunction.bData[1]/255*(width-170), sections[2]-21);
        
        text("Drag the sliders to change the range for each component of the colour.",10,sections[3]-20);
        text("Press C to leave. Press F to randomise the colours.",10,sections[3]);
        text("Below shows how the colour function changes with time:",10,sections[3]+20);
        
        fill(colorFunction.getR(crot), colorFunction.getG(crot), colorFunction.getB(crot));
        crot += PI / 30;
        rect(20,height-20-sections[2]-20,width-40,height-sections[4]+20);
        noFill();
        
        if(mouseDown)
        { // Sliders:
          if((mouseX > 140) && (mouseX < width-30))
          { // Red
            if((mouseY > sections[0]-15)&&(mouseY < sections[0]+15))
            {
              if(dist1(140+colorFunction.rData[0]/255*(width-170),mouseX) < 20)
              {
                colorFunction.rData[0] = (mouseX-140)/(float)(width-170)*255f;
                if(colorFunction.rData[0] >= colorFunction.rData[1]) colorFunction.rData[1]++; 
              }
              else if(dist1(140+colorFunction.rData[1]/255*(width-170),mouseX) < 20)
              {
                colorFunction.rData[1] = (mouseX-140)/(float)(width-170)*255f;
                if(colorFunction.rData[1] <= colorFunction.rData[0]) colorFunction.rData[0] = colorFunction.rData[1]-1;
              }
            } // Green
            else if((mouseY > sections[1]-15)&&(mouseY < sections[1]+15))
            {
              if(dist1(140+colorFunction.gData[0]/255*(width-170),mouseX) < 20)
              {
                colorFunction.gData[0] = (mouseX-140)/(float)(width-170)*255f;
                if(colorFunction.gData[0] >= colorFunction.gData[1]) colorFunction.gData[1]++; 
              }
              else if(dist1(140+colorFunction.gData[1]/255*(width-170),mouseX) < 20)
              {
                colorFunction.gData[1] = (mouseX-140)/(float)(width-170)*255f;
                if(colorFunction.gData[1] <= colorFunction.gData[0]) colorFunction.gData[0] = colorFunction.gData[1]-1;
              }
            } // Blue
            if((mouseY > sections[2]-15)&&(mouseY < sections[2]+15))
            {
              if(dist1(140+colorFunction.bData[0]/255*(width-170),mouseX) < 20)
              {
                colorFunction.bData[0] = (mouseX-140)/(float)(width-170)*255f;
                if(colorFunction.bData[0] >= colorFunction.bData[1]) colorFunction.bData[1]++; 
              }
              else if(dist1(140+colorFunction.bData[1]/255*(width-170),mouseX) < 20)
              {
                colorFunction.bData[1] = (mouseX-140)/(float)(width-170)*255f;
                if(colorFunction.bData[1] <= colorFunction.bData[0]) colorFunction.bData[0] = colorFunction.bData[1]-1;
              }
            }
          }
        }
      }
      else if(!paused)
      {
        s.draw();
        if(speededUp || superSpeedUp)
          s.draw();
        if(superSpeedUp)
        {
          s.draw();
          s.draw();
        }    
      }
    }
    
    float dist1(float point1, float point2)
    {
      return abs(point1 - point2);
    }
    
    static final int Y_AXIS = 0;
    static final int X_AXIS = 1;
    void DrawGradient(int x, int y, float w, float h, color c1, color c2, int axis ){
      // calculate differences between color components 
      float deltaR = red(c2)-red(c1);
      float deltaG = green(c2)-green(c1);
      float deltaB = blue(c2)-blue(c1);
    
      // choose axis
      if(axis == Y_AXIS){
        /*nested for loops set pixels
         in a basic table structure */
        // column
        for (int i=x; i<=(x+w); i++){
          // row
          for (int j = y; j<=(y+h); j++){
            color c = color(
            (red(c1)+(j-y)*(deltaR/h)),
            (green(c1)+(j-y)*(deltaG/h)),
            (blue(c1)+(j-y)*(deltaB/h)) 
              );
            set(i, j, c);
          }
        }  
      }  
      else if(axis == X_AXIS){
        // column 
        for (int i=y; i<=(y+h); i++){
          // row
          for (int j = x; j<=(x+w); j++){
            color c = color(
            (red(c1)+(j-x)*(deltaR/w)),
            (green(c1)+(j-x)*(deltaG/w)),
            (blue(c1)+(j-x)*(deltaB/w)) 
              );
            set(j, i, c);
          }
        }  
      }
    }
    
    void mousePressed()
    {
      mouseDown = true;
    }
    
    void mouseReleased()
    {
      mouseDown = false;
    }
    
    void keyPressed()
    {
      if(infoScreen)
      {
        background(255);
        infoScreen = false;
      }
      else if(key != CODED)
      {
        switch(key)
        {
          case 'n': lastStroke = s.colorFunction.GetColor(s.rot); createOwnStage = -1; ct = -1; if(createOwn == false) createOwn = true; break;
          case 'm': if(createOwn){ lastMousePos[0] = mouseX; lastMousePos[1] = mouseY; mouseX = width/2; mouseY = height/2; if(createOwnStage == 0) mouseDown = true; }; break;
          case 'h': if(createOwn && (createOwnStage == -1)) ct = CYCLOID_HYPO; break;
          case 'e': if(createOwn && (createOwnStage == -1)) ct = CYCLOID_ENTO; break;
          case 'p': paused = !paused; break;
          case 'r': background(255); s = CreateRandomSpirograph(); break;
          case 'f': colorFunction = CreateRandomColorFunction(); break;
          case 'c': background(255); if(cfDesigner) s.colorFunction = colorFunction; cfDesigner = !cfDesigner; infoScreen = false; break;
          case 'i': infoScreen = true; break;
          case 's': speededUp = true; break;
          case 'z': superSpeedUp = true; break;
          // Uncomment this to enable saving (doesn't work on the internet)
          //case 's': Calendar cal = Calendar.getInstance(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss"); save("spirograph-save-" + sdf.format(cal.getTime()) + ".tiff"); break; 
        }
      }
    }
    
    void keyReleased()
    {
      if(key != CODED)
      {
        switch(key)
        {
          case 's': speededUp = false; break;
          case 'z': superSpeedUp = false; break;
        }
      }
    }
    
    void PrintArray(float[] arr)
    {
      for(int i = 0; i < arr.length; i++)
      {
        println(i + ": " + arr[i]);
      }
    }
    
    

    code

    tweaks (0)

    about this sketch

    This sketch is running as Java applet, exported from Processing.

    license

    advertisement

    Callum Rogers

    Spirograph Creator

    Add to Faves Me Likey@! 35
    You must login/register to add this sketch to your favorites.

    This is a sketch that allows you to create spirograph-like cycloids.

    Buttons:

    N: Allows you to create your own custom spirograph!
    R: Creates a random spirograph
    F: Randomizes the colours
    C: Bring up a screen that allows you to create your own custom colours!
    P: Pauses the drawing of the spirograph
    I: Shows the information screen.


    When creating custom spirographs, you may have to move your mouse before clicking to continue.

    Enjoy!

    Guigui plus+
    27 Aug 2009
    Marvelous. Very sober interface but easy to handle and quite complete.
    I did some nice spiros ;^).

    I can't resiste to show you my wheeling 'guiguitrochoid': http://www.openprocessing.org/visuals/?visualID=760
    Callum Rogers
    27 Aug 2009
    Wow, I've just seen yours - its amazing. Even just getting the gear to go around the inside of another is fantastic. The interfaces are a bit minimalistic on mine but they aren't actually complete - those sliders were so annoying. Thanks for the comment!
    sam ng
    31 Aug 2009
    Amazing! But the code may be difficult for me to understand at this moment.
    Callum Rogers
    31 Aug 2009
    The code isn't really that complicated - it's just a bit bulked out by being object orientated and filled with UI stuff. The really important bits are in the draw() methods of the *cycloid.pde files.

    spirograph_creator.pde - all the UI and user interaction.

    Spirograph.pde - class that store some variables that can be inherited by the specific spirograph (*cycloid) classes.

    *cycloid.pde - a specific type of Spirograph. The important bit is in the draw() method where it says "float currx = &lt;something&gt;; float curry = &lt;something&gt;;". These are the parametric equations that describe how the spirograph is drawn. See more information about these at:

    http://en.wikipedia.org/wiki/Epicycloid
    http://en.wikipedia.org/wiki/Hypocycloid
    http://en.wikipedia.org/wiki/Hypotrochoid
    http://en.wikipedia.org/wiki/Epitrochoid
    http://en.wikipedia.org/wiki/Cycloid

    ColorFunction.pde - gets a color from a series of sine functions so that color changes are periodic and dependent on the rotation of the spirograph. The periodicity means that it will always make a pattern of color when applied to the spirograph.

    The function for each component of the color (red, green, blue) is defined by a function similar to 0.5(sinx + 1): http://www.wolframalpha.com/input/?i=0.5(sinx+%2B+1). The information that transforms the sine graph is held within three different float arrays.

    Simple! :p
    Riley Galloway
    2 Oct 2009
    that is crazy, i could never write the code for something like that unless i was able to study processing more, your number thing is just as cool
    Sadiq Ahmad
    12 Dec 2011
    Good Work...
    CgRobot
    20 Aug 2012
    Very cool. I had quite a bit of fun playing with this.
    You need to login/register to comment.