Hacker News new | ask | show | jobs
by throwup238 63 days ago
> This is why most geometry kernels (see open cascade) sport things like "fuzzy boolean operations" [0]) that lean into epsilons. These epsilons mask the error-prone supply chain of these meshes that arrive in your program by allowing some tolerance.

They don’t just lean into epsilons, the session context tolerance is used for almost every single point classification operation in geometric kernels and many primitives carry their own accumulating error component for downstream math.

Even then the current state of the art (in production kernels) is tolerance expansion where the kernel goes through up to 7 expansion steps retrying point classification until it just gives up. Those edge cases were some of the hardest parts of working on a kernel.

This is a fundamentally unsolvable problem with floating point math (I worked on both Parasolid and ACIS in the 2000s). Even the ray-box intersection example TFA gives is a long standing thorn - raytracing is one of the last fallbacks for nasty point classification problems.

3 comments

Nice thanks, gotta love knowing a bit about a niche and then encountering someone who knows a great deal more. That's the beauty of HN.

Could you point to any literature/freely available resource that comes close to the SOTA for these kinds of operations? I would be greatly helped.

> This is a fundamentally unsolvable problem with floating point math

It's a fundamentally unsolvable problem with B-reps! The problem completely disappears with F-reps. (In exchange for some other difficult problems).

>(In exchange for some other difficult problems).

Ahhaha.

(I used to work in nTop, and boy is this an understatement when it comes to field based solid modeling)

I was working on an SDF-based CAD tool but gave up when I couldn't find a good way to do fillets.

It's very deceptive because the easy way works so well (Use smoothmin instead of min and you get smooth blends for free! You can even use a circular approximation of smoothmin and get proper fillets!). But when you want the user to be able to pick a couple of surfaces and fillet between them, it gets really hard.

This is the best I got: https://www.youtube.com/watch?v=LOvqdlDbkBs

It worked by rewriting the expression tree so that the blend arguments become sibling nodes and then applying the blend to the union/intersection that is their parent.

That works every time if you only want 1 single targeted blend, but if you want several of them then you can run into unsatisfiable cases where the same object needs to blended with several others and can't be siblings of all of them.

So I gave up :(. For me, CAD without fillets and chamfers is no CAD at all.

(Also, apropos for this thread: the discontinuity in the chamfer was a floating point precision problem...)

Well, user picking a couple of surfaces is literally an operation on a boundary representation, so of course it's a PITA with fields :)

I think the future is CAD is combined fields and breps. They're literally dual, one is covariant, the other contravariant (breps facilitate pushforwards, fields facilitate pullbacks).

One without the other is necessarily going to be limited in some way.

Picking surfaces is easy.

The distance field tells you the distance to the nearest surface at any point. You can have a "surface id field" tell you the id of the nearest surface to any point, and then when you raymarch to find the intersection of a line with a surface, you can read out the ID from the ID field after finding the intersection point. (Of course the ID field is also implemented as a function mapping points to surfaces).

So when the mouse is hovered or clicked in the 3d view you can easily find the ID of the surface under the pointer, and you can draw that surface in a different colour to show it is selected. No boundary representation needed.

The hard part is, given 2 surface ids, how do you add a fillet between them in the general case?

Another idea I had was to set the fillet radius on every min/max node based on the derived surface id's from the child nodes, but I couldn't find a good way to do this without making the field discontinuous.

I have more notes in this blog post: https://incoherency.co.uk/blog/stories/frep-cad-building-blo...

If you have good ideas for this I'd love to hear them and resume working on Isoform.

>Picking surfaces is easy.

That depends on what we mean by surfaces, and in the case of filleting, the user really wants to be picking adjacent faces (as in: an edge between two adjacent faces). That, or even a region to roll a ball along to generate a fillet.

The semantics of fillets even in the simplest case is that it's doing something to the edges, i.e. elements of the boundary representation, so that's a more natural structure for filleting.

>The distance field tells you the distance to the nearest surface at any point.

What you're describing isn't the same. You really are picking solids, not faces.

This wouldn't work even in the simplest case of a cube.

You can define a cube by a distance field:

    f(x, y, z) = max(|x|, |y|, |z|) - 1
If the user wants to fillet just one the edges, then what? You only have one surface (the boundary of a cube), and one ID.

The field doesn't know anything about the edges.

OK, OK, we can ignore this edge case (badum-tss), but even if you only allow filleting "two surfaces", those two "surfaces" (really: boundaries of solids) aren't necessarily going to intersect along one edge (which is what the user wants to fillet).

The intersection may have multiple components. Or may not be manifold.

As a concrete example:

    f(x, y, z) = z - cos(x)
    g(x, y, z) = z - cos(y)
Look ma, no absolute values! Let me smooth-talk a little though:

    f(x, y, z) = z - cos(x)cos(y)
    g(x, y, z) = z - 0.25


.....and that's before we get to the reality where the user's understanding of "edge" isn't topological (as in, component of intersection between surfaces), but geometric (sharp corners).

B-reps can get away with making no distinction between them... Until you have messy geometry from elsewhere.

Say, an STL file from a scan. Or a mesh that came from an F-rep by marching cubes. Or whatever unholy mess OpenSCAD can cook with CGAL.

It doesn't matter if you use F-rep or convert to one: chisel out a cube as an intersection of half-spaces, then cut with half-spaces that narrowly touch the edges.

It'll look like a cube, and it'll be a cube functionally if you manufacture it.

Good luck with that one.

>If you have good ideas for this I'd love to hear them and resume working on Isoform

Well. The good news is that putting fillets on every edge is kind of easy with fields because you can do it with offsets.

If F(x, y, z) is a distance field that defines a solid, G(x, y, z) = F(x, y, z) + c offsets F inwards by c.

G is not a distance field anymore though, it's giving values that arent distances on the outside of convex corners.

Renormalize G to be a distance field, call it G'.

Now offset G' outwards by c: H = G' - c.

Ta-da! Concave corners aren't touched, convex corners are rounded.

Flip the + and -, and you're filleting concave corners (G = F - c is a field that defines an outwards offset that fails to be a distance field inside the body near concave corners; compute G' — the distance field for G; offset G inwards: H = G' + c).

Now, the "just normalize a field into a distance field" is doing a lot of heavy lifting here.

But that can give you something to think about.

Nice use of the inigo sdf shader. Too bad this is so hard, I was hoping it would help solve the problem.
> They don’t just lean into epsilons, the session context tolerance is used for almost every single point classification operation in geometric kernels and many primitives carry their own accumulating error component for downstream math.

The GP wasn't wrong. To "lean in" means to fully commit to, go all in on, (or, equivalently, go all out on).

I think his point is: rather than "leaning into" it as in, masking through epsilons, he argues that tolerance is fundamental to the problem space, not a way to resolve edge cases.
Right. And my point is that "leaning in" doesn't mean masking, it means committing to. Taking seriously. Exactly the sort of thing he's describing.

I'm wondering if people have heard the expression "leaning in" from people who were insincere/lying, and assumed that that was what the phrase means?

I think you should revisit the word "just", its presence in the comment you're trying to discuss, and how it's used.
To me it seems it's used with the intent "They don’t just <do X>, they <do Y>," implying that Y is a proper superset of X. My point is that X is in fact a superset of Y, making the most charitable reading "They don’t just <do X>, they <do X in more words>."

Is there another potential reading of "just" that I'm missing?