From 1bda154bc0e666962a54a30d54a3469d99ee5814 Mon Sep 17 00:00:00 2001 From: "behinger (s-ccs 001)" Date: Tue, 17 Sep 2024 14:57:02 +0000 Subject: [PATCH 01/10] fix labels, fix #39 --- .gitignore | 1 + docs/src/eeg.md | 6 +++--- src/TopoPlots.jl | 4 ++-- src/eeg.jl | 48 +++++++++++++++++++++++++++++++++--------------- 4 files changed, 39 insertions(+), 20 deletions(-) diff --git a/.gitignore b/.gitignore index f85cbbe..81a331a 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,5 @@ test/test_images test/Manifest.toml docs/Manifest.toml +docs/.CondaPkg deps/build.log diff --git a/docs/src/eeg.md b/docs/src/eeg.md index 44ab292..baaba71 100644 --- a/docs/src/eeg.md +++ b/docs/src/eeg.md @@ -9,12 +9,12 @@ TopoPlots.eeg_topoplot -So for the standard 10/20 montage, one can drop the `positions` attribute: +For the standard 10/20 montage, one can drop the `positions` attribute: ```@example 1 using TopoPlots, CairoMakie labels = TopoPlots.CHANNELS_10_20 -TopoPlots.eeg_topoplot(rand(19), labels; axis=(aspect=DataAspect(),), label_text=true, label_scatter=(markersize=10, strokewidth=2,)) +TopoPlots.eeg_topoplot(rand(19); labels=labels, axis=(aspect=DataAspect(),), label_text=true, label_scatter=(markersize=10, strokewidth=2,)) ``` If the channels aren't 10/20, one can still plot them, but then the positions need to be passed as well: @@ -22,7 +22,7 @@ If the channels aren't 10/20, one can still plot them, but then the positions ne ```@example 1 data, positions = TopoPlots.example_data() labels = ["s$i" for i in 1:size(data, 1)] -TopoPlots.eeg_topoplot(data[:, 340, 1], labels; positions=positions, axis=(aspect=DataAspect(),)) +TopoPlots.eeg_topoplot(data[:, 340, 1]; labels = labels, label_text = true, positions=positions, axis=(aspect=DataAspect(),)) ``` ```@docs diff --git a/src/TopoPlots.jl b/src/TopoPlots.jl index 10085fd..45efce5 100644 --- a/src/TopoPlots.jl +++ b/src/TopoPlots.jl @@ -63,8 +63,8 @@ export GeomExtrapolation, NullExtrapolation @compile_workload begin # all calls in this block will be precompiled, regardless of whether # they belong to your package or not (on Julia 1.8 and higher) - eeg_topoplot(view(data, :, 340, 1); positions) - eeg_topoplot(data[:, 340, 1]; positions) + eeg_topoplot(view(data, :, 340, 1); positions) + eeg_topoplot(data[:, 340, 1]; positions) end end diff --git a/src/eeg.jl b/src/eeg.jl index 9dd8752..b9f60a5 100644 --- a/src/eeg.jl +++ b/src/eeg.jl @@ -1,12 +1,13 @@ -@recipe(EEG_TopoPlot, data, labels) do scene +@recipe(EEG_TopoPlot, data) do scene return Attributes(; - head = (color=:black, linewidth=3), + head=(color=:black, linewidth=3), + labels=Makie.automatic, positions = Makie.automatic, # overwrite some topoplot defaults default_theme(scene, TopoPlot)..., - label_scatter = true, - contours = true, + label_scatter=true, + contours=true, ) end @@ -16,7 +17,7 @@ end Attributes: * `positions::Vector{<: Point} = Makie.automatic`: Can be calculated from label (channel) names. Currently, only 10/20 montage has default coordinates provided. - +* `labels::Vector{<: String} = Makie.automatic`: Add custom labels, in case `label_text` is set to true. If `positions` is not specified, `labels` are used to look up the 10/20 coordinates. * `head = (color=:black, linewidth=3)`: draw the outline of the head. Set to nothing to not draw the head outline, otherwise set to a namedtuple that get passed down to the `line!` call that draws the shape. # Some attributes from topoplot are set to different defaults: * `label_scatter = true` @@ -33,15 +34,18 @@ function draw_ear_nose!(parent, circle; kw...) diameter = 2GeometryBasics.radius(circle) middle = GeometryBasics.origin(circle) nose = (Point2f[(-0.05, 0.5), (0.0, 0.55), (0.05, 0.5)] .* diameter) .+ (middle,) - push!(points, Point2f(NaN)); append!(points, nose) + push!(points, Point2f(NaN)) + append!(points, nose) ear = (Point2f[ (0.497, 0.0555), (0.51, 0.0775), (0.518, 0.0783), (0.5299, 0.0746), (0.5419, 0.0555), (0.54, -0.0055), (0.547, -0.0932), (0.532, -0.1313), (0.51, -0.1384), (0.489, -0.1199)] .* diameter) - push!(points, Point2f(NaN)); append!(points, ear .+ middle) - push!(points, Point2f(NaN)); append!(points, (ear .* Point2f(-1, 1)) .+ (middle,)) + push!(points, Point2f(NaN)) + append!(points, ear .+ middle) + push!(points, Point2f(NaN)) + append!(points, (ear .* Point2f(-1, 1)) .+ (middle,)) return points end @@ -57,7 +61,7 @@ const CHANNEL_TO_POSITION_10_20 = begin result = Matrix{Float64}(undef, 19, 2) read!(assetpath("layout_10_20.bin"), result) positions = Point2f.(result[:, 1], result[:, 2]) - Dict{String, Point2f}(zip(CHANNELS_10_20, positions)) + Dict{String,Point2f}(zip(CHANNELS_10_20, positions)) end """ @@ -70,26 +74,40 @@ function labels2positions(labels) if haskey(CHANNEL_TO_POSITION_10_20, key) return CHANNEL_TO_POSITION_10_20[key] else - error("Currently only 10_20 is supported. Found: $(label)") + error("Currently only 10_20 is supported. Found label: $(label)") end end end -function Makie.convert_arguments(::Type{<:EEG_TopoPlot}, data::AbstractVector{<: Real}) - return (data, ["sensor $i" for i in 1:length(data)]) -end +#function Makie.convert_arguments(::Type{<:EEG_TopoPlot}, data::AbstractVector{<:Real}) +# return (data, labels2positions(labels))# + + # +#end function Makie.plot!(plot::EEG_TopoPlot) + positions = lift(plot.labels, plot.positions) do labels, positions + if positions isa Makie.Automatic + @assert !isnothing(labels) && labels != Makie.Automatic "Either positions or labels (10/20-lookup) have to be specified" return labels2positions(labels) else # apply same conversion as for e.g. the scatter arguments return convert_arguments(Makie.PointBased(), positions)[1] end end - - tplot = topoplot!(plot, Attributes(plot), plot.data, positions; labels=plot.labels) + labels = lift(plot.labels, plot.positions) do labels, positions + + if isnothing(labels) || labels isa Makie.Automatic + return ["sensor $i" for i in 1:length(positions)] + else + return labels + end + end + plot.labels = labels + + tplot = topoplot!(plot, Attributes(plot), plot.data, positions;) head = plot_or_defaults(to_value(plot.head), Attributes(), :head) if !isnothing(head) draw_ear_nose!(plot, tplot.geometry; head...) From 3ecdeac628b37951de297a28881bf87dc3b2830f Mon Sep 17 00:00:00 2001 From: "behinger (s-ccs 001)" Date: Wed, 18 Sep 2024 14:08:59 +0000 Subject: [PATCH 02/10] added a backward compatability with warning --- src/eeg.jl | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/eeg.jl b/src/eeg.jl index b9f60a5..d6e2808 100644 --- a/src/eeg.jl +++ b/src/eeg.jl @@ -26,7 +26,15 @@ Attributes: Otherwise the recipe just uses the [`topoplot`](@ref) defaults and passes through the attributes. """ eeg_topoplot - + function eeg_topoplot(data,labels;kwargs...) + @warn "labels as positional arguments have been deprecated. Please provide them as keyword arguments" + eeg_topoplot(data;labels=labels,kwargs...) + end + function eeg_topoplot!(fig, data,labels;kwargs...) + @warn "labels as positional arguments have been deprecated. Please provide them as keyword arguments" + eeg_topoplot!(fig,data;labels=labels,kwargs...) + end + function draw_ear_nose!(parent, circle; kw...) # draw circle head_points = lift(circle) do circle From b8a74c0fa482e0928255266d56a911b188e3cf78 Mon Sep 17 00:00:00 2001 From: "behinger (s-ccs 001)" Date: Wed, 18 Sep 2024 14:18:03 +0000 Subject: [PATCH 03/10] removed NaturalNeighbours for now until #55 is fixed --- docs/src/general.md | 4 +- docs/src/topo_series.jl | 180 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 182 insertions(+), 2 deletions(-) create mode 100644 docs/src/topo_series.jl diff --git a/docs/src/general.md b/docs/src/general.md index 1748d0b..c2d5b60 100644 --- a/docs/src/general.md +++ b/docs/src/general.md @@ -42,13 +42,13 @@ using TopoPlots, CairoMakie, ScatteredInterpolation, NaturalNeighbours data, positions = TopoPlots.example_data() -f = Figure(resolution=(1000, 1250)) +f = Figure(size=(1000, 1250)) interpolators = [ DelaunayMesh() CloughTocher(); SplineInterpolator() NullInterpolator(); ScatteredInterpolationMethod(ThinPlate()) ScatteredInterpolationMethod(Shepard(3)); - NaturalNeighboursMethod(Sibson(1)) NaturalNeighboursMethod(Triangle()); + #NaturalNeighboursMethod(Sibson(1)) NaturalNeighboursMethod(Triangle()); ] data_slice = data[:, 360, 1] diff --git a/docs/src/topo_series.jl b/docs/src/topo_series.jl new file mode 100644 index 0000000..0f841aa --- /dev/null +++ b/docs/src/topo_series.jl @@ -0,0 +1,180 @@ +### A Pluto.jl notebook ### +# v0.19.9 + +using Markdown +using InteractiveUtils + +# ╔═╡ 2fafb0da-f3a9-11ec-0ddf-6725344070fe +begin +using Pkg +Pkg.activate("../../devEnv") # docs +#Pkg.add("PyMNE") +#Pkg.add(path="../../../TopoPlotsjl/") +Pkg.develop(path="../../../TopoPlotsjl/") +#Pkg.add("DataFrames") +#Pkg.add("AlgebraOfGraphics") + #Pkg.add("StatsBase") + #Pkg.add("CategoricalArrays") + + #Pkg.add("JLD2") + + #Pkg.add("CairoMakie") +end + +# ╔═╡ c4a25915-c7f5-453a-a4f0-4b40ebedea4c +using Revise + +# ╔═╡ 59b87673-02d2-4deb-90be-74d923d170eb + using TopoPlots + + +# ╔═╡ 452a245c-773a-4303-a970-f2592c3e879f +begin + #using TopoPlots + #using ../../../Topoplotsjl + using CairoMakie + using DataFrames + + +end + + +# ╔═╡ 77dc1ba9-9484-485b-a49d-9aa231ef4983 +using Statistics + +# ╔═╡ 311f10ff-deb8-4f82-8b12-d5b643656828 +using PyMNE + +# ╔═╡ 9fa5c598-3578-4989-9585-29fd32ae1056 +using Distributions + +# ╔═╡ 6cda29dc-7086-4079-83c6-3650204a82ff +pathof(TopoPlots) + +# ╔═╡ e0cc560f-d3e8-415b-b22d-6bca23ef093c +revise(TopoPlots) + +# ╔═╡ f4b81740-d907-42ae-a0df-f46fb2f2cb15 +begin + +data = Array{Float32}(undef, 64, 400, 3) +#read!(TopoPlots.assetpath("example-data.bin"), data) + read!(splitdir(pathof(TopoPlots))[1]*"/../assets/example-data.bin",data) + +positions = Vector{Point2f}(undef, 64) + read!(splitdir(pathof(TopoPlots))[1]*"/../assets/layout64.bin",positions) +#read!(TopoPlots.assetpath("layout64.bin"), positions) + +end; + + +# ╔═╡ 42f7755b-80f4-4185-8d21-42e11730e0fc +begin + using Random + pos = positions[1:10] +eeg_topoplot(rand(MersenneTwister(1),length(pos)), string.(1:length(pos));positions=pos,pad_value=0.) +end + +# ╔═╡ aad784ee-6bb7-4f3c-8444-be050456ddea +eeg_topoplot(data[:, 340, 1], string.(1:length(positions));positions=positions) + +# ╔═╡ 237e4f4a-cdf2-4bac-8096-de8050251745 +eeg_topoplot(data[:, 340, 1], string.(1:length(positions));positions=positions,pad_value=0.1) + +# ╔═╡ f522329b-3653-4059-9955-8cd05570e923 +topoplot(rand(MersenneTwister(1),length(pos)),pos) + +# ╔═╡ a9d2a2e2-6c8c-4cfc-9fed-b5e082cb44af +let +mon = PyMNE.channels.make_standard_montage("standard_1020") + +posMat = (Matrix(hcat(pos...)).-0.5).*0.5 + #pos = PyMNE.channels.make_eeg_layout(mon).pos +PyMNE.viz.plot_topomap(rand(MersenneTwister(1),length(pos)),posMat',cmap="RdBu_r",extrapolate="box",border=-1) +end + +# ╔═╡ c358633f-8d18-4c5e-80f7-ab972e8860be +Pkg.status("TopoPlots") + +# ╔═╡ c0a2ad2e-ccce-4e80-b52c-75f1428ed182 +e1eg_topoplot(data[:, 340, 1], string.(1:length(positions));positions=positions,interpolation = TopoPlots.NormalMixtureInterpolator() ) + +# ╔═╡ d7620a42-d54c-4244-a820-d15aecdae626 +@time TopoPlots.eeg_topoplot_series(data[:,:,1],40;topoplotCfg=(positions=positions,label_scatter=false)) + +# ╔═╡ ec59c704-ae33-4a62-82ce-63acc6b17793 +f, ax, pl = TopoPlots.eeg_topoplot(1:length(TopoPlots.CHANNELS_10_20),TopoPlots.CHANNELS_10_20; interpolation=TopoPlots.NullInterpolator(),) + +# ╔═╡ f3d1f3cc-f7c9-4ef4-ba4f-3d32f2509cad +let + # 4 coordinates with one peak + positions = Point2f[(-1, 0), (0, -1), (1, 0), (0, 1)] + i = 1 + peak_xy = positions[i] + data = zeros(length(positions)) + data[i] = 1.1 + fig = topoplot(data, positions) + # tighten the limits so that the limits of the axis and the data will match + tightlimits!(fig.axis) + + # retrieve the interpolated data + m = fig.plot.plots[].color[] + # get the limits of the axes and data + rect = fig.axis.targetlimits[] + minx, miny = minimum(rect) + maxx, maxy = maximum(rect) + # recreate the coordinates of the data + x = range(minx, maxx, length=size(m, 1)) + y = range(miny, maxy, length=size(m, 2)) + xys = Point2f.(x, y') + + # find the highest point + _, i = findmax(x -> isnan(x) ? -Inf : x, m) + xy = xys[i] + @show peak_xy + @show xy + #@test isapprox(xy, peak_xy; atol=0.02) + @show isapprox(xy, peak_xy; atol=0.02) + fig +end + +# ╔═╡ 872ac6a4-ddaa-4dfb-a40d-9d5ea55bdb3d +let + f = Figure() + axis = Axis(f[1, 1], aspect = 1) + xlims!(low = -2, high = 2) + ylims!(low = -2, high = 2) + + data = [0, 0, 0] + pos1 = [Point2f(-1, -1), Point2f(-1.0, 0.0), Point2f(0, -1)] + pos2 = [Point2f(1, 1), Point2f(1.0, 0.0), Point2f(0, 1)] + + pos1 = pos1 .- mean(pos1) + pos2 = pos2 .- mean(pos2) + eeg_topoplot!(axis, data, positions=pos1) + eeg_topoplot!(axis, data, positions=pos2) + f +end + +# ╔═╡ Cell order: +# ╠═2fafb0da-f3a9-11ec-0ddf-6725344070fe +# ╠═6cda29dc-7086-4079-83c6-3650204a82ff +# ╠═c4a25915-c7f5-453a-a4f0-4b40ebedea4c +# ╠═e0cc560f-d3e8-415b-b22d-6bca23ef093c +# ╠═59b87673-02d2-4deb-90be-74d923d170eb +# ╠═452a245c-773a-4303-a970-f2592c3e879f +# ╠═f4b81740-d907-42ae-a0df-f46fb2f2cb15 +# ╠═77dc1ba9-9484-485b-a49d-9aa231ef4983 +# ╠═aad784ee-6bb7-4f3c-8444-be050456ddea +# ╠═237e4f4a-cdf2-4bac-8096-de8050251745 +# ╠═42f7755b-80f4-4185-8d21-42e11730e0fc +# ╠═f522329b-3653-4059-9955-8cd05570e923 +# ╠═311f10ff-deb8-4f82-8b12-d5b643656828 +# ╠═a9d2a2e2-6c8c-4cfc-9fed-b5e082cb44af +# ╠═c358633f-8d18-4c5e-80f7-ab972e8860be +# ╠═9fa5c598-3578-4989-9585-29fd32ae1056 +# ╠═c0a2ad2e-ccce-4e80-b52c-75f1428ed182 +# ╠═d7620a42-d54c-4244-a820-d15aecdae626 +# ╠═ec59c704-ae33-4a62-82ce-63acc6b17793 +# ╠═f3d1f3cc-f7c9-4ef4-ba4f-3d32f2509cad +# ╠═872ac6a4-ddaa-4dfb-a40d-9d5ea55bdb3d From c33146e4372f20812131a00ad1695982c5332b2c Mon Sep 17 00:00:00 2001 From: "behinger (s-ccs 001)" Date: Wed, 18 Sep 2024 19:30:22 +0000 Subject: [PATCH 04/10] added example of contourf and surface to docs --- docs/src/general.md | 22 ++++++++++++++++++++++ src/core-recipe.jl | 11 ++++++++--- test/runtests.jl | 7 +++++++ 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/docs/src/general.md b/docs/src/general.md index c2d5b60..4cd7af5 100644 --- a/docs/src/general.md +++ b/docs/src/general.md @@ -146,3 +146,25 @@ TopoPlots.topoplot( label_scatter=(; strokewidth=2), contours=(linewidth=2, color=:white)) ``` + + +## Different plotfunctions + +It is possible to exchange the plotting function, from `heatmap!` to `contourf!` or `surface!`. Due to different keyword arguments, one needs to filter which keywords are passed to the plotting function manually. + +```@example 1 +f = Figure() + +TopoPlots.topoplot(f[1,1], + rand(10), rand(Point2f, 10), + axis=(; aspect=DataAspect()), + plotfnc! = contourf!, plotfnc_kwargs_filter=[:colormap]) + +TopoPlots.topoplot(f[1,2], + rand(10), rand(Point2f, 10), + axis=(; aspect=DataAspect()), + plotfnc! = surface!) # surface can take all default kwargs similar to heatmap! + +f + +``` diff --git a/src/core-recipe.jl b/src/core-recipe.jl index eaec5c5..60edc98 100644 --- a/src/core-recipe.jl +++ b/src/core-recipe.jl @@ -13,7 +13,9 @@ labels = nothing, label_text = false, label_scatter = false, - contours = false + contours = false, + plotfnc! = heatmap!, + plotfnc_kwargs_filter= [:colorrange, :colormap, :interpolate], ) end @@ -39,6 +41,8 @@ Creates an irregular interpolation for each `data[i]` point at `positions[i]`. * true: add point for each position with default attributes * NamedTuple: Attributes get passed to the Makie.scatter! call. * `markersize = 5`: size of the points defined by positions, shortcut for label_scatter=(markersize=5,) +* `plotfnc! = heatmap!`: function to use for plotting the interpolation +* `plotfnc_kwargs_filter = [:colorrange, :colormap, :interpolate]`: different `plotfnc` support different kwargs, this array contains the keys to filter the full list which is [:colorrange, :colormap, :interpolate] * `contours = false`: * true: add scatter point for each position @@ -116,8 +120,9 @@ function Makie.plot!(p::TopoPlot) # z[mask] .= NaN return z end - - heatmap!(p, xg, yg, data, colormap=p.colormap, colorrange=colorrange, interpolate=true) + kwargs_all = Dict(:colorrange => colorrange, :colormap => p.colormap, :interpolate => true) + + p.plotfnc![](p, xg, yg, data; (p.plotfnc_kwargs_filter[].=>getindex.(Ref(kwargs_all),p.plotfnc_kwargs_filter[]))...) contours = to_value(p.contours) attributes = @plot_or_defaults contours Attributes(color=(:black, 0.5), linestyle=:dot, levels=6) if !isnothing(attributes) && !(p.interpolation[] isa NullInterpolator) diff --git a/test/runtests.jl b/test/runtests.jl index f5de00e..9ada81e 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -183,3 +183,10 @@ begin lines!(ax, rect_extended, color=:red) @test_figure("test-extrapolate-data-circle", f) end + +let + data, positions = TopoPlots.example_data() + f, ax, pl = topoplot(1:10,positions[1:10];plotfnc! = contourf!,plotfnc_kwargs_filter=[:colormap]) + f, ax, pl = topoplot(1:10,positions[1:10];plotfnc! = (args...;kwargs...)->heatmap!(args...;alpha=0.3,kwargs...)) + #@test_figure("ClaughTochter", f) +end \ No newline at end of file From d94f8ddc06016eb4a0266f383b20acf8809225a9 Mon Sep 17 00:00:00 2001 From: "behinger (s-ccs 001)" Date: Wed, 18 Sep 2024 19:46:29 +0000 Subject: [PATCH 05/10] added an example with custom bounds #38 --- docs/src/eeg.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/docs/src/eeg.md b/docs/src/eeg.md index baaba71..d87a6f8 100644 --- a/docs/src/eeg.md +++ b/docs/src/eeg.md @@ -25,6 +25,24 @@ labels = ["s$i" for i in 1:size(data, 1)] TopoPlots.eeg_topoplot(data[:, 340, 1]; labels = labels, label_text = true, positions=positions, axis=(aspect=DataAspect(),)) ``` + +## Subset of channels +If you only ask to plot a subset of channels, we highly recommend to define your bounding geometry yourself. We follow MNE functionality and normalize the positions prior to interpolation / plotting. If you only use a subset of channels, the positions will be relative to eachother, not at absolute coordinates. + +```@example 1 +f = Figure() +ax1 = f[1,1] = Axis(f;aspect=DataAspect()) +ax2 = f[1,2] = Axis(f;aspect=DataAspect()) +kwlist = (;label_text=true,label_scatter=(markersize=10, strokewidth=2,color=:white)) +TopoPlots.eeg_topoplot!(ax1,[1,0.5,0]; labels=["Cz","Fz","Fp1"],kwlist...) +TopoPlots.eeg_topoplot!(ax2,[1,0.5,05]; labels=["Cz","Fz","Fp1"], bounding_geometry=Circle(Point2f(0.5,0.5), 0.5),kwlist...) + +f +``` +As visible in the left plot, the positions are normalized to the bounding geometry. The right plot shows the same data, but with Cz correctly centered. + + + ```@docs TopoPlots.example_data ``` From 5d75468cbf546fef3c60d8e49f35564bc8b6e52c Mon Sep 17 00:00:00 2001 From: "behinger (s-ccs 001)" Date: Wed, 18 Sep 2024 21:12:27 +0000 Subject: [PATCH 06/10] added 10-05 template electrodes; breaking: new default enlarge = 1.0 for eeg-topoplot only --- assets/1005.tsv | 349 ++++++++++++++++++ assets/layout_10_05.bin | Bin 0 -> 5568 bytes ...bin.jl => write_template-positions-bin.jl} | 9 +- docs/src/eeg.md | 11 +- src/eeg.jl | 23 +- test/runtests.jl | 3 +- 6 files changed, 386 insertions(+), 9 deletions(-) create mode 100644 assets/1005.tsv create mode 100644 assets/layout_10_05.bin rename assets/{write_10_20-bin.jl => write_template-positions-bin.jl} (56%) diff --git a/assets/1005.tsv b/assets/1005.tsv new file mode 100644 index 0000000..dda25d5 --- /dev/null +++ b/assets/1005.tsv @@ -0,0 +1,349 @@ +label x y +AF1 -0.1371 0.6754 +AF10 0.8580 1.1810 +AF10h 0.7078 0.9743 +AF1h -0.0683 0.6700 +AF2 0.1371 0.6754 +AF2h 0.0683 0.6700 +AF3 -0.2781 0.6983 +AF3h -0.2069 0.6848 +AF4 0.2781 0.6983 +AF4h 0.2069 0.6848 +AF5 -0.4269 0.7404 +AF5h -0.3513 0.7166 +AF6 0.4269 0.7404 +AF6h 0.3513 0.7166 +AF7 -0.5878 0.8090 +AF7h -0.5056 0.7708 +AF8 0.5878 0.8090 +AF8h 0.5056 0.7708 +AF9 -0.8580 1.1810 +AF9h -0.7078 0.9743 +AFF1 -0.1588 0.5428 +AFF10 1.0322 1.0322 +AFF10h 0.8515 0.8515 +AFF1h -0.0790 0.5366 +AFF2 0.1588 0.5428 +AFF2h 0.0790 0.5366 +AFF3 -0.3244 0.5692 +AFF3h -0.2403 0.5535 +AFF4 0.3244 0.5692 +AFF4h 0.2403 0.5535 +AFF5 -0.5042 0.6197 +AFF5h -0.4120 0.5908 +AFF6 0.5042 0.6197 +AFF6h 0.4120 0.5908 +AFF7 -0.7071 0.7071 +AFF7h -0.6021 0.6576 +AFF8 0.7071 0.7071 +AFF8h 0.6021 0.6576 +AFF9 -1.0322 1.0322 +AFF9h -0.8515 0.8515 +AFFz 0.0000 0.5345 +AFp1 -0.1089 0.8246 +AFp10 0.6626 1.3007 +AFp10h 0.5467 1.0730 +AFp1h -0.0543 0.8217 +AFp2 0.1089 0.8246 +AFp2h 0.0543 0.8217 +AFp3 -0.2195 0.8369 +AFp3h -0.1639 0.8296 +AFp4 0.2195 0.8369 +AFp4h 0.1639 0.8296 +AFp5 -0.3339 0.8583 +AFp5h -0.2761 0.8463 +AFp6 0.3339 0.8583 +AFp6h 0.2761 0.8463 +AFp7 -0.4540 0.8910 +AFp7h -0.3931 0.8731 +AFp8 0.4540 0.8910 +AFp8h 0.3931 0.8731 +AFp9 -0.6626 1.3007 +AFp9h -0.5467 1.0730 +AFpz 0.0000 0.8207 +AFz 0.0000 0.6682 +C1 -0.1965 0.0000 +C1h -0.0973 0.0000 +C2 0.1965 0.0000 +C2h 0.0973 0.0000 +C3 -0.4088 0.0000 +C3h -0.2996 0.0000 +C4 0.4088 0.0000 +C4h 0.2996 0.0000 +C5 -0.6582 0.0000 +C5h -0.5271 0.0000 +C6 0.6582 0.0000 +C6h 0.5271 0.0000 +CCP1 -0.1976 -0.1008 +CCP1h -0.0978 -0.0991 +CCP2 0.1976 -0.1008 +CCP2h 0.0978 -0.0991 +CCP3 -0.4110 -0.1085 +CCP3h -0.3012 -0.1039 +CCP4 0.4110 -0.1085 +CCP4h 0.3012 -0.1039 +CCP5 -0.6620 -0.1244 +CCP5h -0.5300 -0.1151 +CCP6 0.6620 -0.1244 +CCP6h 0.5300 -0.1151 +CCPz 0.0000 -0.0985 +CP1 -0.1933 -0.2034 +CP1h -0.0958 -0.2000 +CP2 0.1933 -0.2034 +CP2h 0.0958 -0.2000 +CP3 -0.4013 -0.2183 +CP3h -0.2944 -0.2094 +CP4 0.4013 -0.2183 +CP4h 0.2944 -0.2094 +CP5 -0.6433 -0.2489 +CP5h -0.5164 -0.2311 +CP6 0.6433 -0.2489 +CP6h 0.5164 -0.2311 +CPP1 -0.1859 -0.3098 +CPP1h -0.0922 -0.3050 +CPP2 0.1859 -0.3098 +CPP2h 0.0922 -0.3050 +CPP3 -0.3843 -0.3307 +CPP3h -0.2826 -0.3181 +CPP4 0.3843 -0.3307 +CPP4h 0.2826 -0.3181 +CPP5 -0.6113 -0.3731 +CPP5h -0.4930 -0.3486 +CPP6 0.6113 -0.3731 +CPP6h 0.4930 -0.3486 +CPPz 0.0000 -0.3034 +CPz 0.0000 -0.1989 +Cz 0.0000 0.0000 +F1 -0.1747 0.4220 +F10 1.1810 0.8580 +F10h 0.9743 0.7078 +F1h -0.0867 0.4161 +F2 0.1747 0.4220 +F2h 0.0867 0.4161 +F3 -0.3592 0.4472 +F3h -0.2650 0.4321 +F4 0.3592 0.4472 +F4h 0.2650 0.4321 +F5 -0.5653 0.4970 +F5h -0.4586 0.4683 +F6 0.5653 0.4970 +F6h 0.4586 0.4683 +F7 -0.8090 0.5878 +F7h -0.6812 0.5356 +F8 0.8090 0.5878 +F8h 0.6812 0.5356 +F9 -1.1810 0.8580 +F9h -0.9743 0.7078 +FC1 -0.1933 0.2034 +FC1h -0.0958 0.2000 +FC2 0.1933 0.2034 +FC2h 0.0958 0.2000 +FC3 -0.4013 0.2183 +FC3h -0.2944 0.2094 +FC4 0.4013 0.2183 +FC4h 0.2944 0.2094 +FC5 -0.6433 0.2489 +FC5h -0.5164 0.2311 +FC6 0.6433 0.2489 +FC6h 0.5164 0.2311 +FCC1 -0.1976 0.1008 +FCC1h -0.0978 0.0991 +FCC2 0.1976 0.1008 +FCC2h 0.0978 0.0991 +FCC3 -0.4110 0.1085 +FCC3h -0.3012 0.1039 +FCC4 0.4110 0.1085 +FCC4h 0.3012 0.1039 +FCC5 -0.6620 0.1244 +FCC5h -0.5300 0.1151 +FCC6 0.6620 0.1244 +FCC6h 0.5300 0.1151 +FCCz 0.0000 0.0985 +FCz 0.0000 0.1989 +FFC1 -0.1859 0.3098 +FFC1h -0.0922 0.3050 +FFC2 0.1859 0.3098 +FFC2h 0.0922 0.3050 +FFC3 -0.3843 0.3307 +FFC3h -0.2826 0.3181 +FFC4 0.3843 0.3307 +FFC4h 0.2826 0.3181 +FFC5 -0.6113 0.3731 +FFC5h -0.4930 0.3486 +FFC6 0.6113 0.3731 +FFC6h 0.4930 0.3486 +FFCz 0.0000 0.3034 +FFT10 1.3007 0.6626 +FFT10h 1.0730 0.5467 +FFT7 -0.8910 0.4540 +FFT7h -0.7425 0.4070 +FFT8 0.8910 0.4540 +FFT8h 0.7425 0.4070 +FFT9 -1.3007 0.6626 +FFT9h -1.0730 0.5467 +FT10 1.3884 0.4512 +FT10h 1.1453 0.3722 +FT7 -0.9511 0.3090 +FT7h -0.7860 0.2738 +FT8 0.9511 0.3090 +FT8h 0.7860 0.2738 +FT9 -1.3884 0.4512 +FT9h -1.1453 0.3722 +FTT10 1.4418 0.2284 +FTT10h 1.1894 0.1884 +FTT7 -0.9877 0.1564 +FTT7h -0.8120 0.1376 +FTT8 0.9877 0.1564 +FTT8h 0.8120 0.1376 +FTT9 -1.4418 0.2284 +FTT9h -1.1894 0.1884 +Fp1 -0.3090 0.9511 +Fp1h -0.1564 0.9877 +Fp2 0.3090 0.9511 +Fp2h 0.1564 0.9877 +Fpz 0.0000 1.0000 +Fz 0.0000 0.4142 +I1 -0.4512 -1.3884 +I1h -0.2284 -1.4418 +I2 0.4512 -1.3884 +I2h 0.2284 -1.4418 +Iz 0.0000 -1.4598 +LPA -1.4598 -0.0000 +N1 -0.4512 1.3884 +N1h -0.2284 1.4418 +N2 0.4512 1.3884 +N2h 0.2284 1.4418 +NAS -0.0000 1.4598 +NFp1 -0.3722 1.1453 +NFp1h -0.1884 1.1894 +NFp2 0.3722 1.1453 +NFp2h 0.1884 1.1894 +NFpz -0.0000 1.2042 +Nz -0.0000 1.4598 +O1 -0.3090 -0.9511 +O1h -0.1564 -0.9877 +O2 0.3090 -0.9511 +O2h 0.1564 -0.9877 +OI1 -0.3722 -1.1453 +OI1h -0.1884 -1.1894 +OI2 0.3722 -1.1453 +OI2h 0.1884 -1.1894 +OIz 0.0000 -1.2042 +Oz 0.0000 -1.0000 +P1 -0.1747 -0.4220 +P10 1.1810 -0.8580 +P10h 0.9743 -0.7078 +P1h -0.0867 -0.4161 +P2 0.1747 -0.4220 +P2h 0.0867 -0.4161 +P3 -0.3592 -0.4472 +P3h -0.2650 -0.4321 +P4 0.3592 -0.4472 +P4h 0.2650 -0.4321 +P5 -0.5653 -0.4970 +P5h -0.4586 -0.4683 +P6 0.5653 -0.4970 +P6h 0.4586 -0.4683 +P7 -0.8090 -0.5878 +P7h -0.6812 -0.5356 +P8 0.8090 -0.5878 +P8h 0.6812 -0.5356 +P9 -1.1810 -0.8580 +P9h -0.9743 -0.7078 +PO1 -0.1371 -0.6754 +PO10 0.8580 -1.1810 +PO10h 0.7078 -0.9743 +PO1h -0.0683 -0.6700 +PO2 0.1371 -0.6754 +PO2h 0.0683 -0.6700 +PO3 -0.2781 -0.6983 +PO3h -0.2069 -0.6848 +PO4 0.2781 -0.6983 +PO4h 0.2069 -0.6848 +PO5 -0.4269 -0.7404 +PO5h -0.3513 -0.7166 +PO6 0.4269 -0.7404 +PO6h 0.3513 -0.7166 +PO7 -0.5878 -0.8090 +PO7h -0.5056 -0.7708 +PO8 0.5878 -0.8090 +PO8h 0.5056 -0.7708 +PO9 -0.8580 -1.1810 +PO9h -0.7078 -0.9743 +POO1 -0.1089 -0.8246 +POO10 0.6626 -1.3007 +POO10h 0.5467 -1.0730 +POO1h -0.0543 -0.8217 +POO2 0.1089 -0.8246 +POO2h 0.0543 -0.8217 +POO3 -0.2195 -0.8369 +POO3h -0.1639 -0.8296 +POO4 0.2195 -0.8369 +POO4h 0.1639 -0.8296 +POO5 -0.3339 -0.8583 +POO5h -0.2761 -0.8463 +POO6 0.3339 -0.8583 +POO6h 0.2761 -0.8463 +POO7 -0.4540 -0.8910 +POO7h -0.3931 -0.8731 +POO8 0.4540 -0.8910 +POO8h 0.3931 -0.8731 +POO9 -0.6626 -1.3007 +POO9h -0.5467 -1.0730 +POOz 0.0000 -0.8207 +POz 0.0000 -0.6682 +PPO1 -0.1588 -0.5428 +PPO10 1.0322 -1.0322 +PPO10h 0.8515 -0.8515 +PPO1h -0.0790 -0.5366 +PPO2 0.1588 -0.5428 +PPO2h 0.0790 -0.5366 +PPO3 -0.3244 -0.5692 +PPO3h -0.2403 -0.5535 +PPO4 0.3244 -0.5692 +PPO4h 0.2403 -0.5535 +PPO5 -0.5042 -0.6197 +PPO5h -0.4120 -0.5908 +PPO6 0.5042 -0.6197 +PPO6h 0.4120 -0.5908 +PPO7 -0.7071 -0.7071 +PPO7h -0.6021 -0.6576 +PPO8 0.7071 -0.7071 +PPO8h 0.6021 -0.6576 +PPO9 -1.0322 -1.0322 +PPO9h -0.8515 -0.8515 +PPOz 0.0000 -0.5345 +Pz 0.0000 -0.4142 +RPA 1.4598 0.0000 +T10 1.4598 0.0000 +T10h 1.2042 0.0000 +T7 -1.0000 0.0000 +T7h -0.8072 0.0000 +T8 1.0000 0.0000 +T8h 0.8072 -0.0000 +T9 -1.4598 -0.0000 +T9h -1.2042 -0.0000 +TP10 1.3884 -0.4512 +TP10h 1.1453 -0.3722 +TP7 -0.9511 -0.3090 +TP7h -0.7860 -0.2738 +TP8 0.9511 -0.3090 +TP8h 0.7860 -0.2738 +TP9 -1.3884 -0.4512 +TP9h -1.1453 -0.3722 +TPP10 1.3007 -0.6626 +TPP10h 1.0730 -0.5467 +TPP7 -0.8910 -0.4540 +TPP7h -0.7425 -0.4070 +TPP8 0.8910 -0.4540 +TPP8h 0.7425 -0.4070 +TPP9 -1.3007 -0.6626 +TPP9h -1.0730 -0.5467 +TTP10 1.4418 -0.2284 +TTP10h 1.1894 -0.1884 +TTP7 -0.9877 -0.1564 +TTP7h -0.8120 -0.1376 +TTP8 0.9877 -0.1564 +TTP8h 0.8120 -0.1376 +TTP9 -1.4418 -0.2284 +TTP9h -1.1894 -0.1884 \ No newline at end of file diff --git a/assets/layout_10_05.bin b/assets/layout_10_05.bin new file mode 100644 index 0000000000000000000000000000000000000000..e828a2385e91854f38127ecb0d14b9d1ff963f15 GIT binary patch literal 5568 zcmdUzc~n(Z6vkO-Dw&y_VFoRt>%<@ z$2j@nl%a>mW>d$>{&hQVPN(SiyC#+Q-NQciabfCLx#ex|$XNFL;nb78lg}`iz;t5J z)&XC2OUC#G24nmJjY%k-+wxorj~`vWgxS^1U;-27!I%d-4|Tt@p!-R#pT6^>H2)kL zGi31%U&zj-?>`F_oQ3yu2w_WG+j?|CwMy#e$X=*OkhGhBc5Gbe~_Kh=ss-AWp({^)63VzhsXg%K9PLe{mUKXMGm{Is;ya{Ot#18 z5Ln{jf9||+y?N1k_oDTxs_g-Wei=GCy4jlvzm2kzj0<~DO>8!?S31?O838M)Y%5cH zGN_Q#oJ!s|(@AB~&Hb7S9klhD=qd@TdT#f17V(vazisjEz!RoA7WhC53ES&=3i%&B zv!U$)3+j6T+xIb*G?P8}d`1fPwSdLXUauQcqhxWb_+1iKefGX^-H2J}ilhF*S)xaV ze(BwSXQwP#lys zMP~bZhqB6nn#y)+9L&1Ac62(!0ju;+#4}v(XkV=(JIlMY?kF(o469r%`0>oU$>-R8 zS_zL`OQTq258J^q@$~GjK^3Q)SY_XV^?TH-eV#Sbp*=dDRkqjh1m3>mqUL}vpwB7b zR{Aj|HZl!)}2(wIuq8NRK_|J)}2(w zc|m>$lgcD&oK6tu+OA2_L;EHq%!td`c9+&mA=%z zp-IxWlCat@i$4@U2j51z%gvb{i*+y=tj3G46dx$Q)L!S089r55jTiqcK3HYZ%MlIq zik4*fYw^kAw}r*mq5%1ce-~X-S@c@`z4&yMMb9N(`T+6sDvSRYAFnc=Cw_pDf8rmF z`xxY-F)oaJWaKBSjC^F|C#x*?LdZwC7h+-cUI@A^{jt%PLdRLgg`wjNU1yb{;|yJA zm8Cz1j!S>c!fJmEy=1k2gFdp13qv0nddVt79~pWH-N-gr`bg-b^pPyA&I5YM&`ao; zMef;*`z`nmV_X_`L+;sFSiNV14`Jk;p(FoN*!?6A wHkn~81+W_TZ7ej^`1@cheatmap!(args...;alpha=0.3,kwargs...)) #@test_figure("ClaughTochter", f) -end \ No newline at end of file +end + From 41ef83c5c6ef6be9b970c871a09b6369a5de135f Mon Sep 17 00:00:00 2001 From: "behinger (s-ccs 001)" Date: Wed, 18 Sep 2024 21:48:58 +0000 Subject: [PATCH 07/10] renamed T3 is now T7 T4 is now T8 T5 is now P7 T6 is now P8 according to the MCN system --- src/eeg.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/eeg.jl b/src/eeg.jl index 93e3659..422ad5d 100644 --- a/src/eeg.jl +++ b/src/eeg.jl @@ -78,7 +78,7 @@ const CHANNEL_TO_POSITION_10_05 = begin end # even though these are not actively used, sometimes they can be helpful just to plot a default subset of channels. Therefore we havent deleted them yet (because 10_05 is a superset) -const CHANNELS_10_20 = ["fp1", "f3", "c3", "p3", "o1", "f7", "t3", "t5", "fz", "cz", "pz", "fp2", "f4", "c4", "p4", "o2", "f8", "t4", "t6"] +const CHANNELS_10_20 = ["fp1", "f3", "c3", "p3", "o1", "f7", "t7", "p7", "fz", "cz", "pz", "fp2", "f4", "c4", "p4", "o2", "f8", "t8", "p8"] const CHANNEL_TO_POSITION_10_20 = begin # We load this during precompilation, so that this gets stored as a global @@ -91,7 +91,7 @@ end """ labels2positions(labels) -Currently only supports 10/20 layout, by looking it up in `TopoPlots.CHANNEL_TO_POSITION_10_20`. +Currently supports 10/05 layout, by looking it up in `TopoPlots.CHANNEL_TO_POSITION_10_05`. """ function labels2positions(labels) return map(labels) do label @@ -99,7 +99,7 @@ function labels2positions(labels) if haskey(CHANNEL_TO_POSITION_10_05, key) return CHANNEL_TO_POSITION_10_05[key] else - error("Currently only 10_20 is supported. Found label: $(label)") + error("Currently only 10_05 is supported. Found label: $(label)") end end end From 32f9b7ec9979c43b4808b20387227dc2cdba5757 Mon Sep 17 00:00:00 2001 From: "behinger (s-ccs 001)" Date: Thu, 19 Sep 2024 07:39:18 +0000 Subject: [PATCH 08/10] fixed 10/20 old labels work now too --- src/eeg.jl | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/eeg.jl b/src/eeg.jl index 422ad5d..f6cb474 100644 --- a/src/eeg.jl +++ b/src/eeg.jl @@ -74,11 +74,16 @@ const CHANNEL_TO_POSITION_10_05 = begin result = Matrix{Float64}(undef, 348, 2) read!(assetpath("layout_10_05.bin"), result) positions = Point2f.(result[:, 1], result[:, 2]) - Dict{String,Point2f}(zip(CHANNELS_10_05, positions)) + d = Dict{String,Point2f}(zip(CHANNELS_10_05, positions)) + d["t3"] = d["t7"] + d["t4"] = d["t8"] + d["t5"] = d["p7"] + d["t6"] = d["p8"] + end # even though these are not actively used, sometimes they can be helpful just to plot a default subset of channels. Therefore we havent deleted them yet (because 10_05 is a superset) -const CHANNELS_10_20 = ["fp1", "f3", "c3", "p3", "o1", "f7", "t7", "p7", "fz", "cz", "pz", "fp2", "f4", "c4", "p4", "o2", "f8", "t8", "p8"] +const CHANNELS_10_20 = ["fp1", "f3", "c3", "p3", "o1", "f7", "t3", "t5", "fz", "cz", "pz", "fp2", "f4", "c4", "p4", "o2", "f8", "t4", "t6"] const CHANNEL_TO_POSITION_10_20 = begin # We load this during precompilation, so that this gets stored as a global @@ -91,7 +96,7 @@ end """ labels2positions(labels) -Currently supports 10/05 layout, by looking it up in `TopoPlots.CHANNEL_TO_POSITION_10_05`. +Currently supports 10/20 and 10/05 layout, by looking it up in `TopoPlots.CHANNEL_TO_POSITION_10_05`. """ function labels2positions(labels) return map(labels) do label From e182052588483f4720fbc851bda32373cde90a1a Mon Sep 17 00:00:00 2001 From: "behinger (s-ccs 001)" Date: Thu, 19 Sep 2024 07:56:53 +0000 Subject: [PATCH 09/10] fixed bug --- src/eeg.jl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/eeg.jl b/src/eeg.jl index f6cb474..cb6904e 100644 --- a/src/eeg.jl +++ b/src/eeg.jl @@ -79,13 +79,14 @@ const CHANNEL_TO_POSITION_10_05 = begin d["t4"] = d["t8"] d["t5"] = d["p7"] d["t6"] = d["p8"] + d end # even though these are not actively used, sometimes they can be helpful just to plot a default subset of channels. Therefore we havent deleted them yet (because 10_05 is a superset) const CHANNELS_10_20 = ["fp1", "f3", "c3", "p3", "o1", "f7", "t3", "t5", "fz", "cz", "pz", "fp2", "f4", "c4", "p4", "o2", "f8", "t4", "t6"] -const CHANNEL_TO_POSITION_10_20 = begin +#=const CHANNEL_TO_POSITION_10_20 = begin # We load this during precompilation, so that this gets stored as a global # that's immediately loaded when loading the package result = Matrix{Float64}(undef, 19, 2) @@ -93,6 +94,7 @@ const CHANNEL_TO_POSITION_10_20 = begin positions = Point2f.(result[:, 1], result[:, 2]) Dict{String,Point2f}(zip(CHANNELS_10_20, positions)) end +=# """ labels2positions(labels) From 504e0e26b38929a6a46ba4f855c8883932ec54f4 Mon Sep 17 00:00:00 2001 From: "behinger (s-ccs 001)" Date: Thu, 19 Sep 2024 07:58:18 +0000 Subject: [PATCH 10/10] added test for 10-20/10-05 --- test/runtests.jl | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/runtests.jl b/test/runtests.jl index 8c61f3f..990cf0b 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -191,3 +191,13 @@ let #@test_figure("ClaughTochter", f) end + +let + pos = TopoPlots.labels2positions(TopoPlots.CHANNELS_10_20) + pos10_05 = TopoPlots.labels2positions(TopoPlots.CHANNELS_10_05) + @test pos[TopoPlots.CHANNELS_10_20 .== "t3"] == pos10_05[TopoPlots.CHANNELS_10_05 .== "t7"] + @test pos[TopoPlots.CHANNELS_10_20 .== "t4"] == pos10_05[TopoPlots.CHANNELS_10_05 .== "t8"] + @test pos[TopoPlots.CHANNELS_10_20 .== "t5"] == pos10_05[TopoPlots.CHANNELS_10_05 .== "p7"] + @test pos[TopoPlots.CHANNELS_10_20 .== "t6"] == pos10_05[TopoPlots.CHANNELS_10_05 .== "p8"] +end +