Skip to content

Commit 3e80f3d

Browse files
committed
better mesh conversion interface
1 parent e08a101 commit 3e80f3d

File tree

10 files changed

+185
-155
lines changed

10 files changed

+185
-155
lines changed

src/GeometryBasics.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ module GeometryBasics
77
include("fixed_arrays.jl")
88
include("offsetintegers.jl")
99
include("basic_types.jl")
10+
include("interfaces.jl")
1011
include("metadata.jl")
1112
include("viewtypes.jl")
1213
include("geometry_primitives.jl")
@@ -29,6 +30,7 @@ module GeometryBasics
2930
export FaceView, SimpleFaceView
3031
export AbstractPoint, PointMeta, PointWithUV
3132
export decompose, coordinates, faces, normals, decompose_uv, decompose_normals
33+
export Tesselation
3234
export GLTriangleFace, GLNormalMesh3D, GLPlainTriangleMesh, GLUVMesh3D, GLUVNormalMesh3D
3335
export AbstractMesh, Mesh, TriangleMesh
3436
export GLNormalMesh2D, PlainTriangleMesh

src/basic_types.jl

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -82,14 +82,14 @@ Base.length(::NNgon{N}) where N = N
8282
The Ngon Polytope element type when indexing an array of points with a SimplexFace
8383
"""
8484
function Polytope(P::Type{<: AbstractPoint{Dim, T}}, ::Type{<: AbstractNgonFace{N, IT}}) where {N, Dim, T, IT}
85-
Ngon{Dim, T, N, P}
85+
return Ngon{Dim, T, N, P}
8686
end
8787

8888
"""
8989
The fully concrete Ngon type, when constructed from a point type!
9090
"""
9191
function Polytope(::Type{<: NNgon{N}}, P::Type{<: AbstractPoint{NDim, T}}) where {N, NDim, T}
92-
Ngon{NDim, T, N, P}
92+
return Ngon{NDim, T, N, P}
9393
end
9494

9595

@@ -139,11 +139,10 @@ const Tetrahedron{T} = TetrahedronP{T, Point{3, T}}
139139

140140
Base.show(io::IO, x::TetrahedronP) = print(io, "Tetrahedron(", join(x, ", "), ")")
141141

142-
143142
coordinates(x::Simplex) = x.points
144143

145144
function (::Type{<: NSimplex{N}})(points::Vararg{P, N}) where {P <: AbstractPoint{Dim, T}, N} where {Dim, T}
146-
Simplex{Dim, T, N, P}(SVector(points))
145+
return Simplex{Dim, T, N, P}(SVector(points))
147146
end
148147

149148
# Base Array interface
@@ -154,14 +153,14 @@ Base.length(::NSimplex{N}) where N = N
154153
The Simplex Polytope element type when indexing an array of points with a SimplexFace
155154
"""
156155
function Polytope(P::Type{<: AbstractPoint{Dim, T}}, ::Type{<: AbstractSimplexFace{N}}) where {N, Dim, T}
157-
Simplex{Dim, T, N, P}
156+
return Simplex{Dim, T, N, P}
158157
end
159158

160159
"""
161160
The fully concrete Simplex type, when constructed from a point type!
162161
"""
163162
function Polytope(::Type{<: NSimplex{N}}, P::Type{<: AbstractPoint{NDim, T}}) where {N, NDim, T}
164-
Simplex{NDim, T, N, P}
163+
return Simplex{NDim, T, N, P}
165164
end
166165
Base.show(io::IO, x::LineP) = print(io, "Line(", x[1], " => ", x[2], ")")
167166

@@ -175,13 +174,14 @@ struct LineString{
175174
} <: AbstractVector{LineP{Dim, T, P}}
176175
points::V
177176
end
177+
178178
coordinates(x::LineString) = x.points
179179

180180
Base.size(x::LineString) = size(coordinates(x))
181181
Base.getindex(x::LineString, i) = getindex(coordinates(x), i)
182182

183183
function LineString(points::AbstractVector{LineP{Dim, T, P}}) where {Dim, T, P}
184-
LineString{Dim, T, P, typeof(points)}(points)
184+
return LineString{Dim, T, P, typeof(points)}(points)
185185
end
186186

187187
"""
@@ -196,15 +196,15 @@ linestring = LineString(points)
196196
```
197197
"""
198198
function LineString(points::AbstractVector{<: AbstractPoint}, skip = 1)
199-
LineString(connect(points, LineP, skip))
199+
return LineString(connect(points, LineP, skip))
200200
end
201201

202202
function LineString(points::AbstractVector{<: Pair{P, P}}) where P <: AbstractPoint{N, T} where {N, T}
203-
LineString(reinterpret(LineP{N, T, P}, points))
203+
return LineString(reinterpret(LineP{N, T, P}, points))
204204
end
205205

206206
function LineString(points::AbstractVector{<: AbstractPoint}, faces::AbstractVector{<: LineFace})
207-
LineString(connect(points, faces))
207+
return LineString(connect(points, faces))
208208
end
209209

210210
"""
@@ -228,7 +228,7 @@ linestring = LineString(points, faces, 2)
228228
"""
229229
function LineString(points::AbstractVector{<: AbstractPoint}, indices::AbstractVector{<: Integer}, skip = 1)
230230
faces = connect(indices, LineFace, skip)
231-
LineString(points, faces)
231+
return LineString(points, faces)
232232
end
233233

234234
struct Polygon{
@@ -244,21 +244,21 @@ end
244244
Base.:(==)(a::Polygon, b::Polygon) = (a.exterior == b.exterior) && (a.interiors == b.interiors)
245245

246246
function Polygon(exterior::E, interiors::AbstractVector{E}) where E <: AbstractVector{LineP{Dim, T, P}} where {Dim, T, P}
247-
Polygon{Dim, T, P, typeof(exterior), typeof(interiors)}(exterior, interiors)
247+
return Polygon{Dim, T, P, typeof(exterior), typeof(interiors)}(exterior, interiors)
248248
end
249249

250250
Polygon(exterior::L) where L <: AbstractVector{<: LineP} = Polygon(exterior, L[])
251251

252252
function Polygon(exterior::AbstractVector{P}, skip::Int = 1) where P <: AbstractPoint{Dim, T} where {Dim, T}
253-
Polygon(LineString(exterior, skip))
253+
return Polygon(LineString(exterior, skip))
254254
end
255255

256256
function Polygon(exterior::AbstractVector{P}, faces::AbstractVector{<: Integer}, skip::Int = 1) where P <: AbstractPoint{Dim, T} where {Dim, T}
257-
Polygon(LineString(exterior, faces, skip))
257+
return Polygon(LineString(exterior, faces, skip))
258258
end
259259

260260
function Polygon(exterior::AbstractVector{P}, faces::AbstractVector{<: LineFace}) where P <: AbstractPoint{Dim, T} where {Dim, T}
261-
Polygon(LineString(exterior, faces))
261+
return Polygon(LineString(exterior, faces))
262262
end
263263

264264

@@ -272,7 +272,7 @@ struct MultiPolygon{
272272
end
273273

274274
function MultiPolygon(polygons::AbstractVector{P}; kw...) where P <: AbstractPolygon{Dim, T} where {Dim, T}
275-
MultiPolygon(meta(polygons; kw...))
275+
return MultiPolygon(meta(polygons; kw...))
276276
end
277277

278278
Base.getindex(mp::MultiPolygon, i) = mp.polygons[i]
@@ -288,7 +288,7 @@ struct MultiLineString{
288288
end
289289

290290
function MultiLineString(linestrings::AbstractVector{L}; kw...) where L <: AbstractVector{LineP{Dim, T, P}} where {Dim, T, P}
291-
MultiLineString(meta(linestrings; kw...))
291+
return MultiLineString(meta(linestrings; kw...))
292292
end
293293

294294
Base.getindex(ms::MultiLineString, i) = ms.linestrings[i]

src/geometry_primitives.jl

Lines changed: 4 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
##
22
# Generic base overloads
3-
43
Base.extrema(primitive::GeometryPrimitive) = (minimum(primitive), maximum(primitive))
54
function widths(x::AbstractRange)
65
mini, maxi = Float32.(extrema(x))
@@ -54,6 +53,10 @@ function convert_simplex(::Type{Vec{N, T}}, x) where {N, T}
5453
return (Vec{N, T}(ntuple(i-> i <= N2 ? T(x[i]) : T(0), N)),)
5554
end
5655

56+
collect_with_eltype(::Type{T}, vec::Vector{T}) where T = vec
57+
collect_with_eltype(::Type{P}, vec::Vector{MetaPoint{P}}) where T = vec
58+
collect_with_eltype(::Type{T}, vec::AbstractVector{T}) where T = collect(vec)
59+
5760
function collect_with_eltype(::Type{T}, iter) where T
5861
# TODO we could be super smart about allocating the right length
5962
# but its kinda annoying, since e.g. T == Triangle and first(iter) isa Quad
@@ -69,78 +72,6 @@ function collect_with_eltype(::Type{T}, iter) where T
6972
return result
7073
end
7174

72-
function faces(primitive, nvertices=30)
73-
# doesn't have any specific algorithm to generate faces
74-
# so will try to triangulate the coordinates!
75-
return nothing
76-
end
77-
78-
function decompose(::Type{F}, primitive, args...) where {F<:AbstractFace}
79-
f = faces(primitive, args...)
80-
f === nothing && return nothing
81-
return collect_with_eltype(F, f)
82-
end
83-
84-
function decompose(::Type{T}, primitive::AbstractVector{T}) where {T}
85-
return primitive
86-
end
87-
88-
function decompose(::Type{T}, primitive::AbstractVector{T}) where {T<:AbstractFace}
89-
return primitive
90-
end
91-
92-
function decompose(::Type{T}, primitive::AbstractVector{T}) where {T<:AbstractPoint}
93-
return primitive
94-
end
95-
96-
function decompose(::Type{T}, primitive::AbstractVector{T2}) where {T, T2 <: Union{StaticVector, AbstractPoint}}
97-
return convert(Vector{T}, primitive)
98-
end
99-
100-
function decompose(::Type{T}, primitive::AbstractVector, args...) where {T<:AbstractPoint}
101-
return collect_with_eltype(P, coordinates(primitive, args...))
102-
end
103-
104-
function decompose(::Type{P}, primitive, args...) where {P<:AbstractPoint}
105-
return collect_with_eltype(P, coordinates(primitive, args...))
106-
end
107-
108-
function decompose(::Type{Point}, primitive::Union{GeometryPrimitive{Dim}, Mesh{Dim}}, args...) where {Dim}
109-
return collect_with_eltype(Point{Dim, Float32}, coordinates(primitive, args...))
110-
end
111-
112-
# Dispatch type to make `decompose(UV{Vec2f0}, priomitive)` work
113-
struct UV{T} end
114-
UV(::Type{T}) where T = UV{T}()
115-
struct UVW{T} end
116-
UVW(::Type{T}) where T = UVW{T}()
117-
struct Normal{T} end
118-
Normal(::Type{T}) where T = Normal{T}()
119-
120-
function decompose(::UV{T}, primitive::GeometryPrimitive, args...) where T
121-
return collect_with_eltype(T, texturecoordinates(primitive, args...))
122-
end
123-
124-
decompose_uv(args...) = decompose(UV(Vec2f0), args...)
125-
126-
function decompose(::UVW{T}, primitive::GeometryPrimitive, args...) where T
127-
return collect_with_eltype(T, texturecoordinates(primitive, args...))
128-
end
129-
130-
function normals(primitive, nvertices=30)
131-
# doesn't have any specific algorithm to generate normals
132-
# so will be generated from faces + positions
133-
return nothing
134-
end
135-
136-
137-
function decompose(::Normal{T}, primitive::GeometryPrimitive, args...) where T
138-
n = normals(primitive, args...)
139-
n === nothing && return nothing
140-
return collect_with_eltype(T, n)
141-
end
142-
143-
decompose_normals(args...) = decompose(Normal(Vec3f0), args...)
14475

14576
"""
14677
The unnormalized normal of three vertices.
@@ -372,9 +303,6 @@ function texturecoordinates(s::Circle, nvertices=64)
372303
return coordinates(Circle(Point2f0(0.5), 0.5f0), nvertices)
373304
end
374305

375-
best_nvertices(x::Circle) = 64
376-
best_nvertices(x::Sphere) = 24
377-
378306
function coordinates(s::Sphere, nvertices=24)
379307
θ = LinRange(0, pi, nvertices); φ = LinRange(0, 2pi, nvertices)
380308
inner(θ, φ) = Point(cos(φ)*sin(θ), sin(φ)*sin(θ), cos(θ)) .* s.r .+ s.center

src/interfaces.jl

Lines changed: 103 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,106 @@ function faces(f::AbstractVector{<:AbstractFace})
1818
return f
1919
end
2020

21-
coordinates(x, ::Nothing) = coordinates(x)
22-
faces(x, ::Nothing) = faces(x)
23-
decompose(x, ::Nothing) = decompose(x)
24-
decompose(t::Type{<:Point}, x, ::Nothing) = decompose(t, x)
25-
decompose(t::Type{<:NgonFace}, x, ::Nothing) = decompose(t, x)
26-
decompose(t::Vec, x, ::Nothing) = decompose(t, x)
27-
decompose(t::UV, x, ::Nothing) = decompose(t, x)
28-
decompose(t::UVW, x, ::Nothing) = decompose(t, x)
29-
decompose(t::Normal, x, ::Nothing) = decompose(t, x)
21+
function normals(primitive, nvertices=nothing)
22+
# doesn't have any specific algorithm to generate normals
23+
# so will be generated from faces + positions
24+
# which we indicate by returning nothing!
25+
# Overload normals(primitive::YourPrimitive), to calcalute the normals
26+
# differently
27+
return nothing
28+
end
29+
30+
function faces(primitive, nvertices=nothing)
31+
# doesn't have any specific algorithm to generate faces
32+
# so will try to triangulate the coordinates!
33+
return nothing
34+
end
35+
36+
"""
37+
Tesselation(primitive, nvertices)
38+
For abstract geometries, when we generate
39+
a mesh from them, we need to decide how fine grained we want to mesh them.
40+
To transport this information to the various decompose methods, you can wrap it
41+
in the Tesselation object e.g. like this:
42+
43+
```julia
44+
sphere = Sphere(Point3f0(0), 1)
45+
m1 = mesh(sphere) # uses a default value for tesselation
46+
m2 = mesh(Tesselation(sphere, 64)) # uses 64 for tesselation
47+
length(coordinates(m1)) != length(coordinates(m2))
48+
```
49+
For grid based tesselation, you can also use a tuple:
50+
```julia
51+
rect = Rect2D(0, 0, 1, 1)
52+
Tesselation(rect, (5, 5))
53+
"""
54+
struct Tesselation{Dim, T, Primitive, NGrid}
55+
primitive::Primitive
56+
nvertices::NTuple{NGrid, Int}
57+
end
58+
59+
function Tesselation(primitive::GeometryPrimitive{Dim, T}, nvertices::NTuple{N, <:Integer}) where {Dim, T, N}
60+
return Tesselation{Dim, T, typeof(primitive), N}(primitive, Int.(nvertices))
61+
end
62+
63+
64+
Tesselation(primitive, nvertices::Integer) = Tesselation(primitive, (nvertices,))
65+
66+
# This is a bit lazy, I guess we should just refactor these methods
67+
# to directly work on Tesselation - but this way it's backward compatible and less
68+
# refactor work :D
69+
nvertices(tesselation::Tesselation) = tesselation.nvertices
70+
nvertices(tesselation::Tesselation{T, N, P, 1}) where {T, N, P} = tesselation.nvertices[1]
71+
72+
coordinates(tesselation::Tesselation) = coordinates(tesselation.primitive, nvertices(tesselation))
73+
faces(tesselation::Tesselation) = faces(tesselation.primitive, nvertices(tesselation))
74+
normals(tesselation::Tesselation) = normals(tesselation.primitive, nvertices(tesselation))
75+
texturecoordinates(tesselation::Tesselation) = texturecoordinates(tesselation.primitive, nvertices(tesselation))
76+
77+
78+
## Decompose methods
79+
# Dispatch type to make `decompose(UV{Vec2f0}, priomitive)` work
80+
# and to pass through tesselation information
81+
82+
# Types that can be converted to a mesh via the functions below
83+
const Meshable{Dim, T} = Union{Tesselation{Dim, T}, Mesh{Dim, T},
84+
GeometryPrimitive{Dim, T}, AbstractVector{<: AbstractPoint{Dim, T}}}
85+
86+
struct UV{T} end
87+
UV(::Type{T}) where T = UV{T}()
88+
struct UVW{T} end
89+
UVW(::Type{T}) where T = UVW{T}()
90+
struct Normal{T} end
91+
Normal(::Type{T}) where T = Normal{T}()
92+
93+
function decompose(::Type{F}, primitive) where {F<:AbstractFace}
94+
f = faces(primitive)
95+
f === nothing && return nothing
96+
return collect_with_eltype(F, f)
97+
end
98+
99+
function decompose(::Type{P}, primitive) where {P<:AbstractPoint}
100+
return collect_with_eltype(P, metafree(coordinates(primitive)))
101+
end
102+
103+
function decompose(::Type{Point}, primitive::Meshable{Dim, T}) where {Dim, T}
104+
return collect_with_eltype(Point{Dim, T}, metafree(coordinates(primitive)))
105+
end
106+
107+
function decompose(::Type{T}, primitive) where {T}
108+
return collect_with_eltype(T, primitive)
109+
end
110+
111+
decompose_uv(primitive) = decompose(UV(Vec2f0), primitive)
112+
decompose_uvw(primitive) = decompose(UVW(Vec3f0), primitive)
113+
decompose_normals(primitive) = decompose(Normal(Vec3f0), primitive)
114+
115+
function decompose(::Normal{T}, primitive) where T
116+
n = normals(primitive)
117+
n === nothing && return nothing
118+
return collect_with_eltype(T, n)
119+
end
120+
121+
function decompose(::Union{UV{T}, UVW{T}}, primitive) where T
122+
return collect_with_eltype(T, texturecoordinates(primitive))
123+
end

src/lines.jl

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@ function intersects(a::Line{2, T1}, b::Line{2, T2}) where {T1, T2}
6060
return true, point
6161
end
6262

63-
6463
function simple_concat(vec::AbstractVector, range, endpoint::P) where P
6564
result = Vector{P}(undef, length(range) + 1)
6665
for (i, j) in enumerate(range)
@@ -70,7 +69,6 @@ function simple_concat(vec::AbstractVector, range, endpoint::P) where P
7069
return result
7170
end
7271

73-
7472
function consecutive_pairs(arr)
7573
n = length(arr)
7674
zip(view(arr, 1:n-1), view(arr, 2:n))

0 commit comments

Comments
 (0)