Skip to content

Commit

Permalink
pruning and more docstrings
Browse files Browse the repository at this point in the history
  • Loading branch information
vladdez committed Jan 8, 2025
1 parent 5369cb0 commit 4c92da7
Show file tree
Hide file tree
Showing 9 changed files with 496 additions and 454 deletions.
150 changes: 150 additions & 0 deletions old/unused_functions.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
function rectselect(ax)
selrect, h = select_vspan(ax.scene; color = (0.9))
translate!(h, 0, 0, -1) # move to background
return selrect
end

function Bonito.jsrender(s::Session, selector::SelectSet)
rows = map(selector.items[]) do value
c = Bonito.Checkbox(true; class = "p-1 m-1")
on(s, c.value) do x
values = selector.value[]
has_item = (value in values)
if x
!has_item && push!(values, value)
else
has_item && filter!(x -> x != value, values)
end
notify(selector.value)
end
return Row(value, c)
end
return Bonito.jsrender(s, Card(Col(rows...)))
end

function style_map(::AbstractRange{<:Number})
return Dict(
:color => identity,
:colormap => RGBAf.(Colors.color.(to_colormap(:lighttest)), 0.5),
)
end

function style_map(values::Set)
mpalette = [:circle, :star4, :xcross, :diamond]
dict = Dict(v => mpalette[i] for (i, v) in enumerate(values))
mcmap = Makie.wong_colors(0.5)
mcolor_lookup = Dict(v => mcmap[i] for (i, v) in enumerate(values))
return Dict(:marker => v -> dict[v], :marker_color => mcolor_lookup)
end

function select_vspan(scene; blocking = false, priority = 2, kwargs...)
key = Mouse.left
waspressed = Observable(false)
rect = Observable(Rectf(0, 0, 1, 1)) # plotted rectangle
rect_ret = Observable(Rectf(0, 0, 1, 1)) # returned rectangle

# Create an initially hidden rectangle
low = Observable(0.0f0)
high = Observable(0.0f0)

on(rect) do r
low.val = r.origin[1]
high[] = r.origin[1] + r.widths[1]
end
plotted_span = vspan!(
scene,
low,
high,
visible = false,
kwargs...,
transparency = true,
color = (:black, 0.1),
)

on(events(scene).mousebutton, priority = priority) do event
if event.button == key
if event.action == Mouse.press && is_mouseinside(scene)
mp = mouseposition(scene)
waspressed[] = true
plotted_span[:visible] = true # start displaying
rect[] = Rectf(mp, 0.0, 0.0)
return Consume(blocking)
end
end
if !(event.button == key && event.action == Mouse.press)
if waspressed[] # User has selected the rectangle
waspressed[] = false
r = Makie.absrect(rect[])
w, h = widths(r)
rect_ret[] = r
end
# always hide if not the right key is pressed
#plotted_span[:visible] = false # make the plotted rectangle invisible
return Consume(blocking)
end

return Consume(false)
end
on(events(scene).mouseposition, priority = priority) do event
if waspressed[]
mp = mouseposition(scene)
mini = minimum(rect[])
rect[] = Rectf(mini, mp - mini)
return Consume(blocking)
end
return Consume(false)
end

return rect_ret, plotted_span
end

function Bonito.jsrender(s::Session, selector::SelectSet)
rows = map(selector.items[]) do value
c = Bonito.Checkbox(true; class = "p-1 m-1")
on(s, c.value) do x
values = selector.value[]
has_item = (value in values)
if x
!has_item && push!(values, value)
else
has_item && filter!(x -> x != value, values)
end
notify(selector.value)
end
return Row(value, c)
end
return Bonito.jsrender(s, Card(Col(rows...)))
end

function variable_legend(name, values::AbstractRange{<:Number}, palette::Dict)
range, cmap = palette[:colormap]
return S.Colorbar(limits = range, colormap = cmap, label = string(name))
end

function variable_legend(name, values::Set, palette::Dict)
marker_color_lookup = (x) -> begin
if haskey(palette, :color)
return get(palette[:color], x, :black)
else
return :black
end
end
marker_lookup = (x) -> begin
if haskey(palette, :marker)
return palette[:marker][x]
else
return :rect
end
end
conditions = collect(values)
elements = map(conditions) do c
return MarkerElement(marker = marker_lookup(c), color = marker_color_lookup(c))
end
return S.Legend(elements, conditions)
end


widget_value(w::Vector{<:String}; resolution = 1) = w
widget_value(x::Vector; resolution = 1) =
x[1] x[end] ? Float64[] : range(Float64(x[1]), Float64(x[end]), length = 5)

102 changes: 6 additions & 96 deletions src/ERPExplorer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -17,103 +17,13 @@ using StatsModels
using StatsBase
using TopoPlots

include("formula_extractor.jl")
include("functions.jl")
include("widgets.jl")
include("plot_data.jl")
include("explore.jl")
include("functions_preprocessing.jl")
include("functions_formular.jl")
include("functions_plotting.jl")

"""
explore(model::UnfoldModel; positions = nothing, size = (700, 600))
Run the dashboard for explorative ERP analysis.
include("widgets_short.jl")
include("widgets_long.jl")

Arguments:\\
- `model::UnfoldLinearModel{Float64}` - Unfold linear model with categorical and continuous terms.
- `positions::Vector{Point{2, Float32}}` - x an y coordinates of the channels.
- `size::Tuple{Float64, Float64}` - size of the topoplot panel.
Actions:
- Extract formula terms and itheir features.
- Create interactive formula with checkboxes.
- Arrange and map dropdown menus.
- Create interactive topoplot.
- Create Observable DataFrame with predicted values (yhats) and more.
- Create `GridLayout`.
- Use `Base.ReentrantLock`, a synchronization primitive_ to manage concurrent access to shared resources in multi-threaded programs
**Return Value:** `res::Hyperscript.Node{Hyperscript.HTMLSVG}` - final HTML code of the dashboard.
"""
function explore(model::UnfoldModel; positions = nothing, size = (700, 600))
App() do
variables = extract_variables(model)
widget_checkbox, widget_signal, widget_dom, value_ranges =
formular_widgets(variables)

var_types = map(x -> x[2][3], variables)
varnames = first.(variables)

mapping, mapping_dom = mapping_dropdowns(varnames, var_types)

if isnothing(positions)
channel = Observable(1)
topo_widget = nothing
else
topo_widget, channel = topoplot_widget(positions; size = size .* 0.5)
end

yhats_sig = yhats_signal(model, widget_signal, channel)
on(mapping) do m
ws = widget_signal.val
ks_m = values(m)
ks_ws = [w.first for w in ws]
for k in ks_ws
widget_checkbox[k][] = k ks_m
end
end

obs = Observable(S.GridLayout())
l = Base.ReentrantLock()

Makie.onany_latest(yhats_sig, mapping; update = true) do eff, mapping # update = true means only, that it is run once immediately
lock(l) do
obs[] = plot_data(
eff,
value_ranges,
varnames[var_types.==:CategoricalTerm],
varnames[var_types.==:ContinuousTerm],
mapping,
)
end
return
end
css = Asset(joinpath(@__DIR__, "..", "style.css"))
fig = plot(obs; figure = (size = size,))
res = DOM.div(
css,
Bonito.TailwindCSS,
Grid(
Card(widget_dom, style = Styles("grid-area" => "header")),
Card(mapping_dom, style = Styles("grid-area" => "sidebar")),
Card(topo_widget, style = Styles("grid-area" => "topo")),
Card(fig, style = Styles("grid-area" => "content"));
columns = "5fr 1fr",
rows = "1fr 6fr 4fr",
areas = """
'header header'
'content sidebar'
'content topo'
""",
);
style = Styles(
"height" => "$(1.2*size[2])px",
"width" => "$(size[1])px",
"margin" => "20px",
"position" => :relative,
),
)
return res
end
end
export explore

end
93 changes: 93 additions & 0 deletions src/explore.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
"""
explore(model::UnfoldModel; positions = nothing, size = (700, 600))
Run the dashboard for explorative ERP analysis.
Arguments:\\
- `model::UnfoldLinearModel{Float64}` - Unfold linear model with categorical and continuous terms.
- `positions::Vector{Point{2, Float32}}` - x an y coordinates of the channels.
- `size::Tuple{Float64, Float64}` - size of the topoplot panel.
Actions:
- Extract formula terms and itheir features.
- Create interactive formula with checkboxes.
- Arrange and map dropdown menus.
- Create interactive topoplot.
- Create Observable DataFrame with predicted values (yhats) and more.
- Create `GridLayout`.
- Use `Base.ReentrantLock`, a synchronization primitive_ to manage concurrent access to shared resources in multi-threaded programs
- Create Figure.
- Translate into into HTML code using DOMs.
**Return Value:** `Hyperscript.Node{Hyperscript.HTMLSVG}` - final HTML code of the dashboard.
"""
function explore(model::UnfoldModel; positions = nothing, size = (700, 600))
App() do
variables = extract_variables(model)
widget_checkbox, widget_signal, widget_dom, value_ranges =
formular_widgets(variables)

var_types = map(x -> x[2][3], variables)
varnames = first.(variables)

mapping, mapping_dom = mapping_dropdowns(varnames, var_types)

if isnothing(positions)
channel = Observable(1)
topo_widget = nothing
else
topo_widget, channel = topoplot_widget(positions; size = size .* 0.5)
end

yhats_sig = yhats_signal(model, widget_signal, channel)
on(mapping) do m
ws = widget_signal.val
ks_m = values(m)
ks_ws = [w.first for w in ws]
for k in ks_ws
widget_checkbox[k][] = k ks_m
end
end

obs = Observable(S.GridLayout())
l = Base.ReentrantLock()

Makie.onany_latest(yhats_sig, mapping; update = true) do eff, mapping # update = true means only, that it is run once immediately
lock(l) do
obs[] = plot_data(
eff,
value_ranges,
varnames[var_types.==:CategoricalTerm],
varnames[var_types.==:ContinuousTerm],
mapping,
)
end
return
end
css = Asset(joinpath(@__DIR__, "..", "style.css"))
fig = plot(obs; figure = (size = size,))
res = DOM.div(
css,
Bonito.TailwindCSS,
Grid(
Card(widget_dom, style = Styles("grid-area" => "header")),
Card(mapping_dom, style = Styles("grid-area" => "sidebar")),
Card(topo_widget, style = Styles("grid-area" => "topo")),
Card(fig, style = Styles("grid-area" => "content"));
columns = "5fr 1fr",
rows = "1fr 6fr 4fr",
areas = """
'header header'
'content sidebar'
'content topo'
""",
);
style = Styles(
"height" => "$(1.2*size[2])px",
"width" => "$(size[1])px",
"margin" => "20px",
"position" => :relative,
),
)
return res
end
end
Loading

0 comments on commit 4c92da7

Please sign in to comment.