diff --git a/demo/node/hpainter.js b/demo/node/hpainter.js index 6f47452c9..bbcf3b97e 100644 --- a/demo/node/hpainter.js +++ b/demo/node/hpainter.js @@ -2,8 +2,8 @@ // in batch display one just able to create images -import { version, HierarchyPainter, draw } from 'jsroot'; - +import { version, HierarchyPainter, draw, addDrawFunc } from 'jsroot'; +// import { writeFileSync } from 'fs'; console.log(`JSROOT version ${version}`); @@ -14,27 +14,34 @@ const hp = new HierarchyPainter('hpainter'); hp.setDisplay('batch'); // catch draw function calls -hp.setDrawFunc((dom, obj, opt) => { - console.log(`trying to draw ${obj._typename}`); - return draw(dom, obj, opt); +addDrawFunc({ + name: '*', + func: (dom, obj, opt) => { + console.log(`Actual draw of ${obj._typename}`); + // if function return true no normal drawing will be performed + // do not try to call `draw` function from here !!! + // return true; + } }); await hp.openRootFile('https://root.cern/js/files/hsimple.root'); // display of TH2 histogram +console.log('Invoke histogram drawing'); await hp.display('hpxpy'); await hp.expandItem('ntuple'); // invoking TTree::Draw +console.log('Invoke TBranch drawing'); 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 diff --git a/modules/draw.mjs b/modules/draw.mjs index d2b793423..9f763bd66 100644 --- a/modules/draw.mjs +++ b/modules/draw.mjs @@ -130,14 +130,14 @@ const drawFuncs = { lst: [ { name: 'kind:Command', icon: 'img_execute', execute: true }, { name: 'TFolder', icon: 'img_folder', icon2: 'img_folderopen', noinspect: true, get_expand: () => import_h().then(h => h.folderHierarchy) }, { name: 'TTask', icon: 'img_task', get_expand: () => import_h().then(h => h.taskHierarchy), for_derived: true }, - { name: clTTree, icon: 'img_tree', get_expand: () => import('./tree.mjs').then(h => h.treeHierarchy), draw: () => import_tree().then(h => h.drawTree), dflt: 'expand', opt: 'player;testio', shift: kInspect, pm: true }, + { name: clTTree, icon: 'img_tree', get_expand: () => import('./tree.mjs').then(h => h.treeHierarchy), draw: () => import_tree().then(h => h.drawTree), dflt: 'expand', opt: 'player;testio', shift: kInspect, pm: true, transform: true }, { name: 'TNtuple', sameas: clTTree }, { name: 'TNtupleD', sameas: clTTree }, - { name: clTBranchFunc, icon: 'img_leaf_method', draw: () => import_tree().then(h => h.drawTree), opt: ';dump', noinspect: true }, - { name: /^TBranch/, icon: 'img_branch', draw: () => import_tree().then(h => h.drawTree), dflt: 'expand', opt: ';dump', ctrl: 'dump', shift: kInspect, ignore_online: true, always_draw: true }, - { name: /^TLeaf/, icon: 'img_leaf', noexpand: true, draw: () => import_tree().then(h => h.drawTree), opt: ';dump', ctrl: 'dump', ignore_online: true, always_draw: true }, - { name: 'ROOT::RNTuple', icon: 'img_tree', get_expand: () => import('./rntuple.mjs').then(h => h.tupleHierarchy), draw: () => import('./draw/RNTuple.mjs').then(h => h.drawRNTuple), dflt: 'expand', pm: true }, - { name: 'ROOT::RNTupleField', icon: 'img_leaf', draw: () => import('./draw/RNTuple.mjs').then(h => h.drawRNTuple), opt: ';dump', ctrl: 'dump', shift: kInspect, ignore_online: true, always_draw: true }, + { name: clTBranchFunc, icon: 'img_leaf_method', draw: () => import_tree().then(h => h.drawTree), opt: ';dump', noinspect: true, transform: true }, + { name: /^TBranch/, icon: 'img_branch', draw: () => import_tree().then(h => h.drawTree), dflt: 'expand', opt: ';dump', ctrl: 'dump', shift: kInspect, ignore_online: true, always_draw: true, transform: true }, + { name: /^TLeaf/, icon: 'img_leaf', noexpand: true, draw: () => import_tree().then(h => h.drawTree), opt: ';dump', ctrl: 'dump', ignore_online: true, always_draw: true, transform: true }, + { name: 'ROOT::RNTuple', icon: 'img_tree', get_expand: () => import('./rntuple.mjs').then(h => h.tupleHierarchy), draw: () => import('./draw/RNTuple.mjs').then(h => h.drawRNTuple), dflt: 'expand', pm: true, transform: true }, + { name: 'ROOT::RNTupleField', icon: 'img_leaf', draw: () => import('./draw/RNTuple.mjs').then(h => h.drawRNTuple), opt: ';dump', ctrl: 'dump', shift: kInspect, ignore_online: true, always_draw: true, transform: true }, { name: clTList, icon: 'img_list', draw: () => import_h().then(h => h.drawList), get_expand: () => import_h().then(h => h.listHierarchy), dflt: 'expand' }, { name: clTHashList, sameas: clTList }, { name: clTObjArray, sameas: clTList }, @@ -169,19 +169,28 @@ const drawFuncs = { lst: [ /** @summary Register draw function for the class - * @desc List of supported draw options could be provided, separated with ';' * @param {object} args - arguments - * @param {string|regexp} args.name - class name or regexp pattern + * @param {string|regexp} args.name - class name or regexp pattern or '*' * @param {function} [args.func] - draw function + * @param {string} [args.sameas] - let behave same as specified class * @param {function} [args.draw] - async function to load draw function * @param {function} [args.class] - async function to load painter class with static draw function * @param {boolean} [args.direct] - if true, function is just Redraw() method of ObjectPainter * @param {string} [args.opt] - list of supported draw options (separated with semicolon) like 'col;scat;' * @param {string} [args.icon] - icon name shown for the class in hierarchy browser * @param {string} [args.draw_field] - draw only data member from object, like fHistogram + * @param {string} [args.noinspect] - disable inspect + * @param {string} [args.noexpand] - disable expand + * @param {string} [args.pm] - always show plus or minus sign even when no child items exists + * @desc List of supported draw options could be provided, separated with ';' + * If args.name parameter is '*', function will be invoked before object drawing. + * If such function does not return value - normal drawing will be continued. * @protected */ function addDrawFunc(args) { - drawFuncs.lst.push(args); + if (args?.name === '*') + internals._alt_draw = isFunc(args.func) ? args.func : null; + else + drawFuncs.lst.push(args); return args; } @@ -392,6 +401,12 @@ async function draw(dom, obj, opt) { if (handle.draw_field && obj[handle.draw_field]) return draw(dom, obj[handle.draw_field], opt || handle.draw_field_opt); + if (internals._alt_draw && !handle.transform) { + const v = internals._alt_draw(dom, obj, opt); + if (v) + return v; + } + if (!canDrawHandle(handle)) { if (opt && (opt.indexOf('same') >= 0)) { const main_painter = getElementMainPainter(dom); diff --git a/modules/draw/TTree.mjs b/modules/draw/TTree.mjs index 667a05715..3716e10b2 100644 --- a/modules/draw/TTree.mjs +++ b/modules/draw/TTree.mjs @@ -56,6 +56,12 @@ async function drawTreeDrawResult(dom, obj, opt) { if (!typ || !isStr(typ)) return Promise.reject(Error('Object without type cannot be draw with TTree')); + if (internals._alt_draw) { + const v = internals._alt_draw(dom, obj, opt); + if (v) + return v; + } + if (typ.indexOf(clTH1) === 0) return TH1Painter.draw(dom, obj, opt); if (typ.indexOf(clTH2) === 0) diff --git a/modules/gui/HierarchyPainter.mjs b/modules/gui/HierarchyPainter.mjs index e0978e9ce..7d5065bf6 100644 --- a/modules/gui/HierarchyPainter.mjs +++ b/modules/gui/HierarchyPainter.mjs @@ -825,8 +825,6 @@ 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 @@ -857,23 +855,6 @@ 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() { @@ -2313,7 +2294,7 @@ class HierarchyPainter extends BasePainter { drawopt = handle.dflt; if (dom) - return this.callDrawFunc(dom, obj, drawopt, updating).then(p => complete(p)).catch(err => complete(null, err)); + return (updating ? redraw : draw)(dom, obj, drawopt).then(p => complete(p)).catch(err => complete(null, err)); let did_activate = false; const arr = []; @@ -2357,7 +2338,7 @@ class HierarchyPainter extends BasePainter { cleanup(frame); mdi.activateFrame(frame); - return this.callDrawFunc(frame, obj, drawopt) + return draw(frame, obj, drawopt) .then(p => complete(p)) .catch(err => complete(null, err)); }); @@ -2465,7 +2446,7 @@ class HierarchyPainter extends BasePainter { if (isFunc(dom?.addPadButtons)) dom.addPadButtons(); - return this.callDrawFunc(dom, res.obj, opt).then(p => drop_complete(p, mp === p)); + return draw(dom, res.obj, opt).then(p => drop_complete(p, mp === p)); }); }