hannobraun / fornjot

Early-stage b-rep CAD kernel, written in the Rust programming language.
https://www.fornjot.app/
Other
2.01k stars 113 forks source link

Validate face orientation of `Solid`s #1935

Open hannobraun opened 1 year ago

hannobraun commented 1 year ago

The orientation of a face is defined by the winding of its exterior cycle. On the front side of the face, the exterior cycle is wound counter-clockwise. It is possible to create Solids where some or all of the faces face inwards, which would lead to invalid shading when displaying the Solid, and possibly invalid meshes when exporting it to an external file format.

This should be caught in a validation check, which makes sure that all faces of every solid point outwards.

A-Walrus commented 1 year ago

Some questions:

hannobraun commented 1 year ago

Should we be checking that all faces of a Shell have the same orientation (which can be either inwards or outwards)?

Yeah, that makes sense! A mixed-orientation Shell would definitely be invalid (even though that's not specified anywhere right now), and having that check in place would then make the Solid check easier

If a Solid has a cavity, what should be the orientation of the faces of the cavity?

The cavity is "outside" of the Solid. So if you're sitting inside of the cavity looking at the solid, you should see counter-clockwise faces.

hannobraun commented 10 months ago

I've been looking into coordinate systems and cycle winding while working on https://github.com/hannobraun/fornjot/issues/2098, and I found out that the situation is not as simple as I presented it in the issue description above. I said that the orientation of a face is defined by the winding of its exterior cycle, and that is simply wrong. The orientation of a face is defined by the combination of the winding and the handedness of the surface coordinate system.

To summarize:

All of that is highly confusing, and there is way too much code that must deal with this in too many places.

I hope that it might be reasonable to just require faces to just always be winded counter-clockwise and have a right-handed coordinate system (when looking from the front), but I don't know. It would require reversing the surface when reversing a face, and if you do that, then you have coincident faces that aren't on the same surface (because I've reversed it; it's a different surface now). Which would break all kinds of assumptions that the code currently makes, or might make in the future.

So yeah, I'm confused. I'll keep thinking about it, but for now, if anyone wants to address this issue, the overview above should at least provide the correct set of rules to validate the orientation of faces.

felix91gr commented 10 months ago

I hope these two cents are of a currency that works in this space >.<

If I recall correctly from class, the way face orientation is often coded for computer graphics purposes is by:

Basically what's done is that one of the two degrees of freedom is collapsed, and the other one is used to set the orientation.


Aside: there are other ways to orient faces, but the ones I know of require to add normal vectors to each vertex. To my eyes though, doing so seems unnecessarily wasteful in space for CAD, as well as numerically slightly less stable, than encoding face orientation in the winding order.

hannobraun commented 10 months ago

I hope these two cents are of a currency that works in this space >.<

I'll take them :smile:

If I recall correctly from class, the way face orientation is often coded for computer graphics purposes is by:

[...]

Yeah, that's what I would like to do. It's what I meant above, where I wrote "I hope that it might be reasonable...".

To expand/clarify that, I know that this is a common approach in general, but there are problems with it within the context of Fornjot that would need to be addressed. Specifically, the way surfaces are handled.

The short version is, Fornjot tries to make geometric relationships explicit. Two faces aren't just allowed to be coincident. If they are, they must refer to the same surface[^1]. This is done to handle numerical inaccuracy, and there's some not-quite-up-to-date documentation about it.

So, the problem is, if I have two coincident faces that face in different directions, they must per this rule reference the same surface. But since the coordinate system is defined by the surface, one of the faces must, necessarily, have a left-handed coordinate system.

I'm not sure what to do about that. Maybe the rule about surfaces is not exactly necessary, and we can do without it. Maybe a surface can have two coordinate systems (so each side gets a right-handed one). Or maybe coordinate systems can be decoupled from surfaces completely. Right now, it's not clear to me what the solution should be.

Aside: there are other ways to orient faces, but the ones I know of require to add normal vectors to each vertex. To my eyes though, doing so seems unnecessarily wasteful in space for CAD, as well as numerically slightly less stable, than encoding face orientation in the winding order.

Yeah, I'd like to avoid that. I'd actually like to experiment with decoupling geometric and topological data from each other (which is a topic for another day; I'll open an issue about that when I can). Using normal vectors to define orientation would run counter to that.

[^1]: Actually, as of now, they need not. At least I don't think it's a documented requirement, and it's certainly not enforced. But that's more of an artifact of the current implementation. Half-edges and curves work exactly like that (i.e. coincident half-edges must refer to the same curve), and that works very well.

A-Walrus commented 9 months ago

Can you give an example where we might have coincident faces, cause I'm having a hard time coming up with one?

hannobraun commented 9 months ago

@A-Walrus

Can you give an example where we might have coincident faces, cause I'm having a hard time coming up with one?

Off the top of my head:

None of this is a reality right now (see the footnote in my previous comment), but those are some cases where I assume that whole concept will become relevant (and as I mentioned in the footnote, half-edges/curves already work that way, to great benefit).