Catmull–Clark subdivision surface: Difference between revisions

m
(Added Rust as a translation of Python.)
m (→‎{{header|Wren}}: Minor tidy)
(3 intermediate revisions by 2 users not shown)
Line 1:
{{task|3D}}[[Category:Graphics algorithms]]{{requires|Graphics}}
[[Category:Geometry]]
{{task|3D}}{{requires|Graphics}}
Implement the Catmull-Clark surface subdivision ([[wp:Catmull–Clark_subdivision_surface|description on Wikipedia]]), which is an algorithm that maps from a surface (described as a set of points and a set of polygons with vertices at those points) to another more refined surface. The resulting surface will always consist of a mesh of quadrilaterals.
 
Line 42 ⟶ 44:
 
For edges and vertices not next to a hole, the standard algorithm from above is used.
 
=={{header|C}}==
[[file:catmull-C.png|center]]
Only the subdivision part. The [[Catmull–Clark_subdivision_surface/C|full source]] is way too long to be shown here. Lots of macros, you'll have to see the full code to know what's what.
<langsyntaxhighlight lang="c">vertex face_point(face f)
{
int i;
Line 138 ⟶ 139:
}
return nm;
}</langsyntaxhighlight>
 
=={{header|Go}}==
{{trans|Python}}
Line 146:
 
See the original version for full comments.
<langsyntaxhighlight lang="go">package main
 
import (
Line 431:
fmt.Printf("%2d\n", f)
}
}</langsyntaxhighlight>
 
{{out}}
Line 487:
[ 4 23 13 20]
</pre>
 
=={{header|Haskell}}==
<langsyntaxhighlight lang="haskell">{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE ScopedTypeVariables #-}
 
Line 799 ⟶ 798:
 
main :: IO ()
main = putStr . pShowSimpleMesh $ subdivide testCube</langsyntaxhighlight>
{{out}}
<pre>Vertices:
Line 853 ⟶ 852:
(22,[24,15,5,8])
(23,[22,8,5,7])</pre>
 
=={{header|J}}==
 
<langsyntaxhighlight lang="j">avg=: +/ % #
 
havePoints=: e."1/~ i.@#
Line 878 ⟶ 876:
msh=. (,c0+mesh),.(,e0+edges i. meshEdges),.((#i.)~/$mesh),.,e0+_1|."1 edges i. meshEdges
msh;pts
)</langsyntaxhighlight>
 
Example use:
 
<langsyntaxhighlight lang="j">NB.cube
points=: _1+2*#:i.8
mesh=: 1 A."1 I.(,1-|.)8&$@#&0 1">4 2 1
Line 914 ⟶ 912:
│ │ 0.555556 0.555556 _0.555556│
│ │ 0.555556 0.555556 0.555556│
└──────────┴─────────────────────────────┘</langsyntaxhighlight>
 
=={{header|Julia}}==
<langsyntaxhighlight lang="julia">using Makie, Statistics
 
# Point3f0 is a 3-tuple of 32-bit floats for 3-dimensional space, and all Points are 3D.
Line 1,062 ⟶ 1,059:
 
println("Press Enter to continue", readline())
</syntaxhighlight>
</lang>
 
=={{header|Mathematica}}/{{header|Wolfram Language}}==
This implementation supports tris, quads, and higher polys, as well as surfaces with holes.
The function relies on three externally defined general functionality functions:
 
<langsyntaxhighlight Mathematicalang="mathematica">subSetQ[large_,small_] := MemberQ[large,small]
subSetQ[large_,small_List] := And@@(MemberQ[large,#]&/@small)
 
containing[groupList_,item_]:= Flatten[Position[groupList,group_/;subSetQ[group,item]]]
 
ReplaceFace[face_]:=Transpose[Prepend[Transpose[{#[[1]],face,#[[2]]}&/@Transpose[Partition[face,2,1,1]//{#,RotateRight[#]}&]],face]]</langsyntaxhighlight>
subSetQ[small,large] is a boolean test for whether small is a subset of large. Note this is not a general purpose implimentation and only serves this purpose under the constrictions of the following program.
 
Line 1,083 ⟶ 1,079:
 
 
<langsyntaxhighlight Mathematicalang="mathematica">CatMullClark[{Points_,faces_}]:=Block[{avgFacePoints,avgEdgePoints,updatedPoints,newEdgePoints,newPoints,edges,newFaces,weights,pointUpdate,edgeUpdate,newIndex},
edges = DeleteDuplicates[Flatten[Partition[#,2,1,-1]&/@faces,1],Sort[#1]==Sort[#2]&];
avgFacePoints=Mean[Points[[#]]] &/@ faces;
Line 1,110 ⟶ 1,106:
newFaces = Flatten[Map[newIndex[#,{Points,edges,faces}]&,ReplaceFace/@faces,{-2}],1];
{newPoints,newFaces}
]</langsyntaxhighlight>
 
The implimentation can be tested with polygons with and without holes by using the polydata
<langsyntaxhighlight Mathematicalang="mathematica">{points,faces}=PolyhedronData["Cube",{"VertexCoordinates","FaceIndices"}];
 
Function[iteration,
Graphics3D[(Polygon[iteration[[1]][[#]]]&/@iteration[[2]])]
]/@NestList[CatMullClark,{points,faces},3]//GraphicsRow</langsyntaxhighlight>
[[File:CAM noholes 1.png]]
 
For a surface with holes the resulting iterative subdivision will be:
<langsyntaxhighlight Mathematicalang="mathematica">faces = Delete[faces, 6];
Function[iteration, Graphics3D[
(Polygon[iteration[[1]][[#]]] & /@ iteration[[2]])
]] /@ NestList[CatMullClark, {points, faces}, 3] // GraphicsRow</langsyntaxhighlight>
[[File:CAM holes 1.png]]
 
This code was written in Mathematica 8.
 
=={{header|Nim}}==
{{trans|Go}}
{{trans|Python}}
<langsyntaxhighlight Nimlang="nim">import algorithm
import tables
 
Line 1,487 ⟶ 1,482:
s.add(fmt"{n:2d}")
s.add(']')
echo s</langsyntaxhighlight>
 
{{out}}
Line 1,542 ⟶ 1,537:
[ 2, 20, 13, 17]
[ 4, 23, 13, 20]</pre>
 
=={{header|OCaml}}==
{{incorrect|OCaml|wrong output data}}
Line 1,552 ⟶ 1,546:
In the [[Catmull–Clark subdivision surface/OCaml|sub-page]] there is also a program in OCaml+OpenGL which displays a cube subdivided 2 times with this algorithm.
 
<langsyntaxhighlight lang="ocaml">open Dynar
 
let add3 (x1, y1, z1) (x2, y2, z2) (x3, y3, z3) =
Line 1,687 ⟶ 1,681:
(Dynar.to_array da_points,
Dynar.to_array new_faces)
;;</langsyntaxhighlight>
=== Another implementation ===
Another implementation which should work with holes, but has only been tested on a cube
{{works with|OCaml|4.02+}}
<langsyntaxhighlight OCamllang="ocaml">type point = { x: float; y : float; z : float }
let zero = { x = 0.0; y = 0.0; z = 0.0 }
let add a b = { x = a.x+.b.x; y = a.y+.b.y; z = a.z+.b.z }
Line 1,764 ⟶ 1,758:
Face [c 0 0 0; c 0 1 0; c 1 1 0; c 1 0 0]; Face [c 0 0 1; c 0 1 1; c 1 1 1; c 1 0 1] ] in
show_faces cube;
show_faces (catmull_clark cube)</langsyntaxhighlight>
with output:
<pre>surface {
Line 1,800 ⟶ 1,794:
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)
}</pre>
 
=={{header|Phix}}==
{{trans|Go}}
<!--<langsyntaxhighlight Phixlang="phix">(phixonline)-->
<span style="color: #000080;font-style:italic;">-- demo\rosetta\Catmull_Clark_subdivision_surface.exw</span>
<span style="color: #008080;">with</span> <span style="color: #008080;">javascript_semantics</span>
Line 2,020 ⟶ 2,013:
<span style="color: #7060A8;">pp</span><span style="color: #0000FF;">(</span><span style="color: #000000;">outputPoints</span><span style="color: #0000FF;">)</span>
<span style="color: #7060A8;">pp</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">sq_sub</span><span style="color: #0000FF;">(</span><span style="color: #000000;">outputFaces</span><span style="color: #0000FF;">,</span><span style="color: #000000;">1</span><span style="color: #0000FF;">),{</span><span style="color: #004600;">pp_IntFmt</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"%2d"</span><span style="color: #0000FF;">})</span>
<!--</langsyntaxhighlight>-->
{{out}}
<pre>
Line 2,074 ⟶ 2,067:
{ 4,23,13,20}}
</pre>
 
=={{header|Python}}==
<langsyntaxhighlight lang="python">
"""
 
Line 2,633 ⟶ 2,625:
graph_output(output_points, output_faces)
</syntaxhighlight>
</lang>
 
=={{header|Rust}}==
{{trans|Python}}
<syntaxhighlight lang="rust">
<lang Rust>
 
pub struct Vector3 {pub x: f64, pub y: f64, pub z: f64, pub w: f64}
 
Line 2,662 ⟶ 2,652:
pub fn intersect_plane(plane_n: &Vector3, plane_p: &Vector3, line_start: &Vector3, line_end: &Vector3, mut t: f64) -> Vector3 {
let mut p_n = plane_n.copy();
https://www.youtube.com/watch?v=QuqNwts5mc0&list=PLydfMPb3IeEPQFtfUO_WN7I_86_7WXWvQ&index=15
p_n.normalize();
let plane_d = -Vector3::dot_product(&p_n, plane_p);
Line 2,922 ⟶ 2,912:
}
}
</syntaxhighlight>
</lang>
 
=={{header|Tcl}}==
This code handles both holes and arbitrary polygons in the input data.
<langsyntaxhighlight lang="tcl">package require Tcl 8.5
 
# Use math functions and operators as commands (Lisp-like).
Line 3,047 ⟶ 3,036:
 
list [concat $newPoints $facepoints $edgepoints] $newFaces
}</langsyntaxhighlight>
 
The [[/Tcl Test Code|test code]] for this solution is available as well. The example there produces the following partial toroid output image:
 
<center>[[File:Tcl-Catmull.png]]</center>
 
=={{header|Wren}}==
{{trans|Go}}
Line 3,059 ⟶ 3,047:
{{libheader|Wren-math}}
{{libheader|Wren-fmt}}
<langsyntaxhighlight ecmascriptlang="wren">import "./dynamic" for Tuple, Struct
import "./sort" for Sort
import "./math" for Int
import "./fmt" for Fmt
 
var Point = Tuple.create("Point", ["x", "y", "z"])
Line 3,328 ⟶ 3,316:
for (f in outputFaces) {
Fmt.aprint(f, 2, 0, "[]")
}</langsyntaxhighlight>
 
{{out}}
Line 3,384 ⟶ 3,372:
[ 4 23 13 20]
</pre>
 
[[Category:Geometry]]
9,476

edits