• fullscreen
  • PG_AlgebraicSurfaceViewer.pde
  • SurfaceFunctions.pde
  • //==========================================================
    // sketch: PG_AlgebraicSurfaceViewer.pde - by Gerd Platl
    //
    // Surface Viewer using GLGraphics and Toxiclibs libraries.
    //
    // tested with:
    //  processing v1.5.1:  http://processing.org/download/
    //  GLGraphics v1.0.0:  http://glgraphics.sourceforge.net/
    //  ToxicLibs v0020:    http://toxiclibs.org/
    //                      https://bitbucket.org/postspectacular/toxiclibs/src
    //
    // v1.0  2011-12-15  inital release
    // v1.1  2012-01-04  more surfaces
    // v1.2  2012-02-10  more surfaces
    // v1.3  2012-03-31  advanced color handling
    //                   added mouse wheel handling
    //
    // note: because of using OpenGL it works only offline!
    //
    // See overview picture about surfaces realized in version 1.3... 
    // http://farm8.staticflickr.com/7064/6885477688_a34ddaa46e_b_d.jpg
    //==========================================================
    /*
    mouse input:
     left mouse button   rotate  
     right mouse button  rotation on/off 
           mouse wheel   zoom in/out
    key commands:
     0 .. 99  select surface
     cursor left/right  scrolling speed
     cursor up/down     turn up/down
     F1 toggle help & frames per second
     F3,F4 +/-  zoom in/out
     blanc     show next surface
     backspace show previous surface
     b  random background color
     c  random material & light colors
     m  random material color
     l  random light 1 colors
     L  random light 2 colors
     e  toggle closed sides
     o  toggle rotate
     r  reset camera
     s  save picture file as Surface_<surfaceName>.png
     t  save surface mesh as binary STL file <surfaceName>.stl
     w  white material color
     
    */
    
    import toxi.geom.*;
    import toxi.geom.mesh.*;
    import toxi.volume.*;
    import codeanticode.glgraphics.*;
    import processing.opengl.*;
    import javax.media.opengl.*;   // import for renderer
    import java.awt.event.*;        // import for mouse wheel event handling 
    
    int displayMode = 0;
    int numberInputTimeout = 800;   // milliSeconds
    float shininess = 32;
    boolean doRotate = true;
    boolean closeSides = true;
    boolean showHelp = false;
    color bgColor = (128);
    
    int dimX = 100;
    int dimY = 100;
    int dimZ = 100;
    
    Vec3D volumeScale = new Vec3D(1, 1, 1).scaleSelf(320);
    
    TriangleMesh mesh;
    
    GLModel surfaceMesh;  // used to store mesh on GPU
    int numV = 0;
    
    //-----------------------------------
    void setup()
    {
      size(600, 600, GLConstants.GLGRAPHICS);
      background(bgColor);
      randomMaterialColors();
      surface = new PSurface(initSurface);
      frame.addMouseWheelListener(new MouseWheelInput());   // add mouse wheel listener
    }
    //-----------------------------------
    void draw()
    {
      background(bgColor);
      translate(width/2, height/2, 0);
      rotateX(mouseY*0.01);
      rotateY(mouseX*0.01);
    
      CheckSelectionInput();
      String msg = surface.index + ": " + surface.name;
      switch (displayMode)
      { case 0:                       // display calculating...
          println ("fc="+frameCount+ "  " +surface.name);
          msg += "         calculating...";
          displayMode++;
          HandleCamera();
          if (numV > 0) DoRendering();
          break;
    
        case 1:                       // calculate surface
          CalculateSurface();
          displayMode++;
          text(msg,10,20);
          break;
    
        case 2:                      // draw surface
          msg += "         " + numV + " vertices";
          HandleCamera();
          DoRendering();
      }
      fill(255);
      textMode(SCREEN);
      text (msg,10,20);
      if (showHelp)
        text("keys: cursor,blanc,bs,+,-,b,c,l,o,e,r,s,t,w,0.."+(surfaces-1)+"    fps=" + round(frameRate), 10, height-20);
    }
    //-----------------------------------
    int selectionTime = 99999999;
    int inputNumber = 0;
    //-----------------------------------
    void CheckSelectionInput()
    {
      if (millis() > selectionTime)   // key input timeout?
      {
        println (">>> calculating surface " + inputNumber + ", please wait a moment ...");
        SelectSurface (inputNumber);   // 0..n: select surface function
        inputNumber = 0;
        selectionTime = 999999999;
      }
    }
    //-----------------------------------
    void numberPressed()
    {
      selectionTime = millis() + numberInputTimeout;  // end of key input = current time + 1000 msec
      inputNumber = inputNumber * 10 + keyCode-48;
    }
    //-----------------------------------
    void keyPressed()
    {
      //  if (debug) println (keyCode + " '" + key + "'    ");
      if      ((key     >=   '0')
            && (key     <=   '9')) numberPressed();     // 0..n: select function
      else if (keyCode ==  LEFT) speedX -= 0.1;         // <- auto scroll to left
      else if (keyCode == RIGHT) speedX += 0.1;         // -> auto scroll to right
      else if (keyCode ==    UP) rotY += 1.5;           // W: scroll up
      else if (keyCode ==  DOWN) rotY -= 1.5;           // S: scroll down
      else if (keyCode ==     8) ChangeSurface (-1);    // backspace
      else if (keyCode ==    32) ChangeSurface (+1);    // blanc
      else if ((keyCode ==   114)
            || (key     ==   '+')) zoomCamera (0.99);   // F3, +  zoom in
      else if ((keyCode ==   115)
            || (key     ==   '-')) zoomCamera (1.01);   // F4, -  zoom out
      else if (keyCode ==   112) showHelp = !showHelp;  // F1
      else if (key == 'b') randomBackground();
      else if (key == 'c') randomColors();
      else if (key == 'm') randomMaterialColors();
      else if (key == 'l') randomLight1Colors();
      else if (key == 'L') randomLight2Colors();
      else if (key == 'o') doRotate = !doRotate;
      else if (key == 'e') { closeSides = !closeSides;   displayMode = 0; }
      else if (key == 'r') ResetCamera();
      else if (key == 's') save("Surface_" + surface.name + ".png" );       // save picture file
      else if (key == 't') mesh.saveAsSTL(sketchPath("Surface_" + surface.name+".stl"));   // save mesh as STL file
      else if (key == 'w') whiteColors();
      else if (key == 'x') if (colorM2[3] == 1.0) colorM2[3] = 0.3; else colorM2[3] = 1.0;
      else if (key == 'y') shininess++;
      else if (key == 'z') shininess--;
    }
    
    //-----------------------------------
    void mousePressed()
    {
      if (mouseButton == RIGHT) doRotate = !doRotate;
    }
    
    //===================================
    //    handle camera
    //===================================
    float rotX = 0;        // vertical angle
    float rotY = 100;      // horizontal angle
    float fov = 1.1;       // vertical field-of-view angle (in radians)
    float speedX = 0.2;    // rotation speed
    //-----------------------------------
    void ResetCamera()
    {
      rotX = 0;            // vertical angle
      rotY = 100;          // horizontal angle
      fov = 1.1;           // vertical field-of-view angle (in radians)
      speedX = 0.2;        // rotation speed
      doRotate = true;
    }
    //-----------------------------------
    void HandleCamera()
    {
      beginCamera();
      if (fov < 0.1) fov = 0.1;
      if (fov > 1.5) fov = 1.5;
      perspective(fov, float(width)/height, 1, 800);
    
      if (mousePressed)
      {
        if (mouseButton == LEFT)
        {
          rotX -= 0.5 * (mouseX - pmouseX);
          rotY += 0.5 * (mouseY - pmouseY);
        }
      }
      camera (0, rotY, 400, // eyeX, eyeY, eyeZ
              0, 5, 0,      // centerX, centerY, centerZ
              0, -1, 0);    // upX, upY, upZ
      endCamera();
      rotateY (rotX / 100.0);    // left/right
      if (doRotate) rotX += speedX;
    }
    //-----------------------------------
    int zoomFrame = 0;
    void zoomCamera(float factor)
    {
      fov *= factor;
      if (frameCount != zoomFrame)
        text ("zoom=" + nf(fov,0,2), 340,20);
      zoomFrame = frameCount;
    }
    //-----------------------------------
    // listen for MouseWheelEvent
    //-----------------------------------
    class MouseWheelInput implements MouseWheelListener
    { void mouseWheelMoved(MouseWheelEvent e) 
      { fov *= 1.0 + 0.01 * e.getWheelRotation(); } 
    }
    
    
    //===================================
    //    handle SURFACES
    //===================================
    
    PSurface surface;          // handle surface functions
    
    //-----------------------------------
    void SelectSurface (int sno)
    {
      surface.SelectFunction(sno);
      displayMode = 0;
    }
    //-----------------------------------
    void ChangeSurface (int delta)
    {
      surface.SelectFunction(surface.index + delta);
      displayMode = 0;
    }
    //-----------------------------------
    void CalculateSurface()
    {
      PVector pos = new PVector ();
      float NS=4.2;
    
      VolumetricSpace volume = new VolumetricSpaceArray(volumeScale, dimX, dimY, dimZ);
    
      // fill volume with values
      for (int z=0; z<dimZ; z++)
      {
        pos.z = (z-dimZ/2)*NS;
        for (int y=0; y<dimY; y++)
        {
          pos.y = (y-dimY/2)*NS;
          for (int x=0; x<dimX; x++)
          {
            pos.x = (x-dimX/2)*NS;
            //---------------------------------------------
            volume.setVoxelAt(x, y, z, surface.Value(pos));
            //---------------------------------------------
          }
        }
      }
      if (closeSides) volume.closeSides();
      convertVolumeSpaceToMesh(volume);
    }
    //-----------------------------------
    void convertVolumeSpaceToMesh(VolumetricSpace volume)
    {
      float ISO_THRESHOLD = 0.01;
    
      // store in IsoSurface and compute surface mesh for the given threshold value
      mesh = new TriangleMesh("iso");
      IsoSurface surface = new HashIsoSurface(volume, 0.333333);
      surface.computeSurfaceMesh(mesh, ISO_THRESHOLD);
    
      // update lighting information
      mesh.computeVertexNormals();
      // get flattened vertex array
      float[] verts = mesh.getMeshAsVertexArray();
      // in the array each vertex has 4 entries (XYZ + 1 spacing)
      numV = verts.length / 4;
      float[] norms=mesh.getVertexNormalsAsArray();
    
      surfaceMesh = new GLModel(this, numV, TRIANGLES, GLModel.STATIC);
      surfaceMesh.beginUpdateVertices();
      for (int i = 0; i < numV; i++) 
        surfaceMesh.updateVertex(i, verts[4 * i], verts[4 * i + 1], verts[4 * i + 2]);
      surfaceMesh.endUpdateVertices();
    
      surfaceMesh.initNormals();
      surfaceMesh.beginUpdateNormals();
      for (int i = 0; i < numV; i++) 
        surfaceMesh.updateNormal(i, norms[4 * i], norms[4 * i + 1], norms[4 * i + 2]);
      surfaceMesh.endUpdateNormals();
    
      // Setting the color of all vertices to white, but not directly used, see comments in the draw() method.
      surfaceMesh.initColors();
      surfaceMesh.beginUpdateColors();
      for (int i = 0; i < numV; i++) 
        surfaceMesh.updateColor(i, 255, 255, 255);
      surfaceMesh.endUpdateColors();
    
      // Setting model shininess
      println (shininess);
      surfaceMesh.setShininess(shininess);
    }
    
    //===================================
    //    handle color
    //===================================
    float alpha = 1.0;  // 0..1 transparency, 1.0 = opaque
    float[] colorM1 = new float[] {0.3, 0.3, 0.3, alpha};
    float[] colorM2 = new float[] {0.5, 0.8, 0.6, alpha};
    float[] colorL0d = new float[] {1.0, 1.0, 1.0, 0.1};
    float[] colorL0s = new float[] {1.0, 1.0, 1.0, 0.1};
    float[] colorL1d = new float[] {0.9, 0.3, 0.5, 0.1};
    float[] colorL1s = new float[] {0.8, 0.8, 0.6, 0.1};
    float[] light1pos = new float[] {-100, 600, 2000, 0};
    float[] light2pos = new float[] {1000, -600, -2000, 0};
    //-----------------------------------
    void randomBackground()
    {
      bgColor = color(100+random(55), 100+random(55), 100+random(55)); 
    }
    //-----------------------------------
    void randomColor(float[] rColor)
    {
      rColor[0] = random(1.0);  // R
      rColor[1] = random(1.0);  // G
      rColor[2] = random(1.0);  // B
    //  rColor[3] = random(1.0);  // alpha
      println (nf(rColor[0],0,2) + " " + nf(rColor[1],0,2) + " " + nf(rColor[2],0,2));
    }
    //-----------------------------------
    void whiteColors()
    {
      colorM2 = new float[] {1, 1, 1, alpha};
    }
    //-----------------------------------
    void randomMaterialColors()
    {
      println ("random material colors");
      randomColor (colorM1);
      randomColor (colorM2);
    }
    //-----------------------------------
    void randomLight1Colors()
    {
      println ("random light-1 colors");
      randomColor (colorL0d);
      randomColor (colorL0s);
    }
    //-----------------------------------
    void randomLight2Colors()
    {
      println ("random light-2 colors");
      randomColor (colorL1d);
      randomColor (colorL1s);
    }
    //-----------------------------------
    void randomColors()
    {
      randomMaterialColors();
      randomLight1Colors();
      randomLight2Colors();
    }
    //===================================
    //    do rendering
    //===================================
    void DoRendering()
    {
      // need to switch to pure OpenGL mode first
      GLGraphics renderer = (GLGraphics)g;
      renderer.beginGL();
    
      renderer.gl.glEnable(GL.GL_LIGHTING);
    
      // Disabling color tracking, so the lighting is determined using the colors
      // set only with glMaterialfv()
     // renderer.gl.glDisable(GL.GL_COLOR_MATERIAL);
    
      // Enabling color tracking for the specular component, this means that the
      // specular component to calculate lighting will obtained from the colors of the model.
      // This tutorial is quite good to clarify issues regarding lighting in OpenGL:
      // http://www.sjbaker.org/steve/omniv/opengl_lighting.html
      renderer.gl.glEnable(GL.GL_COLOR_MATERIAL);
    //  renderer.gl.glColorMaterial(GL.GL_FRONT_AND_BACK, GL.GL_SPECULAR);
      renderer.gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_AMBIENT, colorM1, 0);
      renderer.gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_DIFFUSE, colorM2, 0);
    
      renderer.gl.glEnable(GL.GL_LIGHT0);
      renderer.gl.glLightfv(GL.GL_LIGHT0, GL.GL_POSITION, light1pos, 0);
      renderer.gl.glLightfv(GL.GL_LIGHT0, GL.GL_DIFFUSE,  colorL0d, 0);
      renderer.gl.glLightfv(GL.GL_LIGHT0, GL.GL_SPECULAR, colorL0s, 0);
    
      renderer.gl.glEnable(GL.GL_LIGHT1);
      renderer.gl.glLightfv(GL.GL_LIGHT1, GL.GL_POSITION, light2pos, 0);
      renderer.gl.glLightfv(GL.GL_LIGHT1, GL.GL_DIFFUSE,  colorL1d, 0);
      renderer.gl.glLightfv(GL.GL_LIGHT1, GL.GL_SPECULAR, colorL1s, 0);
    
      renderer.model(surfaceMesh);   // render surface triangle mesh
    
      renderer.endGL();      // back to processing
    }
    
    
    //==========================================================
    // sketch: SurfaceFunctions.pde
    // implement class PSurface                   
    // and some examples of algebraic surface functions       
    // v1.0  2011-12-15  inital release
    // v1.1  2012-01-04  more surfaces
    // v1.2  2012-02-10  more surfaces
    // v1.3  2012-03-31  added more surfaces:
    //                   PG_C16CubeValue, BorromeanRings1, PG_DoubleHelix_1, 
    //                   BarthsSextik1, BarthsDecic
    //
    // The default function space in x, y and z direction 
    // is best viewed within -200 .. +200.
    // Positive function values means you are inside the 3d-shape.
    //
    // How to add a new surface function: 
    // - note: replace <XXX> to new surface name
    // - add new <XXX>Value() to 'functions' list
    // - create class <XXX>Value as copy of class XxxValue 
    // - implement function <XXX>Value
    // - use PVector.mult(pos, <scaleFactor>);  for scaling 
    //
    //==========================================================
    
    int initSurface = 29;    // sketch starting index of surface
    
    //----------------------------------------------------------
    // function definition: calculate value at given 3d position 
    //----------------------------------------------------------
    interface SurfaceFunction 
    { 
      String getName();
      float getValue (PVector pos);
    }
    
    //----------------------------------------------------------
    // set list of all surface functions 
    //----------------------------------------------------------
    SurfaceFunction[] functions = new SurfaceFunction[]
    { 
      new PlaneValue(), 
      new CylinderValue(), 
      new SphereValue(), 
      new QSphereValue(), 
      new TorusValue(), 
      new BlobValue(), 
      new Chmutov2Value(),
      new PG_C8CubeValue(),
      new PG_C16CubeValue(),
      new TetrahedralValue(), 
      new McMullenValue(), 
      new HeartValue(), 
      new Bretzel2Value(), 
      new Bretzel6Value(),
      new SchwartzValue(),
      new GyroidValue(),
      new SteinerSurface1(),
      new BorromeanRings1(),
      new SchwartzRing_01(),
      new PG_WireCube_2(),
      new PG_RippleCube_2(),
      new PG_Alienship_2(),
      new PG_Tuetue_1(),
      new PG_Icosa_4(),
      new PG_Meteor_898989(),
      new PG_TwistedTorus_3(),
      new PG_DoubleHelix_1(),
      new PG_Isolator_1(),
      new PG_Beasty_1(),
      new BarthsSextik(),
      new BarthsDecic(),
      // fractals
      new Mandelbulb_8Power(),
      new Julia3d()
    };
    
    int surfaces = functions.length;       // number of surfaces
    
    //==========================================================
    // define class PSurface to handle surfaces
    //==========================================================
    class PSurface
    {
      int index;               // current function index 
      String name;             // current surface name
      SurfaceFunction sFunc;   // current surface function
    
      // set surface function by index
      public PSurface (int functionIndex) 
      { 
        SelectFunction (functionIndex);
      }
    
      // select surface function by index
      void SelectFunction(int functionIndex) 
      { 
        this.index = (functionIndex + surfaces) % surfaces;
        this.name = functions[index].getName();
        this.sFunc = functions[index];   // set function call
      }
      
      // get algebraic surface value at given position
      float Value(PVector pos)
      { //println ("getValue: " +  sfunc.getValue(pos));
        return sFunc.getValue(pos);
      }
    }
    
    
    //==========================================================
    // define algebraic surface functions
    //==========================================================
    
    //PVector v1 = new PVector ();
    
    //---------------------------------------------------------
    // Plane:  z^2 = 0
    //---------------------------------------------------------
    class PlaneValue implements SurfaceFunction 
    { 
      String getName() { return "Plane"; }
      float getValue(PVector pos)
      {
        return 10-100 * sq(pos.z);
      }
    }
    
    //----------------------------------------------------------
    // Cylinder:  y^2+z^2 = r^2      r=radius
    //----------------------------------------------------------
    class CylinderValue implements SurfaceFunction
    { 
      String getName() { return "Cylinder"; }
      float getValue(PVector pos)
      { 
        return 4000 - (sq(pos.z) + sq(pos.y));
      }
    }
    
    //----------------------------------------------------------
    // Sphere:  x^2+y^2+z^2 = r^2    r=radius
    //----------------------------------------------------------
    class SphereValue implements SurfaceFunction
    { 
      String getName() { return "Sphere"; }
      public float getValue(PVector pos)
      { 
        return 40000 - (sq(pos.x) + sq(pos.y) + sq(pos.z)) ;
      }
    }
    
    //----------------------------------------------------------
    // QSphere:  x^8+y^8+z^8 = r^8    r=radius
    //----------------------------------------------------------
    class QSphereValue implements SurfaceFunction
    { 
      String getName() { return "QSphere"; }
      public float getValue(PVector pos)
      { 
        return 25.0E16 - (pow(pos.x,8) + pow(pos.y,8) + pow(pos.z,8)) ;
      }
    }
    
    //----------------------------------------------------------
    // Torus:  rr^2 - z^2 - sqr(sqrt(x^2+y^2)-tr) = 0
    //----------------------------------------------------------
    float tr = 180;    // torus radius
    float rr = 20*20;  // ring radius ^ 2
    
    class TorusValue implements SurfaceFunction
    { 
      String getName() { return "Torus"; }
      public float getValue(PVector pos)
      { 
        return rr - sq(pos.z) - sq (sqrt(sq(pos.x) + sq(pos.y)) - tr);
      }
    }
    
    //----------------------------------------------------------
    // Implicit Blob Surface:   x^2+y^2+z^2 +cos(4*x) +cos(4*y) +cos(4*z)+k
    //----------------------------------------------------------
    class BlobValue implements SurfaceFunction
    { 
      String getName() { return "Blob"; }
      public float getValue(PVector pos)
      { 
        PVector v1 = PVector.mult(pos, 0.006);   
        return 100 - 1000 * (v1.dot(v1) +cos(4*v1.x) +cos(4*v1.y) +cos(4*v1.z));
      }
    }
    //----------------------------------------------------------
    // Chmutov-2 Surface:     
    //    1-(x^2*(3-4*x^2)^2
    //     + y^2*(3-4*y^2)^2
    //     + z^2*(3-4*z^2)^2)
    // http://mathworld.wolfram.com/ChmutovSurface.html
    //----------------------------------------------------------
    class Chmutov2Value implements SurfaceFunction
    { 
      String getName() { return "Chmutov-2"; }
      public float getValue(PVector pos)
      { 
        PVector v1 = PVector.mult(pos, 0.006);   
        float x2 = v1.x*v1.x;
        float y2 = v1.y*v1.y;
        float z2 = v1.z*v1.z;
        return 1.01-(x2*sq(3-4*x2)+y2*sq(3-4*y2)+z2*sq(3-4*z2));
      }
    }
    //----------------------------------------------------------
    // PG_C8Cube
    //  1 -(27*x*x*(4*x*x*(x*x-1)+1)-1)^2
    //    -(27*y*y*(4*y*y*(y*y-1)+1)-1)^2
    //    -(27*z*z*(4*z*z*(z*z-1)+1)-1)^2  
    //----------------------------------------------------------
    class PG_C8CubeValue implements SurfaceFunction
    { 
      String getName() { return "PG_C8Cube"; }
      public float getValue(PVector pos)
      { 
        PVector v1 = PVector.mult(pos, 0.0055);
        float x2 = v1.x*v1.x;   
        float y2 = v1.y*v1.y;   
        float z2 = v1.z*v1.z;   
        return 1.02 -(sq(8*x2*(x2-1) + 1) 
                     +sq(8*y2*(y2-1) + 1) 
                     +sq(8*z2*(z2-1) + 1));
      }
    }
    //----------------------------------------------------------
    // PG_C16Cube
    //  1 -(27*x*x*(4*x*x*(x*x-1)+1)-1)^2
    //    -(27*y*y*(4*y*y*(y*y-1)+1)-1)^2
    //    -(27*z*z*(4*z*z*(z*z-1)+1)-1)^2  
    //----------------------------------------------------------
    class PG_C16CubeValue implements SurfaceFunction
    { 
      String getName() { return "PG_C16Cube"; }
      public float getValue(PVector pos)
      { 
        PVector v1 = PVector.mult(pos, 0.0055);
        float x2 = v1.x*v1.x;   
        float y2 = v1.y*v1.y;   
        float z2 = v1.z*v1.z;   
        return 1.02 -sq(27*x2*(4*x2*(x2-1)+1)-1) 
                    -sq(27*y2*(4*y2*(y2-1)+1)-1)
                    -sq(27*z2*(4*z2*(z2-1)+1)-1);
      }
    }
          
          
    //----------------------------------------------------------
    // Tetrahedral Surface: 
    // http://www.javaview.de/services/algebraic/index.html
    // (x^2 + y^2 + z^2)^2 + 8*x*y*z - 10*(x^2 + y^2 + z^2) + 25=0
    //----------------------------------------------------------
    class TetrahedralValue implements SurfaceFunction
    { 
      String getName() { return "Tetrahedral"; }
      public float getValue(PVector pos)
      { 
        PVector v1 = PVector.mult(pos, 0.014);   
        float f2 = v1.dot(v1);     // f2 = (x^2 + y^2 + z^2)
        return -(f2*f2 + 8 * v1.x * v1.y * v1.z - 10*f2 + 25.0);
      }
    }
    
    //----------------------------------------------------------
    // Implicit McMullenK3
    // (1 + x*x) * (1 + y*y) * (1 + z*z) + 8 * x * y * z - k
    // http://local.wasp.uwa.edu.au/~pbourke/geometry/mullen/
    //----------------------------------------------------------
    class McMullenValue implements SurfaceFunction
    {
      String getName() { return "McMullen"; }
      public float getValue(PVector pos)
      { 
        PVector v1 = PVector.mult(pos, 0.0082);
        float x2 = v1.x*v1.x;
        float y2 = v1.y*v1.y;
        float z2 = v1.z*v1.z;
        return -100 * ((1+x2) * (1+y2) * (1+z2) + 8 * v1.x * v1.y * v1.z - 1.4);
      }
    }
    
    //----------------------------------------------------------
    // Heart
    // (2*x^2+y^2+z^2-1)^3 -0.1*x^2*z^3 - y^2*z^3
    //----------------------------------------------------------
    class HeartValue implements SurfaceFunction
    { 
      String getName() { return "Heart"; }
      public float getValue(PVector pos)
      { 
        PVector v1 = PVector.mult(pos, 0.0062);
        float x2 = v1.x*v1.x;
        float y2 = v1.y*v1.y;
        float z2 = v1.z*v1.z;
        float y3 = v1.y*y2;
        return -400 * (pow(2*z2 +x2 + y2-1, 3) -0.1*z2*y3 -x2*y3);
      }
    }
    //----------------------------------------------------------
    // Implicit Bretzel2
    //  ((x^2 +y^2)^2 - x^2 + y^2)^2 + z^2 - 0.02;
    // http://www.lama.univ-savoie.fr/~simon/Genus2.jpg
    // http://virtualmathmuseum.org/Surface/bretzel2/bretzel2.html
    //----------------------------------------------------------
    class Bretzel2Value implements SurfaceFunction
    { 
      String getName() { return "Bretzel2"; }
      public float getValue(PVector pos)
      { 
        PVector v1 = PVector.mult(pos, 0.0052);
        float x2 = v1.x*v1.x;
        float y2 = v1.y*v1.y;
        float z2 = v1.z*v1.z;
        return -400 * (sq(x2*x2 +y2*y2 -x2 +y2) +z2 -0.01);
      }
    }
    
    //----------------------------------------------------------
    // Isosurface: PG_Bretzel6
    //   (x*x*x*x +y*y*y*y -x*x +y*y)^2 +z*z -0.01)
    //  *(z*z*z*z +y*y*y*y -y*y +z*z)^2 +x*x -0.01)
    //  *(x*x*x*x +z*z*z*z -z*z +x*x)^2 +y*y -0.01)-0.00001
    //----------------------------------------------------------
    class Bretzel6Value implements SurfaceFunction
    { 
      String getName() { return "Bretzel6"; }
      public float getValue(PVector pos)
      { 
        PVector v1 = PVector.mult(pos, 0.0052);
        float x2 = v1.x*v1.x;   
        float x4 = x2*x2;
        float y2 = v1.y*v1.y;   
        float y4 = y2*y2;
        float z2 = v1.z*v1.z;   
        float z4 = z2*z2;
        return -300 * ((sq(x4 +y4 -x2 +y2) +z2 -0.01)
          *(sq(z4 +y4 -y2 +z2) +x2 -0.01)
          *(sq(x4 +z4 -z2 +x2) +y2 -0.01)
          -0.0037);
      }
    }
    //----------------------------------------------------------
    class SchwartzValue implements SurfaceFunction
    { 
      String getName() { return "Schwartz"; }
      public float getValue(PVector pos)
      { 
        PVector v1 = PVector.mult(pos, 0.062);
        return 0.1 - (cos(v1.x) + cos(v1.y) + cos(v1.z));
      }
    }
    
    //----------------------------------------------------------
    class GyroidValue implements SurfaceFunction
    { 
      String getName() { return "Gyroid"; }
      public float getValue(PVector pos)
      { 
        PVector v1 = PVector.mult(pos, 0.04);
        return abs (cos(v1.x)*sin(v1.z) 
                  + cos(v1.y)*sin(v1.x) 
                  + cos(v1.z)*sin(v1.y)) - 1.0;
      }
    }
    //----------------------------------------------------------
    // Isosurface: SteinerSurface1 9/2007 
    //----------------------------------------------------------
    class SteinerSurface1 implements SurfaceFunction
    { 
      String getName() { return "SteinerSurface1"; }
      public float getValue(PVector pos)
      { 
        PVector v1 = PVector.mult(pos, 0.005);
        float x = v1.x;
        float y = v1.y;
        float z = v1.z;
        return 0.01 -2*x*y*z -sq(x*y) -sq(x*z) -sq(y*z);
      }
    }
    //----------------------------------------------------------
    // Isosurface: PG_SteinerSurface_1 9/2007 
    //----------------------------------------------------------
    class BorromeanRings1 implements SurfaceFunction
    { 
      String getName() { return "BorromeanRings1"; }
      public float getValue(PVector pos)
      { 
        PVector v1 = PVector.mult(pos, 0.032);
        float x = sq(v1.x);
        float y = sq(v1.y);
        float z = sq(v1.z);
        return -(sq(0.1*x+y+z+2) -9*(0.1*x+y))
               *(sq(0.1*z+x+y+2) -9*(0.1*z+x)) 
               *(sq(0.1*y+z+x+2) -9*(0.1*y+z));
      }
    }
    
    //----------------------------------------------------------
    // http://k3dsurf.s4.bizhat.com/k3dsurf-ftopic30-0.html
    //----------------------------------------------------------
    class SchwartzRing_01 implements SurfaceFunction
    { 
      String getName() { return "SchwartzRing_01"; }
      public float getValue(PVector pos)
      { 
        PVector v1 = PVector.mult(pos, 0.013);
        float x = v1.x;
        float y = v1.y;
        float z = v1.z;
        return -((cos(x)+ cos(y) + cos(z))
               *((cos(x + sin(x)/4)
                + cos(y + sin(y)/4)
                + cos(z + sin(z)/4)))
                +0.06*sq(sin((y*y)) +sin((x*x)) +sin((z*z))));
      }
    }
    
    //----------------------------------------------------------
    class PG_WireCube_2 implements SurfaceFunction
    { 
      String getName() { return "PG_WireCube_2"; }
      public float getValue(PVector pos)
      { 
        PVector v1 = PVector.mult(pos, 0.0055);
        float x = v1.x;   
        float y = v1.y;   
        float z = v1.z;   
        return   pow(x,16) + pow(y,16) + pow(z,16)
               -(pow(x,18) + pow(y,18) + pow(z,18))
               -0.06;
      }
    }
    //----------------------------------------------------------
    // http://www.flickr.com/photos/flisp3d/4776634565/
    //----------------------------------------------------------
    class PG_RippleCube_2 implements SurfaceFunction
    { 
      String getName() { return "PG_RippleCube_2"; }
      public float getValue(PVector pos)
      { 
        PVector v1 = PVector.mult(pos, 0.0056);
        float x = v1.x;
        float y = v1.y;
        float z = v1.z;
        return 0.05 * (0.5 - sq(1-(pow(x,16) + pow(y,16) + pow(z,16))))
               -( sq(2*x) * sq(2*y) * sq(2*z) 
                * sq(x+y) * sq(z+y) * sq(x+z) 
                * sq(x-z) * sq(x-y) * sq(2*y-2*z));
      }
    }
    
    //----------------------------------------------------------
    // http://k3dsurf.s4.bizhat.com/k3dsurf-ftopic30-0.html
    //----------------------------------------------------------
    class PG_Alienship_2 implements SurfaceFunction
    { 
      String getName() { return "PG_Alienship_2"; }
      public float getValue(PVector pos)
      { 
        PVector v1 = PVector.mult(pos, 0.036);
        float x = v1.x;
        float y = v1.y;
        float z = v1.z;
        return (cos(y) +2*cos(x)*cos(z)
             -(cos(x) + cos(y) + cos(z))
            *((cos(x + sin(x)/2.2)
              +cos(y + sin(y)/2.2)
              +cos(z + sin(z)/2.2)))
             -0.0008*(sq(x*x)+sq(y*y)+sq(z*z)));
      }
    }
    
    //----------------------------------------------------------
    class PG_Icosa_4 implements SurfaceFunction
    { 
      String getName() { return "PG_Icosa_4"; }
      public float getValue(PVector pos)
      { 
        PVector v1 = PVector.mult(pos, 0.033);
        float x = v1.x;
        float y = v1.y;
        float z = v1.z;
        float f1 = (1+sqrt(5))/2;
        return 1 - ( cos(x+f1*y) + cos(x-f1*y)
                   + cos(y+f1*z) + cos(y-f1*z)
                   + cos(z-f1*x) + cos(z+f1*x))*33333
                   / (x*x+y*y+z*z)
                 - sq(x*x+y*y+z*z);
      }
    }   
    //----------------------------------------------------------
    // http://k3dsurf.s4.bizhat.com/k3dsurf-ftopic30-15.html
    //----------------------------------------------------------
    class PG_Tuetue_1 implements SurfaceFunction
    { 
      String getName() { return "PG_Tuetue_1"; }
      public float getValue(PVector pos)
      { 
        PVector v1 = PVector.mult(pos, 0.014);
        float x = v1.x;
        float y = v1.y;
        float z = v1.z;
        return -0.07*(pow(x,4) + pow(y,4) + pow(z,4) - 22)
               +1.05*(sin(x*y) + sin(y*z) + sin(z*x));
      }
    }
    
    //----------------------------------------------------------
    class PG_Meteor_898989 implements SurfaceFunction
    { 
      String getName() { return "PG_Meteor_898989"; }
      public float getValue(PVector pos)
      { 
        PVector v1 = PVector.mult(pos, 0.01);
        float x = v1.x;
        float y = v1.y;
        float z = v1.z;
        float f1 = cos(4*x)*sin(4*y) +cos(4*y)*sin(4*z) +cos(4*z)*sin(4*x);
        float f2 = cos(8*x)*cos(9*y) +cos(8*y)*cos(9*z) +cos(8*z)*cos(9*x);
        float f3 = cos(x) +cos(y) +cos(z);
        float f4 = cos(x+sin(x)/4) +cos(y+sin(y)/4) +cos(z+sin(z)/4);
        return f1 + 0.5*f2 + f3*f4 -4;
      }
    }
    //----------------------------------------------------------
    class PG_TwistedTorus_3 implements SurfaceFunction
    { 
      String getName() { return "PG_TwistedTorus_3"; }
      public float getValue(PVector pos)
      { 
        PVector v1 = PVector.mult(pos, 0.03);
        float x = v1.x;
        float y = v1.y;
        float z = v1.z;
        float f1 = sqrt(x*x+y*y)-5;
        float f2 = atan2(x,y);
        return -1.0 * ( pow(f1*cos(f2) - z*sin(f2), 8)
                       +pow(f1*sin(f2) + z*cos(f2), 8) - 1
                      );
      }
    }
    //----------------------------------------------------------
    // 1.0 - (sqrt(x^2+y^2)-5)^2
    //     - (atan((z*cos(y)-x*sin(y))/(x*cos(y)+z*sin(y))))^2
    //----------------------------------------------------------
    class PG_DoubleHelix_1 implements SurfaceFunction
    { 
      String getName() { return "PG_DoubleHelix_1"; }
      public float getValue(PVector pos)
      { 
        PVector v1 = PVector.mult(pos, 0.035);
        float x = v1.x;
        float y = v1.y;
        float z = v1.z;
        float sinY = sin(y);
        float cosY = cos(y);
        return 1.0 - sq(sqrt(x*x+z*z) - 5.0)
                   - sq(atan( (z*cosY - x*sinY) / (x*cosY + z*sinY)));
      }
    }
    //----------------------------------------------------------
    class PG_Isolator_1 implements SurfaceFunction
    { 
      String getName() { return "PG_Isolator_1"; }
      public float getValue(PVector pos)
      { 
        PVector v1 = PVector.mult(pos, 0.01);
        float x = v1.x;
        float y = v1.y;
        float z = v1.z;
        float xyz = x*y*z;
        return 1 - (x*x+3)*(y*y+3)*(z*z+3) + 33*(xyz+0.96) + sin(xyz*11);
      }
    }
    //----------------------------------------------------------
    class PG_Beasty_1 implements SurfaceFunction
    { 
      String getName() { return "PG_Beasty_1"; }
      public float getValue(PVector pos)
      { 
        PVector v1 = PVector.mult(pos, 0.03);
        float x = sq(v1.x);
        float y = sq(v1.y);
        float z = sq(v1.z);
        return 4*x*y*(1-z) - sq(x+y-z) +222;
      }
    }
    
    //----------------------------------------------------------
    // Barths Sextik Surface
    // http://cage.ugent.be/~hs/barth/barth.html
    // 4*(k*x^2-y^2)*(k*y^2-z^2)*(k*z^2-x^2) 
    //   - (1+2j)*(x^2+y^2+z^2-1)^2 = 0
    //----------------------------------------------------------
    class BarthsSextik implements SurfaceFunction
    { 
      float j = (1+sqrt(5))/2;
      float k = sq(j);
      String getName() { return "BarthsSextik"; }
      public float getValue(PVector pos)
      { 
        PVector v1 = PVector.mult(pos, 0.01);
        float x2 = sq(v1.x);
        float y2 = sq(v1.y);
        float z2 = sq(v1.z);
        return 4*(k*x2-y2)
                *(k*y2-z2)
                *(k*z2-x2)
      -(1+2*j) * sq(x2+y2+z2-1);
      }
    }
    //----------------------------------------------------------
    // Barths Decic Surface
    // http://cage.ugent.be/~hs/barth/barth.html
    // 8*(x2-k*y2)*(y2-k*z2)*(z2-k*x2) 
    //    * (x4+y4+z4 -2*x2*y2 -2*y2*z2 - 2*z2*x2)
    //    + (3+5*j)*(x2+y2+z2-1)^2 * (x2+y2+z2-(2-j))^2 = 0
    //
    //----------------------------------------------------------
    class BarthsDecic implements SurfaceFunction
    { 
      float j = (1+sqrt(5))/2;
      float k = sq(j*j);
      String getName() { return "BarthsDecic"; }
      public float getValue(PVector pos)
      { 
        PVector v1 = PVector.mult(pos, 0.011);
        float x2 = sq(v1.x);
        float y2 = sq(v1.y);
        float z2 = sq(v1.z);
        return -8*(x2-k*y2)*(y2-k*z2)*(z2-k*x2) 
                *(x2*x2+y2*y2+z2*z2 -2*x2*y2 -2*y2*z2 - 2*z2*x2)
               - (3+5*j)*sq(x2+y2+z2-1) * sq(x2+y2+z2-(2-j));
      }
    }
    
    //---------------------------------------------------------
    // Mandelbulb Surface 
    // http://www.skytopia.com/project/fractal/2mandelbulb.html#formula
    //---------------------------------------------------------
    float cosX = 0.0;
    float sinX = 0.0;
    
    class Mandelbulb_8Power implements SurfaceFunction
    { 
      String getName() { return "Mandelbulb_8Power"; }
    
      int iterations = 4;
      public float getValue(PVector pos)
      { 
        PVector v1 = PVector.mult(pos, 0.0052);
        float x = v1.x;
        float y = v1.z;
        float z = v1.y;
        float sinT8, cosT8;
        float r = 0.0;
        for (int ni=0; ni < iterations; ni++)
        {
          // convert (x,y,z) to polar coordinates (r, th, ph)
          float w2 = x*x + y*y;
          float w = sqrt(w2);
          float r2 = w2 + z*z;
          r = sqrt(r2);
          if (r >= 2.0) break;         //===>
    
          // raise (r, th, ph) to the 8th power
          MandelBulb_8th (w, z, r);   sinT8 = sinX;  cosT8 = cosX;   
          MandelBulb_8th (y, x, w); 
    
          //(r, th, ph) <- c + (r, th, ph)^8
          float r8 = r2 * r2 * r2 * r2;
          x = v1.x + r8 * sinT8 * cosX;
          y = v1.z + r8 * sinT8 * sinX;
          z = v1.y + r8 * cosT8;
        }
        return 2.0 - r;
      }
    }
    //---------------------------------------------------------
    // Compute sin(8t) & cos(8t) given r*sin(t), r*cos(t) & r
    //---------------------------------------------------------
    void MandelBulb_8th (float ps, float pc, float r)
    {
      sinX = ps;
      cosX = pc;
      if (r > 0.000001)
      {
        float s = ps / r;
        float c = pc / r;
        // iterate formula for sin(2t) & cos(2t) 3 times to get sin(8t) & cos(8t)
        float t = 2.0*s*c;   c = c*c - s*s;   s = t;
              t = 2.0*s*c;   c = c*c - s*s;   s = t;
              t = 2.0*s*c;   c = c*c - s*s;   s = t;
        sinX = s;
        cosX = c;
      }
    }
    
    //----------------------------------------------------------
    // http://www.fractalforums.com/3d-fractal-generation/true-3d-mandlebrot-type-fractal/msg8470/#msg8470
    //----------------------------------------------------------
    class Julia3d implements SurfaceFunction
    {
      String getName() { return "Julia3d"; }
    
      private float maxRadius = 1000.0;
      float getValue(PVector pos)
      { 
        PVector v1 = PVector.mult(pos, 0.0055);
        float x = v1.x;
        float y = v1.y;
        float z = v1.z;
        float radius = 0.0;
        float ny,nx,nz;
        for(int ni = 0; ni < 45; ni++)
        {
          nx = x*x - z*z - 0.27;      nz = 2*x*z;        // variant 1
    //      nx = x*x-z*z-0.285;         nz = 2*x*z-0.1;    // variant 2
    //      nx = x*x-z*z-0.27;          nz = 3*x*z;        // variant 3
    //      nx = x*x-z*z-0.25;          nz = 16*x*y*z-0.2; // variant 4
          ny = y;
          x = nz;
          y = -nx;
          z = -ny;
          radius = x*x + y*y + z*z;
          if (radius > maxRadius) break;
        }
        return maxRadius - radius;
      }
    }
    //----------------------------------------------------------
    //  use this draft to create your surface function...
    //----------------------------------------------------------
    class XxxValue implements SurfaceFunction
    { 
      String getName() { return "???surfacename???"; }
      public float getValue(PVector pos)
      { 
        return 123.456;
      }
    }
    
    
    

    code

    tweaks (0)

    about this sketch

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

    license

    advertisement

    Gerd Platl

    Algebraic Surface Viewer v1.3

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

    Surface Viewer v1.3 (Mar2012) using GLGraphics and Toxiclibs libraries.

    Because of using OpenGL it works only offline!

    -- mouse input --
    left mouse button rotate
    right mouse button rotation on/off
    mouse wheel zoom in/out

    -- key commands --
    0 .. 99 select surface
    cursor left/right scrolling speed
    cursor up/down turn up/down
    F1 toggle help & frames per second
    F3,F4 +/- zoom in/out
    blanc show next surface
    backspace show previous surface
    r reset camera
    s save picture file as Surface_<surfaceName>.png
    for more commands see source...

    Thomas Diewald
    15 Dec 2011
    very nice, i think i have to study this one!

    i did some tests with k3d-surf about a year ago (http://thomasdiewald.com/blog/?p=573) , but i didnt think, this is possible with processing, its even cooler this way :)
    Gerd Platl
    4 Jan 2012
    Have added some new surfaces to v1.1 where 2 of them are well known fractals. Of course the fractals are not rendered with the appropriate resolution, but I want to know how it looks like.
    ---new surfaces---
    .SchwartzRing_01
    .PG_RippleCube_3
    .PG_Tuetue_1
    .PG_TwistedTorus_3
    .Mandelbulb_8Power
    .Julia3d
    Gerd Platl
    31 Mar 2012
    Here some features about the improved version v1.3:
    - advanced color handling
    - mouse wheel handling added
    - new surfaces:
    . PG_C16CubeValue
    . BorromeanRings1
    . PG_DoubleHelix_1
    . BarthsSextik1
    . BarthsDecic
    E. Berkman
    21 Apr 2012
    Hi Gerd,
    I cannot open your viewer in Chrome, IE or Firefox.
    Ihave the latest java (6.31)
    win7 ,64 bit.
    What could be the reason?
    regards, Eveline
    Gerd Platl
    22 Apr 2012
    Hi
    Because of Processing is not supporting an OPENGL rendering in any Browser you have to download the sketch and run it within Processing. That's what I mean with 'offline'.
    To use a P3D rendering the usage of 'glgraphics' has to be replaced by standard 3D statements. Usefull hints to do this topic are welcome.
    ;-) Gerd
    E. Berkman
    22 Apr 2012
    Ahaaa! Now Iunderstand;-) Thank you!
    E. Berkman
    22 Apr 2012
    Hi Gerd, me again....
    I am a total noob in processing, how do I run your viewer from processing?
    sorry about his, E
    E. Berkman
    23 Apr 2012
    I figured it out, I had to install the GLGraphics and Toxiclibs libraries.
    It's working fine!
    Very nice:-)
    You need to login/register to comment.