Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Geometry Basics refactor #219

Open
wants to merge 117 commits into
base: sd/simple-mesh
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
117 commits
Select commit Hold shift + click to select a range
80b89e3
update MetaMesh
ffreyer Aug 30, 2024
1aa721e
add MultiFace type
ffreyer Aug 31, 2024
b08b6f9
update Mesh & MultiFace types
ffreyer Aug 31, 2024
d51280a
update Mesh & MetaMesh utils
ffreyer Aug 31, 2024
f08b120
add MultiFace remapping code
ffreyer Aug 31, 2024
ced6283
prototype MultiFace Rect -> Mesh pipeline
ffreyer Aug 31, 2024
c8c9a8f
generalize MultiFace getindex to Integer
ffreyer Aug 31, 2024
a1e3137
remove add_meta, pop_meta
ffreyer Aug 31, 2024
d89af0e
update merge for MultiFace and views
ffreyer Aug 31, 2024
5577748
add AbstractVertexFace and AbstractMultiFace
ffreyer Sep 2, 2024
8ccba36
split up mesh() for better usability
ffreyer Sep 2, 2024
3461f82
minor fixes
ffreyer Sep 2, 2024
d6fd0d0
add views to Mesh constructors
ffreyer Sep 2, 2024
8975fb1
add `mesh()` method for converting facetype of mesh
ffreyer Sep 2, 2024
4d8d731
switch back to "normals"
ffreyer Sep 2, 2024
59cd70b
fix missing normals rename
ffreyer Sep 2, 2024
8aa215c
add back point/normal/uv-type kwargs
ffreyer Sep 2, 2024
fc37dc1
consider face views in face decompose
ffreyer Sep 2, 2024
d562ab2
add mesh(mesh; attribs...) & improve dispatch safety
ffreyer Sep 2, 2024
08aedf8
fix normals(), cleanup tests
ffreyer Sep 2, 2024
e594800
add mesh constructor tests + fixes
ffreyer Sep 2, 2024
74e4304
deprecate normals for normal as vertex attribute name
ffreyer Sep 4, 2024
ad567dc
make NormalFace and NormalUVFace types again
ffreyer Sep 4, 2024
20eab7d
cleanup tests & normals vs normal
ffreyer Sep 4, 2024
35ddd36
let ci run
ffreyer Sep 4, 2024
d20c615
remove views aware face decomposition
ffreyer Sep 4, 2024
4774ed4
fix rect index order
ffreyer Sep 4, 2024
c1d9f85
fix MultiFace remapping with OffsetIntegers
ffreyer Sep 4, 2024
0bfe393
add moreMultiFace utils
ffreyer Sep 4, 2024
78cabce
Merge branch 'sd/simple-mesh' into ff/refactor
ffreyer Sep 5, 2024
ab6d803
restore decompose(FaceType, faces, views)
ffreyer Sep 5, 2024
813b3e9
Merge branch 'sd/simple-mesh' into ff/refactor
ffreyer Sep 5, 2024
5dd9650
Merge branch 'sd/simple-mesh' into ff/refactor
ffreyer Sep 5, 2024
0adac8b
Merge branch 'sd/simple-mesh' into ff/refactor
ffreyer Sep 5, 2024
00faf55
allow MultiFace -> LineFace conversion
ffreyer Sep 6, 2024
698ec9f
Merge branch 'sd/simple-mesh' into ff/refactor
ffreyer Sep 6, 2024
2b49fb5
define Rect3 faces counterclockwise
ffreyer Sep 6, 2024
ab78ad3
Merge branch 'sd/simple-mesh' into ff/refactor
ffreyer Sep 6, 2024
d218d6d
add more convenience types
ffreyer Sep 6, 2024
e5cf804
filter nothing attributes
ffreyer Sep 6, 2024
4e4ca8a
drop Base.Pairs for 1.6 compat
ffreyer Sep 6, 2024
e7f12c1
Add depwarn in hasproperty too
ffreyer Sep 9, 2024
055d708
improve MultiFace show
ffreyer Sep 10, 2024
ad03f93
update Pyramid
ffreyer Sep 10, 2024
3620842
update Cylinder
ffreyer Sep 10, 2024
86629b9
add MultiFace decompose
ffreyer Sep 10, 2024
334d897
fix Cylinder tests
ffreyer Sep 10, 2024
6944269
make OffsetInteger printing copyable
ffreyer Sep 10, 2024
883b3e1
update tests for Rect3 and normal gen
ffreyer Sep 10, 2024
7474624
fix remaining tests
ffreyer Sep 10, 2024
e5abc38
fix incorrect vertex index counter
ffreyer Sep 10, 2024
4f0bd49
simplify merge of mixed Face types
ffreyer Sep 10, 2024
5da6c02
test merge(meshes)
ffreyer Sep 10, 2024
90d4794
prototype swapping from MultiFace to FaceViews
ffreyer Sep 10, 2024
b9a0f7e
treat views in vertex index remapping + some fixes
ffreyer Sep 10, 2024
13b9881
fix face type change
ffreyer Sep 10, 2024
aef6ea2
clean up AbstractVertexFace
ffreyer Sep 11, 2024
24c0c00
extend FaceView interface
ffreyer Sep 11, 2024
50451e2
update Cylinder, Pyramids
ffreyer Sep 11, 2024
db6edb5
declutter NgonFace prints
ffreyer Sep 12, 2024
e8d48d0
update tests
ffreyer Sep 12, 2024
5ef74ff
cleanup some test_broken
ffreyer Sep 12, 2024
5aa7fa6
switch to Dict
ffreyer Sep 12, 2024
615af26
fix tests
ffreyer Sep 12, 2024
84bb1d7
remove shorthands
ffreyer Sep 12, 2024
18fff4a
export vertex_attributes and FaceView
ffreyer Sep 12, 2024
7736e84
add center point to Circle to avoid shallow triangles
ffreyer Sep 12, 2024
2015cf2
make untesselated rect vertices counter-clockwise
ffreyer Sep 12, 2024
24e6bfb
fix tests
ffreyer Sep 12, 2024
81e5ce5
fix Cylinder face windig direction
ffreyer Sep 13, 2024
355ee01
add `face_normals()` helepr function
ffreyer Sep 13, 2024
9c039c6
cleanup face_normals and normals a bit more
ffreyer Sep 13, 2024
00af22e
update tests for Cylinder
ffreyer Sep 13, 2024
075dc44
rename connectivity -> faces
ffreyer Sep 13, 2024
6d3384f
update docs (meshes, primitives, decompose, Point, Vec, Mat)
ffreyer Sep 13, 2024
6c38a0d
add/update docstrings
ffreyer Sep 13, 2024
c3bb4b2
add quick test for face_normals()
ffreyer Sep 13, 2024
bfaed13
fix TetrahedronFace conversions
ffreyer Sep 14, 2024
51a008d
restore volume functions
ffreyer Sep 14, 2024
ad90d81
fix tests
ffreyer Sep 14, 2024
05c036b
add some Polygon tests
ffreyer Sep 14, 2024
e76dee1
test Pyramids
ffreyer Sep 14, 2024
4ea085b
test TetrahedronFace decomposition
ffreyer Sep 14, 2024
1c06df4
test and improve Mesh validation
ffreyer Sep 14, 2024
8d17e14
test Mesh inteface functions
ffreyer Sep 14, 2024
0757e58
test decompose with views
ffreyer Sep 14, 2024
728d349
test and fix matrix det, inv, transpose, mat*vec
ffreyer Sep 14, 2024
d679449
fix tests
ffreyer Sep 14, 2024
d39d1dc
cleanup normal gen and export face_normals
ffreyer Sep 14, 2024
6402d75
add util for splitting meshes by views
ffreyer Sep 15, 2024
033de56
fix missing dot in range .+ idx
ffreyer Sep 15, 2024
293a154
improve performance of merge
ffreyer Sep 16, 2024
e9c83ec
fix tests
ffreyer Sep 16, 2024
5491a72
improve GeoInterface conversion performance
ffreyer Sep 16, 2024
fae59cd
switch back to NamedTuple for performance
ffreyer Sep 16, 2024
92f79b4
cleanup merge(meshes)
ffreyer Sep 16, 2024
cb1ad71
test clear_faceviews with mesh.views
ffreyer Sep 16, 2024
5066c8a
fix missing import in docs examples
ffreyer Sep 18, 2024
61759f4
add convert for arrays of meshes
ffreyer Sep 18, 2024
18ad6f0
add function for removing duplicate faces
ffreyer Sep 18, 2024
81488f9
update normal gen tests + fixes
ffreyer Sep 18, 2024
1f0f270
remove time piracy
ffreyer Sep 18, 2024
7fc8760
bring back shorthand types
ffreyer Sep 19, 2024
da91216
restrict type in meshes to error earlier
ffreyer Sep 19, 2024
e084c6a
update precompiles
ffreyer Sep 19, 2024
652b8a7
autoconvert point dim in merge(meshes) instead of restricitng type
ffreyer Sep 19, 2024
d47f511
add compat entry
ffreyer Sep 19, 2024
211209c
ignore unused PrecompileTools in 1.6, 1.7
ffreyer Sep 19, 2024
11d7593
bring back old precompiles
ffreyer Sep 19, 2024
81d5bfe
add convert target to orthogonal_vector
ffreyer Sep 19, 2024
e12e85b
revert triangulation changes of Circle
ffreyer Sep 19, 2024
12eb1c5
revert to using StaticArrays
ffreyer Sep 19, 2024
af27647
avoid some invalidations (and fix get)
ffreyer Sep 26, 2024
d90ca22
fix stale instances due to AbstractVector
ffreyer Sep 26, 2024
b9cb8c2
use string interpolation in error to avoid invalidation from string *
ffreyer Sep 26, 2024
b33f82d
fix test
ffreyer Sep 26, 2024
82dd5ce
export clear_faceviews & update FaceView docstring
ffreyer Oct 9, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ on:
pull_request:
branches:
- master
- sd/simple-mesh
jobs:
test:
name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }}
Expand Down
4 changes: 4 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ Extents = "411431e0-e8b7-467b-b5e0-f676ba4f2910"
GeoInterface = "cf35fbd7-0cd7-5166-be24-54bfbe79505f"
IterTools = "c8e1da08-722c-5040-9ed9-7db0dc04731e"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
PrecompileTools = "aea7be01-6a6a-4083-8856-8a6e6704d82a"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"

[compat]
Aqua = "0.8"
Expand All @@ -20,7 +22,9 @@ GeoJSON = "0.7, 0.8"
IterTools = "1.3.0"
LinearAlgebra = "<0.0.1,1"
OffsetArrays = "1"
PrecompileTools = "1.0"
Random = "<0.0.1,1"
StaticArrays = "0.6, 1"
Test = "<0.0.1,1"
julia = "1.6"

Expand Down
3 changes: 1 addition & 2 deletions docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,10 @@ makedocs(format=Documenter.HTML(prettyurls=get(ENV, "CI", "false") == "true"),
pages=[
"index.md",
"primitives.md",
"rectangles.md",
"polygons.md",
"meshes.md",
"decomposition.md",
"metadata.md",
"static_array_types.md",
"api.md"
],
modules=[GeometryBasics])
Expand Down
105 changes: 18 additions & 87 deletions docs/src/decomposition.md
Original file line number Diff line number Diff line change
@@ -1,89 +1,20 @@
# Decomposition


## GeometryBasics Mesh interface

GeometryBasics defines an interface to decompose abstract geometries into
points and triangle meshes.
This can be done for any arbitrary primitive, by overloading the following interface:

```julia

function GeometryBasics.coordinates(rect::Rect2, nvertices=(2,2))
mini, maxi = extrema(rect)
xrange, yrange = LinRange.(mini, maxi, nvertices)
return ivec(((x,y) for x in xrange, y in yrange))
end

function GeometryBasics.faces(rect::Rect2, nvertices=(2, 2))
w, h = nvertices
idx = LinearIndices(nvertices)
quad(i, j) = QuadFace{Int}(idx[i, j], idx[i+1, j], idx[i+1, j+1], idx[i, j+1])
return ivec((quad(i, j) for i=1:(w-1), j=1:(h-1)))
end
```
Those methods, for performance reasons, expect you to return an iterator, to make
materializing them with different element types allocation free. But of course,
can also return any `AbstractArray`.

With these methods defined, this constructor will magically work:

```julia
rect = Rect2(0.0, 0.0, 1.0, 1.0)
m = GeometryBasics.mesh(rect)
```
If you want to set the `nvertices` argument, you need to wrap your primitive in a `Tesselation`
object:
```julia
m = GeometryBasics.mesh(Tesselation(rect, (50, 50)))
length(coordinates(m)) == 50^2
```

As you can see, `coordinates` and `faces` are also defined on a mesh
```julia
coordinates(m)
faces(m)
```
But will actually not be an iterator anymore. Instead, the mesh constructor uses
the `decompose` function, that will collect the result of coordinates and will
convert it to a concrete element type:
```julia
decompose(Point2f, rect) == convert(Vector{Point2f}, collect(coordinates(rect)))
```
The element conversion is handled by `simplex_convert`, which also handles convert
between different face types:
```julia
decompose(QuadFace{Int}, rect) == convert(Vector{QuadFace{Int}}, collect(faces(rect)))
length(decompose(QuadFace{Int}, rect)) == 1
fs = decompose(GLTriangleFace, rect)
fs isa Vector{GLTriangleFace}
length(fs) == 2 # 2 triangles make up one quad ;)
```
`mesh` uses the most natural element type by default, which you can get with the unqualified Point type:
```julia
decompose(Point, rect) isa Vector{Point{2, Float64}}
```
You can also pass the element type to `mesh`:
```julia
m = GeometryBasics.mesh(rect, pointtype=Point2f, facetype=QuadFace{Int})
```
You can also set the uv and normal type for the mesh constructor, which will then
calculate them for you, with the requested element type:
```julia
m = GeometryBasics.mesh(rect, uv=Vec2f, normaltype=Vec3f)
```

As you can see, the normals are automatically calculated,
the same is true for texture coordinates. You can overload this behavior by overloading
`normals` or `texturecoordinates` the same way as coordinates.
`decompose` works a bit different for normals/texturecoordinates, since they dont have their own element type.
Instead, you can use `decompose` like this:
```julia
decompose(UV(Vec2f), rect)
decompose(Normal(Vec3f), rect)
# the short form for the above:
decompose_uv(rect)
decompose_normals(rect)
```
You can also use `triangle_mesh`, `normal_mesh` and `uv_normal_mesh` to call the
`mesh` constructor with predefined element types (Point2/3f, Vec2/3f), and the requested attributes.
## decompose functions

The `decompose` functions allow you to grab certain data from an `AbstractGeometry` like a mesh or primitive and convert it to a requested type, if possible.
They can also be used to convert an array of e.g. faces into a different face type directly.
The default decomposition implemented by GeoemtryBasics are:
- `decompose(::Type{<: Point}, source)` which collects data from `source` using `coordinates(source)` and converts it to the given point type.
- `decompose_normals([::Type{<: Vec},] source) = decompose([::Type{Normals{<: Vec}}},] source)` which collects data with `normals(source)` and converts it to the given Vec type.
- `decompose_uv([::Type{<: Vec},] source) = decompose([::Type{UV{<: Vec}}},] source)` which collects data with `texturecoordinates(source)` and converts it to the given Vec type. This function also exists with `UVW` texture coordinates.
- `decompose(::Type{<: AbstractFace}, source)` which collects data with `faces(source)` and converts it to the given face type.

## Primitive decomposition

GeometryBasics defines an interface to decompose geometry primitives into vertex attributes and faces.
The interface includes four functions:
- `coordinates(primitive[, nvertices])` which produces the positions associated with the primitive
- `faces(primitive[, nvertices])` which produces the faces which connect the vertex positions to a mesh
- `normals(primitive[, nvertices])` which optionally provide normal vectors of the primitive
- `texturecoordinates(primitive[, nvertices])` which optional provide texture coordinates (uv/uvw) of the primitive
5 changes: 0 additions & 5 deletions docs/src/implementation.md

This file was deleted.

85 changes: 6 additions & 79 deletions docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,56 +22,20 @@ p2 = Point(1, 3);
p3 = Point(4, 4);
```

Geometries can carry metadata:

```@repl quickstart
poi = meta(p1, city="Abuja", rainfall=1221.2)
```

Metadata is stored in a NamedTuple and can be retrieved as such:

```@repl quickstart
meta(poi)
```

Specific metadata attributes can be directly retrieved:

```@repl quickstart
poi.rainfall
```

To remove the metadata and keep only the geometry, use `metafree`:

```@repl quickstart
metafree(poi)
```

Geometries have predefined metatypes:

```@repl quickstart
multipoi = MultiPointMeta([p1], city="Abuja", rainfall=1221.2)
```

Connect the points with lines:
Connect pairs of points as line segments:

```@repl quickstart
l1 = Line(p1, p2)
l2 = Line(p2, p3);
```

Connect the lines in a linestring:

```@repl quickstart
LineString([l1, l2])
```

Linestrings can also be constructed directly from points:
Or connect multiple points as a linestring:

```@repl quickstart
LineString([p1, p2, p3])
```

The same goes for polygons:
You can also create polygons from points:

```@repl quickstart
Polygon(Point{2, Int}[(3, 1), (4, 4), (2, 4), (1, 2), (3, 1)])
Expand All @@ -89,57 +53,20 @@ Decompose the rectangle into two triangular faces:
rect_faces = decompose(TriangleFace{Int}, rect)
```

Decompose the rectangle into four vertices:
Decompose the rectangle into four positions:

```@repl quickstart
rect_vertices = decompose(Point{2, Float64}, rect)
rect_positions = decompose(Point{2, Float64}, rect)
```

Combine the vertices and faces into a triangle mesh:

```@repl quickstart
mesh = Mesh(rect_vertices, rect_faces)
mesh = Mesh(rect_positions, rect_faces)
```

Use `GeometryBasics.mesh` to get a mesh directly from a geometry:

```@repl quickstart
mesh = GeometryBasics.mesh(rect)
```


## Aliases

GeometryBasics exports common aliases for Point, Vec, Mat and Rect:

### Vec

| |`T`(eltype) |`Float64` |`Float32` |`Int` |`UInt` |
|--------|------------|----------|----------|----------|----------|
|`N`(dim)|`Vec{N,T}` |`Vecd{N}` |`Vecf{N}` |`Veci{N}` |`Vecui{N}`|
|`2` |`Vec2{T}` |`Vec2d` |`Vec2f` |`Vec2i` |`Vec2ui` |
|`3` |`Vec3{T}` |`Vec3d` |`Vec3f` |`Vec3i` |`Vec3ui` |

### Point

| |`T`(eltype) |`Float64` |`Float32` |`Int` |`UInt` |
|--------|------------|----------|----------|----------|----------|
|`N`(dim)|`Point{N,T}`|`Pointd{N}`|`Pointf{N}`|`Pointi{N}`|`Pointui{N}`|
|`2` |`Point2{T}` |`Point2d` |`Point2f` |`Point2i` |`Point2ui`|
|`3` |`Point3{T}` |`Point3d` |`Point3f` |`Point3i` |`Point3ui`|

### Mat

| |`T`(eltype) |`Float64` |`Float32` |`Int` |`UInt` |
|--------|------------|----------|----------|----------|----------|
|`N`(dim)|`Mat{N,T}` |`Matd{N}` |`Matf{N}` |`Mati{N}` |`Matui{N}`|
|`2` |`Mat2{T}` |`Mat2d` |`Mat2f` |`Mat2i` |`Mat2ui` |
|`3` |`Mat3{T}` |`Mat3d` |`Mat3f` |`Mat3i` |`Mat3ui` |

### Rect

| |`T`(eltype) |`Float64` |`Float32` |`Int` |`UInt` |
|--------|------------|----------|----------|----------|----------|
|`N`(dim)|`Rect{N,T}` |`Rectd{N}`|`Rectf{N}`|`Recti{N}`|`Rectui{N}`|
|`2` |`Rect2{T}` |`Rect2d` |`Rect2f` |`Rect2i` |`Rect2ui` |
|`3` |`Rect3{T}` |`Rect3d` |`Rect3f` |`Rect3i` |`Rect3ui` |
98 changes: 86 additions & 12 deletions docs/src/meshes.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,98 @@
# Meshes

## Types
GeometryBasics defines two mesh types to work with - `Mesh` and `MetaMesh`

* [`AbstractMesh`](@ref)
* [`Mesh`](@ref)
## Mesh

A `Mesh` is defined by the following struct:

```julia
struct Mesh{
Dim, PositionElType <: Real,
FT <: AbstractFace,
FVT <: AbstractVector{FT}
} <: AbstractMesh{Dim, T}

vertex_attributes::Dict{Symbol, Union{FaceView, AbstractVector}}
faces::FVT
views::Vector{UnitRange{Int}}
end
```

`mesh.vertex_attributes` contains all the per-vertex data like positions, normals, texture coordinates, etc.
They must always contain at least positions.
`mesh.faces` defines a set of (default) faces to use for each vertex attribute.
If a vertex attribute is a `FaceView` it carries its own set of faces which are used instead of `mesh.faces`.
In this case the number of faces and length of each face must match between them.
`mesh.views` is a set of views into the `mesh.faces` which can be used to separate the mesh into smaller submeshes.

You can get data from a mesh using a few interface functions:
- `vertex_attributes(mesh) = mesh.vertex_attributes`
- `coordinates(mesh) = mesh.vertex_attributes[:position]`
- `normals(mesh) = mesh.vertex_attributes[:normal]`
- `texturecoordinates(mesh) = mesh.vertex_attributes[:uv]`
- `faces(mesh) = mesh.faces`

You can also grab the contents of `mesh.vertex_attributes` as if they were fields of the `Mesh`, e.g. `mesh.position` works.

### FaceView

As mentioned above, a vertex attribute can be a `FaceView`.
A `FaceView` is simply defined as a vector of data and a vector of faces:

```julia
struct FaceView{T, AVT <: AbstractVector{T}, FVT <: AbstractVector{<: AbstractFace}}
data::AVT
faces::FVT
end
```

Its purpose is to allow you to add data that needs to be defined per vertex but does not match the vertex structure used by `mesh.faces`.

As a minimal example consider a mesh that is just one triangle, i.e. 3 position and one triangle face `TriangleFace(1,2,3)`.
Let's say we want to add a flat color to the triangle.
In this case we only have one color, but our face refers to 3 different vertices (3 different positions).
To avoid duplicating the color data, we can instead define a new triangle face `TriangleFace(1)` and add the color attribute as a `FaceView([color], [TriangleFace(1)])`.
If we ever need the mesh to be defined with just one common set of faces, i.e. no FaceView and appropriately duplicated vertex data, we can use `clear_faceviews(mesh)` to generate it.

On a larger scale this can be useful for memory and performance reason, e.g. when you do calculations with vertex attributes.
It can also simplify some definitions, like for example `Rect3`.
In that case we have 8 positions and 6 normals with FaceViews, or 24 without (assuming per-face normals).


## MetaMesh

A `MetaMesh` is given by

```julia
struct MetaMesh{Dim, T, M <: AbstractMesh{Dim, T}} <: AbstractMesh{Dim, T}
mesh::M
meta::Dict{Symbol, Any}
end
```

where `meta` may contain any data you want to include with a mesh.
For example, you could include group names or material data corresponding to `mesh.views`.

## How to create a mesh

### Meshing.jl
### GeometryBasics

### MeshIO.jl
In GeometryBasics you mainly create meshes from primitives using a few constructors:
- `triangle_mesh(primitive)` generates the most basic mesh (i.e. positions and faces)
- `normal_mesh(primitive)` generates a mesh with normals (generated if the primitive doesn't implement `normal()`)
- `uv_mesh(primitive)` generates a mesh with texture coordinates (generated if the primitive doesn't implement `texturecoordinates()`)
- `uv_normal_mesh(primitive)` generates a mesh with normals and texture coordinates

The [`MeshIO.jl`](https://github.com/JuliaIO/MeshIO.jl) package provides load/save support for several file formats which store meshes.
Each of these constructors also includes keyword arguments for setting types, i.e. `pointtype`, `facetype`, `normaltype` and `uvtype` as appropriate.
Of course you can also construct a mesh directly from data, either with there various `Mesh()` or `GeometryBasics.mesh()` constructors.
The latter also include a `pointtype` and `facetype` conversion.

## How to access data
Finally there is also a `merge(::Vector{Mesh})` function which combines multiple meshes into a single one.
Note that this doesn't remove any data (e.g. hidden or duplicate vertices), and may remove `FaceView`s if they are incompatible between meshes.

The following functions can be called on an [`AbstractMesh`](@ref) to access its underlying data.
### Meshing.jl

* [`faces`](@ref)
* [`coordinates`](@ref)
* `texturecoordinates`
* [`normals`](@ref)
### MeshIO.jl

The [`MeshIO.jl`](https://github.com/JuliaIO/MeshIO.jl) package provides load/save support for several file formats which store meshes.
Loading
Loading