forked from JuliaGeometry/GeometryBasics.jl
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathviewtypes.jl
161 lines (129 loc) · 5.13 KB
/
viewtypes.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
"""
TupleView{T, N, Skip, A}
TupleView, groups elements of an array as tuples.
N is the dimension of the tuple, M tells how many elements to skip to the next tuple.
By default TupleView{N} defaults to skip N items.
# a few examples:
```julia
x = [1, 2, 3, 4, 5, 6]
TupleView{2, 1}(x):
> [(1, 2), (2, 3), (3, 4), (4, 5), (5, 6)]
TupleView{2}(x):
> [(1, 2), (3, 4), (5, 6)]
TupleView{2, 3}(x):
> [(1, 2), (4, 5)]
TupleView{3, 1}(x):
> [(1, 2, 3), (2, 3, 4), (3, 4, 5), (4, 5, 6)]
```
TupleView can be used together with reinterpret:
```julia
x = [1, 2, 3, 4]
y = reinterpret(Point{2, Int}, TupleView{2, 1}(x))
> [Point(1, 2), Point(2, 3), Point(3, 4)]
```
"""
struct TupleView{T,N,Skip,A} <: AbstractVector{T}
data::A
connect::Bool
end
coordinates(tw::TupleView) = coordinates(tw.data)
function Base.size(x::TupleView{T,N,M}) where {T,N,M}
nitems = length(x.data) ÷ (N - (N - M))
nitems = nitems - max(N - M, 0)
return (nitems + x.connect,) # plus one item if we connect
end
function Base.getindex(x::TupleView{T,N,M}, index::Integer) where {T,N,M}
return ntuple(i -> x.data[mod1(((index - 1) * M) + i, length(x.data))], N)
end
function TupleView{N}(x::AbstractVector; connect=false) where {N}
return TupleView{N,N}(x, connect=connect)
end
function TupleView{N,M}(x::AbstractVector{T}; connect=false) where {T,N,M}
return TupleView{NTuple{N,T},N,M,typeof(x)}(x, connect)
end
@inline function connected_line(points::AbstractVector{<:AbstractPoint{N}},
skip=N) where {N}
return connect(points, Line, skip)
end
"""
connect(points::AbstractVector{<: AbstractPoint}, P::Type{<: Polytope{N}}, skip::Int = N)
Creates a view that connects a number of points to a Polytope `P`.
Between each polytope, `skip` elements are skipped untill the next starts.
Example:
```julia
x = connect(Point[(1, 2), (3, 4), (5, 6), (7, 8)], Line, 2)
x == [Line(Point(1, 2), Point(3, 4)), Line(Point(5, 6), Point(7, 8))]
"""
@inline function connect(points::AbstractVector{Point},
P::Type{<:Polytope{N,T} where {N,T}},
skip::Int=length(P)) where {Point <: AbstractPoint}
return reinterpret(Polytope(P, Point), TupleView{length(P),skip}(points))
end
@inline function connect(points::AbstractVector{T}, P::Type{<:Point{N}},
skip::Int=N) where {T <: Real,N}
return reinterpret(Point{N,T}, TupleView{N,skip}(points))
end
@inline function connect(points::AbstractVector{T}, P::Type{<:AbstractFace{N}},
skip::Int=N) where {T <: Real,N}
return reinterpret(Face(P, T), TupleView{N,skip}(points))
end
@inline function connect(points::AbstractMatrix{T},
P::Type{<:AbstractPoint{N}}) where {T <: Real,N}
return if size(points, 1) === N
return reinterpret(Point{N,T}, points)
elseif size(points, 2) === N
seglen = size(points, 1)
columns = ntuple(N) do i
return view(points, ((i - 1) * seglen + 1):(i * seglen))
end
return StructArray{Point{N,T}}(columns)
else
error("Dim 1 or 2 must be equal to the point dimension!")
end
end
"""
FaceView{Element, Point, Face, P, F}
FaceView enables to link one array of points via a face array, to generate one
abstract array of elements.
E.g., this becomes possible:
```
x = FaceView(rand(Point3f, 10), TriangleFace[(1, 2, 3), (2, 4, 5), ...])
x[1] isa Triangle == true
x isa AbstractVector{<: Triangle} == true
# This means we can use it as a mesh:
Mesh(x) # should just work!
# Can also be used for Points:
linestring = FaceView(points, LineFace[...])
Polygon(linestring)
```
"""
struct FaceView{Element,Point <: AbstractPoint,Face <: AbstractFace,P <: AbstractVector{Point},F <: AbstractVector{Face}} <: AbstractVector{Element}
elements::P
faces::F
end
const SimpleFaceView{Dim,T,NFace,IndexType,PointType <: AbstractPoint{Dim,T},FaceType <: AbstractFace{NFace,IndexType}} = FaceView{Ngon{Dim,T,NFace,PointType},PointType,FaceType,Vector{PointType},Vector{FaceType}}
function Base.getproperty(faceview::FaceView, name::Symbol)
return getproperty(getfield(faceview, :elements), name)
end
function Base.propertynames(faceview::FaceView)
return propertynames(getfield(faceview, :elements))
end
Tables.schema(faceview::FaceView) = Tables.schema(getfield(faceview, :elements))
Base.size(faceview::FaceView) = size(getfield(faceview, :faces))
@propagate_inbounds function Base.getindex(x::FaceView{Element}, i) where {Element}
return Element(map(idx -> coordinates(x)[idx], faces(x)[i]))
end
@propagate_inbounds function Base.setindex!(x::FaceView{Element}, element::Element,
i) where {Element}
face = faces(x)[i]
for (i, f) in enumerate(face) # TODO unroll!?
coordinates(x)[face[i]] = element[i]
end
return element
end
function connect(points::AbstractVector{P},
faces::AbstractVector{F}) where {P <: AbstractPoint,F <: AbstractFace}
return FaceView{Polytope(P, F),P,F,typeof(points),typeof(faces)}(points, faces)
end
coordinates(mesh::FaceView) = getfield(mesh, :elements)
faces(mesh::FaceView) = getfield(mesh, :faces)