musrview is now a client/server application following the ROOT7 architecture
(S. Linev et al., J. Phys. Conf. Ser. 1525 (2020) 012092): the C++ side holds
the data, an ROOT::RWebWindow ships it as JSON to a JSROOT page which renders
it. This addresses three long standing problems.
The fit parameter, theory and run info blocks were TPaveText/TLegend objects
which scaled their content down until it became unreadable as soon as a fit had
many parameters. They are HTML panels now: fixed font size, own scrollbar, and
each of them can be shown and hidden individually. The run info keeps listing
every run with the very symbol it is plotted with.
The command keys ('d', 'f', 'a', ...) were executed even while a text or latex
label was being typed into the canvas. The page checks whether a text input has
the focus, so typing a 'd' inserts a 'd'.
The plot views were encoded in a set of entangled variables plus a static one
which leaked state between canvases. PMusrViewMode describes them as four
orthogonal properties - domain, Fourier component, difference including whether
it is formed before or after the transform, and average - so that combinations
such as 'Fourier-difference-average' are well defined. PMusrViewModel::GetCurves()
is the single place which maps a view mode onto histograms; plotting and ascii
export both go through it and can no longer disagree, which removed some 700
lines of duplicated view logic from the export.
New classes, namespace musrview:
PMusrViewMode the decoupled view state, free of ROOT and GUI, unit tested
PMusrViewModel the GUI-free data layer, physics taken over unchanged
PMusrWebCanvas the web front end and the graphics export
Further notes:
- The interactive path builds no TCanvas at all, the spectra travel as a TList
of drawables. A TCanvas creates and maps its window already in the
constructor, which would put an empty ROOT canvas next to the musrview window.
- The graphics export keeps the classic pad layout and its 800x600 size and
runs with ROOT in batch mode, so batch operation needs no browser.
- The window opens at 1200x900, since toolbar and panels take their share.
- Non-muSR axis labels are taken from the data file again, and ROOT/TLatex
markup is rendered in the HTML panels as well.
- musrview needs a ROOT built with -Droot7=ON -Dwebgui=ON, plus -Dqt6web=ON for
the embedded window (--embedded, the default). --browser uses the standard
browser of the system, --browser=server only prints the URL.
- PMusrCanvas is no longer part of the build, the files are kept for reference.
Tests: 87/87 pass, among them a new unit test of the view modes and a new
musrview test covering the non-muSR fit type.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H6YiUNBTMDX9uA3Ts1w2nW
89 lines
2.7 KiB
C++
89 lines
2.7 KiB
C++
/***************************************************************************
|
|
|
|
viewmode_check.cpp
|
|
|
|
Unit test of PMusrViewMode, the class which decouples the musrview plot
|
|
views (time/Fourier, difference d-f and f-d, average) from each other.
|
|
|
|
Author: Andreas Suter
|
|
e-mail: andreas.suter@psi.ch
|
|
|
|
***************************************************************************/
|
|
|
|
#include "PMusrViewMode.h"
|
|
#include <iostream>
|
|
#include <cassert>
|
|
using namespace musrview;
|
|
|
|
static void show(const char *what, const PMusrViewMode &m) {
|
|
std::cout << what << " -> " << m.GetKey() << " | " << m.GetLabel() << "\n";
|
|
}
|
|
|
|
int main() {
|
|
PMusrViewMode m;
|
|
show("initial", m);
|
|
assert(m.GetKey() == "time");
|
|
|
|
// d then f => d-f
|
|
m.ToggleDifference(); show("d", m);
|
|
assert(m.GetKey() == "time+diff");
|
|
m.ToggleFourier();
|
|
show("d,f", m);
|
|
assert(m.IsDiffThenFourier());
|
|
assert(m.GetKey() == "fourier-power+diff-df");
|
|
|
|
// now 'd' again: old code refused (d-f-d), new one simply drops the difference
|
|
m.ToggleDifference(); show("d,f,d", m);
|
|
assert(m.GetKey() == "fourier-power");
|
|
|
|
// f then d => f-d
|
|
m.Reset();
|
|
m.ToggleFourier(); show("f", m);
|
|
m.ToggleDifference(); show("f,d", m);
|
|
assert(m.IsFourierThenDiff());
|
|
assert(m.GetKey() == "fourier-power+diff-fd");
|
|
|
|
// average is orthogonal: fourier-difference-average
|
|
m.ToggleAverage(); show("f,d,a", m);
|
|
assert(m.GetKey() == "fourier-power+diff-fd+avg");
|
|
assert(m.IsFourierThenDiff() && m.IsAverage() && m.IsFourier());
|
|
|
|
// component switch keeps difference + average
|
|
m.SetFourierComponent(EFourierComponent::kReal); show("+real", m);
|
|
assert(m.GetKey() == "fourier-real+diff-fd+avg");
|
|
assert(m.IsPhaseAdjustable());
|
|
m.SetFourierComponent(EFourierComponent::kPower);
|
|
assert(!m.IsPhaseAdjustable());
|
|
|
|
// back to time domain keeps difference + average
|
|
m.SetFourierComponent(EFourierComponent::kReal);
|
|
m.ToggleFourier(); show("back to time", m);
|
|
assert(m.GetKey() == "time+diff+avg");
|
|
assert(!m.IsPhaseAdjustable());
|
|
|
|
// and forward again -> difference was made in time domain => d-f
|
|
m.ToggleFourier(); show("forward", m);
|
|
assert(m.GetKey() == "fourier-real+diff-df+avg");
|
|
|
|
// capabilities
|
|
PMusrViewMode n;
|
|
n.SetFourierAllowed(false);
|
|
assert(!n.ToggleFourier());
|
|
assert(n.IsTimeDomain());
|
|
n.SetAverageAllowed(false);
|
|
assert(!n.ToggleAverage());
|
|
assert(!n.IsAverage());
|
|
show("non-musr", n);
|
|
|
|
// capability revoked while active
|
|
PMusrViewMode o;
|
|
o.ToggleFourier(); o.ToggleAverage();
|
|
o.SetFourierAllowed(false); o.SetAverageAllowed(false);
|
|
assert(o.IsTimeDomain() && !o.IsAverage());
|
|
show("revoked", o);
|
|
|
|
std::cout << "\nJSON: " << m.ToJson() << "\n";
|
|
std::cout << "\nall mode assertions passed\n";
|
|
return 0;
|
|
}
|