Haskell-Things / ImplicitCAD

A math-inspired CAD program in haskell. CSG, bevels, and shells; 2D & 3D geometry; 2D gcode generation...
https://implicitcad.org/
GNU Affero General Public License v3.0
1.41k stars 142 forks source link

implicit function renders wrong when unioned. #280

Open julialongtin opened 4 years ago

julialongtin commented 4 years ago

implicit (\(x,y,z) -> x^4 + y^4 + z^4 - 15000) ((-20,-20,-20),(20,20,20)) renders fine alone but not inside union with other objects http://48.io/~rmarko/implicit/ok.png http://48.io/~rmarko/implicit/borken.png

isovector commented 3 years ago

@sorki does this happen for any union? do they need to touch? Is it fixed by #355?

sorki commented 3 years ago

Minimal reproducer seems to be

obj :: SymbolicObj3
obj = union $ [
     sphere 15
   , translate (pure 20) demoSymbolic
  ]

demoSymbolic :: SymbolicObj3
demoSymbolic = implicit
  (\(V3 x y z) -> x^(4 :: Int) + y^(4 :: Int) + z^(4 :: Int) - 15000)
  (pure (-20), pure 20)

Removing translate or sphere makes it go away. Adjusting translate amount changes the look of the normals.

implicit_repro

Worth mentioning that this is via obj path not stl path.

sorki commented 3 years ago

Confirmed that this is only obj path issue, you can see normals pointing in weird directions here:

obj_normals

sorki commented 3 years ago

I think that the trouble with vertex normal computation for obj is in the way we try to compute them which only takes one triangle into account at a time. According to the internet, better way to compute vertex normal is to take facet normals of the surrouding triangles and use a normalized weighted sum of these.

https://github.com/colah/ImplicitCAD/blob/2218fc4ef490d6002d1cd90d409274fdd760e93a/Graphics/Implicit/Export/Util.hs#L21-L47

It looks like that this tries to estimate vertex normal from one triangle using derivatives and magic numbers but has number of corner cases.

Solution might be mapping over the mesh with a context so the function can look at adjacent faces and their normals. Possibly redefining NormedTriangleMesh to TriangleMesh with face normals (like STL) and using that to create VertextNormedTriangleMesh.