Drawing an icosphere
In the previous post we drew a 20-sided icosahedron:
We'll use this as our starting point for making an icosphere. First, we divide one of the triangular faces into four triangles:
Here's the code to do it:
(defn average [& xs]
(/ (reduce + xs) (count xs)))
(defn subdivide-face [[a b c]]
(let [ab (map average a b)
ac (map average a c)
bc (map average b c)]
[[a ab ac]
[b bc ab]
[c ac bc]
[ab bc ac]]))
Using mesh
from the previous post, we can subdivide all the faces:
(mapcat subdivide-face mesh)
This isn't any closer to a sphere than the original icosahedron. While we now have 80 triangles, we still only have 20 flat faces. To more closely approximate a sphere, we'll slide each of the new points out to the surface of the bounding sphere.
(defn extend-point [point]
(let [length (Math/sqrt (transduce (map #(* % %)) + point))]
(map #(/ % length) point)))
This function considers each point to be a vector from the center of the icosahedron and scales that vector to length 1, which puts it on the boundary of the unit sphere.
We'll use a transducer to subdivide the mesh:
(def subdivide
(comp
(mapcat subdivide-face)
(map (partial map extend-point))))
(sequence subdivide mesh)
A couple more levels of subdivision:
(sequence (comp subdivide subdivide) mesh)
(sequence (comp subdivide subdivide subdivide) mesh)
We now have 1,280 faces and a decent approximation of a sphere.
Icospheres produce an even distribution of points around the sphere, as opposed to UV spheres, which, like the intersections of lines of longitude and latitude, cluster points more densely around the poles.