datavis

meow 2016-01-02T01:23:39.001024Z

@mikera: I don't understand how to use scale-add!

meow 2016-01-02T01:24:13.001025Z

What I need is to do a linear interpolation from one vector to another by a normalized percentage.

meow 2016-01-02T03:12:03.001026Z

I took it as far as I can and I'm burnt. I checked in the latest code, even though it doesn't work yet. Anyone interested can check it out here: https://github.com/pkobrien/cad/tree/master/src/cad/mesh

meow 2016-01-02T03:14:51.001028Z

There are just a handful of functions that I don't know how to implement using core.matrix. They are in core.clj and face.clj

meow 2016-01-02T03:15:47.001029Z

centroid and mix should be easy to fix by someone who knows core.matrix:

(defn centroid
  [[x & xs :as coll]]
  (case (count coll)
    0 nil
    1 x
    2 (gc/mix x (first xs))
    (let [s (/ 1.0 (count coll))
          f (fn [x _] (* x s))]
      (gc/reduce-vector x + f xs))))

(defn mix
  [v1 v2 amount]
  (gc/mix v1 v2 amount))

meow 2016-01-02T03:17:45.001030Z

I have no idea what reduce-vector is doing and the vector part of http://thi.ng is all but impenetrable to me, though I've tried.

meow 2016-01-02T03:19:18.001031Z

In face.clj I've got this function:

(defn- mag [[x y z]]
  (Math/sqrt (mm/madd x x y y z z)))

meow 2016-01-02T03:19:48.001032Z

I'm not sure how the madd macro should be converted.

meow 2016-01-02T03:22:19.001033Z

Oh, and the catmull-clark in operator.clj is still using this: (gc/addm (gc/madd r 2.0 f) (* vertex (- n 3)) (/ 1.0 n))

meow 2016-01-02T03:24:26.001034Z

I'm done for the day, but this has been a super productive week and I'm really pleased with how this mesh library is shaping up.

meow 2016-01-02T03:27:22.001035Z

I don't think I could have gotten to the end of this without the quirky dubstepesque Beats Antique blowing up my ears. 😉

meow 2016-01-02T13:27:30.001036Z

@mikera: I'm so close to being done with this conversion to core.matrix - can I get a little bit of help?

2016-01-02T13:27:59.001037Z

Sure happy to help if I can

meow 2016-01-02T13:28:26.001038Z

Awesome. TYVM.

meow 2016-01-02T13:29:19.001039Z

Let's start with an easy one:

(defn mix
  "Returns linear interpolation point at amount along the path from v1 to v2."
  [v1 v2 amount]
  (gc/mix v1 v2 amount))

meow 2016-01-02T13:30:16.001040Z

You mentioned scale-add! as a substitute. How?

meow 2016-01-02T13:31:30.001041Z

@mikera: ^

2016-01-02T13:35:04.001042Z

I'm actually adding a lerp function to the next iteration of core.matrix, should be released today :simple_smile:

meow 2016-01-02T13:36:30.001043Z

I don't know what lerp is. Pretty funny how far I've gotten without brushing up on my maths.

2016-01-02T13:38:11.001044Z

This is roughly how scale-add! could be used to do it however

2016-01-02T13:38:12.001045Z

(defn mix "Returns linear interpolation point at amount along the path from v1 to v2." [v1 v2 amount] (let [result (array :vectorz v1)] (scale-add! result (- 1.0 amount) v2 amount)))

2016-01-02T13:38:54.001046Z

You can replace "mix" with "lerp" once the next core.matrix / vectorz-clj is released

meow 2016-01-02T13:39:13.001048Z

Excellent!

meow 2016-01-02T13:41:09.001049Z

Ok, the next one to fix is a centroid for more than 2 points:

(defn centroid
  "Returns the point at the barycenter of the collection of points."
  [[x & xs :as coll]]
  (case (count coll)
    0 nil
    1 x
    2 (mix x (first xs) 0.5)
    (let [s (/ 1.0 (count coll))
          f (fn [x _] (* x s))]
      (gc/reduce-vector x + f xs))))

meow 2016-01-02T13:43:55.001050Z

And here is what reduce-vector is doing:

g/PVectorReduce
(reduce-vector
 [_ f xs]
 (let [^doubles buf' #?(:clj (double-array 3) :cljs (js/Float32Array. buf))]
   #?@(:clj
       [(aset buf' 0 (aget buf 0))
        (aset buf' 1 (aget buf 1))
        (aset buf' 2 (aget buf 2))])
   (Vec3. (vec3-reduce* f buf' xs) nil _meta)))
(reduce-vector
 [_ f f2 xs]
 (let [^doubles buf' #?(:clj (double-array 3) :cljs (js/Float32Array. buf))]
   #?@(:clj
       [(aset buf' 0 (aget buf 0))
        (aset buf' 1 (aget buf 1))
        (aset buf' 2 (aget buf 2))])
   (vec3-reduce* f buf' xs)
   (aset buf' 0 (double (f2 (aget buf' 0) 0)))
   (aset buf' 1 (double (f2 (aget buf' 1) 1)))
   (aset buf' 2 (double (f2 (aget buf' 2) 2)))
   (Vec3. buf' nil _meta)))

meow 2016-01-02T13:44:39.001051Z

@mikera: I just need a centroid function that works with core.matrix.

2016-01-02T13:45:43.001052Z

Hmmm centroid is just the average of points

meow 2016-01-02T13:48:07.001053Z

I'm reading about lerp, slerp and nlerp: https://keithmaggio.wordpress.com/2011/02/15/math-magician-lerp-slerp-and-nlerp/

meow 2016-01-02T13:48:58.001055Z

@mikera: right, so how do I get the average for more than 2 points?

2016-01-02T13:50:18.001056Z

(defn centroid [points] (scale (apply add points) (/ 1.0 (count points))))

2016-01-02T13:50:32.001057Z

Not fully optimised but should work :simple_smile:

meow 2016-01-02T13:52:06.001058Z

Sweet! :simple_smile:

meow 2016-01-02T13:55:03.001059Z

Okay, this one works but you might have a better way to code it:

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

2016-01-02T13:57:06.001060Z

Why the mapv stuff?

2016-01-02T13:57:29.001061Z

Can't you just cross and normalise?

meow 2016-01-02T13:57:29.001062Z

I was wondering that myself. :simple_smile:

2016-01-02T13:57:48.001063Z

Unless it is some funny handling for degenerate triangles

meow 2016-01-02T13:57:55.001064Z

I've been refactoring this code over time and sometimes it gets to the point that the stupidity becomes obvious.

meow 2016-01-02T13:58:53.001065Z

No, I think the beauty of triangles is that there really are no degenerate ones.

2016-01-02T13:59:22.001066Z

I guess I mean triangles where the cross product is the zero vector...

meow 2016-01-02T14:00:01.001067Z

shouldn't be a problem - I added the mu/abs-zero to get rid of -0.0

2016-01-02T14:00:18.001068Z

(defn normal [a b c] (normalise! (mutable (cross (sub b a) (sub c a)))))

2016-01-02T14:00:35.001069Z

I think that would be my preferred implementation

meow 2016-01-02T14:00:41.001070Z

nice

2016-01-02T14:01:04.001071Z

If you are 100% sure you are using vectorz, you don't need the mutable either

meow 2016-01-02T14:02:17.001072Z

I also added the rounding because I was having some issues with my meshes that it fixed. Will have to see if that is still needed or not.

meow 2016-01-02T14:06:56.001073Z

Here comes the fun one because catmull-clark relies on this and I rely on catmull-clark: (gc/addm (gc/madd r 2.0 f) (* vertex (- n 3)) (/ 1.0 n))

meow 2016-01-02T14:09:04.001074Z

Some context might help. This is part of the function that calculates a new vertex point for the mesh subdivision smoothing:

get-vp (fn [mesh vertex]
                 (let [f (mc/centroid (mapv mf/centroid
                                            (mv/faces mesh vertex)))
                       vn (mv/neighbors mesh vertex)
                       n (count vn)
                       r (mc/centroid (mapv #(mc/mix vertex % 0.5) vn))]
                   (gc/addm (gc/madd r 2.0 f) (* vertex (- n 3)) (/ 1.0 n))))

meow 2016-01-02T14:12:03.001075Z

addm and madd are macros

meow 2016-01-02T14:12:14.001076Z

madd - add pairwise multiplies

meow 2016-01-02T14:12:44.001077Z

addm - product of pairwise sums

meow 2016-01-02T14:22:22.001078Z

@mikera: I think this is the last thing that needs work. So close!

2016-01-02T14:22:53.001079Z

What isn't working?

meow 2016-01-02T14:23:39.001080Z

Let me see what the error is. In the mean time, when I do this: (mx/normalise! (mx/cross (mx/sub b a) (mx/sub c a)))

meow 2016-01-02T14:23:53.001081Z

I get CompilerException java.lang.RuntimeException: java.lang.Number instance is not mutable!

meow 2016-01-02T14:24:28.001082Z

And I'm doing (mx/set-current-implementation :vectorz)

2016-01-02T14:24:49.001083Z

Hmmmm and all of a, b, c are Vector3?

meow 2016-01-02T14:26:23.001084Z

Should be.

meow 2016-01-02T14:28:18.001085Z

Okay, those addm and madd macros are protocols that aren't implemented on Vector3 CompilerException java.lang.IllegalArgumentException: No implementation of method: :madd of protocol: #'thi.ng.geom.core/PMathOps found for class: mikera.vectorz.Vector,

meow 2016-01-02T14:28:45.001086Z

So I need to figure out equivalent ways.

2016-01-02T14:29:06.001087Z

I think your args might not be vector3. I get that error if I try to do (normalise!...) on an immutable vector

meow 2016-01-02T14:29:31.001088Z

Ok, I'll check on that.

2016-01-02T14:30:31.001089Z

This works for me:

2016-01-02T14:30:32.001090Z

(let [a (array :vectorz [1 2 3]) b (array :vectorz [2 3 1]) c (array :vectorz [3 1 2])] (normalise! (cross (sub b a) (sub c a))))

2016-01-02T14:31:26.001091Z

You can of course use "normalise" instead of "normalise!"

2016-01-02T14:31:56.001092Z

Then you don't need to worry about whether your vectors are mutable or not.... for a slight performance cost....

meow 2016-01-02T14:33:48.001093Z

This probably is pointing out the fact that I'm not using a Vector3 somewhere.

2016-01-02T14:35:13.001094Z

Probably. Might be wise to put some assertions on the return types of various functions in your unit tests

meow 2016-01-02T14:49:33.001095Z

@mikera: Okay, those addm and madd macros are protocols that aren't implemented on Vector3 CompilerException java.lang.IllegalArgumentException: No implementation of method: :madd of protocol: #'thi.ng.geom.core/PMathOps found for class: mikera.vectorz.Vector, So I need to figure out equivalent ways.

meow 2016-01-02T14:50:26.001096Z

In order to get this to work:

get-vp (fn [mesh vertex]
                 (let [f (mc/centroid (mapv mf/centroid
                                            (mv/faces mesh vertex)))
                       vn (mv/neighbors mesh vertex)
                       n (count vn)
                       r (mc/centroid (mapv #(mc/mix vertex % 0.5) vn))]
                   (gc/addm (gc/madd r 2.0 f) (* vertex (- n 3)) (/ 1.0 n))))

2016-01-02T15:18:24.001097Z

hmmm what are those macros supposed to do?

meow 2016-01-02T15:32:32.001100Z

@mikera: madd - add pairwise multiplies, addm - product of pairwise sums

2016-01-02T15:33:53.001101Z

If "pairwise" means "elementwise" then madd is probably equivalent to add-product or add-scaled

2016-01-02T15:35:14.001102Z

addm is therefore something like (ereduce * (add .......))

2016-01-02T15:36:25.001104Z

Or maybe madd is the dot product?

meow 2016-01-02T15:36:55.001105Z

does this help?

(madd [_ a b]  (vm/v3-op2 #?(:clj (double-array) :cljs (new js/Float32Array)) * + buf a b 1.0 0.0 _meta))
(addm [_ a b]  (vm/v3-op2 #?(:clj (double-array) :cljs (new js/Float32Array)) + * buf a b 0.0 1.0 _meta))

2016-01-02T15:37:35.001106Z

hmmm

2016-01-02T15:37:44.001107Z

I think madd is "dot" then

2016-01-02T15:37:51.001108Z

(dot [1 2 3] [2 3 4]) => 20.0

2016-01-02T15:38:11.001109Z

i.e. the vector dot product

meow 2016-01-02T15:40:27.001112Z

This is what the logic is supposed to be:

For each original point P, take the average F of all n (recently created) face points for faces touching P, and take the average R of all n edge midpoints for edges touching P, where each edge midpoint is the average of its two endpoint vertices. Move each original point to the point
\frac{F + 2R + (n-3)P}{n}.
This is the barycenter of P, R and F with respective weights (n − 3), 2 and 1.

2016-01-02T15:41:57.001114Z

I guess you can try (defn addm [a b] (ereduce * (add a b)))

2016-01-02T15:42:23.001115Z

And (defn madd [a b] (dot a b))

2016-01-02T15:42:33.001116Z

Or something similar

meow 2016-01-02T16:24:10.001117Z

except these take 3 arguments

meow 2016-01-02T16:25:04.001118Z

@mikera: ^

2016-01-02T16:26:11.001119Z

The extension to 3 arguments is hopefully obvious :simple_smile:

meow 2016-01-02T16:26:32.001120Z

ok :simple_smile:

2016-01-02T16:27:05.001121Z

(defn madd [a b c] (ereduce + (mul a b c)))

2016-01-02T16:27:11.001122Z

For madd you might need to do

2016-01-02T16:28:02.001123Z

Actually probably better as (defn madd [a b c] (esum (mul a b c)))

meow 2016-01-02T16:30:50.001124Z

I really just want to do (/ (+ f (* 2 r) (* p (- n 3))) n)

meow 2016-01-02T16:32:28.001125Z

those macros just combine mult/add

meow 2016-01-02T16:35:44.001126Z

works like a charm: (/ (+ f (* r 2) (* vertex (- n 3))) n)

meow 2016-01-02T16:40:55.001127Z

Yeah, I think this looks pretty nice:

get-vp (fn [mesh vert]
                 (let [f (mc/centroid (mapv mf/centroid (mv/faces mesh vert)))
                       vn (mv/neighbors mesh vert)
                       n (count vn)
                       r (mc/centroid (mapv #(mc/lerp vert % 0.5) vn))]
                   (/ (+ f (* r 2) (* vert (- n 3))) n)))