otsdaq_utilities  v2_05_02_indev
JSRootPainter.more.js
1 
5 (function( factory ) {
6  if ( typeof define === "function" && define.amd ) {
7  define( ['JSRootPainter', 'd3', 'JSRootMath'], factory );
8  } else if (typeof exports === 'object' && typeof module !== 'undefined') {
9  factory(require("./JSRootCore.js"), require("d3"), require("./JSRootMath.js"));
10  } else {
11  if (typeof d3 != 'object')
12  throw new Error('This extension requires d3.js', 'JSRootPainter.more.js');
13  if (typeof JSROOT == 'undefined')
14  throw new Error('JSROOT is not defined', 'JSRootPainter.more.js');
15  if (typeof JSROOT.Painter != 'object')
16  throw new Error('JSROOT.Painter not defined', 'JSRootPainter.more.js');
17  factory(JSROOT, d3);
18  }
19 } (function(JSROOT, d3) {
20 
21  "use strict";
22 
23  JSROOT.sources.push("more2d");
24 
25  function drawText() {
26  var text = this.GetObject(),
27  w = this.pad_width(), h = this.pad_height(),
28  pos_x = text.fX, pos_y = text.fY,
29  tcolor = this.get_color(text.fTextColor),
30  use_frame = false,
31  fact = 1., textsize = text.fTextSize || 0.05,
32  main = this.frame_painter();
33 
34  if (text.TestBit(JSROOT.BIT(14))) {
35  // NDC coordinates
36  this.isndc = true;
37  } else if (main && !main.mode3d) {
38  // frame coordiantes
39  w = this.frame_width();
40  h = this.frame_height();
41  use_frame = "upper_layer";
42  } else if (this.root_pad() !== null) {
43  // force pad coordiantes
44  } else {
45  // place in the middle
46  this.isndc = true;
47  pos_x = pos_y = 0.5;
48  text.fTextAlign = 22;
49  if (!tcolor) tcolor = 'black';
50  }
51 
52  this.CreateG(use_frame);
53 
54  this.draw_g.attr("transform",null); // remove transofrm from interactive changes
55 
56  this.pos_x = this.AxisToSvg("x", pos_x, this.isndc);
57  this.pos_y = this.AxisToSvg("y", pos_y, this.isndc);
58 
59  var arg = { align: text.fTextAlign, x: this.pos_x, y: this.pos_y, text: text.fTitle, color: tcolor, latex: 0 };
60 
61  if (text.fTextAngle) arg.rotate = -text.fTextAngle;
62 
63  if (text._typename == 'TLatex') { arg.latex = 1; fact = 0.9; } else
64  if (text._typename == 'TMathText') { arg.latex = 2; fact = 0.8; }
65 
66  this.StartTextDrawing(text.fTextFont, Math.round((textsize>1) ? textsize : textsize*Math.min(w,h)*fact));
67 
68  this.DrawText(arg);
69 
70  this.FinishTextDrawing();
71 
72  this.pos_dx = this.pos_dy = 0;
73 
74  if (!this.moveDrag)
75  this.moveDrag = function(dx,dy) {
76  this.pos_dx += dx;
77  this.pos_dy += dy;
78  this.draw_g.attr("transform", "translate(" + this.pos_dx + "," + this.pos_dy + ")");
79  }
80 
81  if (!this.moveEnd)
82  this.moveEnd = function(not_changed) {
83  if (not_changed) return;
84  var text = this.GetObject();
85  text.fX = this.SvgToAxis("x", this.pos_x + this.pos_dx, this.isndc),
86  text.fY = this.SvgToAxis("y", this.pos_y + this.pos_dy, this.isndc);
87  this.WebCanvasExec("SetX(" + text.fX + ");;SetY(" + text.fY + ");;");
88  }
89 
90  this.AddMove();
91  }
92 
93  // =====================================================================================
94 
95  function drawLine() {
96 
97  var line = this.GetObject(),
98  lineatt = new JSROOT.TAttLineHandler(line),
99  kLineNDC = JSROOT.BIT(14),
100  isndc = line.TestBit(kLineNDC);
101 
102  // create svg:g container for line drawing
103  this.CreateG();
104 
105  this.draw_g
106  .append("svg:line")
107  .attr("x1", this.AxisToSvg("x", line.fX1, isndc))
108  .attr("y1", this.AxisToSvg("y", line.fY1, isndc))
109  .attr("x2", this.AxisToSvg("x", line.fX2, isndc))
110  .attr("y2", this.AxisToSvg("y", line.fY2, isndc))
111  .call(lineatt.func);
112  }
113 
114  // =============================================================================
115 
116  function drawPolyLine() {
117 
118  // create svg:g container for polyline drawing
119  this.CreateG();
120 
121  var polyline = this.GetObject(),
122  lineatt = new JSROOT.TAttLineHandler(polyline),
123  fillatt = this.createAttFill(polyline),
124  kPolyLineNDC = JSROOT.BIT(14),
125  isndc = polyline.TestBit(kPolyLineNDC),
126  cmd = "", func = this.AxisToSvgFunc(isndc);
127 
128  for (var n=0;n<=polyline.fLastPoint;++n)
129  cmd += ((n>0) ? "L" : "M") + func.x(polyline.fX[n]) + "," + func.y(polyline.fY[n]);
130 
131  if (polyline._typename != "TPolyLine") fillatt.SetSolidColor("none");
132 
133  if (!fillatt.empty()) cmd+="Z";
134 
135  this.draw_g
136  .append("svg:path")
137  .attr("d", cmd)
138  .call(lineatt.func)
139  .call(fillatt.func);
140  }
141 
142  // ==============================================================================
143 
144  function drawEllipse() {
145 
146  var ellipse = this.GetObject();
147 
148  this.createAttLine({ attr: ellipse });
149  this.createAttFill({ attr: ellipse });
150 
151  // create svg:g container for ellipse drawing
152  this.CreateG();
153 
154  var x = this.AxisToSvg("x", ellipse.fX1),
155  y = this.AxisToSvg("y", ellipse.fY1),
156  rx = this.AxisToSvg("x", ellipse.fX1 + ellipse.fR1) - x,
157  ry = y - this.AxisToSvg("y", ellipse.fY1 + ellipse.fR2);
158 
159  if (ellipse._typename == "TCrown") {
160  if (ellipse.fR1 <= 0) {
161  // handle same as ellipse with equal radius
162  rx = this.AxisToSvg("x", ellipse.fX1 + ellipse.fR2) - x;
163  } else {
164  var rx1 = rx, ry2 = ry,
165  ry1 = y - this.AxisToSvg("y", ellipse.fY1 + ellipse.fR1),
166  rx2 = this.AxisToSvg("x", ellipse.fX1 + ellipse.fR2) - x;
167 
168  var elem = this.draw_g
169  .attr("transform","translate("+x+","+y+")")
170  .append("svg:path")
171  .call(this.lineatt.func)
172  .call(this.fillatt.func);
173 
174  if ((ellipse.fPhimin == 0) && (ellipse.fPhimax == 360)) {
175  elem.attr("d", "M-"+rx1+",0" +
176  "A"+rx1+","+ry1+",0,1,0,"+rx1+",0" +
177  "A"+rx1+","+ry1+",0,1,0,-"+rx1+",0" +
178  "M-"+rx2+",0" +
179  "A"+rx2+","+ry2+",0,1,0,"+rx2+",0" +
180  "A"+rx2+","+ry2+",0,1,0,-"+rx2+",0");
181 
182  } else {
183  var large_arc = (ellipse.fPhimax-ellipse.fPhimin>=180) ? 1 : 0;
184 
185  var a1 = ellipse.fPhimin*Math.PI/180, a2 = ellipse.fPhimax*Math.PI/180,
186  dx1 = Math.round(rx1*Math.cos(a1)), dy1 = Math.round(ry1*Math.sin(a1)),
187  dx2 = Math.round(rx1*Math.cos(a2)), dy2 = Math.round(ry1*Math.sin(a2)),
188  dx3 = Math.round(rx2*Math.cos(a1)), dy3 = Math.round(ry2*Math.sin(a1)),
189  dx4 = Math.round(rx2*Math.cos(a2)), dy4 = Math.round(ry2*Math.sin(a2));
190 
191  elem.attr("d", "M"+dx2+","+dy2+
192  "A"+rx1+","+ry1+",0,"+large_arc+",0,"+dx1+","+dy1+
193  "L"+dx3+","+dy3 +
194  "A"+rx2+","+ry2+",0,"+large_arc+",1,"+dx4+","+dy4+"Z");
195  }
196 
197  return;
198  }
199  }
200 
201  if ((ellipse.fPhimin == 0) && (ellipse.fPhimax == 360) && (ellipse.fTheta == 0)) {
202  // this is simple case, which could be drawn with svg:ellipse
203  this.draw_g.append("svg:ellipse")
204  .attr("cx", x).attr("cy", y)
205  .attr("rx", rx).attr("ry", ry)
206  .call(this.lineatt.func).call(this.fillatt.func);
207  return;
208  }
209 
210  // here svg:path is used to draw more complex figure
211 
212  var ct = Math.cos(ellipse.fTheta*Math.PI/180),
213  st = Math.sin(ellipse.fTheta*Math.PI/180),
214  dx1 = rx * Math.cos(ellipse.fPhimin*Math.PI/180),
215  dy1 = ry * Math.sin(ellipse.fPhimin*Math.PI/180),
216  x1 = dx1*ct - dy1*st,
217  y1 = -dx1*st - dy1*ct,
218  dx2 = rx * Math.cos(ellipse.fPhimax*Math.PI/180),
219  dy2 = ry * Math.sin(ellipse.fPhimax*Math.PI/180),
220  x2 = dx2*ct - dy2*st,
221  y2 = -dx2*st - dy2*ct;
222 
223  this.draw_g
224  .attr("transform","translate("+x+","+y+")")
225  .append("svg:path")
226  .attr("d", "M0,0" +
227  "L" + Math.round(x1) + "," + Math.round(y1) +
228  "A"+rx+ ","+ry + "," + Math.round(-ellipse.fTheta) + ",1,0," + Math.round(x2) + "," + Math.round(y2) +
229  "Z")
230  .call(this.lineatt.func).call(this.fillatt.func);
231  }
232 
233  // ==============================================================================
234 
235  function drawPie() {
236  var pie = this.GetObject();
237 
238  // create svg:g container for ellipse drawing
239  this.CreateG();
240 
241  var xc = this.AxisToSvg("x", pie.fX),
242  yc = this.AxisToSvg("y", pie.fY),
243  rx = this.AxisToSvg("x", pie.fX + pie.fRadius) - xc,
244  ry = this.AxisToSvg("y", pie.fY + pie.fRadius) - yc;
245 
246  this.draw_g.attr("transform","translate("+xc+","+yc+")");
247 
248  // Draw the slices
249  var nb = pie.fPieSlices.length, total = 0,
250  af = (pie.fAngularOffset*Math.PI)/180,
251  x1 = Math.round(rx*Math.cos(af)), y1 = Math.round(ry*Math.sin(af));
252 
253  for (var n=0;n<nb; n++)
254  total += pie.fPieSlices[n].fValue;
255 
256  for (var n=0; n<nb; n++) {
257  var slice = pie.fPieSlices[n],
258  lineatt = new JSROOT.TAttLineHandler({attr: slice}),
259  fillatt = this.createAttFill(slice);
260 
261  af += slice.fValue/total*2*Math.PI;
262  var x2 = Math.round(rx*Math.cos(af)), y2 = Math.round(ry*Math.sin(af));
263 
264  this.draw_g
265  .append("svg:path")
266  .attr("d", "M0,0L"+x1+","+y1+"A"+rx+","+ry+",0,0,0,"+x2+","+y2+"z")
267  .call(lineatt.func)
268  .call(fillatt.func);
269  x1 = x2; y1 = y2;
270  }
271  }
272 
273  // =============================================================================
274 
275  function drawBox() {
276 
277  var box = this.GetObject(),
278  opt = this.OptionsAsString(),
279  draw_line = (opt.toUpperCase().indexOf("L")>=0),
280  lineatt = this.createAttLine(box),
281  fillatt = this.createAttFill(box);
282 
283  // create svg:g container for box drawing
284  this.CreateG();
285 
286  var x1 = this.AxisToSvg("x", box.fX1),
287  x2 = this.AxisToSvg("x", box.fX2),
288  y1 = this.AxisToSvg("y", box.fY1),
289  y2 = this.AxisToSvg("y", box.fY2),
290  xx = Math.min(x1,x2), yy = Math.min(y1,y2),
291  ww = Math.abs(x2-x1), hh = Math.abs(y1-y2);
292 
293  // if box filled, contour line drawn only with "L" draw option:
294  if (!fillatt.empty() && !draw_line) lineatt.color = "none";
295 
296  this.draw_g
297  .append("svg:rect")
298  .attr("x", xx).attr("y", yy)
299  .attr("width", ww)
300  .attr("height", hh)
301  .call(lineatt.func)
302  .call(fillatt.func);
303 
304  if (box.fBorderMode && box.fBorderSize && (fillatt.color!=='none')) {
305  var pww = box.fBorderSize, phh = box.fBorderSize,
306  side1 = "M"+xx+","+yy + "h"+ww + "l"+(-pww)+","+phh + "h"+(2*pww-ww) +
307  "v"+(hh-2*phh)+ "l"+(-pww)+","+phh + "z",
308  side2 = "M"+(xx+ww)+","+(yy+hh) + "v"+(-hh) + "l"+(-pww)+","+phh + "v"+(hh-2*phh)+
309  "h"+(2*pww-ww) + "l"+(-pww)+","+phh + "z";
310 
311  if (box.fBorderMode<0) { var s = side1; side1 = side2; side2 = s; }
312 
313  this.draw_g.append("svg:path")
314  .attr("d", side1)
315  .style("stroke","none")
316  .call(fillatt.func)
317  .style("fill", d3.rgb(fillatt.color).brighter(0.5).toString());
318 
319  this.draw_g.append("svg:path")
320  .attr("d", side2)
321  .style("stroke","none")
322  .call(fillatt.func)
323  .style("fill", d3.rgb(fillatt.color).darker(0.5).toString());
324  }
325  }
326 
327  // =============================================================================
328 
329  function drawMarker() {
330  var marker = this.GetObject(),
331  att = new JSROOT.TAttMarkerHandler(marker),
332  kMarkerNDC = JSROOT.BIT(14),
333  isndc = marker.TestBit(kMarkerNDC);
334 
335  // create svg:g container for box drawing
336  this.CreateG();
337 
338  var x = this.AxisToSvg("x", marker.fX, isndc),
339  y = this.AxisToSvg("y", marker.fY, isndc),
340  path = att.create(x,y);
341 
342  if (path)
343  this.draw_g.append("svg:path")
344  .attr("d", path)
345  .call(att.func);
346  }
347 
348  // =============================================================================
349 
350  function drawPolyMarker() {
351 
352  // create svg:g container for box drawing
353  this.CreateG();
354 
355  var poly = this.GetObject(),
356  att = new JSROOT.TAttMarkerHandler(poly),
357  path = "",
358  func = this.AxisToSvgFunc();
359 
360  for (var n=0;n<poly.fN;++n)
361  path += att.create(func.x(poly.fX[n]), func.y(poly.fY[n]));
362 
363  if (path)
364  this.draw_g.append("svg:path")
365  .attr("d", path)
366  .call(att.func);
367  }
368 
369  // ======================================================================================
370 
371  function drawArrow() {
372  var arrow = this.GetObject(), kLineNDC = JSROOT.BIT(14), oo = arrow.fOption;
373 
374  this.wsize = Math.max(3, Math.round(Math.max(this.pad_width(), this.pad_height()) * arrow.fArrowSize*0.8));
375  this.isndc = arrow.TestBit(kLineNDC);
376  this.angle2 = arrow.fAngle/2/180 * Math.PI;
377  this.beg = this.mid = this.end = 0;
378 
379  if (oo.indexOf("<")==0)
380  this.beg = (oo.indexOf("<|") == 0) ? 12 : 2;
381  if (oo.indexOf("->-")>=0) this.mid = 1; else
382  if (oo.indexOf("-|>-")>=0) this.mid = 11; else
383  if (oo.indexOf("-<-")>=0) this.mid = 2; else
384  if (oo.indexOf("-<|-")>=0) this.mid = 12;
385  if (oo.lastIndexOf(">") == oo.length-1)
386  this.end = ((oo.lastIndexOf("|>") == oo.length-2) && (oo.length>1)) ? 11 : 1;
387 
388  this.createAttLine({ attr: arrow });
389 
390  this.CreateG();
391 
392  this.x1 = this.AxisToSvg("x", arrow.fX1, this.isndc, true);
393  this.y1 = this.AxisToSvg("y", arrow.fY1, this.isndc, true);
394  this.x2 = this.AxisToSvg("x", arrow.fX2, this.isndc, true);
395  this.y2 = this.AxisToSvg("y", arrow.fY2, this.isndc, true);
396 
397  this.rotate = function(angle, x0, y0) {
398  var dx = this.wsize * Math.cos(angle), dy = this.wsize * Math.sin(angle), res = "";
399  if ((x0 !== undefined) && (y0 !== undefined)) {
400  res = "M" + Math.round(x0-dx) + "," + Math.round(y0-dy);
401  } else {
402  dx = -dx; dy = -dy;
403  }
404  res += "l"+Math.round(dx)+","+Math.round(dy);
405  if (x0 && (y0===undefined)) res+="z";
406  return res;
407  }
408 
409  this.createPath = function() {
410  var angle = Math.atan2(this.y2 - this.y1, this.x2 - this.x1),
411  dlen = this.wsize * Math.cos(this.angle2),
412  dx = dlen*Math.cos(angle), dy = dlen*Math.sin(angle),
413  path = "";
414 
415  if (this.beg)
416  path += this.rotate(angle - Math.PI - this.angle2, this.x1, this.y1) +
417  this.rotate(angle - Math.PI + this.angle2, this.beg > 10);
418 
419  if (this.mid % 10 == 2)
420  path += this.rotate(angle - Math.PI - this.angle2, (this.x1+this.x2-dx)/2, (this.y1+this.y2-dy)/2) +
421  this.rotate(angle - Math.PI + this.angle2, this.mid > 10);
422 
423  if (this.mid % 10 == 1)
424  path += this.rotate(angle - this.angle2, (this.x1+this.x2+dx)/2, (this.y1+this.y2+dy)/2) +
425  this.rotate(angle + this.angle2, this.mid > 10);
426 
427  if (this.end)
428  path += this.rotate(angle - this.angle2, this.x2, this.y2) +
429  this.rotate(angle + this.angle2, this.end > 10);
430 
431  return "M" + Math.round(this.x1 + (this.beg > 10 ? dx : 0)) + "," +
432  Math.round(this.y1 + (this.beg > 10 ? dy : 0)) +
433  "L" + Math.round(this.x2 - (this.end > 10 ? dx : 0)) + "," +
434  Math.round(this.y2 - (this.end > 10 ? dy : 0)) +
435  path;
436  }
437 
438  var elem = this.draw_g.append("svg:path")
439  .attr("d", this.createPath())
440  .call(this.lineatt.func);
441 
442  if ((this.beg > 10) || (this.end > 10)) {
443  this.createAttFill({ attr: arrow });
444  elem.call(this.fillatt.func);
445  } else {
446  elem.style('fill','none');
447  }
448 
449  if (!this.moveStart)
450  this.moveStart = function(x,y) {
451  var fullsize = Math.sqrt(Math.pow(this.x1-this.x2,2) + Math.pow(this.y1-this.y2,2)),
452  sz1 = Math.sqrt(Math.pow(x-this.x1,2) + Math.pow(y-this.y1,2))/fullsize,
453  sz2 = Math.sqrt(Math.pow(x-this.x2,2) + Math.pow(y-this.y2,2))/fullsize;
454  if (sz1>0.9) this.side = 1; else if (sz2>0.9) this.side = -1; else this.side = 0;
455  }
456 
457  if (!this.moveDrag)
458  this.moveDrag = function(dx,dy) {
459  if (this.side != 1) { this.x1 += dx; this.y1 += dy; }
460  if (this.side != -1) { this.x2 += dx; this.y2 += dy; }
461  this.draw_g.select('path').attr("d", this.createPath());
462  }
463 
464  if (!this.moveEnd)
465  this.moveEnd = function(not_changed) {
466  if (not_changed) return;
467  var arrow = this.GetObject(), exec = "";
468  arrow.fX1 = this.SvgToAxis("x", this.x1, this.isndc);
469  arrow.fX2 = this.SvgToAxis("x", this.x2, this.isndc);
470  arrow.fY1 = this.SvgToAxis("y", this.y1, this.isndc);
471  arrow.fY2 = this.SvgToAxis("y", this.y2, this.isndc);
472  if (this.side != 1) exec += "SetX1(" + arrow.fX1 + ");;SetY1(" + arrow.fY1 + ");;";
473  if (this.side != -1) exec += "SetX2(" + arrow.fX2 + ");;SetY2(" + arrow.fY2 + ");;";
474  this.WebCanvasExec(exec + "Notify();;");
475  }
476 
477  this.AddMove();
478  }
479 
480  // =================================================================================
481 
482  function drawRooPlot(divid, plot, opt) {
483 
484  var painter = new JSROOT.TObjectPainter(plot), cnt = -1;
485 
486  function DrawNextItem() {
487  if (++cnt >= plot._items.arr.length) return painter.DrawingReady();
488 
489  JSROOT.draw(divid, plot._items.arr[cnt], plot._items.opt[cnt], DrawNextItem);
490  }
491 
492  JSROOT.draw(divid, plot._hist, "hist", DrawNextItem);
493 
494  return painter;
495  }
496 
497  // ===================================================================================
498 
508  function TF1Painter(tf1) {
509  JSROOT.TObjectPainter.call(this, tf1);
510  this.bins = null;
511  }
512 
513  TF1Painter.prototype = Object.create(JSROOT.TObjectPainter.prototype);
514 
515  TF1Painter.prototype.Eval = function(x) {
516  return this.GetObject().evalPar(x);
517  }
518 
519  //TF1Painter.prototype.UpdateObject = function(obj, opt) {
520  // if (!this.MatchObjectType(obj)) return false;
521  // var tf1 = this.GetObject();
522  // tf1.fSave = obj.fSave;
523  // return true;
524  //}
525 
526  TF1Painter.prototype.CreateBins = function(ignore_zoom) {
527  var main = this.frame_painter(),
528  gxmin = 0, gxmax = 0, tf1 = this.GetObject();
529 
530  if (main && !ignore_zoom) {
531  if (main.zoom_xmin !== main.zoom_xmax) {
532  gxmin = main.zoom_xmin;
533  gxmax = main.zoom_xmax;
534  } else {
535  gxmin = main.xmin;
536  gxmax = main.xmax;
537  }
538  }
539 
540  if ((tf1.fSave.length > 0) && !this.nosave) {
541  // in the case where the points have been saved, useful for example
542  // if we don't have the user's function
543  var np = tf1.fSave.length - 2,
544  xmin = tf1.fSave[np],
545  xmax = tf1.fSave[np+1],
546  dx = (xmax - xmin) / (np-1),
547  res = [];
548 
549  for (var n=0; n < np; ++n) {
550  var xx = xmin + dx*n;
551  // check if points need to be displayed at all, keep at least 4-5 points for Bezier curves
552  if ((gxmin !== gxmax) && ((xx + 2*dx < gxmin) || (xx - 2*dx > gxmax))) continue;
553  var yy = tf1.fSave[n];
554 
555  if (!isNaN(yy)) res.push({ x : xx, y : yy });
556  }
557  return res;
558  }
559 
560  var xmin = tf1.fXmin, xmax = tf1.fXmax, logx = false;
561 
562  if (gxmin !== gxmax) {
563  if (gxmin > xmin) xmin = gxmin;
564  if (gxmax < xmax) xmax = gxmax;
565  }
566 
567  if (main && main.logx && (xmin>0) && (xmax>0)) {
568  logx = true;
569  xmin = Math.log(xmin);
570  xmax = Math.log(xmax);
571  }
572 
573  var np = Math.max(tf1.fNpx, 101),
574  dx = (xmax - xmin) / (np - 1),
575  res = [];
576 
577  for (var n=0; n < np; n++) {
578  var xx = xmin + n*dx;
579  if (logx) xx = Math.exp(xx);
580  var yy = this.Eval(xx);
581  if (!isNaN(yy)) res.push({ x: xx, y: yy });
582  }
583  return res;
584  }
585 
586  TF1Painter.prototype.CreateDummyHisto = function() {
587 
588  var xmin = 0, xmax = 1, ymin = 0, ymax = 1,
589  bins = this.CreateBins(true);
590 
591  if (bins && (bins.length > 0)) {
592 
593  xmin = xmax = bins[0].x;
594  ymin = ymax = bins[0].y;
595 
596  bins.forEach(function(bin) {
597  xmin = Math.min(bin.x, xmin);
598  xmax = Math.max(bin.x, xmax);
599  ymin = Math.min(bin.y, ymin);
600  ymax = Math.max(bin.y, ymax);
601  });
602 
603  if (ymax > 0.0) ymax *= 1.05;
604  if (ymin < 0.0) ymin *= 1.05;
605  }
606 
607  var histo = JSROOT.Create("TH1I"),
608  tf1 = this.GetObject();
609 
610  histo.fName = tf1.fName + "_hist";
611  histo.fTitle = tf1.fTitle;
612 
613  histo.fXaxis.fXmin = xmin;
614  histo.fXaxis.fXmax = xmax;
615  histo.fYaxis.fXmin = ymin;
616  histo.fYaxis.fXmax = ymax;
617 
618  return histo;
619  }
620 
621  TF1Painter.prototype.ProcessTooltip = function(pnt) {
622  var cleanup = false;
623 
624  if ((pnt === null) || (this.bins === null)) {
625  cleanup = true;
626  } else
627  if ((this.bins.length==0) || (pnt.x < this.bins[0].grx) || (pnt.x > this.bins[this.bins.length-1].grx)) {
628  cleanup = true;
629  }
630 
631  if (cleanup) {
632  if (this.draw_g !== null)
633  this.draw_g.select(".tooltip_bin").remove();
634  return null;
635  }
636 
637  var min = 100000, best = -1, bin;
638 
639  for(var n=0; n<this.bins.length; ++n) {
640  bin = this.bins[n];
641  var dist = Math.abs(bin.grx - pnt.x);
642  if (dist < min) { min = dist; best = n; }
643  }
644 
645  bin = this.bins[best];
646 
647  var gbin = this.draw_g.select(".tooltip_bin"),
648  radius = this.lineatt.width + 3;
649 
650  if (gbin.empty())
651  gbin = this.draw_g.append("svg:circle")
652  .attr("class","tooltip_bin")
653  .style("pointer-events","none")
654  .attr("r", radius)
655  .call(this.lineatt.func)
656  .call(this.fillatt.func);
657 
658  var res = { name: this.GetObject().fName,
659  title: this.GetObject().fTitle,
660  x: bin.grx,
661  y: bin.gry,
662  color1: this.lineatt.color,
663  color2: this.fillatt.fillcolor(),
664  lines: [],
665  exact: (Math.abs(bin.grx - pnt.x) < radius) && (Math.abs(bin.gry - pnt.y) < radius) };
666 
667  res.changed = gbin.property("current_bin") !== best;
668  res.menu = res.exact;
669  res.menu_dist = Math.sqrt((bin.grx-pnt.x)*(bin.grx-pnt.x) + (bin.gry-pnt.y)*(bin.gry-pnt.y));
670 
671  if (res.changed)
672  gbin.attr("cx", bin.grx)
673  .attr("cy", bin.gry)
674  .property("current_bin", best);
675 
676  var name = this.GetTipName();
677  if (name.length > 0) res.lines.push(name);
678 
679  var pmain = this.frame_painter();
680  if (pmain)
681  res.lines.push("x = " + pmain.AxisAsText("x",bin.x) + " y = " + pmain.AxisAsText("y",bin.y));
682 
683  return res;
684  }
685 
686  TF1Painter.prototype.Redraw = function() {
687 
688  var w = this.frame_width(),
689  h = this.frame_height(),
690  tf1 = this.GetObject(),
691  fp = this.frame_painter(),
692  pmain = this.main_painter(),
693  name = this.GetTipName("\n");
694 
695  this.CreateG(true);
696 
697  // recalculate drawing bins when necessary
698  this.bins = this.CreateBins(false);
699 
700  this.createAttLine({ attr: tf1 });
701  this.lineatt.used = false;
702 
703  this.createAttFill({ attr: tf1, kind: 1 });
704  this.fillatt.used = false;
705 
706  // first calculate graphical coordinates
707  for(var n=0; n<this.bins.length; ++n) {
708  var bin = this.bins[n];
709  bin.grx = fp.grx(bin.x);
710  bin.gry = fp.gry(bin.y);
711  }
712 
713  if (this.bins.length > 2) {
714 
715  var h0 = h; // use maximal frame height for filling
716  if ((pmain.hmin!==undefined) && (pmain.hmin>=0)) {
717  h0 = Math.round(fp.gry(0));
718  if ((h0 > h) || (h0 < 0)) h0 = h;
719  }
720 
721  var path = JSROOT.Painter.BuildSvgPath("bezier", this.bins, h0, 2);
722 
723  if (this.lineatt.color != "none")
724  this.draw_g.append("svg:path")
725  .attr("class", "line")
726  .attr("d", path.path)
727  .style("fill", "none")
728  .call(this.lineatt.func);
729 
730  if (!this.fillatt.empty())
731  this.draw_g.append("svg:path")
732  .attr("class", "area")
733  .attr("d", path.path + path.close)
734  .style("stroke", "none")
735  .call(this.fillatt.func);
736  }
737  }
738 
739  TF1Painter.prototype.CanZoomIn = function(axis,min,max) {
740  if (axis!=="x") return false;
741 
742  var tf1 = this.GetObject();
743 
744  if (tf1.fSave.length > 0) {
745  // in the case where the points have been saved, useful for example
746  // if we don't have the user's function
747  var nb_points = tf1.fNpx;
748 
749  var xmin = tf1.fSave[nb_points + 1];
750  var xmax = tf1.fSave[nb_points + 2];
751 
752  return Math.abs(xmin - xmax) / nb_points < Math.abs(min - max);
753  }
754 
755  // if function calculated, one always could zoom inside
756  return true;
757  }
758 
759  TF1Painter.prototype.PerformDraw = function() {
760  if (this.main_painter() === null) {
761  var histo = this.CreateDummyHisto(), pthis = this;
762  JSROOT.draw(this.divid, histo, "AXIS", function(hpainter) {
763  pthis.SetDivId(pthis.divid);
764  pthis.Redraw();
765  return pthis.DrawingReady();
766  });
767  return pthis;
768  }
769 
770  this.SetDivId(this.divid);
771  this.Redraw();
772  return this.DrawingReady();
773  }
774 
775  function drawFunction(divid, tf1, opt) {
776 
777  var painter = new TF1Painter(tf1);
778 
779  painter.SetDivId(divid, -1);
780  var d = new JSROOT.DrawOptions(opt);
781  painter.nosave = d.check('NOSAVE');
782 
783  if (JSROOT.Math !== undefined)
784  return painter.PerformDraw();
785 
786  JSROOT.AssertPrerequisites("math", painter.PerformDraw.bind(painter));
787  return painter;
788  }
789 
790  // =======================================================================
791 
801  function TGraphPainter(graph) {
802  JSROOT.TObjectPainter.call(this, graph);
803  this.axes_draw = false; // indicate if graph histogram was drawn for axes
804  this.bins = null;
805  this.xmin = this.ymin = this.xmax = this.ymax = 0;
806  this.wheel_zoomy = true;
807  this.is_bent = (graph._typename == 'TGraphBentErrors');
808  this.has_errors = (graph._typename == 'TGraphErrors') ||
809  (graph._typename == 'TGraphAsymmErrors') ||
810  this.is_bent || graph._typename.match(/^RooHist/);
811  }
812 
813  TGraphPainter.prototype = Object.create(JSROOT.TObjectPainter.prototype);
814 
815  TGraphPainter.prototype.Redraw = function() {
816  this.DrawBins();
817  }
818 
819  TGraphPainter.prototype.Cleanup = function() {
820  delete this.interactive_bin; // break mouse handling
821  delete this.bins;
822  JSROOT.TObjectPainter.prototype.Cleanup.call(this);
823  }
824 
825  TGraphPainter.prototype.DecodeOptions = function(opt) {
826 
827  if (!opt) opt = this.main_painter() ? "lp" : "alp";
828 
829  if ((typeof opt == "string") && (opt.indexOf("same ")==0))
830  opt = opt.substr(5);
831 
832  var graph = this.GetObject(),
833  d = new JSROOT.DrawOptions(opt);
834 
835  if (!this.options) this.options = {};
836 
837  JSROOT.extend(this.options, {
838  Line: 0, Curve: 0, Rect: 0, Mark: 0, Bar: 0, OutRange: 0, EF:0, Fill: 0, NoOpt: 0,
839  MainError: 1, Ends: 1, Axis: "", PadStats: false, PadTitle: false, original: opt
840  });
841 
842  var res = this.options;
843 
844  res.PadStats = d.check("USE_PAD_STATS");
845  res.PadTitle = d.check("USE_PAD_TITLE");
846 
847  res._pfc = d.check("PFC");
848  res._plc = d.check("PLC");
849  res._pmc = d.check("PMC");
850 
851  if (d.check('NOOPT')) res.NoOpt = 1;
852  if (d.check('L')) res.Line = 1;
853  if (d.check('F')) res.Fill = 1;
854  if (d.check('A')) res.Axis = d.check("I") ? "A" : "AXIS"; // I means invisible axis
855  if (d.check('X+')) res.Axis += "X+";
856  if (d.check('Y+')) res.Axis += "Y+";
857  if (d.check('RX')) res.Axis += "RX";
858  if (d.check('RY')) res.Axis += "RY";
859  if (d.check('C')) res.Curve = res.Line = 1;
860  if (d.check('*')) res.Mark = 103;
861  if (d.check('P0')) res.Mark = 104;
862  if (d.check('P')) res.Mark = 1;
863  if (d.check('B')) { res.Bar = 1; res.Errors = 0; }
864  if (d.check('Z')) { res.Errors = 1; res.Ends = 0; }
865  if (d.check('||')) { res.Errors = 1; res.MainError = 0; res.Ends = 1; }
866  if (d.check('[]')) { res.Errors = 1; res.MainError = 0; res.Ends = 2; }
867  if (d.check('|>')) { res.Errors = 1; res.Ends = 3; }
868  if (d.check('>')) { res.Errors = 1; res.Ends = 4; }
869  if (d.check('0')) { res.Mark = 1; res.Errors = 1; res.OutRange = 1; }
870  if (d.check('1')) { if (res.Bar == 1) res.Bar = 2; }
871  if (d.check('2')) { res.Rect = 1; res.Errors = 0; }
872  if (d.check('3')) { res.EF = 1; res.Errors = 0; }
873  if (d.check('4')) { res.EF = 2; res.Errors = 0; }
874  if (d.check('5')) { res.Rect = 2; res.Errors = 0; }
875  if (d.check('X')) res.Errors = 0;
876  // if (d.check('E')) res.Errors = 1; // E option only defined for TGraphPolar
877 
878  if (res.Errors === undefined)
879  res.Errors = this.has_errors ? 1 : 0;
880 
881  // special case - one could use svg:path to draw many pixels (
882  if ((res.Mark == 1) && (graph.fMarkerStyle==1)) res.Mark = 101;
883 
884  // if no drawing option is selected and if opt=='' nothing is done.
885  if (res.Line + res.Fill + res.Mark + res.Bar + res.EF + res.Rect + res.Errors == 0) {
886  if (d.empty()) res.Line = 1;
887  }
888 
889  if (graph._typename == 'TGraphErrors') {
890  if (d3.max(graph.fEX) < 1.0e-300 && d3.max(graph.fEY) < 1.0e-300)
891  res.Errors = 0;
892  }
893 
894  if (!res.Axis) {
895  // check if axis should be drawn
896  // either graph drawn directly or
897  // graph is first object in list of primitives
898  var pad = this.root_pad();
899  if (!pad || (pad.fPrimitives && (pad.fPrimitives.arr[0] === graph))) res.Axis = "AXIS";
900  } else if (res.Axis.indexOf("A")<0) {
901  res.Axis = "AXIS," + res.Axis;
902  }
903 
904  if (res.PadTitle) res.Axis += ";USE_PAD_TITLE";
905 
906  res.HOptions = res.Axis;
907  }
908 
909  TGraphPainter.prototype.CreateBins = function() {
910  var gr = this.GetObject();
911  if (!gr) return;
912 
913  var kind = 0, npoints = gr.fNpoints;
914  if ((gr._typename==="TCutG") && (npoints>3)) npoints--;
915 
916  if (gr._typename == 'TGraphErrors') kind = 1; else
917  if (gr._typename == 'TGraphAsymmErrors' || gr._typename == 'TGraphBentErrors'
918  || gr._typename.match(/^RooHist/)) kind = 2;
919 
920  this.bins = [];
921 
922  for (var p=0; p<npoints; ++p) {
923  var bin = { x: gr.fX[p], y: gr.fY[p], indx: p };
924  switch(kind) {
925  case 1:
926  bin.exlow = bin.exhigh = gr.fEX[p];
927  bin.eylow = bin.eyhigh = gr.fEY[p];
928  break;
929  case 2:
930  bin.exlow = gr.fEXlow[p];
931  bin.exhigh = gr.fEXhigh[p];
932  bin.eylow = gr.fEYlow[p];
933  bin.eyhigh = gr.fEYhigh[p];
934  break;
935  }
936  this.bins.push(bin);
937 
938  if (p===0) {
939  this.xmin = this.xmax = bin.x;
940  this.ymin = this.ymax = bin.y;
941  }
942 
943  if (kind > 0) {
944  this.xmin = Math.min(this.xmin, bin.x - bin.exlow, bin.x + bin.exhigh);
945  this.xmax = Math.max(this.xmax, bin.x - bin.exlow, bin.x + bin.exhigh);
946  this.ymin = Math.min(this.ymin, bin.y - bin.eylow, bin.y + bin.eyhigh);
947  this.ymax = Math.max(this.ymax, bin.y - bin.eylow, bin.y + bin.eyhigh);
948  } else {
949  this.xmin = Math.min(this.xmin, bin.x);
950  this.xmax = Math.max(this.xmax, bin.x);
951  this.ymin = Math.min(this.ymin, bin.y);
952  this.ymax = Math.max(this.ymax, bin.y);
953  }
954  }
955  }
956 
957  TGraphPainter.prototype.CreateHistogram = function(only_set_ranges) {
958  // bins should be created when calling this function
959 
960  var xmin = this.xmin, xmax = this.xmax, ymin = this.ymin, ymax = this.ymax, set_x = true, set_y = true;
961 
962  if (xmin >= xmax) xmax = xmin+1;
963  if (ymin >= ymax) ymax = ymin+1;
964  var dx = (xmax-xmin)*0.1, dy = (ymax-ymin)*0.1,
965  uxmin = xmin - dx, uxmax = xmax + dx,
966  minimum = ymin - dy, maximum = ymax + dy;
967 
968  // this is draw options with maximal axis range which could be unzoomed
969  this.options.HOptions = this.options.Axis + ";ymin:" + minimum + ";ymax:" + maximum;
970 
971  if ((uxmin<0) && (xmin>=0)) uxmin = xmin*0.9;
972  if ((uxmax>0) && (xmax<=0)) uxmax = 0;
973 
974  var graph = this.GetObject();
975 
976  if (graph.fMinimum != -1111) minimum = ymin = graph.fMinimum;
977  if (graph.fMaximum != -1111) maximum = ymax = graph.fMaximum;
978  if ((minimum < 0) && (ymin >=0)) minimum = 0.9*ymin;
979 
980  var kResetHisto = JSROOT.BIT(17);
981 
982  var histo = graph.fHistogram;
983 
984  if (only_set_ranges) {
985  set_x = only_set_ranges.indexOf("x") >= 0;
986  set_y = only_set_ranges.indexOf("y") >= 0;
987  } else if (histo) {
988  // make logic like in the TGraph::GetHistogram
989  if (!graph.TestBit(kResetHisto)) return histo;
990  graph.InvertBit(kResetHisto);
991  } else {
992  graph.fHistogram = histo = JSROOT.CreateHistogram("TH1F", 100);
993  histo.fName = graph.fName + "_h";
994  histo.fTitle = graph.fTitle;
995  histo.fBits = histo.fBits | JSROOT.TH1StatusBits.kNoStats;
996  this._own_histogram = true;
997  }
998 
999  if (set_x) {
1000  histo.fXaxis.fXmin = uxmin;
1001  histo.fXaxis.fXmax = uxmax;
1002  }
1003  if (set_y) {
1004  histo.fYaxis.fXmin = minimum;
1005  histo.fYaxis.fXmax = maximum;
1006  histo.fMinimum = minimum;
1007  histo.fMaximum = maximum;
1008  }
1009 
1010  return histo;
1011  }
1012 
1016  TGraphPainter.prototype.UnzoomUserRange = function(dox, doy, doz) {
1017  var graph = this.GetObject();
1018  if (this._own_histogram || !graph) return false;
1019 
1020  var histo = graph.fHistogram;
1021  if (!histo) return false;
1022 
1023  var arg = "";
1024  if (dox && (histo.fXaxis.fXmin > this.xmin) || (histo.fXaxis.fXmax < this.xmax)) arg += "x";
1025  if (doy && (histo.fYaxis.fXmin > this.ymin) || (histo.fYaxis.fXmax < this.ymax)) arg += "y";
1026  if (!arg) return false;
1027 
1028  this.CreateHistogram(arg);
1029  var hpainter = this.main_painter();
1030  if (hpainter) hpainter.CreateAxisFuncs(false);
1031 
1032  return true;
1033  }
1034 
1036  TGraphPainter.prototype.CanOptimize = function() {
1037  return JSROOT.gStyle.OptimizeDraw > 0 && !this.options.NoOpt;
1038  }
1039 
1041  TGraphPainter.prototype.OptimizeBins = function(maxpnt, filter_func) {
1042  if ((this.bins.length < 30) && !filter_func) return this.bins;
1043 
1044  var selbins = null;
1045  if (typeof filter_func == 'function') {
1046  for (var n = 0; n < this.bins.length; ++n) {
1047  if (filter_func(this.bins[n],n)) {
1048  if (!selbins) selbins = (n==0) ? [] : this.bins.slice(0, n);
1049  } else {
1050  if (selbins) selbins.push(this.bins[n]);
1051  }
1052  }
1053  }
1054  if (!selbins) selbins = this.bins;
1055 
1056  if (!maxpnt) maxpnt = 500000;
1057 
1058  if ((selbins.length < maxpnt) || !this.CanOptimize()) return selbins;
1059  var step = Math.floor(selbins.length / maxpnt);
1060  if (step < 2) step = 2;
1061  var optbins = [];
1062  for (var n = 0; n < selbins.length; n+=step)
1063  optbins.push(selbins[n]);
1064 
1065  return optbins;
1066  }
1067 
1068  TGraphPainter.prototype.TooltipText = function(d) {
1069  var pmain = this.frame_painter(), lines = [];
1070 
1071  lines.push(this.GetTipName());
1072 
1073  if (d && pmain) {
1074  lines.push("x = " + pmain.AxisAsText("x", d.x));
1075  lines.push("y = " + pmain.AxisAsText("y", d.y));
1076 
1077  if (this.options.Errors && (pmain.x_kind=='normal') && ('exlow' in d) && ((d.exlow!=0) || (d.exhigh!=0)))
1078  lines.push("error x = -" + pmain.AxisAsText("x", d.exlow) + "/+" + pmain.AxisAsText("x", d.exhigh));
1079 
1080  if ((this.options.Errors || (this.options.EF > 0)) && (pmain.y_kind=='normal') && ('eylow' in d) && ((d.eylow!=0) || (d.eyhigh!=0)))
1081  lines.push("error y = -" + pmain.AxisAsText("y", d.eylow) + "/+" + pmain.AxisAsText("y", d.eyhigh));
1082  }
1083  return lines;
1084  }
1085 
1086  TGraphPainter.prototype.get_main = function() {
1087  var pmain = this.frame_painter();
1088 
1089  if (pmain && pmain.grx && pmain.gry) return pmain;
1090 
1091  pmain = {
1092  pad_layer: true,
1093  pad: this.root_pad(),
1094  pw: this.pad_width(),
1095  ph: this.pad_height(),
1096  grx: function(value) {
1097  if (this.pad.fLogx)
1098  value = (value>0) ? JSROOT.log10(value) : this.pad.fUxmin;
1099  else
1100  value = (value - this.pad.fX1) / (this.pad.fX2 - this.pad.fX1);
1101  return value*this.pw;
1102  },
1103  gry: function(value) {
1104  if (this.pad.fLogy)
1105  value = (value>0) ? JSROOT.log10(value) : this.pad.fUymin;
1106  else
1107  value = (value - this.pad.fY1) / (this.pad.fY2 - this.pad.fY1);
1108  return (1-value)*this.ph;
1109  }
1110  }
1111 
1112  return pmain.pad ? pmain : null;
1113  }
1114 
1115  TGraphPainter.prototype.DrawBins = function() {
1116 
1117  var pthis = this,
1118  pmain = this.get_main(),
1119  w = this.frame_width(),
1120  h = this.frame_height(),
1121  graph = this.GetObject(),
1122  excl_width = 0,
1123  pp = this.pad_painter();
1124 
1125  if (!pmain) return;
1126 
1127  this.CreateG(!pmain.pad_layer);
1128 
1129  if (pp && (this.options._pfc || this.options._plc || this.options._pmc)) {
1130  var icolor = pp.CreateAutoColor(this);
1131 
1132  if (this.options._pfc) { graph.fFillColor = icolor; delete this.fillatt; }
1133  if (this.options._plc) { graph.fLineColor = icolor; delete this.lineatt; }
1134  if (this.options._pmc) { graph.fMarkerColor = icolor; delete this.markeratt; }
1135 
1136  this.options._pfc = this.options._plc = this.options._pmc = false;
1137  }
1138 
1139  this.createAttLine({ attr: graph, can_excl: true });
1140 
1141  this.createAttFill({ attr: graph, kind: 1 });
1142  this.fillatt.used = false; // mark used only when really used
1143 
1144  this.draw_kind = "none"; // indicate if special svg:g were created for each bin
1145  this.marker_size = 0; // indicate if markers are drawn
1146 
1147  if (this.lineatt.excl_side != 0) {
1148  excl_width = this.lineatt.excl_width;
1149  if (this.lineatt.width > 0) this.options.Line = 1;
1150  }
1151 
1152  var drawbins = null;
1153 
1154  if (this.options.EF) {
1155 
1156  drawbins = this.OptimizeBins((this.options.EF > 1) ? 5000 : 0);
1157 
1158  // build lower part
1159  for (var n=0;n<drawbins.length;++n) {
1160  var bin = drawbins[n];
1161  bin.grx = pmain.grx(bin.x);
1162  bin.gry = pmain.gry(bin.y - bin.eylow);
1163  }
1164 
1165  var path1 = JSROOT.Painter.BuildSvgPath((this.options.EF > 1) ? "bezier" : "line", drawbins),
1166  bins2 = [];
1167 
1168  for (var n=drawbins.length-1;n>=0;--n) {
1169  var bin = drawbins[n];
1170  bin.gry = pmain.gry(bin.y + bin.eyhigh);
1171  bins2.push(bin);
1172  }
1173 
1174  // build upper part (in reverse direction)
1175  var path2 = JSROOT.Painter.BuildSvgPath((this.options.EF > 1) ? "Lbezier" : "Lline", bins2);
1176 
1177  this.draw_g.append("svg:path")
1178  .attr("d", path1.path + path2.path + "Z")
1179  .style("stroke", "none")
1180  .call(this.fillatt.func);
1181  this.draw_kind = "lines";
1182  }
1183 
1184  if (this.options.Line == 1 || this.options.Fill == 1 || (excl_width!==0)) {
1185 
1186  var close_symbol = "";
1187  if (graph._typename=="TCutG") this.options.Fill = 1;
1188 
1189  if (this.options.Fill == 1) {
1190  close_symbol = "Z"; // always close area if we want to fill it
1191  excl_width = 0;
1192  }
1193 
1194  if (!drawbins) drawbins = this.OptimizeBins(this.options.Curve ? 5000 : 0);
1195 
1196  for (var n=0;n<drawbins.length;++n) {
1197  var bin = drawbins[n];
1198  bin.grx = pmain.grx(bin.x);
1199  bin.gry = pmain.gry(bin.y);
1200  }
1201 
1202  var kind = "line"; // simple line
1203  if (this.options.Curve === 1) kind = "bezier"; else
1204  if (excl_width!==0) kind+="calc"; // we need to calculated deltas to build exclusion points
1205 
1206  var path = JSROOT.Painter.BuildSvgPath(kind, drawbins);
1207 
1208  if (excl_width!==0) {
1209  var extrabins = [];
1210  for (var n=drawbins.length-1;n>=0;--n) {
1211  var bin = drawbins[n];
1212  var dlen = Math.sqrt(bin.dgrx*bin.dgrx + bin.dgry*bin.dgry);
1213  // shift point, using
1214  bin.grx += excl_width*bin.dgry/dlen;
1215  bin.gry -= excl_width*bin.dgrx/dlen;
1216  extrabins.push(bin);
1217  }
1218 
1219  var path2 = JSROOT.Painter.BuildSvgPath("L" + ((this.options.Curve === 1) ? "bezier" : "line"), extrabins);
1220 
1221  this.draw_g.append("svg:path")
1222  .attr("d", path.path + path2.path + "Z")
1223  .style("stroke", "none")
1224  .call(this.fillatt.func)
1225  .style('opacity', 0.75);
1226  }
1227 
1228  if (this.options.Line || this.options.Fill) {
1229  var elem = this.draw_g.append("svg:path")
1230  .attr("d", path.path + close_symbol);
1231  if (this.options.Line)
1232  elem.call(this.lineatt.func);
1233  else
1234  elem.style('stroke','none');
1235 
1236  if (this.options.Fill)
1237  elem.call(this.fillatt.func);
1238  else
1239  elem.style('fill','none');
1240  }
1241 
1242  this.draw_kind = "lines";
1243  }
1244 
1245  var nodes = null;
1246 
1247  if (this.options.Errors || this.options.Rect || this.options.Bar) {
1248 
1249  drawbins = this.OptimizeBins(5000, function(pnt,i) {
1250 
1251  var grx = pmain.grx(pnt.x);
1252 
1253  // when drawing bars, take all points
1254  if (!pthis.options.Bar && ((grx<0) || (grx>w))) return true;
1255 
1256  var gry = pmain.gry(pnt.y);
1257 
1258  if (!pthis.options.Bar && !pthis.options.OutRange && ((gry<0) || (gry>h))) return true;
1259 
1260  pnt.grx1 = Math.round(grx);
1261  pnt.gry1 = Math.round(gry);
1262 
1263  if (pthis.has_errors) {
1264  pnt.grx0 = Math.round(pmain.grx(pnt.x - pnt.exlow) - grx);
1265  pnt.grx2 = Math.round(pmain.grx(pnt.x + pnt.exhigh) - grx);
1266  pnt.gry0 = Math.round(pmain.gry(pnt.y - pnt.eylow) - gry);
1267  pnt.gry2 = Math.round(pmain.gry(pnt.y + pnt.eyhigh) - gry);
1268 
1269  if (pthis.is_bent) {
1270  pnt.grdx0 = Math.round(pmain.gry(pnt.y + graph.fEXlowd[i]) - gry);
1271  pnt.grdx2 = Math.round(pmain.gry(pnt.y + graph.fEXhighd[i]) - gry);
1272  pnt.grdy0 = Math.round(pmain.grx(pnt.x + graph.fEYlowd[i]) - grx);
1273  pnt.grdy2 = Math.round(pmain.grx(pnt.x + graph.fEYhighd[i]) - grx);
1274  } else {
1275  pnt.grdx0 = pnt.grdx2 = pnt.grdy0 = pnt.grdy2 = 0;
1276  }
1277  }
1278 
1279  return false;
1280  });
1281 
1282  this.draw_kind = "nodes";
1283 
1284  // here are up to five elements are collected, try to group them
1285  nodes = this.draw_g.selectAll(".grpoint")
1286  .data(drawbins)
1287  .enter()
1288  .append("svg:g")
1289  .attr("class", "grpoint")
1290  .attr("transform", function(d) { return "translate(" + d.grx1 + "," + d.gry1 + ")"; });
1291  }
1292 
1293  if (this.options.Bar) {
1294  // calculate bar width
1295  for (var i=1;i<drawbins.length-1;++i)
1296  drawbins[i].width = Math.max(2, (drawbins[i+1].grx1 - drawbins[i-1].grx1) / 2 - 2);
1297 
1298  // first and last bins
1299  switch (drawbins.length) {
1300  case 0: break;
1301  case 1: drawbins[0].width = w/4; break; // pathologic case of single bin
1302  case 2: drawbins[0].width = drawbins[1].width = (drawbins[1].grx1-drawbins[0].grx1)/2; break;
1303  default:
1304  drawbins[0].width = drawbins[1].width;
1305  drawbins[drawbins.length-1].width = drawbins[drawbins.length-2].width;
1306  }
1307 
1308  var yy0 = Math.round(pmain.gry(0));
1309 
1310  nodes.append("svg:rect")
1311  .attr("x", function(d) { return Math.round(-d.width/2); })
1312  .attr("y", function(d) {
1313  d.bar = true; // element drawn as bar
1314  if (pthis.options.Bar!==1) return 0;
1315  return (d.gry1 > yy0) ? yy0-d.gry1 : 0;
1316  })
1317  .attr("width", function(d) { return Math.round(d.width); })
1318  .attr("height", function(d) {
1319  if (pthis.options.Bar!==1) return h > d.gry1 ? h - d.gry1 : 0;
1320  return Math.abs(yy0 - d.gry1);
1321  })
1322  .call(this.fillatt.func);
1323  }
1324 
1325  if (this.options.Rect)
1326  nodes.filter(function(d) { return (d.exlow > 0) && (d.exhigh > 0) && (d.eylow > 0) && (d.eyhigh > 0); })
1327  .append("svg:rect")
1328  .attr("x", function(d) { d.rect = true; return d.grx0; })
1329  .attr("y", function(d) { return d.gry2; })
1330  .attr("width", function(d) { return d.grx2 - d.grx0; })
1331  .attr("height", function(d) { return d.gry0 - d.gry2; })
1332  .call(this.fillatt.func)
1333  .call(this.options.Rect === 2 ? this.lineatt.func : function() {});
1334 
1335  this.error_size = 0;
1336 
1337  if (this.options.Errors) {
1338  // to show end of error markers, use line width attribute
1339  var lw = this.lineatt.width + JSROOT.gStyle.fEndErrorSize, bb = 0,
1340  vv = this.options.Ends ? "m0," + lw + "v-" + 2*lw : "",
1341  hh = this.options.Ends ? "m" + lw + ",0h-" + 2*lw : "",
1342  vleft = vv, vright = vv, htop = hh, hbottom = hh,
1343  mm = this.options.MainError ? "M0,0L" : "M"; // command to draw main errors
1344 
1345  switch (this.options.Ends) {
1346  case 2: // option []
1347  bb = Math.max(this.lineatt.width+1, Math.round(lw*0.66));
1348  vleft = "m"+bb+","+lw + "h-"+bb + "v-"+2*lw + "h"+bb;
1349  vright = "m-"+bb+","+lw + "h"+bb + "v-"+2*lw + "h-"+bb;
1350  htop = "m-"+lw+","+bb + "v-"+bb + "h"+2*lw + "v"+bb;
1351  hbottom = "m-"+lw+",-"+bb + "v"+bb + "h"+2*lw + "v-"+bb;
1352  break;
1353  case 3: // option |>
1354  lw = Math.max(lw, Math.round(graph.fMarkerSize*8*0.66));
1355  bb = Math.max(this.lineatt.width+1, Math.round(lw*0.66));
1356  vleft = "l"+bb+","+lw + "v-"+2*lw + "l-"+bb+","+lw;
1357  vright = "l-"+bb+","+lw + "v-"+2*lw + "l"+bb+","+lw;
1358  htop = "l-"+lw+","+bb + "h"+2*lw + "l-"+lw+",-"+bb;
1359  hbottom = "l-"+lw+",-"+bb + "h"+2*lw + "l-"+lw+","+bb;
1360  break;
1361  case 4: // option >
1362  lw = Math.max(lw, Math.round(graph.fMarkerSize*8*0.66));
1363  bb = Math.max(this.lineatt.width+1, Math.round(lw*0.66));
1364  vleft = "l"+bb+","+lw + "m0,-"+2*lw + "l-"+bb+","+lw;
1365  vright = "l-"+bb+","+lw + "m0,-"+2*lw + "l"+bb+","+lw;
1366  htop = "l-"+lw+","+bb + "m"+2*lw + ",0l-"+lw+",-"+bb;
1367  hbottom = "l-"+lw+",-"+bb + "m"+2*lw + ",0l-"+lw+","+bb;
1368  break;
1369  }
1370 
1371  this.error_size = lw;
1372 
1373  lw = Math.floor((this.lineatt.width-1)/2); // one should take into account half of end-cup line width
1374  nodes.filter(function(d) { return (d.exlow > 0) || (d.exhigh > 0) || (d.eylow > 0) || (d.eyhigh > 0); })
1375  .append("svg:path")
1376  .call(this.lineatt.func)
1377  .style('fill', "none")
1378  .attr("d", function(d) {
1379  d.error = true;
1380  return ((d.exlow > 0) ? mm + (d.grx0+lw) + "," + d.grdx0 + vleft : "") +
1381  ((d.exhigh > 0) ? mm + (d.grx2-lw) + "," + d.grdx2 + vright : "") +
1382  ((d.eylow > 0) ? mm + d.grdy0 + "," + (d.gry0-lw) + hbottom : "") +
1383  ((d.eyhigh > 0) ? mm + d.grdy2 + "," + (d.gry2+lw) + htop : "");
1384  });
1385  }
1386 
1387  if (this.options.Mark) {
1388  // for tooltips use markers only if nodes where not created
1389  var path = "", pnt, grx, gry;
1390 
1391  this.createAttMarker({ attr: graph, style: this.options.Mark - 100 });
1392 
1393  this.marker_size = this.markeratt.GetFullSize();
1394 
1395  this.markeratt.reset_pos();
1396 
1397  // let produce SVG at maximum 1MB
1398  var maxnummarker = 1000000 / (this.markeratt.MarkerLength() + 7), step = 1;
1399 
1400  if (!drawbins) drawbins = this.OptimizeBins(maxnummarker); else
1401  if (this.CanOptimize() && (drawbins.length > 1.5*maxnummarker))
1402  step = Math.min(2, Math.round(drawbins.length/maxnummarker));
1403 
1404  for (var n=0;n<drawbins.length;n+=step) {
1405  pnt = drawbins[n];
1406  grx = pmain.grx(pnt.x);
1407  if ((grx > -this.marker_size) && (grx < w + this.marker_size)) {
1408  gry = pmain.gry(pnt.y);
1409  if ((gry > -this.marker_size) && (gry < h + this.marker_size))
1410  path += this.markeratt.create(grx, gry);
1411  }
1412  }
1413 
1414  if (path.length>0) {
1415  this.draw_g.append("svg:path")
1416  .attr("d", path)
1417  .call(this.markeratt.func);
1418  if ((nodes===null) && (this.draw_kind=="none"))
1419  this.draw_kind = (this.options.Mark==101) ? "path" : "mark";
1420 
1421  }
1422  }
1423  }
1424 
1425  TGraphPainter.prototype.ExtractTooltip = function(pnt) {
1426  if (!pnt) return null;
1427 
1428  if ((this.draw_kind=="lines") || (this.draw_kind=="path") || (this.draw_kind=="mark"))
1429  return this.ExtractTooltipForPath(pnt);
1430 
1431  if (this.draw_kind!="nodes") return null;
1432 
1433  var width = this.frame_width(),
1434  height = this.frame_height(),
1435  pmain = this.frame_painter(),
1436  painter = this,
1437  findbin = null, best_dist2 = 1e10, best = null,
1438  msize = this.marker_size ? Math.round(this.marker_size/2 + 1.5) : 0;
1439 
1440  this.draw_g.selectAll('.grpoint').each(function() {
1441  var d = d3.select(this).datum();
1442  if (d===undefined) return;
1443  var dist2 = Math.pow(pnt.x - d.grx1, 2);
1444  if (pnt.nproc===1) dist2 += Math.pow(pnt.y - d.gry1, 2);
1445  if (dist2 >= best_dist2) return;
1446 
1447  var rect = null;
1448 
1449  if (d.error || d.rect || d.marker) {
1450  rect = { x1: Math.min(-painter.error_size, d.grx0, -msize),
1451  x2: Math.max(painter.error_size, d.grx2, msize),
1452  y1: Math.min(-painter.error_size, d.gry2, -msize),
1453  y2: Math.max(painter.error_size, d.gry0, msize) };
1454  } else if (d.bar) {
1455  rect = { x1: -d.width/2, x2: d.width/2, y1: 0, y2: height - d.gry1 };
1456 
1457  if (painter.options.Bar===1) {
1458  var yy0 = pmain.gry(0);
1459  rect.y1 = (d.gry1 > yy0) ? yy0-d.gry1 : 0;
1460  rect.y2 = (d.gry1 > yy0) ? 0 : yy0-d.gry1;
1461  }
1462  } else {
1463  rect = { x1: -5, x2: 5, y1: -5, y2: 5 };
1464  }
1465  var matchx = (pnt.x >= d.grx1 + rect.x1) && (pnt.x <= d.grx1 + rect.x2),
1466  matchy = (pnt.y >= d.gry1 + rect.y1) && (pnt.y <= d.gry1 + rect.y2);
1467 
1468  if (matchx && (matchy || (pnt.nproc > 1))) {
1469  best_dist2 = dist2;
1470  findbin = this;
1471  best = rect;
1472  best.exact = matchx && matchy;
1473  }
1474  });
1475 
1476  if (findbin === null) return null;
1477 
1478  var d = d3.select(findbin).datum();
1479 
1480  var res = { name: this.GetObject().fName, title: this.GetObject().fTitle,
1481  x: d.grx1, y: d.gry1,
1482  color1: this.lineatt.color,
1483  lines: this.TooltipText(d),
1484  rect: best, d3bin: findbin };
1485 
1486  if (this.fillatt && this.fillatt.used && !this.fillatt.empty()) res.color2 = this.fillatt.fillcolor();
1487 
1488  if (best.exact) res.exact = true;
1489  res.menu = res.exact; // activate menu only when exactly locate bin
1490  res.menu_dist = 3; // distance always fixed
1491  res.bin = d;
1492  res.binindx = d.indx;
1493 
1494  return res;
1495  }
1496 
1497  TGraphPainter.prototype.ShowTooltip = function(hint) {
1498 
1499  if (!hint) {
1500  if (this.draw_g) this.draw_g.select(".tooltip_bin").remove();
1501  return;
1502  }
1503 
1504  if (hint.usepath) return this.ShowTooltipForPath(hint);
1505 
1506  var d = d3.select(hint.d3bin).datum();
1507 
1508  var ttrect = this.draw_g.select(".tooltip_bin");
1509 
1510  if (ttrect.empty())
1511  ttrect = this.draw_g.append("svg:rect")
1512  .attr("class","tooltip_bin h1bin")
1513  .style("pointer-events","none");
1514 
1515  hint.changed = ttrect.property("current_bin") !== hint.d3bin;
1516 
1517  if (hint.changed)
1518  ttrect.attr("x", d.grx1 + hint.rect.x1)
1519  .attr("width", hint.rect.x2 - hint.rect.x1)
1520  .attr("y", d.gry1 + hint.rect.y1)
1521  .attr("height", hint.rect.y2 - hint.rect.y1)
1522  .style("opacity", "0.3")
1523  .property("current_bin", hint.d3bin);
1524  }
1525 
1526  TGraphPainter.prototype.ProcessTooltip = function(pnt) {
1527  var hint = this.ExtractTooltip(pnt);
1528  if (!pnt || !pnt.disabled) this.ShowTooltip(hint);
1529  return hint;
1530  }
1531 
1532  TGraphPainter.prototype.FindBestBin = function(pnt) {
1533  if (!this.bins) return null;
1534 
1535  var islines = (this.draw_kind=="lines"),
1536  ismark = (this.draw_kind=="mark"),
1537  bestindx = -1,
1538  bestbin = null,
1539  bestdist = 1e10,
1540  pmain = this.frame_painter(),
1541  dist, grx, gry, n, bin;
1542 
1543  for (n=0;n<this.bins.length;++n) {
1544  bin = this.bins[n];
1545 
1546  grx = pmain.grx(bin.x);
1547  gry = pmain.gry(bin.y);
1548 
1549  dist = (pnt.x-grx)*(pnt.x-grx) + (pnt.y-gry)*(pnt.y-gry);
1550 
1551  if (dist < bestdist) {
1552  bestdist = dist;
1553  bestbin = bin;
1554  bestindx = n;
1555  }
1556  }
1557 
1558  // check last point
1559  if ((bestdist > 100) && islines) bestbin = null;
1560 
1561  var radius = Math.max(this.lineatt.width + 3, 4);
1562 
1563  if (this.marker_size > 0) radius = Math.max(this.marker_size, radius);
1564 
1565  if (bestbin)
1566  bestdist = Math.sqrt(Math.pow(pnt.x-pmain.grx(bestbin.x),2) + Math.pow(pnt.y-pmain.gry(bestbin.y),2));
1567 
1568  if (!islines && (bestdist > radius)) bestbin = null;
1569 
1570  if (!bestbin) bestindx = -1;
1571 
1572  var res = { bin: bestbin, indx: bestindx, dist: bestdist, radius: Math.round(radius) };
1573 
1574  if (!bestbin && islines) {
1575 
1576  bestdist = 10000;
1577 
1578  function IsInside(x, x1, x2) {
1579  return ((x1>=x) && (x>=x2)) || ((x1<=x) && (x<=x2));
1580  }
1581 
1582  var bin0 = this.bins[0], grx0 = pmain.grx(bin0.x), gry0, posy = 0;
1583  for (n=1;n<this.bins.length;++n) {
1584  bin = this.bins[n];
1585  grx = pmain.grx(bin.x);
1586 
1587  if (IsInside(pnt.x, grx0, grx)) {
1588  // if inside interval, check Y distance
1589  gry0 = pmain.gry(bin0.y)
1590  gry = pmain.gry(bin.y);
1591 
1592  if (Math.abs(grx - grx0) < 1) {
1593  // very close x - check only y
1594  posy = pnt.y;
1595  dist = IsInside(pnt.y, gry0, gry) ? 0 : Math.min(Math.abs(pnt.y-gry0), Math.abs(pnt.y-gry));
1596  } else {
1597  posy = gry0 + (pnt.x - grx0) / (grx - grx0) * (gry - gry0);
1598  dist = Math.abs(posy - pnt.y);
1599  }
1600 
1601  if (dist < bestdist) {
1602  bestdist = dist;
1603  res.linex = pnt.x;
1604  res.liney = posy;
1605  }
1606  }
1607 
1608  bin0 = bin;
1609  grx0 = grx;
1610  }
1611 
1612  if (bestdist < radius*0.5) {
1613  res.linedist = bestdist;
1614  res.closeline = true;
1615  }
1616  }
1617 
1618  return res;
1619  }
1620 
1621  TGraphPainter.prototype.TestEditable = function(toggle) {
1622  var obj = this.GetObject(),
1623  kNotEditable = JSROOT.BIT(18); // bit set if graph is non editable
1624 
1625  if (!obj) return false;
1626  if (toggle) obj.InvertBit(kNotEditable);
1627  return !obj.TestBit(kNotEditable);
1628  }
1629 
1630  TGraphPainter.prototype.ExtractTooltipForPath = function(pnt) {
1631 
1632  if (this.bins === null) return null;
1633 
1634  var best = this.FindBestBin(pnt);
1635 
1636  if (!best || (!best.bin && !best.closeline)) return null;
1637 
1638  var islines = (this.draw_kind=="lines"),
1639  ismark = (this.draw_kind=="mark"),
1640  pmain = this.frame_painter(),
1641  gr = this.GetObject(),
1642  res = { name: gr.fName, title: gr.fTitle,
1643  x: best.bin ? pmain.grx(best.bin.x) : best.linex,
1644  y: best.bin ? pmain.gry(best.bin.y) : best.liney,
1645  color1: this.lineatt.color,
1646  lines: this.TooltipText(best.bin),
1647  usepath: true };
1648 
1649  res.ismark = ismark;
1650  res.islines = islines;
1651 
1652  if (best.closeline) {
1653  res.menu = res.exact = true;
1654  res.menu_dist = best.linedist;
1655  } else if (best.bin) {
1656  if (this.options.EF && islines) {
1657  res.gry1 = pmain.gry(best.bin.y - best.bin.eylow);
1658  res.gry2 = pmain.gry(best.bin.y + best.bin.eyhigh);
1659  } else {
1660  res.gry1 = res.gry2 = pmain.gry(best.bin.y);
1661  }
1662 
1663  res.binindx = best.indx;
1664  res.bin = best.bin;
1665  res.radius = best.radius;
1666 
1667  res.exact = (Math.abs(pnt.x - res.x) <= best.radius) &&
1668  ((Math.abs(pnt.y - res.gry1) <= best.radius) || (Math.abs(pnt.y - res.gry2) <= best.radius));
1669 
1670  res.menu = res.exact;
1671  res.menu_dist = Math.sqrt((pnt.x-res.x)*(pnt.x-res.x) + Math.pow(Math.min(Math.abs(pnt.y-res.gry1),Math.abs(pnt.y-res.gry2)),2));
1672  }
1673 
1674  if (this.fillatt && this.fillatt.used && !this.fillatt.empty()) res.color2 = this.fillatt.fillcolor();
1675 
1676  if (!islines) {
1677  res.color1 = this.get_color(gr.fMarkerColor);
1678  if (!res.color2) res.color2 = res.color1;
1679  }
1680 
1681  return res;
1682  }
1683 
1684  TGraphPainter.prototype.ShowTooltipForPath = function(hint) {
1685 
1686  var ttbin = this.draw_g.select(".tooltip_bin");
1687 
1688  if (!hint || !hint.bin) {
1689  ttbin.remove();
1690  return;
1691  }
1692 
1693  if (ttbin.empty())
1694  ttbin = this.draw_g.append("svg:g")
1695  .attr("class","tooltip_bin");
1696 
1697  hint.changed = ttbin.property("current_bin") !== hint.bin;
1698 
1699  if (hint.changed) {
1700  ttbin.selectAll("*").remove(); // first delete all children
1701  ttbin.property("current_bin", hint.bin);
1702 
1703  if (hint.ismark) {
1704  ttbin.append("svg:rect")
1705  .attr("class","h1bin")
1706  .style("pointer-events","none")
1707  .style("opacity", "0.3")
1708  .attr("x", Math.round(hint.x - hint.radius))
1709  .attr("y", Math.round(hint.y - hint.radius))
1710  .attr("width", 2*hint.radius)
1711  .attr("height", 2*hint.radius);
1712  } else {
1713  ttbin.append("svg:circle").attr("cy", Math.round(hint.gry1))
1714  if (Math.abs(hint.gry1-hint.gry2) > 1)
1715  ttbin.append("svg:circle").attr("cy", Math.round(hint.gry2));
1716 
1717  var elem = ttbin.selectAll("circle")
1718  .attr("r", hint.radius)
1719  .attr("cx", Math.round(hint.x));
1720 
1721  if (!hint.islines) {
1722  elem.style('stroke', hint.color1 == 'black' ? 'green' : 'black').style('fill','none');
1723  } else {
1724  if (this.options.Line)
1725  elem.call(this.lineatt.func);
1726  else
1727  elem.style('stroke','black');
1728  if (this.options.Fill)
1729  elem.call(this.fillatt.func);
1730  else
1731  elem.style('fill','none');
1732  }
1733  }
1734  }
1735  }
1736 
1738  TGraphPainter.prototype.moveStart = function(x,y) {
1739  this.pos_dx = this.pos_dy = 0;
1740  var hint = this.ExtractTooltip({x:x, y:y});
1741  if (hint && hint.exact && (hint.binindx !== undefined)) {
1742  this.move_binindx = hint.binindx;
1743  this.move_bin = hint.bin;
1744  var main = this.frame_painter();
1745  this.move_x0 = main ? main.grx(this.move_bin.x) : x;
1746  this.move_y0 = main ? main.gry(this.move_bin.y) : y;
1747  } else {
1748  delete this.move_binindx;
1749  }
1750  }
1751 
1753  TGraphPainter.prototype.moveDrag = function(dx,dy) {
1754  this.pos_dx += dx;
1755  this.pos_dy += dy;
1756 
1757  if (this.move_binindx === undefined) {
1758  this.draw_g.attr("transform", "translate(" + this.pos_dx + "," + this.pos_dy + ")");
1759  } else {
1760  var main = this.frame_painter();
1761  if (main && this.move_bin) {
1762  this.move_bin.x = main.RevertX(this.move_x0 + this.pos_dx);
1763  this.move_bin.y = main.RevertY(this.move_y0 + this.pos_dy);
1764  this.DrawBins();
1765  }
1766  }
1767  }
1768 
1770  TGraphPainter.prototype.moveEnd = function(not_changed) {
1771  var exec = "";
1772 
1773  if (this.move_binindx === undefined) {
1774 
1775  this.draw_g.attr("transform", null);
1776 
1777  var main = this.frame_painter();
1778  if (main && this.bins && !not_changed) {
1779  for (var k=0;k<this.bins.length;++k) {
1780  var bin = this.bins[k];
1781  bin.x = main.RevertX(main.grx(bin.x) + this.pos_dx);
1782  bin.y = main.RevertY(main.gry(bin.y) + this.pos_dy);
1783  exec += "SetPoint(" + bin.indx + "," + bin.x + "," + bin.y + ");;";
1784  if ((bin.indx == 0) && this.MatchObjectType('TCutG'))
1785  exec += "SetPoint(" + (this.GetObject().fNpoints-1) + "," + bin.x + "," + bin.y + ");;";
1786  }
1787  this.DrawBins();
1788  }
1789  } else {
1790  var exec = "SetPoint(" + this.move_bin.indx + "," + this.move_bin.x + "," + this.move_bin.y + ")";
1791  if ((this.move_bin.indx == 0) && this.MatchObjectType('TCutG'))
1792  exec += ";;SetPoint(" + (this.GetObject().fNpoints-1) + "," + this.move_bin.x + "," + this.move_bin.y + ")";
1793  delete this.move_binindx;
1794  }
1795 
1796  if (exec && !not_changed)
1797  this.WebCanvasExec(exec);
1798  }
1799 
1800  TGraphPainter.prototype.FillContextMenu = function(menu) {
1801  JSROOT.TObjectPainter.prototype.FillContextMenu.call(this, menu);
1802 
1803  if (!this.snapid)
1804  menu.addchk(this.TestEditable(), "Editable", this.TestEditable.bind(this, true));
1805 
1806  return menu.size() > 0;
1807  }
1808 
1809  TGraphPainter.prototype.ExecuteMenuCommand = function(method, args) {
1810  if (JSROOT.TObjectPainter.prototype.ExecuteMenuCommand.call(this,method,args)) return true;
1811 
1812  var canp = this.canv_painter(), fp = this.frame_painter();
1813 
1814  if ((method.fName == 'RemovePoint') || (method.fName == 'InsertPoint')) {
1815  var pnt = fp ? fp.GetLastEventPos() : null;
1816 
1817  if (!canp || canp._readonly || !fp || !pnt) return true; // ignore function
1818 
1819  var hint = this.ExtractTooltip(pnt);
1820 
1821  if (method.fName == 'InsertPoint') {
1822  var main = this.frame_painter(),
1823  userx = main && main.RevertX ? main.RevertX(pnt.x) : 0,
1824  usery = main && main.RevertY ? main.RevertY(pnt.y) : 0;
1825  canp.ShowMessage('InsertPoint(' + userx.toFixed(3) + ',' + usery.toFixed(3) + ') not yet implemented');
1826  } else if (this.args_menu_id && hint && (hint.binindx !== undefined)) {
1827  this.WebCanvasExec("RemovePoint(" + hint.binindx + ")", this.args_menu_id);
1828  }
1829 
1830  return true; // call is processed
1831  }
1832 
1833  return false;
1834  }
1835 
1836  TGraphPainter.prototype.UpdateObject = function(obj, opt) {
1837  if (!this.MatchObjectType(obj)) return false;
1838 
1839  if ((opt !== undefined) && (opt != this.options.original))
1840  this.DecodeOptions(opt);
1841 
1842  var graph = this.GetObject();
1843  // TODO: make real update of TGraph object content
1844  graph.fBits = obj.fBits;
1845  graph.fTitle = obj.fTitle;
1846  graph.fX = obj.fX;
1847  graph.fY = obj.fY;
1848  graph.fNpoints = obj.fNpoints;
1849  this.CreateBins();
1850 
1851  // if our own histogram was used as axis drawing, we need update histogram as well
1852  if (this.axes_draw) {
1853  var main = this.main_painter(),
1854  fp = this.frame_painter();
1855 
1856  // if zoom was changed - do not update histogram
1857  if (!fp.zoom_changed_interactive)
1858  main.UpdateObject(obj.fHistogram || this.CreateHistogram());
1859 
1860  main.GetObject().fTitle = graph.fTitle; // copy title
1861  }
1862 
1863  return true;
1864  }
1865 
1866  TGraphPainter.prototype.CanZoomIn = function(axis,min,max) {
1867  // allow to zoom TGraph only when at least one point in the range
1868 
1869  var gr = this.GetObject();
1870  if ((gr===null) || (axis!=="x")) return false;
1871 
1872  for (var n=0; n < gr.fNpoints; ++n)
1873  if ((min < gr.fX[n]) && (gr.fX[n] < max)) return true;
1874 
1875  return false;
1876  }
1877 
1878  TGraphPainter.prototype.ButtonClick = function(funcname) {
1879 
1880  if (funcname !== "ToggleZoom") return false;
1881 
1882  var main = this.frame_painter();
1883  if (!main) return false;
1884 
1885  if ((this.xmin===this.xmax) && (this.ymin===this.ymax)) return false;
1886 
1887  main.Zoom(this.xmin, this.xmax, this.ymin, this.ymax);
1888 
1889  return true;
1890  }
1891 
1892  TGraphPainter.prototype.FindFunc = function() {
1893  var gr = this.GetObject();
1894  if (gr && gr.fFunctions)
1895  for (var i = 0; i < gr.fFunctions.arr.length; ++i) {
1896  var func = gr.fFunctions.arr[i];
1897  if ((func._typename == 'TF1') || (func._typename == 'TF2')) return func;
1898  }
1899  return null;
1900  }
1901 
1902  TGraphPainter.prototype.FindStat = function() {
1903  var gr = this.GetObject();
1904  if (gr && gr.fFunctions)
1905  for (var i = 0; i < gr.fFunctions.arr.length; ++i) {
1906  var func = gr.fFunctions.arr[i];
1907  if ((func._typename == 'TPaveStats') && (func.fName == 'stats')) return func;
1908  }
1909 
1910  return null;
1911  }
1912 
1913  TGraphPainter.prototype.CreateStat = function() {
1914  var func = this.FindFunc();
1915  if (!func) return null;
1916 
1917  var stats = this.FindStat();
1918  if (stats) return stats;
1919 
1920  // do not create stats box when drawing canvas
1921  var pp = this.canv_painter();
1922  if (pp && pp.normal_canvas) return null;
1923 
1924  if (this.options.PadStats) return null;
1925 
1926  this.create_stats = true;
1927 
1928  var st = JSROOT.gStyle;
1929 
1930  stats = JSROOT.Create('TPaveStats');
1931  JSROOT.extend(stats, { fName : 'stats',
1932  fOptStat: 0,
1933  fOptFit: st.fOptFit || 111,
1934  fBorderSize : 1} );
1935 
1936  stats.fX1NDC = st.fStatX - st.fStatW;
1937  stats.fY1NDC = st.fStatY - st.fStatH;
1938  stats.fX2NDC = st.fStatX;
1939  stats.fY2NDC = st.fStatY;
1940 
1941  stats.fFillColor = st.fStatColor;
1942  stats.fFillStyle = st.fStatStyle;
1943 
1944  stats.fTextAngle = 0;
1945  stats.fTextSize = st.fStatFontSize; // 9 ??
1946  stats.fTextAlign = 12;
1947  stats.fTextColor = st.fStatTextColor;
1948  stats.fTextFont = st.fStatFont;
1949 
1950  stats.AddText(func.fName);
1951 
1952  // while TF1 was found, one can be sure that stats is existing
1953  this.GetObject().fFunctions.Add(stats);
1954 
1955  return stats;
1956  }
1957 
1958  TGraphPainter.prototype.FillStatistic = function(stat, dostat, dofit) {
1959 
1960  // cannot fill stats without func
1961  var func = this.FindFunc();
1962 
1963  if (!func || !dofit || !this.create_stats) return false;
1964 
1965  stat.ClearPave();
1966 
1967  stat.FillFunctionStat(func, dofit);
1968 
1969  return true;
1970  }
1971 
1972  TGraphPainter.prototype.DrawNextFunction = function(indx, callback) {
1973  // method draws next function from the functions list
1974 
1975  var graph = this.GetObject();
1976 
1977  if (!graph.fFunctions || (indx >= graph.fFunctions.arr.length))
1978  return JSROOT.CallBack(callback);
1979 
1980  var func = graph.fFunctions.arr[indx], opt = graph.fFunctions.opt[indx];
1981 
1982  // required for stats filling
1983  // TODO: use weak reference (via pad list of painters and any kind of string)
1984  func.$main_painter = this;
1985 
1986  JSROOT.draw(this.divid, func, opt, this.DrawNextFunction.bind(this, indx+1, callback));
1987  }
1988 
1989  TGraphPainter.prototype.PerformDrawing = function(divid, hpainter) {
1990  if (hpainter) {
1991  this.axes_draw = true;
1992  if (!this._own_histogram) this.$primary = true;
1993  hpainter.$secondary = true;
1994  }
1995  this.SetDivId(divid);
1996  this.DrawBins();
1997  if (this.TestEditable()) this.AddMove();
1998  this.DrawNextFunction(0, this.DrawingReady.bind(this));
1999  return this;
2000  }
2001 
2002  function drawGraph(divid, graph, opt) {
2003 
2004  var painter = new TGraphPainter(graph);
2005 
2006  painter.SetDivId(divid, -1); // just to get access to existing elements
2007 
2008  painter.DecodeOptions(opt);
2009 
2010  painter.CreateBins();
2011 
2012  painter.CreateStat();
2013 
2014  if (!painter.main_painter() && painter.options.HOptions) {
2015  JSROOT.draw(divid, painter.CreateHistogram(), painter.options.HOptions, painter.PerformDrawing.bind(painter, divid));
2016  } else {
2017  painter.PerformDrawing(divid);
2018  }
2019 
2020  return painter;
2021  }
2022 
2023  // ==============================================================
2024 
2025  function TGraphPolargramPainter(polargram) {
2026  JSROOT.TooltipHandler.call(this, polargram);
2027  this.$polargram = true; // indicate that this is polargram
2028  this.zoom_rmin = this.zoom_rmax = 0;
2029  }
2030 
2031  TGraphPolargramPainter.prototype = Object.create(JSROOT.TooltipHandler.prototype);
2032 
2033  TGraphPolargramPainter.prototype.translate = function(angle, radius, keep_float) {
2034  var _rx = this.r(radius), _ry = _rx/this.szx*this.szy,
2035  pos = {
2036  x: _rx * Math.cos(-angle - this.angle),
2037  y: _ry * Math.sin(-angle - this.angle),
2038  rx: _rx,
2039  ry: _ry
2040  };
2041 
2042  if (!keep_float) {
2043  pos.x = Math.round(pos.x);
2044  pos.y = Math.round(pos.y);
2045  pos.rx = Math.round(pos.rx);
2046  pos.ry = Math.round(pos.ry);
2047  }
2048  return pos;
2049  }
2050 
2051  TGraphPolargramPainter.prototype.format = function(radius) {
2052  // used to format label for radius ticks
2053 
2054  if (radius === Math.round(radius)) return radius.toString();
2055  if (this.ndig>10) return radius.toExponential(4);
2056 
2057  return radius.toFixed((this.ndig > 0) ? this.ndig : 0);
2058  }
2059 
2060  TGraphPolargramPainter.prototype.AxisAsText = function(axis, value) {
2061 
2062  if (axis == "r") {
2063  if (value === Math.round(value)) return value.toString();
2064  if (this.ndig>10) return value.toExponential(4);
2065  return value.toFixed(this.ndig+2);
2066  }
2067 
2068  value *= 180/Math.PI;
2069  return (value === Math.round(value)) ? value.toString() : value.toFixed(1);
2070  }
2071 
2072  TGraphPolargramPainter.prototype.MouseEvent = function(kind) {
2073  var layer = this.svg_layer("primitives_layer"),
2074  interactive = layer.select(".interactive_ellipse");
2075  if (interactive.empty()) return;
2076 
2077  var pnt = null;
2078 
2079  if (kind !== 'leave') {
2080  var pos = d3.mouse(interactive.node());
2081  pnt = { x: pos[0], y: pos[1], touch: false };
2082  }
2083 
2084  this.ProcessTooltipEvent(pnt);
2085  }
2086 
2087  TGraphPolargramPainter.prototype.GetFrameRect = function() {
2088  var pad = this.root_pad(),
2089  w = this.pad_width(),
2090  h = this.pad_height(),
2091  rect = {};
2092 
2093  rect.szx = Math.round(Math.max(0.1, 0.5 - Math.max(pad.fLeftMargin, pad.fRightMargin))*w);
2094  rect.szy = Math.round(Math.max(0.1, 0.5 - Math.max(pad.fBottomMargin, pad.fTopMargin))*h);
2095 
2096  rect.width = 2*rect.szx;
2097  rect.height = 2*rect.szy;
2098  rect.midx = Math.round(w/2);
2099  rect.midy = Math.round(h/2);
2100  rect.x = rect.midx - rect.szx;
2101  rect.y = rect.midy - rect.szy;
2102 
2103  rect.hint_delta_x = rect.szx;
2104  rect.hint_delta_y = rect.szy;
2105 
2106  rect.transform = "translate(" + rect.x + "," + rect.y + ")";
2107 
2108  return rect;
2109  }
2110 
2111  TGraphPolargramPainter.prototype.MouseWheel = function() {
2112  d3.event.stopPropagation();
2113  d3.event.preventDefault();
2114 
2115  this.ProcessTooltipEvent(null); // remove all tooltips
2116 
2117  var polar = this.GetObject();
2118 
2119  if (!d3.event || !polar) return;
2120 
2121  var delta = d3.event.wheelDelta ? -d3.event.wheelDelta : (d3.event.deltaY || d3.event.detail);
2122  if (!delta) return;
2123 
2124  delta = (delta<0) ? -0.2 : 0.2;
2125 
2126  var rmin = this.scale_rmin, rmax = this.scale_rmax, range = rmax - rmin;
2127 
2128  // rmin -= delta*range;
2129  rmax += delta*range;
2130 
2131  if ((rmin<polar.fRwrmin) || (rmax>polar.fRwrmax)) rmin = rmax = 0;
2132 
2133  if ((this.zoom_rmin != rmin) || (this.zoom_rmax != rmax)) {
2134  this.zoom_rmin = rmin;
2135  this.zoom_rmax = rmax;
2136  this.RedrawPad();
2137  }
2138  }
2139 
2140  TGraphPolargramPainter.prototype.Redraw = function() {
2141  if (!this.is_main_painter()) return;
2142 
2143  var pad = this.root_pad(),
2144  polar = this.GetObject(),
2145  rect = this.GetFrameRect();
2146 
2147  this.CreateG();
2148 
2149  this.draw_g.attr("transform", "translate(" + rect.midx + "," + rect.midy + ")");
2150  this.szx = rect.szx;
2151  this.szy = rect.szy;
2152 
2153  this.scale_rmin = polar.fRwrmin;
2154  this.scale_rmax = polar.fRwrmax;
2155  if (this.zoom_rmin != this.zoom_rmax) {
2156  this.scale_rmin = this.zoom_rmin;
2157  this.scale_rmax = this.zoom_rmax;
2158  }
2159 
2160  this.r = d3.scaleLinear().domain([this.scale_rmin, this.scale_rmax]).range([ 0, this.szx ]);
2161  this.angle = polar.fAxisAngle || 0;
2162 
2163  var ticks = this.r.ticks(5),
2164  nminor = Math.floor((polar.fNdivRad % 10000) / 100);
2165 
2166  this.createAttLine({ attr: polar });
2167  if (!this.gridatt) this.gridatt = new JSROOT.TAttLineHandler({ color: polar.fLineColor, style: 2, width: 1 });
2168 
2169  var range = Math.abs(polar.fRwrmax - polar.fRwrmin);
2170  this.ndig = (range <= 0) ? -3 : Math.round(JSROOT.log10(ticks.length / range));
2171 
2172  // verify that all radius labels are unique
2173  var lbls = [], indx = 0;
2174  while (indx<ticks.length) {
2175  var lbl = this.format(ticks[indx]);
2176  if (lbls.indexOf(lbl)>=0) {
2177  if (++this.ndig>10) break;
2178  lbls = []; indx = 0; continue;
2179  }
2180  lbls.push(lbl);
2181  indx++;
2182  }
2183 
2184  var exclude_last = false;
2185 
2186  if ((ticks[ticks.length-1] < polar.fRwrmax) && (this.zoom_rmin == this.zoom_rmax)) {
2187  ticks.push(polar.fRwrmax);
2188  exclude_last = true;
2189  }
2190 
2191  this.StartTextDrawing(polar.fRadialLabelFont, Math.round(polar.fRadialTextSize * this.szy * 2));
2192 
2193  for (var n=0;n<ticks.length;++n) {
2194  var rx = this.r(ticks[n]), ry = rx/this.szx*this.szy;
2195  this.draw_g.append("ellipse")
2196  .attr("cx",0)
2197  .attr("cy",0)
2198  .attr("rx",Math.round(rx))
2199  .attr("ry",Math.round(ry))
2200  .style("fill", "none")
2201  .call(this.lineatt.func);
2202 
2203  if ((n < ticks.length-1) || !exclude_last)
2204  this.DrawText({ align: 23, x: Math.round(rx), y: Math.round(polar.fRadialTextSize * this.szy * 0.5),
2205  text: this.format(ticks[n]), color: this.get_color[polar.fRadialLabelColor], latex: 0 });
2206 
2207  if ((nminor>1) && ((n < ticks.length-1) || !exclude_last)) {
2208  var dr = (ticks[1] - ticks[0]) / nminor;
2209  for (var nn=1;nn<nminor;++nn) {
2210  var gridr = ticks[n] + dr*nn;
2211  if (gridr > this.scale_rmax) break;
2212  rx = this.r(gridr); ry = rx/this.szx*this.szy;
2213  this.draw_g.append("ellipse")
2214  .attr("cx",0)
2215  .attr("cy",0)
2216  .attr("rx",Math.round(rx))
2217  .attr("ry",Math.round(ry))
2218  .style("fill", "none")
2219  .call(this.gridatt.func);
2220  }
2221  }
2222  }
2223 
2224  this.FinishTextDrawing();
2225 
2226  var fontsize = Math.round(polar.fPolarTextSize * this.szy * 2);
2227  this.StartTextDrawing(polar.fPolarLabelFont, fontsize);
2228 
2229  var nmajor = polar.fNdivPol % 100;
2230  if ((nmajor !== 8) && (nmajor !== 3)) nmajor = 8;
2231 
2232  var lbls = (nmajor==8) ? ["0", "#frac{#pi}{4}", "#frac{#pi}{2}", "#frac{3#pi}{4}", "#pi", "#frac{5#pi}{4}", "#frac{3#pi}{2}", "#frac{7#pi}{4}"] : ["0", "#frac{2#pi}{3}", "#frac{4#pi}{3}"],
2233  aligns = [12, 11, 21, 31, 32, 33, 23, 13 ];
2234 
2235  for (var n=0;n<nmajor;++n) {
2236  var angle = -n*2*Math.PI/nmajor - this.angle;
2237  this.draw_g.append("line")
2238  .attr("x1",0)
2239  .attr("y1",0)
2240  .attr("x2", Math.round(this.szx*Math.cos(angle)))
2241  .attr("y2", Math.round(this.szy*Math.sin(angle)))
2242  .call(this.lineatt.func);
2243 
2244  var aindx = Math.round(16 -angle/Math.PI*4) % 8; // index in align table, here absolute angle is important
2245 
2246  this.DrawText({ align: aligns[aindx],
2247  x: Math.round((this.szx+fontsize)*Math.cos(angle)),
2248  y: Math.round((this.szy + fontsize/this.szx*this.szy)*(Math.sin(angle))),
2249  text: lbls[n],
2250  color: this.get_color[polar.fPolarLabelColor], latex: 1 });
2251  }
2252 
2253  this.FinishTextDrawing();
2254 
2255  var nminor = Math.floor((polar.fNdivPol % 10000) / 100);
2256 
2257  if (nminor > 1)
2258  for (var n=0;n<nmajor*nminor;++n) {
2259  if (n % nminor === 0) continue;
2260  var angle = -n*2*Math.PI/nmajor/nminor - this.angle;
2261  this.draw_g.append("line")
2262  .attr("x1",0)
2263  .attr("y1",0)
2264  .attr("x2", Math.round(this.szx*Math.cos(angle)))
2265  .attr("y2", Math.round(this.szy*Math.sin(angle)))
2266  .call(this.gridatt.func);
2267  }
2268 
2269 
2270  if (JSROOT.BatchMode) return;
2271 
2272  var layer = this.svg_layer("primitives_layer"),
2273  interactive = layer.select(".interactive_ellipse");
2274 
2275  if (interactive.empty())
2276  interactive = layer.append("g")
2277  .classed("most_upper_primitives", true)
2278  .append("ellipse")
2279  .classed("interactive_ellipse", true)
2280  .attr("cx",0)
2281  .attr("cy",0)
2282  .style("fill", "none")
2283  .style("pointer-events","visibleFill")
2284  .on('mouseenter', this.MouseEvent.bind(this,'enter'))
2285  .on('mousemove', this.MouseEvent.bind(this,'move'))
2286  .on('mouseleave', this.MouseEvent.bind(this,'leave'));
2287 
2288  interactive.attr("rx", this.szx).attr("ry", this.szy);
2289 
2290  d3.select(interactive.node().parentNode).attr("transform", this.draw_g.attr("transform"));
2291 
2292  if (JSROOT.gStyle.Zooming && JSROOT.gStyle.ZoomWheel)
2293  interactive.on("wheel", this.MouseWheel.bind(this));
2294  }
2295 
2296  function drawGraphPolargram(divid, polargram, opt) {
2297 
2298  var painter = new TGraphPolargramPainter(polargram);
2299 
2300  painter.SetDivId(divid, -1); // just to get access to existing elements
2301 
2302  var main = painter.main_painter();
2303 
2304  if (main) {
2305  if (main.GetObject() !== polargram)
2306  console.error('Cannot superimpose TGraphPolargram with any other drawings');
2307  return null;
2308  }
2309 
2310  painter.SetDivId(divid, 4); // main object without need of frame
2311  painter.Redraw();
2312  return painter.DrawingReady();
2313  }
2314 
2315  // ==============================================================
2316 
2317  function TGraphPolarPainter(graph) {
2318  JSROOT.TObjectPainter.call(this, graph);
2319  }
2320 
2321  TGraphPolarPainter.prototype = Object.create(JSROOT.TObjectPainter.prototype);
2322 
2323  TGraphPolarPainter.prototype.Redraw = function() {
2324  this.DrawBins();
2325  }
2326 
2327  TGraphPolarPainter.prototype.DecodeOptions = function(opt) {
2328 
2329  var d = new JSROOT.DrawOptions(opt || "L");
2330 
2331  if (!this.options) this.options = {};
2332 
2333  JSROOT.extend(this.options, {
2334  mark: d.check("P"),
2335  err: d.check("E"),
2336  fill: d.check("F"),
2337  line: d.check("L"),
2338  curve: d.check("C")
2339  });
2340 
2341  this.OptionsStore(opt);
2342  }
2343 
2344  TGraphPolarPainter.prototype.DrawBins = function() {
2345  var graph = this.GetObject(),
2346  main = this.main_painter();
2347 
2348  if (!graph || !main || !main.$polargram) return;
2349 
2350  if (this.options.mark) this.createAttMarker({ attr: graph });
2351  if (this.options.err || this.options.line || this.options.curve) this.createAttLine({ attr: graph });
2352  if (this.options.fill) this.createAttFill({ attr: graph });
2353 
2354  this.CreateG();
2355 
2356  this.draw_g.attr("transform", main.draw_g.attr("transform"));
2357 
2358  var mpath = "", epath = "", lpath = "", bins = [];
2359 
2360  for (var n=0;n<graph.fNpoints;++n) {
2361 
2362  if (graph.fY[n] > main.scale_rmax) continue;
2363 
2364  if (this.options.err) {
2365  var pos1 = main.translate(graph.fX[n], graph.fY[n] - graph.fEY[n]),
2366  pos2 = main.translate(graph.fX[n], graph.fY[n] + graph.fEY[n]);
2367  epath += "M" + pos1.x + "," + pos1.y + "L" + pos2.x + "," + pos2.y;
2368 
2369  pos1 = main.translate(graph.fX[n] + graph.fEX[n], graph.fY[n]);
2370  pos2 = main.translate(graph.fX[n] - graph.fEX[n], graph.fY[n]);
2371 
2372  epath += "M" + pos1.x + "," + pos1.y + "A" + pos2.rx + "," + pos2.ry+ ",0,0,1," + pos2.x + "," + pos2.y;
2373  }
2374 
2375  var pos = main.translate(graph.fX[n], graph.fY[n]);
2376 
2377  if (this.options.mark) {
2378  mpath += this.markeratt.create(pos.x, pos.y);
2379  }
2380 
2381  if (this.options.line || this.options.fill) {
2382  lpath += (lpath ? "L" : "M") + pos.x + "," + pos.y;
2383  }
2384 
2385  if (this.options.curve) {
2386  pos.grx = pos.x;
2387  pos.gry = pos.y;
2388  bins.push(pos);
2389  }
2390  }
2391 
2392  if (this.options.fill && lpath)
2393  this.draw_g.append("svg:path")
2394  .attr("d",lpath + "Z")
2395  .style("stroke","none")
2396  .call(this.fillatt.func);
2397 
2398  if (this.options.line && lpath)
2399  this.draw_g.append("svg:path")
2400  .attr("d", lpath)
2401  .style("fill", "none")
2402  .call(this.lineatt.func);
2403 
2404  if (this.options.curve && bins.length)
2405  this.draw_g.append("svg:path")
2406  .attr("d", JSROOT.Painter.BuildSvgPath("bezier", bins).path)
2407  .style("fill", "none")
2408  .call(this.lineatt.func);
2409 
2410  if (epath)
2411  this.draw_g.append("svg:path")
2412  .attr("d",epath)
2413  .style("fill","none")
2414  .call(this.lineatt.func);
2415 
2416  if (mpath)
2417  this.draw_g.append("svg:path")
2418  .attr("d",mpath)
2419  .call(this.markeratt.func);
2420 
2421  }
2422 
2423  TGraphPolarPainter.prototype.CreatePolargram = function() {
2424  var polargram = JSROOT.Create("TGraphPolargram"),
2425  gr = this.GetObject();
2426 
2427  var rmin = gr.fY[0] || 0, rmax = rmin;
2428  for (var n=0;n<gr.fNpoints;++n) {
2429  rmin = Math.min(rmin, gr.fY[n] - gr.fEY[n]);
2430  rmax = Math.max(rmax, gr.fY[n] + gr.fEY[n]);
2431  }
2432 
2433  polargram.fRwrmin = rmin - (rmax-rmin)*0.1;
2434  polargram.fRwrmax = rmax + (rmax-rmin)*0.1;
2435 
2436  return polargram;
2437  }
2438 
2439  TGraphPolarPainter.prototype.ExtractTooltip = function(pnt) {
2440  if (!pnt) return null;
2441 
2442  var graph = this.GetObject(),
2443  main = this.main_painter(),
2444  best_dist2 = 1e10, bestindx = -1, bestpos = null;
2445 
2446  for (var n=0;n<graph.fNpoints;++n) {
2447  var pos = main.translate(graph.fX[n], graph.fY[n]);
2448 
2449  var dist2 = (pos.x-pnt.x)*(pos.x-pnt.x) + (pos.y-pnt.y)*(pos.y-pnt.y);
2450  if (dist2<best_dist2) { best_dist2 = dist2; bestindx = n; bestpos = pos; }
2451  }
2452 
2453  var match_distance = 5;
2454  if (this.markeratt && this.markeratt.used) match_distance = this.markeratt.GetFullSize();
2455 
2456  if (Math.sqrt(best_dist2) > match_distance) return null;
2457 
2458  var res = { name: this.GetObject().fName, title: this.GetObject().fTitle,
2459  x: bestpos.x, y: bestpos.y,
2460  color1: this.markeratt && this.markeratt.used ? this.markeratt.color : this.lineatt.color,
2461  exact: Math.sqrt(best_dist2) < 4,
2462  lines: [ this.GetTipName() ],
2463  binindx: bestindx,
2464  menu_dist: match_distance,
2465  radius: match_distance
2466  };
2467 
2468  res.lines.push("r = " + main.AxisAsText("r", graph.fY[bestindx]));
2469  res.lines.push("phi = " + main.AxisAsText("phi",graph.fX[bestindx]));
2470 
2471  if (graph.fEY && graph.fEY[bestindx])
2472  res.lines.push("error r = " + main.AxisAsText("r", graph.fEY[bestindx]));
2473 
2474  if (graph.fEX && graph.fEX[bestindx])
2475  res.lines.push("error phi = " + main.AxisAsText("phi", graph.fEX[bestindx]));
2476 
2477  return res;
2478  }
2479 
2480  TGraphPolarPainter.prototype.ShowTooltip = function(hint) {
2481 
2482  if (!this.draw_g) return;
2483 
2484  var ttcircle = this.draw_g.select(".tooltip_bin");
2485 
2486  if (!hint) {
2487  ttcircle.remove();
2488  return;
2489  }
2490 
2491  if (ttcircle.empty())
2492  ttcircle = this.draw_g.append("svg:ellipse")
2493  .attr("class","tooltip_bin")
2494  .style("pointer-events","none");
2495 
2496  hint.changed = ttcircle.property("current_bin") !== hint.binindx;
2497 
2498  if (hint.changed)
2499  ttcircle.attr("cx", hint.x)
2500  .attr("cy", hint.y)
2501  .attr("rx", Math.round(hint.radius))
2502  .attr("ry", Math.round(hint.radius))
2503  .style("fill", "none")
2504  .style("stroke", hint.color1)
2505  .property("current_bin", hint.binindx);
2506  }
2507 
2508  TGraphPolarPainter.prototype.ProcessTooltip = function(pnt) {
2509  var hint = this.ExtractTooltip(pnt);
2510  if (!pnt || !pnt.disabled) this.ShowTooltip(hint);
2511  return hint;
2512  }
2513 
2514  TGraphPolarPainter.prototype.PerformDrawing = function(divid) {
2515  this.SetDivId(divid);
2516  this.DrawBins();
2517  this.DrawingReady();
2518  }
2519 
2520  function drawGraphPolar(divid, graph, opt) {
2521 
2522  var painter = new TGraphPolarPainter(graph);
2523 
2524  painter.DecodeOptions(opt);
2525 
2526  painter.SetDivId(divid, -1); // just to get access to existing elements
2527 
2528  var main = painter.main_painter();
2529  if (main) {
2530  if (!main.$polargram) {
2531  console.error('Cannot superimpose TGraphPolar with plain histograms');
2532  return null;
2533  }
2534  painter.PerformDrawing(divid);
2535 
2536  return painter;
2537  }
2538 
2539  if (!graph.fPolargram) graph.fPolargram = painter.CreatePolargram();
2540 
2541  return JSROOT.draw(divid, graph.fPolargram, "", painter.PerformDrawing.bind(painter, divid));
2542  }
2543 
2544  // ==============================================================
2545 
2546  function TSplinePainter(spline) {
2547  JSROOT.TObjectPainter.call(this, spline);
2548  this.bins = null;
2549  }
2550 
2551  TSplinePainter.prototype = Object.create(JSROOT.TObjectPainter.prototype);
2552 
2553  TSplinePainter.prototype.UpdateObject = function(obj, opt) {
2554  var spline = this.GetObject();
2555 
2556  if (spline._typename != obj._typename) return false;
2557 
2558  if (spline !== obj) JSROOT.extend(spline, obj);
2559 
2560  if (opt !== undefined) this.DecodeOptions(opt);
2561 
2562  return true;
2563  }
2564 
2565  TSplinePainter.prototype.Eval = function(knot, x) {
2566  var dx = x - knot.fX;
2567 
2568  if (knot._typename == "TSplinePoly3")
2569  return knot.fY + dx*(knot.fB + dx*(knot.fC + dx*knot.fD));
2570 
2571  if (knot._typename == "TSplinePoly5")
2572  return knot.fY + dx*(knot.fB + dx*(knot.fC + dx*(knot.fD + dx*(knot.fE + dx*knot.fF))));
2573 
2574  return knot.fY + dx;
2575  }
2576 
2577  TSplinePainter.prototype.FindX = function(x) {
2578  var spline = this.GetObject(),
2579  klow = 0, khig = spline.fNp - 1;
2580 
2581  if (x <= spline.fXmin) return 0;
2582  if (x >= spline.fXmax) return khig;
2583 
2584  if(spline.fKstep) {
2585  // Equidistant knots, use histogramming
2586  klow = Math.round((x - spline.fXmin)/spline.fDelta);
2587  // Correction for rounding errors
2588  if (x < spline.fPoly[klow].fX) {
2589  klow = Math.max(klow-1,0);
2590  } else if (klow < khig) {
2591  if (x > spline.fPoly[klow+1].fX) ++klow;
2592  }
2593  } else {
2594  // Non equidistant knots, binary search
2595  while(khig-klow>1) {
2596  var khalf = Math.round((klow+khig)/2);
2597  if(x > spline.fPoly[khalf].fX) klow = khalf;
2598  else khig = khalf;
2599  }
2600  }
2601  return klow;
2602  }
2603 
2604  TSplinePainter.prototype.CreateDummyHisto = function() {
2605 
2606  var xmin = 0, xmax = 1, ymin = 0, ymax = 1,
2607  spline = this.GetObject();
2608 
2609  if (spline && spline.fPoly) {
2610 
2611  xmin = xmax = spline.fPoly[0].fX;
2612  ymin = ymax = spline.fPoly[0].fY;
2613 
2614  spline.fPoly.forEach(function(knot) {
2615  xmin = Math.min(knot.fX, xmin);
2616  xmax = Math.max(knot.fX, xmax);
2617  ymin = Math.min(knot.fY, ymin);
2618  ymax = Math.max(knot.fY, ymax);
2619  });
2620 
2621  if (ymax > 0.0) ymax *= 1.05;
2622  if (ymin < 0.0) ymin *= 1.05;
2623  }
2624 
2625  var histo = JSROOT.Create("TH1I");
2626 
2627  histo.fName = spline.fName + "_hist";
2628  histo.fTitle = spline.fTitle;
2629 
2630  histo.fXaxis.fXmin = xmin;
2631  histo.fXaxis.fXmax = xmax;
2632  histo.fYaxis.fXmin = ymin;
2633  histo.fYaxis.fXmax = ymax;
2634 
2635  return histo;
2636  }
2637 
2638  TSplinePainter.prototype.ProcessTooltip = function(pnt) {
2639 
2640  var cleanup = false,
2641  spline = this.GetObject(),
2642  main = this.frame_painter(),
2643  xx, yy, knot = null, indx = 0;
2644 
2645  if ((pnt === null) || !spline || !main) {
2646  cleanup = true;
2647  } else {
2648  xx = main.RevertX(pnt.x);
2649  indx = this.FindX(xx);
2650  knot = spline.fPoly[indx];
2651  yy = this.Eval(knot, xx);
2652 
2653  if ((indx < spline.fN-1) && (Math.abs(spline.fPoly[indx+1].fX-xx) < Math.abs(xx-knot.fX))) knot = spline.fPoly[++indx];
2654 
2655  if (Math.abs(main.grx(knot.fX) - pnt.x) < 0.5*this.knot_size) {
2656  xx = knot.fX; yy = knot.fY;
2657  } else {
2658  knot = null;
2659  if ((xx < spline.fXmin) || (xx > spline.fXmax)) cleanup = true;
2660  }
2661  }
2662 
2663  if (cleanup) {
2664  if (this.draw_g !== null)
2665  this.draw_g.select(".tooltip_bin").remove();
2666  return null;
2667  }
2668 
2669  var gbin = this.draw_g.select(".tooltip_bin"),
2670  radius = this.lineatt.width + 3;
2671 
2672  if (gbin.empty())
2673  gbin = this.draw_g.append("svg:circle")
2674  .attr("class", "tooltip_bin")
2675  .style("pointer-events","none")
2676  .attr("r", radius)
2677  .style("fill", "none")
2678  .call(this.lineatt.func);
2679 
2680  var res = { name: this.GetObject().fName,
2681  title: this.GetObject().fTitle,
2682  x: main.grx(xx),
2683  y: main.gry(yy),
2684  color1: this.lineatt.color,
2685  lines: [],
2686  exact: (knot !== null) || (Math.abs(main.gry(yy) - pnt.y) < radius) };
2687 
2688  res.changed = gbin.property("current_xx") !== xx;
2689  res.menu = res.exact;
2690  res.menu_dist = Math.sqrt((res.x-pnt.x)*(res.x-pnt.x) + (res.y-pnt.y)*(res.y-pnt.y));
2691 
2692  if (res.changed)
2693  gbin.attr("cx", Math.round(res.x))
2694  .attr("cy", Math.round(res.y))
2695  .property("current_xx", xx);
2696 
2697  var name = this.GetTipName();
2698  if (name.length > 0) res.lines.push(name);
2699  res.lines.push("x = " + main.AxisAsText("x", xx))
2700  res.lines.push("y = " + main.AxisAsText("y", yy));
2701  if (knot !== null) {
2702  res.lines.push("knot = " + indx);
2703  res.lines.push("B = " + JSROOT.FFormat(knot.fB, JSROOT.gStyle.fStatFormat));
2704  res.lines.push("C = " + JSROOT.FFormat(knot.fC, JSROOT.gStyle.fStatFormat));
2705  res.lines.push("D = " + JSROOT.FFormat(knot.fD, JSROOT.gStyle.fStatFormat));
2706  if ((knot.fE!==undefined) && (knot.fF!==undefined)) {
2707  res.lines.push("E = " + JSROOT.FFormat(knot.fE, JSROOT.gStyle.fStatFormat));
2708  res.lines.push("F = " + JSROOT.FFormat(knot.fF, JSROOT.gStyle.fStatFormat));
2709  }
2710  }
2711 
2712  return res;
2713  }
2714 
2715  TSplinePainter.prototype.Redraw = function() {
2716 
2717  var w = this.frame_width(),
2718  h = this.frame_height(),
2719  spline = this.GetObject(),
2720  pmain = this.frame_painter(),
2721  name = this.GetTipName("\n");
2722 
2723  this.CreateG(true);
2724 
2725  this.knot_size = 5; // used in tooltip handling
2726 
2727  this.createAttLine({ attr: spline });
2728 
2729  if (this.options.Line || this.options.Curve) {
2730 
2731  var npx = Math.max(10, spline.fNpx);
2732 
2733  var xmin = Math.max(pmain.scale_xmin, spline.fXmin),
2734  xmax = Math.min(pmain.scale_xmax, spline.fXmax),
2735  indx = this.FindX(xmin),
2736  bins = []; // index of current knot
2737 
2738  if (pmain.logx) {
2739  xmin = Math.log(xmin);
2740  xmax = Math.log(xmax);
2741  }
2742 
2743  for (var n=0;n<npx;++n) {
2744  var xx = xmin + (xmax-xmin)/npx*(n-1);
2745  if (pmain.logx) xx = Math.exp(xx);
2746 
2747  while ((indx < spline.fNp-1) && (xx > spline.fPoly[indx+1].fX)) ++indx;
2748 
2749  var yy = this.Eval(spline.fPoly[indx], xx);
2750 
2751  bins.push({ x: xx, y: yy, grx: pmain.grx(xx), gry: pmain.gry(yy) });
2752  }
2753 
2754  var h0 = h; // use maximal frame height for filling
2755  if ((pmain.hmin!==undefined) && (pmain.hmin>=0)) {
2756  h0 = Math.round(pmain.gry(0));
2757  if ((h0 > h) || (h0 < 0)) h0 = h;
2758  }
2759 
2760  var path = JSROOT.Painter.BuildSvgPath("bezier", bins, h0, 2);
2761 
2762  this.draw_g.append("svg:path")
2763  .attr("class", "line")
2764  .attr("d", path.path)
2765  .style("fill", "none")
2766  .call(this.lineatt.func);
2767  }
2768 
2769  if (this.options.Mark) {
2770 
2771  // for tooltips use markers only if nodes where not created
2772  var path = "";
2773 
2774  this.createAttMarker({ attr: spline })
2775 
2776  this.markeratt.reset_pos();
2777 
2778  this.knot_size = this.markeratt.GetFullSize();
2779 
2780  for (var n=0; n<spline.fPoly.length; n++) {
2781  var knot = spline.fPoly[n],
2782  grx = pmain.grx(knot.fX);
2783  if ((grx > -this.knot_size) && (grx < w + this.knot_size)) {
2784  var gry = pmain.gry(knot.fY);
2785  if ((gry > -this.knot_size) && (gry < h + this.knot_size)) {
2786  path += this.markeratt.create(grx, gry);
2787  }
2788  }
2789  }
2790 
2791  if (path)
2792  this.draw_g.append("svg:path")
2793  .attr("d", path)
2794  .call(this.markeratt.func);
2795  }
2796 
2797  }
2798 
2799  TSplinePainter.prototype.CanZoomIn = function(axis,min,max) {
2800  if (axis!=="x") return false;
2801 
2802  var spline = this.GetObject();
2803  if (!spline) return false;
2804 
2805  // if function calculated, one always could zoom inside
2806  return true;
2807  }
2808 
2809  TSplinePainter.prototype.DecodeOptions = function(opt) {
2810  var d = new JSROOT.DrawOptions(opt);
2811 
2812  if (!this.options) this.options = {};
2813 
2814  JSROOT.extend(this.options, {
2815  Same: d.check('SAME'),
2816  Line: d.check('L'),
2817  Curve: d.check('C'),
2818  Mark: d.check('P')
2819  });
2820 
2821  this.OptionsStore(opt);
2822  }
2823 
2824  TSplinePainter.prototype.FirstDraw = function() {
2825  this.SetDivId(this.divid);
2826  this.Redraw();
2827  return this.DrawingReady();
2828  }
2829 
2830  JSROOT.Painter.drawSpline = function(divid, spline, opt) {
2831 
2832  var painter = new TSplinePainter(spline);
2833 
2834  painter.SetDivId(divid, -1);
2835  painter.DecodeOptions(opt);
2836 
2837  if (!painter.main_painter()) {
2838  if (painter.options.Same) {
2839  console.warn('TSpline painter requires histogram to be drawn');
2840  return null;
2841  }
2842  var histo = painter.CreateDummyHisto();
2843  JSROOT.draw(divid, histo, "AXIS", painter.FirstDraw.bind(painter));
2844  return painter;
2845  }
2846 
2847  return painter.FirstDraw();
2848  }
2849 
2850  // =============================================================
2851 
2852  function TGraphTimePainter(gr) {
2853  JSROOT.TObjectPainter.call(this, gr);
2854  }
2855 
2856  TGraphTimePainter.prototype = Object.create(JSROOT.TObjectPainter.prototype);
2857 
2858  TGraphTimePainter.prototype.Redraw = function() {
2859  if (this.step === undefined) this.StartDrawing(false);
2860  }
2861 
2862  TGraphTimePainter.prototype.DecodeOptions = function(opt) {
2863 
2864  var d = new JSROOT.DrawOptions(opt || "REPEAT");
2865 
2866  if (!this.options) this.options = {};
2867 
2868  JSROOT.extend(this.options, {
2869  once: d.check("ONCE"),
2870  repeat: d.check("REPEAT"),
2871  first: d.check("FIRST")
2872  });
2873 
2874  this.OptionsStore(opt);
2875  }
2876 
2877  TGraphTimePainter.prototype.DrawPrimitives = function(indx, callback, ppainter) {
2878 
2879  if (indx===0) {
2880  this._doing_primitives = true;
2881  }
2882 
2883  var lst = this.GetObject().fSteps.arr[this.step];
2884 
2885  while (true) {
2886  if (ppainter) ppainter.$grtimeid = this.selfid; // indicator that painter created by ourself
2887 
2888  if (!lst || (indx >= lst.arr.length)) {
2889  delete this._doing_primitives;
2890  return JSROOT.CallBack(callback);
2891  }
2892 
2893  // handle use to invoke callback only when necessary
2894  var handle = { func: this.DrawPrimitives.bind(this, indx+1, callback) };
2895 
2896  ppainter = JSROOT.draw(this.divid, lst.arr[indx], lst.opt[indx], handle);
2897 
2898  if (!handle.completed) return;
2899  indx++;
2900  }
2901  }
2902 
2903  TGraphTimePainter.prototype.Selector = function(p) {
2904  return p && (p.$grtimeid === this.selfid);
2905  }
2906 
2907  TGraphTimePainter.prototype.ContineDrawing = function() {
2908  if (!this.options) return;
2909 
2910  var gr = this.GetObject();
2911 
2912  if (!this.ready_called) {
2913  this.ready_called = true;
2914  this.DrawingReady(); // do it already here, animation will continue in background
2915  }
2916 
2917  if (this.options.first) {
2918  // draw only single frame, cancel all others
2919  delete this.step;
2920  return;
2921  }
2922 
2923  if (this.wait_animation_frame) {
2924  delete this.wait_animation_frame;
2925 
2926  // clear pad
2927  var pp = this.pad_painter();
2928  if (!pp) {
2929  // most probably, pad is cleared
2930  delete this.step;
2931  return;
2932  }
2933 
2934  // clear primitives produced by the TGraphTime
2935  pp.CleanPrimitives(this.Selector.bind(this));
2936 
2937  // draw ptrimitives again
2938  this.DrawPrimitives(0, this.ContineDrawing.bind(this));
2939  } else if (this.running_timeout) {
2940  clearTimeout(this.running_timeout);
2941  delete this.running_timeout;
2942 
2943  this.wait_animation_frame = true;
2944  // use animation frame to disable update in inactive form
2945  requestAnimationFrame(this.ContineDrawing.bind(this));
2946  } else {
2947 
2948  var sleeptime = gr.fSleepTime;
2949  if (!sleeptime || (sleeptime<100)) sleeptime = 10;
2950 
2951  if (++this.step > gr.fSteps.arr.length) {
2952  if (this.options.repeat) {
2953  this.step = 0; // start again
2954  sleeptime = Math.max(5000, 5*sleeptime); // increase sleep time
2955  } else {
2956  delete this.step; // clear indicator that animation running
2957  return;
2958  }
2959  }
2960 
2961  this.running_timeout = setTimeout(this.ContineDrawing.bind(this), sleeptime);
2962  }
2963  }
2964 
2965  TGraphTimePainter.prototype.StartDrawing = function(once_again) {
2966  if (once_again!==false) this.SetDivId(this.divid);
2967 
2968  this.step = 0;
2969 
2970  this.DrawPrimitives(0, this.ContineDrawing.bind(this));
2971  }
2972 
2973  JSROOT.Painter.drawGraphTime = function(divid,gr,opt) {
2974 
2975  var painter = new TGraphTimePainter(gr);
2976  painter.SetDivId(divid,-1);
2977 
2978  if (painter.main_painter()) {
2979  console.error('Cannot draw graph time on top of other histograms');
2980  return null;
2981  }
2982 
2983  if (!gr.fFrame) {
2984  console.error('Frame histogram not exists');
2985  return null;
2986  }
2987 
2988  painter.DecodeOptions(opt);
2989 
2990  if (!gr.fFrame.fTitle && gr.fTitle) gr.fFrame.fTitle = gr.fTitle;
2991 
2992  painter.selfid = "grtime" + JSROOT.id_counter++; // use to identify primitives which should be clean
2993 
2994  JSROOT.draw(divid, gr.fFrame, "AXIS", painter.StartDrawing.bind(painter));
2995 
2996  return painter;
2997 
2998  }
2999 
3000  // =============================================================
3001 
3002  function TEfficiencyPainter(eff) {
3003  JSROOT.TObjectPainter.call(this, eff);
3004  this.fBoundary = 'Normal';
3005  }
3006 
3007  TEfficiencyPainter.prototype = Object.create(JSROOT.TObjectPainter.prototype);
3008 
3009  TEfficiencyPainter.prototype.GetEfficiency = function(bin) {
3010  var obj = this.GetObject(),
3011  total = obj.fTotalHistogram.getBinContent(bin),
3012  passed = obj.fPassedHistogram.getBinContent(bin);
3013 
3014  return total ? passed/total : 0;
3015  }
3016 
3028  TEfficiencyPainter.prototype.Normal = function(total,passed,level,bUpper) {
3029  if (total == 0) return bUpper ? 1 : 0;
3030 
3031  var alpha = (1.0 - level)/2,
3032  average = passed / total,
3033  sigma = Math.sqrt(average * (1 - average) / total),
3034  delta = JSROOT.Math.normal_quantile(1 - alpha,sigma);
3035 
3036  if(bUpper)
3037  return ((average + delta) > 1) ? 1.0 : (average + delta);
3038 
3039  return ((average - delta) < 0) ? 0.0 : (average - delta);
3040  }
3041 
3042  TEfficiencyPainter.prototype.GetEfficiencyErrorLow = function(bin) {
3043  var obj = this.GetObject(),
3044  total = obj.fTotalHistogram.getBinContent(bin),
3045  passed = obj.fPassedHistogram.getBinContent(bin),
3046  eff = this.GetEfficiency(bin);
3047 
3048  return eff - this[this.fBoundary](total,passed, obj.fConfLevel, false);
3049  }
3050 
3051  TEfficiencyPainter.prototype.GetEfficiencyErrorUp = function(bin) {
3052  var obj = this.GetObject(),
3053  total = obj.fTotalHistogram.getBinContent(bin),
3054  passed = obj.fPassedHistogram.getBinContent(bin),
3055  eff = this.GetEfficiency(bin);
3056 
3057  return this[this.fBoundary]( total, passed, obj.fConfLevel, true) - eff;
3058  }
3059 
3060  TEfficiencyPainter.prototype.CreateGraph = function() {
3061  var gr = JSROOT.Create('TGraphAsymmErrors');
3062  gr.fName = "eff_graph";
3063  return gr;
3064  }
3065 
3066  TEfficiencyPainter.prototype.FillGraph = function(gr, opt) {
3067  var eff = this.GetObject(),
3068  npoints = eff.fTotalHistogram.fXaxis.fNbins,
3069  option = opt.toLowerCase(),
3070  plot0Bins = false, j = 0;
3071  if (option.indexOf("e0")>=0) plot0Bins = true;
3072  for (var n=0;n<npoints;++n) {
3073  if (!plot0Bins && eff.fTotalHistogram.getBinContent(n+1) === 0) continue;
3074  gr.fX[j] = eff.fTotalHistogram.fXaxis.GetBinCenter(n+1);
3075  gr.fY[j] = this.GetEfficiency(n+1);
3076  gr.fEXlow[j] = eff.fTotalHistogram.fXaxis.GetBinCenter(n+1) - eff.fTotalHistogram.fXaxis.GetBinLowEdge(n+1);
3077  gr.fEXhigh[j] = eff.fTotalHistogram.fXaxis.GetBinLowEdge(n+2) - eff.fTotalHistogram.fXaxis.GetBinCenter(n+1);
3078  gr.fEYlow[j] = this.GetEfficiencyErrorLow(n+1);
3079  gr.fEYhigh[j] = this.GetEfficiencyErrorUp(n+1);
3080  ++j;
3081  }
3082  gr.fNpoints = j;
3083  }
3084 
3085  JSROOT.Painter.drawEfficiency = function(divid, eff, opt) {
3086 
3087  if (!eff || !eff.fTotalHistogram || (eff.fTotalHistogram._typename.indexOf("TH1")!=0)) return null;
3088 
3089  var painter = new TEfficiencyPainter(eff);
3090  painter.options = opt;
3091 
3092  var gr = painter.CreateGraph();
3093  painter.FillGraph(gr, opt);
3094 
3095  JSROOT.draw(divid, gr, opt, function() {
3096  painter.SetDivId(divid);
3097  painter.DrawingReady();
3098  });
3099 
3100  return painter;
3101  }
3102 
3103 
3104  // =============================================================
3105 
3106  function TMultiGraphPainter(mgraph) {
3107  JSROOT.TObjectPainter.call(this, mgraph);
3108  this.firstpainter = null;
3109  this.autorange = false;
3110  this.painters = []; // keep painters to be able update objects
3111  }
3112 
3113  TMultiGraphPainter.prototype = Object.create(JSROOT.TObjectPainter.prototype);
3114 
3115  TMultiGraphPainter.prototype.Cleanup = function() {
3116  this.painters = [];
3117  JSROOT.TObjectPainter.prototype.Cleanup.call(this);
3118  }
3119 
3120  TMultiGraphPainter.prototype.UpdateObject = function(obj) {
3121  if (!this.MatchObjectType(obj)) return false;
3122 
3123  var mgraph = this.GetObject(),
3124  graphs = obj.fGraphs;
3125 
3126  mgraph.fTitle = obj.fTitle;
3127 
3128  var isany = false;
3129  if (this.firstpainter) {
3130  var histo = obj.fHistogram;
3131  if (this.autorange && !histo)
3132  histo = this.ScanGraphsRange(graphs);
3133 
3134  if (this.firstpainter.UpdateObject(histo)) isany = true;
3135  }
3136 
3137  for (var i = 0; i < graphs.arr.length; ++i) {
3138  if (i<this.painters.length)
3139  if (this.painters[i].UpdateObject(graphs.arr[i])) isany = true;
3140  }
3141 
3142  if (obj.fFunctions)
3143  for (var i = 0; i < obj.fFunctions.arr.length; ++i) {
3144  var func = obj.fFunctions.arr[i];
3145  if (!func || !func._typename || !func.fName) continue;
3146  var funcpainter = this.FindPainterFor(null, func.fName, func._typename);
3147  if (funcpainter) funcpainter.UpdateObject(func);
3148  }
3149 
3150  return isany;
3151  }
3152 
3153  TMultiGraphPainter.prototype.ComputeGraphRange = function(res, gr) {
3154  // Compute the x/y range of the points in this graph
3155  if (gr.fNpoints == 0) return;
3156  if (res.first) {
3157  res.xmin = res.xmax = gr.fX[0];
3158  res.ymin = res.ymax = gr.fY[0];
3159  res.first = false;
3160  }
3161  for (var i=0; i < gr.fNpoints; ++i) {
3162  res.xmin = Math.min(res.xmin, gr.fX[i]);
3163  res.xmax = Math.max(res.xmax, gr.fX[i]);
3164  res.ymin = Math.min(res.ymin, gr.fY[i]);
3165  res.ymax = Math.max(res.ymax, gr.fY[i]);
3166  }
3167  return res;
3168  }
3169 
3170  TMultiGraphPainter.prototype.padtoX = function(pad, x) {
3171  // Convert x from pad to X.
3172  if (pad.fLogx && (x < 50))
3173  return Math.exp(2.302585092994 * x);
3174  return x;
3175  }
3176 
3177  TMultiGraphPainter.prototype.ScanGraphsRange = function(graphs, histo, pad) {
3178  var mgraph = this.GetObject(),
3179  maximum, minimum, dx, dy, uxmin = 0, uxmax = 0, logx = false, logy = false,
3180  time_display = false, time_format = "",
3181  rw = { xmin: 0, xmax: 0, ymin: 0, ymax: 0, first: true };
3182 
3183  if (pad) {
3184  logx = pad.fLogx;
3185  logy = pad.fLogy;
3186  rw.xmin = pad.fUxmin;
3187  rw.xmax = pad.fUxmax;
3188  rw.ymin = pad.fUymin;
3189  rw.ymax = pad.fUymax;
3190  rw.first = false;
3191  }
3192  if (histo) {
3193  minimum = histo.fYaxis.fXmin;
3194  maximum = histo.fYaxis.fXmax;
3195  if (pad) {
3196  uxmin = this.padtoX(pad, rw.xmin);
3197  uxmax = this.padtoX(pad, rw.xmax);
3198  }
3199  } else {
3200  this.autorange = true;
3201 
3202  for (var i = 0; i < graphs.arr.length; ++i)
3203  this.ComputeGraphRange(rw, graphs.arr[i]);
3204 
3205  if (graphs.arr[0] && graphs.arr[0].fHistogram && graphs.arr[0].fHistogram.fXaxis.fTimeDisplay) {
3206  time_display = true;
3207  time_format = graphs.arr[0].fHistogram.fXaxis.fTimeFormat;
3208  }
3209 
3210  if (rw.xmin == rw.xmax) rw.xmax += 1.;
3211  if (rw.ymin == rw.ymax) rw.ymax += 1.;
3212  dx = 0.05 * (rw.xmax - rw.xmin);
3213  dy = 0.05 * (rw.ymax - rw.ymin);
3214  uxmin = rw.xmin - dx;
3215  uxmax = rw.xmax + dx;
3216  if (logy) {
3217  if (rw.ymin <= 0) rw.ymin = 0.001 * rw.ymax;
3218  minimum = rw.ymin / (1 + 0.5 * JSROOT.log10(rw.ymax / rw.ymin));
3219  maximum = rw.ymax * (1 + 0.2 * JSROOT.log10(rw.ymax / rw.ymin));
3220  } else {
3221  minimum = rw.ymin - dy;
3222  maximum = rw.ymax + dy;
3223  }
3224  if (minimum < 0 && rw.ymin >= 0)
3225  minimum = 0;
3226  if (maximum > 0 && rw.ymax <= 0)
3227  maximum = 0;
3228  }
3229 
3230  if (uxmin < 0 && rw.xmin >= 0)
3231  uxmin = logx ? 0.9 * rw.xmin : 0;
3232  if (uxmax > 0 && rw.xmax <= 0)
3233  uxmax = logx? 1.1 * rw.xmax : 0;
3234 
3235  if (mgraph.fMinimum != -1111)
3236  rw.ymin = minimum = mgraph.fMinimum;
3237  if (mgraph.fMaximum != -1111)
3238  rw.ymax = maximum = mgraph.fMaximum;
3239 
3240  if (minimum < 0 && rw.ymin >= 0 && logy) minimum = 0.9 * rw.ymin;
3241  if (maximum > 0 && rw.ymax <= 0 && logy) maximum = 1.1 * rw.ymax;
3242  if (minimum <= 0 && logy) minimum = 0.001 * maximum;
3243  if (!logy && minimum > 0 && minimum < 0.05*maximum) minimum = 0;
3244  if (uxmin <= 0 && logx)
3245  uxmin = (uxmax > 1000) ? 1 : 0.001 * uxmax;
3246 
3247  // Create a temporary histogram to draw the axis (if necessary)
3248  if (!histo) {
3249  histo = JSROOT.Create("TH1I");
3250  histo.fTitle = mgraph.fTitle;
3251  histo.fXaxis.fXmin = uxmin;
3252  histo.fXaxis.fXmax = uxmax;
3253  histo.fXaxis.fTimeDisplay = time_display;
3254  if (time_display) histo.fXaxis.fTimeFormat = time_format;
3255  }
3256 
3257  histo.fYaxis.fXmin = minimum;
3258  histo.fYaxis.fXmax = maximum;
3259 
3260  return histo;
3261  }
3262 
3263  TMultiGraphPainter.prototype.DrawAxis = function(callback) {
3264  // draw special histogram
3265 
3266  var mgraph = this.GetObject(),
3267  histo = this.ScanGraphsRange(mgraph.fGraphs, mgraph.fHistogram, this.root_pad());
3268 
3269  // histogram painter will be first in the pad, will define axis and
3270  // interactive actions
3271  JSROOT.draw(this.divid, histo, "AXIS", callback);
3272  }
3273 
3274  TMultiGraphPainter.prototype.DrawNextFunction = function(indx, callback) {
3275  // method draws next function from the functions list
3276 
3277  var mgraph = this.GetObject();
3278 
3279  if (!mgraph.fFunctions || (indx >= mgraph.fFunctions.arr.length))
3280  return JSROOT.CallBack(callback);
3281 
3282  JSROOT.draw(this.divid, mgraph.fFunctions.arr[indx], mgraph.fFunctions.opt[indx],
3283  this.DrawNextFunction.bind(this, indx+1, callback));
3284  }
3285 
3286  TMultiGraphPainter.prototype.DrawNextGraph = function(indx, opt, subp, used_timeout) {
3287  if (subp) this.painters.push(subp);
3288 
3289  var graphs = this.GetObject().fGraphs;
3290 
3291  // at the end of graphs drawing draw functions (if any)
3292  if (indx >= graphs.arr.length) {
3293  this._pfc = this._plc = this._pmc = false; // disable auto coloring at the end
3294  return this.DrawNextFunction(0, this.DrawingReady.bind(this));
3295  }
3296 
3297  // when too many graphs are drawn, avoid deep stack with timeout
3298  if ((indx % 500 === 499) && !used_timeout)
3299  return setTimeout(this.DrawNextGraph.bind(this,indx,opt,null,true),0);
3300 
3301  // if there is auto colors assignment, try to provide it
3302  if (this._pfc || this._plc || this._pmc) {
3303  if (!this.pallette && JSROOT.Painter.GetColorPalette)
3304  this.palette = JSROOT.Painter.GetColorPalette();
3305  if (this.palette) {
3306  var color = this.palette.calcColor(indx, graphs.arr.length+1);
3307  var icolor = this.add_color(color);
3308 
3309  if (this._pfc) graphs.arr[indx].fFillColor = icolor;
3310  if (this._plc) graphs.arr[indx].fLineColor = icolor;
3311  if (this._pmc) graphs.arr[indx].fMarkerColor = icolor;
3312  }
3313  }
3314 
3315  JSROOT.draw(this.divid, graphs.arr[indx], graphs.opt[indx] || opt,
3316  this.DrawNextGraph.bind(this, indx+1, opt));
3317  }
3318 
3319  JSROOT.Painter.drawMultiGraph = function(divid, mgraph, opt) {
3320 
3321  var painter = new TMultiGraphPainter(mgraph);
3322 
3323  painter.SetDivId(divid, -1); // it may be no element to set divid
3324 
3325  var d = new JSROOT.DrawOptions(opt);
3326  d.check("3D"); d.check("FB"); // no 3D supported, FB not clear
3327 
3328  painter._pfc = d.check("PFC");
3329  painter._plc = d.check("PLC");
3330  painter._pmc = d.check("PMC");
3331 
3332  if (d.check("A") || !painter.main_painter()) {
3333  painter.DrawAxis(function(hpainter) {
3334  painter.firstpainter = hpainter;
3335  painter.SetDivId(divid);
3336  painter.DrawNextGraph(0, d.remain());
3337  });
3338  } else {
3339  painter.SetDivId(divid);
3340  painter.DrawNextGraph(0, d.remain());
3341  }
3342 
3343  return painter;
3344  }
3345 
3346  // =========================================================================================
3347 
3348  function drawWebPainting(divid, obj, opt) {
3349 
3350  var painter = new JSROOT.TObjectPainter(obj, opt);
3351 
3352  painter.UpdateObject = function(obj) {
3353  if (!this.MatchObjectType(obj)) return false;
3354  this.draw_object = obj;
3355  return true;
3356  }
3357 
3358  painter.ReadAttr = function(str, names) {
3359  var lastp = 0, obj = { _typename: "any" };
3360  for (var k=0;k<names.length;++k) {
3361  var p = str.indexOf(":", lastp+1);
3362  obj[names[k]] = parseInt(str.substr(lastp+1, (p>lastp) ? p-lastp-1 : undefined));
3363  lastp = p;
3364  }
3365  return obj;
3366  }
3367 
3368  painter.Redraw = function() {
3369 
3370  var obj = this.GetObject(), func = this.AxisToSvgFunc();
3371 
3372  if (!obj || !obj.fOper || !func) return;
3373 
3374  var indx = 0, attr = {}, lastpath = null, lastkind = "none", d = "",
3375  oper, k, npoints, n, arr = obj.fOper.split(";");
3376 
3377  function check_attributes(painter, kind) {
3378  if (kind == lastkind) return;
3379 
3380  if (lastpath) {
3381  lastpath.attr("d", d); // flush previous
3382  d = ""; lastpath = null; lastkind = "none";
3383  }
3384 
3385  if (!kind) return;
3386 
3387  lastkind = kind;
3388  lastpath = painter.draw_g.append("svg:path");
3389  switch (kind) {
3390  case "f": lastpath.call(painter.fillatt.func).attr('stroke', 'none'); break;
3391  case "l": lastpath.call(painter.lineatt.func).attr('fill', 'none'); break;
3392  case "m": lastpath.call(painter.markeratt.func); break;
3393  }
3394  }
3395 
3396  this.CreateG();
3397 
3398  for (k=0;k<arr.length;++k) {
3399  oper = arr[k][0];
3400  switch (oper) {
3401  case "z":
3402  this.createAttLine({ attr: this.ReadAttr(arr[k], ["fLineColor", "fLineStyle", "fLineWidth"]), force: true });
3403  check_attributes();
3404  continue;
3405  case "y":
3406  this.createAttFill({ attr: this.ReadAttr(arr[k], ["fFillColor", "fFillStyle"]), force: true });
3407  check_attributes();
3408  continue;
3409  case "x":
3410  this.createAttMarker({ attr: this.ReadAttr(arr[k], ["fMarkerColor", "fMarkerStyle", "fMarkerSize"]), force: true })
3411  check_attributes();
3412  continue;
3413  case "o":
3414  attr = this.ReadAttr(arr[k], ["fTextColor", "fTextFont", "fTextSize", "fTextAlign", "fTextAngle" ]);
3415  if (attr.fTextSize < 0) attr.fTextSize *= -0.001;
3416  check_attributes();
3417  continue;
3418  case "r":
3419  case "b": {
3420 
3421  check_attributes(this, (oper == "b") ? "f" : "l");
3422 
3423  var x1 = func.x(obj.fBuf[indx++]),
3424  y1 = func.y(obj.fBuf[indx++]),
3425  x2 = func.x(obj.fBuf[indx++]),
3426  y2 = func.y(obj.fBuf[indx++]);
3427 
3428  d += "M"+x1+","+y1+"h"+(x2-x1)+"v"+(y2-y1)+"h"+(x1-x2)+"z";
3429 
3430  continue;
3431  }
3432  case "l":
3433  case "f": {
3434 
3435  check_attributes(this, oper);
3436 
3437  npoints = parseInt(arr[k].substr(1));
3438 
3439  for (n=0;n<npoints;++n)
3440  d += ((n>0) ? "L" : "M") +
3441  func.x(obj.fBuf[indx++]) + "," + func.y(obj.fBuf[indx++]);
3442 
3443  if (oper == "f") d+="Z";
3444 
3445  continue;
3446  }
3447 
3448  case "m": {
3449 
3450  check_attributes(this, oper);
3451 
3452  npoints = parseInt(arr[k].substr(1));
3453 
3454  this.markeratt.reset_pos();
3455  for (n=0;n<npoints;++n)
3456  d += this.markeratt.create(func.x(obj.fBuf[indx++]), func.y(obj.fBuf[indx++]));
3457 
3458  continue;
3459  }
3460 
3461  case "h":
3462  case "t": {
3463  if (attr.fTextSize) {
3464 
3465  check_attributes();
3466 
3467  var height = (attr.fTextSize > 1) ? attr.fTextSize : this.pad_height() * attr.fTextSize;
3468 
3469  var group = this.draw_g.append("svg:g");
3470 
3471  this.StartTextDrawing(attr.fTextFont, height, group);
3472 
3473  var angle = attr.fTextAngle;
3474  if (angle >= 360) angle -= Math.floor(angle/360) * 360;
3475 
3476  var txt = arr[k].substr(1);
3477 
3478  if (oper == "h") {
3479  var res = "";
3480  for (n=0;n<txt.length;n+=2)
3481  res += String.fromCharCode(parseInt(txt.substr(n,2), 16));
3482  txt = res;
3483  }
3484 
3485  // todo - correct support of angle
3486  this.DrawText({ align: attr.fTextAlign,
3487  x: func.x(obj.fBuf[indx++]),
3488  y: func.y(obj.fBuf[indx++]),
3489  rotate: -angle,
3490  text: txt,
3491  color: JSROOT.Painter.root_colors[attr.fTextColor], latex: 0, draw_g: group });
3492 
3493  this.FinishTextDrawing(group);
3494  }
3495  continue;
3496  }
3497 
3498  default:
3499  console.log('unsupported operation ' + oper);
3500  }
3501  }
3502 
3503  check_attributes();
3504  }
3505 
3506  painter.SetDivId(divid);
3507 
3508  painter.Redraw();
3509 
3510  return painter.DrawingReady();
3511  }
3512 
3513  JSROOT.Painter.drawASImage = function(divid, obj, opt) {
3514  var painter = new JSROOT.TBasePainter();
3515  painter.SetDivId(divid, -1);
3516 
3517  var main = painter.select_main(); // this is d3 selection of main element for image drawing
3518 
3519  // from here one should insert PNG image
3520 
3521  // this is example how external image can be inserted
3522  // main.append("img").attr("src","https://root.cern/js/files/img/tf1.png");
3523 
3524  // this is potential example how image can be generated
3525  // one could use TASImage member like obj.fPngBuf
3526  // main.append("img").attr("src","data:image/png;base64,xxxxxxxxx..");
3527 
3528  painter.SetDivId(divid);
3529 
3530  return painter.DrawingReady();
3531  }
3532 
3533  JSROOT.Painter.drawJSImage = function(divid, obj, opt) {
3534  var painter = new JSROOT.TBasePainter();
3535  painter.SetDivId(divid, -1);
3536 
3537  var main = painter.select_main();
3538 
3539  // this is example how external image can be inserted
3540  var img = main.append("img").attr("src", obj.fName).attr("title", obj.fTitle || obj.fName);
3541 
3542  if (opt && opt.indexOf("scale")>=0) {
3543  img.style("width","100%").style("height","100%");
3544  } else if (opt && opt.indexOf("center")>=0) {
3545  main.style("position", "relative");
3546  img.attr("style", "margin: 0; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);");
3547  }
3548 
3549  painter.SetDivId(divid);
3550 
3551  return painter.DrawingReady();
3552  }
3553 
3554 
3555  // ==================================================================================================
3556 
3557  JSROOT.Painter.CreateBranchItem = function(node, branch, tree, parent_branch) {
3558  if (!node || !branch) return false;
3559 
3560  var nb_branches = branch.fBranches ? branch.fBranches.arr.length : 0,
3561  nb_leaves = branch.fLeaves ? branch.fLeaves.arr.length : 0;
3562 
3563  function ClearName(arg) {
3564  var pos = arg.indexOf("[");
3565  if (pos>0) arg = arg.substr(0, pos);
3566  if (parent_branch && arg.indexOf(parent_branch.fName)==0) {
3567  arg = arg.substr(parent_branch.fName.length);
3568  if (arg[0]===".") arg = arg.substr(1);
3569  }
3570  return arg;
3571  }
3572 
3573  branch.$tree = tree; // keep tree pointer, later do it more smart
3574 
3575  var subitem = {
3576  _name : ClearName(branch.fName),
3577  _kind : "ROOT." + branch._typename,
3578  _title : branch.fTitle,
3579  _obj : branch
3580  };
3581 
3582  if (!node._childs) node._childs = [];
3583 
3584  node._childs.push(subitem);
3585 
3586  if (branch._typename==='TBranchElement')
3587  subitem._title += " from " + branch.fClassName + ";" + branch.fClassVersion;
3588 
3589  if (nb_branches > 0) {
3590  subitem._more = true;
3591  subitem._expand = function(bnode,bobj) {
3592  // really create all sub-branch items
3593  if (!bobj) return false;
3594 
3595  if (!bnode._childs) bnode._childs = [];
3596 
3597  if (bobj.fLeaves && (bobj.fLeaves.arr.length === 1) &&
3598  ((bobj.fType === JSROOT.BranchType.kClonesNode) || (bobj.fType === JSROOT.BranchType.kSTLNode))) {
3599  bobj.fLeaves.arr[0].$branch = bobj;
3600  bnode._childs.push({
3601  _name: "@size",
3602  _title: "container size",
3603  _kind: "ROOT.TLeafElement",
3604  _icon: "img_leaf",
3605  _obj: bobj.fLeaves.arr[0],
3606  _more : false
3607  });
3608  }
3609 
3610  for (var i=0; i<bobj.fBranches.arr.length; ++i)
3611  JSROOT.Painter.CreateBranchItem(bnode, bobj.fBranches.arr[i], bobj.$tree, bobj);
3612 
3613  var object_class = JSROOT.IO.GetBranchObjectClass(bobj, bobj.$tree, true),
3614  methods = object_class ? JSROOT.getMethods(object_class) : null;
3615 
3616  if (methods && (bobj.fBranches.arr.length>0))
3617  for (var key in methods) {
3618  if (typeof methods[key] !== 'function') continue;
3619  var s = methods[key].toString();
3620  if ((s.indexOf("return")>0) && (s.indexOf("function ()")==0))
3621  bnode._childs.push({
3622  _name: key+"()",
3623  _title: "function " + key + " of class " + object_class,
3624  _kind: "ROOT.TBranchFunc", // fictional class, only for drawing
3625  _obj: { _typename: "TBranchFunc", branch: bobj, func: key },
3626  _more : false
3627  });
3628 
3629  }
3630 
3631  return true;
3632  }
3633  return true;
3634  } else if (nb_leaves === 1) {
3635  subitem._icon = "img_leaf";
3636  subitem._more = false;
3637  } else if (nb_leaves > 1) {
3638  subitem._childs = [];
3639  for (var j = 0; j < nb_leaves; ++j) {
3640  branch.fLeaves.arr[j].$branch = branch; // keep branch pointer for drawing
3641  var leafitem = {
3642  _name : ClearName(branch.fLeaves.arr[j].fName),
3643  _kind : "ROOT." + branch.fLeaves.arr[j]._typename,
3644  _obj: branch.fLeaves.arr[j]
3645  }
3646  subitem._childs.push(leafitem);
3647  }
3648  }
3649 
3650  return true;
3651  }
3652 
3653  JSROOT.Painter.TreeHierarchy = function(node, obj) {
3654  if (obj._typename != 'TTree' && obj._typename != 'TNtuple' && obj._typename != 'TNtupleD' ) return false;
3655 
3656  node._childs = [];
3657  node._tree = obj; // set reference, will be used later by TTree::Draw
3658 
3659  for (var i=0; i<obj.fBranches.arr.length; ++i)
3660  JSROOT.Painter.CreateBranchItem(node, obj.fBranches.arr[i], obj);
3661 
3662  return true;
3663  }
3664 
3669  JSROOT.Painter.drawTree = function(divid, obj, opt) {
3670 
3671  var painter = new JSROOT.TObjectPainter(obj),
3672  tree = obj, args = opt;
3673 
3674  if (obj._typename == "TBranchFunc") {
3675  // fictional object, created only in browser
3676  args = { expr: "." + obj.func + "()", branch: obj.branch };
3677  if (opt && opt.indexOf("dump")==0) args.expr += ">>" + opt; else
3678  if (opt) args.expr += opt;
3679  tree = obj.branch.$tree;
3680  } else if (obj.$branch) {
3681  // this is drawing of the single leaf from the branch
3682  args = { expr: "." + obj.fName + (opt || ""), branch: obj.$branch };
3683  if ((args.branch.fType === JSROOT.BranchType.kClonesNode) || (args.branch.fType === JSROOT.BranchType.kSTLNode)) {
3684  // special case of size
3685  args.expr = opt;
3686  args.direct_branch = true;
3687  }
3688 
3689  tree = obj.$branch.$tree;
3690  } else if (obj.$tree) {
3691  // this is drawing of the branch
3692 
3693  // if generic object tried to be drawn without specifying any options, it will be just dump
3694  if (!opt && obj.fStreamerType && (obj.fStreamerType !== JSROOT.IO.kTString) &&
3695  (obj.fStreamerType >= JSROOT.IO.kObject) && (obj.fStreamerType <= JSROOT.IO.kAnyP)) opt = "dump";
3696 
3697  args = { expr: opt, branch: obj };
3698  tree = obj.$tree;
3699  } else {
3700 
3701  if ((args==='player') || !args) {
3702  JSROOT.AssertPrerequisites("jq2d", function() {
3703  JSROOT.CreateTreePlayer(painter);
3704  painter.ConfigureTree(tree);
3705  painter.Show(divid);
3706  painter.DrawingReady();
3707  });
3708  return painter;
3709  }
3710 
3711  if (typeof args === 'string') args = { expr: args };
3712  }
3713 
3714  if (!tree) {
3715  console.error('No TTree object available for TTree::Draw');
3716  return painter.DrawingReady();
3717  }
3718 
3719  var callback = painter.DrawingReady.bind(painter);
3720  painter._return_res_painter = true; // indicate that TTree::Draw painter returns not itself but drawing of result object
3721 
3722  JSROOT.cleanup(divid);
3723 
3724  tree.Draw(args, function(histo, hopt, intermediate) {
3725 
3726  var drawid = "";
3727 
3728  if (!args.player) drawid = divid; else
3729  if (args.create_player === 2) drawid = painter.drawid;
3730 
3731  if (drawid)
3732  return JSROOT.redraw(drawid, histo, hopt, intermediate ? null : callback);
3733 
3734  if (args.create_player === 1) { args.player_intermediate = intermediate; return; }
3735 
3736  // redirect drawing to the player
3737  args.player_create = 1;
3738  args.player_intermediate = intermediate;
3739  JSROOT.AssertPrerequisites("jq2d", function() {
3740  JSROOT.CreateTreePlayer(painter);
3741  painter.ConfigureTree(tree);
3742  painter.Show(divid, args);
3743  args.create_player = 2;
3744  JSROOT.redraw(painter.drawid, histo, hopt, args.player_intermediate ? null : callback);
3745  painter.SetItemName("TreePlayer"); // item name used by MDI when process resize
3746  });
3747  });
3748 
3749  return painter;
3750  }
3751 
3752  // ==================================================================================================
3753 
3754 
3755  JSROOT.Painter.drawText = drawText;
3756  JSROOT.Painter.drawLine = drawLine;
3757  JSROOT.Painter.drawPolyLine = drawPolyLine;
3758  JSROOT.Painter.drawArrow = drawArrow;
3759  JSROOT.Painter.drawEllipse = drawEllipse;
3760  JSROOT.Painter.drawPie = drawPie;
3761  JSROOT.Painter.drawBox = drawBox;
3762  JSROOT.Painter.drawMarker = drawMarker;
3763  JSROOT.Painter.drawPolyMarker = drawPolyMarker;
3764  JSROOT.Painter.drawWebPainting = drawWebPainting;
3765  JSROOT.Painter.drawRooPlot = drawRooPlot;
3766  JSROOT.Painter.drawGraph = drawGraph;
3767  JSROOT.Painter.drawFunction = drawFunction;
3768  JSROOT.Painter.drawGraphPolar = drawGraphPolar;
3769  JSROOT.Painter.drawGraphPolargram = drawGraphPolargram;
3770 
3771  JSROOT.TF1Painter = TF1Painter;
3772  JSROOT.TGraphPainter = TGraphPainter;
3773  JSROOT.TGraphPolarPainter = TGraphPolarPainter;
3774  JSROOT.TMultiGraphPainter = TMultiGraphPainter;
3775  JSROOT.TSplinePainter = TSplinePainter;
3776 
3777  return JSROOT;
3778 
3779 }));