stevecrayons@gmail.comwww.stevecrayons.com
The showcase player uses a modified version of Processing.js in combination with jsweet to let students program their apps in Java code while still allowing for browser support.
Content created by students is scaled to fit the showcase frame while maintaining aspect ratio and cursor position. This is why some projects may appear blurry in fullscreen, or why some small details may not be visible on a small screen
<iframe width='500px' height='400px' src='https://nest.ktbyte.com/nest#81171' allowfullscreen></iframe>
// Pendulum Wave Effect // by Steve Kranz, 2015 // stevecrayons@gmail.com // www.stevecrayons.com // // Licensed under Creative Commons 0 Universal 1.0 -- Public Domain. Feel free to do whatever with this code. If you do use it, I'd love to see what you did. Send me a note at stevecrayons@gmail.com float[] frequencies; // Holds the frequencies of each pendulum PVector[] positions; // Holds the position of the balls that simulate the pendulum bobs float amplitude; // How far left and right the balls move float freqMult = 0.001; // This frequency multiplier makes the system complete a whole period in 1000 frames. int totalPen; // The total number of pendulums/balls boolean total20 = true; // Are there 20 balls? If false, there are 40. boolean prevTotal20 = true; // Is the value of total20 on the previous frame 20? This is for switching between 20 and 40 pendulums. float xPos, yPos; // x- and y-positions of the pendulums float xCenter; // For alignig the systems of pendulums in the x-direction float boxHeight; // The y-height of the invisible box that the balls exist in; for display purposes. float two_pi = 6.283185307; // Drawing options boolean draw1 = true; boolean draw2 = true; boolean draw3 = true; boolean drawBalls = true; boolean alignSin = true; // TIME float timeValue=0; float timeSpeed=1; boolean debugOn = false; // BUTTONS int clickCount = 0; // so a button is activated only once per click Button ballButton, numBallsButton, line1Button, line2Button, line3Button, labelButton; Button pauseButton, speedButton; void setup(){ size(720, 720); frameRate(30); amplitude = width/8; setupPendulum(); setupButtons(); } void draw(){ background(40,40,45); // Buttons... displayButtons(); displayButtonLabels(); displayTitle(); displayPhaseCircle(); if(debugOn){ debug(); } calculatePositions(); if(labelButton.state){ drawGuides(); drawLabels(); } if(line3Button.state){ drawLine(3, color(230, 180, 70)); } if(line2Button.state){ drawLine(2, color(230, 115, 70)); } if(line1Button.state){ drawLine(1, color(230, 70, 65)); } setBobColor(); if(ballButton.state){ // This loop draws the 'bobs'. for (int i = 0; i<totalPen; i++){ ellipse(positions[i].x, positions[i].y, 18, 18); } } totalPenChange(); incrementTime(); /* if(frameCount % 2 == 0){ saveFrame("save2color/####.png"); }*/ }// end draw class Button { int x, y, w, h; String label, shortcut; String onText, offText; color onColorFore, offColorFore; color onColorBack, offColorBack; boolean state; float labelWidth; Button(int x_, int y_, int w_, int h_, String label_, String shortcut_){ x = x_; y = y_; w = w_; h = h_; label = label_; shortcut = shortcut_; state = true; textSize(16); labelWidth = textWidth(label); onColorFore = color(255, 200); onColorBack = color(255, 100); offColorFore = color(0, 230); offColorBack = color(100, 180); onText = "on"; offText = "off"; } void update(){ if(clickCount == 0){ if(over()){ if(mousePressed==true){ state =! state; clickCount++; } } } display(); } void display(){ textAlign(CENTER, CENTER); textSize(16); if(state==true){ noStroke(); fill(onColorBack); rect(x,y,w,h); fill(onColorFore); text(onText, x+w/2, y+h/2); } else { noStroke(); fill(offColorBack); rect(x,y,w,h); fill(offColorFore); text(offText, x+w/2, y+h/2); } textAlign(LEFT, CENTER); if(state){ fill(255, 160); } else { fill(255, 80); } text(label, x+w+6, y + h/2); textAlign(CENTER, CENTER); text(shortcut, x-28, y + h/2); } boolean over() { if (mouseX >= x -33 && mouseX <= x + w + 6 + labelWidth && mouseY >= y && mouseY <= y + h) { return true; } else { return false; } } } void setBobColor(){ // stroke(10); noStroke(); fill(255, 255); strokeWeight(1); } // This function draws lines that connect the pendulums void drawLine(int n, color c){ strokeWeight(4); stroke(c); for(int i=0; i<totalPen-n; i++){ line(positions[i].x, positions[i].y, positions[i+n].x, positions[i+n].y); } } // This function allows time to progress at a slow or fast rate, or pause void incrementTime(){ if(pauseButton.state==false){ if(speedButton.state==false){ timeValue += timeSpeed; } else{ timeValue += timeSpeed*2; } } } // This function sets up the frequency and position arrays for the pendulums void setupPendulum(){ xCenter = 6*width/10 + 85; boxHeight = height-25; if(total20){ totalPen = 20; freqMult = 0.001; } else { totalPen = 40; freqMult = 0.0005; } frequencies = new float[totalPen]; positions = new PVector[totalPen]; for (int i = 0; i< totalPen; i++){ frequencies[i] = (i+1) * freqMult; } } // Has the state of the "Number of pendulums" button changed? If so, run the setupPendulum() function void totalPenChange(){ total20 = numBallsButton.state; if(total20 == prevTotal20){ setupPendulum(); } prevTotal20 = total20; } // Draw the horizontal lines underneath each of the pendulums void drawGuides(){ strokeWeight(1); stroke(255, 30); for(int i = 0; i<totalPen; i++){ line(xCenter-amplitude, positions[i].y, xCenter+amplitude, positions[i].y); } } // Draw the text labels above and two the right of the pendulums void drawLabels(){ textSize(12); fill(255, 70); for(int i = 0; i<totalPen; i++){ textAlign(CENTER, CENTER); text(i+1, xCenter+amplitude + 27, positions[i].y); } int yOffset; if(total20){ yOffset = 20; } else{ yOffset = 15; } text("freq.", xCenter+amplitude + 27, yOffset); text("pendulum", xCenter, yOffset); } // Draw the title information in the upper-righthand corner void displayTitle(){ int x = 110; int y = 40; textSize(30); textAlign(LEFT, TOP); fill(255, 255); textLeading(32); text("Pendulum\nWave Effect", x, y); fill(255, 0); text("Pendulum\nWave Effect", x+1, y); fill(255, 127); textSize(14); text("by Steve Kranz, 2015", x, y+67); textSize(12); text("stevecrayons@gmail.com",x, y+83 ); } // setup the buttons used to control the simulation. void setupButtons(){ int x = 110; int y = 210; int w = 40; int h = 30; int yGap = 32; int drawGap = 0; ballButton = new Button(x,y+yGap*0+drawGap, w, h, "pendulums", "0"); numBallsButton = new Button(x, y+yGap*4+8, w, h, "number of pendulums", "N"); numBallsButton.onText = "20"; numBallsButton.offText = "40"; line1Button = new Button(x, y+yGap*1+drawGap, w, h, "line 1", "1"); line2Button = new Button(x, y+yGap*2+drawGap, w, h, "line 2", "2"); line3Button = new Button(x, y+yGap*3+drawGap, w, h, "line 3", "3"); labelButton = new Button(x, y+yGap*5+drawGap + 8, w, h, "labels", "L"); // labelButton.state = false; pauseButton = new Button(x, y+yGap*6 + 70, w, h, "pause", "P"); pauseButton.state = false; speedButton = new Button(x, y+yGap*7 + 70, w, h, "speed", "S"); speedButton.onText = "fast"; speedButton.offText = "slow"; speedButton.state = false; } // Draw the labels that annotate the buttons void displayButtonLabels(){ // x and y should match x and y in setupButtons(); int x = 110; int y = 210; textSize(16); fill(255, 255); textAlign(LEFT, TOP); text("Drawing options", x, y-30); text("Timing options", x, y+232); textAlign(CENTER, TOP); textSize(12); textLeading(11); fill(255, 100); text("short\ncut", x-30, y-32); stroke(255, 70); strokeWeight(1); line(x, y-9, x+220, y-9 ); line(x, y+253, x+130, y+253); } void displayButtons(){ ballButton.update(); numBallsButton.update(); line1Button.update(); line2Button.update(); line3Button.update(); labelButton.update(); pauseButton.update(); speedButton.update(); } // This circle simply displays the phase of the system. When the indictor line makes a complete revolution, the pendulum-system repeats itself. void displayPhaseCircle(){ float x = 163; float y = 640; float d = 60; float angle = timeValue*freqMult*two_pi - two_pi/4; textSize(16); textAlign(LEFT, CENTER); fill(255,255); text("Phase", 110, y - d/2 -35); // text("phase", x, y-12); stroke(255, 70); strokeWeight(1); line(110, y - d/2 - 24, 110+105, y - d/2 - 24); strokeWeight(1); stroke(255, 100); fill(255, 30); ellipseMode(CENTER); ellipse(x, y, d, d); // Tick marks strokeWeight(1); stroke(255, 100); float tick = 0.8; line(x, y-tick*d/2, x, y-d/2); // zero or two-pi (top of circle); line(x + tick*d/2, y, x+d/2, y); // pi/2 line(x, y+tick*d/2, x, y+d/2); // pi line(x- tick*d/2, y, x-d/2, y); // 3pi/2 textSize(12); fill(255, 150); textAlign(CENTER, CENTER); text("0", x+1, y -d/2 -10); // text("π/2", x+d/2 + 15, y-2); text("π", x, y + d/2 + 8); //text("3π/2", x-d/2-17, y-2); // 3pi/2 on two lines so it looks nicer text("3π", x-d/2-14, y-8); text("2", x-d/2-14, y+6); stroke(255, 100); strokeWeight(1); line(x-d/2-22, y, x-d/2-6, y); // pi/2 on two lines so it looks nicer text("π", x+d/2+12, y-8); text("2", x+d/2+12, y+6); stroke(255, 100); strokeWeight(1); line(x+d/2+18, y, x+d/2+6, y); // Indicating line...the vertical spacing will depend on how the font is rendered...can be iffy. strokeWeight(2); stroke(255, 200); line(x, y, x+(d/2)*cos(angle), y+(d/2)*sin(angle)); } void debug(){ fill(255); textSize(12); textAlign(LEFT); text("FPS: "+nf(frameRate, 2, 1), 12, 15); text("frame number: "+frameCount, 12, 27); text("time value: "+nfc(timeValue,1), 12, 39); text("Hit 'D' to hide.", 12, 51); } void calculatePositions(){ // This loop calculates the position of all the 'bobs' (or "balls" or "pendulums") for this current frame for (int i = 0; i< totalPen; i++){ yPos = (i+1) * ((float)boxHeight/(totalPen+1)) + 25; if(alignSin==true){ xPos = xCenter + amplitude * sin(frequencies[i]*timeValue*two_pi); } else { xPos = xCenter + amplitude * cos(frequencies[i]*timeValue*two_pi); } positions[i] = new PVector(xPos, yPos); } } void keyPressed(){ if (key=='1'){ line1Button.state =! line1Button.state; } if (key =='2'){ line2Button.state =! line2Button.state; } if (key =='3'){ line3Button.state =! line3Button.state; } if (key =='`'|| key=='0'){ ballButton.state =! ballButton.state; } if (key ==' ' || key == 'p' || key == 'P'){ //paused =! paused; pauseButton.state =! pauseButton.state; } if (key == 's' || key=='S'){ //timeSlow =! timeSlow; speedButton.state =! speedButton.state; } if (key =='n' || key == 'N'){ numBallsButton.state =! numBallsButton.state; } if (key == 'l' || key == 'L'){ labelButton.state =! labelButton.state; } if(key == 'd' || key == 'D'){ debugOn =! debugOn; } if(key =='r' || key == 'R'){ saveFrame(); } if(key == 'q'|| key == 'Q'){ alignSin =! alignSin; } } void mouseReleased(){ clickCount = 0; }