thi-ng / geom

2D/3D geometry toolkit for Clojure/Clojurescript
Apache License 2.0
961 stars 79 forks source link

Missing IMathOps :- method for java.lang.Double when using csg/mesh->csg #85

Open seylerius opened 3 years ago

seylerius commented 3 years ago

When trying to convert a mesh (tested with either basic-mesh or gmesh) to a CSG node (using csg/mesh->csg) to compute the union of several nodes, I receive the following error. No implementation of method: :- of protocol: #'thi.ng.math.core/IMathOps found for class: java.lang.Double. Further investigation shows that it's happening in the ortho-normal function, which is being used when computing the plane for each polygon. At first glance, this seems to be a reoccurrence of #40, which featured the same error with :+ instead. I've reproduced the stack trace below.

  Show: Project-Only All 
  Hide: Clojure Java REPL Tooling Duplicates  (15 frames hidden)

1. Unhandled java.lang.IllegalArgumentException
   No implementation of method: :- of protocol: #'thi.ng.math.core/IMathOps
   found for class: java.lang.Double

          core_deftype.clj:  583  clojure.core/-cache-protocol-fn
          core_deftype.clj:  575  clojure.core/-cache-protocol-fn
                 core.cljc:    8  thi.ng.math.core$eval19046$fn__19138$G__19023__19161/invoke
                utils.cljc:  306  thi.ng.geom.utils$ortho_normal/invokeStatic
                utils.cljc:  303  thi.ng.geom.utils$ortho_normal/invoke
                plane.cljc:   32  thi.ng.geom.plane$plane_from_points/invokeStatic
                plane.cljc:   29  thi.ng.geom.plane$plane_from_points/invoke
                plane.cljc:   30  thi.ng.geom.plane$plane_from_points/invokeStatic
                plane.cljc:   29  thi.ng.geom.plane$plane_from_points/invoke
                  csg.cljc:  169  thi.ng.geom.mesh.csg$csg_polygon/invokeStatic
                  csg.cljc:  164  thi.ng.geom.mesh.csg$csg_polygon/invoke
                  csg.cljc:  212  thi.ng.geom.mesh.csg$mesh__GT_csg$fn__24580/invoke
                  core.clj: 2755  clojure.core/map/fn
              LazySeq.java:   42  clojure.lang.LazySeq/sval
              LazySeq.java:   51  clojure.lang.LazySeq/seq
                   RT.java:  535  clojure.lang.RT/seq
                  core.clj:  137  clojure.core/seq
                  core.clj:  137  clojure.core/seq
                  csg.cljc:  176  thi.ng.geom.mesh.csg$csg_node/invokeStatic
                  csg.cljc:  171  thi.ng.geom.mesh.csg$csg_node/invoke
                  csg.cljc:  213  thi.ng.geom.mesh.csg$mesh__GT_csg/invokeStatic
                  csg.cljc:  209  thi.ng.geom.mesh.csg$mesh__GT_csg/invoke
                  core.clj: 2753  clojure.core/map/fn
              LazySeq.java:   42  clojure.lang.LazySeq/sval
              LazySeq.java:   51  clojure.lang.LazySeq/seq
              LazySeq.java:   73  clojure.lang.LazySeq/first
                   RT.java:  692  clojure.lang.RT/first
                  core.clj:   55  clojure.core/first
                  core.clj:   55  clojure.core/first
                  core.clj:   53  solar-stl.core/parse-roof
                  core.clj:   45  solar-stl.core/parse-roof
                      REPL:  266  solar-stl.core/eval25749
                      REPL:  266  solar-stl.core/eval25749
             Compiler.java: 7177  clojure.lang.Compiler/eval
             Compiler.java: 7132  clojure.lang.Compiler/eval
                  core.clj: 3214  clojure.core/eval
                  core.clj: 3210  clojure.core/eval
    interruptible_eval.clj:   87  nrepl.middleware.interruptible-eval/evaluate/fn/fn
                  AFn.java:  152  clojure.lang.AFn/applyToHelper
                  AFn.java:  144  clojure.lang.AFn/applyTo
                  core.clj:  665  clojure.core/apply
                  core.clj: 1973  clojure.core/with-bindings*
                  core.clj: 1973  clojure.core/with-bindings*
               RestFn.java:  425  clojure.lang.RestFn/invoke
    interruptible_eval.clj:   87  nrepl.middleware.interruptible-eval/evaluate/fn
                  main.clj:  437  clojure.main/repl/read-eval-print/fn
                  main.clj:  437  clojure.main/repl/read-eval-print
                  main.clj:  458  clojure.main/repl/fn
                  main.clj:  458  clojure.main/repl
                  main.clj:  368  clojure.main/repl
               RestFn.java:  137  clojure.lang.RestFn/applyTo
                  core.clj:  665  clojure.core/apply
                  core.clj:  660  clojure.core/apply
                regrow.clj:   20  refactor-nrepl.ns.slam.hound.regrow/wrap-clojure-repl/fn
               RestFn.java: 1523  clojure.lang.RestFn/invoke
    interruptible_eval.clj:   84  nrepl.middleware.interruptible-eval/evaluate
    interruptible_eval.clj:   56  nrepl.middleware.interruptible-eval/evaluate
    interruptible_eval.clj:  152  nrepl.middleware.interruptible-eval/interruptible-eval/fn/fn
                  AFn.java:   22  clojure.lang.AFn/run
               session.clj:  202  nrepl.middleware.session/session-exec/main-loop/fn
               session.clj:  201  nrepl.middleware.session/session-exec/main-loop
                  AFn.java:   22  clojure.lang.AFn/run
               Thread.java:  831  java.lang.Thread/run
seylerius commented 3 years ago

Further investigation reveals that the BasicMesh may be generating wrong. I get the following results when I inspect the generation of the mesh in CIDER:

Class: thi.ng.geom.types.BasicMesh
Contents: 
  :vertices = #{ 0.0 -1.5668 -8.6965 2.7351 -12.0802 ... }
  :faces = #{ [[-8.6965 3.1084 6.294]] [[6.2429 -12.0802 0.0]] [[-19.2099 -1.0464 2.7351]] [[6.2429 -12.0802 2.7351]] [[2.0882 -1.5668 6.294]] }
  :fnormals = {}

The mesh is being generated by having the following sequence of faces passed in, using the form (g/into (bm/basic-mesh) faces), where faces is defined as follows:

( ( [ 2.0882 -1.5668 6.294 ] [ -8.6965 3.1084 6.294 ] [ -19.2099 -1.0464 2.7351 ] [ 6.2429 -12.0802 2.7351 ] )
  [ [ 2.0882 -1.5668 6.294 ] [ 2.0882 -1.5668 0.0 ] [ -8.6965 3.1084 0.0 ] [ -8.6965 3.1084 6.294 ] ]
  [ [ -8.6965 3.1084 6.294 ] [ -8.6965 3.1084 0.0 ] [ -19.2099 -1.0464 0.0 ] [ -19.2099 -1.0464 2.7351 ] ]
  [ [ -19.2099 -1.0464 2.7351 ] [ -19.2099 -1.0464 0.0 ] [ 6.2429 -12.0802 0.0 ] [ 6.2429 -12.0802 2.7351 ] ]
  [ [ 6.2429 -12.0802 2.7351 ] [ 6.2429 -12.0802 0.0 ] [ 2.0882 -1.5668 0.0 ] [ 2.0882 -1.5668 6.294 ] ]
  ( [ 6.2429 -12.0802 0.0 ] [ -19.2099 -1.0464 0.0 ] [ -8.6965 3.1084 0.0 ] [ 2.0882 -1.5668 0.0 ] ) )
seylerius commented 3 years ago

Okay, finally tested and found where the problem is. On line 18 of basicmesh.cljc, where you define the parameters for add-face*, you're excessively deconstructing the passed face.

https://github.com/thi-ng/geom/blob/85783a1f87670c5821473298fa0b491bd40c3028/src/thi/ng/geom/basicmesh.cljc#L17-L22

After removing the extra sequence deconstruction around fverts, the problem disappeared.

seylerius commented 3 years ago

And of course after fixing that I get a stack overflow.

postspectacular commented 3 years ago

Hi @seylerius - I'm in the middle of a relocation and will take a while until I can look into this. In the meanwhile, please post a minimal example to reproduce your issue, thanks! 👍