00001
00002
00003
00004 (function( factory ) {
00005 if ( typeof define === "function" && define.amd ) {
00006
00007 define( ['d3', 'JSRootPainter', 'threejs_all'], factory );
00008 } else {
00009
00010 if (typeof JSROOT == 'undefined')
00011 throw new Error('JSROOT is not defined', 'JSRoot3DPainter.js');
00012
00013 if (typeof d3 != 'object')
00014 throw new Error('This extension requires d3.v3.js', 'JSRoot3DPainter.js');
00015
00016 if (typeof JSROOT.Painter != 'object')
00017 throw new Error('JSROOT.Painter is not defined', 'JSRoot3DPainter.js');
00018
00019 if (typeof THREE == 'undefined')
00020 throw new Error('THREE is not defined', 'JSRoot3DPainter.js');
00021
00022 factory(d3, JSROOT);
00023 }
00024 } (function(d3, JSROOT) {
00025
00026 JSROOT.Painter.TestWebGL = function() {
00027
00033 if (JSROOT.gStyle.NoWebGL) return false;
00034
00035 if ('_Detect_WebGL' in this) return this._Detect_WebGL;
00036
00037 try {
00038 var canvas = document.createElement( 'canvas' );
00039 this._Detect_WebGL = !! ( window.WebGLRenderingContext && ( canvas.getContext( 'webgl' ) || canvas.getContext( 'experimental-webgl' ) ) );
00040
00041 } catch (e) {
00042 return false;
00043 }
00044
00045 return this._Detect_WebGL;
00046 }
00047
00048 JSROOT.Painter.add3DInteraction = function() {
00049
00050
00051 var painter = this;
00052 var mouseX, mouseY, distXY = 0, mouseDowned = false;
00053 var INTERSECTED = null;
00054
00055 var tooltip = {
00056 tt: null, cont: null,
00057 pos : function(e) {
00058 if (this.tt === null) return;
00059 var u = JSROOT.browser.isIE ? (event.clientY + document.documentElement.scrollTop) : e.pageY;
00060 var l = JSROOT.browser.isIE ? (event.clientX + document.documentElement.scrollLeft) : e.pageX;
00061
00062 this.tt.style.top = (u + 15) + 'px';
00063 this.tt.style.left = (l + 3) + 'px';
00064 },
00065 show : function(v) {
00066 if (JSROOT.gStyle.Tooltip <= 0) return;
00067 if (this.tt === null) {
00068 this.tt = document.createElement('div');
00069 this.tt.setAttribute('class', 'jsroot');
00070 var t = document.createElement('div');
00071 t.setAttribute('class', 'tt3d_border');
00072 this.cont = document.createElement('div');
00073 this.cont.setAttribute('class', 'tt3d_cont');
00074 var b = document.createElement('div');
00075 b.setAttribute('class', 'tt3d_border');
00076 this.tt.appendChild(t);
00077 this.tt.appendChild(this.cont);
00078 this.tt.appendChild(b);
00079 document.body.appendChild(this.tt);
00080 this.tt.style.opacity = 1;
00081 this.tt.style.filter = 'alpha(opacity=1)';
00082 this.tt.style.position = 'absolute';
00083 this.tt.style.display = 'block';
00084 }
00085 this.cont.innerHTML = v;
00086 this.tt.style.width = 'auto';
00087 if (JSROOT.browser.isIE)
00088 this.tt.style.width = this.tt.offsetWidth;
00089 },
00090 hide : function() {
00091 if (this.tt !== null)
00092 document.body.removeChild(this.tt);
00093 this.tt = null;
00094 }
00095 };
00096
00097 var raycaster = new THREE.Raycaster();
00098 var do_bins_highlight = painter.first_render_tm < 2000;
00099
00100 function findIntersection(mouse) {
00101
00102
00103 if (JSROOT.gStyle.Tooltip <= 0) return tooltip.hide();
00104
00105 raycaster.setFromCamera( mouse, painter.camera );
00106 var intersects = raycaster.intersectObjects(painter.scene.children, true);
00107 if (intersects.length > 0) {
00108 var pick = null;
00109 for (var i = 0; i < intersects.length; ++i) {
00110 if (('name' in intersects[i].object) && (intersects[i].object.name.length > 0)) {
00111 pick = intersects[i].object;
00112 break;
00113 }
00114 }
00115 if ((pick!==null) && (INTERSECTED !== pick)) {
00116 if (INTERSECTED && do_bins_highlight && ('emissive' in INTERSECTED.material))
00117 INTERSECTED.material.emissive.setHex(INTERSECTED.currentHex);
00118 INTERSECTED = pick;
00119 if (do_bins_highlight && ('emissive' in INTERSECTED.material)) {
00120 INTERSECTED.currentHex = INTERSECTED.material.emissive.getHex();
00121 INTERSECTED.material.emissive.setHex(0x5f5f5f);
00122 painter.Render3D(0);
00123 }
00124
00125 tooltip.show(INTERSECTED.name, 200);
00126 }
00127 } else {
00128 if (INTERSECTED && do_bins_highlight && ('emissive' in INTERSECTED.material)) {
00129 INTERSECTED.material.emissive.setHex(INTERSECTED.currentHex);
00130 painter.Render3D(0);
00131 }
00132 INTERSECTED = null;
00133 tooltip.hide();
00134 }
00135 };
00136
00137 function coordinates(e) {
00138 if ('changedTouches' in e) return e.changedTouches;
00139 if ('touches' in e) return e.touches;
00140 return [e];
00141 }
00142
00143 function mousedown(e) {
00144 tooltip.hide();
00145 e.preventDefault();
00146
00147 var arr = coordinates(e);
00148 if (arr.length == 2) {
00149 distXY = Math.sqrt(Math.pow(arr[0].pageX - arr[1].pageX, 2) + Math.pow(arr[0].pageY - arr[1].pageY, 2));
00150 } else {
00151 mouseX = arr[0].pageX;
00152 mouseY = arr[0].pageY;
00153 }
00154 mouseDowned = true;
00155
00156 }
00157
00158 painter.renderer.domElement.addEventListener('touchstart', mousedown);
00159 painter.renderer.domElement.addEventListener('mousedown', mousedown);
00160
00161 function mousemove(e) {
00162 var arr = coordinates(e);
00163
00164 if (mouseDowned) {
00165 if (arr.length == 2) {
00166 var dist = Math.sqrt(Math.pow(arr[0].pageX - arr[1].pageX, 2) + Math.pow(arr[0].pageY - arr[1].pageY, 2));
00167
00168 var delta = (dist-distXY)/(dist+distXY);
00169 distXY = dist;
00170 if (delta === 1.) return;
00171
00172 painter.camera.position.x += delta * painter.size3d * 10;
00173 painter.camera.position.y += delta * painter.size3d * 10;
00174 painter.camera.position.z -= delta * painter.size3d * 10;
00175 } else {
00176 var moveX = arr[0].pageX - mouseX;
00177 var moveY = arr[0].pageY - mouseY;
00178 var length = painter.camera.position.length();
00179 var ddd = length > painter.size3d ? 0.001*length/painter.size3d : 0.01;
00180
00181
00182
00183
00184 painter.toplevel.rotation.z += moveX * ddd;
00185 painter.toplevel.rotation.x += moveY * ddd;
00186 painter.toplevel.rotation.y -= moveY * ddd;
00187 mouseX = arr[0].pageX;
00188 mouseY = arr[0].pageY;
00189 }
00190 painter.Render3D(0);
00191 } else
00192 if (arr.length == 1) {
00193 var mouse_x = ('offsetX' in arr[0]) ? arr[0].offsetX : arr[0].layerX;
00194 var mouse_y = ('offsetY' in arr[0]) ? arr[0].offsetY : arr[0].layerY;
00195 mouse = { x: (mouse_x / painter.renderer.domElement.width) * 2 - 1,
00196 y: -(mouse_y / painter.renderer.domElement.height) * 2 + 1 };
00197 findIntersection(mouse);
00198 tooltip.pos(arr[0]);
00199 } else {
00200 tooltip.hide();
00201 }
00202
00203 e.stopPropagation();
00204 e.preventDefault();
00205 }
00206
00207 painter.renderer.domElement.addEventListener('touchmove', mousemove);
00208 painter.renderer.domElement.addEventListener('mousemove', mousemove);
00209
00210 function mouseup(e) {
00211 mouseDowned = false;
00212 tooltip.hide();
00213 distXY = 0;
00214 }
00215
00216 painter.renderer.domElement.addEventListener('touchend', mouseup);
00217 painter.renderer.domElement.addEventListener('touchcancel', mouseup);
00218 painter.renderer.domElement.addEventListener('mouseup', mouseup);
00219
00220 function mousewheel(event) {
00221 event.preventDefault();
00222 event.stopPropagation();
00223
00224 var delta = 0;
00225 if ( event.wheelDelta ) {
00226
00227 delta = event.wheelDelta / 400;
00228 } else if ( event.detail ) {
00229
00230 delta = - event.detail / 30;
00231 }
00232 painter.camera.position.x -= delta * painter.size3d;
00233 painter.camera.position.y -= delta * painter.size3d;
00234 painter.camera.position.z += delta * painter.size3d;
00235 painter.Render3D(0);
00236 }
00237
00238 painter.renderer.domElement.addEventListener( 'mousewheel', mousewheel, false );
00239 painter.renderer.domElement.addEventListener( 'MozMousePixelScroll', mousewheel, false );
00240
00241
00242 painter.renderer.domElement.addEventListener('mouseleave', function() {
00243 tooltip.hide();
00244 });
00245
00246
00247 painter.renderer.domElement.addEventListener('contextmenu', function(e) {
00248 e.preventDefault();
00249 tooltip.hide();
00250
00251 painter.ShowContextMenu("hist", e);
00252 });
00253
00254 }
00255
00256 JSROOT.Painter.HPainter_Create3DScene = function(arg) {
00257
00258 if ((arg!==null) && (arg<0)) {
00259 this.clear_3d_canvas();
00260 delete this.size3d;
00261 delete this.scene;
00262 delete this.toplevel;
00263 delete this.camera;
00264 delete this.renderer;
00265 if ('render_tmout' in this) {
00266 clearTimeout(this.render_tmout);
00267 delete this.render_tmout;
00268 }
00269 return;
00270 }
00271
00272 if ('toplevel' in this) {
00273
00274
00275 var newtop = new THREE.Object3D();
00276
00277 newtop.rotation.x = this.toplevel.rotation.x;
00278 newtop.rotation.y = this.toplevel.rotation.y;
00279
00280 this.scene.remove(this.toplevel);
00281
00282 this.scene.add(newtop);
00283
00284 this.toplevel = newtop;
00285
00286 return;
00287 }
00288
00289 var size = this.size_for_3d();
00290
00291 this.size3d = 100;
00292
00293
00294 this.scene = new THREE.Scene();
00295
00296
00297 this.toplevel = new THREE.Object3D();
00298
00299
00300 this.scene.add(this.toplevel);
00301 this.scene_width = size.width;
00302 this.scene_height = size.height
00303
00304 this.camera = new THREE.PerspectiveCamera(45, this.scene_width / this.scene_height, 1, 40*this.size3d);
00305 var pointLight = new THREE.PointLight(0xcfcfcf);
00306 this.camera.add( pointLight );
00307 pointLight.position.set( this.size3d / 10, this.size3d / 10, this.size3d / 10 );
00308 this.camera.position.set(-3*this.size3d, -3*this.size3d, 3*this.size3d);
00309 this.camera.up = new THREE.Vector3(0,0,1);
00310 this.camera.lookAt(new THREE.Vector3(0,0,this.size3d));
00311 this.scene.add( this.camera );
00312
00313 var webgl = JSROOT.Painter.TestWebGL();
00314
00315 this.renderer = webgl ? new THREE.WebGLRenderer({ antialias : true, alpha: true }) :
00316 new THREE.CanvasRenderer({ antialias : true, alpha: true });
00317
00318
00319 this.renderer.setSize(this.scene_width, this.scene_height);
00320
00321 this.add_3d_canvas(size, this.renderer.domElement);
00322
00323 this['DrawXYZ'] = JSROOT.Painter.HPainter_DrawXYZ;
00324 this['Render3D'] = JSROOT.Painter.Render3D;
00325 this['Resize3D'] = JSROOT.Painter.Resize3D;
00326
00327 this.first_render_tm = 0;
00328 }
00329
00330 JSROOT.Painter.HPainter_DrawXYZ = function() {
00331
00332 var grminx = -this.size3d, grmaxx = this.size3d,
00333 grminy = -this.size3d, grmaxy = this.size3d,
00334 grminz = 0, grmaxz = 2*this.size3d,
00335 textsize = Math.round(this.size3d * 0.07),
00336 bothsides = (this.size3d !== 0),
00337 xmin = this.xmin, xmax = this.xmax,
00338 ymin = this.ymin, ymax = this.ymax,
00339 zmin = this.zmin, zmax = this.zmax,
00340 histo = this.histo;
00341
00342 if (this.size3d === 0) {
00343 grminx = this.xmin; grmaxx = this.xmax;
00344 grminy = this.ymin; grmaxy = this.ymax;
00345 grminz = this.zmin; grmaxz = this.zmax;
00346 textsize = (grmaxz - grminz) * 0.05;
00347 }
00348
00349 if (('zoom_xmin' in this) && ('zoom_xmax' in this) && (this.zoom_xmin !== this.zoom_xmax)) {
00350 xmin = this.zoom_xmin; xmax = this.zoom_xmax;
00351 }
00352
00353 if (('zoom_ymin' in this) && ('zoom_ymax' in this) && (this.zoom_ymin !== this.zoom_ymax)) {
00354 ymin = this.zoom_ymin; ymax = this.zoom_ymax;
00355 }
00356
00357 if (('zoom_zmin' in this) && ('zoom_zmax' in this) && (this.zoom_zmin !== this.zoom_zmax)) {
00358 zmin = this.zoom_zmin; zmax = this.zoom_zmax;
00359 }
00360
00361 if (this.options.Logx) {
00362 if (xmax <= 0) xmax = 1.;
00363 if ((xmin <= 0) && (this.nbinsx > 0))
00364 for (var i=0;i<this.nbinsx;++i) {
00365 xmin = Math.max(xmin, this.GetBinX(i));
00366 if (xmin>0) break;
00367 }
00368 if (xmin <= 0) xmin = 1e-4*xmax;
00369 this.tx = d3.scale.log();
00370 this.x_kind = "log";
00371 } else {
00372 this.tx = d3.scale.linear();
00373 this.x_kind = "lin";
00374 }
00375 this.tx.domain([ xmin, xmax ]).range([ grminx, grmaxx ]);
00376 this.x_handle = new JSROOT.TAxisPainter(histo ? histo.fXaxis : null);
00377 this.x_handle.SetAxisConfig("xaxis", this.x_kind, this.tx, this.xmin, this.xmax, xmin, xmax);
00378 this.x_handle.CreateFormatFuncs();
00379
00380 if (this.options.Logy) {
00381 if (ymax <= 0) ymax = 1.;
00382 if ((ymin <= 0) && (this.nbinsy>0))
00383 for (var i=0;i<this.nbinsy;++i) {
00384 ymin = Math.max(ymin, this.GetBinY(i));
00385 if (ymin>0) break;
00386 }
00387
00388 if (ymin <= 0) ymin = 1e-4*ymax;
00389 this.ty = d3.scale.log();
00390 this.y_kind = "log";
00391 } else {
00392 this.ty = d3.scale.linear();
00393 this.y_kind = "lin";
00394 }
00395 this.ty.domain([ ymin, ymax ]).range([ grminy, grmaxy ]);
00396 this.y_handle = new JSROOT.TAxisPainter(histo ? histo.fYaxis : null);
00397 this.y_handle.SetAxisConfig("yaxis", this.y_kind, this.ty, this.ymin, this.ymax, ymin, ymax);
00398 this.y_handle.CreateFormatFuncs();
00399
00400 if (this.options.Logz) {
00401 if (zmax <= 0) zmax = 1;
00402 if (zmin <= 0) zmin = 1e-4*zmax;
00403 this.tz = d3.scale.log();
00404 this.z_kind = "log";
00405 } else {
00406 this.tz = d3.scale.linear();
00407 this.z_kind = "lin";
00408 }
00409 this.tz.domain([ zmin, zmax ]).range([ grminz, grmaxz ]);
00410
00411 this.z_handle = new JSROOT.TAxisPainter(histo ? histo.fZaxis : null);
00412 this.z_handle.SetAxisConfig("zaxis", this.z_kind, this.tz, this.zmin, this.zmax, zmin, zmax);
00413 this.z_handle.CreateFormatFuncs();
00414
00415 var textMaterial = new THREE.MeshBasicMaterial({ color: 0x000000 });
00416 var lineMaterial = new THREE.LineBasicMaterial({ color: 0x000000 });
00417
00418 var ticklen = textsize * 0.5, text, tick;
00419
00420 var xticks = this.x_handle.CreateTicks();
00421
00422
00423 var geometry = new THREE.Geometry();
00424 geometry.vertices.push(new THREE.Vector3(0, 0, 0));
00425 geometry.vertices.push(new THREE.Vector3(0, -1, -1));
00426
00427 while (xticks.next()) {
00428 var grx = xticks.grpos;
00429 var is_major = (xticks.kind===1);
00430 var lbl = this.x_handle.format(xticks.tick, true, true);
00431 if (xticks.last_major()) lbl = "x"; else
00432 if (lbl === null) { is_major = false; lbl = ""; }
00433 var plen = (is_major ? ticklen : ticklen * 0.6) * Math.sin(Math.PI/4);
00434
00435 if (is_major) {
00436 var text3d = new THREE.TextGeometry(lbl, { size : textsize, height : 0, curveSegments : 10 });
00437 text3d.computeBoundingBox();
00438 var centerOffset = 0.5 * (text3d.boundingBox.max.x - text3d.boundingBox.min.x);
00439
00440 if (bothsides) {
00441 text = new THREE.Mesh(text3d, textMaterial);
00442 text.position.set(grx + centerOffset, grmaxy + plen + textsize, grminz - plen - textsize);
00443 text.rotation.x = Math.PI*3/4;
00444 text.rotation.y = Math.PI;
00445 text.name = "X axis";
00446 this.toplevel.add(text);
00447 }
00448
00449 text = new THREE.Mesh(text3d, textMaterial);
00450 text.position.set(grx - centerOffset, grminy - plen - textsize, grminz - plen - textsize);
00451 text.rotation.x = Math.PI/4;
00452 text.name = "X axis";
00453 this.toplevel.add(text);
00454 }
00455
00456 if (bothsides) {
00457 tick = new THREE.Line(geometry, lineMaterial);
00458 tick.position.set(grx,grmaxy, grminz);
00459 tick.scale.set(1,plen,plen);
00460 tick.rotation.z = Math.PI;
00461 tick.name = "X axis: " + this.x_handle.format(xticks.tick);
00462 this.toplevel.add(tick);
00463 }
00464
00465 tick = new THREE.Line(geometry, lineMaterial);
00466 tick.position.set(grx,grminy,grminz);
00467 tick.scale.set(1,plen,plen);
00468 tick.name = "X axis: " + this.x_handle.format(xticks.tick);
00469 this.toplevel.add(tick);
00470 }
00471
00472 var yticks = this.y_handle.CreateTicks();
00473 geometry = new THREE.Geometry();
00474 geometry.vertices.push(new THREE.Vector3(0, 0, 0));
00475 geometry.vertices.push(new THREE.Vector3(-1, 0, -1));
00476
00477 while (yticks.next()) {
00478 var gry = yticks.grpos;
00479 var is_major = (yticks.kind===1);
00480 var lbl = this.y_handle.format(yticks.tick, true, true);
00481 if (yticks.last_major()) lbl = "y"; else
00482 if (lbl === null) { is_major = false; lbl = ""; }
00483 var plen = (is_major ? ticklen : ticklen*0.6) * Math.sin(Math.PI/4);
00484
00485 if (is_major) {
00486 var text3d = new THREE.TextGeometry(lbl, { size : textsize, height : 0, curveSegments : 10 });
00487
00488 text3d.computeBoundingBox();
00489 var centerOffset = 0.5 * (text3d.boundingBox.max.x - text3d.boundingBox.min.x);
00490
00491 if (bothsides) {
00492 text = new THREE.Mesh(text3d, textMaterial);
00493 text.position.set(grmaxx + plen + textsize, gry + centerOffset, grminz - plen - textsize);
00494 text.rotation.y = Math.PI / 4;
00495 text.rotation.z = Math.PI / 2;
00496 text.name = "Y axis";
00497 this.toplevel.add(text);
00498 }
00499
00500 text = new THREE.Mesh(text3d, textMaterial);
00501 text.position.set(grminx - plen - textsize, gry + centerOffset, grminz - plen - textsize);
00502 text.rotation.y = -Math.PI / 4;
00503 text.rotation.z = -Math.PI / 2;
00504 text.name = "Y axis";
00505 this.toplevel.add(text);
00506 }
00507 if (bothsides) {
00508 tick = new THREE.Line(geometry, lineMaterial);
00509 tick.position.set(grmaxx,gry,grminz);
00510 tick.scale.set(plen,1,plen);
00511 tick.rotation.z = Math.PI;
00512 tick.name = "Y axis " + this.y_handle.format(yticks.tick);
00513 this.toplevel.add(tick);
00514 }
00515 tick = new THREE.Line(geometry, lineMaterial);
00516 tick.position.set(grminx,gry,grminz);
00517 tick.scale.set(plen,1, plen);
00518 tick.name = "Y axis " + this.y_handle.format(yticks.tick);
00519 this.toplevel.add(tick);
00520 }
00521
00522 var zticks = this.z_handle.CreateTicks();
00523 geometry = new THREE.Geometry();
00524 geometry.vertices.push(new THREE.Vector3(0, 0, 0));
00525 geometry.vertices.push(new THREE.Vector3(-1, 1, 0));
00526 while (zticks.next()) {
00527 var grz = zticks.grpos;
00528 var is_major = zticks.kind == 1;
00529
00530 var lbl = this.z_handle.format(zticks.tick, true, true);
00531 if (lbl === null) { is_major = false; lbl = ""; }
00532 var plen = (is_major ? ticklen : ticklen * 0.6) * Math.sin(Math.PI/4);
00533
00534 if (is_major) {
00535 var text3d = new THREE.TextGeometry(lbl, { size : textsize, height : 0, curveSegments : 10 });
00536
00537 text3d.computeBoundingBox();
00538 var offset = 0.8 * (text3d.boundingBox.max.x - text3d.boundingBox.min.x) + 0.7 * textsize;
00539
00540 var textz = grz - 0.4*textsize;
00541
00542 if (bothsides) {
00543 text = new THREE.Mesh(text3d, textMaterial);
00544 text.position.set(grmaxx + offset, grmaxy + offset, textz);
00545 text.rotation.x = 0.5*Math.PI;
00546 text.rotation.y = -0.75 * Math.PI;
00547 text.name = "Z axis";
00548 this.toplevel.add(text);
00549
00550 text = new THREE.Mesh(text3d, textMaterial);
00551 text.position.set(grmaxx + offset, grminy - offset, textz);
00552 text.rotation.x = 0.5*Math.PI;
00553 text.rotation.y = 0.75*Math.PI;
00554 text.name = "Z axis";
00555 this.toplevel.add(text);
00556
00557 text = new THREE.Mesh(text3d, textMaterial);
00558 text.position.set(grminx - offset, grminy - offset, textz);
00559 text.rotation.x = 0.5*Math.PI;
00560 text.rotation.y = 0.25*Math.PI;
00561 text.name = "Z axis";
00562 this.toplevel.add(text);
00563 }
00564
00565 text = new THREE.Mesh(text3d, textMaterial);
00566 text.position.set(grminx - offset, grmaxy + offset, textz);
00567 text.rotation.x = 0.5*Math.PI;
00568 text.rotation.y = -0.25*Math.PI;
00569 text.name = "Z axis";
00570 this.toplevel.add(text);
00571 }
00572 if (bothsides) {
00573 tick = new THREE.Line(geometry, lineMaterial);
00574 tick.position.set(grmaxx,grmaxy,grz);
00575 tick.scale.set(plen,plen,1);
00576 tick.rotation.z = -Math.PI/2;
00577 tick.name = "Z axis " + this.z_handle.format(zticks.tick);
00578 this.toplevel.add(tick);
00579
00580 tick = new THREE.Line(geometry, lineMaterial);
00581 tick.position.set(grmaxx,grminy,grz);
00582 tick.scale.set(plen,plen,1);
00583 tick.rotation.z = Math.PI;
00584 tick.name = "Z axis " + this.z_handle.format(zticks.tick);
00585 this.toplevel.add(tick);
00586
00587 tick = new THREE.Line(geometry, lineMaterial);
00588 tick.position.set(grminx,grminy,grz);
00589 tick.scale.set(plen,plen,1);
00590 tick.rotation.z = Math.PI/2;
00591 tick.name = "Z axis " + this.z_handle.format(zticks.tick);
00592 this.toplevel.add(tick);
00593 }
00594
00595 tick = new THREE.Line(geometry, lineMaterial);
00596 tick.position.set(grminx,grmaxy,grz);
00597 tick.scale.set(plen,plen,1);
00598 tick.name = "Z axis " + this.z_handle.format(zticks.tick);
00599 this.toplevel.add(tick);
00600 }
00601
00602
00603 if (this.size3d === 0) return;
00604
00605 var wireMaterial = new THREE.MeshBasicMaterial({
00606 color : 0x000000,
00607 wireframe : true,
00608 wireframeLinewidth : 0.5,
00609 side : THREE.DoubleSide
00610 });
00611
00612
00613
00614 var cube = new THREE.Mesh(new THREE.BoxGeometry(this.size3d * 2, this.size3d * 2, this.size3d * 2), wireMaterial);
00615
00616
00617 var helper = new THREE.BoxHelper(cube);
00618 helper.material.color.set(0x000000);
00619
00620 var box = new THREE.Object3D();
00621 box.add(helper);
00622 box.position.z = this.size3d;
00623
00624
00625 this.toplevel.add(box);
00626 }
00627
00628 JSROOT.Painter.TH2Painter_Draw3DBins = function() {
00629
00630 var fcolor = d3.rgb(JSROOT.Painter.root_colors[this.GetObject().fFillColor]);
00631
00632 var local_bins = this.CreateDrawBins(100, 100);
00633
00634
00635 var fillcolor = new THREE.Color(0xDDDDDD);
00636 fillcolor.setRGB(fcolor.r / 255, fcolor.g / 255, fcolor.b / 255);
00637
00638 var material = new THREE.MeshLambertMaterial({ color : fillcolor.getHex() });
00639
00640 var geom = new THREE.BoxGeometry(1, 1, 1);
00641
00642 var zmin = this.tz.domain()[0], zmax = this.tz.domain()[1];
00643
00644 var z1 = this.tz(zmin);
00645
00646 for (var i = 0; i < local_bins.length; ++i) {
00647 var hh = local_bins[i];
00648 if (hh.z <= zmin) continue;
00649
00650 var x1 = this.tx(hh.x1), x2 = this.tx(hh.x2),
00651 y1 = this.ty(hh.y1), y2 = this.ty(hh.y2),
00652 z2 = (hh.z > zmax) ? this.tz(zmax) : this.tz(hh.z);
00653
00654 if ((x1 < -1.001*this.size3d) || (x2 > 1.001*this.size3d) ||
00655 (y1 < -1.001*this.size3d) || (y2 > 1.001*this.size3d)) continue;
00656
00657
00658 var bin = new THREE.Mesh(geom, material.clone());
00659
00660 bin.position.set((x1+x2)/2, (y1+y2)/2, (z1+z2)/2);
00661 bin.scale.set(x2-x1,y2-y1,z2-z1);
00662
00663 if ('tip' in hh) bin.name = hh.tip;
00664 this.toplevel.add(bin);
00665
00666 var helper = new THREE.BoxHelper(bin);
00667 helper.material.color.set(0x000000);
00668 helper.material.linewidth = 1.0;
00669 this.toplevel.add(helper);
00670 }
00671
00672 delete local_bins;
00673 local_bins = null;
00674 }
00675
00676 JSROOT.Painter.Render3D = function(tmout) {
00677 if (tmout === undefined) tmout = 5;
00678
00679 if (tmout <= 0) {
00680 if ('render_tmout' in this)
00681 clearTimeout(this.render_tmout);
00682
00683 if (this.renderer === undefined) return;
00684
00685 var tm1 = new Date();
00686
00687
00688 this.renderer.render(this.scene, this.camera);
00689
00690 var tm2 = new Date();
00691
00692 delete this.render_tmout;
00693
00694 if (this.first_render_tm === 0) {
00695 this.first_render_tm = tm2.getTime() - tm1.getTime();
00696 console.log('First render tm = ' + this.first_render_tm);
00697 this['Add3DInteraction'] = JSROOT.Painter.add3DInteraction;
00698 this.Add3DInteraction();
00699 }
00700
00701 return;
00702 }
00703
00704
00705 if ('render_tmout' in this) return;
00706
00707 this.render_tmout = setTimeout(this.Render3D.bind(this,0), tmout);
00708 }
00709
00710
00711 JSROOT.Painter.Resize3D = function() {
00712
00713 var size3d = this.size_for_3d(this.svg_pad().property('can3d'));
00714
00715 this.apply_3d_size(size3d);
00716
00717 if ((this.scene_width === size3d.width) && (this.scene_height === size3d.height)) return;
00718
00719 if ((size3d.width<10) || (size3d.height<10)) return;
00720
00721 this.scene_width = size3d.width;
00722 this.scene_height = size3d.height;
00723
00724 this.camera.aspect = this.scene_width / this.scene_height;
00725 this.camera.updateProjectionMatrix();
00726
00727 this.renderer.setSize( this.scene_width, this.scene_height );
00728
00729 this.Render3D();
00730 }
00731
00732 JSROOT.Painter.TH2Painter_Draw3D = function(call_back) {
00733
00734
00735 this.Create3DScene();
00736
00737 this.zmin = this.options.Logz ? this.gmin0bin * 0.3 : this.gminbin;
00738 this.zmax = this.gmaxbin * 1.05;
00739
00740 this.DrawXYZ();
00741
00742 this.Draw3DBins();
00743
00744 this.DrawTitle();
00745
00746 this.Render3D();
00747
00748 JSROOT.CallBack(call_back);
00749 }
00750
00751
00752
00753
00754 JSROOT.TH3Painter = function(histo) {
00755 JSROOT.THistPainter.call(this, histo);
00756
00757 this['Create3DScene'] = JSROOT.Painter.HPainter_Create3DScene;
00758 }
00759
00760 JSROOT.TH3Painter.prototype = Object.create(JSROOT.THistPainter.prototype);
00761
00762 JSROOT.TH3Painter.prototype.ScanContent = function() {
00763 var histo = this.GetObject();
00764
00765 this.nbinsx = histo.fXaxis.fNbins;
00766 this.nbinsy = histo.fYaxis.fNbins;
00767 this.nbinsz = histo.fZaxis.fNbins;
00768
00769 this.xmin = histo.fXaxis.fXmin;
00770 this.xmax = histo.fXaxis.fXmax;
00771
00772 this.ymin = histo.fYaxis.fXmin;
00773 this.ymax = histo.fYaxis.fXmax;
00774
00775 this.zmin = histo.fZaxis.fXmin;
00776 this.zmax = histo.fZaxis.fXmax;
00777
00778
00779
00780 this.gminbin = this.gmaxbin = histo.getBinContent(1,1,1);
00781 var i,j,k;
00782 for (i = 0; i < this.nbinsx; ++i)
00783 for (j = 0; j < this.nbinsy; ++j)
00784 for (k = 0; k < this.nbinsz; ++k) {
00785 var bin_content = histo.getBinContent(i+1, j+1, k+1);
00786 if (bin_content < this.gminbin) this.gminbin = bin_content; else
00787 if (bin_content > this.gmaxbin) this.gmaxbin = bin_content;
00788 }
00789
00790 this.draw_content = this.gmaxbin > 0;
00791
00792 this.CreateAxisFuncs(true, true);
00793 }
00794
00795 JSROOT.TH3Painter.prototype.CountStat = function() {
00796 var histo = this.GetObject(),
00797 stat_sum0 = 0, stat_sumx1 = 0, stat_sumy1 = 0,
00798 stat_sumz1 = 0, stat_sumx2 = 0, stat_sumy2 = 0, stat_sumz2 = 0,
00799 i1 = this.GetSelectIndex("x", "left"),
00800 i2 = this.GetSelectIndex("x", "right"),
00801 j1 = this.GetSelectIndex("y", "left"),
00802 j2 = this.GetSelectIndex("y", "right"),
00803 k1 = this.GetSelectIndex("z", "left"),
00804 k2 = this.GetSelectIndex("z", "right"),
00805 res = { entries: 0, integral: 0, meanx: 0, meany: 0, meanz: 0, rmsx: 0, rmsy: 0, rmsz: 0 };
00806
00807 for (var xi = 0; xi < this.nbinsx+2; ++xi) {
00808
00809 var xx = this.GetBinX(xi - 0.5);
00810 var xside = (xi < i1) ? 0 : (xi > i2 ? 2 : 1);
00811
00812 for (var yi = 0; yi < this.nbinsy+2; ++yi) {
00813
00814 var yy = this.GetBinY(yi - 0.5);
00815 var yside = (yi < j1) ? 0 : (yi > j2 ? 2 : 1);
00816
00817 for (var zi = 0; zi < this.nbinsz+2; ++zi) {
00818
00819 var zz = this.GetBinZ(zi - 0.5);
00820 var zside = (zi < k1) ? 0 : (zi > k2 ? 2 : 1);
00821
00822 var cont = histo.getBinContent(xi, yi, zi);
00823 res.entries += cont;
00824
00825 if ((xside==1) && (yside==1) && (zside==1)) {
00826 stat_sum0 += cont;
00827 stat_sumx1 += xx * cont;
00828 stat_sumy1 += yy * cont;
00829 stat_sumz1 += zz * cont;
00830 stat_sumx2 += xx * xx * cont;
00831 stat_sumy2 += yy * yy * cont;
00832 stat_sumz2 += zz * zz * cont;
00833 }
00834 }
00835 }
00836 }
00837
00838 if (histo.fTsumw > 0) {
00839 stat_sum0 = histo.fTsumw;
00840 stat_sumx1 = histo.fTsumwx;
00841 stat_sumx2 = histo.fTsumwx2;
00842 stat_sumy1 = histo.fTsumwy;
00843 stat_sumy2 = histo.fTsumwy2;
00844 stat_sumz1 = histo.fTsumwz;
00845 stat_sumz2 = histo.fTsumwz2;
00846 }
00847
00848 if (stat_sum0 > 0) {
00849 res.meanx = stat_sumx1 / stat_sum0;
00850 res.meany = stat_sumy1 / stat_sum0;
00851 res.meanz = stat_sumz1 / stat_sum0;
00852 res.rmsx = Math.sqrt(stat_sumx2 / stat_sum0 - res.meanx * res.meanx);
00853 res.rmsy = Math.sqrt(stat_sumy2 / stat_sum0 - res.meany * res.meany);
00854 res.rmsz = Math.sqrt(stat_sumz2 / stat_sum0 - res.meanz * res.meanz);
00855 }
00856
00857 res.integral = stat_sum0;
00858
00859 if (histo.fEntries > 1) res.entries = histo.fEntries;
00860
00861 return res;
00862 }
00863
00864 JSROOT.TH3Painter.prototype.FillStatistic = function(stat, dostat, dofit) {
00865 if (this.GetObject()===null) return false;
00866
00867 var pave = stat.GetObject(),
00868 data = this.CountStat(),
00869 print_name = dostat % 10,
00870 print_entries = Math.floor(dostat / 10) % 10,
00871 print_mean = Math.floor(dostat / 100) % 10,
00872 print_rms = Math.floor(dostat / 1000) % 10,
00873 print_under = Math.floor(dostat / 10000) % 10,
00874 print_over = Math.floor(dostat / 100000) % 10,
00875 print_integral = Math.floor(dostat / 1000000) % 10;
00876
00877
00878
00879 if (print_name > 0)
00880 pave.AddText(this.GetObject().fName);
00881
00882 if (print_entries > 0)
00883 pave.AddText("Entries = " + stat.Format(data.entries,"entries"));
00884
00885 if (print_mean > 0) {
00886 pave.AddText("Mean x = " + stat.Format(data.meanx));
00887 pave.AddText("Mean y = " + stat.Format(data.meany));
00888 pave.AddText("Mean z = " + stat.Format(data.meanz));
00889 }
00890
00891 if (print_rms > 0) {
00892 pave.AddText("Std Dev x = " + stat.Format(data.rmsx));
00893 pave.AddText("Std Dev y = " + stat.Format(data.rmsy));
00894 pave.AddText("Std Dev z = " + stat.Format(data.rmsz));
00895 }
00896
00897 if (print_integral > 0) {
00898 pave.AddText("Integral = " + stat.Format(data.integral,"entries"));
00899 }
00900
00901
00902
00903 var nlines = pave.fLines.arr.length,
00904 stath = nlines * JSROOT.gStyle.StatFontSize;
00905 if (stath <= 0 || 3 == (JSROOT.gStyle.StatFont % 10)) {
00906 stath = 0.25 * nlines * JSROOT.gStyle.StatH;
00907 pave.fY1NDC = 0.93 - stath;
00908 pave.fY2NDC = 0.93;
00909 }
00910
00911 return true;
00912 }
00913
00914 JSROOT.TH3Painter.prototype.Draw3DBins = function() {
00915
00916 if (!this.draw_content) return;
00917
00918 var fcolor = d3.rgb(JSROOT.Painter.root_colors[this.GetObject().fFillColor]);
00919
00920 var fillcolor = new THREE.Color(0xDDDDDD);
00921 fillcolor.setRGB(fcolor.r / 255, fcolor.g / 255, fcolor.b / 255);
00922
00923 var material = null, geom = null;
00924
00925 if (this.options.Box == 11) {
00926 material = new THREE.MeshPhongMaterial({ color : fillcolor.getHex(), specular : 0x4f4f4f });
00927
00928 geom = new THREE.SphereGeometry(0.5);
00929 geom.applyMatrix( new THREE.Matrix4().makeRotationX( Math.PI / 2 ) );
00930
00931 } else {
00932 material = new THREE.MeshLambertMaterial({ color : fillcolor.getHex() });
00933
00934 geom = new THREE.BoxGeometry(1, 1, 1);
00935 }
00936
00937 var histo = this.GetObject(),
00938 i1 = this.GetSelectIndex("x", "left", 0),
00939 i2 = this.GetSelectIndex("x", "right", 0),
00940 j1 = this.GetSelectIndex("y", "left", 0),
00941 j2 = this.GetSelectIndex("y", "right", 0),
00942 k1 = this.GetSelectIndex("z", "left", 0),
00943 k2 = this.GetSelectIndex("z", "right", 0),
00944 name = this.GetTipName("<br/>");
00945
00946 var scalex = (this.tx(this.GetBinX(i2+0.5)) - this.tx(this.GetBinX(i1+0.5))) / (i2-i1),
00947 scaley = (this.ty(this.GetBinY(j2+0.5)) - this.ty(this.GetBinY(j1+0.5))) / (j2-j1),
00948 scalez = (this.tz(this.GetBinZ(k2+0.5)) - this.tz(this.GetBinZ(k1+0.5))) / (k2-k1);
00949
00950 for (var i = i1; i < i2; ++i) {
00951 var binx = this.GetBinX(i+0.5), grx = this.tx(binx);
00952 for (var j = j1; j < j2; ++j) {
00953 var biny = this.GetBinY(j+0.5), gry = this.ty(biny);
00954 for (var k = k1; k < k2; ++k) {
00955 var bin_content = histo.getBinContent(i+1, j+1, k+1);
00956 if (bin_content <= this.gminbin) continue;
00957
00958 var wei = (this.options.Color > 0) ? 1. : bin_content / this.gmaxbin;
00959
00960 if (wei < 1e-5) continue;
00961
00962 var binz = this.GetBinZ(k+0.5), grz = this.tz(binz);
00963
00964 var bin = new THREE.Mesh(geom, material.clone());
00965
00966 bin.position.set( grx, gry, grz );
00967
00968 bin.scale.set(scalex*wei, scaley*wei, scalez*wei);
00969
00970 if (JSROOT.gStyle.Tooltip > 0)
00971 bin.name = name + 'x=' + JSROOT.FFormat(binx,"6.4g") + ' bin=' + (i+1) + '<br/>'
00972 + 'y=' + JSROOT.FFormat(biny,"6.4g") + ' bin=' + (j+1) + '<br/>'
00973 + 'z=' + JSROOT.FFormat(binz,"6.4g") + ' bin=' + (k+1) + '<br/>'
00974 + 'entries=' + JSROOT.FFormat(bin_content, "7.0g");
00975
00976 this.toplevel.add(bin);
00977
00978 if (this.options.Box !== 11) {
00979 var helper = new THREE.BoxHelper(bin);
00980 helper.material.color.set(0x000000);
00981 helper.material.linewidth = 1.0;
00982 this.toplevel.add(helper)
00983 }
00984 }
00985 }
00986 }
00987 }
00988
00989 JSROOT.TH3Painter.prototype.Redraw = function(resize) {
00990 if (resize) {
00991 this.Resize3D();
00992 } else {
00993 this.Create3DScene();
00994 this.DrawXYZ();
00995 this.Draw3DBins();
00996 this.Render3D();
00997 }
00998 }
00999
01000 JSROOT.TH3Painter.prototype.CheckResize = function(size) {
01001 var pad_painter = this.pad_painter();
01002
01003 var changed = true;
01004
01005
01006
01007 if (pad_painter)
01008 changed = pad_painter.CheckCanvasResize(size, JSROOT.browser.isFirefox ? false : true);
01009
01010 if (changed) this.Resize3D(size);
01011 }
01012
01013 JSROOT.TH3Painter.prototype.FillToolbar = function() {
01014 var pp = this.pad_painter(true);
01015 if (pp===null) return;
01016
01017 pp.AddButton(JSROOT.ToolbarIcons.undo, 'Unzoom all axes', 'UnzoomAllAxis');
01018 if (this.draw_content)
01019 pp.AddButton(JSROOT.ToolbarIcons.statbox, 'Toggle stat box', "ToggleStatBox");
01020 }
01021
01022 JSROOT.TH3Painter.prototype.FillHistContextMenu = function(menu) {
01023 if (!this.draw_content) return;
01024
01025 menu.addDrawMenu("Draw with", ["box", "box1"], function(arg) {
01026 this.options = this.DecodeOptions(arg);
01027 this.Redraw();
01028 });
01029 }
01030
01031
01032 JSROOT.Painter.drawHistogram3D = function(divid, histo, opt) {
01033
01034
01035
01036 JSROOT.extend(this, new JSROOT.TH3Painter(histo));
01037
01038 this.SetDivId(divid, 4);
01039
01040 this.options = this.DecodeOptions(opt);
01041
01042 this.CheckPadOptions();
01043
01044 this.ScanContent();
01045
01046 this.Redraw();
01047
01048 this.DrawTitle();
01049
01050 if (JSROOT.gStyle.AutoStat && this.create_canvas) {
01051 var stats = this.CreateStat();
01052 if (stats) JSROOT.draw(this.divid, stats, "");
01053 }
01054
01055 this.FillToolbar();
01056
01057 return this.DrawingReady();
01058 }
01059
01060
01061
01062 JSROOT.Painter.drawPolyMarker3D = function(divid, poly, opt) {
01063
01064
01065 this.SetDivId(divid);
01066
01067 var main = this.main_painter();
01068
01069 if ((main == null) || !('renderer' in main)) return this.DrawingReady();
01070
01071 var cnt = poly.fP.length;
01072 var step = 3;
01073
01074 if ((JSROOT.gStyle.OptimizeDraw > 0) && (cnt > 1000*3)) {
01075 step = Math.floor(cnt / 1000 / 3) * 3;
01076 if (step <= 6) step = 6;
01077 }
01078
01079 var fcolor = d3.rgb(JSROOT.Painter.root_colors[poly.fMarkerColor]);
01080 var fillcolor = new THREE.Color(0xDDDDDD);
01081 fillcolor.setRGB(fcolor.r / 255, fcolor.g / 255, fcolor.b / 255);
01082
01083 var material = new THREE.MeshPhongMaterial({ color : fillcolor.getHex(), specular : 0x4f4f4f});
01084
01085
01086 var geom = new THREE.BoxGeometry(1, 1, 1);
01087
01088 for (var n=0; n<cnt; n+=step) {
01089 var bin = new THREE.Mesh(geom, material.clone());
01090 bin.position.set( main.tx(poly.fP[n]), main.ty(poly.fP[n+1]), main.tz(poly.fP[n+2]) );
01091 bin.name = (poly.fName !== "TPolyMarker3D") ? (poly.fName + ": ") : ("bin " + n/3 + ": ");
01092 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]);
01093 main.toplevel.add(bin);
01094 }
01095
01096 main.Render3D();
01097
01098 return this.DrawingReady();
01099 }
01100
01101 return JSROOT.Painter;
01102
01103 }));
01104