Skip to content

Commit

Permalink
Build with new legend items placing
Browse files Browse the repository at this point in the history
  • Loading branch information
linev committed Dec 4, 2024
1 parent 5608116 commit 4046b58
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 61 deletions.
125 changes: 79 additions & 46 deletions build/jsroot.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const version_id = 'dev',

/** @summary version date
* @desc Release date in format day/month/year like '14/04/2022' */
version_date = '3/12/2024',
version_date = '4/12/2024',

/** @summary version id and date
* @desc Produced by concatenation of {@link version_id} and {@link version_date}
Expand Down Expand Up @@ -73091,18 +73091,16 @@ class TPavePainter extends ObjectPainter {
/** @summary Draw TLegend object */
drawLegend(w, h) {
const legend = this.getObject(),
nlines = legend.fPrimitives.arr.length;
let ncols = legend.fNColumns,
nrows = nlines,
nlines = legend.fPrimitives.arr.length,
ncols = Math.max(1, legend.fNColumns);
let nrows = Math.round(nlines / ncols),
any_text = false,
custom_textg = false; // each text entry has own attributes

if (ncols < 2)
ncols = 1;
else
while ((nrows-1)*ncols >= nlines) nrows--;
if (nrows * ncols < nlines)
nrows++;

const isEmpty = entry => !entry.fObject && !entry.fOption && (!entry.fLabel || (entry.fLabel === ' '));
const isEmpty = entry => !entry.fObject && !entry.fOption && (!entry.fLabel || !entry.fLabel.trim());

for (let ii = 0; ii < nlines; ++ii) {
const entry = legend.fPrimitives.arr[ii];
Expand All @@ -73117,12 +73115,28 @@ class TPavePainter extends ObjectPainter {
}
}

if (nrows < 1) nrows = 1;
if (nrows < 1)
nrows = 1;

const padding_x = Math.round(0.03*w/ncols),
padding_y = Math.round(0.03*h),
row_height = (h - 2*padding_y) / (nrows + (nrows - 1) * legend.fEntrySeparation),
gap_y = row_height * legend.fEntrySeparation;

let gap_x = padding_x,
column_width0 = (w - 2*padding_x - (ncols - 1)* gap_x) / ncols;

if (legend.fColumnSeparation) {
column_width0 = (w - 2*padding_x) / (ncols + (ncols - 1) * legend.fColumnSeparation);
gap_x = column_width0 * legend.fColumnSeparation;
}

// calculate positions of columns by weight - means more letters, more weight
const column_pos = new Array(ncols + 1).fill(0);
const column_pos = new Array(ncols + 1).fill(padding_x),
column_boxwidth = column_width0 * legend.fMargin;
if (ncols > 1) {
const column_weight = new Array(ncols).fill(1);
const column_weight = new Array(ncols).fill(1),
space_for_text = w - 2 * padding_x - (ncols - 1) * gap_x - ncols * column_boxwidth;

for (let ii = 0; ii < nlines; ++ii) {
const entry = legend.fPrimitives.arr[ii];
Expand All @@ -73134,51 +73148,55 @@ class TPavePainter extends ObjectPainter {
let sum_weight = 0;
for (let icol = 0; icol < ncols; ++icol)
sum_weight += column_weight[icol];

for (let icol = 0; icol < ncols-1; ++icol)
column_pos[icol+1] = column_pos[icol] + legend.fMargin*w/ncols + column_weight[icol] * (1-legend.fMargin) * w / sum_weight;
column_pos[icol+1] = column_pos[icol] + column_boxwidth + column_weight[icol] / sum_weight * space_for_text + gap_x;
}
column_pos[ncols] = w;
column_pos[ncols] = w - padding_x;

const padding_x = Math.round(0.03*w/ncols),
padding_y = Math.round(0.03*h),
step_y = (h - 2*padding_y)/nrows,
text_promises = [],
pp = this.getPadPainter();
let font_size = 0.9*step_y,
let font_size = row_height,
max_font_size = 0, // not limited in the beginning
any_opt = false;

this.createAttText({ attr: legend, can_rotate: false });

const tsz = this.textatt.getSize(pp.getPadHeight());
const pp = this.getPadPainter(),
tsz = this.textatt.getSize(pp.getPadHeight());
if (tsz && (tsz < font_size))
font_size = max_font_size = tsz;

const pr = any_text && !custom_textg ? this.startTextDrawingAsync(this.textatt.font, font_size, this.draw_g, max_font_size) : Promise.resolve();
const text_promises = [],
pr = any_text && !custom_textg ? this.startTextDrawingAsync(this.textatt.font, font_size, this.draw_g, max_font_size) : Promise.resolve();

return pr.then(() => {
for (let ii = 0, i = -1; ii < nlines; ++ii) {
const entry = legend.fPrimitives.arr[ii];
if (isEmpty(entry)) continue; // let discard empty entry
if (isEmpty(entry))
continue; // let discard empty entry

if (ncols === 1) ++i; else i = ii;
if (ncols === 1)
++i;
else
i = ii;

const lopt = entry.fOption.toLowerCase(),
icol = i % ncols, irow = (i - icol) / ncols,
x0 = Math.round(column_pos[icol]),
column_width = Math.round(column_pos[icol + 1] - column_pos[icol]),
tpos_x = x0 + Math.round(legend.fMargin*w/ncols),
mid_x = Math.round((x0 + tpos_x)/2),
pos_y = Math.round(irow*step_y + padding_y), // top corner
mid_y = Math.round((irow+0.5)*step_y + padding_y), // center line
icol = i % ncols, irow = (i - icol) / ncols;
column_pos[icol + 1] - column_pos[icol];
const x0 = Math.round(column_pos[icol]),
y0 = Math.round(padding_y + irow * (row_height + gap_y)),
tpos_x = Math.round(x0 + column_boxwidth),
mid_x = Math.round(x0 + (column_boxwidth - padding_x)/2),
box_y = Math.round(y0 + row_height * 0.1),
box_height = Math.round(row_height * 0.8),
mid_y = Math.round(y0 + row_height * 0.5), // center line
mo = entry.fObject,
draw_fill = lopt.indexOf('f') !== -1,
draw_line = lopt.indexOf('l') !== -1,
draw_error = lopt.indexOf('e') !== -1,
draw_marker = lopt.indexOf('p') !== -1;

let o_fill = entry, o_marker = entry, o_line = entry,
painter = null, isany = false;
painter = null, isany = false;

if (isObject(mo)) {
if ('fLineColor' in mo) o_line = mo;
Expand All @@ -73198,10 +73216,9 @@ class TPavePainter extends ObjectPainter {

if (!fillatt.empty() || lineatt) {
isany = true;
// box total height is yspace*0.7
// define x,y as the center of the symbol for this entry
this.draw_g.append('svg:path')
.attr('d', `M${x0 + padding_x},${Math.round(pos_y+step_y*0.1)}v${Math.round(step_y*0.8)}h${tpos_x-2*padding_x-x0}v${-Math.round(step_y*0.8)}z`)
.attr('d', `M${x0},${box_y}v${box_height}h${tpos_x-padding_x-x0}v${-box_height}z`)
.call(fillatt.func)
.call(lineatt ? lineatt.func : () => {});
}
Expand All @@ -73214,11 +73231,11 @@ class TPavePainter extends ObjectPainter {
isany = true;
if (draw_line) {
this.draw_g.append('svg:path')
.attr('d', `M${x0 + padding_x},${mid_y}H${tpos_x - padding_x}`)
.attr('d', `M${x0},${mid_y}h${tpos_x-padding_x-x0}`)
.call(lineatt.func);
}
if (draw_error) {
let endcaps = 0, edx = step_y*0.05;
let endcaps = 0, edx = row_height*0.05;
if (isFunc(painter?.getHisto) && painter.options?.ErrorKind === 1)
endcaps = 1; // draw bars for e1 option in histogram
else if (isFunc(painter?.getGraph) && mo?.fLineWidth !== undefined && mo?.fMarkerSize !== undefined) {
Expand All @@ -73227,9 +73244,9 @@ class TPavePainter extends ObjectPainter {
if (endcaps > 1) edx = Math.max(edx, mo.fMarkerSize*8*0.66);
}

const eoff = (endcaps === 3) ? 0.03 : 0,
ey1 = Math.round(pos_y+step_y*(0.1 + eoff)),
ey2 = Math.round(pos_y+step_y*(0.9 - eoff)),
const eoff = (endcaps === 3) ? 0.2 : 0,
ey1 = Math.round(y0 + row_height*eoff),
ey2 = Math.round(y0 + row_height*(1 - eoff)),
edy = Math.round(edx * 0.66);
edx = Math.round(edx);
let path = `M${mid_x},${ey1}V${ey2}`;
Expand All @@ -73254,15 +73271,15 @@ class TPavePainter extends ObjectPainter {
isany = true;
this.draw_g
.append('svg:path')
.attr('d', marker.create((x0 + tpos_x)/2, mid_y))
.attr('d', marker.create(mid_x, mid_y))
.call(marker.func);
}
}

// special case - nothing draw, try to show rect with line attributes
if (!isany && painter?.lineatt && !painter.lineatt.empty()) {
this.draw_g.append('svg:path')
.attr('d', `M${x0 + padding_x},${Math.round(pos_y+step_y*0.1)}v${Math.round(step_y*0.8)}h${tpos_x-2*padding_x-x0}v${-Math.round(step_y*0.8)}z`)
.attr('d', `M${x0},${box_y}v${box_height}h${tpos_x-padding_x-x0}v${-box_height}z`)
.style('fill', 'none')
.call(painter.lineatt.func);
}
Expand All @@ -73271,14 +73288,15 @@ class TPavePainter extends ObjectPainter {
if (isStr(lopt) && (lopt.toLowerCase() !== 'h'))
any_opt = true;
else if (!any_opt)
pos_x = x0 + padding_x;
pos_x = x0;

if (entry.fLabel) {
const textatt = this.createAttText({ attr: entry, std: false, attr_alt: legend }),
arg = { draw_g: this.draw_g, align: textatt.align, x: pos_x, y: pos_y,
scale: (custom_textg && !entry.fTextSize) || !legend.fTextSize,
width: x0+column_width-pos_x-padding_x, height: step_y,
text: entry.fLabel, color: textatt.color };
arg = { draw_g: this.draw_g, align: textatt.align,
x: pos_x, width: Math.round(column_pos[icol + 1] - pos_x),
y: y0, height: Math.round(row_height),
scale: (custom_textg && !entry.fTextSize) || !legend.fTextSize,
text: entry.fLabel, color: textatt.color };
if (custom_textg) {
arg.draw_g = this.draw_g.append('svg:g');
text_promises.push(this.startTextDrawingAsync(textatt.font, textatt.getSize(pp.getPadHeight()), arg.draw_g, max_font_size)
Expand Down Expand Up @@ -73609,6 +73627,7 @@ class TPavePainter extends ObjectPainter {
else
opt = opt.slice(0, parc) + opt.slice(parc + 3);
this.setPaveDrawOption(opt);
console.log('Exec', `exec:${set_opt}("${opt}")`);
this.interactiveRedraw(true, `exec:${set_opt}("${opt}")`);
}, 'Usage of ARC draw option');
menu.addSizeMenu('radius', 0, 0.2, 0.02, pave.fCornerRadius, val => {
Expand Down Expand Up @@ -73744,11 +73763,25 @@ class TPavePainter extends ObjectPainter {
}, 'Store title position and graphical attributes to gStyle');
}
} else if (pave._typename === clTLegend) {
menu.sub('Legend');
menu.add('Autoplace', () => {
this.autoPlaceLegend(pave, this.getPadPainter()?.getRootPad(true), true).then(res => {
if (res) this.interactiveRedraw(true, 'pave_moved');
});
});
menu.addSizeMenu('Entry separation', 0, 1, 0.1, pave.fEntrySeparation, v => {
pave.fEntrySeparation = v;
this.interactiveRedraw(true, `exec:SetEntrySeparation(${v})`);
}, 'Vertical entries separation, meaningful values between 0 and 1');
menu.addSizeMenu('Columns separation', 0, 1, 0.1, pave.fColumnSeparation, v => {
pave.fColumnSeparation = v;
this.interactiveRedraw(true, `exec:SetColumnSeparation(${v})`);
}, 'Horizontal columns separation, meaningful values between 0 and 1');
menu.addSizeMenu('Num columns', 1, 7, 1, pave.fNColumns, v => {
pave.fNColumns = v;
this.interactiveRedraw(true, `exec:SetNColumns(${v})`);
}, 'Number of columns in the legend');
menu.endsub();
}
}

Expand Down
2 changes: 1 addition & 1 deletion changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
1. Support "same" option for first histogram, draw direcly on pad
1. Support different angle coordiantes in `TGraphPolargram`, handle 'N' and 'O' draw options
1. Implement 'arc' draw option for `TPave`
1. Provide extensive context menus for all derived from `TPave` classes
1. Provide context menus for all derived from `TPave` classes
1. Support Poisson errors for `TH1`/`TH2`, https://root-forum.cern.ch/t/62335/
1. Fix - handle `TPave` NDC position also when fInit is not set
1. Fix - correctly position title according to gStyle->GetTitleAlign()
Expand Down
2 changes: 1 addition & 1 deletion modules/core.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const version_id = 'dev',

/** @summary version date
* @desc Release date in format day/month/year like '14/04/2022' */
version_date = '3/12/2024',
version_date = '4/12/2024',

/** @summary version id and date
* @desc Produced by concatenation of {@link version_id} and {@link version_date}
Expand Down
22 changes: 9 additions & 13 deletions modules/hist/TPavePainter.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -703,9 +703,9 @@ class TPavePainter extends ObjectPainter {
/** @summary Draw TLegend object */
drawLegend(w, h) {
const legend = this.getObject(),
nlines = legend.fPrimitives.arr.length;
let ncols = Math.max(1, legend.fNColumns),
nrows = Math.round(nlines / ncols),
nlines = legend.fPrimitives.arr.length,
ncols = Math.max(1, legend.fNColumns);
let nrows = Math.round(nlines / ncols),
any_text = false,
custom_textg = false; // each text entry has own attributes

Expand Down Expand Up @@ -745,7 +745,7 @@ class TPavePainter extends ObjectPainter {

// calculate positions of columns by weight - means more letters, more weight
const column_pos = new Array(ncols + 1).fill(padding_x),
column_boxwidth = column_width0 * legend.fMargin;
column_boxwidth = column_width0 * legend.fMargin;
if (ncols > 1) {
const column_weight = new Array(ncols).fill(1),
space_for_text = w - 2 * padding_x - (ncols - 1) * gap_x - ncols * column_boxwidth;
Expand Down Expand Up @@ -792,15 +792,11 @@ class TPavePainter extends ObjectPainter {
i = ii;

const lopt = entry.fOption.toLowerCase(),
icol = i % ncols, irow = (i - icol) / ncols;
let x0 = column_pos[icol],
column_width = column_pos[icol + 1] - column_pos[icol],
y0 = padding_y + irow * (row_height + gap_y);

x0 = Math.round(x0);
y0 = Math.round(y0);

const tpos_x = Math.round(x0 + column_boxwidth),
icol = i % ncols, irow = (i - icol) / ncols,
column_width = column_pos[icol + 1] - column_pos[icol],
x0 = Math.round(column_pos[icol]),
y0 = Math.round(padding_y + irow * (row_height + gap_y)),
tpos_x = Math.round(x0 + column_boxwidth),
mid_x = Math.round(x0 + (column_boxwidth - padding_x)/2),
box_y = Math.round(y0 + row_height * 0.1),
box_height = Math.round(row_height * 0.8),
Expand Down

0 comments on commit 4046b58

Please sign in to comment.