Converting sketch from p5.js to Processing (Image art: Messy Curve Draw)

Hi, I’ve found a beautiful starting point for what I am intending to achieve: drawing a portrait of a face with curved lines and exporting it to SVG.

However, it’s written in p5.js and the p5.js version doesn’t support exporting it to an SVG as well. I’ve tried for a few days to get the SVG export working but it doesn’t want to fly.

Now I’ve set out to convert the script to a processing sketch, luckily it isn’t too heavy of a script:
tab 1 has 71 lines
tab 2 has 152 lines

Anyway, I’ve gotten somewhere, but my knowledge just isn’t developed well enough to try it on my own. I’m getting stuck at where the ‘fget’ function is called in tab 1. Fget does appear in tab #2 called paint, but it’s not created as a function. I believe this is where it gets over my head. I have tried converting the createVector commands to PVector, I hope I did that well.

I’m hoping that someone with knowledge in both languages can help me out. I understand what the script does, I have an understanding of the functions and how the data is handled, but I cannot make it my own enough so that I can rewrite it yet.

The original P5 script can be found here:

I need the end result to be saved as an SVG so the animation part isn’t important to me. I am probably able to do the SVG part by myself (I’ve done this countless of times in Processing). My main goal was to learn from this project, but I think the difficulty is too high. So at first I think it would be great to get some pointers and tips to get me on my way, I am not expecting anyone to rewrite the entire script for me. The eventual purpose is that I want to generate images for the pen plotter I’m working on at the same time.

As you will see I also gave up on renaming all the variables as I didn’t understand how I should call some variables in Processing. In p5.js it’s all just ‘var’ which makes it easy in p5.js but hard in Processing.

I hope to learn how to make this sketch work, have SVG export and maybe also a function that imports files. I tried copying parts from another script I had laying around but importing was also a bit complicated to me so I stuck with simply importing it in the script itself with ‘imgs[0] = loadImage(“test3.png”);’.

If you have advice or know anything to get me further, thanks for your time and effort.

My attempt so far;
Tab 1:

PImage imgs[];
int imgIndex = -1;
String img;
PVector paint;
float subStep = 1600;
float z = 0;
boolean isStop = false;
float count = 0;

void preload() {
  imgs[0] = loadImage("test3.png");
}

void setup() {
  size(600, 600);
  frameRate(60);
  PImage img = createImage(width, height, RGB);
  nextImage();
  //paint = new Paint(new PVector(width/2, height/2)); <<<-- Old p5.js approach
  PVector paint = new PVector(width/2, height/2); // <<<-- My failing Processing approach
  background(255);
  colorMode(RGB, 255);
}

void draw() {
  if (!isStop) {
    for (int i = 0; i < subStep; i++) {
      paint();
      //paint.show();
      //z+= 0.01;
    }
  }
  count++;
  if (count > width) {
    isStop = true;
  }
}

void fget(i, j) {
  var index = j * img.width + i;
  index *= 4;
  return color(img.pixels[index], img.pixels[index+1], img.pixels[index+2], img.pixels[index+3]);
}

void fset(i, j, c) {
  var index = j * img.width + i;
  index *= 4;
  img.pixels[index] = red(c);
  img.pixels[index+1] = green(c);
  img.pixels[index+2] = blue(c);
  img.pixels[index+3] = alpha(c);
}

void keyPressed() {
  console.log(key);
  if (key === 's' || key === 'S') {
    isStop = !isStop;
  }
}

void nextImage() {
  if (!img) { 
    return;
  }
  imgIndex = (++imgIndex) % imgs.length;
  var targetImg = imgs[imgIndex];
  img.copy(targetImg, 0, 0, targetImg.width, targetImg.height, 0, 0, img.width, img.height);
  //img.resize(width, height);
  img.loadPixels();
  clear();
}

Tab 2 called ‘paint’:

void Paint(p) {
  
  var ppos = p.copy();
  var pos = p.copy();
  PVector vel = new PVector(0, 0);
  PVector force = new PVector(0, 0);;
  
  float maxSpeed = 3.0;
  int perception = 5;
  int bound = 60;
  float boundForceFactor = 0.16;
  float noiseScale = 100.0;
  float noiseInfluence = 1 / 20.0;
  
  int dropRate = 0;
  int dropRange = 0;
  int dropAlpha = 150;
  int drawAlpha = 50;
  color drawColor = color(0, 0, 0, drawAlpha);
  int drawWeight = 1;
  int count = 0;
  int maxCount = 100;
  
  this.update = function() {
    ppos = pos.copy();
    force.mult(0);
    
    // Add pixels force
    PVector target = new PVector(0, 0);
    var count = 0;
    for (var i = -floor(perception/2) ; i < perception/2 ; i++ ) {
      for (var j = -floor(perception/2) ; j < perception/2 ; j++ ) {
        if (i == 0 && j == 0)
          continue;
        var x = floor(pos.x+i);
        var y = floor(pos.y+j);
        if (x <= img.width - 1 && x >= 0 && y < img.height-1 && y >= 0) {
          var c = fget(x, y);
          var b = brightness(c);
          b = 1 - b/100.0;
          PVector p = new PVector(i, j);
          target.add(p.normalize().copy().mult(b).div(p.mag()));
          count++;
        }
      }
    }
    if (count != 0) {
      force.add(target.div(count));
    }
    
    // Add noise force
    var n = noise(pos.x/noiseScale, pos.y/noiseScale, z);
    n = map(n, 0, 1, 0, 5*TWO_PI);
    var p = p5.Vector.fromAngle(n);
    if(force.mag() < 0.01)
      force.add(p.mult(noiseInfluence * 5));
    else
      force.add(p.mult(noiseInfluence));
    
    // Add bound force
    PVector boundForce = new PVector(0, 0);
    if (pos.x < bound) {
      boundForce.x = (bound-pos.x)/bound;
    } 
    if (pos.x > width - bound) {
      boundForce.x = (pos.x - width)/bound;
    } 
    if (pos.y < bound) {
      boundForce.y = (bound-pos.y)/bound;
    } 
    if (pos.y > height - bound) {
      boundForce.y = (pos.y - height)/bound;
    } 
    force.add(boundForce.mult(boundForceFactor));
    
    
    vel.add(force);
    vel.mult(0.9999);
    if (vel.mag() > maxSpeed) {
      vel.mult(maxSpeed/vel.mag());
    }
    
    pos.add(vel);
    if (pos.x > width || pos.x < 0 || pos.y > height || pos.y < 0) {
      this.reset();
    }
    
  }

  this.reset = function() {
    img.updatePixels();
    img.loadPixels();

    count = 0;
    //maxCount = 200;
    var hasFound = false;
    while (!hasFound) {
      pos.x = random(1)*width;
      pos.y = random(1)*height;
      var c = fget(floor(pos.x), floor(pos.y));
      var b = brightness(c);
      if(b < 35)
        hasFound = true;
    }
    drawColor = fget(floor(pos.x), floor(pos.y));
    drawColor.setAlpha(drawAlpha);
    ppos = pos.copy();
    vel.mult(0);
  }

  this.show = function() {
    count++;
    if (count > maxCount)
      this.reset();
    stroke(drawColor);
    strokeWeight(drawWeight);
    if (force.mag() > 0.1 && random(1) < dropRate) {
      drawColor.setAlpha(dropAlpha);
      stroke(drawColor);
      var boldWeight = drawWeight+random(5);
      strokeWeight(boldWeight);
      drawColor.setAlpha(drawAlpha);
    }
    line(ppos.x, ppos.y, pos.x, pos.y);
    
    this.fadeLineFromImg(ppos.x, ppos.y, pos.x, pos.y);
  }
  
  /* Fade the pixels of the line */
  this.fadeLineFromImg = function(x1, y1, x2, y2) {
    var xOffset = floor(abs(x1 - x2));
    var yOffset = floor(abs(y1 - y2));
    var step = xOffset < yOffset ? yOffset : xOffset;
    for (var i = 0 ; i < step ; i++) {
      var x = floor(x1 + (x2 - x1) * i / step);
      var y = floor(y1 + (y2 - y1) * i / step);
      var originColor = fget(x, y);

      var r = red(originColor);
      var g = green(originColor);
      var b = blue(originColor);

      originColor.setRed(r+50 > 255 ? 255 : r+50);
      originColor.setGreen(g+50 > 255 ? 255 : g+50);
      originColor.setBlue(b+50 > 255 ? 255 : b+50);

      fset(x, y, originColor);
      
    }
  }
  
}

The image I’m using can be found here:

Have a great day, I hope you are all doing well in these difficult times,

  • Marinus
1 Like

this doesn’t work

but it runs…

there must be an error… maybe he is looking at an empty image instead of the existing image of Che…? I wasn’t able to find the error, I am sorry.

But paint in the original Sketch is a CLASS.

Paint paintObject; 

PImage imgs[] = new PImage[1];
int imgIndex = -1;
PImage img;
PVector paint;
float subStep = 1600;
float z = 0.0;
boolean isStop = false;
float count = 0;

// ---------------------------------------------------------------------------

void setup() {
  size(960, 960);
  frameRate(60);

  imgs[0] = loadImage("test3.png");
  println(imgs[0].width, imgs[0].height, " is size");

  img = createImage(imgs[0].width, imgs[0].height, RGB);
  nextImage();
  // 
  paintObject = new Paint (new PVector(width/2, height/2)); // <<<-- My failing Processing approach

  background(255);
  colorMode(RGB, 255);
}

void draw() {

  if (!isStop) {
    for (int i = 0; i < subStep; i++) {
      // paint();
      paintObject.update();
      paintObject.show();
      z += 0.01;
    }
  }
  count++;
  if (count > width) {
    isStop = true;
  }

  if (keyPressed) 
    image(img, 0, 0); 
  //
}

// ------------------------------------------------------------------------

color fget(int i, int j) {
  int index = j * img.width + i;
  //  index *= 4; // ??????

  if (index>=img.pixels.length-5)
    return -1; 
  return 
    color(img.pixels[index], img.pixels[index+1], img.pixels[index+2], img.pixels[index+3]);
}//func 

void fset(int i, int j, color c) {
  int index = j * img.width + i;
  // index *= 4; // ????
  img.pixels[index] = color( red(c), 0, 0 );
  img.pixels[index+1] = int( green(c));
  img.pixels[index+2] = int( blue(c));
  img.pixels[index+3] = int( alpha(c));
}//func 

void keyPressed() {
  println(key);
  if (key == 's' || key == 'S') {
    isStop = !isStop;
  }
}

void nextImage() {
  if (img == null) {
    println("null");
    return;
  }

  imgIndex = 0; //?????
  // imgIndex = (++imgIndex) % imgs.length;  // ???????????????????????????
  PImage targetImg = imgs[imgIndex];
  img.copy(targetImg, 
    0, 0, targetImg.width, targetImg.height, 
    0, 0, img.width, img.height);

  //img.resize(width, height);

  println("nextImage");
  //image(img, 0, 0); 
  //noLoop(); 
  //return; 

  img.loadPixels();
  clear();
}

//================================================================================================

class Paint {

  PVector ppos;
  PVector pos;
  PVector vel = new PVector(0, 0);
  PVector force = new PVector(0, 0);

  float maxSpeed = 3.0;
  float perception = 5.0;
  int bound = 60;
  float boundForceFactor = 0.16;
  float noiseScale = 100.0;
  float noiseInfluence = 1 / 20.0;

  int dropRate = 0;
  int dropRange = 0;
  int dropAlpha = 150;
  int drawAlpha = 50;
  color drawColor = color(0, 0, 0, drawAlpha);
  int drawWeight = 1;
  int count = 0;
  int maxCount = 100;

  // --------------------------------------

  // constructor 
  Paint(PVector p_) {
    ppos = p_.copy();
    pos = p_.copy();
  }// constructor

  void update() {
    ppos = pos.copy();
    force.mult(0);

    // Add pixels force
    PVector target = new PVector(0, 0);
    int count = 0;
    for (int i = -floor(perception/2.0); i < perception/2.0; i++ ) {
      for (int j = -floor(perception/2.0); j < perception/2.0; j++ ) {
        if (i == 0 && j == 0)
          continue;
        int x = floor(pos.x+i);
        int y = floor(pos.y+j);
        if (x <= img.width - 1 && x >= 0 && y < img.height-1 && y >= 0) {
          int c = fget(x, y);
          float b = brightness(c);
          b = 1 - b/100.0;
          PVector p = new PVector(i, j);
          target.add(p.normalize().copy().mult(b).div(p.mag()));
          count++;
        }
      }
    }
    if (count != 0) {
      force.add(target.div(count));
    }

    // Add noise force
    float n = noise(pos.x/noiseScale, pos.y/noiseScale, z);
    n = map(n, 0, 1, 0, 5*TWO_PI); // was 5*TWO_PI 

    PVector p = PVector.fromAngle(n);
    if (force.mag() < 0.01)
      force.add(p.mult(noiseInfluence * 5));
    else
      force.add(p.mult(noiseInfluence));

    // Add bound force
    PVector boundForce = new PVector(0, 0);
    if (pos.x < bound) {
      boundForce.x = (bound-pos.x)/bound;
    } 
    if (pos.x > width - bound) {
      boundForce.x = (pos.x - width)/bound;
    } 
    if (pos.y < bound) {
      boundForce.y = (bound-pos.y)/bound;
    } 
    if (pos.y > height - bound) {
      boundForce.y = (pos.y - height)/bound;
    } 
    force.add(boundForce.mult(boundForceFactor));

    vel.add(force);
    vel.mult(0.9999);
    if (vel.mag() > maxSpeed) {
      vel.mult(maxSpeed/vel.mag());
    }

    pos.add(vel);
    if (pos.x > width || pos.x < 0 || pos.y > height || pos.y < 0) {
      reset();
    }
  }

  void reset() {
    //  println("reset"); 
    img.updatePixels();
    img.loadPixels();

    count = 0;
    maxCount = 200;
    boolean hasFound = false;
    while (!hasFound) {
      pos.x = random(1)*width;
      pos.y = random(1)*height;
      color c = fget(floor(pos.x), floor(pos.y));
      float b = brightness(c);
      if (b < 35)
        hasFound = true;
    }
    drawColor = fget(floor(pos.x), floor(pos.y));
    drawColor =
      color( red(drawColor), 
      green(drawColor), 
      blue(drawColor), 
      alpha(drawAlpha));
    ppos = pos.copy();
    vel.mult(0);
  }

  void show() {
    count++;
    if (count > maxCount)
      reset();
    stroke(drawColor);
    strokeWeight(drawWeight);
    if (force.mag() > 0.1 && random(1) < dropRate) {
      //drawColor.setAlpha(dropAlpha);
      drawColor =
        color( red(drawColor), 
        green(drawColor), 
        blue(drawColor), 
        alpha(dropAlpha));

      stroke(drawColor);
      float boldWeight = drawWeight+random(5);
      boldWeight = drawWeight;
      strokeWeight(boldWeight);
      //drawColor.setAlpha(drawAlpha);
      drawColor =
        color( red(drawColor), 
        green(drawColor), 
        blue(drawColor), 
        alpha(drawAlpha));
    }
    line(ppos.x, ppos.y, pos.x, pos.y);

    fadeLineFromImg(ppos.x, ppos.y, pos.x, pos.y);
  }

  /* Fade the pixels of the line */
  void fadeLineFromImg(float x1, float y1, 
    float x2, float y2) {
    int xOffset = floor(abs(x1 - x2));
    int yOffset = floor(abs(y1 - y2));
    int step = xOffset < yOffset ? yOffset : xOffset;
    for (int i = 0; i < step; i++) {
      int x = floor(x1 + (x2 - x1) * i / step);
      int y = floor(y1 + (y2 - y1) * i / step);
      color originColor = fget(x, y);

      float r = red(originColor);
      float g = green(originColor);
      float b = blue(originColor);

      originColor = color (
        (r+50 > 255 ? 255 : r+50), 
        (g+50 > 255 ? 255 : g+50), 
        (b+50 > 255 ? 255 : b+50));

      fset(x, y, originColor);
    }
  }
}
// ---

1 Like

Thanks a lot for this part of the puzzle! I see, maybe it’s imgIndex doing something weird… :thinking:

I’ve been going through the changes with the original file next to it so I can compare what has changed. I think the keyPressed part can also be removed right? Somehow it changes the line color to red. Maybe there’s a hint in that as well as to why it’s not using the image but a seemingly blank canvas to render the lines.

I have cleaned up some parts from your script, but have not been able to make it work as well:

Paint paintObject; 

PImage imgs[] = new PImage[1];
int imgIndex;
PImage img;
PVector paint;
float subStep = 1600;
float z = 0.0;
boolean isStop = false;
float count = 0;

// ---------------------------------------------------------------------------

void setup() {
  size(960, 960);
  frameRate(60);

  imgs[0] = loadImage("test3.png");
  println(imgs[0].width, imgs[0].height, " is size");

  img = createImage(imgs[0].width, imgs[0].height, ARGB);
  nextImage();
  // 
  paintObject = new Paint (new PVector(width/2, height/2));

  background(255);
  colorMode(RGB, 100);
}

void draw() {

  if (!isStop) {
    for (int i = 0; i < subStep; i++) {
      // paint();
      paintObject.update();
      paintObject.show();
      z += 0.01; // is this necessary?
    }
  }
  count++;
  if (count > width) {
    isStop = true;
  }
}

// ------------------------------------------------------------------------

color fget(int i, int j) {
  int index = j * img.width + i;

  if (index>=img.pixels.length-5)
    return -1; 
  return 
    color(img.pixels[index], img.pixels[index+1], img.pixels[index+2], img.pixels[index+3]);
}//func 

void fset(int i, int j, color c) {
  int index = j * img.width + i;
  // index *= 4; // ????
  img.pixels[index] = color( alpha(c), 0, 0, 0);
  img.pixels[index+1] = color( 0, red(c), 0, 0);
  img.pixels[index+2] = color( 0, 0, green(c), 0);
  img.pixels[index+3] = color( 0, 0, 0, blue(c));
}//func 

void nextImage() {
  if (img == null) {
    println("null");
    return;
  } else {

  imgIndex = 0; //????? what does this do?
  PImage targetImg = imgs[0];
  img.copy(targetImg, 
    0, 0, targetImg.width, targetImg.height, 
    0, 0, img.width, img.height);

  println("nextImage");

  img.loadPixels();
  clear();
  }
}

//================================================================================================

class Paint {

  PVector ppos;
  PVector pos;
  PVector vel = new PVector(0, 0);
  PVector force = new PVector(0, 0);

  float maxSpeed = 3.0;
  float perception = 5.0;
  int bound = 60;
  float boundForceFactor = 0.16;
  float noiseScale = 100.0;
  float noiseInfluence = 1 / 20.0;

  int dropRate = 0;
  int dropRange = 0;
  int dropAlpha = 100;
  int drawAlpha = 30;
  color drawColor = color(drawAlpha, 0, 0, 0);
  int drawWeight = 1;
  int count = 0;
  int maxCount = 100;

  // --------------------------------------

  // constructor 
  Paint(PVector p_) {
    ppos = p_.copy();
    pos = p_.copy();
  }// constructor

  void update() {
    ppos = pos.copy();
    force.mult(0);

    // Add pixels force
    PVector target = new PVector(0, 0);
    int count = 0;
    for (int i = -floor(perception/2.0); i < perception/2.0; i++ ) {
      for (int j = -floor(perception/2.0); j < perception/2.0; j++ ) {
        if (i == 0 && j == 0)
          continue;
        int x = floor(pos.x+i);
        int y = floor(pos.y+j);
        if (x <= img.width - 1 && x >= 0 && y < img.height-1 && y >= 0) {
          int c = fget(x, y);
          float b = brightness(c);
          b = 1 - b/100.0;
          PVector p = new PVector(i, j);
          target.add(p.normalize().copy().mult(b).div(p.mag()));
          count++;
        }
      }
    }
    if (count != 0) {
      force.add(target.div(count));
    }

    // Add noise force
    float n = noise(pos.x/noiseScale, pos.y/noiseScale, z);
    n = map(n, 0, 1, 0, 5*TWO_PI); // was 5*TWO_PI 

    PVector p = PVector.fromAngle(n);
    if (force.mag() < 0.01)
      force.add(p.mult(noiseInfluence * 5));
    else
      force.add(p.mult(noiseInfluence));

    // Add bound force
    PVector boundForce = new PVector(0, 0);
    if (pos.x < bound) {
      boundForce.x = (bound-pos.x)/bound;
    } 
    if (pos.x > width - bound) {
      boundForce.x = (pos.x - width)/bound;
    } 
    if (pos.y < bound) {
      boundForce.y = (bound-pos.y)/bound;
    } 
    if (pos.y > height - bound) {
      boundForce.y = (pos.y - height)/bound;
    } 
    force.add(boundForce.mult(boundForceFactor));

    vel.add(force);
    vel.mult(0.9999);
    if (vel.mag() > maxSpeed) {
      vel.mult(maxSpeed/vel.mag());
    }

    pos.add(vel);
    if (pos.x > width || pos.x < 0 || pos.y > height || pos.y < 0) {
      reset();
    }
  }

  void reset() {
    //  println("reset"); 
    img.updatePixels();
    img.loadPixels();

    count = 0;
    maxCount = 200;
    boolean hasFound = false;
    while (!hasFound) {
      pos.x = random(1)*width;
      pos.y = random(1)*height;
      color c = fget(floor(pos.x), floor(pos.y));
      float b = brightness(c);
      if (b < 35)
        hasFound = true;
    }
    drawColor = fget(floor(pos.x), floor(pos.y));
    drawColor =
      color( alpha(drawAlpha), 
      red(drawColor), 
      green(drawColor), 
      blue(drawColor));
    ppos = pos.copy();
    vel.mult(0);
  }

  void show() {
    count++;
    if (count > maxCount)
      reset();
    stroke(drawColor);
    strokeWeight(drawWeight);
    if (force.mag() > 0.1 && random(1) < dropRate) {
      drawColor =
        color( alpha(dropAlpha), 
        red(drawColor), 
        green(drawColor), 
        blue(drawColor));

      stroke(drawColor);
      float boldWeight = drawWeight+random(5);
      boldWeight = drawWeight;
      strokeWeight(boldWeight);
      //drawColor.setAlpha(drawAlpha);
      drawColor =
        color( alpha(drawAlpha), 
        red(drawColor), 
        green(drawColor), 
        blue(drawColor));
    }
    line(ppos.x, ppos.y, pos.x, pos.y);

    fadeLineFromImg(ppos.x, ppos.y, pos.x, pos.y);
  }

  /* Fade the pixels of the line */
  void fadeLineFromImg(float x1, float y1, 
    float x2, float y2) {
    int xOffset = floor(abs(x1 - x2));
    int yOffset = floor(abs(y1 - y2));
    int step = xOffset < yOffset ? yOffset : xOffset;
    for (int i = 0; i < step; i++) {
      int x = floor(x1 + (x2 - x1) * i / step);
      int y = floor(y1 + (y2 - y1) * i / step);
      color originColor = fget(x, y);

      float r = red(originColor);
      float g = green(originColor);
      float b = blue(originColor);

      originColor = color (
        (r+50 > 255 ? 255 : r+50), 
        (g+50 > 255 ? 255 : g+50), 
        (b+50 > 255 ? 255 : b+50));

      fset(x, y, originColor);
    }
  }
}
// ---

Thanks a lot for your efforts so far. Is there a way to test if variable are being passed on from one part to the next? I think you’re right, it seems to be looking at an empty image, or maybe at the image the wrong way?

Edit:
I think I’ve found why it would draw lines in red, because the image mode was RGB and not ARGB, so it’s missing the alpha channel and therefor seeing the alpha channel as red in this variable: color drawColor = color(drawAlpha, 0, 0, 0);

I’ve been trying to play with the ARGB and Alpha order, changing values, not there yet, but there might be something in there. Problem is that I only half know what I’m doing hehe. I also see createImage supports RGB and not ARGB.

Also I’ve changed the order of drawColor to this:

    drawColor =
      color( alpha(drawAlpha), 
      red(drawColor), 
      green(drawColor), 
      blue(drawColor));

But no change yet. Still the main image seems to lie in that it doesn’t seem to use the actual image.

I’ve also edited this part:

  img.pixels[index] = color( alpha(c), 0, 0, 0);
  img.pixels[index+1] = color( 0, red(c), 0, 0);
  img.pixels[index+2] = color( 0, 0, green(c), 0);
  img.pixels[index+3] = color( 0, 0, 0, blue(c));

But I have no clue if that makes it better or worse.

1 Like

here

I marked some lines with ??? and with !!!



Paint paintObject; 

PImage imgs[] = new PImage[1];
int imgIndex = -1;
PImage img;
PVector paint;
float subStep = 1600;
float z = 0.0;
boolean isStop = false;
float count = 0;

// ---------------------------------------------------------------------------

void setup() {
  size(960, 960);
  frameRate(60);

  imgs[0] = loadImage("test3.png");
  println(imgs[0].width, imgs[0].height, " is size");

  img = createImage(imgs[0].width, imgs[0].height, RGB);
  nextImage();
  // 
  paintObject = new Paint (new PVector(width/2, height/2)); // <<<-- My failing Processing approach

  background(255);
  colorMode(RGB, 255);
}

void draw() {

  if (!isStop) {
    for (int i = 0; i < subStep; i++) {
      paintObject.update();
      paintObject.show();
      z += 0.01;
    }
  }
  count++;
  if (count > width) {
    isStop = true;
  }

  if (keyPressed) 
    image(img, 0, 0); 
  //
}

// ------------------------------------------------------------------------

void keyPressed() {
  println(key);
  if (key == 's' || key == 'S') {
    isStop = !isStop;
  }
}

// ------------------------------------------------------------------------

void nextImage() {
  if (img == null) {
    println("null");
    return;
  }

  imgIndex = 0; //?????
  // imgIndex = (++imgIndex) % imgs.length;  // ???????????????????????????
  PImage targetImg = imgs[imgIndex];
  img.copy(targetImg, 
    0, 0, targetImg.width, targetImg.height, 
    0, 0, img.width, img.height);

  //img.resize(width, height);

  println("nextImage");
  //image(img, 0, 0); 
  //noLoop(); 
  //return; 

  img.loadPixels();
  //clear();
}

//================================================================================================

class Paint {

  PVector ppos;
  PVector pos;
  PVector vel = new PVector(0, 0);
  PVector force = new PVector(0, 0);

  float maxSpeed = 3.0;
  float perception = 5.0;
  int bound = 60;
  float boundForceFactor = 0.16;
  float noiseScale = 100.0;
  float noiseInfluence = 1 / 20.0;

  float dropRate = 0.881; // 0.991;  or 1   // ????!!!!!
  //  float dropRange = 0;
  int dropAlpha = 150;
  int drawAlpha = 50;
  color drawColor = color(0, 0, 0, drawAlpha);
  float drawWeight = 0.58; // was 1 ????!!!!!
  int count = 0;
  int maxCount = 100;

  // --------------------------------------

  // constructor 
  Paint(PVector p_) {
    ppos = p_.copy();
    pos = p_.copy();
  }// constructor

  // --------------------------------------

  color fget(int i, int j) {
    int index = j * img.width + i;
    //  index *= 4; // ??????

    if (index>=img.pixels.length)
      return -1; 
    return 
      color(img.pixels[index]);  // changed this !!!!!!
  }//func 

  void fset(int i, int j, color c) {
    int index = j * img.width + i;
    // index *= 4; // ????

    //img.pixels[index] = c; // color( red(c), 0, 0 ); //  also possible !  ?????!!!!

    img.pixels[index]   =  int( red(c));
    img.pixels[index+1] = int( green(c));
    img.pixels[index+2] = int( blue(c));
    img.pixels[index+3] = int( alpha(c));
    // */
  }//func 

  void update() {
    ppos = pos.copy();
    force.mult(0);

    // Add pixels force
    PVector target = new PVector(0, 0);
    int count = 0;
    for (int i = -floor(perception/2.0); i < perception/2.0; i++ ) {
      for (int j = -floor(perception/2.0); j < perception/2.0; j++ ) {
        if (i == 0 && j == 0)
          continue;
        int x = floor(pos.x+i);
        int y = floor(pos.y+j);
        if (x <= img.width - 1 && x >= 0 && 
          y < img.height-1 && y >= 0) {
          int c = fget(x, y);
          float b = brightness(c);
          b = 1 - b/100.0;
          PVector p = new PVector(i, j);
          target.add(p.normalize().copy().mult(b).div(p.mag()));
          count++;
        }
      }
    }
    if (count != 0) {
      force.add(target.div(count));
    }

    // Add noise force
    float n = noise(pos.x/noiseScale, pos.y/noiseScale, z);
    n = map(n, 0, 1, 0, 5*TWO_PI); // was 5*TWO_PI 

    PVector p = PVector.fromAngle(n);
    if (force.mag() < 0.01)
      force.add(p.mult(noiseInfluence * 5));
    else
      force.add(p.mult(noiseInfluence));

    // Add bound force
    PVector boundForce = new PVector(0, 0);
    if (pos.x < bound) {
      boundForce.x = (bound-pos.x)/bound;
    } 
    if (pos.x > width - bound) {
      boundForce.x = (pos.x - width)/bound;
    } 
    if (pos.y < bound) {
      boundForce.y = (bound-pos.y)/bound;
    } 
    if (pos.y > height - bound) {
      boundForce.y = (pos.y - height)/bound;
    } 
    force.add(boundForce.mult(boundForceFactor));

    vel.add(force);
    vel.mult(0.9999);
    if (vel.mag() > maxSpeed) {
      vel.mult(maxSpeed/vel.mag());
    }

    pos.add(vel);
    if (pos.x > width || pos.x < 0 || pos.y > height || pos.y < 0) {
      reset();
    }
  }

  void reset() {
    //  println("reset"); 
    img.updatePixels();
    img.loadPixels();

    count = 0;
    //  maxCount = 200;
    boolean hasFound = false;
    while (!hasFound) {
      pos.x = random(1)*width;
      pos.y = random(1)*height;
      color c = fget(floor(pos.x), floor(pos.y));
      float b = brightness(c);
      if (b < 35) {
        // println("hasFound"); 
        hasFound = true;
      }//if
    }
    drawColor = fget(floor(pos.x), floor(pos.y));
    drawColor = color( 
      red(drawColor), 
      green(drawColor), 
      blue(drawColor), 
      (drawAlpha));
    ppos = pos.copy();
    vel.mult(0);
  }

  void show() {
    count++;
    if (count > maxCount)
      reset();
    stroke(drawColor);
    strokeWeight(drawWeight);
    if (force.mag() > 0.1 && random(1) < dropRate) {
      //drawColor.setAlpha(dropAlpha);
      drawColor = color( 
        red(drawColor), 
        green(drawColor), 
        blue(drawColor), 
        (dropAlpha));

      stroke(drawColor);
      float boldWeight = drawWeight+random(5);
      boldWeight = drawWeight;
      strokeWeight(boldWeight);
      //drawColor.setAlpha(drawAlpha);
      drawColor =
        color( red(drawColor), 
        green(drawColor), 
        blue(drawColor), 
        alpha(drawAlpha));
    }
    line(ppos.x, ppos.y, pos.x, pos.y);

    fadeLineFromImg(ppos.x, ppos.y, pos.x, pos.y);
  }

  /* Fade the pixels of the line */
  void fadeLineFromImg(float x1, float y1, 
    float x2, float y2) {
    int xOffset = floor(abs(x1 - x2));
    int yOffset = floor(abs(y1 - y2));
    int step = xOffset < yOffset ? yOffset : xOffset;
    for (int i = 0; i < step; i++) {
      int x = floor(x1 + (x2 - x1) * i / step);
      int y = floor(y1 + (y2 - y1) * i / step);
      color originColor = fget(x, y);

      float r = red(originColor);
      float g = green(originColor);
      float b = blue(originColor);

      originColor = color (
        (r+50 > 255 ? 255 : r+50), 
        (g+50 > 255 ? 255 : g+50), 
        (b+50 > 255 ? 255 : b+50));

      fset(x, y, originColor);
    }//for
  }//method
  //
}//class
// ---

1 Like

Wow, it’s working!!! I realize only now it’s actually a trick that’s being used, making parts of the curve white so it appears as if the curves are only black curves making the face. Very interesting.

I guess in the way the paths are now generated this also causes SVG output to only save one frame. I’m going to investigate how I can change that.

Seems like fset all the way at the bottom decided what’s white and black in this curve. I guess I need to add the points to an array each time the loop is performed. Uhoh, arrays again. I need to go to array bootcamp. I keep getting one frame right now… Even with an array… Hmm.

Thanks a lot. If I run into a wall I’ll be back.

Update:
Okay I’m messing up, this array tryout is creating SVG files that are 70 to 90 megabytes :joy: also it’s giving memory errors in Processing itself. I think part of the issue might be that it’s maybe generating a line segment for each pixel?

I wrote a tryout to store some data in an array, I think this is the data I need from line 263 in your latest example:

fadeLineFromImg(ppos.x, ppos.y, pos.x, pos.y);
    
      //if (r+50 <255) {
        px10 = ppos.x;
        py10 = ppos.y;
        x10 = pos.x;
        y10 = pos.y;
        points.add(new PVector(px10, py10) );                  // save to ArrayList
        points.add(new PVector(x10, y10) );                  // save to ArrayList
      //}
      beginShape();
      if (frameCount >1)  for ( int d = 1; d < points.size(); d++ ){
          line(points.get(d-1).x, points.get(d-1).y, points.get(d).x, points.get(d).y );  // draw all
      }
      endShape();

This is what I tried, but it generates:
OutOfMemoryError: GC overhead limit exceeded

Here is the complete file, I added in SVG output capability on key press R:

import processing.svg.*;
boolean record;

Paint paintObject; 

PImage imgs[] = new PImage[1];
int imgIndex = -1;
PImage img;
PVector paint;
float subStep = 1600;
float z = 0.0;
boolean isStop = false;
float count = 0;
float x10, y10, px10, py10;

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

// ---------------------------------------------------------------------------

void setup() {
  size(960, 960);

  imgs[0] = loadImage("test3.png");
  println(imgs[0].width, imgs[0].height, " is size");

  img = createImage(imgs[0].width, imgs[0].height, RGB);
  nextImage();
  // 
  paintObject = new Paint (new PVector(width/2, height/2)); // <<<-- My failing Processing approach

  background(255);
  colorMode(RGB, 255);
  String outfile = "data/"+getClass().getName()+day()+month()+year()+hour()+minute()+second()+".svg";
  beginRecord(SVG, outfile);
}

void draw() {

  if (!isStop) {
    for (int i = 0; i < subStep; i++) {
      paintObject.update();
      paintObject.show();
      z += 0.01;
    }
  }
  count++;
  if (count > width) {
    isStop = true;
    println("Ready! Press key [r] to save");
  }
}

// ------------------------------------------------------------------------

//void keyPressed() {
//  println(key);
//  if (key == 's' || key == 'S') {
//    isStop = !isStop;
//  }
//}

// ------------------------------------------------------------------------

void nextImage() {
  if (img == null) {
    println("null");
    return;
  }

  imgIndex = 0; //?????
  // imgIndex = (++imgIndex) % imgs.length;  // ???????????????????????????
  PImage targetImg = imgs[imgIndex];
  img.copy(targetImg, 
    0, 0, targetImg.width, targetImg.height, 
    0, 0, img.width, img.height);

  //img.resize(width, height);

  println("nextImage");
  //image(img, 0, 0); 
  //noLoop(); 
  //return; 

  img.loadPixels();
  //clear();
}

//================================================================================================

class Paint {

  PVector ppos;
  PVector pos;
  PVector vel = new PVector(0, 0);
  PVector force = new PVector(0, 0);

  float maxSpeed = 3.0;
  float perception = 5.0;
  int bound = 60;
  float boundForceFactor = 0.16;
  float noiseScale = 100.0;
  float noiseInfluence = 1 / 20.0;

  float dropRate = 0.881; // 0.991;  or 1   // ????!!!!!
  //  float dropRange = 0;
  int dropAlpha = 150;
  int drawAlpha = 50;
  color drawColor = color(0, 0, 0, drawAlpha);
  float drawWeight = 0.58; // was 1 ????!!!!!
  int count = 0;
  int maxCount = 100;

  // --------------------------------------

  // constructor 
  Paint(PVector p_) {
    ppos = p_.copy();
    pos = p_.copy();
  }// constructor

  // --------------------------------------

  color fget(int i, int j) {
    int index = j * img.width + i;
    //  index *= 4; // ??????

    if (index>=img.pixels.length)
      return -1; 
    return 
      color(img.pixels[index]);  // changed this !!!!!!
  }//func 

  void fset(int i, int j, color c) {
    int index = j * img.width + i;
    // index *= 4; // ????

    //img.pixels[index] = c; // color( red(c), 0, 0 ); //  also possible !  ?????!!!!

    img.pixels[index]   =  int( red(c));
    img.pixels[index+1] = int( green(c));
    img.pixels[index+2] = int( blue(c));
    img.pixels[index+3] = int( alpha(c));
    // */
  }//func 

  void update() {
    ppos = pos.copy();
    force.mult(0);

    // Add pixels force
    PVector target = new PVector(0, 0);
    int count = 0;
    for (int i = -floor(perception/2.0); i < perception/2.0; i++ ) {
      for (int j = -floor(perception/2.0); j < perception/2.0; j++ ) {
        if (i == 0 && j == 0)
          continue;
        int x = floor(pos.x+i);
        int y = floor(pos.y+j);
        if (x <= img.width - 1 && x >= 0 && 
          y < img.height-1 && y >= 0) {
          int c = fget(x, y);
          float b = brightness(c);
          b = 1 - b/100.0;
          PVector p = new PVector(i, j);
          target.add(p.normalize().copy().mult(b).div(p.mag()));
          count++;
        }
      }
    }
    if (count != 0) {
      force.add(target.div(count));
    }

    // Add noise force
    float n = noise(pos.x/noiseScale, pos.y/noiseScale, z);
    n = map(n, 0, 1, 0, 5*TWO_PI); // was 5*TWO_PI 

    PVector p = PVector.fromAngle(n);
    if (force.mag() < 0.01)
      force.add(p.mult(noiseInfluence * 5));
    else
      force.add(p.mult(noiseInfluence));

    // Add bound force
    PVector boundForce = new PVector(0, 0);
    if (pos.x < bound) {
      boundForce.x = (bound-pos.x)/bound;
    } 
    if (pos.x > width - bound) {
      boundForce.x = (pos.x - width)/bound;
    } 
    if (pos.y < bound) {
      boundForce.y = (bound-pos.y)/bound;
    } 
    if (pos.y > height - bound) {
      boundForce.y = (pos.y - height)/bound;
    } 
    force.add(boundForce.mult(boundForceFactor));

    vel.add(force);
    vel.mult(0.9999);
    if (vel.mag() > maxSpeed) {
      vel.mult(maxSpeed/vel.mag());
    }

    pos.add(vel);
    if (pos.x > width || pos.x < 0 || pos.y > height || pos.y < 0) {
      reset();
    }
  }

  void reset() {
    //  println("reset"); 
    img.updatePixels();
    img.loadPixels();

    count = 0;
    //  maxCount = 200;
    boolean hasFound = false;
    while (!hasFound) {
      pos.x = random(1)*width;
      pos.y = random(1)*height;
      color c = fget(floor(pos.x), floor(pos.y));
      float b = brightness(c);
      if (b < 35) {
        // println("hasFound"); 
        hasFound = true;
      }//if
    }
    drawColor = fget(floor(pos.x), floor(pos.y));
    drawColor = color( 
      red(drawColor), 
      green(drawColor), 
      blue(drawColor), 
      (drawAlpha));
    ppos = pos.copy();
    vel.mult(0);
  }

  void show() {
    count++;
    if (count > maxCount)
      reset();
    stroke(drawColor);
    strokeWeight(drawWeight);
    if (force.mag() > 0.1 && random(1) < dropRate) {
      //drawColor.setAlpha(dropAlpha);
      drawColor = color( 
        red(drawColor), 
        green(drawColor), 
        blue(drawColor), 
        (dropAlpha));

      stroke(drawColor);
      float boldWeight = drawWeight+random(5);
      boldWeight = drawWeight;
      strokeWeight(boldWeight);
      //drawColor.setAlpha(drawAlpha);
      drawColor =
        color( red(drawColor), 
        green(drawColor), 
        blue(drawColor), 
        alpha(drawAlpha));
    }
    line(ppos.x, ppos.y, pos.x, pos.y);

    fadeLineFromImg(ppos.x, ppos.y, pos.x, pos.y);
    
      //if (r+50 <255) {
        px10 = ppos.x;
        py10 = ppos.y;
        x10 = pos.x;
        y10 = pos.y;
        points.add(new PVector(px10, py10) );                  // save to ArrayList
        points.add(new PVector(x10, y10) );                  // save to ArrayList
      //}
      beginShape();
      if (frameCount >1)  for ( int d = 1; d < points.size(); d++ ){
          line(points.get(d-1).x, points.get(d-1).y, points.get(d).x, points.get(d).y );  // draw all
      }
      endShape();
  }

  /* Fade the pixels of the line */
  void fadeLineFromImg(float x1, float y1, 
    float x2, float y2) {
    int xOffset = floor(abs(x1 - x2));
    int yOffset = floor(abs(y1 - y2));
    int step = xOffset < yOffset ? yOffset : xOffset;
    for (int i = 0; i < step; i++) {
      int x = floor(x1 + (x2 - x1) * i / step);
      int y = floor(y1 + (y2 - y1) * i / step);
      color originColor = fget(x, y);

      float r = red(originColor);
      float g = green(originColor);
      float b = blue(originColor);

      originColor = color (
        (r+50 > 255 ? 255 : r+50), 
        (g+50 > 255 ? 255 : g+50), 
        (b+50 > 255 ? 255 : b+50));

      fset(x, y, originColor);

}//for
  }//method
  //
}//class
// ---
void keyPressed() {
  if (key == 'r') { 
    endRecord();                   //press the 'r' key to save SVG
    String outfile = "data/"+getClass().getName()+day()+month()+year()+hour()+minute()+second()+".svg";
    beginRecord(SVG, outfile);
    println("press key [r] to save to "+outfile);
  }
}

Just to update I found someone else who has attempted the same and maybe had more success!
See this one:

I’m still looking into improving it a bit and of course get the white parts of lines to be filtered out and have SVG export. Maybe I should attempt some easier projects first before I continue on this one…

1 Like