Fork me on GitHub
#datavis
<
2016-01-02
>
meow01:01:39

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

meow01:01:13

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

meow03:01:03

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

meow03:01:51

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

meow03:01:47

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

meow03:01:45

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.

meow03:01:18

In face.clj I've got this function:

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

meow03:01:48

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

meow03:01:19

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

meow03:01:26

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.

meow03:01:22

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

meow13:01:30

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

mikera13:01:59

Sure happy to help if I can

meow13:01:26

Awesome. TYVM.

meow13:01:19

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

meow13:01:16

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

mikera13:01:04

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

meow13:01:30

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

mikera13:01:11

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

mikera13:01:12

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

mikera13:01:54

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

meow13:01:13

Excellent!

meow13:01:09

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

meow13:01:55

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

meow13:01:39

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

mikera13:01:43

Hmmm centroid is just the average of points

meow13:01:58

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

mikera13:01:18

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

mikera13:01:32

Not fully optimised but should work simple_smile

meow13:01:03

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

mikera13:01:06

Why the mapv stuff?

mikera13:01:29

Can't you just cross and normalise?

meow13:01:29

I was wondering that myself. simple_smile

mikera13:01:48

Unless it is some funny handling for degenerate triangles

meow13:01:55

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

meow13:01:53

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

mikera13:01:22

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

meow14:01:01

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

mikera14:01:18

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

mikera14:01:35

I think that would be my preferred implementation

mikera14:01:04

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

meow14:01:17

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.

meow14:01:56

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

meow14:01:04

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

meow14:01:03

addm and madd are macros

meow14:01:14

madd - add pairwise multiplies

meow14:01:44

addm - product of pairwise sums

meow14:01:22

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

mikera14:01:53

What isn't working?

meow14:01:39

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

meow14:01:53

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

meow14:01:28

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

mikera14:01:49

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

meow14:01:23

Should be.

meow14:01:18

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,

meow14:01:45

So I need to figure out equivalent ways.

mikera14:01:06

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

meow14:01:31

Ok, I'll check on that.

mikera14:01:31

This works for me:

mikera14:01:32

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

mikera14:01:26

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

mikera14:01:56

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

meow14:01:48

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

mikera14:01:13

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

meow14:01:33

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

meow14:01:26

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

mikera15:01:24

hmmm what are those macros supposed to do?

meow15:01:32

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

mikera15:01:53

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

mikera15:01:14

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

mikera15:01:25

Or maybe madd is the dot product?

meow15:01:55

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

mikera15:01:44

I think madd is "dot" then

mikera15:01:51

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

mikera15:01:11

i.e. the vector dot product

meow15:01:27

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.

mikera15:01:57

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

mikera15:01:23

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

mikera15:01:33

Or something similar

meow16:01:10

except these take 3 arguments

mikera16:01:11

The extension to 3 arguments is hopefully obvious simple_smile

mikera16:01:05

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

mikera16:01:11

For madd you might need to do

mikera16:01:02

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

meow16:01:50

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

meow16:01:28

those macros just combine mult/add

meow16:01:44

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

meow16:01:55

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