Fork me on GitHub
#core-matrix
<
2016-01-03
>
meow00:01:45

My polygon mesh generating/processing/morphing library using core.matrix is shaping up nicely: https://github.com/pkobrien/cad/tree/master/src/cad/mesh

mikera09:01:33

Wow.... looking awesome!

mikera09:01:53

How is performance? Have you identified any hotspots?

mikera09:01:17

You are probably the first person to really test the 3D vector stuff in anger

meow15:01:58

@mikera: Thanks. I call that shape "spore" and it is one of my favorites.

meow15:01:36

Performance is about the same as the vector from http://thi.ng

meow15:01:51

I'm still working out some minor issues to get the code back to being solid.

meow15:01:03

Right now I'm not scaling my seed platonic solids properly. Basic stuff. I'm reading up on 3D graphics algorithms so I actually know what I'm doing at a lower level.

meow15:01:13

And I ran into an old issue with -0.0 in my normals but haven't been able to recreate it yet this morning.

meow15:01:56

That's why the old code applied an abs-zero function when calculating the face normals.

meow15:01:50

Anyhow, I should have these issues resolved today. Then I will start looking at performance more.

meow15:01:23

Right now one of my main bottlenecks is in writing to the X3D xml files. I'm going to have to create a custom writer to make it lazier because I've maxed out all the tricks I can play with making things lazy using clojure.data.xml

meow19:01:31

Okay, scaling has been properly implemented and the platonic solid construction looks pretty good now.

meow22:01:49

@mikera: I need to get the absolute value of a -0.0 component of a Vector3

meow22:01:32

This is how I'm calculating a face normal now:

(defn normal
  "Returns the ortho normal of the first three points passed in."
  ([[a b c]]
   (normal a b c))
  ([a b c]
   (mx/normalise! (mx/cross (mx/sub b a) (mx/sub c a)))))

meow22:01:46

The problem is that this can create a Vector3 having an x, y, or z component with a value of -0.0 and Clojure has a bug related to that which messes up some other functions of mine.

meow22:01:49

That's the Clojure bug: 0.0 and -0.0 compare equal but have different hash values

meow22:01:38

So my fix in my old code was to do this:

(defn normal
  "Returns the ortho normal of the first three points passed in."
  ([[a b c]] (normal a b c))
  ;([a b] (gc/normalize (gc/cross a b)))
  ([a b c] (apply point (mapv (comp mu/round2safe mu/abs-zero)
                              (mx/normalise (mx/cross (- b a) (- c a)))))))

meow22:01:19

(defn abs-zero
  [x]
  (if (zero? x) 0.0 x))

meow22:01:35

@mikera: Now that I'm using core.matrix what's the best way to apply abs-zero to a Vector3 as part of my normal function?

mikera22:01:10

@meow: I'd probably do something like this:

mikera22:01:21

(def ZERO-VECTOR3 (Vector3/of 0.0 0.0 0.0)) (defn abs-zero [^Vector3 v] (if (.isZero v) ZERO-VECTOR3 v))

mikera22:01:52

i.e. replace with a constant zero Vector3 whenever required. Avoids unnecessary new allocations.

mikera22:01:29

if you want to be a bit safer and support arbitrary vector types, you can use:

mikera22:01:02

(defn abs-zero [v] (if (zero-matrix? v) ZERO-VECTOR3 v))

mikera22:01:30

That costs you one extra protocol dispatch.... so not too much but might be painful in an inner loop

meow22:01:58

@mikera: We're not quite on the same page. I only need to replace one component of the Vector3 if it is -0.0. The other components might be okay. So, for example [1.0 -0.0 5.25]

meow22:01:25

except with normalized values...

meow22:01:59

so after calling normalise! I need to replace -0.0 components.

meow22:01:25

not the entire vector3

mikera22:01:32

Hmmmm this is a problem because you are hashing them?

meow22:01:51

and the clojure bug screws that up

meow22:01:33

since this is a calculated vector I can mutate it in place

mikera22:01:39

OK try this

mikera22:01:25

(defn abs-zero ^double [^double v] (if (== 0.0 v) 0.0 v))

mikera22:01:57

(emap abs-zero (Vector3/of -0.0 0.0 -1.0))

mikera22:01:07

=> #vectorz/vector [0.0,0.0,-1.0]

mikera22:01:25

That work? emap is like mapv but for core.matrix arrays

meow22:01:40

yes, emap is what I need

mikera22:01:03

vectorz-clj can also optimise these if there are primitive type hints

mikera22:01:44

It is about 5x faster than a Clojure mapv on my machine, even with small vectors like this

meow22:01:27

you prefer (== 0.0 v) over (zero? v)?

mikera22:01:44

I think they work the same

mikera22:01:19

zero? might be better

mikera22:01:52

About to do a new release of vectorz-clj.... any issues discovered that you'd like a fix for?

meow22:01:00

No, all the problems I'm having are my own doing. Haven't found any problems with core.matrix so far. simple_smile

meow22:01:22

that fix works like a charm: (mx/emap mu/abs-zero (mx/normalise! (mx/cross (mx/sub b a) (mx/sub c a))))

meow22:01:00

oh, I can make that (mx/emap! mu/abs-zero (mx/normalise! (mx/cross (mx/sub b a) (mx/sub c a)))) can't I?

meow22:01:21

instead of emap?

meow23:01:33

@mikera: I'm getting this error: CompilerException java.lang.ClassCastException: mikera.vectorz.Vector3 cannot be cast to java.lang.Number,

meow23:01:04

when I try to do this:

opposite-face (fn [outer-face thickness]
                        (vec (for [vert (reverse outer-face)]
                               (+ vert
                                  (* (vert-normal-map vert) (- thickness))))))

meow23:01:44

actually, the problem lies elsewhere, sorry

meow23:01:50

ah, this is failing:

(defn normal
  "Returns the normal for the vertex as the average of the face normals."
  [mesh vert]
  (let [face-normal-map (mp/face-normal-map mesh)
        vert-faces-map (mp/vert-faces-map mesh)
        xf (comp (map face-normal-map) (distinct))
        vnorm (fn [vert]
                (->> (vert-faces-map vert)
                     (transduce xf + (mc/point 0 0 0))
                     (mx/normalise!)))]
    (vnorm vert)))

mikera23:01:32

Are your + and * the core.matrix operators or the clojure ones? That's the error I'd expect if you try and pass a vector to a clojure numerical operator

mikera23:01:24

Also worth checking the types of values are correct at each stage of your operations. I recommend unit tests to do that, especially for the results of functions like face-normal-map

meow23:01:21

ah, I hadn't imported the operators into this module - mx/add fixed it

meow23:01:55

I have no unit tests yet - this has been a WIP - I've only been doing the mesh processing for a couple of weeks and only a week ago decided to create my own library, so I've only just fleshed this out. Once I'm happy with the overall shape, and I think it has shaped up nicely, I will move all this mesh stuff over to my decomplect.ion namespace/github repository and will add unit tests.