Catmull–Clark subdivision surface: Difference between revisions
Content added Content deleted
(→{{header|C}}: screenshot) |
(→{{header|OCaml}}: (another implementation)) |
||
Line 378: | Line 378: | ||
Dynar.to_array new_faces) |
Dynar.to_array new_faces) |
||
;;</lang> |
;;</lang> |
||
Another implementation which should work with holes, but has only been tested on a cube |
|||
<lang OCaml>type point = { x: float; y : float; z : float } |
|||
let add a b = { x = a.x+.b.x; y = a.y+.b.y; z = a.z+.b.z } |
|||
let mul a k = { x = a.x*.k; y = a.y*.k; z= a.z*.k } |
|||
let div p k = mul p (1.0/.k) |
|||
let fsgn x y = if x < y then -1 else if x > y then 1 else 0 |
|||
let cmp a b = if a.x=b.x then if a.y=b.y then fsgn b.z a.z else fsgn b.y a.y else fsgn b.x a.x |
|||
type face = Face of point list |
|||
type edge = Edge of point*point |
|||
let ecmp (Edge (p1,p2)) (Edge (p3,p4)) = let sgn = cmp p1 p3 in if sgn = 0 then cmp p2 p4 else sgn |
|||
let make_edge a b = if cmp a b < 0 then Edge (b,a) else Edge (a,b) |
|||
let make_face a b c d = Face [a;b;c;d] |
|||
let centroid plist = div (List.fold_left add {x=0.0;y=0.0;z=0.0} plist) (float (List.length plist)) |
|||
let mid_edge (Edge (p1,p2)) = div (add p1 p2) 2.0 |
|||
let face_point (Face pl) = centroid pl |
|||
let point_in_face p (Face pl) = List.mem p pl |
|||
let point_in_edge p (Edge (p1,p2)) = (p = p1 || p = p2) |
|||
let edge_in_face (Edge (p1,p2)) (Face pl) = (List.mem p1 pl && List.mem p2 pl) |
|||
let border_edge faces e = |
|||
List.length (List.filter (edge_in_face e) faces) < 2 |
|||
let edge_point faces e = |
|||
if border_edge faces e then mid_edge e else |
|||
let adjacent = List.filter (edge_in_face e) faces in |
|||
let fps = List.map face_point adjacent in |
|||
centroid [mid_edge e; centroid fps] |
|||
let mod_vertex faces edges (p:point) = |
|||
let v_edges = List.filter (point_in_edge p) edges in |
|||
let v_faces = List.filter (point_in_face p) faces in |
|||
let n = List.length v_faces in |
|||
let is_border = n != (List.length v_edges) in |
|||
if is_border then |
|||
let (border_mids: point list) = List.map mid_edge (List.filter (border_edge faces) v_edges) in |
|||
(* description ambiguity: average (border+p) or average(average(border),p) ?? *) |
|||
centroid (p :: border_mids) |
|||
else |
|||
let avg_face = centroid (List.map face_point v_faces) in |
|||
let avg_mid = centroid (List.map mid_edge v_edges) in |
|||
div (add (add (mul p (float(n-3))) avg_face) (mul avg_mid 2.0)) (float n) |
|||
let iter_edges f (Face pl) = |
|||
let rec next = function |
|||
| [] -> failwith "edgeless face" |
|||
| a :: [] -> f a (List.hd pl) |
|||
| a :: b :: c -> f a b; next (b::c) in |
|||
next pl;; |
|||
module EdgeSet = Set.Make(struct type t = edge let compare = ecmp end) |
|||
let catmull_clark faces = |
|||
let eset = ref EdgeSet.empty in |
|||
let add_edge a b = eset := EdgeSet.add (make_edge a b) !eset in |
|||
let edges = (List.iter (iter_edges add_edge) faces; EdgeSet.elements !eset) in |
|||
let new_faces = ref [] in |
|||
let mod_face ((Face pl) as face) = |
|||
let fp = face_point face in |
|||
let ep = ref [] in ( |
|||
iter_edges (fun a b -> ep := (edge_point faces (make_edge a b)):: !ep) face; |
|||
let e_tl = List.hd (List.rev !ep) in |
|||
let v' = List.map (mod_vertex faces edges) pl in |
|||
let rec add_facet e vl el = (match (vl,el) with |
|||
| (h1::t1),(h2::t2) -> |
|||
new_faces := (make_face e h1 h2 fp) :: !new_faces; |
|||
add_facet h2 t1 t2 |
|||
| ([],[]) -> () |
|||
| _ -> failwith "facet") in |
|||
add_facet e_tl v' !ep) in |
|||
(List.iter mod_face faces; !new_faces) |
|||
let show_faces fl = |
|||
let pr_point p = Printf.printf " (%.4f, %.4f, %.4f)" p.x p.y p.z in |
|||
let pr_face (Face(pl)) = print_string "Face:"; List.iter pr_point pl; print_string "\n" in |
|||
(print_string "surface {\n"; List.iter pr_face fl; print_string "}\n") |
|||
let c000 = { x=(-1.0); y=(-1.0); z=(-1.0) } |
|||
let c001 = { x=(-1.0); y=(-1.0); z= 1.0 } |
|||
let c010 = { x=(-1.0); y= 1.0 ; z=(-1.0) } |
|||
let c011 = { x=(-1.0); y= 1.0 ; z= 1.0 } |
|||
let c100 = { x= 1.0 ; y=(-1.0); z=(-1.0) } |
|||
let c101 = { x= 1.0 ; y=(-1.0); z= 1.0 } |
|||
let c110 = { x= 1.0 ; y= 1.0 ; z=(-1.0) } |
|||
let c111 = { x= 1.0 ; y= 1.0 ; z= 1.0 } |
|||
let cube = [ |
|||
(Face [c000;c001;c011;c010]); (Face [c100;c101;c111;c110]); |
|||
(Face [c000;c100;c101;c001]); (Face [c010;c110;c111;c011]); |
|||
(Face [c000;c010;c110;c100]); (Face [c001;c011;c111;c101]) ];; |
|||
show_faces cube;; |
|||
show_faces (catmull_clark cube);;</lang> |
|||
with output: |
|||
<pre>surface { |
|||
Face: (-1.0000, -1.0000, -1.0000) (-1.0000, -1.0000, 1.0000) (-1.0000, 1.0000, 1.0000) (-1.0000, 1.0000, -1.0000) |
|||
Face: (1.0000, -1.0000, -1.0000) (1.0000, -1.0000, 1.0000) (1.0000, 1.0000, 1.0000) (1.0000, 1.0000, -1.0000) |
|||
Face: (-1.0000, -1.0000, -1.0000) (1.0000, -1.0000, -1.0000) (1.0000, -1.0000, 1.0000) (-1.0000, -1.0000, 1.0000) |
|||
Face: (-1.0000, 1.0000, -1.0000) (1.0000, 1.0000, -1.0000) (1.0000, 1.0000, 1.0000) (-1.0000, 1.0000, 1.0000) |
|||
Face: (-1.0000, -1.0000, -1.0000) (-1.0000, 1.0000, -1.0000) (1.0000, 1.0000, -1.0000) (1.0000, -1.0000, -1.0000) |
|||
Face: (-1.0000, -1.0000, 1.0000) (-1.0000, 1.0000, 1.0000) (1.0000, 1.0000, 1.0000) (1.0000, -1.0000, 1.0000) |
|||
} |
|||
surface { |
|||
Face: (0.0000, 0.7500, 0.7500) (0.5556, -0.5556, 0.5556) (-0.7500, 0.0000, 0.7500) (0.0000, 0.0000, 1.0000) |
|||
Face: (0.7500, 0.0000, 0.7500) (0.5556, 0.5556, 0.5556) (0.0000, 0.7500, 0.7500) (0.0000, 0.0000, 1.0000) |
|||
Face: (0.0000, -0.7500, 0.7500) (-0.5556, 0.5556, 0.5556) (0.7500, 0.0000, 0.7500) (0.0000, 0.0000, 1.0000) |
|||
Face: (-0.7500, 0.0000, 0.7500) (-0.5556, -0.5556, 0.5556) (0.0000, -0.7500, 0.7500) (0.0000, 0.0000, 1.0000) |
|||
Face: (0.0000, 0.7500, -0.7500) (0.5556, -0.5556, -0.5556) (-0.7500, 0.0000, -0.7500) (0.0000, 0.0000, -1.0000) |
|||
Face: (0.7500, 0.0000, -0.7500) (0.5556, 0.5556, -0.5556) (0.0000, 0.7500, -0.7500) (0.0000, 0.0000, -1.0000) |
|||
Face: (0.0000, -0.7500, -0.7500) (-0.5556, 0.5556, -0.5556) (0.7500, 0.0000, -0.7500) (0.0000, 0.0000, -1.0000) |
|||
Face: (-0.7500, 0.0000, -0.7500) (-0.5556, -0.5556, -0.5556) (0.0000, -0.7500, -0.7500) (0.0000, 0.0000, -1.0000) |
|||
Face: (0.7500, 0.7500, 0.0000) (-0.5556, 0.5556, 0.5556) (0.0000, 0.7500, -0.7500) (0.0000, 1.0000, 0.0000) |
|||
Face: (0.0000, 0.7500, 0.7500) (0.5556, 0.5556, 0.5556) (0.7500, 0.7500, 0.0000) (0.0000, 1.0000, 0.0000) |
|||
Face: (-0.7500, 0.7500, 0.0000) (0.5556, 0.5556, -0.5556) (0.0000, 0.7500, 0.7500) (0.0000, 1.0000, 0.0000) |
|||
Face: (0.0000, 0.7500, -0.7500) (-0.5556, 0.5556, -0.5556) (-0.7500, 0.7500, 0.0000) (0.0000, 1.0000, 0.0000) |
|||
Face: (0.7500, -0.7500, 0.0000) (-0.5556, -0.5556, 0.5556) (0.0000, -0.7500, -0.7500) (0.0000, -1.0000, 0.0000) |
|||
Face: (0.0000, -0.7500, 0.7500) (0.5556, -0.5556, 0.5556) (0.7500, -0.7500, 0.0000) (0.0000, -1.0000, 0.0000) |
|||
Face: (-0.7500, -0.7500, 0.0000) (0.5556, -0.5556, -0.5556) (0.0000, -0.7500, 0.7500) (0.0000, -1.0000, 0.0000) |
|||
Face: (0.0000, -0.7500, -0.7500) (-0.5556, -0.5556, -0.5556) (-0.7500, -0.7500, 0.0000) (0.0000, -1.0000, 0.0000) |
|||
Face: (0.7500, 0.0000, 0.7500) (0.5556, 0.5556, -0.5556) (0.7500, -0.7500, 0.0000) (1.0000, 0.0000, 0.0000) |
|||
Face: (0.7500, 0.7500, 0.0000) (0.5556, 0.5556, 0.5556) (0.7500, 0.0000, 0.7500) (1.0000, 0.0000, 0.0000) |
|||
Face: (0.7500, 0.0000, -0.7500) (0.5556, -0.5556, 0.5556) (0.7500, 0.7500, 0.0000) (1.0000, 0.0000, 0.0000) |
|||
Face: (0.7500, -0.7500, 0.0000) (0.5556, -0.5556, -0.5556) (0.7500, 0.0000, -0.7500) (1.0000, 0.0000, 0.0000) |
|||
Face: (-0.7500, 0.0000, 0.7500) (-0.5556, 0.5556, -0.5556) (-0.7500, -0.7500, 0.0000) (-1.0000, 0.0000, 0.0000) |
|||
Face: (-0.7500, 0.7500, 0.0000) (-0.5556, 0.5556, 0.5556) (-0.7500, 0.0000, 0.7500) (-1.0000, 0.0000, 0.0000) |
|||
Face: (-0.7500, 0.0000, -0.7500) (-0.5556, -0.5556, 0.5556) (-0.7500, 0.7500, 0.0000) (-1.0000, 0.0000, 0.0000) |
|||
Face: (-0.7500, -0.7500, 0.0000) (-0.5556, -0.5556, -0.5556) (-0.7500, 0.0000, -0.7500) (-1.0000, 0.0000, 0.0000) |
|||
}</pre> |
|||
=={{header|Tcl}}== |
=={{header|Tcl}}== |