|
| 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 | +=# |
1 | 6 |
|
2 | 7 | """
|
3 |
| -A point type that holds additional metadata |
| 8 | +Gets the column names of any Array like (Table/AbstractArray) |
4 | 9 | """
|
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 |
8 | 17 | end
|
9 | 18 |
|
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)) |
14 | 21 | end
|
15 | 22 |
|
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) |
24 | 31 | end
|
| 32 | +getcolumn(t, colname::Symbol) = getcolumns(t, colname)[1] |
25 | 33 |
|
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") |
33 | 42 |
|
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) |
41 | 115 | end
|
42 | 116 |
|
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