After several rounds of refactoring I've gotten Catmull-Clark down to this (and I'm pretty happy with it):
(defn catmull-clark
"Return a mesh with additional faces and edge points for a smoothing effect."
[{:keys [faces edges vertices] :as mesh} & {:keys [get-f-point get-e-point get-v-point]}]
(let [get-fp (fn [mesh face]
(calc-vertex face))
get-ep (fn [edge e-faces f-points]
(gu/centroid (concat (vec edge) (mapv f-points e-faces))))
get-vp (fn [mesh vertex]
(let [f (gu/centroid (mapv gu/centroid
(gm/vertex-faces* mesh vertex)))
vn (gm/vertex-neighbors* mesh vertex)
n (count vn)
r (gu/centroid (mapv #(g/mix vertex %) vn))]
(g/addm (g/madd r 2.0 f) (g/* vertex (- n 3)) (/ 1.0 n))))
get-f-point (or get-f-point get-fp)
get-e-point (or get-e-point get-ep)
get-v-point (or get-v-point get-vp)
new-face (fn [[p c n] f-point e-points]
[(e-points #{p c}) c (e-points #{c n}) f-point])
new-faces (fn [face f-point e-points]
(mapv #(new-face % f-point e-points) (face-loop-triples face)))
subdivide (fn [[face f-point] e-points]
(new-faces face f-point e-points))
v-replace (fn [face vert-map] (replace vert-map face))
f-points (into {} (map (fn [face]
[face (get-f-point mesh face)]) faces))
e-points (into {} (map (fn [[edge e-faces]]
[edge (get-e-point edge e-faces f-points)]) edges))
v-points (into {} (map (fn [vertex]
[vertex (get-v-point mesh vertex)]) (keys vertices)))]
(->> (mapcat #(subdivide % e-points) f-points)
(map #(v-replace % v-points))
(g/into (g/clear* mesh)))))
And here is an example showing how to create a variant by supplying an alternate function:
(defn not-quite-catmull-clark
"Return a mesh with additional faces and edge points for a smoothing effect."
[mesh]
(let [get-e-point (fn [edge _ _] (gu/centroid (vec edge)))]
(catmull-clark mesh :get-e-point get-e-point)))
I wish @toxi were here to answer some basic question about http://thi.ng geom. 😞
It's frustrating trying to figure out how to do something I know is easy, I just haven't done it before so I'm not sure what particular method of which protocol is going to do the trick. I have a vertex and I have the centroid of the mesh and now I need to calculate a new vertex that is a certain distance closer to the mesh centroid by a fixed (not proportional) amount.
Should be easy, right? Aaarrrggghhh!!!
I've been using g/mix
to get a certain percentage distance between two points.
I might get a clue from one of the extrusion functions, since basically I'm working on "skeletonizing" a mesh, which is kind of like a fancy extrusion of sorts.
I think we forgot to mention to @toxi that once you show your face in #C0F0V8DT5, you're obligated to be here at all times 😉
#datavisyoucancheckoutanytimeyoulike
Yeah, otherwise we'll post annoyingly animated parrot emoticons to take your place.
😉
2D extrude didn't help because that's super easy as it just does a simple offset along the z axis
convex-hull on a polygon sounded like it might hold a clue, but it just looks like Greek to me... 😞
I feel like I'm missing something really basic and I just can't find what I need.
Maybe Line3?
It's probably just a simple vector math operation - guess I get to learn more about vec3
I think I'm going to give up until @toxi comes back because that matrix math stuff doesn't look fun.
Oh well, no skeletons today. 👻
I probably want to use the vertex normals for this anyway, rather than a distance from the vertex to the mesh centroid, to make this work for more complex shapes.
except that requires me to tessellate the mesh, which I don't want to do at this point of the process...
@toxi: when you are here some time I might still need help with skeletonizing a mesh. To begin I'd be happy with just creating a shell or inset hull of the shape, then I won't have any trouble putting holes into each face.
hmm, I could be evil and create another mesh that's tesselated, get the vertex normals for it, then just use it as a lookup for my original mesh so I've got a normal for each vertex I care about but still have faces that haven't been tesselated. Mwhaa haa haa!!! 😉 So evil!!!
I know, I'm talking to myself, aren't I? Sad, but true.
But am I talking back? That's the important question.
I don't want to triangulate my faces until I have to. If I know the face is planar I want to be able to get a normal for it. I see vertices-planar?
but it doesn't seem to be used anywhere.
Also, @toxi, is the inc
here a bug:
(defn vertex-valence*
[mesh v] (inc (count (get (:vertices mesh) v))))
Shouldn't it just be the count?
That's in gmesh.
I'm talking to toxi, but he's not here, that's why it seems like I'm talking to myself, but I'm really not. Really! I'm serious.