Speed Issue - Optimizing frame-rate when playing complex animations
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.)
- // *********** 001 MAIN Tab *************
- //////////////////////////////////////////////////////
- ///// Tesla Toy /////
- //////////////////////////////////////////////////////
- SutcliffePentagon mySutcliffePentagon1;
- PolarPetal myPolarPetal1; // DECLARE objects
- Planet myPlanet1;
- TextRing myTextRing1;
- SunRing mySunRing1;
- //////////////////////////////////////////////////////
- // *** SETUP ***
- //////////////////////////////////////////////////////
- void setup() {
- size(displayWidth, displayHeight);
- smooth();
- frameRate(60);
- /////////////////////////////////////////////////////////////////////
- // Initialize objects by calling the constructor function /////////
- /////////////////////////////////////////////////////////////////////
- mySutcliffePentagon1 = new SutcliffePentagon();
- myTextRing1 = new TextRing();
- myPolarPetal1 = new PolarPetal();
- myPlanet1 = new Planet();
- mySunRing1 = new SunRing();
- }
- //////////////////////////////////////////////////////
- // *** DRAW ***
- //////////////////////////////////////////////////////
- void draw() {
- background(10);
- mySutcliffePentagon1.pulse();
- mySunRing1.shine(); // Call methods on the objects
- myPolarPetal1.bloom();
- myPlanet1.orbit();
- myTextRing1.write();
- pushMatrix();
- ellipseMode(CENTER);
- noFill();
- strokeWeight(10);
- stroke(#E4EA40);
- ellipse(0,0,630,630);
- strokeWeight(14);
- stroke(#333333);
- ellipse(0,0,606,606);
- popMatrix();
- }
- // *********** 002 NEW Tab - Planet *************
- // Based on Shiffman: Convert a polar coordinate (r,theta) to cartesian (x,y):
- // * x = r * cos(theta)
- // * y = r * sin(theta)
- class Planet {
- //VARIABLES
- float theta; // Angle and angular velocity, accleration
- float theta_vel;
- float theta_acc;
- float r;
- //CONSTRUCTOR
- Planet() { //Initialize all values
- r = 333;
- theta = 0;
- theta_vel = 0;
- theta_acc = 0.001;
- }
- void orbit() {
- translate(width/2, height/2); // Translate the origin point to the center of the screen
- float x = r * cos(theta); // Convert polar to cartesian
- float y = r * sin(theta);
- ellipseMode(CENTER); // Draw the ellipse at the cartesian coordinate
- noStroke();
- fill(#0064C8);
- ellipse(x, y, 14, 14);
- theta_vel += theta_acc; // Apply acceleration and velocity to angle (r remains static in this example)
- theta += theta_vel;
- }
- }
- // *********** 003 NEW TAB - PolarPetal *************
- // Class Declaration for PolarPetals. Tweak of brilliant algorithm by Chris B Stones
- class PolarPetal {
- //VARIABLES
- int H, K;
- int N; // Total Steps
- float phi; // shift
- float w; // angular
- color c;
- float A, a; // Amplitude function
- float x, y;
- float phi_delta; // how fast to rotate the thing
- float w_delta; // how many petals??
- //CONSTRUCTOR
- PolarPetal() {
- H = width/2;
- K = height/2;
- N = 600;
- phi = 0.0f;
- w = 0.0f;
- c = color(#0064C8);
- }
- void bloom() { // Draw points function
- fill(c); // Color of PolarPetal
- noStroke();
- for ( int i = 0; i < N; i++ ) {
- a = 275*sin(w_delta*i+phi); // Polar Equation. y = A sin(theta)
- x = a*cos(i) + H; // convert polar to rect coordinates - x value
- y = a*sin(i) + K; // convert polar to rect coordinates - y value
- ellipse(x, y, 5, 5); // plot points
- }
- w_delta = ((float)mouseX)/width;
- phi_delta = ((float)mouseY)/height;
- // rotation is done with shifting
- phi = phi + phi_delta;
- w = w + w_delta;
- }
- } // END entire CLASS
- // *********** 004 NEW Tab - SunRing *************
- // 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();
- }
- }
- }
- // *********** 005 NEW Tab - SutcliffePentagon *************
- // Class Declaration for SutcliffePentagon (with 8 sides) Modified from Matt Pearson
- class SutcliffePentagon {
- //VARIABLES
- FractalRoot pentagon;
- float _strutFactor = 0.3;
- float _strutNoise;
- int _maxlevels = 2;
- int _numSides = 8;
- //CONSTRUCTOR
- SutcliffePentagon() {
- size(displayWidth, displayHeight);
- smooth();
- _strutNoise = random(12);
- }
- //////////////////////////////////////////////////////
- ///// Methods ////
- //////////////////////////////////////////////////////
- void pulse() {
- //background(0);
- frameRate(50);
- _strutNoise += 0.02;
- //_strutFactor = (noise(_strutNoise) * 3) - 1; //Alternative values
- _strutFactor = noise(_strutNoise) * 3; //Constrains outward movement - Set somwehere between 1.5 - 3.0
- pentagon = new FractalRoot(5);
- //pentagon = new FractalRoot(frameCount*2.5);
- //pushMatrix();
- pentagon.drawShape();
- //pushMatrix();
- }
- ////////////////////////////////////////////////////////////
- //Point object
- class PointObj {
- float x, y;
- PointObj(float ex, float why) {
- x = ex;
- y = why;
- }
- }
- //FractalRoot object - Multi-sided Version
- class FractalRoot {
- PointObj[] pointArr = {
- };
- Branch rootBranch;
- FractalRoot(float startAngle) {
- pushMatrix();
- float centX = width/2;
- float centY = height/2;
- float angleStep = 180.0f/_numSides; // *** The degrees here control inward movement
- for (float i = 0; i<360; i+=angleStep) {
- float x = centX + (500 * cos(radians(startAngle + i)));
- float y = centY + (500 * sin(radians(startAngle + i)));
- pointArr = (PointObj[])append(pointArr, new PointObj(x, y));
- }
- rootBranch = new Branch(0, 0, pointArr);
- popMatrix();
- }
- void drawShape() {
- fill(0, 100, 200);
- rootBranch.drawMe();
- }
- }
- //Branch object
- class Branch {
- int level, num;
- PointObj[] outerPoints = {
- };
- PointObj[] midPoints = {
- };
- PointObj[] projPoints = {
- };
- Branch[] myBranches = {
- };
- Branch(int lev, int n, PointObj[] points) {
- level = lev;
- num = n;
- outerPoints = points;
- midPoints = calcMidPoints();
- projPoints = calcStrutPoints();
- if ((level+1) < _maxlevels) {
- Branch childBranch = new Branch(level+1, 0, projPoints);
- myBranches = (Branch[])append(myBranches, childBranch);
- for (int k = 0; k < outerPoints.length; k++) {
- int nextk = k-1;
- if (nextk < 0) {
- nextk += outerPoints.length;
- }
- PointObj[] newPoints = {
- projPoints[k], midPoints[k], outerPoints[k], midPoints[nextk], projPoints[nextk]
- };
- childBranch = new Branch(level+1, k+1, newPoints);
- myBranches = (Branch[])append(myBranches, childBranch);
- }
- }
- }
- PointObj[] calcMidPoints() {
- PointObj[] mpArray = new PointObj[outerPoints.length];
- for (int i = 0; i < outerPoints.length; i++) {
- int nexti = i+1;
- if (nexti == outerPoints.length) {
- nexti = 0;
- }
- PointObj thisMP = calcMidPoint(outerPoints[i],
- outerPoints[nexti]);
- mpArray[i] = thisMP;
- }
- return mpArray;
- }
- PointObj calcMidPoint(PointObj end1, PointObj end2) {
- float mx, my;
- if (end1.x > end2.x) {
- mx = end2.x + ((end1.x - end2.x)/2);
- }
- else {
- mx = end1.x + ((end2.x - end1.x)/2);
- }
- if (end1.y > end2.y) {
- my = end2.y + ((end1.y - end2.y)/2);
- }
- else {
- my = end1.y + ((end2.y - end1.y)/2);
- }
- return new PointObj(mx, my);
- }
- PointObj[] calcStrutPoints() {
- PointObj[] strutArray = new PointObj[midPoints.length];
- for (int i = 0; i < midPoints.length; i++) {
- int nexti = i+3;
- if (nexti >= midPoints.length) {
- nexti -= midPoints.length;
- }
- PointObj thisSP = calcProjPoint(midPoints[i], outerPoints[nexti]);
- strutArray[i] = thisSP;
- }
- return strutArray;
- }
- PointObj calcProjPoint(PointObj mp, PointObj op) {
- float px, py;
- float adj, opp;
- if (op.x > mp.x) {
- opp = op.x - mp.x;
- }
- else {
- opp = mp.x - op.x;
- }
- if (op.y > mp.y) {
- adj = op.y - mp.y;
- }
- else {
- adj = mp.y - op.y;
- }
- if (op.x > mp.x) {
- px = mp.x + (opp * _strutFactor);
- }
- else {
- px = mp.x - (opp * _strutFactor);
- }
- if (op.y > mp.y) {
- py = mp.y + (adj * _strutFactor);
- }
- else {
- py = mp.y - (adj * _strutFactor);
- }
- return new PointObj(px, py);
- }
- void drawMe() {
- //pushMatrix();
- //translate(-screen.width, -screen.height);
- strokeWeight(0.2);
- fill(0, 100, 200);
- //strokeWeight(2 - level);
- // draw outer shape
- for (int i = 0; i < outerPoints.length; i++) {
- int nexti = i+1;
- if (nexti == outerPoints.length) {
- nexti = 0;
- }
- line(outerPoints[i].x, outerPoints[i].y, outerPoints[nexti].x, outerPoints[nexti].y);
- }
- // draw midpoints
- strokeWeight(0.2);
- for (int j = 0; j < midPoints.length; j++) {
- //fill(250, 250, 0);
- fill(#FFF536);
- //stroke(100);
- stroke(#0064C8);
- ellipse(midPoints[j].x, midPoints[j].y, 3, 3);
- line(midPoints[j].x, midPoints[j].y, projPoints[j].x, projPoints[j].y);
- ellipse(projPoints[j].x, projPoints[j].y, 2, 2);
- }
- for (int k = 0; k < myBranches.length; k++) {
- myBranches[k].drawMe();
- }
- //popMatrix();
- }
- }
- }
- // *********** 006 NEW TAB - TextRing *************
- // A string of text running around a circle
- class TextRing {
- //VARIABLES
- PFont f;
- float r = 348; // Radius of the circle - SET between 310 and 360
- 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." ;
- //CONSTRUCTOR
- TextRing() {
- //f = createFont("CourierNewPSMT-20", 30);
- //textFont(f, 24);
- f = createFont("Univers", 14, true);
- textFont(f);
- smooth();
- }
- //FUNCTION
- void write() {
- float arclength = 0; // Keep track of our position along the curve
- for (int i = 0; i < message.length(); i++) { // For every box
- char currentChar = message.charAt(i); // Instead of a constant width, we check the width of each character.
- float w = textWidth(currentChar);
- arclength += w/2; // Each box is centered so we move half the width
- 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
- pushMatrix();
- textAlign(CENTER); // NB!! Keep it inside Matrix to prevent affecting SunRing!
- translate(r*cos(theta), r*sin(theta)); // Polar to cartesian coordinate conversion
- rotate(theta+PI/2); // Rotate the box - rotation is offset by 90 degrees
- fill(#890409); // Display the character
- text(currentChar, 0, 0);
- arclength += w/2; // Move halfway again
- popMatrix();
- }
- }
- }