UNPKG

jsroot

Version:
302 lines (244 loc) 9.78 kB
import { createHistogram, setHistogramTitle, kNoStats, settings, clTF3, clTH2F } from '../core.mjs'; import { TH2Painter } from '../hist/TH2Painter.mjs'; import { proivdeEvalPar } from '../base/func.mjs'; import { produceTAxisLogScale, scanTF1Options } from '../hist/TF1Painter.mjs'; import { DrawOptions } from '../base/BasePainter.mjs'; import { getElementPadPainter } from '../base/ObjectPainter.mjs'; import { THistPainter } from '../hist2d/THistPainter.mjs'; function findZValue(arrz, arrv, cross = 0) { for (let i = arrz.length - 2; i >= 0; --i) { const v1 = arrv[i], v2 = arrv[i + 1], z1 = arrz[i], z2 = arrz[i + 1]; if (v1 === cross) return z1; if (v2 === cross) return z2; if ((v1 < cross) !== (v2 < cross)) return z1 + (cross - v1) / (v2 - v1) * (z2 - z1); } return arrz[0] - 1; } /** * @summary Painter for TF3 object * * @private */ class TF3Painter extends TH2Painter { #use_saved_points; // use saved points for drawing #func; // func object #fail_eval; // fail evaluation of function /** @summary Assign function */ setFunc(f) { this.#func = f; } /** @summary Returns drawn object name */ getObjectName() { return this.#func?.fName ?? 'func'; } /** @summary Returns drawn object class name */ getClassName() { return this.#func?._typename ?? clTF3; } /** @summary Returns true while function is drawn */ isTF1() { return true; } /** @summary Returns primary function which was then drawn as histogram */ getPrimaryObject() { return this.#func; } /** @summary Update histogram */ updateObject(obj /* , opt */) { if (!obj || (this.getClassName() !== obj._typename)) return false; delete obj.evalPar; const histo = this.getHisto(); if (this._webcanv_hist) { const h0 = this.getPadPainter()?.findInPrimitives('Func', clTH2F); if (h0) this.updateAxes(histo, h0, this.getFramePainter()); } this.setFunc(obj); this.createTF3Histogram(obj, histo); this.scanContent(); return true; } /** @summary Redraw TF2 * @private */ redraw(reason) { if (!this.#use_saved_points && (reason === 'logx' || reason === 'logy' || reason === 'logy' || reason === 'zoom')) { this.createTF3Histogram(this.#func, this.getHisto()); this.scanContent(); } return super.redraw(reason); } /** @summary Create histogram for TF3 drawing * @private */ createTF3Histogram(func, hist) { const nsave = func.fSave.length - 9; this.#use_saved_points = (nsave > 0) && (settings.PreferSavedPoints || (this._use_saved > 1)); const fp = this.getFramePainter(), pad = this.getPadPainter()?.getRootPad(true), logx = pad?.fLogx, logy = pad?.fLogy, gr = fp?.getGrFuncs(this.second_x, this.second_y); let xmin = func.fXmin, xmax = func.fXmax, ymin = func.fYmin, ymax = func.fYmax, zmin = func.fZmin, zmax = func.fZmax, npx = Math.max(func.fNpx, 20), npy = Math.max(func.fNpy, 20), npz = Math.max(func.fNpz, 20); if (gr?.zoom_xmin !== gr?.zoom_xmax) { const dx = (xmax - xmin) / npx; if ((xmin < gr.zoom_xmin) && (gr.zoom_xmin < xmax)) xmin = Math.max(xmin, gr.zoom_xmin - dx); if ((xmin < gr.zoom_xmax) && (gr.zoom_xmax < xmax)) xmax = Math.min(xmax, gr.zoom_xmax + dx); } if (gr?.zoom_ymin !== gr?.zoom_ymax) { const dy = (ymax - ymin) / npy; if ((ymin < gr.zoom_ymin) && (gr.zoom_ymin < ymax)) ymin = Math.max(ymin, gr.zoom_ymin - dy); if ((ymin < gr.zoom_ymax) && (gr.zoom_ymax < ymax)) ymax = Math.min(ymax, gr.zoom_ymax + dy); } if (gr?.zoom_zmin !== gr?.zoom_zmax) { // no need for dz here - TH2 is not binned over Z axis if ((zmin < gr.zoom_zmin) && (gr.zoom_zmin < zmax)) zmin = gr.zoom_zmin; if ((zmin < gr.zoom_zmax) && (gr.zoom_zmax < zmax)) zmax = gr.zoom_zmax; } const ensureBins = (nx, ny) => { if (hist.fNcells !== (nx + 2) * (ny + 2)) { hist.fNcells = (nx + 2) * (ny + 2); hist.fArray = new Float32Array(hist.fNcells); } hist.fArray.fill(0); hist.fXaxis.fNbins = nx; hist.fXaxis.fXbins = []; hist.fYaxis.fNbins = ny; hist.fYaxis.fXbins = []; hist.fXaxis.fXmin = xmin; hist.fXaxis.fXmax = xmax; hist.fYaxis.fXmin = ymin; hist.fYaxis.fXmax = ymax; hist.fMinimum = zmin; hist.fMaximum = zmax; }; this.#fail_eval = undefined; if (!this.#use_saved_points) { let iserror = false; if (!func.evalPar && !proivdeEvalPar(func)) iserror = true; ensureBins(npx, npy); if (logx) produceTAxisLogScale(hist.fXaxis, npx, xmin, xmax); if (logy) produceTAxisLogScale(hist.fYaxis, npy, ymin, ymax); const arrv = new Array(npz), arrz = new Array(npz); for (let k = 0; k < npz; ++k) arrz[k] = zmin + k / (npz - 1) * (zmax - zmin); for (let j = 0; (j < npy) && !iserror; ++j) { for (let i = 0; (i < npx) && !iserror; ++i) { const x = hist.fXaxis.GetBinCenter(i + 1), y = hist.fYaxis.GetBinCenter(j + 1); let z = 0; try { for (let k = 0; k < npz; ++k) arrv[k] = func.evalPar(x, y, arrz[k]); z = findZValue(arrz, arrv); } catch { iserror = true; } if (!iserror) hist.setBinContent(hist.getBin(i + 1, j + 1), Number.isFinite(z) ? z : 0); } } if (iserror) this.#fail_eval = true; if (iserror && (nsave > 0)) this.#use_saved_points = true; } if (this.#use_saved_points) { xmin = func.fSave[nsave]; xmax = func.fSave[nsave + 1]; ymin = func.fSave[nsave + 2]; ymax = func.fSave[nsave + 3]; zmin = func.fSave[nsave + 4]; zmax = func.fSave[nsave + 5]; npx = Math.round(func.fSave[nsave + 6]); npy = Math.round(func.fSave[nsave + 7]); npz = Math.round(func.fSave[nsave + 8]); const dz = (zmax - zmin) / npz; ensureBins(npx + 1, npy + 1); const arrv = new Array(npz + 1), arrz = new Array(npz + 1); for (let k = 0; k <= npz; k++) arrz[k] = zmin + k * dz; for (let i = 0; i <= npx; ++i) { for (let j = 0; j <= npy; ++j) { for (let k = 0; k <= npz; k++) arrv[k] = func.fSave[i + (npx + 1) * (j + (npy + 1) * k)]; const z = findZValue(arrz, arrv); hist.setBinContent(hist.getBin(i + 1, j + 1), Number.isFinite(z) ? z : 0); } } } hist.fName = 'Func'; setHistogramTitle(hist, func.fTitle); // hist.fMinimum = func.fMinimum; // hist.fMaximum = func.fMaximum; // fHistogram->SetContour(fContour.fN, levels); hist.fLineColor = func.fLineColor; hist.fLineStyle = func.fLineStyle; hist.fLineWidth = func.fLineWidth; hist.fFillColor = func.fFillColor; hist.fFillStyle = func.fFillStyle; hist.fMarkerColor = func.fMarkerColor; hist.fMarkerStyle = func.fMarkerStyle; hist.fMarkerSize = func.fMarkerSize; hist.fBits |= kNoStats; return hist; } /** @summary Extract function ranges */ extractAxesProperties(ndim) { super.extractAxesProperties(ndim); const func = this.#func, nsave = func?.fSave.length ?? 0; if (nsave > 9 && this.#use_saved_points) { this.xmin = Math.min(this.xmin, func.fSave[nsave - 9]); this.xmax = Math.max(this.xmax, func.fSave[nsave - 8]); this.ymin = Math.min(this.ymin, func.fSave[nsave - 7]); this.ymax = Math.max(this.ymax, func.fSave[nsave - 6]); this.zmin = Math.min(this.zmin, func.fSave[nsave - 5]); this.zmax = Math.max(this.zmax, func.fSave[nsave - 4]); } if (func) { this.xmin = Math.min(this.xmin, func.fXmin); this.xmax = Math.max(this.xmax, func.fXmax); this.ymin = Math.min(this.ymin, func.fYmin); this.ymax = Math.max(this.ymax, func.fYmax); this.zmin = Math.min(this.zmin, func.fZmin); this.zmax = Math.max(this.zmax, func.fZmax); } } /** @summary fill information for TWebCanvas * @desc Used to inform web canvas when evaluation failed * @private */ fillWebObjectOptions(opt) { opt.fcust = this.#fail_eval && !this._use_saved ? 'func_fail' : ''; } /** @summary draw TF3 object */ static async draw(dom, tf3, opt) { const web = scanTF1Options(opt); opt = web.opt; delete web.opt; const d = new DrawOptions(opt); if (d.empty() || (opt === 'gl')) opt = 'surf1'; else if (d.opt === 'SAME') opt = 'surf1 same'; let hist; if (web._webcanv_hist) hist = getElementPadPainter(dom)?.findInPrimitives('Func', clTH2F); if (!hist) { hist = createHistogram(clTH2F, 20, 20); hist.fBits |= kNoStats; } const painter = new TF3Painter(dom, hist); painter.setFunc(tf3, clTF3); Object.assign(painter, web); painter.createTF3Histogram(tf3, hist); return THistPainter._drawHist(painter, opt); } } // class TF3Painter export { TF3Painter };