Skip to content

Commit 6f64432

Browse files
committed
take meta serious
1 parent 9087992 commit 6f64432

File tree

2 files changed

+118
-36
lines changed

2 files changed

+118
-36
lines changed

src/basic_types.jl

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ Face index, connecting points to form a simplex
3030
@fixed_vector SimplexFace AbstractSimplexFace
3131
const LineFace{T} = SimplexFace{2, T}
3232
Face(::Type{<: SimplexFace{N}}, ::Type{T}) where {N, T} = SimplexFace{N, T}
33+
Face(::Type{<: NSimplex{N}}, ::Type{T}) where {N, T} = SimplexFace{N, T}
3334

3435

3536

@@ -71,6 +72,7 @@ const NNgon{N} = Ngon{Dim, T, N, P} where {Dim, T, P}
7172
function (::Type{<: NNgon{N}})(points::Vararg{P, N}) where {P <: AbstractPoint{Dim, T}, N} where {Dim, T}
7273
Ngon{Dim, T, N, P}(SVector(points))
7374
end
75+
Base.show(io::IO, x::NNgon{N}) where N = print(io, "Ngon{$N}(", join(x, ", "), ")")
7476

7577
# Interfaces
7678
coordinates(x::Ngon) = x.points
@@ -100,6 +102,7 @@ const Triangle{Dim, T} = TriangleP{Dim, T, Point{Dim, T}}
100102
const Triangle3d{T} = Triangle{3, T}
101103

102104
Base.show(io::IO, x::TriangleP) = print(io, "Triangle(", join(x, ", "), ")")
105+
Base.summary(io::IO, x::Type{<: TriangleP}) = print(io, "Triangle")
103106

104107
const Quadrilateral{Dim, T} = Ngon{Dim, T, 4, P} where P <: AbstractPoint{Dim, T}
105108

@@ -299,7 +302,11 @@ struct Mesh{
299302

300303
simplices::V
301304
end
302-
305+
function Base.summary(io::IO, x::Mesh{Dim, T, Element}) where {Dim, T, Element}
306+
print(io, "Mesh{$Dim, $T, ")
307+
summary(io, Element)
308+
print(io, "}")
309+
end
303310
Base.size(x::Mesh) = size(x.simplices)
304311
Base.getindex(x::Mesh, i::Integer) = x.simplices[i]
305312

src/metadata.jl

Lines changed: 110 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,122 @@
1+
#=
2+
Helper functions that works around the fact, that there is no generic
3+
Table interface for this functionality. Once this is in e.g. Tables.jl,
4+
it should be removed from GeometryBasics!
5+
=#
16

27
"""
3-
A point type that holds additional metadata
8+
Gets the column names of any Array like (Table/AbstractArray)
49
"""
5-
struct MetaPoint{N, T, Names, Types} <: AbstractPoint{N, T}
6-
point::Point{N, T}
7-
meta::NamedTuple{Names, Types}
10+
function column_names(t)
11+
s = Tables.schema(t)
12+
if s === nothing
13+
return propertynames(first(Tables.rows(t)))
14+
else
15+
s.names
16+
end
817
end
918

10-
function Base.getproperty(x::MetaPoint{Dim, T, Names, Types}, field::Symbol) where {Dim, T, Names, Types}
11-
field === :polygon && return getfield(x, :polygon)
12-
Base.sym_in(field, Names) && return getfield(getfield(x, :meta), field)
13-
error("Field $field not part of Element")
19+
function hascolumn(t, colname::Symbol)
20+
return Base.sym_in(colname, column_names(t))
1421
end
1522

16-
struct MetaPolygon{
17-
Dimension, T <: Real,
18-
P <: AbstractPolygon{Dimension, T},
19-
Names, Types
20-
} <: AbstractPolygon{Dimension, T}
21-
22-
polygon::P
23-
meta::NamedTuple{Names, Types}
23+
"""
24+
getcolumns(t, colnames::Symbol...)
25+
Gets a column from any Array like (Table/AbstractArray).
26+
For AbstractVectors, a column will be the field names of the element type.
27+
"""
28+
function getcolumns(t, colnames::Symbol...)
29+
named_tuple = Tables.columntable(Tables.select(t, colnames...))
30+
getfield.((named_tuple,), colnames)
2431
end
32+
getcolumn(t, colname::Symbol) = getcolumns(t, colname)[1]
2533

26-
MetaPolygon(polygon::AbstractPolygon; meta...) = MetaPolygon(polygon, values(meta))
27-
function MetaPolygon(polygon::AbstractVector; meta...)
28-
MetaPolygon(Polygon(polygon); meta...)
29-
end
30-
function MetaPolygon(exterior::L, interior::AbstractVector{L}; meta...) where L
31-
MetaPolygon(Polygon(exterior, interior); meta...)
32-
end
34+
"""
35+
MetaType(::Type{T})
36+
Returns the Meta Type corresponding to `T`
37+
E.g:
38+
```julia
39+
MetaType(Point) == PointMeta
40+
"""
41+
MetaType(::Type{T}) where T = error("No Meta Type for $T")
3342

34-
function Tables.schema(::AbstractVector{MetaPolygon{Dim, T, P, Names, Types}}) where {Dim, T, P, Names, Types}
35-
Tables.Schema((:polygon, Names...), (P, Types...))
36-
end
37-
function Base.getproperty(x::MetaPolygon{Dim, T, P, Names, Types}, field::Symbol) where {Dim, T, P, Names, Types}
38-
field === :polygon && return getfield(x, :polygon)
39-
Base.sym_in(field, Names) && return getfield(getfield(x, :meta), field)
40-
error("Field $field not part of Element")
43+
"""
44+
MetaFree(::Type{T})
45+
Returns the original type containing no metadata for `T`
46+
E.g:
47+
```julia
48+
MetaFree(PointMeta) == Point
49+
"""
50+
MetaFree(::Type{T}) where T = error("No meta free Type for $T")
51+
meta(x::T) where T = error("$T has no meta!")
52+
metafree(x::T) where T = error("$T has no meta free representation!")
53+
54+
55+
macro meta_type(name, mainfield, supertype, params...)
56+
MetaName = Symbol("$(name)Meta")
57+
field = QuoteNode(mainfield)
58+
NoParams = Symbol("$(MetaName)NoParams")
59+
expr = quote
60+
struct $MetaName{$(params...), Typ <: $supertype{$(params...)}, Names, Types} <: $supertype{$(params...)}
61+
main::Typ
62+
meta::NamedTuple{Names, Types}
63+
end
64+
65+
const $NoParams{Typ, Names, Types} = $MetaName{$(params...), Typ, Names, Types} where {$(params...)}
66+
67+
function Base.getproperty(x::$MetaName{$(params...), Typ, Names, Types}, field::Symbol) where {$(params...), Typ, Names, Types}
68+
field === $field && return getfield(x, $field)
69+
field === :main && return getfield(x, :main)
70+
Base.sym_in(field, Names) && return getfield(getfield(x, :meta), field)
71+
error("Field $field not part of Element")
72+
end
73+
74+
GeometryBasics.MetaType(T::Type{<: $supertype}) = $MetaName{T}
75+
function GeometryBasics.MetaType(
76+
ST::Type{<: $supertype{$(params...)}},
77+
::Type{NamedTuple{Names, Types}}) where {$(params...), Names, Types}
78+
return $MetaName{$(params...), ST, Names, Types}
79+
end
80+
81+
82+
GeometryBasics.MetaFree(::Type{<: $MetaName{Typ}}) where Typ = Typ
83+
GeometryBasics.MetaFree(::Type{<: $MetaName}) = $name
84+
GeometryBasics.metafree(x::$MetaName) = x.main
85+
GeometryBasics.metafree(x::AbstractVector{<: $MetaName}) = getcolumns(x, $field)[1]
86+
GeometryBasics.meta(x::$MetaName) = x.meta
87+
GeometryBasics.meta(x::AbstractVector{<: $MetaName}) = getcolumns(x, :meta)[1]
88+
function (MT::Type{<: $MetaName})(args...; meta...)
89+
nt = values(meta)
90+
obj = MetaFree(MT)(args...)
91+
return MT(obj, nt)
92+
end
93+
94+
95+
function StructArrays.staticschema(::Type{$MetaName{$(params...), Typ, Names, Types}}) where {$(params...), Typ, Names, Types}
96+
NamedTuple{($field, Names...), Base.tuple_type_cons(Typ, Types)}
97+
end
98+
99+
function StructArrays.createinstance(
100+
::Type{$MetaName{$(params...), Typ, Names, Types}},
101+
metafree, args...
102+
) where {$(params...), Typ, Names, Types}
103+
$MetaName(metafree, NamedTuple{Names, Types}(args))
104+
end
105+
106+
function GeometryBasics.meta(elements::AbstractVector{T}; meta...) where T <: $supertype
107+
nt = values(meta)
108+
# get the first element to get the per element named tuple type
109+
ElementNT = typeof(map(first, nt))
110+
StructArray{MetaType(T, ElementNT)}(($(mainfield) = elements, nt...))
111+
end
112+
113+
end
114+
return esc(expr)
41115
end
42116

43-
Tables.rows(x::MultiPolygon) = x.polygons
44-
Tables.istable(::Type{<:MultiPolygon}) = true
45-
Tables.istable(::Type{<:MultiPolygon{<:Tuple}}) = false
46-
Tables.rowaccess(::Type{<:MultiPolygon}) = true
47-
# Tables.columnaccess(::Type{<:MultiPolygon}) = true
117+
@meta_type(Point, point, AbstractPoint, Dim, T)
118+
Base.getindex(x::PointMeta, idx::Int) = getindex(metafree(x), idx)
119+
120+
121+
@meta_type(NgonFace, ngon, AbstractNgonFace, N, T)
122+
Base.getindex(x::NgonFaceMeta, idx::Int) = getindex(metafree(x), idx)

0 commit comments

Comments
 (0)