Loading...
Logo
Processing Forum

In order to teach myself the basics of defining classes and constructing objects, I have assembled a sketch that combines a number of different objects - defined in separate Tabs.


But two of the more complex elements are not playing nicely together.


I'm trying to use a variation of the Sutcliffe Pentagon as a background (as explained by Matt Pearson in Generative Art) with a wonderful old algorithm for a kind of interactive petal-thing that I found on these forums - originally I believe by Chris B Stones.


The sketch runs just fine with the Stones-thingammy in the middle and the simpler bits going on around it. It is fast and wonderfully responsive. But when I add the Sutcliffe Pentagon to the mix it becomes really laggy and sluggish. Is there a way to make the whole thing play back quickly and smoothly - by optimizing my code, 'trimming excess fat', removing redundant commands, etc?  


Or am I bumping up against the limitations of what Processing can be expected to do on an early 21st century desktop computer?


Any advice would be much appreciated! 


PS: I'm very new to Processing. My background is in film and media - with a dash of Flash thrown in. So my expectations of frame-rate and smooth playback are derived from that paradigm.


I have long labored under the (mistaken?) impression that algorithmically-derived (vector) graphics play back more efficiently on a computer platform than pixel-based (raster) images derived from a camera - moving or still. So I'm assuming this has to do with my limitations as a rookie coder rather than the limitations of 'the medium'...


(I have removed the sound file from the sketch in the code that follows.)


Copy code
  1. // *********** 001 MAIN Tab *************

  2. //////////////////////////////////////////////////////
  3. /////               Tesla Toy                    /////
  4. //////////////////////////////////////////////////////

  5. SutcliffePentagon mySutcliffePentagon1;

  6. PolarPetal myPolarPetal1;             // DECLARE objects
  7. Planet myPlanet1;
  8. TextRing myTextRing1;
  9. SunRing mySunRing1;

  10. //////////////////////////////////////////////////////
  11. //                 *** SETUP ***
  12. //////////////////////////////////////////////////////

  13. void setup() {
  14.   size(displayWidth, displayHeight);
  15.   smooth();
  16.   frameRate(60);
  17.   

  18. /////////////////////////////////////////////////////////////////////
  19. //  Initialize objects by calling the constructor function  /////////
  20. /////////////////////////////////////////////////////////////////////

  21.   mySutcliffePentagon1 = new SutcliffePentagon();

  22.   myTextRing1 = new TextRing();
  23.   myPolarPetal1 = new PolarPetal();
  24.   myPlanet1 = new Planet();
  25.   mySunRing1 = new SunRing();
  26.   
  27. }


  28. //////////////////////////////////////////////////////
  29. //                 *** DRAW ***
  30. //////////////////////////////////////////////////////

  31. void draw() {
  32.   
  33.   background(10);
  34.     
  35.   mySutcliffePentagon1.pulse();

  36.   mySunRing1.shine();              // Call methods on the objects
  37.   myPolarPetal1.bloom();
  38.   myPlanet1.orbit();
  39.   myTextRing1.write();


  40. pushMatrix();
  41.   ellipseMode(CENTER);
  42.   noFill();
  43.   strokeWeight(10);
  44.   stroke(#E4EA40);
  45.   ellipse(0,0,630,630);
  46.   strokeWeight(14);
  47.   stroke(#333333);
  48.   ellipse(0,0,606,606);
  49. popMatrix();

  50. }


  51. // *********** 002 NEW Tab - Planet  *************

  52. // Based on Shiffman: Convert a polar coordinate (r,theta) to cartesian (x,y):
  53. // * x = r * cos(theta)
  54. // * y = r * sin(theta)

  55. class Planet {

  56.   //VARIABLES
  57.   float theta;                          // Angle and angular velocity, accleration
  58.   float theta_vel;
  59.   float theta_acc;
  60.   float r;

  61.   //CONSTRUCTOR
  62.   Planet() {                            //Initialize all values
  63.     r = 333;
  64.     theta = 0;
  65.     theta_vel = 0;
  66.     theta_acc = 0.001;
  67.   }

  68.   void orbit() {    
  69.     translate(width/2, height/2);        // Translate the origin point to the center of the screen

  70.     float x = r * cos(theta);            // Convert polar to cartesian
  71.     float y = r * sin(theta);

  72.     ellipseMode(CENTER);                 // Draw the ellipse at the cartesian coordinate
  73.     noStroke();
  74.     fill(#0064C8);
  75.     ellipse(x, y, 14, 14);

  76.     theta_vel += theta_acc;              // Apply acceleration and velocity to angle (r remains static in this example)
  77.     theta += theta_vel;
  78.   }

  79. }

  80. // *********** 003 NEW TAB - PolarPetal *************
  81. // Class Declaration for PolarPetals. Tweak of brilliant algorithm by Chris B Stones

  82. class PolarPetal {                      

  83.   //VARIABLES
  84.   int   H, K;
  85.   int   N;         // Total Steps
  86.   float phi;       // shift
  87.   float w;         // angular
  88.   color c;

  89.   float A, a;      // Amplitude function  
  90.   float x, y;
  91.   
  92.   float phi_delta; // how fast to rotate the thing
  93.   float w_delta;   // how many petals?? 


  94.   //CONSTRUCTOR
  95.   PolarPetal() {
  96.     H = width/2;
  97.     K = height/2;
  98.     N = 600;
  99.     phi = 0.0f;
  100.     w   = 0.0f;
  101.     c = color(#0064C8);
  102.   }
  103.   
  104.   void bloom() {                         // Draw points function    
  105.       fill(c);                           // Color of PolarPetal
  106.       noStroke();
  107.       
  108.       for ( int i = 0; i < N; i++ ) {
  109.         a = 275*sin(w_delta*i+phi);      // Polar Equation. y = A sin(theta)
  110.         x = a*cos(i) + H;                // convert polar to rect coordinates - x value
  111.         y = a*sin(i) + K;                // convert polar to rect coordinates - y value
  112.         ellipse(x, y, 5, 5);             // plot points
  113.       }

  114.       w_delta = ((float)mouseX)/width;
  115.       phi_delta = ((float)mouseY)/height;
  116.       // rotation is done with shifting
  117.       phi = phi + phi_delta;
  118.       w = w + w_delta;
  119.    }

  120. } // END entire CLASS


  121. // *********** 004 NEW Tab - SunRing *************
  122. // A sunburst of text and numbers arranged around the edge of a circle. These are the names and nos of a selection of Tesla's PATENTS

  123. class SunRing {                      

  124.   //VARIABLES
  125.   PFont font;

  126.   String[] names = { 
  127.     "Energy", "174544", "Wireless", "433700", "Generator", "135174", "Electrical", "685958", "Dynamo", "335787", "Current", "593138", "Controller", 
  128.     "447920", "Frequency", "609249", "Alternating", "787412", "Transmit", "186799", "Lighting", "335786", "Oscillate", "723188", "Arc lamp", "512340", 
  129.     "Radiant", "390413", "Turbine", "487796", "Machine", "577670", "Power", "649621", "Rotary", "725605"
  130.   };
  131.   
  132.   float delta = TWO_PI / names.length; 
  133.   int radius = 370; 

  134.   //CONSTRUCTOR
  135.   SunRing() {
  136.     font = createFont("CourierNewPSMT-20", 20);          // Create font
  137.     textFont(font, 15.2);
  138.   }

  139.   //FUNCTION
  140.   void shine() {
  141.     fill(120); 

  142.     for (int i = 0; i<names.length; i++) {                // 36 items in the list (360 degrees/10 = 36)

  143.                                    ///////////////////////////////////////////////////////////
  144.       textAlign(LEFT);             ///   *** VERY NB for correct inner alignment!!! ***    ///
  145.                                    ///////////////////////////////////////////////////////////

  146.       float xPos = width/2+radius * cos(i* delta); 
  147.       float yPos = height/2+radius * sin(i* delta);

  148.       pushMatrix();                                      // ADD words/nos and arrange them around the circle
  149.       translate(xPos, yPos);
  150.       rotate(delta * i);
  151.       text(names[i], 10, 0); 
  152.       popMatrix();
  153.     }
  154.   }
  155. }

  156. // *********** 005 NEW Tab - SutcliffePentagon *************

  157. // Class Declaration for SutcliffePentagon (with 8 sides) Modified from Matt Pearson

  158. class SutcliffePentagon {                      

  159.   //VARIABLES
  160.   FractalRoot pentagon;
  161.   float _strutFactor = 0.3;
  162.   float _strutNoise;
  163.   int _maxlevels = 2;
  164.   int _numSides = 8;

  165.   //CONSTRUCTOR
  166.   SutcliffePentagon() {
  167.     size(displayWidth, displayHeight); 
  168.     smooth();
  169.     _strutNoise = random(12);
  170.   }

  171. //////////////////////////////////////////////////////
  172. /////                  Methods                    ////
  173. //////////////////////////////////////////////////////

  174.   void pulse() {
  175.     //background(0);
  176.     frameRate(50);

  177.     _strutNoise += 0.02;
  178.     //_strutFactor = (noise(_strutNoise) * 3) - 1;   //Alternative values
  179.     _strutFactor = noise(_strutNoise) * 3;        //Constrains outward movement - Set somwehere between 1.5 - 3.0    

  180.     pentagon = new FractalRoot(5);
  181.     //pentagon = new FractalRoot(frameCount*2.5);
  182.     //pushMatrix();
  183.     
  184.     pentagon.drawShape();
  185.     //pushMatrix();
  186.   }


  187. ////////////////////////////////////////////////////////////

  188.   //Point object

  189.   class PointObj {
  190.     float x, y;
  191.     PointObj(float ex, float why) {
  192.       x = ex; 
  193.       y = why;
  194.     }
  195.   }

  196.   //FractalRoot object - Multi-sided Version

  197.   class FractalRoot {
  198.     PointObj[] pointArr = {
  199.     };
  200.     Branch rootBranch;

  201.     FractalRoot(float startAngle) {
  202.       pushMatrix();
  203.      
  204.       float centX = width/2;
  205.       float centY = height/2;
  206.       float angleStep = 180.0f/_numSides;                                // *** The degrees here control inward movement

  207.       for (float i = 0; i<360; i+=angleStep) {
  208.         float x = centX + (500 * cos(radians(startAngle + i)));
  209.         float y = centY + (500 * sin(radians(startAngle + i)));
  210.         pointArr = (PointObj[])append(pointArr, new PointObj(x, y));
  211.       }
  212.       rootBranch = new Branch(0, 0, pointArr);
  213.       popMatrix();
  214.     }

  215.     void drawShape() {
  216.       fill(0, 100, 200);   

  217.       rootBranch.drawMe();
  218.     }
  219.   }

  220.   //Branch object

  221.   class Branch {
  222.     int level, num;
  223.     PointObj[] outerPoints = {
  224.     };
  225.     PointObj[] midPoints = {
  226.     };
  227.     PointObj[] projPoints = {
  228.     };
  229.     Branch[] myBranches = {
  230.     };

  231.     Branch(int lev, int n, PointObj[] points) {
  232.       level = lev;
  233.       num = n;
  234.       outerPoints = points;
  235.       midPoints = calcMidPoints();
  236.       projPoints = calcStrutPoints();

  237.       if ((level+1) < _maxlevels) {
  238.         Branch childBranch = new Branch(level+1, 0, projPoints);
  239.         myBranches = (Branch[])append(myBranches, childBranch); 
  240.         for (int k = 0; k < outerPoints.length; k++) {
  241.           int nextk = k-1;
  242.           if (nextk < 0) { 
  243.             nextk += outerPoints.length;
  244.           }
  245.           PointObj[] newPoints = {  
  246.             projPoints[k], midPoints[k], outerPoints[k], midPoints[nextk], projPoints[nextk]
  247.           };
  248.           childBranch = new Branch(level+1, k+1, newPoints);
  249.           myBranches = (Branch[])append(myBranches, childBranch);
  250.         }
  251.       }
  252.     }

  253.     PointObj[] calcMidPoints() {
  254.       PointObj[] mpArray = new PointObj[outerPoints.length];
  255.       for (int i = 0; i < outerPoints.length; i++) {
  256.         int nexti = i+1;
  257.         if (nexti == outerPoints.length) { 
  258.           nexti = 0;
  259.         }
  260.         PointObj thisMP = calcMidPoint(outerPoints[i],
  261.         outerPoints[nexti]);
  262.         mpArray[i] = thisMP;
  263.       }
  264.       return mpArray;
  265.     }

  266.     PointObj calcMidPoint(PointObj end1, PointObj end2) {
  267.       float mx, my;
  268.       if (end1.x > end2.x) {
  269.         mx = end2.x + ((end1.x - end2.x)/2);
  270.       } 
  271.       else {
  272.         mx = end1.x + ((end2.x - end1.x)/2);
  273.       }
  274.       if (end1.y > end2.y) {
  275.         my = end2.y + ((end1.y - end2.y)/2);
  276.       } 
  277.       else {
  278.         my = end1.y + ((end2.y - end1.y)/2);
  279.       }
  280.       return new PointObj(mx, my);
  281.     }

  282.     PointObj[] calcStrutPoints() {
  283.       PointObj[] strutArray = new PointObj[midPoints.length];
  284.       for (int i = 0; i < midPoints.length; i++) {
  285.         int nexti = i+3;
  286.         if (nexti >= midPoints.length) { 
  287.           nexti -= midPoints.length;
  288.         }
  289.         PointObj thisSP = calcProjPoint(midPoints[i], outerPoints[nexti]); 
  290.         strutArray[i] = thisSP;
  291.       } 
  292.       return strutArray;
  293.     }

  294.     PointObj calcProjPoint(PointObj mp, PointObj op) {
  295.       float px, py;
  296.       float adj, opp;    
  297.       if (op.x > mp.x) {
  298.         opp = op.x - mp.x;
  299.       } 
  300.       else {
  301.         opp = mp.x - op.x;
  302.       }
  303.       if (op.y > mp.y) {
  304.         adj = op.y - mp.y;
  305.       } 
  306.       else {
  307.         adj = mp.y - op.y;
  308.       }
  309.       if (op.x > mp.x) {  
  310.         px = mp.x + (opp * _strutFactor);
  311.       } 
  312.       else {
  313.         px = mp.x - (opp * _strutFactor);
  314.       }
  315.       if (op.y > mp.y) {
  316.         py = mp.y + (adj * _strutFactor);
  317.       } 
  318.       else {
  319.         py = mp.y - (adj * _strutFactor);
  320.       }  
  321.       return new PointObj(px, py);
  322.     }


  323.     void drawMe() {
  324.       //pushMatrix();
  325.       //translate(-screen.width, -screen.height);
  326.       strokeWeight(0.2); 
  327.       fill(0, 100, 200);   
  328.       //strokeWeight(2 - level);      
  329.       // draw outer shape
  330.       for (int i = 0; i < outerPoints.length; i++) {
  331.         int nexti = i+1;
  332.         if (nexti == outerPoints.length) { 
  333.           nexti = 0;
  334.         }
  335.         line(outerPoints[i].x, outerPoints[i].y, outerPoints[nexti].x, outerPoints[nexti].y);
  336.       }
  337.       // draw midpoints
  338.       strokeWeight(0.2);
  339.       for (int j = 0; j < midPoints.length; j++) {
  340.         //fill(250, 250, 0);   
  341.         fill(#FFF536);   
  342.         //stroke(100);   
  343.         stroke(#0064C8);   
  344.         ellipse(midPoints[j].x, midPoints[j].y, 3, 3);
  345.         line(midPoints[j].x, midPoints[j].y, projPoints[j].x, projPoints[j].y);
  346.         ellipse(projPoints[j].x, projPoints[j].y, 2, 2);
  347.       }

  348.       for (int k = 0; k < myBranches.length; k++) {
  349.         myBranches[k].drawMe();
  350.       }
  351.       //popMatrix();
  352.     }
  353.   }
  354. }

  355. // *********** 006 NEW TAB - TextRing *************

  356. // A string of text running around a circle

  357. class TextRing {                      

  358.   //VARIABLES
  359.   PFont f;
  360.   float r = 348;    // Radius of the circle - SET between 310 and 360
  361.   String message = "I think we all misunderstood Tesla. We thought he was a dreamer and a visionary. He did dream but his dreams came true. He did have visions but they were of a real future. I think we all misunderstood Tesla.  We thought he was a dreamer and a visionary.  He did dream but his dreams came true." ;
  362.   
  363.   //CONSTRUCTOR
  364.   TextRing() {
  365.     //f = createFont("CourierNewPSMT-20", 30);
  366.     //textFont(f, 24);

  367.     f = createFont("Univers", 14, true);
  368.     textFont(f);

  369.     smooth();
  370.   }

  371.   //FUNCTION
  372.   void write() {
  373.     
  374.     float arclength = 0;                            // Keep track of our position along the curve

  375.     for (int i = 0; i < message.length(); i++) {    // For every box

  376.       char currentChar = message.charAt(i);         // Instead of a constant width, we check the width of each character.
  377.       float w = textWidth(currentChar);
  378.       arclength += w/2;                             // Each box is centered so we move half the width
  379.       float theta = PI + arclength / r;             // Angle in radians is the arclength divided by the radius. Starting on the left side of the circle by adding PI

  380.       pushMatrix();      
  381.       textAlign(CENTER);                            // NB!! Keep it inside Matrix to prevent affecting SunRing! 
  382.       translate(r*cos(theta), r*sin(theta));        // Polar to cartesian coordinate conversion
  383.       rotate(theta+PI/2);                           // Rotate the box - rotation is offset by 90 degrees

  384.       fill(#890409);                                // Display the character
  385.       text(currentChar, 0, 0);

  386.       arclength += w/2;                             // Move halfway again
  387.       popMatrix();
  388.       }
  389.   }
  390.   
  391. }

Replies(4)


PS Not sure... Is it better to post code in this format on these forums?

Copy code
  1. // A sunburst of text and numbers arranged around the edge of a circle. These are the names and nos of a selection of Tesla's PATENTS
    
    class SunRing {                      
    
      //VARIABLES
      PFont font;
    
      String[] names = { 
        "Energy", "174544", "Wireless", "433700", "Generator", "135174", "Electrical", "685958", "Dynamo", "335787", "Current", "593138", "Controller", 
        "447920", "Frequency", "609249", "Alternating", "787412", "Transmit", "186799", "Lighting", "335786", "Oscillate", "723188", "Arc lamp", "512340", 
        "Radiant", "390413", "Turbine", "487796", "Machine", "577670", "Power", "649621", "Rotary", "725605"
      };
      
      float delta = TWO_PI / names.length; 
      int radius = 370; 
    
      //CONSTRUCTOR
      SunRing() {
        font = createFont("CourierNewPSMT-20", 20);          // Create font
        textFont(font, 15.2);
      }
    
      //FUNCTION
      void shine() {
        fill(120); 
    
        for (int i = 0; i<names.length; i++) {                // 36 items in the list (360 degrees/10 = 36)
    
                                       ///////////////////////////////////////////////////////////
          textAlign(LEFT);             ///   *** VERY NB for correct inner alignment!!! ***    ///
                                       ///////////////////////////////////////////////////////////
    
          float xPos = width/2+radius * cos(i* delta); 
          float yPos = height/2+radius * sin(i* delta);
    
          pushMatrix();                                      // ADD words/nos and arrange them around the circle
          translate(xPos, yPos);
          rotate(delta * i);
          text(names[i], 10, 0); 
          popMatrix();
        }
      }
    }
    
    
My advice is to identify which parts of your drawing is static and which 1s are dynamic.
The raster happening inside the big yellow circle and many outward lines and little dot stars are dynamic.
All of the text and the sun disk are rendered at the same place over & over. So they're static.

What you can do is within setup(), render all static drawings in a separate PGraphics.
Once done, extract a PImage from it using get() method.
Now, inside draw(), use that PImage as background()!

Another tidbit:
Before any loops, pre-calculate everything you can and also, prepare any graphics setup
which won't keep changing inside that loop.

For example, there's no need to keep telling Processing that textAlign() is LEFT or CENTER inside a loop.
It's redundant and unnecessarily slows down your program. Just set it once before starting that loop! 
Great stuff, thanks GoTo - just the kind of tips for improvement that I was hoping for!

Will try all of that - was actually already busy reading up on PGraphics and PImage...

Also stumbled on some of the info about the changes in ver2.x  involving OpenGL and P2D - but not clear about the implications. Could that be affecting performance with a project like this?

Thanks again for the advice!

The original software-based P2D & P3D rendering modes from v1.5.1- are no more!
Now from v2+ versions on, they're emulated through OPENGL v3+ I think.
Also, v1.5.1- used an old OPENGL version.
The only common mode still in place is the default renderer -> JAVA2D