Total Eclipse of the Heart.

Solar eclipse animation made with Processing.

Description of this creative coding work.

A creative coding artwork made with Processing.
It draws many sine curves surround the circle with blendMode(SCREEN) and draw a black circle with blendMode(BLEND) on these to express the eclipse.

I pursued OOP in this code.
Did I do this well? I think NO or TOO MUCH!
I feel it's too difficult to balance on.

Processing example code.

Please feel free to use this example code, if you like it.
To see other works based on my code is my pleasure. And my honor.


* It's so slow to draw!

// Total Eclipse of the Heart.
// @author @deconbatch
// @version 0.4
// Processing 3.2.1
// 2018.07.21

/* WaveMaker ---------- */
public class WaveMaker {

  private Calculator ct;
  private float noiseStart;
  
  WaveMaker(Calculator ct) {
    this.ct    = ct;
    noiseStart = random(100.0);
  }

  public void drawWave(float frequency, float frequencyRate,float phase) {

    noiseStart += 0.0005;
    
    for (float beltRate = 0.0; beltRate <= 1.0; beltRate += ct.beltRateDiv) {

      float noiseParam = noiseStart;
      ct.setFactor(frequency, frequencyRate, phase, beltRate);

      for (float radianRate = 0; radianRate <= 1.0; radianRate += ct.radianRateDiv) {
        plot(radianRate, noiseParam);
        noiseParam += 0.005;
      }

    }

  }

  private void plot(float radianRate, float noiseParam) {

    WaveParticle wp = ct.calcAll(radianRate, noiseParam);
      
    fill(
         (wp.hue + 360.0) % 360.0,
         wp.saturation,
         wp.brightness,
         100.0
         );

    pushMatrix();
    rotate(2.0 * PI * radianRate);
    ellipse(
            0.0,
            wp.amplitude + height * 0.28,
            wp.size,
            wp.size
            );
    popMatrix();

  }

}

/* Abstract Calculator ---------- */
public abstract class Calculator {

  protected float radianRateDiv;
  protected float beltRateDiv;
  protected float frequency;
  protected float frequencyRate;
  protected float phase;
  protected float beltRate;
  protected float hueBase;
  
  protected WaveParticle wp;

  Calculator(float hueBase) {
    this.wp       = new WaveParticle();
    this.hueBase  = hueBase;
    radianRateDiv = 1.0;
    beltRateDiv   = 1.0;
    frequency     = 1.0;
    frequencyRate = 1.0;
    phase         = 0.0;
    beltRate      = 1.0;
  }

  public void setFactor(float frequency, float frequencyRate, float phase, float beltRate) {
    this.frequency     = frequency;
    this.frequencyRate = frequencyRate;
    this.phase         = phase;
    this.beltRate      = beltRate;
  }

  public WaveParticle calcAll(float radianRate, float noiseParam) {
    calcHue(radianRate, noiseParam);
    calcSaturation(radianRate, noiseParam);
    calcBrightness(radianRate, noiseParam);
    calcAmplitude(radianRate, noiseParam);
    calcSize(radianRate, noiseParam);
    return this.wp;
  }

  protected abstract void calcHue(float radianRate, float noiseParam);
  protected abstract void calcSaturation(float radianRate, float noiseParam);
  protected abstract void calcBrightness(float radianRate, float noiseParam);
  protected abstract void calcAmplitude(float radianRate, float noiseParam);
  protected abstract void calcSize(float radianRate, float noiseParam);
  
}

/* Concrete Calculators ---------- */
public class Chromosphere extends Calculator {

  Chromosphere(float hueBase) {
    super(hueBase);
    radianRateDiv = 0.0005;
    beltRateDiv   = 0.05;
  }
  
  @Override
  protected void calcHue(float radianRate, float noiseParam) {
    wp.hue =
      hueBase
      + map(frequencyRate, 0.0, 1.0, 0.0, 30.0)
      + sin(2.0 * PI * noise(noiseParam)) * 15.0; // animate color with noise
  }

  @Override
  protected void calcSaturation(float radianRate, float noiseParam) {
    wp.saturation = 40.0;
  }
  
  @Override
  protected void calcBrightness(float radianRate, float noiseParam) {
    wp.brightness =
      map(frequencyRate, 0.0, 1.0, 1.0, 3.0)
      * sin(PI * (0.1 + 0.9 * radianRate))            // darker in center and end
      * sin(PI * map(beltRate, 0.0, 1.0, 0.1, 0.5)); // brighter with beltRate
  }

  @Override
  protected void calcAmplitude(float radianRate, float noiseParam) {
    wp.amplitude =
      setAmpBase() 
      * sin(2.0 * PI * (-phase + frequency * radianRate)) // sine wave
      * sin(PI * map(beltRate, 0.0, 1.0, 0.1, 0.5));      // bigger wave with beltRate
    if (wp.amplitude < 0.0) {
      wp.amplitude = height * 2.0;  // do not draw
    }
  }

  protected float setAmpBase() {
    return height * 0.08 * pow(map(frequencyRate, 0.0, 1.0, 1.0, 0.5), 2);
  }

  @Override
  protected void calcSize(float radianRate, float noiseParam) {
    wp.size =
      1.0
      + pow(sin(2.0 * PI * (phase + frequency * radianRate)), 4);
  }
 
}

/* Concrete Calculators ---------- */
public class Photosphere extends Chromosphere {

  Photosphere(float hueBase) {
    super(hueBase);
  }

  @Override
  protected float setAmpBase() {
    return height * 0.05 * pow(map(frequencyRate, 0.0, 1.0, 1.0, 0.1), 2);
  }

}

/* Concrete Calculators ---------- */
public class Corona extends Calculator {

  Corona(float hueBase) {
    super(hueBase);
    radianRateDiv = 0.02;
    beltRateDiv   = 0.1;
  }

  @Override
  protected void calcHue(float radianRate, float noiseParam) {
    wp.hue = hueBase + map(frequencyRate, 0.0, 1.0, 0.0, 30.0);
  }

  @Override
  protected void calcSaturation(float radianRate, float noiseParam) {
    wp.saturation = 60.0;
  }

  @Override
  protected void calcBrightness(float radianRate, float noiseParam) {
    wp.brightness =
      pow(map(frequencyRate, 0.0, 1.0, 1.0, 0.1), 2)
      * abs(sin(2.0 * PI * radianRate))              // darker in center, start and end
      * sin(PI * map(beltRate, 0.0, 1.0, 0.3, 0.5)); // little brighter with beltRate
  }

  @Override
  protected void calcAmplitude(float radianRate, float noiseParam) {
    wp.amplitude = abs(
                       height * 0.4 * pow(map(frequencyRate, 0.0, 1.0, 1.0, 0.2), 2)
                       * sin(2.0 * PI * (-phase + frequency * radianRate)) // sine wave
                       * sin(PI * map(beltRate, 0.0, 1.0, 0.1, 0.5))       // bigger wave with beltRate
                       );
  }

  @Override
  protected void calcSize(float radianRate, float noiseParam) {
    wp.size =
      pow(sin(PI * (phase + frequency * radianRate)), 2) * 30.0
      + noise(noiseParam) * 50.0;  // animate corona size with noise 
  }

}

/* Concrete Calculators ---------- */
public class Prominence extends Calculator {

  Prominence(float hueBase) {
    super(hueBase);
    radianRateDiv = 0.001;
    beltRateDiv   = 0.01;
  }

  @Override
  protected void calcHue(float radianRate, float noiseParam) {
    wp.hue =
      hueBase
      + sin(1.0 * PI * (phase + frequency * radianRate)) * 30.0; // animate color with phase
  }

  @Override
  protected void calcSaturation(float radianRate, float noiseParam) {
    wp.saturation =
      10.0
      + sin(1.0 * PI * (phase + frequency * radianRate)) * 20.0;
  }

  @Override
  protected void calcBrightness(float radianRate, float noiseParam) {
    wp.brightness =
      pow(map(frequencyRate, 0.0, 1.0, 1.0, 0.0), 2) // brighter in low frequency wave
      * sin(PI * map(beltRate, 0.0, 1.0, 0.1, 0.5)); // brighter with beltRate
  }

  @Override
  protected void calcAmplitude(float radianRate, float noiseParam) {
    wp.amplitude = abs(
                       height * 0.2 * map(frequencyRate, 0.0, 1.0, 1.0, 0.5)
                       * sin(2.0 * PI * (-phase + frequency * radianRate))    // sine wave
                       * sin(PI * map(beltRate, 0.0, 1.0, 0.5, 0.3))          // smaller wave with beltRate
                       * 10.0 * pow(noise(0.002 * radianRate, noiseParam), 4) // waving animation with noise
                       );
  }

  @Override
  protected void calcSize(float radianRate, float noiseParam) {
    wp.size = pow(sin(PI * (phase + frequency * radianRate)), 4) * 2.0;
  }

}

/* Calculate Results ---------- */
public class WaveParticle {

  // wave
  public float amplitude;
  public float radian;

  // particle
  public float hue;
  public float saturation;
  public float brightness;
  public float size;

  WaveParticle() {
    amplitude  = 0.0;
    radian     = 0.0;

    hue        = 0.0;
    saturation = 0.0;
    brightness = 0.0;
    size       = 0.0;
  }

}

/* Main ---------- */
void setup() {

  size(720, 720);
  colorMode(HSB, 360, 100, 100, 100);
  blendMode(ADD);
  smooth();
  noLoop();
  noStroke();

}

void draw() {

  translate(width / 2, height / 2);

  float hueBase        = random(360.0);
  int   chromosphereNo = 5;
  int   photosphereNo  = 6;
  int   coronaNo       = 3;
  int   prominenceNo   = 1;
  int   frameCntMax    = ceil(15 * 6.5);  // 15fps x 6.5s

  WaveMaker[] ph = newWaveMaker(chromosphereNo, new Chromosphere(hueBase),  hueBase);
  WaveMaker[] pl = newWaveMaker(photosphereNo,  new Photosphere(hueBase),   hueBase);
  WaveMaker[] rd = newWaveMaker(coronaNo,       new Corona(hueBase),        hueBase);
  WaveMaker[] fl = newWaveMaker(prominenceNo,   new Prominence(hueBase),    hueBase);

  for (int frameCnt = 0; frameCnt < frameCntMax; ++frameCnt) {

    background(hueBase, 100, 5, 100);

    float phase = map(frameCnt, 0, frameCntMax, 0.0, 0.5);  // animate sine wave with phase
    drawFrame(ph, chromosphereNo, phase);
    drawFrame(pl, photosphereNo,  phase);
    drawFrame(rd, coronaNo,       phase);
    drawFrame(fl, prominenceNo,   phase);

    saveFrame("frames/" + String.format("%04d", frameCnt) + ".png");

  }

  exit();
  
}

WaveMaker[] newWaveMaker(int instanceNo, Calculator ct, float hueBase) {

  WaveMaker[] wm = new WaveMaker[instanceNo];
  for (int i = 0; i < instanceNo; ++i) {
    wm[i] = new WaveMaker(ct);
  }
  return wm;

}

void drawFrame(WaveMaker[] wm, float rotateCntMax, float phase) {

  pushMatrix();
  for (int rotateCnt = 0; rotateCnt < rotateCntMax; ++rotateCnt) {
    rotate(2.0 * PI / (rotateCntMax * 1.0));
    drawWave(wm[rotateCnt], phase);
  }
  popMatrix();
    
}

void drawWave(WaveMaker wm, float phase) {

  float frequencyMin = 3.0;
  float frequencyMax = 12.0;

  for (float frequency = frequencyMin; frequency <= frequencyMax; frequency += 1.0) {
    float frequencyRate = map(frequency, frequencyMin, frequencyMax, 0.0, 1.0);
    wm.drawWave(frequency, frequencyRate, phase);
  }

}

/*
Copyright (C) 2018- 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 
*/



Yet another color example.

Solar eclipse animation made with Processing.

No comments :

Post a Comment