Progress Report April 2020
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.
STEP export
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.
DRC
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++ json
object.
Second, we need to provide something that implements IDocument
as
it’s required by the check cache. Up until this point the only
implementation of IDocument
was to be found in the Core
class,
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
Core
and CoreBoard
to Document
and DocumentBoard
.
All of this was enabled by the introduction of
the 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 Board
and
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.
Pool update
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.
Docker image
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
With the above things in place, actually implementing CI for the pool repository was rather straightforward and adding more checks in the future is even easier!
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 Canvas3D
were
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.
Release 1.1.0
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.