1 - For each face in the mesh, form new vertices as follows:

- Form the face's centroid - average of the vertices
- Form the edge midpoints of the face
- Each new vertex is the average of a face vertex, the centroid, and the two adjacent edge midpoints.

- F-faces are constructed by joining new vertices within a face
- E-faces are constructed by joining new vertices as the edges of neighboring old faces
- V-faces are constructed by joining all new vertices around an old vertex

My initial thought is to represent a 3D vertex as a list. A face is then a list of vertices. I'm hijacking a couple of routines I wrote for vector math to help compute the centroid.

> addVec :: RealFloat a => [a] -> [a] -> [a]

> addVec v w = [ a + b | (a, b) <- zip v w]

> scaleVec :: RealFloat a => [a] -> a -> [a]

> scaleVec v c = [a * c | a <- v]

> centroid :: RealFloat a => [[a]] -> [a]

> centroid p = scaleVec (foldl1 addVec p) (1/fromIntegral (length p))

Time for a clever hack. I need to compute the midpoints of each edge of a face. Imagine that we have a triangular face with vertices v1, v2, v3. I need to compute the midpoint of v1v2, v2v3 and v3v1. By zipping a shifted version of my vertex list with itself, I'll have the desired vertices paired up.

> shiftr :: [a] -> [a]

> shiftr [] = []

> shiftr (x:y) = y ++ [x]

> midPoint :: RealFloat a => [a] -> [a] -> [a]

> midPoint x y = [(a+b)/2 | (a,b) <- zip x y]

> midPoints :: RealFloat a => [[a]] -> [[a]]

> midPoints p = [midPoint a b | (a,b) <- zip p (shiftr p)]

Now that I have the centroid and midpoints, I just need to compute the new vertices. The trick here is to get the midpoints adjacent to a face vertex. Another shift is in order, but in the opposite direction. My initial routine was this:

> shiftl :: [a] -> [a]

> shiftl [] = []

> shiftl x = [last x] ++ init x

I don't like the fact that this routine makes two passes though the list. I posted this code to the haskell-cafe list looking for better ideas. I got this really nifty routine as a response.

> shiftl (x1:x2:xs) = last:x1:init

> where last:init = shiftl (x2:xs)

> shiftl xs = xs

So, here is the routine to compute the new vertices:

> dooSabinNewVertices :: RealFloat a => [[a]] -> [[a]]

> dooSabinNewVertices n = [ centroid [c, x, y, z] | (x,(y,z)) <-

> zip n (zip m (shiftl m))]

> where

> c = centroid n

> m = midPoints n

Next up is how to compute the new faces.

## No comments:

Post a Comment