import { create, clTPad, clTLine, isFunc } from '../core.mjs';
import { ObjectPainter } from '../base/ObjectPainter.mjs';
import { ensureTCanvas } from '../gpad/TCanvasPainter.mjs';
import { TLinePainter } from './TLinePainter.mjs';
/**
* @summary Painter class for TRatioPlot
*
* @private
*/
const k_upper_pad = 'upper_pad', k_lower_pad = 'lower_pad', k_top_pad = 'top_pad';
class TRatioPlotPainter extends ObjectPainter {
/** @summary Set grids range */
setGridsRange(xmin, xmax, ymin, ymax, low_p) {
const ratio = this.getObject();
if (xmin === xmax) {
const x_handle = this.getPadPainter()?.findPainterFor(ratio.fLowerPad, k_lower_pad, clTPad)?.getFramePainter()?.x_handle;
if (!x_handle) return;
if (xmin === 0) {
// in case of unzoom full range should be used
xmin = x_handle.full_min;
xmax = x_handle.full_max;
} else {
// in case of y-scale zooming actual range has to be used
xmin = x_handle.scale_min;
xmax = x_handle.scale_max;
}
}
ratio.fGridlines.forEach(line => {
line.fX1 = xmin;
line.fX2 = xmax;
});
const nlines = Math.min(ratio.fGridlines.length, ratio.fGridlinePositions.length);
for (let i = 0; i < nlines; ++i) {
const y = ratio.fGridlinePositions[i],
line = ratio.fGridlines[i];
if (ymin !== 'ignorey') {
line.$do_not_draw = (ymin !== ymax) && ((y < ymin) || (y > ymax));
line.fY1 = line.fY2 = y;
}
low_p?.findPainterFor(line)?.redraw();
}
}
/** @summary Configure custom interactive handlers for ratio plot
* @desc Should work for both new and old code */
configureInteractive() {
const ratio = this.getObject(),
pp = this.getPadPainter(),
up_p = pp.findPainterFor(ratio.fUpperPad, k_upper_pad, clTPad),
up_fp = up_p?.getFramePainter(),
low_p = pp.findPainterFor(ratio.fLowerPad, k_lower_pad, clTPad),
low_fp = low_p?.getFramePainter();
if (!up_p || !low_p)
return;
low_p.forEachPainterInPad(objp => {
if (isFunc(objp?.testEditable))
objp.testEditable(false);
});
this.setGridsRange(low_fp.scale_xmin, low_fp.scale_xmax, low_fp.scale_ymin, low_fp.scale_ymax, low_p);
if (up_p._ratio_interactive && low_p._ratio_interactive)
return;
up_p._ratio_interactive = true;
low_p._ratio_interactive = true;
up_fp.o_zoom = up_fp.zoom;
up_fp._ratio_low_fp = low_fp;
up_fp._ratio_painter = this;
up_fp.zoom = function(xmin, xmax, ymin, ymax, zmin, zmax) {
return this.o_zoom(xmin, xmax, ymin, ymax, zmin, zmax).then(res => {
this._ratio_painter.setGridsRange(up_fp.scale_xmin, up_fp.scale_xmax, 'ignory');
return this._ratio_low_fp.o_zoom(up_fp.scale_xmin, up_fp.scale_xmax).then(() => res);
});
};
up_fp.o_sizeChanged = up_fp.sizeChanged;
up_fp.sizeChanged = function() {
this.o_sizeChanged();
this._ratio_low_fp.fX1NDC = this.fX1NDC;
this._ratio_low_fp.fX2NDC = this.fX2NDC;
this._ratio_low_fp.o_sizeChanged();
};
low_fp.o_zoom = low_fp.zoom;
low_fp._ratio_up_fp = up_fp;
low_fp._ratio_painter = this;
low_fp.zoom = function(xmin, xmax, ymin, ymax, zmin, zmax) {
if (xmin === xmax) {
xmin = up_fp.xmin;
xmax = up_fp.xmax;
} else {
if (xmin < up_fp.xmin) xmin = up_fp.xmin;
if (xmax > up_fp.xmax) xmax = up_fp.xmax;
}
this._ratio_painter.setGridsRange(xmin, xmax, ymin, ymax);
return this._ratio_up_fp.o_zoom(xmin, xmax).then(() => this.o_zoom(xmin, xmax, ymin, ymax, zmin, zmax));
};
low_fp.o_sizeChanged = low_fp.sizeChanged;
low_fp.sizeChanged = function() {
this.o_sizeChanged();
this._ratio_up_fp.fX1NDC = this.fX1NDC;
this._ratio_up_fp.fX2NDC = this.fX2NDC;
this._ratio_up_fp.o_sizeChanged();
};
}
/** @summary Redraw old TRatioPlot where object was in very end of list of primitives */
async redrawOld() {
const ratio = this.getObject(),
pp = this.getPadPainter(),
top_p = pp.findPainterFor(ratio.fTopPad, k_top_pad, clTPad),
pad = pp.getRootPad(),
mirrow_axis = (pad.fFrameFillStyle === 0) ? 1 : 0,
tick_x = pad.fTickx || mirrow_axis,
tick_y = pad.fTicky || mirrow_axis;
top_p?.disablePadDrawing();
const up_p = pp.findPainterFor(ratio.fUpperPad, k_upper_pad, clTPad),
up_main = up_p?.getMainPainter(),
up_fp = up_p?.getFramePainter(),
low_p = pp.findPainterFor(ratio.fLowerPad, k_lower_pad, clTPad),
low_main = low_p?.getMainPainter(),
low_fp = low_p?.getFramePainter();
let promise_up = Promise.resolve(true);
if (up_p && up_main && up_fp && low_fp && !up_p._ratio_configured) {
up_p._ratio_configured = true;
up_main.options.Axis = 0; // draw both axes
const h = up_main.getHisto();
h.fYaxis.$use_top_pad = true; // workaround to use same scaling
h.fXaxis.fLabelSize = 0; // do not draw X axis labels
h.fXaxis.fTitle = ''; // do not draw X axis title
up_p.getRootPad().fTickx = tick_x;
up_p.getRootPad().fTicky = tick_y;
promise_up = up_p.redrawPad();
}
return promise_up.then(() => {
if (!low_p || !low_main || !low_fp || !up_fp || low_p._ratio_configured)
return this;
low_p._ratio_configured = true;
low_main.options.Axis = 0; // draw both axes
const h = low_main.getHisto();
h.fXaxis.fTitle = 'x';
h.fXaxis.$use_top_pad = true;
h.fYaxis.$use_top_pad = true;
low_p.getRootPad().fTickx = tick_x;
low_p.getRootPad().fTicky = tick_y;
const arr = [];
// add missing lines in old ratio painter
if ((ratio.fGridlinePositions.length > 0) && (ratio.fGridlines.length < ratio.fGridlinePositions.length)) {
ratio.fGridlinePositions.forEach(gridy => {
let found = false;
ratio.fGridlines.forEach(line => {
if ((line.fY1 === line.fY2) && (Math.abs(line.fY1 - gridy) < 1e-6)) found = true;
});
if (!found) {
const line = create(clTLine);
line.fX1 = up_fp.scale_xmin;
line.fX2 = up_fp.scale_xmax;
line.fY1 = line.fY2 = gridy;
line.fLineStyle = 2;
ratio.fGridlines.push(line);
arr.push(TLinePainter.draw(low_p, line));
}
});
}
return Promise.all(arr)
.then(() => low_fp.zoomSingle('x', up_fp.scale_xmin, up_fp.scale_xmax))
.then(changed => { return changed ? true : low_p.redrawPad(); })
.then(() => this);
});
}
/** @summary Redraw TRatioPlot */
async redraw() {
const ratio = this.getObject(),
pp = this.getPadPainter();
if (this.$oldratio === undefined)
this.$oldratio = Boolean(pp.findPainterFor(ratio.fTopPad, k_top_pad, clTPad));
// configure ratio interactive at the end
pp.$userInteractive = () => this.configureInteractive();
if (this.$oldratio)
return this.redrawOld();
const pad = pp.getRootPad(),
mirrow_axis = (pad.fFrameFillStyle === 0) ? 1 : 0,
tick_x = pad.fTickx || mirrow_axis,
tick_y = pad.fTicky || mirrow_axis;
// do not draw primitives and pad itself
ratio.fTopPad.$disable_drawing = true;
ratio.fUpperPad.$ratio_pad = 'up'; // indicate drawing of the axes for main painter
ratio.fUpperPad.fTickx = tick_x;
ratio.fUpperPad.fTicky = tick_y;
ratio.fLowerPad.$ratio_pad = 'low'; // indicate drawing of the axes for main painter
ratio.fLowerPad.fTickx = tick_x;
ratio.fLowerPad.fTicky = tick_y;
return this;
}
/** @summary Draw TRatioPlot */
static async draw(dom, ratio, opt) {
const painter = new TRatioPlotPainter(dom, ratio, opt);
return ensureTCanvas(painter, false).then(() => painter.redraw());
}
} // class TRatioPlotPainter
export { TRatioPlotPainter };