UNPKG

jsroot

Version:
235 lines (201 loc) 7.38 kB
import { clTF1 } from '../core.mjs'; import { scaleLinear as d3_scaleLinear, scaleLog as d3_scaleLog } from '../d3.mjs'; import { makeTranslate } from '../base/BasePainter.mjs'; import { EAxisBits, TAxisPainter } from '../gpad/TAxisPainter.mjs'; import { ensureTCanvas } from '../gpad/TCanvasPainter.mjs'; import { addMoveHandler } from '../gui/utils.mjs'; import { assignContextMenu, kNoReorder } from '../gui/menu.mjs'; import { getHPainter } from '../gui/display.mjs'; import { proivdeEvalPar } from '../base/func.mjs'; /** @summary Drawing TGaxis * @private */ class TGaxisPainter extends TAxisPainter { /** @summary Convert TGaxis position into NDC to fix it when frame zoomed */ convertTo(opt) { const gaxis = this.getObject(), x1 = this.axisToSvg('x', gaxis.fX1), y1 = this.axisToSvg('y', gaxis.fY1), x2 = this.axisToSvg('x', gaxis.fX2), y2 = this.axisToSvg('y', gaxis.fY2); if (opt === 'ndc') { const pw = this.getPadPainter().getPadWidth(), ph = this.getPadPainter().getPadHeight(); gaxis.fX1 = x1 / pw; gaxis.fX2 = x2 / pw; gaxis.fY1 = (ph - y1) / ph; gaxis.fY2 = (ph - y2)/ ph; this.use_ndc = true; } else if (opt === 'frame') { const rect = this.getFramePainter().getFrameRect(); gaxis.fX1 = (x1 - rect.x) / rect.width; gaxis.fX2 = (x2 - rect.x) / rect.width; gaxis.fY1 = (y1 - rect.y) / rect.height; gaxis.fY2 = (y2 - rect.y) / rect.height; this.bind_frame = true; } } /** @summary Drag moving handle */ moveDrag(dx, dy) { this.gaxis_x += dx; this.gaxis_y += dy; makeTranslate(this.getG(), this.gaxis_x, this.gaxis_y); } /** @summary Drag end handle */ moveEnd(not_changed) { if (not_changed) return; const gaxis = this.getObject(); let fx, fy; if (this.bind_frame) { const rect = this.getFramePainter().getFrameRect(); fx = (this.gaxis_x - rect.x) / rect.width; fy = (this.gaxis_y - rect.y) / rect.height; } else { fx = this.svgToAxis('x', this.gaxis_x, this.use_ndc); fy = this.svgToAxis('y', this.gaxis_y, this.use_ndc); } if (this.vertical) { gaxis.fX1 = gaxis.fX2 = fx; if (this.reverse) { gaxis.fY2 = fy + (gaxis.fY2 - gaxis.fY1); gaxis.fY1 = fy; } else { gaxis.fY1 = fy + (gaxis.fY1 - gaxis.fY2); gaxis.fY2 = fy; } } else { if (this.reverse) { gaxis.fX1 = fx + (gaxis.fX1 - gaxis.fX2); gaxis.fX2 = fx; } else { gaxis.fX2 = fx + (gaxis.fX2 - gaxis.fX1); gaxis.fX1 = fx; } gaxis.fY1 = gaxis.fY2 = fy; } this.submitAxisExec(`SetX1(${gaxis.fX1});;SetX2(${gaxis.fX2});;SetY1(${gaxis.fY1});;SetY2(${gaxis.fY2})`, true); } /** @summary Redraw axis, used in standalone mode for TGaxis */ redraw() { const gaxis = this.getObject(), min = gaxis.fWmin, max = gaxis.fWmax; let x1, y1, x2, y2; if (this.bind_frame) { const rect = this.getFramePainter().getFrameRect(); x1 = Math.round(rect.x + gaxis.fX1 * rect.width); x2 = Math.round(rect.x + gaxis.fX2 * rect.width); y1 = Math.round(rect.y + gaxis.fY1 * rect.height); y2 = Math.round(rect.y + gaxis.fY2 * rect.height); } else { x1 = this.axisToSvg('x', gaxis.fX1, this.use_ndc); y1 = this.axisToSvg('y', gaxis.fY1, this.use_ndc); x2 = this.axisToSvg('x', gaxis.fX2, this.use_ndc); y2 = this.axisToSvg('y', gaxis.fY2, this.use_ndc); } const w = x2 - x1, h = y1 - y2, vertical = Math.abs(w) < Math.abs(h); let sz = vertical ? h : w, reverse = false; if (sz < 0) { reverse = true; sz = -sz; if (vertical) y2 = y1; else x1 = x2; } this.configureAxis(vertical ? 'yaxis' : 'xaxis', min, max, min, max, vertical, [0, sz], { time_scale: gaxis.fChopt.indexOf('t') >= 0, log: (gaxis.fChopt.indexOf('G') >= 0) ? 1 : 0, reverse, swap_side: reverse, axis_func: this.axis_func }); this.createG(); this.gaxis_x = x1; this.gaxis_y = y2; return this.drawAxis(this.getG(), Math.abs(w), Math.abs(h), makeTranslate(this.gaxis_x, this.gaxis_y) || '').then(() => { addMoveHandler(this); assignContextMenu(this, kNoReorder); return this; }); } /** @summary Fill TGaxis context menu items */ fillContextMenuItems(menu) { menu.addTAxisMenu(EAxisBits, this, this.getObject(), ''); } /** @summary Check if there is function for TGaxis can be found */ async checkFuncion() { const gaxis = this.getObject(); if (!gaxis.fFunctionName) { this.axis_func = null; return; } const func = this.getPadPainter()?.findInPrimitives(gaxis.fFunctionName, clTF1); let promise = Promise.resolve(func); if (!func) { const h = getHPainter(), item = h?.findItem({ name: gaxis.fFunctionName, check_keys: true }); if (item) { promise = h.getObject({ item }).then(res => { return res?.obj?._typename === clTF1 ? res.obj : null; }); } } return promise.then(f => { this.axis_func = f; if (f) proivdeEvalPar(f); }); } /** @summary Create handle for custom function in the axis */ createFuncHandle(func, logbase, smin, smax) { const res = function(v) { return res.toGraph(v); }; res._func = func; res._domain = [smin, smax]; res._scale = logbase ? d3_scaleLog().base(logbase) : d3_scaleLinear(); res._scale.domain(res._domain).range([0, 100]); res.eval = function(v) { try { v = res._func.evalPar(v); } catch { v = 0; } return Number.isFinite(v) ? v : 0; }; const vmin = res.eval(smin), vmax = res.eval(smax); if ((vmin < vmax) === (smin < smax)) { res._vmin = vmin; res._vk = 1/(vmax - vmin); } else if (vmin === vmax) { res._vmin = 0; res._vk = 1; } else { res._vmin = vmax; res._vk = 1/(vmin - vmax); } res._range = [0, 100]; res.range = function(arr) { if (arr) { res._range = arr; return res; } return res._range; }; res.domain = function() { return res._domain; }; res.toGraph = function(v) { const rel = (res.eval(v) - res._vmin) * res._vk; return res._range[0] * (1 - rel) + res._range[1] * rel; }; res.ticks = function(arg) { return res._scale.ticks(arg); }; return res; } /** @summary Draw TGaxis object */ static async draw(dom, obj, opt) { const painter = new TGaxisPainter(dom, obj, false); return ensureTCanvas(painter, false).then(() => { if (opt) painter.convertTo(opt); return painter.checkFuncion(); }).then(() => painter.redraw()); } } // class TGaxisPainter export { TGaxisPainter };