• fullscreen
  • PVector2.pde
  • Shell.pde
  • ShellNode.pde
  • sketch_feb28a_Sydney_Shell08.pde
  • v1v2.pde
  • class PVector2 extends PVector
    {
      PVector2() { 
        super();
      }
      PVector2(float a, float b, float c)
      { 
        super(a, b, c); 
      };
    
      void apply(PMatrix3D mat) {
        PVector2 cp = new PVector2(this.x, this.y, this.z);
        this.x = mat.m00 * cp.x + mat.m01 * cp.y + mat.m02 * cp.z + mat.m03;
        this.y = mat.m10 * cp.x + mat.m11 * cp.y + mat.m12 * cp.z + mat.m13;
        this.z = mat.m20 * cp.x + mat.m21 * cp.y + mat.m22 * cp.z + mat.m23 ; //? 
        float t = mat.m30 * cp.x + mat.m31 * cp.y + mat.m32 * cp.z + mat.m33;
          //println(this.x + " " + this.y);
        this.x /= t;
        this.y /= t;
        this.z /= t;
        // mat.print();
      }
    }
    
    
    
    class Shell
    {
      // parameters to control the big circle
      float bR = 150.0;
      float angleZ = 60.0; // angle between two big circles
      float angleZ0 = 0.0; // the angleZ start with
    
      // parameters to control the small circle
      float sR = 120.0;
      float angleQ = 15.0; // angle with Z axis in ZX plane
      float angleA = 100.0; // angle with X axis in XY plane
      float sH;
      String screenText;
      boolean showSphere = false;
    
      // parameters to control the division of the shell
      int divNum = 24;
      PVector [] vB; // intersection points of big circles with small circle
      PVector2 vS; // centre point of small circle
      PMatrix3D matrixS; // matrix of small circle
    
      Shell()
      {
      }
      Shell(float bigRadius, float smallRadius, float Z,float Z0, float Q, float A)
      {
        bR = bigRadius;
        sR = smallRadius;
        angleZ0 = Z0;
        angleZ = Z; 
        angleQ = Q;
        angleA = A;
      }
    
      void draw() 
      {
        randomSeed(3);
        pushMatrix();
        noFill();
        translate(-sH, 0, 0);
        box(10,10,10);
        rotateY(PI/2);
        rotateZ(PI + radians(angleZ0));
    
        PMatrix3D m1 = new PMatrix3D();
        PMatrix3D m2 = new PMatrix3D();
        m1.rotateY(-radians(angleQ));
        m1.rotateZ(-radians(angleA));   
        pushMatrix();
        applyMatrix(m1);
        if (showSphere) {
          displaySphere();
        }
        calculateIntersection(1);
        displayMath(1);
        popMatrix();
    
        m2.rotateY(-radians(angleQ)-PI);
        m2.rotateZ(radians(angleA)); 
        translate(0, 0, 2*sH); 
        rotateZ(PI);
        pushMatrix();
        applyMatrix(m2);
        calculateIntersection(-1);
        displayMath(-1);
        popMatrix();   
    
        popMatrix();
        screenText = "radius:"+ str(sR) + ",  angleZ:"+ angleZ + ",  angleZ0:"+ str(angleZ0) + ",  angleQ:"+ str(angleQ) + ",  angleA:"+ str(angleA) + ",  division:" + str(divNum);
      }
    
      void calculateIntersection(int k)
      {
        stroke(0, random(100,255), random(100,255));
        strokeWeight(2);
        matrixS = new PMatrix3D();
        vB = new PVector[divNum+1];
        vS = new PVector2();
        sH = sqrt(sq(bR) - sq(sR));
        vS.x = sH * sin(radians(angleQ)) * cos(radians(angleA)*k);
        vS.y = sH * sin(radians(angleQ)) * sin(radians(angleA)*k);
        vS.z = sH * cos(radians(angleQ));
    
        float divAngle = k*angleZ / divNum; //divNum
        float angleWithZ;
        PVector vZ = new PVector(0, 0, 1);
        PVector2 v = new PVector2(vS.x, vS.y, vS.z);
        for (int i=0; i<vB.length; i++) {   
          matrixS.rotateZ(-radians(divAngle*i));
          v.apply(matrixS);
          vB[i] = vBefore(v, bR);
          vB[i] = vAfter(vB[i], radians(divAngle*i)); // the second intesRsection point
          matrixS.reset();
          v = new PVector2(vS.x, vS.y, vS.z);
          angleWithZ = PVector.angleBetween(vB[i], vZ);
          pushMatrix();
          rotateZ(radians(divAngle*i));    
          rotateX(PI/2);
          arc(0, 0, 2*bR, 2*bR, PI/2, PI/2 + angleWithZ);
          popMatrix();
        }
      }
    
      void displayMath(int k)
      {
        stroke(255, 255, 0, 50);
        strokeWeight(0.5);
        for (int i=0; i<vB.length; i++) {
          line(0,0,0,vB[i].x, vB[i].y, vB[i].z);
        }
        stroke(55, 55, 0);
        pushMatrix();
        rotateZ(radians(angleA)*k);
        rotateY(radians(angleQ));
        line(0, 0, 0, 0, 0, sH);
        translate(0, 0, sH);
        ellipse(0, 0, sR*2, sR*2);
        popMatrix();
      }
    
      void displaySphere()
      {
        noStroke();
        fill(100,25);
        sphere(bR);
        strokeWeight(1);
        stroke(255, 0, 0); // X axis
        line(-200, 0, 0, 300, 0, 0);
        pushMatrix();
        rotateX(PI/2);
        ellipse(0, 0, 300, 300);
        popMatrix();
        stroke(0, 255, 0); // Y axis
        line(0, -200, 0, 0, 300, 0);
        pushMatrix();
        rotateZ(-PI/2+radians(angleZ));
        rotateY(PI/2);
        ellipse(0, 0, 300, 300);
        popMatrix();
        stroke(0, 0, 255); // Z axis
        line(0, 0,-300, 0, 0, 200);
      }
    
      void keyPressed()
      {
        if (key == CODED) {
          if (keyCode == UP){
            angleZ0 -= 0.5;
          }
          if (keyCode == DOWN){
            angleZ0 += 0.5;    
          }
          if (keyCode == LEFT){
            angleZ -= 0.5;
          }
          if (keyCode == RIGHT){
            angleZ += 0.5;
          }
          if (keyCode == CONTROL){
            showSphere = !showSphere;
          }
        }
        if (key == 'q'|| key == 'Q') { 
          angleQ -= 0.5;
        }
        else if (key == 'w'|| key == 'W') {
          angleQ += 0.5;
        }
        if (key == 'a'|| key == 'A') { 
          angleA -= 0.5;
        }
        else if (key == 's'|| key == 'S') {
          angleA += 0.5;
        }
        if (key == 'z'|| key == 'Z') {
          if (divNum > 1) divNum--;
        }
        else if (key == 'x'|| key == 'X') {
          if (divNum < 36) divNum++;
        }
    
        if (key == '['|| key == '{') {
          if (sR > 2)   sR -= 0.5;
        }
        else if (key == ']'|| key == '}') {
          if (sR < bR)    sR += 0.5;
        }
    
        println("radius:"+ sR + ", angleZ:"+ angleZ + ", angleZ0:"+ angleZ0 + ", angleQ:"+ angleQ + ", angleA:"+ angleA + ", division:" + divNum);
      } 
    
    }
    
    
    
    
    
    
    
    
    
    
    class ShellNode 
    {
      int num;
      float diam;
      float nx, ny, nz;
      float angle = 0;
      float angle1 = 0;
      PVector[] nodes;
    
      // number of nodes, diameter, and which floor it is
      ShellNode (int number, float diameter, float R) 
      {
        num = number;
        diam = diameter; 
        angle = TWO_PI / num;   
        nodes = new PVector[num+2];
        nz = sqrt(sq(R) - sq(diam));
        for (int i=0; i<nodes.length; i++) {
          nx = diam * cos(angle * i + angle1 ) /2;
          ny = diam * sin(angle * i + angle1) /2;
          nodes[i] = new PVector(nx, ny, nz);
        }
      }
    
      void display()
      { 
        stroke(255, 255, 0, 200);
        strokeWeight(1.5);
        noFill();
        for (int i=0; i<nodes.length; i++) {
          pushMatrix();
          translate(nodes[i].x, nodes[i].y, nodes[i].z);
          box(5, 5, 5);
          popMatrix();
        }
      }
    }
    
    
    
    /* By Sheng Cheng, MSc AAC project, March 2009, cooprerate with Ankon and Scott
    
    This code is illustrating parametric design of the shells of the Sydney Opera House.
    
    The geometory of the shell is generated from a sphere. The shell could be cut by three
    circles. The first two of them are the circles that share a same axis which passing 
    through the sphere centre and have the same radius with the sphere; and the third smaller 
    circle will be always on the surface of the sphere, and its radius is changable.
    
    The parametric shells are controlled by following indications:
    1. Choose one shell from the four by pressing key of either 0, 1, 2, or 3, in the keyboard. 
       The information of the chosen one will display in green color on the top of the screen.
    2. [{ and ]} keys control the 'radius' of the small circle.
    3. LEFT and RIGHT direction keys control the 'angleZ'.
    4. UP and DOWN direction keys control the 'angleZ0'.
    5. Qq and Ww keys control the 'angleQ'.
    6. Aa and Ss keys control the 'angleA'.
    7. Zz and Xx keys control the 'division' of the shell.
    8. CONTROL key control whether to show the sphere and the other two circles.
    */
    
    int shellNum = 4;
    int keyControl = 1;
    Shell [] shells;
    boolean mouseControl = true;
    PFont screenFont;
    
    void setup()
    {
      size(720, 720, P3D);
      screenFont = loadFont("Arial-BoldMT-15.vlw");
      shells  = new Shell[shellNum];
      // five parameters: bigRadius, smallRadius, angleZ, angleZ0, angleQ and angleA
      shells[0] = new Shell(150.0, 94.6, 69.0, -100.0, 10.0, 26.0);
      shells[1] = new Shell(150.0, 118.4, 74.0, 0.0, 20.0, 104.0);
      shells[2] = new Shell(150.0, 126.0, 36.0, -50.0, 18.0, 56.0);
      shells[3] = new Shell(150.0, 125.4, 45.0, -80.0, 24.0, 45.0);
    }
    
    
    void draw() 
    {
      lights();
      smooth();
      boolean S = false;
      for (int i=0; i<shells.length; i++) {
        S = S || shells[i].showSphere;
      }
      if (S) noSmooth();
      background(0); 
    
      translate(width/2, height/2, 50);
      rotateX(-PI/2);  
      if (mouseControl) {
        rotateX(PI * (height/2-mouseY)/height);
        rotateY(-PI * (width/2-mouseX)/width);
      }
      shells[0].draw();
      translate(0, -41.3, 160);
      shells[1].draw();
      translate(0, 32.5, 32.5); 
      shells[2].draw();
      translate(0, 19.5, 19.5); 
      shells[3].draw();
      textFont(screenFont);
      textMode(SCREEN);
      for (int i=0; i<shells.length; i++) {
        if (keyControl == i) fill(0, 255, 0);
        else fill(255);
        text("shell " + str(i),10, 20*(i+1));
        text(shells[i].screenText,80, 20*(i+1));
      }
    }
    
    void keyPressed()
    {
      for (int i=0; i<shells.length; i++) {
        char c = char(i+48);
        if (key == c)  {
          keyControl = i;
          break; 
        }
      }
      shells[keyControl].keyPressed();
    }
    
    void mousePressed() 
    { 
      mouseControl = !mouseControl;
    }
    
    
    PVector vBefore (PVector2 v1, float R)
    {
      PVector v2 = new PVector(0, 0, 0);
      float sqH = sq(v1.mag());
      if (sqH < sq(R)) {
        float a = sq(v1.x) + sq(v1.z);
        float b = -2 * sqH * v1.x;
        float c = sq(sqH) - sq(R * v1.z);
        if (sq(b) >= 4*a*c) {
          v2.x = (-b - sqrt(sq(b)-4*a*c)) / (2*a);
          v2.z = (sqH - v1.x*v2.x) / v1.z;
          v2.y = 0.0;
        }
      }
      return v2;  
    }
    
    PVector vAfter (PVector v, float rotAngle)
    {
      float x = v.x;
      v.x = x * cos(rotAngle);
      v.y = x * sin(rotAngle);
      return v;
    }
    

    code

    tweaks (0)

    about this sketch

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

    license

    advertisement

    sheng cheng

    Shells of Sydney Opera House

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

    A case revealing the secret of the shells. Use mouse and key boards to change the form.

    Very nice study. It is a pity that user interaction is so rough.
    You need to login/register to comment.