Can't save current frame as SVG

I have tried to find a solution to my issue, but couldn’t. I am using Processing 4 with Python Mode.
I am trying to save the current frame as SVG and exit by pressing the “s” key. However, I am getting the following error:

ClassCastException: class jdk.internal.loader.ClassLoaders$AppClassLoader cannot be cast to class java.net.URLClassLoader (jdk.internal.loader.ClassLoaders$AppClassLoader and java.net.URLClassLoader are in module java.base of loader 'bootstrap')
	at jycessing.LibraryImporter.addJarToClassLoader(LibraryImporter.java:315)
	at jycessing.LibraryImporter.recursivelyAddJarsToClasspath(LibraryImporter.java:164)
	at jycessing.LibraryImporter.addLibrary(LibraryImporter.java:140)
	at jycessing.LibraryImporter$1.__call__(LibraryImporter.java:82)
	at org.python.core.PyObject.__call__(PyObject.java:480)
	at org.python.core.PyObject.__call__(PyObject.java:484)
	at org.python.pycode._pyx38.f$0(sketch_220501a.pyde:1)
	at org.python.pycode._pyx38.call_function(sketch_220501a.pyde)
	at org.python.core.PyTableCode.call(PyTableCode.java:171)
	at org.python.core.PyCode.call(PyCode.java:18)
	at org.python.core.Py.runCode(Py.java:1614)
	at org.python.core.Py.exec(Py.java:1658)
	at org.python.pycode._pyx37.f$0(C:/Users/user/AppData/Local/Temp/sketch_220501a16544851592084895088/sketch_220501a.pyde:96)
	at org.python.pycode._pyx37.call_function(C:/Users/user/AppData/Local/Temp/sketch_220501a16544851592084895088/sketch_220501a.pyde)
	at org.python.core.PyTableCode.call(PyTableCode.java:171)
	at org.python.core.PyCode.call(PyCode.java:18)
	at org.python.core.Py.runCode(Py.java:1614)
	at org.python.core.Py.exec(Py.java:1658)
	at org.python.util.PythonInterpreter.exec(PythonInterpreter.java:276)
	at jycessing.PAppletJythonDriver.processSketch(PAppletJythonDriver.java:233)
	at jycessing.PAppletJythonDriver.findSketchMethods(PAppletJythonDriver.java:613)
	at jycessing.Runner.runSketchBlocking(Runner.java:399)
	at jycessing.mode.run.SketchRunner.lambda$2(SketchRunner.java:112)
	at java.base/java.lang.Thread.run(Thread.java:833)

Here is my full code:

add_library("svg")
save_frame = False
total_degrees = 360
radius = 0
center_x = 0
center_y = 0
frames = 0

def setup():
    global radius, center_x, center_y, svg_output
    size(1600, 1000)
    background(255)
    stroke(0)
    strokeWeight(1)
    noFill()
    svg_output = createGraphics(width, height, SVG, "file_01.svg")
    radius = height * 0.6
    center_x = width * 0.25
    center_y = height / 2

def draw():    
    global total_degrees, radius, center_x, center_y, frames
    if save_frame:
        beginRecord(svg_output)
        
    translate(frames * 3, 0)
    beginShape()
    for i in range(total_degrees):
        noiseFactor = noise(i * 0.0130, float(frameCount) * 0.0115)
        x = center_x + radius * cos(radians(i)) * noiseFactor
        y = center_y + radius * sin(radians(i)) * noiseFactor
        curveVertex(x, y)
    endShape(CLOSE)

    frames += 1
    radius -= 2
    if radius < 20:
        noLoop()
        
    if save_frame:
        endRecord()
        exit()
 
def keyPressed():
    global save_frame
    if key == "s":
        save_frame = True
        
    

I found this solution on the forum and tried to implement, but can’t debug the issue. Any advice is appreciated!

Edit: deleted parts of the code that were not relevant to the issue

Perhaps try Processing 3.5.4 instead. Processing 4 is still in beta, so there are issues. I tested the following code in Processing 3.5.4, and I can confirm it works fine –

add_library('svg')
save_frame = False

def setup():
    size(400, 400)

def draw():
    if save_frame:
        beginRecord(SVG, 'file_01.svg')
    square(200, 200, 100)
    if save_frame:
        endRecord()
        exit()
    
def keyPressed():
    global save_frame
    if key == 's':
        save_frame = True

Hi there,
Thanks for the prompt reply, I have now gone back to 3.5.4 until version 4 gets more reliable.

Your example seems to work, but the issue is that it only exports the current frame. For example, if I have a loop of translated circles, the “save_frame” wouldn’t export the view with all 300 of them, but only the 300th circle itself. How can I capture everything that has been drawn on my screen up until that moment?

It sounds like you don’t actually need the keyPressed() part? Like, you already know how many frames you’d like? So maybe change out the approach for something that employs a loop, which also means you don’t have to wait for the draw() to run 300 times –

add_library('svg')

def setup():
    size(400, 400, SVG, 'output.svg')
    noFill()

def draw():    
    for i in range(300):
        circle(random(width), random(height), 30) 
    exit()

No display window appears, but you’ll see the SVG file pop-out next to your sketch file.

This didn’t work for me either. I need to keep the option to stop the draw() at any point when I am satisfied with the result. Therefore a keypressed() is preferred option. It’s just that I can’t seem to find a way to export the full sketch and not only the last frame. Any ideas how to do that and keep the keyPressed() setup?

Here’s a hacky solution –

Maybe you use two sketches? One sketch to grab the noise-seed and frame-count values. Then another sketch that renders, which employs a loop and exits after a single draw() (like the example I suggested above).

Sketch 1 looks something like this –

r = int(random(10000))
print('noise seed: ' + str(r))
noiseSeed(r)

def setup():
    size(400, 400)
    noFill()

def draw():
    nx = noise(1, frameCount) * width
    ny = noise(2, frameCount) * height
    circle(nx, ny, 30)

def keyPressed():
    print('stop frame: ' + str(frameCount))
    noLoop()

Let’s say you stop on frame 75 with a noise-seed of 1316. You insert those values into a second sketch (to render the SVG) that looks like this –

add_library('svg')
noiseSeed(1316)  # noise seed value
stop_frame = 75  # stop frame value

def setup():
    size(400, 400, SVG, 'output.svg')
    noFill()

def draw():    
    for i in range(stop_frame):
        nx = noise(1, i) * width
        ny = noise(2, i) * height
        circle(nx, ny, 30)
    exit()

You might combine these two sketches into a single program, somehow. But this two-step process should work fine.