@@ -14,7 +14,7 @@ const version_id = 'dev',
1414
1515/** @summary version date
1616 * @desc Release date in format day/month/year like '14/04/2022' */
17- version_date = '17 /02/2026',
17+ version_date = '18 /02/2026',
1818
1919/** @summary version id and date
2020 * @desc Produced by concatenation of {@link version_id} and {@link version_date}
@@ -79082,6 +79082,20 @@ class JSRootMenu {
7908279082 this.endsub();
7908379083 }
7908479084
79085+ /** @summary Add log scale selection for pad
79086+ * @protected */
79087+ addPadLogMenu(kind, value, func) {
79088+ this.sub('SetLog ' + kind,
79089+ () => this.input('Enter log kind: 0 - off, 1 - log10, 2 - log2, 3 - ln, ...', value, 'int', 0, 10000).then(func));
79090+ this.addchk(value === 0, 'linear', () => func(0));
79091+ this.addchk(value === 1, 'log10', () => func(1));
79092+ this.addchk(value === 2, 'log2', () => func(2));
79093+ this.addchk(value === 3, 'ln', () => func(3));
79094+ this.addchk(value === 4, 'log4', () => func(4));
79095+ this.addchk(value === 8, 'log8', () => func(8));
79096+ this.endsub();
79097+ }
79098+
7908579099 /** @summary Add palette menu entries
7908679100 * @protected */
7908779101 addPaletteMenu(curr, set_func) {
@@ -79527,38 +79541,40 @@ class JSRootMenu {
7952779541 faxis.fNdivisions = val; painter.interactiveRedraw('pad', `exec:SetNdivisions(${val})`, kind);
7952879542 }));
7952979543
79530- this.sub('Labels');
79531- this.addchk(faxis.TestBit(EAxisBits.kCenterLabels), 'Center',
79532- arg => { faxis.SetBit(EAxisBits.kCenterLabels, arg); painter.interactiveRedraw('pad', `exec:CenterLabels(${arg})`, kind); });
79533- this.addchk(faxis.TestBit(EAxisBits.kLabelsVert), 'Rotate',
79534- arg => { faxis.SetBit(EAxisBits.kLabelsVert, arg); painter.interactiveRedraw('pad', `exec:SetBit(TAxis::kLabelsVert,${arg})`, kind); });
79535- this.addColorMenu('Color', faxis.fLabelColor,
79536- arg => { faxis.fLabelColor = arg; painter.interactiveRedraw('pad', getColorExec(arg, 'SetLabelColor'), kind); });
79537- this.addSizeMenu('Offset', -0.02, 0.1, 0.01, faxis.fLabelOffset,
79538- arg => { faxis.fLabelOffset = arg; painter.interactiveRedraw('pad', `exec:SetLabelOffset(${arg})`, kind); });
79539- let a = faxis.fLabelSize >= 1;
79540- this.addSizeMenu('Size', a ? 2 : 0.02, a ? 30 : 0.11, a ? 2 : 0.01, faxis.fLabelSize,
79541- arg => { faxis.fLabelSize = arg; painter.interactiveRedraw('pad', `exec:SetLabelSize(${arg})`, kind); });
79542-
79543- if (frame_painter && (axis_painter?.kind === kAxisLabels) && (faxis.fNbins > 20)) {
79544- this.add('Find label', () => this.input('Label id').then(id => {
79545- if (!id)
79546- return;
79547- for (let bin = 0; bin < faxis.fNbins; ++bin) {
79548- const lbl = axis_painter.formatLabels(bin);
79549- if (lbl === id)
79550- return frame_painter.zoomSingle(kind, Math.max(0, bin - 4), Math.min(faxis.fNbins, bin + 5));
79551- }
79552- }), 'Zoom into region around specific label');
79553- }
79554- if (frame_painter && faxis.fLabels) {
79555- const ignore = `${kind}_ignore_labels`;
79556- this.addchk(!frame_painter[ignore], 'Custom', flag => {
79557- frame_painter[ignore] = !flag;
79558- painter.interactiveRedraw('pad');
79559- }, `Use of custom labels in axis ${kind}`);
79544+ if (kind !== 'v') {
79545+ this.sub('Labels');
79546+ this.addchk(faxis.TestBit(EAxisBits.kCenterLabels), 'Center',
79547+ arg => { faxis.SetBit(EAxisBits.kCenterLabels, arg); painter.interactiveRedraw('pad', `exec:CenterLabels(${arg})`, kind); });
79548+ this.addchk(faxis.TestBit(EAxisBits.kLabelsVert), 'Rotate',
79549+ arg => { faxis.SetBit(EAxisBits.kLabelsVert, arg); painter.interactiveRedraw('pad', `exec:SetBit(TAxis::kLabelsVert,${arg})`, kind); });
79550+ this.addColorMenu('Color', faxis.fLabelColor,
79551+ arg => { faxis.fLabelColor = arg; painter.interactiveRedraw('pad', getColorExec(arg, 'SetLabelColor'), kind); });
79552+ this.addSizeMenu('Offset', -0.02, 0.1, 0.01, faxis.fLabelOffset,
79553+ arg => { faxis.fLabelOffset = arg; painter.interactiveRedraw('pad', `exec:SetLabelOffset(${arg})`, kind); });
79554+ const a = faxis.fLabelSize >= 1;
79555+ this.addSizeMenu('Size', a ? 2 : 0.02, a ? 30 : 0.11, a ? 2 : 0.01, faxis.fLabelSize,
79556+ arg => { faxis.fLabelSize = arg; painter.interactiveRedraw('pad', `exec:SetLabelSize(${arg})`, kind); });
79557+
79558+ if (frame_painter && (axis_painter?.kind === kAxisLabels) && (faxis.fNbins > 20)) {
79559+ this.add('Find label', () => this.input('Label id').then(id => {
79560+ if (!id)
79561+ return;
79562+ for (let bin = 0; bin < faxis.fNbins; ++bin) {
79563+ const lbl = axis_painter.formatLabels(bin);
79564+ if (lbl === id)
79565+ return frame_painter.zoomSingle(kind, Math.max(0, bin - 4), Math.min(faxis.fNbins, bin + 5));
79566+ }
79567+ }), 'Zoom into region around specific label');
79568+ }
79569+ if (frame_painter && faxis.fLabels) {
79570+ const ignore = `${kind}_ignore_labels`;
79571+ this.addchk(!frame_painter[ignore], 'Custom', flag => {
79572+ frame_painter[ignore] = !flag;
79573+ painter.interactiveRedraw('pad');
79574+ }, `Use of custom labels in axis ${kind}`);
79575+ }
79576+ this.endsub();
7956079577 }
79561- this.endsub();
7956279578
7956379579 this.sub('Title');
7956479580 this.add('SetTitle', () => {
@@ -79585,8 +79601,8 @@ class JSRootMenu {
7958579601 });
7958679602 this.addSizeMenu('Offset', 0, 3, 0.2, faxis.fTitleOffset,
7958779603 arg => { faxis.fTitleOffset = arg; painter.interactiveRedraw('pad', `exec:SetTitleOffset(${arg})`, kind); });
79588- a = faxis.fTitleSize >= 1;
79589- this.addSizeMenu('Size', a ? 2 : 0.02, a ? 30 : 0.11, a ? 2 : 0.01, faxis.fTitleSize,
79604+ const p = faxis.fTitleSize >= 1;
79605+ this.addSizeMenu('Size', p ? 2 : 0.02, p ? 30 : 0.11, p ? 2 : 0.01, faxis.fTitleSize,
7959079606 arg => { faxis.fTitleSize = arg; painter.interactiveRedraw('pad', `exec:SetTitleSize(${arg})`, kind); });
7959179607 this.endsub();
7959279608
@@ -84958,21 +84974,9 @@ class TFramePainter extends FrameInteractive {
8495884974 }
8495984975 menu.endsub();
8496084976
84961- if (pad) {
84962- const member = 'fLog' + kind[0];
84963- menu.sub('SetLog ' + kind[0], () => {
84964- menu.input('Enter log kind: 0 - off, 1 - log10, 2 - log2, 3 - ln, ...', pad[member], 'int', 0, 10000).then(v => {
84965- this.changeAxisLog(kind[0], v);
84966- });
84967- });
84968- menu.addchk(pad[member] === 0, 'linear', () => this.changeAxisLog(kind[0], 0));
84969- menu.addchk(pad[member] === 1, 'log10', () => this.changeAxisLog(kind[0], 1));
84970- menu.addchk(pad[member] === 2, 'log2', () => this.changeAxisLog(kind[0], 2));
84971- menu.addchk(pad[member] === 3, 'ln', () => this.changeAxisLog(kind[0], 3));
84972- menu.addchk(pad[member] === 4, 'log4', () => this.changeAxisLog(kind[0], 4));
84973- menu.addchk(pad[member] === 8, 'log8', () => this.changeAxisLog(kind[0], 8));
84974- menu.endsub();
84975- }
84977+ if (pad)
84978+ menu.addPadLogMenu(kind[0], pad[`fLog${kind[0]}`], v => this.changeAxisLog(kind[0], v));
84979+
8497684980 menu.addchk(faxis.TestBit(EAxisBits.kMoreLogLabels), 'More log', flag => {
8497784981 faxis.SetBit(EAxisBits.kMoreLogLabels, flag);
8497884982 if (hist_painter?.getSnapId() && (kind.length === 1))
@@ -92275,10 +92279,10 @@ class TPavePainter extends ObjectPainter {
9227592279 height = pp.getPadHeight(),
9227692280 pad = pp.getRootPad(true),
9227792281 main = palette.$main_painter || this.getMainPainter(),
92282+ is_th3 = isFunc(main.getDimension) && (main.getDimension() === 3),
9227892283 fp = this.getFramePainter(),
92279- contour = main.getContour(false ),
92284+ contour = main.getContour(is_th3 ),
9228092285 levels = contour?.getLevels(),
92281- is_th3 = isFunc(main.getDimension) && (main.getDimension() === 3),
9228292286 is_scatter = isFunc(main.getZaxis),
9228392287 log = pad?.fLogv ?? (is_th3 ? false : pad?.fLogz),
9228492288 draw_palette = main.getHistPalette(),
@@ -92288,6 +92292,7 @@ class TPavePainter extends ObjectPainter {
9228892292 let zmin = 0, zmax = 100, gzmin, gzmax, axis_transform, axis_second = 0;
9228992293
9229092294 this.#palette_vertical = (palette.fX2NDC - palette.fX1NDC) < (palette.fY2NDC - palette.fY1NDC);
92295+ this.is_th3 = is_th3;
9229192296
9229292297 axis.fTickSize = 0.03; // adjust axis ticks size
9229392298
@@ -92324,12 +92329,16 @@ class TPavePainter extends ObjectPainter {
9232492329 }
9232592330 } else if ((main.gmaxbin !== undefined) && (main.gminbin !== undefined)) {
9232692331 // this is case of TH2 (needs only for size adjustment)
92327- zmin = main.gminbin;
92328- zmax = main.gmaxbin;
92332+ gzmin = zmin = main.gminbin;
92333+ gzmax = zmax = main.gmaxbin;
92334+ if (contour?.colzmin !== undefined && contour?.colzmax !== undefined) {
92335+ zmin = contour.colzmin;
92336+ zmax = contour.colzmax;
92337+ }
9232992338 } else if ((main.hmin !== undefined) && (main.hmax !== undefined)) {
9233092339 // this is case of TH1
92331- zmin = main.hmin;
92332- zmax = main.hmax;
92340+ gzmin = zmin = main.hmin;
92341+ gzmax = zmax = main.hmax;
9233392342 }
9233492343
9233592344 g.selectAll('rect').style('fill', 'white');
@@ -92501,6 +92510,17 @@ class TPavePainter extends ObjectPainter {
9250192510 zoom_rect.attr('x', Math.min(sel1, sel2))
9250292511 .attr('width', Math.abs(sel2 - sel1));
9250392512 }
92513+ }, zoomPalette = (z1, z2) => {
92514+ if (!this.is_th3)
92515+ return this.getFramePainter().zoomSingle('z', z1, z2, true);
92516+ const maino = this.getMainPainter().options;
92517+ if (z1 === z2)
92518+ maino.minimum = maino.maximum = kNoZoom;
92519+ else {
92520+ maino.minimum = z1;
92521+ maino.maximum = z2;
92522+ }
92523+ this.interactiveRedraw('pad');
9250492524 }, endRectSel = evnt => {
9250592525 if (!doing_zoom)
9250692526 return;
@@ -92512,10 +92532,13 @@ class TPavePainter extends ObjectPainter {
9251292532 zoom_rect = null;
9251392533 doing_zoom = false;
9251492534
92535+ if (sel1 === sel2)
92536+ return;
92537+
9251592538 const z1 = this.z_handle.revertPoint(sel1),
9251692539 z2 = this.z_handle.revertPoint(sel2);
9251792540
92518- this.getFramePainter().zoomSingle('z', Math.min(z1, z2), Math.max(z1, z2), true );
92541+ zoomPalette( Math.min(z1, z2), Math.max(z1, z2));
9251992542 }, startRectSel = evnt => {
9252092543 // ignore when touch selection is activated
9252192544 if (doing_zoom)
@@ -92550,7 +92573,7 @@ class TPavePainter extends ObjectPainter {
9255092573 if (settings.Zooming) {
9255192574 this.getG().selectAll('.axis_zoom')
9255292575 .on('mousedown', startRectSel)
92553- .on('dblclick', () => this.getFramePainter().zoomSingle('z', 0, 0, true ));
92576+ .on('dblclick', () => zoomPalette( 0, 0));
9255492577 }
9255592578
9255692579 if (settings.ZoomWheel) {
@@ -92559,7 +92582,7 @@ class TPavePainter extends ObjectPainter {
9255992582 coord = this.#palette_vertical ? (1 - pos[1] / s_height) : pos[0] / s_width,
9256092583 item = this.z_handle.analyzeWheelEvent(evnt, coord);
9256192584 if (item?.changed)
92562- this.getFramePainter().zoomSingle('z', item.min, item.max, true );
92585+ zoomPalette( item.min, item.max);
9256392586 });
9256492587 }
9256592588 }
@@ -92764,12 +92787,51 @@ class TPavePainter extends ObjectPainter {
9276492787
9276592788 /** @summary Show pave context menu */
9276692789 paveContextMenu(evnt) {
92767- if (this.z_handle) {
92790+ if (!this.z_handle)
92791+ return showPainterMenu(evnt, this);
92792+ if (!this.is_th3) {
9276892793 const fp = this.getFramePainter();
9276992794 if (isFunc(fp?.showContextMenu))
9277092795 fp.showContextMenu('pal', evnt);
92771- } else
92772- showPainterMenu(evnt, this);
92796+ return;
92797+ }
92798+
92799+ const pp = this.getPadPainter(),
92800+ pad = pp?.getRootPad(true),
92801+ faxis = this.z_handle.getObject(),
92802+ hist_painter = this.z_handle.hist_painter || this.getMainPainter(true);
92803+
92804+ if (!pad || !hist_painter)
92805+ return;
92806+
92807+ if (isFunc(evnt?.stopPropagation)) {
92808+ evnt.preventDefault();
92809+ evnt.stopPropagation(); // disable main context menu
92810+ }
92811+
92812+ createMenu(evnt, this).then(menu => {
92813+ menu.header('V axis', `${urlClassPrefix}${clTAxis}.html`);
92814+
92815+ menu.addPadLogMenu('v', pad.fLogv || 0, v => {
92816+ pad.fLogv = v;
92817+ this.interactiveRedraw('pad', 'log');
92818+ });
92819+
92820+ menu.addchk(faxis.TestBit(EAxisBits.kMoreLogLabels), 'More log', flag => {
92821+ faxis.SetBit(EAxisBits.kMoreLogLabels, flag);
92822+ this.interactiveRedraw('pad');
92823+ });
92824+ menu.addchk(faxis.TestBit(EAxisBits.kNoExponent), 'No exponent', flag => {
92825+ faxis.SetBit(EAxisBits.kNoExponent, flag);
92826+ this.interactiveRedraw('pad');
92827+ });
92828+
92829+ hist_painter.fillPaletteMenu(menu, false);
92830+
92831+ menu.addTAxisMenu(EAxisBits, hist_painter || this, faxis, 'v', this.z_handle, null);
92832+
92833+ menu.show();
92834+ });
9277392835 }
9277492836
9277592837 /** @summary Returns true when stat box is drawn */
@@ -95411,6 +95473,12 @@ class THistPainter extends ObjectPainter {
9541195473 return cntr;
9541295474 }
9541395475
95476+ /** @summary Reset contour object
95477+ * @private */
95478+ resetContour() {
95479+ this.#contour = undefined;
95480+ }
95481+
9541495482 /** @summary Return Z-scale ranges to create contour */
9541595483 #getContourRanges(main, fp) {
9541695484 const o = this.getOptions(),
@@ -95991,7 +96059,7 @@ class THistPainter extends ObjectPainter {
9599196059 this.maxbin = this.minbin = 0;
9599296060
9599396061 // force recalculation of z levels
95994- this.#contour = undefined ;
96062+ this.resetContour() ;
9599596063
9599696064 if (args.zrange)
9599796065 Object.assign(res, this.#getContourRanges(this.getMainPainter(), this.getFramePainter()));
@@ -104654,6 +104722,9 @@ class TH3Painter extends THistPainter {
104654104722 const histo = this.getHisto(),
104655104723 fp = this.getFramePainter();
104656104724
104725+ // ensure proper colors
104726+ this.resetContour();
104727+
104657104728 let use_lambert = false,
104658104729 use_helper = false, use_colors = false, use_opacity = 1, exclude_content = -1,
104659104730 logv = this.getPadPainter()?.getRootPad()?.fLogv,
@@ -104727,19 +104798,6 @@ class TH3Painter extends THistPainter {
104727104798 } else if (use_scale)
104728104799 use_scale = (this.gminbin || this.gmaxbin) ? 1 / Math.max(Math.abs(this.gminbin), Math.abs(this.gmaxbin)) : 1;
104729104800
104730- const get_bin_weight = content => {
104731- if ((exclude_content >= 0) && (content < exclude_content))
104732- return 0;
104733- if (!use_scale)
104734- return 1;
104735- if (logv) {
104736- if (content <= 0)
104737- return 0;
104738- content = Math.log(content) - scale_offset;
104739- }
104740- return Math.pow(Math.abs(content * use_scale), 0.3333);
104741- };
104742- // eslint-disable-next-line one-var
104743104801 const i1 = this.getSelectIndex('x', 'left', 0.5),
104744104802 i2 = this.getSelectIndex('x', 'right', 0),
104745104803 j1 = this.getSelectIndex('y', 'left', 0.5),
@@ -104753,7 +104811,19 @@ class TH3Painter extends THistPainter {
104753104811 const cntr = use_colors ? this.getContour() : null,
104754104812 palette = use_colors ? this.getHistPalette() : null,
104755104813 bins_matrixes = [], bins_colors = [], bins_ids = [], negative_matrixes = [], bin_opacities = [],
104756- transfer = (this.transferFunc && proivdeEvalPar(this.transferFunc, true)) ? this.transferFunc : null;
104814+ transfer = (this.transferFunc && proivdeEvalPar(this.transferFunc, true)) ? this.transferFunc : null,
104815+ get_bin_weight = content => {
104816+ if ((exclude_content >= 0) && (content < exclude_content))
104817+ return 0;
104818+ if (!use_scale)
104819+ return 1;
104820+ if (logv) {
104821+ if (content <= 0)
104822+ return 0;
104823+ content = Math.log(content) - scale_offset;
104824+ }
104825+ return Math.pow(Math.abs(content * use_scale), 0.3333);
104826+ };
104757104827
104758104828 for (let i = i1; i < i2; ++i) {
104759104829 const grx1 = fp.grx(histo.fXaxis.GetBinLowEdge(i + 1)),
0 commit comments