musrview: let "Save graphics" mirror the window instead of a fixed layout
The interactive export always produced the classic 800x600 layout, so neither the size of the musrview window nor the proportions the user had dragged the 'Fit parameters', 'Theory' and 'RUN INFO' panels to showed up in the saved graphics. The page now measures its current view -- window size without toolbar and status bar, the height of the title and the run info, the width of the parameter/theory column, the split between the two, plus the font size of the panels -- and hands it over with the 'save:' message. A boundary which is a splitter bar on screen is reported as the bar's centre line, since the export draws the blocks edge to edge. The C++ side takes that as the new ExportGeometry, which replaces the hard wired fractions in RenderGraphics(). With a font size given, the panels keep the size the text has on screen (with the line spacing of the HTML panels) rather than TPaveText/TLegend scaling the text up to the pave: a larger window then shows more text instead of bigger text, as the HTML panels do. The text is only ever scaled down, as far as the pave needs it in height and in width, the latter measured the way TPaveText measures it. ExportGeometry's defaults are exactly the previous layout, hence the --png/--pdf/... batch export, which has no window to take anything over from, is unchanged -- bit for bit, the musrview_check reference images included. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
778c8fbff3
commit
8ad77694d7
4 files changed
+262
-52
No files matched your search
@@ -40,7 +40,11 @@ shortcomings:
|
||||
|
||||
The musrview window opens larger than the ROOT6 canvas used to (1200x900
|
||||
instead of 800x600), since the toolbar and the panels take their share of it.
|
||||
The graphics export is unaffected and keeps producing 800x600 images.
|
||||
The interactive 'Save graphics' mirrors the window: the exported graphics have
|
||||
its size and the proportions the title, 'Fit parameters', 'Theory' and
|
||||
'RUN INFO' blocks have been dragged to, and these panels keep the font size
|
||||
they have on screen. The batch export (--png, --pdf, ...) has no window to
|
||||
take anything over from and keeps producing the classic 800x600 layout.
|
||||
|
||||
musrview can either open its window in the standard browser of the system
|
||||
(--browser) or in an embedded Qt6 WebEngine window (--embedded, the default),
|
||||
|
||||
+172
-39
@@ -31,6 +31,7 @@
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
@@ -46,6 +47,7 @@
|
||||
#include <TVirtualX.h>
|
||||
|
||||
#include "PMusrWebCanvas.h"
|
||||
#include "PStringUtils.h"
|
||||
|
||||
namespace musrview {
|
||||
|
||||
@@ -201,8 +203,83 @@ class PBatchGraphicsGuard {
|
||||
TVirtualX *fSavedVirtualX;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
/**
|
||||
* <p>Line spacing of the HTML panels (the 'line-height' of musrview.html),
|
||||
* used to lay the exported panels out the same way.
|
||||
*/
|
||||
constexpr Double_t gPanelLineSpacing = 1.45;
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
/**
|
||||
* <p>Text size of an exported panel which mirrors what the interactive view
|
||||
* shows: an HTML panel keeps its font size and scrolls, whereas a TPaveText
|
||||
* left to itself always scales its text to the pave. Here the font size of
|
||||
* the page is the starting point and is only scaled *down*, as far as needed
|
||||
* for all lines to fit into the pave -- in height as well as in width, the
|
||||
* latter measured the same way TPaveText measures it.
|
||||
*
|
||||
* \param lines the text lines to be shown
|
||||
* \param font ROOT font code the panel is drawn with
|
||||
* \param wanted font height of the page as a fraction of the window height
|
||||
* \param width width of the pave [NDC]
|
||||
* \param height height of the pave [NDC]
|
||||
* \param nSlots number of line slots which have to fit into the pave
|
||||
*
|
||||
* \return the text size [NDC], or \p wanted if there is nothing to scale
|
||||
*/
|
||||
Double_t PanelTextSize(const std::vector<std::string> &lines, Int_t font, Double_t wanted,
|
||||
Double_t width, Double_t height, size_t nSlots)
|
||||
{
|
||||
if (wanted <= 0.0 || nSlots == 0 || width <= 0.0 || height <= 0.0 || lines.empty())
|
||||
return wanted;
|
||||
|
||||
// vertical: nSlots lines, laid out with the line spacing of the HTML panel
|
||||
Double_t size = std::min(wanted, 0.96*height/(gPanelLineSpacing*nSlots));
|
||||
|
||||
// horizontal: the longest line has to fit into the pave
|
||||
TLatex latex;
|
||||
latex.SetTextFont(font);
|
||||
latex.SetTextSize(size);
|
||||
Double_t longest = 0.0;
|
||||
for (const auto &line : lines) {
|
||||
latex.SetTitle(line.c_str());
|
||||
const Double_t w = latex.GetXsize();
|
||||
if (w > longest)
|
||||
longest = w;
|
||||
}
|
||||
if (longest > 0.94*width)
|
||||
size *= 0.94*width/longest;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// ExportGeometry::Sanitize (public)
|
||||
//--------------------------------------------------------------------------
|
||||
/**
|
||||
* <p>Clips a geometry coming from the page into a range which still gives a
|
||||
* usable layout, so that a bogus message cannot produce a degenerate canvas.
|
||||
* The defaults (the classic 800x600 layout) are a fix point of this.
|
||||
*/
|
||||
void PMusrWebCanvas::ExportGeometry::Sanitize()
|
||||
{
|
||||
width = std::clamp(width, 200, 4000);
|
||||
height = std::clamp(height, 150, 4000);
|
||||
|
||||
titleFrac = std::clamp(titleFrac, 0.0, 0.30);
|
||||
infoFrac = std::clamp(infoFrac, 0.0, 0.60);
|
||||
sideFrac = std::clamp(sideFrac, 0.0, 0.70);
|
||||
fontFrac = std::clamp(fontFrac, 0.0, 0.10);
|
||||
|
||||
// the parameter/theory split has to stay inside the side column
|
||||
const Double_t lo = infoFrac + 0.02;
|
||||
const Double_t hi = 1.0 - titleFrac - 0.02;
|
||||
splitFrac = (hi > lo) ? std::clamp(splitFrac, lo, hi) : 0.5*(lo + hi);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Constructor
|
||||
//--------------------------------------------------------------------------
|
||||
@@ -904,26 +981,43 @@ void PMusrWebCanvas::ProcessMessage(unsigned connid, const std::string &msg)
|
||||
}
|
||||
|
||||
if (msg.rfind("save:", 0) == 0) {
|
||||
// save:<format>:<title><params><theory><info> - each of the four trailing
|
||||
// characters is '1'/'0', reflecting the page's current panel visibility
|
||||
// (see the "panels" object in musrview.html). The file is written to the
|
||||
// current working directory and handed to the browser as a download, so
|
||||
// its own save dialog (or download folder) decides where it ends up.
|
||||
const std::string arg = msg.substr(5);
|
||||
const size_t sep = arg.find(':');
|
||||
const std::string format = (sep == std::string::npos) ? arg : arg.substr(0, sep);
|
||||
// save:<format>:<title><params><theory><info>
|
||||
// [:<width>:<height>:<titleFrac>:<infoFrac>:<sideFrac>:<splitFrac>:<fontFrac>]
|
||||
//
|
||||
// Each of the four characters of the second field is '1'/'0', reflecting
|
||||
// the page's current panel visibility (see the "panels" object in
|
||||
// musrview.html). The seven numbers behind it are the size and the
|
||||
// proportions of the window, so that the exported graphics mirror what is
|
||||
// on screen (see exportGeometry() there and ExportGeometry here); they are
|
||||
// optional, a message without them gets the default layout.
|
||||
//
|
||||
// The file is written to the current working directory and handed to the
|
||||
// browser as a download, so its own save dialog (or download folder)
|
||||
// decides where it ends up.
|
||||
const std::vector<std::string> tok = PStringUtils::Split(msg.substr(5), ":");
|
||||
const std::string format = tok.empty() ? std::string() : tok[0];
|
||||
PanelVisibility panels;
|
||||
if (sep != std::string::npos) {
|
||||
const std::string flags = arg.substr(sep+1);
|
||||
if (flags.size() == 4) {
|
||||
panels.title = (flags[0] == '1');
|
||||
panels.params = (flags[1] == '1');
|
||||
panels.theory = (flags[2] == '1');
|
||||
panels.info = (flags[3] == '1');
|
||||
}
|
||||
if (tok.size() > 1 && tok[1].size() == 4) {
|
||||
panels.title = (tok[1][0] == '1');
|
||||
panels.params = (tok[1][1] == '1');
|
||||
panels.theory = (tok[1][2] == '1');
|
||||
panels.info = (tok[1][3] == '1');
|
||||
}
|
||||
ExportGeometry geom;
|
||||
if (tok.size() >= 9) {
|
||||
bool ok = true, allOk = true;
|
||||
const Int_t w = PStringUtils::ToInt(tok[2], &ok); allOk &= ok;
|
||||
const Int_t h = PStringUtils::ToInt(tok[3], &ok); allOk &= ok;
|
||||
const Double_t title = PStringUtils::ToDouble(tok[4], &ok); allOk &= ok;
|
||||
const Double_t info = PStringUtils::ToDouble(tok[5], &ok); allOk &= ok;
|
||||
const Double_t side = PStringUtils::ToDouble(tok[6], &ok); allOk &= ok;
|
||||
const Double_t split = PStringUtils::ToDouble(tok[7], &ok); allOk &= ok;
|
||||
const Double_t font = PStringUtils::ToDouble(tok[8], &ok); allOk &= ok;
|
||||
if (allOk)
|
||||
geom = ExportGeometry{w, h, title, info, side, split, font};
|
||||
}
|
||||
std::string relUrl;
|
||||
if (SaveGraphicsForDownload(format, panels, relUrl))
|
||||
if (SaveGraphicsForDownload(format, panels, geom, relUrl))
|
||||
fWindow->Send(connid, "DOWNLOAD:" + JsonEscape(relUrl));
|
||||
else
|
||||
fWindow->Send(connid, "ERROR:" + JsonEscape(fLastError));
|
||||
@@ -1074,7 +1168,7 @@ Bool_t PMusrWebCanvas::SaveGraphics(const std::string &graphicsFormat)
|
||||
return false;
|
||||
}
|
||||
|
||||
return RenderGraphics(base + "." + graphicsFormat, PanelVisibility{});
|
||||
return RenderGraphics(base + "." + graphicsFormat, PanelVisibility{}, ExportGeometry{});
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
@@ -1097,13 +1191,15 @@ Bool_t PMusrWebCanvas::SaveGraphics(const std::string &graphicsFormat)
|
||||
* \param graphicsFormat extension such as "png", "pdf", "eps", ...
|
||||
* \param panels which of the title/parameters/theory/run-info blocks to
|
||||
* include, mirroring the interactive view's current panel visibility
|
||||
* \param geom size and proportions of the interactive view, so that the
|
||||
* exported graphics mirror the window rather than a fixed layout
|
||||
* \param relUrl the URL (relative to the web window) the file can be
|
||||
* downloaded from, on success
|
||||
*
|
||||
* \return true on success
|
||||
*/
|
||||
Bool_t PMusrWebCanvas::SaveGraphicsForDownload(const std::string &graphicsFormat, const PanelVisibility &panels,
|
||||
std::string &relUrl)
|
||||
const ExportGeometry &geom, std::string &relUrl)
|
||||
{
|
||||
if (DefaultGraphicsBaseName().empty()) {
|
||||
fLastError = "msr-file name '" + fMsrFileName + "' is invalid.";
|
||||
@@ -1123,7 +1219,7 @@ Bool_t PMusrWebCanvas::SaveGraphicsForDownload(const std::string &graphicsFormat
|
||||
break;
|
||||
}
|
||||
|
||||
if (!RenderGraphics(fileName, panels))
|
||||
if (!RenderGraphics(fileName, panels, geom))
|
||||
return false;
|
||||
|
||||
relUrl = "currentdir/" + fileName;
|
||||
@@ -1139,18 +1235,22 @@ Bool_t PMusrWebCanvas::SaveGraphicsForDownload(const std::string &graphicsFormat
|
||||
*
|
||||
* <p>This path deliberately does not involve the browser: the file is written
|
||||
* directly by ROOT from a batch TCanvas, so it works whether or not a browser
|
||||
* is even installed, and interactive Save produces bit-for-bit the same
|
||||
* output as the --png/--pdf/... batch export (which passes a default-constructed,
|
||||
* all-visible \p panels).
|
||||
* is even installed. The --png/--pdf/... batch export passes a default
|
||||
* constructed, all-visible \p panels and \p geom and hence always produces the
|
||||
* classic 800x600 layout, whereas the interactive Save hands over the state of
|
||||
* the window, so that what is exported is what is on screen.
|
||||
*
|
||||
* \param fileName the file to write; its extension selects the format
|
||||
* \param panels which of the title/parameters/theory/run-info blocks to draw;
|
||||
* a hidden block is left out and its space given to its neighbours,
|
||||
* mirroring how the interactive view's CSS grid collapses hidden panels
|
||||
* \param geom size of the canvas and the proportions of the blocks, i.e. the
|
||||
* window size and the splitter positions of the interactive view
|
||||
*
|
||||
* \return true on success
|
||||
*/
|
||||
Bool_t PMusrWebCanvas::RenderGraphics(const std::string &fileName, const PanelVisibility &panels)
|
||||
Bool_t PMusrWebCanvas::RenderGraphics(const std::string &fileName, const PanelVisibility &panels,
|
||||
const ExportGeometry &geomIn)
|
||||
{
|
||||
if (!fValid) {
|
||||
fLastError = "canvas is not valid.";
|
||||
@@ -1159,11 +1259,16 @@ Bool_t PMusrWebCanvas::RenderGraphics(const std::string &fileName, const PanelVi
|
||||
|
||||
TString str(fileName.c_str());
|
||||
|
||||
// Build the classic layout. Its size is deliberately not the size of the
|
||||
// musrview window: the exported graphics have always been 800x600 and users
|
||||
// (and the reference images of the test suite) rely on that.
|
||||
const Int_t exportWidth = 800;
|
||||
const Int_t exportHeight = 600;
|
||||
ExportGeometry geom(geomIn);
|
||||
geom.Sanitize();
|
||||
|
||||
// The size of the exported graphics is the size of the musrview window
|
||||
// (without its toolbar and status bar, which the export does not have), so
|
||||
// that resizing the window also resizes what is saved. The defaults, used
|
||||
// by the --png/--pdf/... batch export where there is no window, are the
|
||||
// classic 800x600 the reference images of the test suite rely on.
|
||||
const Int_t exportWidth = geom.width;
|
||||
const Int_t exportHeight = geom.height;
|
||||
|
||||
// TCanvas decides at *construction* time, from gROOT->IsBatch(), whether to
|
||||
// build a real (mapped) GUI canvas or a headless one; canvas.SetBatch(kTRUE)
|
||||
@@ -1200,11 +1305,12 @@ Bool_t PMusrWebCanvas::RenderGraphics(const std::string &fileName, const PanelVi
|
||||
|
||||
// a hidden block gives its space to its neighbours, mirroring how the
|
||||
// interactive view's CSS grid collapses hidden panels (see updateLayout()/
|
||||
// updateSideLayout() in musrview.html)
|
||||
const Double_t yInfo = panels.info ? 0.1 : 0.0;
|
||||
const Double_t yTitle = panels.title ? 0.95 : 1.0;
|
||||
// updateSideLayout() in musrview.html); the sizes of the visible ones are
|
||||
// the ones the user has dragged the splitters to
|
||||
const Double_t yInfo = panels.info ? geom.infoFrac : 0.0;
|
||||
const Double_t yTitle = panels.title ? 1.0 - geom.titleFrac : 1.0;
|
||||
const Bool_t sideVisible = panels.params || panels.theory;
|
||||
const Double_t xTheo = sideVisible ? 0.75 : 1.0;
|
||||
const Double_t xTheo = sideVisible ? 1.0 - geom.sideFrac : 1.0;
|
||||
const Int_t white = TColor::GetColor(255, 255, 255);
|
||||
|
||||
std::unique_ptr<TPaveText> titlePad;
|
||||
@@ -1220,11 +1326,19 @@ Bool_t PMusrWebCanvas::RenderGraphics(const std::string &fileName, const PanelVi
|
||||
dataTheoryPad.SetFillColor(white);
|
||||
dataTheoryPad.Draw();
|
||||
|
||||
// when both are visible the column is split 50/50, matching paramsFrac's
|
||||
// default in musrview.html; a lone survivor takes the full column height
|
||||
// (yInfo..yTitle) instead
|
||||
// when both are visible the column is split where the user has dragged the
|
||||
// handle to (0.5 by default, matching paramsFrac in musrview.html); a lone
|
||||
// survivor takes the full column height (yInfo..yTitle) instead
|
||||
const Bool_t bothVisible = panels.params && panels.theory;
|
||||
const Double_t sideSplit = bothVisible ? 0.5 : yInfo;
|
||||
const Double_t sideSplit = bothVisible ? geom.splitFrac : yInfo;
|
||||
|
||||
// Text size of the three panels. With a font size handed over by the page
|
||||
// they keep the size they have on screen (scaled down where the pave is too
|
||||
// small for it), instead of TPaveText/TLegend scaling the text to the pave:
|
||||
// enlarging the window then shows more text rather than bigger text, just
|
||||
// as the HTML panels do. Without it (batch export) the text is scaled by
|
||||
// ROOT as before.
|
||||
const Double_t sideWidth = 1.0 - xTheo;
|
||||
|
||||
std::unique_ptr<TPaveText> parameterPad;
|
||||
if (panels.params) {
|
||||
@@ -1233,7 +1347,13 @@ Bool_t PMusrWebCanvas::RenderGraphics(const std::string &fileName, const PanelVi
|
||||
parameterPad->SetTextAlign(13); // top, left
|
||||
parameterPad->SetTextFont(102); // courier bold
|
||||
const std::vector<std::string> &lines = fModel->GetParameterLines();
|
||||
const Double_t yoffset = (lines.size() > 20) ? 1.0/(lines.size()+1) : 0.05;
|
||||
Double_t yoffset = (lines.size() > 20) ? 1.0/(lines.size()+1) : 0.05;
|
||||
const Double_t textSize = PanelTextSize(lines, 102, geom.fontFrac, sideWidth,
|
||||
yTitle-sideSplit, lines.size());
|
||||
if (textSize > 0.0) {
|
||||
parameterPad->SetTextSize(textSize);
|
||||
yoffset = gPanelLineSpacing*textSize/(yTitle-sideSplit);
|
||||
}
|
||||
for (size_t i=0; i<lines.size(); i++)
|
||||
parameterPad->AddText(0.03, 0.98-i*yoffset, lines[i].c_str());
|
||||
parameterPad->Draw();
|
||||
@@ -1247,7 +1367,13 @@ Bool_t PMusrWebCanvas::RenderGraphics(const std::string &fileName, const PanelVi
|
||||
theoryPad->SetTextAlign(13);
|
||||
theoryPad->SetTextFont(102);
|
||||
const std::vector<std::string> &lines = fModel->GetTheoryLines();
|
||||
const Double_t yoffset = (lines.size() > 20) ? 1.0/(lines.size()+1) : 0.05;
|
||||
Double_t yoffset = (lines.size() > 20) ? 1.0/(lines.size()+1) : 0.05;
|
||||
const Double_t textSize = PanelTextSize(lines, 102, geom.fontFrac, sideWidth,
|
||||
theoryTop-yInfo, lines.size()+1);
|
||||
if (textSize > 0.0) {
|
||||
theoryPad->SetTextSize(textSize);
|
||||
yoffset = gPanelLineSpacing*textSize/(theoryTop-yInfo);
|
||||
}
|
||||
for (size_t i=0; i<lines.size(); i++)
|
||||
theoryPad->AddText(0.03, 0.98-(i+1)*yoffset, lines[i].c_str());
|
||||
theoryPad->Draw();
|
||||
@@ -1259,6 +1385,13 @@ Bool_t PMusrWebCanvas::RenderGraphics(const std::string &fileName, const PanelVi
|
||||
infoPad->SetFillColor(white);
|
||||
infoPad->SetTextAlign(12);
|
||||
infoPad->SetHeader(fModel->GetStatisticLine().c_str());
|
||||
// the legend keeps a margin for the symbols, hence the reduced width
|
||||
std::vector<std::string> infoText(fModel->GetInfoLines());
|
||||
infoText.push_back(fModel->GetStatisticLine());
|
||||
const Double_t textSize = PanelTextSize(infoText, 42, geom.fontFrac, 1.0-infoPad->GetMargin(),
|
||||
yInfo, infoText.size());
|
||||
if (textSize > 0.0)
|
||||
infoPad->SetTextSize(textSize);
|
||||
// the legend always lists the runs of the plot block, also in the averaged
|
||||
// view where a single curve is shown
|
||||
const std::vector<std::string> &lines = fModel->GetInfoLines();
|
||||
|
||||
@@ -130,16 +130,38 @@ class PMusrWebCanvas
|
||||
Bool_t info{true};
|
||||
};
|
||||
|
||||
/// Size and proportions of the exported layout. The interactive view fills
|
||||
/// it in from its window, so that "Save graphics" reproduces what is on
|
||||
/// screen: the window size as well as how much of it the user has given to
|
||||
/// the title, to the 'Fit parameters'/'Theory' column and to the run info
|
||||
/// by dragging the splitters. The defaults are the classic 800x600 layout
|
||||
/// and are what the --png/--pdf/... batch export uses.
|
||||
struct ExportGeometry {
|
||||
Int_t width{800}; ///< canvas width [pixel]
|
||||
Int_t height{600}; ///< canvas height [pixel]
|
||||
Double_t titleFrac{0.05}; ///< height fraction of the title block
|
||||
Double_t infoFrac{0.10}; ///< height fraction of the run-info block
|
||||
Double_t sideFrac{0.25}; ///< width fraction of the parameter/theory column
|
||||
Double_t splitFrac{0.50}; ///< height fraction, measured from the bottom, at
|
||||
///< which 'Fit parameters' and 'Theory' are split
|
||||
Double_t fontFrac{0.0}; ///< on-screen panel font height as a fraction of
|
||||
///< the window height; <= 0 lets ROOT scale the text
|
||||
/// Clips everything into a range which still gives a usable layout.
|
||||
void Sanitize();
|
||||
};
|
||||
|
||||
/// Writes the classic canvas layout into a graphics file named after the
|
||||
/// msr-file (batch export, e.g. --png). All panels are included, since
|
||||
/// there is no interactive view whose visibility state could be mirrored.
|
||||
/// msr-file (batch export, e.g. --png). All panels are included and the
|
||||
/// default geometry is used, since there is no interactive view whose
|
||||
/// state could be mirrored.
|
||||
Bool_t SaveGraphics(const std::string &graphicsFormat);
|
||||
/// Writes the classic canvas layout into the current working directory and
|
||||
/// returns the URL (relative to the web window) it can be downloaded from;
|
||||
/// used by the interactive "Save graphics" toolbar entry. \p panels mirrors
|
||||
/// which blocks the current view has shown/hidden.
|
||||
/// used by the interactive "Save graphics" toolbar entry. \p panels and
|
||||
/// \p geom mirror which blocks the current view has shown/hidden and how
|
||||
/// large the window and its panels currently are.
|
||||
Bool_t SaveGraphicsForDownload(const std::string &graphicsFormat, const PanelVisibility &panels,
|
||||
std::string &relUrl);
|
||||
const ExportGeometry &geom, std::string &relUrl);
|
||||
/// The name/location that graphics files are derived from by default,
|
||||
/// without extension.
|
||||
std::string DefaultGraphicsBaseName() const;
|
||||
@@ -194,8 +216,10 @@ class PMusrWebCanvas
|
||||
/// Draws the spectra of the current mode into the given pad (graphics export).
|
||||
Bool_t FillPlotPad(TVirtualPad *pad);
|
||||
/// Builds the classic layout and writes it into the given graphics file,
|
||||
/// including only the panels \p panels marks visible.
|
||||
Bool_t RenderGraphics(const std::string &fileName, const PanelVisibility &panels);
|
||||
/// including only the panels \p panels marks visible, sized and split
|
||||
/// according to \p geom.
|
||||
Bool_t RenderGraphics(const std::string &fileName, const PanelVisibility &panels,
|
||||
const ExportGeometry &geom);
|
||||
/// Serializes the objects of the current view for JSROOT.
|
||||
Bool_t BuildPlotJson(TString &json);
|
||||
|
||||
|
||||
@@ -597,6 +597,50 @@ makeSplitter($('side-split'), ev => {
|
||||
updateSideLayout();
|
||||
});
|
||||
|
||||
/** Size and proportions of the current view, handed to the C++ side with
|
||||
* 'Save graphics' so that the exported graphics mirror the window instead of
|
||||
* always using the fixed 800x600 layout of the batch export (see
|
||||
* PMusrWebCanvas::ExportGeometry / RenderGraphics()).
|
||||
*
|
||||
* The exported layout has neither a toolbar nor a status bar, so the size
|
||||
* reported here is the window without those two, and every fraction is
|
||||
* relative to that content area. A boundary which is a splitter bar on
|
||||
* screen is reported as the bar's centre line, since the export draws the
|
||||
* blocks edge to edge. The font size is passed on as well: it is what lets
|
||||
* the exported panels keep the size the text has on screen.
|
||||
*
|
||||
* Returns the values in the order the 'save:' message expects them, or null
|
||||
* if the window is too small to measure anything sensible. */
|
||||
function exportGeometry() {
|
||||
const appRect = $('app').getBoundingClientRect();
|
||||
const top = $('toolbar').getBoundingClientRect().bottom;
|
||||
const bottom = $('statusbar').getBoundingClientRect().top;
|
||||
|
||||
const w = Math.round(appRect.width);
|
||||
const h = Math.round(bottom - top);
|
||||
if (!(w > 50) || !(h > 50)) return null;
|
||||
|
||||
const fracY = v => clamp(v / h, 0, 1);
|
||||
const centreY = el => { const r = el.getBoundingClientRect(); return r.top + r.height/2; };
|
||||
const centreX = el => { const r = el.getBoundingClientRect(); return r.left + r.width/2; };
|
||||
|
||||
const sideVisible = panels.params.visible || panels.theory.visible;
|
||||
const bothVisible = panels.params.visible && panels.theory.visible;
|
||||
|
||||
const titleFrac = panels.title.visible
|
||||
? fracY($('titlebar').getBoundingClientRect().height) : 0;
|
||||
const infoFrac = panels.info.visible ? fracY(bottom - centreY($('hsplit'))) : 0;
|
||||
const sideFrac = sideVisible
|
||||
? clamp((appRect.right - centreX($('vsplit'))) / w, 0, 1) : 0;
|
||||
const splitFrac = bothVisible ? fracY(bottom - centreY($('side-split'))) : 0;
|
||||
|
||||
// the panel text size, as a fraction of the content height, is what the
|
||||
// exported panels are scaled with
|
||||
const fontPx = parseFloat(getComputedStyle($('params')).fontSize) || 12;
|
||||
|
||||
return [w, h, titleFrac, infoFrac, sideFrac, splitFrac, fontPx / h];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// plotting
|
||||
//--------------------------------------------------------------------------
|
||||
@@ -873,11 +917,16 @@ $('btn-export').onclick = () => {
|
||||
if (name) send('export:' + name);
|
||||
};
|
||||
|
||||
$('btn-save').onclick = () => send('save:' + $('sel-save-format').value + ':' +
|
||||
(panels.title.visible ? '1' : '0') +
|
||||
(panels.params.visible ? '1' : '0') +
|
||||
(panels.theory.visible ? '1' : '0') +
|
||||
(panels.info.visible ? '1' : '0'));
|
||||
$('btn-save').onclick = () => {
|
||||
const flags = (panels.title.visible ? '1' : '0') +
|
||||
(panels.params.visible ? '1' : '0') +
|
||||
(panels.theory.visible ? '1' : '0') +
|
||||
(panels.info.visible ? '1' : '0');
|
||||
const geom = exportGeometry();
|
||||
const geomMsg = geom
|
||||
? ':' + geom.map((v, i) => (i < 2 ? String(v) : v.toFixed(5))).join(':') : '';
|
||||
send('save:' + $('sel-save-format').value + ':' + flags + geomMsg);
|
||||
};
|
||||
|
||||
$('pan-title').onclick = () => togglePanel('title');
|
||||
$('pan-params').onclick = () => togglePanel('params');
|
||||
|
||||
Reference in new issue
Block a user