Let's see...

A plotter update.

It's been a while since I posted here with a plotter update, mostly because I've been busy with life. In April my job at the time wound up and it took me six weeks to find another one. While unemployed I caught COVID for the first time in 4 years, which didn't help the job search. And since starting a new role in June I've been busy ramping up there. Still ramping now.

But in the meantime I've updated the renderer to use yet another algorithm for line determination.

The issue I've had, almost from the start, is I think due to the nature of floating point imprecision. When calculating line intersections between boundary contours or between intersecting faces, the old renderer worked 99% of the time. But, often enough to be annoying, it would miss intersections when they should have just touched. Due to floating point shenanigans they slightly missed each other. So lines which should be hidden were still visible.

This is why I tried a vectoriser late last year. That version was sampling every pixel in the image and using Sobel filters and other skeletonisation techniques to attempt to convert the rasterised image back to plot lines. It wasn't very good, especially for lines on faces which were almost perpendicular to the eye, and it introduced more errors than it fixed. So I abandoned it.

The solution I've settled on (for now?) is a compromise. All the potential lines in a scene are determined from the geometry using boundary contour calculations and face intersections as usual. But for visibility, instead of finding collisions between lines and checking which sub-lines are still visible, I'm sampling along the line using ray tracing, pixel by pixel, and simply checking if the ray hits what I'd expect it to hit. If it doesn't then that part of the line is obscured.

It needed some new maths to work out how to get the sampling rays right. And a 'pixel' isn't really a pixel when dealing with plots. It is for my test images, but for actual pen on paper a pixel is a discrete sampling distance, which at the moment defaults to 0.1mm. But that's configurable. So when checking if a whole line is visible I take the face edge(s) the line is generated from and sample along it in 0.1mm steps, confirming the ray trace hits those face(s). If not it's hidden.

And it works pretty well. There are some rough edges I still need to work out but I'm pleased with the results. Here's an example, where pairs of cubes are overlapping each other but I'm able to render them as constructive solids. Obviously not plotted, just output using my debug PNG writer, which looks a bit janky, but demonstrates the algorithm at work.

intersecting-cubes-montage

Next I'd really like to start working on face shading and shadows using cross-hatching. I also have plans to improve the plotter firmware and to abandon G-code which I haven't started on yet. But there's also a lot of technical debt accrued over the months and need to refactor the system. The todo list has about 30 items which I need to look at before getting to shading.