Skip to content

Commit c22086d

Browse files
authored
Merge pull request #57 from JuliaGeometry/sd/meta2
improve thay docs
2 parents 93bdce4 + 95c65ab commit c22086d

File tree

4 files changed

+80
-38
lines changed

4 files changed

+80
-38
lines changed

docs/src/decomposition.md

Lines changed: 71 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,89 @@
11
# Decomposition
22

33

4-
## Displaying primitives
4+
## GeometryBasic Mesh interface
55

6-
To display geometry primitives, they need to be decomposable.
6+
GeometryBasic defines an interface, to decompose abstract geometries into
7+
points and triangle meshes.
78
This can be done for any arbitrary primitive, by overloading the following interface:
89

910
```julia
10-
# Let's take SimpleRectangle as an example:
11-
# Below is a minimal set of decomposable attributes to build up a triangle mesh:
12-
isdecomposable(::Type{T}, ::Type{HR}) where {T<:Point, HR<:SimpleRectangle} = true
13-
isdecomposable(::Type{T}, ::Type{HR}) where {T<:Face, HR<:SimpleRectangle} = true
14-
15-
# This is an example implementation of `decompose` for points.
16-
function GeometryBasics.decompose(P::Type{Point{3, PT}}, r::SimpleRectangle, resolution=(2,2)) where PT
17-
w,h = resolution
18-
vec(
19-
PT[
20-
(x,y,0)
21-
for x in range(r.x, stop = r.x+r.w, length = w),
22-
y in range(r.y, stop = r.y+ r .h, length = h)
23-
]
24-
)
11+
12+
function GeometryBasics.coordinates(rect::Rect2D, nvertices=(2,2))
13+
mini, maxi = extrema(rect)
14+
xrange, yrange = LinRange.(mini, maxi, nvertices)
15+
return ivec(((x,y) for x in xrange, y in yrange))
2516
end
2617

27-
function GeometryBasics.decompose(::Type{T}, r::SimpleRectangle, resolution=(2,2)) where T <: Face
28-
w,h = resolution
29-
Idx = LinearIndices(resolution)
30-
faces = vec([Face{4, Int}(
31-
Idx[i, j], Idx[i+1, j],
32-
Idx[i+1, j+1], Idx[i, j+1]
33-
) for i=1:(w-1), j=1:(h-1)]
34-
)
35-
decompose(T, faces)
18+
function GeometryBasics.faces(rect::Rect2D, nvertices=(2, 2))
19+
w, h = nvertices
20+
idx = LinearIndices(nvertices)
21+
quad(i, j) = QuadFace{Int}(idx[i, j], idx[i+1, j], idx[i+1, j+1], idx[i, j+1])
22+
return ivec((quad(i, j) for i=1:(w-1), j=1:(h-1)))
3623
end
3724
```
25+
Those methods, for performance reasons, expect you to return an iterator, to make
26+
materializing them with different element types allocation free. But of course,
27+
can also return any `AbstractArray`.
3828

3929
With these methods defined, this constructor will magically work:
4030

4131
```julia
42-
rect = SimpleRectangle(0, 0, 1, 1)
43-
m = GLNormalMesh(rect)
44-
vertices(m) == decompose(Point3f0, rect)
32+
rect = Rect2D(0.0, 0.0, 1.0, 1.0)
33+
m = GeometryBasics.mesh(rect)
34+
```
35+
If you want to set the `nvertices` argument, you need to wrap your primitive in a `Tesselation`
36+
object:
37+
```julia
38+
m = GeometryBasics.mesh(Tesselation(rect, (50, 50)))
39+
length(coordinates(m)) == 50^2
40+
```
4541

46-
faces(m) == decompose(GLTriangle, rect) # GLFace{3} == GLTriangle
47-
normals(m) # automatically calculated from mesh
42+
As you can see, `coordinates` and `faces` is also defined on a mesh
43+
```julia
44+
coordinates(m)
45+
faces(m)
46+
```
47+
But will actually not be an iterator anymore. Instead, the mesh constructor uses
48+
the `decompose` function, that will collect the result of coordinates and will
49+
convert it to a concrete element type:
50+
```julia
51+
decompose(Point2f0, rect) == convert(Vector{Point2f0}, collect(coordinates(rect)))
52+
```
53+
The element conversion is handled by `simplex_convert`, which also handles convert
54+
between different face types:
55+
```julia
56+
decompose(QuadFace{Int}, rect) == convert(Vector{QuadFace{Int}}, collect(faces(rect)))
57+
length(decompose(QuadFace{Int}, rect)) == 1
58+
fs = decompose(GLTriangleFace, rect)
59+
fs isa Vector{GLTriangleFace}
60+
length(fs) == 2 # 2 triangles make up one quad ;)
61+
```
62+
`mesh` uses the most natural element type by default, which you can get with the unqualified Point type:
63+
```julia
64+
decompose(Point, rect) isa Vector{Point{2, Float64}}
65+
```
66+
You can also pass the element type to `mesh`:
67+
```julia
68+
m = GeometryBasics.mesh(rect, pointtype=Point2f0, facetype=QuadFace{Int})
69+
```
70+
You can also set the uv and normal type for the mesh constructor, which will then
71+
calculate them for you, with the requested element type:
72+
```julia
73+
m = GeometryBasics.mesh(rect, uv=Vec2f0, normaltype=Vec3f0)
4874
```
4975

50-
As you can see, the normals are automatically calculated only with the faces and points.
51-
You can overwrite that behavior by also defining decompose for the `Normal` type!
76+
As you can see, the normals are automatically calculated,
77+
the same is true for texture coordinates. You can overload this behavior by overloading
78+
`normals` or `texturecoordinates` the same way as coordinates.
79+
`decompose` works a bit different for normals/texturecoordinates, since they dont have their own element type.
80+
Instead, you can use `decompose` like this:
81+
```julia
82+
decompose(UV(Vec2f0), rect)
83+
decompose(Normal(Vec3f0), rect)
84+
# the short form for the above:
85+
decompose_uv(rect)
86+
decompose_normals(rect)
87+
```
88+
You can also use `triangle_mesh`, `normal_mesh` and `uv_normal_mesh` to call the
89+
`mesh` constructor with predefined element types (Point2/3f0, Vec2/3f0), and the requested attributes.

src/basic_types.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,10 @@ Base.summary(io::IO, x::Type{<: TriangleP}) = print(io, "Triangle")
108108

109109
const Quadrilateral{Dim, T} = Ngon{Dim, T, 4, P} where P <: AbstractPoint{Dim, T}
110110

111+
Base.show(io::IO, x::Quadrilateral) = print(io, "Quad(", join(x, ", "), ")")
112+
Base.summary(io::IO, x::Type{<: Quadrilateral}) = print(io, "Quad")
113+
114+
111115
"""
112116
A `Simplex` is a generalization of an N-dimensional tetrahedra and can be thought
113117
of as a minimal convex set containing the specified points.

src/geometry_primitives.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ end
88

99
##
1010
# conversion & decompose
11-
11+
convert_simplex(::Type{T}, x::T) where T = (x,)
1212
"""
1313
convert_simplex(::Type{Face{3}}, f::Face{N})
1414
@@ -42,7 +42,9 @@ end
4242

4343
to_pointn(::Type{T}, x) where T<:Point = convert_simplex(T, x)[1]
4444

45+
# disambiguation method overlords
4546
convert_simplex(::Type{Point}, x::Point) = (x,)
47+
convert_simplex(::Type{Point{N,T}}, p::Point{N,T}) where {N, T} = (p,)
4648
function convert_simplex(::Type{Point{N, T}}, x) where {N, T}
4749
N2 = length(x)
4850
return (Point{N, T}(ntuple(i-> i <= N2 ? T(x[i]) : T(0), N)),)

src/meshes.jl

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,11 +85,9 @@ const GLNormalUVWMesh{Dim} = NormalUVWMesh{Dim, Float32}
8585
const GLNormalUVWMesh2D = GLNormalUVWMesh{2}
8686
const GLNormalUVWMesh3D = GLNormalUVWMesh{3}
8787

88-
best_pointtype(::Meshable{Dim, T}) where {Dim, T} = Point{Dim, T}
89-
9088
"""
9189
mesh(primitive::GeometryPrimitive;
92-
pointtype=best_pointtype(primitive), facetype=GLTriangle,
90+
pointtype=Point, facetype=GLTriangle,
9391
uvtype=nothing, normaltype=nothing)
9492
9593
Creates a mesh from `primitive`.
@@ -100,7 +98,7 @@ It also only losely correlates to the number of vertices, depending on the algor
10098
#TODO: find a better number here!
10199
"""
102100
function mesh(primitive::Meshable;
103-
pointtype=best_pointtype(primitive), facetype=GLTriangleFace,
101+
pointtype=Point, facetype=GLTriangleFace,
104102
uv=nothing, normaltype=nothing)
105103

106104
positions = decompose(pointtype, primitive)

0 commit comments

Comments
 (0)