The Fountain of Polaris


About this creative coding animation.

It's an animation made with Processing programming language and Kdenlive video editor.
I tried to express the water droplet in this code. And I learned about 3D light also.

Thanks to nice music:
Juillet by Monplaisir
2017/03/17
http://freemusicarchive.org/music/Monplaisir/A_Monplaisir_Best-Of__Lets_hear_that_crap/Monplaisir_-_A_Monplaisir_Best-of__Lets_here_that_crap_-_17_Juillet
Juillet by Monplaisir is licensed under a CC0 1.0 Universal License.
For more permissions:
contact artist
Public Domain Mark


Processing example code.

// The Fountain of Polaris
// Processing 3.2.1
// 2017.04.19

import java.util.Random;

Utils ut;
World wd;
FountainSurface fs;
FloatingDrops[] fd;

int NUM_FLOAT_LINE = 3;
float BACK_WIDTH = 500;
float BACK_HEIGHT = 500;
float FOUNTAIN_RADIUS = 190;

/* ---------------------------------------------------------------------- */
class World {

  StarElement st;
  PVector[] locateStars;
  float[] brightStars, colorStars;
  float[] noiseBF;
  int numStars;

  World() {

    numStars = 240;
    st = new StarElement();
    st.setElementFill(230, 75, 30, 10);

    float divWidth = width / numStars;
    locateStars = new PVector[numStars];
    brightStars = new float[numStars];
    colorStars = new float[numStars];
    noiseBF = new float[numStars];
    for (int i = 0; i < numStars; ++i) {
      locateStars[i] = new PVector(
                                   map(i, 0, numStars - 1, -width, width),
                                   ut.gaussdist(-height / 5, height * 2 / 3, height / 2),
                                   ut.gaussdist(-700, 400, 150)
                                   );
      brightStars[i] = ut.gaussdist(60, 20, 15);
      colorStars[i] = ut.gaussdist(200, 40, 20);
      noiseBF[i] = random(numStars);
    }

  }

  void redraw() {

    // draw smoke
    strokeWeight(3);
    for (float i = height; i > 0; --i) {
      stroke(
             map(i, height, 0, 160, 220),
             80,
             map(i, height, 0, 25, 0),
             100
             );
      pushMatrix();
      translate(0, i, -500);
      line(-width, 0, width, 0);
      popMatrix();
    }
    noStroke();

    // draw stars
    for (int i = 0; i < numStars; ++i) {
      pushMatrix();
      translate(locateStars[i].x, locateStars[i].y, locateStars[i].z);
      //      st.changeBright(ut.gaussdist(brightStars[i], 40, 8));
      st.changeBright(brightStars[i] + map(ut.sigmoid(noise(noiseBF[i]), 2.0, 5.0), 0, 1, -20, 40));
      st.changeColor(colorStars[i]);
      st.show();
      popMatrix();
      noiseBF[i] += 0.05;
    }

    // for half surface & diagonally drops flow
    rotateX(PI/2);
    rotateZ(PI/10);
    rotateY(PI/8);

  }

}

/* ---------------------------------------------------------------------- */
class FountainSurface {

  SurfaceElement se;
  PVector locateSe;
  float radiusV;
  float divIdo, divKdo;
  float noiseCIS, noiseCKS;
  float noiseSIS, noiseSKS;
  float noiseBIS, noiseBKS;

  FountainSurface() {
    radiusV = FOUNTAIN_RADIUS * 0.99;
    divIdo = 2;
    divKdo = 0; // dummy

    noiseCIS = random(50);
    noiseCKS = random(50);
    noiseSIS = random(50);
    noiseSKS = random(50);
    noiseBIS = random(50);
    noiseBKS = random(50);
    
    se = new SurfaceElement();
    se.setElementFill(0, 0, 0, 0);
    se.changeSize(divIdo * 3.55, divIdo * 4.10, 1);
    locateSe = new PVector(0, 0, 0);
  }

  void makeSurface() {

    float ido, kdo;
    float radianIdo, radianKdo;

    noiseCKS += 0.006;
    noiseCIS += 0.012;
    noiseSKS += 0.016;
    noiseSIS -= 0.008;
    noiseBKS -= 0.018;
    noiseBIS += 0.018;
    float noiseCIF = noiseCIS;
    float noiseSIF = noiseSIS;
    float noiseBIF = noiseBIS;

    for (ido = 0; ido <=180; ido += divIdo) { // Y
      radianIdo = radians(ido);
      divKdo = 180 / max(160 / divIdo * sin(radianIdo), 1);

      float noiseCKF = noiseCKS;
      float noiseSKF = noiseSKS;
      float noiseBKF = noiseBKS;

      for (kdo = 0; kdo <= 150; kdo += divKdo) { // Z
        //      for (kdo = 0; kdo <= 360; kdo += divKdo) { // Z
        // element point
        radianKdo = radians(kdo);
        locateSe.set(
                     radiusV * cos(radianKdo) * sin(radianIdo),
                     radiusV * sin(radianKdo) * sin(radianIdo),
                     radiusV * cos(radianIdo)
                     );
        se.setElementFill(
                          map(noise(noiseCIF, noiseCKF), 0, 1,  180, 255),
                          map(noise(noiseSIF, noiseSKF), 0, 1,  60, 110),
                          map(noise(noiseBIF, noiseBKF), 0, 1,   10, 50),
                          80
                          );
        pushMatrix();
        translate(locateSe.x, locateSe.y, locateSe.z);
        rotateZ(radianKdo); // must be this order Z -> Y
        rotateY(radianIdo);
        se.show();
        popMatrix();

        noiseCKF += 0.020;
        noiseSKF += 0.008;
        noiseBKF += 0.012;

      }
      
      noiseCIF += 0.010;
      noiseSIF += 0.016;
      noiseBIF += 0.012;
      
    }
  }
  
  void makeSphere() {
    pushMatrix();
    translate(0, 0, 0);
    fill(240, 100, 20, 50);
    sphere(FOUNTAIN_RADIUS);
    popMatrix();

  }


}

/* ---------------------------------------------------------------------- */
class FloatingDrops {

  DropElement[] dp;
  PVector[] locateDp;
  int numDrops;
  float radiusV;
  float noiseRIS, noiseRKS, noiseRRS;
  float speedRotation;

  FloatingDrops() {
    numDrops = 120;
    radiusV = FOUNTAIN_RADIUS * 1.2;

    noiseRIS = random(50);
    noiseRKS = random(50);
    noiseRRS = random(50);

    dp = new DropElement[numDrops];
    locateDp = new PVector[numDrops];
    for (int cntDrop = 0; cntDrop < numDrops; ++cntDrop) {
      float rnd = ut.gaussdist(3.0, 2.0, 1.5);
      dp[cntDrop] = new DropElement();
      dp[cntDrop].changeSize(
                             rnd,
                             rnd * ut.gaussdist(1.0, 0.3, 0.1),
                             rnd * ut.gaussdist(1.0, 0.4, 0.2)
                             );
      dp[cntDrop].setElementFill(210,
                                 map(rnd, 0, 7, 55, 75),
                                 60,
                                 100);
      locateDp[cntDrop] = new PVector(0, 0, 0);
    }
    speedRotation = 0;
  }

  void floatDrops() {

    noiseRIS += 0.003;
    noiseRKS += 0.001;
    noiseRRS += 0.008;

    float ido, kdo;
    float radianIdo, radianKdo;
    float fnumDrops = numDrops - 1.0;

    float noiseRIF = noiseRIS;
    float noiseRKF = noiseRKS;
    float noiseRRF = noiseRRS;

    for (int cntDrop = 0; cntDrop < numDrops; ++cntDrop) {

      float divZ = 0.3 + pow(2.6 * (cntDrop / fnumDrops - 0.5), 2);
      ido = map(cntDrop, 0., fnumDrops, 30.0, 150.0) *
        map(noise(noiseRIF), 0, 1, 0.8, 1.2);
      kdo = map(cntDrop, 0., fnumDrops, 580.0, -430.0) *
        map(noise(noiseRKF), 0, 1, 0.8, 1.2);
      radiusV = FOUNTAIN_RADIUS * map(noise(noiseRRF), 0, 1, 1.20, 1.50);

      radianIdo = radians(ido);
      radianKdo = radians(kdo);
      locateDp[cntDrop].set(
                            radiusV * cos(radianKdo) * sin(radianIdo),
                            radiusV * sin(radianKdo) * sin(radianIdo),
                            radiusV * cos(radianIdo) * divZ
                            );
      pushMatrix();
      rotateZ(radians(speedRotation));
      translate(locateDp[cntDrop].x, locateDp[cntDrop].y, locateDp[cntDrop].z);
      dp[cntDrop].rotate(0.03, 0.012, 0.08);
      dp[cntDrop].show();
      popMatrix();

      noiseRIF += 0.18;
      noiseRKF += 0.25;
      noiseRRF += 0.02;
      
    }

    speedRotation += 0.25;

  }


}

/* ---------------------------------------------------------------------- */
abstract class FountainElement {

  PShape anElement;
  float elementColor, elementSaturation, elementBright, elementAlpha;
  
  FountainElement() {
    anElement = pscreateElement();
    elementColor = 0;
    elementSaturation = 0;
    elementBright = 0;
    elementAlpha = 0;
  }

  abstract PShape pscreateElement();

  void setElementFill(float pcolor, float psaturation, float pbright, float palpha) {
    elementColor = pcolor;
    elementSaturation = psaturation;
    elementBright = pbright;
    elementAlpha = palpha;
    resetColor();
  }

  void resetColor() {
    anElement.setFill(color(elementColor, elementSaturation, elementBright, elementAlpha));
  }

  void changeColor(float scolor) {
    elementColor = scolor;
    resetColor();
  }

  void changeBright(float sbright) {
    elementBright = sbright;
    resetColor();
  }

  void resetSize() {
    anElement.resetMatrix();
  }

  void changeSize(float scaleX, float scaleY, float scaleZ) {
    anElement.scale(scaleX, scaleY, scaleZ);
  }

  void rotate(float radX, float radY, float radZ) {
    anElement.rotateX(radX);
    anElement.rotateY(radY);
    anElement.rotateZ(radZ);
  }

  void show() {
    shape(anElement);
  }

}



/* ---------------------------------------------------------------------- */
class SurfaceElement extends FountainElement {
  
  SurfaceElement() {
    super();
  }

  PShape pscreateElement() {
    noStroke();
    PShape psSe = createShape(RECT, 0, 0, 1, 1);
    return psSe;
  }

}

/* ---------------------------------------------------------------------- */
class DropElement extends FountainElement {

  DropElement() {
    super();
  }

  PShape pscreateElement() {

    noStroke();
    PShape psDp = createShape(SPHERE, 1);
    return psDp;

  }

}

/* ---------------------------------------------------------------------- */
class StarElement extends FountainElement {

  StarElement() {
    super();
  }

  PShape pscreateElement() {

    noStroke();
    PShape psDp = createShape(GROUP);

    PShape psCh;
    for (int i = 0; i < 5; ++i) {
      float rad = 1 + i * i;
      psCh = createShape(ELLIPSE, 0, 0, rad, rad);
      psDp.addChild(psCh);
    }

    return psDp;

  }

}

/* ---------------------------------------------------------------------- */
class Utils {

  Random obj_random;

  Utils() {
    obj_random = new Random();
  }
  
  float sigmoid(float x, float pcenter, float pgradient) {
    /**
       Sigmoid function
       1.parameters.
       x : 1 > x value > 0
       pcenter : center of curve. 1 - 1 / pcenter
       ex. 10 > pcenter > 1 : 0.9 > center of curve > 0
       pgradient : gradient of curve.
       ex. 20 > pgradient > 4 : steep > gradient of curve > gentle
       2.return.
       1 > y value > 0
    **/

    float modx = pcenter * (x - 1) + 1;
    return (float)(1.0 / (1.0 + Math.exp(-modx * pgradient)));
  }

  float gaussdist(float pmean, float plimit, float pdevi) {
    /**
       Gaussian distribution
       1.parameters.
       pmean  : mean value
       plimit : max value of abs(deviation)
       ex. plimit >= 0
       pmean = 0.5, plimit = 0.5 -> return value = from 0.0 to 1.0
       pdevi  : standard deviation value
       ex. good value? -> pdevi = plimit / 2
       2.return.
       gaussian distribution
    **/

    if (plimit == 0) {
      return pmean;
    }

    float gauss = (float) obj_random.nextGaussian() * pdevi;
    // not good idea
    if (abs(gauss) > plimit) {
      gauss = pow(plimit, 2) / gauss;
    }

    return pmean + gauss;
    
  }
}

/* ---------------------------------------------------------------------- */
void setup() {
  size(1280, 720, P3D);
  colorMode(HSB, 360, 100, 100, 100);
  smooth();
  background(0, 0, 0);
  frameRate(30);

  ut = new Utils();
  wd = new World();
  fs = new FountainSurface();
  fd = new FloatingDrops[NUM_FLOAT_LINE];
  for (int i = 0; i < NUM_FLOAT_LINE; ++i) {
    fd[i] = new FloatingDrops();
  }

}

void draw() {

  background(0, 0, 0);
  translate(0, 0, 0);

  camera(0, 0, 600,
         0, 0, 0,
         0, 1, 0);

  lightFalloff(0.3, 0.0008, 0.0);
  pointLight(0, 0, 100, -80, -140, 300);
  ambientLight(0, 0, 60);
    
  wd.redraw();

  for (int i = 0; i < NUM_FLOAT_LINE; ++i) {
    fd[i].floatDrops();
  }
  fs.makeSurface();
  fs.makeSphere();

  saveFrame("frames/####.png");
  if (frameCount >= 4590) {
    exit();
  }

}

/*
Copyright (C) 2017- deconbatch

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see 
*/



Next Post Previous Post
No Comment
Add Comment
comment url