/** *

Esfera. David Pena.

* *

Distribucion aleatoria uniforme sobre la superficie de una esfera.

* *

Modifications by subpixel (v4).
* subpixels.com

* *

Keys:

* */ /* * Modifications by subpixel: * v1: * removal of unused global arrays; * calculate end point of a hair as the hairLength distance from the hair base, not as a point at a fixed distance from the centre of the sphere (which causes hairs to stretch); * reverse Ymouse reactivity so ball spins in the direction of mouse movement instead of against it; * added start/pause functionality on mouse click event * v2: 2008-11-09 * renamed/repurposed some identifiers * Globals: cuantos -> NUM_HAIRS, lista[] -> HAIRS[], radio -> BASE_RADIUS, * hairmin/max -> MIN/MAX_HAIR_LENGTH, rx/ry -> RX/RY * class pelo -> class Hair * dibujar() -> draw() * new class Oscillator * Oscillator introduced for the radius of the inner sphere, the base of the "outer sphere" (the hairs), * and also the number of hairs displayed. * The inner sphere is now red so you can see it * Moving the mouse increases the "heart rate" of the inner sphere (which gradually slows down) * Moving the mouse also increases the base radius of the outer sphere based on the speed * v3: 2008-11-17 * Extracted class Hair and class Oscillator to separate files. * Removed redundant FRAME variable (can use global frameCount if necessary) * Added some keyboard controls. * [up]/[down] control outer sphere osciallation step * [z] toggles pause for inner sphere oscillator * [x] toggles pause for outer sphere oscillator * [c] toggles pause for hair count osciallator * Added some 3D text * Added some snaking tentacles; three new osciallators control parameters. * v4: 2008-11-24 * MASSIVE CHANGES! * Move references to outerOscillator.value and millis() out of Hair class * Oscillators now based around a period instead of a phase step. * Oscillator.tick() now requires a parameter for the number of milliseconds that have passed * 8 "Hello" petals replaced by details of available oscillators. * Concept of a "selected" oscillator, which can be manipulated by keyboard input: * Selected oscillator details shown at top-left of window * Selected oscillator hilighted a different coloured "petal" around the inner sphere * [LEFT]/[RIGHT] to select previous/next oscillator * [z],[x],..[m] to directly select an oscillator * [UP]/[DOWN] to increase/decrese the oscillation period * [1]..[9] to directly set the period to 1..9 seconds * [0] to pause * [SPACE] to toggle pause/unpause * [BACKSPACE] to toggle backwards/forwards direction */ import processing.opengl.*; // ------------------------------------------------------------ // Global variables // ------------------------------------------------------------ boolean looping = false; // Opening state of the sketch. Set true to open un-paused. ArrayList oscillators = new ArrayList(); int clockMillis = 0; // Milliseconds since start of applet to start of current frame. int clockPrevMillis = 0; // Milliseconds since start of applet to start of previous frame. int runMillis = 0; // Milliseconds we have been running. int FRAME_RATE = 30; int NUM_HAIRS = 800; // Number of hairs Hair[] HAIRS; // Array/list of hairs float BASE_RADIUS; // Radius of the sphere float MIN_HAIR_LENGTH; // Minimum hair length float MAX_HAIR_LENGTH; // Maximum hair length float RX = 0; float RY = 0; float agitation; float maxInnerPeriod; // Slowest sphere will pulsate float minInnerPeriod; // Fastest sphere will pulsate Oscillator innerOscillator; // Oscillator for inner sphere Oscillator outerOscillator; // Oscillator for outer sphere (base of hairs) Oscillator countOscillator; // Oscillator for number of hairs to display Oscillator tentacleCurl; // Oscillator for the tentacle curl amount Oscillator tentacleTwist; // Oscillator for the tentacle twist amount Oscillator tentacleScale; // Oscillator for the scaling of each tentacle. <1 grow smalled, >1 grow larger Oscillator spin; // Phase ssed for rotation of the petals/tentacles Oscillator selectedOscillator; // Allow modification of the selected oscillator int selectedOscillatorIndex; PFont font; void init() { // frame.setUndecorated(true); super.init(); } // ------------------------------------------------------------ // Main setup method. // ------------------------------------------------------------ void setup() { size(640, 480, OPENGL); // Set the canvas size, and use OpenGL rendering frameRate(FRAME_RATE); background(0); if (!looping) noLoop(); // Opportunity to open the sketch in a paused state BASE_RADIUS = height/4; // The central sphere is half the window height (radius therefore 1/4 window height) MIN_HAIR_LENGTH = BASE_RADIUS * 0.1; // Minimum hair length set proportionally to the sphere radius MAX_HAIR_LENGTH = BASE_RADIUS * 0.4; // Maximum hair length ser proportionally to the sphere radius HAIRS = new Hair[NUM_HAIRS]; // Initialise the array for the number of hairs for (int i=0; i < NUM_HAIRS; i++) // For each hair... { float phi = random(TWO_PI); // Any angle in 360 degrees float theta = asin(random(-1, 1)); float hairLength = random(MIN_HAIR_LENGTH, MAX_HAIR_LENGTH); HAIRS[i] = new Hair(phi, theta, hairLength); // Create a new hair object and save it in the array } maxInnerPeriod = 3; minInnerPeriod = 0.5; float b = BASE_RADIUS; oscillators.add(innerOscillator = new Oscillator("inner", maxInnerPeriod, 0.2 * b, 0.9 * b)); oscillators.add(outerOscillator = new Oscillator("outer", 4, 0.2 * b, 1.2 * b)); oscillators.add(countOscillator = new Oscillator("haircount", 5, 0.5 * b, 0.5 * b)); oscillators.add(tentacleCurl = new Oscillator("curl", 6, PI/12)); oscillators.add(tentacleTwist = new Oscillator("twist", 7, PI/12, 0, PI/4)); oscillators.add(tentacleScale = new Oscillator("scale", 9, 0.15, 0.9, -TWO_PI/3)); oscillators.add(spin = new Oscillator("spin", 8)); // Since there are 8 petals/tentacles // Start with the first oscillator selected. selectOscillator(0); noiseDetail(3); font = loadFont("BroadwayBT-Regular-48.vlw"); textFont(font); clockMillis = millis(); // Ignore any time passed up til now. } // ------------------------------------------------------------ // Main draw method. // ------------------------------------------------------------ void draw() { clockPrevMillis = clockMillis; clockMillis = millis(); int millisSinceLastFrame = clockMillis - clockPrevMillis; runMillis += millisSinceLastFrame; int numOsc = oscillators.size(); // Update all of the oscillators if (frameCount > 1) for (int i = 0; i < numOsc; i++) ((Oscillator)(oscillators.get(i))).tick(millisSinceLastFrame); // Clear the display. Black background. background(0); // Display information about the selected oscillator fill(200, 200, 200); textAlign(LEFT); String message = looping ? selectedOscillator.str() : "Click mouse to start"; text(message, 10, 48, 0); // Centre the coordinate system translate(width/2, height/2, -width/4); // Position the coordinate origin at the centre of the window // Ease towards current mouse "position" (orientation determined by mouse position)... "step 10% of the current difference each frame float rxp = ((mouseX-(width/2))*0.005); float ryp = (((height/2)-mouseY)*0.005); float rotDist = dist(rxp, ryp, RX, RY); RX = (RX*0.9)+(rxp*0.1); RY = (RY*0.9)+(ryp*0.1); rotateY(RX); rotateX(RY); // Mouse movement causes the inner sphere to be "agitated"... faster heartbeat float mouseMoveDist = dist(mouseX, mouseY, pmouseX, pmouseY); agitation = (agitation + mouseMoveDist * 0.1) * 0.97; innerOscillator.setPeriod( minInnerPeriod + (maxInnerPeriod - minInnerPeriod) / ( 1 + agitation ) ); // Inner sphere fill(64, 0, 0); // 25% Red... noStroke(); // No outline... sphere( innerOscillator.value() ); // Draw the sphere (to occlude hairs on the side away from the viewpoint) // Outer sphere (hairs) int displayHairs = round(countOscillator.value()); float baseRadius = outerOscillator.value() + rotDist * 40; for (int i=0; i < displayHairs; i++) // For each hair... { HAIRS[i].draw( baseRadius, runMillis ); // Calculate and draw the hair } // Petals/tentacles for (int i = 0; i < numOsc; i++) { float angle = TWO_PI * i / numOsc; pushMatrix(); // Remember base matrix of the sphere so we can come back for the next text/tentacle // Add text information about the indexed oscillator sticking out from the edge of the sphere. // Tilt it towards the "front" a bit so it is kind of like a flower. pushMatrix(); // Remember state so we can go back and insert a tentacle rotateZ(angle + spin.phase); translate(innerOscillator.value(), 0, 0); rotateY(-PI/6); fill(0, 102, 153); if (i == selectedOscillatorIndex) fill (220, 220, 0); textAlign(LEFT); message = ((Oscillator)(oscillators.get(i))).str(); text(message, 0, 15, 0); popMatrix(); // Add a tentacle made up of a sequence of relatively sized/positioned/oriented boxes rotateZ(angle - spin.phase); translate(innerOscillator.value(), 0, 0); float opacity = 200; for (int j = 0; j < 15; j++) { translate(20, 0, 0); // Move along a bit rotateZ(tentacleCurl.value()); // Curl around a bit rotateX(tentacleTwist.value()); // Twist a bit scale(tentacleScale.value()); // Scale each segment relative to the last (may be bigger or smaller depending on oscialltor) fill(200, 200, 0, opacity); // Colour is yellowish, alpha set to "opacity" noStroke(); // No outline... box(40); opacity *= 0.7; // Make each segment less opaque than the last } popMatrix(); // Go back to base matrix of the sphere for the next text/tentacle } } // ------------------------------------------------------------ // Start / stop (pause) the main loop each time a mouse // button is pressed. // ------------------------------------------------------------ void mousePressed() { if (looping) { // Pause the main loop noLoop(); looping = false; } else { // (Re-)start the main loop loop(); looping = true; clockMillis = millis(); // Ignore any time passed up til now. } } // ------------------------------------------------------------ // Handle keyboard input // ------------------------------------------------------------ void keyPressed() { if (key == CODED) { if (keyCode == LEFT || keyCode == RIGHT) { selectOscillator( (selectedOscillatorIndex + (keyCode == RIGHT ? 1 : oscillators.size() - 1)) % oscillators.size() ); } else if (keyCode == UP) { setSelectedPeriod(selectedOscillator.period + 0.1); } else if (keyCode == DOWN) { setSelectedPeriod(selectedOscillator.period - 0.1); } } else if (key == BACKSPACE) { selectedOscillator.reverseDirection(); } else if (key == ' ') { selectedOscillator.togglePause(); } else if (key == ',') { selectedOscillator.waveShape = 0; } else if (key == '.') { selectedOscillator.waveShape = 1; } else if (key == '/') { selectedOscillator.waveShape = 2; } else if (key == '-') { selectedOscillator.waveShape = 3; } else if (key == '0') { selectedOscillator.pause(); } else if (key == '1') { setSelectedPeriod(1); } else if (key == '2') { setSelectedPeriod(2); } else if (key == '3') { setSelectedPeriod(3); } else if (key == '4') { setSelectedPeriod(4); } else if (key == '5') { setSelectedPeriod(5); } else if (key == '6') { setSelectedPeriod(6); } else if (key == '7') { setSelectedPeriod(7); } else if (key == '8') { setSelectedPeriod(8); } else if (key == '9') { setSelectedPeriod(9); } else if (key == 'z') { selectOscillator(0); } else if (key == 'x') { selectOscillator(1); } else if (key == 'c') { selectOscillator(2); } else if (key == 'v') { selectOscillator(3); } else if (key == 'b') { selectOscillator(4); } else if (key == 'n') { selectOscillator(5); } else if (key == 'm') { selectOscillator(6); } } // ------------------------------------------------------------ // Helper function to change the currently selected oscillator // ------------------------------------------------------------ void selectOscillator(int i) { selectedOscillatorIndex = constrain(i, 0, oscillators.size() - 1); selectedOscillator = (Oscillator)(oscillators.get(selectedOscillatorIndex)); } void setSelectedPeriod(float period) { selectedOscillator.setPeriod(period); selectedOscillator.unPause(); }