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

(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

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))]
#[email protected](: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))]
#[email protected](: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

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.

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

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

mikera15:01:53

mikera15:01:14

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

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

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