Skip to content

Commit 3003c07

Browse files
committed
Not replace meta
1 parent 4076f51 commit 3003c07

File tree

3 files changed

+111
-145
lines changed

3 files changed

+111
-145
lines changed

src/GeometryBasics.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,13 @@ module GeometryBasics
2929
export OffsetInteger, ZeroIndex, OneIndex, GLIndex
3030
export FaceView, SimpleFaceView
3131
export AbstractPoint, PointMeta, PointWithUV
32+
export PolygonMeta, MultiPointMeta, MultiLineStringMeta, MeshMeta, LineStringMeta, MultiPolygonMeta
3233
export decompose, coordinates, faces, normals, decompose_uv, decompose_normals, texturecoordinates
3334
export Tesselation, pointmeta, Normal, UV, UVW
3435
export GLTriangleFace, GLNormalMesh3D, GLPlainTriangleMesh, GLUVMesh3D, GLUVNormalMesh3D
3536
export AbstractMesh, Mesh, TriangleMesh
3637
export GLNormalMesh2D, PlainTriangleMesh
37-
38+
export Feature, collect_feature
3839
# all the different predefined mesh types
3940
# Note: meshes can contain arbitrary meta information,
4041
export AbstractMesh, TriangleMesh, PlainMesh, GLPlainMesh, GLPlainMesh2D, GLPlainMesh3D

src/metadata.jl

Lines changed: 64 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,3 @@
1-
struct Feature{T, Names, Types} #have a better name?
2-
data::T
3-
rest::NamedTuple{Names, Types}
4-
end
5-
6-
Feature(x; kwargs...) = Feature(x, values(kwargs))
7-
8-
#can change names?
9-
"""
10-
Frees the Feature out of metadata
11-
i.e. returns and array of geometries
12-
"""
13-
function metafree(F::Feature)
14-
getproperty(F, :data)
15-
end
16-
metafree(x::AbstractVector{<: Feature}) = [getproperty(i, :data) for i in x]
17-
18-
"""
19-
Returns the metadata of a Feature
20-
"""
21-
function meta(x::Feature)
22-
getfield(x, :rest)
23-
end
24-
meta(x::AbstractVector{<: Feature}) = [getproperty(i, :rest) for i in x]
25-
26-
# helper methods
27-
Base.getproperty(f::Feature, s::Symbol) = s == :data ? getfield(f, 1) : s == :rest ? getfield(f, 2) : getproperty(getfield(f, 2), s)
28-
Base.propertynames(f::Feature) = (:data, propertynames(f.rest)...)
29-
getnamestypes(::Type{Feature{T, Names, Types}}) where {T, Names, Types} = (T, Names, Types)
30-
31-
# explicitly give the "schema" of the object to StructArrays
32-
function StructArrays.staticschema(::Type{F}) where {F<:Feature}
33-
T, names, types = getnamestypes(F)
34-
NamedTuple{(:data, names...), Base.tuple_type_cons(T, types)}
35-
end
36-
37-
# generate an instance of Feature type
38-
function StructArrays.createinstance(::Type{F}, x, args...) where {F<:Feature}
39-
T , names, types = getnamestypes(F)
40-
Feature(x, NamedTuple{names, types}(args))
41-
end
42-
43-
"""
44-
Accepts an Array/iterator of Features and put it into a StructArray
45-
"""
46-
function structarray(iter)
47-
cols = Tables.columntable(iter)
48-
structarray(first(cols), Base.tail(cols))
49-
end
50-
51-
function structarray(data, rest::NamedTuple{names, types}) where {names, types}
52-
F = Feature{eltype(data), names, StructArrays.eltypes(types)}
53-
return StructArray{F}(; data=data, rest...)
54-
end
55-
56-
#=---------------------------------------------------------------------------------------------
57-
----------------------------------------------------------------------------------------------
58-
Old meta
59-
----------------------------------------------------------------------------------------------=#
60-
611
#=
622
Helper functions that works around the fact, that there is no generic
633
Table interface for this functionality. Once this is in e.g. Tables.jl,
@@ -249,4 +189,67 @@ Base.size(x::MultiPolygonMeta) = size(metafree(x))
249189

250190
@meta_type(Mesh, mesh, AbstractMesh, Element <: Polytope)
251191
Base.getindex(x::MeshMeta, idx::Int) = getindex(metafree(x), idx)
252-
Base.size(x::MeshMeta) = size(metafree(x))
192+
Base.size(x::MeshMeta) = size(metafree(x))
193+
194+
195+
#=
196+
`Feature` type acts same as Meta method
197+
The difference lies in the fact that it is designed to handle
198+
heterogeneous types.
199+
=#
200+
struct Feature{T, Names, Types} #<:AbstractVector{T}
201+
data::T
202+
rest::NamedTuple{Names, Types}
203+
end
204+
205+
Feature(x; kwargs...) = Feature(x, values(kwargs))
206+
207+
"""
208+
Frees the Feature out of metadata
209+
i.e. returns and array of geometries
210+
"""
211+
function metafree(F::Feature)
212+
getproperty(F, :data)
213+
end
214+
metafree(x::AbstractVector{<: Feature}) = [getproperty(i, :data) for i in x]
215+
216+
"""
217+
Returns the metadata of a Feature
218+
"""
219+
function meta(x::Feature)
220+
getfield(x, :rest)
221+
end
222+
meta(x::AbstractVector{<: Feature}) = [getproperty(i, :rest) for i in x]
223+
224+
# helper methods
225+
Base.getproperty(f::Feature, s::Symbol) = s == :data ? getfield(f, 1) : s == :rest ? getfield(f, 2) : getproperty(getfield(f, 2), s)
226+
Base.propertynames(f::Feature) = (:data, propertynames(f.rest)...)
227+
getnamestypes(::Type{Feature{T, Names, Types}}) where {T, Names, Types} = (T, Names, Types)
228+
229+
# explicitly give the "schema" of the object to StructArrays
230+
function StructArrays.staticschema(::Type{F}) where {F<:Feature}
231+
T, names, types = getnamestypes(F)
232+
NamedTuple{(:data, names...), Base.tuple_type_cons(T, types)}
233+
end
234+
235+
# generate an instance of Feature type
236+
function StructArrays.createinstance(::Type{F}, x, args...) where {F<:Feature}
237+
T , names, types = getnamestypes(F)
238+
Feature(x, NamedTuple{names, types}(args))
239+
end
240+
241+
"""
242+
Accepts an Array/iterator of Features and put it into a StructArray
243+
"""
244+
function collect_feature(iter)
245+
cols = Tables.columntable(iter)
246+
collect_feature(first(cols), Base.tail(cols))
247+
end
248+
249+
function collect_feature(data, rest::NamedTuple{names, types}) where {names, types}
250+
F = Feature{eltype(data), names, StructArrays.eltypes(types)}
251+
return StructArray{F}(; data=data, rest...)
252+
end
253+
254+
Base.getindex(f::Feature, idx::Int) = getindex(metafree(f), idx)
255+
Base.size(f::Feature) = size(metafree(f))

test/runtests.jl

Lines changed: 45 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@ using GeometryBasics: attributes
55

66
@testset "GeometryBasics" begin
77

8-
#= This left till meta is removed completely
9-
# @testset "embedding metadata" begin
8+
@testset "embedding metadata" begin
109
@testset "Meshes" begin
1110

1211
@testset "per vertex attributes" begin
@@ -130,78 +129,26 @@ using GeometryBasics: attributes
130129
@test metafree(m_meta) === m
131130
@test propertynames(m_meta) == (:mesh, :boundingbox)
132131
end
133-
# end
134-
=#
135-
136-
@testset "embedding metadata(new)" begin
137-
# @testset "Meshes" begin
138-
139-
# @testset "per vertex attributes" begin
140-
# points = rand(Point{3, Float64}, 8)
141-
# tfaces = TetrahedronFace{Int}[(1, 2, 3, 4), (5, 6, 7, 8)]
142-
# normals = rand(SVector{3, Float64}, 8)
143-
# stress = LinRange(0, 1, 8)
144-
# mesh = Mesh(meta(points, normals = normals, stress = stress), tfaces)
145-
146-
# @test hasproperty(coordinates(mesh), :stress)
147-
# @test hasproperty(coordinates(mesh), :normals)
148-
# @test coordinates(mesh).stress === stress
149-
# @test coordinates(mesh).normals === normals
150-
# @test coordinates(mesh).normals === normals
151-
# @test GeometryBasics.faces(mesh) === tfaces
152-
# @test propertynames(coordinates(mesh)) == (:position, :normals, :stress)
153-
154-
# end
155-
156-
# @testset "per face attributes" begin
157-
158-
# # Construct a cube out of Quads
159-
# points = Point{3, Float64}[
160-
# (0.0, 0.0, 0.0), (2.0, 0.0, 0.0),
161-
# (2.0, 2.0, 0.0), (0.0, 2.0, 0.0),
162-
# (0.0, 0.0, 12.0), (2.0, 0.0, 12.0),
163-
# (2.0, 2.0, 12.0), (0.0, 2.0, 12.0)
164-
# ]
165-
166-
# facets = QuadFace{Cint}[
167-
# 1:4,
168-
# 5:8,
169-
# [1,5,6,2],
170-
# [2,6,7,3],
171-
# [3, 7, 8, 4],
172-
# [4, 8, 5, 1]
173-
# ]
174-
175-
# markers = Cint[-1, -2, 0, 0, 0, 0]
176-
# # attach some additional information to our faces!
177-
# mesh = Mesh(points, meta(facets, markers = markers))
178-
# @test hasproperty(GeometryBasics.faces(mesh), :markers)
179-
# # test with === to assert we're not doing any copies
180-
# @test GeometryBasics.faces(mesh).markers === markers
181-
# @test coordinates(mesh) === points
182-
# @test metafree(GeometryBasics.faces(mesh)) === facets
183-
184-
# end
185-
186-
# end
187-
188-
@testset "polygon with metadata" begin
132+
end
133+
134+
@testset "embedding Feature" begin
135+
@testset "Feature{Polygon}" begin
189136
polys = [Polygon(rand(Point{2, Float32}, 20)) for i in 1:10]
190137
multipol = MultiPolygon(polys)
191138
pnames = [randstring(4) for i in 1:10]
192139
numbers = LinRange(0.0, 1.0, 10)
193140
bin = rand(Bool, 10)
194141
# create a polygon
195-
poly = GeometryBasics.Feature(polys[1], name = pnames[1], value = numbers[1], category = bin[1])
142+
poly = Feature(polys[1], name = pnames[1], value = numbers[1], category = bin[1])
196143
# create a MultiPolygon with the right type & meta information!
197-
multipoly = GeometryBasics.Feature(multipol, name = pnames, value = numbers, category = bin)
198-
@test multipoly isa GeometryBasics.Feature
199-
@test poly isa GeometryBasics.Feature
144+
multipoly = Feature(multipol, name = pnames, value = numbers, category = bin)
145+
@test multipoly isa Feature
146+
@test poly isa Feature
200147

201148
@test GeometryBasics.getcolumn(poly, :name) == pnames[1]
202149
@test GeometryBasics.getcolumn(multipoly, :name) == pnames
203150

204-
meta_p = GeometryBasics.Feature(polys[1], boundingbox=Rect(0, 0, 2, 2))
151+
meta_p = Feature(polys[1], boundingbox=Rect(0, 0, 2, 2))
205152
@test meta_p.boundingbox === Rect(0, 0, 2, 2)
206153
@test GeometryBasics.metafree(meta_p) == polys[1]
207154
@test GeometryBasics.metafree(poly) == polys[1]
@@ -211,10 +158,10 @@ using GeometryBasics: attributes
211158
@test GeometryBasics.meta(multipoly) == (name = pnames, value = numbers, category = bin)
212159
end
213160

214-
@testset "point with metadata" begin
161+
@testset "Feature{Point}" begin
215162
p = Point(1.1, 2.2)
216163
@test p isa AbstractVector{Float64}
217-
pm = GeometryBasics.Feature(Point(1.1, 2.2); a=1, b=2)
164+
pm = Feature(Point(1.1, 2.2); a=1, b=2)
218165
p1 = Point(2.2, 3.6)
219166
p2 = [p, p1]
220167
@test coordinates(p2) == p2
@@ -225,10 +172,10 @@ using GeometryBasics: attributes
225172
@test GeometryBasics.meta(pm) == (a = 1, b = 2)
226173
end
227174

228-
@testset "MultiPoint with metadata" begin
175+
@testset "Feature{MultiPoint}" begin
229176
p = collect(Point{2, Float64}(x, x+1) for x in 1:5)
230177
@test p isa AbstractVector
231-
mpm = GeometryBasics.Feature(MultiPoint(p); a=1, b=2)
178+
mpm = Feature(MultiPoint(p); a=1, b=2)
232179
@test coordinates(mpm.data) == Point{2, Float64}[(x, x+1) for x in 1:5]
233180
@test mpm.rest === (a=1, b=2)
234181
@test mpm.data == p
@@ -237,35 +184,50 @@ using GeometryBasics: attributes
237184
@test GeometryBasics.meta(mpm) == (a = 1, b = 2)
238185
end
239186

240-
@testset "LineString with metadata" begin
241-
linestring = GeometryBasics.Feature(LineString(Point{2, Int}[(10, 10), (20, 20), (10, 40)]), a = 1, b = 2)
242-
@test linestring isa GeometryBasics.Feature
187+
@testset "Feature{LineString}" begin
188+
linestring = Feature(LineString(Point{2, Int}[(10, 10), (20, 20), (10, 40)]), a = 1, b = 2)
189+
@test linestring isa Feature
243190
@test linestring.rest === (a = 1, b = 2)
244191
@test propertynames(linestring) == (:data, :a, :b)
245192
@test GeometryBasics.metafree(linestring) == LineString(Point{2, Int}[(10, 10), (20, 20), (10, 40)])
246193
@test GeometryBasics.meta(linestring) == (a = 1, b = 2)
247194
end
248195

249-
@testset "MultiLineString with metadata" begin
196+
@testset "Feature{MultiLineString}" begin
250197
linestring1 = LineString(Point{2, Int}[(10, 10), (20, 20), (10, 40)])
251198
linestring2 = LineString(Point{2, Int}[(40, 40), (30, 30), (40, 20), (30, 10)])
252199
multilinestring = MultiLineString([linestring1, linestring2])
253-
multilinestringmeta = GeometryBasics.Feature(MultiLineString([linestring1, linestring2]); boundingbox = Rect(1.0, 1.0, 2.0, 2.0))
254-
@test multilinestringmeta isa GeometryBasics.Feature
200+
multilinestringmeta = Feature(MultiLineString([linestring1, linestring2]); boundingbox = Rect(1.0, 1.0, 2.0, 2.0))
201+
@test multilinestringmeta isa Feature
255202
@test multilinestringmeta.rest === (boundingbox = Rect(1.0, 1.0, 2.0, 2.0),)
256203
@test multilinestringmeta.data == multilinestring
257204
@test propertynames(multilinestringmeta) == (:data, :boundingbox)
258205
@test GeometryBasics.metafree(multilinestringmeta) == multilinestring
259206
@test GeometryBasics.meta(multilinestringmeta) == (boundingbox = GeometryBasics.HyperRectangle{2,Float64}([1.0, 1.0], [2.0, 2.0]),)
260207
end
261208

262-
# @testset "Mesh with metadata" begin
263-
# m = triangle_mesh(Sphere(Point3f0(0), 1))
264-
# m_meta = MeshMeta(m; boundingbox=Rect(1.0, 1.0, 2.0, 2.0))
265-
# @test meta(m_meta) === (boundingbox = Rect(1.0, 1.0, 2.0, 2.0),)
266-
# @test metafree(m_meta) === m
267-
# @test propertynames(m_meta) == (:mesh, :boundingbox)
268-
# end
209+
#=
210+
So mesh works differently for Feature
211+
since `Feature{Point}` not subtyped to `AbstractPoint`
212+
=#
213+
214+
@testset "Feature{Mesh}" begin
215+
@testset "per vertex attributes" begin
216+
points = rand(Point{3, Float64}, 8)
217+
tfaces = TetrahedronFace{Int}[(1, 2, 3, 4), (5, 6, 7, 8)]
218+
normals = rand(SVector{3, Float64}, 8)
219+
stress = LinRange(0, 1, 8)
220+
mesh_nometa = Mesh(points, tfaces)
221+
mesh = Feature(mesh_nometa, normals = normals, stress = stress)
222+
223+
@test hasproperty(mesh, :stress)
224+
@test hasproperty(mesh, :normals)
225+
@test mesh.stress == stress
226+
@test mesh.normals == normals
227+
@test GeometryBasics.faces(mesh.data) == tfaces
228+
@test propertynames(mesh) == (:data, :normals, :stress)
229+
end
230+
end
269231
end
270232

271233
@testset "view" begin
@@ -649,11 +611,11 @@ end
649611
geom = [ls..., mls, poly]
650612
prop = [(country_states = "India$(i)", rainfall = i*10) for i in 1:12]
651613

652-
feat = [GeometryBasics.Feature(i, j) for (i,j) = zip(geom, prop)]
653-
sa = GeometryBasics.structarray(feat)
614+
feat = [Feature(i, j) for (i,j) = zip(geom, prop)]
615+
sa = collect_feature(feat)
654616

655617
@test nameof(eltype(feat)) == :Feature
656-
@test eltype(sa) === GeometryBasics.Feature{Any,(:country_states, :rainfall),Tuple{String,Int64}}
618+
@test eltype(sa) === Feature{Any,(:country_states, :rainfall),Tuple{String,Int64}}
657619
@test propertynames(sa) === (:data, :country_states, :rainfall)
658620
@test getproperty(sa, :country_states) isa Array{String}
659621
@test getproperty(sa, :data) == geom

0 commit comments

Comments
 (0)