artdaq_node_server  v1_01_00
JSRoot3DPainter.js
Go to the documentation of this file.
1 
4 (function() {
5 
6  if (typeof JSROOT != 'object') {
7  var e1 = new Error('JSROOT is not defined');
8  e1.source = 'JSRoot3DPainter.js';
9  throw e1;
10  }
11 
12  if (typeof d3 != 'object') {
13  var e1 = new Error('This extension requires d3.v3.js');
14  e1.source = 'JSRoot3DPainter.js';
15  throw e1;
16  }
17 
18  if (typeof JSROOT.Painter != 'object') {
19  var e1 = new Error('JSROOT.Painter is not defined');
20  e1.source = 'JSRoot3DPainter.js';
21  throw e1;
22  }
23 
24  JSROOT.Painter.add3DInteraction = function(renderer, scene, camera, toplevel, painter) {
25  // add 3D mouse interactive functions
26  var mouseX, mouseY, mouseDowned = false;
27  var mouse = { x : 0, y : 0 }, INTERSECTED;
28 
29  var tooltip = function() {
30  var id = 'tt';
31  var top = 3;
32  var left = 3;
33  var maxw = 150;
34  var speed = 10;
35  var timer = 20;
36  var endalpha = 95;
37  var alpha = 0;
38  var tt, t, c, b, h;
39  var ie = document.all ? true : false;
40  return {
41  show : function(v, w) {
42  if (tt == null) {
43  tt = document.createElement('div');
44  tt.setAttribute('id', id);
45  t = document.createElement('div');
46  t.setAttribute('id', id + 'top');
47  c = document.createElement('div');
48  c.setAttribute('id', id + 'cont');
49  b = document.createElement('div');
50  b.setAttribute('id', id + 'bot');
51  tt.appendChild(t);
52  tt.appendChild(c);
53  tt.appendChild(b);
54  document.body.appendChild(tt);
55  tt.style.opacity = 0;
56  tt.style.filter = 'alpha(opacity=0)';
57  document.onmousemove = this.pos;
58  }
59  tt.style.display = 'block';
60  c.innerHTML = v;
61  tt.style.width = w ? w + 'px' : 'auto';
62  tt.style.width = 'auto'; // let it be automatically resizing...
63  if (!w && ie) {
64  t.style.display = 'none';
65  b.style.display = 'none';
66  tt.style.width = tt.offsetWidth;
67  t.style.display = 'block';
68  b.style.display = 'block';
69  }
70  // if (tt.offsetWidth > maxw) { tt.style.width = maxw + 'px'; }
71  h = parseInt(tt.offsetHeight) + top;
72  clearInterval(tt.timer);
73  tt.timer = setInterval(function() { tooltip.fade(1) }, timer);
74  },
75  pos : function(e) {
76  var u = ie ? event.clientY + document.documentElement.scrollTop : e.pageY;
77  var l = ie ? event.clientX + document.documentElement.scrollLeft : e.pageX;
78  tt.style.top = u + 15 + 'px';// (u - h) + 'px';
79  tt.style.left = (l + left) + 'px';
80  },
81  fade : function(d) {
82  var a = alpha;
83  if ((a != endalpha && d == 1) || (a != 0 && d == -1)) {
84  var i = speed;
85  if (endalpha - a < speed && d == 1) {
86  i = endalpha - a;
87  } else if (alpha < speed && d == -1) {
88  i = a;
89  }
90  alpha = a + (i * d);
91  tt.style.opacity = alpha * .01;
92  tt.style.filter = 'alpha(opacity=' + alpha + ')';
93  } else {
94  clearInterval(tt.timer);
95  if (d == -1) {
96  tt.style.display = 'none';
97  }
98  }
99  },
100  hide : function() {
101  if (tt == null)
102  return;
103  clearInterval(tt.timer);
104  tt.timer = setInterval(function() {
105  tooltip.fade(-1);
106  }, timer);
107  }
108  };
109  }();
110 
111  var radius = 100;
112  var theta = 0;
113  var projector = new THREE.Projector();
114  function findIntersection() {
115  // find intersections
116  if (mouseDowned) {
117  if (INTERSECTED) {
118  INTERSECTED.material.emissive.setHex(INTERSECTED.currentHex);
119  renderer.render(scene, camera);
120  }
121  INTERSECTED = null;
122  if (JSROOT.gStyle.Tooltip)
123  tooltip.hide();
124  return;
125  }
126  var vector = new THREE.Vector3(mouse.x, mouse.y, 1);
127  projector.unprojectVector(vector, camera);
128  var raycaster = new THREE.Raycaster(camera.position, vector.sub(
129  camera.position).normalize());
130  var intersects = raycaster.intersectObjects(scene.children, true);
131  if (intersects.length > 0) {
132  var pick = null;
133  for (var i = 0; i < intersects.length; ++i) {
134  if ('emissive' in intersects[i].object.material) {
135  pick = intersects[i];
136  break;
137  }
138  }
139  if (pick && INTERSECTED != pick.object) {
140  if (INTERSECTED)
141  INTERSECTED.material.emissive.setHex(INTERSECTED.currentHex);
142  INTERSECTED = pick.object;
143  INTERSECTED.currentHex = INTERSECTED.material.emissive.getHex();
144  INTERSECTED.material.emissive.setHex(0x5f5f5f);
145  renderer.render(scene, camera);
146  if (JSROOT.gStyle.Tooltip)
147  tooltip.show(INTERSECTED.name.length > 0 ? INTERSECTED.name
148  : INTERSECTED.parent.name, 200);
149  }
150  } else {
151  if (INTERSECTED) {
152  INTERSECTED.material.emissive.setHex(INTERSECTED.currentHex);
153  renderer.render(scene, camera);
154  }
155  INTERSECTED = null;
156  if (JSROOT.gStyle.Tooltip)
157  tooltip.hide();
158  }
159  }
160  ;
161 
162  $(renderer.domElement).on('touchstart mousedown', function(e) {
163  // var touch = e.changedTouches[0] || {};
164  if (JSROOT.gStyle.Tooltip)
165  tooltip.hide();
166  e.preventDefault();
167  var touch = e;
168  if ('changedTouches' in e)
169  touch = e.changedTouches[0];
170  else if ('touches' in e)
171  touch = e.touches[0];
172  else if ('originalEvent' in e) {
173  if ('changedTouches' in e.originalEvent)
174  touch = e.originalEvent.changedTouches[0];
175  else if ('touches' in e.originalEvent)
176  touch = e.originalEvent.touches[0];
177  }
178  mouseX = touch.pageX;
179  mouseY = touch.pageY;
180  mouseDowned = true;
181  });
182  $(renderer.domElement).on('touchmove mousemove', function(e) {
183  if (mouseDowned) {
184  var touch = e;
185  if ('changedTouches' in e)
186  touch = e.changedTouches[0];
187  else if ('touches' in e)
188  touch = e.touches[0];
189  else if ('originalEvent' in e) {
190  if ('changedTouches' in e.originalEvent)
191  touch = e.originalEvent.changedTouches[0];
192  else if ('touches' in e.originalEvent)
193  touch = e.originalEvent.touches[0];
194  }
195  var moveX = touch.pageX - mouseX;
196  var moveY = touch.pageY - mouseY;
197  // limited X rotate in -45 to 135 deg
198  if ((moveY > 0 && toplevel.rotation.x < Math.PI * 3 / 4)
199  || (moveY < 0 && toplevel.rotation.x > -Math.PI / 4)) {
200  toplevel.rotation.x += moveY * 0.02;
201  }
202  toplevel.rotation.y += moveX * 0.02;
203  renderer.render(scene, camera);
204  mouseX = touch.pageX;
205  mouseY = touch.pageY;
206  } else {
207  e.preventDefault();
208  var mouse_x = 'offsetX' in e.originalEvent ? e.originalEvent.offsetX : e.originalEvent.layerX;
209  var mouse_y = 'offsetY' in e.originalEvent ? e.originalEvent.offsetY : e.originalEvent.layerY;
210  mouse.x = (mouse_x / renderer.domElement.width) * 2 - 1;
211  mouse.y = -(mouse_y / renderer.domElement.height) * 2 + 1;
212  // enable picking once tootips are available...
213  findIntersection();
214  }
215  });
216  $(renderer.domElement).on('touchend mouseup', function(e) {
217  mouseDowned = false;
218  });
219 
220  $(renderer.domElement).on('mousewheel', function(e, d) {
221  e.preventDefault();
222  camera.position.z += d * 20;
223  renderer.render(scene, camera);
224  });
225 
226  $(renderer.domElement).on('contextmenu', function(e) {
227 
228  e.preventDefault();
229 
230  if (JSROOT.gStyle.Tooltip) tooltip.hide();
231 
232  var menu = JSROOT.Painter.createmenu(e.originalEvent);
233 
234  var item = JSROOT.gStyle.Tooltip ? "Disable tooltip" : "Enable tooltip";
235 
236  JSROOT.Painter.menuitem(menu, item, function() {
237  JSROOT.gStyle.Tooltip = !JSROOT.gStyle.Tooltip;
238  tooltip.hide();
239  });
240 
241  if (painter)
242  JSROOT.Painter.menuitem(menu, "Switch to 2D", function() {
243  $(painter.svg_pad()).show().parent().find(renderer.domElement).remove();
244  tooltip.hide();
245  painter.Draw2D();
246  });
247  JSROOT.Painter.menuitem(menu, "Close", function() { });
248 
249  });
250  }
251 
252  JSROOT.Painter.real_drawHistogram2D = function(painter) {
253 
254  var w = Number(painter.svg_pad(true).attr("width")),
255  h = Number(painter.svg_pad(true).attr("height")), size = 100;
256 
257  var xmin = painter.xmin, xmax = painter.xmax;
258  if (painter.zoom_xmin != painter.zoom_xmax) {
259  xmin = painter.zoom_xmin;
260  xmax = painter.zoom_xmax;
261  }
262  var ymin = painter.ymin, ymax = painter.ymax;
263  if (painter.zoom_ymin != painter.zoom_ymax) {
264  ymin = painter.zoom_ymin;
265  ymax = painter.zoom_ymax;
266  }
267 
268  var tx, utx, ty, uty, tz, utz;
269 
270  if (painter.options.Logx) {
271  tx = d3.scale.log().domain([ xmin, xmax ]).range([ -size, size ]);
272  utx = d3.scale.log().domain([ -size, size ]).range([ xmin, xmax ]);
273  } else {
274  tx = d3.scale.linear().domain([ xmin, xmax ]).range([ -size, size ]);
275  utx = d3.scale.linear().domain([ -size, size ]).range([ xmin, xmax ]);
276  }
277  if (painter.options.Logy) {
278  ty = d3.scale.log().domain([ ymin, ymax ]).range([ -size, size ]);
279  uty = d3.scale.log().domain([ size, -size ]).range([ ymin, ymax ]);
280  } else {
281  ty = d3.scale.linear().domain([ ymin, ymax ]).range([ -size, size ]);
282  uty = d3.scale.linear().domain([ size, -size ]).range([ ymin, ymax ]);
283  }
284  if (painter.options.Logz) {
285  tz = d3.scale.log().domain([ painter.gminbin, Math.ceil(painter.gmaxbin / 100) * 105 ]).range([ 0, size * 2 ]);
286  utz = d3.scale.log().domain([ 0, size * 2 ]).range([ painter.gminbin, Math.ceil(painter.gmaxbin / 100) * 105 ]);
287  } else {
288  tz = d3.scale.linear().domain([ painter.gminbin, Math.ceil(painter.gmaxbin / 100) * 105 ]).range( [ 0, size * 2 ]);
289  utz = d3.scale.linear().domain([ 0, size * 2 ]).range( [ painter.gminbin, Math.ceil(painter.gmaxbin / 100) * 105 ]);
290  }
291 
292  var constx = (size * 2 / painter.nbinsx) / painter.gmaxbin;
293  var consty = (size * 2 / painter.nbinsy) / painter.gmaxbin;
294 
295  var colorFlag = (painter.options.Color > 0);
296  var fcolor = d3.rgb(JSROOT.Painter.root_colors[painter.histo['fFillColor']]);
297 
298  var local_bins = painter.CreateDrawBins(100, 100, 2, (JSROOT.gStyle.Tooltip ? 1 : 0));
299 
300  // three.js 3D drawing
301  var scene = new THREE.Scene();
302 
303  var toplevel = new THREE.Object3D();
304  toplevel.rotation.x = 30 * Math.PI / 180;
305  toplevel.rotation.y = 30 * Math.PI / 180;
306  scene.add(toplevel);
307 
308  var wireMaterial = new THREE.MeshBasicMaterial({
309  color : 0x000000,
310  wireframe : true,
311  wireframeLinewidth : 0.5,
312  side : THREE.DoubleSide
313  });
314 
315  // create a new mesh with cube geometry
316  var cube = new THREE.Mesh(new THREE.BoxGeometry(size * 2, size * 2, size * 2), wireMaterial);
317  //cube.position.y = size;
318 
319  var helper = new THREE.BoxHelper(cube);
320  helper.material.color.set(0x000000);
321 
322  var box = new THREE.Object3D();
323  box.add(helper);
324  box.position.y = size;
325 
326  // add the cube to the scene
327  toplevel.add(box);
328 
329  var textMaterial = new THREE.MeshBasicMaterial({ color : 0x000000 });
330 
331  // add the calibration vectors and texts
332  var geometry = new THREE.Geometry();
333  var imax, istep, len = 3, plen, sin45 = Math.sin(45);
334  var text3d, text;
335  var xmajors = tx.ticks(8);
336  var xminors = tx.ticks(50);
337  for (var i = -size, j = 0, k = 0; i < size; ++i) {
338  var is_major = (utx(i) <= xmajors[j] && utx(i + 1) > xmajors[j]) ? true : false;
339  var is_minor = (utx(i) <= xminors[k] && utx(i + 1) > xminors[k]) ? true : false;
340  plen = (is_major ? len + 2 : len) * sin45;
341  if (is_major) {
342  text3d = new THREE.TextGeometry(xmajors[j], { size : 7, height : 0, curveSegments : 10 });
343  ++j;
344 
345  text3d.computeBoundingBox();
346  var centerOffset = 0.5 * (text3d.boundingBox.max.x - text3d.boundingBox.min.x);
347 
348  text = new THREE.Mesh(text3d, textMaterial);
349  text.position.set(i - centerOffset, -13, size + plen);
350  toplevel.add(text);
351 
352  text = new THREE.Mesh(text3d, textMaterial);
353  text.position.set(i + centerOffset, -13, -size - plen);
354  text.rotation.y = Math.PI;
355  toplevel.add(text);
356  }
357  if (is_major || is_minor) {
358  ++k;
359  geometry.vertices.push(new THREE.Vector3(i, 0, size));
360  geometry.vertices.push(new THREE.Vector3(i, -plen, size + plen));
361  geometry.vertices.push(new THREE.Vector3(i, 0, -size));
362  geometry.vertices.push(new THREE.Vector3(i, -plen, -size - plen));
363  }
364  }
365  var ymajors = ty.ticks(8);
366  var yminors = ty.ticks(50);
367  for (var i = size, j = 0, k = 0; i > -size; --i) {
368  var is_major = (uty(i) <= ymajors[j] && uty(i - 1) > ymajors[j]) ? true : false;
369  var is_minor = (uty(i) <= yminors[k] && uty(i - 1) > yminors[k]) ? true : false;
370  plen = (is_major ? len + 2 : len) * sin45;
371  if (is_major) {
372  text3d = new THREE.TextGeometry(ymajors[j], { size : 7, height : 0, curveSegments : 10 });
373  ++j;
374 
375  text3d.computeBoundingBox();
376  var centerOffset = 0.5 * (text3d.boundingBox.max.x - text3d.boundingBox.min.x);
377 
378  text = new THREE.Mesh(text3d, textMaterial);
379  text.position.set(size + plen, -13, i + centerOffset);
380  text.rotation.y = Math.PI / 2;
381  toplevel.add(text);
382 
383  text = new THREE.Mesh(text3d, textMaterial);
384  text.position.set(-size - plen, -13, i - centerOffset);
385  text.rotation.y = -Math.PI / 2;
386  toplevel.add(text);
387  }
388  if (is_major || is_minor) {
389  ++k;
390  geometry.vertices.push(new THREE.Vector3(size, 0, i));
391  geometry.vertices.push(new THREE.Vector3(size + plen, -plen, i));
392  geometry.vertices.push(new THREE.Vector3(-size, 0, i));
393  geometry.vertices.push(new THREE.Vector3(-size - plen, -plen, i));
394  }
395  }
396  var zmajors = tz.ticks(8);
397  var zminors = tz.ticks(50);
398  for (var i = 0, j = 0, k = 0; i < (size * 2); ++i) {
399  var is_major = (utz(i) <= zmajors[j] && utz(i + 1) > zmajors[j]) ? true : false;
400  var is_minor = (utz(i) <= zminors[k] && utz(i + 1) > zminors[k]) ? true : false;
401  plen = (is_major ? len + 2 : len) * sin45;
402  if (is_major) {
403  text3d = new THREE.TextGeometry(zmajors[j], { size : 7, height : 0, curveSegments : 10 });
404  ++j;
405 
406  text3d.computeBoundingBox();
407  var offset = 0.8 * (text3d.boundingBox.max.x - text3d.boundingBox.min.x);
408 
409  text = new THREE.Mesh(text3d, textMaterial);
410  text.position.set(size + offset + 5, i - 2.5, size + offset + 5);
411  text.rotation.y = Math.PI * 3 / 4;
412  toplevel.add(text);
413 
414  text = new THREE.Mesh(text3d, textMaterial);
415  text.position.set(size + offset + 5, i - 2.5, -size - offset - 5);
416  text.rotation.y = -Math.PI * 3 / 4;
417  toplevel.add(text);
418 
419  text = new THREE.Mesh(text3d, textMaterial);
420  text.position.set(-size - offset - 5, i - 2.5, size + offset + 5);
421  text.rotation.y = Math.PI / 4;
422  toplevel.add(text);
423 
424  text = new THREE.Mesh(text3d, textMaterial);
425  text.position.set(-size - offset - 5, i - 2.5, -size - offset - 5);
426  text.rotation.y = -Math.PI / 4;
427  toplevel.add(text);
428  }
429  if (is_major || is_minor) {
430  ++k;
431  geometry.vertices.push(new THREE.Vector3(size, i, size));
432  geometry.vertices.push(new THREE.Vector3(size + plen, i, size + plen));
433  geometry.vertices.push(new THREE.Vector3(size, i, -size));
434  geometry.vertices.push(new THREE.Vector3(size + plen, i, -size - plen));
435  geometry.vertices.push(new THREE.Vector3(-size, i, size));
436  geometry.vertices.push(new THREE.Vector3(-size - plen, i, size + plen));
437  geometry.vertices.push(new THREE.Vector3(-size, i, -size));
438  geometry.vertices.push(new THREE.Vector3(-size - plen, i, -size - plen));
439  }
440  }
441 
442  // add the calibration lines
443  var lineMaterial = new THREE.LineBasicMaterial({ color : 0x000000 });
444  var line = new THREE.Line(geometry, lineMaterial);
445  line.type = THREE.LinePieces;
446  toplevel.add(line);
447 
448  // create the bin cubes
449 
450  var fillcolor = new THREE.Color(0xDDDDDD);
451  fillcolor.setRGB(fcolor.r / 255, fcolor.g / 255, fcolor.b / 255);
452  var bin, wei, hh;
453 
454  for (var i = 0; i < local_bins.length; ++i) {
455  hh = local_bins[i];
456  wei = tz(hh.z);
457 
458  bin = THREE.SceneUtils.createMultiMaterialObject(
459  new THREE.BoxGeometry(2 * size / painter.nbinsx, wei, 2 * size / painter.nbinsy),
460  [ new THREE.MeshLambertMaterial({ color : fillcolor.getHex(), shading : THREE.NoShading }), wireMaterial ]);
461  bin.position.x = tx(hh.x);
462  bin.position.y = wei / 2;
463  bin.position.z = -(ty(hh.y));
464 
465  if (JSROOT.gStyle.Tooltip)
466  bin.name = hh.tip;
467  toplevel.add(bin);
468  }
469 
470  delete local_bins;
471  local_bins = null;
472 
473  // create a point light
474  var pointLight = new THREE.PointLight(0xcfcfcf);
475  pointLight.position.set(0, 50, 250);
476  scene.add(pointLight);
477 
478  // var directionalLight = new THREE.DirectionalLight(
479  // 0x7f7f7f );
480  // directionalLight.position.set( 0, -70, 100
481  // ).normalize();
482  // scene.add( directionalLight );
483 
484  var camera = new THREE.PerspectiveCamera(45, w / h, 1, 1000);
485  camera.position.set(0, size / 2, 500);
486  camera.lookat = cube;
487 
492  var Detector = {
493  canvas : !!window.CanvasRenderingContext2D,
494  webgl : (function() { try {
495  return !!window.WebGLRenderingContext && !!document.createElement('canvas').getContext('experimental-webgl');
496  } catch (e) {
497  return false;
498  }
499  })(),
500  workers : !!window.Worker,
501  fileapi : window.File && window.FileReader && window.FileList && window.Blob
502  };
503 
504  var renderer = Detector.webgl ? new THREE.WebGLRenderer({ antialias : true }) :
505  new THREE.CanvasRenderer({ antialias : true });
506  renderer.setClearColor(0xffffff, 1);
507  renderer.setSize(w, h);
508  $(painter.svg_pad()).hide().parent().append(renderer.domElement);
509  renderer.render(scene, camera);
510 
511  JSROOT.Painter.add3DInteraction(renderer, scene, camera, toplevel, painter);
512  }
513 
514  JSROOT.Painter.real_drawHistogram3D = function(divid, histo, opt) {
515 
516  var logx = false, logy = false, logz = false, gridx = false, gridy = false, gridz = false;
517 
518  var painter = new JSROOT.TObjectPainter();
519  painter.SetDivId(divid, -1);
520  var pad = painter.root_pad();
521 
522  var render_to;
523  if (painter.svg_pad())
524  render_to = $(painter.svg_pad()).hide().parent();
525  else
526  render_to = $("#" + divid);
527 
528  var opt = histo['fOption'].toLowerCase();
529  // if (opt=="") opt = "colz";
530 
531  if (pad) {
532  logx = pad['fLogx'];
533  logy = pad['fLogy'];
534  logz = pad['fLogz'];
535  gridx = pad['fGridx'];
536  gridy = pad['fGridy'];
537  gridz = pad['fGridz'];
538  }
539 
540  var fillcolor = JSROOT.Painter.root_colors[histo['fFillColor']];
541  var linecolor = JSROOT.Painter.root_colors[histo['fLineColor']];
542  if (histo['fFillColor'] == 0) {
543  fillcolor = '#4572A7';
544  }
545  if (histo['fLineColor'] == 0) {
546  linecolor = '#4572A7';
547  }
548  var nbinsx = histo['fXaxis']['fNbins'];
549  var nbinsy = histo['fYaxis']['fNbins'];
550  var nbinsz = histo['fZaxis']['fNbins'];
551  var scalex = (histo['fXaxis']['fXmax'] - histo['fXaxis']['fXmin']) / histo['fXaxis']['fNbins'];
552  var scaley = (histo['fYaxis']['fXmax'] - histo['fYaxis']['fXmin']) / histo['fYaxis']['fNbins'];
553  var scalez = (histo['fZaxis']['fXmax'] - histo['fZaxis']['fXmin']) / histo['fZaxis']['fNbins'];
554  var maxbin = -1e32, minbin = 1e32;
555  maxbin = d3.max(histo['fArray']);
556  minbin = d3.min(histo['fArray']);
557  var bins = new Array();
558  for (var i = 0; i <= nbinsx + 2; ++i) {
559  for (var j = 0; j < nbinsy + 2; ++j) {
560  for (var k = 0; k < nbinsz + 2; ++k) {
561  var bin_content = histo.getBinContent(i, j, k);
562  if (bin_content > minbin) {
563  var point = {
564  x : histo['fXaxis']['fXmin'] + (i * scalex),
565  y : histo['fYaxis']['fXmin'] + (j * scaley),
566  z : histo['fZaxis']['fXmin'] + (k * scalez),
567  n : bin_content
568  };
569  bins.push(point);
570  }
571  }
572  }
573  }
574  var w = render_to.width(), h = render_to.height(), size = 100;
575  if (h<10) { render_to.height(0.66*w); h = render_to.height(); }
576 
577  if (logx) {
578  var tx = d3.scale.log().domain([ histo['fXaxis']['fXmin'], histo['fXaxis']['fXmax'] ]).range( [ -size, size ]);
579  var utx = d3.scale.log().domain([ -size, size ]).range([ histo['fXaxis']['fXmin'], histo['fXaxis']['fXmax'] ]);
580  } else {
581  var tx = d3.scale.linear().domain( [ histo['fXaxis']['fXmin'], histo['fXaxis']['fXmax'] ]).range( [ -size, size ]);
582  var utx = d3.scale.linear().domain([ -size, size ]).range([ histo['fXaxis']['fXmin'], histo['fXaxis']['fXmax'] ]);
583  }
584  if (logy) {
585  var ty = d3.scale.log().domain([ histo['fYaxis']['fXmin'], histo['fYaxis']['fXmax'] ]).range( [ -size, size ]);
586  var uty = d3.scale.log().domain([ size, -size ]).range([ histo['fYaxis']['fXmin'], histo['fYaxis']['fXmax'] ]);
587  } else {
588  var ty = d3.scale.linear().domain( [ histo['fYaxis']['fXmin'], histo['fYaxis']['fXmax'] ]).range([ -size, size ]);
589  var uty = d3.scale.linear().domain([ size, -size ]).range([ histo['fYaxis']['fXmin'], histo['fYaxis']['fXmax'] ]);
590  }
591  if (logz) {
592  var tz = d3.scale.log().domain([ histo['fZaxis']['fXmin'], histo['fZaxis']['fXmax'] ]).range([ -size, size ]);
593  var utz = d3.scale.log().domain([ -size, size ]).range([ histo['fZaxis']['fXmin'], histo['fZaxis']['fXmax'] ]);
594  } else {
595  var tz = d3.scale.linear().domain([ histo['fZaxis']['fXmin'], histo['fZaxis']['fXmax'] ]).range([ -size, size ]);
596  var utz = d3.scale.linear().domain([ -size, size ]).range([ histo['fZaxis']['fXmin'], histo['fZaxis']['fXmax'] ]);
597  }
598 
599  // three.js 3D drawing
600  var scene = new THREE.Scene();
601 
602  var toplevel = new THREE.Object3D();
603  toplevel.rotation.x = 30 * Math.PI / 180;
604  toplevel.rotation.y = 30 * Math.PI / 180;
605  scene.add(toplevel);
606 
607  var wireMaterial = new THREE.MeshBasicMaterial({
608  color : 0x000000,
609  wireframe : true,
610  wireframeLinewidth : 0.5,
611  side : THREE.DoubleSide
612  });
613 
614  // create a new mesh with cube geometry
615  var cube = new THREE.Mesh(new THREE.BoxGeometry(size * 2, size * 2, size * 2), wireMaterial);
616 
617  var helper = new THREE.BoxHelper(cube);
618  helper.material.color.set(0x000000);
619 
620  // add the cube to the scene
621  toplevel.add(helper);
622 
623  var textMaterial = new THREE.MeshBasicMaterial({ color : 0x000000 });
624 
625  // add the calibration vectors and texts
626  var geometry = new THREE.Geometry();
627  var imax, istep, len = 3, plen, sin45 = Math.sin(45);
628  var text3d, text;
629  var xmajors = tx.ticks(5);
630  var xminors = tx.ticks(25);
631  for (var i = -size, j = 0, k = 0; i <= size; ++i) {
632  var is_major = (utx(i) <= xmajors[j] && utx(i + 1) > xmajors[j]) ? true : false;
633  var is_minor = (utx(i) <= xminors[k] && utx(i + 1) > xminors[k]) ? true : false;
634  plen = (is_major ? len + 2 : len) * sin45;
635  if (is_major) {
636  text3d = new THREE.TextGeometry(xmajors[j], { size : 7, height : 0, curveSegments : 10 });
637  ++j;
638 
639  text3d.computeBoundingBox();
640  var centerOffset = 0.5 * (text3d.boundingBox.max.x - text3d.boundingBox.min.x);
641 
642  text = new THREE.Mesh(text3d, textMaterial);
643  text.position.set(i - centerOffset, -size - 13, size + plen);
644  toplevel.add(text);
645 
646  text = new THREE.Mesh(text3d, textMaterial);
647  text.position.set(i + centerOffset, -size - 13, -size - plen);
648  text.rotation.y = Math.PI;
649  toplevel.add(text);
650  }
651  if (is_major || is_minor) {
652  ++k;
653  geometry.vertices.push(new THREE.Vector3(i, -size, size));
654  geometry.vertices.push(new THREE.Vector3(i, -size - plen, size + plen));
655  geometry.vertices.push(new THREE.Vector3(i, -size, -size));
656  geometry.vertices.push(new THREE.Vector3(i, -size - plen, -size - plen));
657  }
658  }
659  var ymajors = ty.ticks(5);
660  var yminors = ty.ticks(25);
661  for (var i = size, j = 0, k = 0; i > -size; --i) {
662  var is_major = (uty(i) <= ymajors[j] && uty(i - 1) > ymajors[j]) ? true : false;
663  var is_minor = (uty(i) <= yminors[k] && uty(i - 1) > yminors[k]) ? true : false;
664  plen = (is_major ? len + 2 : len) * sin45;
665  if (is_major) {
666  text3d = new THREE.TextGeometry(ymajors[j], { size : 7, height : 0, curveSegments : 10 });
667  ++j;
668 
669  text3d.computeBoundingBox();
670  var centerOffset = 0.5 * (text3d.boundingBox.max.x - text3d.boundingBox.min.x);
671 
672  text = new THREE.Mesh(text3d, textMaterial);
673  text.position.set(size + plen, -size - 13, i + centerOffset);
674  text.rotation.y = Math.PI / 2;
675  toplevel.add(text);
676 
677  text = new THREE.Mesh(text3d, textMaterial);
678  text.position.set(-size - plen, -size - 13, i - centerOffset);
679  text.rotation.y = -Math.PI / 2;
680  toplevel.add(text);
681  }
682  if (is_major || is_minor) {
683  ++k;
684  geometry.vertices.push(new THREE.Vector3(size, -size, i));
685  geometry.vertices.push(new THREE.Vector3(size + plen, -size - plen, i));
686  geometry.vertices.push(new THREE.Vector3(-size, -size, i));
687  geometry.vertices.push(new THREE.Vector3(-size - plen, -size - plen, i));
688  }
689  }
690  var zmajors = tz.ticks(5);
691  var zminors = tz.ticks(25);
692  for (var i = -size, j = 0, k = 0; i <= size; ++i) {
693  var is_major = (utz(i) <= zmajors[j] && utz(i + 1) > zmajors[j]) ? true : false;
694  var is_minor = (utz(i) <= zminors[k] && utz(i + 1) > zminors[k]) ? true : false;
695  plen = (is_major ? len + 2 : len) * sin45;
696  if (is_major) {
697  text3d = new THREE.TextGeometry(zmajors[j], { size : 7, height : 0, curveSegments : 10 });
698  ++j;
699 
700  text3d.computeBoundingBox();
701  var offset = 0.6 * (text3d.boundingBox.max.x - text3d.boundingBox.min.x);
702 
703  text = new THREE.Mesh(text3d, textMaterial);
704  text.position.set(size + offset + 7, i - 2.5, size + offset + 7);
705  text.rotation.y = Math.PI * 3 / 4;
706  toplevel.add(text);
707 
708  text = new THREE.Mesh(text3d, textMaterial);
709  text.position.set(size + offset + 7, i - 2.5, -size - offset - 7);
710  text.rotation.y = -Math.PI * 3 / 4;
711  toplevel.add(text);
712 
713  text = new THREE.Mesh(text3d, textMaterial);
714  text.position.set(-size - offset - 7, i - 2.5, size + offset + 7);
715  text.rotation.y = Math.PI / 4;
716  toplevel.add(text);
717 
718  text = new THREE.Mesh(text3d, textMaterial);
719  text.position.set(-size - offset - 7, i - 2.5, -size - offset - 7);
720  text.rotation.y = -Math.PI / 4;
721  toplevel.add(text);
722  }
723  if (is_major || is_minor) {
724  ++k;
725  geometry.vertices.push(new THREE.Vector3(size, i, size));
726  geometry.vertices.push(new THREE.Vector3(size + plen, i, size + plen));
727  geometry.vertices.push(new THREE.Vector3(size, i, -size));
728  geometry.vertices.push(new THREE.Vector3(size + plen, i, -size - plen));
729  geometry.vertices.push(new THREE.Vector3(-size, i, size));
730  geometry.vertices.push(new THREE.Vector3(-size - plen, i, size + plen));
731  geometry.vertices.push(new THREE.Vector3(-size, i, -size));
732  geometry.vertices.push(new THREE.Vector3(-size - plen, i, -size - plen));
733  }
734  }
735 
736  // add the calibration lines
737  var lineMaterial = new THREE.LineBasicMaterial({ color : 0x000000 });
738  var line = new THREE.Line(geometry, lineMaterial);
739  line.type = THREE.LinePieces;
740  toplevel.add(line);
741 
742  // create the bin cubes
743  var constx = (size * 2 / histo['fXaxis']['fNbins']) / maxbin;
744  var consty = (size * 2 / histo['fYaxis']['fNbins']) / maxbin;
745  var constz = (size * 2 / histo['fZaxis']['fNbins']) / maxbin;
746 
747  var optFlag = (opt.indexOf('colz') != -1 || opt.indexOf('col') != -1);
748  var fcolor = d3.rgb(JSROOT.Painter.root_colors[histo['fFillColor']]);
749  var fillcolor = new THREE.Color(0xDDDDDD);
750  fillcolor.setRGB(fcolor.r / 255, fcolor.g / 255, fcolor.b / 255);
751  var bin, wei;
752  for (var i = 0; i < bins.length; ++i) {
753  wei = (optFlag ? maxbin : bins[i].n);
754  if (opt.indexOf('box1') != -1) {
755  bin = new THREE.Mesh(new THREE.SphereGeometry(0.5 * wei * constx /* , 16, 16 */),
756  new THREE.MeshPhongMaterial({ color : fillcolor.getHex(), specular : 0xbfbfbf/* , shading: THREE.NoShading */}));
757  } else {
758  bin = THREE.SceneUtils.createMultiMaterialObject(
759  new THREE.BoxGeometry(wei * constx, wei * constz, wei * consty),
760  [ new THREE.MeshLambertMaterial({ color : fillcolor.getHex(), shading : THREE.NoShading }), wireMaterial ]);
761  }
762  bin.position.x = tx(bins[i].x - (scalex / 2));
763  bin.position.y = tz(bins[i].z - (scalez / 2));
764  bin.position.z = -(ty(bins[i].y - (scaley / 2)));
765  bin.name = "x: [" + bins[i].x.toPrecision(4) + ", "
766  + (bins[i].x + scalex).toPrecision(4) + "]<br/>"
767  + "y: [" + bins[i].y.toPrecision(4) + ", "
768  + (bins[i].y + scaley).toPrecision(4) + "]<br/>"
769  + "z: [" + bins[i].z.toPrecision(4) + ", "
770  + (bins[i].z + scalez).toPrecision(4) + "]<br/>"
771  + "entries: " + bins[i].n.toFixed();
772  toplevel.add(bin);
773  }
774  // create a point light
775  var pointLight = new THREE.PointLight(0xcfcfcf);
776  pointLight.position.set(0, 50, 250);
777  scene.add(pointLight);
778 
779  // var directionalLight = new THREE.DirectionalLight( 0x7f7f7f );
780  // directionalLight.position.set( 0, -70, 100).normalize();
781  // scene.add( directionalLight );
782 
783  var camera = new THREE.PerspectiveCamera(45, w / h, 1, 1000);
784  camera.position.set(0, 0, 500);
785  camera.lookat = cube;
786 
791  var Detector = {
792  canvas : !!window.CanvasRenderingContext2D,
793  webgl : (function() {
794  try {
795  return !!window.WebGLRenderingContext
796  && !!document.createElement('canvas')
797  .getContext('experimental-webgl');
798  } catch (e) {
799  return false;
800  }
801  })(),
802  workers : !!window.Worker,
803  fileapi : window.File && window.FileReader
804  && window.FileList && window.Blob
805  };
806 
807  var renderer = Detector.webgl ?
808  new THREE.WebGLRenderer({ antialias : true }) :
809  new THREE.CanvasRenderer({antialias : true });
810  renderer.setClearColor(0xffffff, 1);
811  renderer.setSize(w, h);
812  render_to.append(renderer.domElement);
813  renderer.render(scene, camera);
814 
815  JSROOT.Painter.add3DInteraction(renderer, scene, camera, toplevel, null);
816  }
817 
818 })();
819