thi-ng / geom

2D/3D geometry toolkit for Clojure/Clojurescript

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

mesh->csg stack overflow

jackrusher opened this issue · comments

(-> (cuboid 80)
    (g/as-mesh {:mesh (gm/gmesh)})
    csg/mesh->csg)

... works as expected, but if I try to catmull-clark the mesh before converting to CSG, like this:

(-> (cuboid 80)
    (g/as-mesh {:mesh (gm/gmesh)})
    sd/catmull-clark
    csg/mesh->csg)

... I get this stack overflow:

                       RT.java:  721  clojure.lang.RT/get
                      csg.cljc:   58  thi.ng.geom.mesh.csg$split_poly/invoke
                      csg.cljc:  143  thi.ng.geom.mesh.csg$csg_node$fn__18902/invoke
         PersistentVector.java:  333  clojure.lang.PersistentVector/reduce
                      core.clj: 6518  clojure.core/reduce
                      csg.cljc:  142  thi.ng.geom.mesh.csg$csg_node/invoke

... do more complicated meshes push the recursive csg-node function too deep?

Hi @jackrusher - there definitely is a point where a complex mesh will blow the stack, but it shouldn't be after 1 iteration of subdivision. Will investigate! Thanks

So, the thing works if you pre-tessellate the meshes. This makes me think that there're might be some issues with face normals in the original quad mesh. Because the face normals are used to define cutting planes in the BSP construction, if there're invalid values (e.g. two neighboring faces with opposite orientations), then the BSP construction will go into an infinite loop and blow the stack...

(require '[thi.ng.geom.cuboid :as c])
(require '[thi.ng.geom.gmesh :as gm])
(require '[thi.ng.geom.core :as g])
(require '[thi.ng.geom.mesh.csg :as csg])
(require '[thi.ng.geom.mesh.io :as mio])
(require '[clojure.java.io :as io])

(def m (-> (c/cuboid 80) (g/as-mesh {:mesh (gm/gmesh)})))
(def m2 (sd/catmull-clark m))
(def m3 (last (take 4 (iterate sd/catmull-clark (g/translate m [60 0 0])))))
(def m4 (apply csg/union (map (comp csg/mesh->csg g/tessellate) [m2 m3])))
(with-open [o (io/output-stream "foo.ply")]
  (mio/write-ply (mio/wrapped-output-stream o) (csg/csg->mesh m4)))

Btw. The 4 iterations of subdivs seem to be the maximum for this example. With 5 it throws a different SO, but this time it has to do with concat's laziness...

#error {
 :cause nil
 :via
 [{:type clojure.lang.Compiler$CompilerException
   :message "java.lang.StackOverflowError, compiling:(form-init8102643818965635375.clj:1:9)"
   :at [clojure.lang.Compiler$InvokeExpr eval "Compiler.java" 3628]}
  {:type java.lang.StackOverflowError
   :message nil
   :at [clojure.core$seq__4128 invoke "core.clj" 137]}]
 :trace
 [[clojure.core$seq__4128 invoke "core.clj" 137]
  [clojure.core$concat$fn__4215 invoke "core.clj" 691]
  [clojure.lang.LazySeq sval "LazySeq.java" 40]
  [clojure.lang.LazySeq seq "LazySeq.java" 49]
  [clojure.lang.RT seq "RT.java" 507]
  [clojure.core$seq__4128 invoke "core.clj" 137]
  [clojure.core$concat$cat__4217$fn__4218 invoke "core.clj" 700]
  [clojure.lang.LazySeq sval "LazySeq.java" 40]
  [clojure.lang.LazySeq seq "LazySeq.java" 56]
  [clojure.lang.RT seq "RT.java" 507]
 ...

Proposed fix: Avoid concat calls in csg/clip-polygons & csg/all-polygons, use into or reducers' cat

Thanks, mate. Having fun with CSG now!

Great! :) Looking forward to see some of that - alas due to the current major mesh refactoring, there's no point in attempting to fix the above in master before the other things have settled