Here is my solution to this quiz.

It's fairly straightforward the only (slightly) noteworthy point is that I  
decided to use spherical interpolation instead of linear interpolation  
+ normalize to calculate the subdivided vertices.
When using linear interpolation + normalize along a line, the points in  
the middle will be further apart than the ones at the ends. With spherical  
interpolation they are distributed evenly across the arc on the spherical  
surface.
I thought that would make the resulting triangles equal sized but I had to  
realise that this is actually impossible on the surface of a sphere.
So the triangles of my solution are closer to being equal sized but the  
ones in the centres of the original faces are still bigger than the ones  
at the original vertices.

Anyway, here is my code:

class Vector
   include Math

   attr_reader :x, :y, :z

   def self.[](x, y, z)
     self.new(x, y, z)
   end

   def initialize(x, y, z)
     @x = x
     @y = y
     @z = z
   end

   def +(o)
     Vector.new(@x+o.x, @y+o.y, @z+o.z)
   end

   def -(o)
     Vector.new(@x-o.x, @y-o.y, @z-o.z)
   end

   def *(o)
     case o
     when Vector
       @x*o.x + @y*o.y + @z*o.z
     else
       Vector.new(@x*o, @y*o, @z*o)
     end
   end

   def length
     sqrt(self*self)
   end

   def normalize
     return self * (1 / length)
   end

   def slerp(vec, f)
     cosinus = self * vec
     angle = acos(cosinus)
     l1 = sin(angle / 2)
     r = cos(angle / 2)
     l2 = r * tan(angle * (f - 0.5))
     f = l2 / l1 * 0.5 + 0.5
     return (self * (1 - f) + vec * f).normalize
   end

   def to_s
     "[%7.4f, %7.4f, %7.4f]" % [@x, @y, @z]
   end
end

class Triangle
   def initialize(v1, v2, v3)
     @v1 = v1
     @v2 = v2
     @v3 = v3
   end

   def subdivide(frequency)
     @freq = frequency + 1
     faces = []
     for y in 0..frequency
       for x in 0..y
         faces << Triangle.new(self[x, y], self[x, y+1], self[x+1, y+1])
         faces << Triangle.new(self[x, y], self[x+1, y+1], self[x+1, y]) if  
x < y
       end
     end
     return faces
   end

   def to_s
     "[%s, %s, %s]" % [@v1, @v2, @v3]
   end

private

   def [](x, y)
     return @v1 if y == 0
     p1 = @v1.slerp(@v2, y.to_f / @freq)
     p2 = @v1.slerp(@v3, y.to_f / @freq)
     return p1.slerp(p2, x.to_f / y)
   end
end

class Dome
   def initialize(datahash = nil)
     @faces = []
     if datahash
       points = datahash[:points]
       datahash[:faces].each do |face|
         vertices = face.split(//).map {|v| points[v]}
         add_face(Triangle.new(*vertices))
       end
     end
   end

   def subdivide(frequency)
     new_dome = Dome.new
     @faces.each do |face|
       face.subdivide(frequency).each do |new_face|
         new_dome.add_face(new_face)
       end
     end
     return new_dome
   end

   def add_face(face)
     @faces << face
   end

   def to_s
     @faces.join("\n")
   end
end

SQRT2 = Math.sqrt(2)
SQRT3 = Math.sqrt(3)
TETRA_Q = SQRT2 / 3
TETRA_R = 1.0 / 3
TETRA_S = SQRT2 / SQRT3
TETRA_T = 2 * SQRT2 / 3
GOLDEN_MEAN = (Math.sqrt(5)+1)/2

PRIMITIVES = {
   :tetrahedron => {
     :points => {
       'a' => Vector[ -TETRA_S, -TETRA_Q, -TETRA_R ],
       'b' => Vector[  TETRA_S, -TETRA_Q, -TETRA_R ],
       'c' => Vector[        0,  TETRA_T, -TETRA_R ],
       'd' => Vector[        0,        0,        1 ]
     },
     :faces => %w| acb abd adc dbc |
   },
   :octahedron => {
     :points => {
       'a' => Vector[  0,  0,  1 ],
       'b' => Vector[  1,  0,  0 ],
       'c' => Vector[  0, -1,  0 ],
       'd' => Vector[ -1,  0,  0 ],
       'e' => Vector[  0,  1,  0 ],
       'f' => Vector[  0,  0, -1 ]
     },
     :faces => %w| cba dca eda bea
       def ebf bcf cdf |
   },
   :icosahedron => {
     :points => {
       'a' => Vector[  1,  GOLDEN_MEAN, 0 ],
       'b' => Vector[  1, -GOLDEN_MEAN, 0 ],
       'c' => Vector[ -1, -GOLDEN_MEAN, 0 ],
       'd' => Vector[ -1,  GOLDEN_MEAN, 0 ],
       'e' => Vector[  GOLDEN_MEAN, 0,  1 ],
       'f' => Vector[ -GOLDEN_MEAN, 0,  1 ],
       'g' => Vector[ -GOLDEN_MEAN, 0, -1 ],
       'h' => Vector[  GOLDEN_MEAN, 0, -1 ],
       'i' => Vector[ 0,  1,  GOLDEN_MEAN ],
       'j' => Vector[ 0,  1, -GOLDEN_MEAN ],
       'k' => Vector[ 0, -1, -GOLDEN_MEAN ],
       'l' => Vector[ 0, -1,  GOLDEN_MEAN ]
     },
     :faces => %w| iea iad idf ifl ile
       eha ajd dgf fcl lbe
       ebh ahj djg fgc lcb
       khb kjh kgj kcg kbc |
   }
}

puts Dome.new(PRIMITIVES[:octahedron]).subdivide(2)

-- 
exoticorn/farbrausch