Skip to content

Commit a6d6996

Browse files
committed
distinguish points and intervals in plots
1 parent e34e5ab commit a6d6996

File tree

3 files changed

+208
-36
lines changed

3 files changed

+208
-36
lines changed

ext/DimensionalDataMakie.jl

Lines changed: 205 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,74 @@ const SurfaceLikeCompat = isdefined(Makie, :SurfaceLike) ? Makie.SurfaceLike : U
1010

1111
_paired(args...) = map(x -> x isa Pair ? x : x => x, args)
1212

13+
struct DimLines{T,N,D<:DD.DimTuple,O} <: DD.AbstractDimIndices{T,N}
14+
dims::D
15+
order::O
16+
end
17+
function DimLines(dims::DD.DimTuple; order=dims)
18+
@assert count(isintervals(dims)) == 1
19+
order = map(d -> basetypeof(d)(), order)
20+
T = typeof(_getline(dims, map(firstindex, dims)))
21+
N = length(dims)
22+
dims = N > 0 ? DD._format(dims) : dims
23+
DimLines{T,N,typeof(dims),typeof(order)}(dims, order)
24+
end
25+
26+
Base.getindex(dp::DimLines, i1::Int, i2::Int, Is::Int...) = _getline(dims(dp), (i1, i2, Is...))
27+
Base.getindex(di::DimLines{<:Any,1}, i::Int) = (dims(di, 1)[i],)
28+
Base.getindex(di::DimLines, i::Int) = di[Tuple(CartesianIndices(di)[i])...]
29+
30+
function _getline(ds, I::Tuple)
31+
D = map(rebuild, ds, I)
32+
# Get dim-wrapped point values at i1, I...
33+
intervaldim = only(dims(ds, isintervals))
34+
ods = otherdims(ds, intervaldim)
35+
bounds = intervalbounds(lookup(intervaldim), val(dims(D, intervaldim))...)
36+
return map(bounds) do b
37+
(b, map(getindex, dims(ds, ods), map(val, dims(D, ods)))...)
38+
end
39+
end
40+
41+
struct DimPolygons{T,N,D<:DD.DimTuple,O} <: DD.AbstractDimIndices{T,N}
42+
dims::D
43+
order::O
44+
end
45+
function DimPolygons(dims::DD.DimTuple; order=dims)
46+
@assert count(isintervals(dims)) == 2 "num intervaldims $(count(isintervals(dims))) is not 2"
47+
order = map(d -> basetypeof(d)(), order)
48+
T = typeof(_getpolygon(dims, map(firstindex, dims)))
49+
N = length(dims)
50+
dims = N > 0 ? DD._format(dims) : dims
51+
DimPolygons{T,N,typeof(dims),typeof(order)}(dims, order)
52+
end
53+
54+
Base.getindex(dp::DimPolygons, i1::Int, i2::Int, Is::Int...) = _getpolygon(dims(dp), (i1, i2, Is...))
55+
Base.getindex(di::DimPolygons{<:Any,1}, i::Int) = (dims(di, 1)[i],)
56+
Base.getindex(di::DimPolygons, i::Int) = di[Tuple(CartesianIndices(di)[i])...]
57+
58+
function _getpolygon(ds::DD.DimTuple, I::Tuple)
59+
D = map(rebuild, ds, I)
60+
# Get dim-wrapped point values at i1, I...
61+
intervaldims = dims(ds, isintervals)
62+
ods = otherdims(ds, intervaldims)
63+
bvs = map(intervaldims) do id
64+
rebuild(id, intervalbounds(lookup(id), val(dims(D, id))...))
65+
end
66+
ovs = map(rebuild, ods, map(getindex, ods, val(dims(D, ods))))
67+
points = [
68+
_polypoint(ds, bvs, ovs, 1, 1),
69+
_polypoint(ds, bvs, ovs, 1, 2),
70+
_polypoint(ds, bvs, ovs, 2, 2),
71+
_polypoint(ds, bvs, ovs, 2, 1),
72+
_polypoint(ds, bvs, ovs, 1, 1),
73+
]
74+
return Makie.Polygon(Makie.LineString(points))
75+
end
76+
function _polypoint(order, (bs1, bs2), ovs, i, j)
77+
vs = (rebuild(bs1, val(bs1)[i]), rebuild(bs2, val(bs2)[j]), ovs...)
78+
Point(map(val, dims(vs, order)))
79+
end
80+
1381

1482
# Shared docstrings: keep things consistent.
1583

@@ -59,33 +127,62 @@ end
59127
# 1d PointBased
60128

61129
# 1d plots are scatter by default
62-
for (f1, f2) in _paired(:plot => :scatter, :scatter, :lines, :scatterlines, :stairs, :stem, :barplot, :waterfall)
63-
f1!, f2! = Symbol(f1, '!'), Symbol(f2, '!')
130+
for f in (:scatter, :lines, :scatterlines, :stairs, :stem, :barplot, :waterfall)
131+
f! = Symbol(f, '!')
64132
docstring = """
65-
$f1(A::AbstractDimArray{<:Any,1}; attributes...)
133+
$f(A::AbstractDimVector; attributes...)
66134
67-
Plot a 1-dimensional `AbstractDimArray` with `Makie.$f2`.
135+
Plot a 1-dimensional `AbstractDimArray` with `Makie`.
68136
69137
The X axis will be labelled with the dimension name and and use ticks from its lookup.
70138
71-
$(_keyword_heading_doc(f1))
139+
$(_keyword_heading_doc(f))
72140
$AXISLEGENDKW_DOC
73141
"""
74142
@eval begin
75143
@doc $docstring
76-
function Makie.$f1(A::AbstractDimArray{<:Any,1}; axislegendkw=(;), attributes...)
144+
function Makie.$f(A::AbstractDimVector; axislegendkw=(;), attributes...)
77145
args, merged_attributes = _pointbased1(A, attributes)
78-
p = Makie.$f2(args...; merged_attributes...)
146+
p = Makie.$f(args...; merged_attributes...)
79147
axislegend(p.axis; merge=false, unique=false, axislegendkw...)
80148
return p
81149
end
82-
function Makie.$f1!(axis, A::AbstractDimArray{<:Any,1}; axislegendkw=(;), attributes...)
150+
function Makie.$f!(axis, A::AbstractDimVector; axislegendkw=(;), attributes...)
83151
args, merged_attributes = _pointbased1(A, attributes; set_axis_attributes=false)
84-
return Makie.$f2!(axis, args...; merged_attributes...)
152+
return Makie.$f!(axis, args...; merged_attributes...)
85153
end
86154
end
87155
end
88156

157+
158+
function Makie.linesegments(A::AbstractDimVector; axislegendkw=(;), attributes...)
159+
args, merged_attributes = _pointbased1(A, attributes)
160+
lines = _interval_lines(A)
161+
p = Makie.linesegments(lines; merged_attributes...)
162+
axislegend(p.axis; merge=false, unique=false, axislegendkw...)
163+
return p
164+
end
165+
166+
function Makie.linesegments!(x, A::AbstractDimVector; attributes...)
167+
args, merged_attributes = _pointbased1(A, attributes)
168+
lines = _interval_lines(A)
169+
return Makie.linesegments(x, lines; merged_attributes...)
170+
end
171+
172+
function _interval_lines(A::AbstractDimVector)
173+
map(intervalbounds(only(dims(A))), A) do bounds, val
174+
T = promote_type(typeof(first(bounds)), typeof(last(bounds)), typeof(val))
175+
(T(bounds[1]), T(val)), (T(bounds[2]), T(val))
176+
end
177+
end
178+
179+
function Makie.plot(A::AbstractDimVector; kw...)
180+
only(isintervals(A)) ? Makie.linesegments(A; kw...) : Makie.scatter(A; kw...)
181+
end
182+
function Makie.plot!(ax, A::AbstractDimVector; kw...)
183+
only(isintervals(A)) ? Makie.linesegments!(ax, A; kw...) : Makie.scatter(axis, A; kw...)
184+
end
185+
89186
function _pointbased1(A, attributes; set_axis_attributes=true)
90187
# Array/Dimension manipulation
91188
A1 = _prepare_for_makie(A)
@@ -118,52 +215,85 @@ end
118215

119216
# 2d SurfaceLike
120217

121-
for (f1, f2) in _paired(:plot => :heatmap, :heatmap, :image, :contour, :contourf, :spy, :surface)
122-
f1!, f2! = Symbol(f1, '!'), Symbol(f2, '!')
218+
for f in (:heatmap, :image, :contour, :contourf, :spy, :surface)
219+
f! = Symbol(f, '!')
123220
docstring = """
124-
$f1(A::AbstractDimArray{<:Any,2}; attributes...)
221+
$f(A::AbstractDimMatrix; attributes...)
125222
126-
Plot a 2-dimensional `AbstractDimArray` with `Makie.$f2`.
223+
Plot a 2-dimensional `AbstractDimArray` with `Makie.$f`.
127224
128-
$(_keyword_heading_doc(f1))
129-
$(_xy(f1))
130-
$(_maybe_colorbar_doc(f1))
225+
$(_keyword_heading_doc(f))
226+
$(_xy(f))
227+
$(_maybe_colorbar_doc(f))
131228
"""
132229
@eval begin
133230
@doc $docstring
134-
function Makie.$f1(A::AbstractDimArray{T,2};
231+
function Makie.$f(A::AbstractDimMatrix;
135232
x=nothing, y=nothing, colorbarkw=(;), attributes...
136233
) where T
137234
replacements = _keywords2dimpairs(x, y)
138235
A1, A2, args, merged_attributes = _surface2(A, attributes, replacements)
139-
p = if $(f1 == :surface)
236+
p = if $(f == :surface)
140237
# surface is an LScene so we cant pass attributes
141-
p = Makie.$f2(args...; attributes...)
238+
p = Makie.$f(args...; attributes...)
142239
# And instead set axisnames manually
143240
p.axis.scene[OldAxis][:names, :axisnames] = map(DD.label, DD.dims(A2))
144241
p
145242
else
146-
Makie.$f2(args...; merged_attributes...)
243+
Makie.$f(args...; merged_attributes...)
147244
end
148245
# Add a Colorbar for heatmaps and contourf
149-
if T isa Real && $(f1 in (:plot, :heatmap, :contourf))
246+
if T isa Real && $(f in (:plot, :heatmap, :contourf))
150247
Colorbar(p.figure[1, 2], p.plot;
151248
label=DD.label(A), colorbarkw...
152249
)
153250
end
154251
return p
155252
end
156-
function Makie.$f1!(axis, A::AbstractDimArray{<:Any,2};
253+
function Makie.$f!(axis, A::AbstractDimMatrix;
157254
x=nothing, y=nothing, colorbarkw=(;), attributes...
158255
)
159256
replacements = _keywords2dimpairs(x, y)
160257
_, _, args, _ = _surface2(A, attributes, replacements)
161258
# No ColourBar in the ! in-place versions
162-
return Makie.$f2!(axis, args...; attributes...)
259+
return Makie.$f!(axis, args...; attributes...)
163260
end
164261
end
165262
end
166263

264+
function Makie.plot(A::AbstractDimMatrix; kw...)
265+
if all(isintervals(dims(A)))
266+
Makie.heatmap(A; kw...)
267+
elseif any(isintervals(A))
268+
Makie.linesegments(A; kw...)
269+
else
270+
Makie.scatter(A; kw...)
271+
end
272+
end
273+
function Makie.plot!(ax, A::AbstractDimMatrix; kw...)
274+
if all(isintervals(A))
275+
Makie.heatmap!(ax, A; kw...)
276+
elseif any(isintervals(A))
277+
Makie.linesegments!(ax, A; kw...)
278+
else
279+
Makie.scatter!(ax, A; kw...)
280+
end
281+
end
282+
283+
function Makie.linesegments(A::AbstractDimArray; axislegendkw=(;), attributes...)
284+
# args, merged_attributes = _pointbased1(A, attributes)
285+
lines = vec(collect(DimLines(A)))
286+
p = Makie.linesegments(lines; color=vec(A))#, merged_attributes...)
287+
# axislegend(p.axis; merge=false, unique=false, axislegendkw...)
288+
return p
289+
end
290+
291+
function Makie.linesegments!(x, A::AbstractDimArray; attributes...)
292+
# args, merged_attributes = _pointbased1(A, attributes)
293+
lines = collect(vec(DimLines(A)))
294+
return Makie.linesegments(x, lines; colors=vec(A))#, merged_attributes...)
295+
end
296+
167297
function _surface2(A, attributes, replacements)
168298
# Array/Dimension manipulation
169299
A1 = _prepare_for_makie(A, replacements)
@@ -188,34 +318,75 @@ end
188318

189319
# 3d VolumeLike
190320

191-
for (f1, f2) in _paired(:plot => :volume, :volume, :volumeslices)
192-
f1!, f2! = Symbol(f1, '!'), Symbol(f2, '!')
321+
for f in (:volume, :volumeslices)
322+
f! = Symbol(f, '!')
193323
docstring = """
194-
$f1(A::AbstractDimArray{<:Any,3}; attributes...)
324+
$f(A::AbstractDimArray{<:Any,3}; attributes...)
195325
196-
Plot a 3-dimensional `AbstractDimArray` with `Makie.$f2`.
326+
Plot a 3-dimensional `AbstractDimArray` with `Makie.$f`.
197327
198-
$(_keyword_heading_doc(f1))
199-
$(_xy(f1))
200-
$(_z(f1))
328+
$(_keyword_heading_doc(f))
329+
$(_xy(f))
330+
$(_z(f))
201331
"""
202332
@eval begin
203333
@doc $docstring
204-
function Makie.$f1(A::AbstractDimArray{<:Any,3}; x=nothing, y=nothing, z=nothing, attributes...)
334+
function Makie.$f(A::AbstractDimArray{<:Any,3}; x=nothing, y=nothing, z=nothing, attributes...)
205335
replacements = _keywords2dimpairs(x, y, z)
206336
A1, A2, args, merged_attributes = _volume3(A, attributes, replacements)
207-
p = Makie.$f2(args...; merged_attributes...)
337+
p = Makie.$f(args...; merged_attributes...)
208338
p.axis.scene[OldAxis][:names, :axisnames] = map(DD.label, DD.dims(A2))
209339
return p
210340
end
211-
function Makie.$f1!(axis, A::AbstractDimArray{<:Any,3}; x=nothing, y=nothing, z=nothing, attributes...)
341+
function Makie.$f!(axis, A::AbstractDimArray{<:Any,3}; x=nothing, y=nothing, z=nothing, attributes...)
212342
replacements = _keywords2dimpairs(x, y, z)
213343
_, _, args, _ = _volume3(A, attributes, replacements)
214-
return Makie.$f2!(axis, args...; attributes...)
344+
return Makie.$f!(axis, args...; attributes...)
215345
end
216346
end
217347
end
218348

349+
function Makie.plot(A::AbstractDimArray{<:Any,3}; kw...)
350+
if all(isintervals(dims(A)))
351+
Makie.volume(A; kw...)
352+
elseif count(isintervals(A)) == 2
353+
Makie.poly(A; kw...)
354+
elseif count(isintervals(A)) == 1
355+
Makie.linesegments(A; kw...)
356+
else
357+
Makie.scatter(A; kw...)
358+
end
359+
end
360+
function Makie.plot!(ax, A::AbstractDimArray{<:Any,3}; kw...)
361+
if all(isintervals(A))
362+
Makie.volume!(ax, A; kw...)
363+
elseif count(isintervals(A)) == 2
364+
Makie.poly!(ax, A; kw...)
365+
elseif count(isintervals(A)) == 1
366+
Makie.linesegments!(ax, A; kw...)
367+
else
368+
Makie.scatter!(ax, A; kw...)
369+
end
370+
end
371+
372+
function Makie.poly(A::AbstractDimArray; axislegendkw=(;), attributes...)
373+
# args, merged_attributes = _pointbased1(A, attributes)
374+
dps = DimPolygons(A)
375+
polys = vec(collect(dps))
376+
# TODO this doesn't plot properly?
377+
# All polygons plat on the same plane
378+
p = Makie.poly(polys; color=vec(A))#, merged_attributes...)
379+
axislegend(p.axis; merge=false, unique=false, axislegendkw...)
380+
return p
381+
end
382+
383+
function Makie.poly!(x, A::AbstractDimArray; attributes...)
384+
# args, merged_attributes = _pointbased1(A, attributes)
385+
polys = collect(vec(DimPolygons(A)))
386+
return Makie.poly(x, polys; colors=vec(A))#, merged_attributes...)
387+
end
388+
389+
219390
function _volume3(A, attributes, replacements)
220391
# Array/Dimension manipulation
221392
A1 = _prepare_for_makie(A, replacements)

src/Dimensions/predicates.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ for f in (
1616
@eval begin
1717
LookupArrays.$f(x::Dimension) = $f(val(x))
1818
LookupArrays.$f(::Nothing) = false
19-
LookupArrays.$f(xs::DimTuple) = all(map($f, xs))
19+
LookupArrays.$f(xs::DimTuple) = map($f, xs)
2020
LookupArrays.$f(x::Any) = $f(dims(x))
2121
LookupArrays.$f(x::Any, ds) = $f(dims(x, ds))
2222
end

src/LookupArrays/lookup_arrays.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -593,7 +593,8 @@ Base.:(==)(l1::Transformed, l2::Transformed) = typeof(l1) == typeof(l2) && f(l1)
593593

594594
intervalbounds(l::LookupArray, args...) = _intervalbounds_no_interval_error()
595595
intervalbounds(l::AbstractSampled, args...) = intervalbounds(span(l), sampling(l), l, args...)
596-
intervalbounds(span::Span, ::Points, l::LookupArray, args...) = _intervalbounds_no_interval_error()
596+
intervalbounds(span::Span, ::Points, l::LookupArray, i::Int) = l[i], l[i]
597+
intervalbounds(span::Span, ::Points, l::LookupArray) = map(x -> (x, x), l)
597598
intervalbounds(span::Span, sampling::Intervals, l::LookupArray, i::Int) =
598599
intervalbounds(order(l), locus(sampling), span, l, i)
599600
function intervalbounds(order::ForwardOrdered, locus::Start, span::Span, l::LookupArray, i::Int)

0 commit comments

Comments
 (0)