5 if ( typeof define ===
"function" && define.amd ) {
7 define( [
'd3',
'JSRootPainter',
'threejs_all'], factory );
10 if (typeof JSROOT ==
'undefined')
11 throw new Error(
'JSROOT is not defined',
'JSRoot3DPainter.js');
13 if (typeof d3 !=
'object')
14 throw new Error(
'This extension requires d3.v3.js',
'JSRoot3DPainter.js');
16 if (typeof JSROOT.Painter !=
'object')
17 throw new Error(
'JSROOT.Painter is not defined',
'JSRoot3DPainter.js');
19 if (typeof THREE ==
'undefined')
20 throw new Error(
'THREE is not defined',
'JSRoot3DPainter.js');
24 } (
function(d3, JSROOT) {
26 JSROOT.Painter.TestWebGL =
function() {
33 if (JSROOT.gStyle.NoWebGL)
return false;
35 if (
'_Detect_WebGL' in
this)
return this._Detect_WebGL;
38 var canvas = document.createElement(
'canvas' );
39 this._Detect_WebGL = !! ( window.WebGLRenderingContext && ( canvas.getContext(
'webgl' ) || canvas.getContext(
'experimental-webgl' ) ) );
45 return this._Detect_WebGL;
48 JSROOT.Painter.add3DInteraction =
function() {
52 var mouseX, mouseY, distXY = 0, mouseDowned =
false;
53 var INTERSECTED = null;
58 if (this.tt === null)
return;
59 var u = JSROOT.browser.isIE ? (
event.clientY + document.documentElement.scrollTop) : e.pageY;
60 var l = JSROOT.browser.isIE ? (event.clientX + document.documentElement.scrollLeft) : e.pageX;
62 this.tt.style.top = (u + 15) +
'px';
63 this.tt.style.left = (l + 3) +
'px';
66 if (JSROOT.gStyle.Tooltip <= 0)
return;
67 if (this.tt === null) {
68 this.tt = document.createElement(
'div');
69 this.tt.setAttribute(
'class',
'jsroot');
70 var t = document.createElement(
'div');
71 t.setAttribute(
'class',
'tt3d_border');
72 this.cont = document.createElement(
'div');
73 this.cont.setAttribute(
'class',
'tt3d_cont');
74 var b = document.createElement(
'div');
75 b.setAttribute(
'class',
'tt3d_border');
76 this.tt.appendChild(t);
77 this.tt.appendChild(this.cont);
78 this.tt.appendChild(b);
79 document.body.appendChild(this.tt);
80 this.tt.style.opacity = 1;
81 this.tt.style.filter =
'alpha(opacity=1)';
82 this.tt.style.position =
'absolute';
83 this.tt.style.display =
'block';
85 this.cont.innerHTML = v;
86 this.tt.style.width =
'auto';
87 if (JSROOT.browser.isIE)
88 this.tt.style.width = this.tt.offsetWidth;
92 document.body.removeChild(this.tt);
97 var raycaster =
new THREE.Raycaster();
98 var do_bins_highlight = painter.first_render_tm < 2000;
100 function findIntersection(mouse) {
103 if (JSROOT.gStyle.Tooltip <= 0)
return tooltip.hide();
105 raycaster.setFromCamera( mouse, painter.camera );
106 var intersects = raycaster.intersectObjects(painter.scene.children,
true);
107 if (intersects.length > 0) {
109 for (var i = 0; i < intersects.length; ++i) {
110 if ((
'name' in intersects[i].
object) && (intersects[i].
object.name.length > 0)) {
111 pick = intersects[i].object;
115 if ((pick!==null) && (INTERSECTED !== pick)) {
116 if (INTERSECTED && do_bins_highlight && (
'emissive' in INTERSECTED.material))
117 INTERSECTED.material.emissive.setHex(INTERSECTED.currentHex);
119 if (do_bins_highlight && (
'emissive' in INTERSECTED.material)) {
120 INTERSECTED.currentHex = INTERSECTED.material.emissive.getHex();
121 INTERSECTED.material.emissive.setHex(0x5f5f5f);
125 tooltip.show(INTERSECTED.name, 200);
128 if (INTERSECTED && do_bins_highlight && (
'emissive' in INTERSECTED.material)) {
129 INTERSECTED.material.emissive.setHex(INTERSECTED.currentHex);
137 function coordinates(e) {
138 if (
'changedTouches' in e)
return e.changedTouches;
139 if (
'touches' in e)
return e.touches;
143 function mousedown(e) {
147 var arr = coordinates(e);
148 if (arr.length == 2) {
149 distXY = Math.sqrt(Math.pow(arr[0].pageX - arr[1].pageX, 2) + Math.pow(arr[0].pageY - arr[1].pageY, 2));
151 mouseX = arr[0].pageX;
152 mouseY = arr[0].pageY;
158 painter.renderer.domElement.addEventListener(
'touchstart', mousedown);
159 painter.renderer.domElement.addEventListener(
'mousedown', mousedown);
161 function mousemove(e) {
162 var arr = coordinates(e);
165 if (arr.length == 2) {
166 var dist = Math.sqrt(Math.pow(arr[0].pageX - arr[1].pageX, 2) + Math.pow(arr[0].pageY - arr[1].pageY, 2));
168 var delta = (dist-distXY)/(dist+distXY);
170 if (delta === 1.)
return;
172 painter.camera.position.x += delta * painter.size3d * 10;
173 painter.camera.position.y += delta * painter.size3d * 10;
174 painter.camera.position.z -= delta * painter.size3d * 10;
176 var moveX = arr[0].pageX - mouseX;
177 var moveY = arr[0].pageY - mouseY;
178 var length = painter.camera.position.length();
179 var ddd = length > painter.size3d ? 0.001*length/painter.size3d : 0.01;
184 painter.toplevel.rotation.z += moveX * ddd;
185 painter.toplevel.rotation.x += moveY * ddd;
186 painter.toplevel.rotation.y -= moveY * ddd;
187 mouseX = arr[0].pageX;
188 mouseY = arr[0].pageY;
192 if (arr.length == 1) {
193 var mouse_x = (
'offsetX' in arr[0]) ? arr[0].offsetX : arr[0].layerX;
194 var mouse_y = (
'offsetY' in arr[0]) ? arr[0].offsetY : arr[0].layerY;
195 mouse = { x: (mouse_x / painter.renderer.domElement.width) * 2 - 1,
196 y: -(mouse_y / painter.renderer.domElement.height) * 2 + 1 };
197 findIntersection(mouse);
207 painter.renderer.domElement.addEventListener(
'touchmove', mousemove);
208 painter.renderer.domElement.addEventListener(
'mousemove', mousemove);
210 function mouseup(e) {
216 painter.renderer.domElement.addEventListener(
'touchend', mouseup);
217 painter.renderer.domElement.addEventListener(
'touchcancel', mouseup);
218 painter.renderer.domElement.addEventListener(
'mouseup', mouseup);
220 function mousewheel(event) {
221 event.preventDefault();
222 event.stopPropagation();
225 if ( event.wheelDelta ) {
227 delta =
event.wheelDelta / 400;
228 }
else if ( event.detail ) {
230 delta = -
event.detail / 30;
232 painter.camera.position.x -= delta * painter.size3d;
233 painter.camera.position.y -= delta * painter.size3d;
234 painter.camera.position.z += delta * painter.size3d;
238 painter.renderer.domElement.addEventListener(
'mousewheel', mousewheel,
false );
239 painter.renderer.domElement.addEventListener(
'MozMousePixelScroll', mousewheel,
false );
242 painter.renderer.domElement.addEventListener(
'mouseleave',
function() {
247 painter.renderer.domElement.addEventListener(
'contextmenu',
function(e) {
251 painter.ShowContextMenu(
"hist", e);
256 JSROOT.Painter.HPainter_Create3DScene =
function(arg) {
258 if ((arg!==null) && (arg<0)) {
259 this.clear_3d_canvas();
262 delete this.toplevel;
264 delete this.renderer;
265 if (
'render_tmout' in
this) {
266 clearTimeout(this.render_tmout);
267 delete this.render_tmout;
272 if (
'toplevel' in
this) {
275 var newtop =
new THREE.Object3D();
277 newtop.rotation.x = this.toplevel.rotation.x;
278 newtop.rotation.y = this.toplevel.rotation.y;
280 this.scene.remove(this.toplevel);
282 this.scene.add(newtop);
284 this.toplevel = newtop;
289 var size = this.size_for_3d();
294 this.scene =
new THREE.Scene();
297 this.toplevel =
new THREE.Object3D();
300 this.scene.add(this.toplevel);
301 this.scene_width = size.width;
302 this.scene_height = size.height
304 this.camera =
new THREE.PerspectiveCamera(45, this.scene_width / this.scene_height, 1, 40*this.size3d);
305 var pointLight =
new THREE.PointLight(0xcfcfcf);
306 this.camera.add( pointLight );
307 pointLight.position.set( this.size3d / 10, this.size3d / 10, this.size3d / 10 );
308 this.camera.position.set(-3*this.size3d, -3*this.size3d, 3*this.size3d);
309 this.camera.up =
new THREE.Vector3(0,0,1);
310 this.camera.lookAt(
new THREE.Vector3(0,0,
this.size3d));
311 this.scene.add( this.camera );
313 var webgl = JSROOT.Painter.TestWebGL();
315 this.renderer = webgl ?
new THREE.WebGLRenderer({ antialias :
true, alpha:
true }) :
316 new THREE.CanvasRenderer({ antialias :
true, alpha:
true });
319 this.renderer.setSize(this.scene_width, this.scene_height);
321 this.add_3d_canvas(size, this.renderer.domElement);
323 this[
'DrawXYZ'] = JSROOT.Painter.HPainter_DrawXYZ;
324 this[
'Render3D'] = JSROOT.Painter.Render3D;
325 this[
'Resize3D'] = JSROOT.Painter.Resize3D;
327 this.first_render_tm = 0;
330 JSROOT.Painter.HPainter_DrawXYZ =
function() {
332 var grminx = -this.size3d, grmaxx = this.size3d,
333 grminy = -this.size3d, grmaxy = this.size3d,
334 grminz = 0, grmaxz = 2*this.size3d,
335 textsize = Math.round(this.size3d * 0.07),
336 bothsides = (this.size3d !== 0),
337 xmin = this.xmin, xmax = this.xmax,
338 ymin = this.ymin, ymax = this.ymax,
339 zmin = this.zmin, zmax = this.zmax,
342 if (this.size3d === 0) {
343 grminx = this.xmin; grmaxx = this.xmax;
344 grminy = this.ymin; grmaxy = this.ymax;
345 grminz = this.zmin; grmaxz = this.zmax;
346 textsize = (grmaxz - grminz) * 0.05;
349 if ((
'zoom_xmin' in
this) && (
'zoom_xmax' in
this) && (this.zoom_xmin !== this.zoom_xmax)) {
350 xmin = this.zoom_xmin; xmax = this.zoom_xmax;
353 if ((
'zoom_ymin' in
this) && (
'zoom_ymax' in
this) && (this.zoom_ymin !== this.zoom_ymax)) {
354 ymin = this.zoom_ymin; ymax = this.zoom_ymax;
357 if ((
'zoom_zmin' in
this) && (
'zoom_zmax' in
this) && (this.zoom_zmin !== this.zoom_zmax)) {
358 zmin = this.zoom_zmin; zmax = this.zoom_zmax;
361 if (this.options.Logx) {
362 if (xmax <= 0) xmax = 1.;
363 if ((xmin <= 0) && (this.nbinsx > 0))
364 for (var i=0;i<this.nbinsx;++i) {
365 xmin = Math.max(xmin, this.GetBinX(i));
368 if (xmin <= 0) xmin = 1e-4*xmax;
369 this.tx = d3.scale.log();
372 this.tx = d3.scale.linear();
375 this.tx.domain([ xmin, xmax ]).range([ grminx, grmaxx ]);
376 this.x_handle =
new JSROOT.TAxisPainter(histo ? histo.fXaxis : null);
377 this.x_handle.SetAxisConfig(
"xaxis", this.x_kind, this.tx, this.xmin, this.xmax, xmin, xmax);
378 this.x_handle.CreateFormatFuncs();
380 if (this.options.Logy) {
381 if (ymax <= 0) ymax = 1.;
382 if ((ymin <= 0) && (this.nbinsy>0))
383 for (var i=0;i<this.nbinsy;++i) {
384 ymin = Math.max(ymin, this.GetBinY(i));
388 if (ymin <= 0) ymin = 1e-4*ymax;
389 this.ty = d3.scale.log();
392 this.ty = d3.scale.linear();
395 this.ty.domain([ ymin, ymax ]).range([ grminy, grmaxy ]);
396 this.y_handle =
new JSROOT.TAxisPainter(histo ? histo.fYaxis : null);
397 this.y_handle.SetAxisConfig(
"yaxis", this.y_kind, this.ty, this.ymin, this.ymax, ymin, ymax);
398 this.y_handle.CreateFormatFuncs();
400 if (this.options.Logz) {
401 if (zmax <= 0) zmax = 1;
402 if (zmin <= 0) zmin = 1e-4*zmax;
403 this.tz = d3.scale.log();
406 this.tz = d3.scale.linear();
409 this.tz.domain([ zmin, zmax ]).range([ grminz, grmaxz ]);
411 this.z_handle =
new JSROOT.TAxisPainter(histo ? histo.fZaxis : null);
412 this.z_handle.SetAxisConfig(
"zaxis", this.z_kind, this.tz, this.zmin, this.zmax, zmin, zmax);
413 this.z_handle.CreateFormatFuncs();
415 var textMaterial =
new THREE.MeshBasicMaterial({ color: 0x000000 });
416 var lineMaterial =
new THREE.LineBasicMaterial({ color: 0x000000 });
418 var ticklen = textsize * 0.5, text, tick;
420 var xticks = this.x_handle.CreateTicks();
423 var geometry =
new THREE.Geometry();
424 geometry.vertices.push(
new THREE.Vector3(0, 0, 0));
425 geometry.vertices.push(
new THREE.Vector3(0, -1, -1));
427 while (xticks.next()) {
428 var grx = xticks.grpos;
429 var is_major = (xticks.kind===1);
430 var lbl = this.x_handle.format(xticks.tick,
true,
true);
431 if (xticks.last_major()) lbl =
"x";
else
432 if (lbl === null) { is_major =
false; lbl =
""; }
433 var plen = (is_major ? ticklen : ticklen * 0.6) * Math.sin(Math.PI/4);
436 var text3d =
new THREE.TextGeometry(lbl, { size : textsize, height : 0, curveSegments : 10 });
437 text3d.computeBoundingBox();
438 var centerOffset = 0.5 * (text3d.boundingBox.max.x - text3d.boundingBox.min.x);
441 text =
new THREE.Mesh(text3d, textMaterial);
442 text.position.set(grx + centerOffset, grmaxy + plen + textsize, grminz - plen - textsize);
443 text.rotation.x = Math.PI*3/4;
444 text.rotation.y = Math.PI;
445 text.name =
"X axis";
446 this.toplevel.add(text);
449 text =
new THREE.Mesh(text3d, textMaterial);
450 text.position.set(grx - centerOffset, grminy - plen - textsize, grminz - plen - textsize);
451 text.rotation.x = Math.PI/4;
452 text.name =
"X axis";
453 this.toplevel.add(text);
457 tick =
new THREE.Line(geometry, lineMaterial);
458 tick.position.set(grx,grmaxy, grminz);
459 tick.scale.set(1,plen,plen);
460 tick.rotation.z = Math.PI;
461 tick.name =
"X axis: " + this.x_handle.format(xticks.tick);
462 this.toplevel.add(tick);
465 tick =
new THREE.Line(geometry, lineMaterial);
466 tick.position.set(grx,grminy,grminz);
467 tick.scale.set(1,plen,plen);
468 tick.name =
"X axis: " + this.x_handle.format(xticks.tick);
469 this.toplevel.add(tick);
472 var yticks = this.y_handle.CreateTicks();
473 geometry =
new THREE.Geometry();
474 geometry.vertices.push(
new THREE.Vector3(0, 0, 0));
475 geometry.vertices.push(
new THREE.Vector3(-1, 0, -1));
477 while (yticks.next()) {
478 var gry = yticks.grpos;
479 var is_major = (yticks.kind===1);
480 var lbl = this.y_handle.format(yticks.tick,
true,
true);
481 if (yticks.last_major()) lbl =
"y";
else
482 if (lbl === null) { is_major =
false; lbl =
""; }
483 var plen = (is_major ? ticklen : ticklen*0.6) * Math.sin(Math.PI/4);
486 var text3d =
new THREE.TextGeometry(lbl, { size : textsize, height : 0, curveSegments : 10 });
488 text3d.computeBoundingBox();
489 var centerOffset = 0.5 * (text3d.boundingBox.max.x - text3d.boundingBox.min.x);
492 text =
new THREE.Mesh(text3d, textMaterial);
493 text.position.set(grmaxx + plen + textsize, gry + centerOffset, grminz - plen - textsize);
494 text.rotation.y = Math.PI / 4;
495 text.rotation.z = Math.PI / 2;
496 text.name =
"Y axis";
497 this.toplevel.add(text);
500 text =
new THREE.Mesh(text3d, textMaterial);
501 text.position.set(grminx - plen - textsize, gry + centerOffset, grminz - plen - textsize);
502 text.rotation.y = -Math.PI / 4;
503 text.rotation.z = -Math.PI / 2;
504 text.name =
"Y axis";
505 this.toplevel.add(text);
508 tick =
new THREE.Line(geometry, lineMaterial);
509 tick.position.set(grmaxx,gry,grminz);
510 tick.scale.set(plen,1,plen);
511 tick.rotation.z = Math.PI;
512 tick.name =
"Y axis " + this.y_handle.format(yticks.tick);
513 this.toplevel.add(tick);
515 tick =
new THREE.Line(geometry, lineMaterial);
516 tick.position.set(grminx,gry,grminz);
517 tick.scale.set(plen,1, plen);
518 tick.name =
"Y axis " + this.y_handle.format(yticks.tick);
519 this.toplevel.add(tick);
522 var zticks = this.z_handle.CreateTicks();
523 geometry =
new THREE.Geometry();
524 geometry.vertices.push(
new THREE.Vector3(0, 0, 0));
525 geometry.vertices.push(
new THREE.Vector3(-1, 1, 0));
526 while (zticks.next()) {
527 var grz = zticks.grpos;
528 var is_major = zticks.kind == 1;
530 var lbl = this.z_handle.format(zticks.tick,
true,
true);
531 if (lbl === null) { is_major =
false; lbl =
""; }
532 var plen = (is_major ? ticklen : ticklen * 0.6) * Math.sin(Math.PI/4);
535 var text3d =
new THREE.TextGeometry(lbl, { size : textsize, height : 0, curveSegments : 10 });
537 text3d.computeBoundingBox();
538 var offset = 0.8 * (text3d.boundingBox.max.x - text3d.boundingBox.min.x) + 0.7 * textsize;
540 var textz = grz - 0.4*textsize;
543 text =
new THREE.Mesh(text3d, textMaterial);
544 text.position.set(grmaxx + offset, grmaxy + offset, textz);
545 text.rotation.x = 0.5*Math.PI;
546 text.rotation.y = -0.75 * Math.PI;
547 text.name =
"Z axis";
548 this.toplevel.add(text);
550 text =
new THREE.Mesh(text3d, textMaterial);
551 text.position.set(grmaxx + offset, grminy - offset, textz);
552 text.rotation.x = 0.5*Math.PI;
553 text.rotation.y = 0.75*Math.PI;
554 text.name =
"Z axis";
555 this.toplevel.add(text);
557 text =
new THREE.Mesh(text3d, textMaterial);
558 text.position.set(grminx - offset, grminy - offset, textz);
559 text.rotation.x = 0.5*Math.PI;
560 text.rotation.y = 0.25*Math.PI;
561 text.name =
"Z axis";
562 this.toplevel.add(text);
565 text =
new THREE.Mesh(text3d, textMaterial);
566 text.position.set(grminx - offset, grmaxy + offset, textz);
567 text.rotation.x = 0.5*Math.PI;
568 text.rotation.y = -0.25*Math.PI;
569 text.name =
"Z axis";
570 this.toplevel.add(text);
573 tick =
new THREE.Line(geometry, lineMaterial);
574 tick.position.set(grmaxx,grmaxy,grz);
575 tick.scale.set(plen,plen,1);
576 tick.rotation.z = -Math.PI/2;
577 tick.name =
"Z axis " + this.z_handle.format(zticks.tick);
578 this.toplevel.add(tick);
580 tick =
new THREE.Line(geometry, lineMaterial);
581 tick.position.set(grmaxx,grminy,grz);
582 tick.scale.set(plen,plen,1);
583 tick.rotation.z = Math.PI;
584 tick.name =
"Z axis " + this.z_handle.format(zticks.tick);
585 this.toplevel.add(tick);
587 tick =
new THREE.Line(geometry, lineMaterial);
588 tick.position.set(grminx,grminy,grz);
589 tick.scale.set(plen,plen,1);
590 tick.rotation.z = Math.PI/2;
591 tick.name =
"Z axis " + this.z_handle.format(zticks.tick);
592 this.toplevel.add(tick);
595 tick =
new THREE.Line(geometry, lineMaterial);
596 tick.position.set(grminx,grmaxy,grz);
597 tick.scale.set(plen,plen,1);
598 tick.name =
"Z axis " + this.z_handle.format(zticks.tick);
599 this.toplevel.add(tick);
603 if (this.size3d === 0)
return;
605 var wireMaterial =
new THREE.MeshBasicMaterial({
608 wireframeLinewidth : 0.5,
609 side : THREE.DoubleSide
614 var cube =
new THREE.Mesh(
new THREE.BoxGeometry(
this.size3d * 2,
this.size3d * 2,
this.size3d * 2), wireMaterial);
617 var helper =
new THREE.BoxHelper(cube);
618 helper.material.color.set(0x000000);
620 var box =
new THREE.Object3D();
622 box.position.z = this.size3d;
625 this.toplevel.add(box);
628 JSROOT.Painter.TH2Painter_Draw3DBins =
function() {
630 var fcolor = d3.rgb(JSROOT.Painter.root_colors[
this.GetObject().fFillColor]);
632 var local_bins = this.CreateDrawBins(100, 100);
635 var fillcolor =
new THREE.Color(0xDDDDDD);
636 fillcolor.setRGB(fcolor.r / 255, fcolor.g / 255, fcolor.b / 255);
638 var material =
new THREE.MeshLambertMaterial({ color : fillcolor.getHex() });
640 var geom =
new THREE.BoxGeometry(1, 1, 1);
642 var zmin = this.tz.domain()[0], zmax = this.tz.domain()[1];
644 var z1 = this.tz(zmin);
646 for (var i = 0; i < local_bins.length; ++i) {
647 var hh = local_bins[i];
648 if (hh.z <= zmin)
continue;
650 var x1 = this.tx(hh.x1), x2 = this.tx(hh.x2),
651 y1 = this.ty(hh.y1), y2 = this.ty(hh.y2),
652 z2 = (hh.z > zmax) ? this.tz(zmax) : this.tz(hh.z);
654 if ((x1 < -1.001*this.size3d) || (x2 > 1.001*this.size3d) ||
655 (y1 < -1.001*this.size3d) || (y2 > 1.001*this.size3d))
continue;
658 var bin =
new THREE.Mesh(geom, material.clone());
660 bin.position.set((x1+x2)/2, (y1+y2)/2, (z1+z2)/2);
661 bin.scale.set(x2-x1,y2-y1,z2-z1);
663 if (
'tip' in hh) bin.name = hh.tip;
664 this.toplevel.add(bin);
666 var helper =
new THREE.BoxHelper(bin);
667 helper.material.color.set(0x000000);
668 helper.material.linewidth = 1.0;
669 this.toplevel.add(helper);
676 JSROOT.Painter.Render3D =
function(tmout) {
677 if (tmout === undefined) tmout = 5;
680 if (
'render_tmout' in
this)
681 clearTimeout(this.render_tmout);
683 if (this.renderer === undefined)
return;
685 var tm1 =
new Date();
688 this.renderer.render(this.scene, this.camera);
690 var tm2 =
new Date();
692 delete this.render_tmout;
694 if (this.first_render_tm === 0) {
695 this.first_render_tm = tm2.getTime() - tm1.getTime();
696 console.log(
'First render tm = ' + this.first_render_tm);
697 this[
'Add3DInteraction'] = JSROOT.Painter.add3DInteraction;
698 this.Add3DInteraction();
705 if (
'render_tmout' in
this)
return;
707 this.render_tmout = setTimeout(this.Render3D.bind(
this,0), tmout);
711 JSROOT.Painter.Resize3D =
function() {
713 var size3d = this.size_for_3d(this.svg_pad().property(
'can3d'));
715 this.apply_3d_size(size3d);
717 if ((this.scene_width === size3d.width) && (
this.scene_height === size3d.height))
return;
719 if ((size3d.width<10) || (size3d.height<10))
return;
721 this.scene_width = size3d.width;
722 this.scene_height = size3d.height;
724 this.camera.aspect = this.scene_width / this.scene_height;
725 this.camera.updateProjectionMatrix();
727 this.renderer.setSize( this.scene_width, this.scene_height );
732 JSROOT.Painter.TH2Painter_Draw3D =
function(call_back) {
735 this.Create3DScene();
737 this.zmin = this.options.Logz ? this.gmin0bin * 0.3 : this.gminbin;
738 this.zmax = this.gmaxbin * 1.05;
748 JSROOT.CallBack(call_back);
754 JSROOT.TH3Painter =
function(histo) {
755 JSROOT.THistPainter.call(
this, histo);
757 this[
'Create3DScene'] = JSROOT.Painter.HPainter_Create3DScene;
760 JSROOT.TH3Painter.prototype = Object.create(JSROOT.THistPainter.prototype);
762 JSROOT.TH3Painter.prototype.ScanContent =
function() {
763 var histo = this.GetObject();
765 this.nbinsx = histo.fXaxis.fNbins;
766 this.nbinsy = histo.fYaxis.fNbins;
767 this.nbinsz = histo.fZaxis.fNbins;
769 this.xmin = histo.fXaxis.fXmin;
770 this.xmax = histo.fXaxis.fXmax;
772 this.ymin = histo.fYaxis.fXmin;
773 this.ymax = histo.fYaxis.fXmax;
775 this.zmin = histo.fZaxis.fXmin;
776 this.zmax = histo.fZaxis.fXmax;
780 this.gminbin = this.gmaxbin = histo.getBinContent(1,1,1);
782 for (i = 0; i < this.nbinsx; ++i)
783 for (j = 0; j < this.nbinsy; ++j)
784 for (k = 0; k < this.nbinsz; ++k) {
785 var bin_content = histo.getBinContent(i+1, j+1, k+1);
786 if (bin_content < this.gminbin) this.gminbin = bin_content;
else
787 if (bin_content > this.gmaxbin) this.gmaxbin = bin_content;
790 this.draw_content = this.gmaxbin > 0;
792 this.CreateAxisFuncs(
true,
true);
795 JSROOT.TH3Painter.prototype.CountStat =
function() {
796 var histo = this.GetObject(),
797 stat_sum0 = 0, stat_sumx1 = 0, stat_sumy1 = 0,
798 stat_sumz1 = 0, stat_sumx2 = 0, stat_sumy2 = 0, stat_sumz2 = 0,
799 i1 = this.GetSelectIndex(
"x",
"left"),
800 i2 = this.GetSelectIndex(
"x",
"right"),
801 j1 = this.GetSelectIndex(
"y",
"left"),
802 j2 = this.GetSelectIndex(
"y",
"right"),
803 k1 = this.GetSelectIndex(
"z",
"left"),
804 k2 = this.GetSelectIndex(
"z",
"right"),
805 res = { entries: 0, integral: 0, meanx: 0, meany: 0, meanz: 0, rmsx: 0, rmsy: 0, rmsz: 0 };
807 for (var xi = 0; xi < this.nbinsx+2; ++xi) {
809 var xx = this.GetBinX(xi - 0.5);
810 var xside = (xi < i1) ? 0 : (xi > i2 ? 2 : 1);
812 for (var yi = 0; yi < this.nbinsy+2; ++yi) {
814 var yy = this.GetBinY(yi - 0.5);
815 var yside = (yi < j1) ? 0 : (yi > j2 ? 2 : 1);
817 for (var zi = 0; zi < this.nbinsz+2; ++zi) {
819 var zz = this.GetBinZ(zi - 0.5);
820 var zside = (zi < k1) ? 0 : (zi > k2 ? 2 : 1);
822 var cont = histo.getBinContent(xi, yi, zi);
825 if ((xside==1) && (yside==1) && (zside==1)) {
827 stat_sumx1 += xx * cont;
828 stat_sumy1 += yy * cont;
829 stat_sumz1 += zz * cont;
830 stat_sumx2 += xx * xx * cont;
831 stat_sumy2 += yy * yy * cont;
832 stat_sumz2 += zz * zz * cont;
838 if (histo.fTsumw > 0) {
839 stat_sum0 = histo.fTsumw;
840 stat_sumx1 = histo.fTsumwx;
841 stat_sumx2 = histo.fTsumwx2;
842 stat_sumy1 = histo.fTsumwy;
843 stat_sumy2 = histo.fTsumwy2;
844 stat_sumz1 = histo.fTsumwz;
845 stat_sumz2 = histo.fTsumwz2;
849 res.meanx = stat_sumx1 / stat_sum0;
850 res.meany = stat_sumy1 / stat_sum0;
851 res.meanz = stat_sumz1 / stat_sum0;
852 res.rmsx = Math.sqrt(stat_sumx2 / stat_sum0 - res.meanx * res.meanx);
853 res.rmsy = Math.sqrt(stat_sumy2 / stat_sum0 - res.meany * res.meany);
854 res.rmsz = Math.sqrt(stat_sumz2 / stat_sum0 - res.meanz * res.meanz);
857 res.integral = stat_sum0;
859 if (histo.fEntries > 1) res.entries = histo.fEntries;
864 JSROOT.TH3Painter.prototype.FillStatistic =
function(stat, dostat, dofit) {
865 if (this.GetObject()===null)
return false;
867 var pave = stat.GetObject(),
868 data = this.CountStat(),
869 print_name = dostat % 10,
870 print_entries = Math.floor(dostat / 10) % 10,
871 print_mean = Math.floor(dostat / 100) % 10,
872 print_rms = Math.floor(dostat / 1000) % 10,
873 print_under = Math.floor(dostat / 10000) % 10,
874 print_over = Math.floor(dostat / 100000) % 10,
875 print_integral = Math.floor(dostat / 1000000) % 10;
880 pave.AddText(this.GetObject().fName);
882 if (print_entries > 0)
883 pave.AddText(
"Entries = " + stat.Format(data.entries,
"entries"));
885 if (print_mean > 0) {
886 pave.AddText(
"Mean x = " + stat.Format(data.meanx));
887 pave.AddText(
"Mean y = " + stat.Format(data.meany));
888 pave.AddText(
"Mean z = " + stat.Format(data.meanz));
892 pave.AddText(
"Std Dev x = " + stat.Format(data.rmsx));
893 pave.AddText(
"Std Dev y = " + stat.Format(data.rmsy));
894 pave.AddText(
"Std Dev z = " + stat.Format(data.rmsz));
897 if (print_integral > 0) {
898 pave.AddText(
"Integral = " + stat.Format(data.integral,
"entries"));
903 var nlines = pave.fLines.arr.length,
904 stath = nlines * JSROOT.gStyle.StatFontSize;
905 if (stath <= 0 || 3 == (JSROOT.gStyle.StatFont % 10)) {
906 stath = 0.25 * nlines * JSROOT.gStyle.StatH;
907 pave.fY1NDC = 0.93 - stath;
914 JSROOT.TH3Painter.prototype.Draw3DBins =
function() {
916 if (!this.draw_content)
return;
918 var fcolor = d3.rgb(JSROOT.Painter.root_colors[
this.GetObject().fFillColor]);
920 var fillcolor =
new THREE.Color(0xDDDDDD);
921 fillcolor.setRGB(fcolor.r / 255, fcolor.g / 255, fcolor.b / 255);
923 var material = null, geom = null;
925 if (this.options.Box == 11) {
926 material =
new THREE.MeshPhongMaterial({ color : fillcolor.getHex(), specular : 0x4f4f4f });
928 geom =
new THREE.SphereGeometry(0.5);
929 geom.applyMatrix(
new THREE.Matrix4().makeRotationX( Math.PI / 2 ) );
932 material =
new THREE.MeshLambertMaterial({ color : fillcolor.getHex() });
934 geom =
new THREE.BoxGeometry(1, 1, 1);
937 var histo = this.GetObject(),
938 i1 = this.GetSelectIndex(
"x",
"left", 0),
939 i2 = this.GetSelectIndex(
"x",
"right", 0),
940 j1 = this.GetSelectIndex(
"y",
"left", 0),
941 j2 = this.GetSelectIndex(
"y",
"right", 0),
942 k1 = this.GetSelectIndex(
"z",
"left", 0),
943 k2 = this.GetSelectIndex(
"z",
"right", 0),
944 name = this.GetTipName(
"<br/>");
946 var scalex = (this.tx(this.GetBinX(i2+0.5)) - this.tx(this.GetBinX(i1+0.5))) / (i2-i1),
947 scaley = (this.ty(this.GetBinY(j2+0.5)) - this.ty(this.GetBinY(j1+0.5))) / (j2-j1),
948 scalez = (this.tz(this.GetBinZ(k2+0.5)) - this.tz(this.GetBinZ(k1+0.5))) / (k2-k1);
950 for (var i = i1; i < i2; ++i) {
951 var binx = this.GetBinX(i+0.5), grx = this.tx(binx);
952 for (var j = j1; j < j2; ++j) {
953 var biny = this.GetBinY(j+0.5), gry = this.ty(biny);
954 for (var k = k1; k < k2; ++k) {
955 var bin_content = histo.getBinContent(i+1, j+1, k+1);
956 if (bin_content <= this.gminbin)
continue;
958 var wei = (this.options.Color > 0) ? 1. : bin_content / this.gmaxbin;
960 if (wei < 1e-5)
continue;
962 var binz = this.GetBinZ(k+0.5), grz = this.tz(binz);
964 var bin =
new THREE.Mesh(geom, material.clone());
966 bin.position.set( grx, gry, grz );
968 bin.scale.set(scalex*wei, scaley*wei, scalez*wei);
970 if (JSROOT.gStyle.Tooltip > 0)
971 bin.name = name +
'x=' + JSROOT.FFormat(binx,
"6.4g") +
' bin=' + (i+1) +
'<br/>'
972 +
'y=' + JSROOT.FFormat(biny,
"6.4g") +
' bin=' + (j+1) +
'<br/>'
973 +
'z=' + JSROOT.FFormat(binz,
"6.4g") +
' bin=' + (k+1) +
'<br/>'
974 +
'entries=' + JSROOT.FFormat(bin_content,
"7.0g");
976 this.toplevel.add(bin);
978 if (this.options.Box !== 11) {
979 var helper =
new THREE.BoxHelper(bin);
980 helper.material.color.set(0x000000);
981 helper.material.linewidth = 1.0;
982 this.toplevel.add(helper)
989 JSROOT.TH3Painter.prototype.Redraw =
function(resize) {
993 this.Create3DScene();
1000 JSROOT.TH3Painter.prototype.CheckResize =
function(size) {
1001 var pad_painter = this.pad_painter();
1008 changed = pad_painter.CheckCanvasResize(size, JSROOT.browser.isFirefox ?
false :
true);
1010 if (changed) this.Resize3D(size);
1013 JSROOT.TH3Painter.prototype.FillToolbar =
function() {
1014 var pp = this.pad_painter(
true);
1015 if (pp===null)
return;
1017 pp.AddButton(JSROOT.ToolbarIcons.undo,
'Unzoom all axes',
'UnzoomAllAxis');
1018 if (this.draw_content)
1019 pp.AddButton(JSROOT.ToolbarIcons.statbox,
'Toggle stat box',
"ToggleStatBox");
1022 JSROOT.TH3Painter.prototype.FillHistContextMenu =
function(menu) {
1023 if (!this.draw_content)
return;
1025 menu.addDrawMenu(
"Draw with", [
"box",
"box1"],
function(arg) {
1026 this.options = this.DecodeOptions(arg);
1032 JSROOT.Painter.drawHistogram3D =
function(divid, histo, opt) {
1036 JSROOT.extend(
this,
new JSROOT.TH3Painter(histo));
1038 this.SetDivId(divid, 4);
1040 this.options = this.DecodeOptions(opt);
1042 this.CheckPadOptions();
1050 if (JSROOT.gStyle.AutoStat &&
this.create_canvas) {
1051 var stats = this.CreateStat();
1052 if (stats) JSROOT.draw(this.divid, stats,
"");
1057 return this.DrawingReady();
1062 JSROOT.Painter.drawPolyMarker3D =
function(divid, poly, opt) {
1065 this.SetDivId(divid);
1067 var main = this.main_painter();
1069 if ((main == null) || !(
'renderer' in main))
return this.DrawingReady();
1071 var cnt = poly.fP.length;
1074 if ((JSROOT.gStyle.OptimizeDraw > 0) && (cnt > 1000*3)) {
1075 step = Math.floor(cnt / 1000 / 3) * 3;
1076 if (step <= 6) step = 6;
1079 var fcolor = d3.rgb(JSROOT.Painter.root_colors[poly.fMarkerColor]);
1080 var fillcolor =
new THREE.Color(0xDDDDDD);
1081 fillcolor.setRGB(fcolor.r / 255, fcolor.g / 255, fcolor.b / 255);
1083 var material =
new THREE.MeshPhongMaterial({ color : fillcolor.getHex(), specular : 0x4f4f4f});
1086 var geom =
new THREE.BoxGeometry(1, 1, 1);
1088 for (var n=0; n<cnt; n+=step) {
1089 var bin =
new THREE.Mesh(geom, material.clone());
1090 bin.position.set( main.tx(poly.fP[n]), main.ty(poly.fP[n+1]), main.tz(poly.fP[n+2]) );
1091 bin.name = (poly.fName !==
"TPolyMarker3D") ? (poly.fName +
": ") : (
"bin " + n/3 +
": ");
1092 bin.name += main.x_handle.format(poly.fP[n]) +
"," + main.y_handle.format(poly.fP[n+1]) +
"," + main.z_handle.format(poly.fP[n+2]);
1093 main.toplevel.add(bin);
1098 return this.DrawingReady();
1101 return JSROOT.Painter;