This progress report is published a bit earlier than usual to coincide with the release of version 1.1.0.
Python module enhancements
The python module has seen quite a lot of development this month in order to make it possible to do export tasks in continuous integration environments and other scripts.
Adding STEP export to the python module was pretty straightforward as the STEP exporter itself doesn’t have any dependencies inside of Horizon EDA. However this means that the python module now has a hard dependency on opencascade.
Implementing running checks from the python module was a bit more involved though as it required two changes to be made beforehand:
First of all, we
need to be able to return the check results. The easiest way of
accomplishing this is serializing the results as JSON as there’s already
code to create a python
dict from a C++
Second, we need to provide something that implements
it’s required by the check cache. Up until this point the only
IDocument was to be found in the
that’s unsuitable for inclusion in the python module as it has indirect
UI dependencies. To make it easier to write other classes that
implement this interface, common parts were factored out from
All of this was enabled by the introduction of
IDocument interface a few months earlier.
With this in place, the only left things left to do were to make the
BoardWrapper class that holds the
associated objects inherit from
DocumentBoard, implement the
required virtual methods and write the code that calls the checks and
takes care of argument type conversion.
This makes Horizon EDA one of the few PCB layout tools that can run a headless design rule check.
With software projects these days, it’s usual to have some sort of continuous integration that tests pull requests as they’re opened to make sure that the code still builds (and passes tests) with the PR merged. So why not have CI for the pool repository?
The first step in this is to make sure that the pool still updates without errors, i.e. all dependencies are met. To do so the python module gained support for opening and updating pools.
To make the pool CI convenient to implement on a variety of CI platforms, the horizon python module is now available on dockerhub! The Dockerfile to build the image is quite simple thanks to Horizon EDA trying to be as maintainer-friendly as possible.
Continuous integration for the pool repository
Exporting 3D renderings
Since setting up continuous integration can be a bit dull at times, I decided it’s time for some fun features that nobody ever asked for but might come in handy nevertheless!
Originally, the only way to export a 3D rendering of the PCB was to take a screenshot of the 3D view which has several shortcomings:
- Hard to reproduce the exact camera angle
- No alpha transparency
- Resolution limited by screen resolution
- Impossible to automate
All of these shortcomings have been eliminated with the python module now
being able to export 3D renderings. In order to reuse the existing
OpenGL code and shaders, OSMesa
is used to create an offscreen OpenGL context without the need for any
GPU hardware as it makes use of Mesa’s software renderer. All of the UI-independent parts of
factored out into
Canvas3DBase to avoid code duplication.
The rendered image can then either be
directly saved to a PNG file or retrieved as a cairo surface for
further post processing.
Since exporting a single still image is boring, let’s have some fun by exporting many turning them into a silly loop, because we can!
Here’s the code:
import horizon import numpy as np import subprocess import matplotlib prj = horizon.Project('hubble-pub/hw/main/hubble.hprj') brd = prj.open_board() ex = brd.export_3d(720, 540) ex.load_3d_models() ex.view_all() ex.cam_elevation=-30-90 ex.render_background = True ex.background_top_color = matplotlib.colors.to_rgb("#333365") ex.background_bottom_color = matplotlib.colors.to_rgb("#B3A26B") ex.cam_distance *= 1.2 prefix = 'brd' n = 60*5 for i, angle, h in zip(range(n), np.linspace(0, 360, n, endpoint=False), np.linspace(0, 1, n, endpoint=False)) : ex.cam_azimuth = angle ex.solder_mask_color = matplotlib.colors.hsv_to_rgb((h,1,.9)) ex.render_to_png("%s%02d.png"%(prefix, i)) print(i) subprocess.call(["ffmpeg", "-y", "-r", "60", "-i", prefix+"%02d.png", "-pix_fmt", "yuv420p", prefix+".mp4"])
Roundoff polygon vertex
Unfortunately, no one jumped in to solve #322 and other users started asking for this feature, so I decided to implement it. Compared to other tools, implementing this tool involved a bit of trigonometry to be worked out beforehand in order to place the arc’s center at the correct position. This tool also makes use of the non-modal tool window infrastructure to provide a means of directly entering the radius with a live preview.
Select on work layer only
When the selection filter got revamped to also support filtering layers, the “Select only on work layer” checkbox got removed as I deemed it’s now redundant. However, users told me that it made working on boards with many layers more difficult than before. To alleviate this, the selection filter dialog grew a new checkbox “Work layer only” that makes the selection filter behave the same way as it did before as in that the selection filter specifies the objects and the work layer specifies the layer objects can be selected on.
Last but not least, this progress report marks the release of version 1.1.0 “Blue sky”. For a summary of the changes relative to version 1.0.0, see the changelog or the last progress report posts.