jsrootdi
Version:
JavaScript ROOT
296 lines (239 loc) • 9.85 kB
JavaScript
import { createHistogram, setHistogramTitle, kNoStats, settings, clTF3, clTH2F, isStr } from '../core.mjs';
import { TH2Painter } from '../hist/TH2Painter.mjs';
import { proivdeEvalPar } from '../base/func.mjs';
import { produceTAxisLogScale } from '../hist/TF1Painter.mjs';
import { ObjectPainter, getElementMainPainter } from '../base/ObjectPainter.mjs';
import { DrawOptions } from '../base/BasePainter.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 {
/** @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.$func = 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.force_saved);
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;
if (gr?.zoom_xmin !== gr?.zoom_xmax) {
xmin = Math.min(xmin, gr.zoom_xmin);
xmax = Math.max(xmax, gr.zoom_xmax);
}
if (gr?.zoom_ymin !== gr?.zoom_ymax) {
ymin = Math.min(ymin, gr.zoom_ymin);
ymax = Math.max(ymax, gr.zoom_ymax);
}
if (gr?.zoom_zmin !== gr?.zoom_zmax) {
zmin = Math.min(zmin, gr.zoom_zmin);
zmax = Math.max(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;
};
delete this._fail_eval;
if (!this._use_saved_points) {
const npx = Math.max(func.fNpx, 20),
npy = Math.max(func.fNpy, 20),
npz = Math.max(func.fNpz, 20);
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];
const npx = Math.round(func.fSave[nsave+6]),
npy = Math.round(func.fSave[nsave+7]),
npz = Math.round(func.fSave[nsave+8]),
// dx = (xmax - xmin) / npx,
// dy = (ymax - ymin) / npy,
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
* @private */
fillWebObjectOptions(opt) {
// mark that saved points are used or evaluation failed
opt.fcust = this._fail_eval ? 'func_fail' : '';
}
/** @summary draw TF3 object */
static async draw(dom, tf3, opt) {
if (!isStr(opt)) opt = '';
let p = opt.indexOf(';webcanv_hist'), webcanv_hist = false, force_saved = false;
if (p >= 0) {
webcanv_hist = true;
opt = opt.slice(0, p);
}
p = opt.indexOf(';force_saved');
if (p >= 0) {
force_saved = true;
opt = opt.slice(0, p);
}
const d = new DrawOptions(opt);
if (d.empty() || (opt === 'gl'))
opt = 'surf1';
else if (d.opt === 'SAME')
opt = 'surf1 same';
if ((opt.indexOf('same') === 0) || (opt.indexOf('SAME') === 0)) {
if (!getElementMainPainter(dom))
opt = 'A_ADJUST_FRAME_' + opt.slice(4);
}
let hist;
if (webcanv_hist) {
const dummy = new ObjectPainter(dom);
hist = dummy.getPadPainter()?.findInPrimitives('Func', clTH2F);
}
if (!hist) {
hist = createHistogram(clTH2F, 20, 20);
hist.fBits |= kNoStats;
}
const painter = new TF3Painter(dom, hist);
painter.$func = tf3;
painter.webcanv_hist = webcanv_hist;
painter.force_saved = force_saved;
painter.createTF3Histogram(tf3, hist);
return THistPainter._drawHist(painter, opt);
}
} // class TF3Painter
export { TF3Painter };