Purrspective.

an image like a multi point perspective drawing with random walking of three primary colors. 

With the image from
Two Tabby Cats
http://www.photos-public-domain.com/2016/11/15/two-tabby-cats/

Multi poing perspective drawing with random walking of three primary colors.

It's a Image Manipulation type creative coding made with Processing. It draws an image like a multi point perspective drawing with random walking of three primary colors.

The beginShape(QUAD_STRIP) and the beginShape(LINES) creates this perspective drawing feeling.
The blendMode(SUBTRACT) reproduces the original image with three primary colors. And I found that these three primary colors (I called so) are any three hue values with a 120-degree difference each other!

Random walking result with first primary color.
Random walking result with second primary color.
Random walking result with third primary color.

You can use your photo with this code.

example : if your photo file is './data/your_photo.jpg'.
PImage img = loadImage("your_photo.jpg");

An example source code of Processing.

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.

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


/**
 * Purrspective.
 * It draws an image like a multi point perspective drawing with random walking of three primary colors.
 * 
 * @author @deconbatch
 * @version 0.1
 * @license GPL Version 3 http://www.gnu.org/licenses/
 * Processing 3.5.3
 * 2020.09.26
 */

void setup() {
  size(980, 980, P2D);
  colorMode(HSB, 360.0, 100.0, 100.0, 100.0);
  rectMode(CENTER);
  strokeJoin(ROUND);
  strokeCap(PROJECT);
  noLoop();
}

void draw() {

  PImage img = loadImage("your_photo.jpg");
    
  int   caseWidth  = 36;
  int   baseCanvas = max(width, height) - caseWidth * 2;
  float rateSize   = baseCanvas * 1.0 / max(img.width, img.height);
  img.resize(floor(img.width * rateSize), floor(img.height * rateSize));
  img.loadPixels();

  int   iW     = img.width;
  int   iH     = img.height;
  int   steps  = iW * iH / 3600;
  float slant  = PI * 0.25 * floor(random(2.0));
  float hueDiv = random(360.0);

  pushMatrix();
  translate((width - iW) / 2, (height - iH) / 2);

  ArrayList<ArrayList<PVector>> paths = new ArrayList<ArrayList<PVector>>();

  // draw first primary color
  paths.clear();
  paths = getPaths(iW, iH, 75, 10, steps * 6, slant);
  blendMode(BLEND);
  background(0.0, 0.0, 90.0, 100.0);
  blendMode(SUBTRACT);
  drawPath(paths, img, iW, (0.0 + hueDiv) % 360.0);  
  blendMode(BLEND);
  casing(iW, iH, caseWidth);
  saveFrame("frames/pp0001.png");

  // draw second primary color
  paths.clear();
  paths = getPaths(iW, iH, 100, 20, steps * 6, slant);
  blendMode(BLEND);
  background(0.0, 0.0, 90.0, 100.0);
  blendMode(SUBTRACT);
  drawPath(paths, img, iW, (120.0 + hueDiv) % 360.0);
  blendMode(BLEND);
  casing(iW, iH, caseWidth);
  saveFrame("frames/pp0002.png");

  // draw third primary color
  paths.clear();
  paths = getPaths(iW, iH, 50, 5, steps * 6, slant);
  blendMode(BLEND);
  background(0.0, 0.0, 90.0, 100.0);
  blendMode(SUBTRACT);
  drawPath(paths, img, iW, (240.0 + hueDiv) % 360.0);
  blendMode(BLEND);
  casing(iW, iH, caseWidth);
  saveFrame("frames/pp0003.png");

  // draw with three primary colors
  paths.clear();
  paths.addAll(getPaths(iW, iH, 75, 10, steps, slant));
  paths.addAll(getPaths(iW, iH, 100, 20, steps, slant));
  paths.addAll(getPaths(iW, iH, 50, 5, steps, slant));
  blendMode(BLEND);
  background(0.0, 0.0, 90.0, 100.0);
  blendMode(SUBTRACT);
  drawPath(paths, img, iW, (0.0 + hueDiv) % 360.0);
  drawPath(paths, img, iW, (120.0 + hueDiv) % 360.0);
  drawPath(paths, img, iW, (240.0 + hueDiv) % 360.0);
  blendMode(BLEND);
  casing(iW, iH, caseWidth);
  saveFrame("frames/pp0004.png");

  popMatrix();

  exit();
}

/**
 * getPaths : calculate the random walking paths.
 * @param _xMax, _yMax : width and height of walking area.
 * @param _pathMax     : path number.
 * @param _locateDiv   : minimum distance of path start locations.
 * @param _stepMax     : step number of each path.
 * @param _slant       : slant of walking. 
 */
ArrayList<ArrayList<PVector>> getPaths(int _xMax, int _yMax, int _pathMax, int _locateDiv, int _stepMax, float _slant) {

  ArrayList<ArrayList<PVector>> paths = new ArrayList<ArrayList<PVector>>();

  for (int pathCnt = 0; pathCnt < _pathMax; pathCnt++) {

    ArrayList<PVector> path = new ArrayList<PVector>();

    // path start location
    int x = floor(random(_xMax * 1.0 / _locateDiv)) * _locateDiv;
    int y = floor(random(_yMax * 1.0 / _locateDiv)) * _locateDiv;
    
    float slant = (sin(_slant) == 0.0) ? HALF_PI : _slant; // avoid 0 divide
    float stepDiv = 25.0 / sin(slant); //20.0 ... 30.0 may be good

    for (int stepCnt = 0; stepCnt < _stepMax; stepCnt++) {
      float theta = slant + TWO_PI * floor(random(4.0)) / 4.0;
      x += round(stepDiv * cos(theta));
      y += round(stepDiv * sin(theta));
      if (x >= _xMax || x <= 0 || y >= _yMax || y <= 0) {
        break;
      }
      path.add(new PVector(x, y));
    }
    paths.add(path);
  }
  return paths;
}

/**
 * drawPath : draw perspective drawing.
 *            tuning alpha value with distance from drawing primary color.
 * @param _paths  : array of path. it must be nest for vertex.
 * @param _img    : original image.
 * @param _iW     : original image's width. for calculating pixel location.
 * @param _hueVal : drawing primary color.
 */
void drawPath(ArrayList<ArrayList<PVector>> _paths, PImage _img, int iW, float _hueVal) {
  
  for (ArrayList<PVector> path : _paths) {
    noStroke();
    beginShape(QUAD_STRIP);
    for (PVector p : path) {
      int pIdx = floor(p.y * iW + p.x);
      float hueDiv = abs(hue(_img.pixels[pIdx]) - _hueVal);
      float pAlp = (hueDiv > 180 ? 360.0 - hueDiv : hueDiv) / 180.0;
      fill(
           _hueVal,
           100.0 - saturation(_img.pixels[pIdx]),
           100.0 - brightness(_img.pixels[pIdx]),
           10.0 * pAlp
           );
      vertex(p.x, p.y);
    }
    endShape();

    noFill();
    strokeWeight(floor(random(1.0, 3.0)));
    beginShape(LINES);
    for (PVector p : path) {
      int pIdx = floor(p.y * iW + p.x);
      float hueDiv = abs(hue(_img.pixels[pIdx]) - _hueVal);
      float pAlp = (hueDiv > 180 ? 360.0 - hueDiv : hueDiv) / 180.0;
      stroke(
             _hueVal,
             100.0 - saturation(_img.pixels[pIdx]),
             100.0 - brightness(_img.pixels[pIdx]),
             100.0 * pAlp
             );
      vertex(p.x, p.y);
    }
    endShape();
  }
}

/**
 * casing : draw fancy casing
 * @param _w, _h     : width and height of drawing area.
 * @param _caseWidth : border width of the case.
 */
public void casing(float _w, float _h, float _caseWidth) {

  pushMatrix();
  translate(_w * 0.5, _h * 0.5);
  fill(0.0, 0.0, 0.0, 0.0);
  strokeWeight(_caseWidth * 0.85);
  stroke(0.0, 0.0, 30.0, 100.0);
  rect(0.0, 0.0, _w + _caseWidth * 1.25, _h + _caseWidth * 1.25);
  strokeWeight(_caseWidth * 0.75);
  stroke(0.0, 0.0, 100.0, 100.0);
  rect(0.0, 0.0, _w + _caseWidth * 1.25, _h + _caseWidth * 1.25);
  noStroke();
  noFill();
  popMatrix();
  
}


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



 

Yet another example image.

With the image from
Orange And White Cat Closeup
http://www.photos-public-domain.com/2011/01/07/orange-and-white-cat-closeup/
 

 

No comments :

Post a Comment