Creating SVG only saves a single frame

Okay, so I have done the Coding Challenge #93 by Daniel Shiffman yesterday. It is a model to draw a line created by a double pendulum swing.

Now I want to plot the result with my DIY pen plotter but I can’t for the life of me figure out how to export the entire result as an SVG. I once got it to save only a single frame or animation but now I can’t even get that to work.

Now I believe this is due to how the script uses image() and PGraphics.

The best way of generating a line would probably be to create one long single line since the pen plotter works best with that.

Would anyone have a clue how I could convert this script so that it outputs both one long curvy line and makes SVG export possible?

float r1 = 200;
float r2 = 200;
float m1 = 40;
float m2 = 40;
float a1 = PI/2;
float a2 = PI/2;
float a1_v = 0;
float a2_v = 0;
float g = 1;

float px2 = -1;
float py2 = -1;

float cx, cy;

import processing.svg.*;
boolean record;

PGraphics canvas;

void setup() {
  size(900, 800);
  cx = width/2;
  cy = 350;
  canvas = createGraphics(width, height);
  canvas.beginDraw();
  canvas.background(255);
  canvas.endDraw();
}


void draw() {

  if (record) {
    //save to file with same name as the sketch + timestamp
    beginRecord(SVG, getClass().getName()+day()+month()+year()+hour()+minute()+second()+".svg");
  }

  float num1 = -g * (2 * m1 + m2) * sin(a1);
  float num2 = -m2 * g * sin(a1 - 2 * a2);
  float num3 = -2 * sin(a1 - a2) * m2;
  float num4 = a2_v * a2_v * r2 + a1_v * a1_v * r1 * cos(a1-a2);
  float den = r1 * (2 * m1 + m2 - m2 * cos(2 * a1 - 2 * a2));
  float a1_a = (num1 + num2 + num3 * num4) / den; 


  num1 = 2 * sin(a1 - a2);
  num2 = (a1_v * a1_v * r1 * (m1 + m2));
  num3 = g * (m1 + m2) * cos(a1);
  num4 = a2_v * a2_v * r2 * m2 * cos(a1-a2);
  den = r2 * (2 * m1 + m2 - m2 * cos(2 * a1 - 2 * a2));

  float a2_a = (num1 * (num2 + num3 + num4)) / den;
  image(canvas, 0, 0);
  stroke(0);
  strokeWeight(2);

  translate(cx, cy);

  float x1 = r1 * sin(a1); 
  float y1 = r1 * cos(a1);

  float x2 = x1 + r2 * sin(a2);
  float y2 = y1 + r2 * cos(a2);

  a1_v += a1_a;
  a2_v += a2_a;
  a1 += a1_v;
  a2 += a2_v;

  canvas.beginDraw();
  canvas.translate(cx, cy);
  canvas.strokeWeight(1);
  canvas.stroke(0);
  if (frameCount >1) {
    canvas.line(px2, py2, x2, y2);
  }
  canvas.endDraw();

  px2 = x2;
  py2 = y2;

  //saveFrame("output/card_####.png");
  //enable filesave for svg
  if (record == true) {
    endRecord();
    record = false;
  }
}
//press the 'r' key to save a frame
void keyPressed() {
  if (key == 'r') {
    record = true;
  }
}

I know the solution probably lies in how the drawing of the image is done by

image(canvas, 0, 0);

and

  canvas.beginDraw();
  canvas.translate(cx, cy);
  canvas.strokeWeight(1);
  canvas.stroke(0);
  if (frameCount >1) {
    canvas.line(px2, py2, x2, y2);
  }
  canvas.endDraw();

But I do not understand how to create a solution.

Also canvas.line(px2, py2, x2, y2); probably has to be substituted with curveVertex(); somehow in order to create one lone curvy line.

I hope the solution is simple, it is mostly me lacking the experience and knowledge and hope someone knows what to change in order to make SVG export work.

Thanks for your time,
Marinus

1 Like

-a- yes SVG only saves the last frame
-b- and not images like you created…

so i see only this way:

  • run your calculation in each loop like now,
  • save that new point (x2,y2) to a ArrayList
  • clear canvas ( background ) and draw all array points as lines in a FOR loop
  • can start the SVG at setup
  • stop at key press [r] ( save that last frame and open new SVG file )
//https://discourse.processing.org/t/creating-svg-only-saves-a-single-frame/14509
/*
Unlike the PDF renderer, the SVG renderer will only save the final frame of a sequence.
 */

import processing.svg.*;

float r1 = 200;
float r2 = 200;
float m1 = 40;
float m2 = 40;
float a1 = PI/2;
float a2 = PI/2;
float a1_v = 0;
float a2_v = 0;
float g = 1;

float px2 = -1;
float py2 = -1;

float cx, cy;
float num1, num2, num3, num4, den, a1_a, a2_a, x1, x2, y1, y2;   // x2,y2 is the new POINT! in each loop.

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

//PGraphics canvas;

void setup() {
  size(900, 800);
  stroke(0);
  strokeWeight(2);
  cx = width/2;
  cy = 350;
  String outfile = "data/"+getClass().getName()+day()+month()+year()+hour()+minute()+second()+".svg";
  beginRecord(SVG, outfile);
  println("press key [r] to save to "+outfile);
}


void draw() {
  background(255);
  translate(cx, cy);

  num1 = -g * (2 * m1 + m2) * sin(a1);
  num2 = -m2 * g * sin(a1 - 2 * a2);
  num3 = -2 * sin(a1 - a2) * m2;
  num4 = a2_v * a2_v * r2 + a1_v * a1_v * r1 * cos(a1-a2);
  den = r1 * (2 * m1 + m2 - m2 * cos(2 * a1 - 2 * a2));
  a1_a = (num1 + num2 + num3 * num4) / den; 

  num1 = 2 * sin(a1 - a2);
  num2 = (a1_v * a1_v * r1 * (m1 + m2));
  num3 = g * (m1 + m2) * cos(a1);
  num4 = a2_v * a2_v * r2 * m2 * cos(a1-a2);
  den = r2 * (2 * m1 + m2 - m2 * cos(2 * a1 - 2 * a2));
  a2_a = (num1 * (num2 + num3 + num4)) / den;


  x1 = r1 * sin(a1); 
  y1 = r1 * cos(a1);

  x2 = x1 + r2 * sin(a2);
  y2 = y1 + r2 * cos(a2);
  points.add(new PVector(x2, y2) );                  // save to ArrayList

  a1_v += a1_a;                                     // for next loop
  a2_v += a2_a;
  a1 += a1_v;
  a2 += a2_v;

  if (frameCount >1)  for ( int i = 1; i < points.size(); i++ ) line(points.get(i-1).x, points.get(i-1).y, points.get(i).x, points.get(i).y );     // darw all
}

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);
  }
}


this code might eat memory…

2 Likes

Wow, this works magic! Thanks a lot @kll

I have changed the line drawing method to:

  beginShape();
  if (frameCount >1)  for ( int i = 1; i < points.size(); i++ ){
    vertex(points.get(i-1).x, points.get(i-1).y);
    vertex(points.get(i).x, points.get(i).y );     // darw all
  }
  endShape();

With line() the line was segmented causing my pen plotter to lift the pen every mm. With the vertex method the line is constant and creates a smooth line. Will try a new test print now and let you know how it goes!

Edit: curveVertex() actually gives an even better result but also causes the script to be tediously slow and the eventual rendered curve is very heavy for my machine to draw. I will try to see what I can do to improve on this.

2 Likes

try again,
i not see the need for two vertex points every FOR loop

  beginShape();
  if (frameCount >1)  
    //for ( int i = 1; i < points.size(); i++ ) 
    //  line(points.get(i-1).x, points.get(i-1).y, points.get(i).x, points.get(i).y );     // darw all
    for ( int i = 0; i < points.size(); i++ )    
       vertex(points.get(i).x, points.get(i).y );  
  endShape();

1 Like

Thanks, you are right! I was using too many vertexes.

I have now made it so that it only saves one out of four curveVertex() calls and this saves up a HUGE amount of data and makes my machine not choke on the amount of datapoints.

So to update with an improved solution:

  beginShape();
  if (frameCount >1)  for ( int i = 1; i < points.size(); i++ ){
    if (i % 4 == 0){
    curveVertex(points.get(i).x, points.get(i).y );     // darw all
    }
  }
  endShape();

I’m so amazed. Two months ago I had never touched Processing nor Java. And now I’m learning all these new things and I even manage to do improvements by myself. This could never have taken place without forum members helping me out, so thanks a lot.

Here a not-so-perfect first try to draw the result on the DIY pen plotter! (silver ink on black paper)

2 Likes

I still keep running into issues with this script. Mainly that it generates curves with curveVertex() every frame. I had made a script that generated a new point every 4 frames for example but this caused other issues. The main problem is that my CNC controller chokes up on the amount of data this script generates. But when I use straight segments through using vertex() the machine is completely fine eating those even though they are also thousands of lines.

I accepted this now but am wondering if there is a way to connect the curveVertex() and simplify the amount of points somehow.

Printed result:

Edit;
It is possible to use ‘simplify’ in InkScape and reduce the amount of points greatly, however, this slightly alters the image and is yet another manual step. It would be interesting if this was possible within Processing:

hm, what you say is that

  • the SVG
  • AND the CNC machine

works with a few curve Vertex points.
but the calculation required to execute them is too much
or what means

sorry, it might be needed to check the SVG file lines

sure real vertex lines just A to B to C
not need much computing esp. array handling for a plotter.

no idea if there is a middle way,
like short curve vertex segments.
instead ONE 1000 points CURVE

just the control points thing ( add a (A-1) at begin and (B+1) point at end )
makes the for loop tricky.

but that segments could cause

too many options…