import { settings } from '../core.mjs';
import { makeTranslate } from '../base/BasePainter.mjs';
import { RObjectPainter } from '../base/RObjectPainter.mjs';
import { ensureRCanvas } from '../gpad/RCanvasPainter.mjs';
import { addDragHandler } from '../gpad/TFramePainter.mjs';
const ECorner = { kTopLeft: 1, kTopRight: 2, kBottomLeft: 3, kBottomRight: 4 };
/**
* @summary Painter for RPave class
*
* @private
*/
class RPavePainter extends RObjectPainter {
/** @summary Draw pave content
* @desc assigned depending on pave class */
async drawContent() { return this; }
/** @summary Draw pave */
async drawPave() {
const rect = this.getPadPainter().getPadRect(),
fp = this.getFramePainter();
this.onFrame = fp && this.v7EvalAttr('onFrame', true);
this.corner = this.v7EvalAttr('corner', ECorner.kTopRight);
const visible = this.v7EvalAttr('visible', true),
offsetx = this.v7EvalLength('offsetX', rect.width, 0.02),
offsety = this.v7EvalLength('offsetY', rect.height, 0.02),
pave_width = this.v7EvalLength('width', rect.width, 0.3),
pave_height = this.v7EvalLength('height', rect.height, 0.3);
this.createG();
this.draw_g.classed('most_upper_primitives', true); // this primitive will remain on top of list
if (!visible)
return this;
this.createv7AttLine('border_');
this.createv7AttFill();
const fr = this.onFrame ? fp.getFrameRect() : rect;
let pave_x = 0, pave_y = 0;
switch (this.corner) {
case ECorner.kTopLeft:
pave_x = fr.x + offsetx;
pave_y = fr.y + offsety;
break;
case ECorner.kBottomLeft:
pave_x = fr.x + offsetx;
pave_y = fr.y + fr.height - offsety - pave_height;
break;
case ECorner.kBottomRight:
pave_x = fr.x + fr.width - offsetx - pave_width;
pave_y = fr.y + fr.height - offsety - pave_height;
break;
case ECorner.kTopRight:
default:
pave_x = fr.x + fr.width - offsetx - pave_width;
pave_y = fr.y + offsety;
}
makeTranslate(this.draw_g, pave_x, pave_y);
this.draw_g.append('svg:rect')
.attr('x', 0)
.attr('width', pave_width)
.attr('y', 0)
.attr('height', pave_height)
.call(this.lineatt.func)
.call(this.fillatt.func);
this.pave_width = pave_width;
this.pave_height = pave_height;
// here should be fill and draw of text
return this.drawContent().then(() => {
if (!this.isBatchMode()) {
// TODO: provide pave context menu as in v6
if (settings.ContextMenu && this.paveContextMenu)
this.draw_g.on('contextmenu', evnt => this.paveContextMenu(evnt));
addDragHandler(this, { x: pave_x, y: pave_y, width: pave_width, height: pave_height,
minwidth: 20, minheight: 20, redraw: d => this.sizeChanged(d) });
}
return this;
});
}
/** @summary Process interactive moving of the stats box */
sizeChanged(drag) {
this.pave_width = drag.width;
this.pave_height = drag.height;
const pave_x = drag.x,
pave_y = drag.y,
rect = this.getPadPainter().getPadRect(),
fr = this.onFrame ? this.getFramePainter().getFrameRect() : rect,
changes = {};
let offsetx, offsety;
switch (this.corner) {
case ECorner.kTopLeft:
offsetx = pave_x - fr.x;
offsety = pave_y - fr.y;
break;
case ECorner.kBottomLeft:
offsetx = pave_x - fr.x;
offsety = fr.y + fr.height - pave_y - this.pave_height;
break;
case ECorner.kBottomRight:
offsetx = fr.x + fr.width - pave_x - this.pave_width;
offsety = fr.y + fr.height - pave_y - this.pave_height;
break;
case ECorner.kTopRight:
default:
offsetx = fr.x + fr.width - pave_x - this.pave_width;
offsety = pave_y - fr.y;
}
this.v7AttrChange(changes, 'offsetX', offsetx / rect.width);
this.v7AttrChange(changes, 'offsetY', offsety / rect.height);
this.v7AttrChange(changes, 'width', this.pave_width / rect.width);
this.v7AttrChange(changes, 'height', this.pave_height / rect.height);
this.v7SendAttrChanges(changes, false); // do not invoke canvas update on the server
this.draw_g.selectChild('rect')
.attr('width', this.pave_width)
.attr('height', this.pave_height);
this.drawContent();
}
/** @summary Redraw RPave object */
async redraw(/* reason */) {
return this.drawPave();
}
/** @summary draw RPave object */
static async draw(dom, pave, opt) {
const painter = new RPavePainter(dom, pave, opt, 'pave');
return ensureRCanvas(painter, false).then(() => painter.drawPave());
}
}
/**
* @summary Painter for RLegend class
*
* @private
*/
class RLegendPainter extends RPavePainter {
/** @summary draw RLegend content */
async drawContent() {
const legend = this.getObject(),
textFont = this.v7EvalFont('text', { size: 12, color: 'black', align: 22 }),
width = this.pave_width,
height = this.pave_height,
pp = this.getPadPainter();
let nlines = legend.fEntries.length;
if (legend.fTitle) nlines++;
if (!nlines || !pp) return this;
const stepy = height / nlines, margin_x = 0.02 * width;
textFont.setSize(height/(nlines * 1.2));
return this.startTextDrawingAsync(textFont, 'font').then(() => {
let posy = 0;
if (legend.fTitle) {
this.drawText({ latex: 1, width: width - 2*margin_x, height: stepy, x: margin_x, y: posy, text: legend.fTitle });
posy += stepy;
}
for (let i = 0; i < legend.fEntries.length; ++i) {
const entry = legend.fEntries[i], w4 = Math.round(width/4);
let objp = null;
this.drawText({ latex: 1, width: 0.75*width - 3*margin_x, height: stepy, x: 2*margin_x + w4, y: posy, text: entry.fLabel });
if (entry.fDrawableId !== 'custom')
objp = pp.findSnap(entry.fDrawableId, true);
else if (entry.fDrawable.fIO) {
objp = new RObjectPainter(this.getPadPainter(), entry.fDrawable.fIO);
if (entry.fLine) objp.createv7AttLine();
if (entry.fFill) objp.createv7AttFill();
if (entry.fMarker) objp.createv7AttMarker();
}
if (entry.fFill && objp?.fillatt) {
this.draw_g
.append('svg:path')
.attr('d', `M${Math.round(margin_x)},${Math.round(posy + stepy*0.1)}h${w4}v${Math.round(stepy*0.8)}h${-w4}z`)
.call(objp.fillatt.func);
}
if (entry.fLine && objp?.lineatt) {
this.draw_g
.append('svg:path')
.attr('d', `M${Math.round(margin_x)},${Math.round(posy + stepy/2)}h${w4}`)
.call(objp.lineatt.func);
}
if (entry.fError && objp?.lineatt) {
this.draw_g
.append('svg:path')
.attr('d', `M${Math.round(margin_x + width/8)},${Math.round(posy + stepy*0.2)}v${Math.round(stepy*0.6)}`)
.call(objp.lineatt.func);
}
if (entry.fMarker && objp?.markeratt) {
this.draw_g.append('svg:path')
.attr('d', objp.markeratt.create(margin_x + width/8, posy + stepy/2))
.call(objp.markeratt.func);
}
posy += stepy;
}
return this.finishTextDrawing();
});
}
/** @summary draw RLegend object */
static async draw(dom, legend, opt) {
const painter = new RLegendPainter(dom, legend, opt, 'legend');
return ensureRCanvas(painter, false).then(() => painter.drawPave());
}
} // class RLegendPainter
/**
* @summary Painter for RPaveText class
*
* @private
*/
class RPaveTextPainter extends RPavePainter {
/** @summary draw RPaveText content */
async drawContent() {
const pavetext = this.getObject(),
textFont = this.v7EvalFont('text', { size: 12, color: 'black', align: 22 }),
width = this.pave_width,
height = this.pave_height,
nlines = pavetext.fText.length;
if (!nlines) return;
const stepy = height / nlines, margin_x = 0.02 * width;
textFont.setSize(height/(nlines * 1.2));
return this.startTextDrawingAsync(textFont, 'font').then(() => {
for (let i = 0, posy = 0; i < pavetext.fText.length; ++i, posy += stepy)
this.drawText({ latex: 1, width: width - 2*margin_x, height: stepy, x: margin_x, y: posy, text: pavetext.fText[i] });
return this.finishTextDrawing(undefined, true);
});
}
/** @summary draw RPaveText object */
static async draw(dom, pave, opt) {
const painter = new RPaveTextPainter(dom, pave, opt, 'pavetext');
return ensureRCanvas(painter, false).then(() => painter.drawPave());
}
} // class RPaveTextPainter
export { RPavePainter, RLegendPainter, RPaveTextPainter };