From 0000d4fa50eb2a3b703fa5c3452fbc5eb1571215 Mon Sep 17 00:00:00 2001 From: Sergey Linev <S.Linev@gsi.de> Date: Thu, 23 Mar 2023 15:05:00 +0100 Subject: [PATCH] [jsroot] support projx2_y5 draw option Allows to configure different bins width for X and Y projections --- js/build/jsroot.js | 162 +++++++++++++++++++---------- js/changes.md | 2 +- js/modules/hist2d/RH2Painter.mjs | 103 ++++++++++++------ js/modules/hist2d/TH2Painter.mjs | 57 ++++++---- js/modules/hist2d/THistPainter.mjs | 4 +- 5 files changed, 220 insertions(+), 108 deletions(-) diff --git a/js/build/jsroot.js b/js/build/jsroot.js index 3559f92cf76..5608d0a8e5f 100644 --- a/js/build/jsroot.js +++ b/js/build/jsroot.js @@ -73703,8 +73703,8 @@ class THistDrawOptions { if (this.Proj > 0) this.Contour = 14; if (d.check('PROJXY', true)) this.Project = 'XY' + d.partAsInt(0,1); - if (d.check('PROJX', true)) this.Project = 'X' + d.partAsInt(0,1); - if (d.check('PROJY', true)) this.Project = 'Y' + d.partAsInt(0,1); + if (d.check('PROJX', true)) this.Project = 'X' + d.part; + if (d.check('PROJY', true)) this.Project = 'Y' + d.part; if (d.check('PROJ')) this.Project = 'Y1'; if (check3dbox) { @@ -76953,22 +76953,37 @@ let TH2Painter$2 = class TH2Painter extends THistPainter { if ((kind == 'Projections') || (kind == 'Off')) kind = ''; + let widthX = width, widthY = width; + if (isStr(kind) && (kind.indexOf('XY') == 0)) { - if (kind.length > 2) - width = parseInt(kind.slice(2)); + let ws = (kind.length > 2) ? kind.slice(2) : ''; kind = 'XY'; + widthX = widthY = parseInt(ws) || 1; } else if (isStr(kind) && (kind.length > 1)) { - width = parseInt(kind.slice(1)); - kind = kind[0]; + let ps = kind.indexOf('_'); + if ((ps > 0) && (kind[0] == 'X') && (kind[ps+1] == 'Y')) { + widthX = parseInt(kind.slice(1, ps)) || 1; + widthY = parseInt(kind.slice(ps+2)) || 1; + kind = 'XY'; + } else if ((ps > 0) && (kind[0] == 'Y') && (kind[ps+1] == 'X')) { + widthY = parseInt(kind.slice(1, ps)) || 1; + widthX = parseInt(kind.slice(ps+2)) || 1; + kind = 'XY'; + } else { + widthX = widthY = parseInt(kind.slice(1)) || 1; + kind = kind[0]; + } } - if (!width) width = 1; + if (!widthX && !widthY) + widthX = widthY = 1; if (kind && (this.is_projection == kind)) { - if (this.projection_width === width) { + if ((this.projection_widthX === widthX) && (this.projection_widthY === widthY)) { kind = ''; } else { - this.projection_width = width; + this.projection_widthX = widthX; + this.projection_widthY = widthY; return; } } @@ -76976,7 +76991,8 @@ let TH2Painter$2 = class TH2Painter extends THistPainter { delete this.proj_hist; let new_proj = (this.is_projection === kind) ? '' : kind; - this.projection_width = width; + this.projection_widthX = widthX; + this.projection_widthY = widthY; this.is_projection = ''; // avoid projection handling until area is created this.provideSpecialDrawArea(new_proj).then(() => { this.is_projection = new_proj; return this.redrawProjection(); }); @@ -77105,11 +77121,15 @@ let TH2Painter$2 = class TH2Painter extends THistPainter { /** @summary Fill histogram context menu */ fillHistContextMenu(menu) { if (!this.isTH2Poly()) { - menu.add('sub:Projections', () => this.toggleProjection()); let kind = this.is_projection || ''; - if (kind) kind += this.projection_width; + if (kind) kind += this.projection_widthX; + if ((this.projection_widthX != this.projection_widthY) && (this.is_projection == 'XY')) + kind = `X${this.projection_widthX}_Y${this.projection_widthY}`; + const kinds = ['X1', 'X2', 'X3', 'X5', 'X10', 'Y1', 'Y2', 'Y3', 'Y5', 'Y10', 'XY1', 'XY2', 'XY3', 'XY5', 'XY10']; - if (this.is_projection) kinds.push('Off'); + if (kind) kinds.unshift('Off'); + + menu.add('sub:Projections', () => menu.input('Input projection kind X1 or XY2', kind, 'string').then(val => this.toggleProjection(val))); for (let k = 0; k < kinds.length; ++k) menu.addchk(kind==kinds[k], kinds[k], kinds[k], arg => this.toggleProjection(arg)); menu.add('endsub:'); @@ -79550,14 +79570,15 @@ let TH2Painter$2 = class TH2Painter extends THistPainter { let binid = i*10000 + j, path; if (this.is_projection) { - let pw = this.projection_width || 1, dd = (pw - 1) / 2; - if ((this.is_projection.indexOf('X')) >= 0 && (pw > 1)) { - if (j2+dd >= h.j2) { j2 = Math.min(Math.round(j2+dd), h.j2); j1 = Math.max(j2 - pw, h.j1); } - else { j1 = Math.max(Math.round(j1-dd), h.j1); j2 = Math.min(j1 + pw, h.j2); } + let pwx = this.projection_widthX || 1, ddx = (pwx - 1) / 2; + if ((this.is_projection.indexOf('X')) >= 0 && (pwx > 1)) { + if (j2+ddx >= h.j2) { j2 = Math.min(Math.round(j2+ddx), h.j2); j1 = Math.max(j2-pwx, h.j1); } + else { j1 = Math.max(Math.round(j1-ddx), h.j1); j2 = Math.min(j1+pwx, h.j2); } } - if ((this.is_projection.indexOf('Y')) >= 0 && (pw > 1)) { - if (i2+dd >= h.i2) { i2 = Math.min(Math.round(i2+dd), h.i2); i1 = Math.max(i2 - pw, h.i1); } - else { i1 = Math.max(Math.round(i1-dd), h.i1); i2 = Math.min(i1 + pw, h.i2); } + let pwy = this.projection_widthY || 1, ddy = (pwy - 1) / 2; + if ((this.is_projection.indexOf('Y')) >= 0 && (pwy > 1)) { + if (i2+ddy >= h.i2) { i2 = Math.min(Math.round(i2+ddy), h.i2); i1 = Math.max(i2-pwy, h.i1); } + else { i1 = Math.max(Math.round(i1-ddy), h.i1); i2 = Math.min(i1+pwy, h.i2); } } } @@ -119367,20 +119388,40 @@ let RH2Painter$2 = class RH2Painter extends RHistPainter { /** @summary Toggle projection */ toggleProjection(kind, width) { - if (kind == 'Projections') kind = ''; + if ((kind == 'Projections') || (kind == 'Off')) + kind = ''; + + let widthX = width, widthY = width; - if (isStr(kind) && (kind.length > 1)) { - width = parseInt(kind.slice(1)); - kind = kind[0]; + if (isStr(kind) && (kind.indexOf('XY') == 0)) { + let ws = kind.length > 2 ? kind.slice(2) : ''; + kind = 'XY'; + widthX = widthY = parseInt(ws); + } else if (isStr(kind) && (kind.length > 1)) { + let ps = kind.indexOf('_'); + if ((ps > 0) && (kind[0] == 'X') && (kind[ps+1] == 'Y')) { + widthX = parseInt(kind.slice(1, ps)) || 1; + widthY = parseInt(kind.slice(ps+2)) || 1; + kind = 'XY'; + } else if ((ps > 0) && (kind[0] == 'Y') && (kind[ps+1] == 'X')) { + widthY = parseInt(kind.slice(1, ps)) || 1; + widthX = parseInt(kind.slice(ps+2)) || 1; + kind = 'XY'; + } else { + widthX = widthY = parseInt(kind.slice(1)) || 1; + kind = kind[0]; + } } - if (!width) width = 1; + if (!widthX && !widthY) + widthX = widthY = 1; - if (kind && (this.is_projection==kind)) { - if (this.projection_width === width) { + if (kind && (this.is_projection == kind)) { + if ((this.projection_widthX === widthX) && (this.projection_widthY === widthY)) { kind = ''; } else { - this.projection_width = width; + this.projection_widthX = widthX; + this.projection_widthY = widthY; return; } } @@ -119388,8 +119429,9 @@ let RH2Painter$2 = class RH2Painter extends RHistPainter { delete this.proj_hist; let new_proj = (this.is_projection === kind) ? '' : kind; - this.is_projection = ''; // disable projection redraw until callback - this.projection_width = width; + this.projection_widthX = widthX; + this.projection_widthY = widthY; + this.is_projection = ''; // avoid projection handling until area is created this.provideSpecialDrawArea(new_proj).then(() => { this.is_projection = new_proj; return this.redrawProjection(); }); } @@ -119415,10 +119457,16 @@ let RH2Painter$2 = class RH2Painter extends RHistPainter { /** @summary Fill histogram context menu */ fillHistContextMenu(menu) { - menu.add('sub:Projections', () => this.toggleProjection()); let kind = this.is_projection || ''; - if (kind) kind += this.projection_width; - let kinds = ['X1', 'X2', 'X3', 'X5', 'X10', 'Y1', 'Y2', 'Y3', 'Y5', 'Y10']; + if (kind) kind += this.projection_widthX; + if ((this.projection_widthX != this.projection_widthY) && (this.is_projection == 'XY')) + kind = `X${this.projection_widthX}_Y${this.projection_widthY}`; + + menu.add('sub:Projections', () => menu.input('Input projection kind X1 or XY2', kind, 'string').then(val => this.toggleProjection(val))); + + let kinds = ['X1', 'X2', 'X3', 'X5', 'X10', 'Y1', 'Y2', 'Y3', 'Y5', 'Y10', 'XY1', 'XY2', 'XY3', 'XY5', 'XY10']; + if (kind) kinds.unshift('Off'); + for (let k = 0; k < kinds.length; ++k) menu.addchk(kind == kinds[k], kinds[k], kinds[k], arg => this.toggleProjection(arg)); menu.add('endsub:'); @@ -120551,14 +120599,15 @@ let RH2Painter$2 = class RH2Painter extends RHistPainter { color2: this.fillatt?.getFillColorAlt('blue') ?? 'blue', lines: this.getBinTooltips(i, j), exact: true, menu: true }; - if (this.options.Color) res.color2 = h.palette.getColor(colindx); + if (this.options.Color) + res.color2 = h.palette.getColor(colindx); if (pnt.disabled && !this.is_projection) { ttrect.remove(); res.changed = true; } else { if (ttrect.empty()) - ttrect = this.draw_g.append('svg:rect') + ttrect = this.draw_g.append('svg:path') .attr('class','tooltip_bin h1bin') .style('pointer-events','none'); @@ -120566,35 +120615,42 @@ let RH2Painter$2 = class RH2Painter extends RHistPainter { j1 = j, j2 = j+1, x1 = h.grx[i1], x2 = h.grx[i2], y1 = h.gry[j2], y2 = h.gry[j1], - binid = i*10000 + j; + binid = i*10000 + j, + pmain = this.getFramePainter(), + path; - if (this.is_projection == 'X') { - x1 = 0; x2 = this.getFramePainter().getFrameWidth(); - if (this.projection_width > 1) { - let dd = (this.projection_width-1)/2; - if (j2+dd >= h.j2) { j2 = Math.min(Math.round(j2+dd), h.j2); j1 = Math.max(j2 - this.projection_width, h.j1); } - else { j1 = Math.max(Math.round(j1-dd), h.j1); j2 = Math.min(j1 + this.projection_width, h.j2); } + if (this.is_projection) { + let pwx = this.projection_widthX || 1, ddx = (pwx - 1) / 2; + if ((this.is_projection.indexOf('X')) >= 0 && (pwx > 1)) { + if (j2+ddx >= h.j2) { j2 = Math.min(Math.round(j2+ddx), h.j2); j1 = Math.max(j2-pwx, h.j1); } + else { j1 = Math.max(Math.round(j1-ddx), h.j1); j2 = Math.min(j1+pwx, h.j2); } + } + let pwy = this.projection_widthY || 1, ddy = (pwy - 1) / 2; + if ((this.is_projection.indexOf('Y')) >= 0 && (pwy > 1)) { + if (i2+ddy >= h.i2) { i2 = Math.min(Math.round(i2+ddy), h.i2); i1 = Math.max(i2-pwy, h.i1); } + else { i1 = Math.max(Math.round(i1-ddy), h.i1); i2 = Math.min(i1+pwy, h.i2); } } + } + + if (this.is_projection == 'X') { + x1 = 0; x2 = pmain.getFrameWidth(); y1 = h.gry[j2]; y2 = h.gry[j1]; binid = j1*777 + j2*333; } else if (this.is_projection == 'Y') { - y1 = 0; y2 = this.getFramePainter().getFrameHeight(); - if (this.projection_width > 1) { - let dd = (this.projection_width-1)/2; - if (i2+dd >= h.i2) { i2 = Math.min(Math.round(i2+dd), h.i2); i1 = Math.max(i2 - this.projection_width, h.i1); } - else { i1 = Math.max(Math.round(i1-dd), h.i1); i2 = Math.min(i1 + this.projection_width, h.i2); } - } - x1 = h.grx[i1], x2 = h.grx[i2], + y1 = 0; y2 = pmain.getFrameHeight(); + x1 = h.grx[i1]; x2 = h.grx[i2]; binid = i1*777 + i2*333; + } else if (this.is_projection == 'XY') { + y1 = h.gry[j2]; y2 = h.gry[j1]; + x1 = h.grx[i1]; x2 = h.grx[i2]; + binid = i1*789 + i2*653 + j1*12345 + j2*654321; + path = `M${x1},0H${x2}V${y1}H${pmain.getFrameWidth()}V${y2}H${x2}V${pmain.getFrameHeight()}H${x1}V${y2}H0V${y1}H${x1}Z`; } res.changed = ttrect.property('current_bin') !== binid; if (res.changed) - ttrect.attr('x', x1) - .attr('width', x2 - x1) - .attr('y', y1) - .attr('height', y2 - y1) + ttrect.attr('d', path || `M${x1},${y1}H${x2}V${y2}H${x1}Z`) .style('opacity', '0.7') .property('current_bin', binid); diff --git a/js/changes.md b/js/changes.md index fcc97edb408..3bdf3ffb6c8 100644 --- a/js/changes.md +++ b/js/changes.md @@ -15,7 +15,7 @@ 12. Adjust TAxis title positioning to native ROOT behavior 13. Let change physical node visibility in TGeo drawings 14. Use TGaxis attributes from gStyle - fAxisMaxDigits, fStripDecimals and exponent offset -15. Implement "projxy" draw option for TH2 +15. Implement "projxy" draw option for TH2 - like projxy3 or projx1_y5 ## Changes in 7.3.0 diff --git a/js/modules/hist2d/RH2Painter.mjs b/js/modules/hist2d/RH2Painter.mjs index 78ea6968841..ca20fab29c7 100644 --- a/js/modules/hist2d/RH2Painter.mjs +++ b/js/modules/hist2d/RH2Painter.mjs @@ -33,20 +33,40 @@ class RH2Painter extends RHistPainter { /** @summary Toggle projection */ toggleProjection(kind, width) { - if (kind == 'Projections') kind = ''; - - if (isStr(kind) && (kind.length > 1)) { - width = parseInt(kind.slice(1)); - kind = kind[0]; + if ((kind == 'Projections') || (kind == 'Off')) + kind = ''; + + let widthX = width, widthY = width; + + if (isStr(kind) && (kind.indexOf('XY') == 0)) { + let ws = kind.length > 2 ? kind.slice(2) : ''; + kind = 'XY'; + widthX = widthY = parseInt(ws); + } else if (isStr(kind) && (kind.length > 1)) { + let ps = kind.indexOf('_'); + if ((ps > 0) && (kind[0] == 'X') && (kind[ps+1] == 'Y')) { + widthX = parseInt(kind.slice(1, ps)) || 1; + widthY = parseInt(kind.slice(ps+2)) || 1; + kind = 'XY'; + } else if ((ps > 0) && (kind[0] == 'Y') && (kind[ps+1] == 'X')) { + widthY = parseInt(kind.slice(1, ps)) || 1; + widthX = parseInt(kind.slice(ps+2)) || 1; + kind = 'XY'; + } else { + widthX = widthY = parseInt(kind.slice(1)) || 1; + kind = kind[0]; + } } - if (!width) width = 1; + if (!widthX && !widthY) + widthX = widthY = 1; - if (kind && (this.is_projection==kind)) { - if (this.projection_width === width) { + if (kind && (this.is_projection == kind)) { + if ((this.projection_widthX === widthX) && (this.projection_widthY === widthY)) { kind = ''; } else { - this.projection_width = width; + this.projection_widthX = widthX; + this.projection_widthY = widthY; return; } } @@ -54,8 +74,9 @@ class RH2Painter extends RHistPainter { delete this.proj_hist; let new_proj = (this.is_projection === kind) ? '' : kind; - this.is_projection = ''; // disable projection redraw until callback - this.projection_width = width; + this.projection_widthX = widthX; + this.projection_widthY = widthY; + this.is_projection = ''; // avoid projection handling until area is created this.provideSpecialDrawArea(new_proj).then(() => { this.is_projection = new_proj; return this.redrawProjection(); }); } @@ -81,10 +102,16 @@ class RH2Painter extends RHistPainter { /** @summary Fill histogram context menu */ fillHistContextMenu(menu) { - menu.add('sub:Projections', () => this.toggleProjection()); let kind = this.is_projection || ''; - if (kind) kind += this.projection_width; - let kinds = ['X1', 'X2', 'X3', 'X5', 'X10', 'Y1', 'Y2', 'Y3', 'Y5', 'Y10']; + if (kind) kind += this.projection_widthX; + if ((this.projection_widthX != this.projection_widthY) && (this.is_projection == 'XY')) + kind = `X${this.projection_widthX}_Y${this.projection_widthY}`; + + menu.add('sub:Projections', () => menu.input('Input projection kind X1 or XY2', kind, 'string').then(val => this.toggleProjection(val))); + + let kinds = ['X1', 'X2', 'X3', 'X5', 'X10', 'Y1', 'Y2', 'Y3', 'Y5', 'Y10', 'XY1', 'XY2', 'XY3', 'XY5', 'XY10']; + if (kind) kinds.unshift('Off'); + for (let k = 0; k < kinds.length; ++k) menu.addchk(kind == kinds[k], kinds[k], kinds[k], arg => this.toggleProjection(arg)); menu.add('endsub:'); @@ -1229,14 +1256,15 @@ class RH2Painter extends RHistPainter { color2: this.fillatt?.getFillColorAlt('blue') ?? 'blue', lines: this.getBinTooltips(i, j), exact: true, menu: true }; - if (this.options.Color) res.color2 = h.palette.getColor(colindx); + if (this.options.Color) + res.color2 = h.palette.getColor(colindx); if (pnt.disabled && !this.is_projection) { ttrect.remove(); res.changed = true; } else { if (ttrect.empty()) - ttrect = this.draw_g.append('svg:rect') + ttrect = this.draw_g.append('svg:path') .attr('class','tooltip_bin h1bin') .style('pointer-events','none'); @@ -1244,35 +1272,42 @@ class RH2Painter extends RHistPainter { j1 = j, j2 = j+1, x1 = h.grx[i1], x2 = h.grx[i2], y1 = h.gry[j2], y2 = h.gry[j1], - binid = i*10000 + j; + binid = i*10000 + j, + pmain = this.getFramePainter(), + path; - if (this.is_projection == 'X') { - x1 = 0; x2 = this.getFramePainter().getFrameWidth(); - if (this.projection_width > 1) { - let dd = (this.projection_width-1)/2; - if (j2+dd >= h.j2) { j2 = Math.min(Math.round(j2+dd), h.j2); j1 = Math.max(j2 - this.projection_width, h.j1); } - else { j1 = Math.max(Math.round(j1-dd), h.j1); j2 = Math.min(j1 + this.projection_width, h.j2); } + if (this.is_projection) { + let pwx = this.projection_widthX || 1, ddx = (pwx - 1) / 2; + if ((this.is_projection.indexOf('X')) >= 0 && (pwx > 1)) { + if (j2+ddx >= h.j2) { j2 = Math.min(Math.round(j2+ddx), h.j2); j1 = Math.max(j2-pwx, h.j1); } + else { j1 = Math.max(Math.round(j1-ddx), h.j1); j2 = Math.min(j1+pwx, h.j2); } + } + let pwy = this.projection_widthY || 1, ddy = (pwy - 1) / 2; + if ((this.is_projection.indexOf('Y')) >= 0 && (pwy > 1)) { + if (i2+ddy >= h.i2) { i2 = Math.min(Math.round(i2+ddy), h.i2); i1 = Math.max(i2-pwy, h.i1); } + else { i1 = Math.max(Math.round(i1-ddy), h.i1); i2 = Math.min(i1+pwy, h.i2); } } + } + + if (this.is_projection == 'X') { + x1 = 0; x2 = pmain.getFrameWidth(); y1 = h.gry[j2]; y2 = h.gry[j1]; binid = j1*777 + j2*333; } else if (this.is_projection == 'Y') { - y1 = 0; y2 = this.getFramePainter().getFrameHeight(); - if (this.projection_width > 1) { - let dd = (this.projection_width-1)/2; - if (i2+dd >= h.i2) { i2 = Math.min(Math.round(i2+dd), h.i2); i1 = Math.max(i2 - this.projection_width, h.i1); } - else { i1 = Math.max(Math.round(i1-dd), h.i1); i2 = Math.min(i1 + this.projection_width, h.i2); } - } - x1 = h.grx[i1], x2 = h.grx[i2], + y1 = 0; y2 = pmain.getFrameHeight(); + x1 = h.grx[i1]; x2 = h.grx[i2]; binid = i1*777 + i2*333; + } else if (this.is_projection == 'XY') { + y1 = h.gry[j2]; y2 = h.gry[j1]; + x1 = h.grx[i1]; x2 = h.grx[i2]; + binid = i1*789 + i2*653 + j1*12345 + j2*654321; + path = `M${x1},0H${x2}V${y1}H${pmain.getFrameWidth()}V${y2}H${x2}V${pmain.getFrameHeight()}H${x1}V${y2}H0V${y1}H${x1}Z`; } res.changed = ttrect.property('current_bin') !== binid; if (res.changed) - ttrect.attr('x', x1) - .attr('width', x2 - x1) - .attr('y', y1) - .attr('height', y2 - y1) + ttrect.attr('d', path || `M${x1},${y1}H${x2}V${y2}H${x1}Z`) .style('opacity', '0.7') .property('current_bin', binid); diff --git a/js/modules/hist2d/TH2Painter.mjs b/js/modules/hist2d/TH2Painter.mjs index 9e32a1ed1d0..877947ad6f2 100644 --- a/js/modules/hist2d/TH2Painter.mjs +++ b/js/modules/hist2d/TH2Painter.mjs @@ -36,22 +36,37 @@ class TH2Painter extends THistPainter { if ((kind == 'Projections') || (kind == 'Off')) kind = ''; + let widthX = width, widthY = width; + if (isStr(kind) && (kind.indexOf('XY') == 0)) { - if (kind.length > 2) - width = parseInt(kind.slice(2)); + let ws = (kind.length > 2) ? kind.slice(2) : ''; kind = 'XY'; + widthX = widthY = parseInt(ws) || 1; } else if (isStr(kind) && (kind.length > 1)) { - width = parseInt(kind.slice(1)); - kind = kind[0]; + let ps = kind.indexOf('_'); + if ((ps > 0) && (kind[0] == 'X') && (kind[ps+1] == 'Y')) { + widthX = parseInt(kind.slice(1, ps)) || 1; + widthY = parseInt(kind.slice(ps+2)) || 1; + kind = 'XY'; + } else if ((ps > 0) && (kind[0] == 'Y') && (kind[ps+1] == 'X')) { + widthY = parseInt(kind.slice(1, ps)) || 1; + widthX = parseInt(kind.slice(ps+2)) || 1; + kind = 'XY'; + } else { + widthX = widthY = parseInt(kind.slice(1)) || 1; + kind = kind[0]; + } } - if (!width) width = 1; + if (!widthX && !widthY) + widthX = widthY = 1; if (kind && (this.is_projection == kind)) { - if (this.projection_width === width) { + if ((this.projection_widthX === widthX) && (this.projection_widthY === widthY)) { kind = ''; } else { - this.projection_width = width; + this.projection_widthX = widthX; + this.projection_widthY = widthY; return; } } @@ -59,7 +74,8 @@ class TH2Painter extends THistPainter { delete this.proj_hist; let new_proj = (this.is_projection === kind) ? '' : kind; - this.projection_width = width; + this.projection_widthX = widthX; + this.projection_widthY = widthY; this.is_projection = ''; // avoid projection handling until area is created this.provideSpecialDrawArea(new_proj).then(() => { this.is_projection = new_proj; return this.redrawProjection(); }); @@ -188,11 +204,15 @@ class TH2Painter extends THistPainter { /** @summary Fill histogram context menu */ fillHistContextMenu(menu) { if (!this.isTH2Poly()) { - menu.add('sub:Projections', () => this.toggleProjection()); let kind = this.is_projection || ''; - if (kind) kind += this.projection_width; + if (kind) kind += this.projection_widthX; + if ((this.projection_widthX != this.projection_widthY) && (this.is_projection == 'XY')) + kind = `X${this.projection_widthX}_Y${this.projection_widthY}`; + const kinds = ['X1', 'X2', 'X3', 'X5', 'X10', 'Y1', 'Y2', 'Y3', 'Y5', 'Y10', 'XY1', 'XY2', 'XY3', 'XY5', 'XY10']; - if (this.is_projection) kinds.push('Off'); + if (kind) kinds.unshift('Off'); + + menu.add('sub:Projections', () => menu.input('Input projection kind X1 or XY2', kind, 'string').then(val => this.toggleProjection(val))); for (let k = 0; k < kinds.length; ++k) menu.addchk(kind==kinds[k], kinds[k], kinds[k], arg => this.toggleProjection(arg)); menu.add('endsub:'); @@ -2638,14 +2658,15 @@ class TH2Painter extends THistPainter { let binid = i*10000 + j, path; if (this.is_projection) { - let pw = this.projection_width || 1, dd = (pw - 1) / 2; - if ((this.is_projection.indexOf('X')) >= 0 && (pw > 1)) { - if (j2+dd >= h.j2) { j2 = Math.min(Math.round(j2+dd), h.j2); j1 = Math.max(j2 - pw, h.j1); } - else { j1 = Math.max(Math.round(j1-dd), h.j1); j2 = Math.min(j1 + pw, h.j2); } + let pwx = this.projection_widthX || 1, ddx = (pwx - 1) / 2; + if ((this.is_projection.indexOf('X')) >= 0 && (pwx > 1)) { + if (j2+ddx >= h.j2) { j2 = Math.min(Math.round(j2+ddx), h.j2); j1 = Math.max(j2-pwx, h.j1); } + else { j1 = Math.max(Math.round(j1-ddx), h.j1); j2 = Math.min(j1+pwx, h.j2); } } - if ((this.is_projection.indexOf('Y')) >= 0 && (pw > 1)) { - if (i2+dd >= h.i2) { i2 = Math.min(Math.round(i2+dd), h.i2); i1 = Math.max(i2 - pw, h.i1); } - else { i1 = Math.max(Math.round(i1-dd), h.i1); i2 = Math.min(i1 + pw, h.i2); } + let pwy = this.projection_widthY || 1, ddy = (pwy - 1) / 2; + if ((this.is_projection.indexOf('Y')) >= 0 && (pwy > 1)) { + if (i2+ddy >= h.i2) { i2 = Math.min(Math.round(i2+ddy), h.i2); i1 = Math.max(i2-pwy, h.i1); } + else { i1 = Math.max(Math.round(i1-ddy), h.i1); i2 = Math.min(i1+pwy, h.i2); } } } diff --git a/js/modules/hist2d/THistPainter.mjs b/js/modules/hist2d/THistPainter.mjs index 43ccb8d4277..34ac9a9b891 100644 --- a/js/modules/hist2d/THistPainter.mjs +++ b/js/modules/hist2d/THistPainter.mjs @@ -471,8 +471,8 @@ class THistDrawOptions { if (this.Proj > 0) this.Contour = 14; if (d.check('PROJXY', true)) this.Project = 'XY' + d.partAsInt(0,1); - if (d.check('PROJX', true)) this.Project = 'X' + d.partAsInt(0,1); - if (d.check('PROJY', true)) this.Project = 'Y' + d.partAsInt(0,1); + if (d.check('PROJX', true)) this.Project = 'X' + d.part; + if (d.check('PROJY', true)) this.Project = 'Y' + d.part; if (d.check('PROJ')) this.Project = 'Y1'; if (check3dbox) { -- GitLab