core-matrix

intended for specific discussion around core.matrix (usage and development) For general data work check out #data-science
meow 2016-01-03T00:11:45.000096Z

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

2016-01-03T09:47:33.000099Z

Wow.... looking awesome!

2016-01-03T09:47:53.000100Z

How is performance? Have you identified any hotspots?

2016-01-03T09:48:17.000101Z

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

meow 2016-01-03T15:07:58.000104Z

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

meow 2016-01-03T15:08:36.000105Z

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

meow 2016-01-03T15:09:51.000106Z

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

meow 2016-01-03T15:11:03.000107Z

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.

meow 2016-01-03T15:12:13.000108Z

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

meow 2016-01-03T15:12:56.000109Z

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

meow 2016-01-03T15:13:50.000110Z

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

meow 2016-01-03T15:15:23.000111Z

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

meow 2016-01-03T19:33:31.000112Z

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

meow 2016-01-03T22:08:49.000113Z

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

meow 2016-01-03T22:19:32.000114Z

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)))))

meow 2016-01-03T22:20:46.000115Z

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.

meow 2016-01-03T22:21:06.000116Z

http://dev.clojure.org/jira/browse/CLJ-1860

meow 2016-01-03T22:21:49.000117Z

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

meow 2016-01-03T22:22:38.000118Z

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)))))))

meow 2016-01-03T22:23:19.000119Z

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

meow 2016-01-03T22:24:35.000120Z

@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?

2016-01-03T22:32:10.000121Z

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

2016-01-03T22:32:21.000122Z

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

2016-01-03T22:32:52.000123Z

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

2016-01-03T22:34:29.000124Z

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

2016-01-03T22:36:02.000125Z

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

2016-01-03T22:36:30.000126Z

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

meow 2016-01-03T22:40:58.000127Z

@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]

meow 2016-01-03T22:41:25.000128Z

except with normalized values...

meow 2016-01-03T22:42:59.000129Z

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

meow 2016-01-03T22:43:25.000130Z

not the entire vector3

2016-01-03T22:43:32.000131Z

Hmmmm this is a problem because you are hashing them?

meow 2016-01-03T22:43:38.000132Z

yes

meow 2016-01-03T22:43:51.000133Z

and the clojure bug screws that up

2016-01-03T22:44:27.000134Z

Hmmm

meow 2016-01-03T22:44:33.000135Z

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

2016-01-03T22:44:39.000136Z

OK try this

2016-01-03T22:45:25.000137Z

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

2016-01-03T22:45:57.000138Z

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

2016-01-03T22:46:07.000139Z

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

2016-01-03T22:46:25.000140Z

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

meow 2016-01-03T22:46:40.000141Z

yes, emap is what I need

2016-01-03T22:47:03.000142Z

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

2016-01-03T22:48:44.000143Z

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

meow 2016-01-03T22:49:04.000144Z

sweet

meow 2016-01-03T22:49:27.000145Z

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

2016-01-03T22:51:44.000146Z

I think they work the same

2016-01-03T22:52:19.000147Z

zero? might be better

2016-01-03T22:53:52.000149Z

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

meow 2016-01-03T22:55:00.000150Z

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

meow 2016-01-03T22:55:22.000151Z

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

meow 2016-01-03T22:56:00.000152Z

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?

meow 2016-01-03T22:56:08.000153Z

emap!

meow 2016-01-03T22:56:21.000154Z

instead of emap?

meow 2016-01-03T23:05:33.000155Z

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

meow 2016-01-03T23:06:04.000156Z

when I try to do this:

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

meow 2016-01-03T23:09:44.000157Z

actually, the problem lies elsewhere, sorry

meow 2016-01-03T23:12:50.000158Z

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)))

2016-01-03T23:15:32.000159Z

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

2016-01-03T23:17:24.000160Z

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

meow 2016-01-03T23:22:21.000161Z

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

meow 2016-01-03T23:24:55.000162Z

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.