diff --git a/demo/node/hpainter.js b/demo/node/hpainter.js new file mode 100644 index 000000000..6f47452c9 --- /dev/null +++ b/demo/node/hpainter.js @@ -0,0 +1,42 @@ +// demo how HierarchyPainter can be used without direct display +// in batch display one just able to create images + + +import { version, HierarchyPainter, draw } from 'jsroot'; + + +console.log(`JSROOT version ${version}`); + + +const hp = new HierarchyPainter('hpainter'); + +// configure batch display to properly handle DOM in the node.js +hp.setDisplay('batch'); + +// catch draw function calls +hp.setDrawFunc((dom, obj, opt) => { + console.log(`trying to draw ${obj._typename}`); + return draw(dom, obj, opt); +}); + +await hp.openRootFile('https://root.cern/js/files/hsimple.root'); + +// display of TH2 histogram +await hp.display('hpxpy'); + +await hp.expandItem('ntuple'); + +// invoking TTree::Draw +await hp.display('ntuple/pz'); + + +// should be BatchDisplay +const disp = hp.getDisplay(); + +for (let id = 0; id < disp.numFrames(); ++id) { + const svg = await disp.makeSVG(id); + console.log(`Frame ${id} create svg size ${svg.length}`); + + // one can save svg plain file + // writeFileSync(`frame${id}.svg`, svg); +} diff --git a/modules/gui/HierarchyPainter.mjs b/modules/gui/HierarchyPainter.mjs index 1e6f2e6c5..12ec66051 100644 --- a/modules/gui/HierarchyPainter.mjs +++ b/modules/gui/HierarchyPainter.mjs @@ -825,6 +825,8 @@ class HierarchyPainter extends BasePainter { #one_by_one; // process drop items one by one #topname; // top item name #cached_draw_object; // cached object for first draw + #draw_func; // alternative draw function + #redraw_func; // alternative redraw function /** @summary Create painter * @param {string} name - symbolic name @@ -855,6 +857,23 @@ class HierarchyPainter extends BasePainter { this.textcolor = settings.DarkMode ? '#eee' : '#111'; } + /** @summary Set alternative draw/redraw functions + * @desc If only only draw function specified - it also will be used for re-drawing + * @protected */ + setDrawFunc(_draw, _redraw) { + if (isFunc(_draw)) { + this.#draw_func = _draw; + this.#redraw_func = isFunc(_redraw) ? _redraw : _draw; + } + } + + /** @summary Invoke configured draw or redraw function + * @protected */ + async callDrawFunc(dom, obj, opt, doredraw) { + const func = doredraw ? (this.#redraw_func || redraw) : (this.#draw_func || draw); + return func(dom, obj, opt); + } + /** @summary Cleanup hierarchy painter * @desc clear drawing and browser */ cleanup() { @@ -2279,10 +2298,8 @@ class HierarchyPainter extends BasePainter { if (use_dflt_opt && !drawopt && handle?.dflt && (handle.dflt !== kExpand)) drawopt = handle.dflt; - if (dom) { - const func = updating ? redraw : draw; - return func(dom, obj, drawopt).then(p => complete(p)).catch(err => complete(null, err)); - } + if (dom) + return this.callDrawFunc(dom, obj, drawopt, updating).then(p => complete(p)).catch(err => complete(null, err)); let did_activate = false; const arr = []; @@ -2326,9 +2343,9 @@ class HierarchyPainter extends BasePainter { cleanup(frame); mdi.activateFrame(frame); - return draw(frame, obj, drawopt) - .then(p => complete(p)) - .catch(err => complete(null, err)); + return this.callDrawFunc(frame, obj, drawopt) + .then(p => complete(p)) + .catch(err => complete(null, err)); }); }); } @@ -2434,7 +2451,7 @@ class HierarchyPainter extends BasePainter { if (isFunc(dom?.addPadButtons)) dom.addPadButtons(); - return draw(dom, res.obj, opt).then(p => drop_complete(p, mp === p)); + return this.callDrawFunc(dom, res.obj, opt).then(p => drop_complete(p, mp === p)); }); } @@ -3601,7 +3618,7 @@ class HierarchyPainter extends BasePainter { } // check that we can found frame where drawing should be done - if (!document.getElementById(this.disp_frameid)) + if (!this.disp_frameid || !document.getElementById(this.disp_frameid)) return null; if (isBatchMode())