Skip to content

Commit

Permalink
manually merge #2261 (DataInspector callback attributes)
Browse files Browse the repository at this point in the history
Changes:
- add `inspector_hover` attribute to allow replacing `show_data` on a per-plot basis
- add `inspector_label` attribute to allows for per-plot labels
- add `inspector_clear` attribute to allow for custom cleanup code (e.g. for an indicator visualization)
- adjust backends to ignore those attributes for rendering
- minor cleanup in `pick_sorted`
  • Loading branch information
ffreyer committed Oct 8, 2022
1 parent be78bea commit 1bc8927
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 27 deletions.
6 changes: 5 additions & 1 deletion GLMakie/src/drawing_primitives.jl
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,11 @@ function cached_robj!(robj_func, screen, scene, x::AbstractPlot)
pollevents(screen)
robj = get!(screen.cache, objectid(x)) do
filtered = filter(x.attributes) do (k, v)
!(k in (:transformation, :tickranges, :ticklabels, :raw, :SSAO, :lightposition, :material))
!in(k, (
:transformation, :tickranges, :ticklabels, :raw, :SSAO,
:lightposition, :material,
:inspector_label, :inspector_hover, :inspector_clear
))
end

gl_attributes = Dict{Symbol, Any}(map(filtered) do key_value
Expand Down
1 change: 1 addition & 0 deletions WGLMakie/src/imagelike.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ nothing_or_color(c::Nothing) = RGBAf(0, 0, 0, 1)

function draw_mesh(mscene::Scene, mesh, plot; uniforms...)
uniforms = Dict(uniforms)
filter!(kv -> !(kv[2] isa Function), uniforms)

colormap = if haskey(plot, :colormap)
cmap = lift(el32convert to_colormap, plot.colormap)
Expand Down
4 changes: 3 additions & 1 deletion WGLMakie/src/particles.jl
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ end
const IGNORE_KEYS = Set([
:shading, :overdraw, :rotation, :distancefield, :space, :markerspace, :fxaa,
:visible, :transformation, :alpha, :linewidth, :transparency, :marker,
:lightposition, :cycle, :label
:lightposition, :cycle, :label, :inspector_clear, :inspector_hover,
:inspector_label
])

function create_shader(scene::Scene, plot::MeshScatter)
Expand Down Expand Up @@ -177,6 +178,7 @@ function create_shader(scene::Scene, plot::Scatter)
attributes[:depth_shift] = get(plot, :depth_shift, Observable(0f0))

delete!(attributes, :uv_offset_width)
filter!(kv -> !(kv[2] isa Function), attributes)
return scatter_shader(scene, attributes)
end

Expand Down
110 changes: 92 additions & 18 deletions src/interaction/inspector.jl
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,9 @@ function DataInspector(scene::Scene; priority = 100, kwargs...)

# Reusable values for creating indicators
indicator_visible = false,

# General reusable
_color = RGBAf(0,0,0,0),
)

plot = tooltip!(parent, Observable(Point2f(0)), text = Observable(""); visible=false, attrib_dict...)
Expand Down Expand Up @@ -325,6 +328,10 @@ function on_hover(inspector)
end

if should_clear
plot = inspector.selection
if haskey(plot, :inspector_clear)
plot[:inspector_clear][](inspector, plot)
end
inspector.plot.visible[] = false
inspector.attributes.indicator_visible[] = false
inspector.plot.offset.val = inspector.attributes.offset[]
Expand All @@ -339,7 +346,20 @@ function show_data_recursion(inspector, plot, idx)
if processed
return true
else
return show_data(inspector, plot, idx)
# Some show_data methods use the current selection to tell whether the
# temporary plots (indicator plots) are theirs or not, so we want to
# reset after processing them. We also don't want to reset when the
processed = if haskey(plot, :inspector_hover)
plot[:inspector_hover][](inspector, plot, idx)
else
show_data(inspector, plot, idx)
end

if processed
inspector.selection = plot
end

return processed
end
end
show_data_recursion(inspector, plot, idx, source) = false
Expand All @@ -348,7 +368,20 @@ function show_data_recursion(inspector, plot::AbstractPlot, idx, source)
if processed
return true
else
return show_data(inspector, plot, idx, source)
# Some show_data methods use the current selection to tell whether the
# temporary plots (indicator plots) are theirs or not, so we want to
# reset after processing them. We also don't want to reset when the
processed = if haskey(plot, :inspector_hover)
plot[:inspector_hover][](inspector, plot, idx, source)
else
show_data(inspector, plot, idx, source)
end

if processed
inspector.selection = plot
end

return processed
end
end

Expand Down Expand Up @@ -410,7 +443,11 @@ function show_data(inspector::DataInspector, plot::Scatter, idx)
ms = plot.markersize[]

tt.offset[] = 0.5ms + 2
tt.text[] = position2string(plot[1][][idx])
if haskey(plot, :inspector_label)
tt.text[] = plot[:inspector_label][](plot, idx, plot[1][][idx])
else
tt.text[] = position2string(plot[1][][idx])
end
tt.visible[] = true
a.indicator_visible[] && (a.indicator_visible[] = false)

Expand Down Expand Up @@ -467,7 +504,11 @@ function show_data(inspector::DataInspector, plot::MeshScatter, idx)
proj_pos = shift_project(scene, plot, to_ndim(Point3f, plot[1][][idx], 0))
update_tooltip_alignment!(inspector, proj_pos)

tt.text[] = position2string(plot[1][][idx])
if haskey(plot, :inspector_label)
tt.text[] = plot[:inspector_label][](plot, idx, plot[1][][idx])
else
tt.text[] = position2string(plot[1][][idx])
end
tt.visible[] = true

return true
Expand All @@ -489,7 +530,11 @@ function show_data(inspector::DataInspector, plot::Union{Lines, LineSegments}, i
update_tooltip_alignment!(inspector, proj_pos)

tt.offset[] = lw + 2
tt.text[] = position2string(typeof(p0)(pos))
if haskey(plot, :inspector_label)
tt.text[] = plot[:inspector_label][](plot, idx, typeof(p0)(pos))
else
tt.text[] = position2string(typeof(p0)(pos))
end
tt.visible[] = true
a.indicator_visible[] && (a.indicator_visible[] = false)

Expand Down Expand Up @@ -535,7 +580,11 @@ function show_data(inspector::DataInspector, plot::Mesh, idx)
end

tt[1][] = proj_pos
tt.text[] = bbox2string(bbox)
if haskey(plot, :inspector_label)
tt.text[] = plot[:inspector_label][](plot, idx, bbox)
else
tt.text[] = bbox2string(bbox)
end
tt.visible[] = true

return true
Expand Down Expand Up @@ -585,7 +634,11 @@ function show_data(inspector::DataInspector, plot::Surface, idx)

if !isnan(pos)
tt[1][] = proj_pos
tt.text[] = position2string(pos)
if haskey(plot, :inspector_label)
tt.text[] = plot[:inspector_label][](plot, idx, pos)
else
tt.text[] = position2string(pos)
end
tt.visible[] = true
tt.offset[] = 0f0
else
Expand All @@ -612,10 +665,16 @@ function show_imagelike(inspector, plot, name, edge_based)

if plot.interpolate[]
i, j, z = _interpolated_getindex(plot[1][], plot[2][], plot[3][], mpos)
tt.text[] = color2text(name, mpos[1], mpos[2], z)
x, y = mpos
else
i, j, z = _pixelated_getindex(plot[1][], plot[2][], plot[3][], mpos, edge_based)
tt.text[] = color2text(name, i, j, z)
x = i; y = j
end

if haskey(plot, :inspector_label)
tt.text[] = plot[:inspector_label][](plot, (i, j), Point3f(mpos[1], mpos[2], z))
else
tt.text[] = color2text(name, x, y, z)
end

# in case we hover over NaN values
Expand Down Expand Up @@ -643,7 +702,7 @@ function show_imagelike(inspector, plot, name, edge_based)
if inspector.selection != plot
clear_temporary_plots!(inspector, plot)
p = scatter!(
scene, position, color = color,
scene, position, color = a._color,
visible = a.indicator_visible,
inspectable = false,
marker=:rect, markersize = map(r -> 3r, a.range),
Expand All @@ -656,7 +715,6 @@ function show_imagelike(inspector, plot, name, edge_based)
p = inspector.temp_plots[1]
p[1].val[1] = position
notify(p[1])
p.color[] = color
end
else
bbox = _pixelated_image_bbox(plot[1][], plot[2][], plot[3][], i, j, edge_based)
Expand Down Expand Up @@ -801,7 +859,11 @@ function show_data(inspector::DataInspector, plot::BarPlot, idx)
a.indicator_visible[] = true
end

tt.text[] = position2string(pos)
if haskey(plot, :inspector_label)
tt.text[] = plot[:inspector_label][](plot, idx, pos)
else
tt.text[] = position2string(pos)
end
tt.visible[] = true

return true
Expand All @@ -821,7 +883,11 @@ function show_data(inspector::DataInspector, plot::Arrows, idx, source)
v = vec2string(plot[2][][idx])

tt[1][] = mpos
tt.text[] = "Position:\n $p\nDirection:\n $v"
if haskey(plot, :inspector_label)
tt.text[] = plot[:inspector_label][](plot, idx, mpos)
else
tt.text[] = "Position:\n $p\nDirection:\n $v"
end
tt.visible[] = true
a.indicator_visible[] && (a.indicator_visible[] = false)

Expand All @@ -838,7 +904,11 @@ function show_data(inspector::DataInspector, plot::Contourf, idx, source::Mesh)
mpos = Point2f(mouseposition_px(inspector.root))
update_tooltip_alignment!(inspector, mpos)
tt[1][] = mpos
tt.text[] = @sprintf("level = %0.3f", level)
if haskey(plot, :inspector_label)
tt.text[] = plot[:inspector_label][](plot, idx, mpos)
else
tt.text[] = @sprintf("level = %0.3f", level)
end
tt.visible[] = true

return true
Expand Down Expand Up @@ -929,10 +999,14 @@ function show_data(inspector::DataInspector, plot::VolumeSlices, idx, child::Hea
val = data[i, j]

tt[1][] = proj_pos
tt.text[] = @sprintf(
"x: %0.6f\ny: %0.6f\nz: %0.6f\n%0.6f0",
pos[1], pos[2], pos[3], val
)
if haskey(plot, :inspector_label)
tt.text[] = plot[:inspector_label][](plot, (i, j), pos)
else
tt.text[] = @sprintf(
"x: %0.6f\ny: %0.6f\nz: %0.6f\n%0.6f0",
pos[1], pos[2], pos[3], val
)
end
tt.visible[] = true
else
tt.visible[] = false
Expand Down
12 changes: 5 additions & 7 deletions src/interaction/interactive_api.jl
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ function pick_sorted(scene::Scene, xy, range)
end

function pick_sorted(scene::Scene, screen, xy, range)
w, h = widths(screen)
w, h = widths(events(scene).window_area[])
if !((1.0 <= xy[1] <= w) && (1.0 <= xy[2] <= h))
return Tuple{AbstractPlot, Int}[]
end
Expand All @@ -158,16 +158,14 @@ function pick_sorted(scene::Scene, screen, xy, range)

picks = pick(scene, screen, Rect2i(x0, y0, dx, dy))

selected = filter(x -> x[1] != nothing, unique(vec(picks)))
selected = filter(x -> x[1] !== nothing, unique(vec(picks)))
distances = [range^2 for _ in selected]
x, y = xy .+ 1 .- Vec2f(x0, y0)
for i in 1:dx, j in 1:dy
if picks[i, j][1] != nothing
if picks[i, j][1] !== nothing
d = (x-i)^2 + (y-j)^2
i = findfirst(isequal(picks[i, j]), selected)
if i === nothing
@warn "This shouldn't happen..."
elseif distances[i] > d
i = findfirst(isequal(picks[i, j]), selected)::Int
if distances[i] > d
distances[i] = d
end
end
Expand Down

0 comments on commit 1bc8927

Please sign in to comment.