odf/elm-mesh/1.1.0

NOTE: the latest version is 1.4.0

Mesh

This module provides functions for working with indexed meshes. You can:

  • Construct meshes from vertices and face indices
  • Extract vertices, faces and edges in various ways
  • Combine multiple meshes into a single mesh
type Mesh vertex

A Mesh is an instance of a polygon mesh which is implemented as a so-called half-edge data structure. Each mesh consists of an array of vertices and some connectivity information that describes how faces, edges and vertices are related to each other. A face must have at least three vertices, but can also have four, five or more.

The half-edge data structure guarantees that the mesh describes a surface with a consistent "orientation", which intuitively means that if one were to walk along on the surface of the mesh one could never find oneself back at the starting point but standing upside-down on the other side of the mesh.

The vertices themselves can be any type you want. For a 2D mesh, you might have each vertex be simply a point:

type alias Mesh2d =
    Mesh Point2d

For a 3D mesh, each vertex might be a (point, normal) tuple:

type alias Mesh3d =
    Mesh ( Point3d, Vector3d )

In more complex cases, each vertex might be a record:

type alias VertexData =
    { position : Point3d
    , normal : Vector3d
    , color : Color
    }


type alias RenderMesh =
    Mesh VertexData

Constants

empty : Mesh vertex

A mesh with no vertices or faces.

Constructors

fromOrientedFaces : Array vertex -> List (List Int) -> Result String (Mesh vertex)

Create a mesh from an array of vertices and a list of face indices. For example, to construct a square pyramid where a is the front right corner, b is the front left corner, c is the back left corner, d is the back right corner and e is the apex:

vertices =
    Array.fromList [ a, b, c, d, e ]


faceIndices =
    [ [ 0, 1, 2, 3 ]
    , [ 0, 4, 1 ]
    , [ 1, 4, 2 ]
    , [ 2, 4, 3 ]
    , [ 3, 4, 0 ]
    ]


pyramid =
    Mesh.fromOrientedFaces vertices faceIndices
        |> Result.withDefault Mesh.empty

The return type here is a Result String (Mesh vertex) rather than just a Mesh vertex so that a specific error message can be produced if there is a problem with the input.

Interop

fromTriangularMesh : TriangularMesh vertex -> Result String (Mesh vertex)

Try to convert a TriangularMesh instance into a Mesh. This could potentially fail because a Mesh is more restrictive.

toTriangularMesh : Mesh vertex -> TriangularMesh vertex

Triangulate each face of the mesh and return the result as a TriangularMesh instance.

Combining meshes

combine : List (Mesh a) -> Mesh a

Combine a list of meshes into a single mesh. This concatenates the vertex arrays of each mesh and adjusts face indices to refer to the combined vertex array.

pyramid =
    Mesh.fromOrientedFaces
        (Array.fromList [ a, b, c, d, e ])
        [ [ 0, 1, 2, 3 ]
        , [ 0, 4, 1 ]
        , [ 1, 4, 2 ]
        , [ 2, 4, 3 ]
        , [ 3, 4, 0 ]
        ]


tetrahedron =
    Mesh.fromOrientedFaces
        (Array.fromList [ A, B, C, D ])
        [ [ 0, 1, 2 ]
        , [ 0, 3, 1 ]
        , [ 1, 3, 2 ]
        , [ 2, 3, 0 ]
        ]


combined =
    Mesh.combine [ pyramid, tetrahedron ]


Mesh.vertices combined
--> Array.fromList [ a, b, c, d, e, A, B, C, D ]


Mesh.faceIndices combined
--> [ [ 0, 1, 2, 3 ]
--> , [ 0, 4, 1 ]
--> , [ 1, 4, 2 ]
--> , [ 2, 4, 3 ]
--> , [ 0, 3, 4 ]
--> , [ 5, 6, 7 ]
--> , [ 5, 8, 6 ]
--> , [ 6, 8, 7 ]
--> , [ 5, 7, 8 ]
--> ]

Properties

vertices : Mesh vertex -> Array vertex

Get the vertices of a mesh.

Mesh.vertices pyramid
--> Array.fromList [ a, b, c, d, e ]
vertex : Int -> Mesh vertex -> Maybe vertex

Get a particular vertex of a mesh by index. If the index is out of range, returns Nothing.

Mesh.vertex 1 pyramid
--> Just b


Mesh.vertex 5 pyramid
--> Nothing
faceIndices : Mesh vertex -> List (List Int)

Get the faces of a mesh as lists of vertex indices. Each face is normalized so that its smallest vertex index comes first.

Mesh.faceIndices pyramid
--> [ [ 0, 1, 2, 3 ]
--> , [ 0, 4, 1 ]
--> , [ 1, 4, 2 ]
--> , [ 2, 4, 3 ]
--> , [ 0, 3, 4 ]
--> ]
faceVertices : Mesh vertex -> List (List vertex)

Get the faces of a mesh as lists of vertices. The ordering of vertices for each face is the same as in faceIndices.

Mesh.faceVertices pyramid
--> [ [ a, b, c, d ]
--> , [ a, e, b ]
--> , [ b, e, c ]
--> , [ c, e, d ]
--> , [ a, d, e ]
--> ]
boundaryIndices : Mesh vertex -> List (List Int)

Get the boundary components of a mesh as lists of vertex indices. Each component is normalized so that its smallest vertex index comes first.

pyramidTop =
    Mesh.fromOrientedFaces
        (Array.fromList [ a, b, c, d, e ])
        [ [ 0, 4, 1 ]
        , [ 1, 4, 2 ]
        , [ 2, 4, 3 ]
        , [ 3, 4, 0 ] ]
        |> Result.withDefault Mesh.empty


Mesh.faceIndices pyramidTop
--> [ [ 0, 4, 1 ]
--> , [ 1, 4, 2 ]
--> , [ 2, 4, 3 ]
--> , [ 0, 3, 4 ]
--> ]


Mesh.boundaryIndices pyramidTop
--> [ [ 0, 1, 2, 3 ] ]
boundaryVertices : Mesh vertex -> List (List vertex)

Get the boundary components of a mesh as lists of vertices. The ordering of vertices for each component is the same as in faceIndices.

Mesh.boundaryVertices pyramidTop
--> [ [ a, b, c, d ] ]
edgeIndices : Mesh vertex -> List ( Int, Int )

Get all of the edges of a mesh as pairs of vertex indices. Each edge will only be returned once, with the lower-index vertex listed first, and will be returned in sorted order.

Mesh.edgeIndices pyramid
--> [ ( 0, 1 )
--> , ( 0, 3 )
--> , ( 0, 4 )
--> , ( 1, 2 )
--> , ( 1, 4 )
--> , ( 2, 3 )
--> , ( 2, 4 )
--> , ( 3, 4 )
--> ]
edgeVertices : Mesh vertex -> List ( vertex, vertex )

Get all of the edges of a mesh as pairs of vertices. Each edge will only be returned once, with the lower-index vertex listed first, and will be returned in sorted order (by index).

Mesh.edgeVertices pyramid
--> [ ( a, b )
--> , ( a, d )
--> , ( a, e )
--> , ( b, c )
--> , ( b, e )
--> , ( c, d )
--> , ( c, e )
--> , ( d, e )
--> ]
neighborIndices : Mesh vertex -> Array (List Int)

Get the neighbors of all vertices as lists of vertex indices. The i-th entry in the output array corresponds to the i-th vertex of the mesh. The neighbors are ordered as one would encounter them while walking around the vertex in the same direction (clockwise or counterclockwise) as the vertices for each face are listed, starting from the one with the lowest vertex index.

Mesh.neighborIndices pyramid
--> [ [ 1, 3, 4 ]
--> , [ 0, 4, 2 ]
--> , [ 1, 4, 3 ]
--> , [ 0, 2, 4 ]
--> , [ 0, 3, 2, 1 ]
--> ]
neighborVertices : Mesh vertex -> Array (List vertex)

Get the neighbors of all vertices as lists of vertices. Further details are as in neighborIndices.

Transformations

mapVertices : (a -> b) -> Mesh a -> Mesh b

Transform a mesh by applying the given function to each of its vertices. For example, if you had a 2D mesh where each vertex was an ( x, y ) tuple and you wanted to convert it to a 3D mesh on the XY plane, you might use

mesh2d : Mesh ( Float, Float )
mesh2d =
    ...


to3d : ( Float, Float ) -> ( Float, Float, Float )
to3d ( x, y ) =
    ( x, y, 0 )


mesh3d : Mesh ( Float, Float, Float )
mesh3d =
    Mesh.mapVertices to3d mesh2d
(a -> Point3d units coordinates)
-> (a -> Vector3d Unitless coordinates -> b)
-> Mesh a
-> Mesh b

Calculate normals for a mesh. The first argument is a function that takes a vertex of the input mesh and returns its position as a Point3d. The second argument is a function that takes a vertex of the input mesh and its computed normal as a Vector3d, and returns the corresponding vertex for the output mesh.

subdivide : (List vertex -> vertex) -> Mesh vertex -> Mesh vertex

Subdivide a mesh by first adding a new vertex in the center of each edge, subdividing each edge into two in the process, then adding a new vertex in the center of each face and splitting the face into quadrangles by connecting the new face center to the new edge centers.

The first argument is a function that takes a list of input vertices, namely the vertices of an edge or face to be subdivided, and returns the new "center" vertex.

(vertex -> Bool)
-> (vertex -> Point3d units coordinates)
-> (List vertex -> Point3d units coordinates -> vertex)
-> Mesh vertex
-> Mesh vertex

Subdivide a mesh into quadrangles using the Catmull-Clark method. The connectivity of the result will be the same as in subdivide, but the vertices will be repositioned to make it appear smoother.

The first argument is a function that returns True if the input vertex should not be moved. The second argument is a function that returns the position of an input vertex. The third argument is a function that takes a list of input vertices and a computed output position and returns an output vertex.