The original idea of this media art.

When I played with ntsutae (@ntsutae) san's work 'Symmetry 2'.


I found it creates interesting shapes if I tuned the size of rectangles.


And I tried to make it interesting media art, I used 'blendMode(DIFFERENCE)' and tuned the edge size of the rectangle.

Making my own work.

The original ntsutae san's work used logical operation to determine the shape. But it does not necessarily need the logical operation to make a shape randomly. So I changed the formula of shape calculation to the formula I can easily understand.

float theta = HALF_PI * floor(8 * sin(shapeBase + shapeDiv * i));

I proceeded to tune the size and the width of the edge of rectangles and made the whole shape locate to the center of the canvas.



Make it move with simple morphing technique.

I called it 'Simple morphing technique', but it is just a linear interpolation.

I used to calculate the interpolation by myself like 'Johnny on the Monorail'.

float xF = _to.get(f).x * _rate + _from.get(f).x * (1.0 - _rate);

And I found the 'lerp()' function at last! Ahh! what a divine blessing!

float x = lerp(shapeFrom.get(i).x, shapeTo.get(i).x, easeRatio);

Finally, I made this media art as a loop animation.

During the way, I made some failed creative work. It's a 'strokeWeight(random())'.








The example code of the 'Processing'.

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

This code does not display any images on the screen but generates image files in frames directory. You can make an animation with these files.


/**
 * Good Times Bad Times.
 * It's a media art that displays the morphing generative design of rectangles.
 * original idea : https://openprocessing.org/sketch/1125777
 * 
 * @author @deconbatch
 * @version 0.1
 * @license GPL Version 3 http://www.gnu.org/licenses/
 * Processing 3.5.3
 * 2021.05.23
 */

void setup() {
  size(720, 720);
  colorMode(HSB, 360.0, 100.0, 100.0, 100.0);
  rectMode(CENTER);
  smooth();
}

void draw() {
  
  int   frmRate  = 30;
  int   frmMorph = frmRate * 1;       // morphing duration frames
  int   cycles   = 8;                 // animation cycle no
  int   frmMax   = frmMorph * cycles; // whole frames
  float rSize    = 25.0;

  // generate shapes
  ArrayList<ArrayList<PVector>> shapes = new ArrayList<ArrayList<PVector>>();
  float weights[] = new float[cycles];
  for (int i = 0; i < cycles; i++) {
    shapes.add(getShape(rSize * 2.0));
    weights[i] = rSize * floor(random(6));
  }

  // variables for animation
  ArrayList<PVector> shapeFrom = new ArrayList<PVector>();
  ArrayList<PVector> shapeTo   = new ArrayList<PVector>();
  float weightFrom = 0.0;
  float weightTo   = 0.0;
  int   cycleCnt   = 0;

  for (int frmCnt = 0; frmCnt < frmMax; frmCnt++) {
    float frmRatio  = map(frmCnt, 0, frmMax, 0.0, 1.0);
    float easeRatio = InFourthPow(map(frmCnt % frmMorph, 0, frmMorph - 1, 0.0, 1.0));
  
    // shape for morphing animation. loops cyclic.
    if (frmCnt % frmMorph == 0) {
      cycleCnt   = frmCnt / frmMorph;
      shapeFrom  = shapes.get(cycleCnt);
      shapeTo    = shapes.get((cycleCnt + 1) % cycles);
      weightFrom = weights[cycleCnt];
      weightTo   = weights[(cycleCnt + 1) % cycles];
    }

    // draw
    blendMode(BLEND);
    background(0.0, 0.0, 100.0, 100.0);

    blendMode(DIFFERENCE);
    fill(0.0, 0.0, 100.0, 100.0);
    stroke(0.0, 0.0, 100.0, 100.0);
    strokeWeight(lerp(weightFrom, weightTo, easeRatio));
    for (int i = 0; i < shapeFrom.size(); i++) {
      float x = lerp(shapeFrom.get(i).x, shapeTo.get(i).x, easeRatio);
      float y = lerp(shapeFrom.get(i).y, shapeTo.get(i).y, easeRatio);
      rect(x, y, rSize, rSize);
    }

    blendMode(BLEND);
    casing();

    // for stop motion
    if (frmCnt % frmMorph == 0) {
      for (int i = 0; i < frmRate; i++) {
        saveFrame("frames/" + String.format("%04d", cycleCnt) + ".00." + String.format("%04d", i) + ".png");
      }
    }

    // for moving motion
    saveFrame("frames/" + String.format("%04d", cycleCnt) + ".01." + String.format("%04d", frmCnt) + ".png");
  }
  
  exit();
}

/**
 * getShape : generates the shape
 */
ArrayList<PVector> getShape(float _size) {

  int   pvNum     = 15;
  float shapeBase = random(TWO_PI);
  float shapeDiv  = random(HALF_PI);

  float x = 0.0;
  float y = 0.0;
  ArrayList<PVector> pvs = new ArrayList<PVector>();
  for (int i = 0; i < pvNum; i++) {
    float theta = HALF_PI * floor(8 * sin(shapeBase + shapeDiv * i));
    x += _size * cos(theta);
    y += _size * sin(theta);
    pvs.add(new PVector(x, y));
  }

  // centering the shape
  float xMin = width;
  float xMax = 0;
  float yMin = height;
  float yMax = 0;
  for (PVector pv : pvs) {
    xMin = min(xMin, pv.x);
    xMax = max(xMax, pv.x);
    yMin = min(yMin, pv.y);
    yMax = max(yMax, pv.y);
  }
  float xDiv = (width - xMin - xMax) * 0.5;
  float yDiv = (height - yMin - yMax) * 0.5;
  ArrayList<PVector> pvsCentered = new ArrayList<PVector>();
  for (PVector pv : pvs) {
    pvsCentered.add(pv.add(xDiv, yDiv));
  }

  return pvsCentered;
}
  
/**
 * InFourthPow : easing function.
 */
private float InFourthPow(float _t) {
  return 1.0 - pow(1.0 - _t, 4);
}

/**
 * casing : draws fancy casing
 */
void casing() {
  float w = min(width, height) * 0.05;
  fill(0.0, 0.0, 0.0, 0.0);
  strokeWeight(w + 4.0);
  stroke(0.0, 0.0, 0.0, 100.0);
  rect(width * 0.5, height * 0.5, width, height);
  strokeWeight(w);
  stroke(0.0, 0.0, 100.0, 100.0);
  rect(width * 0.5, height * 0.5, width, height);
}


/*
Copyright (C) 2021- 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 <http://www.gnu.org/licenses/>
*/