Announcing Sdfy package! It's a pure Nim signed distance function library for 2D graphics libraries.
Why? To make fast and nice looking shadows! Signed distance functions provide a nice mathematical way to get the nearest distance from any point to a shape. Positive distances are inside, while negative ones are outside. This can be given to a gaussian function with some tweaking out comes a decent looking shadow. There's a whole bunch more SDF functions here Íñigo Quílez: 2D Distance Functions.
For background, I've been on a quest lately to make better shadows for Figuro which has been a limit for the styling I tend to prefer: those with shadows and highlights giving a subtle physical feeling. Designs with more depth are making a bit of a comeback lately.
Here's the initial results for a generic box with 4 different rounded corners which is the staple of most GUIs:
For comparison a rounded rect with Pixie and a gaussian blur shadow:
Notes: There's a few differences, the obvious one is that default Pixie rounded rect proc only supports a single radius. The more other one that's important but hard to see in this example: but the corner from a true 2D gaussian blur seems to a bit sharper than the SDF + 1D Gaussian. Though that could be something resolvable or may just be a difference with SDF approximations.
Importantly it's fast! About a 62x speedup.
Mode | With Neon SIMD | Without SIMD |
---|---|---|
Pixie Shadow | 434 ms | 441 ms |
SDF Clip | 4 ms | 19 ms |
SDF Feather | 6 ms | 20 ms |
SDF Feather Inverse | 6 ms | 20 ms |
SDF Feather Gaussian | 7 ms | 22 ms |
SDF Drop Shadow | 7 ms | 22 ms |
Note: I've only implemented NEON SIMD. I gotta thank Claude for doing the heavy lifting in implementing the NEON SIMD. It hopefully can spit out some SSE or AVX SIMD as well.
Eventually I'd like to get it running with Shady in addition to adding a few more SDF's. PR's welcome if anyone else wants another SDF.
For example the Ellipse has odd singularities at the foci with clipping and AA. Probably something that could be cleaned up since it's an artifact of the AA method used.
Note no SSE SIMD yet! Any want to give it a try? I've added a prompts.txt file I used to get Claude 4 to implement the Neon SIMD for me. Probably it'd work for SSE and AVX. But my x86's are gathering dust somewhere...
Making great progress with this little side project. Version 0.7.1 is out with SSE2 implementation!
I also fixed an annoying off-by-one pixel issue by doing some pixel shifting. Now a 200x200 rounded box is 200x200 pixels.
Otherwise it's fairly feature complete at this point. I might add a Shady implementation later. It'd be cool to render the vector shapes purely on GPU, including feathering and shadows!
Same here, I was kinda wondering how hard an openscad like system would be. Sdfy could make a good base, but it would need 3D support. Not too hard really. Probably the harder bit would be tessellation to an STL or STEP file.
The amazing bit nowadays is how good Claude and Gemini are at translating code. First there would need to be a bit of 3D framework in place. Then take the tessellation algorithms from the Go project and use LLMs to translate to Nim and plug it in. Then use LLMs to make some SIMD versions.
I found the LLMs are almost perfect at translating nowadays. Though you need to give them a structure and to narrow their focus down or they struggle.
A Nim based openscad would be amazing though!
Not that I'm an expert, but seems folks have been using them in games for a while, but with mixed results:
https://www.redblobgames.com/x/2403-distance-field-fonts/
As I'm finding the basic technique for anti-aliasing SDFs yields some issues on straight edges. I think it looks fine on a hi res screen and makes the edge less harsh, but I'm not sure about low res screens.
For my usages the traditional route using Pixie to render text glyphs and then upload them to a texture atlas works well. Pixie is likely to be as fast, or faster, than doing the text rasterization using SDFs on the CPU. That's partly because the Bezier curves resist SIMD optimization. Maybe on a GPU that'd be different as each pixel in SDFs are parallel.
That SDF distance-texture approach looks good.
Version 0.7.6 is out where I've fixed the shadow sizing to mostly match CSS3/Web. That seems to be spread + 1.5 * blur according the LLMs and the web. E.g. TIL that gaussian 2 std devs == blur radius.
I've started integrating into Figuro! I'm already sorta wanting to try out shaders for it. Alas, time to get some work work done.