diff --git a/build/jsroot.js b/build/jsroot.js
index 5ee3124b4..9ef43aa8e 100644
--- a/build/jsroot.js
+++ b/build/jsroot.js
@@ -12,7 +12,7 @@ const version_id = 'dev',
/** @summary version date
* @desc Release date in format day/month/year like '14/04/2022' */
-version_date = '25/09/2025',
+version_date = '29/09/2025',
/** @summary version id and date
* @desc Produced by concatenation of {@link version_id} and {@link version_date}
@@ -173,6 +173,8 @@ const constants$1 = {
WebGLImage: 2,
/** @summary Use SVG rendering, slow, imprecise and not interactive, not recommended */
SVG: 3,
+ /** @summary Disable renderer, used for three.js model creation, only for internal use recommended */
+ None: 4,
fromString(s) {
if ((s === 'webgl') || (s === 'gl'))
return this.WebGL;
@@ -180,6 +182,8 @@ const constants$1 = {
return this.WebGLImage;
if (s === 'svg')
return this.SVG;
+ if (s === 'none')
+ return this.None;
return this.Default;
}
},
@@ -684,7 +688,8 @@ function parse$1(json) {
if (isArrayProto(proto) > 0) {
for (let i = 0; i < value.length; ++i) {
const res = unref_value(value[i]);
- if (res !== undefined) value[i] = res;
+ if (res !== undefined)
+ value[i] = res;
}
return;
}
@@ -732,8 +737,10 @@ function parse$1(json) {
// compressed coding
let nkey = 2, p = 0;
while (nkey < len) {
- if (ks[nkey][0] === 'p') p = value[ks[nkey++]]; // position
- if (ks[nkey][0] !== 'v') throw new Error(`Unexpected member ${ks[nkey]} in array decoding`);
+ if (ks[nkey][0] === 'p')
+ p = value[ks[nkey++]]; // position
+ if (ks[nkey][0] !== 'v')
+ throw new Error(`Unexpected member ${ks[nkey]} in array decoding`);
const v = value[ks[nkey++]]; // value
if (typeof v === 'object') {
for (let k = 0; k < v.length; ++k)
@@ -755,8 +762,10 @@ function parse$1(json) {
newfmt = true;
const f1 = unref_value(value.first),
s1 = unref_value(value.second);
- if (f1 !== undefined) value.first = f1;
- if (s1 !== undefined) value.second = s1;
+ if (f1 !== undefined)
+ value.first = f1;
+ if (s1 !== undefined)
+ value.second = s1;
value._typename = value.$pair;
delete value.$pair;
return; // pair object is not counted in the objects map
@@ -770,11 +779,13 @@ function parse$1(json) {
map.push(value);
// add methods to all objects, where _typename is specified
- if (value._typename) exports.addMethods(value);
+ if (value._typename)
+ exports.addMethods(value);
for (let k = 0; k < len; ++k) {
const i = ks[k], res = unref_value(value[i]);
- if (res !== undefined) value[i] = res;
+ if (res !== undefined)
+ value[i] = res;
}
};
@@ -875,7 +886,7 @@ function decodeUrl(url) {
const res = {
opts: {},
has(opt) { return this.opts[opt] !== undefined; },
- get(opt, dflt) { const v = this.opts[opt]; return v !== undefined ? v : dflt; }
+ get(opt, dflt) { return this.opts[opt] ?? dflt; }
};
if (!url || !isStr(url)) {
@@ -11500,10 +11511,12 @@ class TAttMarkerHandler {
mv = ''; // pathological case, but let exclude it
else {
const m2 = `m${xx-this.lastx},${yy - this.lasty}`;
- if (m2.length < mv.length) mv = m2;
+ if (m2.length < mv.length)
+ mv = m2;
}
}
- this.lastx = xx + 1; this.lasty = yy;
+ this.lastx = xx + 1;
+ this.lasty = yy;
return mv + 'h1';
}
@@ -11520,9 +11533,12 @@ class TAttMarkerHandler {
change(color, style, size) {
this.changed = true;
- if (color !== undefined) this.color = color;
- if ((style !== undefined) && (style >= 0)) this.style = style;
- if (size !== undefined) this.size = size;
+ if (color !== undefined)
+ this.color = color;
+ if ((style !== undefined) && (style >= 0))
+ this.style = style;
+ if (size !== undefined)
+ this.size = size;
this._configure();
}
@@ -12206,7 +12222,8 @@ class TAttLineHandler {
constructor(args) {
this.func = this.apply.bind(this);
this.used = true;
- if (args._typename && (args.fLineStyle !== undefined)) args = { attr: args };
+ if (args._typename && (args.fLineStyle !== undefined))
+ args = { attr: args };
this.setArgs(args);
}
@@ -12387,7 +12404,8 @@ class TAttTextHandler {
* @param {object} attr - attributes, see {@link TAttTextHandler#setArgs} */
constructor(args) {
this.used = true;
- if (args._typename && (args.fTextFont !== undefined)) args = { attr: args };
+ if (args._typename && (args.fTextFont !== undefined))
+ args = { attr: args };
this.setArgs(args);
}
@@ -13552,7 +13570,8 @@ class ObjectPainter extends BasePainter {
txt = arg.txt_node;
delete arg.txt_node;
is_txt = true;
- if (optimize_arr !== null) optimize_arr.push(txt);
+ if (optimize_arr !== null)
+ optimize_arr.push(txt);
} else if (arg.txt_g) {
txt = arg.txt_g;
delete arg.txt_g;
@@ -73803,11 +73822,14 @@ function getRender3DKind(render3d, is_batch) {
if (is_batch === undefined)
is_batch = isBatchMode();
- if (!render3d) render3d = is_batch ? settings.Render3DBatch : settings.Render3D;
+ if (!render3d)
+ render3d = is_batch ? settings.Render3DBatch : settings.Render3D;
const rc = constants$1.Render3D;
- if (render3d === rc.Default) render3d = is_batch ? rc.WebGLImage : rc.WebGL;
- if (is_batch && (render3d === rc.WebGL)) render3d = rc.WebGLImage;
+ if (render3d === rc.Default)
+ render3d = is_batch ? rc.WebGLImage : rc.WebGL;
+ if (is_batch && (render3d === rc.WebGL))
+ render3d = rc.WebGLImage;
return render3d;
}
@@ -73959,7 +73981,7 @@ const Handling3DDrawings = {
// case when 3D object drawn without canvas
const main = this.selectDom().node();
- if (main !== null) {
+ if (main) {
main.appendChild(canv);
canv.painter = this;
canv.$jsroot = '3d'; // mark canvas as added by jsroot
@@ -74092,11 +74114,14 @@ async function createRender3D(width, height, render3d, args) {
render3d = getRender3DKind(render3d);
- if (!args) args = { antialias: true, alpha: true };
+ if (!args)
+ args = { antialias: true, alpha: true };
let promise;
- if (render3d === rc.SVG) {
+ if (render3d === rc.None)
+ promise = Promise.resolve(null);
+ else if (render3d === rc.SVG) {
// SVG rendering
const r = createSVGRenderer(false, 0);
r.jsroot_dom = doc.createElementNS(nsSVG, 'svg');
@@ -74139,6 +74164,9 @@ async function createRender3D(width, height, render3d, args) {
}
return promise.then(renderer => {
+ if (!renderer)
+ return renderer;
+
if (!renderer.jsroot_dom)
renderer.jsroot_dom = renderer.domElement;
else
@@ -74309,11 +74337,11 @@ class TooltipFor3D {
const rect1 = this.parent.getBoundingClientRect(),
rect2 = this.canvas.getBoundingClientRect();
- if ((rect1.left !== undefined) && (rect2.left!== undefined))
- pos.l += (rect2.left-rect1.left);
+ if ((rect1.left !== undefined) && (rect2.left !== undefined))
+ pos.l += (rect2.left - rect1.left);
- if ((rect1.top !== undefined) && (rect2.top!== undefined))
- pos.u += rect2.top-rect1.top;
+ if ((rect1.top !== undefined) && (rect2.top !== undefined))
+ pos.u += rect2.top - rect1.top;
if (pos.l + this.tt.offsetWidth + 3 >= this.parent.offsetWidth)
pos.l = this.parent.offsetWidth - this.tt.offsetWidth - 3;
@@ -74382,7 +74410,7 @@ class TooltipFor3D {
/** @summary Hide tooltip */
hide() {
- if (this.tt !== null)
+ if (this.tt)
this.parent.removeChild(this.tt);
this.tt = null;
@@ -77666,8 +77694,7 @@ function registerForResize(handle, delay) {
if (!handle || isBatchMode() || (typeof window === 'undefined') || (typeof document === 'undefined'))
return;
- let myInterval = null, myDelay = delay || 300;
- if (myDelay < 20) myDelay = 20;
+ let myInterval = null;
function ResizeTimer() {
myInterval = null;
@@ -77691,8 +77718,9 @@ function registerForResize(handle, delay) {
}
window.addEventListener('resize', () => {
- if (myInterval !== null) clearTimeout(myInterval);
- myInterval = setTimeout(ResizeTimer, myDelay);
+ if (myInterval)
+ clearTimeout(myInterval);
+ myInterval = setTimeout(ResizeTimer, Math.max(20, delay || 300));
});
}
@@ -79089,8 +79117,10 @@ class JSRootMenu {
let ranges = '';
if ((value === undefined) || (value === null)) value = '';
if (kind === 'int') {
- if (min !== undefined) ranges += ` min="${min}"`;
- if (max !== undefined) ranges += ` max="${max}"`;
+ if (min !== undefined)
+ ranges += ` min="${min}"`;
+ if (max !== undefined)
+ ranges += ` max="${max}"`;
}
const main_content =
@@ -80134,8 +80164,7 @@ const AxisPainterMethods = {
} else
item.min = item.max = undefined;
-
- item.changed = ((item.min !== undefined) && (item.max !== undefined));
+ item.changed = (item.min !== undefined) && (item.max !== undefined);
return item;
}
@@ -81708,7 +81737,8 @@ class TooltipHandler extends ObjectPainter {
nhints++;
- if (hint.exact) nexact++;
+ if (hint.exact)
+ nexact++;
hint.lines.forEach(line => { maxlen = Math.max(maxlen, line.length); });
@@ -87578,9 +87608,7 @@ class TPadPainter extends ObjectPainter {
/** @summary indicates if painter performing objects draw
* @private */
- doingDraw() {
- return this.#doing_draw !== undefined;
- }
+ doingDraw() { return this.#doing_draw !== undefined; }
/** @summary confirms that drawing is completed, may trigger next drawing immediately
* @private */
@@ -93908,7 +93936,7 @@ class THistPainter extends ObjectPainter {
else {
if (nlevels < 2)
nlevels = gStyle.fNumberContours;
- const pad = this.getPadPainter().getRootPad(true),
+ const pad = this.getPadPainter()?.getRootPad(true),
logv = pad?.fLogv ?? ((ndim === 2) && pad?.fLogz);
cntr.createNormal(nlevels, logv ?? 0, zminpositive);
@@ -98239,15 +98267,17 @@ let TH2Painter$2 = class TH2Painter extends THistPainter {
}; // class TH2Painter
-function createLatexGeometry(painter, lbl, size) {
+function createLatexGeometry(painter, lbl, size, as_array) {
const geom_args = { font: getHelveticaFont(), size, height: 0, curveSegments: 5 };
if (THREE.REVISION > 162)
geom_args.depth = 0;
else
geom_args.height = 0;
- if (isPlainText(lbl))
- return new THREE.TextGeometry(translateLaTeX(lbl), geom_args);
+ if (isPlainText(lbl)) {
+ const res = new THREE.TextGeometry(translateLaTeX(lbl), geom_args);
+ return as_array ? [res] : res;
+ }
const font_size = size * 100, geoms = [];
let stroke_width = 5;
@@ -98321,6 +98351,8 @@ function createLatexGeometry(painter, lbl, size) {
this.x += Number.parseInt(value)*0.01;
else if ((name === 'y') && (this.kind === 'text'))
this.y -= Number.parseInt(value)*0.01;
+ else if ((name === 'fill') && (this.kind === 'text'))
+ this.fill = value;
else if ((name === 'd') && (this.kind === 'path')) {
if (get() !== 'M')
return console.error('Not starts with M');
@@ -98351,6 +98383,7 @@ function createLatexGeometry(painter, lbl, size) {
const pos = new Float32Array(pnts);
this.geom = new THREE.BufferGeometry();
+ this.geom._fill = this.fill;
this.geom.setAttribute('position', new THREE.BufferAttribute(pos, 3));
this.geom.scale(0.01, -0.01, 0.01);
this.geom.computeVertexNormals();
@@ -98364,6 +98397,7 @@ function createLatexGeometry(painter, lbl, size) {
if (this.kind === 'text') {
geom_args.size = Math.round(0.01*this.font_size);
this.geom = new THREE.TextGeometry(v, geom_args);
+ this.geom._fill = this.fill;
geoms.push(this.geom);
}
}
@@ -98377,11 +98411,15 @@ function createLatexGeometry(painter, lbl, size) {
if (!geoms.length) {
geom_args.size = size;
- return new THREE.TextGeometry(translateLaTeX(lbl), geom_args);
+ const res = new THREE.TextGeometry(translateLaTeX(lbl), geom_args);
+ return as_array ? [res] : res;
}
node.translate(); // apply translate attributes
+ if (as_array)
+ return geoms;
+
if (geoms.length === 1)
return geoms[0];
@@ -98409,6 +98447,53 @@ function createLatexGeometry(painter, lbl, size) {
return fullgeom;
}
+/** @summary Build three.js object for the TLatex
+ * @private */
+function build3dlatex(obj) {
+ const painter = new ObjectPainter(null, obj),
+ handle = painter.createAttText({ attr: obj }),
+ valign = handle.align % 10,
+ halign = handle.align - valign,
+ arr3d = createLatexGeometry(painter, obj.fTitle, handle.getSize() || 10, true),
+ bb = new THREE.Box3().makeEmpty();
+
+ arr3d.forEach(geom => {
+ geom.computeBoundingBox();
+ bb.expandByPoint(geom.boundingBox.max);
+ bb.expandByPoint(geom.boundingBox.min);
+ });
+
+ let width = bb.max.x - bb.min.x,
+ height = bb.max.y - bb.min.y;
+
+ if (halign === 1)
+ width = 0;
+ else if (halign === 2)
+ width *= 0.5;
+
+ if (valign === 1)
+ height = 0;
+ else if (valign === 2)
+ height *= 0.5;
+
+ const obj3d = new THREE.Object3D(),
+ materials = [],
+ getMaterial = color => {
+ if (!color)
+ color = 'black';
+ if (!materials[color])
+ materials[color] = new THREE.MeshBasicMaterial(getMaterialArgs(color, { vertexColors: false }));
+ return materials[color];
+ };
+
+ arr3d.forEach(geom => {
+ geom.translate(-width, -height, 0);
+ obj3d.add(new THREE.Mesh(geom, getMaterial(geom._fill || handle.color)));
+ });
+
+ return arr3d.length === 1 ? obj3d.children[0] : obj3d;
+}
+
/** @summary Text 3d axis visibility
* @private */
function testAxisVisibility(camera, toplevel, fb = false, bb = false) {
@@ -98568,7 +98653,7 @@ function create3DCamera(fp, orthographic) {
/** @summary Returns camera default position
* @private */
function getCameraDefaultPosition(fp, first_time) {
- const pad = fp.getPadPainter().getRootPad(true),
+ const pad = fp.getPadPainter()?.getRootPad(true),
kz = fp.camera.isOrthographicCamera ? 1 : 1.4;
let max3dx = Math.max(0.75*fp.size_x3d, fp.size_z3d),
max3dy = Math.max(0.75*fp.size_y3d, fp.size_z3d),
@@ -98740,6 +98825,12 @@ function create3DScene(render3d, x3dscale, y3dscale, orthographic) {
return;
}
+ const res = x3dscale ? this.toplevel : null;
+ if (res) {
+ this.scene?.remove(res);
+ this.toplevel = null;
+ }
+
testAxisVisibility(null, this.toplevel);
this.clear3dCanvas();
@@ -98766,10 +98857,10 @@ function create3DScene(render3d, x3dscale, y3dscale, orthographic) {
this.mode3d = false;
- if (this.getG())
+ if (this.getG() && !x3dscale)
this.createFrameG();
- return;
+ return res;
}
this.mode3d = true; // indicate 3d mode as hist painter does
@@ -98828,6 +98919,8 @@ function create3DScene(render3d, x3dscale, y3dscale, orthographic) {
return createRender3D(this.scene_width, this.scene_height, render3d);
}).then(r => {
this.renderer = r;
+ if (!r)
+ return this;
this.webgl = r.jsroot_render3d === constants$1.Render3D.WebGL;
this.add3dCanvas(sz, r.jsroot_dom, this.webgl);
@@ -99113,14 +99206,15 @@ function set3DOptions(hopt) {
/** @summary Draw axes in 3D mode
* @private */
function drawXYZ(toplevel, AxisPainter, opts) {
- if (!opts) opts = { ndim: 2 };
+ if (!opts)
+ opts = { ndim: 2 };
if (opts.drawany === false)
opts.draw = false;
else
opts.drawany = true;
- const pad = opts.v7 ? null : this.getPadPainter().getRootPad(true);
+ const pad = opts.v7 ? null : this.getPadPainter()?.getRootPad(true);
let grminx = -this.size_x3d, grmaxx = this.size_x3d,
grminy = -this.size_y3d, grmaxy = this.size_y3d,
grminz = 0, grmaxz = 2*this.size_z3d,
@@ -99157,7 +99251,8 @@ function drawXYZ(toplevel, AxisPainter, opts) {
this.lego_zmin = zmin; this.lego_zmax = zmax;
// factor 1.1 used in ROOT for lego plots
- if ((opts.zmult !== undefined) && !z_zoomed) zmax *= opts.zmult;
+ if ((opts.zmult !== undefined) && !z_zoomed)
+ zmax *= opts.zmult;
this.x_handle = new AxisPainter(null, this.xaxis);
if (opts.v7) {
@@ -99234,7 +99329,8 @@ function drawXYZ(toplevel, AxisPainter, opts) {
lbl = this.x_handle.format(xticks.tick, 2);
if (xticks.last_major()) {
- if (!this.x_handle.fTitle) lbl = 'x';
+ if (!this.x_handle.fTitle)
+ lbl = 'x';
} else if (lbl === null) {
is_major = false; lbl = '';
}
@@ -99502,7 +99598,8 @@ function drawXYZ(toplevel, AxisPainter, opts) {
lbl = this.y_handle.format(yticks.tick, 2);
if (yticks.last_major()) {
- if (!this.y_handle.fTitle) lbl = 'y';
+ if (!this.y_handle.fTitle)
+ lbl = 'y';
} else if (lbl === null) {
is_major = false; lbl = '';
}
@@ -99646,11 +99743,15 @@ function drawXYZ(toplevel, AxisPainter, opts) {
let is_major = (zticks.kind === 1),
lbl = this.z_handle.format(zticks.tick, 2);
- if (lbl === null) { is_major = false; lbl = ''; }
+ if (lbl === null) {
+ is_major = false;
+ lbl = '';
+ }
if (is_major && lbl && opts.draw && (!center_z || !zticks.last_major())) {
const mod = zticks.get_modifier();
- if (mod?.fLabText) lbl = mod.fLabText;
+ if (mod?.fLabText)
+ lbl = mod.fLabText;
const text3d = createLatexGeometry(this, lbl, this.z_handle.labelsFont.size);
text3d.computeBoundingBox();
@@ -99658,7 +99759,8 @@ function drawXYZ(toplevel, AxisPainter, opts) {
draw_height = text3d.boundingBox.max.y - text3d.boundingBox.min.y;
text3d.translate(-draw_width, -draw_height/2, 0);
- if (mod?.fTextColor) text3d.color = this.getColor(mod.fTextColor);
+ if (mod?.fTextColor)
+ text3d.color = this.getColor(mod.fTextColor);
text3d.grz = grz;
lbls.push(text3d);
@@ -99858,6 +99960,35 @@ function assignFrame3DMethods(fp) {
Object.assign(fp, { create3DScene, add3DMesh, remove3DMeshes, getRenderer, render3D, resize3D, change3DCamera, highlightBin3D, set3DOptions, drawXYZ, convert3DtoPadNDC });
}
+
+/** @summary Create 3D objects in the frame
+ * @private */
+async function crete3DFrame(painter, AxisPainterClass, render3d = constants$1.Render3D.None) {
+ const fp = painter.getFramePainter(),
+ o = painter.getOptions(),
+ histo = painter.getHisto(),
+ ndim = painter.getDimension();
+
+ assignFrame3DMethods(fp);
+
+ return fp.create3DScene(render3d, o.x3dscale, o.y3dscale, o.Ortho).then(() => {
+ fp.setAxesRanges(histo.fXaxis, painter.xmin, painter.xmax, histo.fYaxis, painter.ymin, painter.ymax, histo.fZaxis, painter.zmin, painter.zmax, painter);
+ fp.set3DOptions(o);
+ fp.drawXYZ(fp.toplevel, AxisPainterClass, {
+ ndim,
+ use_y_for_z: ndim === 1,
+ hist_painter: painter,
+ zmult: o.zmult ?? 1,
+ zoom: (render3d !== constants$1.Render3D.None) && settings.Zooming,
+ draw: o.Axis !== -1,
+ drawany: o.isCartesian(),
+ reverse_x: o.RevX,
+ reverse_y: o.RevY
+ });
+ return fp;
+ });
+}
+
function _meshLegoToolTip(intersect) {
if ((intersect.faceIndex < 0) || (intersect.faceIndex >= this.face_to_bins_index.length))
return null;
@@ -99940,8 +100071,7 @@ function drawBinsLego(painter, is_v7 = false) {
if ((binz1 >= zmax) || (binz2 < zmin))
return false;
- if (test_cutg &&
- !test_cutg.IsInside(histo.fXaxis.GetBinCoord(ii + 0.5), histo.fYaxis.GetBinCoord(jj + 0.5)))
+ if (test_cutg && !test_cutg.IsInside(histo.fXaxis.GetBinCoord(ii + 0.5), histo.fYaxis.GetBinCoord(jj + 0.5)))
return false;
reduced = (binz2 === zmin) || (binz1 >= binz2);
@@ -99983,7 +100113,8 @@ function drawBinsLego(painter, is_v7 = false) {
zmax = levels[nlevel+1];
// artificially extend last level of color palette to maximal visible value
- if (palette && (nlevel === levels.length - 2) && zmax < axis_zmax) zmax = axis_zmax;
+ if (palette && (nlevel === levels.length - 2) && zmax < axis_zmax)
+ zmax = axis_zmax;
const grzmin = fp.grz(zmin), grzmax = fp.grz(zmax);
let numvertices = 0, num2vertices = 0;
@@ -100586,6 +100717,19 @@ function drawBinsSurf3D(painter, is_v7 = false) {
}
}
+var hist3d = /*#__PURE__*/Object.freeze({
+__proto__: null,
+assignFrame3DMethods: assignFrame3DMethods,
+build3dlatex: build3dlatex,
+convertLegoBuf: convertLegoBuf,
+createLegoGeom: createLegoGeom,
+crete3DFrame: crete3DFrame,
+drawBinsContour3D: drawBinsContour3D,
+drawBinsError3D: drawBinsError3D,
+drawBinsLego: drawBinsLego,
+drawBinsSurf3D: drawBinsSurf3D
+});
+
/** @summary Assign `evalPar` function for TF1 object
* @private */
@@ -101018,7 +101162,8 @@ let TH1Painter$2 = class TH1Painter extends THistPainter {
let stat_sumw = 0, stat_sumw2 = 0, stat_sumwx = 0, stat_sumwx2 = 0, stat_sumwy = 0, stat_sumwy2 = 0,
i, xx, w, xmax = null, wmax = null;
- if (!isFunc(cond)) cond = null;
+ if (!isFunc(cond))
+ cond = null;
for (i = left; i < right; ++i) {
xx = xaxis.GetBinCoord(i + 0.5);
@@ -102168,9 +102313,9 @@ class TH1Painter extends TH1Painter$2 {
const fp = this.getFramePainter(), // who makes axis drawing
is_main = this.isMainPainter(), // is main histogram
- histo = this.getHisto(),
- o = this.getOptions(),
- zmult = 1 + 2*gStyle.fHistTopMargin;
+ o = this.getOptions();
+
+ o.zmult = 1 + 2*gStyle.fHistTopMargin;
let pr = Promise.resolve(true), full_draw = true;
if (reason === 'resize') {
@@ -102187,17 +102332,8 @@ class TH1Painter extends TH1Painter$2 {
this.scanContent(reason === 'zoom'); // may be required for axis drawings
- if (is_main) {
- assignFrame3DMethods(fp);
- pr = fp.create3DScene(o.Render3D, o.x3dscale, o.y3dscale, o.Ortho).then(() => {
- fp.setAxesRanges(histo.fXaxis, this.xmin, this.xmax, histo.fYaxis, this.ymin, this.ymax, histo.fZaxis, 0, 0, this);
- fp.set3DOptions(o);
- fp.drawXYZ(fp.toplevel, TAxisPainter, {
- ndim: 1, hist_painter: this, use_y_for_z: true, zmult, zoom: settings.Zooming,
- draw: (o.Axis !== -1), drawany: o.isCartesian()
- });
- });
- }
+ if (is_main)
+ pr = crete3DFrame(this, TAxisPainter, o.Render3D);
if (fp.mode3d) {
pr = pr.then(() => {
@@ -102217,6 +102353,24 @@ class TH1Painter extends TH1Painter$2 {
.then(() => this);
}
+ /** @summary Build three.js object for the histogram */
+ static async build3d(histo, opt) {
+ const painter = new TH1Painter(null, histo);
+ painter.decodeOptions(opt);
+ painter.scanContent();
+
+ painter.createHistDrawAttributes(true);
+ painter.options.zmult = 1 + 2*gStyle.fHistTopMargin;
+
+ const fp = new TFramePainter(null, null);
+ painter.getFramePainter = () => fp;
+
+ return crete3DFrame(painter, TAxisPainter)
+ .then(() => drawBinsLego(painter))
+ .then(() => fp.create3DScene(-1, true));
+ }
+
+
/** @summary draw TH1 object */
static async draw(dom, histo, opt) {
return THistPainter._drawHist(new TH1Painter(dom, histo), opt);
@@ -102427,13 +102581,53 @@ function drawTH2PolyLego(painter) {
* @private */
class TH2Painter extends TH2Painter$2 {
+ /** @summary Check range for 3D
+ * @private */
+ checkRangeFor3D(o) {
+ const pad = this.getPadPainter()?.getRootPad(true),
+ logz = pad?.fLogv ?? pad?.fLogz;
+ let zmult = 1;
+
+ if (o.ohmin && o.ohmax) {
+ this.zmin = o.hmin;
+ this.zmax = o.hmax;
+ } else if (o.minimum !== kNoZoom && o.maximum !== kNoZoom) {
+ this.zmin = o.minimum;
+ this.zmax = o.maximum;
+ } else if (this.draw_content || this.gmaxbin) {
+ this.zmin = logz ? this.gminposbin * 0.3 : this.gminbin;
+ this.zmax = this.gmaxbin;
+ zmult = 1 + 2*gStyle.fHistTopMargin;
+ }
+
+ if (logz && (this.zmin <= 0))
+ this.zmin = this.zmax * 1e-5;
+
+ this.createHistDrawAttributes(true);
+ return zmult;
+ }
+
+ /** @summary Create 3D object for histogram bins
+ * @private */
+ draw3DBins(o) {
+ if (this.isTH2Poly())
+ drawTH2PolyLego(this);
+ else if (o.Contour)
+ drawBinsContour3D(this, true);
+ else if (o.Surf)
+ drawBinsSurf3D(this);
+ else if (o.Error)
+ drawBinsError3D(this);
+ else
+ drawBinsLego(this);
+ }
+
/** @summary draw TH2 object in 3D mode */
async draw3D(reason) {
this.mode3d = true;
const fp = this.getFramePainter(), // who makes axis drawing
is_main = this.isMainPainter(), // is main histogram
- histo = this.getHisto(),
o = this.getOptions();
let pr = Promise.resolve(true), full_draw = true;
@@ -102448,54 +102642,16 @@ class TH2Painter extends TH2Painter$2 {
}
if (full_draw) {
- const pad = this.getPadPainter().getRootPad(true),
- logz = pad?.fLogv ?? pad?.fLogz;
- let zmult = 1;
-
- if (o.ohmin && o.ohmax) {
- this.zmin = o.hmin;
- this.zmax = o.hmax;
- } else if (o.minimum !== kNoZoom && o.maximum !== kNoZoom) {
- this.zmin = o.minimum;
- this.zmax = o.maximum;
- } else if (this.draw_content || this.gmaxbin) {
- this.zmin = logz ? this.gminposbin * 0.3 : this.gminbin;
- this.zmax = this.gmaxbin;
- zmult = 1 + 2*gStyle.fHistTopMargin;
- }
-
- if (logz && (this.zmin <= 0))
- this.zmin = this.zmax * 1e-5;
-
- this.createHistDrawAttributes(true);
+ o.zmult = this.checkRangeFor3D(o);
- if (is_main) {
- assignFrame3DMethods(fp);
- pr = fp.create3DScene(o.Render3D, o.x3dscale, o.y3dscale, o.Ortho).then(() => {
- fp.setAxesRanges(histo.fXaxis, this.xmin, this.xmax, histo.fYaxis, this.ymin, this.ymax, histo.fZaxis, this.zmin, this.zmax, this);
- fp.set3DOptions(o);
- fp.drawXYZ(fp.toplevel, TAxisPainter, {
- ndim: 2, hist_painter: this, zmult, zoom: settings.Zooming,
- draw: o.Axis !== -1, drawany: o.isCartesian(),
- reverse_x: o.RevX, reverse_y: o.RevY
- });
- });
- }
+ if (is_main)
+ pr = crete3DFrame(this, TAxisPainter, o.Render3D);
if (fp.mode3d) {
pr = pr.then(() => {
- if (this.draw_content) {
- if (this.isTH2Poly())
- drawTH2PolyLego(this);
- else if (o.Contour)
- drawBinsContour3D(this, true);
- else if (o.Surf)
- drawBinsSurf3D(this);
- else if (o.Error)
- drawBinsError3D(this);
- else
- drawBinsLego(this);
- } else if (o.Axis && o.Zscale) {
+ if (this.draw_content)
+ this.draw3DBins(o);
+ else if (o.Axis && o.Zscale) {
this.getContourLevels(true);
this.getHistPalette();
}
@@ -102517,6 +102673,30 @@ class TH2Painter extends TH2Painter$2 {
.then(() => this);
}
+ /** @summary Build three.js object for the histogram */
+ static async build3d(histo, opt, get_painter) {
+ const painter = new TH2Painter(null, histo);
+ painter.decodeOptions(opt);
+
+ const o = painter.getOptions();
+ if (painter.isTH2Poly())
+ o.Lego = 12;
+ painter.scanContent();
+
+ o.zmult = painter.checkRangeFor3D(o);
+
+ const fp = new TFramePainter(null, null);
+ // return dummy frame painter as result
+ painter.getFramePainter = () => fp;
+
+ return crete3DFrame(painter, TAxisPainter).then(() => {
+ if (painter.draw_content)
+ painter.draw3DBins(o);
+
+ return get_painter ? painter : fp.create3DScene(-1, true);
+ });
+ }
+
/** @summary draw TH2 object */
static async draw(dom, histo, opt) {
return THistPainter._drawHist(new TH2Painter(dom, histo), opt);
@@ -103155,7 +103335,6 @@ class TH3Painter extends THistPainter {
/** @summary Redraw TH3 histogram */
async redraw(reason) {
const fp = this.getFramePainter(), // who makes axis and 3D drawing
- histo = this.getHisto(),
o = this.getOptions();
let pr = Promise.resolve(true), full_draw = true;
@@ -103170,20 +103349,13 @@ class TH3Painter extends THistPainter {
}
if (full_draw) {
- assignFrame3DMethods(fp);
- pr = fp.create3DScene(o.Render3D, o.x3dscale, o.y3dscale, o.Ortho).then(() => {
- fp.setAxesRanges(histo.fXaxis, this.xmin, this.xmax, histo.fYaxis, this.ymin, this.ymax, histo.fZaxis, this.zmin, this.zmax, this);
- fp.set3DOptions(o);
- fp.drawXYZ(fp.toplevel, TAxisPainter, {
- ndim: 3, hist_painter: this, zoom: settings.Zooming,
- draw: o.Axis !== -1, drawany: o.isCartesian()
- });
- return this.draw3DBins();
- }).then(() => {
- fp.render3D();
- this.updateStatWebCanvas();
- fp.addKeysHandler();
- });
+ pr = crete3DFrame(this, TAxisPainter, o.Render3D)
+ .then(() => this.draw3DBins())
+ .then(() => {
+ fp.render3D();
+ this.updateStatWebCanvas();
+ fp.addKeysHandler();
+ });
}
if (this.isMainPainter())
@@ -103298,6 +103470,21 @@ class TH3Painter extends THistPainter {
});
}
+ /** @summary Build three.js object for the histogram */
+ static async build3d(histo, opt) {
+ const painter = new TH3Painter(null, histo);
+ painter.mode3d = true;
+ painter.decodeOptions(opt);
+ painter.scanContent();
+
+ const fp = new TFramePainter(null, null);
+ painter.getFramePainter = () => fp;
+
+ return crete3DFrame(painter, TAxisPainter)
+ .then(() => painter.draw3DBins())
+ .then(() => fp.create3DScene(-1, true));
+ }
+
/** @summary draw TH3 object */
static async draw(dom, histo, opt) {
const painter = new TH3Painter(dom, histo);
@@ -105174,11 +105361,9 @@ class RTreeMapTooltip {
if (isLeaf && node.fType !== undefined)
content += `Type: ${node.fType}
`;
-
if (!isLeaf)
content += `Children: ${node.fNChildren}
`;
-
const obj = this.painter.getObject();
if (obj.fNodes && obj.fNodes.length > 0) {
const totalSize = obj.fNodes[0].fSize,
@@ -105917,7 +106102,8 @@ class Node {
this.divider.splitPolygon(polygons[i], this.polygons, this.polygons, front, back);
}
- if (nodeid !== undefined) this.maxnodeid = nodeid;
+ if (nodeid !== undefined)
+ this.maxnodeid = nodeid;
if (front.length)
this.front = new Node(front);
@@ -106176,7 +106362,8 @@ class Geometry {
}
this.tree = new Node(polygons, nodeid);
- if (nodeid !== undefined) this.maxid = this.tree.maxnodeid;
+ if (nodeid !== undefined)
+ this.maxid = this.tree.maxnodeid;
}
subtract(other_tree) {
@@ -108425,8 +108612,7 @@ createGeometry = function(shape, limit = 0) {
let place = '';
if (e.stack !== undefined) {
place = e.stack.split('\n')[0];
- if (place.indexOf(e.message) >= 0) place = e.stack.split('\n')[1];
- else place = 'at: ' + place;
+ place = place.indexOf(e.message) >= 0 ? e.stack.split('\n')[1] : 'at: ' + place;
}
geoWarn(`${shape._typename} err: ${e.message} ${place}`);
}
@@ -113837,9 +114023,8 @@ class TGeoPainter extends ObjectPainter {
if (this.#superimpose && (opt.indexOf('same') === 0))
opt = opt.slice(4);
- const res = this.ctrl,
+ const res = this.ctrl, macro = opt.indexOf('macro:');
- macro = opt.indexOf('macro:');
if (macro >= 0) {
let separ = opt.indexOf(';', macro+6);
if (separ < 0) separ = opt.length;
@@ -114893,17 +115078,19 @@ class TGeoPainter extends ObjectPainter {
const obj = intersects[n].object;
let unique = obj.visible && (getIntersectStack(intersects[n]) || (obj.geo_name !== undefined));
- if (unique && obj.material && (obj.material.opacity !== undefined))
- unique = (obj.material.opacity >= 0.1);
+ if (unique && (obj.material?.opacity !== undefined))
+ unique = obj.material.opacity >= 0.1;
- if (obj.jsroot_special) unique = false;
+ if (obj.jsroot_special)
+ unique = false;
for (let k = 0; (k < n) && unique; ++k) {
if (intersects[k].object === obj)
unique = false;
}
- if (!unique) intersects.splice(n, 1);
+ if (!unique)
+ intersects.splice(n, 1);
}
const clip = this.ctrl.clip;
@@ -118394,6 +118581,153 @@ class TGeoPainter extends ObjectPainter {
return this.prepareObjectDraw(draw_obj, name_prefix);
}
+ /** @summary Build three.js object */
+ static build3d(obj, sopt) {
+ if (!obj)
+ return null;
+
+ let opt = sopt || {};
+ if (isStr(sopt)) {
+ const painter = new TGeoPainter(null, obj);
+ painter.decodeOptions(sopt);
+ opt = painter.ctrl;
+ opt.numfaces = opt.maxfaces;
+ opt.numnodes = opt.maxnodes;
+ }
+
+ if (!opt.numfaces)
+ opt.numfaces = 100000;
+ if (!opt.numnodes)
+ opt.numnodes = 1000;
+ if (!opt.frustum)
+ opt.frustum = null;
+
+ opt.res_mesh = opt.res_faces = 0;
+
+ if (opt.instancing === undefined)
+ opt.instancing = -1;
+
+ opt.info = { num_meshes: 0, num_faces: 0 };
+
+ let clones, visibles;
+
+ if (obj.visibles && obj.nodes && obj.numnodes) {
+ // case of draw message from geometry viewer
+
+ const nodes = obj.numnodes > 1e6 ? { length: obj.numnodes } : new Array(obj.numnodes);
+
+ obj.nodes.forEach(node => {
+ nodes[node.id] = ClonedNodes.formatServerElement(node);
+ });
+
+ clones = new ClonedNodes(null, nodes);
+ clones.name_prefix = clones.getNodeName(0);
+
+ // normally only need when making selection, not used in geo viewer
+ // this.geo_clones.setMaxVisNodes(draw_msg.maxvisnodes);
+ // this.geo_clones.setVisLevel(draw_msg.vislevel);
+ // TODO: provide from server
+ clones.maxdepth = 20;
+
+ const nsegm = obj.cfg?.nsegm || 30;
+
+ for (let cnt = 0; cnt < obj.visibles.length; ++cnt) {
+ const item = obj.visibles[cnt], rd = item.ri;
+
+ // entry may be provided without shape - it is ok
+ if (rd)
+ item.server_shape = rd.server_shape = createServerGeometry(rd, nsegm);
+ }
+
+ visibles = obj.visibles;
+ } else {
+ let shape = null, hide_top = false;
+
+ if (('fShapeBits' in obj) && ('fShapeId' in obj)) {
+ shape = obj; obj = null;
+ } else if ((obj._typename === clTGeoVolumeAssembly) || (obj._typename === clTGeoVolume))
+ shape = obj.fShape;
+ else if ((obj._typename === clTEveGeoShapeExtract) || (obj._typename === clREveGeoShapeExtract))
+ shape = obj.fShape;
+ else if (obj._typename === clTGeoManager) {
+ obj = obj.fMasterVolume;
+ hide_top = !opt.showtop;
+ shape = obj.fShape;
+ } else if (obj.fVolume)
+ shape = obj.fVolume.fShape;
+ else
+ obj = null;
+
+ if (opt.composite && shape && (shape._typename === clTGeoCompositeShape) && shape.fNode)
+ obj = buildCompositeVolume(shape);
+
+ if (!obj && shape)
+ obj = Object.assign(create$1(clTNamed), { _typename: clTEveGeoShapeExtract, fTrans: null, fShape: shape, fRGBA: [0, 1, 0, 1], fElements: null, fRnrSelf: true });
+
+ if (!obj)
+ return null;
+
+ if (obj._typename.indexOf(clTGeoVolume) === 0)
+ obj = { _typename: clTGeoNode, fVolume: obj, fName: obj.fName, $geoh: obj.$geoh, _proxy: true };
+
+ clones = new ClonedNodes(obj);
+ clones.setVisLevel(opt.vislevel);
+ clones.setMaxVisNodes(opt.numnodes);
+
+ if (opt.dflt_colors)
+ clones.setDefaultColors(true);
+
+ const uniquevis = opt.no_screen ? 0 : clones.markVisibles(true);
+ if (uniquevis <= 0)
+ clones.markVisibles(false, false, hide_top);
+ else
+ clones.markVisibles(true, true, hide_top); // copy bits once and use normal visibility bits
+
+ clones.produceIdShifts();
+
+ // collect visible nodes
+ const res = clones.collectVisibles(opt.numfaces, opt.frustum);
+
+ visibles = res.lst;
+ }
+
+ if (!opt.material_kind)
+ opt.material_kind = 'lambert';
+ if (opt.set_names === undefined)
+ opt.set_names = true;
+
+ clones.setConfig(opt);
+
+ // collect shapes
+ const shapes = clones.collectShapes(visibles);
+
+ clones.buildShapes(shapes, opt.numfaces);
+
+ const toplevel = new THREE.Object3D();
+ toplevel.clones = clones; // keep reference on JSROOT data
+
+ const colors = getRootColors();
+
+ if (clones.createInstancedMeshes(opt, toplevel, visibles, shapes, colors))
+ return toplevel;
+
+ for (let n = 0; n < visibles.length; ++n) {
+ const entry = visibles[n];
+ if (entry.done)
+ continue;
+
+ const shape = entry.server_shape || shapes[entry.shapeid];
+ if (!shape.ready) {
+ console.warn('shape marked as not ready when it should');
+ break;
+ }
+
+ clones.createEntryMesh(opt, toplevel, entry, shape, colors);
+ }
+
+ return toplevel;
+ }
+
/** @summary draw TGeo object */
static async draw(dom, obj, opt) {
if (!obj)
@@ -118943,143 +119277,7 @@ function drawAxis3D() {
* // this is three.js object and can be now inserted in the scene
*/
function build(obj, opt) {
- if (!obj)
- return null;
-
- if (!opt)
- opt = {};
- if (!opt.numfaces)
- opt.numfaces = 100000;
- if (!opt.numnodes)
- opt.numnodes = 1000;
- if (!opt.frustum)
- opt.frustum = null;
-
- opt.res_mesh = opt.res_faces = 0;
-
- if (opt.instancing === undefined)
- opt.instancing = -1;
-
- opt.info = { num_meshes: 0, num_faces: 0 };
-
- let clones, visibles;
-
- if (obj.visibles && obj.nodes && obj.numnodes) {
- // case of draw message from geometry viewer
-
- const nodes = obj.numnodes > 1e6 ? { length: obj.numnodes } : new Array(obj.numnodes);
-
- obj.nodes.forEach(node => {
- nodes[node.id] = ClonedNodes.formatServerElement(node);
- });
-
- clones = new ClonedNodes(null, nodes);
- clones.name_prefix = clones.getNodeName(0);
-
- // normally only need when making selection, not used in geo viewer
- // this.geo_clones.setMaxVisNodes(draw_msg.maxvisnodes);
- // this.geo_clones.setVisLevel(draw_msg.vislevel);
- // TODO: provide from server
- clones.maxdepth = 20;
-
- const nsegm = obj.cfg?.nsegm || 30;
-
- for (let cnt = 0; cnt < obj.visibles.length; ++cnt) {
- const item = obj.visibles[cnt], rd = item.ri;
-
- // entry may be provided without shape - it is ok
- if (rd)
- item.server_shape = rd.server_shape = createServerGeometry(rd, nsegm);
- }
-
- visibles = obj.visibles;
- } else {
- let shape = null, hide_top = false;
-
- if (('fShapeBits' in obj) && ('fShapeId' in obj)) {
- shape = obj; obj = null;
- } else if ((obj._typename === clTGeoVolumeAssembly) || (obj._typename === clTGeoVolume))
- shape = obj.fShape;
- else if ((obj._typename === clTEveGeoShapeExtract) || (obj._typename === clREveGeoShapeExtract))
- shape = obj.fShape;
- else if (obj._typename === clTGeoManager) {
- obj = obj.fMasterVolume;
- hide_top = !opt.showtop;
- shape = obj.fShape;
- } else if (obj.fVolume)
- shape = obj.fVolume.fShape;
- else
- obj = null;
-
-
- if (opt.composite && shape && (shape._typename === clTGeoCompositeShape) && shape.fNode)
- obj = buildCompositeVolume(shape);
-
- if (!obj && shape)
- obj = Object.assign(create$1(clTNamed), { _typename: clTEveGeoShapeExtract, fTrans: null, fShape: shape, fRGBA: [0, 1, 0, 1], fElements: null, fRnrSelf: true });
-
- if (!obj)
- return null;
-
- if (obj._typename.indexOf(clTGeoVolume) === 0)
- obj = { _typename: clTGeoNode, fVolume: obj, fName: obj.fName, $geoh: obj.$geoh, _proxy: true };
-
- clones = new ClonedNodes(obj);
- clones.setVisLevel(opt.vislevel);
- clones.setMaxVisNodes(opt.numnodes);
-
- if (opt.dflt_colors)
- clones.setDefaultColors(true);
-
- const uniquevis = opt.no_screen ? 0 : clones.markVisibles(true);
- if (uniquevis <= 0)
- clones.markVisibles(false, false, hide_top);
- else
- clones.markVisibles(true, true, hide_top); // copy bits once and use normal visibility bits
-
- clones.produceIdShifts();
-
- // collect visible nodes
- const res = clones.collectVisibles(opt.numfaces, opt.frustum);
-
- visibles = res.lst;
- }
-
- if (!opt.material_kind)
- opt.material_kind = 'lambert';
- if (opt.set_names === undefined)
- opt.set_names = true;
-
- clones.setConfig(opt);
-
- // collect shapes
- const shapes = clones.collectShapes(visibles);
-
- clones.buildShapes(shapes, opt.numfaces);
-
- const toplevel = new THREE.Object3D();
- toplevel.clones = clones; // keep reference on JSROOT data
-
- const colors = getRootColors();
-
- if (clones.createInstancedMeshes(opt, toplevel, visibles, shapes, colors))
- return toplevel;
-
- for (let n = 0; n < visibles.length; ++n) {
- const entry = visibles[n];
- if (entry.done)
- continue;
-
- const shape = entry.server_shape || shapes[entry.shapeid];
- if (!shape.ready) {
- console.warn('shape marked as not ready when it should');
- break;
- }
-
- clones.createEntryMesh(opt, toplevel, entry, shape, colors);
- }
-
- return toplevel;
+ return TGeoPainter.build3d(obj, opt);
}
var TGeoPainter$1 = /*#__PURE__*/Object.freeze({
@@ -124003,7 +124201,7 @@ class TDrawSelector extends TSelector {
break;
case 'mon':
case 'monitor':
- args.monitoring = (intvalue !== undefined) ? intvalue : 5000;
+ args.monitoring = intvalue ?? 5000;
break;
case 'player':
args.player = true;
@@ -164432,7 +164630,7 @@ drawFuncs = { lst: [
{ name: clTDiamond, sameas: clTPave },
{ name: clTLegend, icon: 'img_pavelabel', sameas: clTPave },
{ name: clTPaletteAxis, icon: 'img_colz', sameas: clTPave },
- { name: clTLatex, icon: 'img_text', draw: () => import_more().then(h => h.drawText), direct: true },
+ { name: clTLatex, icon: 'img_text', draw: () => import_more().then(h => h.drawText), build3d: () => Promise.resolve().then(function () { return hist3d; }).then(h => h.build3dlatex), direct: true },
{ name: clTMathText, sameas: clTLatex },
{ name: clTText, sameas: clTLatex },
{ name: clTLink, sameas: clTText },
@@ -164930,6 +165128,30 @@ async function redraw(dom, obj, opt) {
return draw(dom, obj, opt);
}
+/** @summary Create three.js model for object
+ * @param {object} obj - object
+ * @param {string} opt - draw options
+ * @return {Promise} with three.js model */
+
+async function build3d(obj, opt) {
+ if (!isObject(obj) || !obj?._typename)
+ return Promise.reject(Error('not an object in build3d'));
+
+ const handle = getDrawHandle(getKindForType(obj._typename));
+ if (!handle?.class && !handle.build3d)
+ return Promise.reject(Error(`not able to create three.js for ${obj._typename}`));
+
+ if (handle.build3d)
+ return handle.build3d().then(func => func(obj, opt));
+
+ return handle.class().then(cl => {
+ if (!isFunc(cl?.build3d))
+ return Promise.reject(Error(`painter class for ${obj._typename} does not implement build3d method`));
+
+ return cl.build3d(obj, opt);
+ });
+}
+
/** @summary Scan streamer infos for derived classes
* @desc Assign draw functions for such derived classes
* @private */
@@ -168734,7 +168956,10 @@ class HierarchyPainter extends BasePainter {
opt = (separ > 0) ? opt.slice(separ+1) : '';
let canarray = true;
- if (part[0] === '#') { part = part.slice(1); canarray = false; }
+ if (part[0] === '#') {
+ part = part.slice(1);
+ canarray = false;
+ }
const val = d.get(part, null);
@@ -172583,7 +172808,8 @@ class TGraph2DPainter extends ObjectPainter {
res.Axis = '';
else if (res.isAny()) {
res.Axis = 'lego2';
- if (res.Zscale) res.Axis += 'z';
+ if (res.Zscale)
+ res.Axis += 'z';
} else
res.Axis = opt;
@@ -172876,7 +173102,10 @@ class TGraph2DPainter extends ObjectPainter {
if (fp.usesvg)
scale *= 0.3;
- scale *= 7 * Math.max(fp.size_x3d / fp.getFrameWidth(), fp.size_z3d / fp.getFrameHeight());
+ const fw = fp.getFrameWidth(), fh = fp.getFrameHeight();
+
+ if ((fw > 10) && (fh > 10))
+ scale *= 7 * Math.max(fp.size_x3d / fw, fp.size_z3d / fh);
if (o.Color || (o.Triangles >= 10)) {
levels = main.getContourLevels(true);
@@ -173038,6 +173267,27 @@ class TGraph2DPainter extends ObjectPainter {
});
}
+ /** @summary Build three.js of TGraph2D object */
+ static async build3d(gr, opt) {
+ const painter = new TGraph2DPainter(null, gr);
+ painter.decodeOptions(opt, gr);
+
+ if (painter.options.Contour) {
+ console.error('Contour plot is not 3d');
+ return null;
+ }
+
+ return TH2Painter.build3d(painter.createHistogram(), painter.options.Axis, true).then(hist_painter => {
+ painter.axes_draw = true;
+ const fp = hist_painter.getFramePainter();
+
+ painter.getFramePainter = () => fp;
+ painter.getMainPainter = () => hist_painter;
+
+ return painter.drawGraph2D().then(() => fp.create3DScene(-1, true));
+ });
+ }
+
/** @summary draw TGraph2D object */
static async draw(dom, gr, opt) {
const painter = new TGraph2DPainter(dom, gr);
@@ -176420,7 +176670,8 @@ class TSplinePainter extends ObjectPainter {
}
const name = this.getObjectHint();
- if (name) res.lines.push(name);
+ if (name)
+ res.lines.push(name);
res.lines.push(`x = ${funcs.axisAsText('x', xx)}`,
`y = ${funcs.axisAsText('y', yy)}`);
if (knot !== null) {
@@ -179274,7 +179525,8 @@ class RAxisPainter extends RObjectPainter {
let toffset = this.v7EvalAttr('timeOffset');
if (toffset !== undefined) {
toffset = parseFloat(toffset);
- if (Number.isFinite(toffset)) this.timeoffset = toffset*1000;
+ if (Number.isFinite(toffset))
+ this.timeoffset = toffset*1000;
}
} else if (this.axis?.fLabelsIndex) {
this.kind = kAxisLabels;
@@ -179803,9 +180055,11 @@ class RAxisPainter extends RObjectPainter {
if (this.handle.kind === 1) {
// if not showing labels, not show large tick
- if ((this.kind === kAxisLabels) || (this.format(this.handle.tick, true) !== null)) h1 = this.ticksSize;
+ if ((this.kind === kAxisLabels) || (this.format(this.handle.tick, true) !== null))
+ h1 = this.ticksSize;
- if (main_draw) this.ticks.push(grpos); // keep graphical positions of major ticks
+ if (main_draw)
+ this.ticks.push(grpos); // keep graphical positions of major ticks
}
if (ticks_plusminus > 0)
@@ -183621,7 +183875,10 @@ class RCanvasPainter extends RPadPainter {
return handle !== undefined;
if (tm === 'reset') {
- if (handle) { clearTimeout(handle); delete this.#websocket._tmouts[name]; }
+ if (handle) {
+ clearTimeout(handle);
+ delete this.#websocket._tmouts[name];
+ }
} else if (!handle && Number.isInteger(tm))
this.#websocket._tmouts[name] = setTimeout(() => { delete this.#websocket._tmouts[name]; }, tm);
}
@@ -185020,6 +185277,7 @@ exports.assignContextMenu = assignContextMenu;
exports.atob_func = atob_func;
exports.browser = browser;
exports.btoa_func = btoa_func;
+exports.build3d = build3d;
exports.buildGUI = buildGUI;
exports.buildSvgCurve = buildSvgCurve;
exports.clTAnnotation = clTAnnotation;
diff --git a/changes.md b/changes.md
index 88f1f8e1f..4ddc81792 100644
--- a/changes.md
+++ b/changes.md
@@ -3,6 +3,7 @@
## Changes in dev
1. RNtuple support, thanks to Kriti Mahajan (https://github.com/Krmjn09)
1. Implement RTreeMapPainter to display RNTuple structure, thanks to Patryk Pilichowski (https://github.com/magnustymoteus)
+1. Implement `build3d` function for supported classes #368
1. Let use hex colors in histogram draw options like "fill_00ff00" or "line_77aa1166"
1. Let configure exact axis ticks position via draw option like "xticks:[-3,-1,1,3]"
1. Support gStyle.fBarOffset for `TGraph` bar drawing
diff --git a/demo/hist_build3d.htm b/demo/hist_build3d.htm
new file mode 100644
index 000000000..b3fd3359e
--- /dev/null
+++ b/demo/hist_build3d.htm
@@ -0,0 +1,144 @@
+
+
+