This is my fourth post in a series about the open source split-flap display I’ve been designing in my free time. Check out a video of the prototype.
Posts in the series:
Scripting KiCad Pcbnew exports
Automated KiCad, OpenSCAD rendering using Travis CI
Using UI automation to export KiCad schematics
Early when designing the split flap 3D model using OpenSCAD I wanted to include a visualization in the project’s README so others could see what it looked like. It’s possible to capture an image manually (File→Export→Export as Image), but that’s an extra thing to remember to do after every change and it’s also not very consistent. The image that’s exported is basically a snapshot of the current preview window, so the image dimensions and camera angle would be different each time. Plus, a single static image doesn’t fully convey the 3D model, so I wanted something more dynamic.
I was inspired by Bryan Duxbury’s blog post on creating an animated gif from an OpenSCAD model. He used OpenSCAD’s built-in animation feature, which lets you parameterize your model using a special animation time variable,
To fully automate this, I used OpenSCAD’s command-line interface which lets you specify options like
(This uses a simple Python wrapper to invoke OpenSCAD’s command line interface)
In addition to a simple rotation, I wanted to showcase different parts of the model in the animation. At the top of
Then from a script, I can invoke OpenSCAD using arguments like
Unfortunately, by invoking
Using a threadpool (
As a minor aside, it’s not really necessary to use separate threads, since each task is already launching a separate subprocess, but a threadpool provides a convenient abstraction for bounded parallel execution.
On my machine, rendering with a 4-thread Pool reduced the rendering time from 6 minutes 41 seconds down to just under 3 minutes!
The last step is to put all those frames together as an animated gif, which is fairly straightforward using ImageMagick:
The full script implementation can be found in the following files:
/3d/generate_gif.py
/3d/openscad.py
In a past blog post, I discussed how I run this script using Travis CI to automatically re-render the 3d animation every time I make a change to the source code. You should check it out if you haven’t already: Automated KiCad, OpenSCAD rendering using Travis CI.
Thanks for reading! In part 2 I’ll cover some more OpenSCAD tricks with similar command line scripting techniques to easily export a design for laser cutting.
Posts in the series:
Scripting KiCad Pcbnew exports
Automated KiCad, OpenSCAD rendering using Travis CI
Using UI automation to export KiCad schematics
OpenSCAD Rendering Tricks, Part 1: Animated GIF
Early when designing the split flap 3D model using OpenSCAD I wanted to include a visualization in the project’s README so others could see what it looked like. It’s possible to capture an image manually (File→Export→Export as Image), but that’s an extra thing to remember to do after every change and it’s also not very consistent. The image that’s exported is basically a snapshot of the current preview window, so the image dimensions and camera angle would be different each time. Plus, a single static image doesn’t fully convey the 3D model, so I wanted something more dynamic.
The final product: a 360° animation that cycles through three views of the model. |
I was inspired by Bryan Duxbury’s blog post on creating an animated gif from an OpenSCAD model. He used OpenSCAD’s built-in animation feature, which lets you parameterize your model using a special animation time variable,
$t
. To make a spinning animation, you can just wrap your model in a rotate
transformation proportional to $t
. This works well, but still requires some manual export steps from the GUI.To fully automate this, I used OpenSCAD’s command-line interface which lets you specify options like
--imgsize=width,height
and --camera=translatex,y,z,rotx,y,z,dist
to control the exported image. This makes it easy to write a script that exports snapshots from 360 degrees:
num_frames = 50
start_angle = 135
for i in range(num_frames):
angle = start_angle + (i * 360 / num_frames)
openscad.run(
'splitflap.scad',
'frame_%05d.png' % i,
output_size = [320, 240],
camera_translation = [0, 0, 0],
camera_rotation = [60, 0, angle],
camera_distance = 600,
)
(This uses a simple Python wrapper to invoke OpenSCAD’s command line interface)
In addition to a simple rotation, I wanted to showcase different parts of the model in the animation. At the top of
splitflap.scad
, I defined a few variables that control the visibility/opacity of the enclosure and flaps (this was also useful while designing the model):
render_enclosure = 1; // 2=opaque color; 1=translucent; 0=invisible
render_flaps = true;
Then from a script, I can invoke OpenSCAD using arguments like
-D render_enclosure=0 -D render_flaps=false
which override the variable definitions in the file. I use this so that over the course of three animated revolutions you can see all the different parts of the design.Three different views of the model by changing the render_enclosure and render_flaps variables. |
openscad
once per frame, the 3D model’s geometry needs to be recompiled for every camera angle rendered, which takes a nontrivial amount of time. With a desired 50 frames per revolution * 3 rendering options, that’s 150 total invocations of OpenSCAD! As far as I can tell there’s no easy way around this, but we can still speed it up by using multiple cores.Using a threadpool (
multiprocessing.dummy.Pool
in Python) we can enqueue each of the OpenSCAD frame-rendering tasks to be run in parallel across a specified number of workers. Since each OpenSCAD process uses up to a single core, we can choose a pool size to match the number of cores available.
from multiprocessing.dummy import Pool
num_frames = 50
start_angle = 135
def render_frame(i):
angle = start_angle + (i * 360 / num_frames)
openscad.run(
'splitflap.scad',
'frame_%05d.png' % i,
output_size = [320, 240],
camera_translation = [0, 0, 0],
camera_rotation = [60, 0, angle],
camera_distance = 600,
)
pool = Pool() # By default, Pool uses one thread per available CPU
for _ in pool.imap_unordered(render_frame, range(num_frames)):
# Consume results as they occur so any task exceptions are rethrown asap
pass
pool.close()
pool.join()
As a minor aside, it’s not really necessary to use separate threads, since each task is already launching a separate subprocess, but a threadpool provides a convenient abstraction for bounded parallel execution.
On my machine, rendering with a 4-thread Pool reduced the rendering time from 6 minutes 41 seconds down to just under 3 minutes!
The last step is to put all those frames together as an animated gif, which is fairly straightforward using ImageMagick:
convert 'frame_*.png' -set delay 1x15 animation.gif
The full script implementation can be found in the following files:
/3d/generate_gif.py
/3d/openscad.py
In a past blog post, I discussed how I run this script using Travis CI to automatically re-render the 3d animation every time I make a change to the source code. You should check it out if you haven’t already: Automated KiCad, OpenSCAD rendering using Travis CI.
Thanks for reading! In part 2 I’ll cover some more OpenSCAD tricks with similar command line scripting techniques to easily export a design for laser cutting.
This is cool! Thanks for sharing.
ReplyDelete