From a123f211b3085079cafb713e493c8fa9968846d3 Mon Sep 17 00:00:00 2001 From: nHackel Date: Fri, 23 Aug 2024 17:22:38 +0200 Subject: [PATCH 01/11] Update Gtk4, CairoMakie and add Gtk4Makie --- Project.toml | 7 +++++-- src/GtkUtils.jl | 8 ++++---- src/LCRMeter.jl | 4 ++-- src/MPIUI.jl | 3 ++- src/OnlineReco/OnlineReco.jl | 2 +- src/Viewer/DataViewer/DataViewer.jl | 4 ++-- src/Viewer/DataViewer/Drawing.jl | 14 +++++++------- src/Viewer/RawDataViewer.jl | 4 ++-- src/Viewer/SFViewerWidget.jl | 2 +- src/Viewer/SpectrogramViewer.jl | 4 ++-- 10 files changed, 28 insertions(+), 24 deletions(-) diff --git a/Project.toml b/Project.toml index a5ae1e8..6111dd6 100644 --- a/Project.toml +++ b/Project.toml @@ -15,6 +15,7 @@ FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" Graphics = "a2bd30eb-e257-5431-a919-1863eab51364" Gtk4 = "9db2cae5-386f-4011-9d63-a5602296539b" +Gtk4Makie = "478199e7-b407-4926-87ea-7196203a28d8" HDF5 = "f67ccb44-e63f-5c2f-98bd-6dc0ccc4ba2f" ImageUtils = "8ad4436d-4835-5a14-8bce-3ae014d2950b" Images = "916415d5-f1e6-5110-898d-aaa5f9f070e0" @@ -52,9 +53,11 @@ MPIMeasurements = "0.5" MPIReco = "0.5" MPISphericalHarmonics = "0.0.10" Reexport = "0.2,1.0" -CairoMakie = "0.10.2" +CairoMakie = "0.12" julia = "1.7" -Gtk4 = "0.4.2" +Gtk4 = "0.6" +Gtk4Makie = "0.2" + [extras] HTTP = "cd3eb016-35fb-5094-929b-558a96fad6f3" diff --git a/src/GtkUtils.jl b/src/GtkUtils.jl index ad48c1c..bdb5b88 100644 --- a/src/GtkUtils.jl +++ b/src/GtkUtils.jl @@ -6,13 +6,13 @@ using Graphics function Base.copy!(ctx::CairoContext, img::AbstractArray{C}) where C<:Union{Colorant,Number} Cairo.save(ctx) Cairo.reset_transform(ctx) - image(ctx, image_surface(img), 0, 0, width(ctx), height(ctx)) - restore(ctx) + Cairo.image(ctx, image_surface(img), 0, 0, Gtk4.width(ctx), Gtk4.height(ctx)) + Cairo.restore(ctx) end Base.copy!(c::GtkCanvas, img) = copy!(getgc(c), img) function Base.fill!(c::GtkCanvas, color::Colorant) ctx = getgc(c) - w, h = width(c), height(c) + w, h = Gtk4.width(c), Gtk4.height(c) rectangle(ctx, 0, 0, w, h) set_source(ctx, color) fill(ctx) @@ -32,7 +32,7 @@ function drawonto(canvas, figure) @guarded draw(canvas) do _ scene = figure.scene CairoMakie.resize!(scene, Gtk4.width(canvas), Gtk4.height(canvas)) - config = CairoMakie.ScreenConfig(1.0, 1.0, :good, true, false) + config = CairoMakie.ScreenConfig(1.0, 1.0, :good, true, false, nothing) screen = CairoMakie.Screen(scene, config, Gtk4.cairo_surface(canvas)) CairoMakie.cairo_draw(screen, scene) end diff --git a/src/LCRMeter.jl b/src/LCRMeter.jl index f887784..edd41fe 100755 --- a/src/LCRMeter.jl +++ b/src/LCRMeter.jl @@ -211,7 +211,7 @@ function sweepAndShow(m::LCRMeterUI) @info m.datay f1, ax1, l1 = CairoMakie.lines(freq, x_list, - figure = (; resolution = (1000, 800), fontsize = 12), + figure = (; size = (1000, 800), fontsize = 12), axis = (; title = "What is this"), color = CairoMakie.RGBf(colors[1]...)) @@ -223,7 +223,7 @@ function sweepAndShow(m::LCRMeterUI) ax1.ylabel = ylabel1 f2, ax2, l2 = CairoMakie.lines(freq, y_list, - figure = (; resolution = (1000, 800), fontsize = 12), + figure = (; size = (1000, 800), fontsize = 12), axis = (; title = "What is this"), color = CairoMakie.RGBf(colors[1]...)) diff --git a/src/MPIUI.jl b/src/MPIUI.jl index 852623a..e21710a 100644 --- a/src/MPIUI.jl +++ b/src/MPIUI.jl @@ -21,6 +21,7 @@ using NLsolve # for MagneticFieldViewer: findFFP() using DataFrames, CSV # for MagneticFieldViewer: export as csv using Unitful import CairoMakie +using Gtk4Makie ENV["MPILIB_UI"] = "Nothing" @@ -28,7 +29,7 @@ ENV["MPILIB_UI"] = "Nothing" @reexport using MPIReco using MPIReco.RegularizedLeastSquares -using ImageUtils: makeAxisArray +using ImageUtils: makeAxisArray, Axis using Gtk4, Gtk4.G_, Gtk4.GLib using Cairo diff --git a/src/OnlineReco/OnlineReco.jl b/src/OnlineReco/OnlineReco.jl index ff15d9a..e633ba3 100755 --- a/src/OnlineReco/OnlineReco.jl +++ b/src/OnlineReco/OnlineReco.jl @@ -158,7 +158,7 @@ function onlineReco(bSF::MPIFile, b::MPIFile; proj="MIP", ### f, ax, l = CairoMakie.lines(1:length(profile), profile, - figure = (; resolution = (1000, 800), fontsize = 12), + figure = (; size = (1000, 800), fontsize = 12), # axis = (; title = "What is this"), color = CairoMakie.RGBf(colors[1]...)) diff --git a/src/Viewer/DataViewer/DataViewer.jl b/src/Viewer/DataViewer/DataViewer.jl index 6b023b2..b318cd5 100644 --- a/src/Viewer/DataViewer/DataViewer.jl +++ b/src/Viewer/DataViewer/DataViewer.jl @@ -321,7 +321,7 @@ end function updateData!(m::DataViewerWidget, data::ImageMeta{T,5}, dataBG=nothing; params=nothing, ampPhase=false) where T #try m.updating = true - numChan = size(data,Axis{:color}) + numChan = size(data,ImageUtils.Axis{:color}) visible(m["mbFusion"], dataBG != nothing) visible(m["lbFusion"], dataBG != nothing) @@ -478,7 +478,7 @@ function showData(m::DataViewerWidget) dat = vec(data_) fig, ax, l_ = CairoMakie.lines(1:length(dat), dat, - figure = (; resolution = (1000, 800), fontsize = 12), + figure = (; size = (1000, 800), fontsize = 12), color = CairoMakie.RGBf(colors[1]...)) CairoMakie.autolimits!(ax) diff --git a/src/Viewer/DataViewer/Drawing.jl b/src/Viewer/DataViewer/Drawing.jl index dbcba3a..9d70ff0 100644 --- a/src/Viewer/DataViewer/Drawing.jl +++ b/src/Viewer/DataViewer/Drawing.jl @@ -192,8 +192,8 @@ function drawImageCairo(c, image, isDrawSectionalLines, isDrawAxes, xsec, ysec, @guarded Gtk4.draw(c) do widget #c = reshape(c,size(c,1), size(c,2)) ctx = getgc(c) - h = height(ctx) - w = width(ctx) + h = Gtk4.height(ctx) + w = Gtk4.width(ctx) im = copy(reverse(arraydata(convert(ImageMeta{RGB{N0f8}},image)),dims=1)) xsec_ = !flipX ? xsec : (size(im,2)-xsec+1) @@ -232,8 +232,8 @@ function drawImageCairo(c, image, isDrawSectionalLines, isDrawAxes, xsec, ysec, w = widget(controller) ctx = getgc(w) reveal(w) - h = height(ctx) - w = width(ctx) + h = Gtk4.height(ctx) + w = Gtk4.width(ctx) xx = x / w*size(image,2) + 0.5 yy = y / h*size(image,1) + 0.5 xx = !flipX ? xx : (size(image,2)-xx+1) @@ -246,8 +246,8 @@ end ## Draw coordinate system function drawAxes(ctx,slide; rgb=[1,1,1]) - h = height(ctx) - w = width(ctx) + h = Gtk4.height(ctx) + w = Gtk4.width(ctx) center = h/40 if slide == "xy" @@ -324,7 +324,7 @@ end function showProfile(m::DataViewerWidget, data, xLabel::String, yLabel::String) f, ax, l = CairoMakie.lines(1:length(data), data, - figure = (; resolution = (1000, 800), figure_padding=4, fontsize = 12), + figure = (; size = (1000, 800), figure_padding=4, fontsize = 12), axis = (; title = "Profile"), color = CairoMakie.RGBf(colors[1]...)) diff --git a/src/Viewer/RawDataViewer.jl b/src/Viewer/RawDataViewer.jl index 2383a47..7c9468d 100644 --- a/src/Viewer/RawDataViewer.jl +++ b/src/Viewer/RawDataViewer.jl @@ -401,7 +401,7 @@ end end fTD, axTD, lTD1 = CairoMakie.lines(timePoints[steps], dataCompressed[:,1], - figure = (; figure_padding=4, resolution = (1000, 800), fontsize = 11), + figure = (; figure_padding=4, size = (1000, 800), fontsize = 11), axis = (; title = "Time Domain"), color = CairoMakie.RGBf(colors[1]...), label = labels_[1]) @@ -450,7 +450,7 @@ end end fFD, axFD, lFD1 = CairoMakie.lines(freq[stepsFr],freqDataCompressed[:,1], - figure = (; figure_padding=4, resolution = (1000, 800), fontsize = 11), + figure = (; figure_padding=4, size = (1000, 800), fontsize = 11), axis = (; title = "Frequency Domain", yscale=log10), color = CairoMakie.RGBf(colors[1]...), label=labels_[1]) diff --git a/src/Viewer/SFViewerWidget.jl b/src/Viewer/SFViewerWidget.jl index ae66736..492f110 100644 --- a/src/Viewer/SFViewerWidget.jl +++ b/src/Viewer/SFViewerWidget.jl @@ -256,7 +256,7 @@ function updateSF(m::SFViewerWidget) end fFD, axFD, lFD1 = CairoMakie.lines(m.frequencies[stepsFr], snrCompressed, - figure = (; resolution = (1000, 800), fontsize = 12), + figure = (; size = (1000, 800), fontsize = 12), axis = (; title = "SNR", yscale=log10), color = CairoMakie.RGBf(colors[1]...)) CairoMakie.scatter!(axFD, [m.frequencies[freq]], [m.SNR[freq,recChan,period]], diff --git a/src/Viewer/SpectrogramViewer.jl b/src/Viewer/SpectrogramViewer.jl index 53f7472..bf3968e 100644 --- a/src/Viewer/SpectrogramViewer.jl +++ b/src/Viewer/SpectrogramViewer.jl @@ -538,7 +538,7 @@ end steps = minTP:sp_:maxTP fTD, axTD, lTD1 = CairoMakie.lines(timePoints[steps], data_[steps], - figure = (; figure_padding=4, resolution = (1000, 800), fontsize = 10), + figure = (; figure_padding=4, size = (1000, 800), fontsize = 10), axis = (; title = "Time Domain"), color = CairoMakie.RGBf(colors[1]...)) @@ -563,7 +563,7 @@ end stepsFr = minFr:spFr:maxFr fFD, axFD, lFD1 = CairoMakie.lines(freq[stepsFr],freqdata[stepsFr,patch], - figure = (; figure_padding=4, resolution = (1000, 800), fontsize = 10), + figure = (; figure_padding=4, size = (1000, 800), fontsize = 10), axis = (; title = "Frequency Domain", yscale=log10), color = CairoMakie.RGBf(colors[1]...)) From 3a66c989b91e6c6d049fce6d585213ee0462f56a Mon Sep 17 00:00:00 2001 From: nHackel Date: Fri, 13 Sep 2024 12:09:15 +0200 Subject: [PATCH 02/11] Add basic Volume and Section viewer --- src/Viewer/3DViewer/3DViewer.jl | 51 +++++++++ src/Viewer/3DViewer/3DViewerWidget.jl | 150 ++++++++++++++++++++++++++ src/Viewer/3DViewer/SectionalMode.jl | 105 ++++++++++++++++++ src/Viewer/3DViewer/VolumeMode.jl | 23 ++++ src/Viewer/Viewer.jl | 1 + 5 files changed, 330 insertions(+) create mode 100644 src/Viewer/3DViewer/3DViewer.jl create mode 100644 src/Viewer/3DViewer/3DViewerWidget.jl create mode 100644 src/Viewer/3DViewer/SectionalMode.jl create mode 100644 src/Viewer/3DViewer/VolumeMode.jl diff --git a/src/Viewer/3DViewer/3DViewer.jl b/src/Viewer/3DViewer/3DViewer.jl new file mode 100644 index 0000000..2b2139f --- /dev/null +++ b/src/Viewer/3DViewer/3DViewer.jl @@ -0,0 +1,51 @@ +abstract type Abstract3DViewerMode end +modeName(m::Abstract3DViewerMode) = string(typeof(m)) + +#= + At the moment it is not possible to update a GtkMakieWidget with new plots. + This means we either have to create a new GtkMakieWidget to update the plot + or we hook into the observables of the plots. + + The second approach breaks for certain plots, for example for isovolumes (if the isovalue changed). +=# +abstract type Abstract3DViewerModeRedrawType end +struct ObservableRedraw <: Abstract3DViewerModeRedrawType end +struct WidgetRedraw <: Abstract3DViewerModeRedrawType end +redrawType(::Abstract3DViewerMode) = ObservableRedraw() + +include("3DViewerWidget.jl") +include("VolumeMode.jl") +include("SectionalMode.jl") +#include("IsoSurfaceMode.jl") + +function updateData!(m::Abstract3DViewerMode, arr) + # NOP +end + +function showData!(gm::Gtk4Makie.GtkGLMakie, mode, data; kwargs...) + fig = Figure() + lscene = LScene(fig[1,1]) + res = showData!(WidgetRedraw(), lscene, mode, data; kwargs...) + push!(gm, fig) + return res +end + +export DataViewer3D, DataViewer3DWidget +mutable struct DataViewer3D + w::Gtk4.GtkWindowLeaf + dvw::DataViewer3DWidget +end + +function DataViewer3D(imFG::ImageMeta; kwargs...) + dv = DataViewer3D(; kwargs...) + updateData!(dv.dvw,imFG) + return dv +end + +function DataViewer3D(; kwargs...) + w = GtkWindow("Data Viewer",800,600) + dw = DataViewer3DWidget(; kwargs...) + push!(w,dw) + show(w) + return DataViewer3D(w,dw) +end \ No newline at end of file diff --git a/src/Viewer/3DViewer/3DViewerWidget.jl b/src/Viewer/3DViewer/3DViewerWidget.jl new file mode 100644 index 0000000..8e81f0f --- /dev/null +++ b/src/Viewer/3DViewer/3DViewerWidget.jl @@ -0,0 +1,150 @@ +mutable struct DataViewer3DWidget{A, C} <: Gtk4.GtkGrid + handle::Ptr{Gtk4.GObject} + lscene::LScene + scene::Scene + axis::A + cam::C + gm::Gtk4Makie.GtkGLMakie + # Controls + controlGrid::GtkGrid + modeCb::GtkComboBoxText + modeOpt::GtkMenuButton + frameAdj::GtkAdjustment + cmapCb::GtkComboBoxText + # Data + data::AbstractArray + # Mode + modes::Vector{<:Abstract3DViewerMode} +end + +function DataViewer3DWidget(; modes = [VolumeMode, SectionalMode]) + grid = GtkGrid() + handle = grid.handle + + # Setup the controls + controls = GtkGrid() + + # Mode selection + # We have to initialize the modeBox and the options later + # Once the viewer exits we can initialize the modes with the viewer as parent + # And then fill out our options + modeBox = GtkComboBoxText() + controls[1, 1] = GtkLabel("Mode") + controls[1, 2] = modeBox + modeOptions = GtkMenuButton() + controls[2, 2] = modeOptions + controls[3, 1:2] = GtkSeparator(:v) + + # Frame/Time Selection + frameAdj = GtkAdjustment(1, 1, 1, 1, 1, 1) + frameSlider = GtkSpinButton(frameAdj, 1, 0) + controls[4, 1] = GtkLabel("Frames") + controls[4, 2] = frameSlider + controls[5, 1:2] = GtkSeparator(:v) + + # Colormap Selection + colormapBox = GtkComboBoxText() + cmaps = ["foo"]#important_colormaps() + foreach(cm -> push!(colormapBox, cm), cmaps) + controls[6, 1] = GtkLabel("Colormap") + controls[6, 2] = colormapBox + + grid[1, 1] = controls + + # Setup the 3D viewing widget + fig = Figure() + lscene = LScene(fig[1,1]) + scene = lscene.scene + cam = scene.camera_controls + axis = first(scene.plots) # Initial plot is an axis for LScene + gm = GtkMakieWidget() + push!(gm, fig) + + grid[1, 2] = gm + + viewer = DataViewer3DWidget(handle, lscene, scene, axis, cam, gm, controls, modeBox, modeOptions, frameAdj, colormapBox, [], Abstract3DViewerMode[]) + + # Initialize the modes + modes = map(mode -> mode(viewer), modes) + for mode in modes + push!(modeBox, modeName(mode)) + end + modeBox.active = 0 + modeOptions.popover = popover(first(modes)) + viewer.modes = modes + + initCallbacks(viewer) + + return viewer +end + +function initCallbacks(m::DataViewer3DWidget) + signal_connect(m.modeCb, "changed") do widget + @show m.modeCb.active + m.modeOpt.popover = popover(m.modes[m.modeCb.active + 1]) + showData!(WidgetRedraw(), m) + end +# + #signal_connect(m.frameAdj, "value-changed") do widget + # showData!(m) + #end +# + #signal_connect(m.cmapCb, "changed") do widget + # showData!(m) + #end +end + + +function updateData!(m::DataViewer3DWidget, file::Union{MDFFile, String}) + imMeta = loadRecoData(file) + updateData!(m, imMeta) +end + +# TODO handle 3 and 4 dim ImageMeta +function updateData!(m::DataViewer3DWidget, imMeta::ImageMeta{T, 5}) where T + m.data = imMeta + map(mode -> updateData!(mode, m.data), m.modes) + showData!(WidgetRedraw(), m) +end + +function updateData!(m::DataViewer3DWidget, array::AbstractArray{T, 5}) where T + m.data = array + map(mode -> updateData!(mode, m.data), m.modes) + showData!(WidgetRedraw(), m) +end + +showData!(m::DataViewer3DWidget) = showData!(redrawType(m.modes[m.modeCb.active + 1]), m) + +function showData!(::WidgetRedraw, m::DataViewer3DWidget) + # Prepare new GtkMakieWidget + delete!(m, m[1, 2]) + m.gm = GtkMakieWidget() + m[1, 2] = m.gm + + mode = m.modes[m.modeCb.active + 1] + frame = round(Int64, m.frameAdj.value) + data = ustrip.(m.data[1, :, :, :, frame]) + + lscene = showData!(m.gm, mode, data) + + m.lscene = lscene + m.scene = lscene.scene + m.axis = first(m.scene.plots) + + # Set the camera to the old position + eyeposition = m.cam.eyeposition[] + upvector = m.cam.upvector[] + lookat = m.cam.lookat[] + m.cam = m.scene.camera_controls + update_cam!(m.scene, eyeposition, lookat, upvector) + return nothing +end + +function showData!(re::ObservableRedraw, m::DataViewer3DWidget) + # TODO move these into a function + mode = m.modes[m.modeCb.active + 1] + frame = round(Int64, m.frameAdj.value) + data = ustrip.(m.data[1, :, :, :, frame]) + showData!(re, m.lscene, mode, data) + return nothing +end diff --git a/src/Viewer/3DViewer/SectionalMode.jl b/src/Viewer/3DViewer/SectionalMode.jl new file mode 100644 index 0000000..1653296 --- /dev/null +++ b/src/Viewer/3DViewer/SectionalMode.jl @@ -0,0 +1,105 @@ + +export SectionalMode + +mutable struct SectionalMode{P} <: Abstract3DViewerMode + pop::GtkPopover + parent::P + yzAdj::GtkAdjustment + xzAdj::GtkAdjustment + xyAdj::GtkAdjustment + yzToggler::GtkCheckButton + xzToggler::GtkCheckButton + xyToggler::GtkCheckButton + #slicePar::Makie.VolumeSlices + #heatMaps::Vector{Heatmap{Tuple{Vector{Float32}, Vector{Float32}, Matrix{Float32}}}} +end + +function SectionalMode(parent::P) where P + pop = GtkPopover() + yzAdj = GtkAdjustment(1, 1, 1, 1, 1, 1) + xzAdj = GtkAdjustment(1, 1, 1, 1, 1, 1) + xyAdj = GtkAdjustment(1, 1, 1, 1, 1, 1) + yzToggler = GtkCheckButton("Visible") + xzToggler = GtkCheckButton("Visible") + xyToggler = GtkCheckButton("Visible") + + grid = GtkGrid() + grid[1, 1] = GtkLabel("xy") + grid[1, 2] = GtkSpinButton(xyAdj, 1, 0) + grid[1, 3] = xyToggler + + grid[2, 1] = GtkLabel("xz") + grid[2, 2] = GtkSpinButton(xzAdj, 1, 0) + grid[2, 3] = xzToggler + + grid[3, 1] = GtkLabel("yz") + grid[3, 2] = GtkSpinButton(yzAdj, 1, 0) + grid[3, 3] = yzToggler + + pop.child = grid + mode = SectionalMode(pop, parent, yzAdj, xzAdj, xyAdj, yzToggler, xzToggler, xyToggler) + + initCallbacks!(mode) + + return mode +end + +popover(m::SectionalMode) = m.pop + +function initCallbacks!(mode::SectionalMode) + signal_connect(mode.yzAdj, "value_changed") do widget + showData!(ObservableRedraw(), mode.parent) + end + signal_connect(mode.xzAdj, "value_changed") do widget + showData!(ObservableRedraw(), mode.parent) + end + signal_connect(mode.xyAdj, "value_changed") do widget + showData!(ObservableRedraw(), mode.parent) + end + + signal_connect(mode.yzToggler, "toggled") do widget + showData!(ObservableRedraw(), mode.parent) + end + signal_connect(mode.xzToggler, "toggled") do widget + showData!(ObservableRedraw(), mode.parent) + end + signal_connect(mode.xyToggler, "toggled") do widget + showData!(ObservableRedraw(), mode.parent) + end +end + +function updateData!(m::SectionalMode, data5D::AbstractArray{T, 5}) where T + m.yzAdj.upper = size(data5D, 2) + m.xzAdj.upper = size(data5D, 3) + m.xyAdj.upper = size(data5D, 4) + m.yzToggler.active = true + m.xzToggler.active = true + m.xyToggler.active = true +end + +function showData!(re, scene::LScene, mode::SectionalMode, data; kwargs...) + showData!(re, scene.scene, mode, data; kwargs...) + return scene +end +function showData!(::WidgetRedraw, scene::Scene, mode::SectionalMode, data; kwargs...) + plt = volumeslices!(scene, map(i -> 1:i, size(data))..., data) + plt.heatmap_xy[].visible = Observable{Any}(true) + plt.heatmap_xz[].visible = Observable{Any}(true) + plt.heatmap_yz[].visible = Observable{Any}(true) + return scene +end +function showData!(::ObservableRedraw, scene::Scene, mode::SectionalMode, data; kwargs...) + # TODO robust plot selection + plt = scene.plots[2] + plt[1] = 1:size(data, 1) + plt[2] = 1:size(data, 2) + plt[3] = 1:size(data, 3) + plt[4] = data + plt.update_xz[](Int64(mode.xzAdj.value)) + plt.update_xy[](Int64(mode.xyAdj.value)) + plt.update_yz[](Int64(mode.yzAdj.value)) + plt.heatmap_xy[].visible[] = mode.xyToggler.active + plt.heatmap_xz[].visible[] = mode.xzToggler.active + plt.heatmap_yz[].visible[] = mode.yzToggler.active + return scene +end \ No newline at end of file diff --git a/src/Viewer/3DViewer/VolumeMode.jl b/src/Viewer/3DViewer/VolumeMode.jl new file mode 100644 index 0000000..65e987c --- /dev/null +++ b/src/Viewer/3DViewer/VolumeMode.jl @@ -0,0 +1,23 @@ +export VolumeMode + +mutable struct VolumeMode{P} <: Abstract3DViewerMode + pop::GtkPopover + parent::P +end +VolumeMode(parent::P) where P = VolumeMode(GtkPopover(), parent) + +popover(m::VolumeMode) = m.pop + +function showData!(re, scene::LScene, mode::VolumeMode, data; kwargs...) + showData!(re, scene.scene, mode, data; kwargs...) + return scene +end +function showData!(::WidgetRedraw, scene::Scene, mode::VolumeMode, data; kwargs...) + volume!(scene, data) + return scene +end +function showData!(::ObservableRedraw, scene::Scene, mode::VolumeMode, data; kwargs...) + # TODO robust + scene.plots[2][4] = data + return scene +end diff --git a/src/Viewer/Viewer.jl b/src/Viewer/Viewer.jl index 9dacb40..8a51903 100644 --- a/src/Viewer/Viewer.jl +++ b/src/Viewer/Viewer.jl @@ -5,3 +5,4 @@ include("RawDataViewer.jl") include("SpectrogramViewer.jl") include("SFViewerWidget.jl") include("MagneticFieldViewer/MagneticFieldViewer.jl") +include("3DViewer/3DViewer.jl") From bf04240540946fc0d8506f9f5905ddcac51649eb Mon Sep 17 00:00:00 2001 From: nHackel Date: Fri, 13 Sep 2024 14:28:21 +0200 Subject: [PATCH 03/11] Fix mode callback errors and add algorithm selection to volume plot --- src/Viewer/3DViewer/3DViewerWidget.jl | 9 +++++-- src/Viewer/3DViewer/SectionalMode.jl | 32 +++++++++++++++-------- src/Viewer/3DViewer/VolumeMode.jl | 37 +++++++++++++++++++++++++-- 3 files changed, 64 insertions(+), 14 deletions(-) diff --git a/src/Viewer/3DViewer/3DViewerWidget.jl b/src/Viewer/3DViewer/3DViewerWidget.jl index 8e81f0f..4be31ea 100644 --- a/src/Viewer/3DViewer/3DViewerWidget.jl +++ b/src/Viewer/3DViewer/3DViewerWidget.jl @@ -23,6 +23,8 @@ function DataViewer3DWidget(; modes = [VolumeMode, SectionalMode]) # Setup the controls controls = GtkGrid() + controls.column_spacing = 5 + controls.row_spacing = 5 # Mode selection # We have to initialize the modeBox and the options later @@ -32,6 +34,7 @@ function DataViewer3DWidget(; modes = [VolumeMode, SectionalMode]) controls[1, 1] = GtkLabel("Mode") controls[1, 2] = modeBox modeOptions = GtkMenuButton() + controls[2, 1] = GtkLabel("Options") controls[2, 2] = modeOptions controls[3, 1:2] = GtkSeparator(:v) @@ -80,8 +83,10 @@ end function initCallbacks(m::DataViewer3DWidget) signal_connect(m.modeCb, "changed") do widget - @show m.modeCb.active - m.modeOpt.popover = popover(m.modes[m.modeCb.active + 1]) + foreach(mode -> mode.active = false, m.modes) + mode = m.modes[m.modeCb.active + 1] + mode.active = true + m.modeOpt.popover = popover(mode) showData!(WidgetRedraw(), m) end # diff --git a/src/Viewer/3DViewer/SectionalMode.jl b/src/Viewer/3DViewer/SectionalMode.jl index 1653296..a1fcfdc 100644 --- a/src/Viewer/3DViewer/SectionalMode.jl +++ b/src/Viewer/3DViewer/SectionalMode.jl @@ -4,14 +4,13 @@ export SectionalMode mutable struct SectionalMode{P} <: Abstract3DViewerMode pop::GtkPopover parent::P + active::Bool yzAdj::GtkAdjustment xzAdj::GtkAdjustment xyAdj::GtkAdjustment yzToggler::GtkCheckButton xzToggler::GtkCheckButton xyToggler::GtkCheckButton - #slicePar::Makie.VolumeSlices - #heatMaps::Vector{Heatmap{Tuple{Vector{Float32}, Vector{Float32}, Matrix{Float32}}}} end function SectionalMode(parent::P) where P @@ -37,34 +36,47 @@ function SectionalMode(parent::P) where P grid[3, 3] = yzToggler pop.child = grid - mode = SectionalMode(pop, parent, yzAdj, xzAdj, xyAdj, yzToggler, xzToggler, xyToggler) + mode = SectionalMode(pop, parent, false, yzAdj, xzAdj, xyAdj, yzToggler, xzToggler, xyToggler) initCallbacks!(mode) return mode end +modeName(m::SectionalMode) = "Volume Slices" popover(m::SectionalMode) = m.pop function initCallbacks!(mode::SectionalMode) signal_connect(mode.yzAdj, "value_changed") do widget - showData!(ObservableRedraw(), mode.parent) + if mode.active + showData!(ObservableRedraw(), mode.parent) + end end signal_connect(mode.xzAdj, "value_changed") do widget - showData!(ObservableRedraw(), mode.parent) + if mode.active + showData!(ObservableRedraw(), mode.parent) + end end signal_connect(mode.xyAdj, "value_changed") do widget - showData!(ObservableRedraw(), mode.parent) + if mode.active + showData!(ObservableRedraw(), mode.parent) + end end signal_connect(mode.yzToggler, "toggled") do widget - showData!(ObservableRedraw(), mode.parent) + if mode.active + showData!(ObservableRedraw(), mode.parent) + end end signal_connect(mode.xzToggler, "toggled") do widget - showData!(ObservableRedraw(), mode.parent) + if mode.active + showData!(ObservableRedraw(), mode.parent) + end end signal_connect(mode.xyToggler, "toggled") do widget - showData!(ObservableRedraw(), mode.parent) + if mode.active + showData!(ObservableRedraw(), mode.parent) + end end end @@ -82,7 +94,7 @@ function showData!(re, scene::LScene, mode::SectionalMode, data; kwargs...) return scene end function showData!(::WidgetRedraw, scene::Scene, mode::SectionalMode, data; kwargs...) - plt = volumeslices!(scene, map(i -> 1:i, size(data))..., data) + plt = volumeslices!(scene, map(i -> 1:i, size(data))..., data; bbox_visible = false) plt.heatmap_xy[].visible = Observable{Any}(true) plt.heatmap_xz[].visible = Observable{Any}(true) plt.heatmap_yz[].visible = Observable{Any}(true) diff --git a/src/Viewer/3DViewer/VolumeMode.jl b/src/Viewer/3DViewer/VolumeMode.jl index 65e987c..c089b8a 100644 --- a/src/Viewer/3DViewer/VolumeMode.jl +++ b/src/Viewer/3DViewer/VolumeMode.jl @@ -1,11 +1,43 @@ export VolumeMode + mutable struct VolumeMode{P} <: Abstract3DViewerMode pop::GtkPopover parent::P + active::Bool + algBox::GtkComboBoxText + algorithms::Vector{Symbol} +end +function VolumeMode(parent::P) where P + algoStrings = ["Absorption", "Additive RGBA", "Absorption RGBA", "MIP"] + algoSymbols = [:absorption, :additive, :absorptionrgba, :mip] + box = GtkComboBoxText() + foreach(algo -> push!(box, algo), algoStrings) + box.active = 3 + + pop = GtkPopover() + grid = GtkGrid() + grid[1, 1] = GtkLabel("Algorithm") + grid[1, 2] = box + + pop.child = grid + + mode = VolumeMode(pop, parent, false, box, algoSymbols) + + initCallbacks!(mode) + + return mode +end + +function initCallbacks!(mode::VolumeMode) + signal_connect(mode.algBox, "changed") do widget + if mode.active + showData!(WidgetRedraw(), mode.parent) + end + end end -VolumeMode(parent::P) where P = VolumeMode(GtkPopover(), parent) +modeName(m::VolumeMode) = "Volume" popover(m::VolumeMode) = m.pop function showData!(re, scene::LScene, mode::VolumeMode, data; kwargs...) @@ -13,7 +45,8 @@ function showData!(re, scene::LScene, mode::VolumeMode, data; kwargs...) return scene end function showData!(::WidgetRedraw, scene::Scene, mode::VolumeMode, data; kwargs...) - volume!(scene, data) + algo = mode.algorithms[mode.algBox.active + 1] + volume!(scene, data; algorithm=algo) return scene end function showData!(::ObservableRedraw, scene::Scene, mode::VolumeMode, data; kwargs...) From 585ca1692bf97936ee4d10fe1feabf04939c0116 Mon Sep 17 00:00:00 2001 From: nHackel Date: Fri, 13 Sep 2024 15:23:57 +0200 Subject: [PATCH 04/11] Add interactive iso surface viewer --- src/Viewer/3DViewer/3DViewer.jl | 2 +- src/Viewer/3DViewer/3DViewerWidget.jl | 2 +- src/Viewer/3DViewer/IsoSurfaceMode.jl | 75 +++++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 src/Viewer/3DViewer/IsoSurfaceMode.jl diff --git a/src/Viewer/3DViewer/3DViewer.jl b/src/Viewer/3DViewer/3DViewer.jl index 2b2139f..a5cbab8 100644 --- a/src/Viewer/3DViewer/3DViewer.jl +++ b/src/Viewer/3DViewer/3DViewer.jl @@ -16,7 +16,7 @@ redrawType(::Abstract3DViewerMode) = ObservableRedraw() include("3DViewerWidget.jl") include("VolumeMode.jl") include("SectionalMode.jl") -#include("IsoSurfaceMode.jl") +include("IsoSurfaceMode.jl") function updateData!(m::Abstract3DViewerMode, arr) # NOP diff --git a/src/Viewer/3DViewer/3DViewerWidget.jl b/src/Viewer/3DViewer/3DViewerWidget.jl index 4be31ea..f58a419 100644 --- a/src/Viewer/3DViewer/3DViewerWidget.jl +++ b/src/Viewer/3DViewer/3DViewerWidget.jl @@ -17,7 +17,7 @@ mutable struct DataViewer3DWidget{A, C} <: Gtk4.GtkGrid modes::Vector{<:Abstract3DViewerMode} end -function DataViewer3DWidget(; modes = [VolumeMode, SectionalMode]) +function DataViewer3DWidget(; modes = [VolumeMode, SectionalMode, IsoSurfaceMode]) grid = GtkGrid() handle = grid.handle diff --git a/src/Viewer/3DViewer/IsoSurfaceMode.jl b/src/Viewer/3DViewer/IsoSurfaceMode.jl new file mode 100644 index 0000000..aa7a935 --- /dev/null +++ b/src/Viewer/3DViewer/IsoSurfaceMode.jl @@ -0,0 +1,75 @@ +export IsoSurfaceMode + + +mutable struct IsoSurfaceMode{P} <: Abstract3DViewerMode + pop::GtkPopover + parent::P + active::Bool + isoAdj::GtkAdjustment + isoMin::GtkLabel + isoMax::GtkLabel + isorange::GtkEntry +end +function IsoSurfaceMode(parent::P) where P + pop = GtkPopover() + grid = GtkGrid() + grid[1, 1] = GtkLabel("Iso Value:") + minLabel = GtkLabel("0.0") + maxLabel = GtkLabel("1.0") + adj = GtkAdjustment(0.5, 0.0, 1.0, 0.05, 0.05, 0.05) + scale = GtkScale(:h, adj) + scale.hexpand = true + grid[2, 1] = minLabel + grid[3, 1] = scale + grid[4, 1] = maxLabel + + grid[1, 2] = GtkLabel("Iso Range:") + range = GtkEntry() + range.text = "0.05" + grid[2:4, 2] = range + + + pop.child = grid + + mode = IsoSurfaceMode(pop, parent, false, adj, minLabel, maxLabel, range) + + initCallbacks!(mode) + + return mode +end + +function initCallbacks!(mode::IsoSurfaceMode) + signal_connect(mode.isoAdj, "value_changed") do widget + if mode.active + showData!(WidgetRedraw(), mode.parent) + end + end + signal_connect(mode.isorange, "changed") do widget + if mode.active + showData!(WidgetRedraw(), mode.parent) + end + end +end + +modeName(m::IsoSurfaceMode) = "Iso Surface" +popover(m::IsoSurfaceMode) = m.pop + +function updateData!(m::IsoSurfaceMode, data) + min, max = extrema(data) + m.isoAdj.upper = max + m.isoAdj.lower = min + m.isoAdj.step_increment = (max - min) / 100 + m.isoMin.label = string(min) + m.isoMax.label = string(max) +end + +function showData!(re, scene::LScene, mode::IsoSurfaceMode, data; kwargs...) + showData!(re, scene.scene, mode, data; kwargs...) + return scene +end +function showData!(::WidgetRedraw, scene::Scene, mode::IsoSurfaceMode, data; kwargs...) + isovalue = mode.isoAdj.value + isoRange = parse(Float64, mode.isorange.text) + volume!(scene, data; algorithm=:iso, isovalue=isovalue, isorange=isoRange) + return scene +end From 303fbd8dfd5a176429c98f0cf2a05da308a0a399 Mon Sep 17 00:00:00 2001 From: nHackel Date: Fri, 13 Sep 2024 15:46:35 +0200 Subject: [PATCH 05/11] Add frame selection --- src/Viewer/3DViewer/3DViewerWidget.jl | 36 +++++++++++++++++++-------- src/Viewer/3DViewer/IsoSurfaceMode.jl | 2 ++ src/Viewer/3DViewer/VolumeMode.jl | 2 +- 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/src/Viewer/3DViewer/3DViewerWidget.jl b/src/Viewer/3DViewer/3DViewerWidget.jl index f58a419..6f0055a 100644 --- a/src/Viewer/3DViewer/3DViewerWidget.jl +++ b/src/Viewer/3DViewer/3DViewerWidget.jl @@ -10,6 +10,7 @@ mutable struct DataViewer3DWidget{A, C} <: Gtk4.GtkGrid modeCb::GtkComboBoxText modeOpt::GtkMenuButton frameAdj::GtkAdjustment + channelAdj::GtkAdjustment cmapCb::GtkComboBoxText # Data data::AbstractArray @@ -43,14 +44,18 @@ function DataViewer3DWidget(; modes = [VolumeMode, SectionalMode, IsoSurfaceMode frameSlider = GtkSpinButton(frameAdj, 1, 0) controls[4, 1] = GtkLabel("Frames") controls[4, 2] = frameSlider - controls[5, 1:2] = GtkSeparator(:v) + channelAdj = GtkAdjustment(1, 1, 1, 1, 1, 1) + channelSlider = GtkSpinButton(channelAdj, 1, 0) + controls[5, 1] = GtkLabel("Channels") + controls[5, 2] = channelSlider + controls[6, 1:2] = GtkSeparator(:v) # Colormap Selection colormapBox = GtkComboBoxText() cmaps = ["foo"]#important_colormaps() foreach(cm -> push!(colormapBox, cm), cmaps) - controls[6, 1] = GtkLabel("Colormap") - controls[6, 2] = colormapBox + controls[7, 1] = GtkLabel("Colormap") + controls[7, 2] = colormapBox grid[1, 1] = controls @@ -65,7 +70,7 @@ function DataViewer3DWidget(; modes = [VolumeMode, SectionalMode, IsoSurfaceMode grid[1, 2] = gm - viewer = DataViewer3DWidget(handle, lscene, scene, axis, cam, gm, controls, modeBox, modeOptions, frameAdj, colormapBox, [], Abstract3DViewerMode[]) + viewer = DataViewer3DWidget(handle, lscene, scene, axis, cam, gm, controls, modeBox, modeOptions, frameAdj, channelAdj, colormapBox, [], Abstract3DViewerMode[]) # Initialize the modes modes = map(mode -> mode(viewer), modes) @@ -89,11 +94,14 @@ function initCallbacks(m::DataViewer3DWidget) m.modeOpt.popover = popover(mode) showData!(WidgetRedraw(), m) end -# - #signal_connect(m.frameAdj, "value-changed") do widget - # showData!(m) - #end -# + + signal_connect(m.frameAdj, "value_changed") do widget + showData!(m) + end + signal_connect(m.channelAdj, "value_changed") do widget + showData!(m) + end + #signal_connect(m.cmapCb, "changed") do widget # showData!(m) #end @@ -108,12 +116,16 @@ end # TODO handle 3 and 4 dim ImageMeta function updateData!(m::DataViewer3DWidget, imMeta::ImageMeta{T, 5}) where T m.data = imMeta + m.frameAdj.upper = size(m.data, 5) + m.channelAdj.upper = size(m.data, 1) map(mode -> updateData!(mode, m.data), m.modes) showData!(WidgetRedraw(), m) end function updateData!(m::DataViewer3DWidget, array::AbstractArray{T, 5}) where T m.data = array + m.frameAdj.upper = size(m.data, 5) + m.channelAdj.upper = size(m.data, 1) map(mode -> updateData!(mode, m.data), m.modes) showData!(WidgetRedraw(), m) end @@ -128,7 +140,8 @@ function showData!(::WidgetRedraw, m::DataViewer3DWidget) mode = m.modes[m.modeCb.active + 1] frame = round(Int64, m.frameAdj.value) - data = ustrip.(m.data[1, :, :, :, frame]) + channel = round(Int64, m.channelAdj.value) + data = ustrip.(m.data[channel, :, :, :, frame]) lscene = showData!(m.gm, mode, data) @@ -149,7 +162,8 @@ function showData!(re::ObservableRedraw, m::DataViewer3DWidget) # TODO move these into a function mode = m.modes[m.modeCb.active + 1] frame = round(Int64, m.frameAdj.value) - data = ustrip.(m.data[1, :, :, :, frame]) + channel = round(Int64, m.channelAdj.value) + data = ustrip.(m.data[channel, :, :, :, frame]) showData!(re, m.lscene, mode, data) return nothing end diff --git a/src/Viewer/3DViewer/IsoSurfaceMode.jl b/src/Viewer/3DViewer/IsoSurfaceMode.jl index aa7a935..aa74488 100644 --- a/src/Viewer/3DViewer/IsoSurfaceMode.jl +++ b/src/Viewer/3DViewer/IsoSurfaceMode.jl @@ -53,6 +53,8 @@ end modeName(m::IsoSurfaceMode) = "Iso Surface" popover(m::IsoSurfaceMode) = m.pop +redrawType(::IsoSurfaceMode) = WidgetRedraw() + function updateData!(m::IsoSurfaceMode, data) min, max = extrema(data) diff --git a/src/Viewer/3DViewer/VolumeMode.jl b/src/Viewer/3DViewer/VolumeMode.jl index c089b8a..9eb52ae 100644 --- a/src/Viewer/3DViewer/VolumeMode.jl +++ b/src/Viewer/3DViewer/VolumeMode.jl @@ -51,6 +51,6 @@ function showData!(::WidgetRedraw, scene::Scene, mode::VolumeMode, data; kwargs. end function showData!(::ObservableRedraw, scene::Scene, mode::VolumeMode, data; kwargs...) # TODO robust - scene.plots[2][4] = data + scene.plots[2][4][] = data return scene end From 3d7e6e0170429718399f61ed84a3b9bdc93dbfe5 Mon Sep 17 00:00:00 2001 From: nHackel Date: Fri, 13 Sep 2024 18:38:09 +0200 Subject: [PATCH 06/11] Init colormap for volume/slices plots --- src/Viewer/3DViewer/3DViewerWidget.jl | 61 +++++++++++++++++++++------ src/Viewer/3DViewer/SectionalMode.jl | 10 +++-- src/Viewer/3DViewer/VolumeMode.jl | 15 +++++-- 3 files changed, 65 insertions(+), 21 deletions(-) diff --git a/src/Viewer/3DViewer/3DViewerWidget.jl b/src/Viewer/3DViewer/3DViewerWidget.jl index 6f0055a..d2e136c 100644 --- a/src/Viewer/3DViewer/3DViewerWidget.jl +++ b/src/Viewer/3DViewer/3DViewerWidget.jl @@ -12,6 +12,8 @@ mutable struct DataViewer3DWidget{A, C} <: Gtk4.GtkGrid frameAdj::GtkAdjustment channelAdj::GtkAdjustment cmapCb::GtkComboBoxText + cmin::GtkScaleButton + cmax::GtkScaleButton # Data data::AbstractArray # Mode @@ -52,10 +54,19 @@ function DataViewer3DWidget(; modes = [VolumeMode, SectionalMode, IsoSurfaceMode # Colormap Selection colormapBox = GtkComboBoxText() - cmaps = ["foo"]#important_colormaps() + cmaps = important_cmaps() foreach(cm -> push!(colormapBox, cm), cmaps) controls[7, 1] = GtkLabel("Colormap") controls[7, 2] = colormapBox + colormapBox.active = 5 # viridis + cmin = GtkScaleButton(0, 99, 1, ["audio-volume-low"]) + cmax = GtkScaleButton(1, 100, 1, ["audio-volume-high"]) + cmin.value = 0 + cmax.value = 100 + controls[8, 1] = GtkLabel("Min") + controls[8, 2] = cmin + controls[9, 1] = GtkLabel("Max") + controls[9, 2] = cmax grid[1, 1] = controls @@ -70,7 +81,7 @@ function DataViewer3DWidget(; modes = [VolumeMode, SectionalMode, IsoSurfaceMode grid[1, 2] = gm - viewer = DataViewer3DWidget(handle, lscene, scene, axis, cam, gm, controls, modeBox, modeOptions, frameAdj, channelAdj, colormapBox, [], Abstract3DViewerMode[]) + viewer = DataViewer3DWidget(handle, lscene, scene, axis, cam, gm, controls, modeBox, modeOptions, frameAdj, channelAdj, colormapBox, cmin, cmax, [], Abstract3DViewerMode[]) # Initialize the modes modes = map(mode -> mode(viewer), modes) @@ -102,9 +113,16 @@ function initCallbacks(m::DataViewer3DWidget) showData!(m) end - #signal_connect(m.cmapCb, "changed") do widget - # showData!(m) - #end + signal_connect(m.cmapCb, "changed") do widget + showData!(m) + end + + signal_connect(m.cmin, "value_changed") do widget, val + showData!(m) + end + signal_connect(m.cmax, "value_changed") do widget, val + showData!(m) + end end @@ -126,10 +144,28 @@ function updateData!(m::DataViewer3DWidget, array::AbstractArray{T, 5}) where T m.data = array m.frameAdj.upper = size(m.data, 5) m.channelAdj.upper = size(m.data, 1) + m.cmin = 0 + m.cmax = 100 map(mode -> updateData!(mode, m.data), m.modes) showData!(WidgetRedraw(), m) end +function prepareData(m::DataViewer3DWidget) + frame = round(Int64, m.frameAdj.value) + channel = round(Int64, m.channelAdj.value) + data = ustrip.(m.data[channel, :, :, :, frame]) + return data +end + +function prepareDrawKwargs(m::DataViewer3DWidget) + dict = Dict{Symbol, Any}() + max = maximum(m.data) + cmin = (m.cmin.value/100) * max + cmax = (m.cmax.value/100) * max + dict[:cparams] = ColoringParams(cmin, cmax, Gtk4.active_text(m.cmapCb)) + return dict +end + showData!(m::DataViewer3DWidget) = showData!(redrawType(m.modes[m.modeCb.active + 1]), m) function showData!(::WidgetRedraw, m::DataViewer3DWidget) @@ -139,11 +175,9 @@ function showData!(::WidgetRedraw, m::DataViewer3DWidget) m[1, 2] = m.gm mode = m.modes[m.modeCb.active + 1] - frame = round(Int64, m.frameAdj.value) - channel = round(Int64, m.channelAdj.value) - data = ustrip.(m.data[channel, :, :, :, frame]) - - lscene = showData!(m.gm, mode, data) + data = prepareData(m) + kwargs = prepareDrawKwargs(m) + lscene = showData!(m.gm, mode, data; kwargs...) m.lscene = lscene m.scene = lscene.scene @@ -161,9 +195,8 @@ end function showData!(re::ObservableRedraw, m::DataViewer3DWidget) # TODO move these into a function mode = m.modes[m.modeCb.active + 1] - frame = round(Int64, m.frameAdj.value) - channel = round(Int64, m.channelAdj.value) - data = ustrip.(m.data[channel, :, :, :, frame]) - showData!(re, m.lscene, mode, data) + data = prepareData(m) + kwargs = prepareDrawKwargs(m) + showData!(re, m.lscene, mode, data; kwargs...) return nothing end diff --git a/src/Viewer/3DViewer/SectionalMode.jl b/src/Viewer/3DViewer/SectionalMode.jl index a1fcfdc..764f0dc 100644 --- a/src/Viewer/3DViewer/SectionalMode.jl +++ b/src/Viewer/3DViewer/SectionalMode.jl @@ -93,15 +93,17 @@ function showData!(re, scene::LScene, mode::SectionalMode, data; kwargs...) showData!(re, scene.scene, mode, data; kwargs...) return scene end -function showData!(::WidgetRedraw, scene::Scene, mode::SectionalMode, data; kwargs...) - plt = volumeslices!(scene, map(i -> 1:i, size(data))..., data; bbox_visible = false) +function showData!(::WidgetRedraw, scene::Scene, mode::SectionalMode, data; cparams = ColoringParams(extrema(data)..., "viridis"), kwargs...) + cmap = to_colormap(Symbol(cparams.cmap)) + plt = volumeslices!(scene, map(i -> 1:i, size(data))..., data; bbox_visible = false, colormap=cmap, colorrange = (cparams.cmin, cparams.cmax)) plt.heatmap_xy[].visible = Observable{Any}(true) plt.heatmap_xz[].visible = Observable{Any}(true) plt.heatmap_yz[].visible = Observable{Any}(true) return scene end -function showData!(::ObservableRedraw, scene::Scene, mode::SectionalMode, data; kwargs...) +function showData!(::ObservableRedraw, scene::Scene, mode::SectionalMode, data; cparams = ColoringParams(extrema(data)..., "viridis"), kwargs...) # TODO robust plot selection + cmap = to_colormap(Symbol(cparams.cmap)) plt = scene.plots[2] plt[1] = 1:size(data, 1) plt[2] = 1:size(data, 2) @@ -113,5 +115,7 @@ function showData!(::ObservableRedraw, scene::Scene, mode::SectionalMode, data; plt.heatmap_xy[].visible[] = mode.xyToggler.active plt.heatmap_xz[].visible[] = mode.xzToggler.active plt.heatmap_yz[].visible[] = mode.yzToggler.active + plt.colormap[] = cmap + plt.colorrange[] = (cparams.cmin, cparams.cmax) return scene end \ No newline at end of file diff --git a/src/Viewer/3DViewer/VolumeMode.jl b/src/Viewer/3DViewer/VolumeMode.jl index 9eb52ae..ae869fb 100644 --- a/src/Viewer/3DViewer/VolumeMode.jl +++ b/src/Viewer/3DViewer/VolumeMode.jl @@ -44,13 +44,20 @@ function showData!(re, scene::LScene, mode::VolumeMode, data; kwargs...) showData!(re, scene.scene, mode, data; kwargs...) return scene end -function showData!(::WidgetRedraw, scene::Scene, mode::VolumeMode, data; kwargs...) +function showData!(::WidgetRedraw, scene::Scene, mode::VolumeMode, data; cparams = ColoringParams(extrema(data)..., "viridis"), kwargs...) algo = mode.algorithms[mode.algBox.active + 1] - volume!(scene, data; algorithm=algo) + cmap = to_colormap(Symbol(cparams.cmap)) + cmap[1] = RGBA(0.0, 0.0, 0.0, 0.0) + volume!(scene, data; algorithm=algo, colormap=cmap, colorrange = (cparams.cmin, cparams.cmax)) return scene end -function showData!(::ObservableRedraw, scene::Scene, mode::VolumeMode, data; kwargs...) +function showData!(::ObservableRedraw, scene::Scene, mode::VolumeMode, data; cparams = ColoringParams(extrema(data)..., "viridis"), kwargs...) # TODO robust - scene.plots[2][4][] = data + plt = scene.plots[2] + cmap = to_colormap(Symbol(cparams.cmap)) + cmap[1] = RGBA(0.0, 0.0, 0.0, 0.0) + plt.colormap[] = cmap + plt.colorrange[] = (cparams.cmin, cparams.cmax) + plt[4][] = data return scene end From 1d4a9647a57d16611841dd4a8f6b96c00ed0df8c Mon Sep 17 00:00:00 2001 From: nHackel Date: Fri, 13 Sep 2024 19:44:12 +0200 Subject: [PATCH 07/11] Allow starting viewer with data --- src/Viewer/3DViewer/3DViewer.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Viewer/3DViewer/3DViewer.jl b/src/Viewer/3DViewer/3DViewer.jl index a5cbab8..54d5ba6 100644 --- a/src/Viewer/3DViewer/3DViewer.jl +++ b/src/Viewer/3DViewer/3DViewer.jl @@ -36,7 +36,7 @@ mutable struct DataViewer3D dvw::DataViewer3DWidget end -function DataViewer3D(imFG::ImageMeta; kwargs...) +function DataViewer3D(imFG; kwargs...) dv = DataViewer3D(; kwargs...) updateData!(dv.dvw,imFG) return dv From 9a5d28082c8920f494083c9ffd1fabb6a670f8f7 Mon Sep 17 00:00:00 2001 From: nHackel Date: Fri, 20 Sep 2024 17:59:13 +0200 Subject: [PATCH 08/11] Init standalone viewer package --- MPIViewers/Project.toml | 65 + MPIViewers/src/3DViewer/3DViewer.jl | 51 + MPIViewers/src/3DViewer/3DViewerWidget.jl | 202 ++ MPIViewers/src/3DViewer/IsoSurfaceMode.jl | 77 + MPIViewers/src/3DViewer/ObliqueSliceMode.jl | 74 + MPIViewers/src/3DViewer/SectionalMode.jl | 121 ++ MPIViewers/src/3DViewer/VolumeMode.jl | 63 + MPIViewers/src/BaseViewer.jl | 55 + MPIViewers/src/DataViewer/DataViewer.jl | 620 ++++++ MPIViewers/src/DataViewer/Drawing.jl | 339 +++ MPIViewers/src/DataViewer/Export.jl | 211 ++ MPIViewers/src/GtkUtils.jl | 34 + MPIViewers/src/MPIViewers.jl | 95 + MPIViewers/src/MagneticFieldViewer/Export.jl | 238 ++ .../MagneticFieldViewer.jl | 885 ++++++++ .../src/MagneticFieldViewer/Plotting.jl | 424 ++++ .../src/MagneticFieldViewer/TikzExport.jl | 348 +++ MPIViewers/src/RawDataViewer.jl | 592 +++++ MPIViewers/src/SFViewerWidget.jl | 369 ++++ MPIViewers/src/SimpleDataViewer.jl | 91 + MPIViewers/src/SpectrogramViewer.jl | 601 +++++ MPIViewers/src/builder/baseViewer.ui | 130 ++ MPIViewers/src/builder/dataviewer.ui | 1322 +++++++++++ MPIViewers/src/builder/magneticFieldViewer.ui | 1936 +++++++++++++++++ MPIViewers/src/builder/rawDataViewer.ui | 436 ++++ MPIViewers/src/builder/simpleDataViewer.ui | 106 + MPIViewers/src/builder/spectrogramViewer.ui | 452 ++++ 27 files changed, 9937 insertions(+) create mode 100644 MPIViewers/Project.toml create mode 100644 MPIViewers/src/3DViewer/3DViewer.jl create mode 100644 MPIViewers/src/3DViewer/3DViewerWidget.jl create mode 100644 MPIViewers/src/3DViewer/IsoSurfaceMode.jl create mode 100644 MPIViewers/src/3DViewer/ObliqueSliceMode.jl create mode 100644 MPIViewers/src/3DViewer/SectionalMode.jl create mode 100644 MPIViewers/src/3DViewer/VolumeMode.jl create mode 100644 MPIViewers/src/BaseViewer.jl create mode 100644 MPIViewers/src/DataViewer/DataViewer.jl create mode 100644 MPIViewers/src/DataViewer/Drawing.jl create mode 100644 MPIViewers/src/DataViewer/Export.jl create mode 100644 MPIViewers/src/GtkUtils.jl create mode 100644 MPIViewers/src/MPIViewers.jl create mode 100644 MPIViewers/src/MagneticFieldViewer/Export.jl create mode 100644 MPIViewers/src/MagneticFieldViewer/MagneticFieldViewer.jl create mode 100644 MPIViewers/src/MagneticFieldViewer/Plotting.jl create mode 100644 MPIViewers/src/MagneticFieldViewer/TikzExport.jl create mode 100644 MPIViewers/src/RawDataViewer.jl create mode 100644 MPIViewers/src/SFViewerWidget.jl create mode 100644 MPIViewers/src/SimpleDataViewer.jl create mode 100644 MPIViewers/src/SpectrogramViewer.jl create mode 100644 MPIViewers/src/builder/baseViewer.ui create mode 100644 MPIViewers/src/builder/dataviewer.ui create mode 100644 MPIViewers/src/builder/magneticFieldViewer.ui create mode 100644 MPIViewers/src/builder/rawDataViewer.ui create mode 100644 MPIViewers/src/builder/simpleDataViewer.ui create mode 100644 MPIViewers/src/builder/spectrogramViewer.ui diff --git a/MPIViewers/Project.toml b/MPIViewers/Project.toml new file mode 100644 index 0000000..284a909 --- /dev/null +++ b/MPIViewers/Project.toml @@ -0,0 +1,65 @@ +name = "MPIViewers" +uuid = "8bda2715-69d8-46f6-8f4f-eacbc82b6e08" +authors = ["Tobias Knopp "] +version = "0.1.0" + +[deps] +Cairo = "159f3aea-2a34-519c-b102-8c37f9878175" +CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0" +Colors = "5ae59095-9a9b-59fe-a467-6f913c188581" +CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b" +DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" +Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" +DelimitedFiles = "8bb1440f-4735-579b-a4ab-409b98df4dab" +FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" +FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" +Gtk4 = "9db2cae5-386f-4011-9d63-a5602296539b" +Gtk4Makie = "478199e7-b407-4926-87ea-7196203a28d8" +Graphics = "a2bd30eb-e257-5431-a919-1863eab51364" +HDF5 = "f67ccb44-e63f-5c2f-98bd-6dc0ccc4ba2f" +ImageUtils = "8ad4436d-4835-5a14-8bce-3ae014d2950b" +Images = "916415d5-f1e6-5110-898d-aaa5f9f070e0" +InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240" +LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" +Logging = "56ddb016-857b-54e1-b83d-db4d58db5568" +LoggingExtras = "e6f89c97-d47a-5376-807f-9c37f3926c36" +MPIFiles = "371237a9-e6c1-5201-9adb-3d8cfa78fa9f" +MPIMeasurements = "a874a27a-e9a7-503d-98b6-bf61df4bb725" +MPIReco = "e4246700-6248-511e-8146-a1d1f47669d2" +MPISphericalHarmonics = "2527f7a4-0af4-4016-a944-036fbac19de9" +NLsolve = "2774e3e8-f4cf-5e23-947b-6d7e65073b56" +Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" +Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7" +REPL = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" +Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" +Reexport = "189a3867-3050-52da-a836-e630ba90ab69" +SphericalHarmonicExpansions = "504afa71-bae7-47b4-8ec9-3851161806ac" +Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" + +[compat] +Cairo = "1.0" +Colors = "0.12" +FFTW = "1.3" +FileIO = "1.6" +Graphics = "1.1" +HDF5 = "0.15, 0.16" +ImageUtils = "0.2" +Images = "0.23, 0.24, 0.25" +LoggingExtras = ">= 0.4.2" +MPIFiles = "0.15, 0.16" +MPIMeasurements = "0.5, 0.6" +MPIReco = "0.7" +MPISphericalHarmonics = "0.0.10" +Reexport = "0.2,1.0" +CairoMakie = "0.12" +julia = "1.7" +Gtk4 = "0.6" +Gtk4Makie = "0.2" + + +[extras] +HTTP = "cd3eb016-35fb-5094-929b-558a96fad6f3" +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[targets] +test = ["Test", "HTTP"] diff --git a/MPIViewers/src/3DViewer/3DViewer.jl b/MPIViewers/src/3DViewer/3DViewer.jl new file mode 100644 index 0000000..54d5ba6 --- /dev/null +++ b/MPIViewers/src/3DViewer/3DViewer.jl @@ -0,0 +1,51 @@ +abstract type Abstract3DViewerMode end +modeName(m::Abstract3DViewerMode) = string(typeof(m)) + +#= + At the moment it is not possible to update a GtkMakieWidget with new plots. + This means we either have to create a new GtkMakieWidget to update the plot + or we hook into the observables of the plots. + + The second approach breaks for certain plots, for example for isovolumes (if the isovalue changed). +=# +abstract type Abstract3DViewerModeRedrawType end +struct ObservableRedraw <: Abstract3DViewerModeRedrawType end +struct WidgetRedraw <: Abstract3DViewerModeRedrawType end +redrawType(::Abstract3DViewerMode) = ObservableRedraw() + +include("3DViewerWidget.jl") +include("VolumeMode.jl") +include("SectionalMode.jl") +include("IsoSurfaceMode.jl") + +function updateData!(m::Abstract3DViewerMode, arr) + # NOP +end + +function showData!(gm::Gtk4Makie.GtkGLMakie, mode, data; kwargs...) + fig = Figure() + lscene = LScene(fig[1,1]) + res = showData!(WidgetRedraw(), lscene, mode, data; kwargs...) + push!(gm, fig) + return res +end + +export DataViewer3D, DataViewer3DWidget +mutable struct DataViewer3D + w::Gtk4.GtkWindowLeaf + dvw::DataViewer3DWidget +end + +function DataViewer3D(imFG; kwargs...) + dv = DataViewer3D(; kwargs...) + updateData!(dv.dvw,imFG) + return dv +end + +function DataViewer3D(; kwargs...) + w = GtkWindow("Data Viewer",800,600) + dw = DataViewer3DWidget(; kwargs...) + push!(w,dw) + show(w) + return DataViewer3D(w,dw) +end \ No newline at end of file diff --git a/MPIViewers/src/3DViewer/3DViewerWidget.jl b/MPIViewers/src/3DViewer/3DViewerWidget.jl new file mode 100644 index 0000000..d2e136c --- /dev/null +++ b/MPIViewers/src/3DViewer/3DViewerWidget.jl @@ -0,0 +1,202 @@ +mutable struct DataViewer3DWidget{A, C} <: Gtk4.GtkGrid + handle::Ptr{Gtk4.GObject} + lscene::LScene + scene::Scene + axis::A + cam::C + gm::Gtk4Makie.GtkGLMakie + # Controls + controlGrid::GtkGrid + modeCb::GtkComboBoxText + modeOpt::GtkMenuButton + frameAdj::GtkAdjustment + channelAdj::GtkAdjustment + cmapCb::GtkComboBoxText + cmin::GtkScaleButton + cmax::GtkScaleButton + # Data + data::AbstractArray + # Mode + modes::Vector{<:Abstract3DViewerMode} +end + +function DataViewer3DWidget(; modes = [VolumeMode, SectionalMode, IsoSurfaceMode]) + grid = GtkGrid() + handle = grid.handle + + # Setup the controls + controls = GtkGrid() + controls.column_spacing = 5 + controls.row_spacing = 5 + + # Mode selection + # We have to initialize the modeBox and the options later + # Once the viewer exits we can initialize the modes with the viewer as parent + # And then fill out our options + modeBox = GtkComboBoxText() + controls[1, 1] = GtkLabel("Mode") + controls[1, 2] = modeBox + modeOptions = GtkMenuButton() + controls[2, 1] = GtkLabel("Options") + controls[2, 2] = modeOptions + controls[3, 1:2] = GtkSeparator(:v) + + # Frame/Time Selection + frameAdj = GtkAdjustment(1, 1, 1, 1, 1, 1) + frameSlider = GtkSpinButton(frameAdj, 1, 0) + controls[4, 1] = GtkLabel("Frames") + controls[4, 2] = frameSlider + channelAdj = GtkAdjustment(1, 1, 1, 1, 1, 1) + channelSlider = GtkSpinButton(channelAdj, 1, 0) + controls[5, 1] = GtkLabel("Channels") + controls[5, 2] = channelSlider + controls[6, 1:2] = GtkSeparator(:v) + + # Colormap Selection + colormapBox = GtkComboBoxText() + cmaps = important_cmaps() + foreach(cm -> push!(colormapBox, cm), cmaps) + controls[7, 1] = GtkLabel("Colormap") + controls[7, 2] = colormapBox + colormapBox.active = 5 # viridis + cmin = GtkScaleButton(0, 99, 1, ["audio-volume-low"]) + cmax = GtkScaleButton(1, 100, 1, ["audio-volume-high"]) + cmin.value = 0 + cmax.value = 100 + controls[8, 1] = GtkLabel("Min") + controls[8, 2] = cmin + controls[9, 1] = GtkLabel("Max") + controls[9, 2] = cmax + + grid[1, 1] = controls + + # Setup the 3D viewing widget + fig = Figure() + lscene = LScene(fig[1,1]) + scene = lscene.scene + cam = scene.camera_controls + axis = first(scene.plots) # Initial plot is an axis for LScene + gm = GtkMakieWidget() + push!(gm, fig) + + grid[1, 2] = gm + + viewer = DataViewer3DWidget(handle, lscene, scene, axis, cam, gm, controls, modeBox, modeOptions, frameAdj, channelAdj, colormapBox, cmin, cmax, [], Abstract3DViewerMode[]) + + # Initialize the modes + modes = map(mode -> mode(viewer), modes) + for mode in modes + push!(modeBox, modeName(mode)) + end + modeBox.active = 0 + modeOptions.popover = popover(first(modes)) + viewer.modes = modes + + initCallbacks(viewer) + + return viewer +end + +function initCallbacks(m::DataViewer3DWidget) + signal_connect(m.modeCb, "changed") do widget + foreach(mode -> mode.active = false, m.modes) + mode = m.modes[m.modeCb.active + 1] + mode.active = true + m.modeOpt.popover = popover(mode) + showData!(WidgetRedraw(), m) + end + + signal_connect(m.frameAdj, "value_changed") do widget + showData!(m) + end + signal_connect(m.channelAdj, "value_changed") do widget + showData!(m) + end + + signal_connect(m.cmapCb, "changed") do widget + showData!(m) + end + + signal_connect(m.cmin, "value_changed") do widget, val + showData!(m) + end + signal_connect(m.cmax, "value_changed") do widget, val + showData!(m) + end +end + + +function updateData!(m::DataViewer3DWidget, file::Union{MDFFile, String}) + imMeta = loadRecoData(file) + updateData!(m, imMeta) +end + +# TODO handle 3 and 4 dim ImageMeta +function updateData!(m::DataViewer3DWidget, imMeta::ImageMeta{T, 5}) where T + m.data = imMeta + m.frameAdj.upper = size(m.data, 5) + m.channelAdj.upper = size(m.data, 1) + map(mode -> updateData!(mode, m.data), m.modes) + showData!(WidgetRedraw(), m) +end + +function updateData!(m::DataViewer3DWidget, array::AbstractArray{T, 5}) where T + m.data = array + m.frameAdj.upper = size(m.data, 5) + m.channelAdj.upper = size(m.data, 1) + m.cmin = 0 + m.cmax = 100 + map(mode -> updateData!(mode, m.data), m.modes) + showData!(WidgetRedraw(), m) +end + +function prepareData(m::DataViewer3DWidget) + frame = round(Int64, m.frameAdj.value) + channel = round(Int64, m.channelAdj.value) + data = ustrip.(m.data[channel, :, :, :, frame]) + return data +end + +function prepareDrawKwargs(m::DataViewer3DWidget) + dict = Dict{Symbol, Any}() + max = maximum(m.data) + cmin = (m.cmin.value/100) * max + cmax = (m.cmax.value/100) * max + dict[:cparams] = ColoringParams(cmin, cmax, Gtk4.active_text(m.cmapCb)) + return dict +end + +showData!(m::DataViewer3DWidget) = showData!(redrawType(m.modes[m.modeCb.active + 1]), m) + +function showData!(::WidgetRedraw, m::DataViewer3DWidget) + # Prepare new GtkMakieWidget + delete!(m, m[1, 2]) + m.gm = GtkMakieWidget() + m[1, 2] = m.gm + + mode = m.modes[m.modeCb.active + 1] + data = prepareData(m) + kwargs = prepareDrawKwargs(m) + lscene = showData!(m.gm, mode, data; kwargs...) + + m.lscene = lscene + m.scene = lscene.scene + m.axis = first(m.scene.plots) + + # Set the camera to the old position + eyeposition = m.cam.eyeposition[] + upvector = m.cam.upvector[] + lookat = m.cam.lookat[] + m.cam = m.scene.camera_controls + update_cam!(m.scene, eyeposition, lookat, upvector) + return nothing +end + +function showData!(re::ObservableRedraw, m::DataViewer3DWidget) + # TODO move these into a function + mode = m.modes[m.modeCb.active + 1] + data = prepareData(m) + kwargs = prepareDrawKwargs(m) + showData!(re, m.lscene, mode, data; kwargs...) + return nothing +end diff --git a/MPIViewers/src/3DViewer/IsoSurfaceMode.jl b/MPIViewers/src/3DViewer/IsoSurfaceMode.jl new file mode 100644 index 0000000..aa74488 --- /dev/null +++ b/MPIViewers/src/3DViewer/IsoSurfaceMode.jl @@ -0,0 +1,77 @@ +export IsoSurfaceMode + + +mutable struct IsoSurfaceMode{P} <: Abstract3DViewerMode + pop::GtkPopover + parent::P + active::Bool + isoAdj::GtkAdjustment + isoMin::GtkLabel + isoMax::GtkLabel + isorange::GtkEntry +end +function IsoSurfaceMode(parent::P) where P + pop = GtkPopover() + grid = GtkGrid() + grid[1, 1] = GtkLabel("Iso Value:") + minLabel = GtkLabel("0.0") + maxLabel = GtkLabel("1.0") + adj = GtkAdjustment(0.5, 0.0, 1.0, 0.05, 0.05, 0.05) + scale = GtkScale(:h, adj) + scale.hexpand = true + grid[2, 1] = minLabel + grid[3, 1] = scale + grid[4, 1] = maxLabel + + grid[1, 2] = GtkLabel("Iso Range:") + range = GtkEntry() + range.text = "0.05" + grid[2:4, 2] = range + + + pop.child = grid + + mode = IsoSurfaceMode(pop, parent, false, adj, minLabel, maxLabel, range) + + initCallbacks!(mode) + + return mode +end + +function initCallbacks!(mode::IsoSurfaceMode) + signal_connect(mode.isoAdj, "value_changed") do widget + if mode.active + showData!(WidgetRedraw(), mode.parent) + end + end + signal_connect(mode.isorange, "changed") do widget + if mode.active + showData!(WidgetRedraw(), mode.parent) + end + end +end + +modeName(m::IsoSurfaceMode) = "Iso Surface" +popover(m::IsoSurfaceMode) = m.pop +redrawType(::IsoSurfaceMode) = WidgetRedraw() + + +function updateData!(m::IsoSurfaceMode, data) + min, max = extrema(data) + m.isoAdj.upper = max + m.isoAdj.lower = min + m.isoAdj.step_increment = (max - min) / 100 + m.isoMin.label = string(min) + m.isoMax.label = string(max) +end + +function showData!(re, scene::LScene, mode::IsoSurfaceMode, data; kwargs...) + showData!(re, scene.scene, mode, data; kwargs...) + return scene +end +function showData!(::WidgetRedraw, scene::Scene, mode::IsoSurfaceMode, data; kwargs...) + isovalue = mode.isoAdj.value + isoRange = parse(Float64, mode.isorange.text) + volume!(scene, data; algorithm=:iso, isovalue=isovalue, isorange=isoRange) + return scene +end diff --git a/MPIViewers/src/3DViewer/ObliqueSliceMode.jl b/MPIViewers/src/3DViewer/ObliqueSliceMode.jl new file mode 100644 index 0000000..e25c602 --- /dev/null +++ b/MPIViewers/src/3DViewer/ObliqueSliceMode.jl @@ -0,0 +1,74 @@ +export ObliqueSliceMode + +# Based on https://discourse.julialang.org/t/oblique-slices-in-makie/83879/12 + +mutable struct ObliqueSliceMode{P} <: Abstract3DViewerMode + pop::GtkPopover + parent::P + active::Bool + algBox::GtkComboBoxText + algorithms::Vector{Symbol} +end +function ObliqueSliceMode(parent::P) where P + algoStrings = ["Absorption", "Additive RGBA", "Absorption RGBA", "MIP"] + algoSymbols = [:absorption, :additive, :absorptionrgba, :mip] + box = GtkComboBoxText() + foreach(algo -> push!(box, algo), algoStrings) + box.active = 3 + + pop = GtkPopover() + grid = GtkGrid() + grid[1, 1] = GtkLabel("Algorithm") + grid[1, 2] = box + + pop.child = grid + + mode = ObliqueSliceMode(pop, parent, false, box, algoSymbols) + + initCallbacks!(mode) + + return mode +end + +function initCallbacks!(mode::ObliqueSliceMode) + signal_connect(mode.algBox, "changed") do widget + if mode.active + showData!(WidgetRedraw(), mode.parent) + end + end +end + +modeName(m::ObliqueSliceMode) = "Oblique Slice" +popover(m::ObliqueSliceMode) = m.pop + +function showData!(re, scene::LScene, mode::ObliqueSliceMode, data; kwargs...) + showData!(re, scene.scene, mode, data; kwargs...) + return scene +end +function showData!(::WidgetRedraw, scene::Scene, mode::ObliqueSliceMode, data; cparams = ColoringParams(extrema(data)..., "viridis"), kwargs...) + algo = mode.algorithms[mode.algBox.active + 1] + cmap = to_colormap(Symbol(cparams.cmap)) + cmap[1] = RGBA(0.0, 0.0, 0.0, 0.0) + volume!(scene, data; algorithm=algo, colormap=cmap, colorrange = (cparams.cmin, cparams.cmax)) + return scene +end +function showData!(::ObservableRedraw, scene::Scene, mode::ObliqueSliceMode, data; cparams = ColoringParams(extrema(data)..., "viridis"), kwargs...) + # TODO robust + plt = scene.plots[2] + cmap = to_colormap(Symbol(cparams.cmap)) + cmap[1] = RGBA(0.0, 0.0, 0.0, 0.0) + plt.colormap[] = cmap + plt.colorrange[] = (cparams.cmin, cparams.cmax) + plt[4][] = data + return scene +end + + +function distance_to_plane(P, point, normal) + return dot(P - point, normal) +end + +function isinplane(P, point, normal; rtol=1e-3) + d = distance_to_plane(P, point, normal) + return isapprox(1 + d, 1; rtol=rtol) # more precise when compare to 1 +end \ No newline at end of file diff --git a/MPIViewers/src/3DViewer/SectionalMode.jl b/MPIViewers/src/3DViewer/SectionalMode.jl new file mode 100644 index 0000000..764f0dc --- /dev/null +++ b/MPIViewers/src/3DViewer/SectionalMode.jl @@ -0,0 +1,121 @@ + +export SectionalMode + +mutable struct SectionalMode{P} <: Abstract3DViewerMode + pop::GtkPopover + parent::P + active::Bool + yzAdj::GtkAdjustment + xzAdj::GtkAdjustment + xyAdj::GtkAdjustment + yzToggler::GtkCheckButton + xzToggler::GtkCheckButton + xyToggler::GtkCheckButton +end + +function SectionalMode(parent::P) where P + pop = GtkPopover() + yzAdj = GtkAdjustment(1, 1, 1, 1, 1, 1) + xzAdj = GtkAdjustment(1, 1, 1, 1, 1, 1) + xyAdj = GtkAdjustment(1, 1, 1, 1, 1, 1) + yzToggler = GtkCheckButton("Visible") + xzToggler = GtkCheckButton("Visible") + xyToggler = GtkCheckButton("Visible") + + grid = GtkGrid() + grid[1, 1] = GtkLabel("xy") + grid[1, 2] = GtkSpinButton(xyAdj, 1, 0) + grid[1, 3] = xyToggler + + grid[2, 1] = GtkLabel("xz") + grid[2, 2] = GtkSpinButton(xzAdj, 1, 0) + grid[2, 3] = xzToggler + + grid[3, 1] = GtkLabel("yz") + grid[3, 2] = GtkSpinButton(yzAdj, 1, 0) + grid[3, 3] = yzToggler + + pop.child = grid + mode = SectionalMode(pop, parent, false, yzAdj, xzAdj, xyAdj, yzToggler, xzToggler, xyToggler) + + initCallbacks!(mode) + + return mode +end + +modeName(m::SectionalMode) = "Volume Slices" +popover(m::SectionalMode) = m.pop + +function initCallbacks!(mode::SectionalMode) + signal_connect(mode.yzAdj, "value_changed") do widget + if mode.active + showData!(ObservableRedraw(), mode.parent) + end + end + signal_connect(mode.xzAdj, "value_changed") do widget + if mode.active + showData!(ObservableRedraw(), mode.parent) + end + end + signal_connect(mode.xyAdj, "value_changed") do widget + if mode.active + showData!(ObservableRedraw(), mode.parent) + end + end + + signal_connect(mode.yzToggler, "toggled") do widget + if mode.active + showData!(ObservableRedraw(), mode.parent) + end + end + signal_connect(mode.xzToggler, "toggled") do widget + if mode.active + showData!(ObservableRedraw(), mode.parent) + end + end + signal_connect(mode.xyToggler, "toggled") do widget + if mode.active + showData!(ObservableRedraw(), mode.parent) + end + end +end + +function updateData!(m::SectionalMode, data5D::AbstractArray{T, 5}) where T + m.yzAdj.upper = size(data5D, 2) + m.xzAdj.upper = size(data5D, 3) + m.xyAdj.upper = size(data5D, 4) + m.yzToggler.active = true + m.xzToggler.active = true + m.xyToggler.active = true +end + +function showData!(re, scene::LScene, mode::SectionalMode, data; kwargs...) + showData!(re, scene.scene, mode, data; kwargs...) + return scene +end +function showData!(::WidgetRedraw, scene::Scene, mode::SectionalMode, data; cparams = ColoringParams(extrema(data)..., "viridis"), kwargs...) + cmap = to_colormap(Symbol(cparams.cmap)) + plt = volumeslices!(scene, map(i -> 1:i, size(data))..., data; bbox_visible = false, colormap=cmap, colorrange = (cparams.cmin, cparams.cmax)) + plt.heatmap_xy[].visible = Observable{Any}(true) + plt.heatmap_xz[].visible = Observable{Any}(true) + plt.heatmap_yz[].visible = Observable{Any}(true) + return scene +end +function showData!(::ObservableRedraw, scene::Scene, mode::SectionalMode, data; cparams = ColoringParams(extrema(data)..., "viridis"), kwargs...) + # TODO robust plot selection + cmap = to_colormap(Symbol(cparams.cmap)) + plt = scene.plots[2] + plt[1] = 1:size(data, 1) + plt[2] = 1:size(data, 2) + plt[3] = 1:size(data, 3) + plt[4] = data + plt.update_xz[](Int64(mode.xzAdj.value)) + plt.update_xy[](Int64(mode.xyAdj.value)) + plt.update_yz[](Int64(mode.yzAdj.value)) + plt.heatmap_xy[].visible[] = mode.xyToggler.active + plt.heatmap_xz[].visible[] = mode.xzToggler.active + plt.heatmap_yz[].visible[] = mode.yzToggler.active + plt.colormap[] = cmap + plt.colorrange[] = (cparams.cmin, cparams.cmax) + return scene +end \ No newline at end of file diff --git a/MPIViewers/src/3DViewer/VolumeMode.jl b/MPIViewers/src/3DViewer/VolumeMode.jl new file mode 100644 index 0000000..ae869fb --- /dev/null +++ b/MPIViewers/src/3DViewer/VolumeMode.jl @@ -0,0 +1,63 @@ +export VolumeMode + + +mutable struct VolumeMode{P} <: Abstract3DViewerMode + pop::GtkPopover + parent::P + active::Bool + algBox::GtkComboBoxText + algorithms::Vector{Symbol} +end +function VolumeMode(parent::P) where P + algoStrings = ["Absorption", "Additive RGBA", "Absorption RGBA", "MIP"] + algoSymbols = [:absorption, :additive, :absorptionrgba, :mip] + box = GtkComboBoxText() + foreach(algo -> push!(box, algo), algoStrings) + box.active = 3 + + pop = GtkPopover() + grid = GtkGrid() + grid[1, 1] = GtkLabel("Algorithm") + grid[1, 2] = box + + pop.child = grid + + mode = VolumeMode(pop, parent, false, box, algoSymbols) + + initCallbacks!(mode) + + return mode +end + +function initCallbacks!(mode::VolumeMode) + signal_connect(mode.algBox, "changed") do widget + if mode.active + showData!(WidgetRedraw(), mode.parent) + end + end +end + +modeName(m::VolumeMode) = "Volume" +popover(m::VolumeMode) = m.pop + +function showData!(re, scene::LScene, mode::VolumeMode, data; kwargs...) + showData!(re, scene.scene, mode, data; kwargs...) + return scene +end +function showData!(::WidgetRedraw, scene::Scene, mode::VolumeMode, data; cparams = ColoringParams(extrema(data)..., "viridis"), kwargs...) + algo = mode.algorithms[mode.algBox.active + 1] + cmap = to_colormap(Symbol(cparams.cmap)) + cmap[1] = RGBA(0.0, 0.0, 0.0, 0.0) + volume!(scene, data; algorithm=algo, colormap=cmap, colorrange = (cparams.cmin, cparams.cmax)) + return scene +end +function showData!(::ObservableRedraw, scene::Scene, mode::VolumeMode, data; cparams = ColoringParams(extrema(data)..., "viridis"), kwargs...) + # TODO robust + plt = scene.plots[2] + cmap = to_colormap(Symbol(cparams.cmap)) + cmap[1] = RGBA(0.0, 0.0, 0.0, 0.0) + plt.colormap[] = cmap + plt.colorrange[] = (cparams.cmin, cparams.cmax) + plt[4][] = data + return scene +end diff --git a/MPIViewers/src/BaseViewer.jl b/MPIViewers/src/BaseViewer.jl new file mode 100644 index 0000000..dd1adef --- /dev/null +++ b/MPIViewers/src/BaseViewer.jl @@ -0,0 +1,55 @@ +export baseViewer, baseViewerStandAlone, updateView, drawMIP + +mutable struct BaseViewerWidget + builder + zxSliceGrid + zySliceGrid + yxSliceGrid +end + + +function baseViewerStandAlone() + w = Window("Base Viewer",800,600) + set_gtk_property!(w,:hexpand,true) + set_gtk_property!(w,:vexpand,true) + m, bv = baseViewer() + push!(w, bv) + show(w) + return w,m,bv +end + +getindex(m::BaseViewerWidget, w::AbstractString) = Gtk4.G_.get_object(m.builder, w) + +function baseViewer() + uifile = joinpath(@__DIR__,"builder","baseViewer.ui") + b = GtkBuilder(uifile) + m = BaseViewerWidget(b, nothing,nothing,nothing) + w = m["parentGrid"] + m.zxSliceGrid = m["zxSlice"] + m.zySliceGrid = m["zySlice"] + m.yxSliceGrid = m["yxSlice"] + m.zxSliceGrid[1,1] = GtkCanvas() + m.zySliceGrid[1,1] = GtkCanvas() + m.yxSliceGrid[1,1] = GtkCanvas() + + + + return m, w +end + +function updateView(m::BaseViewerWidget,zx,zy,yx) + drawMIP(m.zxSliceGrid[1,1],m.zySliceGrid[1,1],m.yxSliceGrid[1,1],zx,zy,yx) +end + +function drawMIP(controlzx,controlzy,controlyx,zx,zy,yx) + drawSlice(controlzx, zx) + drawSlice(controlzy, zy) + drawSlice(controlyx, yx) +end + +function drawSlice(control, slice) + @guarded Gtk4.draw(control) do widget + ctx = getgc(control) + copy!(ctx, slice) + end +end diff --git a/MPIViewers/src/DataViewer/DataViewer.jl b/MPIViewers/src/DataViewer/DataViewer.jl new file mode 100644 index 0000000..23facdb --- /dev/null +++ b/MPIViewers/src/DataViewer/DataViewer.jl @@ -0,0 +1,620 @@ +using Gtk4 + +export DataViewer, DataViewerWidget + +########### DataViewerWidget ################# + +mutable struct DataViewerWidget <: Gtk4.GtkBox + handle::Ptr{Gtk4.GObject} + builder::GtkBuilder + grid2D::Gtk4.GtkGridLeaf + grid3D::Gtk4.GtkGridLeaf + coloring::Vector{ColoringParams} + upgradeColoringWInProgress::Bool + cacheSelectedFovXYZ::Array{Float64,1} + cacheSelectedMovePos::Array{Float64,1} + stopPlayingMovie::Bool + updating::Bool + data + dataBG + dataBGNotPermuted + currentlyShownImages + currentlyShownData + currentProfile +end + +getindex(m::DataViewerWidget, w::AbstractString) = Gtk4.G_.get_object(m.builder, w) + +mutable struct DataViewer + w::Gtk4.GtkWindowLeaf + dvw::DataViewerWidget +end + +function DataViewer(imFG::ImageMeta, imBG=nothing; params=nothing) + dv = DataViewer() + updateData!(dv.dvw,imFG,imBG) + if params!=nothing + setParams(dv.dvw,params) + end + return dv +end + +function DataViewer() + w = GtkWindow("Data Viewer",800,600) + dw = DataViewerWidget() + push!(w,dw) + show(w) + + #=signal_connect(w, "key-press-event") do widget, event + if event.keyval == Gtk4.GConstants.GDK_KEY_c + if event.state & 0x04 != 0x00 # Control key is pressed + @debug "copy visu params to clipboard..." + str = string( getParams(dw) ) + str_ = replace(str,",Pair",",\n Pair") + clipboard( str_ ) + end + end + end=# + + return DataViewer(w,dw) +end + +include("Export.jl") +include("Drawing.jl") + +function DataViewerWidget() + + uifile = joinpath(@__DIR__,"..","builder","dataviewer.ui") + + b = GtkBuilder(uifile) + mainBox = Gtk4.G_.get_object(b, "boxDataViewer") + m = DataViewerWidget( mainBox.handle, b, + Gtk4.G_.get_object(b, "gridDataViewer2D"), + Gtk4.G_.get_object(b, "gridDataViewer3D"), + Vector{ColoringParams}(), false, + [0.0,0.0,0.0], [0.0,0.0,0.0], false, false, + nothing, nothing, nothing,nothing, nothing, nothing) + Gtk4.GLib.gobject_move_ref(m, mainBox) + + m.grid3D[2,1] = GtkCanvas() + m.grid3D[1,1] = GtkCanvas() + m.grid3D[2,2] = GtkCanvas() + m.grid3D[1,2] = GtkCanvas() + m.grid2D[1,1] = GtkCanvas() + + show(m) + + choices = important_cmaps() + for c in choices + push!(m["cbCMaps"], c) + push!(m["cbCMapsBG"], c) + end + set_gtk_property!(m["cbCMaps"],:active,5) # default: viridis + set_gtk_property!(m["cbCMapsBG"],:active,0) # default: gray + + permutes = permuteCombinationsName() + for c in permutes + push!(m["cbPermutes"], c) + end + set_gtk_property!(m["cbPermutes"], :active, 0) + + flippings = flippingsName() + for c in flippings + push!(m["cbFlips"], c) + end + set_gtk_property!(m["cbFlips"], :active, 0) + + choicesFrameProj = ["None", "MIP", "TTP"] + for c in choicesFrameProj + push!(m["cbFrameProj"], c) + end + set_gtk_property!(m["cbFrameProj"],:active,0) + + visible(m["mbFusion"], false) + visible(m["lbFusion"], false) + visible(m["sepFusion"], false) + + signal_connect(m["btnPlayMovie"], "toggled") do widget + isActive = get_gtk_property(m["btnPlayMovie"], :active, Bool) + if isActive + m.stopPlayingMovie = false + @idle_add_guarded playMovie() + else + m.stopPlayingMovie = true + end + end + + function playMovie() + @async begin + L = get_gtk_property(m["adjFrames"],:upper, Int64) + curFrame = get_gtk_property(m["adjFrames"],:value, Int64) + while !m.stopPlayingMovie + @idle_add_guarded set_gtk_property!(m["adjFrames"],:value, curFrame) + yield() + sleep(0.01) + curFrame = mod1(curFrame+1,L) + end + m.stopPlayingMovie = false + end + end + + initCallbacks(m) + + return m +end + +function initCallbacks(m_::DataViewerWidget) + let m=m_ + + function update( widget ) + if !m.upgradeColoringWInProgress + updateColoring( m ) + showData( m ) + end + end + + function updateChan( widget ) + updateColoringWidgets( m ) + update( m ) + end + + widgets = ["cbSpatialMIP", "cbShowSlices", "cbHideFG", "cbHideBG", + "cbBlendChannels", "cbTranslucentBlending", + "cbSpatialBGMIP", "cbShowDFFOV", "cbComplexBlending", + "cbShowAxes"] + for w in widgets + signal_connect(update, m[w], "toggled") + end + + widgets = ["adjFrames", "adjSliceX", "adjSliceY","adjSliceZ", + "adjCMin", "adjCMax", "adjCMinBG", "adjCMaxBG", + "adjTransX", "adjTransY", "adjTransZ", + "adjRotX", "adjRotY", "adjRotZ", + "adjTransBGX", "adjTransBGY", "adjTransBGZ", + "adjRotBGX", "adjRotBGY", "adjRotBGZ", + "adjTTPThresh"] + for w in widgets + signal_connect(update, m[w], "value_changed") + end + + widgets = ["cbCMaps", "cbCMapsBG", "cbFrameProj"] + for w in widgets + signal_connect(update, m[w], "changed") + end + + signal_connect(updateChan, m["cbChannel"], "changed") + + signal_connect(m["cbPermutes"], "changed") do widget + try + m.updating = true + permuteBGData(m) + updateSliceWidgets(m) + m.updating = false + update(m) + catch e + @error e + showError(e) + end + end + + signal_connect(m["cbFlips"], "changed") do widget + try + m.updating = true + permuteBGData(m) + updateSliceWidgets(m) + m.updating = false + update(m) + catch e + @error e + showError(e) + end + end + + signal_connect(m["btnSaveVisu"], "clicked") do widget + try + params = getParams(m) + # Need to convert the coloring into the old Int format + coloring = params[:coloring] + + coloringInt = ColoringParamsInt[] + for c in coloring + idx = findfirst(a->a==c.cmap,important_cmaps())-1 + push!(coloringInt, ColoringParamsInt(c.cmin, c.cmax, idx)) + end + params[:coloring] = coloringInt + + c = params[:coloringBG] + idx = findfirst(a->a==c.cmap,important_cmaps())-1 + params[:coloringBG] = ColoringParamsInt(c.cmin, c.cmax, idx) + + addVisu(mpilab[], params) + catch e + showError(e) + end + end + + initExportCallbacks(m) + end +end + +function permuteBGData(m::DataViewerWidget) + if m.dataBGNotPermuted != nothing + permInd = get_gtk_property(m["cbPermutes"], :active, Int64) + 1 + flipInd = get_gtk_property(m["cbFlips"], :active, Int64) + 1 + perm = permuteCombinations()[permInd] + flip = flippings()[flipInd] + m.dataBG = applyPermutions(m.dataBGNotPermuted, perm, flip) + + fov = collect(size(m.dataBG)).*collect(converttometer(pixelspacing(m.dataBG))).*1000 + + set_gtk_property!(m["adjTransX"],:lower,-fov[1]/2) + set_gtk_property!(m["adjTransY"],:lower,-fov[2]/2) + set_gtk_property!(m["adjTransZ"],:lower,-fov[3]/2) + set_gtk_property!(m["adjTransX"],:upper,fov[1]/2) + set_gtk_property!(m["adjTransY"],:upper,fov[2]/2) + set_gtk_property!(m["adjTransZ"],:upper,fov[3]/2) + + set_gtk_property!(m["adjTransBGX"],:lower,-fov[1]/2) + set_gtk_property!(m["adjTransBGY"],:lower,-fov[2]/2) + set_gtk_property!(m["adjTransBGZ"],:lower,-fov[3]/2) + set_gtk_property!(m["adjTransBGX"],:upper,fov[1]/2) + set_gtk_property!(m["adjTransBGY"],:upper,fov[2]/2) + set_gtk_property!(m["adjTransBGZ"],:upper,fov[3]/2) + end +end + +function updateSliceWidgets(m::DataViewerWidget) + + sfw = get_gtk_property(m["adjFrames"],:upper,Int64) + sxw = get_gtk_property(m["adjSliceX"],:upper,Int64) + syw = get_gtk_property(m["adjSliceY"],:upper,Int64) + szw = get_gtk_property(m["adjSliceZ"],:upper,Int64) + refdata = (m.dataBG == nothing) ? m.data[1,:,:,:,:] : m.dataBG + + if refdata != nothing + + if size(refdata,4) != sfw + @idle_add_guarded set_gtk_property!(m["adjFrames"],:value, 1) + @idle_add_guarded set_gtk_property!(m["adjFrames"],:upper,size(m.data,Axis{:time})) + end + + if size(refdata,1) != sxw || size(refdata,2) != syw || size(refdata,3) != szw + @idle_add_guarded set_gtk_property!(m["adjSliceX"],:upper,size(refdata,1)) + @idle_add_guarded set_gtk_property!(m["adjSliceY"],:upper,size(refdata,2)) + @idle_add_guarded set_gtk_property!(m["adjSliceZ"],:upper,size(refdata,3)) + + @idle_add_guarded set_gtk_property!(m["adjSliceX"],:value,max(div(size(refdata,1),2),1)) + @idle_add_guarded set_gtk_property!(m["adjSliceY"],:value,max(div(size(refdata,2),2),1)) + @idle_add_guarded set_gtk_property!(m["adjSliceZ"],:value,max(div(size(refdata,3),2),1)) + end + end + return +end + +function updateData!(m::DataViewerWidget, data::ImageMeta{T,3}, dataBG=nothing; kargs...) where T + ax = ImageUtils.AxisArrays.axes(data) + dAx = AxisArray(reshape(data.data.data, 1, size(data,1), size(data,2), size(data,3), 1), + Axis{:color}(1:1), ax[1], ax[2], ax[3], + Axis{:time}(range(0*unit(1u"s"),step=1u"s",length=1))) + dIm = copyproperties(data, dAx) + updateData!(m, dIm, dataBG; kargs...) +end + +function updateData!(m::DataViewerWidget, data::ImageMeta{T,4}, dataBG=nothing; kargs...) where T + ax = ImageUtils.AxisArrays.axes(data) + + if timeaxis(data) == nothing + dAx = AxisArray(reshape(data.data.data, size(data,1), size(data,2), + size(data,3), size(data,4), 1), + ax[1], ax[2], ax[3], ax[4], + Axis{:time}(range(0*unit(1u"s"),step=1u"s",length=1))) + else + dAx = AxisArray(reshape(data.data.data, 1, size(data,1), size(data,2), + size(data,3), size(data,4)), + Axis{:color}(1:1), ax[1], ax[2], ax[3], ax[4]) + end + + dIm = copyproperties(data, dAx) + updateData!(m, dIm, dataBG; kargs...) +end + +function updateData!(m::DataViewerWidget, data::ImageMeta{T,5}, dataBG=nothing; params=nothing, ampPhase=false) where T + #try + m.updating = true + numChan = size(data,ImageUtils.Axis{:color}) + + visible(m["mbFusion"], dataBG != nothing) + visible(m["lbFusion"], dataBG != nothing) + visible(m["sepFusion"], dataBG != nothing) + + multiChannel = numChan > 1 + visible(m["cbBlendChannels"], multiChannel) + visible(m["cbChannel"], multiChannel) + visible(m["lbChannel"], multiChannel) + + + if m.data == nothing || (size(m.data,Axis{:color}) != numChan) + m.coloring = Array{ColoringParams}(undef,numChan) + if numChan == 1 + m.coloring[1] = ColoringParams(0.0,1.0,"viridis") + end + if numChan > 2 + for l=1:numChan + m.coloring[l] = ColoringParams(0.0,1.0,l) + end + end + if numChan == 2 + if ampPhase + m.coloring[1] = ColoringParams(0.0,1.0,"viridis") + m.coloring[2] = ColoringParams(0.0,1.0,"viridis") + else + m.coloring[1] = ColoringParams(0.0,1.0,"blue") + m.coloring[2] = ColoringParams(0.0,1.0,"red") + end + end + + @idle_add_guarded begin + empty!(m["cbChannel"]) + for i=1:numChan + push!(m["cbChannel"], "$i") + end + set_gtk_property!(m["cbChannel"],:active,0) + updateColoringWidgets( m ) + end + + @idle_add_guarded begin + strProfile = ["x direction", "y direction", "z direction","temporal"] + empty!(m["cbProfile"]) + nd = size(data,5) > 1 ? 4 : 3 + for i=1:nd + push!(m["cbProfile"], strProfile[i]) + end + set_gtk_property!(m["cbProfile"],:active,nd==4 ? 3 : 0) + end + + set_gtk_property!(m["cbBlendChannels"], :active, numChan>1 ) + set_gtk_property!(m["cbComplexBlending"], :active, ampPhase) + end + + m.data = data + + m.dataBGNotPermuted = dataBG + m.dataBG = nothing + permuteBGData(m) + updateSliceWidgets(m) + m.updating = false + showData(m) + @idle_add_guarded set_gtk_property!(m["adjPixelResizeFactor"],:value, (dataBG==nothing) ? 5 : 1 ) + + if params!=nothing + if data != nothing + params[:sliceX] = min(params[:sliceX],size(data,Axis{:x})) + params[:sliceY] = min(params[:sliceY],size(data,Axis{:y})) + params[:sliceZ] = min(params[:sliceZ],size(data,Axis{:z})) + end + setParams(m,params) + end + #catch ex + # @warn "Exception" ex stacktrace(catch_backtrace()) + #end +end + +function updateColoringWidgets(m::DataViewerWidget) + m.upgradeColoringWInProgress = true + chan = max(get_gtk_property(m["cbChannel"],:active, Int64) + 1,1) + @idle_add_guarded set_gtk_property!(m["adjCMin"],:value, m.coloring[chan].cmin) + @idle_add_guarded set_gtk_property!(m["adjCMax"],:value, m.coloring[chan].cmax) + idx = findfirst(a->a==m.coloring[chan].cmap,important_cmaps())-1 + @idle_add_guarded set_gtk_property!(m["cbCMaps"],:active, idx) + m.upgradeColoringWInProgress = false +end + +function updateColoring(m::DataViewerWidget) + chan = max(get_gtk_property(m["cbChannel"],:active, Int64) + 1,1) + cmin = get_gtk_property(m["adjCMin"],:value, Float64) + cmax = get_gtk_property(m["adjCMax"],:value, Float64) + cmap = important_cmaps()[get_gtk_property(m["cbCMaps"],:active, Int64)+1] + m.coloring[chan] = ColoringParams(cmin,cmax,cmap) +end + +function showData(m::DataViewerWidget) + if !m.updating + try + params = getParams(m) + if m.data != nothing + + data_ = sliceTimeDim(m.data, params[:frameProj] == 1 ? "MIP" : params[:frame]) + + slices = (params[:sliceX],params[:sliceY],params[:sliceZ]) + + if params[:spatialMIP] + proj = "MIP" + else + proj = slices + end + + # global windowing + maxval = [maximum(sliceColorDim(m.data,d)) for d=1:size(m.data,1)] + minval = [minimum(sliceColorDim(m.data,d)) for d=1:size(m.data,1)] + #if params[:frameProj] == 2 && ndims(m.data[1]) == 4 + # maxval = [maximum(d) for d in data_] + # minval = [minimum(d) for d in data_] + #end + + if m.dataBG != nothing + slicesInRawData = ImageUtils.indexFromBGToFG(m.dataBG, data_, params) + @debug "Slices in raw data:" slicesInRawData + data = interpolateToRefImage(m.dataBG, data_, params) + dataBG = interpolateToRefImage(m.dataBG, params) + else + # not ideal .... + params[:sliceX] = min(params[:sliceX],size(m.data,Axis{:x})) + params[:sliceY] = min(params[:sliceY],size(m.data,Axis{:y})) + params[:sliceZ] = min(params[:sliceZ],size(m.data,Axis{:z})) + + data = data_ + slicesInRawData = slices + dataBG = nothing + end + + m.currentlyShownData = data + + if ndims(squeeze(data[1,:,:,:])) >= 2 + cdata_zx, cdata_zy, cdata_xy = getColoredSlices(data, dataBG, m.coloring, minval, maxval, params) + isDrawSectionalLines = params[:showSlices] && proj != "MIP" + isDrawRectangle = params[:showDFFOV] + isDrawAxes = params[:showAxes] + pixelSpacingBG = (dataBG==nothing) ? [0.002,0.002,0.001] : collect(converttometer(pixelspacing(dataBG))) + sizeBG = (dataBG==nothing) ? [128,128,64] : collect(size(dataBG)) + drawImages(m,slices, isDrawSectionalLines, isDrawRectangle, isDrawAxes, cdata_zx, cdata_zy, cdata_xy, + [params[:transX], params[:transY], params[:transZ]], pixelSpacingBG, sizeBG) + + if ndims(m.data) >= 3 && slicesInRawData != (0,0,0) + showProfile(m, params, slicesInRawData) + end + Gtk4.G_.set_current_page(m["nb2D3D"], 0) + m.currentlyShownImages = [cdata_xy, cdata_zx, cdata_zy] + else + dat = vec(data_) + + fig, ax, l_ = CairoMakie.lines(1:length(dat), dat, + figure = (; size = (1000, 800), fontsize = 12), + color = CairoMakie.RGBf(colors[1]...)) + + CairoMakie.autolimits!(ax) + if length(dat) > 1 + CairoMakie.xlims!(ax, 1, length(dat)) + end + ax.xlabel = "x" + ax.ylabel = "y" + drawonto(m.grid2D[1,1], fig) + + Gtk4.G_.set_current_page(m["nb2D3D"], 1) + + #m.currentlyShownImages = cdata + end + end + catch ex + @info ex + showError(ex) + # @warn "Exception" ex stacktrace(catch_backtrace()) + end + end +end + +function getParams(m::DataViewerWidget) + params = defaultVisuParams() + params[:sliceX] = get_gtk_property(m["adjSliceX"], :value, Int64) + params[:sliceY] = get_gtk_property(m["adjSliceY"], :value, Int64) + params[:sliceZ] = get_gtk_property(m["adjSliceZ"], :value, Int64) + params[:frame] = get_gtk_property(m["adjFrames"], :value, Int64) + params[:spatialMIP] = get_gtk_property(m["cbSpatialMIP"], :active, Bool) + params[:coloring] = m.coloring + params[:description] = get_gtk_property(m["entVisuName"], :text, String) + params[:showSlices] = get_gtk_property(m["cbShowSlices"], :active, Bool) + + params[:permuteBG] = permuteCombinations()[get_gtk_property(m["cbPermutes"], :active, Int64) + 1] + params[:flipBG] = flippings()[get_gtk_property(m["cbFlips"], :active, Int64) + 1] + + params[:transX] = get_gtk_property(m["adjTransX"], :value, Float64) / 1000 + params[:transY] = get_gtk_property(m["adjTransY"], :value, Float64) / 1000 + params[:transZ] = get_gtk_property(m["adjTransZ"], :value, Float64) / 1000 + params[:rotX] = get_gtk_property(m["adjRotX"], :value, Float64) + params[:rotY] = get_gtk_property(m["adjRotY"], :value, Float64) + params[:rotZ] = get_gtk_property(m["adjRotZ"], :value, Float64) + params[:transBGX] = get_gtk_property(m["adjTransBGX"], :value, Float64) / 1000 + params[:transBGY] = get_gtk_property(m["adjTransBGY"], :value, Float64) / 1000 + params[:transBGZ] = get_gtk_property(m["adjTransBGZ"], :value, Float64) / 1000 + params[:rotBGX] = get_gtk_property(m["adjRotBGX"], :value, Float64) + params[:rotBGY] = get_gtk_property(m["adjRotBGY"], :value, Float64) + params[:rotBGZ] = get_gtk_property(m["adjRotBGZ"], :value, Float64) + params[:coloringBG] = ColoringParams(get_gtk_property(m["adjCMinBG"], :value, Float64), + get_gtk_property(m["adjCMaxBG"], :value, Float64), + get_gtk_property(m["cbCMapsBG"], :active, Int64)) + + params[:filenameBG] = (m.dataBGNotPermuted != nothing) && haskey(m.dataBGNotPermuted, "filename") ? m.dataBGNotPermuted["filename"] : "" + + params[:hideFG] = get_gtk_property(m["cbHideFG"], :active, Bool) + params[:hideBG] = get_gtk_property(m["cbHideBG"], :active, Bool) + params[:showDFFOV] = get_gtk_property(m["cbShowDFFOV"], :active, Bool) + params[:translucentBlending] = get_gtk_property(m["cbTranslucentBlending"], :active, Bool) + params[:spatialMIPBG] = get_gtk_property(m["cbSpatialBGMIP"], :active, Bool) + + + params[:TTPThresh] = get_gtk_property(m["adjTTPThresh"], :value, Float64) + params[:frameProj] = get_gtk_property(m["cbFrameProj"], :active, Int64) + + params[:blendChannels] = get_gtk_property(m["cbBlendChannels"], :active, Bool) + params[:complexBlending] = get_gtk_property(m["cbComplexBlending"], :active, Bool) + params[:showAxes] = get_gtk_property(m["cbShowAxes"], :active, Bool) + + params[:profile] = get_gtk_property(m["cbProfile"], :active, Int64) + + params[:activeChannel] = max(get_gtk_property(m["cbChannel"],:active, Int64) + 1,1) + + return params +end + +function setParams(m::DataViewerWidget, params) + @idle_add_guarded set_gtk_property!(m["adjSliceX"], :value, params[:sliceX]) + @idle_add_guarded set_gtk_property!(m["adjSliceY"], :value, params[:sliceY]) + @idle_add_guarded set_gtk_property!(m["adjSliceZ"], :value, params[:sliceZ]) + @idle_add_guarded set_gtk_property!(m["adjFrames"], :value, params[:frame]) + @idle_add_guarded set_gtk_property!(m["cbSpatialMIP"], :active, params[:spatialMIP]) + m.coloring = Vector{ColoringParams}(undef,0) + for col in params[:coloring] + push!(m.coloring, ColoringParams(col.cmin, col.cmax, col.cmap)) + end + updateColoringWidgets(m) + @idle_add_guarded set_gtk_property!(m["entVisuName"], :text, params[:description]) + @idle_add_guarded set_gtk_property!(m["cbShowSlices"], :active, get(params,:showSlices,false)) + + # The following is for backwards compatibility with a former data format + # where instead of permuteBG and flipBG we stored permutionBG + if haskey(params, :permutionBG) + perm, flip = convertMode2PermFlip(params[:permutionBG]) + @idle_add_guarded set_gtk_property!(m["cbPermutes"], :active, findall(x->x==perm,permuteCombinations())[1] - 1) + @idle_add_guarded set_gtk_property!(m["cbFlips"], :active, findall(x->x==flip,flippings())[1] - 1) + else + @idle_add_guarded set_gtk_property!(m["cbPermutes"], :active, findall(x->x==params[:permuteBG],permuteCombinations())[1] - 1) + @idle_add_guarded set_gtk_property!(m["cbFlips"], :active, findall(x->x==params[:flipBG],flippings())[1] - 1) + end + + @idle_add_guarded set_gtk_property!(m["adjTransX"], :value, params[:transX]*1000) + @idle_add_guarded set_gtk_property!(m["adjTransY"], :value, params[:transY]*1000) + @idle_add_guarded set_gtk_property!(m["adjTransZ"], :value, params[:transZ]*1000) + @idle_add_guarded set_gtk_property!(m["adjRotX"], :value, params[:rotX]) + @idle_add_guarded set_gtk_property!(m["adjRotY"], :value, params[:rotY]) + @idle_add_guarded set_gtk_property!(m["adjRotZ"], :value, params[:rotZ]) + + @idle_add_guarded set_gtk_property!(m["adjTransBGX"], :value, get(params,:transBGX,0.0)*1000) + @idle_add_guarded set_gtk_property!(m["adjTransBGY"], :value, get(params,:transBGY,0.0)*1000) + @idle_add_guarded set_gtk_property!(m["adjTransBGZ"], :value, get(params,:transBGZ,0.0)*1000) + @idle_add_guarded set_gtk_property!(m["adjRotBGX"], :value, get(params,:rotBGX,0.0)) + @idle_add_guarded set_gtk_property!(m["adjRotBGY"], :value, get(params,:rotBGY,0.0)) + @idle_add_guarded set_gtk_property!(m["adjRotBGZ"], :value, get(params,:rotBGZ,0.0)) + + @idle_add_guarded set_gtk_property!(m["adjCMinBG"], :value, params[:coloringBG].cmin) + @idle_add_guarded set_gtk_property!(m["adjCMaxBG"], :value, params[:coloringBG].cmax) + @idle_add_guarded set_gtk_property!(m["cbCMapsBG"], :active, params[:coloringBG].cmap) + @idle_add_guarded set_gtk_property!(m["cbHideFG"], :active, get(params,:hideFG, false)) + @idle_add_guarded set_gtk_property!(m["cbHideBG"], :active, get(params,:hideBG, false)) + @idle_add_guarded set_gtk_property!(m["cbShowDFFOV"], :active, get(params,:showDFFOV, false)) + @idle_add_guarded set_gtk_property!(m["cbTranslucentBlending"], :active, get(params,:translucentBlending, false)) + @idle_add_guarded set_gtk_property!(m["cbSpatialBGMIP"], :active, get(params,:spatialMIPBG, false)) + + @idle_add_guarded set_gtk_property!(m["adjTTPThresh"], :value, get(params,:TTPThresh, 0.4)) + @idle_add_guarded set_gtk_property!(m["cbFrameProj"], :active, get(params,:frameProj, 0)) + + @idle_add_guarded set_gtk_property!(m["cbBlendChannels"], :active, get(params,:blendChannels, false)) + @idle_add_guarded set_gtk_property!(m["cbShowAxes"], :active, get(params,:showAxes, false)) + + @idle_add_guarded set_gtk_property!(m["cbProfile"], :active, get(params,:profile, 0)) + + showData(m) +end + +function defaultVisuParams() + params = Dict{Symbol,Any}() + return params +end diff --git a/MPIViewers/src/DataViewer/Drawing.jl b/MPIViewers/src/DataViewer/Drawing.jl new file mode 100644 index 0000000..9d70ff0 --- /dev/null +++ b/MPIViewers/src/DataViewer/Drawing.jl @@ -0,0 +1,339 @@ + + +function getMetaDataSlices(m::DataViewerWidget) + ctxZY = Gtk4.getgc(m.grid3D[2,1]) + ctxZX = Gtk4.getgc(m.grid3D[1,1]) + ctxXY = Gtk4.getgc(m.grid3D[2,2]) + hZY = height(ctxZY) + wZY = width(ctxZY) + hZX = height(ctxZX) + wZX = width(ctxZX) + hXY = height(ctxXY) + wXY = width(ctxXY) + return ctxZY,ctxZX,ctxXY,hZY,wZY,hZX,wZX,hXY,wXY +end + +function getImSizes(cdata_zy,cdata_zx,cdata_xy) + imSizeZY = [size(cdata_zy)...]#flipdim([size(cdata_zy)...],1) + imSizeZX = [size(cdata_zx)...]#flipdim([size(cdata_zx)...],1) + imSizeXY = [size(cdata_xy)...]#flipdim([size(cdata_xy)...],1) + return imSizeZY,imSizeZX,imSizeXY +end + + +function cacheSelectedFovXYZPos(m::DataViewerWidget, cachePos::Array{Float64,1}, pixelSpacingBG, sizeBG, hX,wY,hZ) + m.cacheSelectedFovXYZ = cachePos + screenXYZtoBackgroundXYZ = (cachePos .-[hX/2,wY/2,hZ/2]) .* (sizeBG ./ [hX,wY,hZ]) + m.cacheSelectedMovePos = screenXYZtoBackgroundXYZ .* pixelSpacingBG + @debug "" cachePos screenXYZtoBackgroundXYZ m.cacheSelectedMovePos +end + + +function drawRectangle(ctx,h,w, p, imSize, xy, xyOffset;rgb=[0,1,0], lineWidth=3.0) + sFac = calcMeta(h,w, imSize) + set_source_rgb(ctx, rgb...) + createRectangle(ctx, p, sFac, xy, xyOffset) + set_line_width(ctx, lineWidth) + Cairo.stroke(ctx) +end + +function calcMeta(h,w,imSize) + cDA = [w/2,h/2] + cIA = [imSize[2]/2,imSize[1]/2] + sFac = cDA ./ cIA + @debug "" cIA sFac + return sFac +end + +function createRectangle(ctx, cDA, sFac, xy, xyOffset) + lowCX = cDA[1] - sFac[1] * xy[1]/2 + sFac[1] * xyOffset[1] + lowCY= cDA[2] - sFac[2] * xy[2]/2 + sFac[2] * xyOffset[2] + highCX =lowCX + sFac[1]*xy[1] + highCY =lowCY + sFac[2]*xy[2] + @debug "" lowCX lowCY highCX highCY + move_to(ctx, lowCX, lowCY) + line_to(ctx, highCX, lowCY) + move_to(ctx, lowCX, lowCY) + line_to(ctx, lowCX, highCY) + move_to(ctx, highCX, lowCY) + line_to(ctx, highCX, highCY) + move_to(ctx, lowCX, highCY) + line_to(ctx, highCX, highCY) +end + +function getSliceSizes(fov_mm, pixelSpacing) + fov_vox = fov_mm ./ pixelSpacing + xy = [fov_vox[2],fov_vox[1]] + xz = [fov_vox[1],fov_vox[3]] + yz = [fov_vox[2],fov_vox[3]] + return xy,xz,yz +end + + +function calcDFFovRectangle(m::DataViewerWidget, trans::Vector, pixelSpacingBG::Vector) + dfFov = getDFFov(m.currentlyShownData) + xy,xz,yz = getSliceSizes(dfFov, pixelSpacingBG) + @debug "" dfFov xy xz yz + offsetxy = ([trans[2], trans[1]])./([pixelSpacingBG[2],pixelSpacingBG[1]]) + offsetxz = ([-trans[1], -trans[3]])./([pixelSpacingBG[1],pixelSpacingBG[3]]) + offsetyz = ([trans[2], -trans[3]])./([pixelSpacingBG[2],pixelSpacingBG[3]]) + return xy,xz,yz,offsetxy,offsetxz,offsetyz +end + +function getDFFov(im::ImageMeta) + props = properties(im) + if haskey(props, "dfStrength") && haskey(props, "acqGradient") + dfS = squeeze(props["dfStrength"]) + acqGrad = squeeze(props["acqGradient"]) + acqGrad_ = zeros(size(acqGrad,2),size(acqGrad,3)) + for k=1:size(acqGrad,3) + acqGrad_[:,k]=diag(acqGrad[:,:,k]) + end + dfFov = abs.(2*(dfS./acqGrad_)) + else + dfFov = [0.05,0.05,0.025] # use better default... + #warn("using default dfFov: ",dfFov) + end + return dfFov +end + +function drawImages(m::DataViewerWidget,slices,isDrawSectionalLines,isDrawRectangle,isDrawAxes, + cdata_zx, cdata_zy, cdata_xy, trans, pixelSpacingBG, sizeBG) + + xy,zx,zy,offsetxy,offsetzx,offsetzy = calcDFFovRectangle(m, trans, pixelSpacingBG) + + drawSlice(m,slices,isDrawSectionalLines,isDrawRectangle,isDrawAxes, cdata_zx, cdata_zy, cdata_xy, xy,zx,zy,offsetxy,offsetzx,offsetzy) + + + g1 = GtkGestureClick(m.grid3D[1,1],3) + signal_connect(g1, "pressed") do controller, n_press, x, y + w = widget(controller) + @guarded Gtk4.draw(w) do widget + if isDrawRectangle + @debug "mouse event ZX" + ctxZY,ctxZX,ctxXY,hZY,wZY,hZX,wZX,hXY,wXY = getMetaDataSlices(m) + reveal(widget) + pZX = [x, y] + ZXtoXYforX = (wZX-pZX[1])/wZX *hXY # X coord in ZX width direction and in XY in height direction + cacheSelectedFovXYZPos(m, [ZXtoXYforX,m.cacheSelectedFovXYZ[2], pZX[2]], pixelSpacingBG, sizeBG, hXY,wZY,hZY) + @debug "cacheSelectedFovXYZ" m.cacheSelectedFovXYZ + pZY = [m.cacheSelectedFovXYZ[2], pZX[2]] + pXY = [m.cacheSelectedFovXYZ[2], ZXtoXYforX] + imSizeZY,imSizeZX,imSizeXY= getImSizes(cdata_zy,cdata_zx,cdata_xy) + @debug "" pZX zx offsetzy imSizeZY + drawSlice(m,slices,isDrawSectionalLines,isDrawRectangle,isDrawAxes, cdata_zx, cdata_zy, cdata_xy, xy,zx,zy,offsetxy,offsetzx,offsetzy) + drawRectangle(ctxZY, hZY,wZY, pZY, imSizeZY, zy, offsetzy, rgb=[1,0,0],lineWidth=3.0) + drawRectangle(ctxZX, hZX,wZX, pZX, imSizeZX, zx, offsetzx, rgb=[1,0,0],lineWidth=3.0) + drawRectangle(ctxXY, hXY,wXY, pXY, imSizeXY, xy, offsetxy, rgb=[1,0,0],lineWidth=3.0) + end + end + end + g2 = GtkGestureClick(m.grid3D[2,1],3) + signal_connect(g2, "pressed") do controller, n_press, x, y + w = widget(controller) + @guarded Gtk4.draw(w) do widget + if isDrawRectangle + @debug "mouse event ZY" + ctxZY,ctxZX,ctxXY,hZY,wZY,hZX,wZX,hXY,wXY = getMetaDataSlices(m) + reveal(widget) + pZY = [x, y] + cacheSelectedFovXYZPos(m, [m.cacheSelectedFovXYZ[1],pZY[1], pZY[2]], pixelSpacingBG, sizeBG, hXY,wZY,hZY) + @debug "" m.cacheSelectedFovXYZ + XYtoZXforX = wZX-(m.cacheSelectedFovXYZ[1]/hXY *wZX) # X coord in ZX width direction and in XY in height direction + pZX = [XYtoZXforX, pZY[2]] + pXY = [pZY[1],m.cacheSelectedFovXYZ[1]] + imSizeZY,imSizeZX,imSizeXY= getImSizes(cdata_zy,cdata_zx,cdata_xy) + @debug "" pZY zy offsetzy imSizeZY + drawSlice(m,slices,isDrawSectionalLines,isDrawRectangle,isDrawAxes, cdata_zx, cdata_zy, cdata_xy, xy,zx,zy,offsetxy,offsetzx,offsetzy) + drawRectangle(ctxZY, hZY,wZY, pZY, imSizeZY, zy, offsetzy, rgb=[1,0,0],lineWidth=3.0) + drawRectangle(ctxZX, hZX,wZX, pZX, imSizeZX, zx, offsetzx, rgb=[1,0,0],lineWidth=3.0) + drawRectangle(ctxXY, hXY,wXY, pXY, imSizeXY, xy, offsetxy, rgb=[1,0,0],lineWidth=3.0) + end + end + end + g3 = GtkGestureClick(m.grid3D[2,2],3) + signal_connect(g3, "pressed") do controller, n_press, x, y + w = widget(controller) + @guarded Gtk4.draw(w) do widget + if isDrawRectangle + @debug "mouse event XY" + ctxZY,ctxZX,ctxXY,hZY,wZY,hZX,wZX,hXY,wXY = getMetaDataSlices(m) + reveal(widget) + pXY = [x, y] + XYtoZXforX = wZX-(pXY[2]/hXY *wZX) # X coord in ZX width direction and in XY in height direction + cacheSelectedFovXYZPos(m, [pXY[2],pXY[1],m.cacheSelectedFovXYZ[3]], pixelSpacingBG, sizeBG, hXY,wZY,hZY) + @debug "" m.cacheSelectedFovXYZ + pZY = [pXY[1],m.cacheSelectedFovXYZ[3]] + pZX = [XYtoZXforX, m.cacheSelectedFovXYZ[3]] + imSizeZY,imSizeZX,imSizeXY= getImSizes(cdata_zy,cdata_zx,cdata_xy) + @debug pXY xy offsetzy imSizeZY + drawSlice(m,slices,isDrawSectionalLines,isDrawRectangle,isDrawAxes, cdata_zx, cdata_zy, cdata_xy, xy,zx,zy,offsetxy,offsetzx,offsetzy) + drawRectangle(ctxZY, hZY,wZY, pZY, imSizeZY, zy, offsetzy, rgb=[1,0,0],lineWidth=3.0) + drawRectangle(ctxZX, hZX,wZX, pZX, imSizeZX, zx, offsetzx, rgb=[1,0,0],lineWidth=3.0) + drawRectangle(ctxXY, hXY,wXY, pXY, imSizeXY, xy, offsetxy, rgb=[1,0,0],lineWidth=3.0) + end + end + end + + return nothing +end + +function drawSlice(m::DataViewerWidget,slices,isDrawSectionalLines,isDrawRectangle,isDrawAxes, cdata_zx, cdata_zy, cdata_xy, xy,zx,zy,offsetxy,offsetzx,offsetzy) + drawImageCairo(m.grid3D[2,1], cdata_zy, isDrawSectionalLines, isDrawAxes, + slices[2], slices[3], false, true, m["adjSliceY"], m["adjSliceZ"], isDrawRectangle,zy, offsetzy, "yz") + drawImageCairo(m.grid3D[1,1], cdata_zx, isDrawSectionalLines, isDrawAxes, + slices[1], slices[3], true, true, m["adjSliceX"], m["adjSliceZ"], isDrawRectangle,zx, offsetzx, "xz") + drawImageCairo(m.grid3D[2,2], cdata_xy, isDrawSectionalLines, isDrawAxes, + slices[2], slices[1], false, false, m["adjSliceY"], m["adjSliceX"], isDrawRectangle,xy, offsetxy, "xy") +end + +function drawImageCairo(c, image, isDrawSectionalLines, isDrawAxes, xsec, ysec, + flipX, flipY, adjX, adjY, isDrawRectangle, xy, xyOffset, slide) + @guarded Gtk4.draw(c) do widget + #c = reshape(c,size(c,1), size(c,2)) + ctx = getgc(c) + h = Gtk4.height(ctx) + w = Gtk4.width(ctx) + + im = copy(reverse(arraydata(convert(ImageMeta{RGB{N0f8}},image)),dims=1)) + xsec_ = !flipX ? xsec : (size(im,2)-xsec+1) + ysec_ = !flipY ? ysec : (size(im,1)-ysec+1) + xx = w*(xsec_-0.5)/size(im,2) + yy = h*(ysec_-0.5)/size(im,1) + copy!(ctx,im) + + if isDrawSectionalLines + set_source_rgb(ctx, 0, 1, 0) + move_to(ctx, xx, 0) + line_to(ctx, xx, h) + move_to(ctx, 0, yy) + line_to(ctx, w, yy) + #set_line_width(ctx, 3.0) + # Cairo.stroke(ctx) + end + imSize = size(im) + if isDrawRectangle + @debug "" imSize + drawRectangle(ctx,h,w,[w/2,h/2], imSize, xy, xyOffset) + end + if isDrawSectionalLines || isDrawRectangle + set_line_width(ctx, 3.0) + Cairo.stroke(ctx) + end + if isDrawAxes + drawAxes(ctx,slide) + set_line_width(ctx, 3.0) + Cairo.stroke(ctx) + end + end + + g = GtkGestureClick(c,1) + signal_connect(g, "pressed") do controller, n_press, x, y + w = widget(controller) + ctx = getgc(w) + reveal(w) + h = Gtk4.height(ctx) + w = Gtk4.width(ctx) + xx = x / w*size(image,2) + 0.5 + yy = y / h*size(image,1) + 0.5 + xx = !flipX ? xx : (size(image,2)-xx+1) + yy = !flipY ? yy : (size(image,1)-yy+1) + @idle_add_guarded set_gtk_property!(adjX, :value, round(Int64,xx)) + @idle_add_guarded set_gtk_property!(adjY, :value, round(Int64,yy)) + end + +end + +## Draw coordinate system +function drawAxes(ctx,slide; rgb=[1,1,1]) + h = Gtk4.height(ctx) + w = Gtk4.width(ctx) + + center = h/40 + if slide == "xy" + posx = center + posy = center + tox = [h/4, posy] + toy = [posx, h/4] + si = [-1,-1] + la = ["y","x"] + elseif slide == "xz" + posx = w-center + posy = h-center + tox = [posx-h/4, posy] + toy = [posx, posy-h/4] + si = [1, 1] + la = ["x","z"] + else # slide == "yz" + posx = center + posy = h-center + tox = [h/4, posy] + toy = [posx, posy-h/4] + si = [-1, 1] + la = ["y","z"] + end + + set_source_rgb(ctx, rgb...) + select_font_face(ctx, "Sans", Cairo.FONT_SLANT_NORMAL, + Cairo.FONT_WEIGHT_BOLD); + set_font_size(ctx, h/20); + scale = h/64 + # x-axis + move_to(ctx, posx, posy) + line_to(ctx, tox[1], tox[2]) + rel_move_to(ctx, si[1]*(2*scale), -scale) + line_to(ctx, tox[1], tox[2]) + rel_line_to(ctx, si[1]*(2*scale), scale) + # xlabel + extents = text_extents(ctx, la[1]) + move_to(ctx, tox[1] - (extents[3]/2 + extents[1]) - si[1]*(2*scale), tox[2]-(extents[4]/2 + extents[2])) + show_text(ctx,la[1]) + # y-axis + move_to(ctx, posx, posy) + line_to(ctx, toy[1], toy[2]) + rel_move_to(ctx, -scale, si[2]*(2*scale)) + line_to(ctx, toy[1], toy[2]) + rel_line_to(ctx, scale, si[2]*(2*scale)) + # ylabel + extents = text_extents(ctx, la[2]) + move_to(ctx, toy[1] - (extents[3]/2 + extents[1]), toy[2]-(extents[4]/2 + extents[2]) - si[2]*(2*scale)) + show_text(ctx,la[2]) +end + + +### Profile Plotting ### + + +function showProfile(m::DataViewerWidget, params, slicesInRawData) + chan = params[:activeChannel] + prof = get_gtk_property(m["cbProfile"],:active, Int64) + 1 + if prof == 1 + m.currentProfile = vec(m.data[chan,:,slicesInRawData[2],slicesInRawData[3],params[:frame]]) + showProfile(m, m.currentProfile, "x", "c") + elseif prof == 2 + m.currentProfile = vec(m.data[chan,slicesInRawData[1],:,slicesInRawData[3],params[:frame]]) + showProfile(m, m.currentProfile, "y", "c") + elseif prof == 3 + m.currentProfile = vec(m.data[chan,slicesInRawData[1],slicesInRawData[2],:,params[:frame]]) + showProfile(m, m.currentProfile, "z", "c") + else + m.currentProfile = vec(m.data[chan,slicesInRawData[1],slicesInRawData[2],slicesInRawData[3],:]) + showProfile(m, m.currentProfile, "t", "c") + end +end + +function showProfile(m::DataViewerWidget, data, xLabel::String, yLabel::String) + f, ax, l = CairoMakie.lines(1:length(data), data, + figure = (; size = (1000, 800), figure_padding=4, fontsize = 12), + axis = (; title = "Profile"), + color = CairoMakie.RGBf(colors[1]...)) + + CairoMakie.autolimits!(ax) + if length(data) > 1 + CairoMakie.xlims!(ax, 1, length(data)) + end + ax.xlabel = xLabel + ax.ylabel = yLabel + drawonto(m.grid3D[1,2], f) +end + diff --git a/MPIViewers/src/DataViewer/Export.jl b/MPIViewers/src/DataViewer/Export.jl new file mode 100644 index 0000000..55a8418 --- /dev/null +++ b/MPIViewers/src/DataViewer/Export.jl @@ -0,0 +1,211 @@ + +function initExportCallbacks(m::DataViewerWidget) + signal_connect(m["btnExportImages"], "clicked") do widget + try + exportImages(m) + catch e + showError(e) + end + end + + signal_connect(m["btnExportTikz"], "clicked") do widget + try + exportTikz(m) + catch e + showError(e) + end + end + + signal_connect(m["btnExportMovi"], "clicked") do widget + try + exportMovi(m) + catch e + @error e + # showError(e) + end + end + + signal_connect(m["btnExportAllData"], "clicked") do widget + try + exportAllData(m) + catch e + showError(e) + end + end + + signal_connect(m["btnExportRealDataAllFr"], "clicked") do widget + try + exportRealDataAllFr(m) + catch e + showError(e) + end + end + + signal_connect(m["btnExportData"], "clicked") do widget + try + exportData(m) + catch e + showError(e) + end + end + + signal_connect(m["btnExportProfile"], "clicked") do widget + exportProfile(m) + end +end + +function exportImages(m::DataViewerWidget) + if m.currentlyShownImages != nothing + filter = Gtk4.GtkFileFilter(pattern=String("*.png"), mimetype=String("image/png")) + diag = save_dialog("Select Export File", mpilab[]["mainWindow"], (filter, )) do filenameImageData + if filenameImageData != "" + pixelResizeFactor = get_gtk_property(m["adjPixelResizeFactor"],:value,Int64) + @info "Export Image as" filenameImageData + exportImage(filenameImageData, m.currentlyShownImages, pixelResizeFactor=pixelResizeFactor) + end + end + diag.modal = true + end +end + +function exportTikz(m::DataViewerWidget) + if m.currentlyShownImages != nothing + filter = Gtk4.GtkFileFilter(pattern=String("*.tikz*"), mimetype=String("image/tikz")) + diag = save_dialog("Select Export File", mpilab[]["mainWindow"], (filter, )) do filenameImageData + if filenameImageData != "" + pixelResizeFactor = get_gtk_property(m["adjPixelResizeFactor"],:value,Int64) + @info "Export Tikz as" filenameImageData + props = m.currentlyShownData[1].properties + SFPath=props["recoParams"][:SFPath] + bSF = MPIFile(SFPath) + exportTikz(filenameImageData, m.currentlyShownImages, collect(size(m.dataBG)), + collect(converttometer(pixelspacing(m.dataBG))),fov(bSF),getParams(m); pixelResizeFactor=pixelResizeFactor) + end + end + diag.modal = true + end +end + +function exportMovi(m::DataViewerWidget) + filter = Gtk4.GtkFileFilter(pattern=String("*.gif"), mimetype=String("image/gif")) + diag = save_dialog("Select Export File", mpilab[]["mainWindow"], (filter, )) do filenameMovie + if filenameMovie != "" + params = getParams(m) + sliceMovies = getColoredSlicesMovie(m.data, m.dataBG, m.coloring, params) + pixelResizeFactor = get_gtk_property(m["adjPixelResizeFactor"],:value, Int64) + @info "Export Movie as" filenameMovie + exportMovies(filenameMovie, sliceMovies, pixelResizeFactor=pixelResizeFactor) + end + end + diag.modal = true +end + +function exportAllData(m::DataViewerWidget) + if m.data != nothing + filter = Gtk4.GtkFileFilter(pattern=String("*.nii"), mimetype=String("application/x-nifti")) + diag = save_dialog("Select Export File", mpilab[]["mainWindow"], (filter, )) do filenameData + if filenameData != "" + params = getParams(m) + + maxval = [maximum(d) for d in m.data] + minval = [minimum(d) for d in m.data] + + if m.dataBG != nothing + data_ = interpolateToRefImage(m.dataBG, m.data, params) + dataBG = interpolateToRefImage(m.dataBG, params) + + data__ = [data(d) for d in data_] + + cdataFG = colorize(data__, m.coloring, minval, maxval, params) + + minval,maxval = extrema(dataBG) + cdataBG = colorize(dataBG,params[:coloringBG],minval,maxval) + + blendF = get(params, :translucentBlending, false) ? blend : dogyDoge + cdata = blendF(cdataBG, cdataFG) + else + data_ = [data(d) for d in m.data] + cdata = colorize(data_, m.coloring, minval, maxval, params) + end + + prop = properties(m.data[1]) + cdata_ = similar(cdata, RGB{N0f8}) + cdata_[:] = convert(ImageMeta{RGB},cdata)[:] #TK: ugly hack + + file, ext = splitext(filenameData) + savedata_analyze(string(file,".nii"), ImageMeta(cdata_,prop), permRGBData=true) + end + end + diag.modal = true + end +end + +function exportRealDataAllFr(m::DataViewerWidget) + if m.data != nothing && m.dataBG != nothing + filter = Gtk4.GtkFileFilter(pattern=String("*.nii"), mimetype=String("application/x-nifti")) + diag = save_dialog("Select Export File", mpilab[]["mainWindow"], (filter, )) do filenameData + if filenameData != "" + params = getParams(m) + + data = interpolateToRefImageAllFr(m.dataBG, m.data, params) + dataBG = interpolateToRefImage(m.dataBG, params) + + #dataBG_ = applyPermutionsRev(m, dataBG) + + file, ext = splitext(filenameData) + savedata_analyze(string(file,".nii"), data) + savedata_analyze(string(file,"_BG.nii"), dataBG) + end + end + diag.modal = true + end +end + +function exportData(m::DataViewerWidget) + if m.currentlyShownData != nothing + filter = Gtk4.GtkFileFilter(pattern=String("*.nii"), mimetype=String("application/x-nifti")) + diag = save_dialog("Select Export File", mpilab[]["mainWindow"], (filter, )) do filenameData + if filenameData != "" + + params = getParams(m) + + maxval = [maximum(d) for d in m.data] + minval = [minimum(d) for d in m.data] + + data_ = [data(d) for d in m.currentlyShownData] + + cdata = colorize(data_,m.coloring,minval,maxval,params) + + if m.dataBG != nothing + minval,maxval = extrema(m.dataBG) + cdataBG = colorize(m.dataBG,params[:coloringBG],minval,maxval) + + blendF = get(params, :translucentBlending, false) ? blend : dogyDoge + cdata = blendF(cdataBG, cdata) + end + + prop = properties(m.currentlyShownData[1]) + cdata_ = similar(cdata, RGB{N0f8}) + cdata_[:] = convert(ImageMeta{RGB},cdata)[:] #TK: ugly hack + + file, ext = splitext(filenameData) + savedata_analyze(string(file,".nii"), ImageMeta(cdata_,prop), permRGBData=true) + end + end + diag.modal = true + end +end + + +function exportProfile(m::DataViewerWidget) + if m.currentlyShownData != nothing + filter = Gtk4.GtkFileFilter(pattern=String("*.csv"), mimetype=String("text/comma-separated-values")) + diag = save_dialog("Select Export File", mpilab[]["mainWindow"], (filter, )) do filenameImageData + if filenameImageData != "" && m.currentProfile != nothing + @info "Export Image as" filenameImageData + writedlm(filenameImageData, m.currentProfile ) + end + end + diag.modal = true + end +end diff --git a/MPIViewers/src/GtkUtils.jl b/MPIViewers/src/GtkUtils.jl new file mode 100644 index 0000000..38d5643 --- /dev/null +++ b/MPIViewers/src/GtkUtils.jl @@ -0,0 +1,34 @@ +function Base.copy!(ctx::CairoContext, img::AbstractArray{C}) where C<:Union{Colorant,Number} + Cairo.save(ctx) + Cairo.reset_transform(ctx) + Cairo.image(ctx, image_surface(img), 0, 0, Gtk4.width(ctx), Gtk4.height(ctx)) + Cairo.restore(ctx) +end +Base.copy!(c::GtkCanvas, img) = copy!(getgc(c), img) +function Base.fill!(c::GtkCanvas, color::Colorant) + ctx = getgc(c) + w, h = Gtk4.width(c), Gtk4.height(c) + rectangle(ctx, 0, 0, w, h) + set_source(ctx, color) + fill(ctx) +end + +image_surface(img::Matrix{Gray24}) = CairoImageSurface(copy(reinterpret(UInt32, img)), Cairo.FORMAT_RGB24) +image_surface(img::Matrix{RGB24}) = CairoImageSurface(copy(reinterpret(UInt32, img)), Cairo.FORMAT_RGB24) +image_surface(img::Matrix{ARGB32}) = CairoImageSurface(copy(reinterpret(UInt32, img)), Cairo.FORMAT_ARGB32) + +image_surface(img::AbstractArray{T}) where {T<:Number} = image_surface(convert(Matrix{Gray24}, img)) +image_surface(img::AbstractArray{C}) where {C<:Color} = image_surface(convert(Matrix{RGB24}, img)) +image_surface(img::AbstractArray{C}) where {C<:Colorant} = image_surface(convert(Matrix{ARGB32}, img)) + + +function drawonto(canvas, figure) + CairoMakie.activate!(px_per_unit=3, pt_per_unit=3) + @guarded draw(canvas) do _ + scene = figure.scene + CairoMakie.resize!(scene, Gtk4.width(canvas), Gtk4.height(canvas)) + config = CairoMakie.ScreenConfig(1.0, 1.0, :good, true, false, nothing) + screen = CairoMakie.Screen(scene, config, Gtk4.cairo_surface(canvas)) + CairoMakie.cairo_draw(screen, scene) + end +end \ No newline at end of file diff --git a/MPIViewers/src/MPIViewers.jl b/MPIViewers/src/MPIViewers.jl new file mode 100644 index 0000000..6a8a7a9 --- /dev/null +++ b/MPIViewers/src/MPIViewers.jl @@ -0,0 +1,95 @@ +module MPIViewers + +using Reexport + +# Data Handling +@reexport using MPIFiles +@reexport using MPIReco +@reexport using MPISphericalHarmonics +using SphericalHarmonicExpansions +using Unitful +using MPISphericalHarmonics.NLsolve +using FFTW + +# File Handling +#using DelimtedFiles +using DataFrames +using CSV + +# Visualization +using CairoMakie +using Colors +using Gtk4 +using Gtk4Makie +using Gtk4.G_, Gtk4.GLib +using Cairo +using Images +using ImageUtils +using ImageUtils: converttometer, ColoringParams, makeAxisArray, Axis + + +import Base: getindex +import MPIFiles: addReco, getVisu, addVisu + +function object_(builder::GtkBuilder,name::AbstractString, T::Type)::T + return convert(T,ccall((:gtk_builder_get_object,Gtk4.libgtk),Ptr{Gtk4.GObject},(Ptr{Gtk4.GObject},Ptr{UInt8}),builder,name)) +end + +function imToVecIm(image::ImageMeta) + out = ImageMeta[] + for i=1:size(image,1) + I = getindex(image, i, ntuple(x->:,ndims(image)-1)...) + push!(out, I) + end + return out +end + +macro guard(ex) + return :(try; begin $(ex) end; catch e; showError(e); end) +end + +macro idle_add_guarded(ex) + quote + g_idle_add() do + try + $(esc(ex)) + catch err + @warn("Error in @guarded callback", exception=(err, catch_backtrace())) + end + return false + end + end +end + +const colors = [(0/255,73/255,146/255), # UKE blau +(239/255,123/255,5/255), # Orange (dunkel) +(138/255,189/255,36/255), # Grün +(178/255,34/255,41/255), # Rot +(170/255,156/255,143/255), # Mocca +(87/255,87/255,86/255), # Schwarz (Schrift) +(255/255,223/255,0/255), # Gelb +(104/255,195/255,205/255), # "TUHH" +(45/255,198/255,214/255), # TUHH +(193/255,216/255,237/255)] + +function showError(ex) + exTrunc = first(string(ex), 500) + if length(string(ex)) > 500 + exTrunc *="..." + end + str = string("Something went wrong!\n", exTrunc) + d = info_dialog(()-> nothing, str) + d.modal = true +end + +include("GtkUtils.jl") +include("BaseViewer.jl") +include("SimpleDataViewer.jl") +include("DataViewer/DataViewer.jl") +include("RawDataViewer.jl") +include("SpectrogramViewer.jl") +include("SFViewerWidget.jl") +include("MagneticFieldViewer/MagneticFieldViewer.jl") +include("3DViewer/3DViewer.jl") + +end # module MPIViewers \ No newline at end of file diff --git a/MPIViewers/src/MagneticFieldViewer/Export.jl b/MPIViewers/src/MagneticFieldViewer/Export.jl new file mode 100644 index 0000000..1d96713 --- /dev/null +++ b/MPIViewers/src/MagneticFieldViewer/Export.jl @@ -0,0 +1,238 @@ +## Export magnetic fields + +# link buttons to corresponding functions +function initExportCallbacks(m::MagneticFieldViewerWidget) + # export shown plots as png + @guarded signal_connect(m["btnExportImages"], "clicked") do widget + exportCanvas(m) + end + # CSV: Export Coeffs + @guarded signal_connect(m["btnExportCoeffsCSV"], "clicked") do widget + saveCoeffsAsCSV(m) + end + # CSV: Export Field + @guarded signal_connect(m["btnExportFieldCSV"], "clicked") do widget + saveFieldAsCSV(m) + end + # Tikz + @guarded signal_connect(m["btnExportTikz"], "clicked") do widget + saveTikz(m) + end +end + +# Export plots as pngs +function exportCanvas(m::MagneticFieldViewerWidget) + # choose destination + filter = Gtk4.GtkFileFilter(pattern=String("*.png"), mimetype=String("image/png")) + diag = save_dialog("Select Export File", nothing, (filter, )) do filename + if filename != "" + @info "Export Image as" filename + + # get only filename + file, ext = splitext(filename) + # Field plots + if get_gtk_property(m["cbFieldExport"],:active,Bool) + write_to_png(getgc(m.fv.grid[1,1]).surface,file*"_xz.png") + write_to_png(getgc(m.fv.grid[2,1]).surface,file*"_yz.png") + write_to_png(getgc(m.fv.grid[2,2]).surface,file*"_xy.png") + end + # Coefficients + if get_gtk_property(m["cbCoeffsExport"],:active,Bool) + write_to_png(getgc(m.grid[1,3]).surface,file*"_coeffs.png") + end + # Profile + if get_gtk_property(m["cbProfileExport"],:active,Bool) + write_to_png(getgc(m.fv.grid[1,2]).surface,file*"_profile.png") + end + + return filename + end + end + diag.modal = true +end + +################# +# Export as csv # +################# +# export coefficients + +function saveCoeffsAsCSV(m::MagneticFieldViewerWidget) + # choose destination + filter = Gtk4.GtkFileFilter(pattern=String("*.csv"), mimetype=String("image/csv")) + diag = save_dialog("Select Export File", nothing, (filter, )) do filename + if filename != "" + @info "Export coefficients as" filename + + saveCoeffsAsCSV(m, filename) + + return filename + end + end + diag.modal = true +end + +function saveCoeffsAsCSV(m::MagneticFieldViewerWidget, filename) + # get only filename + file, ext = splitext(filename) + + # get some values + p = get_gtk_property(m["adjPatches"],:value, Int64) # patch + L = get_gtk_property(m["adjL"],:value, Int64) # L + L² = (L+1)^2 # number of coefficients to be saved + R = m.coeffs.radius # radius for normalization + + # Create Array with the data + coeffsSave = ["num" "x" "y" "z"] + coeffsSave = vcat(coeffsSave,zeros(L²,4)) # length(coeffs[1,1].c) = (L+1)^2 + coeffsSave[2:end,1] = 1:L² + for j=1:3 + coeffsSave[2:end,j+1] = normalize(m.coeffsPlot[j,p],1/R).c[1:L²] + end + + # save + writedlm(file*".csv",coeffsSave,';'); +end + +# export magnetic field +function saveFieldAsCSV(m::MagneticFieldViewerWidget) + # choose destination + filter = Gtk4.GtkFileFilter(pattern=String("*.csv"), mimetype=String("image/csv")) + diag = save_dialog("Select Export File", nothing, (filter, )) do filename + if filename != "" + @info "Export field as" filename + + saveFieldAsCSV(m, filename) + + return filename + end + end + diag.modal = true +end + +function saveFieldAsCSV(m::MagneticFieldViewerWidget, filename) + # get only filename + file, ext = splitext(filename) + + # load all parameters + discretization = Int(get_gtk_property(m["adjDiscretization"],:value, Int64)*2+1) # odd number of voxel + R = m.coeffs.radius # radius of measurement data + + # get FOV + fovString = get_gtk_property(m["entFOV"], :text, String) # FOV + fov = tryparse.(Float64,split(fovString,"x")) ./ 1000 + + # Grid + N = [range(-fov[i]/2,stop=fov[i]/2,length=discretization) for i=1:3]; + + # calculate field for plot + fieldNorm = zeros(discretization,discretization,3); + fieldxyz = zeros(3,discretization,discretization,3); + setPatch!(m.field,m.patch) # set selected patch + for i = 1:discretization + for j = 1:discretization + fieldxyz[:,i,j,1] = m.field[m.fv.intersection[1],N[2][i],N[3][j]] + fieldNorm[i,j,1] = norm(fieldxyz[:,i,j,1]) + fieldxyz[:,i,j,2] = m.field[N[1][i],m.fv.intersection[2],N[3][j]] + fieldNorm[i,j,2] = norm(fieldxyz[:,i,j,2]) + fieldxyz[:,i,j,3] = m.field[N[1][i],N[2][j],m.fv.intersection[3]] + fieldNorm[i,j,3] = norm(fieldxyz[:,i,j,3]) + end + end + + # collect all coordinates for export to tikz + Nyz = hcat([[N[2][i],N[3][j]] for i=1:discretization for j=1:discretization]...) + Nxz = hcat([[N[1][i],N[3][j]] for i=1:discretization for j=1:discretization]...) + Nxy = hcat([[N[1][i],N[2][j]] for i=1:discretization for j=1:discretization]...) + + # create DataFrame + df = DataFrame("PlaneYZ_y"=> Nyz[1,:], + "PlaneYZ_z" => Nyz[2,:], + "PlaneYZ_f" => vec(permutedims(fieldNorm[:,:,1],[2,1])), + "PlaneXZ_x" => Nxz[1,:], + "PlaneXZ_z" => Nxz[2,:], + "PlaneXZ_f" => vec(permutedims(fieldNorm[:,:,2],[2,1])), + "PlaneXY_x" => Nxy[1,:], + "PlaneXY_y" => Nxy[2,:], + "PlaneXY_f" => vec(permutedims(fieldNorm[:,:,3],[2,1])) + ) + + # save as csv + CSV.write(file*".csv",df); + + ############ + ## Quiver ## + ############ + discr = floor(Int,0.1*discretization) # reduce number of arrows + fieldxyz = [fieldxyz[d,1:discr:end,1:discr:end,:] for d=1:3] + + # collect all coordinates for export to tikz + Nyz = hcat([[N[2][i],N[3][j]] for i=1:discr:discretization for j=1:discr:discretization]...) + Nxz = hcat([[N[1][i],N[3][j]] for i=1:discr:discretization for j=1:discr:discretization]...) + Nxy = hcat([[N[1][i],N[2][j]] for i=1:discr:discretization for j=1:discr:discretization]...) + + # create DataFrame + df = DataFrame("PlaneYZ_y"=> Nyz[1,:], + "PlaneYZ_z" => Nyz[2,:], + "quiver_yzu" => vec(permutedims(fieldxyz[2][:,:,1],[2,1])), # "PlaneYZ_u" + "quiver_yzv" => vec(permutedims(fieldxyz[3][:,:,1],[2,1])), # "PlaneYZ_v" + "PlaneXZ_x" => Nxz[1,:], + "PlaneXZ_z" => Nxz[2,:], + "PlaneXZ_u" => vec(permutedims(fieldxyz[1][:,:,2],[2,1])), + "PlaneXZ_v" => vec(permutedims(fieldxyz[3][:,:,2],[2,1])), + "PlaneXY_x" => Nxy[1,:], + "PlaneXY_y" => Nxy[2,:], + "Xyu" => vec(permutedims(fieldxyz[1][:,:,3]',[2,1])), # "PlaneXY_u" + "Xyv" => vec(permutedims(fieldxyz[2][:,:,3]',[2,1])), # "PlaneXY_v" + ) + + # save as csv + CSV.write(file*"_quiver"*".csv",df); +end + +############### +# Export tikz # +############### +# export plots as tex-file +function saveTikz(m::MagneticFieldViewerWidget) + # choose destination + filter = Gtk4.GtkFileFilter(pattern=String("*.tex"), mimetype=String("image/tex")) + diag = save_dialog("Select Export File", nothing, (filter, )) do filename + if filename != "" + @info "Export tikz as" filename + + saveTikz(m, filename) + + return filename + end + end + diag.modal = true +end + +function saveTikz(m::MagneticFieldViewerWidget, filename) + # get only filename + file, ext = splitext(filename) + + # load parameter + cmin = m.fv.coloring.cmin + cmax = m.fv.coloring.cmax + discretization = Int(get_gtk_property(m["adjDiscretization"],:value, Int64)*2+1) # odd number of voxel + arrowLength = get_gtk_property(m["adjArrowLength"],:value, Int64) + R = m.coeffs.radius + L = m.coeffs.coeffs[1].L + # get tex code + tex = exportTikz(file,cmin,cmax,discretization,arrowLength,R,L) + + # export data + saveCoeffsAsCSV(m, file*"_coeffs") + saveFieldAsCSV(m, file*"_field") + + # save tex-file + write(file*".tex",tex) + + # compile tex-file + if get_gtk_property(m["cbBuildPdf"], :active, Bool) + # get output folder + path, = splitdir(file) + run(`pdflatex -output-directory=$(path) $(file).tex`) + end +end \ No newline at end of file diff --git a/MPIViewers/src/MagneticFieldViewer/MagneticFieldViewer.jl b/MPIViewers/src/MagneticFieldViewer/MagneticFieldViewer.jl new file mode 100644 index 0000000..d094acd --- /dev/null +++ b/MPIViewers/src/MagneticFieldViewer/MagneticFieldViewer.jl @@ -0,0 +1,885 @@ +export MagneticFieldViewer + +mutable struct FieldViewerWidget <: Gtk4.GtkBox + handle::Ptr{Gtk4.GObject} + builder::GtkBuilder + coloring::ColoringParams + updating::Bool + fieldNorm::Array{Float64,3} # data to be plotted (norm) + field::Array{Float64,4} # data to be plotted (field values) + currentProfile::Array{Float64,3} # data of profile plot + positions # Vector containing the spatial positions of the plotted field + intersection::Vector{Float64} # intersection of the three plots + centerFFP::Bool # center of plot (FFP (true) or center of measured sphere (false)) + grid::Gtk4.GtkGridLeaf +end + +mutable struct MagneticFieldViewerWidget <: Gtk4.GtkBox + handle::Ptr{Gtk4.GObject} + builder::GtkBuilder + fv::FieldViewerWidget + updating::Bool + coeffsInit::MagneticFieldCoefficients + coeffs::MagneticFieldCoefficients + coeffsPlot::Array{SphericalHarmonicCoefficients} + field # SphericalHarmonicsDefinedField (Functions of the field) + patch::Int + grid::Gtk4.GtkGridLeaf + cmapsTree::Gtk4.GtkTreeModelFilter +end + +getindex(m::MagneticFieldViewerWidget, w::AbstractString) = G_.get_object(m.builder, w) +getindex(m::FieldViewerWidget, w::AbstractString) = G_.get_object(m.builder, w) + +mutable struct MagneticFieldViewer + w::Gtk4.GtkWindowLeaf + mf::MagneticFieldViewerWidget +end + +# plotting functions +include("Plotting.jl") +# export functions +include("Export.jl") +include("TikzExport.jl") # tikz stuff + +# Viewer can be started with MagneticFieldCoefficients or with a path to a file with some coefficients +function MagneticFieldViewer(filename::Union{AbstractString,MagneticFieldCoefficients}) + mfViewerWidget = MagneticFieldViewerWidget() + w = GtkWindow("Magnetic Field Viewer: $(filename)",800,600) + push!(w,mfViewerWidget) + show(w) + updateData!(mfViewerWidget, filename) + return MagneticFieldViewer(w, mfViewerWidget) +end + +function FieldViewerWidget() + uifile = joinpath(@__DIR__,"..","builder","magneticFieldViewer.ui") + + b = GtkBuilder(uifile) + mainBox = G_.get_object(b, "boxFieldViewer") + + fv = FieldViewerWidget(mainBox.handle, b, ColoringParams(0,0,0), + false, zeros(0,0,0), zeros(0,0,0,0), zeros(0,0,0), zeros(0), zeros(0), true, + G_.get_object(b, "gridFieldViewer"),) + Gtk4.GLib.gobject_move_ref(fv, mainBox) + + # initialize plots + fv.grid[1,1] = GtkCanvas() + fv.grid[1,2] = GtkCanvas() + fv.grid[2,1] = GtkCanvas() + fv.grid[2,2] = GtkCanvas() + + fv.grid.hexpand = fv.grid.vexpand = true + # expand plots + ### set_gtk_property!(fv, :expand, fv.grid, true) + + return fv +end + +function MagneticFieldViewerWidget() + uifile = joinpath(@__DIR__,"..","..","builder","magneticFieldViewer.ui") + + b = GtkBuilder(uifile) + mainBox = G_.get_object(b, "boxMagneticFieldViewer") + + m = MagneticFieldViewerWidget(mainBox.handle, b, FieldViewerWidget(), + false, MagneticFieldCoefficients(0), MagneticFieldCoefficients(0), + [SphericalHarmonicCoefficients(0)], nothing, 1, + GtkGrid(), GtkTreeModelFilter(GtkListStore(Bool))) + Gtk4.GLib.gobject_move_ref(m, mainBox) + + # build up plots + m.grid = m["gridMagneticFieldViewer"] + m.grid[1,1:2] = m.fv + m.grid[1,3] = GtkCanvas() + # expand plot + ### set_gtk_property!(m, :expand, m.grid, true) + + show(m) + + ## setup colormap search + # create list + ls = GtkListStore(String, Bool) + for c in existing_cmaps() + push!(ls,(c,true)) + end + # create TreeViewColumn + rTxt = GtkCellRendererText() + c = GtkTreeViewColumn("Colormaps", rTxt, Dict([("text",0)]))#, sort_column_id=0) # column + # add column to TreeView + m.cmapsTree = GtkTreeModelFilter(ls) + G_.set_visible_column(m.cmapsTree,1) + tv = GtkTreeView(GtkTreeModel(m.cmapsTree)) + push!(tv, c) + # add to popover + push!(m["boxCMaps"],tv) + show(m["boxCMaps"]) + + # set important colormaps + choices = important_cmaps() + for c in choices + push!(m["cbCMaps"], c) + end + set_gtk_property!(m["cbCMaps"],:active,5) # default: viridis + m.fv.coloring = ColoringParams(0,1,important_cmaps()[6]) # set default colormap + + # searching for specific colormaps + signal_connect(m["entCMaps"], "changed") do w + @idle_add_guarded begin + searchText = get_gtk_property(m["entCMaps"], :text, String) + if searchText == "Martin" + searchText = "jet1" + end + for l=1:length(ls) + showMe = true + if length(searchText) > 0 + showMe = showMe && occursin(lowercase(searchText), lowercase(ls[l,1])) + end + ls[l,2] = showMe + end + end + end + + # setup volume choices for FFP search + for v in instances(MPISphericalHarmonics.Volume) + push!(m["cbVolumeFFP"], "$v") + end + set_gtk_property!(m["cbVolumeFFP"],:active,0) # default: xyz + + # Allow to change between gradient and offset output + for c in ["Offset:", "Gradient:", "Eigenvalues:", "Singular values:"] + push!(m["cbGradientOffset"], c) + end + set_gtk_property!(m["cbGradientOffset"],:active,1) # default: Gradient + + # change between different profile options + for c in ["Norm","xyz","x","y","z"] # field + push!(m["cbFrameProj"], c) + end + set_gtk_property!(m["cbFrameProj"],:active,0) # default: Norm along all axes + for c in ["all", "x", "y", "z"] # axes + push!(m["cbProfile"], c) + end + set_gtk_property!(m["cbProfile"],:active,0) # default: Norm along all axes + + # change discretization + signal_connect(m["adjDiscretization"], "value_changed") do w + @idle_add_guarded begin + # adapt min/max slice + d = get_gtk_property(m["adjDiscretization"],:value, Int64) + for w in ["adjSliceX", "adjSliceY", "adjSliceZ"] + set_gtk_property!(m[w], :value, 0) + set_gtk_property!(m[w], :lower, -d) + set_gtk_property!(m[w], :upper, d) + end + # update plot + calcField(m) # calculate new field values + updateField(m) + updateProfile(m) + end + end + + # change patch + signal_connect(m["adjPatches"], "value_changed") do w + @idle_add_guarded begin + m.patch = get_gtk_property(m["adjPatches"],:value, Int64) # update patch + updateInfos(m) # update patch-dependent infos (like FFP) + stayInFFP(m) + end + end + + # change length of arrows + signal_connect(m["adjArrowLength"], "value_changed") do w + @idle_add_guarded updateField(m) + end + + # change fontsize + signal_connect(m["adjFontsize"], "value_changed") do w + @idle_add_guarded updateCoeffsPlot(m) + if get_gtk_property(m["cbShowAxes"], :active, Bool) # update field plots only if axes shown + @idle_add_guarded updateField(m) + end + @idle_add_guarded updateProfile(m) + end + + # change unit + signal_connect(m["cbUseMilli"], "toggled") do w + @idle_add_guarded updateCoeffsPlot(m) + if get_gtk_property(m["cbShowAxes"], :active, Bool) # update field plots only if axes shown + @idle_add_guarded updateField(m) + end + @idle_add_guarded updateProfile(m) + end + + # update coeffs plot + widgets = ["adjL"] + for ws in widgets + signal_connect(m[ws], "value_changed") do w + @idle_add_guarded updateCoeffsPlot(m) + end + end + signal_connect(m["cbShowLegend"], "toggled") do w + @idle_add_guarded updateCoeffsPlot(m) + end + signal_connect(m["cbScaleR"], "toggled") do w + @idle_add_guarded normalizeCoeffs(m) + end + + # update plot (clicked button) + signal_connect(m["btnUpdate"], "clicked") do w + @idle_add_guarded begin + updateIntersection(m) + updatePlots(m) + end + end + + # update magnetic field plot: + # center = center of sphere + signal_connect(m["btnCenterSphere"], "clicked") do w + if m.fv.centerFFP + @idle_add_guarded begin + set_gtk_property!(m["btnCenterSphere"],:sensitive,false) # disable button + set_gtk_property!(m["btnCenterFFP"],:sensitive,true) # enable button + m.fv.centerFFP = false + calcCenterCoeffs(m,true) + stayInFFP(m) + end + end + end + # center = FFP + signal_connect(m["btnCenterFFP"], "clicked") do w + if !(m.fv.centerFFP) + @idle_add_guarded begin + set_gtk_property!(m["btnCenterFFP"],:sensitive,false) # disable button + set_gtk_property!(m["btnCenterSphere"],:sensitive,true) # enable button + m.fv.centerFFP = true + calcCenterCoeffs(m,true) + updatePlots(m) + end + end + end + + # go to FFP + signal_connect(m["btnGoToFFP"], "clicked") do w + @idle_add_guarded begin + m.updating = true + goToFFP(m) + m.updating = false + end + end + # go to zero + signal_connect(m["btnGoToZero"], "clicked") do w + @idle_add_guarded begin + m.updating = true + goToFFP(m,true) + m.updating = false + end + end + + # calculate FFP + signal_connect(m["btnCalcFFP"], "clicked") do w + @idle_add_guarded calcFFP(m) # calculate FFP + @idle_add_guarded goToFFP(m) # go to FFP + end + + # reset FFP + signal_connect(m["btnResetFFP"], "clicked") do w + @idle_add_guarded resetFFP(m) + end + + # reset everything -> reload Viewer + signal_connect(m["btnReset"], "clicked") do w + @idle_add_guarded updateData!(m, m.coeffsInit) + end + + # checkbuttons changed + for cb in ["cbShowSphere", "cbShowSlices", "cbShowAxes", "cbShowCS"] + signal_connect(m[cb], "toggled") do w + @idle_add_guarded updateField(m) + end + end + signal_connect(m["cbStayFFP"], "toggled") do w + if get_gtk_property(m["cbStayFFP"], :active, Bool) # go to FFP only if active + @idle_add_guarded begin + goToFFP(m) + end + end + end + # checkbutton cmin = 0 + signal_connect(m["cbCMin"], "toggled") do w + @idle_add_guarded begin + if get_gtk_property(m["cbCMin"], :active, Bool) # cmin = 0 + set_gtk_property!(m["adjCMin"], :lower, 0) + set_gtk_property!(m["adjCMin"], :value, 0) + set_gtk_property!(m["vbCMin"], :sensitive, false) # disable changing cmin + else + set_gtk_property!(m["vbCMin"], :sensitive, true) # enable changing cmin + updateField(m) + end + # updateCol done automatically since value changed for adjCMin + end + end + # checkbutton keeping cmin/cmax + signal_connect(m["cbKeepC"], "toggled") do w + @idle_add_guarded begin + if get_gtk_property(m["cbKeepC"], :active, Bool) # keep the values + set_gtk_property!(m["vbCMin"], :sensitive, false) # disable changing cmin + set_gtk_property!(m["vbCMax"], :sensitive, false) # disable changing cmax + # disable writing cmin/cmax + set_gtk_property!(m["entCMin"], :editable, false) + set_gtk_property!(m["entCMax"], :editable, false) + else + set_gtk_property!(m["vbCMin"], :sensitive, true) # enable changing cmin + set_gtk_property!(m["vbCMax"], :sensitive, true) # enable changing cmax + if get_gtk_property(m["cbWriteC"], :active, Bool) + # enable writing cmin/cmax + set_gtk_property!(m["entCMin"], :editable, true) + set_gtk_property!(m["entCMax"], :editable, true) + end + end + end + end + # checkbutton write cmin/cmax + signal_connect(m["cbWriteC"], "toggled") do w + @idle_add_guarded begin + if get_gtk_property(m["cbWriteC"], :active, Bool) + # enable writing cmin/cmax + set_gtk_property!(m["entCMin"], :editable, true) + set_gtk_property!(m["entCMax"], :editable, true) + else + # disable writing cmin/cmax + set_gtk_property!(m["entCMin"], :editable, false) + set_gtk_property!(m["entCMax"], :editable, false) + # update field plot + updateField(m) + end + end + end + + # update measurement infos + signal_connect(m["cbGradientOffset"], "changed") do w + @idle_add_guarded updateInfos(m) + end + + # update profile plot + for cb in ["cbFrameProj", "cbProfile"] + signal_connect(m[cb], "changed") do w + @idle_add_guarded updateProfile(m) + end + end + + ## Video + # update min/max values of the patches + signal_connect(m["adjPatchesVideoLower"], "value_changed") do w + @idle_add_guarded set_gtk_property!(m["adjPatchesVideoUpper"], :lower, get_gtk_property(m["adjPatchesVideoLower"], :value, Int64)) + end + signal_connect(m["adjPatchesVideoUpper"], "value_changed") do w + @idle_add_guarded set_gtk_property!(m["adjPatchesVideoLower"], :upper, get_gtk_property(m["adjPatchesVideoUpper"], :value, Int64)) + end + # continous sweeps + playActive = false + signal_connect(m["tbPlayVideo"], :toggled) do w + if get_gtk_property(m["tbPlayVideo"], :active, Bool) + @info "Start Video" + # if video should not be repeated toggle play button + !(get_gtk_property(m["cbVideoRepeat"], :active, Bool)) && (@idle_add_guarded set_gtk_property!(m["tbPlayVideo"], :active, false)) + # get lower and upper patch + lowerPatch = get_gtk_property(m["adjPatchesVideoLower"], :value, Int64) + upperPatch = get_gtk_property(m["adjPatchesVideoUpper"], :value, Int64) + # get sleep time + sleepTime = tryparse.(Float64,MPIUI.get_gtk_property(m["entSleepVideo"],:text,String)) ./ 1000 + # timer for video repetitions + playActive = true + function update_(::Timer) + if playActive + # sweep over chosen patches + for p = lowerPatch:upperPatch + set_gtk_property!(m["adjPatches"], :value, p) + sleep(sleepTime) + end + else + close(timer) + end + end + timer = Timer(update_, 0.0, interval=1) + else + playActive = false + end + end + + initCallbacks(m) + + return m +end + + +function initCallbacks(m::MagneticFieldViewerWidget) + + ## update coloring + function updateCol(widget , importantCMaps::Bool=true) + + # update plots + @idle_add_guarded updateColoring(m,importantCMaps) + @idle_add_guarded updateField(m,true) + end + + + # choose new slice + function newSlice(widget) + if !m.updating # don't update slices if they are set by other functions + m.updating = true + + # get chosen slices + sl = [get_gtk_property(m[w],:value, Int64) for w in ["adjSliceX", "adjSliceY", "adjSliceZ"]] + + # get current FOV + fovString = get_gtk_property(m["entFOV"], :text, String) # FOV + fov = tryparse.(Float64,split(fovString,"x")) ./ 1000 + + # calculate voxel size + discretization = Int(get_gtk_property(m["adjDiscretization"],:value, Int64)*2+1) # odd number of voxel + voxel = fov ./ discretization + + # calculate new intersection + intersection = voxel .* sl + + # set intersection + interString = round.(intersection .* 1000, digits=1) + set_gtk_property!(m["entInters"], :text, + "$(interString[1]) x $(interString[2]) x $(interString[3])") + + # update coefficients with new intersection and plot everything + updateIntersection(m) + updatePlots(m) + + m.updating = false + end + end + + # cmin/cmax + widgets = ["adjCMin", "adjCMax"] + for w in widgets + signal_connect(m[w], "value_changed") do widget + @idle_add_guarded updateColLims(m) # update color range + @idle_add_guarded updateField(m,true) + end + end + + # colormap + signal_connect(updateCol, m["cbCMaps"], "changed") + signal_connect(G_.get_selection( Gtk4.next_sibling(Gtk4.first_child(m["boxCMaps"]))), "changed") do widget + updateCol( widget, false ) + end + + ## reset FOV + function resetFOV( widget ) + R = m.coeffs.radius # radius + set_gtk_property!(m["entFOV"], :text, "$(R*2000) x $(R*2000) x $(R*2000)") # initial FOV + calcField(m) # calculate new field values + updateField(m) + updateProfile(m) + end + signal_connect(resetFOV, m["btnResetFOV"], "clicked") + + # change slice + widgets = ["adjSliceX", "adjSliceY", "adjSliceZ"] + for w in widgets + signal_connect(newSlice, m[w], "value_changed") + end + + ## click on image to choose a slice + function on_pressed(controller, n_press, x, y) + if get_gtk_property(m["cbShowCS"], :active, Bool) + @info "Disable axes to change the intersection with a mouse click." + else + # position on the image: (x,y) + # calculate actual position in the fields coordinates to update intersection + # get image size + w = widget(controller) # get current widget + ctx = getgc(w) + hh = height(ctx) # height of the plot + ww = width(ctx) # width of the plot + # calculate pixel size + d = get_gtk_property(m["adjDiscretization"],:value, Int64) # number of voxel + hpix = hh / (d*2+1) # height of pixel + wpix = ww / (d*2+1) # width of pixel + # position (pixel) (counted from the upper left corner) + pixel = [floor(Int, x / wpix), floor(Int, y / hpix)] + + # update intersection (depending on the image) + @idle_add_guarded begin + if w == m.fv.grid[1,1] # XZ + set_gtk_property!(m["adjSliceX"], :value, -pixel[1]+d) + set_gtk_property!(m["adjSliceZ"], :value, -pixel[2]+d) + elseif w == m.fv.grid[2,1] # YZ + set_gtk_property!(m["adjSliceY"], :value, pixel[1]-d) + set_gtk_property!(m["adjSliceZ"], :value, -pixel[2]+d) + else # YX + set_gtk_property!(m["adjSliceY"], :value, pixel[1]-d) + set_gtk_property!(m["adjSliceX"], :value, pixel[2]-d) + end + end + end + end + + for c in [m.fv.grid[1,1], m.fv.grid[2,1], m.fv.grid[2,2]] + # add the event controller for mouse clicks to the widget + g = GtkGestureClick() + push!(c,g) + # call function + @idle_add_guarded signal_connect(on_pressed, g, "pressed") + end + + + # export images/data + initExportCallbacks(m) +end + +# load all necessary data and set up the values in the GUI +updateData!(m::MagneticFieldViewerWidget, filenameCoeffs::String) = updateData!(m, MagneticFieldCoefficients(filenameCoeffs)) + +function updateData!(m::MagneticFieldViewerWidget, coeffs::MagneticFieldCoefficients) + + m.coeffsInit = deepcopy(coeffs) # save initial coefficients for reloading + + # load magnetic fields + m.coeffs = deepcopy(coeffs) # load coefficients (do not change the given coefficients!) + m.coeffsPlot = deepcopy(m.coeffs.coeffs) # load coefficients + + m.field = SphericalHarmonicsDefinedField(m.coeffs) + m.patch = 1 + setPatch!(m.field,m.patch) # set selected patch + R = m.coeffs.radius # radius + center = m.coeffs.center[:,m.patch] # center of the measurement + m.fv.centerFFP = (m.coeffs.ffp !== nothing) ? true : false # if FFP is given, it is the plotting center + set_gtk_property!(m["btnCenterFFP"],:sensitive,false) # disable button + set_gtk_property!(m["btnCenterSphere"],:sensitive,true) # enable button + + m.updating = true + + # set some values + set_gtk_property!(m["adjPatches"], :upper, size(m.coeffs.coeffs,2) ) + set_gtk_property!(m["adjPatches"], :value, m.patch ) + set_gtk_property!(m["adjL"], :upper, m.coeffs.coeffs[1].L ) + set_gtk_property!(m["adjL"], :value, min(m.coeffs.coeffs[1].L,3) ) # use L=3 as maximum + set_gtk_property!(m["entRadius"], :text, "$(round(R*1000,digits=1))") # show radius of measurement + # center/FFP in coordinate system of coefficients + centerText = round.(center .* 1000, digits=1) + set_gtk_property!(m["entCenterMeas"], :text, "$(centerText[1]) x $(centerText[2]) x $(centerText[3])") # show center of measurement + if m.coeffs.ffp !== nothing + ffpText = round.((m.coeffs.ffp[:,m.patch]) .* 1000, digits=1) + set_gtk_property!(m["entFFP"], :text, "$(ffpText[1]) x $(ffpText[2]) x $(ffpText[3])") # show FFP of current patch + else + set_gtk_property!(m["entFFP"], :text, "no FFP") + end + set_gtk_property!(m["entFOV"], :text, "$(R*2000) x $(R*2000) x $(R*2000)") # initial FOV + set_gtk_property!(m["entInters"], :text, "0.0 x 0.0 x 0.0") # initial FOV + d = get_gtk_property(m["adjDiscretization"],:value, Int64) # get discretization as min/max for slices + for w in ["adjSliceX", "adjSliceY", "adjSliceZ"] + set_gtk_property!(m[w], :lower, -d) + set_gtk_property!(m[w], :value, 0) + set_gtk_property!(m[w], :upper, d) + end + # Video params + set_gtk_property!(m["adjPatchesVideoLower"], :upper, size(m.coeffs.coeffs,2) ) + set_gtk_property!(m["adjPatchesVideoUpper"], :upper, size(m.coeffs.coeffs,2) ) + set_gtk_property!(m["adjPatchesVideoUpper"], :value, size(m.coeffs.coeffs,2) ) + set_gtk_property!(m["entSleepVideo"], :text, "50") + + # if no FFPs are given, don't show the buttons + if isnothing(m.coeffs.ffp) + set_gtk_property!(m["btnGoToFFP"],:visible,false) # FFP as intersection not available + set_gtk_property!(m["btnCenterFFP"],:visible,false) # FFP as center not available + set_gtk_property!(m["btnCenterSphere"],:sensitive,false) # Center of sphere automatically plotting center + set_gtk_property!(m["btnCalcFFP"],:sensitive,true) # FFP can be calculated + set_gtk_property!(m["btnResetFFP"],:sensitive,false) # no FFP to be reset + else + # disable the calcFFP button + set_gtk_property!(m["btnCalcFFP"],:sensitive,false) # FFP already calculated + set_gtk_property!(m["btnResetFFP"],:sensitive,true) # FFP can be reseted + end + + # start with invisible axes for field plots + #set_gtk_property!(m["cbShowAxes"], :active, 1) # axes are always shown + + # update measurement infos + if isnothing(m.coeffs.ffp) + # default: show the offset field values if no FFP is given + set_gtk_property!(m["cbGradientOffset"],:active,1) + end + updateInfos(m) + + # plotting + updatePlots(m) + show(m) + + m.updating = false +end + +# update the coloring params +function updateColoring(m::MagneticFieldViewerWidget, importantCMaps::Bool=true) + if !m.fv.updating + m.fv.updating = true + # scale adjCMin/adjCMax with 1000 as values below 1 don't work + cmin = get_gtk_property(m["adjCMin"],:value, Float64) / 1000 + cmax = get_gtk_property(m["adjCMax"],:value, Float64) / 1000 + if importantCMaps # choice happend within the important colormaps + idx = get_gtk_property(m["cbCMaps"],:active, Int64)+1 + if idx <= 4 # first 4 colormaps not available + idx = 6 + set_gtk_property!(m["cbCMaps"], :active, 5) + end + cmap = important_cmaps()[idx] + else # choice happened within all colormaps + selection = G_.get_selection( Gtk4.next_sibling(Gtk4.first_child(m["boxCMaps"])) ) ### G_.get_selection(m["boxCMaps"][2]) + if hasselection(selection) + chosenCMap = selected(selection) + cmap = GtkTreeModel(m.cmapsTree)[chosenCMap,1] + else # use last choice of important colormaps + cmap = important_cmaps()[get_gtk_property(m["cbCMaps"],:active, Int64)+1] + end + end + m.fv.coloring = ColoringParams(cmin,cmax,cmap) + m.fv.updating = false + end +end + +# Coloring: Update cmin/cmax only +function updateColLims(m::MagneticFieldViewerWidget) + if !m.fv.updating + m.fv.updating = true + cmin = get_gtk_property(m["adjCMin"],:value, Float64) / 1000 + cmax = get_gtk_property(m["adjCMax"],:value, Float64) / 1000 + m.fv.coloring = ColoringParams(cmin,cmax,m.fv.coloring.cmap) # keep cmap + m.fv.updating = false + end +end + + +# update slices +function updateSlices(m::MagneticFieldViewerWidget) + # get current intersection + intersString = get_gtk_property(m["entInters"], :text, String) # intersection + intersection = tryparse.(Float64,split(intersString,"x")) ./ 1000 # conversion from mm to m + + # get voxel size + fovString = get_gtk_property(m["entFOV"], :text, String) # FOV + fov = tryparse.(Float64, split(fovString,"x")) ./ 1000 # conversion from mm to m + discretization = Int(get_gtk_property(m["adjDiscretization"], :value, Int64)*2+1) # odd number of voxel + voxel = fov ./ discretization + + # get slice numbers (rounded) + sl = round.(Int,intersection ./ voxel) + + # set slice + for (i,w) in enumerate(["adjSliceX", "adjSliceY", "adjSliceZ"]) + set_gtk_property!(m[w], :value, sl[i]) + end + +end + +# update intersection +function updateIntersection(m::MagneticFieldViewerWidget) + # get intersection (= new expansion point of coefficients) + intersString = get_gtk_property(m["entInters"], :text, String) # intersection + intersection = tryparse.(Float64,split(intersString,"x")) ./ 1000 # conversion from mm to m + + updateSlices(m) # update slice numbers + calcCenterCoeffs(m) # recalculate coefficients regarding to the center + updateCoeffs(m, intersection) # shift to intersection +end + +function goToFFP(m::MagneticFieldViewerWidget, goToZero=false) + if m.coeffs.ffp === nothing && !goToZero + # no FFP given + return nothing + end + + m.updating = true + + # set new intersection + intersection = (goToZero || m.fv.centerFFP) ? [0.0,0.0,0.0] : m.coeffs.ffp[:,m.patch] + interString = round.(intersection .* 1000, digits=1) + set_gtk_property!(m["entInters"], :text, + "$(interString[1]) x $(interString[2]) x $(interString[3])") + + updateSlices(m) # update slice numbers + calcCenterCoeffs(m) # recalculate coefficients regarding to the center + updateCoeffs(m, intersection) # shift coefficients into new intersection + updatePlots(m) + + m.updating = false +end + +# calculate the FFP of the given coefficients +function calcFFP(m::MagneticFieldViewerWidget) + + # calculate the FFP (don't shift the coefficients into the FFP) + vol = get_gtk_property(m["cbVolumeFFP"], :active, Int64) # volume where to search + findFFP!(m.coeffs, + start = [m.fv.intersection], # use intersection as start value + vol = MPISphericalHarmonics.Volume(vol)) + + # show FFP + ffpText = round.((m.coeffs.ffp[:,m.patch]) .* 1000, digits=1) + set_gtk_property!(m["entFFP"], :text, "$(ffpText[1]) x $(ffpText[2]) x $(ffpText[3])") # show FFP of current patch + + # show FFP regarding buttons + set_gtk_property!(m["btnGoToFFP"],:visible,true) # FFP as intersection + set_gtk_property!(m["btnCenterFFP"],:visible,true) # FFP as center + set_gtk_property!(m["btnCenterFFP"],:sensitive,true) # FFP as center + # disable calcFFP button + set_gtk_property!(m["btnCalcFFP"],:sensitive,false) # FFP already calculated + set_gtk_property!(m["btnResetFFP"],:sensitive,true) # allow to reset the FFP + set_gtk_property!(m["cbVolumeFFP"],:sensitive,false) # FFP already calculated in this volume +# set_gtk_property!(m["btnCenterSphere"],:sensitive,false) # Center of sphere automatically plotting center +end + +# reset the FFP of the given coefficients +function resetFFP(m::MagneticFieldViewerWidget) + + # reset the FFP + m.coeffs.ffp = nothing + + # show FFP + set_gtk_property!(m["entFFP"], :text, "no FFP") # show FFP of current patch + + # show FFP regarding buttons + set_gtk_property!(m["btnGoToFFP"],:visible,false) # FFP as intersection + set_gtk_property!(m["btnCenterFFP"],:visible,false) # FFP as center + set_gtk_property!(m["btnCenterFFP"],:sensitive,false) # FFP as center + # disable calcFFP button + set_gtk_property!(m["btnCalcFFP"],:sensitive,true) # FFP can be calculated again + set_gtk_property!(m["btnResetFFP"],:sensitive,false) # reset done + set_gtk_property!(m["cbVolumeFFP"],:sensitive,true) # allow to choose new volume + + # go to center sphere + m.fv.centerFFP = false + calcCenterCoeffs(m,true) + updatePlots(m) + set_gtk_property!(m["btnCenterSphere"],:sensitive,false) # Coefficients are in the sphere center +end + +# if button "Stay in FFP" true, everything is shifted into FFP, else the plots are updated +function stayInFFP(m::MagneticFieldViewerWidget) + # update plots + if get_gtk_property(m["cbStayFFP"], :active, Bool) && m.coeffs.ffp !== nothing + # stay in (resp. go to) the FFP with the plot + goToFFP(m) + else + # use intersection and just update all plots + updatePlots(m) + end +end + +# normalize coefficients with radius +function normalizeCoeffs(m::MagneticFieldViewerWidget) + # set some default values if the checkbutton is changed + if get_gtk_property(m["cbScaleR"], :active, Bool) + set_gtk_property!(m["adjL"], :value, min(m.coeffs.coeffs[1].L,3) ) # use L=3 as maximum + set_gtk_property!(m["cbUseMilli"], :active, true) # use mT + else + set_gtk_property!(m["adjL"], :value, 1 ) # use L=1 for coefficients scaled with R + set_gtk_property!(m["cbUseMilli"], :active, false) # use T/m^l + end + + updateCoeffsPlot(m) # update plot +end + +# update coefficients +function updateCoeffs(m::MagneticFieldViewerWidget, shift) + + # translate coefficients + for p = 1:size(m.coeffs.coeffs,2) + if size(shift) != size(m.coeffs.coeffs) # same shift for all coefficients + m.coeffsPlot[:,p] = SphericalHarmonicExpansions.translation.(m.coeffsPlot[:,p],[shift]) + else + m.coeffsPlot[:,p] = SphericalHarmonicExpansions.translation.(m.coeffsPlot[:,p],[shift[:,p]]) + end + end + + # update measurement infos + updateInfos(m) + +end + +# move coefficients +function calcCenterCoeffs(m::MagneticFieldViewerWidget,resetIntersection=false) + + # reset intersection + if resetIntersection + intersection = [0.0,0.0,0.0] + set_gtk_property!(m["entInters"], :text, + "$(intersection[1]*1000) x $(intersection[2]*1000) x $(intersection[3]*1000)") + end + + if m.fv.centerFFP + # shift coefficients into FFP (updates ffp and center) + MPISphericalHarmonics.shift!(m.coeffs, m.coeffs.ffp) + m.coeffsPlot = deepcopy(m.coeffs.coeffs) + else + # translate coefficients from FFP to center of measured sphere + MPISphericalHarmonics.shift!(m.coeffs, m.coeffs.center) + m.coeffsPlot = deepcopy(m.coeffs.coeffs) + end + + # update field + m.field = SphericalHarmonicsDefinedField(m.coeffs) + + # update measurement infos + updateInfos(m) +end + +# updating the measurement informations +function updateInfos(m::MagneticFieldViewerWidget) + # return new FFP + if m.coeffs.ffp !== nothing + ffpText = round.((m.coeffs.ffp[:,m.patch]) .* 1000, digits=1) + set_gtk_property!(m["entFFP"], :text, "$(ffpText[1]) x $(ffpText[2]) x $(ffpText[3])") # show FFP of current patch + end + + # return new center + centerText = round.((m.coeffs.center[:,m.patch]) .* 1000, digits=1) + set_gtk_property!(m["entCenterMeas"], :text, "$(centerText[1]) x $(centerText[2]) x $(centerText[3])") # show FFP of current patch + + # update gradient/offset information + if get_gtk_property(m["cbGradientOffset"],:active, Int) == 1 # show gradient + # gradient + set_gtk_property!(m["entGradientX"], :text, "$(round(m.coeffsPlot[1,m.patch][1,1],digits=3))") # show gradient in x + set_gtk_property!(m["entGradientY"], :text, "$(round(m.coeffsPlot[2,m.patch][1,-1],digits=3))") # show gradient in y + set_gtk_property!(m["entGradientZ"], :text, "$(round(m.coeffsPlot[3,m.patch][1,0],digits=3))") # show gradient in z + # unit + for i=1:3 + set_gtk_property!(m["labelTpm$i"], :label, "T/m") + set_gtk_property!(m["labelGradient$i"], :label, ["x","y","z"][i]) + end + elseif get_gtk_property(m["cbGradientOffset"],:active, Int) == 0 # show offset field + for (i,x) in enumerate(["X","Y","Z"]) + # offset + set_gtk_property!(m["entGradient"*x], :text, "$(round(m.coeffsPlot[i,m.patch][0,0]*1000,digits=3))") # show gradient in x + # unit + set_gtk_property!(m["labelTpm$i"], :label, "mT") + set_gtk_property!(m["labelGradient$i"], :label, ["x","y","z"][i]) + end + else # show eigenvalues or singular values + # calculate jacobian matrix + @polyvar x y z + expansion = sphericalHarmonicsExpansion.(m.coeffsPlot,[x],[y],[z]) + jexp = differentiate(expansion[:,m.patch],[x,y,z]); + J = [jexp[i,j]((x,y,z) => [0.0,0.0,0.0]) for i=1:3, j=1:3] # jacobian matrix + if get_gtk_property(m["cbGradientOffset"],:active, Int) == 2 + # get eigenvalues + sv = eigvals(J) + else + # get singular values + sv = svd(J).S + end + # show values + for (i,x) in enumerate(["X","Y","Z"]) + set_gtk_property!(m["entGradient"*x], :text, "$(round(sv[i],digits=3))") # eigenvalues/singular values + set_gtk_property!(m["labelTpm$i"], :label, "T/m") # unit + set_gtk_property!(m["labelGradient$i"], :label, "$i") + end + end + +end diff --git a/MPIViewers/src/MagneticFieldViewer/Plotting.jl b/MPIViewers/src/MagneticFieldViewer/Plotting.jl new file mode 100644 index 0000000..62817c5 --- /dev/null +++ b/MPIViewers/src/MagneticFieldViewer/Plotting.jl @@ -0,0 +1,424 @@ +############## +## Plotting ## +############## + +# update all plots +function updatePlots(m::MagneticFieldViewerWidget) + + # Coefficients + updateCoeffsPlot(m) + # calculate new field values + calcField(m) + # Field + updateField(m) + # Profile + updateProfile(m) +end + + +################## +# Magnetic field # +################## +# calculate the field (and profiles) +function calcField(m::MagneticFieldViewerWidget) + + discretization = Int(get_gtk_property(m["adjDiscretization"],:value, Int64)*2+1) # odd number of voxel + + # get current intersection + intersString = get_gtk_property(m["entInters"], :text, String) # intersection + m.fv.intersection = tryparse.(Float64,split(intersString,"x")) ./ 1000 # conversion from mm to m + + # get FOV + fovString = get_gtk_property(m["entFOV"], :text, String) # FOV + fov = tryparse.(Float64,split(fovString,"x")) ./ 1000 # conversion from mm to m + + # Grid (fov denotes the size and not the first and last pixel) + # center = m.coeffs.center # center of measurement data (TODO: adapt axis with measurement center) + m.fv.positions = [range(-fov[i]/2,stop=fov[i]/2,length=discretization+1) for i=1:3]; + for i=1:3 + m.fv.positions[i] = m.fv.positions[i][1:end-1] .+ Float64(m.fv.positions[i].step)/2 + end + N = m.fv.positions # renaming + + # calculate field for plot + m.fv.fieldNorm = zeros(discretization,discretization,3) + m.fv.field = zeros(3,discretization,discretization,3) + m.fv.currentProfile = zeros(4,discretization,3) + setPatch!(m.field,m.patch) # set selected patch + for i = 1:discretization + for j = 1:discretization + m.fv.field[:,i,j,1] = m.field[m.fv.intersection[1],N[2][i],N[3][j]] + m.fv.fieldNorm[i,j,1] = norm(m.fv.field[:,i,j,1]) + m.fv.field[:,i,j,2] = m.field[N[1][i],m.fv.intersection[2],N[3][j]] + m.fv.fieldNorm[i,j,2] = norm(m.fv.field[:,i,j,2]) + m.fv.field[:,i,j,3] = m.field[N[1][i],N[2][j],m.fv.intersection[3]] + m.fv.fieldNorm[i,j,3] = norm(m.fv.field[:,i,j,3]) + end + + # get current profile + m.fv.currentProfile[1:3,i,1] = m.field[N[1][i],m.fv.intersection[2],m.fv.intersection[3]] # along x-axis + m.fv.currentProfile[1:3,i,2] = m.field[m.fv.intersection[1],N[2][i],m.fv.intersection[3]] # along y-axis + m.fv.currentProfile[1:3,i,3] = m.field[m.fv.intersection[1],m.fv.intersection[2],N[3][i]] # along z-axis + m.fv.currentProfile[4,i,:] = [norm(m.fv.currentProfile[1:3,i,d]) for d=1:3] # norm along all axes + end + +end + +# plotting the magnetic field +function updateField(m::MagneticFieldViewerWidget, updateColoring=false) + + useMilli = get_gtk_property(m["cbUseMilli"], :active, Bool) # convert everything to mT or mm + discretization = Int(get_gtk_property(m["adjDiscretization"],:value, Int64)*2+1) # odd number of voxel + R = m.coeffs.radius # radius of measurement data + if m.coeffs.ffp !== nothing + ffp = useMilli ? m.coeffs.ffp .* 1000 : m.coeffs.ffp # used for correct positioning of the sphere + end + center = m.coeffs.center[:,m.patch] # center of measured sphere + N = m.fv.positions + + # coloring params + if updateColoring + # use params from GUI + cmin = m.fv.coloring.cmin + cmax = m.fv.coloring.cmax + cmap = m.fv.coloring.cmap + # set new min/max values + set_gtk_property!(m["adjCMin"], :upper, 0.99*cmax * 1000) # prevent cmin=cmax + set_gtk_property!(m["adjCMax"], :lower, 1.01*cmin * 1000) # prevent cmin=cmax + elseif get_gtk_property(m["cbKeepC"], :active, Bool) + # don't change min/max if the checkbutton is active + cmin = m.fv.coloring.cmin + cmax = m.fv.coloring.cmax + cmap = m.fv.coloring.cmap + elseif get_gtk_property(m["cbWriteC"], :active, Bool) + # get min/max from entCMin/Max + cminString = get_gtk_property(m["entCMin"], :text) + cmin = tryparse.(Float64,cminString) ./ 1000 + cmaxString = get_gtk_property(m["entCMax"], :text) + cmax = tryparse.(Float64,cmaxString) ./ 1000 + cmap = m.fv.coloring.cmap + m.fv.coloring = ColoringParams(cmin, cmax, cmap) # set coloring + else + # set new coloring params + cmin, cmax = minimum(m.fv.fieldNorm), maximum(m.fv.fieldNorm) + cmin = (get_gtk_property(m["cbCMin"], :active, Bool)) ? 0.0 : cmin # set cmin to 0 if checkbutton is active + cmap = m.fv.coloring.cmap + m.fv.coloring = ColoringParams(cmin, cmax, cmap) # set coloring + # set cmin and cmax + set_gtk_property!(m["adjCMin"], :lower, cmin * 1000) + set_gtk_property!(m["adjCMin"], :upper, 0.99*cmax * 1000) # prevent cmin=cmax + set_gtk_property!(m["adjCMax"], :lower, 1.01*cmin * 1000) # prevent cmin=cmax + set_gtk_property!(m["adjCMax"], :upper, cmax * 1000) + @idle_add_guarded set_gtk_property!(m["adjCMin"], :value, cmin * 1000) + @idle_add_guarded set_gtk_property!(m["adjCMax"], :value, cmax * 1000) + end + # update coloring infos + set_gtk_property!(m["entCMin"], :text, "$(round(m.fv.coloring.cmin * 1000, digits=3))") + set_gtk_property!(m["entCMax"], :text, "$(round(m.fv.coloring.cmax * 1000, digits=3))") + + # convert N to mT + N = useMilli ? N .* 1000 : N + + # heatmap plots + # label + lab = [useMilli ? "$i / mm" : "$i / m" for i in ["x", "y", "z"]] + # YZ + figYZ = CairoMakie.Figure(figure_padding=0); + axYZ = CairoMakie.Axis(figYZ[1,1], xlabel=lab[2], ylabel=lab[3]) + CairoMakie.heatmap!(axYZ, N[2], N[3], m.fv.fieldNorm[:,:,1], colorrange=(cmin,cmax), colormap=cmap) + # XZ + figXZ = CairoMakie.Figure(figure_padding=0); + axXZ = CairoMakie.Axis(figXZ[1,1], xlabel=lab[1], ylabel=lab[3]) + axXZ.xreversed = true # reverse x + CairoMakie.heatmap!(axXZ, N[1], N[3], m.fv.fieldNorm[:,:,2], colorrange=(cmin,cmax), colormap=cmap) + # XY + figXY = CairoMakie.Figure(figure_padding=0); + axXY = CairoMakie.Axis(figXY[1,1], xlabel=lab[2], ylabel=lab[1]) + CairoMakie.heatmap!(axXY, N[2], N[1], m.fv.fieldNorm[:,:,3]', colorrange=(cmin,cmax), colormap=cmap) + axXY.yreversed = true # reverse x + + + # disable ticks and labels + if !(get_gtk_property(m["cbShowCS"], :active, Bool)) + for ax in [axYZ, axXZ, axXY] + ax.xlabelvisible = false; ax.ylabelvisible = false; + ax.xticklabelsvisible = false; ax.yticklabelsvisible = false; + ax.xticksvisible = false; ax.yticksvisible = false; + end + end + + ## arrows ## + discr = floor(Int,0.1*discretization) # reduce number of arrows + ## positioning + NN = [N[i][1:discr:end] for i=1:3] + # vectors (arrows) (adapted to chosen coordinate orientations) + arYZ = [[m.fv.field[2,i,j,1],m.fv.field[3,i,j,1]] for i=1:discr:discretization, j=1:discr:discretization] + arXZ = [[m.fv.field[1,i,j,2],m.fv.field[3,i,j,2]] for i=1:discr:discretization, j=1:discr:discretization] + arXY = [[m.fv.field[2,i,j,3],m.fv.field[1,i,j,3]] for i=1:discr:discretization, j=1:discr:discretization] + + # calculate [u,v] for each arrow + # YZ + arYZu = [ar[1] for ar in arYZ] + arYZv = [ar[2] for ar in arYZ] + maxYZ = maximum([norm([arYZu[i],arYZv[i]]) for i in eachindex(arYZu)]) # for proper scaling + # XZ + arXZu = [ar[1] for ar in arXZ] + arXZv = [ar[2] for ar in arXZ] + maxXZ = maximum([norm([arXZu[i],arXZv[i]]) for i in eachindex(arXZu)]) # for proper scaling + # XY + arXYu = [ar[1] for ar in arXY] + arXYv = [ar[2] for ar in arXY] + maxXY = maximum([norm([arXYu[i],arXYv[i]]) for i in eachindex(arXYu)]) # for proper scaling + + # scale arrows + al = get_gtk_property(m["adjArrowLength"],:value, Float64) + al /= max(maxYZ,maxXZ,maxXY) + al /= useMilli ? 1 : 1000 # scale depends on m resp. mm + + # add arrows to plots + # YZ + CairoMakie.arrows!(axYZ, NN[2], NN[3], arYZu, arYZv, + color=:white, linewidth=1, arrowsize = 6, lengthscale = al) + # XZ + CairoMakie.arrows!(axXZ, NN[1], NN[3], arXZu, arXZv, + color=:white, linewidth=1, arrowsize = 6, lengthscale = al) + # XY + CairoMakie.arrows!(axXY, NN[2], NN[1], arXYu', arXYv', + color=:white, linewidth=1, arrowsize = 6, lengthscale = al) + + # set fontsize + fs = get_gtk_property(m["adjFontsize"],:value, Int64) # fontsize + CairoMakie.set_theme!(CairoMakie.Theme(fontsize = fs)) # set fontsize for the whole plot + + # Show slices + if get_gtk_property(m["cbShowSlices"], :active, Bool) + # draw lines to mark 0 + intersec = useMilli ? m.fv.intersection .*1000 : m.fv.intersection # scale intersection to the chosen unit + # YZ + CairoMakie.hlines!(axYZ, intersec[3], color=:white, linestyle=:dash, linewidth=0.5) + CairoMakie.vlines!(axYZ, intersec[2], color=:white, linestyle=:dash, linewidth=0.5) + # XZ + CairoMakie.hlines!(axXZ, intersec[3], color=:white, linestyle=:dash, linewidth=0.5) + CairoMakie.vlines!(axXZ, intersec[1], color=:white, linestyle=:dash, linewidth=0.5) + # XY + CairoMakie.hlines!(axXY, intersec[1], color=:white, linestyle=:dash, linewidth=0.5) + CairoMakie.vlines!(axXY, intersec[2], color=:white, linestyle=:dash, linewidth=0.5) + end + + # show sphere + if get_gtk_property(m["cbShowSphere"], :active, Bool) + # sphere + ϕ=range(0,stop=2*pi,length=100) + rr = zeros(100,2,3) + # adapt radius depending on the current intersection + r = m.fv.centerFFP ? m.fv.intersection-center : m.fv.intersection + r = sqrt.(max.(0, R^2 .- r .^ 2)) + for i=1:100 + rr[i,1,:] = r .* sin(ϕ[i]); + rr[i,2,:] = r .* cos(ϕ[i]); + end + rr = useMilli ? rr .* 1000 : rr # convert from m to mm + + # shift sphere to its center + center = useMilli ? center .* 1000 : center + if m.fv.centerFFP + CairoMakie.lines!(axYZ, rr[:,1,1].+center[2], rr[:,2,1].+center[3], + color=:white, linestyle=:dash, linewidth=1) + CairoMakie.lines!(axXZ, rr[:,1,2].+center[1], rr[:,2,2].+center[3], + color=:white, linestyle=:dash, linewidth=1) + CairoMakie.lines!(axXY, rr[:,1,3].+center[2], rr[:,2,3].+center[1], + color=:white, linestyle=:dash, linewidth=1) + else + CairoMakie.lines!(axYZ, rr[:,1,1], rr[:,2,1], color=:white, linestyle=:dash, linewidth=1) + CairoMakie.lines!(axXZ, rr[:,1,2], rr[:,2,2], color=:white, linestyle=:dash, linewidth=1) + CairoMakie.lines!(axXY, rr[:,1,3], rr[:,2,3], color=:white, linestyle=:dash, linewidth=1) + end + + end + + # show fields + drawonto(m.fv.grid[1,1], figXZ) + drawonto(m.fv.grid[2,1], figYZ) + drawonto(m.fv.grid[2,2], figXY) + + # draw axes (only arrows) + if get_gtk_property(m["cbShowAxes"], :active, Bool) + for w in [[m.fv.grid[1,1],"xz"], [m.fv.grid[2,1],"yz"], [m.fv.grid[2,2], "xy"]] + @idle_add_guarded Gtk4.draw(w[1]) do widget + ctx = getgc(w[1]) + drawAxes(ctx, w[2]) + set_line_width(ctx, 3.0) + Cairo.stroke(ctx) + end + end + end + +end + +################ +# Coefficients # +################ +# plotting the coefficients +function updateCoeffsPlot(m::MagneticFieldViewerWidget) + + p = get_gtk_property(m["adjPatches"],:value, Int64) # patch + L = get_gtk_property(m["adjL"],:value, Int64) # L + L² = (L+1)^2 # number of coeffs + R = m.coeffs.radius + useMilli = get_gtk_property(m["cbUseMilli"], :active, Bool) # convert everything to mT or mm + scaleR = get_gtk_property(m["cbScaleR"], :active, Bool) # normalize coefficients with radius R + + # normalize coefficients + c = scaleR ? normalize.(m.coeffsPlot[:,p], 1/R) : m.coeffsPlot[:,p] + cs = vcat([c[d].c[1:L²] for d=1:3]...) # stack all coefficients + cs = useMilli ? cs .* 1000 : cs # convert to mT + grp = repeat(1:3, inner=L²) # grouping the coefficients + + # set fontsize + fs = get_gtk_property(m["adjFontsize"],:value, Int64) # fontsize + CairoMakie.set_theme!(CairoMakie.Theme(fontsize = fs)) # set fontsize for the whole plot + + # create plot + fig = CairoMakie.Figure(figure_padding=2) + xticklabel = ["[$l,$m]" for l=0:L for m=-l:l] + # ylabel + if useMilli && scaleR + ylabel = CairoMakie.L"\gamma^R_{l,m}~/~\text{mT}" + elseif !useMilli && scaleR + ylabel = CairoMakie.L"\gamma^R_{l,m}~/~\text{T}" + elseif useMilli && !scaleR + ylabel = CairoMakie.L"\gamma_{l,m}~/~\text{mT/m}^l" + else + ylabel = CairoMakie.L"\gamma_{l,m}~/~\text{T/m}^l" + end + ax = CairoMakie.Axis(fig[1,1], xticks = (1:L², xticklabel), + #title="Coefficients", + xlabel = CairoMakie.L"[l,m]", ylabel = ylabel) + + # x values + y = range(1,L²,length=L²) + y = repeat(y, outer=3) # for each direction + + # create bars + colorsCoeffs = [CairoMakie.RGBf(MPIUI.colors[i]...) for i in [1,3,7]] # use blue, green and yellow + CairoMakie.barplot!(ax, # axis + y, cs, # x- and y-values + dodge=grp, color=colorsCoeffs[grp]) + CairoMakie.autolimits!(ax) # auto axis limits + + # draw line to mark 0 + CairoMakie.ablines!(0, 0, color=:black, linewidth=1) + + # legend + if get_gtk_property(m["cbShowLegend"], :active, Bool) + labels = ["x","y","z"] + elements = [CairoMakie.PolyElement(polycolor = colorsCoeffs[i], + ) for i in 1:length(labels)] + CairoMakie.axislegend(ax, elements, labels, position=:rt, patchsize=(15,0.8*fs)) # pos: right, top + end + + # show coeffs + drawonto(m.grid[1,3], fig) +end + + +################ +# profile plot # +################ +# update profile plot data +function updateProfile(m::MagneticFieldViewerWidget) + # ["Norm","xyz","x","y","z"] # cbFrameProj - field + # ["all", "x", "y", "z"] # cbProfile - axes + + # get chosen profiles + fields = get_gtk_property(m["cbFrameProj"],:active, Int64) + axesDir = get_gtk_property(m["cbProfile"],:active, Int64) + + # positioning + useMilli = get_gtk_property(m["cbUseMilli"], :active, Bool) # convert everything to mT or mm + N = m.fv.positions # renaming + # convert N to mT + N = useMilli ? N .* 1000 : N + + # colors + colorsAll = [CairoMakie.RGBf(MPIUI.colors[i]...) for i in [1,3,7]] # use blue, green and yellow + + # label + xlabel = ["xyz", "x", "y", "z"][axesDir+1] + xlabel *= useMilli ? " / mm" : " / m" + ylabel = (fields == 0) ? "||B||" : "B"*["","x","y","z"][fields] + ylabel *= useMilli ? " / mT" : " / T" + + # choose colors and data for the plot + if fields == 1 || axesDir == 0 + + # plot all three fields in one direction or one field/norm in all directions + colorsPlot = colorsAll # all three colors + + # data + x = (axesDir == 0) ? N : N[axesDir] # x values (all axes or one axis) + if fields == 1 && axesDir == 0 # all fields in their main direction + y = vcat([m.fv.currentProfile[j,:,j] for j=1:3]'...) + elseif fields == 1 # all fields in one direction + y = m.fv.currentProfile[1:3,:,axesDir] + elseif fields == 0 # norm in all directions + y = m.fv.currentProfile[4,:,:]' + else # one field in all directions + y = m.fv.currentProfile[fields-1,:,:]' + end + + elseif fields != 0 #|| (fields == 0 && axesDir == 1) + + # plot a field in one direction + colorsPlot = [colorsAll[fields-1]] # x-direction (field or axis) + + # data + x = N[axesDir] # x values + y = m.fv.currentProfile[fields-1,:,axesDir] + + else #fields == 0 && axesDir != 0 + + # plot norm in one direction + colorsPlot = [colorsAll[axesDir]] # y-direction (field or axis) + + # data + x = N[axesDir] # x values + y = m.fv.currentProfile[4,:,axesDir] + + end + + y *= useMilli ? 1000 : 1 + + showProfile(m, x, y, xlabel, ylabel, colorsPlot) + end + + # drawing profile plot + function showProfile(m::MagneticFieldViewerWidget, dataX, dataY, + xLabel::String, yLabel::String, + colors::Vector{RGB{Float32}}) + + # set fontsize + fs = get_gtk_property(m["adjFontsize"],:value, Int64) # fontsize + CairoMakie.set_theme!(CairoMakie.Theme(fontsize = fs)) # set fontsize for the whole plot + + # figure + fig = CairoMakie.Figure(figure_padding=2) + + # axis + ax = CairoMakie.Axis(fig[1,1], + #title="Profile", + xlabel = xLabel, ylabel = yLabel) + + # Plot + for i = 1:length(colors) # number of profiles + X = (typeof(dataX) <: Vector) ? dataX[i] : dataX + Y = (typeof(dataY) <: Vector) ? dataY : dataY[i,:] + CairoMakie.lines!(ax, X, Y, color=colors[i]) + end + CairoMakie.autolimits!(ax) # auto axis limits + + # draw line to mark 0 + CairoMakie.ablines!(0, 0, color=:black, linewidth=1) + + drawonto(m.fv.grid[1,2], fig) + end \ No newline at end of file diff --git a/MPIViewers/src/MagneticFieldViewer/TikzExport.jl b/MPIViewers/src/MagneticFieldViewer/TikzExport.jl new file mode 100644 index 0000000..fde3968 --- /dev/null +++ b/MPIViewers/src/MagneticFieldViewer/TikzExport.jl @@ -0,0 +1,348 @@ +# Functions with tex-code to build up the entire file + +# combine tikz into final tex file +function exportTikz(filename::String, cmin, cmax, discr::Int, arrowL::Int, radius, L::Int) + + # get all tex-stuff + texPre, texPost = prepareTex() # tex stuff around tikzfigure + tikzParams = setTikzParams(filename,cmin,cmax,discr,arrowL,radius,L) # parameter defined in the viewer + tikzConfig = prepareTikzConfig() # plot configurations + tikzPlot = prepareTikzPlot() # plotting stuff + + # build tikzfigure + tikz = prepareTikz(tikzConfig, tikzPlot) + + # final tex file + tex = texPre * tikzParams * tikz * texPost + + return tex + +end + +# prepare tex stuff around tikzfigure +function prepareTex() + + texPre = """ + \\documentclass{standalone} + \\standaloneconfig{border=-1.0cm 0.0cm 0.0cm 0cm} % cut off unnecessary spaces (left, ...) + + % tikz & pgfplots + \\usepackage{tikz} + \\usepackage{pgfplots} + \\pgfplotsset{compat=newest} + + \\usetikzlibrary{positioning, backgrounds, fit, calc} + \\usepgfplotslibrary{units} + \\usepgfplotslibrary{groupplots} % positioning of the plots + + \\pgfdeclarelayer{background} + \\pgfdeclarelayer{foreground} + \\pgfsetlayers{background,main,foreground} + + % Coefficients plot + \\pgfplotsset{ + unit markings=slash space, + /pgfplots/xbar/.style={ + /pgf/bar shift={-0.5*(\\numplotsofactualtype*\\pgfplotbarwidth + (\\numplotsofactualtype-1)*#1) + (.5+\\plotnumofactualtype)*\\pgfplotbarwidth + \\plotnumofactualtype*#1}, + }, + } + % Style to select only points from #1 to #2 (inclusive) + \\pgfplotsset{select coords between index/.style 2 args={ + x filter/.code={ + \\ifnum\\coordindex<#1\\def\\pgfmathresult{}\\fi + \\ifnum\\coordindex>#2\\def\\pgfmathresult{}\\fi + } + }} + + %% Colors + \\definecolor{ibidark}{RGB}{0,73,146} % blue + \\definecolor{ukesec1}{RGB}{255,223,0} % yellow + \\definecolor{ukesec4}{RGB}{138,189,36} % green + + + \\usepackage{bm, amsmath, amssymb} + + \\begin{document} + """ + + texPost = """ + \\end{document} + """ + + return texPre, texPost + +end + +# set all necessary parameter +function setTikzParams(filename,cmin,cmax,discr,arrowL,radius,L) + + tikzParams = """ + % data path + \\def\\pathFile{$(filename)} + + % define sizes + \\def\\h{5cm} + \\def\\w{3.88*\\h} + \\def\\arrowLength{$(arrowL)} + \\pgfmathsetmacro\\scaleArrow{0.04*\\arrowLength} + \\pgfmathsetmacro\\vsep{1cm} % vertical separation between subplots + \\pgfmathsetmacro\\hsep{1.3cm} % horizontal separation between subplots + + % define some field-specific stuff + \\def\\cmin{$(cmin)} % field plot + \\def\\cmax{$(cmax)} % field plot + \\def\\discr{$(discr)} % discretization + \\def\\radius{$(radius)} + \\def\\L{$(L)} + + \\pgfmathsetmacro\\maxTick{(\\L+1)^2+0.5} + """ + + return tikzParams +end + + +# prepare plot configurations +function prepareTikzConfig() + + tikzConfig = """ + % data + \\pgfplotstableread[col sep=comma,]{\\pathFile_field.csv}\\datatableField % load data + \\pgfplotstableread[col sep=comma,]{\\pathFile_field_quiver.csv}\\datatableQuiver % load data + \\pgfplotstableread[col sep=semicolon,]{\\pathFile_coeffs.csv}\\datatableCoeffs % load data + + % coefficients: xlabel + \\gdef\\labellist{} + \\foreach \\l in {0,1,...,\\L} { + \\foreach \\m in {-\\l,...,\\l} { + \\xdef\\labellist{\\labellist {[\\l,\\m]},} + } + } + \\edef\\temp{\\noexpand\\pgfplotsset{x tick labels list/.style={xticklabels={\\labellist}}}}% + \\temp + + %% Setup of the plots + \\pgfplotsset{ + field/.style={ + clip mode=individual, + height=\\h, width=\\h, + axis equal image, + %% + view={0}{90}, + point meta = explicit, + %%% + mesh/cols=\\discr, + %% ticks %% + tick align=outside, + tickpos=left, + tick style={/pgfplots/major tick length=3pt}, + x tick label style={yshift=1pt}, y tick label style={xshift=1pt}, + %% labels %% + change x base = true, change y base = true, + ylabel={\$z\$}, + xlabel={\$x\$}, + x unit = m, x SI prefix = milli, + y unit = m, y SI prefix = milli, + xlabel shift={-6pt}, + ylabel shift={-5pt}, + %% Colors %% + colormap/viridis, + shader=interp, + %% Colorbar: + point meta min=\\cmin, + point meta max=\\cmax, + small, + colorbar style={ + %% sizes %% + colorbar shift/.style={xshift=0.075*\\h}, + footnotesize, + width=0.3cm, + height=1.12*\\h, + %% ticks %% + xticklabel style = {xshift=0.0cm,yshift=0.0cm}, + x tick style= {color=black}, + extra x tick style={tickwidth=0pt}, + change y base = true, + %% label %% + ylabel = {\$\\lVert \\bm B \\rVert_2\$}, + y unit = T, y SI prefix = milli, + unit markings=slash space, + ylabel style={rotate=180} + }, + }, + coeffs/.style = { + set layers=standard, + footnotesize, + width=\\w, + height=\\h, + % select only part of the coefficients + select coords between index={0}{15}, + %% labels %% + change y base = true, + xlabel={\$[l,m]\$}, + ylabel={coefficients}, + y unit = T, %m^{-\\mathnormal{l}}, % mathnormal for italic l + y SI prefix = milli, + xlabel shift={2pt}, + ylabel shift={-8pt}, + label style={font=\\small}, + %% ticks %% + scaled y ticks=base 10:3, % quasi milliTesla Angabe + scaled ticks=false, % ohne einzelnes 10^-2 + tick label style={/pgf/number format/fixed,}, % einzelne konkrete ticks label + % font=\\tiny}, + tick style={major grid style={thin,dotted,gray}}, + tick align=outside,%center, + tickpos=left, + x tick labels list, + % label as interval for 3 bars each + xtick={0.5,1.5,...,\\maxTick}, + x tick label as interval, + enlarge x limits=0.05, + tick style={/pgfplots/major tick length=3pt}, + x tick label style={yshift=1pt}, y tick label style={xshift=1pt}, + %% grid %% + grid=major, + extra y ticks={0.0}, % black line for y = 0 + extra y tick labels={}, + extra x tick style={major grid style={thin,gray}}, + extra y tick style={major grid style={solid,black,on layer=axis foreground}}, % black line for y = 0 + %% legend %% + legend style = {anchor=north east, at={(0.9925,0.975)}}, + legend columns=1, + }, + sphere/.style = {color=white, dashed, very thick,outer sep=2pt}, % measured sphere + } + """ + + return tikzConfig + +end + +# prepare plotting stuff +function prepareTikzPlot() + + tikzPlot = """ + %% Plot + \\begin{groupplot}[ + group style={ + group size=3 by 2, + vertical sep=\\vsep, + horizontal sep=\\hsep, + }, + height=\\h, + width=\\h, + scale only axis] + + %%% Magnetic fields + % xz-plane + \\nextgroupplot[field, + % title + title = {\$xz\$-plane}, + title style = {yshift=-5pt}, + ] + % norm + \\addplot3[surf] table [x=PlaneXZ_x,y=PlaneXZ_z,meta=PlaneXZ_f] {\\datatableField}; + % arrows + \\addplot[ + quiver = { + u = \\thisrow{PlaneXZ_u}, + v = \\thisrow{PlaneXZ_v}, + scale arrows = \\scaleArrow, + update limits=false, + }, + -stealth, + ] + table [x=PlaneXZ_x,y=PlaneXZ_z, ] {\\datatableQuiver}; + % circle + \\addplot [domain=-180:180, sphere] ({\\radius*cos(x)},{\\radius*sin(x)}); + + % yz-plane + \\nextgroupplot[field, + xlabel={\$y\$}, ylabel={\$z\$}, + % title + title = {\$yz\$-plane}, + title style = {yshift=-5pt}, + ] + % norm + \\addplot3[surf] table [x=PlaneYZ_y,y=PlaneYZ_z,meta=PlaneYZ_f] {\\datatableField}; + % arrows + \\addplot[ + quiver = { + u = \\thisrow{quiver_yzu}, % u=PlaneXZ_u + v = \\thisrow{quiver_yzv}, % v=PlaneXZ_v + scale arrows = \\scaleArrow, + update limits=false, + }, + -stealth, + ] + table [x=PlaneYZ_y,y=PlaneYZ_z, ] {\\datatableQuiver}; + % circle + \\addplot [domain=-180:180, sphere] ({\\radius*cos(x)},{\\radius*sin(x)}); + + % xy-plane + \\nextgroupplot[field, + xlabel={\$x\$}, ylabel={\$y\$}, + % title + title = {\$xy\$-plane}, + title style = {yshift=-5pt}, + % colorbar + colorbar, + ] + % field + \\addplot3[surf] table [x=PlaneXY_x,y=PlaneXY_y,meta=PlaneXY_f] {\\datatableField}; + % arrows + \\addplot[ + quiver = { + u = \\thisrow{Xyu}, % u=PlaneXZ_u + v = \\thisrow{Xyv}, % v=PlaneXZ_v + scale arrows = \\scaleArrow, + update limits=false, + }, + -stealth, + ] + table [x=PlaneXY_y,y=PlaneXY_x, ] {\\datatableQuiver}; + % circle + \\addplot [domain=-180:180, sphere] ({\\radius*cos(x)},{\\radius*sin(x)}); + + \\nextgroupplot[group/empty plot] + + %% Coefficients %% + \\nextgroupplot[coeffs, + % bar plot + ybar=1pt, + bar width=4.0pt, + ] + + \\addplot[fill = ibidark,draw=none] table [x = num, y = x] {\\datatableCoeffs}; % x + \\addlegendentry{\$x\$} + \\addplot[fill=ukesec4,draw=none] table [x = num, y = y] {\\datatableCoeffs}; % y + \\addlegendentry{\$y\$} + \\addplot[fill=ukesec1,draw=none] table [x = num, y = z] {\\datatableCoeffs}; % z + \\addlegendentry{\$z\$} + + \\nextgroupplot[group/empty plot] + + \\end{groupplot} + """ + + return tikzPlot + +end + +# prepare complete tikzpicture +function prepareTikz(tikzConfig, tikzPlot) + + tikzPre = """ + \\begin{tikzpicture} + """ + + tikzPost = """ + \\end{tikzpicture} + """ + + tikz = tikzPre * tikzConfig * tikzPlot * tikzPost + + return tikz + +end \ No newline at end of file diff --git a/MPIViewers/src/RawDataViewer.jl b/MPIViewers/src/RawDataViewer.jl new file mode 100644 index 0000000..fb47d01 --- /dev/null +++ b/MPIViewers/src/RawDataViewer.jl @@ -0,0 +1,592 @@ +mutable struct RawDataWidget <: Gtk4.GtkBox + handle::Ptr{Gtk4.GObject} + builder::GtkBuilder + data::Array{Float32,5} + dataBG::Array{Float32,5} + labels::Vector{String} + cTD::GtkCanvas + cFD::GtkCanvas + deltaT::Float64 + filenamesData::Vector{String} + loadingData::Bool + updatingData::Bool + fileModus::Bool + rangeTD::NTuple{2,Float32} + rangeFD::NTuple{2,Float32} +end + +getindex(m::RawDataWidget, w::AbstractString) = Gtk4.G_.get_object(m.builder, w) + +function RawDataWidget(filenameConfig=nothing) + @info "Starting RawDataWidget" + uifile = joinpath(@__DIR__,"builder","rawDataViewer.ui") + + b = GtkBuilder(uifile) + mainBox = Gtk4.G_.get_object(b, "boxRawViewer") + + m = RawDataWidget( mainBox.handle, b, + zeros(Float32,0,0,0,0,0), zeros(Float32,0,0,0,0,0), + [""], GtkCanvas(), GtkCanvas(), + 1.0, [""], false, false, false, + (0.0,1.0), (0.0,1.0)) + Gtk4.GLib.gobject_move_ref(m, mainBox) + + @debug "Type constructed" + + push!(m["boxTD"],m.cTD) + + show(m.cTD) + show(m["boxTD"]) + + draw(m.cTD) + + m.cTD.hexpand = true + m.cTD.vexpand = true + + push!(m["boxFD"],m.cFD) + + m.cFD.hexpand = true + m.cFD.vexpand = true + + @debug "InitCallbacks" + + initCallbacks(m) + + @info "Finished starting RawDataWidget" + + return m +end + +function initCallbacks(m_::RawDataWidget) + let m=m_ + for sl in ["adjPatch","adjRxChan"] + signal_connect(m[sl], "value_changed") do w + showData(C_NULL, m) + end + #signal_connect(showData, m[sl], "value_changed", Nothing, (), false, m ) + end + + signal_connect(m["adjMinTP"], "value_changed") do w + minTP = get_gtk_property(m["adjMinTP"],:value, Int) + maxTP = get_gtk_property(m["adjMaxTP"],:value, Int) + maxValTP = get_gtk_property(m["adjMaxTP"],:upper, Int) + + if minTP > maxTP + @idle_add_guarded set_gtk_property!(m["adjMaxTP"],:value, min(maxValTP,minTP+10)) + else + showData(C_NULL, m) + end + end + + signal_connect(m["adjMaxTP"], "value_changed") do w + minTP = get_gtk_property(m["adjMinTP"],:value, Int) + maxTP = get_gtk_property(m["adjMaxTP"],:value, Int) + + if minTP > maxTP + @idle_add_guarded set_gtk_property!(m["adjMinTP"],:value, max(1,maxTP-10)) + else + showData(C_NULL, m) + end + end + + signal_connect(m["adjMinFre"], "value_changed") do w + minFre = get_gtk_property(m["adjMinFre"],:value, Int) + maxFre = get_gtk_property(m["adjMaxFre"],:value, Int) + maxValFre = get_gtk_property(m["adjMaxFre"],:upper, Int) + + if minFre > maxFre + @idle_add_guarded set_gtk_property!(m["adjMaxFre"],:value, min(maxValFre,minFre+10)) + else + showData(C_NULL, m) + end + end + + signal_connect(m["adjMaxFre"], "value_changed") do w + minFre = get_gtk_property(m["adjMinFre"],:value, Int) + maxFre = get_gtk_property(m["adjMaxFre"],:value, Int) + + if minFre > maxFre + @idle_add_guarded set_gtk_property!(m["adjMinFre"],:value, max(1,maxFre-10)) + else + showData(C_NULL, m) + end + end + + oldAdjPatchAvValue = 1 + signal_connect(m["adjPatchAv"], "value_changed") do w + if !m.updatingData + m.updatingData = true + patchAv = max(get_gtk_property(m["adjPatchAv"], :value, Int64),1) + numPatches = size(m.data,3) + if mod(numPatches, patchAv) != 0 + if 1 < patchAv < numPatches + while mod(numPatches, patchAv) != 0 + patchAv += sign(patchAv-oldAdjPatchAvValue)*1 + end + elseif patchAv < 1 + patchAv = 1 + elseif patchAv > numPatches + patchAv = numPatches + end + oldAdjPatchAvValue = patchAv + + @idle_add_guarded begin + set_gtk_property!(m["adjPatchAv"], :value, patchAv) + showAllPatchesChanged(m) + end + else + @idle_add_guarded showAllPatchesChanged(m) + end + oldAdjPatchAvValue = patchAv + m.updatingData = false + end + end + + for cb in ["cbShowBG", "cbSubtractBG", "cbShowFreq", "cbReversePlots"] + signal_connect(m[cb], :toggled) do w + showData(C_NULL, m) + end + #signal_connect(showData, m[cb], "toggled", Nothing, (), false, m) + end + + signal_connect(m["cbShowAllPatches"], :toggled) do w + @idle_add_guarded begin + showAllPatchesChanged(m) + end + end + + @guarded function showAllPatchesChanged(m) + m.updatingData = true + showAllPatches = get_gtk_property(m["cbShowAllPatches"], :active, Bool) + patchAv = max(get_gtk_property(m["adjPatchAv"], :value, Int64),1) + numPatches = div(size(m.data,3), patchAv) + + maxValTP = showAllPatches ? size(m.data,1)*numPatches : size(m.data,1) + maxValFre = div(maxValTP,2)+1 + + set_gtk_property!(m["adjMinTP"],:upper,maxValTP) + set_gtk_property!(m["adjMinTP"],:value,1) + set_gtk_property!(m["adjMaxTP"],:upper,maxValTP) + set_gtk_property!(m["adjMaxTP"],:value,maxValTP) + + set_gtk_property!(m["adjMinFre"],:upper,maxValFre) + set_gtk_property!(m["adjMinFre"],:value,1) + set_gtk_property!(m["adjMaxFre"],:upper,maxValFre) + set_gtk_property!(m["adjMaxFre"],:value,maxValFre) + m.updatingData = false + showData(C_NULL, m) + end + + + for cb in ["cbCorrTF","cbSLCorr","cbAbsFrameAverage"] + signal_connect(m[cb], :toggled) do w + loadData(C_NULL, m) + end + end + + for cb in ["adjFrame"] + signal_connect(m[cb], "value_changed") do w + loadData(C_NULL, m) + end + end + + for sl in ["entTDMinVal","entTDMaxVal","entFDMinVal","entFDMaxVal"] + signal_connect(m[sl], "changed") do w + showData(C_NULL, m) + end + #signal_connect(showData, m[sl], "value_changed", Nothing, (), false, m ) + end + + for sl in ["TD", "FD"] + signal_connect(m["btn$(sl)Apply"], "clicked") do w + if !m.updatingData + @idle_add_guarded begin + m.updatingData = true + r = (sl == "TD") ? m.rangeTD : m.rangeFD + set_gtk_property!( m["ent$(sl)MinVal"] ,:text, string(r[1])) + set_gtk_property!( m["ent$(sl)MaxVal"] ,:text, string(r[2])) + m.updatingData = false + showData(C_NULL, m) + end + end + end + + signal_connect(m["btn$(sl)Clear"], "clicked") do w + if !m.updatingData + @idle_add_guarded begin + m.updatingData = true + set_gtk_property!( m["ent$(sl)MinVal"] ,:text, "") + set_gtk_property!( m["ent$(sl)MaxVal"] ,:text, "") + m.updatingData = false + showData(C_NULL, m) + end + end + end + end + + #signal_connect(loadData, m["cbCorrTF"], "toggled", Nothing, (), false, m) + end +end + + + +@guarded function loadData(widgetptr::Ptr, m::RawDataWidget) + if !m.loadingData + m.loadingData = true + @info "Loading Data ..." + deltaT = 1.0 + + if m.filenamesData != [""] && all(ispath.(m.filenamesData)) + fs = MPIFile(m.filenamesData) #, isCalib=false) + + # TODO: Ensure that the measurements fit together (num samples / patches) + # otherwise -> error + + numFGFrames = minimum(acqNumFGFrames.(fs)) + numBGFrames = minimum(acqNumBGFrames.(fs)) + + dataFGVec = Any[] + dataBGVec = Any[] + + for (i,f) in enumerate(fs) + params = MPIFiles.loadMetadata(f) + params[:acqNumFGFrames] = acqNumFGFrames(f) + params[:acqNumBGFrames] = acqNumBGFrames(f) + + @idle_add_guarded set_gtk_property!(m["adjFrame"], :upper, numFGFrames) + + if get_gtk_property(m["cbAbsFrameAverage"], :active, Bool) + frame = 1:numFGFrames + else + frame = max( get_gtk_property(m["adjFrame"], :value, Int64), 1) + end + + timePoints = rxTimePoints(f) + deltaT = timePoints[2] - timePoints[1] + + data = getMeasurements(f, true, frames=frame, + bgCorrection=false, spectralLeakageCorrection = get_gtk_property(m["cbSLCorr"], :active, Bool), + tfCorrection=get_gtk_property(m["cbCorrTF"], :active, Bool)) + push!(dataFGVec, data) + + if acqNumBGFrames(f) > 0 + dataBG = getMeasurements(f, false, frames=measBGFrameIdx(f), + bgCorrection=false, spectralLeakageCorrection = get_gtk_property(m["cbSLCorr"], :active, Bool), + tfCorrection=get_gtk_property(m["cbCorrTF"], :active, Bool)) + else + dataBG = zeros(Float32,0,0,0,0) + end + push!(dataBGVec, dataBG) + end + + + m.dataBG = cat(dataBGVec..., dims=5) + dataFG = cat(dataFGVec..., dims=5) + m.labels = ["expnum "*string(experimentNumber(f)) for f in fs] + + updateData(m, dataFG, deltaT, true) + end + m.loadingData = false + end + return nothing +end + + +@guarded function showData(widgetptr::Ptr, m::RawDataWidget) + + if length(m.data) > 0 && !m.updatingData + chan = max(get_gtk_property(m["adjRxChan"], :value, Int64),1) + patch = max(get_gtk_property(m["adjPatch"], :value, Int64),1) + minTP = max(get_gtk_property(m["adjMinTP"], :value, Int64),1) + maxTP = max(get_gtk_property(m["adjMaxTP"], :value, Int64),1) + minFr = max(get_gtk_property(m["adjMinFre"], :value, Int64),1) + maxFr = max(get_gtk_property(m["adjMaxFre"], :value, Int64),1) + patchAv = max(get_gtk_property(m["adjPatchAv"], :value, Int64),1) + numPatches = div(size(m.data,3), patchAv) + numSignals = size(m.data,5) + showFD = get_gtk_property(m["cbShowFreq"], :active, Bool) + reversePlots = get_gtk_property(m["cbReversePlots"], :active, Bool) + + autoRangingTD = true + autoRangingFD = true + minValTD_ = tryparse(Float64,get_gtk_property( m["entTDMinVal"] ,:text,String)) + maxValTD_ = tryparse(Float64,get_gtk_property( m["entTDMaxVal"] ,:text,String)) + minValFD_ = tryparse(Float64,get_gtk_property( m["entFDMinVal"] ,:text,String)) + maxValFD_ = tryparse(Float64,get_gtk_property( m["entFDMaxVal"] ,:text,String)) + + if minValTD_ != nothing && maxValTD_ != nothing + minValTD = minValTD_ + maxValTD = maxValTD_ + autoRangingTD = false + end + + if minValFD_ != nothing && maxValFD_ != nothing + minValFD = minValFD_ + maxValFD = maxValFD_ + autoRangingFD = false + end + + if get_gtk_property(m["cbShowAllPatches"], :active, Bool) + data = vec( mean( reshape(m.data[:,chan,:,1,:],:, patchAv, numPatches, numSignals), dims=2) ) + if length(m.dataBG) > 0 + dataBG = vec(mean(reshape(m.dataBG[:,chan,:,:,:],size(m.dataBG,1), patchAv, numPatches, :, numSignals), dims=(2,4)) ) + + if get_gtk_property(m["cbSubtractBG"], :active, Bool) + data[:] .-= dataBG + end + end + else + if get_gtk_property(m["cbAbsFrameAverage"], :active, Bool) + dataFD = rfft(m.data[:,chan,patch,:,:],1) + dataFD_ = reshape(mean(abs.(dataFD), dims=2),:,numSignals) + data = irfft(dataFD_, 2*size(dataFD_, 1) -2, 1) + + if length(m.dataBG) > 0 + dataBGFD = rfft(m.dataBG[:,chan,patch,:,:], 1) + dataBGFD_ = reshape(mean(abs.(dataBGFD), dims=2), :, numSignals) + dataBG = irfft(dataBGFD_, 2*size(dataBGFD_, 1) -2, 1) + if get_gtk_property(m["cbSubtractBG"], :active, Bool) + data .-= dataBG + end + end + else + data = vec(m.data[:,chan,patch,1,:]) + if length(m.dataBG) > 0 + #dataBG = vec(m.dataBG[:,chan,patch,1] .- mean(m.dataBG[:,chan,patch,:], dims=2)) + dataBG = vec( mean(m.dataBG[:,chan,patch,:,:],dims=2)) + if get_gtk_property(m["cbSubtractBG"], :active, Bool) + data[:] .-= dataBG + end + end + end + end + + data = reshape(data, :, numSignals) + if reversePlots + reverse!(data, dims=2) + labels_ = reverse(m.labels) + else + labels_ = m.labels + end + if length(m.dataBG) > 0 && get_gtk_property(m["cbShowBG"], :active, Bool) + dataBG = reshape(dataBG, :, numSignals) + end + m.rangeTD = extrema(data) + + #colors = ["blue", "red", "green", "yellow", "black", "cyan", "magenta"] + + timePoints = (0:(size(data,1)-1)).*m.deltaT + numFreq = floor(Int, size(data,1) ./ 2 .+ 1) + + maxPoints = 5000 + sp = length(minTP:maxTP) > maxPoints ? round(Int,length(minTP:maxTP) / maxPoints) : 1 + + steps = minTP:sp:maxTP + dataCompressed = zeros(length(steps), size(data,2)) + if sp > 1 + for j=1:size(data,2) + for l=1:length(steps) + st = steps[l] + en = min(st+sp,steps[end]) + med_ = median(data[st:en,j]) + max_ = maximum(data[st:en,j]) + min_ = minimum(data[st:en,j]) + dataCompressed[l,j] = rand(Bool) ? max_ : min_ #abs(max_ - med_) > abs(med_ - min_) ? max_ : min_ + end + end + else + dataCompressed = data[steps,:] + end + + fTD, axTD, lTD1 = CairoMakie.lines(timePoints[steps], dataCompressed[:,1], + figure = (; figure_padding=4, size = (1000, 800), fontsize = 11), + axis = (; title = "Time Domain"), + color = CairoMakie.RGBf(colors[1]...), + label = labels_[1]) + for j=2:size(data,2) + CairoMakie.lines!(axTD, timePoints[steps],dataCompressed[:,j], + color = CairoMakie.RGBf(colors[j]...), #linewidth=3) + label = labels_[j]) + end + if length(m.dataBG) > 0 && get_gtk_property(m["cbShowBG"], :active, Bool) + CairoMakie.lines!(axTD, timePoints[minTP:sp:maxTP],dataBG[minTP:sp:maxTP,1], color=:black, + label="BG", linestyle = :dash) + end + CairoMakie.autolimits!(axTD) + if timePoints[steps[end]] > timePoints[steps[1]] + CairoMakie.xlims!(axTD, timePoints[steps[1]], timePoints[steps[end]]) + end + if !autoRangingTD && maxValTD > minValTD + CairoMakie.ylims!(axTD, minValTD, maxValTD) + end + axTD.xlabel = "t / ms" + axTD.ylabel = "u / V" + + if (size(data,2) > 1 && length(m.labels) == size(data,2)) || + (length(m.dataBG) > 0 && get_gtk_property(m["cbShowBG"], :active, Bool)) + CairoMakie.axislegend() + end + + if showFD + freq = collect(0:(numFreq-1))./(numFreq-1)./m.deltaT./2.0 + freqdata = abs.(rfft(data, 1)) / size(data,1) + m.rangeFD = extrema(freqdata) + spFr = length(minFr:maxFr) > maxPoints ? round(Int,length(minFr:maxFr) / maxPoints) : 1 + + stepsFr = minFr:spFr:maxFr + freqDataCompressed = zeros(length(stepsFr), size(freqdata,2)) + if spFr > 1 + for j=1:size(freqdata,2) + for l=1:length(stepsFr) + st = stepsFr[l] + en = min(st+spFr,stepsFr[end]) + freqDataCompressed[l,j] = maximum(freqdata[st:en,j]) + end + end + else + freqDataCompressed = freqdata[stepsFr,:] + end + + fFD, axFD, lFD1 = CairoMakie.lines(freq[stepsFr],freqDataCompressed[:,1], + figure = (; figure_padding=4, size = (1000, 800), fontsize = 11), + axis = (; title = "Frequency Domain", yscale=log10), + color = CairoMakie.RGBf(colors[1]...), + label=labels_[1]) + for j=2:size(data,2) + CairoMakie.lines!(axFD, freq[stepsFr], freqDataCompressed[:,j], + color = CairoMakie.RGBf(colors[j]...), label=labels_[j]) + end + if length(m.dataBG) > 0 && get_gtk_property(m["cbShowBG"], :active, Bool) + CairoMakie.lines!(axTD, timePoints[minTP:sp:maxTP],dataBG[minTP:sp:maxTP,1], color=:black, + label="BG", linestyle = :dash) + if showFD + CairoMakie.lines!(axFD, freq[minFr:spFr:maxFr],abs.(rfft(dataBG,1)[minFr:spFr:maxFr,1]) / size(dataBG,1), + color=:black, label="BG", linestyle = :dash) + end + end + CairoMakie.autolimits!(axFD) + if freq[stepsFr[end]] > freq[stepsFr[1]] + CairoMakie.xlims!(axFD, freq[stepsFr[1]], freq[stepsFr[end]]) + end + if !autoRangingFD && maxValFD > minValFD + CairoMakie.ylims!(axFD, minValFD, maxValFD) + end + axFD.xlabel = "f / kHz" + axFD.ylabel = "u / V" + + + else + @guarded Gtk4.draw(m.cFD) do widget + + ctx = getgc(m.cFD) + h = height(ctx) + w = width(ctx) + Cairo.set_source_rgb(ctx,1.0,1.0,1.0) + Cairo.rectangle(ctx, 0,0,w,h) + Cairo.paint(ctx) + Cairo.stroke(ctx) + end + end + + @idle_add_guarded drawonto(m.cTD, fTD) + if showFD + @idle_add_guarded drawonto(m.cFD, fFD) + end + + end + return nothing +end + +function setBG(m::RawDataWidget, dataBG) + if ndims(dataBG) == 5 + m.dataBG = dataBG + else + m.dataBG = reshape(dataBG, size(dataBG)..., 1) + end +end + +@guarded function updateData(m::RawDataWidget, data::Array, deltaT=1.0, fileModus=false) + maxValTPOld = get_gtk_property(m["adjMinTP"],:upper, Int64) + maxValFreOld = get_gtk_property(m["adjMinFre"],:upper, Int64) + + if ndims(data) == 5 + m.data = data + else + m.data = reshape(data, size(data)..., 1) + end + m.deltaT = deltaT .* 1000 # convert to ms and kHz + m.fileModus = fileModus + + if !isempty(m.dataBG) + if size(m.data)[1:3] != size(m.dataBG)[1:3] + @info "Background data does not fit to foreground data! Dropping BG data." + @info size(m.data) + @info size(m.dataBG) + m.dataBG = zeros(Float32,0,0,0,0,0) + end + end + + showAllPatches = get_gtk_property(m["cbShowAllPatches"], :active, Bool) + patchAv = max(get_gtk_property(m["adjPatchAv"], :value, Int64),1) + numPatches = div(size(m.data,3), patchAv) + maxValTP = showAllPatches ? size(m.data,1)*numPatches : size(m.data,1) + maxValFre = div(maxValTP,2)+1 + + @idle_add_guarded begin + m.updatingData = true + if !fileModus + set_gtk_property!(m["adjFrame"],:upper,size(data,4)) + if !(1 <= get_gtk_property(m["adjFrame"],:value,Int64) <= size(data,4)) + set_gtk_property!(m["adjFrame"],:value,1) + end + end + set_gtk_property!(m["adjRxChan"],:upper,size(data,2)) + if !(1 <= get_gtk_property(m["adjRxChan"],:value,Int64) <= size(data,2)) + set_gtk_property!(m["adjRxChan"],:value,1) + end + set_gtk_property!(m["adjPatch"],:upper,size(data,3)) + if !(1 <= get_gtk_property(m["adjPatch"],:value,Int64) <= size(data,3)) + set_gtk_property!(m["adjPatch"],:value,1) + end + set_gtk_property!(m["adjMinTP"],:upper,maxValTP) + if !(1 <= get_gtk_property(m["adjMinTP"],:value,Int64) <= maxValTP) || maxValTP != maxValTPOld + set_gtk_property!(m["adjMinTP"],:value,1) + end + set_gtk_property!(m["adjMaxTP"],:upper, maxValTP) + if !(1 <= get_gtk_property(m["adjMaxTP"],:value,Int64) <= maxValTP) || maxValTP != maxValTPOld + set_gtk_property!(m["adjMaxTP"],:value, maxValTP) + end + set_gtk_property!(m["adjMinFre"],:upper, maxValFre) + if !(1 <= get_gtk_property(m["adjMinFre"],:value,Int64) <= maxValFre) || maxValFre != maxValFreOld + set_gtk_property!(m["adjMinFre"],:value,1) + end + set_gtk_property!(m["adjMaxFre"],:upper, maxValFre) + if !(1 <= get_gtk_property(m["adjMaxFre"],:value,Int64) <= maxValFre) || maxValFre != maxValFreOld + set_gtk_property!(m["adjMaxFre"],:value, maxValFre) + end + + m.updatingData = false + showData(C_NULL, m) + end +end + +@guarded function updateData(m::RawDataWidget, filenames::Vector{<:AbstractString}) + m.filenamesData = filenames + @idle_add_guarded begin + m.updatingData = true + set_gtk_property!(m["adjFrame"],:upper,1) + set_gtk_property!(m["adjFrame"],:value,1) + set_gtk_property!(m["adjPatch"],:upper,1) + set_gtk_property!(m["adjPatch"],:value,1) + set_gtk_property!(m["adjPatchAv"],:upper,1) + set_gtk_property!(m["adjPatchAv"],:value,1) + m.updatingData = false + loadData(C_NULL, m) + end + return nothing +end + +@guarded function updateData(m::RawDataWidget, filename::String) + updateData(m, [filename]) + return nothing +end diff --git a/MPIViewers/src/SFViewerWidget.jl b/MPIViewers/src/SFViewerWidget.jl new file mode 100644 index 0000000..93fff2d --- /dev/null +++ b/MPIViewers/src/SFViewerWidget.jl @@ -0,0 +1,369 @@ +export SFViewer +mutable struct SFViewerWidget <: Gtk4.GtkPaned + handle::Ptr{Gtk4.GObject} + builder::GtkBuilder + dv::DataViewerWidget + bSF::MPIFile + updating::Bool + maxFreq::Int + maxChan::Int + SNR::Array{Float64,3} + freqIndices::Vector{CartesianIndex{2}} + SNRSortedIndices::Array{Int64,1} + SNRSortedIndicesInverse::Array{Int64,1} + SNRSortedIndicesRecChan::Array{Array{Int64,1},1} + SNRSortedIndicesRecChanInverse::Array{Array{Int64,1},1} + mixFac::Array{Int64,2} + mxyz::Array{Int64,1} + frequencies::Array{Float64,1} + frequencySelection::Array{Int,1} + grid::Gtk4.GtkGridLeaf +end + +getindex(m::SFViewerWidget, w::AbstractString) = Gtk4.G_.get_object(m.builder, w) + +mutable struct SFViewer + w::Gtk4.GtkWindowLeaf + sf::SFViewerWidget +end + +function SFViewer(filename::AbstractString) + sfViewerWidget = SFViewerWidget() + w = GtkWindow("SF Viewer: $(filename)",800,600) + push!(w,sfViewerWidget) + show(w) + updateData!(sfViewerWidget, filename) + return SFViewer(w, sfViewerWidget) +end + +function SFViewerWidget() + uifile = joinpath(@__DIR__,"..", "..", "src", "builder","mpiLab.ui") # very hack since ui file is in other package + + b = GtkBuilder(uifile) + mainBox = GtkPaned(:h) + + m = SFViewerWidget(mainBox.handle, b, DataViewerWidget(), + BrukerFile(), false, 0, 0, zeros(0,0,0), CartesianIndex{2}[], + zeros(0), zeros(0), zeros(0), zeros(0), zeros(0,0), zeros(0), zeros(0), zeros(Int,0), GtkGrid()) + Gtk4.GLib.gobject_move_ref(m, mainBox) + + m.grid[1,1:2] = m.dv + m.grid[1,3] = GtkCanvas() + set_gtk_property!(m.grid, :row_homogeneous, true) + set_gtk_property!(m.grid[1,2], :height_request, 200) + + m[1] = m.grid + m[2] = m["swSFViewer"] + + Gtk4.resize_start_child(m, true) + Gtk4.shrink_start_child(m, true) + Gtk4.resize_end_child(m, false) + Gtk4.shrink_end_child(m, false) + + G_.set_size_request(m["swSFViewer"], 250, -1) + + function updateSFMixO( widget ) + if !m.updating + @idle_add_guarded begin + m.updating = true + mx = get_gtk_property(m["adjSFMixX"],:value, Int64) + my = get_gtk_property(m["adjSFMixY"],:value, Int64) + mz = get_gtk_property(m["adjSFMixZ"],:value, Int64) + + freq = 0 + m_ = [mx,my,mz] + for d=1:length(m.mxyz) + freq += m_[d]*m.mxyz[d] + end + + freq = clamp(freq,0,m.maxFreq-1) + updateFreq(m, freq) + updateSigOrd(m) + updateSF(m) + m.updating = false + end + end + end + + function updateSFSignalOrdered( widget ) + if !m.updating + @idle_add_guarded begin + m.updating = true + if !(get_gtk_property(m["cbFixRecChan"],:active, Bool)) + k = m.SNRSortedIndices[get_gtk_property(m["adjSFSignalOrdered"],:value, Int64)] + recChan = m.freqIndices[k][2] + else + # fix the current receive channel for ordered signal + recChan = get_gtk_property(m["adjSFRecChan"],:value, Int64) + k = m.SNRSortedIndicesRecChan[recChan][get_gtk_property(m["adjSFSignalOrdered"],:value, Int64)] + recChan = m.freqIndices[k][2] + end + + freq = m.freqIndices[k][1] + updateFreq(m, freq) + updateRecChan(m, recChan) + updateMix(m) + updateSF(m) + m.updating = false + + end + end + end + + # BG correction + signal_connect(m["cbSFBGCorr"], :toggled) do w + @idle_add_guarded updateSF(m) + end + + # TF correction + signal_connect(m["cbSFTFCorr"], :toggled) do w + @idle_add_guarded updateSF(m) + end + + signal_connect(m["adjSFPatch"], "value_changed") do w + @idle_add_guarded updateSF(m) + end + + signal_connect(m["adjSNRMinFreq"], "value_changed") do w + @idle_add_guarded updateSF(m) + end + + signal_connect(m["adjSNRMaxFreq"], "value_changed") do w + @idle_add_guarded updateSF(m) + end + + signal_connect(m["adjSFRecChan"], "value_changed") do w + if !m.updating + @idle_add_guarded begin + m.updating = true + updateMix(m) + updateSigOrd(m) + updateSF(m) + m.updating = false + end + end + end + signal_connect(m["adjSFFreq"], "value_changed") do w + if !m.updating + @idle_add_guarded begin + m.updating = true + updateMix(m) + updateSigOrd(m) + updateSF(m) + m.updating = false + end + end + end + + for w in Any["adjSFMixX","adjSFMixY","adjSFMixZ"] + signal_connect(updateSFMixO, m[w], "value_changed") + end + + signal_connect(m["cbFixRecChan"], :toggled) do w + @idle_add_guarded updateSigOrd(m) + end + signal_connect(updateSFSignalOrdered, m["adjSFSignalOrdered"], "value_changed") + + signal_connect(m["btnRecalcSNR"], :clicked) do w + @idle_add_guarded recalcSNR(m) + end + + return m +end + + +function updateFreq(m::SFViewerWidget, freq) + set_gtk_property!(m["adjSFFreq"],:value, freq) +end + +function updateRecChan(m::SFViewerWidget, recChan) + set_gtk_property!(m["adjSFRecChan"],:value, recChan) +end + +function updateSigOrd(m::SFViewerWidget) + freq = get_gtk_property(m["adjSFFreq"],:value, Int64)+1 + recChan = get_gtk_property(m["adjSFRecChan"],:value, Int64) + if !(get_gtk_property(m["cbFixRecChan"],:active, Bool)) + k = freq + m.maxFreq*((recChan-1)) + set_gtk_property!(m["adjSFSignalOrdered"],:value, m.SNRSortedIndicesInverse[k] ) + else + # fix the current receive channel for ordered signal + set_gtk_property!(m["adjSFSignalOrdered"],:value, m.SNRSortedIndicesRecChanInverse[recChan][freq] ) + end +end + +function updateMix(m::SFViewerWidget) + freq = get_gtk_property(m["adjSFFreq"],:value, Int64)+1 + set_gtk_property!(m["adjSFMixX"],:value, m.mixFac[freq,1]) + set_gtk_property!(m["adjSFMixY"],:value, m.mixFac[freq,2]) + set_gtk_property!(m["adjSFMixZ"],:value, m.mixFac[freq,3]) +end + + +function updateSF(m::SFViewerWidget) + freq = get_gtk_property(m["adjSFFreq"],:value, Int64)+1 + recChan = get_gtk_property(m["adjSFRecChan"],:value, Int64) + period = get_gtk_property(m["adjSFPatch"],:value, Int64) + minFr = get_gtk_property(m["adjSNRMinFreq"],:value, Int64)+1 + maxFr = get_gtk_property(m["adjSNRMaxFreq"],:value, Int64)+1 + + # BG correction + bgcorrection = get_gtk_property(m["cbSFBGCorr"],:active, Bool) + # disable BG correction if no BG frames are available + if maximum(Int.(measIsBGFrame(m.bSF))) == 0 + bgcorrection = false + set_gtk_property!(m["cbSFBGCorr"],:active,false) + end + + # TF correction + tfcorrection = get_gtk_property(m["cbSFTFCorr"],:active, Bool) + + k = CartesianIndex(freq, recChan) + + if !measIsFrequencySelection(m.bSF) || k in m.frequencySelection + sfData_ = getSF(m.bSF, [k], returnasmatrix = true, bgcorrection=bgcorrection, tfCorrection=tfcorrection)[1][:,period] + sfData_[:] ./= rxNumSamplingPoints(m.bSF) + else + # set sfData to one for frequencies ∉ frequencySelection + sfData_ = ones(ComplexF32,prod(calibSize(m.bSF))) + end + + sfData = reshape(sfData_, calibSize(m.bSF)...) + + set_gtk_property!(m["entSFSNR"],:text,string(round(m.SNR[freq,recChan,period],digits=2))) + #set_gtk_property!(m["entSFSNR2"],:text,string(round(calcSNRF(sfData_),digits=2))) + snr5 = [string(sum(m.SNR[:,d,1] .> 5)," ") for d=1:size(m.SNR,2)] + set_gtk_property!(m["entSFSNR2"],:text, prod( snr5 ) ) + + + maxPoints = 5000 + spFr = length(minFr:maxFr) > maxPoints ? round(Int,length(minFr:maxFr) / maxPoints) : 1 + + stepsFr = minFr:spFr:maxFr + snrCompressed = zeros(length(stepsFr)) + if spFr > 1 + for l=1:length(stepsFr) + st = stepsFr[l] + en = min(st+spFr,stepsFr[end]) + snrCompressed[l] = maximum(m.SNR[st:en,recChan,period]) + end + else + snrCompressed = vec(m.SNR[stepsFr,recChan,period]) + end + + fFD, axFD, lFD1 = CairoMakie.lines(m.frequencies[stepsFr], snrCompressed, + figure = (; size = (1000, 800), fontsize = 12), + axis = (; title = "SNR", yscale=log10), + color = CairoMakie.RGBf(colors[1]...)) + CairoMakie.scatter!(axFD, [m.frequencies[freq]], [m.SNR[freq,recChan,period]], + markersize=9, color=:red, marker=:xcross) + + CairoMakie.autolimits!(axFD) + if m.frequencies[stepsFr[end]] > m.frequencies[stepsFr[1]] + CairoMakie.xlims!(axFD, m.frequencies[stepsFr[1]], m.frequencies[stepsFr[end]]) + end + axFD.xlabel = "f / kHz" + + drawonto(m.grid[1,3], fFD) + + show(m) + + c = reshape(sfData, 1, size(sfData,1), size(sfData,2), size(sfData,3), 1) + c_ = cat(abs.(c),angle.(c), dims=1) + im = AxisArray(c_, (:color,:x,:y,:z,:time), + tuple(1.0, 1.0, 1.0, 1.0, 1.0), + tuple(0.0, 0.0, 0.0, 0.0, 0.0)) + + imMeta = ImageMeta(im, Dict{Symbol,Any}()) + + updateData!(m.dv, imMeta, ampPhase=true) +end + +function updateData!(m::SFViewerWidget, filenameSF::String) + m.bSF = MPIFile(filenameSF, fastMode=true) + m.maxChan = rxNumChannels(m.bSF) + m.frequencies = rxFrequencies(m.bSF)./1000 + m.maxFreq = length(m.frequencies) + if measIsFrequencySelection(m.bSF) + # Workaround for FrequencySelection + m.frequencySelection = vcat([measFrequencySelection(m.bSF).+i*m.maxFreq for i=0:m.maxChan-1]...) + end + m.updating = true + set_gtk_property!(m["adjSFFreq"],:value, 2 ) + set_gtk_property!(m["adjSFFreq"],:upper, m.maxFreq-1 ) + if measIsFrequencySelection(m.bSF) + # first frequency of frequencySelection + set_gtk_property!(m["adjSFFreq"],:value, m.frequencySelection[1]-1 ) + end + set_gtk_property!(m["adjSFSignalOrdered"],:value, 1 ) + set_gtk_property!(m["adjSFSignalOrdered"],:upper, m.maxFreq*m.maxChan ) + set_gtk_property!(m["adjSFMixX"],:value, 0 ) + set_gtk_property!(m["adjSFMixY"],:value, 0 ) + set_gtk_property!(m["adjSFMixZ"],:value, 0 ) + set_gtk_property!(m["adjSFRecChan"],:value, 1 ) + set_gtk_property!(m["adjSFRecChan"],:upper, m.maxChan ) + set_gtk_property!(m["adjSFPatch"],:value, 1 ) + set_gtk_property!(m["adjSFPatch"],:upper, acqNumPeriodsPerFrame(m.bSF) ) + set_gtk_property!(m["adjSNRMinFreq"],:upper, m.maxFreq-1 ) + set_gtk_property!(m["adjSNRMaxFreq"],:upper, m.maxFreq-1 ) + set_gtk_property!(m["adjSNRMinFreq"],:value, 0 ) + set_gtk_property!(m["adjSNRMaxFreq"],:value, m.maxFreq-1 ) + + + m.SNR = calibSNR(m.bSF)[:,:,:] + if measIsFrequencySelection(m.bSF) + # set SNR to zero for frequencies ∉ frequencySelection + snr = zeros(Float64, m.maxFreq*size(m.SNR,2), size(m.SNR,3)) + snr[m.frequencySelection,:] = reshape(calibSNR(m.bSF), size(m.SNR,1)*size(m.SNR,2), :) + m.SNR = reshape(snr, m.maxFreq, size(m.SNR,2), size(m.SNR,3)) + end + + m.freqIndices = collect(vec(CartesianIndices((m.maxFreq, m.maxChan)))) + + updateDerivedSNRLUTs(m) + + m.mixFac = MPIFiles.mixingFactors(m.bSF) + mxyz, mask, freqNumber = MPIFiles.calcPrefactors(m.bSF) + m.mxyz = mxyz + + set_gtk_property!(m["adjSFMixX"],:upper, maximum(m.mixFac[:, 1])) + set_gtk_property!(m["adjSFMixY"],:upper, maximum(m.mixFac[:, 2])) + set_gtk_property!(m["adjSFMixZ"],:upper, maximum(m.mixFac[:, 3])) + + + # show frequency component with highest SNR + k = m.freqIndices[m.SNRSortedIndices[1]] + recChan = k[2] + freq = k[1] + updateFreq(m, freq) + updateRecChan(m, recChan) + + # disable TF correction button if no TF available + if !rxHasTransferFunction(m.bSF) + set_gtk_property!(m["cbSFTFCorr"], :sensitive, false) + set_gtk_property!(m["cbSFTFCorr"], :active, false) + end + + updateMix(m) + updateSigOrd(m) + updateSF(m) + m.updating = false +end + +function recalcSNR(m) + @info "Recalculate SNR" + m.updating = true + m.SNR = calculateSystemMatrixSNR(m.bSF) + updateDerivedSNRLUTs(m) + m.updating = false + updateSF(m) +end + +function updateDerivedSNRLUTs(m) + m.SNRSortedIndices = reverse(sortperm(vec(m.SNR))) + m.SNRSortedIndicesInverse = sortperm(m.SNRSortedIndices) + # sort SNR channel-wise + m.SNRSortedIndicesRecChan = [reverse(sortperm(m.SNR[:,i,1])) for i=1:m.maxChan] + m.SNRSortedIndicesRecChanInverse = [sortperm(snr) for snr in m.SNRSortedIndicesRecChan] + return +end diff --git a/MPIViewers/src/SimpleDataViewer.jl b/MPIViewers/src/SimpleDataViewer.jl new file mode 100644 index 0000000..ffd7958 --- /dev/null +++ b/MPIViewers/src/SimpleDataViewer.jl @@ -0,0 +1,91 @@ +export SimpleDataViewer, SimpleDataViewerWidget, simpleDrawImageCairo + +function SimpleDataViewer() + w = Window("Data Viewer",1024,768) + dw = SimpleDataViewerWidget() + push!(w,dw) + show(w) + return dw, w +end + +########### SimpleDataViewerWidget ################# + + +mutable struct SimpleDataViewerWidget <: Gtk4.GtkBox + handle::Ptr{Gtk4.GObject} + builder + grid3D + grid2D +end + +getindex(m::SimpleDataViewerWidget, w::AbstractString) = Gtk4.G_.get_object(m.builder, w) + + +function SimpleDataViewerWidget() + uifile = joinpath(@__DIR__,"builder","simpleDataViewer.ui") + b = GtkBuilder(uifile) + mainBox = Gtk4.G_.get_object(b, "boxSimpleDataViewer") + m = SimpleDataViewerWidget( mainBox.handle, b, nothing, nothing) + Gtk4.GLib.gobject_move_ref(m, mainBox) + + m.grid3D = m["gridDataViewer3D"] + m.grid2D = m["gridDataViewer2D"] + + m.grid3D[2,1] = GtkCanvas() + m.grid3D[1,1] = GtkCanvas() + m.grid3D[2,2] = GtkCanvas() + m.grid3D[1,2] = GtkCanvas() + m.grid2D[1,1] = GtkCanvas() + + return m +end + +function showData(m::SimpleDataViewerWidget, cdata_zy, cdata_zx, cdata_xy, drawSectionalLines, slices) + try + simpleDrawImageCairo(m.grid3D[2,1], cdata_zy, drawSectionalLines, + slices[2], slices[3], false, true) + simpleDrawImageCairo(m.grid3D[1,1], cdata_zx, drawSectionalLines, + slices[1], slices[3], true, true) + simpleDrawImageCairo(m.grid3D[2,2], cdata_xy, drawSectionalLines, + slices[2], slices[1], false, false) + + Gtk4.G_.set_current_page(m["nb2D3D"], 0) + catch ex + @warn "Exception" ex stacktrace(catch_backtrace()) + end +end + +function showData(m::SimpleDataViewerWidget, cdata) + try + pZ = drawImage( convert(Array,cdata.data) ) + display(m.grid2D[1,1],pZ) + Gtk4.G_.set_current_page(m["nb2D3D"], 1) + catch ex + @warn "Exception" ex stacktrace(catch_backtrace()) + end +end + +function simpleDrawImageCairo(c, image, drawSectionalLines, xsec, ysec, + flipX, flipY) + @guarded Gtk4.draw(c) do widget + ctx = getgc(c) + h = height(ctx) + w = width(ctx) + + im = reverse(convert(ImageMeta{RGB{N0f8}},image).data,dims=1) + xsec_ = !flipX ? xsec : (size(im,2)-xsec+1) + ysec_ = !flipY ? ysec : (size(im,1)-ysec+1) + xx = w*(xsec_-0.5)/size(im,2) + yy = h*(ysec_-0.5)/size(im,1) + copy!(ctx,im) + if drawSectionalLines + set_source_rgb(ctx, 0, 1, 0) + move_to(ctx, xx, 0) + line_to(ctx, xx, h) + move_to(ctx, 0, yy) + line_to(ctx, w, yy) + set_line_width(ctx, 3.0) + Cairo.stroke(ctx) + end # end if + end # guard +end # end function diff --git a/MPIViewers/src/SpectrogramViewer.jl b/MPIViewers/src/SpectrogramViewer.jl new file mode 100644 index 0000000..6fbe59f --- /dev/null +++ b/MPIViewers/src/SpectrogramViewer.jl @@ -0,0 +1,601 @@ +export SpectrogramViewer + +mutable struct SpectrogramWidget <: Gtk4.GtkBox + handle::Ptr{Gtk4.GObject} + builder::GtkBuilder + data::Array{Float32,5} + dataBG::Array{Float32,5} + labels::Vector{String} + cTD::GtkCanvas + cFD::GtkCanvas + cSpect::GtkCanvas + deltaT::Float64 + filenamesData::Vector{String} + updatingData::Bool + fileModus::Bool + rangeTD::NTuple{2,Float32} + rangeFD::NTuple{2,Float32} +end + +getindex(m::SpectrogramWidget, w::AbstractString) = Gtk4.G_.get_object(m.builder, w) + +mutable struct SpectrogramViewer + w::Gtk4.GtkWindowLeaf + sw::SpectrogramWidget +end + +function SpectrogramViewer(filename::AbstractString) + sw = SpectrogramWidget() + w = GtkWindow("Spectrogram Viewer: $(filename)",800,600) + push!(w, sw) + show(w) + updateData(sw, filename) + return SpectrogramViewer(w, sw) +end + +function SpectrogramWidget(filenameConfig=nothing) + @info "Starting SpectrogramWidget" + uifile = joinpath(@__DIR__,"builder","spectrogramViewer.ui") + + b = GtkBuilder(uifile) + mainBox = Gtk4.G_.get_object(b, "boxSpectrogramViewer") + + m = SpectrogramWidget( mainBox.handle, b, + zeros(Float32,0,0,0,0,0), zeros(Float32,0,0,0,0,0), + [""], GtkCanvas(), GtkCanvas(), GtkCanvas(), + 1.0, [""], false, false, + (0.0,1.0), (0.0,1.0)) + Gtk4.GLib.gobject_move_ref(m, mainBox) + + @debug "Type constructed" + + push!(m["boxTD"],m.cTD) + m.cTD.hexpand = true + m.cTD.vexpand = true + + pane = m["paned"] + set_gtk_property!(pane, :position, 300) + + push!(m["boxSpectro"], m.cSpect) + m.cSpect.hexpand = true + m.cSpect.vexpand = true + + push!(m["boxFD"],m.cFD) + m.cFD.hexpand = true + m.cFD.vexpand = true + + @debug "InitCallbacks" + + initCallbacks(m) + + @info "Finished starting SpectrogramWidget" + + return m +end + +function initCallbacks(m_::SpectrogramWidget) + let m=m_ + for sl in ["adjPatch","adjRxChan", "adjLogPlot"] + signal_connect(m[sl], "value_changed") do w + showData(C_NULL, m) + end + #signal_connect(showData, m[sl], "value_changed", Nothing, (), false, m ) + end + + signal_connect(m["adjMinTP"], "value_changed") do w + if !m.updatingData + minTP = get_gtk_property(m["adjMinTP"],:value, Int) + maxTP = get_gtk_property(m["adjMaxTP"],:value, Int) + maxValTP = get_gtk_property(m["adjMaxTP"],:upper, Int) + + if minTP > maxTP + @idle_add_guarded set_gtk_property!(m["adjMaxTP"],:value, min(maxValTP,minTP+10)) + else + showData(C_NULL, m) + end + end + end + + signal_connect(m["adjMaxTP"], "value_changed") do w + if !m.updatingData + minTP = get_gtk_property(m["adjMinTP"],:value, Int) + maxTP = get_gtk_property(m["adjMaxTP"],:value, Int) + + if minTP > maxTP + @idle_add_guarded set_gtk_property!(m["adjMinTP"],:value, max(1,maxTP-10)) + else + showData(C_NULL, m) + end + end + end + + signal_connect(m["adjMinFre"], "value_changed") do w + if !m.updatingData + minFre = get_gtk_property(m["adjMinFre"],:value, Int) + maxFre = get_gtk_property(m["adjMaxFre"],:value, Int) + maxValFre = get_gtk_property(m["adjMaxFre"],:upper, Int) + + if minFre > maxFre + @idle_add_guarded set_gtk_property!(m["adjMaxFre"],:value, min(maxValFre,minFre+10)) + else + showData(C_NULL, m) + end + end + end + + signal_connect(m["adjMaxFre"], "value_changed") do w + if !m.updatingData + minFre = get_gtk_property(m["adjMinFre"],:value, Int) + maxFre = get_gtk_property(m["adjMaxFre"],:value, Int) + + if minFre > maxFre + @idle_add_guarded set_gtk_property!(m["adjMinFre"],:value, max(1,maxFre-10)) + else + showData(C_NULL, m) + end + end + end + + @guarded function groupingChanged() + if !m.updatingData + @idle_add_guarded begin + m.updatingData = true + timedata, sp = getData(m) + + maxValTP = length(timedata[1]) + maxValFre = size(sp.power,1) + numPatches = size(sp.power,2) + + set_gtk_property!(m["adjMinTP"],:upper,maxValTP) + set_gtk_property!(m["adjMinTP"],:value,1) + set_gtk_property!(m["adjMaxTP"],:upper,maxValTP) + set_gtk_property!(m["adjMaxTP"],:value,maxValTP) + + set_gtk_property!(m["adjMinFre"],:upper,maxValFre) + set_gtk_property!(m["adjMinFre"],:value,1) + set_gtk_property!(m["adjMaxFre"],:upper,maxValFre) + set_gtk_property!(m["adjMaxFre"],:value,maxValFre) + + set_gtk_property!(m["adjPatch"],:upper,numPatches) + if get_gtk_property(m["adjPatch"],:value, Int) >= numPatches + set_gtk_property!(m["adjPatch"],:value, 1) + end + + m.updatingData = false + showData(C_NULL, m) + end + end + end + + signal_connect(m["adjGrouping"], "value_changed") do w + groupingChanged() + end + + oldAdjPatchAvValue = 1 + signal_connect(m["adjPatchAv"], "value_changed") do w + if !m.updatingData + m.updatingData = true + patchAv = max(get_gtk_property(m["adjPatchAv"], :value, Int64),1) + numPatches = size(m.data,3) + if mod(numPatches, patchAv) != 0 + if 1 < patchAv < numPatches + while mod(numPatches, patchAv) != 0 + patchAv += sign(patchAv-oldAdjPatchAvValue)*1 + end + elseif patchAv < 1 + patchAv = 1 + elseif patchAv > numPatches + patchAv = numPatches + end + oldAdjPatchAvValue = patchAv + + @idle_add_guarded begin + set_gtk_property!(m["adjPatchAv"], :value, patchAv) + groupingChanged() + end + else + @idle_add_guarded groupingChanged() + end + oldAdjPatchAvValue = patchAv + m.updatingData = false + end + end + + for cb in ["cbShowBG", "cbSubtractBG", "cbShowFreq"] + signal_connect(m[cb], :toggled) do w + showData(C_NULL, m) + end + #signal_connect(showData, m[cb], "toggled", Nothing, (), false, m) + end + + + for cb in ["cbShowAllFrames", "cbShowFixDistortions"] #"cbCorrTF","cbSLCorr","cbAbsFrameAverage" + signal_connect(m[cb], :toggled) do w + loadData(C_NULL, m) + end + end + + for cb in ["adjFrame"] + signal_connect(m[cb], "value_changed") do w + loadData(C_NULL, m) + end + end + + for sl in ["entTDMinVal","entTDMaxVal","entFDMinVal","entFDMaxVal"] + signal_connect(m[sl], "changed") do w + showData(C_NULL, m) + end + end + + + for sl in ["TD", "FD"] + signal_connect(m["btn$(sl)Apply"], "clicked") do w + if !m.updatingData + @idle_add_guarded begin + m.updatingData = true + r = (sl == "TD") ? m.rangeTD : m.rangeFD + set_gtk_property!( m["ent$(sl)MinVal"] ,:text, string(r[1])) + set_gtk_property!( m["ent$(sl)MaxVal"] ,:text, string(r[2])) + m.updatingData = false + showData(C_NULL, m) + end + end + end + + signal_connect(m["btn$(sl)Clear"], "clicked") do w + if !m.updatingData + @idle_add_guarded begin + m.updatingData = true + set_gtk_property!( m["ent$(sl)MinVal"] ,:text, "") + set_gtk_property!( m["ent$(sl)MaxVal"] ,:text, "") + m.updatingData = false + showData(C_NULL, m) + end + end + end + end + + #signal_connect(loadData, m["cbCorrTF"], "toggled", Nothing, (), false, m) + end +end + +@guarded function loadData(widgetptr::Ptr, m::SpectrogramWidget) + if !m.updatingData + m.updatingData = true + @info "Loading Data ..." + deltaT = 1.0 + + if m.filenamesData != [""] && all(ispath.(m.filenamesData)) + fs = MPIFile(m.filenamesData) #, isCalib=false) + + # TODO: Ensure that the measurements fit together (num samples / patches) + # otherwise -> error + + numFGFrames = minimum(acqNumFGFrames.(fs)) + numBGFrames = minimum(acqNumBGFrames.(fs)) + + dataFGVec = Any[] + dataBGVec = Any[] + + for (i,f) in enumerate(fs) + params = MPIFiles.loadMetadata(f) + params[:acqNumFGFrames] = acqNumFGFrames(f) + params[:acqNumBGFrames] = acqNumBGFrames(f) + + @idle_add_guarded set_gtk_property!(m["adjFrame"], :upper, numFGFrames) + + if get_gtk_property(m["cbShowAllFrames"], :active, Bool) + frame = 1:numFGFrames + else + frame = max( get_gtk_property(m["adjFrame"], :value, Int64), 1) + end + + timePoints = rxTimePoints(f) + deltaT = timePoints[2] - timePoints[1] + + data = getMeasurements(f, true, frames=frame, + bgCorrection=false, spectralLeakageCorrection = get_gtk_property(m["cbSLCorr"], :active, Bool), + tfCorrection=get_gtk_property(m["cbCorrTF"], :active, Bool), + fixDistortions=m["cbShowFixDistortions"].active) + push!(dataFGVec, data) + + if acqNumBGFrames(f) > 0 + dataBG = getMeasurements(f, false, frames=measBGFrameIdx(f), + bgCorrection=false, spectralLeakageCorrection = get_gtk_property(m["cbSLCorr"], :active, Bool), + tfCorrection=get_gtk_property(m["cbCorrTF"], :active, Bool)) + else + dataBG = zeros(Float32,0,0,0,0) + end + push!(dataBGVec, dataBG) + end + + + m.dataBG = cat(dataBGVec..., dims=5) + dataFG = cat(dataFGVec..., dims=5) + m.labels = ["expnum "*string(experimentNumber(f)) for f in fs] + + updateData(m, dataFG, deltaT, true) + end + m.updatingData = false + end + return nothing +end + +@guarded function getData(m::SpectrogramWidget) + chan = max(get_gtk_property(m["adjRxChan"], :value, Int64),1) + group = get_gtk_property(m["adjGrouping"],:value,Int64) + patchAv = max(get_gtk_property(m["adjPatchAv"], :value, Int64),1) + numPatches = div(size(m.data,3), patchAv) + showBG = get_gtk_property(m["cbShowBG"], :active, Bool) + allFrames = get_gtk_property(m["cbShowAllFrames"], :active, Bool) + + data_ = mean( reshape(m.data[:,chan,:,:,:], + size(m.data,1), patchAv, numPatches, size(m.data,4), : ), dims=(2,)) + if length(m.dataBG) > 0 + dataBG_ = mean( reshape(m.dataBG[:,chan,:,:,:], + size(m.dataBG,1), patchAv, numPatches, size(m.dataBG,4), : ), dims=(2,4)) + + if get_gtk_property(m["cbSubtractBG"], :active, Bool) + data_ .-= dataBG_ + end + if showBG + data_ .= dataBG_ + end + dataBG_ = vec(dataBG_) + end + + data_ = vec(data_) + + timedata = arraysplit(data_, size(m.data,1)*group, div(size(m.data,1)*group,2)) + sp = DSP.spectrogram(vec(data_), size(m.data,1)*group) + + return timedata, sp +end + +################ + +function setBG(m::SpectrogramWidget, dataBG) + if ndims(dataBG) == 5 + m.dataBG = dataBG + else + m.dataBG = reshape(dataBG, size(dataBG)..., 1) + end +end + +@guarded function updateData(m::SpectrogramWidget, data::Array, deltaT=1.0, fileModus=false) + maxValTPOld = get_gtk_property(m["adjMinTP"],:upper, Int64) + maxValFreOld = get_gtk_property(m["adjMinFre"],:upper, Int64) + allFrames = get_gtk_property(m["cbShowAllFrames"], :active, Bool) + + if ndims(data) == 5 + m.data = data + else + m.data = reshape(data, size(data)..., 1) + end + m.deltaT = deltaT .* 1000 # convert to ms and kHz + m.fileModus = fileModus + + if !isempty(m.dataBG) + if size(m.data)[1:3] != size(m.dataBG)[1:3] + @info "Background data does not fit to foreground data! Dropping BG data." + @info size(m.data) + @info size(m.dataBG) + m.dataBG = zeros(Float32,0,0,0,0,0) + end + end + + timedata, sp = getData(m) + maxValTP = length(timedata[1]) + maxValFre = size(sp.power,1) + numPatches = size(sp.power,2) + maxGrouping = allFrames ? size(m.data,3)*size(m.data,4) : size(m.data,3) + + @idle_add_guarded begin + m.updatingData = true + set_gtk_property!(m["adjGrouping"],:upper,maxGrouping) + if !(1 <= get_gtk_property(m["adjGrouping"],:value,Int64) <= maxGrouping) + set_gtk_property!(m["adjGrouping"],:value, 1) + end + if !fileModus + set_gtk_property!(m["adjFrame"],:upper,size(m.data,4)) + if !(1 <= get_gtk_property(m["adjFrame"],:value,Int64) <= size(m.data,4)) + set_gtk_property!(m["adjFrame"],:value,1) + end + end + set_gtk_property!(m["adjRxChan"],:upper,size(m.data,2)) + if !(1 <= get_gtk_property(m["adjRxChan"],:value,Int64) <= size(m.data,2)) + set_gtk_property!(m["adjRxChan"],:value,1) + end + set_gtk_property!(m["adjPatch"],:upper,numPatches) + if !(1 <= get_gtk_property(m["adjPatch"],:value,Int64) <= numPatches) + set_gtk_property!(m["adjPatch"],:value,1) + end + set_gtk_property!(m["adjMinTP"],:upper,maxValTP) + if !(1 <= get_gtk_property(m["adjMinTP"],:value,Int64) <= maxValTP) || maxValTP != maxValTPOld + set_gtk_property!(m["adjMinTP"],:value,1) + end + set_gtk_property!(m["adjMaxTP"],:upper, maxValTP) + if !(1 <= get_gtk_property(m["adjMaxTP"],:value,Int64) <= maxValTP) || maxValTP != maxValTPOld + set_gtk_property!(m["adjMaxTP"],:value, maxValTP) + end + set_gtk_property!(m["adjMinFre"],:upper, maxValFre) + if !(1 <= get_gtk_property(m["adjMinFre"],:value,Int64) <= maxValFre) || maxValFre != maxValFreOld + set_gtk_property!(m["adjMinFre"],:value,1) + end + set_gtk_property!(m["adjMaxFre"],:upper, maxValFre) + if !(1 <= get_gtk_property(m["adjMaxFre"],:value,Int64) <= maxValFre) || maxValFre != maxValFreOld + set_gtk_property!(m["adjMaxFre"],:value, maxValFre) + end + + m.updatingData = false + showData(C_NULL, m) + end +end + +@guarded function updateData(m::SpectrogramWidget, filenames::Vector{<:AbstractString}) + m.filenamesData = filenames + @idle_add_guarded begin + m.updatingData = true + set_gtk_property!(m["adjFrame"],:upper,1) + set_gtk_property!(m["adjFrame"],:value,1) + set_gtk_property!(m["adjPatch"],:upper,1) + set_gtk_property!(m["adjPatch"],:value,1) + m.updatingData = false + loadData(C_NULL, m) + end + return nothing +end + +@guarded function updateData(m::SpectrogramWidget, filename::String) + updateData(m, [filename]) + return nothing +end + +################ + +@guarded function showData(widgetptr::Ptr, m::SpectrogramWidget) + if length(m.data) > 0 && !m.updatingData + chan = max(get_gtk_property(m["adjRxChan"], :value, Int64),1) + patch = max(get_gtk_property(m["adjPatch"], :value, Int64),1) + minTP = max(get_gtk_property(m["adjMinTP"], :value, Int64),1) + maxTP = max(get_gtk_property(m["adjMaxTP"], :value, Int64),1) + minFr = max(get_gtk_property(m["adjMinFre"], :value, Int64),1) + maxFr = max(get_gtk_property(m["adjMaxFre"], :value, Int64),1) + group = get_gtk_property(m["adjGrouping"],:value,Int64) + logVal = get_gtk_property(m["adjLogPlot"],:value, Float64) + numSignals = size(m.data,5) + showFD = get_gtk_property(m["cbShowFreq"], :active, Bool) + numShownFrames = size(m.data,4) + + autoRangingTD = true + autoRangingFD = true + minValTD_ = tryparse(Float64,get_gtk_property( m["entTDMinVal"] ,:text,String)) + maxValTD_ = tryparse(Float64,get_gtk_property( m["entTDMaxVal"] ,:text,String)) + minValFD_ = tryparse(Float64,get_gtk_property( m["entFDMinVal"] ,:text,String)) + maxValFD_ = tryparse(Float64,get_gtk_property( m["entFDMaxVal"] ,:text,String)) + + if minValTD_ != nothing && maxValTD_ != nothing + minValTD = minValTD_ + maxValTD = maxValTD_ + autoRangingTD = false + end + + if minValFD_ != nothing && maxValFD_ != nothing + minValFD = minValFD_ + maxValFD = maxValFD_ + autoRangingFD = false + end + + timedata, sp = getData(m) + data_ = timedata[patch] + m.rangeTD = extrema(data_) + + maxFr = min(maxFr,size(sp.power,1)) + maxTP = min(maxTP,size(data_,1)) + minFr = min(minFr,size(sp.power,1)) + minTP = min(minTP,size(data_,1)) + + spdata = sp.power[minFr:maxFr,:] + maxT = m.deltaT*size(m.data,1)*size(m.data,3)*numShownFrames/1000 + + if size(spdata,1) > 2^15 # Magic number of Winston image display + sliceFr = 1:ceil(Int,size(spdata,1)/2^15):size(spdata,1) + else + sliceFr = Colon() + end + + if size(spdata,2) > 2^15 # Magic number of Winston image display + sliceTime = 1:ceil(Int,size(spdata,2)/2^15):size(spdata,2) + else + sliceTime = Colon() + end + patch_ = patch/size(sp.power,2) * maxT + + fSp, axSp, pSp = CairoMakie.heatmap( range(0.0, step=maxT/size(spdata[sliceFr, sliceTime],2), + length=size(spdata[sliceFr, sliceTime],2)), + range( (minFr-1)/size(sp.power,1) / m.deltaT / 2.0, + step = (maxFr-1)/size(sp.power,1) / m.deltaT / 2.0 / size(spdata[sliceFr, sliceTime],1), + length = size(spdata[sliceFr, sliceTime],1)), + log.(10.0^(-(2+10*logVal)) .+ transpose(spdata[sliceFr, sliceTime]) ); + figure = (; figure_padding=4, fontsize = 10), + ) + + CairoMakie.lines!(axSp, [patch_,patch_], [0,(maxFr-1)/size(sp.power,1) / m.deltaT / 2.0], + color=:white, linewidth=2 ) + + axSp.ylabel = "freq / kHz" + axSp.xlabel = "t / s" + axSp.yreversed = true + + @idle_add_guarded drawonto(m.cSpect, fSp) + + timePoints = (0:(length(data_)-1)).*m.deltaT + + maxPoints = 5000 + sp_ = length(minTP:maxTP) > maxPoints ? round(Int,length(minTP:maxTP) / maxPoints) : 1 + steps = minTP:sp_:maxTP + + fTD, axTD, lTD1 = CairoMakie.lines(timePoints[steps], data_[steps], + figure = (; figure_padding=4, size = (1000, 800), fontsize = 10), + axis = (; title = "Time Domain"), + color = CairoMakie.RGBf(colors[1]...)) + + CairoMakie.autolimits!(axTD) + if timePoints[steps[end]] > timePoints[steps[1]] + CairoMakie.xlims!(axTD, timePoints[steps[1]], timePoints[steps[end]]) + end + if !autoRangingTD && maxValTD > minValTD + CairoMakie.ylims!(axTD, minValTD, maxValTD) + end + axTD.xlabel = "t / ms" + axTD.ylabel = "u / V" + + + if showFD + numFreq = size(sp.power,1) + freq = collect(0:(numFreq-1))./(numFreq-1)./m.deltaT./2.0 + freqdata = sp.power[:,:] + m.rangeFD = extrema(freqdata[:,patch]) + spFr = length(minFr:maxFr) > maxPoints ? round(Int,length(minFr:maxFr) / maxPoints) : 1 + + stepsFr = minFr:spFr:maxFr + + fFD, axFD, lFD1 = CairoMakie.lines(freq[stepsFr],freqdata[stepsFr,patch], + figure = (; figure_padding=4, size = (1000, 800), fontsize = 10), + axis = (; title = "Frequency Domain", yscale=log10), + color = CairoMakie.RGBf(colors[1]...)) + + CairoMakie.autolimits!(axFD) + if freq[stepsFr[end]] > freq[stepsFr[1]] + CairoMakie.xlims!(axFD, freq[stepsFr[1]], freq[stepsFr[end]]) + end + if !autoRangingFD && maxValFD > minValFD + CairoMakie.ylims!(axFD, minValFD, maxValFD) + end + axFD.xlabel = "f / kHz" + axFD.ylabel = "u / V" + + else + @guarded Gtk4.draw(m.cFD) do widget + + ctx = getgc(m.cFD) + h = height(ctx) + w = width(ctx) + Cairo.set_source_rgb(ctx,1.0,1.0,1.0) + Cairo.rectangle(ctx, 0,0,w,h) + Cairo.paint(ctx) + Cairo.stroke(ctx) + end + end + + + @idle_add_guarded drawonto(m.cTD, fTD) + if showFD + @idle_add_guarded drawonto(m.cFD, fFD) + end + + end + return nothing +end + + diff --git a/MPIViewers/src/builder/baseViewer.ui b/MPIViewers/src/builder/baseViewer.ui new file mode 100644 index 0000000..c1fd800 --- /dev/null +++ b/MPIViewers/src/builder/baseViewer.ui @@ -0,0 +1,130 @@ + + + + + 1 + 1 + + + 1 + 1 + 1 + 1 + + + + + 1 + 0 + + + + + + z-Axis + + 0 + 0 + + + + + + - x-Axis + + + 1 + 1 + + + + + + 1 + 1 + 1 + 1 + + + + + 3 + 0 + + + + + + + +z-Axis + - + + 2 + 0 + + + + + + - y-Axis + + + 3 + 1 + + + + + + 1 + 1 + 1 + 1 + + + + + 3 + 2 + + + + + + x Axis + + 2 + 2 + + + + + + y-Axis + + 3 + 3 + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MPIViewers/src/builder/dataviewer.ui b/MPIViewers/src/builder/dataviewer.ui new file mode 100644 index 0000000..ba8f6e1 --- /dev/null +++ b/MPIViewers/src/builder/dataviewer.ui @@ -0,0 +1,1322 @@ + + + + + 1 + 1 + 0.050000000000000003 + 0.050000000000000003 + + + 1 + 1 + 1 + 10 + + + 1 + 0.050000000000000003 + 0.050000000000000003 + + + 1 + 1 + 10 + + + 1 + 100 + 1 + 1 + 10 + + + 1 + 1000000 + 1 + 1 + 10 + + + 1 + 100 + 1 + 1 + 10 + + + 100000000 + 1 + 10 + + + 100000000 + 0.01 + 10 + + + 1 + 1000000 + 1 + 1 + 10 + + + 1 + 100 + 1 + 1 + 10 + + + 1250 + 1250 + 10 + 10 + + + 1250 + 80 + 10 + 10 + + + 100 + 1 + 1 + 10 + + + 1 + 100 + 5 + 1 + 10 + + + + + 5 + 5 + + + Export as PNG + 1 + 1 + + 0 + 1 + + + + + + Save Params + 1 + 1 + + 1 + 0 + + + + + + Exp. Nifti C. Fr. + 1 + 1 + + 1 + 1 + + + + + + Export Movie + 1 + 1 + + 1 + 2 + + + + + + 1 + 3 + + 0 + 0 + + + + + + Export Profile + 1 + 1 + + 0 + 2 + + + + + + Exp. Nifti All Fr. + 1 + 1 + + 1 + 3 + + + + + + Exp. Real All Fr. + 1 + 1 + + 0 + 3 + + + + + + Pixel Resize Factor + + 0 + 5 + + + + + + 1 + 5 + adjPixelResizeFactor + 5 + + 1 + 5 + + + + + + Export as Tikz + 1 + 1 + + 0 + 4 + + + + + + + + + + + 1 + 0.01 + 0.01 + 10 + + + -3.1499999999999999 + 3.1499999999999999 + 0.01 + 10 + + + -3.1499999999999999 + 3.1499999999999999 + 0.01 + 10 + + + -3.1499999999999999 + 3.1499999999999999 + 0.01 + 10 + + + -3.1499999999999999 + 3.1499999999999999 + 0.01 + 10 + + + -3.1499999999999999 + 3.1499999999999999 + 0.01 + 10 + + + -3.1499999999999999 + 3.1499999999999999 + 0.01 + 10 + + + 100 + 1 + 1 + 10 + + + 100 + 1 + 10 + + + 100 + 1 + 10 + + + 100 + 1 + 10 + + + 1 + 3 + 1 + 1 + 10 + + + 1 + 100 + 1 + 10 + + + 1 + 100 + 1 + 1 + 10 + + + 1 + 100 + 1 + 1 + 10 + + + 1 + 100 + 1 + 1 + 10 + + + + + 2 + 2 + + + 1 + 2 + 1 + adjSliceZ + 1 + + 2 + 1 + + + + + + 1 + 0 + 2 + 1 + adjSliceX + 1 + + 0 + 1 + + + + + + 1 + 2 + 1 + adjSliceY + 1 + + 1 + 1 + + + + + + x + + 0 + 0 + + + + + + y + + 1 + 0 + + + + + + z + + 2 + 0 + + + + + + Show Slices + 1 + 1 + + 0 + 2 + + + + + + SpatialMIP + 1 + + 1 + 2 + + + + + + + + + + + 1 + 0.40000000000000002 + 1 + 10 + + + + + 5 + 5 + + + Frame Projection + + 0 + 0 + + + + + + 0 + + 1 + 0 + + + + + + Play Movie + 1 + 1 + + 0 + 1 + + + + + + 1 + 1 + adjTTPThresh + 1 + + 1 + 1 + + + + + + Profile + + 0 + 2 + + + + + + 1 + + 1 + 2 + + + + + + + + 100 + 1 + 10 + + + 100 + 1 + 10 + + + 100 + 1 + 10 + + + 100 + 0.10000000000000001 + 10 + + + 100 + 0.10000000000000001 + 10 + + + 100 + 0.10000000000000001 + 10 + + + 500 + + + 5 + 5 + + + Permutation + + 0 + 0 + + + + + + Spatial BG MIP + 1 + + 0 + 15 + + + + + + Transp. Blend + 1 + + 0 + 14 + + + + + + Hide BG Data + 1 + + 0 + 13 + + + + + + Hide MPI Data + 1 + + 1 + 13 + + + + + + cmax + + 0 + 12 + + + + + + 1 + 1 + adjCMaxBG + 1 + + 1 + 12 + + + + + + 1 + 1 + adjCMinBG + 1 + + 1 + 11 + + + + + + cmin + + 0 + 11 + + + + + + Colormap + + 0 + 10 + + + + + + 0 + + 1 + 10 + + + + + + 1 + 0,000 + adjRotBGZ + 3 + + 0 + 9 + + + + + + 1 + 0,000 + adjRotZ + 3 + + 1 + 9 + + + + + + 1 + 0,000 + adjRotY + 3 + + 1 + 8 + + + + + + 1 + 0,000 + adjRotBGY + 3 + + 0 + 8 + + + + + + 1 + 0,000 + adjRotBGX + 3 + + 0 + 7 + + + + + + Rotation + + 0 + 6 + + + + + + 1 + 0,000 + adjRotX + 3 + + 1 + 7 + + + + + + Rotation FG + + 1 + 6 + + + + + + 1 + 0,0 + adjTransZ + 1 + + 1 + 5 + + + + + + 1 + 0,0 + adjTransBGZ + 1 + + 0 + 5 + + + + + + 1 + 0,0 + adjTransBGY + 1 + + 0 + 4 + + + + + + 1 + 0,0 + adjTransBGX + 1 + + 0 + 3 + + + + + + 1 + 0,0 + adjTransY + 1 + + 1 + 4 + + + + + + 1 + 0,0 + adjTransX + 1 + + 1 + 3 + + + + + + Translation + + 0 + 2 + + + + + + Translation FG + + 1 + 2 + + + + + + + 1 + 1 + + + + + + + 1 + 0 + + + + + + Flipping + + 0 + 1 + + + + + + Show FOV + 1 + + 1 + 14 + + + + + + + + + + + + + + + + + + + + + Blend + 1 + + 0 + 0 + 2 + + + + + + Complex Blending + 1 + + 0 + 1 + 2 + + + + + + Show Axes + 1 + + 0 + 2 + 2 + + + + + + + + + + + + + + 1 + 1 + vertical + + + + + 3 + 3 + 3 + 3 + 1 + 3 + + + 1 + 2 + 1 + adjFrames + 1 + + 0 + 1 + + + + + + Frames + + 0 + 0 + 2 + + + + + + 0 + + 6 + 1 + + + + + + Colormap + + 6 + 0 + + + + + + 1 + 1 + popFrames + + + + + 1 + 1 + + + + + + 1 + 1 + popSlices + edit-copy + + 3 + 1 + + + + + + Slices + + 3 + 0 + + + + + + + 2 + 0 + 2 + + + + + + Chan + + 5 + 0 + + + + + + + 5 + 1 + + + + + + + 4 + 0 + 2 + + + + + + + 10 + 0 + 2 + + + + + + 1 + 0 + 1 + adjCMin + audio-volume-muted-symbolic +audio-volume-high-symbolic +audio-volume-low-symbolic +audio-volume-medium-symbolic + + + 1 + 1 + 1 + center + center + + + + + 1 + 1 + 1 + center + center + + + + 7 + 1 + + + + + + 1 + 0 + 1 + adjCMax + audio-volume-muted-symbolic +audio-volume-high-symbolic +audio-volume-low-symbolic +audio-volume-medium-symbolic + + + 1 + 1 + 1 + center + center + + + + + 1 + 1 + 1 + center + center + + + + 8 + 1 + + + + + + cmin + + 7 + 0 + + + + + + cmax + + 8 + 0 + + + + + + Export + + 11 + 0 + + + + + + 1 + 1 + popExport + document-save + + 11 + 1 + + + + + + + 12 + 0 + 2 + + + + + + Fusion + + 13 + 0 + + + + + + 1 + 1 + popFusion + + 13 + 1 + + + + + + + 14 + 0 + 2 + + + + + + More + + 15 + 0 + + + + + + 1 + 1 + popOptions + preferences-desktop + + 15 + 1 + + + + + + + + + + + + + + + + 1 + + + 1 + 0 + 0 + + + + + 4 + 4 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + page 1 + + + + + + + 1 + + + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + page 2 + + + + + + + + + + diff --git a/MPIViewers/src/builder/magneticFieldViewer.ui b/MPIViewers/src/builder/magneticFieldViewer.ui new file mode 100644 index 0000000..19afbb6 --- /dev/null +++ b/MPIViewers/src/builder/magneticFieldViewer.ui @@ -0,0 +1,1936 @@ + + + + + 20 + 5 + 0.5 + 10 + + + 1 + 30 + 10 + 2 + 2 + + + 1 + 1 + 0.005000000000000003 + 0.005000000000000003 + + + 1 + 0.005000000000000003 + 0.005000000000000003 + + + 5 + 500 + 25 + 5 + 10 + + + 1 + 1 + 10 + + + 1 + 100 + 1 + 1 + 10 + + + 1 + 100 + 1 + 1 + 10 + + + 1 + 100 + 1 + 1 + 10 + + + 1 + 100 + 5 + 1 + 10 + + + 1 + 0.01 + 0.01 + 10 + + + -3.1499999999999999 + 3.1499999999999999 + 0.01 + 10 + + + -3.1499999999999999 + 3.1499999999999999 + 0.01 + 10 + + + -3.1499999999999999 + 3.1499999999999999 + 0.01 + 10 + + + -3.1499999999999999 + 3.1499999999999999 + 0.01 + 10 + + + 100 + 1 + 1 + 10 + + + 100 + 1 + 10 + + + 100 + 1 + 10 + + + 100 + 1 + 10 + + + 1 + 3 + 1 + 1 + 10 + + + 1 + 100 + 1 + 10 + + + -1 + 1 + 1 + 10 + + + -1 + 1 + 1 + 10 + + + -1 + 1 + 1 + 10 + + + + + 2 + 2 + + + 1 + 2 + 1 + adjSliceZ + 1 + + 2 + 1 + + + + + + 1 + 0 + 2 + 1 + adjSliceX + 1 + + 0 + 1 + + + + + + 1 + 2 + 1 + adjSliceY + 1 + + 1 + 1 + + + + + + x + + 0 + 0 + + + + + + y + + 1 + 0 + + + + + + z + + 2 + 0 + + + + + + Show Slices + 1 + 1 + + 0 + 2 + + + + + + Go to FFP + 1 + 1 + + 2 + 2 + + + + + + Go to Zero + 1 + 1 + + 1 + 2 + + + + + + + + + + 5 + 5 + + + Profile settings + + 0 + 0 + + + + + + Field + + 0 + 1 + + + + + + 0 + + 1 + 1 + + + + + + Axes + + 0 + 2 + + + + + + 1 + + 1 + 2 + + + + + + + + 100 + 1 + 10 + + + 100 + 1 + 10 + + + 100 + 1 + 10 + + + 100 + 0.10000000000000001 + 10 + + + 100 + 0.10000000000000001 + 10 + + + 100 + 0.10000000000000001 + 10 + + + vertical + + + 4 + 4 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + 300 + 300 + + + 3 + 3 + 3 + 3 + + + 3 + 3 + 3 + 3 + 1 + 1 + vertical + + + center + 1 + 10 + + + + + + + + + + + + + + + + + 5 + 5 + + + 2 + + + Fields + 1 + 1 + + 0 + 0 + + + + + + Coefficients + 1 + 1 + + 1 + 0 + + + + + + Profile + 1 + 1 + + 2 + 0 + + + + + 0 + 1 + 2 + + + + + + Export as PNG + 1 + 1 + + 0 + 0 + + + + + + Export as Video + 0 + 1 + 1 + + 1 + 0 + + + + + + + 0 + 2 + 2 + + + + + + Export as Tikz + 1 + 1 + + 0 + 3 + + + + + + Build pdf + 1 + 1 + + 1 + 3 + + + + + + + 0 + 4 + 2 + + + + + + Export as CSV + + 0 + 5 + + + + + + 2 + + + Coefficients + 1 + 1 + + 0 + 0 + + + + + + Magnetic Field + 1 + 1 + + 0 + 1 + + + + + 1 + 5 + + + + + + + + + + + + + + + Show CS + 1 + + 0 + 0 + + + + + + Show Axes + 1 + + 0 + 1 + + + + + + Show Sphere + 1 + + 0 + 2 + + + + + + + + 4 + 4 + 4 + + + 1 + 1 + vertical + + + + + 3 + 80 + 3 + 3 + 1 + 3 + + + 1 + 2 + 1 + adjPatches + 1 + + 0 + 1 + + + + + + Patches + + 0 + 0 + 2 + + + + + + Colormap + + 6 + 0 + + + + + + 1 + 1 + popFrames + + + + + 1 + 1 + + + + + + 1 + 1 + popSlices + edit-copy + + 3 + 1 + + + + + + Slices + + 3 + 0 + + + + + + + 2 + 0 + 2 + + + + + + + 4 + 0 + 2 + + + + + + + 10 + 0 + 2 + + + + + + 1 + 0 + 1 + adjCMin + audio-volume-muted-symbolic +audio-volume-high-symbolic +audio-volume-low-symbolic +audio-volume-medium-symbolic + + + 1 + 1 + 1 + center + center + + + + + 1 + 1 + 1 + center + center + + + + 7 + 1 + + + + + + 1 + 0 + 1 + adjCMax + audio-volume-muted-symbolic +audio-volume-high-symbolic +audio-volume-low-symbolic +audio-volume-medium-symbolic + + + 1 + 1 + 1 + center + center + + + + + 1 + 1 + 1 + center + center + + + + 8 + 1 + + + + + + cmin + + 7 + 0 + + + + + + cmax + + 8 + 0 + + + + + + Export + + 11 + 0 + + + + + + 1 + 1 + popExport + document-save + + 11 + 1 + + + + + + + 12 + 0 + 2 + + + + + + More + + 14 + 0 + + + + + + 1 + 1 + popOptions + preferences-desktop + + 14 + 1 + + + + + + + + + 0 + 0 + + + + + + 1 + 1 + popCMaps + applications-graphics + + + 1 + 0 + + + + + 6 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 1 + + + 1 + 0 + 0 + + + + + 4 + 4 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + page 1 + + + + + + + + + + + + + + + + 1 + + 220 + + + 3 + 3 + + + 15 + + + 1 + 1 + + + 4 + 4 + 4 + 5 + 5 + + + FOV + + 0 + 0 + + + + + + 1 + 14 + + 1 + 0 + + + + + + Inters. + + 0 + 2 + + + + + + 1 + 14 + + 1 + 2 + + + + + + mm + + 2 + 0 + + + + + + mm + + 2 + 2 + + + + + + 2 + + + Center Sphere + 1 + 1 + + 0 + 0 + + + + + + FFP + 1 + 1 + + 1 + 0 + + + + + 1 + 4 + 2 + + + + + + Center Sphere + + 0 + 5 + + + + + + mm + + 2 + 5 + + + + + + 1 + 0 + 14 + + 1 + 5 + + + + + + FFP + + 0 + 6 + + + + + + 1 + 0 + 14 + + 1 + 6 + + + + + + mm + + 2 + 6 + + + + + + 0 + 5 + + 0 + 7 + 3 + + + + + 0 + 0 + + + + + + Calc FFP + 1 + 1 + + 1 + 0 + + + + + + Reset FFP + 1 + 1 + + 2 + 0 + + + + + + + + Update Plot + 1 + 1 + + 0 + 8 + 3 + + + + + + Center Plot + + 0 + 4 + + + + + + Reset FOV + 1 + 1 + + 1 + 1 + + + + + + Stay in FFP + 1 + + 1 + 3 + + + + + + Reset All + 1 + 1 + + 0 + 9 + 3 + + + + + + + + + + + + + + + + + + + + Magnetic Field + + + + 0 + 0 + + + + + + 1 + 1 + + + 4 + 4 + 4 + 5 + 5 + + + L + + 0 + 0 + + + + + + 1 + 4 + 5 + 51 + adjL + + 1 + 0 + + + + + + scale with radius + 1 + 1 + + 1 + 1 + + + + + + + + Coefficients + + + + 0 + 1 + + + + + + 1 + + + 4 + 5 + 5 + + + |arrow| + + 0 + 1 + + + + + + 1 + 5 + 5 + 0 + adjArrowLength + 1 + 5 + + 1 + 1 + + + + + + fontsize + + 0 + 2 + + + + + + 1 + 5 + 5 + 1 + adjFontsize + 12 + + 1 + 2 + + + + + + Discr. + + 0 + 0 + + + + + + 1 + 4 + 5 + 25 + adjDiscretization + 25 + + 1 + 0 + + + + + + cmin = 0 + 1 + + 1 + 6 + + + + + + show legend + 1 + 1 + + 1 + 3 + + + + + + convert to milli + 1 + 1 + + 1 + 4 + + + + + + Colorbar + + 0 + 5 + + + + + + 2 + 5 + + + 1 + 0 + 8 + + 1 + 0 + + + + + + 1 + 0 + 8 + + 1 + 1 + + + + + + cmin + + 0 + 0 + + + + + + cmax + + 0 + 1 + + + + + + mT + + 2 + 0 + + + + + + mT + + 2 + 1 + + + + + 1 + 5 + + + + + + keep cmin/cmax + 1 + + 1 + 7 + + + + + + write cmin/cmax + 1 + + 1 + 8 + + + + + + + + + + + + + + Plotting + + + + 0 + 3 + + + + + + 1 + + + 4 + 5 + 5 + + + Radius + + 0 + 0 + + + + + + mm + + 2 + 0 + + + + + + 1 + 0 + 14 + 10 + + 1 + 0 + + + + + + Center Sphere + + 0 + 1 + + + + + + mm + + 2 + 1 + + + + + + 1 + 0 + 14 + + 1 + 1 + + + + + + 9 + + + 1 + 0 + 9 + + 1 + 0 + + + + + + x + + 0 + 0 + + + + + + y + + 0 + 1 + + + + + + z + + 0 + 2 + + + + + + 1 + 0 + 12 + + 1 + 1 + + + + + + 1 + 0 + 9 + + 1 + 2 + + + + + 1 + 4 + + + + + + baseline + 8 + 8 + 17 + 1 + + + T/m + + 0 + 0 + + + + + + T/m + + 0 + 1 + + + + + + T/m + + 0 + 2 + + + + + 2 + 4 + + + + + + FFP + + 0 + 2 + + + + + + mm + + 2 + 2 + + + + + + 0 + + 0 + 3 + 2 + + + + + + 1 + 0 + 14 + + 1 + 2 + + + + + + + + + + + + + + + + + + + + Measurement Infos + + + + 0 + 2 + + + + + + 1 + 0 + + + 4 + 4 + 4 + 5 + 5 + + + patch start + + 0 + 0 + + + + + + patch end + + 0 + 1 + + + + + + 1 + 4 + 5 + 51 + adjPatchesVideoLower + + 1 + 0 + + + + + + 1 + 4 + 5 + 51 + adjPatchesVideoUpper + + 1 + 1 + + + + + + pause + + 0 + 2 + + + + + + 4 + + + 1 + 1 + 3 + + 1 + 0 + + + + + + ms + + 2 + 0 + + + + + 1 + 2 + 2 + + + + + + repeat video + 1 + 1 + + 1 + 3 + + + + + + 1 + Play + 1 + 1 + Play Video + + 1 + 4 + 2 + + + + + + + + Video + + + + 0 + 4 + + + + + + + + + + + diff --git a/MPIViewers/src/builder/rawDataViewer.ui b/MPIViewers/src/builder/rawDataViewer.ui new file mode 100644 index 0000000..56f00dc --- /dev/null +++ b/MPIViewers/src/builder/rawDataViewer.ui @@ -0,0 +1,436 @@ + + + + + 1 + 1000000 + 1 + 10 + + + 1 + 100 + 1 + 10 + + + 1 + 100 + 1 + 10 + + + 1 + 100 + 1 + 10 + + + 1 + 100 + 1 + 10 + + + 1 + 100 + 1 + 10 + + + 1 + 100 + 1 + 10 + + + 1 + 100 + 1 + 10 + + + 1 + 100 + 1 + 10 + + + 1 + 100 + 1 + 10 + + + 1 + 10000 + 1 + 10 + + + 1 + 10000 + 1 + 1 + 10 + + + + + + + Show BG + 1 + + 0 + 0 + + + + + + Leak Corr + 1 + + 0 + 1 + + + + + + Subtr. BG + 1 + 1 + + 1 + 0 + + + + + + Corr. TF + 1 + + 1 + 1 + + + + + + All Patches + 1 + + 0 + 2 + + + + + + Harm. View + 1 + + 1 + 2 + + + + + + AbsAv + 1 + THIS IS EXPERIMENTAL!!!!!! + + 0 + 3 + + + + + + Show Freq + 1 + 1 + + 1 + 3 + + + + + + 1 + adjPatchAv + + 1 + 5 + + + + + + Patch Av + + 0 + 5 + + + + + + Reverse Graphs + 1 + + 1 + 4 + + + + + + + + + + + 1 + 100 + 1 + 10 + + + vertical + + + 2 + 2 + 1 + 2 + 2 + + + 1 + center + 3 + 1 + adjFrame + 1 + + 0 + 1 + + + + + + 1 + center + 3 + 1 + adjRxChan + 1 + + 1 + 1 + + + + + + 1 + start + center + 0 + 0 + 3 + 1 + adjPatch + 1 + + 2 + 1 + + + + + + Chan + + 1 + 0 + + + + + + Patch + + 2 + 0 + + + + + + Frame + + 0 + 0 + + + + + + 1 + 1 + popOptions + preferences-desktop + + 3 + 1 + + + + + + Options + + 3 + 0 + + + + + + + + 1 + vertical + + + 1 + vertical + + + 1 + + + 1 + 1 + right + 1 + adjMinTP + 0 + 0 + + + + + 1 + 1 + right + 1 + adjMaxTP + 0 + 0 + + + + + 1 + 12 + + + + + 1 + 12 + + + + + 1 + 1 + list-add + + + + + 1 + 1 + edit-clear + + + + + + + + + 1 + vertical + + + 0 + 1 + + + 1 + 1 + right + 1 + adjMinFre + 0 + 0 + + + + + 1 + 1 + right + 1 + adjMaxFre + 0 + 0 + + + + + 0 + 1 + 12 + + + + + 0 + 1 + 12 + + + + + 1 + 1 + list-add + + + + + 1 + 1 + edit-clear + + + + + + + + + + diff --git a/MPIViewers/src/builder/simpleDataViewer.ui b/MPIViewers/src/builder/simpleDataViewer.ui new file mode 100644 index 0000000..a2ae0e3 --- /dev/null +++ b/MPIViewers/src/builder/simpleDataViewer.ui @@ -0,0 +1,106 @@ + + + + + vertical + + + 1 + + + 1 + 0 + 0 + + + + + 4 + 4 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + page 1 + + + + + + + 1 + + + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + page 2 + + + + + + + + + + diff --git a/MPIViewers/src/builder/spectrogramViewer.ui b/MPIViewers/src/builder/spectrogramViewer.ui new file mode 100644 index 0000000..69c9f08 --- /dev/null +++ b/MPIViewers/src/builder/spectrogramViewer.ui @@ -0,0 +1,452 @@ + + + + + 1 + 1000000 + 1 + 10 + + + 1 + 100 + 1 + 10 + + + 1 + 100 + 1 + 10 + + + 1 + 100 + 1 + 10 + + + 1 + 100 + 1 + 10 + + + 1 + 100 + 1 + 10 + + + 1 + 100 + 1 + 10 + + + 1 + 0.5 + 0.01 + 10 + + + 1 + 100 + 1 + 10 + + + 1 + 100 + 1 + 10 + + + 1 + 100 + 1 + 10 + + + 1 + 100 + 1 + 10 + + + 1 + 10000 + 1 + 10 + + + 1 + 10000 + 1 + 1 + 10 + + + 1 + 100 + 1 + 10 + + + + + + + Show BG + 1 + + 0 + 0 + + + + + + Leak Corr + 1 + + 0 + 1 + + + + + + Subtr. BG + 1 + 1 + + 1 + 0 + + + + + + Corr. TF + 1 + + 1 + 1 + + + + + + Show Freq + 1 + 1 + + 1 + 2 + + + + + + All Frames + 1 + + 0 + 2 + + + + + + Fix Distortions + 1 + + 0 + 3 + + + + + + + + vertical + + + 2 + 2 + 1 + 2 + 2 + + + 1 + center + 3 + 1 + adjFrame + 1 + + 0 + 1 + + + + + + 1 + center + 3 + 1 + adjRxChan + 1 + + 1 + 1 + + + + + + 1 + start + center + 0 + 0 + 3 + 1 + adjPatchAv + 1 + + 2 + 1 + + + + + + Chan + + 1 + 0 + + + + + + Patch Av + + 2 + 0 + + + + + + Frame + + 0 + 0 + + + + + + 1 + 1 + popOptions + preferences-desktop + + 4 + 1 + + + + + + Options + + 4 + 0 + + + + + + 1 + start + center + 0 + 0 + 3 + 1 + adjGrouping + 1 + + 3 + 1 + + + + + + Grouping + + 3 + 0 + + + + + + + + 1 + 1 + 316 + + + vertical + + + 1 + vertical + + + 1 + + + 1 + 0 + 1 + adjMinTP + 0 + 0 + + + + + 1 + 0 + 1 + adjMaxTP + 0 + 0 + + + + + 1 + 6 + + + + + 1 + 6 + + + + + 1 + 1 + list-add + + + + + 1 + 1 + edit-clear + + + + + + + + + 1 + vertical + + + 1 + + + 1 + 0 + 1 + adjMinFre + 0 + 0 + + + + + 1 + 0 + 1 + adjMaxFre + 0 + 0 + + + + + 1 + 6 + + + + + 1 + 6 + + + + + 1 + 1 + list-add + + + + + 1 + 1 + edit-clear + + + + + + + + + + + vertical + + + 1 + 1 + adjPatch + 1 + 0 + + + + + 1 + 1 + adjLogPlot + 1 + + + + + + + + From 014344a3ecb3d523c3f5f4315c474fe5b8b18617 Mon Sep 17 00:00:00 2001 From: nHackel Date: Thu, 28 Nov 2024 16:05:03 +0100 Subject: [PATCH 09/11] Move viewrs and builders to subpackage --- MPIViewers/Project.toml | 10 +- src/GtkUtils.jl | 39 - src/Viewer/3DViewer/3DViewer.jl | 51 - src/Viewer/3DViewer/3DViewerWidget.jl | 202 -- src/Viewer/3DViewer/IsoSurfaceMode.jl | 77 - src/Viewer/3DViewer/SectionalMode.jl | 121 -- src/Viewer/3DViewer/VolumeMode.jl | 63 - src/Viewer/BaseViewer.jl | 57 - src/Viewer/DataViewer/DataViewer.jl | 620 ------ src/Viewer/DataViewer/Drawing.jl | 339 --- src/Viewer/DataViewer/Export.jl | 211 -- src/Viewer/MagneticFieldViewer/Export.jl | 238 -- .../MagneticFieldViewer.jl | 885 -------- src/Viewer/MagneticFieldViewer/Plotting.jl | 424 ---- src/Viewer/MagneticFieldViewer/TikzExport.jl | 348 --- src/Viewer/RawDataViewer.jl | 594 ----- src/Viewer/SFViewerWidget.jl | 373 ---- src/Viewer/SimpleDataViewer.jl | 93 - src/Viewer/SpectrogramViewer.jl | 603 ----- src/Viewer/Viewer.jl | 8 - src/builder/baseViewer.ui | 130 -- src/builder/dataviewer.ui | 1322 ----------- src/builder/magneticFieldViewer.ui | 1936 ----------------- src/builder/rawDataViewer.ui | 436 ---- src/builder/simpleDataViewer.ui | 106 - src/builder/spectrogramViewer.ui | 452 ---- 26 files changed, 4 insertions(+), 9734 deletions(-) delete mode 100644 src/GtkUtils.jl delete mode 100644 src/Viewer/3DViewer/3DViewer.jl delete mode 100644 src/Viewer/3DViewer/3DViewerWidget.jl delete mode 100644 src/Viewer/3DViewer/IsoSurfaceMode.jl delete mode 100644 src/Viewer/3DViewer/SectionalMode.jl delete mode 100644 src/Viewer/3DViewer/VolumeMode.jl delete mode 100644 src/Viewer/BaseViewer.jl delete mode 100644 src/Viewer/DataViewer/DataViewer.jl delete mode 100644 src/Viewer/DataViewer/Drawing.jl delete mode 100644 src/Viewer/DataViewer/Export.jl delete mode 100644 src/Viewer/MagneticFieldViewer/Export.jl delete mode 100644 src/Viewer/MagneticFieldViewer/MagneticFieldViewer.jl delete mode 100644 src/Viewer/MagneticFieldViewer/Plotting.jl delete mode 100644 src/Viewer/MagneticFieldViewer/TikzExport.jl delete mode 100644 src/Viewer/RawDataViewer.jl delete mode 100644 src/Viewer/SFViewerWidget.jl delete mode 100644 src/Viewer/SimpleDataViewer.jl delete mode 100644 src/Viewer/SpectrogramViewer.jl delete mode 100644 src/Viewer/Viewer.jl delete mode 100644 src/builder/baseViewer.ui delete mode 100644 src/builder/dataviewer.ui delete mode 100644 src/builder/magneticFieldViewer.ui delete mode 100644 src/builder/rawDataViewer.ui delete mode 100644 src/builder/simpleDataViewer.ui delete mode 100644 src/builder/spectrogramViewer.ui diff --git a/MPIViewers/Project.toml b/MPIViewers/Project.toml index 284a909..e43fc36 100644 --- a/MPIViewers/Project.toml +++ b/MPIViewers/Project.toml @@ -24,7 +24,6 @@ LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" Logging = "56ddb016-857b-54e1-b83d-db4d58db5568" LoggingExtras = "e6f89c97-d47a-5376-807f-9c37f3926c36" MPIFiles = "371237a9-e6c1-5201-9adb-3d8cfa78fa9f" -MPIMeasurements = "a874a27a-e9a7-503d-98b6-bf61df4bb725" MPIReco = "e4246700-6248-511e-8146-a1d1f47669d2" MPISphericalHarmonics = "2527f7a4-0af4-4016-a944-036fbac19de9" NLsolve = "2774e3e8-f4cf-5e23-947b-6d7e65073b56" @@ -47,14 +46,13 @@ ImageUtils = "0.2" Images = "0.23, 0.24, 0.25" LoggingExtras = ">= 0.4.2" MPIFiles = "0.15, 0.16" -MPIMeasurements = "0.5, 0.6" MPIReco = "0.7" MPISphericalHarmonics = "0.0.10" -Reexport = "0.2,1.0" +Reexport = "1.0" CairoMakie = "0.12" -julia = "1.7" -Gtk4 = "0.6" -Gtk4Makie = "0.2" +julia = "1.10" +Gtk4 = "0.7" +Gtk4Makie = "0.2, 0.3" [extras] diff --git a/src/GtkUtils.jl b/src/GtkUtils.jl deleted file mode 100644 index bdb5b88..0000000 --- a/src/GtkUtils.jl +++ /dev/null @@ -1,39 +0,0 @@ -using Cairo, Colors - -using Graphics - - -function Base.copy!(ctx::CairoContext, img::AbstractArray{C}) where C<:Union{Colorant,Number} - Cairo.save(ctx) - Cairo.reset_transform(ctx) - Cairo.image(ctx, image_surface(img), 0, 0, Gtk4.width(ctx), Gtk4.height(ctx)) - Cairo.restore(ctx) -end -Base.copy!(c::GtkCanvas, img) = copy!(getgc(c), img) -function Base.fill!(c::GtkCanvas, color::Colorant) - ctx = getgc(c) - w, h = Gtk4.width(c), Gtk4.height(c) - rectangle(ctx, 0, 0, w, h) - set_source(ctx, color) - fill(ctx) -end - -image_surface(img::Matrix{Gray24}) = CairoImageSurface(copy(reinterpret(UInt32, img)), Cairo.FORMAT_RGB24) -image_surface(img::Matrix{RGB24}) = CairoImageSurface(copy(reinterpret(UInt32, img)), Cairo.FORMAT_RGB24) -image_surface(img::Matrix{ARGB32}) = CairoImageSurface(copy(reinterpret(UInt32, img)), Cairo.FORMAT_ARGB32) - -image_surface(img::AbstractArray{T}) where {T<:Number} = image_surface(convert(Matrix{Gray24}, img)) -image_surface(img::AbstractArray{C}) where {C<:Color} = image_surface(convert(Matrix{RGB24}, img)) -image_surface(img::AbstractArray{C}) where {C<:Colorant} = image_surface(convert(Matrix{ARGB32}, img)) - - -function drawonto(canvas, figure) - CairoMakie.activate!(px_per_unit=3, pt_per_unit=3) - @guarded draw(canvas) do _ - scene = figure.scene - CairoMakie.resize!(scene, Gtk4.width(canvas), Gtk4.height(canvas)) - config = CairoMakie.ScreenConfig(1.0, 1.0, :good, true, false, nothing) - screen = CairoMakie.Screen(scene, config, Gtk4.cairo_surface(canvas)) - CairoMakie.cairo_draw(screen, scene) - end -end \ No newline at end of file diff --git a/src/Viewer/3DViewer/3DViewer.jl b/src/Viewer/3DViewer/3DViewer.jl deleted file mode 100644 index 54d5ba6..0000000 --- a/src/Viewer/3DViewer/3DViewer.jl +++ /dev/null @@ -1,51 +0,0 @@ -abstract type Abstract3DViewerMode end -modeName(m::Abstract3DViewerMode) = string(typeof(m)) - -#= - At the moment it is not possible to update a GtkMakieWidget with new plots. - This means we either have to create a new GtkMakieWidget to update the plot - or we hook into the observables of the plots. - - The second approach breaks for certain plots, for example for isovolumes (if the isovalue changed). -=# -abstract type Abstract3DViewerModeRedrawType end -struct ObservableRedraw <: Abstract3DViewerModeRedrawType end -struct WidgetRedraw <: Abstract3DViewerModeRedrawType end -redrawType(::Abstract3DViewerMode) = ObservableRedraw() - -include("3DViewerWidget.jl") -include("VolumeMode.jl") -include("SectionalMode.jl") -include("IsoSurfaceMode.jl") - -function updateData!(m::Abstract3DViewerMode, arr) - # NOP -end - -function showData!(gm::Gtk4Makie.GtkGLMakie, mode, data; kwargs...) - fig = Figure() - lscene = LScene(fig[1,1]) - res = showData!(WidgetRedraw(), lscene, mode, data; kwargs...) - push!(gm, fig) - return res -end - -export DataViewer3D, DataViewer3DWidget -mutable struct DataViewer3D - w::Gtk4.GtkWindowLeaf - dvw::DataViewer3DWidget -end - -function DataViewer3D(imFG; kwargs...) - dv = DataViewer3D(; kwargs...) - updateData!(dv.dvw,imFG) - return dv -end - -function DataViewer3D(; kwargs...) - w = GtkWindow("Data Viewer",800,600) - dw = DataViewer3DWidget(; kwargs...) - push!(w,dw) - show(w) - return DataViewer3D(w,dw) -end \ No newline at end of file diff --git a/src/Viewer/3DViewer/3DViewerWidget.jl b/src/Viewer/3DViewer/3DViewerWidget.jl deleted file mode 100644 index d2e136c..0000000 --- a/src/Viewer/3DViewer/3DViewerWidget.jl +++ /dev/null @@ -1,202 +0,0 @@ -mutable struct DataViewer3DWidget{A, C} <: Gtk4.GtkGrid - handle::Ptr{Gtk4.GObject} - lscene::LScene - scene::Scene - axis::A - cam::C - gm::Gtk4Makie.GtkGLMakie - # Controls - controlGrid::GtkGrid - modeCb::GtkComboBoxText - modeOpt::GtkMenuButton - frameAdj::GtkAdjustment - channelAdj::GtkAdjustment - cmapCb::GtkComboBoxText - cmin::GtkScaleButton - cmax::GtkScaleButton - # Data - data::AbstractArray - # Mode - modes::Vector{<:Abstract3DViewerMode} -end - -function DataViewer3DWidget(; modes = [VolumeMode, SectionalMode, IsoSurfaceMode]) - grid = GtkGrid() - handle = grid.handle - - # Setup the controls - controls = GtkGrid() - controls.column_spacing = 5 - controls.row_spacing = 5 - - # Mode selection - # We have to initialize the modeBox and the options later - # Once the viewer exits we can initialize the modes with the viewer as parent - # And then fill out our options - modeBox = GtkComboBoxText() - controls[1, 1] = GtkLabel("Mode") - controls[1, 2] = modeBox - modeOptions = GtkMenuButton() - controls[2, 1] = GtkLabel("Options") - controls[2, 2] = modeOptions - controls[3, 1:2] = GtkSeparator(:v) - - # Frame/Time Selection - frameAdj = GtkAdjustment(1, 1, 1, 1, 1, 1) - frameSlider = GtkSpinButton(frameAdj, 1, 0) - controls[4, 1] = GtkLabel("Frames") - controls[4, 2] = frameSlider - channelAdj = GtkAdjustment(1, 1, 1, 1, 1, 1) - channelSlider = GtkSpinButton(channelAdj, 1, 0) - controls[5, 1] = GtkLabel("Channels") - controls[5, 2] = channelSlider - controls[6, 1:2] = GtkSeparator(:v) - - # Colormap Selection - colormapBox = GtkComboBoxText() - cmaps = important_cmaps() - foreach(cm -> push!(colormapBox, cm), cmaps) - controls[7, 1] = GtkLabel("Colormap") - controls[7, 2] = colormapBox - colormapBox.active = 5 # viridis - cmin = GtkScaleButton(0, 99, 1, ["audio-volume-low"]) - cmax = GtkScaleButton(1, 100, 1, ["audio-volume-high"]) - cmin.value = 0 - cmax.value = 100 - controls[8, 1] = GtkLabel("Min") - controls[8, 2] = cmin - controls[9, 1] = GtkLabel("Max") - controls[9, 2] = cmax - - grid[1, 1] = controls - - # Setup the 3D viewing widget - fig = Figure() - lscene = LScene(fig[1,1]) - scene = lscene.scene - cam = scene.camera_controls - axis = first(scene.plots) # Initial plot is an axis for LScene - gm = GtkMakieWidget() - push!(gm, fig) - - grid[1, 2] = gm - - viewer = DataViewer3DWidget(handle, lscene, scene, axis, cam, gm, controls, modeBox, modeOptions, frameAdj, channelAdj, colormapBox, cmin, cmax, [], Abstract3DViewerMode[]) - - # Initialize the modes - modes = map(mode -> mode(viewer), modes) - for mode in modes - push!(modeBox, modeName(mode)) - end - modeBox.active = 0 - modeOptions.popover = popover(first(modes)) - viewer.modes = modes - - initCallbacks(viewer) - - return viewer -end - -function initCallbacks(m::DataViewer3DWidget) - signal_connect(m.modeCb, "changed") do widget - foreach(mode -> mode.active = false, m.modes) - mode = m.modes[m.modeCb.active + 1] - mode.active = true - m.modeOpt.popover = popover(mode) - showData!(WidgetRedraw(), m) - end - - signal_connect(m.frameAdj, "value_changed") do widget - showData!(m) - end - signal_connect(m.channelAdj, "value_changed") do widget - showData!(m) - end - - signal_connect(m.cmapCb, "changed") do widget - showData!(m) - end - - signal_connect(m.cmin, "value_changed") do widget, val - showData!(m) - end - signal_connect(m.cmax, "value_changed") do widget, val - showData!(m) - end -end - - -function updateData!(m::DataViewer3DWidget, file::Union{MDFFile, String}) - imMeta = loadRecoData(file) - updateData!(m, imMeta) -end - -# TODO handle 3 and 4 dim ImageMeta -function updateData!(m::DataViewer3DWidget, imMeta::ImageMeta{T, 5}) where T - m.data = imMeta - m.frameAdj.upper = size(m.data, 5) - m.channelAdj.upper = size(m.data, 1) - map(mode -> updateData!(mode, m.data), m.modes) - showData!(WidgetRedraw(), m) -end - -function updateData!(m::DataViewer3DWidget, array::AbstractArray{T, 5}) where T - m.data = array - m.frameAdj.upper = size(m.data, 5) - m.channelAdj.upper = size(m.data, 1) - m.cmin = 0 - m.cmax = 100 - map(mode -> updateData!(mode, m.data), m.modes) - showData!(WidgetRedraw(), m) -end - -function prepareData(m::DataViewer3DWidget) - frame = round(Int64, m.frameAdj.value) - channel = round(Int64, m.channelAdj.value) - data = ustrip.(m.data[channel, :, :, :, frame]) - return data -end - -function prepareDrawKwargs(m::DataViewer3DWidget) - dict = Dict{Symbol, Any}() - max = maximum(m.data) - cmin = (m.cmin.value/100) * max - cmax = (m.cmax.value/100) * max - dict[:cparams] = ColoringParams(cmin, cmax, Gtk4.active_text(m.cmapCb)) - return dict -end - -showData!(m::DataViewer3DWidget) = showData!(redrawType(m.modes[m.modeCb.active + 1]), m) - -function showData!(::WidgetRedraw, m::DataViewer3DWidget) - # Prepare new GtkMakieWidget - delete!(m, m[1, 2]) - m.gm = GtkMakieWidget() - m[1, 2] = m.gm - - mode = m.modes[m.modeCb.active + 1] - data = prepareData(m) - kwargs = prepareDrawKwargs(m) - lscene = showData!(m.gm, mode, data; kwargs...) - - m.lscene = lscene - m.scene = lscene.scene - m.axis = first(m.scene.plots) - - # Set the camera to the old position - eyeposition = m.cam.eyeposition[] - upvector = m.cam.upvector[] - lookat = m.cam.lookat[] - m.cam = m.scene.camera_controls - update_cam!(m.scene, eyeposition, lookat, upvector) - return nothing -end - -function showData!(re::ObservableRedraw, m::DataViewer3DWidget) - # TODO move these into a function - mode = m.modes[m.modeCb.active + 1] - data = prepareData(m) - kwargs = prepareDrawKwargs(m) - showData!(re, m.lscene, mode, data; kwargs...) - return nothing -end diff --git a/src/Viewer/3DViewer/IsoSurfaceMode.jl b/src/Viewer/3DViewer/IsoSurfaceMode.jl deleted file mode 100644 index aa74488..0000000 --- a/src/Viewer/3DViewer/IsoSurfaceMode.jl +++ /dev/null @@ -1,77 +0,0 @@ -export IsoSurfaceMode - - -mutable struct IsoSurfaceMode{P} <: Abstract3DViewerMode - pop::GtkPopover - parent::P - active::Bool - isoAdj::GtkAdjustment - isoMin::GtkLabel - isoMax::GtkLabel - isorange::GtkEntry -end -function IsoSurfaceMode(parent::P) where P - pop = GtkPopover() - grid = GtkGrid() - grid[1, 1] = GtkLabel("Iso Value:") - minLabel = GtkLabel("0.0") - maxLabel = GtkLabel("1.0") - adj = GtkAdjustment(0.5, 0.0, 1.0, 0.05, 0.05, 0.05) - scale = GtkScale(:h, adj) - scale.hexpand = true - grid[2, 1] = minLabel - grid[3, 1] = scale - grid[4, 1] = maxLabel - - grid[1, 2] = GtkLabel("Iso Range:") - range = GtkEntry() - range.text = "0.05" - grid[2:4, 2] = range - - - pop.child = grid - - mode = IsoSurfaceMode(pop, parent, false, adj, minLabel, maxLabel, range) - - initCallbacks!(mode) - - return mode -end - -function initCallbacks!(mode::IsoSurfaceMode) - signal_connect(mode.isoAdj, "value_changed") do widget - if mode.active - showData!(WidgetRedraw(), mode.parent) - end - end - signal_connect(mode.isorange, "changed") do widget - if mode.active - showData!(WidgetRedraw(), mode.parent) - end - end -end - -modeName(m::IsoSurfaceMode) = "Iso Surface" -popover(m::IsoSurfaceMode) = m.pop -redrawType(::IsoSurfaceMode) = WidgetRedraw() - - -function updateData!(m::IsoSurfaceMode, data) - min, max = extrema(data) - m.isoAdj.upper = max - m.isoAdj.lower = min - m.isoAdj.step_increment = (max - min) / 100 - m.isoMin.label = string(min) - m.isoMax.label = string(max) -end - -function showData!(re, scene::LScene, mode::IsoSurfaceMode, data; kwargs...) - showData!(re, scene.scene, mode, data; kwargs...) - return scene -end -function showData!(::WidgetRedraw, scene::Scene, mode::IsoSurfaceMode, data; kwargs...) - isovalue = mode.isoAdj.value - isoRange = parse(Float64, mode.isorange.text) - volume!(scene, data; algorithm=:iso, isovalue=isovalue, isorange=isoRange) - return scene -end diff --git a/src/Viewer/3DViewer/SectionalMode.jl b/src/Viewer/3DViewer/SectionalMode.jl deleted file mode 100644 index 764f0dc..0000000 --- a/src/Viewer/3DViewer/SectionalMode.jl +++ /dev/null @@ -1,121 +0,0 @@ - -export SectionalMode - -mutable struct SectionalMode{P} <: Abstract3DViewerMode - pop::GtkPopover - parent::P - active::Bool - yzAdj::GtkAdjustment - xzAdj::GtkAdjustment - xyAdj::GtkAdjustment - yzToggler::GtkCheckButton - xzToggler::GtkCheckButton - xyToggler::GtkCheckButton -end - -function SectionalMode(parent::P) where P - pop = GtkPopover() - yzAdj = GtkAdjustment(1, 1, 1, 1, 1, 1) - xzAdj = GtkAdjustment(1, 1, 1, 1, 1, 1) - xyAdj = GtkAdjustment(1, 1, 1, 1, 1, 1) - yzToggler = GtkCheckButton("Visible") - xzToggler = GtkCheckButton("Visible") - xyToggler = GtkCheckButton("Visible") - - grid = GtkGrid() - grid[1, 1] = GtkLabel("xy") - grid[1, 2] = GtkSpinButton(xyAdj, 1, 0) - grid[1, 3] = xyToggler - - grid[2, 1] = GtkLabel("xz") - grid[2, 2] = GtkSpinButton(xzAdj, 1, 0) - grid[2, 3] = xzToggler - - grid[3, 1] = GtkLabel("yz") - grid[3, 2] = GtkSpinButton(yzAdj, 1, 0) - grid[3, 3] = yzToggler - - pop.child = grid - mode = SectionalMode(pop, parent, false, yzAdj, xzAdj, xyAdj, yzToggler, xzToggler, xyToggler) - - initCallbacks!(mode) - - return mode -end - -modeName(m::SectionalMode) = "Volume Slices" -popover(m::SectionalMode) = m.pop - -function initCallbacks!(mode::SectionalMode) - signal_connect(mode.yzAdj, "value_changed") do widget - if mode.active - showData!(ObservableRedraw(), mode.parent) - end - end - signal_connect(mode.xzAdj, "value_changed") do widget - if mode.active - showData!(ObservableRedraw(), mode.parent) - end - end - signal_connect(mode.xyAdj, "value_changed") do widget - if mode.active - showData!(ObservableRedraw(), mode.parent) - end - end - - signal_connect(mode.yzToggler, "toggled") do widget - if mode.active - showData!(ObservableRedraw(), mode.parent) - end - end - signal_connect(mode.xzToggler, "toggled") do widget - if mode.active - showData!(ObservableRedraw(), mode.parent) - end - end - signal_connect(mode.xyToggler, "toggled") do widget - if mode.active - showData!(ObservableRedraw(), mode.parent) - end - end -end - -function updateData!(m::SectionalMode, data5D::AbstractArray{T, 5}) where T - m.yzAdj.upper = size(data5D, 2) - m.xzAdj.upper = size(data5D, 3) - m.xyAdj.upper = size(data5D, 4) - m.yzToggler.active = true - m.xzToggler.active = true - m.xyToggler.active = true -end - -function showData!(re, scene::LScene, mode::SectionalMode, data; kwargs...) - showData!(re, scene.scene, mode, data; kwargs...) - return scene -end -function showData!(::WidgetRedraw, scene::Scene, mode::SectionalMode, data; cparams = ColoringParams(extrema(data)..., "viridis"), kwargs...) - cmap = to_colormap(Symbol(cparams.cmap)) - plt = volumeslices!(scene, map(i -> 1:i, size(data))..., data; bbox_visible = false, colormap=cmap, colorrange = (cparams.cmin, cparams.cmax)) - plt.heatmap_xy[].visible = Observable{Any}(true) - plt.heatmap_xz[].visible = Observable{Any}(true) - plt.heatmap_yz[].visible = Observable{Any}(true) - return scene -end -function showData!(::ObservableRedraw, scene::Scene, mode::SectionalMode, data; cparams = ColoringParams(extrema(data)..., "viridis"), kwargs...) - # TODO robust plot selection - cmap = to_colormap(Symbol(cparams.cmap)) - plt = scene.plots[2] - plt[1] = 1:size(data, 1) - plt[2] = 1:size(data, 2) - plt[3] = 1:size(data, 3) - plt[4] = data - plt.update_xz[](Int64(mode.xzAdj.value)) - plt.update_xy[](Int64(mode.xyAdj.value)) - plt.update_yz[](Int64(mode.yzAdj.value)) - plt.heatmap_xy[].visible[] = mode.xyToggler.active - plt.heatmap_xz[].visible[] = mode.xzToggler.active - plt.heatmap_yz[].visible[] = mode.yzToggler.active - plt.colormap[] = cmap - plt.colorrange[] = (cparams.cmin, cparams.cmax) - return scene -end \ No newline at end of file diff --git a/src/Viewer/3DViewer/VolumeMode.jl b/src/Viewer/3DViewer/VolumeMode.jl deleted file mode 100644 index ae869fb..0000000 --- a/src/Viewer/3DViewer/VolumeMode.jl +++ /dev/null @@ -1,63 +0,0 @@ -export VolumeMode - - -mutable struct VolumeMode{P} <: Abstract3DViewerMode - pop::GtkPopover - parent::P - active::Bool - algBox::GtkComboBoxText - algorithms::Vector{Symbol} -end -function VolumeMode(parent::P) where P - algoStrings = ["Absorption", "Additive RGBA", "Absorption RGBA", "MIP"] - algoSymbols = [:absorption, :additive, :absorptionrgba, :mip] - box = GtkComboBoxText() - foreach(algo -> push!(box, algo), algoStrings) - box.active = 3 - - pop = GtkPopover() - grid = GtkGrid() - grid[1, 1] = GtkLabel("Algorithm") - grid[1, 2] = box - - pop.child = grid - - mode = VolumeMode(pop, parent, false, box, algoSymbols) - - initCallbacks!(mode) - - return mode -end - -function initCallbacks!(mode::VolumeMode) - signal_connect(mode.algBox, "changed") do widget - if mode.active - showData!(WidgetRedraw(), mode.parent) - end - end -end - -modeName(m::VolumeMode) = "Volume" -popover(m::VolumeMode) = m.pop - -function showData!(re, scene::LScene, mode::VolumeMode, data; kwargs...) - showData!(re, scene.scene, mode, data; kwargs...) - return scene -end -function showData!(::WidgetRedraw, scene::Scene, mode::VolumeMode, data; cparams = ColoringParams(extrema(data)..., "viridis"), kwargs...) - algo = mode.algorithms[mode.algBox.active + 1] - cmap = to_colormap(Symbol(cparams.cmap)) - cmap[1] = RGBA(0.0, 0.0, 0.0, 0.0) - volume!(scene, data; algorithm=algo, colormap=cmap, colorrange = (cparams.cmin, cparams.cmax)) - return scene -end -function showData!(::ObservableRedraw, scene::Scene, mode::VolumeMode, data; cparams = ColoringParams(extrema(data)..., "viridis"), kwargs...) - # TODO robust - plt = scene.plots[2] - cmap = to_colormap(Symbol(cparams.cmap)) - cmap[1] = RGBA(0.0, 0.0, 0.0, 0.0) - plt.colormap[] = cmap - plt.colorrange[] = (cparams.cmin, cparams.cmax) - plt[4][] = data - return scene -end diff --git a/src/Viewer/BaseViewer.jl b/src/Viewer/BaseViewer.jl deleted file mode 100644 index 1cd4f47..0000000 --- a/src/Viewer/BaseViewer.jl +++ /dev/null @@ -1,57 +0,0 @@ -using Gtk4, Cairo - -export baseViewer, baseViewerStandAlone, updateView, drawMIP - -mutable struct BaseViewerWidget - builder - zxSliceGrid - zySliceGrid - yxSliceGrid -end - - -function baseViewerStandAlone() - w = Window("Base Viewer",800,600) - set_gtk_property!(w,:hexpand,true) - set_gtk_property!(w,:vexpand,true) - m, bv = baseViewer() - push!(w, bv) - show(w) - return w,m,bv -end - -getindex(m::BaseViewerWidget, w::AbstractString) = Gtk4.G_.get_object(m.builder, w) - -function baseViewer() - uifile = joinpath(@__DIR__,"..","builder","baseViewer.ui") - b = GtkBuilder(uifile) - m = BaseViewerWidget(b, nothing,nothing,nothing) - w = m["parentGrid"] - m.zxSliceGrid = m["zxSlice"] - m.zySliceGrid = m["zySlice"] - m.yxSliceGrid = m["yxSlice"] - m.zxSliceGrid[1,1] = GtkCanvas() - m.zySliceGrid[1,1] = GtkCanvas() - m.yxSliceGrid[1,1] = GtkCanvas() - - - - return m, w -end - -function updateView(m::BaseViewerWidget,zx,zy,yx) - drawMIP(m.zxSliceGrid[1,1],m.zySliceGrid[1,1],m.yxSliceGrid[1,1],zx,zy,yx) -end - -function drawMIP(controlzx,controlzy,controlyx,zx,zy,yx) - drawSlice(controlzx, zx) - drawSlice(controlzy, zy) - drawSlice(controlyx, yx) -end - -function drawSlice(control, slice) - @guarded Gtk4.draw(control) do widget - ctx = getgc(control) - copy!(ctx, slice) - end -end diff --git a/src/Viewer/DataViewer/DataViewer.jl b/src/Viewer/DataViewer/DataViewer.jl deleted file mode 100644 index b318cd5..0000000 --- a/src/Viewer/DataViewer/DataViewer.jl +++ /dev/null @@ -1,620 +0,0 @@ -using Gtk4 - -export DataViewer, DataViewerWidget - -########### DataViewerWidget ################# - -mutable struct DataViewerWidget <: Gtk4.GtkBox - handle::Ptr{Gtk4.GObject} - builder::GtkBuilder - grid2D::Gtk4.GtkGridLeaf - grid3D::Gtk4.GtkGridLeaf - coloring::Vector{ColoringParams} - upgradeColoringWInProgress::Bool - cacheSelectedFovXYZ::Array{Float64,1} - cacheSelectedMovePos::Array{Float64,1} - stopPlayingMovie::Bool - updating::Bool - data - dataBG - dataBGNotPermuted - currentlyShownImages - currentlyShownData - currentProfile -end - -getindex(m::DataViewerWidget, w::AbstractString) = Gtk4.G_.get_object(m.builder, w) - -mutable struct DataViewer - w::Gtk4.GtkWindowLeaf - dvw::DataViewerWidget -end - -function DataViewer(imFG::ImageMeta, imBG=nothing; params=nothing) - dv = DataViewer() - updateData!(dv.dvw,imFG,imBG) - if params!=nothing - setParams(dv.dvw,params) - end - return dv -end - -function DataViewer() - w = GtkWindow("Data Viewer",800,600) - dw = DataViewerWidget() - push!(w,dw) - show(w) - - #=signal_connect(w, "key-press-event") do widget, event - if event.keyval == Gtk4.GConstants.GDK_KEY_c - if event.state & 0x04 != 0x00 # Control key is pressed - @debug "copy visu params to clipboard..." - str = string( getParams(dw) ) - str_ = replace(str,",Pair",",\n Pair") - clipboard( str_ ) - end - end - end=# - - return DataViewer(w,dw) -end - -include("Export.jl") -include("Drawing.jl") - -function DataViewerWidget() - - uifile = joinpath(@__DIR__,"..","..","builder","dataviewer.ui") - - b = GtkBuilder(uifile) - mainBox = Gtk4.G_.get_object(b, "boxDataViewer") - m = DataViewerWidget( mainBox.handle, b, - Gtk4.G_.get_object(b, "gridDataViewer2D"), - Gtk4.G_.get_object(b, "gridDataViewer3D"), - Vector{ColoringParams}(), false, - [0.0,0.0,0.0], [0.0,0.0,0.0], false, false, - nothing, nothing, nothing,nothing, nothing, nothing) - Gtk4.GLib.gobject_move_ref(m, mainBox) - - m.grid3D[2,1] = GtkCanvas() - m.grid3D[1,1] = GtkCanvas() - m.grid3D[2,2] = GtkCanvas() - m.grid3D[1,2] = GtkCanvas() - m.grid2D[1,1] = GtkCanvas() - - show(m) - - choices = important_cmaps() - for c in choices - push!(m["cbCMaps"], c) - push!(m["cbCMapsBG"], c) - end - set_gtk_property!(m["cbCMaps"],:active,5) # default: viridis - set_gtk_property!(m["cbCMapsBG"],:active,0) # default: gray - - permutes = permuteCombinationsName() - for c in permutes - push!(m["cbPermutes"], c) - end - set_gtk_property!(m["cbPermutes"], :active, 0) - - flippings = flippingsName() - for c in flippings - push!(m["cbFlips"], c) - end - set_gtk_property!(m["cbFlips"], :active, 0) - - choicesFrameProj = ["None", "MIP", "TTP"] - for c in choicesFrameProj - push!(m["cbFrameProj"], c) - end - set_gtk_property!(m["cbFrameProj"],:active,0) - - visible(m["mbFusion"], false) - visible(m["lbFusion"], false) - visible(m["sepFusion"], false) - - signal_connect(m["btnPlayMovie"], "toggled") do widget - isActive = get_gtk_property(m["btnPlayMovie"], :active, Bool) - if isActive - m.stopPlayingMovie = false - @idle_add_guarded playMovie() - else - m.stopPlayingMovie = true - end - end - - function playMovie() - @async begin - L = get_gtk_property(m["adjFrames"],:upper, Int64) - curFrame = get_gtk_property(m["adjFrames"],:value, Int64) - while !m.stopPlayingMovie - @idle_add_guarded set_gtk_property!(m["adjFrames"],:value, curFrame) - yield() - sleep(0.01) - curFrame = mod1(curFrame+1,L) - end - m.stopPlayingMovie = false - end - end - - initCallbacks(m) - - return m -end - -function initCallbacks(m_::DataViewerWidget) - let m=m_ - - function update( widget ) - if !m.upgradeColoringWInProgress - updateColoring( m ) - showData( m ) - end - end - - function updateChan( widget ) - updateColoringWidgets( m ) - update( m ) - end - - widgets = ["cbSpatialMIP", "cbShowSlices", "cbHideFG", "cbHideBG", - "cbBlendChannels", "cbTranslucentBlending", - "cbSpatialBGMIP", "cbShowDFFOV", "cbComplexBlending", - "cbShowAxes"] - for w in widgets - signal_connect(update, m[w], "toggled") - end - - widgets = ["adjFrames", "adjSliceX", "adjSliceY","adjSliceZ", - "adjCMin", "adjCMax", "adjCMinBG", "adjCMaxBG", - "adjTransX", "adjTransY", "adjTransZ", - "adjRotX", "adjRotY", "adjRotZ", - "adjTransBGX", "adjTransBGY", "adjTransBGZ", - "adjRotBGX", "adjRotBGY", "adjRotBGZ", - "adjTTPThresh"] - for w in widgets - signal_connect(update, m[w], "value_changed") - end - - widgets = ["cbCMaps", "cbCMapsBG", "cbFrameProj"] - for w in widgets - signal_connect(update, m[w], "changed") - end - - signal_connect(updateChan, m["cbChannel"], "changed") - - signal_connect(m["cbPermutes"], "changed") do widget - try - m.updating = true - permuteBGData(m) - updateSliceWidgets(m) - m.updating = false - update(m) - catch e - @error e - showError(e) - end - end - - signal_connect(m["cbFlips"], "changed") do widget - try - m.updating = true - permuteBGData(m) - updateSliceWidgets(m) - m.updating = false - update(m) - catch e - @error e - showError(e) - end - end - - signal_connect(m["btnSaveVisu"], "clicked") do widget - try - params = getParams(m) - # Need to convert the coloring into the old Int format - coloring = params[:coloring] - - coloringInt = ColoringParamsInt[] - for c in coloring - idx = findfirst(a->a==c.cmap,important_cmaps())-1 - push!(coloringInt, ColoringParamsInt(c.cmin, c.cmax, idx)) - end - params[:coloring] = coloringInt - - c = params[:coloringBG] - idx = findfirst(a->a==c.cmap,important_cmaps())-1 - params[:coloringBG] = ColoringParamsInt(c.cmin, c.cmax, idx) - - addVisu(mpilab[], params) - catch e - showError(e) - end - end - - initExportCallbacks(m) - end -end - -function permuteBGData(m::DataViewerWidget) - if m.dataBGNotPermuted != nothing - permInd = get_gtk_property(m["cbPermutes"], :active, Int64) + 1 - flipInd = get_gtk_property(m["cbFlips"], :active, Int64) + 1 - perm = permuteCombinations()[permInd] - flip = flippings()[flipInd] - m.dataBG = applyPermutions(m.dataBGNotPermuted, perm, flip) - - fov = collect(size(m.dataBG)).*collect(converttometer(pixelspacing(m.dataBG))).*1000 - - set_gtk_property!(m["adjTransX"],:lower,-fov[1]/2) - set_gtk_property!(m["adjTransY"],:lower,-fov[2]/2) - set_gtk_property!(m["adjTransZ"],:lower,-fov[3]/2) - set_gtk_property!(m["adjTransX"],:upper,fov[1]/2) - set_gtk_property!(m["adjTransY"],:upper,fov[2]/2) - set_gtk_property!(m["adjTransZ"],:upper,fov[3]/2) - - set_gtk_property!(m["adjTransBGX"],:lower,-fov[1]/2) - set_gtk_property!(m["adjTransBGY"],:lower,-fov[2]/2) - set_gtk_property!(m["adjTransBGZ"],:lower,-fov[3]/2) - set_gtk_property!(m["adjTransBGX"],:upper,fov[1]/2) - set_gtk_property!(m["adjTransBGY"],:upper,fov[2]/2) - set_gtk_property!(m["adjTransBGZ"],:upper,fov[3]/2) - end -end - -function updateSliceWidgets(m::DataViewerWidget) - - sfw = get_gtk_property(m["adjFrames"],:upper,Int64) - sxw = get_gtk_property(m["adjSliceX"],:upper,Int64) - syw = get_gtk_property(m["adjSliceY"],:upper,Int64) - szw = get_gtk_property(m["adjSliceZ"],:upper,Int64) - refdata = (m.dataBG == nothing) ? m.data[1,:,:,:,:] : m.dataBG - - if refdata != nothing - - if size(refdata,4) != sfw - @idle_add_guarded set_gtk_property!(m["adjFrames"],:value, 1) - @idle_add_guarded set_gtk_property!(m["adjFrames"],:upper,size(m.data,Axis{:time})) - end - - if size(refdata,1) != sxw || size(refdata,2) != syw || size(refdata,3) != szw - @idle_add_guarded set_gtk_property!(m["adjSliceX"],:upper,size(refdata,1)) - @idle_add_guarded set_gtk_property!(m["adjSliceY"],:upper,size(refdata,2)) - @idle_add_guarded set_gtk_property!(m["adjSliceZ"],:upper,size(refdata,3)) - - @idle_add_guarded set_gtk_property!(m["adjSliceX"],:value,max(div(size(refdata,1),2),1)) - @idle_add_guarded set_gtk_property!(m["adjSliceY"],:value,max(div(size(refdata,2),2),1)) - @idle_add_guarded set_gtk_property!(m["adjSliceZ"],:value,max(div(size(refdata,3),2),1)) - end - end - return -end - -function updateData!(m::DataViewerWidget, data::ImageMeta{T,3}, dataBG=nothing; kargs...) where T - ax = ImageUtils.AxisArrays.axes(data) - dAx = AxisArray(reshape(data.data.data, 1, size(data,1), size(data,2), size(data,3), 1), - Axis{:color}(1:1), ax[1], ax[2], ax[3], - Axis{:time}(range(0*unit(1u"s"),step=1u"s",length=1))) - dIm = copyproperties(data, dAx) - updateData!(m, dIm, dataBG; kargs...) -end - -function updateData!(m::DataViewerWidget, data::ImageMeta{T,4}, dataBG=nothing; kargs...) where T - ax = ImageUtils.AxisArrays.axes(data) - - if timeaxis(data) == nothing - dAx = AxisArray(reshape(data.data.data, size(data,1), size(data,2), - size(data,3), size(data,4), 1), - ax[1], ax[2], ax[3], ax[4], - Axis{:time}(range(0*unit(1u"s"),step=1u"s",length=1))) - else - dAx = AxisArray(reshape(data.data.data, 1, size(data,1), size(data,2), - size(data,3), size(data,4)), - Axis{:color}(1:1), ax[1], ax[2], ax[3], ax[4]) - end - - dIm = copyproperties(data, dAx) - updateData!(m, dIm, dataBG; kargs...) -end - -function updateData!(m::DataViewerWidget, data::ImageMeta{T,5}, dataBG=nothing; params=nothing, ampPhase=false) where T - #try - m.updating = true - numChan = size(data,ImageUtils.Axis{:color}) - - visible(m["mbFusion"], dataBG != nothing) - visible(m["lbFusion"], dataBG != nothing) - visible(m["sepFusion"], dataBG != nothing) - - multiChannel = numChan > 1 - visible(m["cbBlendChannels"], multiChannel) - visible(m["cbChannel"], multiChannel) - visible(m["lbChannel"], multiChannel) - - - if m.data == nothing || (size(m.data,Axis{:color}) != numChan) - m.coloring = Array{ColoringParams}(undef,numChan) - if numChan == 1 - m.coloring[1] = ColoringParams(0.0,1.0,"viridis") - end - if numChan > 2 - for l=1:numChan - m.coloring[l] = ColoringParams(0.0,1.0,l) - end - end - if numChan == 2 - if ampPhase - m.coloring[1] = ColoringParams(0.0,1.0,"viridis") - m.coloring[2] = ColoringParams(0.0,1.0,"viridis") - else - m.coloring[1] = ColoringParams(0.0,1.0,"blue") - m.coloring[2] = ColoringParams(0.0,1.0,"red") - end - end - - @idle_add_guarded begin - empty!(m["cbChannel"]) - for i=1:numChan - push!(m["cbChannel"], "$i") - end - set_gtk_property!(m["cbChannel"],:active,0) - updateColoringWidgets( m ) - end - - @idle_add_guarded begin - strProfile = ["x direction", "y direction", "z direction","temporal"] - empty!(m["cbProfile"]) - nd = size(data,5) > 1 ? 4 : 3 - for i=1:nd - push!(m["cbProfile"], strProfile[i]) - end - set_gtk_property!(m["cbProfile"],:active,nd==4 ? 3 : 0) - end - - set_gtk_property!(m["cbBlendChannels"], :active, numChan>1 ) - set_gtk_property!(m["cbComplexBlending"], :active, ampPhase) - end - - m.data = data - - m.dataBGNotPermuted = dataBG - m.dataBG = nothing - permuteBGData(m) - updateSliceWidgets(m) - m.updating = false - showData(m) - @idle_add_guarded set_gtk_property!(m["adjPixelResizeFactor"],:value, (dataBG==nothing) ? 5 : 1 ) - - if params!=nothing - if data != nothing - params[:sliceX] = min(params[:sliceX],size(data,Axis{:x})) - params[:sliceY] = min(params[:sliceY],size(data,Axis{:y})) - params[:sliceZ] = min(params[:sliceZ],size(data,Axis{:z})) - end - setParams(m,params) - end - #catch ex - # @warn "Exception" ex stacktrace(catch_backtrace()) - #end -end - -function updateColoringWidgets(m::DataViewerWidget) - m.upgradeColoringWInProgress = true - chan = max(get_gtk_property(m["cbChannel"],:active, Int64) + 1,1) - @idle_add_guarded set_gtk_property!(m["adjCMin"],:value, m.coloring[chan].cmin) - @idle_add_guarded set_gtk_property!(m["adjCMax"],:value, m.coloring[chan].cmax) - idx = findfirst(a->a==m.coloring[chan].cmap,important_cmaps())-1 - @idle_add_guarded set_gtk_property!(m["cbCMaps"],:active, idx) - m.upgradeColoringWInProgress = false -end - -function updateColoring(m::DataViewerWidget) - chan = max(get_gtk_property(m["cbChannel"],:active, Int64) + 1,1) - cmin = get_gtk_property(m["adjCMin"],:value, Float64) - cmax = get_gtk_property(m["adjCMax"],:value, Float64) - cmap = important_cmaps()[get_gtk_property(m["cbCMaps"],:active, Int64)+1] - m.coloring[chan] = ColoringParams(cmin,cmax,cmap) -end - -function showData(m::DataViewerWidget) - if !m.updating - try - params = getParams(m) - if m.data != nothing - - data_ = sliceTimeDim(m.data, params[:frameProj] == 1 ? "MIP" : params[:frame]) - - slices = (params[:sliceX],params[:sliceY],params[:sliceZ]) - - if params[:spatialMIP] - proj = "MIP" - else - proj = slices - end - - # global windowing - maxval = [maximum(sliceColorDim(m.data,d)) for d=1:size(m.data,1)] - minval = [minimum(sliceColorDim(m.data,d)) for d=1:size(m.data,1)] - #if params[:frameProj] == 2 && ndims(m.data[1]) == 4 - # maxval = [maximum(d) for d in data_] - # minval = [minimum(d) for d in data_] - #end - - if m.dataBG != nothing - slicesInRawData = ImageUtils.indexFromBGToFG(m.dataBG, data_, params) - @debug "Slices in raw data:" slicesInRawData - data = interpolateToRefImage(m.dataBG, data_, params) - dataBG = interpolateToRefImage(m.dataBG, params) - else - # not ideal .... - params[:sliceX] = min(params[:sliceX],size(m.data,Axis{:x})) - params[:sliceY] = min(params[:sliceY],size(m.data,Axis{:y})) - params[:sliceZ] = min(params[:sliceZ],size(m.data,Axis{:z})) - - data = data_ - slicesInRawData = slices - dataBG = nothing - end - - m.currentlyShownData = data - - if ndims(squeeze(data[1,:,:,:])) >= 2 - cdata_zx, cdata_zy, cdata_xy = getColoredSlices(data, dataBG, m.coloring, minval, maxval, params) - isDrawSectionalLines = params[:showSlices] && proj != "MIP" - isDrawRectangle = params[:showDFFOV] - isDrawAxes = params[:showAxes] - pixelSpacingBG = (dataBG==nothing) ? [0.002,0.002,0.001] : collect(converttometer(pixelspacing(dataBG))) - sizeBG = (dataBG==nothing) ? [128,128,64] : collect(size(dataBG)) - drawImages(m,slices, isDrawSectionalLines, isDrawRectangle, isDrawAxes, cdata_zx, cdata_zy, cdata_xy, - [params[:transX], params[:transY], params[:transZ]], pixelSpacingBG, sizeBG) - - if ndims(m.data) >= 3 && slicesInRawData != (0,0,0) - showProfile(m, params, slicesInRawData) - end - Gtk4.G_.set_current_page(m["nb2D3D"], 0) - m.currentlyShownImages = [cdata_xy, cdata_zx, cdata_zy] - else - dat = vec(data_) - - fig, ax, l_ = CairoMakie.lines(1:length(dat), dat, - figure = (; size = (1000, 800), fontsize = 12), - color = CairoMakie.RGBf(colors[1]...)) - - CairoMakie.autolimits!(ax) - if length(dat) > 1 - CairoMakie.xlims!(ax, 1, length(dat)) - end - ax.xlabel = "x" - ax.ylabel = "y" - drawonto(m.grid2D[1,1], fig) - - Gtk4.G_.set_current_page(m["nb2D3D"], 1) - - #m.currentlyShownImages = cdata - end - end - catch ex - @info ex - showError(ex) - # @warn "Exception" ex stacktrace(catch_backtrace()) - end - end -end - -function getParams(m::DataViewerWidget) - params = defaultVisuParams() - params[:sliceX] = get_gtk_property(m["adjSliceX"], :value, Int64) - params[:sliceY] = get_gtk_property(m["adjSliceY"], :value, Int64) - params[:sliceZ] = get_gtk_property(m["adjSliceZ"], :value, Int64) - params[:frame] = get_gtk_property(m["adjFrames"], :value, Int64) - params[:spatialMIP] = get_gtk_property(m["cbSpatialMIP"], :active, Bool) - params[:coloring] = m.coloring - params[:description] = get_gtk_property(m["entVisuName"], :text, String) - params[:showSlices] = get_gtk_property(m["cbShowSlices"], :active, Bool) - - params[:permuteBG] = permuteCombinations()[get_gtk_property(m["cbPermutes"], :active, Int64) + 1] - params[:flipBG] = flippings()[get_gtk_property(m["cbFlips"], :active, Int64) + 1] - - params[:transX] = get_gtk_property(m["adjTransX"], :value, Float64) / 1000 - params[:transY] = get_gtk_property(m["adjTransY"], :value, Float64) / 1000 - params[:transZ] = get_gtk_property(m["adjTransZ"], :value, Float64) / 1000 - params[:rotX] = get_gtk_property(m["adjRotX"], :value, Float64) - params[:rotY] = get_gtk_property(m["adjRotY"], :value, Float64) - params[:rotZ] = get_gtk_property(m["adjRotZ"], :value, Float64) - params[:transBGX] = get_gtk_property(m["adjTransBGX"], :value, Float64) / 1000 - params[:transBGY] = get_gtk_property(m["adjTransBGY"], :value, Float64) / 1000 - params[:transBGZ] = get_gtk_property(m["adjTransBGZ"], :value, Float64) / 1000 - params[:rotBGX] = get_gtk_property(m["adjRotBGX"], :value, Float64) - params[:rotBGY] = get_gtk_property(m["adjRotBGY"], :value, Float64) - params[:rotBGZ] = get_gtk_property(m["adjRotBGZ"], :value, Float64) - params[:coloringBG] = ColoringParams(get_gtk_property(m["adjCMinBG"], :value, Float64), - get_gtk_property(m["adjCMaxBG"], :value, Float64), - get_gtk_property(m["cbCMapsBG"], :active, Int64)) - - params[:filenameBG] = (m.dataBGNotPermuted != nothing) && haskey(m.dataBGNotPermuted, "filename") ? m.dataBGNotPermuted["filename"] : "" - - params[:hideFG] = get_gtk_property(m["cbHideFG"], :active, Bool) - params[:hideBG] = get_gtk_property(m["cbHideBG"], :active, Bool) - params[:showDFFOV] = get_gtk_property(m["cbShowDFFOV"], :active, Bool) - params[:translucentBlending] = get_gtk_property(m["cbTranslucentBlending"], :active, Bool) - params[:spatialMIPBG] = get_gtk_property(m["cbSpatialBGMIP"], :active, Bool) - - - params[:TTPThresh] = get_gtk_property(m["adjTTPThresh"], :value, Float64) - params[:frameProj] = get_gtk_property(m["cbFrameProj"], :active, Int64) - - params[:blendChannels] = get_gtk_property(m["cbBlendChannels"], :active, Bool) - params[:complexBlending] = get_gtk_property(m["cbComplexBlending"], :active, Bool) - params[:showAxes] = get_gtk_property(m["cbShowAxes"], :active, Bool) - - params[:profile] = get_gtk_property(m["cbProfile"], :active, Int64) - - params[:activeChannel] = max(get_gtk_property(m["cbChannel"],:active, Int64) + 1,1) - - return params -end - -function setParams(m::DataViewerWidget, params) - @idle_add_guarded set_gtk_property!(m["adjSliceX"], :value, params[:sliceX]) - @idle_add_guarded set_gtk_property!(m["adjSliceY"], :value, params[:sliceY]) - @idle_add_guarded set_gtk_property!(m["adjSliceZ"], :value, params[:sliceZ]) - @idle_add_guarded set_gtk_property!(m["adjFrames"], :value, params[:frame]) - @idle_add_guarded set_gtk_property!(m["cbSpatialMIP"], :active, params[:spatialMIP]) - m.coloring = Vector{ColoringParams}(undef,0) - for col in params[:coloring] - push!(m.coloring, ColoringParams(col.cmin, col.cmax, col.cmap)) - end - updateColoringWidgets(m) - @idle_add_guarded set_gtk_property!(m["entVisuName"], :text, params[:description]) - @idle_add_guarded set_gtk_property!(m["cbShowSlices"], :active, get(params,:showSlices,false)) - - # The following is for backwards compatibility with a former data format - # where instead of permuteBG and flipBG we stored permutionBG - if haskey(params, :permutionBG) - perm, flip = convertMode2PermFlip(params[:permutionBG]) - @idle_add_guarded set_gtk_property!(m["cbPermutes"], :active, findall(x->x==perm,permuteCombinations())[1] - 1) - @idle_add_guarded set_gtk_property!(m["cbFlips"], :active, findall(x->x==flip,flippings())[1] - 1) - else - @idle_add_guarded set_gtk_property!(m["cbPermutes"], :active, findall(x->x==params[:permuteBG],permuteCombinations())[1] - 1) - @idle_add_guarded set_gtk_property!(m["cbFlips"], :active, findall(x->x==params[:flipBG],flippings())[1] - 1) - end - - @idle_add_guarded set_gtk_property!(m["adjTransX"], :value, params[:transX]*1000) - @idle_add_guarded set_gtk_property!(m["adjTransY"], :value, params[:transY]*1000) - @idle_add_guarded set_gtk_property!(m["adjTransZ"], :value, params[:transZ]*1000) - @idle_add_guarded set_gtk_property!(m["adjRotX"], :value, params[:rotX]) - @idle_add_guarded set_gtk_property!(m["adjRotY"], :value, params[:rotY]) - @idle_add_guarded set_gtk_property!(m["adjRotZ"], :value, params[:rotZ]) - - @idle_add_guarded set_gtk_property!(m["adjTransBGX"], :value, get(params,:transBGX,0.0)*1000) - @idle_add_guarded set_gtk_property!(m["adjTransBGY"], :value, get(params,:transBGY,0.0)*1000) - @idle_add_guarded set_gtk_property!(m["adjTransBGZ"], :value, get(params,:transBGZ,0.0)*1000) - @idle_add_guarded set_gtk_property!(m["adjRotBGX"], :value, get(params,:rotBGX,0.0)) - @idle_add_guarded set_gtk_property!(m["adjRotBGY"], :value, get(params,:rotBGY,0.0)) - @idle_add_guarded set_gtk_property!(m["adjRotBGZ"], :value, get(params,:rotBGZ,0.0)) - - @idle_add_guarded set_gtk_property!(m["adjCMinBG"], :value, params[:coloringBG].cmin) - @idle_add_guarded set_gtk_property!(m["adjCMaxBG"], :value, params[:coloringBG].cmax) - @idle_add_guarded set_gtk_property!(m["cbCMapsBG"], :active, params[:coloringBG].cmap) - @idle_add_guarded set_gtk_property!(m["cbHideFG"], :active, get(params,:hideFG, false)) - @idle_add_guarded set_gtk_property!(m["cbHideBG"], :active, get(params,:hideBG, false)) - @idle_add_guarded set_gtk_property!(m["cbShowDFFOV"], :active, get(params,:showDFFOV, false)) - @idle_add_guarded set_gtk_property!(m["cbTranslucentBlending"], :active, get(params,:translucentBlending, false)) - @idle_add_guarded set_gtk_property!(m["cbSpatialBGMIP"], :active, get(params,:spatialMIPBG, false)) - - @idle_add_guarded set_gtk_property!(m["adjTTPThresh"], :value, get(params,:TTPThresh, 0.4)) - @idle_add_guarded set_gtk_property!(m["cbFrameProj"], :active, get(params,:frameProj, 0)) - - @idle_add_guarded set_gtk_property!(m["cbBlendChannels"], :active, get(params,:blendChannels, false)) - @idle_add_guarded set_gtk_property!(m["cbShowAxes"], :active, get(params,:showAxes, false)) - - @idle_add_guarded set_gtk_property!(m["cbProfile"], :active, get(params,:profile, 0)) - - showData(m) -end - -function defaultVisuParams() - params = Dict{Symbol,Any}() - return params -end diff --git a/src/Viewer/DataViewer/Drawing.jl b/src/Viewer/DataViewer/Drawing.jl deleted file mode 100644 index 9d70ff0..0000000 --- a/src/Viewer/DataViewer/Drawing.jl +++ /dev/null @@ -1,339 +0,0 @@ - - -function getMetaDataSlices(m::DataViewerWidget) - ctxZY = Gtk4.getgc(m.grid3D[2,1]) - ctxZX = Gtk4.getgc(m.grid3D[1,1]) - ctxXY = Gtk4.getgc(m.grid3D[2,2]) - hZY = height(ctxZY) - wZY = width(ctxZY) - hZX = height(ctxZX) - wZX = width(ctxZX) - hXY = height(ctxXY) - wXY = width(ctxXY) - return ctxZY,ctxZX,ctxXY,hZY,wZY,hZX,wZX,hXY,wXY -end - -function getImSizes(cdata_zy,cdata_zx,cdata_xy) - imSizeZY = [size(cdata_zy)...]#flipdim([size(cdata_zy)...],1) - imSizeZX = [size(cdata_zx)...]#flipdim([size(cdata_zx)...],1) - imSizeXY = [size(cdata_xy)...]#flipdim([size(cdata_xy)...],1) - return imSizeZY,imSizeZX,imSizeXY -end - - -function cacheSelectedFovXYZPos(m::DataViewerWidget, cachePos::Array{Float64,1}, pixelSpacingBG, sizeBG, hX,wY,hZ) - m.cacheSelectedFovXYZ = cachePos - screenXYZtoBackgroundXYZ = (cachePos .-[hX/2,wY/2,hZ/2]) .* (sizeBG ./ [hX,wY,hZ]) - m.cacheSelectedMovePos = screenXYZtoBackgroundXYZ .* pixelSpacingBG - @debug "" cachePos screenXYZtoBackgroundXYZ m.cacheSelectedMovePos -end - - -function drawRectangle(ctx,h,w, p, imSize, xy, xyOffset;rgb=[0,1,0], lineWidth=3.0) - sFac = calcMeta(h,w, imSize) - set_source_rgb(ctx, rgb...) - createRectangle(ctx, p, sFac, xy, xyOffset) - set_line_width(ctx, lineWidth) - Cairo.stroke(ctx) -end - -function calcMeta(h,w,imSize) - cDA = [w/2,h/2] - cIA = [imSize[2]/2,imSize[1]/2] - sFac = cDA ./ cIA - @debug "" cIA sFac - return sFac -end - -function createRectangle(ctx, cDA, sFac, xy, xyOffset) - lowCX = cDA[1] - sFac[1] * xy[1]/2 + sFac[1] * xyOffset[1] - lowCY= cDA[2] - sFac[2] * xy[2]/2 + sFac[2] * xyOffset[2] - highCX =lowCX + sFac[1]*xy[1] - highCY =lowCY + sFac[2]*xy[2] - @debug "" lowCX lowCY highCX highCY - move_to(ctx, lowCX, lowCY) - line_to(ctx, highCX, lowCY) - move_to(ctx, lowCX, lowCY) - line_to(ctx, lowCX, highCY) - move_to(ctx, highCX, lowCY) - line_to(ctx, highCX, highCY) - move_to(ctx, lowCX, highCY) - line_to(ctx, highCX, highCY) -end - -function getSliceSizes(fov_mm, pixelSpacing) - fov_vox = fov_mm ./ pixelSpacing - xy = [fov_vox[2],fov_vox[1]] - xz = [fov_vox[1],fov_vox[3]] - yz = [fov_vox[2],fov_vox[3]] - return xy,xz,yz -end - - -function calcDFFovRectangle(m::DataViewerWidget, trans::Vector, pixelSpacingBG::Vector) - dfFov = getDFFov(m.currentlyShownData) - xy,xz,yz = getSliceSizes(dfFov, pixelSpacingBG) - @debug "" dfFov xy xz yz - offsetxy = ([trans[2], trans[1]])./([pixelSpacingBG[2],pixelSpacingBG[1]]) - offsetxz = ([-trans[1], -trans[3]])./([pixelSpacingBG[1],pixelSpacingBG[3]]) - offsetyz = ([trans[2], -trans[3]])./([pixelSpacingBG[2],pixelSpacingBG[3]]) - return xy,xz,yz,offsetxy,offsetxz,offsetyz -end - -function getDFFov(im::ImageMeta) - props = properties(im) - if haskey(props, "dfStrength") && haskey(props, "acqGradient") - dfS = squeeze(props["dfStrength"]) - acqGrad = squeeze(props["acqGradient"]) - acqGrad_ = zeros(size(acqGrad,2),size(acqGrad,3)) - for k=1:size(acqGrad,3) - acqGrad_[:,k]=diag(acqGrad[:,:,k]) - end - dfFov = abs.(2*(dfS./acqGrad_)) - else - dfFov = [0.05,0.05,0.025] # use better default... - #warn("using default dfFov: ",dfFov) - end - return dfFov -end - -function drawImages(m::DataViewerWidget,slices,isDrawSectionalLines,isDrawRectangle,isDrawAxes, - cdata_zx, cdata_zy, cdata_xy, trans, pixelSpacingBG, sizeBG) - - xy,zx,zy,offsetxy,offsetzx,offsetzy = calcDFFovRectangle(m, trans, pixelSpacingBG) - - drawSlice(m,slices,isDrawSectionalLines,isDrawRectangle,isDrawAxes, cdata_zx, cdata_zy, cdata_xy, xy,zx,zy,offsetxy,offsetzx,offsetzy) - - - g1 = GtkGestureClick(m.grid3D[1,1],3) - signal_connect(g1, "pressed") do controller, n_press, x, y - w = widget(controller) - @guarded Gtk4.draw(w) do widget - if isDrawRectangle - @debug "mouse event ZX" - ctxZY,ctxZX,ctxXY,hZY,wZY,hZX,wZX,hXY,wXY = getMetaDataSlices(m) - reveal(widget) - pZX = [x, y] - ZXtoXYforX = (wZX-pZX[1])/wZX *hXY # X coord in ZX width direction and in XY in height direction - cacheSelectedFovXYZPos(m, [ZXtoXYforX,m.cacheSelectedFovXYZ[2], pZX[2]], pixelSpacingBG, sizeBG, hXY,wZY,hZY) - @debug "cacheSelectedFovXYZ" m.cacheSelectedFovXYZ - pZY = [m.cacheSelectedFovXYZ[2], pZX[2]] - pXY = [m.cacheSelectedFovXYZ[2], ZXtoXYforX] - imSizeZY,imSizeZX,imSizeXY= getImSizes(cdata_zy,cdata_zx,cdata_xy) - @debug "" pZX zx offsetzy imSizeZY - drawSlice(m,slices,isDrawSectionalLines,isDrawRectangle,isDrawAxes, cdata_zx, cdata_zy, cdata_xy, xy,zx,zy,offsetxy,offsetzx,offsetzy) - drawRectangle(ctxZY, hZY,wZY, pZY, imSizeZY, zy, offsetzy, rgb=[1,0,0],lineWidth=3.0) - drawRectangle(ctxZX, hZX,wZX, pZX, imSizeZX, zx, offsetzx, rgb=[1,0,0],lineWidth=3.0) - drawRectangle(ctxXY, hXY,wXY, pXY, imSizeXY, xy, offsetxy, rgb=[1,0,0],lineWidth=3.0) - end - end - end - g2 = GtkGestureClick(m.grid3D[2,1],3) - signal_connect(g2, "pressed") do controller, n_press, x, y - w = widget(controller) - @guarded Gtk4.draw(w) do widget - if isDrawRectangle - @debug "mouse event ZY" - ctxZY,ctxZX,ctxXY,hZY,wZY,hZX,wZX,hXY,wXY = getMetaDataSlices(m) - reveal(widget) - pZY = [x, y] - cacheSelectedFovXYZPos(m, [m.cacheSelectedFovXYZ[1],pZY[1], pZY[2]], pixelSpacingBG, sizeBG, hXY,wZY,hZY) - @debug "" m.cacheSelectedFovXYZ - XYtoZXforX = wZX-(m.cacheSelectedFovXYZ[1]/hXY *wZX) # X coord in ZX width direction and in XY in height direction - pZX = [XYtoZXforX, pZY[2]] - pXY = [pZY[1],m.cacheSelectedFovXYZ[1]] - imSizeZY,imSizeZX,imSizeXY= getImSizes(cdata_zy,cdata_zx,cdata_xy) - @debug "" pZY zy offsetzy imSizeZY - drawSlice(m,slices,isDrawSectionalLines,isDrawRectangle,isDrawAxes, cdata_zx, cdata_zy, cdata_xy, xy,zx,zy,offsetxy,offsetzx,offsetzy) - drawRectangle(ctxZY, hZY,wZY, pZY, imSizeZY, zy, offsetzy, rgb=[1,0,0],lineWidth=3.0) - drawRectangle(ctxZX, hZX,wZX, pZX, imSizeZX, zx, offsetzx, rgb=[1,0,0],lineWidth=3.0) - drawRectangle(ctxXY, hXY,wXY, pXY, imSizeXY, xy, offsetxy, rgb=[1,0,0],lineWidth=3.0) - end - end - end - g3 = GtkGestureClick(m.grid3D[2,2],3) - signal_connect(g3, "pressed") do controller, n_press, x, y - w = widget(controller) - @guarded Gtk4.draw(w) do widget - if isDrawRectangle - @debug "mouse event XY" - ctxZY,ctxZX,ctxXY,hZY,wZY,hZX,wZX,hXY,wXY = getMetaDataSlices(m) - reveal(widget) - pXY = [x, y] - XYtoZXforX = wZX-(pXY[2]/hXY *wZX) # X coord in ZX width direction and in XY in height direction - cacheSelectedFovXYZPos(m, [pXY[2],pXY[1],m.cacheSelectedFovXYZ[3]], pixelSpacingBG, sizeBG, hXY,wZY,hZY) - @debug "" m.cacheSelectedFovXYZ - pZY = [pXY[1],m.cacheSelectedFovXYZ[3]] - pZX = [XYtoZXforX, m.cacheSelectedFovXYZ[3]] - imSizeZY,imSizeZX,imSizeXY= getImSizes(cdata_zy,cdata_zx,cdata_xy) - @debug pXY xy offsetzy imSizeZY - drawSlice(m,slices,isDrawSectionalLines,isDrawRectangle,isDrawAxes, cdata_zx, cdata_zy, cdata_xy, xy,zx,zy,offsetxy,offsetzx,offsetzy) - drawRectangle(ctxZY, hZY,wZY, pZY, imSizeZY, zy, offsetzy, rgb=[1,0,0],lineWidth=3.0) - drawRectangle(ctxZX, hZX,wZX, pZX, imSizeZX, zx, offsetzx, rgb=[1,0,0],lineWidth=3.0) - drawRectangle(ctxXY, hXY,wXY, pXY, imSizeXY, xy, offsetxy, rgb=[1,0,0],lineWidth=3.0) - end - end - end - - return nothing -end - -function drawSlice(m::DataViewerWidget,slices,isDrawSectionalLines,isDrawRectangle,isDrawAxes, cdata_zx, cdata_zy, cdata_xy, xy,zx,zy,offsetxy,offsetzx,offsetzy) - drawImageCairo(m.grid3D[2,1], cdata_zy, isDrawSectionalLines, isDrawAxes, - slices[2], slices[3], false, true, m["adjSliceY"], m["adjSliceZ"], isDrawRectangle,zy, offsetzy, "yz") - drawImageCairo(m.grid3D[1,1], cdata_zx, isDrawSectionalLines, isDrawAxes, - slices[1], slices[3], true, true, m["adjSliceX"], m["adjSliceZ"], isDrawRectangle,zx, offsetzx, "xz") - drawImageCairo(m.grid3D[2,2], cdata_xy, isDrawSectionalLines, isDrawAxes, - slices[2], slices[1], false, false, m["adjSliceY"], m["adjSliceX"], isDrawRectangle,xy, offsetxy, "xy") -end - -function drawImageCairo(c, image, isDrawSectionalLines, isDrawAxes, xsec, ysec, - flipX, flipY, adjX, adjY, isDrawRectangle, xy, xyOffset, slide) - @guarded Gtk4.draw(c) do widget - #c = reshape(c,size(c,1), size(c,2)) - ctx = getgc(c) - h = Gtk4.height(ctx) - w = Gtk4.width(ctx) - - im = copy(reverse(arraydata(convert(ImageMeta{RGB{N0f8}},image)),dims=1)) - xsec_ = !flipX ? xsec : (size(im,2)-xsec+1) - ysec_ = !flipY ? ysec : (size(im,1)-ysec+1) - xx = w*(xsec_-0.5)/size(im,2) - yy = h*(ysec_-0.5)/size(im,1) - copy!(ctx,im) - - if isDrawSectionalLines - set_source_rgb(ctx, 0, 1, 0) - move_to(ctx, xx, 0) - line_to(ctx, xx, h) - move_to(ctx, 0, yy) - line_to(ctx, w, yy) - #set_line_width(ctx, 3.0) - # Cairo.stroke(ctx) - end - imSize = size(im) - if isDrawRectangle - @debug "" imSize - drawRectangle(ctx,h,w,[w/2,h/2], imSize, xy, xyOffset) - end - if isDrawSectionalLines || isDrawRectangle - set_line_width(ctx, 3.0) - Cairo.stroke(ctx) - end - if isDrawAxes - drawAxes(ctx,slide) - set_line_width(ctx, 3.0) - Cairo.stroke(ctx) - end - end - - g = GtkGestureClick(c,1) - signal_connect(g, "pressed") do controller, n_press, x, y - w = widget(controller) - ctx = getgc(w) - reveal(w) - h = Gtk4.height(ctx) - w = Gtk4.width(ctx) - xx = x / w*size(image,2) + 0.5 - yy = y / h*size(image,1) + 0.5 - xx = !flipX ? xx : (size(image,2)-xx+1) - yy = !flipY ? yy : (size(image,1)-yy+1) - @idle_add_guarded set_gtk_property!(adjX, :value, round(Int64,xx)) - @idle_add_guarded set_gtk_property!(adjY, :value, round(Int64,yy)) - end - -end - -## Draw coordinate system -function drawAxes(ctx,slide; rgb=[1,1,1]) - h = Gtk4.height(ctx) - w = Gtk4.width(ctx) - - center = h/40 - if slide == "xy" - posx = center - posy = center - tox = [h/4, posy] - toy = [posx, h/4] - si = [-1,-1] - la = ["y","x"] - elseif slide == "xz" - posx = w-center - posy = h-center - tox = [posx-h/4, posy] - toy = [posx, posy-h/4] - si = [1, 1] - la = ["x","z"] - else # slide == "yz" - posx = center - posy = h-center - tox = [h/4, posy] - toy = [posx, posy-h/4] - si = [-1, 1] - la = ["y","z"] - end - - set_source_rgb(ctx, rgb...) - select_font_face(ctx, "Sans", Cairo.FONT_SLANT_NORMAL, - Cairo.FONT_WEIGHT_BOLD); - set_font_size(ctx, h/20); - scale = h/64 - # x-axis - move_to(ctx, posx, posy) - line_to(ctx, tox[1], tox[2]) - rel_move_to(ctx, si[1]*(2*scale), -scale) - line_to(ctx, tox[1], tox[2]) - rel_line_to(ctx, si[1]*(2*scale), scale) - # xlabel - extents = text_extents(ctx, la[1]) - move_to(ctx, tox[1] - (extents[3]/2 + extents[1]) - si[1]*(2*scale), tox[2]-(extents[4]/2 + extents[2])) - show_text(ctx,la[1]) - # y-axis - move_to(ctx, posx, posy) - line_to(ctx, toy[1], toy[2]) - rel_move_to(ctx, -scale, si[2]*(2*scale)) - line_to(ctx, toy[1], toy[2]) - rel_line_to(ctx, scale, si[2]*(2*scale)) - # ylabel - extents = text_extents(ctx, la[2]) - move_to(ctx, toy[1] - (extents[3]/2 + extents[1]), toy[2]-(extents[4]/2 + extents[2]) - si[2]*(2*scale)) - show_text(ctx,la[2]) -end - - -### Profile Plotting ### - - -function showProfile(m::DataViewerWidget, params, slicesInRawData) - chan = params[:activeChannel] - prof = get_gtk_property(m["cbProfile"],:active, Int64) + 1 - if prof == 1 - m.currentProfile = vec(m.data[chan,:,slicesInRawData[2],slicesInRawData[3],params[:frame]]) - showProfile(m, m.currentProfile, "x", "c") - elseif prof == 2 - m.currentProfile = vec(m.data[chan,slicesInRawData[1],:,slicesInRawData[3],params[:frame]]) - showProfile(m, m.currentProfile, "y", "c") - elseif prof == 3 - m.currentProfile = vec(m.data[chan,slicesInRawData[1],slicesInRawData[2],:,params[:frame]]) - showProfile(m, m.currentProfile, "z", "c") - else - m.currentProfile = vec(m.data[chan,slicesInRawData[1],slicesInRawData[2],slicesInRawData[3],:]) - showProfile(m, m.currentProfile, "t", "c") - end -end - -function showProfile(m::DataViewerWidget, data, xLabel::String, yLabel::String) - f, ax, l = CairoMakie.lines(1:length(data), data, - figure = (; size = (1000, 800), figure_padding=4, fontsize = 12), - axis = (; title = "Profile"), - color = CairoMakie.RGBf(colors[1]...)) - - CairoMakie.autolimits!(ax) - if length(data) > 1 - CairoMakie.xlims!(ax, 1, length(data)) - end - ax.xlabel = xLabel - ax.ylabel = yLabel - drawonto(m.grid3D[1,2], f) -end - diff --git a/src/Viewer/DataViewer/Export.jl b/src/Viewer/DataViewer/Export.jl deleted file mode 100644 index 55a8418..0000000 --- a/src/Viewer/DataViewer/Export.jl +++ /dev/null @@ -1,211 +0,0 @@ - -function initExportCallbacks(m::DataViewerWidget) - signal_connect(m["btnExportImages"], "clicked") do widget - try - exportImages(m) - catch e - showError(e) - end - end - - signal_connect(m["btnExportTikz"], "clicked") do widget - try - exportTikz(m) - catch e - showError(e) - end - end - - signal_connect(m["btnExportMovi"], "clicked") do widget - try - exportMovi(m) - catch e - @error e - # showError(e) - end - end - - signal_connect(m["btnExportAllData"], "clicked") do widget - try - exportAllData(m) - catch e - showError(e) - end - end - - signal_connect(m["btnExportRealDataAllFr"], "clicked") do widget - try - exportRealDataAllFr(m) - catch e - showError(e) - end - end - - signal_connect(m["btnExportData"], "clicked") do widget - try - exportData(m) - catch e - showError(e) - end - end - - signal_connect(m["btnExportProfile"], "clicked") do widget - exportProfile(m) - end -end - -function exportImages(m::DataViewerWidget) - if m.currentlyShownImages != nothing - filter = Gtk4.GtkFileFilter(pattern=String("*.png"), mimetype=String("image/png")) - diag = save_dialog("Select Export File", mpilab[]["mainWindow"], (filter, )) do filenameImageData - if filenameImageData != "" - pixelResizeFactor = get_gtk_property(m["adjPixelResizeFactor"],:value,Int64) - @info "Export Image as" filenameImageData - exportImage(filenameImageData, m.currentlyShownImages, pixelResizeFactor=pixelResizeFactor) - end - end - diag.modal = true - end -end - -function exportTikz(m::DataViewerWidget) - if m.currentlyShownImages != nothing - filter = Gtk4.GtkFileFilter(pattern=String("*.tikz*"), mimetype=String("image/tikz")) - diag = save_dialog("Select Export File", mpilab[]["mainWindow"], (filter, )) do filenameImageData - if filenameImageData != "" - pixelResizeFactor = get_gtk_property(m["adjPixelResizeFactor"],:value,Int64) - @info "Export Tikz as" filenameImageData - props = m.currentlyShownData[1].properties - SFPath=props["recoParams"][:SFPath] - bSF = MPIFile(SFPath) - exportTikz(filenameImageData, m.currentlyShownImages, collect(size(m.dataBG)), - collect(converttometer(pixelspacing(m.dataBG))),fov(bSF),getParams(m); pixelResizeFactor=pixelResizeFactor) - end - end - diag.modal = true - end -end - -function exportMovi(m::DataViewerWidget) - filter = Gtk4.GtkFileFilter(pattern=String("*.gif"), mimetype=String("image/gif")) - diag = save_dialog("Select Export File", mpilab[]["mainWindow"], (filter, )) do filenameMovie - if filenameMovie != "" - params = getParams(m) - sliceMovies = getColoredSlicesMovie(m.data, m.dataBG, m.coloring, params) - pixelResizeFactor = get_gtk_property(m["adjPixelResizeFactor"],:value, Int64) - @info "Export Movie as" filenameMovie - exportMovies(filenameMovie, sliceMovies, pixelResizeFactor=pixelResizeFactor) - end - end - diag.modal = true -end - -function exportAllData(m::DataViewerWidget) - if m.data != nothing - filter = Gtk4.GtkFileFilter(pattern=String("*.nii"), mimetype=String("application/x-nifti")) - diag = save_dialog("Select Export File", mpilab[]["mainWindow"], (filter, )) do filenameData - if filenameData != "" - params = getParams(m) - - maxval = [maximum(d) for d in m.data] - minval = [minimum(d) for d in m.data] - - if m.dataBG != nothing - data_ = interpolateToRefImage(m.dataBG, m.data, params) - dataBG = interpolateToRefImage(m.dataBG, params) - - data__ = [data(d) for d in data_] - - cdataFG = colorize(data__, m.coloring, minval, maxval, params) - - minval,maxval = extrema(dataBG) - cdataBG = colorize(dataBG,params[:coloringBG],minval,maxval) - - blendF = get(params, :translucentBlending, false) ? blend : dogyDoge - cdata = blendF(cdataBG, cdataFG) - else - data_ = [data(d) for d in m.data] - cdata = colorize(data_, m.coloring, minval, maxval, params) - end - - prop = properties(m.data[1]) - cdata_ = similar(cdata, RGB{N0f8}) - cdata_[:] = convert(ImageMeta{RGB},cdata)[:] #TK: ugly hack - - file, ext = splitext(filenameData) - savedata_analyze(string(file,".nii"), ImageMeta(cdata_,prop), permRGBData=true) - end - end - diag.modal = true - end -end - -function exportRealDataAllFr(m::DataViewerWidget) - if m.data != nothing && m.dataBG != nothing - filter = Gtk4.GtkFileFilter(pattern=String("*.nii"), mimetype=String("application/x-nifti")) - diag = save_dialog("Select Export File", mpilab[]["mainWindow"], (filter, )) do filenameData - if filenameData != "" - params = getParams(m) - - data = interpolateToRefImageAllFr(m.dataBG, m.data, params) - dataBG = interpolateToRefImage(m.dataBG, params) - - #dataBG_ = applyPermutionsRev(m, dataBG) - - file, ext = splitext(filenameData) - savedata_analyze(string(file,".nii"), data) - savedata_analyze(string(file,"_BG.nii"), dataBG) - end - end - diag.modal = true - end -end - -function exportData(m::DataViewerWidget) - if m.currentlyShownData != nothing - filter = Gtk4.GtkFileFilter(pattern=String("*.nii"), mimetype=String("application/x-nifti")) - diag = save_dialog("Select Export File", mpilab[]["mainWindow"], (filter, )) do filenameData - if filenameData != "" - - params = getParams(m) - - maxval = [maximum(d) for d in m.data] - minval = [minimum(d) for d in m.data] - - data_ = [data(d) for d in m.currentlyShownData] - - cdata = colorize(data_,m.coloring,minval,maxval,params) - - if m.dataBG != nothing - minval,maxval = extrema(m.dataBG) - cdataBG = colorize(m.dataBG,params[:coloringBG],minval,maxval) - - blendF = get(params, :translucentBlending, false) ? blend : dogyDoge - cdata = blendF(cdataBG, cdata) - end - - prop = properties(m.currentlyShownData[1]) - cdata_ = similar(cdata, RGB{N0f8}) - cdata_[:] = convert(ImageMeta{RGB},cdata)[:] #TK: ugly hack - - file, ext = splitext(filenameData) - savedata_analyze(string(file,".nii"), ImageMeta(cdata_,prop), permRGBData=true) - end - end - diag.modal = true - end -end - - -function exportProfile(m::DataViewerWidget) - if m.currentlyShownData != nothing - filter = Gtk4.GtkFileFilter(pattern=String("*.csv"), mimetype=String("text/comma-separated-values")) - diag = save_dialog("Select Export File", mpilab[]["mainWindow"], (filter, )) do filenameImageData - if filenameImageData != "" && m.currentProfile != nothing - @info "Export Image as" filenameImageData - writedlm(filenameImageData, m.currentProfile ) - end - end - diag.modal = true - end -end diff --git a/src/Viewer/MagneticFieldViewer/Export.jl b/src/Viewer/MagneticFieldViewer/Export.jl deleted file mode 100644 index 1d96713..0000000 --- a/src/Viewer/MagneticFieldViewer/Export.jl +++ /dev/null @@ -1,238 +0,0 @@ -## Export magnetic fields - -# link buttons to corresponding functions -function initExportCallbacks(m::MagneticFieldViewerWidget) - # export shown plots as png - @guarded signal_connect(m["btnExportImages"], "clicked") do widget - exportCanvas(m) - end - # CSV: Export Coeffs - @guarded signal_connect(m["btnExportCoeffsCSV"], "clicked") do widget - saveCoeffsAsCSV(m) - end - # CSV: Export Field - @guarded signal_connect(m["btnExportFieldCSV"], "clicked") do widget - saveFieldAsCSV(m) - end - # Tikz - @guarded signal_connect(m["btnExportTikz"], "clicked") do widget - saveTikz(m) - end -end - -# Export plots as pngs -function exportCanvas(m::MagneticFieldViewerWidget) - # choose destination - filter = Gtk4.GtkFileFilter(pattern=String("*.png"), mimetype=String("image/png")) - diag = save_dialog("Select Export File", nothing, (filter, )) do filename - if filename != "" - @info "Export Image as" filename - - # get only filename - file, ext = splitext(filename) - # Field plots - if get_gtk_property(m["cbFieldExport"],:active,Bool) - write_to_png(getgc(m.fv.grid[1,1]).surface,file*"_xz.png") - write_to_png(getgc(m.fv.grid[2,1]).surface,file*"_yz.png") - write_to_png(getgc(m.fv.grid[2,2]).surface,file*"_xy.png") - end - # Coefficients - if get_gtk_property(m["cbCoeffsExport"],:active,Bool) - write_to_png(getgc(m.grid[1,3]).surface,file*"_coeffs.png") - end - # Profile - if get_gtk_property(m["cbProfileExport"],:active,Bool) - write_to_png(getgc(m.fv.grid[1,2]).surface,file*"_profile.png") - end - - return filename - end - end - diag.modal = true -end - -################# -# Export as csv # -################# -# export coefficients - -function saveCoeffsAsCSV(m::MagneticFieldViewerWidget) - # choose destination - filter = Gtk4.GtkFileFilter(pattern=String("*.csv"), mimetype=String("image/csv")) - diag = save_dialog("Select Export File", nothing, (filter, )) do filename - if filename != "" - @info "Export coefficients as" filename - - saveCoeffsAsCSV(m, filename) - - return filename - end - end - diag.modal = true -end - -function saveCoeffsAsCSV(m::MagneticFieldViewerWidget, filename) - # get only filename - file, ext = splitext(filename) - - # get some values - p = get_gtk_property(m["adjPatches"],:value, Int64) # patch - L = get_gtk_property(m["adjL"],:value, Int64) # L - L² = (L+1)^2 # number of coefficients to be saved - R = m.coeffs.radius # radius for normalization - - # Create Array with the data - coeffsSave = ["num" "x" "y" "z"] - coeffsSave = vcat(coeffsSave,zeros(L²,4)) # length(coeffs[1,1].c) = (L+1)^2 - coeffsSave[2:end,1] = 1:L² - for j=1:3 - coeffsSave[2:end,j+1] = normalize(m.coeffsPlot[j,p],1/R).c[1:L²] - end - - # save - writedlm(file*".csv",coeffsSave,';'); -end - -# export magnetic field -function saveFieldAsCSV(m::MagneticFieldViewerWidget) - # choose destination - filter = Gtk4.GtkFileFilter(pattern=String("*.csv"), mimetype=String("image/csv")) - diag = save_dialog("Select Export File", nothing, (filter, )) do filename - if filename != "" - @info "Export field as" filename - - saveFieldAsCSV(m, filename) - - return filename - end - end - diag.modal = true -end - -function saveFieldAsCSV(m::MagneticFieldViewerWidget, filename) - # get only filename - file, ext = splitext(filename) - - # load all parameters - discretization = Int(get_gtk_property(m["adjDiscretization"],:value, Int64)*2+1) # odd number of voxel - R = m.coeffs.radius # radius of measurement data - - # get FOV - fovString = get_gtk_property(m["entFOV"], :text, String) # FOV - fov = tryparse.(Float64,split(fovString,"x")) ./ 1000 - - # Grid - N = [range(-fov[i]/2,stop=fov[i]/2,length=discretization) for i=1:3]; - - # calculate field for plot - fieldNorm = zeros(discretization,discretization,3); - fieldxyz = zeros(3,discretization,discretization,3); - setPatch!(m.field,m.patch) # set selected patch - for i = 1:discretization - for j = 1:discretization - fieldxyz[:,i,j,1] = m.field[m.fv.intersection[1],N[2][i],N[3][j]] - fieldNorm[i,j,1] = norm(fieldxyz[:,i,j,1]) - fieldxyz[:,i,j,2] = m.field[N[1][i],m.fv.intersection[2],N[3][j]] - fieldNorm[i,j,2] = norm(fieldxyz[:,i,j,2]) - fieldxyz[:,i,j,3] = m.field[N[1][i],N[2][j],m.fv.intersection[3]] - fieldNorm[i,j,3] = norm(fieldxyz[:,i,j,3]) - end - end - - # collect all coordinates for export to tikz - Nyz = hcat([[N[2][i],N[3][j]] for i=1:discretization for j=1:discretization]...) - Nxz = hcat([[N[1][i],N[3][j]] for i=1:discretization for j=1:discretization]...) - Nxy = hcat([[N[1][i],N[2][j]] for i=1:discretization for j=1:discretization]...) - - # create DataFrame - df = DataFrame("PlaneYZ_y"=> Nyz[1,:], - "PlaneYZ_z" => Nyz[2,:], - "PlaneYZ_f" => vec(permutedims(fieldNorm[:,:,1],[2,1])), - "PlaneXZ_x" => Nxz[1,:], - "PlaneXZ_z" => Nxz[2,:], - "PlaneXZ_f" => vec(permutedims(fieldNorm[:,:,2],[2,1])), - "PlaneXY_x" => Nxy[1,:], - "PlaneXY_y" => Nxy[2,:], - "PlaneXY_f" => vec(permutedims(fieldNorm[:,:,3],[2,1])) - ) - - # save as csv - CSV.write(file*".csv",df); - - ############ - ## Quiver ## - ############ - discr = floor(Int,0.1*discretization) # reduce number of arrows - fieldxyz = [fieldxyz[d,1:discr:end,1:discr:end,:] for d=1:3] - - # collect all coordinates for export to tikz - Nyz = hcat([[N[2][i],N[3][j]] for i=1:discr:discretization for j=1:discr:discretization]...) - Nxz = hcat([[N[1][i],N[3][j]] for i=1:discr:discretization for j=1:discr:discretization]...) - Nxy = hcat([[N[1][i],N[2][j]] for i=1:discr:discretization for j=1:discr:discretization]...) - - # create DataFrame - df = DataFrame("PlaneYZ_y"=> Nyz[1,:], - "PlaneYZ_z" => Nyz[2,:], - "quiver_yzu" => vec(permutedims(fieldxyz[2][:,:,1],[2,1])), # "PlaneYZ_u" - "quiver_yzv" => vec(permutedims(fieldxyz[3][:,:,1],[2,1])), # "PlaneYZ_v" - "PlaneXZ_x" => Nxz[1,:], - "PlaneXZ_z" => Nxz[2,:], - "PlaneXZ_u" => vec(permutedims(fieldxyz[1][:,:,2],[2,1])), - "PlaneXZ_v" => vec(permutedims(fieldxyz[3][:,:,2],[2,1])), - "PlaneXY_x" => Nxy[1,:], - "PlaneXY_y" => Nxy[2,:], - "Xyu" => vec(permutedims(fieldxyz[1][:,:,3]',[2,1])), # "PlaneXY_u" - "Xyv" => vec(permutedims(fieldxyz[2][:,:,3]',[2,1])), # "PlaneXY_v" - ) - - # save as csv - CSV.write(file*"_quiver"*".csv",df); -end - -############### -# Export tikz # -############### -# export plots as tex-file -function saveTikz(m::MagneticFieldViewerWidget) - # choose destination - filter = Gtk4.GtkFileFilter(pattern=String("*.tex"), mimetype=String("image/tex")) - diag = save_dialog("Select Export File", nothing, (filter, )) do filename - if filename != "" - @info "Export tikz as" filename - - saveTikz(m, filename) - - return filename - end - end - diag.modal = true -end - -function saveTikz(m::MagneticFieldViewerWidget, filename) - # get only filename - file, ext = splitext(filename) - - # load parameter - cmin = m.fv.coloring.cmin - cmax = m.fv.coloring.cmax - discretization = Int(get_gtk_property(m["adjDiscretization"],:value, Int64)*2+1) # odd number of voxel - arrowLength = get_gtk_property(m["adjArrowLength"],:value, Int64) - R = m.coeffs.radius - L = m.coeffs.coeffs[1].L - # get tex code - tex = exportTikz(file,cmin,cmax,discretization,arrowLength,R,L) - - # export data - saveCoeffsAsCSV(m, file*"_coeffs") - saveFieldAsCSV(m, file*"_field") - - # save tex-file - write(file*".tex",tex) - - # compile tex-file - if get_gtk_property(m["cbBuildPdf"], :active, Bool) - # get output folder - path, = splitdir(file) - run(`pdflatex -output-directory=$(path) $(file).tex`) - end -end \ No newline at end of file diff --git a/src/Viewer/MagneticFieldViewer/MagneticFieldViewer.jl b/src/Viewer/MagneticFieldViewer/MagneticFieldViewer.jl deleted file mode 100644 index 8d3ba49..0000000 --- a/src/Viewer/MagneticFieldViewer/MagneticFieldViewer.jl +++ /dev/null @@ -1,885 +0,0 @@ -export MagneticFieldViewer - -mutable struct FieldViewerWidget <: Gtk4.GtkBox - handle::Ptr{Gtk4.GObject} - builder::GtkBuilder - coloring::ColoringParams - updating::Bool - fieldNorm::Array{Float64,3} # data to be plotted (norm) - field::Array{Float64,4} # data to be plotted (field values) - currentProfile::Array{Float64,3} # data of profile plot - positions # Vector containing the spatial positions of the plotted field - intersection::Vector{Float64} # intersection of the three plots - centerFFP::Bool # center of plot (FFP (true) or center of measured sphere (false)) - grid::Gtk4.GtkGridLeaf -end - -mutable struct MagneticFieldViewerWidget <: Gtk4.GtkBox - handle::Ptr{Gtk4.GObject} - builder::GtkBuilder - fv::FieldViewerWidget - updating::Bool - coeffsInit::MagneticFieldCoefficients - coeffs::MagneticFieldCoefficients - coeffsPlot::Array{SphericalHarmonicCoefficients} - field # SphericalHarmonicsDefinedField (Functions of the field) - patch::Int - grid::Gtk4.GtkGridLeaf - cmapsTree::Gtk4.GtkTreeModelFilter -end - -getindex(m::MagneticFieldViewerWidget, w::AbstractString) = G_.get_object(m.builder, w) -getindex(m::FieldViewerWidget, w::AbstractString) = G_.get_object(m.builder, w) - -mutable struct MagneticFieldViewer - w::Gtk4.GtkWindowLeaf - mf::MagneticFieldViewerWidget -end - -# plotting functions -include("Plotting.jl") -# export functions -include("Export.jl") -include("TikzExport.jl") # tikz stuff - -# Viewer can be started with MagneticFieldCoefficients or with a path to a file with some coefficients -function MagneticFieldViewer(filename::Union{AbstractString,MagneticFieldCoefficients}) - mfViewerWidget = MagneticFieldViewerWidget() - w = GtkWindow("Magnetic Field Viewer: $(filename)",800,600) - push!(w,mfViewerWidget) - show(w) - updateData!(mfViewerWidget, filename) - return MagneticFieldViewer(w, mfViewerWidget) -end - -function FieldViewerWidget() - uifile = joinpath(@__DIR__,"..","..","builder","magneticFieldViewer.ui") - - b = GtkBuilder(uifile) - mainBox = G_.get_object(b, "boxFieldViewer") - - fv = FieldViewerWidget(mainBox.handle, b, ColoringParams(0,0,0), - false, zeros(0,0,0), zeros(0,0,0,0), zeros(0,0,0), zeros(0), zeros(0), true, - G_.get_object(b, "gridFieldViewer"),) - Gtk4.GLib.gobject_move_ref(fv, mainBox) - - # initialize plots - fv.grid[1,1] = GtkCanvas() - fv.grid[1,2] = GtkCanvas() - fv.grid[2,1] = GtkCanvas() - fv.grid[2,2] = GtkCanvas() - - fv.grid.hexpand = fv.grid.vexpand = true - # expand plots - ### set_gtk_property!(fv, :expand, fv.grid, true) - - return fv -end - -function MagneticFieldViewerWidget() - uifile = joinpath(@__DIR__,"..","..","builder","magneticFieldViewer.ui") - - b = GtkBuilder(uifile) - mainBox = G_.get_object(b, "boxMagneticFieldViewer") - - m = MagneticFieldViewerWidget(mainBox.handle, b, FieldViewerWidget(), - false, MagneticFieldCoefficients(0), MagneticFieldCoefficients(0), - [SphericalHarmonicCoefficients(0)], nothing, 1, - GtkGrid(), GtkTreeModelFilter(GtkListStore(Bool))) - Gtk4.GLib.gobject_move_ref(m, mainBox) - - # build up plots - m.grid = m["gridMagneticFieldViewer"] - m.grid[1,1:2] = m.fv - m.grid[1,3] = GtkCanvas() - # expand plot - ### set_gtk_property!(m, :expand, m.grid, true) - - show(m) - - ## setup colormap search - # create list - ls = GtkListStore(String, Bool) - for c in existing_cmaps() - push!(ls,(c,true)) - end - # create TreeViewColumn - rTxt = GtkCellRendererText() - c = GtkTreeViewColumn("Colormaps", rTxt, Dict([("text",0)]))#, sort_column_id=0) # column - # add column to TreeView - m.cmapsTree = GtkTreeModelFilter(ls) - G_.set_visible_column(m.cmapsTree,1) - tv = GtkTreeView(GtkTreeModel(m.cmapsTree)) - push!(tv, c) - # add to popover - push!(m["boxCMaps"],tv) - show(m["boxCMaps"]) - - # set important colormaps - choices = important_cmaps() - for c in choices - push!(m["cbCMaps"], c) - end - set_gtk_property!(m["cbCMaps"],:active,5) # default: viridis - m.fv.coloring = ColoringParams(0,1,important_cmaps()[6]) # set default colormap - - # searching for specific colormaps - signal_connect(m["entCMaps"], "changed") do w - @idle_add_guarded begin - searchText = get_gtk_property(m["entCMaps"], :text, String) - if searchText == "Martin" - searchText = "jet1" - end - for l=1:length(ls) - showMe = true - if length(searchText) > 0 - showMe = showMe && occursin(lowercase(searchText), lowercase(ls[l,1])) - end - ls[l,2] = showMe - end - end - end - - # setup volume choices for FFP search - for v in instances(MPISphericalHarmonics.Volume) - push!(m["cbVolumeFFP"], "$v") - end - set_gtk_property!(m["cbVolumeFFP"],:active,0) # default: xyz - - # Allow to change between gradient and offset output - for c in ["Offset:", "Gradient:", "Eigenvalues:", "Singular values:"] - push!(m["cbGradientOffset"], c) - end - set_gtk_property!(m["cbGradientOffset"],:active,1) # default: Gradient - - # change between different profile options - for c in ["Norm","xyz","x","y","z"] # field - push!(m["cbFrameProj"], c) - end - set_gtk_property!(m["cbFrameProj"],:active,0) # default: Norm along all axes - for c in ["all", "x", "y", "z"] # axes - push!(m["cbProfile"], c) - end - set_gtk_property!(m["cbProfile"],:active,0) # default: Norm along all axes - - # change discretization - signal_connect(m["adjDiscretization"], "value_changed") do w - @idle_add_guarded begin - # adapt min/max slice - d = get_gtk_property(m["adjDiscretization"],:value, Int64) - for w in ["adjSliceX", "adjSliceY", "adjSliceZ"] - set_gtk_property!(m[w], :value, 0) - set_gtk_property!(m[w], :lower, -d) - set_gtk_property!(m[w], :upper, d) - end - # update plot - calcField(m) # calculate new field values - updateField(m) - updateProfile(m) - end - end - - # change patch - signal_connect(m["adjPatches"], "value_changed") do w - @idle_add_guarded begin - m.patch = get_gtk_property(m["adjPatches"],:value, Int64) # update patch - updateInfos(m) # update patch-dependent infos (like FFP) - stayInFFP(m) - end - end - - # change length of arrows - signal_connect(m["adjArrowLength"], "value_changed") do w - @idle_add_guarded updateField(m) - end - - # change fontsize - signal_connect(m["adjFontsize"], "value_changed") do w - @idle_add_guarded updateCoeffsPlot(m) - if get_gtk_property(m["cbShowAxes"], :active, Bool) # update field plots only if axes shown - @idle_add_guarded updateField(m) - end - @idle_add_guarded updateProfile(m) - end - - # change unit - signal_connect(m["cbUseMilli"], "toggled") do w - @idle_add_guarded updateCoeffsPlot(m) - if get_gtk_property(m["cbShowAxes"], :active, Bool) # update field plots only if axes shown - @idle_add_guarded updateField(m) - end - @idle_add_guarded updateProfile(m) - end - - # update coeffs plot - widgets = ["adjL"] - for ws in widgets - signal_connect(m[ws], "value_changed") do w - @idle_add_guarded updateCoeffsPlot(m) - end - end - signal_connect(m["cbShowLegend"], "toggled") do w - @idle_add_guarded updateCoeffsPlot(m) - end - signal_connect(m["cbScaleR"], "toggled") do w - @idle_add_guarded normalizeCoeffs(m) - end - - # update plot (clicked button) - signal_connect(m["btnUpdate"], "clicked") do w - @idle_add_guarded begin - updateIntersection(m) - updatePlots(m) - end - end - - # update magnetic field plot: - # center = center of sphere - signal_connect(m["btnCenterSphere"], "clicked") do w - if m.fv.centerFFP - @idle_add_guarded begin - set_gtk_property!(m["btnCenterSphere"],:sensitive,false) # disable button - set_gtk_property!(m["btnCenterFFP"],:sensitive,true) # enable button - m.fv.centerFFP = false - calcCenterCoeffs(m,true) - stayInFFP(m) - end - end - end - # center = FFP - signal_connect(m["btnCenterFFP"], "clicked") do w - if !(m.fv.centerFFP) - @idle_add_guarded begin - set_gtk_property!(m["btnCenterFFP"],:sensitive,false) # disable button - set_gtk_property!(m["btnCenterSphere"],:sensitive,true) # enable button - m.fv.centerFFP = true - calcCenterCoeffs(m,true) - updatePlots(m) - end - end - end - - # go to FFP - signal_connect(m["btnGoToFFP"], "clicked") do w - @idle_add_guarded begin - m.updating = true - goToFFP(m) - m.updating = false - end - end - # go to zero - signal_connect(m["btnGoToZero"], "clicked") do w - @idle_add_guarded begin - m.updating = true - goToFFP(m,true) - m.updating = false - end - end - - # calculate FFP - signal_connect(m["btnCalcFFP"], "clicked") do w - @idle_add_guarded calcFFP(m) # calculate FFP - @idle_add_guarded goToFFP(m) # go to FFP - end - - # reset FFP - signal_connect(m["btnResetFFP"], "clicked") do w - @idle_add_guarded resetFFP(m) - end - - # reset everything -> reload Viewer - signal_connect(m["btnReset"], "clicked") do w - @idle_add_guarded updateData!(m, m.coeffsInit) - end - - # checkbuttons changed - for cb in ["cbShowSphere", "cbShowSlices", "cbShowAxes", "cbShowCS"] - signal_connect(m[cb], "toggled") do w - @idle_add_guarded updateField(m) - end - end - signal_connect(m["cbStayFFP"], "toggled") do w - if get_gtk_property(m["cbStayFFP"], :active, Bool) # go to FFP only if active - @idle_add_guarded begin - goToFFP(m) - end - end - end - # checkbutton cmin = 0 - signal_connect(m["cbCMin"], "toggled") do w - @idle_add_guarded begin - if get_gtk_property(m["cbCMin"], :active, Bool) # cmin = 0 - set_gtk_property!(m["adjCMin"], :lower, 0) - set_gtk_property!(m["adjCMin"], :value, 0) - set_gtk_property!(m["vbCMin"], :sensitive, false) # disable changing cmin - else - set_gtk_property!(m["vbCMin"], :sensitive, true) # enable changing cmin - updateField(m) - end - # updateCol done automatically since value changed for adjCMin - end - end - # checkbutton keeping cmin/cmax - signal_connect(m["cbKeepC"], "toggled") do w - @idle_add_guarded begin - if get_gtk_property(m["cbKeepC"], :active, Bool) # keep the values - set_gtk_property!(m["vbCMin"], :sensitive, false) # disable changing cmin - set_gtk_property!(m["vbCMax"], :sensitive, false) # disable changing cmax - # disable writing cmin/cmax - set_gtk_property!(m["entCMin"], :editable, false) - set_gtk_property!(m["entCMax"], :editable, false) - else - set_gtk_property!(m["vbCMin"], :sensitive, true) # enable changing cmin - set_gtk_property!(m["vbCMax"], :sensitive, true) # enable changing cmax - if get_gtk_property(m["cbWriteC"], :active, Bool) - # enable writing cmin/cmax - set_gtk_property!(m["entCMin"], :editable, true) - set_gtk_property!(m["entCMax"], :editable, true) - end - end - end - end - # checkbutton write cmin/cmax - signal_connect(m["cbWriteC"], "toggled") do w - @idle_add_guarded begin - if get_gtk_property(m["cbWriteC"], :active, Bool) - # enable writing cmin/cmax - set_gtk_property!(m["entCMin"], :editable, true) - set_gtk_property!(m["entCMax"], :editable, true) - else - # disable writing cmin/cmax - set_gtk_property!(m["entCMin"], :editable, false) - set_gtk_property!(m["entCMax"], :editable, false) - # update field plot - updateField(m) - end - end - end - - # update measurement infos - signal_connect(m["cbGradientOffset"], "changed") do w - @idle_add_guarded updateInfos(m) - end - - # update profile plot - for cb in ["cbFrameProj", "cbProfile"] - signal_connect(m[cb], "changed") do w - @idle_add_guarded updateProfile(m) - end - end - - ## Video - # update min/max values of the patches - signal_connect(m["adjPatchesVideoLower"], "value_changed") do w - @idle_add_guarded set_gtk_property!(m["adjPatchesVideoUpper"], :lower, get_gtk_property(m["adjPatchesVideoLower"], :value, Int64)) - end - signal_connect(m["adjPatchesVideoUpper"], "value_changed") do w - @idle_add_guarded set_gtk_property!(m["adjPatchesVideoLower"], :upper, get_gtk_property(m["adjPatchesVideoUpper"], :value, Int64)) - end - # continous sweeps - playActive = false - signal_connect(m["tbPlayVideo"], :toggled) do w - if get_gtk_property(m["tbPlayVideo"], :active, Bool) - @info "Start Video" - # if video should not be repeated toggle play button - !(get_gtk_property(m["cbVideoRepeat"], :active, Bool)) && (@idle_add_guarded set_gtk_property!(m["tbPlayVideo"], :active, false)) - # get lower and upper patch - lowerPatch = get_gtk_property(m["adjPatchesVideoLower"], :value, Int64) - upperPatch = get_gtk_property(m["adjPatchesVideoUpper"], :value, Int64) - # get sleep time - sleepTime = tryparse.(Float64,MPIUI.get_gtk_property(m["entSleepVideo"],:text,String)) ./ 1000 - # timer for video repetitions - playActive = true - function update_(::Timer) - if playActive - # sweep over chosen patches - for p = lowerPatch:upperPatch - set_gtk_property!(m["adjPatches"], :value, p) - sleep(sleepTime) - end - else - close(timer) - end - end - timer = Timer(update_, 0.0, interval=1) - else - playActive = false - end - end - - initCallbacks(m) - - return m -end - - -function initCallbacks(m::MagneticFieldViewerWidget) - - ## update coloring - function updateCol(widget , importantCMaps::Bool=true) - - # update plots - @idle_add_guarded updateColoring(m,importantCMaps) - @idle_add_guarded updateField(m,true) - end - - - # choose new slice - function newSlice(widget) - if !m.updating # don't update slices if they are set by other functions - m.updating = true - - # get chosen slices - sl = [get_gtk_property(m[w],:value, Int64) for w in ["adjSliceX", "adjSliceY", "adjSliceZ"]] - - # get current FOV - fovString = get_gtk_property(m["entFOV"], :text, String) # FOV - fov = tryparse.(Float64,split(fovString,"x")) ./ 1000 - - # calculate voxel size - discretization = Int(get_gtk_property(m["adjDiscretization"],:value, Int64)*2+1) # odd number of voxel - voxel = fov ./ discretization - - # calculate new intersection - intersection = voxel .* sl - - # set intersection - interString = round.(intersection .* 1000, digits=1) - set_gtk_property!(m["entInters"], :text, - "$(interString[1]) x $(interString[2]) x $(interString[3])") - - # update coefficients with new intersection and plot everything - updateIntersection(m) - updatePlots(m) - - m.updating = false - end - end - - # cmin/cmax - widgets = ["adjCMin", "adjCMax"] - for w in widgets - signal_connect(m[w], "value_changed") do widget - @idle_add_guarded updateColLims(m) # update color range - @idle_add_guarded updateField(m,true) - end - end - - # colormap - signal_connect(updateCol, m["cbCMaps"], "changed") - signal_connect(G_.get_selection( Gtk4.next_sibling(Gtk4.first_child(m["boxCMaps"]))), "changed") do widget - updateCol( widget, false ) - end - - ## reset FOV - function resetFOV( widget ) - R = m.coeffs.radius # radius - set_gtk_property!(m["entFOV"], :text, "$(R*2000) x $(R*2000) x $(R*2000)") # initial FOV - calcField(m) # calculate new field values - updateField(m) - updateProfile(m) - end - signal_connect(resetFOV, m["btnResetFOV"], "clicked") - - # change slice - widgets = ["adjSliceX", "adjSliceY", "adjSliceZ"] - for w in widgets - signal_connect(newSlice, m[w], "value_changed") - end - - ## click on image to choose a slice - function on_pressed(controller, n_press, x, y) - if get_gtk_property(m["cbShowCS"], :active, Bool) - @info "Disable axes to change the intersection with a mouse click." - else - # position on the image: (x,y) - # calculate actual position in the fields coordinates to update intersection - # get image size - w = widget(controller) # get current widget - ctx = getgc(w) - hh = height(ctx) # height of the plot - ww = width(ctx) # width of the plot - # calculate pixel size - d = get_gtk_property(m["adjDiscretization"],:value, Int64) # number of voxel - hpix = hh / (d*2+1) # height of pixel - wpix = ww / (d*2+1) # width of pixel - # position (pixel) (counted from the upper left corner) - pixel = [floor(Int, x / wpix), floor(Int, y / hpix)] - - # update intersection (depending on the image) - @idle_add_guarded begin - if w == m.fv.grid[1,1] # XZ - set_gtk_property!(m["adjSliceX"], :value, -pixel[1]+d) - set_gtk_property!(m["adjSliceZ"], :value, -pixel[2]+d) - elseif w == m.fv.grid[2,1] # YZ - set_gtk_property!(m["adjSliceY"], :value, pixel[1]-d) - set_gtk_property!(m["adjSliceZ"], :value, -pixel[2]+d) - else # YX - set_gtk_property!(m["adjSliceY"], :value, pixel[1]-d) - set_gtk_property!(m["adjSliceX"], :value, pixel[2]-d) - end - end - end - end - - for c in [m.fv.grid[1,1], m.fv.grid[2,1], m.fv.grid[2,2]] - # add the event controller for mouse clicks to the widget - g = GtkGestureClick() - push!(c,g) - # call function - @idle_add_guarded signal_connect(on_pressed, g, "pressed") - end - - - # export images/data - initExportCallbacks(m) -end - -# load all necessary data and set up the values in the GUI -updateData!(m::MagneticFieldViewerWidget, filenameCoeffs::String) = updateData!(m, MagneticFieldCoefficients(filenameCoeffs)) - -function updateData!(m::MagneticFieldViewerWidget, coeffs::MagneticFieldCoefficients) - - m.coeffsInit = deepcopy(coeffs) # save initial coefficients for reloading - - # load magnetic fields - m.coeffs = deepcopy(coeffs) # load coefficients (do not change the given coefficients!) - m.coeffsPlot = deepcopy(m.coeffs.coeffs) # load coefficients - - m.field = SphericalHarmonicsDefinedField(m.coeffs) - m.patch = 1 - setPatch!(m.field,m.patch) # set selected patch - R = m.coeffs.radius # radius - center = m.coeffs.center[:,m.patch] # center of the measurement - m.fv.centerFFP = (m.coeffs.ffp !== nothing) ? true : false # if FFP is given, it is the plotting center - set_gtk_property!(m["btnCenterFFP"],:sensitive,false) # disable button - set_gtk_property!(m["btnCenterSphere"],:sensitive,true) # enable button - - m.updating = true - - # set some values - set_gtk_property!(m["adjPatches"], :upper, size(m.coeffs.coeffs,2) ) - set_gtk_property!(m["adjPatches"], :value, m.patch ) - set_gtk_property!(m["adjL"], :upper, m.coeffs.coeffs[1].L ) - set_gtk_property!(m["adjL"], :value, min(m.coeffs.coeffs[1].L,3) ) # use L=3 as maximum - set_gtk_property!(m["entRadius"], :text, "$(round(R*1000,digits=1))") # show radius of measurement - # center/FFP in coordinate system of coefficients - centerText = round.(center .* 1000, digits=1) - set_gtk_property!(m["entCenterMeas"], :text, "$(centerText[1]) x $(centerText[2]) x $(centerText[3])") # show center of measurement - if m.coeffs.ffp !== nothing - ffpText = round.((m.coeffs.ffp[:,m.patch]) .* 1000, digits=1) - set_gtk_property!(m["entFFP"], :text, "$(ffpText[1]) x $(ffpText[2]) x $(ffpText[3])") # show FFP of current patch - else - set_gtk_property!(m["entFFP"], :text, "no FFP") - end - set_gtk_property!(m["entFOV"], :text, "$(R*2000) x $(R*2000) x $(R*2000)") # initial FOV - set_gtk_property!(m["entInters"], :text, "0.0 x 0.0 x 0.0") # initial FOV - d = get_gtk_property(m["adjDiscretization"],:value, Int64) # get discretization as min/max for slices - for w in ["adjSliceX", "adjSliceY", "adjSliceZ"] - set_gtk_property!(m[w], :lower, -d) - set_gtk_property!(m[w], :value, 0) - set_gtk_property!(m[w], :upper, d) - end - # Video params - set_gtk_property!(m["adjPatchesVideoLower"], :upper, size(m.coeffs.coeffs,2) ) - set_gtk_property!(m["adjPatchesVideoUpper"], :upper, size(m.coeffs.coeffs,2) ) - set_gtk_property!(m["adjPatchesVideoUpper"], :value, size(m.coeffs.coeffs,2) ) - set_gtk_property!(m["entSleepVideo"], :text, "50") - - # if no FFPs are given, don't show the buttons - if isnothing(m.coeffs.ffp) - set_gtk_property!(m["btnGoToFFP"],:visible,false) # FFP as intersection not available - set_gtk_property!(m["btnCenterFFP"],:visible,false) # FFP as center not available - set_gtk_property!(m["btnCenterSphere"],:sensitive,false) # Center of sphere automatically plotting center - set_gtk_property!(m["btnCalcFFP"],:sensitive,true) # FFP can be calculated - set_gtk_property!(m["btnResetFFP"],:sensitive,false) # no FFP to be reset - else - # disable the calcFFP button - set_gtk_property!(m["btnCalcFFP"],:sensitive,false) # FFP already calculated - set_gtk_property!(m["btnResetFFP"],:sensitive,true) # FFP can be reseted - end - - # start with invisible axes for field plots - #set_gtk_property!(m["cbShowAxes"], :active, 1) # axes are always shown - - # update measurement infos - if isnothing(m.coeffs.ffp) - # default: show the offset field values if no FFP is given - set_gtk_property!(m["cbGradientOffset"],:active,1) - end - updateInfos(m) - - # plotting - updatePlots(m) - show(m) - - m.updating = false -end - -# update the coloring params -function updateColoring(m::MagneticFieldViewerWidget, importantCMaps::Bool=true) - if !m.fv.updating - m.fv.updating = true - # scale adjCMin/adjCMax with 1000 as values below 1 don't work - cmin = get_gtk_property(m["adjCMin"],:value, Float64) / 1000 - cmax = get_gtk_property(m["adjCMax"],:value, Float64) / 1000 - if importantCMaps # choice happend within the important colormaps - idx = get_gtk_property(m["cbCMaps"],:active, Int64)+1 - if idx <= 4 # first 4 colormaps not available - idx = 6 - set_gtk_property!(m["cbCMaps"], :active, 5) - end - cmap = important_cmaps()[idx] - else # choice happened within all colormaps - selection = G_.get_selection( Gtk4.next_sibling(Gtk4.first_child(m["boxCMaps"])) ) ### G_.get_selection(m["boxCMaps"][2]) - if hasselection(selection) - chosenCMap = selected(selection) - cmap = GtkTreeModel(m.cmapsTree)[chosenCMap,1] - else # use last choice of important colormaps - cmap = important_cmaps()[get_gtk_property(m["cbCMaps"],:active, Int64)+1] - end - end - m.fv.coloring = ColoringParams(cmin,cmax,cmap) - m.fv.updating = false - end -end - -# Coloring: Update cmin/cmax only -function updateColLims(m::MagneticFieldViewerWidget) - if !m.fv.updating - m.fv.updating = true - cmin = get_gtk_property(m["adjCMin"],:value, Float64) / 1000 - cmax = get_gtk_property(m["adjCMax"],:value, Float64) / 1000 - m.fv.coloring = ColoringParams(cmin,cmax,m.fv.coloring.cmap) # keep cmap - m.fv.updating = false - end -end - - -# update slices -function updateSlices(m::MagneticFieldViewerWidget) - # get current intersection - intersString = get_gtk_property(m["entInters"], :text, String) # intersection - intersection = tryparse.(Float64,split(intersString,"x")) ./ 1000 # conversion from mm to m - - # get voxel size - fovString = get_gtk_property(m["entFOV"], :text, String) # FOV - fov = tryparse.(Float64, split(fovString,"x")) ./ 1000 # conversion from mm to m - discretization = Int(get_gtk_property(m["adjDiscretization"], :value, Int64)*2+1) # odd number of voxel - voxel = fov ./ discretization - - # get slice numbers (rounded) - sl = round.(Int,intersection ./ voxel) - - # set slice - for (i,w) in enumerate(["adjSliceX", "adjSliceY", "adjSliceZ"]) - set_gtk_property!(m[w], :value, sl[i]) - end - -end - -# update intersection -function updateIntersection(m::MagneticFieldViewerWidget) - # get intersection (= new expansion point of coefficients) - intersString = get_gtk_property(m["entInters"], :text, String) # intersection - intersection = tryparse.(Float64,split(intersString,"x")) ./ 1000 # conversion from mm to m - - updateSlices(m) # update slice numbers - calcCenterCoeffs(m) # recalculate coefficients regarding to the center - updateCoeffs(m, intersection) # shift to intersection -end - -function goToFFP(m::MagneticFieldViewerWidget, goToZero=false) - if m.coeffs.ffp === nothing && !goToZero - # no FFP given - return nothing - end - - m.updating = true - - # set new intersection - intersection = (goToZero || m.fv.centerFFP) ? [0.0,0.0,0.0] : m.coeffs.ffp[:,m.patch] - interString = round.(intersection .* 1000, digits=1) - set_gtk_property!(m["entInters"], :text, - "$(interString[1]) x $(interString[2]) x $(interString[3])") - - updateSlices(m) # update slice numbers - calcCenterCoeffs(m) # recalculate coefficients regarding to the center - updateCoeffs(m, intersection) # shift coefficients into new intersection - updatePlots(m) - - m.updating = false -end - -# calculate the FFP of the given coefficients -function calcFFP(m::MagneticFieldViewerWidget) - - # calculate the FFP (don't shift the coefficients into the FFP) - vol = get_gtk_property(m["cbVolumeFFP"], :active, Int64) # volume where to search - findFFP!(m.coeffs, - start = [m.fv.intersection], # use intersection as start value - vol = MPISphericalHarmonics.Volume(vol)) - - # show FFP - ffpText = round.((m.coeffs.ffp[:,m.patch]) .* 1000, digits=1) - set_gtk_property!(m["entFFP"], :text, "$(ffpText[1]) x $(ffpText[2]) x $(ffpText[3])") # show FFP of current patch - - # show FFP regarding buttons - set_gtk_property!(m["btnGoToFFP"],:visible,true) # FFP as intersection - set_gtk_property!(m["btnCenterFFP"],:visible,true) # FFP as center - set_gtk_property!(m["btnCenterFFP"],:sensitive,true) # FFP as center - # disable calcFFP button - set_gtk_property!(m["btnCalcFFP"],:sensitive,false) # FFP already calculated - set_gtk_property!(m["btnResetFFP"],:sensitive,true) # allow to reset the FFP - set_gtk_property!(m["cbVolumeFFP"],:sensitive,false) # FFP already calculated in this volume -# set_gtk_property!(m["btnCenterSphere"],:sensitive,false) # Center of sphere automatically plotting center -end - -# reset the FFP of the given coefficients -function resetFFP(m::MagneticFieldViewerWidget) - - # reset the FFP - m.coeffs.ffp = nothing - - # show FFP - set_gtk_property!(m["entFFP"], :text, "no FFP") # show FFP of current patch - - # show FFP regarding buttons - set_gtk_property!(m["btnGoToFFP"],:visible,false) # FFP as intersection - set_gtk_property!(m["btnCenterFFP"],:visible,false) # FFP as center - set_gtk_property!(m["btnCenterFFP"],:sensitive,false) # FFP as center - # disable calcFFP button - set_gtk_property!(m["btnCalcFFP"],:sensitive,true) # FFP can be calculated again - set_gtk_property!(m["btnResetFFP"],:sensitive,false) # reset done - set_gtk_property!(m["cbVolumeFFP"],:sensitive,true) # allow to choose new volume - - # go to center sphere - m.fv.centerFFP = false - calcCenterCoeffs(m,true) - updatePlots(m) - set_gtk_property!(m["btnCenterSphere"],:sensitive,false) # Coefficients are in the sphere center -end - -# if button "Stay in FFP" true, everything is shifted into FFP, else the plots are updated -function stayInFFP(m::MagneticFieldViewerWidget) - # update plots - if get_gtk_property(m["cbStayFFP"], :active, Bool) && m.coeffs.ffp !== nothing - # stay in (resp. go to) the FFP with the plot - goToFFP(m) - else - # use intersection and just update all plots - updatePlots(m) - end -end - -# normalize coefficients with radius -function normalizeCoeffs(m::MagneticFieldViewerWidget) - # set some default values if the checkbutton is changed - if get_gtk_property(m["cbScaleR"], :active, Bool) - set_gtk_property!(m["adjL"], :value, min(m.coeffs.coeffs[1].L,3) ) # use L=3 as maximum - set_gtk_property!(m["cbUseMilli"], :active, true) # use mT - else - set_gtk_property!(m["adjL"], :value, 1 ) # use L=1 for coefficients scaled with R - set_gtk_property!(m["cbUseMilli"], :active, false) # use T/m^l - end - - updateCoeffsPlot(m) # update plot -end - -# update coefficients -function updateCoeffs(m::MagneticFieldViewerWidget, shift) - - # translate coefficients - for p = 1:size(m.coeffs.coeffs,2) - if size(shift) != size(m.coeffs.coeffs) # same shift for all coefficients - m.coeffsPlot[:,p] = SphericalHarmonicExpansions.translation.(m.coeffsPlot[:,p],[shift]) - else - m.coeffsPlot[:,p] = SphericalHarmonicExpansions.translation.(m.coeffsPlot[:,p],[shift[:,p]]) - end - end - - # update measurement infos - updateInfos(m) - -end - -# move coefficients -function calcCenterCoeffs(m::MagneticFieldViewerWidget,resetIntersection=false) - - # reset intersection - if resetIntersection - intersection = [0.0,0.0,0.0] - set_gtk_property!(m["entInters"], :text, - "$(intersection[1]*1000) x $(intersection[2]*1000) x $(intersection[3]*1000)") - end - - if m.fv.centerFFP - # shift coefficients into FFP (updates ffp and center) - MPISphericalHarmonics.shift!(m.coeffs, m.coeffs.ffp) - m.coeffsPlot = deepcopy(m.coeffs.coeffs) - else - # translate coefficients from FFP to center of measured sphere - MPISphericalHarmonics.shift!(m.coeffs, m.coeffs.center) - m.coeffsPlot = deepcopy(m.coeffs.coeffs) - end - - # update field - m.field = SphericalHarmonicsDefinedField(m.coeffs) - - # update measurement infos - updateInfos(m) -end - -# updating the measurement informations -function updateInfos(m::MagneticFieldViewerWidget) - # return new FFP - if m.coeffs.ffp !== nothing - ffpText = round.((m.coeffs.ffp[:,m.patch]) .* 1000, digits=1) - set_gtk_property!(m["entFFP"], :text, "$(ffpText[1]) x $(ffpText[2]) x $(ffpText[3])") # show FFP of current patch - end - - # return new center - centerText = round.((m.coeffs.center[:,m.patch]) .* 1000, digits=1) - set_gtk_property!(m["entCenterMeas"], :text, "$(centerText[1]) x $(centerText[2]) x $(centerText[3])") # show FFP of current patch - - # update gradient/offset information - if get_gtk_property(m["cbGradientOffset"],:active, Int) == 1 # show gradient - # gradient - set_gtk_property!(m["entGradientX"], :text, "$(round(m.coeffsPlot[1,m.patch][1,1],digits=3))") # show gradient in x - set_gtk_property!(m["entGradientY"], :text, "$(round(m.coeffsPlot[2,m.patch][1,-1],digits=3))") # show gradient in y - set_gtk_property!(m["entGradientZ"], :text, "$(round(m.coeffsPlot[3,m.patch][1,0],digits=3))") # show gradient in z - # unit - for i=1:3 - set_gtk_property!(m["labelTpm$i"], :label, "T/m") - set_gtk_property!(m["labelGradient$i"], :label, ["x","y","z"][i]) - end - elseif get_gtk_property(m["cbGradientOffset"],:active, Int) == 0 # show offset field - for (i,x) in enumerate(["X","Y","Z"]) - # offset - set_gtk_property!(m["entGradient"*x], :text, "$(round(m.coeffsPlot[i,m.patch][0,0]*1000,digits=3))") # show gradient in x - # unit - set_gtk_property!(m["labelTpm$i"], :label, "mT") - set_gtk_property!(m["labelGradient$i"], :label, ["x","y","z"][i]) - end - else # show eigenvalues or singular values - # calculate jacobian matrix - @polyvar x y z - expansion = sphericalHarmonicsExpansion.(m.coeffsPlot,[x],[y],[z]) - jexp = differentiate(expansion[:,m.patch],[x,y,z]); - J = [jexp[i,j]((x,y,z) => [0.0,0.0,0.0]) for i=1:3, j=1:3] # jacobian matrix - if get_gtk_property(m["cbGradientOffset"],:active, Int) == 2 - # get eigenvalues - sv = eigvals(J) - else - # get singular values - sv = svd(J).S - end - # show values - for (i,x) in enumerate(["X","Y","Z"]) - set_gtk_property!(m["entGradient"*x], :text, "$(round(sv[i],digits=3))") # eigenvalues/singular values - set_gtk_property!(m["labelTpm$i"], :label, "T/m") # unit - set_gtk_property!(m["labelGradient$i"], :label, "$i") - end - end - -end diff --git a/src/Viewer/MagneticFieldViewer/Plotting.jl b/src/Viewer/MagneticFieldViewer/Plotting.jl deleted file mode 100644 index 62817c5..0000000 --- a/src/Viewer/MagneticFieldViewer/Plotting.jl +++ /dev/null @@ -1,424 +0,0 @@ -############## -## Plotting ## -############## - -# update all plots -function updatePlots(m::MagneticFieldViewerWidget) - - # Coefficients - updateCoeffsPlot(m) - # calculate new field values - calcField(m) - # Field - updateField(m) - # Profile - updateProfile(m) -end - - -################## -# Magnetic field # -################## -# calculate the field (and profiles) -function calcField(m::MagneticFieldViewerWidget) - - discretization = Int(get_gtk_property(m["adjDiscretization"],:value, Int64)*2+1) # odd number of voxel - - # get current intersection - intersString = get_gtk_property(m["entInters"], :text, String) # intersection - m.fv.intersection = tryparse.(Float64,split(intersString,"x")) ./ 1000 # conversion from mm to m - - # get FOV - fovString = get_gtk_property(m["entFOV"], :text, String) # FOV - fov = tryparse.(Float64,split(fovString,"x")) ./ 1000 # conversion from mm to m - - # Grid (fov denotes the size and not the first and last pixel) - # center = m.coeffs.center # center of measurement data (TODO: adapt axis with measurement center) - m.fv.positions = [range(-fov[i]/2,stop=fov[i]/2,length=discretization+1) for i=1:3]; - for i=1:3 - m.fv.positions[i] = m.fv.positions[i][1:end-1] .+ Float64(m.fv.positions[i].step)/2 - end - N = m.fv.positions # renaming - - # calculate field for plot - m.fv.fieldNorm = zeros(discretization,discretization,3) - m.fv.field = zeros(3,discretization,discretization,3) - m.fv.currentProfile = zeros(4,discretization,3) - setPatch!(m.field,m.patch) # set selected patch - for i = 1:discretization - for j = 1:discretization - m.fv.field[:,i,j,1] = m.field[m.fv.intersection[1],N[2][i],N[3][j]] - m.fv.fieldNorm[i,j,1] = norm(m.fv.field[:,i,j,1]) - m.fv.field[:,i,j,2] = m.field[N[1][i],m.fv.intersection[2],N[3][j]] - m.fv.fieldNorm[i,j,2] = norm(m.fv.field[:,i,j,2]) - m.fv.field[:,i,j,3] = m.field[N[1][i],N[2][j],m.fv.intersection[3]] - m.fv.fieldNorm[i,j,3] = norm(m.fv.field[:,i,j,3]) - end - - # get current profile - m.fv.currentProfile[1:3,i,1] = m.field[N[1][i],m.fv.intersection[2],m.fv.intersection[3]] # along x-axis - m.fv.currentProfile[1:3,i,2] = m.field[m.fv.intersection[1],N[2][i],m.fv.intersection[3]] # along y-axis - m.fv.currentProfile[1:3,i,3] = m.field[m.fv.intersection[1],m.fv.intersection[2],N[3][i]] # along z-axis - m.fv.currentProfile[4,i,:] = [norm(m.fv.currentProfile[1:3,i,d]) for d=1:3] # norm along all axes - end - -end - -# plotting the magnetic field -function updateField(m::MagneticFieldViewerWidget, updateColoring=false) - - useMilli = get_gtk_property(m["cbUseMilli"], :active, Bool) # convert everything to mT or mm - discretization = Int(get_gtk_property(m["adjDiscretization"],:value, Int64)*2+1) # odd number of voxel - R = m.coeffs.radius # radius of measurement data - if m.coeffs.ffp !== nothing - ffp = useMilli ? m.coeffs.ffp .* 1000 : m.coeffs.ffp # used for correct positioning of the sphere - end - center = m.coeffs.center[:,m.patch] # center of measured sphere - N = m.fv.positions - - # coloring params - if updateColoring - # use params from GUI - cmin = m.fv.coloring.cmin - cmax = m.fv.coloring.cmax - cmap = m.fv.coloring.cmap - # set new min/max values - set_gtk_property!(m["adjCMin"], :upper, 0.99*cmax * 1000) # prevent cmin=cmax - set_gtk_property!(m["adjCMax"], :lower, 1.01*cmin * 1000) # prevent cmin=cmax - elseif get_gtk_property(m["cbKeepC"], :active, Bool) - # don't change min/max if the checkbutton is active - cmin = m.fv.coloring.cmin - cmax = m.fv.coloring.cmax - cmap = m.fv.coloring.cmap - elseif get_gtk_property(m["cbWriteC"], :active, Bool) - # get min/max from entCMin/Max - cminString = get_gtk_property(m["entCMin"], :text) - cmin = tryparse.(Float64,cminString) ./ 1000 - cmaxString = get_gtk_property(m["entCMax"], :text) - cmax = tryparse.(Float64,cmaxString) ./ 1000 - cmap = m.fv.coloring.cmap - m.fv.coloring = ColoringParams(cmin, cmax, cmap) # set coloring - else - # set new coloring params - cmin, cmax = minimum(m.fv.fieldNorm), maximum(m.fv.fieldNorm) - cmin = (get_gtk_property(m["cbCMin"], :active, Bool)) ? 0.0 : cmin # set cmin to 0 if checkbutton is active - cmap = m.fv.coloring.cmap - m.fv.coloring = ColoringParams(cmin, cmax, cmap) # set coloring - # set cmin and cmax - set_gtk_property!(m["adjCMin"], :lower, cmin * 1000) - set_gtk_property!(m["adjCMin"], :upper, 0.99*cmax * 1000) # prevent cmin=cmax - set_gtk_property!(m["adjCMax"], :lower, 1.01*cmin * 1000) # prevent cmin=cmax - set_gtk_property!(m["adjCMax"], :upper, cmax * 1000) - @idle_add_guarded set_gtk_property!(m["adjCMin"], :value, cmin * 1000) - @idle_add_guarded set_gtk_property!(m["adjCMax"], :value, cmax * 1000) - end - # update coloring infos - set_gtk_property!(m["entCMin"], :text, "$(round(m.fv.coloring.cmin * 1000, digits=3))") - set_gtk_property!(m["entCMax"], :text, "$(round(m.fv.coloring.cmax * 1000, digits=3))") - - # convert N to mT - N = useMilli ? N .* 1000 : N - - # heatmap plots - # label - lab = [useMilli ? "$i / mm" : "$i / m" for i in ["x", "y", "z"]] - # YZ - figYZ = CairoMakie.Figure(figure_padding=0); - axYZ = CairoMakie.Axis(figYZ[1,1], xlabel=lab[2], ylabel=lab[3]) - CairoMakie.heatmap!(axYZ, N[2], N[3], m.fv.fieldNorm[:,:,1], colorrange=(cmin,cmax), colormap=cmap) - # XZ - figXZ = CairoMakie.Figure(figure_padding=0); - axXZ = CairoMakie.Axis(figXZ[1,1], xlabel=lab[1], ylabel=lab[3]) - axXZ.xreversed = true # reverse x - CairoMakie.heatmap!(axXZ, N[1], N[3], m.fv.fieldNorm[:,:,2], colorrange=(cmin,cmax), colormap=cmap) - # XY - figXY = CairoMakie.Figure(figure_padding=0); - axXY = CairoMakie.Axis(figXY[1,1], xlabel=lab[2], ylabel=lab[1]) - CairoMakie.heatmap!(axXY, N[2], N[1], m.fv.fieldNorm[:,:,3]', colorrange=(cmin,cmax), colormap=cmap) - axXY.yreversed = true # reverse x - - - # disable ticks and labels - if !(get_gtk_property(m["cbShowCS"], :active, Bool)) - for ax in [axYZ, axXZ, axXY] - ax.xlabelvisible = false; ax.ylabelvisible = false; - ax.xticklabelsvisible = false; ax.yticklabelsvisible = false; - ax.xticksvisible = false; ax.yticksvisible = false; - end - end - - ## arrows ## - discr = floor(Int,0.1*discretization) # reduce number of arrows - ## positioning - NN = [N[i][1:discr:end] for i=1:3] - # vectors (arrows) (adapted to chosen coordinate orientations) - arYZ = [[m.fv.field[2,i,j,1],m.fv.field[3,i,j,1]] for i=1:discr:discretization, j=1:discr:discretization] - arXZ = [[m.fv.field[1,i,j,2],m.fv.field[3,i,j,2]] for i=1:discr:discretization, j=1:discr:discretization] - arXY = [[m.fv.field[2,i,j,3],m.fv.field[1,i,j,3]] for i=1:discr:discretization, j=1:discr:discretization] - - # calculate [u,v] for each arrow - # YZ - arYZu = [ar[1] for ar in arYZ] - arYZv = [ar[2] for ar in arYZ] - maxYZ = maximum([norm([arYZu[i],arYZv[i]]) for i in eachindex(arYZu)]) # for proper scaling - # XZ - arXZu = [ar[1] for ar in arXZ] - arXZv = [ar[2] for ar in arXZ] - maxXZ = maximum([norm([arXZu[i],arXZv[i]]) for i in eachindex(arXZu)]) # for proper scaling - # XY - arXYu = [ar[1] for ar in arXY] - arXYv = [ar[2] for ar in arXY] - maxXY = maximum([norm([arXYu[i],arXYv[i]]) for i in eachindex(arXYu)]) # for proper scaling - - # scale arrows - al = get_gtk_property(m["adjArrowLength"],:value, Float64) - al /= max(maxYZ,maxXZ,maxXY) - al /= useMilli ? 1 : 1000 # scale depends on m resp. mm - - # add arrows to plots - # YZ - CairoMakie.arrows!(axYZ, NN[2], NN[3], arYZu, arYZv, - color=:white, linewidth=1, arrowsize = 6, lengthscale = al) - # XZ - CairoMakie.arrows!(axXZ, NN[1], NN[3], arXZu, arXZv, - color=:white, linewidth=1, arrowsize = 6, lengthscale = al) - # XY - CairoMakie.arrows!(axXY, NN[2], NN[1], arXYu', arXYv', - color=:white, linewidth=1, arrowsize = 6, lengthscale = al) - - # set fontsize - fs = get_gtk_property(m["adjFontsize"],:value, Int64) # fontsize - CairoMakie.set_theme!(CairoMakie.Theme(fontsize = fs)) # set fontsize for the whole plot - - # Show slices - if get_gtk_property(m["cbShowSlices"], :active, Bool) - # draw lines to mark 0 - intersec = useMilli ? m.fv.intersection .*1000 : m.fv.intersection # scale intersection to the chosen unit - # YZ - CairoMakie.hlines!(axYZ, intersec[3], color=:white, linestyle=:dash, linewidth=0.5) - CairoMakie.vlines!(axYZ, intersec[2], color=:white, linestyle=:dash, linewidth=0.5) - # XZ - CairoMakie.hlines!(axXZ, intersec[3], color=:white, linestyle=:dash, linewidth=0.5) - CairoMakie.vlines!(axXZ, intersec[1], color=:white, linestyle=:dash, linewidth=0.5) - # XY - CairoMakie.hlines!(axXY, intersec[1], color=:white, linestyle=:dash, linewidth=0.5) - CairoMakie.vlines!(axXY, intersec[2], color=:white, linestyle=:dash, linewidth=0.5) - end - - # show sphere - if get_gtk_property(m["cbShowSphere"], :active, Bool) - # sphere - ϕ=range(0,stop=2*pi,length=100) - rr = zeros(100,2,3) - # adapt radius depending on the current intersection - r = m.fv.centerFFP ? m.fv.intersection-center : m.fv.intersection - r = sqrt.(max.(0, R^2 .- r .^ 2)) - for i=1:100 - rr[i,1,:] = r .* sin(ϕ[i]); - rr[i,2,:] = r .* cos(ϕ[i]); - end - rr = useMilli ? rr .* 1000 : rr # convert from m to mm - - # shift sphere to its center - center = useMilli ? center .* 1000 : center - if m.fv.centerFFP - CairoMakie.lines!(axYZ, rr[:,1,1].+center[2], rr[:,2,1].+center[3], - color=:white, linestyle=:dash, linewidth=1) - CairoMakie.lines!(axXZ, rr[:,1,2].+center[1], rr[:,2,2].+center[3], - color=:white, linestyle=:dash, linewidth=1) - CairoMakie.lines!(axXY, rr[:,1,3].+center[2], rr[:,2,3].+center[1], - color=:white, linestyle=:dash, linewidth=1) - else - CairoMakie.lines!(axYZ, rr[:,1,1], rr[:,2,1], color=:white, linestyle=:dash, linewidth=1) - CairoMakie.lines!(axXZ, rr[:,1,2], rr[:,2,2], color=:white, linestyle=:dash, linewidth=1) - CairoMakie.lines!(axXY, rr[:,1,3], rr[:,2,3], color=:white, linestyle=:dash, linewidth=1) - end - - end - - # show fields - drawonto(m.fv.grid[1,1], figXZ) - drawonto(m.fv.grid[2,1], figYZ) - drawonto(m.fv.grid[2,2], figXY) - - # draw axes (only arrows) - if get_gtk_property(m["cbShowAxes"], :active, Bool) - for w in [[m.fv.grid[1,1],"xz"], [m.fv.grid[2,1],"yz"], [m.fv.grid[2,2], "xy"]] - @idle_add_guarded Gtk4.draw(w[1]) do widget - ctx = getgc(w[1]) - drawAxes(ctx, w[2]) - set_line_width(ctx, 3.0) - Cairo.stroke(ctx) - end - end - end - -end - -################ -# Coefficients # -################ -# plotting the coefficients -function updateCoeffsPlot(m::MagneticFieldViewerWidget) - - p = get_gtk_property(m["adjPatches"],:value, Int64) # patch - L = get_gtk_property(m["adjL"],:value, Int64) # L - L² = (L+1)^2 # number of coeffs - R = m.coeffs.radius - useMilli = get_gtk_property(m["cbUseMilli"], :active, Bool) # convert everything to mT or mm - scaleR = get_gtk_property(m["cbScaleR"], :active, Bool) # normalize coefficients with radius R - - # normalize coefficients - c = scaleR ? normalize.(m.coeffsPlot[:,p], 1/R) : m.coeffsPlot[:,p] - cs = vcat([c[d].c[1:L²] for d=1:3]...) # stack all coefficients - cs = useMilli ? cs .* 1000 : cs # convert to mT - grp = repeat(1:3, inner=L²) # grouping the coefficients - - # set fontsize - fs = get_gtk_property(m["adjFontsize"],:value, Int64) # fontsize - CairoMakie.set_theme!(CairoMakie.Theme(fontsize = fs)) # set fontsize for the whole plot - - # create plot - fig = CairoMakie.Figure(figure_padding=2) - xticklabel = ["[$l,$m]" for l=0:L for m=-l:l] - # ylabel - if useMilli && scaleR - ylabel = CairoMakie.L"\gamma^R_{l,m}~/~\text{mT}" - elseif !useMilli && scaleR - ylabel = CairoMakie.L"\gamma^R_{l,m}~/~\text{T}" - elseif useMilli && !scaleR - ylabel = CairoMakie.L"\gamma_{l,m}~/~\text{mT/m}^l" - else - ylabel = CairoMakie.L"\gamma_{l,m}~/~\text{T/m}^l" - end - ax = CairoMakie.Axis(fig[1,1], xticks = (1:L², xticklabel), - #title="Coefficients", - xlabel = CairoMakie.L"[l,m]", ylabel = ylabel) - - # x values - y = range(1,L²,length=L²) - y = repeat(y, outer=3) # for each direction - - # create bars - colorsCoeffs = [CairoMakie.RGBf(MPIUI.colors[i]...) for i in [1,3,7]] # use blue, green and yellow - CairoMakie.barplot!(ax, # axis - y, cs, # x- and y-values - dodge=grp, color=colorsCoeffs[grp]) - CairoMakie.autolimits!(ax) # auto axis limits - - # draw line to mark 0 - CairoMakie.ablines!(0, 0, color=:black, linewidth=1) - - # legend - if get_gtk_property(m["cbShowLegend"], :active, Bool) - labels = ["x","y","z"] - elements = [CairoMakie.PolyElement(polycolor = colorsCoeffs[i], - ) for i in 1:length(labels)] - CairoMakie.axislegend(ax, elements, labels, position=:rt, patchsize=(15,0.8*fs)) # pos: right, top - end - - # show coeffs - drawonto(m.grid[1,3], fig) -end - - -################ -# profile plot # -################ -# update profile plot data -function updateProfile(m::MagneticFieldViewerWidget) - # ["Norm","xyz","x","y","z"] # cbFrameProj - field - # ["all", "x", "y", "z"] # cbProfile - axes - - # get chosen profiles - fields = get_gtk_property(m["cbFrameProj"],:active, Int64) - axesDir = get_gtk_property(m["cbProfile"],:active, Int64) - - # positioning - useMilli = get_gtk_property(m["cbUseMilli"], :active, Bool) # convert everything to mT or mm - N = m.fv.positions # renaming - # convert N to mT - N = useMilli ? N .* 1000 : N - - # colors - colorsAll = [CairoMakie.RGBf(MPIUI.colors[i]...) for i in [1,3,7]] # use blue, green and yellow - - # label - xlabel = ["xyz", "x", "y", "z"][axesDir+1] - xlabel *= useMilli ? " / mm" : " / m" - ylabel = (fields == 0) ? "||B||" : "B"*["","x","y","z"][fields] - ylabel *= useMilli ? " / mT" : " / T" - - # choose colors and data for the plot - if fields == 1 || axesDir == 0 - - # plot all three fields in one direction or one field/norm in all directions - colorsPlot = colorsAll # all three colors - - # data - x = (axesDir == 0) ? N : N[axesDir] # x values (all axes or one axis) - if fields == 1 && axesDir == 0 # all fields in their main direction - y = vcat([m.fv.currentProfile[j,:,j] for j=1:3]'...) - elseif fields == 1 # all fields in one direction - y = m.fv.currentProfile[1:3,:,axesDir] - elseif fields == 0 # norm in all directions - y = m.fv.currentProfile[4,:,:]' - else # one field in all directions - y = m.fv.currentProfile[fields-1,:,:]' - end - - elseif fields != 0 #|| (fields == 0 && axesDir == 1) - - # plot a field in one direction - colorsPlot = [colorsAll[fields-1]] # x-direction (field or axis) - - # data - x = N[axesDir] # x values - y = m.fv.currentProfile[fields-1,:,axesDir] - - else #fields == 0 && axesDir != 0 - - # plot norm in one direction - colorsPlot = [colorsAll[axesDir]] # y-direction (field or axis) - - # data - x = N[axesDir] # x values - y = m.fv.currentProfile[4,:,axesDir] - - end - - y *= useMilli ? 1000 : 1 - - showProfile(m, x, y, xlabel, ylabel, colorsPlot) - end - - # drawing profile plot - function showProfile(m::MagneticFieldViewerWidget, dataX, dataY, - xLabel::String, yLabel::String, - colors::Vector{RGB{Float32}}) - - # set fontsize - fs = get_gtk_property(m["adjFontsize"],:value, Int64) # fontsize - CairoMakie.set_theme!(CairoMakie.Theme(fontsize = fs)) # set fontsize for the whole plot - - # figure - fig = CairoMakie.Figure(figure_padding=2) - - # axis - ax = CairoMakie.Axis(fig[1,1], - #title="Profile", - xlabel = xLabel, ylabel = yLabel) - - # Plot - for i = 1:length(colors) # number of profiles - X = (typeof(dataX) <: Vector) ? dataX[i] : dataX - Y = (typeof(dataY) <: Vector) ? dataY : dataY[i,:] - CairoMakie.lines!(ax, X, Y, color=colors[i]) - end - CairoMakie.autolimits!(ax) # auto axis limits - - # draw line to mark 0 - CairoMakie.ablines!(0, 0, color=:black, linewidth=1) - - drawonto(m.fv.grid[1,2], fig) - end \ No newline at end of file diff --git a/src/Viewer/MagneticFieldViewer/TikzExport.jl b/src/Viewer/MagneticFieldViewer/TikzExport.jl deleted file mode 100644 index fde3968..0000000 --- a/src/Viewer/MagneticFieldViewer/TikzExport.jl +++ /dev/null @@ -1,348 +0,0 @@ -# Functions with tex-code to build up the entire file - -# combine tikz into final tex file -function exportTikz(filename::String, cmin, cmax, discr::Int, arrowL::Int, radius, L::Int) - - # get all tex-stuff - texPre, texPost = prepareTex() # tex stuff around tikzfigure - tikzParams = setTikzParams(filename,cmin,cmax,discr,arrowL,radius,L) # parameter defined in the viewer - tikzConfig = prepareTikzConfig() # plot configurations - tikzPlot = prepareTikzPlot() # plotting stuff - - # build tikzfigure - tikz = prepareTikz(tikzConfig, tikzPlot) - - # final tex file - tex = texPre * tikzParams * tikz * texPost - - return tex - -end - -# prepare tex stuff around tikzfigure -function prepareTex() - - texPre = """ - \\documentclass{standalone} - \\standaloneconfig{border=-1.0cm 0.0cm 0.0cm 0cm} % cut off unnecessary spaces (left, ...) - - % tikz & pgfplots - \\usepackage{tikz} - \\usepackage{pgfplots} - \\pgfplotsset{compat=newest} - - \\usetikzlibrary{positioning, backgrounds, fit, calc} - \\usepgfplotslibrary{units} - \\usepgfplotslibrary{groupplots} % positioning of the plots - - \\pgfdeclarelayer{background} - \\pgfdeclarelayer{foreground} - \\pgfsetlayers{background,main,foreground} - - % Coefficients plot - \\pgfplotsset{ - unit markings=slash space, - /pgfplots/xbar/.style={ - /pgf/bar shift={-0.5*(\\numplotsofactualtype*\\pgfplotbarwidth + (\\numplotsofactualtype-1)*#1) + (.5+\\plotnumofactualtype)*\\pgfplotbarwidth + \\plotnumofactualtype*#1}, - }, - } - % Style to select only points from #1 to #2 (inclusive) - \\pgfplotsset{select coords between index/.style 2 args={ - x filter/.code={ - \\ifnum\\coordindex<#1\\def\\pgfmathresult{}\\fi - \\ifnum\\coordindex>#2\\def\\pgfmathresult{}\\fi - } - }} - - %% Colors - \\definecolor{ibidark}{RGB}{0,73,146} % blue - \\definecolor{ukesec1}{RGB}{255,223,0} % yellow - \\definecolor{ukesec4}{RGB}{138,189,36} % green - - - \\usepackage{bm, amsmath, amssymb} - - \\begin{document} - """ - - texPost = """ - \\end{document} - """ - - return texPre, texPost - -end - -# set all necessary parameter -function setTikzParams(filename,cmin,cmax,discr,arrowL,radius,L) - - tikzParams = """ - % data path - \\def\\pathFile{$(filename)} - - % define sizes - \\def\\h{5cm} - \\def\\w{3.88*\\h} - \\def\\arrowLength{$(arrowL)} - \\pgfmathsetmacro\\scaleArrow{0.04*\\arrowLength} - \\pgfmathsetmacro\\vsep{1cm} % vertical separation between subplots - \\pgfmathsetmacro\\hsep{1.3cm} % horizontal separation between subplots - - % define some field-specific stuff - \\def\\cmin{$(cmin)} % field plot - \\def\\cmax{$(cmax)} % field plot - \\def\\discr{$(discr)} % discretization - \\def\\radius{$(radius)} - \\def\\L{$(L)} - - \\pgfmathsetmacro\\maxTick{(\\L+1)^2+0.5} - """ - - return tikzParams -end - - -# prepare plot configurations -function prepareTikzConfig() - - tikzConfig = """ - % data - \\pgfplotstableread[col sep=comma,]{\\pathFile_field.csv}\\datatableField % load data - \\pgfplotstableread[col sep=comma,]{\\pathFile_field_quiver.csv}\\datatableQuiver % load data - \\pgfplotstableread[col sep=semicolon,]{\\pathFile_coeffs.csv}\\datatableCoeffs % load data - - % coefficients: xlabel - \\gdef\\labellist{} - \\foreach \\l in {0,1,...,\\L} { - \\foreach \\m in {-\\l,...,\\l} { - \\xdef\\labellist{\\labellist {[\\l,\\m]},} - } - } - \\edef\\temp{\\noexpand\\pgfplotsset{x tick labels list/.style={xticklabels={\\labellist}}}}% - \\temp - - %% Setup of the plots - \\pgfplotsset{ - field/.style={ - clip mode=individual, - height=\\h, width=\\h, - axis equal image, - %% - view={0}{90}, - point meta = explicit, - %%% - mesh/cols=\\discr, - %% ticks %% - tick align=outside, - tickpos=left, - tick style={/pgfplots/major tick length=3pt}, - x tick label style={yshift=1pt}, y tick label style={xshift=1pt}, - %% labels %% - change x base = true, change y base = true, - ylabel={\$z\$}, - xlabel={\$x\$}, - x unit = m, x SI prefix = milli, - y unit = m, y SI prefix = milli, - xlabel shift={-6pt}, - ylabel shift={-5pt}, - %% Colors %% - colormap/viridis, - shader=interp, - %% Colorbar: - point meta min=\\cmin, - point meta max=\\cmax, - small, - colorbar style={ - %% sizes %% - colorbar shift/.style={xshift=0.075*\\h}, - footnotesize, - width=0.3cm, - height=1.12*\\h, - %% ticks %% - xticklabel style = {xshift=0.0cm,yshift=0.0cm}, - x tick style= {color=black}, - extra x tick style={tickwidth=0pt}, - change y base = true, - %% label %% - ylabel = {\$\\lVert \\bm B \\rVert_2\$}, - y unit = T, y SI prefix = milli, - unit markings=slash space, - ylabel style={rotate=180} - }, - }, - coeffs/.style = { - set layers=standard, - footnotesize, - width=\\w, - height=\\h, - % select only part of the coefficients - select coords between index={0}{15}, - %% labels %% - change y base = true, - xlabel={\$[l,m]\$}, - ylabel={coefficients}, - y unit = T, %m^{-\\mathnormal{l}}, % mathnormal for italic l - y SI prefix = milli, - xlabel shift={2pt}, - ylabel shift={-8pt}, - label style={font=\\small}, - %% ticks %% - scaled y ticks=base 10:3, % quasi milliTesla Angabe - scaled ticks=false, % ohne einzelnes 10^-2 - tick label style={/pgf/number format/fixed,}, % einzelne konkrete ticks label - % font=\\tiny}, - tick style={major grid style={thin,dotted,gray}}, - tick align=outside,%center, - tickpos=left, - x tick labels list, - % label as interval for 3 bars each - xtick={0.5,1.5,...,\\maxTick}, - x tick label as interval, - enlarge x limits=0.05, - tick style={/pgfplots/major tick length=3pt}, - x tick label style={yshift=1pt}, y tick label style={xshift=1pt}, - %% grid %% - grid=major, - extra y ticks={0.0}, % black line for y = 0 - extra y tick labels={}, - extra x tick style={major grid style={thin,gray}}, - extra y tick style={major grid style={solid,black,on layer=axis foreground}}, % black line for y = 0 - %% legend %% - legend style = {anchor=north east, at={(0.9925,0.975)}}, - legend columns=1, - }, - sphere/.style = {color=white, dashed, very thick,outer sep=2pt}, % measured sphere - } - """ - - return tikzConfig - -end - -# prepare plotting stuff -function prepareTikzPlot() - - tikzPlot = """ - %% Plot - \\begin{groupplot}[ - group style={ - group size=3 by 2, - vertical sep=\\vsep, - horizontal sep=\\hsep, - }, - height=\\h, - width=\\h, - scale only axis] - - %%% Magnetic fields - % xz-plane - \\nextgroupplot[field, - % title - title = {\$xz\$-plane}, - title style = {yshift=-5pt}, - ] - % norm - \\addplot3[surf] table [x=PlaneXZ_x,y=PlaneXZ_z,meta=PlaneXZ_f] {\\datatableField}; - % arrows - \\addplot[ - quiver = { - u = \\thisrow{PlaneXZ_u}, - v = \\thisrow{PlaneXZ_v}, - scale arrows = \\scaleArrow, - update limits=false, - }, - -stealth, - ] - table [x=PlaneXZ_x,y=PlaneXZ_z, ] {\\datatableQuiver}; - % circle - \\addplot [domain=-180:180, sphere] ({\\radius*cos(x)},{\\radius*sin(x)}); - - % yz-plane - \\nextgroupplot[field, - xlabel={\$y\$}, ylabel={\$z\$}, - % title - title = {\$yz\$-plane}, - title style = {yshift=-5pt}, - ] - % norm - \\addplot3[surf] table [x=PlaneYZ_y,y=PlaneYZ_z,meta=PlaneYZ_f] {\\datatableField}; - % arrows - \\addplot[ - quiver = { - u = \\thisrow{quiver_yzu}, % u=PlaneXZ_u - v = \\thisrow{quiver_yzv}, % v=PlaneXZ_v - scale arrows = \\scaleArrow, - update limits=false, - }, - -stealth, - ] - table [x=PlaneYZ_y,y=PlaneYZ_z, ] {\\datatableQuiver}; - % circle - \\addplot [domain=-180:180, sphere] ({\\radius*cos(x)},{\\radius*sin(x)}); - - % xy-plane - \\nextgroupplot[field, - xlabel={\$x\$}, ylabel={\$y\$}, - % title - title = {\$xy\$-plane}, - title style = {yshift=-5pt}, - % colorbar - colorbar, - ] - % field - \\addplot3[surf] table [x=PlaneXY_x,y=PlaneXY_y,meta=PlaneXY_f] {\\datatableField}; - % arrows - \\addplot[ - quiver = { - u = \\thisrow{Xyu}, % u=PlaneXZ_u - v = \\thisrow{Xyv}, % v=PlaneXZ_v - scale arrows = \\scaleArrow, - update limits=false, - }, - -stealth, - ] - table [x=PlaneXY_y,y=PlaneXY_x, ] {\\datatableQuiver}; - % circle - \\addplot [domain=-180:180, sphere] ({\\radius*cos(x)},{\\radius*sin(x)}); - - \\nextgroupplot[group/empty plot] - - %% Coefficients %% - \\nextgroupplot[coeffs, - % bar plot - ybar=1pt, - bar width=4.0pt, - ] - - \\addplot[fill = ibidark,draw=none] table [x = num, y = x] {\\datatableCoeffs}; % x - \\addlegendentry{\$x\$} - \\addplot[fill=ukesec4,draw=none] table [x = num, y = y] {\\datatableCoeffs}; % y - \\addlegendentry{\$y\$} - \\addplot[fill=ukesec1,draw=none] table [x = num, y = z] {\\datatableCoeffs}; % z - \\addlegendentry{\$z\$} - - \\nextgroupplot[group/empty plot] - - \\end{groupplot} - """ - - return tikzPlot - -end - -# prepare complete tikzpicture -function prepareTikz(tikzConfig, tikzPlot) - - tikzPre = """ - \\begin{tikzpicture} - """ - - tikzPost = """ - \\end{tikzpicture} - """ - - tikz = tikzPre * tikzConfig * tikzPlot * tikzPost - - return tikz - -end \ No newline at end of file diff --git a/src/Viewer/RawDataViewer.jl b/src/Viewer/RawDataViewer.jl deleted file mode 100644 index 7c9468d..0000000 --- a/src/Viewer/RawDataViewer.jl +++ /dev/null @@ -1,594 +0,0 @@ -import Base: getindex - -mutable struct RawDataWidget <: Gtk4.GtkBox - handle::Ptr{Gtk4.GObject} - builder::GtkBuilder - data::Array{Float32,5} - dataBG::Array{Float32,5} - labels::Vector{String} - cTD::GtkCanvas - cFD::GtkCanvas - deltaT::Float64 - filenamesData::Vector{String} - loadingData::Bool - updatingData::Bool - fileModus::Bool - rangeTD::NTuple{2,Float32} - rangeFD::NTuple{2,Float32} -end - -getindex(m::RawDataWidget, w::AbstractString) = Gtk4.G_.get_object(m.builder, w) - -function RawDataWidget(filenameConfig=nothing) - @info "Starting RawDataWidget" - uifile = joinpath(@__DIR__,"..","builder","rawDataViewer.ui") - - b = GtkBuilder(uifile) - mainBox = Gtk4.G_.get_object(b, "boxRawViewer") - - m = RawDataWidget( mainBox.handle, b, - zeros(Float32,0,0,0,0,0), zeros(Float32,0,0,0,0,0), - [""], GtkCanvas(), GtkCanvas(), - 1.0, [""], false, false, false, - (0.0,1.0), (0.0,1.0)) - Gtk4.GLib.gobject_move_ref(m, mainBox) - - @debug "Type constructed" - - push!(m["boxTD"],m.cTD) - - show(m.cTD) - show(m["boxTD"]) - - draw(m.cTD) - - m.cTD.hexpand = true - m.cTD.vexpand = true - - push!(m["boxFD"],m.cFD) - - m.cFD.hexpand = true - m.cFD.vexpand = true - - @debug "InitCallbacks" - - initCallbacks(m) - - @info "Finished starting RawDataWidget" - - return m -end - -function initCallbacks(m_::RawDataWidget) - let m=m_ - for sl in ["adjPatch","adjRxChan"] - signal_connect(m[sl], "value_changed") do w - showData(C_NULL, m) - end - #signal_connect(showData, m[sl], "value_changed", Nothing, (), false, m ) - end - - signal_connect(m["adjMinTP"], "value_changed") do w - minTP = get_gtk_property(m["adjMinTP"],:value, Int) - maxTP = get_gtk_property(m["adjMaxTP"],:value, Int) - maxValTP = get_gtk_property(m["adjMaxTP"],:upper, Int) - - if minTP > maxTP - @idle_add_guarded set_gtk_property!(m["adjMaxTP"],:value, min(maxValTP,minTP+10)) - else - showData(C_NULL, m) - end - end - - signal_connect(m["adjMaxTP"], "value_changed") do w - minTP = get_gtk_property(m["adjMinTP"],:value, Int) - maxTP = get_gtk_property(m["adjMaxTP"],:value, Int) - - if minTP > maxTP - @idle_add_guarded set_gtk_property!(m["adjMinTP"],:value, max(1,maxTP-10)) - else - showData(C_NULL, m) - end - end - - signal_connect(m["adjMinFre"], "value_changed") do w - minFre = get_gtk_property(m["adjMinFre"],:value, Int) - maxFre = get_gtk_property(m["adjMaxFre"],:value, Int) - maxValFre = get_gtk_property(m["adjMaxFre"],:upper, Int) - - if minFre > maxFre - @idle_add_guarded set_gtk_property!(m["adjMaxFre"],:value, min(maxValFre,minFre+10)) - else - showData(C_NULL, m) - end - end - - signal_connect(m["adjMaxFre"], "value_changed") do w - minFre = get_gtk_property(m["adjMinFre"],:value, Int) - maxFre = get_gtk_property(m["adjMaxFre"],:value, Int) - - if minFre > maxFre - @idle_add_guarded set_gtk_property!(m["adjMinFre"],:value, max(1,maxFre-10)) - else - showData(C_NULL, m) - end - end - - oldAdjPatchAvValue = 1 - signal_connect(m["adjPatchAv"], "value_changed") do w - if !m.updatingData - m.updatingData = true - patchAv = max(get_gtk_property(m["adjPatchAv"], :value, Int64),1) - numPatches = size(m.data,3) - if mod(numPatches, patchAv) != 0 - if 1 < patchAv < numPatches - while mod(numPatches, patchAv) != 0 - patchAv += sign(patchAv-oldAdjPatchAvValue)*1 - end - elseif patchAv < 1 - patchAv = 1 - elseif patchAv > numPatches - patchAv = numPatches - end - oldAdjPatchAvValue = patchAv - - @idle_add_guarded begin - set_gtk_property!(m["adjPatchAv"], :value, patchAv) - showAllPatchesChanged(m) - end - else - @idle_add_guarded showAllPatchesChanged(m) - end - oldAdjPatchAvValue = patchAv - m.updatingData = false - end - end - - for cb in ["cbShowBG", "cbSubtractBG", "cbShowFreq", "cbReversePlots"] - signal_connect(m[cb], :toggled) do w - showData(C_NULL, m) - end - #signal_connect(showData, m[cb], "toggled", Nothing, (), false, m) - end - - signal_connect(m["cbShowAllPatches"], :toggled) do w - @idle_add_guarded begin - showAllPatchesChanged(m) - end - end - - @guarded function showAllPatchesChanged(m) - m.updatingData = true - showAllPatches = get_gtk_property(m["cbShowAllPatches"], :active, Bool) - patchAv = max(get_gtk_property(m["adjPatchAv"], :value, Int64),1) - numPatches = div(size(m.data,3), patchAv) - - maxValTP = showAllPatches ? size(m.data,1)*numPatches : size(m.data,1) - maxValFre = div(maxValTP,2)+1 - - set_gtk_property!(m["adjMinTP"],:upper,maxValTP) - set_gtk_property!(m["adjMinTP"],:value,1) - set_gtk_property!(m["adjMaxTP"],:upper,maxValTP) - set_gtk_property!(m["adjMaxTP"],:value,maxValTP) - - set_gtk_property!(m["adjMinFre"],:upper,maxValFre) - set_gtk_property!(m["adjMinFre"],:value,1) - set_gtk_property!(m["adjMaxFre"],:upper,maxValFre) - set_gtk_property!(m["adjMaxFre"],:value,maxValFre) - m.updatingData = false - showData(C_NULL, m) - end - - - for cb in ["cbCorrTF","cbSLCorr","cbAbsFrameAverage"] - signal_connect(m[cb], :toggled) do w - loadData(C_NULL, m) - end - end - - for cb in ["adjFrame"] - signal_connect(m[cb], "value_changed") do w - loadData(C_NULL, m) - end - end - - for sl in ["entTDMinVal","entTDMaxVal","entFDMinVal","entFDMaxVal"] - signal_connect(m[sl], "changed") do w - showData(C_NULL, m) - end - #signal_connect(showData, m[sl], "value_changed", Nothing, (), false, m ) - end - - for sl in ["TD", "FD"] - signal_connect(m["btn$(sl)Apply"], "clicked") do w - if !m.updatingData - @idle_add_guarded begin - m.updatingData = true - r = (sl == "TD") ? m.rangeTD : m.rangeFD - set_gtk_property!( m["ent$(sl)MinVal"] ,:text, string(r[1])) - set_gtk_property!( m["ent$(sl)MaxVal"] ,:text, string(r[2])) - m.updatingData = false - showData(C_NULL, m) - end - end - end - - signal_connect(m["btn$(sl)Clear"], "clicked") do w - if !m.updatingData - @idle_add_guarded begin - m.updatingData = true - set_gtk_property!( m["ent$(sl)MinVal"] ,:text, "") - set_gtk_property!( m["ent$(sl)MaxVal"] ,:text, "") - m.updatingData = false - showData(C_NULL, m) - end - end - end - end - - #signal_connect(loadData, m["cbCorrTF"], "toggled", Nothing, (), false, m) - end -end - - - -@guarded function loadData(widgetptr::Ptr, m::RawDataWidget) - if !m.loadingData - m.loadingData = true - @info "Loading Data ..." - deltaT = 1.0 - - if m.filenamesData != [""] && all(ispath.(m.filenamesData)) - fs = MPIFile(m.filenamesData) #, isCalib=false) - - # TODO: Ensure that the measurements fit together (num samples / patches) - # otherwise -> error - - numFGFrames = minimum(acqNumFGFrames.(fs)) - numBGFrames = minimum(acqNumBGFrames.(fs)) - - dataFGVec = Any[] - dataBGVec = Any[] - - for (i,f) in enumerate(fs) - params = MPIFiles.loadMetadata(f) - params[:acqNumFGFrames] = acqNumFGFrames(f) - params[:acqNumBGFrames] = acqNumBGFrames(f) - - @idle_add_guarded set_gtk_property!(m["adjFrame"], :upper, numFGFrames) - - if get_gtk_property(m["cbAbsFrameAverage"], :active, Bool) - frame = 1:numFGFrames - else - frame = max( get_gtk_property(m["adjFrame"], :value, Int64), 1) - end - - timePoints = rxTimePoints(f) - deltaT = timePoints[2] - timePoints[1] - - data = getMeasurements(f, true, frames=frame, - bgCorrection=false, spectralLeakageCorrection = get_gtk_property(m["cbSLCorr"], :active, Bool), - tfCorrection=get_gtk_property(m["cbCorrTF"], :active, Bool)) - push!(dataFGVec, data) - - if acqNumBGFrames(f) > 0 - dataBG = getMeasurements(f, false, frames=measBGFrameIdx(f), - bgCorrection=false, spectralLeakageCorrection = get_gtk_property(m["cbSLCorr"], :active, Bool), - tfCorrection=get_gtk_property(m["cbCorrTF"], :active, Bool)) - else - dataBG = zeros(Float32,0,0,0,0) - end - push!(dataBGVec, dataBG) - end - - - m.dataBG = cat(dataBGVec..., dims=5) - dataFG = cat(dataFGVec..., dims=5) - m.labels = ["expnum "*string(experimentNumber(f)) for f in fs] - - updateData(m, dataFG, deltaT, true) - end - m.loadingData = false - end - return nothing -end - - -@guarded function showData(widgetptr::Ptr, m::RawDataWidget) - - if length(m.data) > 0 && !m.updatingData - chan = max(get_gtk_property(m["adjRxChan"], :value, Int64),1) - patch = max(get_gtk_property(m["adjPatch"], :value, Int64),1) - minTP = max(get_gtk_property(m["adjMinTP"], :value, Int64),1) - maxTP = max(get_gtk_property(m["adjMaxTP"], :value, Int64),1) - minFr = max(get_gtk_property(m["adjMinFre"], :value, Int64),1) - maxFr = max(get_gtk_property(m["adjMaxFre"], :value, Int64),1) - patchAv = max(get_gtk_property(m["adjPatchAv"], :value, Int64),1) - numPatches = div(size(m.data,3), patchAv) - numSignals = size(m.data,5) - showFD = get_gtk_property(m["cbShowFreq"], :active, Bool) - reversePlots = get_gtk_property(m["cbReversePlots"], :active, Bool) - - autoRangingTD = true - autoRangingFD = true - minValTD_ = tryparse(Float64,get_gtk_property( m["entTDMinVal"] ,:text,String)) - maxValTD_ = tryparse(Float64,get_gtk_property( m["entTDMaxVal"] ,:text,String)) - minValFD_ = tryparse(Float64,get_gtk_property( m["entFDMinVal"] ,:text,String)) - maxValFD_ = tryparse(Float64,get_gtk_property( m["entFDMaxVal"] ,:text,String)) - - if minValTD_ != nothing && maxValTD_ != nothing - minValTD = minValTD_ - maxValTD = maxValTD_ - autoRangingTD = false - end - - if minValFD_ != nothing && maxValFD_ != nothing - minValFD = minValFD_ - maxValFD = maxValFD_ - autoRangingFD = false - end - - if get_gtk_property(m["cbShowAllPatches"], :active, Bool) - data = vec( mean( reshape(m.data[:,chan,:,1,:],:, patchAv, numPatches, numSignals), dims=2) ) - if length(m.dataBG) > 0 - dataBG = vec(mean(reshape(m.dataBG[:,chan,:,:,:],size(m.dataBG,1), patchAv, numPatches, :, numSignals), dims=(2,4)) ) - - if get_gtk_property(m["cbSubtractBG"], :active, Bool) - data[:] .-= dataBG - end - end - else - if get_gtk_property(m["cbAbsFrameAverage"], :active, Bool) - dataFD = rfft(m.data[:,chan,patch,:,:],1) - dataFD_ = reshape(mean(abs.(dataFD), dims=2),:,numSignals) - data = irfft(dataFD_, 2*size(dataFD_, 1) -2, 1) - - if length(m.dataBG) > 0 - dataBGFD = rfft(m.dataBG[:,chan,patch,:,:], 1) - dataBGFD_ = reshape(mean(abs.(dataBGFD), dims=2), :, numSignals) - dataBG = irfft(dataBGFD_, 2*size(dataBGFD_, 1) -2, 1) - if get_gtk_property(m["cbSubtractBG"], :active, Bool) - data .-= dataBG - end - end - else - data = vec(m.data[:,chan,patch,1,:]) - if length(m.dataBG) > 0 - #dataBG = vec(m.dataBG[:,chan,patch,1] .- mean(m.dataBG[:,chan,patch,:], dims=2)) - dataBG = vec( mean(m.dataBG[:,chan,patch,:,:],dims=2)) - if get_gtk_property(m["cbSubtractBG"], :active, Bool) - data[:] .-= dataBG - end - end - end - end - - data = reshape(data, :, numSignals) - if reversePlots - reverse!(data, dims=2) - labels_ = reverse(m.labels) - else - labels_ = m.labels - end - if length(m.dataBG) > 0 && get_gtk_property(m["cbShowBG"], :active, Bool) - dataBG = reshape(dataBG, :, numSignals) - end - m.rangeTD = extrema(data) - - #colors = ["blue", "red", "green", "yellow", "black", "cyan", "magenta"] - - timePoints = (0:(size(data,1)-1)).*m.deltaT - numFreq = floor(Int, size(data,1) ./ 2 .+ 1) - - maxPoints = 5000 - sp = length(minTP:maxTP) > maxPoints ? round(Int,length(minTP:maxTP) / maxPoints) : 1 - - steps = minTP:sp:maxTP - dataCompressed = zeros(length(steps), size(data,2)) - if sp > 1 - for j=1:size(data,2) - for l=1:length(steps) - st = steps[l] - en = min(st+sp,steps[end]) - med_ = median(data[st:en,j]) - max_ = maximum(data[st:en,j]) - min_ = minimum(data[st:en,j]) - dataCompressed[l,j] = rand(Bool) ? max_ : min_ #abs(max_ - med_) > abs(med_ - min_) ? max_ : min_ - end - end - else - dataCompressed = data[steps,:] - end - - fTD, axTD, lTD1 = CairoMakie.lines(timePoints[steps], dataCompressed[:,1], - figure = (; figure_padding=4, size = (1000, 800), fontsize = 11), - axis = (; title = "Time Domain"), - color = CairoMakie.RGBf(colors[1]...), - label = labels_[1]) - for j=2:size(data,2) - CairoMakie.lines!(axTD, timePoints[steps],dataCompressed[:,j], - color = CairoMakie.RGBf(colors[j]...), #linewidth=3) - label = labels_[j]) - end - if length(m.dataBG) > 0 && get_gtk_property(m["cbShowBG"], :active, Bool) - CairoMakie.lines!(axTD, timePoints[minTP:sp:maxTP],dataBG[minTP:sp:maxTP,1], color=:black, - label="BG", linestyle = :dash) - end - CairoMakie.autolimits!(axTD) - if timePoints[steps[end]] > timePoints[steps[1]] - CairoMakie.xlims!(axTD, timePoints[steps[1]], timePoints[steps[end]]) - end - if !autoRangingTD && maxValTD > minValTD - CairoMakie.ylims!(axTD, minValTD, maxValTD) - end - axTD.xlabel = "t / ms" - axTD.ylabel = "u / V" - - if (size(data,2) > 1 && length(m.labels) == size(data,2)) || - (length(m.dataBG) > 0 && get_gtk_property(m["cbShowBG"], :active, Bool)) - CairoMakie.axislegend() - end - - if showFD - freq = collect(0:(numFreq-1))./(numFreq-1)./m.deltaT./2.0 - freqdata = abs.(rfft(data, 1)) / size(data,1) - m.rangeFD = extrema(freqdata) - spFr = length(minFr:maxFr) > maxPoints ? round(Int,length(minFr:maxFr) / maxPoints) : 1 - - stepsFr = minFr:spFr:maxFr - freqDataCompressed = zeros(length(stepsFr), size(freqdata,2)) - if spFr > 1 - for j=1:size(freqdata,2) - for l=1:length(stepsFr) - st = stepsFr[l] - en = min(st+spFr,stepsFr[end]) - freqDataCompressed[l,j] = maximum(freqdata[st:en,j]) - end - end - else - freqDataCompressed = freqdata[stepsFr,:] - end - - fFD, axFD, lFD1 = CairoMakie.lines(freq[stepsFr],freqDataCompressed[:,1], - figure = (; figure_padding=4, size = (1000, 800), fontsize = 11), - axis = (; title = "Frequency Domain", yscale=log10), - color = CairoMakie.RGBf(colors[1]...), - label=labels_[1]) - for j=2:size(data,2) - CairoMakie.lines!(axFD, freq[stepsFr], freqDataCompressed[:,j], - color = CairoMakie.RGBf(colors[j]...), label=labels_[j]) - end - if length(m.dataBG) > 0 && get_gtk_property(m["cbShowBG"], :active, Bool) - CairoMakie.lines!(axTD, timePoints[minTP:sp:maxTP],dataBG[minTP:sp:maxTP,1], color=:black, - label="BG", linestyle = :dash) - if showFD - CairoMakie.lines!(axFD, freq[minFr:spFr:maxFr],abs.(rfft(dataBG,1)[minFr:spFr:maxFr,1]) / size(dataBG,1), - color=:black, label="BG", linestyle = :dash) - end - end - CairoMakie.autolimits!(axFD) - if freq[stepsFr[end]] > freq[stepsFr[1]] - CairoMakie.xlims!(axFD, freq[stepsFr[1]], freq[stepsFr[end]]) - end - if !autoRangingFD && maxValFD > minValFD - CairoMakie.ylims!(axFD, minValFD, maxValFD) - end - axFD.xlabel = "f / kHz" - axFD.ylabel = "u / V" - - - else - @guarded Gtk4.draw(m.cFD) do widget - - ctx = getgc(m.cFD) - h = height(ctx) - w = width(ctx) - Cairo.set_source_rgb(ctx,1.0,1.0,1.0) - Cairo.rectangle(ctx, 0,0,w,h) - Cairo.paint(ctx) - Cairo.stroke(ctx) - end - end - - @idle_add_guarded drawonto(m.cTD, fTD) - if showFD - @idle_add_guarded drawonto(m.cFD, fFD) - end - - end - return nothing -end - -function setBG(m::RawDataWidget, dataBG) - if ndims(dataBG) == 5 - m.dataBG = dataBG - else - m.dataBG = reshape(dataBG, size(dataBG)..., 1) - end -end - -@guarded function updateData(m::RawDataWidget, data::Array, deltaT=1.0, fileModus=false) - maxValTPOld = get_gtk_property(m["adjMinTP"],:upper, Int64) - maxValFreOld = get_gtk_property(m["adjMinFre"],:upper, Int64) - - if ndims(data) == 5 - m.data = data - else - m.data = reshape(data, size(data)..., 1) - end - m.deltaT = deltaT .* 1000 # convert to ms and kHz - m.fileModus = fileModus - - if !isempty(m.dataBG) - if size(m.data)[1:3] != size(m.dataBG)[1:3] - @info "Background data does not fit to foreground data! Dropping BG data." - @info size(m.data) - @info size(m.dataBG) - m.dataBG = zeros(Float32,0,0,0,0,0) - end - end - - showAllPatches = get_gtk_property(m["cbShowAllPatches"], :active, Bool) - patchAv = max(get_gtk_property(m["adjPatchAv"], :value, Int64),1) - numPatches = div(size(m.data,3), patchAv) - maxValTP = showAllPatches ? size(m.data,1)*numPatches : size(m.data,1) - maxValFre = div(maxValTP,2)+1 - - @idle_add_guarded begin - m.updatingData = true - if !fileModus - set_gtk_property!(m["adjFrame"],:upper,size(data,4)) - if !(1 <= get_gtk_property(m["adjFrame"],:value,Int64) <= size(data,4)) - set_gtk_property!(m["adjFrame"],:value,1) - end - end - set_gtk_property!(m["adjRxChan"],:upper,size(data,2)) - if !(1 <= get_gtk_property(m["adjRxChan"],:value,Int64) <= size(data,2)) - set_gtk_property!(m["adjRxChan"],:value,1) - end - set_gtk_property!(m["adjPatch"],:upper,size(data,3)) - if !(1 <= get_gtk_property(m["adjPatch"],:value,Int64) <= size(data,3)) - set_gtk_property!(m["adjPatch"],:value,1) - end - set_gtk_property!(m["adjMinTP"],:upper,maxValTP) - if !(1 <= get_gtk_property(m["adjMinTP"],:value,Int64) <= maxValTP) || maxValTP != maxValTPOld - set_gtk_property!(m["adjMinTP"],:value,1) - end - set_gtk_property!(m["adjMaxTP"],:upper, maxValTP) - if !(1 <= get_gtk_property(m["adjMaxTP"],:value,Int64) <= maxValTP) || maxValTP != maxValTPOld - set_gtk_property!(m["adjMaxTP"],:value, maxValTP) - end - set_gtk_property!(m["adjMinFre"],:upper, maxValFre) - if !(1 <= get_gtk_property(m["adjMinFre"],:value,Int64) <= maxValFre) || maxValFre != maxValFreOld - set_gtk_property!(m["adjMinFre"],:value,1) - end - set_gtk_property!(m["adjMaxFre"],:upper, maxValFre) - if !(1 <= get_gtk_property(m["adjMaxFre"],:value,Int64) <= maxValFre) || maxValFre != maxValFreOld - set_gtk_property!(m["adjMaxFre"],:value, maxValFre) - end - - m.updatingData = false - showData(C_NULL, m) - end -end - -@guarded function updateData(m::RawDataWidget, filenames::Vector{<:AbstractString}) - m.filenamesData = filenames - @idle_add_guarded begin - m.updatingData = true - set_gtk_property!(m["adjFrame"],:upper,1) - set_gtk_property!(m["adjFrame"],:value,1) - set_gtk_property!(m["adjPatch"],:upper,1) - set_gtk_property!(m["adjPatch"],:value,1) - set_gtk_property!(m["adjPatchAv"],:upper,1) - set_gtk_property!(m["adjPatchAv"],:value,1) - m.updatingData = false - loadData(C_NULL, m) - end - return nothing -end - -@guarded function updateData(m::RawDataWidget, filename::String) - updateData(m, [filename]) - return nothing -end diff --git a/src/Viewer/SFViewerWidget.jl b/src/Viewer/SFViewerWidget.jl deleted file mode 100644 index 492f110..0000000 --- a/src/Viewer/SFViewerWidget.jl +++ /dev/null @@ -1,373 +0,0 @@ -export SFViewer - -import Base: getindex - - -mutable struct SFViewerWidget <: Gtk4.GtkPaned - handle::Ptr{Gtk4.GObject} - builder::GtkBuilder - dv::DataViewerWidget - bSF::MPIFile - updating::Bool - maxFreq::Int - maxChan::Int - SNR::Array{Float64,3} - freqIndices::Vector{CartesianIndex{2}} - SNRSortedIndices::Array{Int64,1} - SNRSortedIndicesInverse::Array{Int64,1} - SNRSortedIndicesRecChan::Array{Array{Int64,1},1} - SNRSortedIndicesRecChanInverse::Array{Array{Int64,1},1} - mixFac::Array{Int64,2} - mxyz::Array{Int64,1} - frequencies::Array{Float64,1} - frequencySelection::Array{Int,1} - grid::Gtk4.GtkGridLeaf -end - -getindex(m::SFViewerWidget, w::AbstractString) = Gtk4.G_.get_object(m.builder, w) - -mutable struct SFViewer - w::Gtk4.GtkWindowLeaf - sf::SFViewerWidget -end - -function SFViewer(filename::AbstractString) - sfViewerWidget = SFViewerWidget() - w = GtkWindow("SF Viewer: $(filename)",800,600) - push!(w,sfViewerWidget) - show(w) - updateData!(sfViewerWidget, filename) - return SFViewer(w, sfViewerWidget) -end - -function SFViewerWidget() - uifile = joinpath(@__DIR__,"..","builder","mpiLab.ui") - - b = GtkBuilder(uifile) - mainBox = GtkPaned(:h) - - m = SFViewerWidget(mainBox.handle, b, DataViewerWidget(), - BrukerFile(), false, 0, 0, zeros(0,0,0), CartesianIndex{2}[], - zeros(0), zeros(0), zeros(0), zeros(0), zeros(0,0), zeros(0), zeros(0), zeros(Int,0), GtkGrid()) - Gtk4.GLib.gobject_move_ref(m, mainBox) - - m.grid[1,1:2] = m.dv - m.grid[1,3] = GtkCanvas() - set_gtk_property!(m.grid, :row_homogeneous, true) - set_gtk_property!(m.grid[1,2], :height_request, 200) - - m[1] = m.grid - m[2] = m["swSFViewer"] - - Gtk4.resize_start_child(m, true) - Gtk4.shrink_start_child(m, true) - Gtk4.resize_end_child(m, false) - Gtk4.shrink_end_child(m, false) - - G_.set_size_request(m["swSFViewer"], 250, -1) - - function updateSFMixO( widget ) - if !m.updating - @idle_add_guarded begin - m.updating = true - mx = get_gtk_property(m["adjSFMixX"],:value, Int64) - my = get_gtk_property(m["adjSFMixY"],:value, Int64) - mz = get_gtk_property(m["adjSFMixZ"],:value, Int64) - - freq = 0 - m_ = [mx,my,mz] - for d=1:length(m.mxyz) - freq += m_[d]*m.mxyz[d] - end - - freq = clamp(freq,0,m.maxFreq-1) - updateFreq(m, freq) - updateSigOrd(m) - updateSF(m) - m.updating = false - end - end - end - - function updateSFSignalOrdered( widget ) - if !m.updating - @idle_add_guarded begin - m.updating = true - if !(get_gtk_property(m["cbFixRecChan"],:active, Bool)) - k = m.SNRSortedIndices[get_gtk_property(m["adjSFSignalOrdered"],:value, Int64)] - recChan = m.freqIndices[k][2] - else - # fix the current receive channel for ordered signal - recChan = get_gtk_property(m["adjSFRecChan"],:value, Int64) - k = m.SNRSortedIndicesRecChan[recChan][get_gtk_property(m["adjSFSignalOrdered"],:value, Int64)] - recChan = m.freqIndices[k][2] - end - - freq = m.freqIndices[k][1] - updateFreq(m, freq) - updateRecChan(m, recChan) - updateMix(m) - updateSF(m) - m.updating = false - - end - end - end - - # BG correction - signal_connect(m["cbSFBGCorr"], :toggled) do w - @idle_add_guarded updateSF(m) - end - - # TF correction - signal_connect(m["cbSFTFCorr"], :toggled) do w - @idle_add_guarded updateSF(m) - end - - signal_connect(m["adjSFPatch"], "value_changed") do w - @idle_add_guarded updateSF(m) - end - - signal_connect(m["adjSNRMinFreq"], "value_changed") do w - @idle_add_guarded updateSF(m) - end - - signal_connect(m["adjSNRMaxFreq"], "value_changed") do w - @idle_add_guarded updateSF(m) - end - - signal_connect(m["adjSFRecChan"], "value_changed") do w - if !m.updating - @idle_add_guarded begin - m.updating = true - updateMix(m) - updateSigOrd(m) - updateSF(m) - m.updating = false - end - end - end - signal_connect(m["adjSFFreq"], "value_changed") do w - if !m.updating - @idle_add_guarded begin - m.updating = true - updateMix(m) - updateSigOrd(m) - updateSF(m) - m.updating = false - end - end - end - - for w in Any["adjSFMixX","adjSFMixY","adjSFMixZ"] - signal_connect(updateSFMixO, m[w], "value_changed") - end - - signal_connect(m["cbFixRecChan"], :toggled) do w - @idle_add_guarded updateSigOrd(m) - end - signal_connect(updateSFSignalOrdered, m["adjSFSignalOrdered"], "value_changed") - - signal_connect(m["btnRecalcSNR"], :clicked) do w - @idle_add_guarded recalcSNR(m) - end - - return m -end - - -function updateFreq(m::SFViewerWidget, freq) - set_gtk_property!(m["adjSFFreq"],:value, freq) -end - -function updateRecChan(m::SFViewerWidget, recChan) - set_gtk_property!(m["adjSFRecChan"],:value, recChan) -end - -function updateSigOrd(m::SFViewerWidget) - freq = get_gtk_property(m["adjSFFreq"],:value, Int64)+1 - recChan = get_gtk_property(m["adjSFRecChan"],:value, Int64) - if !(get_gtk_property(m["cbFixRecChan"],:active, Bool)) - k = freq + m.maxFreq*((recChan-1)) - set_gtk_property!(m["adjSFSignalOrdered"],:value, m.SNRSortedIndicesInverse[k] ) - else - # fix the current receive channel for ordered signal - set_gtk_property!(m["adjSFSignalOrdered"],:value, m.SNRSortedIndicesRecChanInverse[recChan][freq] ) - end -end - -function updateMix(m::SFViewerWidget) - freq = get_gtk_property(m["adjSFFreq"],:value, Int64)+1 - set_gtk_property!(m["adjSFMixX"],:value, m.mixFac[freq,1]) - set_gtk_property!(m["adjSFMixY"],:value, m.mixFac[freq,2]) - set_gtk_property!(m["adjSFMixZ"],:value, m.mixFac[freq,3]) -end - - -function updateSF(m::SFViewerWidget) - freq = get_gtk_property(m["adjSFFreq"],:value, Int64)+1 - recChan = get_gtk_property(m["adjSFRecChan"],:value, Int64) - period = get_gtk_property(m["adjSFPatch"],:value, Int64) - minFr = get_gtk_property(m["adjSNRMinFreq"],:value, Int64)+1 - maxFr = get_gtk_property(m["adjSNRMaxFreq"],:value, Int64)+1 - - # BG correction - bgcorrection = get_gtk_property(m["cbSFBGCorr"],:active, Bool) - # disable BG correction if no BG frames are available - if maximum(Int.(measIsBGFrame(m.bSF))) == 0 - bgcorrection = false - set_gtk_property!(m["cbSFBGCorr"],:active,false) - end - - # TF correction - tfcorrection = get_gtk_property(m["cbSFTFCorr"],:active, Bool) - - k = CartesianIndex(freq, recChan) - - if !measIsFrequencySelection(m.bSF) || k in m.frequencySelection - sfData_ = getSF(m.bSF, [k], returnasmatrix = true, bgcorrection=bgcorrection, tfCorrection=tfcorrection)[1][:,period] - sfData_[:] ./= rxNumSamplingPoints(m.bSF) - else - # set sfData to one for frequencies ∉ frequencySelection - sfData_ = ones(ComplexF32,prod(calibSize(m.bSF))) - end - - sfData = reshape(sfData_, calibSize(m.bSF)...) - - set_gtk_property!(m["entSFSNR"],:text,string(round(m.SNR[freq,recChan,period],digits=2))) - #set_gtk_property!(m["entSFSNR2"],:text,string(round(calcSNRF(sfData_),digits=2))) - snr5 = [string(sum(m.SNR[:,d,1] .> 5)," ") for d=1:size(m.SNR,2)] - set_gtk_property!(m["entSFSNR2"],:text, prod( snr5 ) ) - - - maxPoints = 5000 - spFr = length(minFr:maxFr) > maxPoints ? round(Int,length(minFr:maxFr) / maxPoints) : 1 - - stepsFr = minFr:spFr:maxFr - snrCompressed = zeros(length(stepsFr)) - if spFr > 1 - for l=1:length(stepsFr) - st = stepsFr[l] - en = min(st+spFr,stepsFr[end]) - snrCompressed[l] = maximum(m.SNR[st:en,recChan,period]) - end - else - snrCompressed = vec(m.SNR[stepsFr,recChan,period]) - end - - fFD, axFD, lFD1 = CairoMakie.lines(m.frequencies[stepsFr], snrCompressed, - figure = (; size = (1000, 800), fontsize = 12), - axis = (; title = "SNR", yscale=log10), - color = CairoMakie.RGBf(colors[1]...)) - CairoMakie.scatter!(axFD, [m.frequencies[freq]], [m.SNR[freq,recChan,period]], - markersize=9, color=:red, marker=:xcross) - - CairoMakie.autolimits!(axFD) - if m.frequencies[stepsFr[end]] > m.frequencies[stepsFr[1]] - CairoMakie.xlims!(axFD, m.frequencies[stepsFr[1]], m.frequencies[stepsFr[end]]) - end - axFD.xlabel = "f / kHz" - - drawonto(m.grid[1,3], fFD) - - show(m) - - c = reshape(sfData, 1, size(sfData,1), size(sfData,2), size(sfData,3), 1) - c_ = cat(abs.(c),angle.(c), dims=1) - im = AxisArray(c_, (:color,:x,:y,:z,:time), - tuple(1.0, 1.0, 1.0, 1.0, 1.0), - tuple(0.0, 0.0, 0.0, 0.0, 0.0)) - - imMeta = ImageMeta(im, Dict{Symbol,Any}()) - - updateData!(m.dv, imMeta, ampPhase=true) -end - -function updateData!(m::SFViewerWidget, filenameSF::String) - m.bSF = MPIFile(filenameSF, fastMode=true) - m.maxChan = rxNumChannels(m.bSF) - m.frequencies = rxFrequencies(m.bSF)./1000 - m.maxFreq = length(m.frequencies) - if measIsFrequencySelection(m.bSF) - # Workaround for FrequencySelection - m.frequencySelection = vcat([measFrequencySelection(m.bSF).+i*m.maxFreq for i=0:m.maxChan-1]...) - end - m.updating = true - set_gtk_property!(m["adjSFFreq"],:value, 2 ) - set_gtk_property!(m["adjSFFreq"],:upper, m.maxFreq-1 ) - if measIsFrequencySelection(m.bSF) - # first frequency of frequencySelection - set_gtk_property!(m["adjSFFreq"],:value, m.frequencySelection[1]-1 ) - end - set_gtk_property!(m["adjSFSignalOrdered"],:value, 1 ) - set_gtk_property!(m["adjSFSignalOrdered"],:upper, m.maxFreq*m.maxChan ) - set_gtk_property!(m["adjSFMixX"],:value, 0 ) - set_gtk_property!(m["adjSFMixY"],:value, 0 ) - set_gtk_property!(m["adjSFMixZ"],:value, 0 ) - set_gtk_property!(m["adjSFRecChan"],:value, 1 ) - set_gtk_property!(m["adjSFRecChan"],:upper, m.maxChan ) - set_gtk_property!(m["adjSFPatch"],:value, 1 ) - set_gtk_property!(m["adjSFPatch"],:upper, acqNumPeriodsPerFrame(m.bSF) ) - set_gtk_property!(m["adjSNRMinFreq"],:upper, m.maxFreq-1 ) - set_gtk_property!(m["adjSNRMaxFreq"],:upper, m.maxFreq-1 ) - set_gtk_property!(m["adjSNRMinFreq"],:value, 0 ) - set_gtk_property!(m["adjSNRMaxFreq"],:value, m.maxFreq-1 ) - - - m.SNR = calibSNR(m.bSF)[:,:,:] - if measIsFrequencySelection(m.bSF) - # set SNR to zero for frequencies ∉ frequencySelection - snr = zeros(Float64, m.maxFreq*size(m.SNR,2), size(m.SNR,3)) - snr[m.frequencySelection,:] = reshape(calibSNR(m.bSF), size(m.SNR,1)*size(m.SNR,2), :) - m.SNR = reshape(snr, m.maxFreq, size(m.SNR,2), size(m.SNR,3)) - end - - m.freqIndices = collect(vec(CartesianIndices((m.maxFreq, m.maxChan)))) - - updateDerivedSNRLUTs(m) - - m.mixFac = MPIFiles.mixingFactors(m.bSF) - mxyz, mask, freqNumber = MPIFiles.calcPrefactors(m.bSF) - m.mxyz = mxyz - - set_gtk_property!(m["adjSFMixX"],:upper, maximum(m.mixFac[:, 1])) - set_gtk_property!(m["adjSFMixY"],:upper, maximum(m.mixFac[:, 2])) - set_gtk_property!(m["adjSFMixZ"],:upper, maximum(m.mixFac[:, 3])) - - - # show frequency component with highest SNR - k = m.freqIndices[m.SNRSortedIndices[1]] - recChan = k[2] - freq = k[1] - updateFreq(m, freq) - updateRecChan(m, recChan) - - # disable TF correction button if no TF available - if !rxHasTransferFunction(m.bSF) - set_gtk_property!(m["cbSFTFCorr"], :sensitive, false) - set_gtk_property!(m["cbSFTFCorr"], :active, false) - end - - updateMix(m) - updateSigOrd(m) - updateSF(m) - m.updating = false -end - -function recalcSNR(m) - @info "Recalculate SNR" - m.updating = true - m.SNR = calculateSystemMatrixSNR(m.bSF) - updateDerivedSNRLUTs(m) - m.updating = false - updateSF(m) -end - -function updateDerivedSNRLUTs(m) - m.SNRSortedIndices = reverse(sortperm(vec(m.SNR))) - m.SNRSortedIndicesInverse = sortperm(m.SNRSortedIndices) - # sort SNR channel-wise - m.SNRSortedIndicesRecChan = [reverse(sortperm(m.SNR[:,i,1])) for i=1:m.maxChan] - m.SNRSortedIndicesRecChanInverse = [sortperm(snr) for snr in m.SNRSortedIndicesRecChan] - return -end diff --git a/src/Viewer/SimpleDataViewer.jl b/src/Viewer/SimpleDataViewer.jl deleted file mode 100644 index 1c0da76..0000000 --- a/src/Viewer/SimpleDataViewer.jl +++ /dev/null @@ -1,93 +0,0 @@ -using Gtk4, Cairo - -export SimpleDataViewer, SimpleDataViewerWidget, simpleDrawImageCairo - -function SimpleDataViewer() - w = Window("Data Viewer",1024,768) - dw = SimpleDataViewerWidget() - push!(w,dw) - show(w) - return dw, w -end - -########### SimpleDataViewerWidget ################# - - -mutable struct SimpleDataViewerWidget <: Gtk4.GtkBox - handle::Ptr{Gtk4.GObject} - builder - grid3D - grid2D -end - -getindex(m::SimpleDataViewerWidget, w::AbstractString) = Gtk4.G_.get_object(m.builder, w) - - -function SimpleDataViewerWidget() - uifile = joinpath(@__DIR__,"..","builder","simpleDataViewer.ui") - b = GtkBuilder(uifile) - mainBox = Gtk4.G_.get_object(b, "boxSimpleDataViewer") - m = SimpleDataViewerWidget( mainBox.handle, b, nothing, nothing) - Gtk4.GLib.gobject_move_ref(m, mainBox) - - m.grid3D = m["gridDataViewer3D"] - m.grid2D = m["gridDataViewer2D"] - - m.grid3D[2,1] = GtkCanvas() - m.grid3D[1,1] = GtkCanvas() - m.grid3D[2,2] = GtkCanvas() - m.grid3D[1,2] = GtkCanvas() - m.grid2D[1,1] = GtkCanvas() - - return m -end - -function showData(m::SimpleDataViewerWidget, cdata_zy, cdata_zx, cdata_xy, drawSectionalLines, slices) - try - simpleDrawImageCairo(m.grid3D[2,1], cdata_zy, drawSectionalLines, - slices[2], slices[3], false, true) - simpleDrawImageCairo(m.grid3D[1,1], cdata_zx, drawSectionalLines, - slices[1], slices[3], true, true) - simpleDrawImageCairo(m.grid3D[2,2], cdata_xy, drawSectionalLines, - slices[2], slices[1], false, false) - - Gtk4.G_.set_current_page(m["nb2D3D"], 0) - catch ex - @warn "Exception" ex stacktrace(catch_backtrace()) - end -end - -function showData(m::SimpleDataViewerWidget, cdata) - try - pZ = drawImage( convert(Array,cdata.data) ) - display(m.grid2D[1,1],pZ) - Gtk4.G_.set_current_page(m["nb2D3D"], 1) - catch ex - @warn "Exception" ex stacktrace(catch_backtrace()) - end -end - -function simpleDrawImageCairo(c, image, drawSectionalLines, xsec, ysec, - flipX, flipY) - @guarded Gtk4.draw(c) do widget - ctx = getgc(c) - h = height(ctx) - w = width(ctx) - - im = reverse(convert(ImageMeta{RGB{N0f8}},image).data,dims=1) - xsec_ = !flipX ? xsec : (size(im,2)-xsec+1) - ysec_ = !flipY ? ysec : (size(im,1)-ysec+1) - xx = w*(xsec_-0.5)/size(im,2) - yy = h*(ysec_-0.5)/size(im,1) - copy!(ctx,im) - if drawSectionalLines - set_source_rgb(ctx, 0, 1, 0) - move_to(ctx, xx, 0) - line_to(ctx, xx, h) - move_to(ctx, 0, yy) - line_to(ctx, w, yy) - set_line_width(ctx, 3.0) - Cairo.stroke(ctx) - end # end if - end # guard -end # end function diff --git a/src/Viewer/SpectrogramViewer.jl b/src/Viewer/SpectrogramViewer.jl deleted file mode 100644 index bf3968e..0000000 --- a/src/Viewer/SpectrogramViewer.jl +++ /dev/null @@ -1,603 +0,0 @@ -import Base: getindex - -export SpectrogramViewer - -mutable struct SpectrogramWidget <: Gtk4.GtkBox - handle::Ptr{Gtk4.GObject} - builder::GtkBuilder - data::Array{Float32,5} - dataBG::Array{Float32,5} - labels::Vector{String} - cTD::GtkCanvas - cFD::GtkCanvas - cSpect::GtkCanvas - deltaT::Float64 - filenamesData::Vector{String} - updatingData::Bool - fileModus::Bool - rangeTD::NTuple{2,Float32} - rangeFD::NTuple{2,Float32} -end - -getindex(m::SpectrogramWidget, w::AbstractString) = Gtk4.G_.get_object(m.builder, w) - -mutable struct SpectrogramViewer - w::Gtk4.GtkWindowLeaf - sw::SpectrogramWidget -end - -function SpectrogramViewer(filename::AbstractString) - sw = SpectrogramWidget() - w = GtkWindow("Spectrogram Viewer: $(filename)",800,600) - push!(w, sw) - show(w) - updateData(sw, filename) - return SpectrogramViewer(w, sw) -end - -function SpectrogramWidget(filenameConfig=nothing) - @info "Starting SpectrogramWidget" - uifile = joinpath(@__DIR__,"..","builder","spectrogramViewer.ui") - - b = GtkBuilder(uifile) - mainBox = Gtk4.G_.get_object(b, "boxSpectrogramViewer") - - m = SpectrogramWidget( mainBox.handle, b, - zeros(Float32,0,0,0,0,0), zeros(Float32,0,0,0,0,0), - [""], GtkCanvas(), GtkCanvas(), GtkCanvas(), - 1.0, [""], false, false, - (0.0,1.0), (0.0,1.0)) - Gtk4.GLib.gobject_move_ref(m, mainBox) - - @debug "Type constructed" - - push!(m["boxTD"],m.cTD) - m.cTD.hexpand = true - m.cTD.vexpand = true - - pane = m["paned"] - set_gtk_property!(pane, :position, 300) - - push!(m["boxSpectro"], m.cSpect) - m.cSpect.hexpand = true - m.cSpect.vexpand = true - - push!(m["boxFD"],m.cFD) - m.cFD.hexpand = true - m.cFD.vexpand = true - - @debug "InitCallbacks" - - initCallbacks(m) - - @info "Finished starting SpectrogramWidget" - - return m -end - -function initCallbacks(m_::SpectrogramWidget) - let m=m_ - for sl in ["adjPatch","adjRxChan", "adjLogPlot"] - signal_connect(m[sl], "value_changed") do w - showData(C_NULL, m) - end - #signal_connect(showData, m[sl], "value_changed", Nothing, (), false, m ) - end - - signal_connect(m["adjMinTP"], "value_changed") do w - if !m.updatingData - minTP = get_gtk_property(m["adjMinTP"],:value, Int) - maxTP = get_gtk_property(m["adjMaxTP"],:value, Int) - maxValTP = get_gtk_property(m["adjMaxTP"],:upper, Int) - - if minTP > maxTP - @idle_add_guarded set_gtk_property!(m["adjMaxTP"],:value, min(maxValTP,minTP+10)) - else - showData(C_NULL, m) - end - end - end - - signal_connect(m["adjMaxTP"], "value_changed") do w - if !m.updatingData - minTP = get_gtk_property(m["adjMinTP"],:value, Int) - maxTP = get_gtk_property(m["adjMaxTP"],:value, Int) - - if minTP > maxTP - @idle_add_guarded set_gtk_property!(m["adjMinTP"],:value, max(1,maxTP-10)) - else - showData(C_NULL, m) - end - end - end - - signal_connect(m["adjMinFre"], "value_changed") do w - if !m.updatingData - minFre = get_gtk_property(m["adjMinFre"],:value, Int) - maxFre = get_gtk_property(m["adjMaxFre"],:value, Int) - maxValFre = get_gtk_property(m["adjMaxFre"],:upper, Int) - - if minFre > maxFre - @idle_add_guarded set_gtk_property!(m["adjMaxFre"],:value, min(maxValFre,minFre+10)) - else - showData(C_NULL, m) - end - end - end - - signal_connect(m["adjMaxFre"], "value_changed") do w - if !m.updatingData - minFre = get_gtk_property(m["adjMinFre"],:value, Int) - maxFre = get_gtk_property(m["adjMaxFre"],:value, Int) - - if minFre > maxFre - @idle_add_guarded set_gtk_property!(m["adjMinFre"],:value, max(1,maxFre-10)) - else - showData(C_NULL, m) - end - end - end - - @guarded function groupingChanged() - if !m.updatingData - @idle_add_guarded begin - m.updatingData = true - timedata, sp = getData(m) - - maxValTP = length(timedata[1]) - maxValFre = size(sp.power,1) - numPatches = size(sp.power,2) - - set_gtk_property!(m["adjMinTP"],:upper,maxValTP) - set_gtk_property!(m["adjMinTP"],:value,1) - set_gtk_property!(m["adjMaxTP"],:upper,maxValTP) - set_gtk_property!(m["adjMaxTP"],:value,maxValTP) - - set_gtk_property!(m["adjMinFre"],:upper,maxValFre) - set_gtk_property!(m["adjMinFre"],:value,1) - set_gtk_property!(m["adjMaxFre"],:upper,maxValFre) - set_gtk_property!(m["adjMaxFre"],:value,maxValFre) - - set_gtk_property!(m["adjPatch"],:upper,numPatches) - if get_gtk_property(m["adjPatch"],:value, Int) >= numPatches - set_gtk_property!(m["adjPatch"],:value, 1) - end - - m.updatingData = false - showData(C_NULL, m) - end - end - end - - signal_connect(m["adjGrouping"], "value_changed") do w - groupingChanged() - end - - oldAdjPatchAvValue = 1 - signal_connect(m["adjPatchAv"], "value_changed") do w - if !m.updatingData - m.updatingData = true - patchAv = max(get_gtk_property(m["adjPatchAv"], :value, Int64),1) - numPatches = size(m.data,3) - if mod(numPatches, patchAv) != 0 - if 1 < patchAv < numPatches - while mod(numPatches, patchAv) != 0 - patchAv += sign(patchAv-oldAdjPatchAvValue)*1 - end - elseif patchAv < 1 - patchAv = 1 - elseif patchAv > numPatches - patchAv = numPatches - end - oldAdjPatchAvValue = patchAv - - @idle_add_guarded begin - set_gtk_property!(m["adjPatchAv"], :value, patchAv) - groupingChanged() - end - else - @idle_add_guarded groupingChanged() - end - oldAdjPatchAvValue = patchAv - m.updatingData = false - end - end - - for cb in ["cbShowBG", "cbSubtractBG", "cbShowFreq"] - signal_connect(m[cb], :toggled) do w - showData(C_NULL, m) - end - #signal_connect(showData, m[cb], "toggled", Nothing, (), false, m) - end - - - for cb in ["cbShowAllFrames", "cbShowFixDistortions"] #"cbCorrTF","cbSLCorr","cbAbsFrameAverage" - signal_connect(m[cb], :toggled) do w - loadData(C_NULL, m) - end - end - - for cb in ["adjFrame"] - signal_connect(m[cb], "value_changed") do w - loadData(C_NULL, m) - end - end - - for sl in ["entTDMinVal","entTDMaxVal","entFDMinVal","entFDMaxVal"] - signal_connect(m[sl], "changed") do w - showData(C_NULL, m) - end - end - - - for sl in ["TD", "FD"] - signal_connect(m["btn$(sl)Apply"], "clicked") do w - if !m.updatingData - @idle_add_guarded begin - m.updatingData = true - r = (sl == "TD") ? m.rangeTD : m.rangeFD - set_gtk_property!( m["ent$(sl)MinVal"] ,:text, string(r[1])) - set_gtk_property!( m["ent$(sl)MaxVal"] ,:text, string(r[2])) - m.updatingData = false - showData(C_NULL, m) - end - end - end - - signal_connect(m["btn$(sl)Clear"], "clicked") do w - if !m.updatingData - @idle_add_guarded begin - m.updatingData = true - set_gtk_property!( m["ent$(sl)MinVal"] ,:text, "") - set_gtk_property!( m["ent$(sl)MaxVal"] ,:text, "") - m.updatingData = false - showData(C_NULL, m) - end - end - end - end - - #signal_connect(loadData, m["cbCorrTF"], "toggled", Nothing, (), false, m) - end -end - -@guarded function loadData(widgetptr::Ptr, m::SpectrogramWidget) - if !m.updatingData - m.updatingData = true - @info "Loading Data ..." - deltaT = 1.0 - - if m.filenamesData != [""] && all(ispath.(m.filenamesData)) - fs = MPIFile(m.filenamesData) #, isCalib=false) - - # TODO: Ensure that the measurements fit together (num samples / patches) - # otherwise -> error - - numFGFrames = minimum(acqNumFGFrames.(fs)) - numBGFrames = minimum(acqNumBGFrames.(fs)) - - dataFGVec = Any[] - dataBGVec = Any[] - - for (i,f) in enumerate(fs) - params = MPIFiles.loadMetadata(f) - params[:acqNumFGFrames] = acqNumFGFrames(f) - params[:acqNumBGFrames] = acqNumBGFrames(f) - - @idle_add_guarded set_gtk_property!(m["adjFrame"], :upper, numFGFrames) - - if get_gtk_property(m["cbShowAllFrames"], :active, Bool) - frame = 1:numFGFrames - else - frame = max( get_gtk_property(m["adjFrame"], :value, Int64), 1) - end - - timePoints = rxTimePoints(f) - deltaT = timePoints[2] - timePoints[1] - - data = getMeasurements(f, true, frames=frame, - bgCorrection=false, spectralLeakageCorrection = get_gtk_property(m["cbSLCorr"], :active, Bool), - tfCorrection=get_gtk_property(m["cbCorrTF"], :active, Bool), - fixDistortions=m["cbShowFixDistortions"].active) - push!(dataFGVec, data) - - if acqNumBGFrames(f) > 0 - dataBG = getMeasurements(f, false, frames=measBGFrameIdx(f), - bgCorrection=false, spectralLeakageCorrection = get_gtk_property(m["cbSLCorr"], :active, Bool), - tfCorrection=get_gtk_property(m["cbCorrTF"], :active, Bool)) - else - dataBG = zeros(Float32,0,0,0,0) - end - push!(dataBGVec, dataBG) - end - - - m.dataBG = cat(dataBGVec..., dims=5) - dataFG = cat(dataFGVec..., dims=5) - m.labels = ["expnum "*string(experimentNumber(f)) for f in fs] - - updateData(m, dataFG, deltaT, true) - end - m.updatingData = false - end - return nothing -end - -@guarded function getData(m::SpectrogramWidget) - chan = max(get_gtk_property(m["adjRxChan"], :value, Int64),1) - group = get_gtk_property(m["adjGrouping"],:value,Int64) - patchAv = max(get_gtk_property(m["adjPatchAv"], :value, Int64),1) - numPatches = div(size(m.data,3), patchAv) - showBG = get_gtk_property(m["cbShowBG"], :active, Bool) - allFrames = get_gtk_property(m["cbShowAllFrames"], :active, Bool) - - data_ = mean( reshape(m.data[:,chan,:,:,:], - size(m.data,1), patchAv, numPatches, size(m.data,4), : ), dims=(2,)) - if length(m.dataBG) > 0 - dataBG_ = mean( reshape(m.dataBG[:,chan,:,:,:], - size(m.dataBG,1), patchAv, numPatches, size(m.dataBG,4), : ), dims=(2,4)) - - if get_gtk_property(m["cbSubtractBG"], :active, Bool) - data_ .-= dataBG_ - end - if showBG - data_ .= dataBG_ - end - dataBG_ = vec(dataBG_) - end - - data_ = vec(data_) - - timedata = arraysplit(data_, size(m.data,1)*group, div(size(m.data,1)*group,2)) - sp = DSP.spectrogram(vec(data_), size(m.data,1)*group) - - return timedata, sp -end - -################ - -function setBG(m::SpectrogramWidget, dataBG) - if ndims(dataBG) == 5 - m.dataBG = dataBG - else - m.dataBG = reshape(dataBG, size(dataBG)..., 1) - end -end - -@guarded function updateData(m::SpectrogramWidget, data::Array, deltaT=1.0, fileModus=false) - maxValTPOld = get_gtk_property(m["adjMinTP"],:upper, Int64) - maxValFreOld = get_gtk_property(m["adjMinFre"],:upper, Int64) - allFrames = get_gtk_property(m["cbShowAllFrames"], :active, Bool) - - if ndims(data) == 5 - m.data = data - else - m.data = reshape(data, size(data)..., 1) - end - m.deltaT = deltaT .* 1000 # convert to ms and kHz - m.fileModus = fileModus - - if !isempty(m.dataBG) - if size(m.data)[1:3] != size(m.dataBG)[1:3] - @info "Background data does not fit to foreground data! Dropping BG data." - @info size(m.data) - @info size(m.dataBG) - m.dataBG = zeros(Float32,0,0,0,0,0) - end - end - - timedata, sp = getData(m) - maxValTP = length(timedata[1]) - maxValFre = size(sp.power,1) - numPatches = size(sp.power,2) - maxGrouping = allFrames ? size(m.data,3)*size(m.data,4) : size(m.data,3) - - @idle_add_guarded begin - m.updatingData = true - set_gtk_property!(m["adjGrouping"],:upper,maxGrouping) - if !(1 <= get_gtk_property(m["adjGrouping"],:value,Int64) <= maxGrouping) - set_gtk_property!(m["adjGrouping"],:value, 1) - end - if !fileModus - set_gtk_property!(m["adjFrame"],:upper,size(m.data,4)) - if !(1 <= get_gtk_property(m["adjFrame"],:value,Int64) <= size(m.data,4)) - set_gtk_property!(m["adjFrame"],:value,1) - end - end - set_gtk_property!(m["adjRxChan"],:upper,size(m.data,2)) - if !(1 <= get_gtk_property(m["adjRxChan"],:value,Int64) <= size(m.data,2)) - set_gtk_property!(m["adjRxChan"],:value,1) - end - set_gtk_property!(m["adjPatch"],:upper,numPatches) - if !(1 <= get_gtk_property(m["adjPatch"],:value,Int64) <= numPatches) - set_gtk_property!(m["adjPatch"],:value,1) - end - set_gtk_property!(m["adjMinTP"],:upper,maxValTP) - if !(1 <= get_gtk_property(m["adjMinTP"],:value,Int64) <= maxValTP) || maxValTP != maxValTPOld - set_gtk_property!(m["adjMinTP"],:value,1) - end - set_gtk_property!(m["adjMaxTP"],:upper, maxValTP) - if !(1 <= get_gtk_property(m["adjMaxTP"],:value,Int64) <= maxValTP) || maxValTP != maxValTPOld - set_gtk_property!(m["adjMaxTP"],:value, maxValTP) - end - set_gtk_property!(m["adjMinFre"],:upper, maxValFre) - if !(1 <= get_gtk_property(m["adjMinFre"],:value,Int64) <= maxValFre) || maxValFre != maxValFreOld - set_gtk_property!(m["adjMinFre"],:value,1) - end - set_gtk_property!(m["adjMaxFre"],:upper, maxValFre) - if !(1 <= get_gtk_property(m["adjMaxFre"],:value,Int64) <= maxValFre) || maxValFre != maxValFreOld - set_gtk_property!(m["adjMaxFre"],:value, maxValFre) - end - - m.updatingData = false - showData(C_NULL, m) - end -end - -@guarded function updateData(m::SpectrogramWidget, filenames::Vector{<:AbstractString}) - m.filenamesData = filenames - @idle_add_guarded begin - m.updatingData = true - set_gtk_property!(m["adjFrame"],:upper,1) - set_gtk_property!(m["adjFrame"],:value,1) - set_gtk_property!(m["adjPatch"],:upper,1) - set_gtk_property!(m["adjPatch"],:value,1) - m.updatingData = false - loadData(C_NULL, m) - end - return nothing -end - -@guarded function updateData(m::SpectrogramWidget, filename::String) - updateData(m, [filename]) - return nothing -end - -################ - -@guarded function showData(widgetptr::Ptr, m::SpectrogramWidget) - if length(m.data) > 0 && !m.updatingData - chan = max(get_gtk_property(m["adjRxChan"], :value, Int64),1) - patch = max(get_gtk_property(m["adjPatch"], :value, Int64),1) - minTP = max(get_gtk_property(m["adjMinTP"], :value, Int64),1) - maxTP = max(get_gtk_property(m["adjMaxTP"], :value, Int64),1) - minFr = max(get_gtk_property(m["adjMinFre"], :value, Int64),1) - maxFr = max(get_gtk_property(m["adjMaxFre"], :value, Int64),1) - group = get_gtk_property(m["adjGrouping"],:value,Int64) - logVal = get_gtk_property(m["adjLogPlot"],:value, Float64) - numSignals = size(m.data,5) - showFD = get_gtk_property(m["cbShowFreq"], :active, Bool) - numShownFrames = size(m.data,4) - - autoRangingTD = true - autoRangingFD = true - minValTD_ = tryparse(Float64,get_gtk_property( m["entTDMinVal"] ,:text,String)) - maxValTD_ = tryparse(Float64,get_gtk_property( m["entTDMaxVal"] ,:text,String)) - minValFD_ = tryparse(Float64,get_gtk_property( m["entFDMinVal"] ,:text,String)) - maxValFD_ = tryparse(Float64,get_gtk_property( m["entFDMaxVal"] ,:text,String)) - - if minValTD_ != nothing && maxValTD_ != nothing - minValTD = minValTD_ - maxValTD = maxValTD_ - autoRangingTD = false - end - - if minValFD_ != nothing && maxValFD_ != nothing - minValFD = minValFD_ - maxValFD = maxValFD_ - autoRangingFD = false - end - - timedata, sp = getData(m) - data_ = timedata[patch] - m.rangeTD = extrema(data_) - - maxFr = min(maxFr,size(sp.power,1)) - maxTP = min(maxTP,size(data_,1)) - minFr = min(minFr,size(sp.power,1)) - minTP = min(minTP,size(data_,1)) - - spdata = sp.power[minFr:maxFr,:] - maxT = m.deltaT*size(m.data,1)*size(m.data,3)*numShownFrames/1000 - - if size(spdata,1) > 2^15 # Magic number of Winston image display - sliceFr = 1:ceil(Int,size(spdata,1)/2^15):size(spdata,1) - else - sliceFr = Colon() - end - - if size(spdata,2) > 2^15 # Magic number of Winston image display - sliceTime = 1:ceil(Int,size(spdata,2)/2^15):size(spdata,2) - else - sliceTime = Colon() - end - patch_ = patch/size(sp.power,2) * maxT - - fSp, axSp, pSp = CairoMakie.heatmap( range(0.0, step=maxT/size(spdata[sliceFr, sliceTime],2), - length=size(spdata[sliceFr, sliceTime],2)), - range( (minFr-1)/size(sp.power,1) / m.deltaT / 2.0, - step = (maxFr-1)/size(sp.power,1) / m.deltaT / 2.0 / size(spdata[sliceFr, sliceTime],1), - length = size(spdata[sliceFr, sliceTime],1)), - log.(10.0^(-(2+10*logVal)) .+ transpose(spdata[sliceFr, sliceTime]) ); - figure = (; figure_padding=4, fontsize = 10), - ) - - CairoMakie.lines!(axSp, [patch_,patch_], [0,(maxFr-1)/size(sp.power,1) / m.deltaT / 2.0], - color=:white, linewidth=2 ) - - axSp.ylabel = "freq / kHz" - axSp.xlabel = "t / s" - axSp.yreversed = true - - @idle_add_guarded drawonto(m.cSpect, fSp) - - timePoints = (0:(length(data_)-1)).*m.deltaT - - maxPoints = 5000 - sp_ = length(minTP:maxTP) > maxPoints ? round(Int,length(minTP:maxTP) / maxPoints) : 1 - steps = minTP:sp_:maxTP - - fTD, axTD, lTD1 = CairoMakie.lines(timePoints[steps], data_[steps], - figure = (; figure_padding=4, size = (1000, 800), fontsize = 10), - axis = (; title = "Time Domain"), - color = CairoMakie.RGBf(colors[1]...)) - - CairoMakie.autolimits!(axTD) - if timePoints[steps[end]] > timePoints[steps[1]] - CairoMakie.xlims!(axTD, timePoints[steps[1]], timePoints[steps[end]]) - end - if !autoRangingTD && maxValTD > minValTD - CairoMakie.ylims!(axTD, minValTD, maxValTD) - end - axTD.xlabel = "t / ms" - axTD.ylabel = "u / V" - - - if showFD - numFreq = size(sp.power,1) - freq = collect(0:(numFreq-1))./(numFreq-1)./m.deltaT./2.0 - freqdata = sp.power[:,:] - m.rangeFD = extrema(freqdata[:,patch]) - spFr = length(minFr:maxFr) > maxPoints ? round(Int,length(minFr:maxFr) / maxPoints) : 1 - - stepsFr = minFr:spFr:maxFr - - fFD, axFD, lFD1 = CairoMakie.lines(freq[stepsFr],freqdata[stepsFr,patch], - figure = (; figure_padding=4, size = (1000, 800), fontsize = 10), - axis = (; title = "Frequency Domain", yscale=log10), - color = CairoMakie.RGBf(colors[1]...)) - - CairoMakie.autolimits!(axFD) - if freq[stepsFr[end]] > freq[stepsFr[1]] - CairoMakie.xlims!(axFD, freq[stepsFr[1]], freq[stepsFr[end]]) - end - if !autoRangingFD && maxValFD > minValFD - CairoMakie.ylims!(axFD, minValFD, maxValFD) - end - axFD.xlabel = "f / kHz" - axFD.ylabel = "u / V" - - else - @guarded Gtk4.draw(m.cFD) do widget - - ctx = getgc(m.cFD) - h = height(ctx) - w = width(ctx) - Cairo.set_source_rgb(ctx,1.0,1.0,1.0) - Cairo.rectangle(ctx, 0,0,w,h) - Cairo.paint(ctx) - Cairo.stroke(ctx) - end - end - - - @idle_add_guarded drawonto(m.cTD, fTD) - if showFD - @idle_add_guarded drawonto(m.cFD, fFD) - end - - end - return nothing -end - - diff --git a/src/Viewer/Viewer.jl b/src/Viewer/Viewer.jl deleted file mode 100644 index 8a51903..0000000 --- a/src/Viewer/Viewer.jl +++ /dev/null @@ -1,8 +0,0 @@ -include("BaseViewer.jl") -include("SimpleDataViewer.jl") -include("DataViewer/DataViewer.jl") -include("RawDataViewer.jl") -include("SpectrogramViewer.jl") -include("SFViewerWidget.jl") -include("MagneticFieldViewer/MagneticFieldViewer.jl") -include("3DViewer/3DViewer.jl") diff --git a/src/builder/baseViewer.ui b/src/builder/baseViewer.ui deleted file mode 100644 index c1fd800..0000000 --- a/src/builder/baseViewer.ui +++ /dev/null @@ -1,130 +0,0 @@ - - - - - 1 - 1 - - - 1 - 1 - 1 - 1 - - - - - 1 - 0 - - - - - - z-Axis - - 0 - 0 - - - - - - - x-Axis + - - 1 - 1 - - - - - - 1 - 1 - 1 - 1 - - - - - 3 - 0 - - - - - - + -z-Axis - - - - 2 - 0 - - - - - - - y-Axis + - - 3 - 1 - - - - - - 1 - 1 - 1 - 1 - - - - - 3 - 2 - - - - - - x Axis - - 2 - 2 - - - - - - y-Axis - - 3 - 3 - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/builder/dataviewer.ui b/src/builder/dataviewer.ui deleted file mode 100644 index ba8f6e1..0000000 --- a/src/builder/dataviewer.ui +++ /dev/null @@ -1,1322 +0,0 @@ - - - - - 1 - 1 - 0.050000000000000003 - 0.050000000000000003 - - - 1 - 1 - 1 - 10 - - - 1 - 0.050000000000000003 - 0.050000000000000003 - - - 1 - 1 - 10 - - - 1 - 100 - 1 - 1 - 10 - - - 1 - 1000000 - 1 - 1 - 10 - - - 1 - 100 - 1 - 1 - 10 - - - 100000000 - 1 - 10 - - - 100000000 - 0.01 - 10 - - - 1 - 1000000 - 1 - 1 - 10 - - - 1 - 100 - 1 - 1 - 10 - - - 1250 - 1250 - 10 - 10 - - - 1250 - 80 - 10 - 10 - - - 100 - 1 - 1 - 10 - - - 1 - 100 - 5 - 1 - 10 - - - - - 5 - 5 - - - Export as PNG - 1 - 1 - - 0 - 1 - - - - - - Save Params - 1 - 1 - - 1 - 0 - - - - - - Exp. Nifti C. Fr. - 1 - 1 - - 1 - 1 - - - - - - Export Movie - 1 - 1 - - 1 - 2 - - - - - - 1 - 3 - - 0 - 0 - - - - - - Export Profile - 1 - 1 - - 0 - 2 - - - - - - Exp. Nifti All Fr. - 1 - 1 - - 1 - 3 - - - - - - Exp. Real All Fr. - 1 - 1 - - 0 - 3 - - - - - - Pixel Resize Factor - - 0 - 5 - - - - - - 1 - 5 - adjPixelResizeFactor - 5 - - 1 - 5 - - - - - - Export as Tikz - 1 - 1 - - 0 - 4 - - - - - - - - - - - 1 - 0.01 - 0.01 - 10 - - - -3.1499999999999999 - 3.1499999999999999 - 0.01 - 10 - - - -3.1499999999999999 - 3.1499999999999999 - 0.01 - 10 - - - -3.1499999999999999 - 3.1499999999999999 - 0.01 - 10 - - - -3.1499999999999999 - 3.1499999999999999 - 0.01 - 10 - - - -3.1499999999999999 - 3.1499999999999999 - 0.01 - 10 - - - -3.1499999999999999 - 3.1499999999999999 - 0.01 - 10 - - - 100 - 1 - 1 - 10 - - - 100 - 1 - 10 - - - 100 - 1 - 10 - - - 100 - 1 - 10 - - - 1 - 3 - 1 - 1 - 10 - - - 1 - 100 - 1 - 10 - - - 1 - 100 - 1 - 1 - 10 - - - 1 - 100 - 1 - 1 - 10 - - - 1 - 100 - 1 - 1 - 10 - - - - - 2 - 2 - - - 1 - 2 - 1 - adjSliceZ - 1 - - 2 - 1 - - - - - - 1 - 0 - 2 - 1 - adjSliceX - 1 - - 0 - 1 - - - - - - 1 - 2 - 1 - adjSliceY - 1 - - 1 - 1 - - - - - - x - - 0 - 0 - - - - - - y - - 1 - 0 - - - - - - z - - 2 - 0 - - - - - - Show Slices - 1 - 1 - - 0 - 2 - - - - - - SpatialMIP - 1 - - 1 - 2 - - - - - - - - - - - 1 - 0.40000000000000002 - 1 - 10 - - - - - 5 - 5 - - - Frame Projection - - 0 - 0 - - - - - - 0 - - 1 - 0 - - - - - - Play Movie - 1 - 1 - - 0 - 1 - - - - - - 1 - 1 - adjTTPThresh - 1 - - 1 - 1 - - - - - - Profile - - 0 - 2 - - - - - - 1 - - 1 - 2 - - - - - - - - 100 - 1 - 10 - - - 100 - 1 - 10 - - - 100 - 1 - 10 - - - 100 - 0.10000000000000001 - 10 - - - 100 - 0.10000000000000001 - 10 - - - 100 - 0.10000000000000001 - 10 - - - 500 - - - 5 - 5 - - - Permutation - - 0 - 0 - - - - - - Spatial BG MIP - 1 - - 0 - 15 - - - - - - Transp. Blend - 1 - - 0 - 14 - - - - - - Hide BG Data - 1 - - 0 - 13 - - - - - - Hide MPI Data - 1 - - 1 - 13 - - - - - - cmax - - 0 - 12 - - - - - - 1 - 1 - adjCMaxBG - 1 - - 1 - 12 - - - - - - 1 - 1 - adjCMinBG - 1 - - 1 - 11 - - - - - - cmin - - 0 - 11 - - - - - - Colormap - - 0 - 10 - - - - - - 0 - - 1 - 10 - - - - - - 1 - 0,000 - adjRotBGZ - 3 - - 0 - 9 - - - - - - 1 - 0,000 - adjRotZ - 3 - - 1 - 9 - - - - - - 1 - 0,000 - adjRotY - 3 - - 1 - 8 - - - - - - 1 - 0,000 - adjRotBGY - 3 - - 0 - 8 - - - - - - 1 - 0,000 - adjRotBGX - 3 - - 0 - 7 - - - - - - Rotation - - 0 - 6 - - - - - - 1 - 0,000 - adjRotX - 3 - - 1 - 7 - - - - - - Rotation FG - - 1 - 6 - - - - - - 1 - 0,0 - adjTransZ - 1 - - 1 - 5 - - - - - - 1 - 0,0 - adjTransBGZ - 1 - - 0 - 5 - - - - - - 1 - 0,0 - adjTransBGY - 1 - - 0 - 4 - - - - - - 1 - 0,0 - adjTransBGX - 1 - - 0 - 3 - - - - - - 1 - 0,0 - adjTransY - 1 - - 1 - 4 - - - - - - 1 - 0,0 - adjTransX - 1 - - 1 - 3 - - - - - - Translation - - 0 - 2 - - - - - - Translation FG - - 1 - 2 - - - - - - - 1 - 1 - - - - - - - 1 - 0 - - - - - - Flipping - - 0 - 1 - - - - - - Show FOV - 1 - - 1 - 14 - - - - - - - - - - - - - - - - - - - - - Blend - 1 - - 0 - 0 - 2 - - - - - - Complex Blending - 1 - - 0 - 1 - 2 - - - - - - Show Axes - 1 - - 0 - 2 - 2 - - - - - - - - - - - - - - 1 - 1 - vertical - - - - - 3 - 3 - 3 - 3 - 1 - 3 - - - 1 - 2 - 1 - adjFrames - 1 - - 0 - 1 - - - - - - Frames - - 0 - 0 - 2 - - - - - - 0 - - 6 - 1 - - - - - - Colormap - - 6 - 0 - - - - - - 1 - 1 - popFrames - - - - - 1 - 1 - - - - - - 1 - 1 - popSlices - edit-copy - - 3 - 1 - - - - - - Slices - - 3 - 0 - - - - - - - 2 - 0 - 2 - - - - - - Chan - - 5 - 0 - - - - - - - 5 - 1 - - - - - - - 4 - 0 - 2 - - - - - - - 10 - 0 - 2 - - - - - - 1 - 0 - 1 - adjCMin - audio-volume-muted-symbolic -audio-volume-high-symbolic -audio-volume-low-symbolic -audio-volume-medium-symbolic - - - 1 - 1 - 1 - center - center - - - - - 1 - 1 - 1 - center - center - - - - 7 - 1 - - - - - - 1 - 0 - 1 - adjCMax - audio-volume-muted-symbolic -audio-volume-high-symbolic -audio-volume-low-symbolic -audio-volume-medium-symbolic - - - 1 - 1 - 1 - center - center - - - - - 1 - 1 - 1 - center - center - - - - 8 - 1 - - - - - - cmin - - 7 - 0 - - - - - - cmax - - 8 - 0 - - - - - - Export - - 11 - 0 - - - - - - 1 - 1 - popExport - document-save - - 11 - 1 - - - - - - - 12 - 0 - 2 - - - - - - Fusion - - 13 - 0 - - - - - - 1 - 1 - popFusion - - 13 - 1 - - - - - - - 14 - 0 - 2 - - - - - - More - - 15 - 0 - - - - - - 1 - 1 - popOptions - preferences-desktop - - 15 - 1 - - - - - - - - - - - - - - - - 1 - - - 1 - 0 - 0 - - - - - 4 - 4 - 1 - 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - page 1 - - - - - - - 1 - - - 1 - 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - page 2 - - - - - - - - - - diff --git a/src/builder/magneticFieldViewer.ui b/src/builder/magneticFieldViewer.ui deleted file mode 100644 index 19afbb6..0000000 --- a/src/builder/magneticFieldViewer.ui +++ /dev/null @@ -1,1936 +0,0 @@ - - - - - 20 - 5 - 0.5 - 10 - - - 1 - 30 - 10 - 2 - 2 - - - 1 - 1 - 0.005000000000000003 - 0.005000000000000003 - - - 1 - 0.005000000000000003 - 0.005000000000000003 - - - 5 - 500 - 25 - 5 - 10 - - - 1 - 1 - 10 - - - 1 - 100 - 1 - 1 - 10 - - - 1 - 100 - 1 - 1 - 10 - - - 1 - 100 - 1 - 1 - 10 - - - 1 - 100 - 5 - 1 - 10 - - - 1 - 0.01 - 0.01 - 10 - - - -3.1499999999999999 - 3.1499999999999999 - 0.01 - 10 - - - -3.1499999999999999 - 3.1499999999999999 - 0.01 - 10 - - - -3.1499999999999999 - 3.1499999999999999 - 0.01 - 10 - - - -3.1499999999999999 - 3.1499999999999999 - 0.01 - 10 - - - 100 - 1 - 1 - 10 - - - 100 - 1 - 10 - - - 100 - 1 - 10 - - - 100 - 1 - 10 - - - 1 - 3 - 1 - 1 - 10 - - - 1 - 100 - 1 - 10 - - - -1 - 1 - 1 - 10 - - - -1 - 1 - 1 - 10 - - - -1 - 1 - 1 - 10 - - - - - 2 - 2 - - - 1 - 2 - 1 - adjSliceZ - 1 - - 2 - 1 - - - - - - 1 - 0 - 2 - 1 - adjSliceX - 1 - - 0 - 1 - - - - - - 1 - 2 - 1 - adjSliceY - 1 - - 1 - 1 - - - - - - x - - 0 - 0 - - - - - - y - - 1 - 0 - - - - - - z - - 2 - 0 - - - - - - Show Slices - 1 - 1 - - 0 - 2 - - - - - - Go to FFP - 1 - 1 - - 2 - 2 - - - - - - Go to Zero - 1 - 1 - - 1 - 2 - - - - - - - - - - 5 - 5 - - - Profile settings - - 0 - 0 - - - - - - Field - - 0 - 1 - - - - - - 0 - - 1 - 1 - - - - - - Axes - - 0 - 2 - - - - - - 1 - - 1 - 2 - - - - - - - - 100 - 1 - 10 - - - 100 - 1 - 10 - - - 100 - 1 - 10 - - - 100 - 0.10000000000000001 - 10 - - - 100 - 0.10000000000000001 - 10 - - - 100 - 0.10000000000000001 - 10 - - - vertical - - - 4 - 4 - 1 - 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1 - - 300 - 300 - - - 3 - 3 - 3 - 3 - - - 3 - 3 - 3 - 3 - 1 - 1 - vertical - - - center - 1 - 10 - - - - - - - - - - - - - - - - - 5 - 5 - - - 2 - - - Fields - 1 - 1 - - 0 - 0 - - - - - - Coefficients - 1 - 1 - - 1 - 0 - - - - - - Profile - 1 - 1 - - 2 - 0 - - - - - 0 - 1 - 2 - - - - - - Export as PNG - 1 - 1 - - 0 - 0 - - - - - - Export as Video - 0 - 1 - 1 - - 1 - 0 - - - - - - - 0 - 2 - 2 - - - - - - Export as Tikz - 1 - 1 - - 0 - 3 - - - - - - Build pdf - 1 - 1 - - 1 - 3 - - - - - - - 0 - 4 - 2 - - - - - - Export as CSV - - 0 - 5 - - - - - - 2 - - - Coefficients - 1 - 1 - - 0 - 0 - - - - - - Magnetic Field - 1 - 1 - - 0 - 1 - - - - - 1 - 5 - - - - - - - - - - - - - - - Show CS - 1 - - 0 - 0 - - - - - - Show Axes - 1 - - 0 - 1 - - - - - - Show Sphere - 1 - - 0 - 2 - - - - - - - - 4 - 4 - 4 - - - 1 - 1 - vertical - - - - - 3 - 80 - 3 - 3 - 1 - 3 - - - 1 - 2 - 1 - adjPatches - 1 - - 0 - 1 - - - - - - Patches - - 0 - 0 - 2 - - - - - - Colormap - - 6 - 0 - - - - - - 1 - 1 - popFrames - - - - - 1 - 1 - - - - - - 1 - 1 - popSlices - edit-copy - - 3 - 1 - - - - - - Slices - - 3 - 0 - - - - - - - 2 - 0 - 2 - - - - - - - 4 - 0 - 2 - - - - - - - 10 - 0 - 2 - - - - - - 1 - 0 - 1 - adjCMin - audio-volume-muted-symbolic -audio-volume-high-symbolic -audio-volume-low-symbolic -audio-volume-medium-symbolic - - - 1 - 1 - 1 - center - center - - - - - 1 - 1 - 1 - center - center - - - - 7 - 1 - - - - - - 1 - 0 - 1 - adjCMax - audio-volume-muted-symbolic -audio-volume-high-symbolic -audio-volume-low-symbolic -audio-volume-medium-symbolic - - - 1 - 1 - 1 - center - center - - - - - 1 - 1 - 1 - center - center - - - - 8 - 1 - - - - - - cmin - - 7 - 0 - - - - - - cmax - - 8 - 0 - - - - - - Export - - 11 - 0 - - - - - - 1 - 1 - popExport - document-save - - 11 - 1 - - - - - - - 12 - 0 - 2 - - - - - - More - - 14 - 0 - - - - - - 1 - 1 - popOptions - preferences-desktop - - 14 - 1 - - - - - - - - - 0 - 0 - - - - - - 1 - 1 - popCMaps - applications-graphics - - - 1 - 0 - - - - - 6 - 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - 0 - 1 - - - 1 - 0 - 0 - - - - - 4 - 4 - 1 - 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - page 1 - - - - - - - - - - - - - - - - 1 - - 220 - - - 3 - 3 - - - 15 - - - 1 - 1 - - - 4 - 4 - 4 - 5 - 5 - - - FOV - - 0 - 0 - - - - - - 1 - 14 - - 1 - 0 - - - - - - Inters. - - 0 - 2 - - - - - - 1 - 14 - - 1 - 2 - - - - - - mm - - 2 - 0 - - - - - - mm - - 2 - 2 - - - - - - 2 - - - Center Sphere - 1 - 1 - - 0 - 0 - - - - - - FFP - 1 - 1 - - 1 - 0 - - - - - 1 - 4 - 2 - - - - - - Center Sphere - - 0 - 5 - - - - - - mm - - 2 - 5 - - - - - - 1 - 0 - 14 - - 1 - 5 - - - - - - FFP - - 0 - 6 - - - - - - 1 - 0 - 14 - - 1 - 6 - - - - - - mm - - 2 - 6 - - - - - - 0 - 5 - - 0 - 7 - 3 - - - - - 0 - 0 - - - - - - Calc FFP - 1 - 1 - - 1 - 0 - - - - - - Reset FFP - 1 - 1 - - 2 - 0 - - - - - - - - Update Plot - 1 - 1 - - 0 - 8 - 3 - - - - - - Center Plot - - 0 - 4 - - - - - - Reset FOV - 1 - 1 - - 1 - 1 - - - - - - Stay in FFP - 1 - - 1 - 3 - - - - - - Reset All - 1 - 1 - - 0 - 9 - 3 - - - - - - - - - - - - - - - - - - - - Magnetic Field - - - - 0 - 0 - - - - - - 1 - 1 - - - 4 - 4 - 4 - 5 - 5 - - - L - - 0 - 0 - - - - - - 1 - 4 - 5 - 51 - adjL - - 1 - 0 - - - - - - scale with radius - 1 - 1 - - 1 - 1 - - - - - - - - Coefficients - - - - 0 - 1 - - - - - - 1 - - - 4 - 5 - 5 - - - |arrow| - - 0 - 1 - - - - - - 1 - 5 - 5 - 0 - adjArrowLength - 1 - 5 - - 1 - 1 - - - - - - fontsize - - 0 - 2 - - - - - - 1 - 5 - 5 - 1 - adjFontsize - 12 - - 1 - 2 - - - - - - Discr. - - 0 - 0 - - - - - - 1 - 4 - 5 - 25 - adjDiscretization - 25 - - 1 - 0 - - - - - - cmin = 0 - 1 - - 1 - 6 - - - - - - show legend - 1 - 1 - - 1 - 3 - - - - - - convert to milli - 1 - 1 - - 1 - 4 - - - - - - Colorbar - - 0 - 5 - - - - - - 2 - 5 - - - 1 - 0 - 8 - - 1 - 0 - - - - - - 1 - 0 - 8 - - 1 - 1 - - - - - - cmin - - 0 - 0 - - - - - - cmax - - 0 - 1 - - - - - - mT - - 2 - 0 - - - - - - mT - - 2 - 1 - - - - - 1 - 5 - - - - - - keep cmin/cmax - 1 - - 1 - 7 - - - - - - write cmin/cmax - 1 - - 1 - 8 - - - - - - - - - - - - - - Plotting - - - - 0 - 3 - - - - - - 1 - - - 4 - 5 - 5 - - - Radius - - 0 - 0 - - - - - - mm - - 2 - 0 - - - - - - 1 - 0 - 14 - 10 - - 1 - 0 - - - - - - Center Sphere - - 0 - 1 - - - - - - mm - - 2 - 1 - - - - - - 1 - 0 - 14 - - 1 - 1 - - - - - - 9 - - - 1 - 0 - 9 - - 1 - 0 - - - - - - x - - 0 - 0 - - - - - - y - - 0 - 1 - - - - - - z - - 0 - 2 - - - - - - 1 - 0 - 12 - - 1 - 1 - - - - - - 1 - 0 - 9 - - 1 - 2 - - - - - 1 - 4 - - - - - - baseline - 8 - 8 - 17 - 1 - - - T/m - - 0 - 0 - - - - - - T/m - - 0 - 1 - - - - - - T/m - - 0 - 2 - - - - - 2 - 4 - - - - - - FFP - - 0 - 2 - - - - - - mm - - 2 - 2 - - - - - - 0 - - 0 - 3 - 2 - - - - - - 1 - 0 - 14 - - 1 - 2 - - - - - - - - - - - - - - - - - - - - Measurement Infos - - - - 0 - 2 - - - - - - 1 - 0 - - - 4 - 4 - 4 - 5 - 5 - - - patch start - - 0 - 0 - - - - - - patch end - - 0 - 1 - - - - - - 1 - 4 - 5 - 51 - adjPatchesVideoLower - - 1 - 0 - - - - - - 1 - 4 - 5 - 51 - adjPatchesVideoUpper - - 1 - 1 - - - - - - pause - - 0 - 2 - - - - - - 4 - - - 1 - 1 - 3 - - 1 - 0 - - - - - - ms - - 2 - 0 - - - - - 1 - 2 - 2 - - - - - - repeat video - 1 - 1 - - 1 - 3 - - - - - - 1 - Play - 1 - 1 - Play Video - - 1 - 4 - 2 - - - - - - - - Video - - - - 0 - 4 - - - - - - - - - - - diff --git a/src/builder/rawDataViewer.ui b/src/builder/rawDataViewer.ui deleted file mode 100644 index 56f00dc..0000000 --- a/src/builder/rawDataViewer.ui +++ /dev/null @@ -1,436 +0,0 @@ - - - - - 1 - 1000000 - 1 - 10 - - - 1 - 100 - 1 - 10 - - - 1 - 100 - 1 - 10 - - - 1 - 100 - 1 - 10 - - - 1 - 100 - 1 - 10 - - - 1 - 100 - 1 - 10 - - - 1 - 100 - 1 - 10 - - - 1 - 100 - 1 - 10 - - - 1 - 100 - 1 - 10 - - - 1 - 100 - 1 - 10 - - - 1 - 10000 - 1 - 10 - - - 1 - 10000 - 1 - 1 - 10 - - - - - - - Show BG - 1 - - 0 - 0 - - - - - - Leak Corr - 1 - - 0 - 1 - - - - - - Subtr. BG - 1 - 1 - - 1 - 0 - - - - - - Corr. TF - 1 - - 1 - 1 - - - - - - All Patches - 1 - - 0 - 2 - - - - - - Harm. View - 1 - - 1 - 2 - - - - - - AbsAv - 1 - THIS IS EXPERIMENTAL!!!!!! - - 0 - 3 - - - - - - Show Freq - 1 - 1 - - 1 - 3 - - - - - - 1 - adjPatchAv - - 1 - 5 - - - - - - Patch Av - - 0 - 5 - - - - - - Reverse Graphs - 1 - - 1 - 4 - - - - - - - - - - - 1 - 100 - 1 - 10 - - - vertical - - - 2 - 2 - 1 - 2 - 2 - - - 1 - center - 3 - 1 - adjFrame - 1 - - 0 - 1 - - - - - - 1 - center - 3 - 1 - adjRxChan - 1 - - 1 - 1 - - - - - - 1 - start - center - 0 - 0 - 3 - 1 - adjPatch - 1 - - 2 - 1 - - - - - - Chan - - 1 - 0 - - - - - - Patch - - 2 - 0 - - - - - - Frame - - 0 - 0 - - - - - - 1 - 1 - popOptions - preferences-desktop - - 3 - 1 - - - - - - Options - - 3 - 0 - - - - - - - - 1 - vertical - - - 1 - vertical - - - 1 - - - 1 - 1 - right - 1 - adjMinTP - 0 - 0 - - - - - 1 - 1 - right - 1 - adjMaxTP - 0 - 0 - - - - - 1 - 12 - - - - - 1 - 12 - - - - - 1 - 1 - list-add - - - - - 1 - 1 - edit-clear - - - - - - - - - 1 - vertical - - - 0 - 1 - - - 1 - 1 - right - 1 - adjMinFre - 0 - 0 - - - - - 1 - 1 - right - 1 - adjMaxFre - 0 - 0 - - - - - 0 - 1 - 12 - - - - - 0 - 1 - 12 - - - - - 1 - 1 - list-add - - - - - 1 - 1 - edit-clear - - - - - - - - - - diff --git a/src/builder/simpleDataViewer.ui b/src/builder/simpleDataViewer.ui deleted file mode 100644 index a2ae0e3..0000000 --- a/src/builder/simpleDataViewer.ui +++ /dev/null @@ -1,106 +0,0 @@ - - - - - vertical - - - 1 - - - 1 - 0 - 0 - - - - - 4 - 4 - 1 - 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - page 1 - - - - - - - 1 - - - 1 - 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - page 2 - - - - - - - - - - diff --git a/src/builder/spectrogramViewer.ui b/src/builder/spectrogramViewer.ui deleted file mode 100644 index 69c9f08..0000000 --- a/src/builder/spectrogramViewer.ui +++ /dev/null @@ -1,452 +0,0 @@ - - - - - 1 - 1000000 - 1 - 10 - - - 1 - 100 - 1 - 10 - - - 1 - 100 - 1 - 10 - - - 1 - 100 - 1 - 10 - - - 1 - 100 - 1 - 10 - - - 1 - 100 - 1 - 10 - - - 1 - 100 - 1 - 10 - - - 1 - 0.5 - 0.01 - 10 - - - 1 - 100 - 1 - 10 - - - 1 - 100 - 1 - 10 - - - 1 - 100 - 1 - 10 - - - 1 - 100 - 1 - 10 - - - 1 - 10000 - 1 - 10 - - - 1 - 10000 - 1 - 1 - 10 - - - 1 - 100 - 1 - 10 - - - - - - - Show BG - 1 - - 0 - 0 - - - - - - Leak Corr - 1 - - 0 - 1 - - - - - - Subtr. BG - 1 - 1 - - 1 - 0 - - - - - - Corr. TF - 1 - - 1 - 1 - - - - - - Show Freq - 1 - 1 - - 1 - 2 - - - - - - All Frames - 1 - - 0 - 2 - - - - - - Fix Distortions - 1 - - 0 - 3 - - - - - - - - vertical - - - 2 - 2 - 1 - 2 - 2 - - - 1 - center - 3 - 1 - adjFrame - 1 - - 0 - 1 - - - - - - 1 - center - 3 - 1 - adjRxChan - 1 - - 1 - 1 - - - - - - 1 - start - center - 0 - 0 - 3 - 1 - adjPatchAv - 1 - - 2 - 1 - - - - - - Chan - - 1 - 0 - - - - - - Patch Av - - 2 - 0 - - - - - - Frame - - 0 - 0 - - - - - - 1 - 1 - popOptions - preferences-desktop - - 4 - 1 - - - - - - Options - - 4 - 0 - - - - - - 1 - start - center - 0 - 0 - 3 - 1 - adjGrouping - 1 - - 3 - 1 - - - - - - Grouping - - 3 - 0 - - - - - - - - 1 - 1 - 316 - - - vertical - - - 1 - vertical - - - 1 - - - 1 - 0 - 1 - adjMinTP - 0 - 0 - - - - - 1 - 0 - 1 - adjMaxTP - 0 - 0 - - - - - 1 - 6 - - - - - 1 - 6 - - - - - 1 - 1 - list-add - - - - - 1 - 1 - edit-clear - - - - - - - - - 1 - vertical - - - 1 - - - 1 - 0 - 1 - adjMinFre - 0 - 0 - - - - - 1 - 0 - 1 - adjMaxFre - 0 - 0 - - - - - 1 - 6 - - - - - 1 - 6 - - - - - 1 - 1 - list-add - - - - - 1 - 1 - edit-clear - - - - - - - - - - - vertical - - - 1 - 1 - adjPatch - 1 - 0 - - - - - 1 - 1 - adjLogPlot - 1 - - - - - - - - From 726d6dfcf7270c66a710190e8d076551a84e35c0 Mon Sep 17 00:00:00 2001 From: nHackel Date: Thu, 28 Nov 2024 17:31:27 +0100 Subject: [PATCH 10/11] Update MPIViewers exports and imports/deps --- MPIViewers/Project.toml | 1 + MPIViewers/src/3DViewer/3DViewer.jl | 2 +- MPIViewers/src/MPIViewers.jl | 3 +++ MPIViewers/src/MagneticFieldViewer/MagneticFieldViewer.jl | 2 +- MPIViewers/src/RawDataViewer.jl | 1 + MPIViewers/src/SFViewerWidget.jl | 2 +- MPIViewers/src/SpectrogramViewer.jl | 2 +- MPIViewers/src/interface.jl | 0 8 files changed, 9 insertions(+), 4 deletions(-) create mode 100644 MPIViewers/src/interface.jl diff --git a/MPIViewers/Project.toml b/MPIViewers/Project.toml index e43fc36..936d763 100644 --- a/MPIViewers/Project.toml +++ b/MPIViewers/Project.toml @@ -33,6 +33,7 @@ REPL = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" Reexport = "189a3867-3050-52da-a836-e630ba90ab69" SphericalHarmonicExpansions = "504afa71-bae7-47b4-8ec9-3851161806ac" +Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" [compat] diff --git a/MPIViewers/src/3DViewer/3DViewer.jl b/MPIViewers/src/3DViewer/3DViewer.jl index 54d5ba6..0733ae1 100644 --- a/MPIViewers/src/3DViewer/3DViewer.jl +++ b/MPIViewers/src/3DViewer/3DViewer.jl @@ -43,7 +43,7 @@ function DataViewer3D(imFG; kwargs...) end function DataViewer3D(; kwargs...) - w = GtkWindow("Data Viewer",800,600) + w = GtkWindow("Data Viewer 3D",800,600) dw = DataViewer3DWidget(; kwargs...) push!(w,dw) show(w) diff --git a/MPIViewers/src/MPIViewers.jl b/MPIViewers/src/MPIViewers.jl index 6a8a7a9..e147425 100644 --- a/MPIViewers/src/MPIViewers.jl +++ b/MPIViewers/src/MPIViewers.jl @@ -10,6 +10,7 @@ using SphericalHarmonicExpansions using Unitful using MPISphericalHarmonics.NLsolve using FFTW +using Statistics # File Handling #using DelimtedFiles @@ -82,6 +83,8 @@ function showError(ex) d.modal = true end +export updateData, updateData!, showData, showData! + include("GtkUtils.jl") include("BaseViewer.jl") include("SimpleDataViewer.jl") diff --git a/MPIViewers/src/MagneticFieldViewer/MagneticFieldViewer.jl b/MPIViewers/src/MagneticFieldViewer/MagneticFieldViewer.jl index d094acd..9847df0 100644 --- a/MPIViewers/src/MagneticFieldViewer/MagneticFieldViewer.jl +++ b/MPIViewers/src/MagneticFieldViewer/MagneticFieldViewer.jl @@ -1,4 +1,4 @@ -export MagneticFieldViewer +export MagneticFieldViewer, MagneticFieldViewerWidget, FieldViewerWidget mutable struct FieldViewerWidget <: Gtk4.GtkBox handle::Ptr{Gtk4.GObject} diff --git a/MPIViewers/src/RawDataViewer.jl b/MPIViewers/src/RawDataViewer.jl index fb47d01..b9adbe5 100644 --- a/MPIViewers/src/RawDataViewer.jl +++ b/MPIViewers/src/RawDataViewer.jl @@ -1,3 +1,4 @@ +export RawDataWidget mutable struct RawDataWidget <: Gtk4.GtkBox handle::Ptr{Gtk4.GObject} builder::GtkBuilder diff --git a/MPIViewers/src/SFViewerWidget.jl b/MPIViewers/src/SFViewerWidget.jl index 93fff2d..cf87d90 100644 --- a/MPIViewers/src/SFViewerWidget.jl +++ b/MPIViewers/src/SFViewerWidget.jl @@ -1,4 +1,4 @@ -export SFViewer +export SFViewerWidget, SFViewer mutable struct SFViewerWidget <: Gtk4.GtkPaned handle::Ptr{Gtk4.GObject} builder::GtkBuilder diff --git a/MPIViewers/src/SpectrogramViewer.jl b/MPIViewers/src/SpectrogramViewer.jl index 6fbe59f..fc4c13a 100644 --- a/MPIViewers/src/SpectrogramViewer.jl +++ b/MPIViewers/src/SpectrogramViewer.jl @@ -1,4 +1,4 @@ -export SpectrogramViewer +export SpectrogramViewer, SpectrogramWidget mutable struct SpectrogramWidget <: Gtk4.GtkBox handle::Ptr{Gtk4.GObject} diff --git a/MPIViewers/src/interface.jl b/MPIViewers/src/interface.jl new file mode 100644 index 0000000..e69de29 From deddd3361c2804bb311c0740308bd195881b065f Mon Sep 17 00:00:00 2001 From: nHackel Date: Thu, 28 Nov 2024 17:31:45 +0100 Subject: [PATCH 11/11] Make MPIUI depend on MPIViewers --- Project.toml | 16 ++++++++++------ src/MPIUI.jl | 5 +++-- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/Project.toml b/Project.toml index 6111dd6..d1179ac 100644 --- a/Project.toml +++ b/Project.toml @@ -27,6 +27,7 @@ MPIFiles = "371237a9-e6c1-5201-9adb-3d8cfa78fa9f" MPIMeasurements = "a874a27a-e9a7-503d-98b6-bf61df4bb725" MPIReco = "e4246700-6248-511e-8146-a1d1f47669d2" MPISphericalHarmonics = "2527f7a4-0af4-4016-a944-036fbac19de9" +MPIViewers = "8bda2715-69d8-46f6-8f4f-eacbc82b6e08" NLsolve = "2774e3e8-f4cf-5e23-947b-6d7e65073b56" Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7" @@ -48,16 +49,19 @@ HDF5 = "0.15, 0.16" ImageUtils = "0.2" Images = "0.23, 0.24, 0.25" LoggingExtras = ">= 0.4.2" -MPIFiles = "0.15" -MPIMeasurements = "0.5" -MPIReco = "0.5" +MPIFiles = "0.15, 0.16" +MPIMeasurements = "0.5, 0.6" +MPIViewers = "0.1" +MPIReco = "0.7" MPISphericalHarmonics = "0.0.10" Reexport = "0.2,1.0" CairoMakie = "0.12" -julia = "1.7" -Gtk4 = "0.6" -Gtk4Makie = "0.2" +julia = "1.11" +Gtk4 = "0.7" +Gtk4Makie = "0.3" +[sources] +MPIViewers = {path = "./MPIViewers"} [extras] HTTP = "cd3eb016-35fb-5094-929b-558a96fad6f3" diff --git a/src/MPIUI.jl b/src/MPIUI.jl index e21710a..84c6950 100644 --- a/src/MPIUI.jl +++ b/src/MPIUI.jl @@ -27,8 +27,11 @@ ENV["MPILIB_UI"] = "Nothing" @reexport using MPIMeasurements @reexport using MPIReco +@reexport using MPIViewers using MPIReco.RegularizedLeastSquares +import MPIViewers: updateData!, showData!, updateData, showData + using ImageUtils: makeAxisArray, Axis using Gtk4, Gtk4.G_, Gtk4.GLib @@ -118,8 +121,6 @@ const colors = [(0/255,73/255,146/255), # UKE blau include("LogMessagesWidget.jl") -include("GtkUtils.jl") -include("Viewer/Viewer.jl") include("Reconstruction/OfflineRecoWidget.jl") include("Protocol/ProtocolWidget.jl") include("SFBrowser.jl")