@mikera: I don't understand how to use scale-add!
What I need is to do a linear interpolation from one vector to another by a normalized percentage.
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
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
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))
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.
In face.clj
I've got this function:
(defn- mag [[x y z]]
(Math/sqrt (mm/madd x x y y z z)))
I'm not sure how the madd
macro should be converted.
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))
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.
I don't think I could have gotten to the end of this without the quirky dubstepesque Beats Antique blowing up my ears. 😉
@mikera: I'm so close to being done with this conversion to core.matrix - can I get a little bit of help?
Sure happy to help if I can
Awesome. TYVM.
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))
You mentioned scale-add!
as a substitute. How?
@mikera: ^
I'm actually adding a lerp function to the next iteration of core.matrix, should be released today :simple_smile:
I don't know what lerp is. Pretty funny how far I've gotten without brushing up on my maths.
This is roughly how scale-add! could be used to do it however
(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)))
You can replace "mix" with "lerp" once the next core.matrix / vectorz-clj is released
Excellent!
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))))
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)))
@mikera: I just need a centroid
function that works with core.matrix
.
Hmmm centroid is just the average of points
I'm reading about lerp, slerp and nlerp: https://keithmaggio.wordpress.com/2011/02/15/math-magician-lerp-slerp-and-nlerp/
@mikera: right, so how do I get the average for more than 2 points?
(defn centroid [points] (scale (apply add points) (/ 1.0 (count points))))
Not fully optimised but should work :simple_smile:
Sweet! :simple_smile:
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)))))))
Why the mapv stuff?
Can't you just cross and normalise?
I was wondering that myself. :simple_smile:
Unless it is some funny handling for degenerate triangles
I've been refactoring this code over time and sometimes it gets to the point that the stupidity becomes obvious.
No, I think the beauty of triangles is that there really are no degenerate ones.
I guess I mean triangles where the cross product is the zero vector...
shouldn't be a problem - I added the mu/abs-zero to get rid of -0.0
(defn normal [a b c] (normalise! (mutable (cross (sub b a) (sub c a)))))
I think that would be my preferred implementation
nice
If you are 100% sure you are using vectorz, you don't need the mutable either
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.
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))
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))))
addm
and madd
are macros
madd - add pairwise multiplies
addm - product of pairwise sums
@mikera: I think this is the last thing that needs work. So close!
What isn't working?
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)))
I get CompilerException java.lang.RuntimeException: java.lang.Number instance is not mutable!
And I'm doing (mx/set-current-implementation :vectorz)
Hmmmm and all of a, b, c are Vector3?
Should be.
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.
I think your args might not be vector3. I get that error if I try to do (normalise!...) on an immutable vector
Ok, I'll check on that.
This works for me:
(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))))
You can of course use "normalise" instead of "normalise!"
Then you don't need to worry about whether your vectors are mutable or not.... for a slight performance cost....
This probably is pointing out the fact that I'm not using a Vector3 somewhere.
Probably. Might be wise to put some assertions on the return types of various functions in your unit tests
@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.
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))))
hmmm what are those macros supposed to do?
@mikera: madd - add pairwise multiplies, addm - product of pairwise sums
If "pairwise" means "elementwise" then madd is probably equivalent to add-product or add-scaled
addm is therefore something like (ereduce * (add .......))
Or maybe madd is the dot product?
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))
hmmm
I think madd is "dot" then
(dot [1 2 3] [2 3 4]) => 20.0
i.e. the vector dot product
https://github.com/thi-ng/geom/blob/master/geom-core/src/vector.org
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.
details here: https://en.wikipedia.org/wiki/Catmull%E2%80%93Clark_subdivision_surface
I guess you can try (defn addm [a b] (ereduce * (add a b)))
And (defn madd [a b] (dot a b))
Or something similar
except these take 3 arguments
@mikera: ^
The extension to 3 arguments is hopefully obvious :simple_smile:
ok :simple_smile:
(defn madd [a b c] (ereduce + (mul a b c)))
For madd you might need to do
Actually probably better as (defn madd [a b c] (esum (mul a b c)))
I really just want to do (/ (+ f (* 2 r) (* p (- n 3))) n)
those macros just combine mult/add
works like a charm: (/ (+ f (* r 2) (* vertex (- n 3))) n)
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)))