otsdaq_utilities  v2_05_02_indev
JSRootPainter.hist.js
1 
4 (function( factory ) {
5  if ( typeof define === "function" && define.amd ) {
6  define( ['JSRootPainter', 'd3'], factory );
7  } else if (typeof exports === 'object' && typeof module !== 'undefined') {
8  factory(require("./JSRootCore.js"), require("d3"));
9  } else {
10  if (typeof d3 != 'object')
11  throw new Error('This extension requires d3.js', 'JSRootPainter.hist.js');
12  if (typeof JSROOT == 'undefined')
13  throw new Error('JSROOT is not defined', 'JSRootPainter.hist.js');
14  if (typeof JSROOT.Painter != 'object')
15  throw new Error('JSROOT.Painter not defined', 'JSRootPainter.hist.js');
16  factory(JSROOT, d3);
17  }
18 } (function(JSROOT, d3) {
19 
20  "use strict";
21 
22  JSROOT.sources.push("hist");
23 
24  JSROOT.ToolbarIcons.th2color = {
25  recs: [{x:0,y:256,w:13,h:39,f:'rgb(38,62,168)'},{x:13,y:371,w:39,h:39},{y:294,h:39},{y:256,h:39},{y:218,h:39},{x:51,y:410,w:39,h:39},{y:371,h:39},{y:333,h:39},{y:294},{y:256,h:39},{y:218,h:39},{y:179,h:39},{y:141,h:39},{y:102,h:39},{y:64},{x:90,y:448,w:39,h:39},{y:410},{y:371,h:39},{y:333,h:39,f:'rgb(22,82,205)'},{y:294},{y:256,h:39,f:'rgb(16,100,220)'},{y:218,h:39},{y:179,h:39,f:'rgb(22,82,205)'},{y:141,h:39},{y:102,h:39,f:'rgb(38,62,168)'},{y:64},{y:0,h:27},{x:128,y:448,w:39,h:39},{y:410},{y:371,h:39},{y:333,h:39,f:'rgb(22,82,205)'},{y:294,f:'rgb(20,129,214)'},{y:256,h:39,f:'rgb(9,157,204)'},{y:218,h:39,f:'rgb(14,143,209)'},{y:179,h:39,f:'rgb(20,129,214)'},{y:141,h:39,f:'rgb(16,100,220)'},{y:102,h:39,f:'rgb(22,82,205)'},{y:64,f:'rgb(38,62,168)'},{y:26,h:39},{y:0,h:27},{x:166,y:486,h:14},{y:448,h:39},{y:410},{y:371,h:39,f:'rgb(22,82,205)'},{y:333,h:39,f:'rgb(20,129,214)'},{y:294,f:'rgb(82,186,146)'},{y:256,h:39,f:'rgb(179,189,101)'},{y:218,h:39,f:'rgb(116,189,129)'},{y:179,h:39,f:'rgb(82,186,146)'},{y:141,h:39,f:'rgb(14,143,209)'},{y:102,h:39,f:'rgb(16,100,220)'},{y:64,f:'rgb(38,62,168)'},{y:26,h:39},{x:205,y:486,w:39,h:14},{y:448,h:39},{y:410},{y:371,h:39,f:'rgb(16,100,220)'},{y:333,h:39,f:'rgb(9,157,204)'},{y:294,f:'rgb(149,190,113)'},{y:256,h:39,f:'rgb(244,198,59)'},{y:218,h:39},{y:179,h:39,f:'rgb(226,192,75)'},{y:141,h:39,f:'rgb(13,167,195)'},{y:102,h:39,f:'rgb(18,114,217)'},{y:64,f:'rgb(22,82,205)'},{y:26,h:39,f:'rgb(38,62,168)'},{x:243,y:448,w:39,h:39},{y:410},{y:371,h:39,f:'rgb(18,114,217)'},{y:333,h:39,f:'rgb(30,175,179)'},{y:294,f:'rgb(209,187,89)'},{y:256,h:39,f:'rgb(251,230,29)'},{y:218,h:39,f:'rgb(249,249,15)'},{y:179,h:39,f:'rgb(226,192,75)'},{y:141,h:39,f:'rgb(30,175,179)'},{y:102,h:39,f:'rgb(18,114,217)'},{y:64,f:'rgb(38,62,168)'},{y:26,h:39},{x:282,y:448,h:39},{y:410},{y:371,h:39,f:'rgb(18,114,217)'},{y:333,h:39,f:'rgb(14,143,209)'},{y:294,f:'rgb(149,190,113)'},{y:256,h:39,f:'rgb(226,192,75)'},{y:218,h:39,f:'rgb(244,198,59)'},{y:179,h:39,f:'rgb(149,190,113)'},{y:141,h:39,f:'rgb(9,157,204)'},{y:102,h:39,f:'rgb(18,114,217)'},{y:64,f:'rgb(38,62,168)'},{y:26,h:39},{x:320,y:448,w:39,h:39},{y:410},{y:371,h:39,f:'rgb(22,82,205)'},{y:333,h:39,f:'rgb(20,129,214)'},{y:294,f:'rgb(46,183,164)'},{y:256,h:39},{y:218,h:39,f:'rgb(82,186,146)'},{y:179,h:39,f:'rgb(9,157,204)'},{y:141,h:39,f:'rgb(20,129,214)'},{y:102,h:39,f:'rgb(16,100,220)'},{y:64,f:'rgb(38,62,168)'},{y:26,h:39},{x:358,y:448,h:39},{y:410},{y:371,h:39,f:'rgb(22,82,205)'},{y:333,h:39},{y:294,f:'rgb(16,100,220)'},{y:256,h:39,f:'rgb(20,129,214)'},{y:218,h:39,f:'rgb(14,143,209)'},{y:179,h:39,f:'rgb(18,114,217)'},{y:141,h:39,f:'rgb(22,82,205)'},{y:102,h:39,f:'rgb(38,62,168)'},{y:64},{y:26,h:39},{x:397,y:448,w:39,h:39},{y:371,h:39},{y:333,h:39},{y:294,f:'rgb(22,82,205)'},{y:256,h:39},{y:218,h:39},{y:179,h:39,f:'rgb(38,62,168)'},{y:141,h:39},{y:102,h:39},{y:64},{y:26,h:39},{x:435,y:410,h:39},{y:371,h:39},{y:333,h:39},{y:294},{y:256,h:39},{y:218,h:39},{y:179,h:39},{y:141,h:39},{y:102,h:39},{y:64},{x:474,y:256,h:39},{y:179,h:39}]
26  };
27 
28  JSROOT.ToolbarIcons.th2colorz = {
29  recs: [{x:128,y:486,w:256,h:26,f:'rgb(38,62,168)'},{y:461,f:'rgb(22,82,205)'},{y:435,f:'rgb(16,100,220)'},{y:410,f:'rgb(18,114,217)'},{y:384,f:'rgb(20,129,214)'},{y:358,f:'rgb(14,143,209)'},{y:333,f:'rgb(9,157,204)'},{y:307,f:'rgb(13,167,195)'},{y:282,f:'rgb(30,175,179)'},{y:256,f:'rgb(46,183,164)'},{y:230,f:'rgb(82,186,146)'},{y:205,f:'rgb(116,189,129)'},{y:179,f:'rgb(149,190,113)'},{y:154,f:'rgb(179,189,101)'},{y:128,f:'rgb(209,187,89)'},{y:102,f:'rgb(226,192,75)'},{y:77,f:'rgb(244,198,59)'},{y:51,f:'rgb(253,210,43)'},{y:26,f:'rgb(251,230,29)'},{y:0,f:'rgb(249,249,15)'}]
30  };
31 
32  JSROOT.ToolbarIcons.th2draw3d = {
33  path: "M172.768,0H51.726C23.202,0,0.002,23.194,0.002,51.712v89.918c0,28.512,23.2,51.718,51.724,51.718h121.042 c28.518,0,51.724-23.2,51.724-51.718V51.712C224.486,23.194,201.286,0,172.768,0z M177.512,141.63c0,2.611-2.124,4.745-4.75,4.745 H51.726c-2.626,0-4.751-2.134-4.751-4.745V51.712c0-2.614,2.125-4.739,4.751-4.739h121.042c2.62,0,4.75,2.125,4.75,4.739 L177.512,141.63L177.512,141.63z "+
34  "M460.293,0H339.237c-28.521,0-51.721,23.194-51.721,51.712v89.918c0,28.512,23.2,51.718,51.721,51.718h121.045 c28.521,0,51.721-23.2,51.721-51.718V51.712C512.002,23.194,488.802,0,460.293,0z M465.03,141.63c0,2.611-2.122,4.745-4.748,4.745 H339.237c-2.614,0-4.747-2.128-4.747-4.745V51.712c0-2.614,2.133-4.739,4.747-4.739h121.045c2.626,0,4.748,2.125,4.748,4.739 V141.63z "+
35  "M172.768,256.149H51.726c-28.524,0-51.724,23.205-51.724,51.726v89.915c0,28.504,23.2,51.715,51.724,51.715h121.042 c28.518,0,51.724-23.199,51.724-51.715v-89.915C224.486,279.354,201.286,256.149,172.768,256.149z M177.512,397.784 c0,2.615-2.124,4.736-4.75,4.736H51.726c-2.626-0.006-4.751-2.121-4.751-4.736v-89.909c0-2.626,2.125-4.753,4.751-4.753h121.042 c2.62,0,4.75,2.116,4.75,4.753L177.512,397.784L177.512,397.784z "+
36  "M460.293,256.149H339.237c-28.521,0-51.721,23.199-51.721,51.726v89.915c0,28.504,23.2,51.715,51.721,51.715h121.045 c28.521,0,51.721-23.199,51.721-51.715v-89.915C512.002,279.354,488.802,256.149,460.293,256.149z M465.03,397.784 c0,2.615-2.122,4.736-4.748,4.736H339.237c-2.614,0-4.747-2.121-4.747-4.736v-89.909c0-2.626,2.121-4.753,4.747-4.753h121.045 c2.615,0,4.748,2.116,4.748,4.753V397.784z"
37  };
38 
39  JSROOT.Painter.CreateDefaultPalette = function() {
40 
41  function HLStoRGB(h, l, s) {
42  var r, g, b;
43  if (s < 1e-100) {
44  r = g = b = l; // achromatic
45  } else {
46  function hue2rgb(p, q, t) {
47  if (t < 0) t += 1;
48  if (t > 1) t -= 1;
49  if (t < 1 / 6) return p + (q - p) * 6 * t;
50  if (t < 1 / 2) return q;
51  if (t < 2 / 3) return p + (q - p) * (2/3 - t) * 6;
52  return p;
53  }
54  var q = (l < 0.5) ? l * (1 + s) : l + s - l * s,
55  p = 2 * l - q;
56  r = hue2rgb(p, q, h + 1/3);
57  g = hue2rgb(p, q, h);
58  b = hue2rgb(p, q, h - 1/3);
59  }
60  return 'rgb(' + Math.round(r*255) + ',' + Math.round(g*255) + ',' + Math.round(b*255) + ')';
61  }
62 
63  var palette = [], saturation = 1, lightness = 0.5, maxHue = 280, minHue = 0, maxPretty = 50;
64  for (var i = 0; i < maxPretty; ++i) {
65  var hue = (maxHue - (i + 1) * ((maxHue - minHue) / maxPretty)) / 360,
66  rgbval = HLStoRGB(hue, lightness, saturation);
67  palette.push(rgbval);
68  }
69  return new JSROOT.ColorPalette(palette);
70  }
71 
72  JSROOT.Painter.CreateGrayPalette = function() {
73  var palette = [];
74  for (var i = 0; i < 50; ++i) {
75  var code = Math.round((i+2)/60*255);
76  palette.push('rgb('+code+','+code+','+code+')');
77  }
78  return new JSROOT.ColorPalette(palette);
79  }
80 
81  JSROOT.Painter.CreateGradientColorTable = function(Stops, Red, Green, Blue, NColors, alpha) {
82  // skip all checks
83  var palette = [];
84 
85  for (var g = 1; g < Stops.length; g++) {
86  // create the colors...
87  var nColorsGradient = parseInt(Math.floor(NColors*Stops[g]) - Math.floor(NColors*Stops[g-1]));
88  for (var c = 0; c < nColorsGradient; c++) {
89  var col = Math.round(Red[g-1] + c * (Red[g] - Red[g-1])/nColorsGradient) + "," +
90  Math.round(Green[g-1] + c * (Green[g] - Green[g-1])/ nColorsGradient) + "," +
91  Math.round(Blue[g-1] + c * (Blue[g] - Blue[g-1])/ nColorsGradient);
92  palette.push("rgb("+col+")");
93  }
94  }
95 
96  return new JSROOT.ColorPalette(palette);
97  }
98 
99  JSROOT.Painter.GetColorPalette = function(col,alfa) {
100  col = col || JSROOT.gStyle.Palette;
101  if ((col>0) && (col<10)) return JSROOT.Painter.CreateGrayPalette();
102  if (col < 51) return JSROOT.Painter.CreateDefaultPalette();
103  if (col > 113) col = 57;
104  var red, green, blue,
105  stops = [ 0.0000, 0.1250, 0.2500, 0.3750, 0.5000, 0.6250, 0.7500, 0.8750, 1.0000 ];
106  switch(col) {
107 
108  // Deep Sea
109  case 51:
110  red = [ 0, 9, 13, 17, 24, 32, 27, 25, 29];
111  green = [ 0, 0, 0, 2, 37, 74, 113, 160, 221];
112  blue = [ 28, 42, 59, 78, 98, 129, 154, 184, 221];
113  break;
114 
115  // Grey Scale
116  case 52:
117  red = [ 0, 32, 64, 96, 128, 160, 192, 224, 255];
118  green = [ 0, 32, 64, 96, 128, 160, 192, 224, 255];
119  blue = [ 0, 32, 64, 96, 128, 160, 192, 224, 255];
120  break;
121 
122  // Dark Body Radiator
123  case 53:
124  red = [ 0, 45, 99, 156, 212, 230, 237, 234, 242];
125  green = [ 0, 0, 0, 45, 101, 168, 238, 238, 243];
126  blue = [ 0, 1, 1, 3, 9, 8, 11, 95, 230];
127  break;
128 
129  // Two-color hue (dark blue through neutral gray to bright yellow)
130  case 54:
131  red = [ 0, 22, 44, 68, 93, 124, 160, 192, 237];
132  green = [ 0, 16, 41, 67, 93, 125, 162, 194, 241];
133  blue = [ 97, 100, 99, 99, 93, 68, 44, 26, 74];
134  break;
135 
136  // Rain Bow
137  case 55:
138  red = [ 0, 5, 15, 35, 102, 196, 208, 199, 110];
139  green = [ 0, 48, 124, 192, 206, 226, 97, 16, 0];
140  blue = [ 99, 142, 198, 201, 90, 22, 13, 8, 2];
141  break;
142 
143  // Inverted Dark Body Radiator
144  case 56:
145  red = [ 242, 234, 237, 230, 212, 156, 99, 45, 0];
146  green = [ 243, 238, 238, 168, 101, 45, 0, 0, 0];
147  blue = [ 230, 95, 11, 8, 9, 3, 1, 1, 0];
148  break;
149 
150  // Bird
151  case 57:
152  red = [ 0.2082*255, 0.0592*255, 0.0780*255, 0.0232*255, 0.1802*255, 0.5301*255, 0.8186*255, 0.9956*255, 0.9764*255];
153  green = [ 0.1664*255, 0.3599*255, 0.5041*255, 0.6419*255, 0.7178*255, 0.7492*255, 0.7328*255, 0.7862*255, 0.9832*255];
154  blue = [ 0.5293*255, 0.8684*255, 0.8385*255, 0.7914*255, 0.6425*255, 0.4662*255, 0.3499*255, 0.1968*255, 0.0539*255];
155  break;
156 
157  // Cubehelix
158  case 58:
159  red = [ 0.0000, 0.0956*255, 0.0098*255, 0.2124*255, 0.6905*255, 0.9242*255, 0.7914*255, 0.7596*255, 1.0000*255];
160  green = [ 0.0000, 0.1147*255, 0.3616*255, 0.5041*255, 0.4577*255, 0.4691*255, 0.6905*255, 0.9237*255, 1.0000*255];
161  blue = [ 0.0000, 0.2669*255, 0.3121*255, 0.1318*255, 0.2236*255, 0.6741*255, 0.9882*255, 0.9593*255, 1.0000*255];
162  break;
163 
164  // Green Red Violet
165  case 59:
166  red = [13, 23, 25, 63, 76, 104, 137, 161, 206];
167  green = [95, 67, 37, 21, 0, 12, 35, 52, 79];
168  blue = [ 4, 3, 2, 6, 11, 22, 49, 98, 208];
169  break;
170 
171  // Blue Red Yellow
172  case 60:
173  red = [0, 61, 89, 122, 143, 160, 185, 204, 231];
174  green = [0, 0, 0, 0, 14, 37, 72, 132, 235];
175  blue = [0, 140, 224, 144, 4, 5, 6, 9, 13];
176  break;
177 
178  // Ocean
179  case 61:
180  red = [ 14, 7, 2, 0, 5, 11, 55, 131, 229];
181  green = [105, 56, 26, 1, 42, 74, 131, 171, 229];
182  blue = [ 2, 21, 35, 60, 92, 113, 160, 185, 229];
183  break;
184 
185  // Color Printable On Grey
186  case 62:
187  red = [ 0, 0, 0, 70, 148, 231, 235, 237, 244];
188  green = [ 0, 0, 0, 0, 0, 69, 67, 216, 244];
189  blue = [ 0, 102, 228, 231, 177, 124, 137, 20, 244];
190  break;
191 
192  // Alpine
193  case 63:
194  red = [ 50, 56, 63, 68, 93, 121, 165, 192, 241];
195  green = [ 66, 81, 91, 96, 111, 128, 155, 189, 241];
196  blue = [ 97, 91, 75, 65, 77, 103, 143, 167, 217];
197  break;
198 
199  // Aquamarine
200  case 64:
201  red = [ 145, 166, 167, 156, 131, 114, 101, 112, 132];
202  green = [ 158, 178, 179, 181, 163, 154, 144, 152, 159];
203  blue = [ 190, 199, 201, 192, 176, 169, 160, 166, 190];
204  break;
205 
206  // Army
207  case 65:
208  red = [ 93, 91, 99, 108, 130, 125, 132, 155, 174];
209  green = [ 126, 124, 128, 129, 131, 121, 119, 153, 173];
210  blue = [ 103, 94, 87, 85, 80, 85, 107, 120, 146];
211  break;
212 
213  // Atlantic
214  case 66:
215  red = [ 24, 40, 69, 90, 104, 114, 120, 132, 103];
216  green = [ 29, 52, 94, 127, 150, 162, 159, 151, 101];
217  blue = [ 29, 52, 96, 132, 162, 181, 184, 186, 131];
218  break;
219 
220  // Aurora
221  case 67:
222  red = [ 46, 38, 61, 92, 113, 121, 132, 150, 191];
223  green = [ 46, 36, 40, 69, 110, 135, 131, 92, 34];
224  blue = [ 46, 80, 74, 70, 81, 105, 165, 211, 225];
225  break;
226 
227  // Avocado
228  case 68:
229  red = [ 0, 4, 12, 30, 52, 101, 142, 190, 237];
230  green = [ 0, 40, 86, 121, 140, 172, 187, 213, 240];
231  blue = [ 0, 9, 14, 18, 21, 23, 27, 35, 101];
232  break;
233 
234  // Beach
235  case 69:
236  red = [ 198, 206, 206, 211, 198, 181, 161, 171, 244];
237  green = [ 103, 133, 150, 172, 178, 174, 163, 175, 244];
238  blue = [ 49, 54, 55, 66, 91, 130, 184, 224, 244];
239  break;
240 
241  // Black Body
242  case 70:
243  red = [ 243, 243, 240, 240, 241, 239, 186, 151, 129];
244  green = [ 0, 46, 99, 149, 194, 220, 183, 166, 147];
245  blue = [ 6, 8, 36, 91, 169, 235, 246, 240, 233];
246  break;
247 
248  // Blue Green Yellow
249  case 71:
250  red = [ 22, 19, 19, 25, 35, 53, 88, 139, 210];
251  green = [ 0, 32, 69, 108, 135, 159, 183, 198, 215];
252  blue = [ 77, 96, 110, 116, 110, 100, 90, 78, 70];
253  break;
254 
255  // Brown Cyan
256  case 72:
257  red = [ 68, 116, 165, 182, 189, 180, 145, 111, 71];
258  green = [ 37, 82, 135, 178, 204, 225, 221, 202, 147];
259  blue = [ 16, 55, 105, 147, 196, 226, 232, 224, 178];
260  break;
261 
262  // CMYK
263  case 73:
264  red = [ 61, 99, 136, 181, 213, 225, 198, 136, 24];
265  green = [ 149, 140, 96, 83, 132, 178, 190, 135, 22];
266  blue = [ 214, 203, 168, 135, 110, 100, 111, 113, 22];
267  break;
268 
269  // Candy
270  case 74:
271  red = [ 76, 120, 156, 183, 197, 180, 162, 154, 140];
272  green = [ 34, 35, 42, 69, 102, 137, 164, 188, 197];
273  blue = [ 64, 69, 78, 105, 142, 177, 205, 217, 198];
274  break;
275 
276  // Cherry
277  case 75:
278  red = [ 37, 102, 157, 188, 196, 214, 223, 235, 251];
279  green = [ 37, 29, 25, 37, 67, 91, 132, 185, 251];
280  blue = [ 37, 32, 33, 45, 66, 98, 137, 187, 251];
281  break;
282 
283  // Coffee
284  case 76:
285  red = [ 79, 100, 119, 137, 153, 172, 192, 205, 250];
286  green = [ 63, 79, 93, 103, 115, 135, 167, 196, 250];
287  blue = [ 51, 59, 66, 61, 62, 70, 110, 160, 250];
288  break;
289 
290  // Dark Rain Bow
291  case 77:
292  red = [ 43, 44, 50, 66, 125, 172, 178, 155, 157];
293  green = [ 63, 63, 85, 101, 138, 163, 122, 51, 39];
294  blue = [ 121, 101, 58, 44, 47, 55, 57, 44, 43];
295  break;
296 
297  // Dark Terrain
298  case 78:
299  red = [ 0, 41, 62, 79, 90, 87, 99, 140, 228];
300  green = [ 0, 57, 81, 93, 85, 70, 71, 125, 228];
301  blue = [ 95, 91, 91, 82, 60, 43, 44, 112, 228];
302  break;
303 
304  // Fall
305  case 79:
306  red = [ 49, 59, 72, 88, 114, 141, 176, 205, 222];
307  green = [ 78, 72, 66, 57, 59, 75, 106, 142, 173];
308  blue = [ 78, 55, 46, 40, 39, 39, 40, 41, 47];
309  break;
310 
311  // Fruit Punch
312  case 80:
313  red = [ 243, 222, 201, 185, 165, 158, 166, 187, 219];
314  green = [ 94, 108, 132, 135, 125, 96, 68, 51, 61];
315  blue = [ 7, 9, 12, 19, 45, 89, 118, 146, 118];
316  break;
317 
318  // Fuchsia
319  case 81:
320  red = [ 19, 44, 74, 105, 137, 166, 194, 206, 220];
321  green = [ 19, 28, 40, 55, 82, 110, 159, 181, 220];
322  blue = [ 19, 42, 68, 96, 129, 157, 188, 203, 220];
323  break;
324 
325  // Grey Yellow
326  case 82:
327  red = [ 33, 44, 70, 99, 140, 165, 199, 211, 216];
328  green = [ 38, 50, 76, 105, 140, 165, 191, 189, 167];
329  blue = [ 55, 67, 97, 124, 140, 166, 163, 129, 52];
330  break;
331 
332  // Green Brown Terrain
333  case 83:
334  red = [ 0, 33, 73, 124, 136, 152, 159, 171, 223];
335  green = [ 0, 43, 92, 124, 134, 126, 121, 144, 223];
336  blue = [ 0, 43, 68, 76, 73, 64, 72, 114, 223];
337  break;
338 
339  // Green Pink
340  case 84:
341  red = [ 5, 18, 45, 124, 193, 223, 205, 128, 49];
342  green = [ 48, 134, 207, 230, 193, 113, 28, 0, 7];
343  blue = [ 6, 15, 41, 121, 193, 226, 208, 130, 49];
344  break;
345 
346  // Island
347  case 85:
348  red = [ 180, 106, 104, 135, 164, 188, 189, 165, 144];
349  green = [ 72, 126, 154, 184, 198, 207, 205, 190, 179];
350  blue = [ 41, 120, 158, 188, 194, 181, 145, 100, 62];
351  break;
352 
353  // Lake
354  case 86:
355  red = [ 57, 72, 94, 117, 136, 154, 174, 192, 215];
356  green = [ 0, 33, 68, 109, 140, 171, 192, 196, 209];
357  blue = [ 116, 137, 173, 201, 200, 201, 203, 190, 187];
358  break;
359 
360  // Light Temperature
361  case 87:
362  red = [ 31, 71, 123, 160, 210, 222, 214, 199, 183];
363  green = [ 40, 117, 171, 211, 231, 220, 190, 132, 65];
364  blue = [ 234, 214, 228, 222, 210, 160, 105, 60, 34];
365  break;
366 
367  // Light Terrain
368  case 88:
369  red = [ 123, 108, 109, 126, 154, 172, 188, 196, 218];
370  green = [ 184, 138, 130, 133, 154, 175, 188, 196, 218];
371  blue = [ 208, 130, 109, 99, 110, 122, 150, 171, 218];
372  break;
373 
374  // Mint
375  case 89:
376  red = [ 105, 106, 122, 143, 159, 172, 176, 181, 207];
377  green = [ 252, 197, 194, 187, 174, 162, 153, 136, 125];
378  blue = [ 146, 133, 144, 155, 163, 167, 166, 162, 174];
379  break;
380 
381  // Neon
382  case 90:
383  red = [ 171, 141, 145, 152, 154, 159, 163, 158, 177];
384  green = [ 236, 143, 100, 63, 53, 55, 44, 31, 6];
385  blue = [ 59, 48, 46, 44, 42, 54, 82, 112, 179];
386  break;
387 
388  // Pastel
389  case 91:
390  red = [ 180, 190, 209, 223, 204, 228, 205, 152, 91];
391  green = [ 93, 125, 147, 172, 181, 224, 233, 198, 158];
392  blue = [ 236, 218, 160, 133, 114, 132, 162, 220, 218];
393  break;
394 
395  // Pearl
396  case 92:
397  red = [ 225, 183, 162, 135, 115, 111, 119, 145, 211];
398  green = [ 205, 177, 166, 135, 124, 117, 117, 132, 172];
399  blue = [ 186, 165, 155, 135, 126, 130, 150, 178, 226];
400  break;
401 
402  // Pigeon
403  case 93:
404  red = [ 39, 43, 59, 63, 80, 116, 153, 177, 223];
405  green = [ 39, 43, 59, 74, 91, 114, 139, 165, 223];
406  blue = [ 39, 50, 59, 70, 85, 115, 151, 176, 223];
407  break;
408 
409  // Plum
410  case 94:
411  red = [ 0, 38, 60, 76, 84, 89, 101, 128, 204];
412  green = [ 0, 10, 15, 23, 35, 57, 83, 123, 199];
413  blue = [ 0, 11, 22, 40, 63, 86, 97, 94, 85];
414  break;
415 
416  // Red Blue
417  case 95:
418  red = [ 94, 112, 141, 165, 167, 140, 91, 49, 27];
419  green = [ 27, 46, 88, 135, 166, 161, 135, 97, 58];
420  blue = [ 42, 52, 81, 106, 139, 158, 155, 137, 116];
421  break;
422 
423  // Rose
424  case 96:
425  red = [ 30, 49, 79, 117, 135, 151, 146, 138, 147];
426  green = [ 63, 60, 72, 90, 94, 94, 68, 46, 16];
427  blue = [ 18, 28, 41, 56, 62, 63, 50, 36, 21];
428  break;
429 
430  // Rust
431  case 97:
432  red = [ 0, 30, 63, 101, 143, 152, 169, 187, 230];
433  green = [ 0, 14, 28, 42, 58, 61, 67, 74, 91];
434  blue = [ 39, 26, 21, 18, 15, 14, 14, 13, 13];
435  break;
436 
437  // Sandy Terrain
438  case 98:
439  red = [ 149, 140, 164, 179, 182, 181, 131, 87, 61];
440  green = [ 62, 70, 107, 136, 144, 138, 117, 87, 74];
441  blue = [ 40, 38, 45, 49, 49, 49, 38, 32, 34];
442  break;
443 
444  // Sienna
445  case 99:
446  red = [ 99, 112, 148, 165, 179, 182, 183, 183, 208];
447  green = [ 39, 40, 57, 79, 104, 127, 148, 161, 198];
448  blue = [ 15, 16, 18, 33, 51, 79, 103, 129, 177];
449  break;
450 
451  // Solar
452  case 100:
453  red = [ 99, 116, 154, 174, 200, 196, 201, 201, 230];
454  green = [ 0, 0, 8, 32, 58, 83, 119, 136, 173];
455  blue = [ 5, 6, 7, 9, 9, 14, 17, 19, 24];
456  break;
457 
458  // South West
459  case 101:
460  red = [ 82, 106, 126, 141, 155, 163, 142, 107, 66];
461  green = [ 62, 44, 69, 107, 135, 152, 149, 132, 119];
462  blue = [ 39, 25, 31, 60, 73, 68, 49, 72, 188];
463  break;
464 
465  // Starry Night
466  case 102:
467  red = [ 18, 29, 44, 72, 116, 158, 184, 208, 221];
468  green = [ 27, 46, 71, 105, 146, 177, 189, 190, 183];
469  blue = [ 39, 55, 80, 108, 130, 133, 124, 100, 76];
470  break;
471 
472  // Sunset
473  case 103:
474  red = [ 0, 48, 119, 173, 212, 224, 228, 228, 245];
475  green = [ 0, 13, 30, 47, 79, 127, 167, 205, 245];
476  blue = [ 0, 68, 75, 43, 16, 22, 55, 128, 245];
477  break;
478 
479  // Temperature Map
480  case 104:
481  red = [ 34, 70, 129, 187, 225, 226, 216, 193, 179];
482  green = [ 48, 91, 147, 194, 226, 229, 196, 110, 12];
483  blue = [ 234, 212, 216, 224, 206, 110, 53, 40, 29];
484  break;
485 
486  // Thermometer
487  case 105:
488  red = [ 30, 55, 103, 147, 174, 203, 188, 151, 105];
489  green = [ 0, 65, 138, 182, 187, 175, 121, 53, 9];
490  blue = [ 191, 202, 212, 208, 171, 140, 97, 57, 30];
491  break;
492 
493  // Valentine
494  case 106:
495  red = [ 112, 97, 113, 125, 138, 159, 178, 188, 225];
496  green = [ 16, 17, 24, 37, 56, 81, 110, 136, 189];
497  blue = [ 38, 35, 46, 59, 78, 103, 130, 152, 201];
498  break;
499 
500  // Visible Spectrum
501  case 107:
502  red = [ 18, 72, 5, 23, 29, 201, 200, 98, 29];
503  green = [ 0, 0, 43, 167, 211, 117, 0, 0, 0];
504  blue = [ 51, 203, 177, 26, 10, 9, 8, 3, 0];
505  break;
506 
507  // Water Melon
508  case 108:
509  red = [ 19, 42, 64, 88, 118, 147, 175, 187, 205];
510  green = [ 19, 55, 89, 125, 154, 169, 161, 129, 70];
511  blue = [ 19, 32, 47, 70, 100, 128, 145, 130, 75];
512  break;
513 
514  // Cool
515  case 109:
516  red = [ 33, 31, 42, 68, 86, 111, 141, 172, 227];
517  green = [ 255, 175, 145, 106, 88, 55, 15, 0, 0];
518  blue = [ 255, 205, 202, 203, 208, 205, 203, 206, 231];
519  break;
520 
521  // Copper
522  case 110:
523  red = [ 0, 25, 50, 79, 110, 145, 181, 201, 254];
524  green = [ 0, 16, 30, 46, 63, 82, 101, 124, 179];
525  blue = [ 0, 12, 21, 29, 39, 49, 61, 74, 103];
526  break;
527 
528  // Gist Earth
529  case 111:
530  red = [ 0, 13, 30, 44, 72, 120, 156, 200, 247];
531  green = [ 0, 36, 84, 117, 141, 153, 151, 158, 247];
532  blue = [ 0, 94, 100, 82, 56, 66, 76, 131, 247];
533  break;
534 
535  // Viridis
536  case 112:
537  red = [ 26, 51, 43, 33, 28, 35, 74, 144, 246];
538  green = [ 9, 24, 55, 87, 118, 150, 180, 200, 222];
539  blue = [ 30, 96, 112, 114, 112, 101, 72, 35, 0];
540  break;
541 
542  // Cividis
543  case 113:
544  red = [ 0, 5, 65, 97, 124, 156, 189, 224, 255 ];
545  green = [ 32, 54, 77, 100, 123, 148, 175, 203, 234 ];
546  blue = [ 77, 110, 107, 111, 120, 119, 111, 94, 70 ];
547  break;
548 
549  default:
550  return JSROOT.Painter.CreateDefaultPalette();
551  }
552 
553  return JSROOT.Painter.CreateGradientColorTable(stops, red, green, blue, 255, alfa);
554  }
555 
556  // ============================================================
557 
558  // painter class for objects, derived from TPave
559  function TPavePainter(pave) {
560  JSROOT.TObjectPainter.call(this, pave);
561  this.Enabled = true;
562  this.UseContextMenu = true;
563  this.UseTextColor = false; // indicates if text color used, enabled menu entry
564  this.FirstRun = 1; // counter required to correctly complete drawing
565  this.AssignFinishPave();
566  }
567 
568  TPavePainter.prototype = Object.create(JSROOT.TObjectPainter.prototype);
569 
570  TPavePainter.prototype.AssignFinishPave = function() {
571  function func() {
572  // function used to signal drawing ready, required when text drawing postponed due to mathjax
573  if (this.FirstRun <= 0) return;
574  this.FirstRun--;
575  if (this.FirstRun!==0) return;
576  delete this.FinishPave; // no need for that callback
577  this.DrawingReady();
578  }
579  this.FinishPave = func.bind(this);
580  }
581 
582  TPavePainter.prototype.DrawPave = function(arg) {
583  // this draw only basic TPave
584 
585  this.UseTextColor = false;
586 
587  if (!this.Enabled)
588  return this.RemoveDrawG();
589 
590  var pt = this.GetObject(), opt = pt.fOption.toUpperCase();
591 
592  if (pt.fInit===0) {
593  this.stored = JSROOT.extend({}, pt); // store coordinates to use them when updating
594  pt.fInit = 1;
595  var pad = this.root_pad();
596 
597  if ((pt._typename == "TPaletteAxis") && !pt.fX1 && !pt.fX2 && !pt.fY1 && !pt.fY2) {
598  var fp = this.frame_painter();
599  if (fp) {
600  pt.fX1NDC = fp.fX2NDC + 0.01;
601  pt.fX2NDC = Math.min(0.96, fp.fX2NDC + 0.06);
602  pt.fY1NDC = fp.fY1NDC;
603  pt.fY2NDC = fp.fY2NDC;
604  } else {
605  pt.fX2NDC = 0.8;
606  pt.fX1NDC = 0.9;
607  pt.fY1NDC = 0.1;
608  pt.fY2NDC = 0.9;
609  }
610  } else if (opt.indexOf("NDC")>=0) {
611  pt.fX1NDC = pt.fX1; pt.fX2NDC = pt.fX2;
612  pt.fY1NDC = pt.fY1; pt.fY2NDC = pt.fY2;
613  } else if (pad) {
614  if (pad.fLogx) {
615  if (pt.fX1 > 0) pt.fX1 = JSROOT.log10(pt.fX1);
616  if (pt.fX2 > 0) pt.fX2 = JSROOT.log10(pt.fX2);
617  }
618  if (pad.fLogy) {
619  if (pt.fY1 > 0) pt.fY1 = JSROOT.log10(pt.fY1);
620  if (pt.fY2 > 0) pt.fY2 = JSROOT.log10(pt.fY2);
621  }
622  pt.fX1NDC = (pt.fX1 - pad.fX1) / (pad.fX2 - pad.fX1);
623  pt.fY1NDC = (pt.fY1 - pad.fY1) / (pad.fY2 - pad.fY1);
624  pt.fX2NDC = (pt.fX2 - pad.fX1) / (pad.fX2 - pad.fX1);
625  pt.fY2NDC = (pt.fY2 - pad.fY1) / (pad.fY2 - pad.fY1);
626  } else {
627  pt.fX1NDC = pt.fY1NDC = 0.1;
628  pt.fX2NDC = pt.fY2NDC = 0.9;
629  }
630 
631  if ((pt.fX1NDC == pt.fX2NDC) && (pt.fY1NDC == pt.fY2NDC) && (pt._typename == "TLegend")) {
632  pt.fX1NDC = Math.max(pad ? pad.fLeftMargin : 0, pt.fX2NDC - 0.3);
633  pt.fX2NDC = Math.min(pt.fX1NDC + 0.3, pad ? 1-pad.fRightMargin : 1);
634  var h0 = Math.max(pt.fPrimitives ? pt.fPrimitives.arr.length*0.05 : 0, 0.2);
635  pt.fY2NDC = Math.min(pad ? 1-pad.fTopMargin : 1, pt.fY1NDC + h0);
636  pt.fY1NDC = Math.max(pt.fY2NDC - h0, pad ? pad.fBottomMargin : 0);
637  }
638  }
639 
640  var pos_x = Math.round(pt.fX1NDC * this.pad_width()),
641  pos_y = Math.round((1.0 - pt.fY2NDC) * this.pad_height()),
642  width = Math.round((pt.fX2NDC - pt.fX1NDC) * this.pad_width()),
643  height = Math.round((pt.fY2NDC - pt.fY1NDC) * this.pad_height()),
644  brd = pt.fBorderSize,
645  dx = (opt.indexOf("L")>=0) ? -1 : ((opt.indexOf("R")>=0) ? 1 : 0),
646  dy = (opt.indexOf("T")>=0) ? -1 : ((opt.indexOf("B")>=0) ? 1 : 0);
647 
648  // container used to recalculate coordinates
649  this.CreateG();
650 
651  this.draw_g.attr("transform", "translate(" + pos_x + "," + pos_y + ")");
652 
653  //if (!this.lineatt)
654  // this.lineatt = new JSROOT.TAttLineHandler(pt, brd>0 ? 1 : 0);
655 
656  this.createAttLine({ attr: pt, width: (brd > 0) ? pt.fLineWidth : 0 });
657 
658  this.createAttFill({ attr: pt });
659 
660  if (pt._typename == "TDiamond") {
661  var h2 = Math.round(height/2), w2 = Math.round(width/2),
662  dpath = "l"+w2+",-"+h2 + "l"+w2+","+h2 + "l-"+w2+","+h2+"z";
663 
664  if ((brd > 1) && (pt.fShadowColor > 0) && (dx || dy) && !this.fillatt.empty())
665  this.draw_g.append("svg:path")
666  .attr("d","M0,"+(h2+brd) + dpath)
667  .style("fill", this.get_color(pt.fShadowColor))
668  .style("stroke", this.get_color(pt.fShadowColor))
669  .style("stroke-width", "1px");
670 
671  this.draw_g.append("svg:path")
672  .attr("d", "M0,"+h2 +dpath)
673  .call(this.fillatt.func)
674  .call(this.lineatt.func);
675 
676  var text_g = this.draw_g.append("svg:g")
677  .attr("transform", "translate(" + Math.round(width/4) + "," + Math.round(height/4) + ")");
678 
679  this.DrawPaveText(w2, h2, arg, text_g);
680 
681  return;
682  }
683 
684  // add shadow decoration before main rect
685  if ((brd > 1) && (pt.fShadowColor > 0) && !pt.fNpaves && (dx || dy)) {
686  var spath = "", scol = this.get_color(pt.fShadowColor);
687  if (this.fillatt.empty()) {
688  if ((dx<0) && (dy<0))
689  spath = "M0,0v"+(height-brd)+"h-"+brd+"v-"+height+"h"+width+"v"+brd;
690  else // ((dx<0) && (dy>0))
691  spath = "M0,"+height+"v-"+(height-brd)+"h-"+brd+"v"+height+"h"+width+"v-"+brd;
692  } else {
693  // when main is filled, one also can use fill for shadow to avoid complexity
694  spath = "M"+(dx*brd)+","+(dy*brd) + "v"+height + "h"+width + "v-"+height
695  }
696  this.draw_g.append("svg:path")
697  .attr("d", spath + "z")
698  .style("fill", scol)
699  .style("stroke", scol)
700  .style("stroke-width", "1px");
701  }
702 
703  if (pt.fNpaves)
704  for (var n = pt.fNpaves-1; n>0; --n)
705  this.draw_g.append("svg:path")
706  .attr("d", "M" + (dx*4*n) + ","+ (dy*4*n) + "h"+width + "v"+height + "h-"+width + "z")
707  .call(this.fillatt.func)
708  .call(this.lineatt.func);
709 
710  var rect =
711  this.draw_g.append("svg:path")
712  .attr("d", "M0,0h"+width + "v"+height + "h-"+width + "z")
713  .call(this.fillatt.func)
714  .call(this.lineatt.func);
715 
716  if ('PaveDrawFunc' in this)
717  this.PaveDrawFunc(width, height, arg);
718 
719  if (JSROOT.BatchMode || (pt._typename=="TPave")) return;
720 
721  // here all kind of interactive settings
722 
723  rect.style("pointer-events", "visibleFill")
724  .on("mouseenter", this.ShowObjectStatus.bind(this))
725 
726  // position and size required only for drag functions
727  this.draw_g.attr("x", pos_x)
728  .attr("y", pos_y)
729  .attr("width", width)
730  .attr("height", height);
731 
732  this.AddDrag({ obj: pt, minwidth: 10, minheight: 20, canselect: true,
733  redraw: this.DragRedraw.bind(this),
734  ctxmenu: JSROOT.touches && JSROOT.gStyle.ContextMenu && this.UseContextMenu });
735 
736  if (this.UseContextMenu && JSROOT.gStyle.ContextMenu)
737  this.draw_g.on("contextmenu", this.ShowContextMenu.bind(this));
738  }
739 
740  TPavePainter.prototype.DragRedraw = function() {
741  this.InteractiveRedraw(false, "pave_moved");
742  this.DrawPave();
743  }
744 
745  TPavePainter.prototype.FillWebObjectOptions = function(res) {
746  if (!res) {
747  if (!this.snapid) return null;
748  res = { _typename: "TWebObjectOptions", snapid: this.snapid.toString(), opt: this.OptionsAsString(), fcust: "", fopt: [] };
749  }
750 
751  var pave = this.GetObject();
752 
753  if (pave && pave.fInit) {
754  res.fcust = "pave";
755  res.fopt = [pave.fX1NDC,pave.fY1NDC,pave.fX2NDC,pave.fY2NDC];
756  }
757 
758  return res;
759  }
760 
761  TPavePainter.prototype.DrawPaveLabel = function(_width, _height) {
762  this.UseTextColor = true;
763 
764  var pave = this.GetObject();
765 
766  this.StartTextDrawing(pave.fTextFont, _height/1.2);
767 
768  this.DrawText({ align: pave.fTextAlign, width: _width, height: _height, text: pave.fLabel, color: this.get_color(pave.fTextColor) });
769 
770  this.FinishTextDrawing(null, this.FinishPave);
771  }
772 
773  TPavePainter.prototype.DrawPaveStats = function(width, height) {
774 
775  if (this.IsStats()) this.FillStatistic();
776 
777  var pt = this.GetObject(), lines = [],
778  tcolor = this.get_color(pt.fTextColor),
779  first_stat = 0, num_cols = 0, maxlen = 0;
780 
781  // now draw TLine and TBox objects
782  for (var j=0;j<pt.fLines.arr.length;++j) {
783  var entry = pt.fLines.arr[j];
784  if ((entry._typename=="TText") || (entry._typename=="TLatex"))
785  lines.push(entry.fTitle);
786  }
787 
788  var nlines = lines.length;
789 
790  // adjust font size
791  for (var j = 0; j < nlines; ++j) {
792  var line = lines[j];
793  if (j>0) maxlen = Math.max(maxlen, line.length);
794  if ((j == 0) || (line.indexOf('|') < 0)) continue;
795  if (first_stat === 0) first_stat = j;
796  var parts = line.split("|");
797  if (parts.length > num_cols)
798  num_cols = parts.length;
799  }
800 
801  // for characters like 'p' or 'y' several more pixels required to stay in the box when drawn in last line
802  var stepy = height / nlines, has_head = false, margin_x = pt.fMargin * width;
803 
804  this.StartTextDrawing(pt.fTextFont, height/(nlines * 1.2));
805 
806  this.UseTextColor = true;
807 
808  if (nlines == 1) {
809  this.DrawText({ align: pt.fTextAlign, width: width, height: height, text: lines[0], color: tcolor, latex: 1 });
810  } else
811  for (var j = 0; j < nlines; ++j) {
812  var posy = j*stepy, jcolor = tcolor;
813  this.UseTextColor = true;
814 
815  if (first_stat && (j >= first_stat)) {
816  var parts = lines[j].split("|");
817  for (var n = 0; n < parts.length; ++n)
818  this.DrawText({ align: "middle", x: width * n / num_cols, y: posy, latex: 0,
819  width: width/num_cols, height: stepy, text: parts[n], color: jcolor });
820  } else if (lines[j].indexOf('=') < 0) {
821  if (j==0) {
822  has_head = true;
823  if (lines[j].length > maxlen + 5)
824  lines[j] = lines[j].substr(0,maxlen+2) + "...";
825  }
826  this.DrawText({ align: (j == 0) ? "middle" : "start", x: margin_x, y: posy,
827  width: width-2*margin_x, height: stepy, text: lines[j], color: jcolor });
828  } else {
829  var parts = lines[j].split("="), sumw = 0;
830  for (var n = 0; n < 2; ++n)
831  sumw += this.DrawText({ align: (n == 0) ? "start" : "end", x: margin_x, y: posy,
832  width: width-2*margin_x, height: stepy, text: parts[n], color: jcolor });
833  this.TextScaleFactor(1.05*sumw/(width-2*margin_x), this.draw_g);
834  }
835  }
836 
837  var lpath = "";
838 
839  if ((pt.fBorderSize > 0) && has_head)
840  lpath += "M0," + Math.round(stepy) + "h" + width;
841 
842  if ((first_stat > 0) && (num_cols > 1)) {
843  for (var nrow = first_stat; nrow < nlines; ++nrow)
844  lpath += "M0," + Math.round(nrow * stepy) + "h" + width;
845  for (var ncol = 0; ncol < num_cols - 1; ++ncol)
846  lpath += "M" + Math.round(width / num_cols * (ncol + 1)) + "," + Math.round(first_stat * stepy) + "V" + height;
847  }
848 
849  if (lpath) this.draw_g.append("svg:path").attr("d",lpath).call(this.lineatt.func);
850 
851  this.FinishTextDrawing(undefined, this.FinishPave);
852 
853  this.draw_g.classed("most_upper_primitives", true); // this primitive will remain on top of list
854  }
855 
856  TPavePainter.prototype.DrawPaveText = function(width, height, dummy_arg, text_g) {
857 
858  var pt = this.GetObject(),
859  tcolor = this.get_color(pt.fTextColor),
860  nlines = 0, lines = [],
861  can_height = this.pad_height(),
862  pp = this.pad_painter(),
863  individual_positioning = false,
864  draw_header = (pt.fLabel.length>0);
865 
866  if (draw_header) this.FirstRun++; // increment finish counter
867 
868  if (!text_g) text_g = this.draw_g;
869 
870  // first check how many text lines in the list
871  for (var j=0;j<pt.fLines.arr.length;++j) {
872  var entry = pt.fLines.arr[j];
873  if ((entry._typename=="TText") || (entry._typename=="TLatex")) {
874  nlines++; // count lines
875  if ((entry.fX>0) || (entry.fY>0)) individual_positioning = true;
876  }
877  }
878 
879  var fast_draw = (nlines==1) && pp && pp._fast_drawing, nline = 0;
880 
881  // now draw TLine and TBox objects
882  for (var j=0;j<pt.fLines.arr.length;++j) {
883  var entry = pt.fLines.arr[j],
884  ytext = (nlines>0) ? Math.round((1-(nline-0.5)/nlines)*height) : 0;
885  switch (entry._typename) {
886  case "TText":
887  case "TLatex":
888  nline++; // just count line number
889  if (individual_positioning) {
890  // each line should be drawn and scaled separately
891 
892  var lx = entry.fX, ly = entry.fY;
893 
894  if ((lx>0) && (lx<1)) lx = Math.round(lx*width); else lx = pt.fMargin * width;
895  if ((ly>0) && (ly<1)) ly = Math.round((1-ly)*height); else ly = ytext;
896 
897  var jcolor = entry.fTextColor ? this.get_color(entry.fTextColor) : "";
898  if (!jcolor) {
899  jcolor = tcolor;
900  this.UseTextColor = true;
901  }
902 
903  this.StartTextDrawing(pt.fTextFont, (entry.fTextSize || pt.fTextSize) * can_height, text_g);
904 
905  this.DrawText({ align: entry.fTextAlign || pt.fTextAlign, x: lx, y: ly, text: entry.fTitle, color: jcolor,
906  latex: (entry._typename == "TText") ? 0 : 1, draw_g: text_g, fast: fast_draw });
907 
908  this.FinishTextDrawing(text_g, this.FinishPave);
909 
910  this.FirstRun++;
911 
912  } else {
913  lines.push(entry); // make as before
914  }
915  break;
916  case "TLine":
917  case "TBox":
918  var lx1 = entry.fX1, lx2 = entry.fX2,
919  ly1 = entry.fY1, ly2 = entry.fY2;
920  if (lx1!==0) lx1 = Math.round(lx1*width);
921  lx2 = lx2 ? Math.round(lx2*width) : width;
922  ly1 = ly1 ? Math.round((1-ly1)*height) : ytext;
923  ly2 = ly2 ? Math.round((1-ly2)*height) : ytext;
924  if (entry._typename == "TLine") {
925  var lineatt = new JSROOT.TAttLineHandler(entry);
926  text_g.append("svg:line")
927  .attr("x1", lx1)
928  .attr("y1", ly1)
929  .attr("x2", lx2)
930  .attr("y2", ly2)
931  .call(lineatt.func);
932  } else {
933  var fillatt = this.createAttFill(entry);
934 
935  text_g.append("svg:rect")
936  .attr("x", lx1)
937  .attr("y", ly2)
938  .attr("width", lx2-lx1)
939  .attr("height", ly1-ly2)
940  .call(fillatt.func);
941  }
942  break;
943  }
944  }
945 
946  if (individual_positioning) {
947 
948  // we should call FinishPave
949  if (this.FinishPave) this.FinishPave();
950 
951  } else {
952 
953  // for characters like 'p' or 'y' several more pixels required to stay in the box when drawn in last line
954  var stepy = height / nlines, has_head = false, margin_x = pt.fMargin * width, max_font_size = 0;
955 
956  // for single line (typically title) limit font size
957  if ((nlines == 1) && (pt.fTextSize > 0)) {
958  max_font_size = Math.round(pt.fTextSize*can_height);
959  if (max_font_size < 3) max_font_size = 3;
960  }
961 
962  this.StartTextDrawing(pt.fTextFont, height/(nlines * 1.2), text_g, max_font_size);
963 
964  for (var j = 0; j < nlines; ++j) {
965  var arg = null, lj = lines[j];
966 
967  if (nlines == 1) {
968  arg = { x:0, y:0, width: width, height: height };
969  } else {
970  arg = { x: margin_x, y: j*stepy, width: width-2*margin_x, height: stepy };
971  if (lj.fTextColor) arg.color = this.get_color(lj.fTextColor);
972  if (lj.fTextSize) arg.font_size = Math.round(lj.fTextSize*can_height);
973  }
974 
975  arg.align = pt.fTextAlign;
976  arg.draw_g = text_g;
977  arg.latex = (lj._typename == "TText" ? 0 : 1);
978  arg.text = lj.fTitle;
979  arg.fast = fast_draw;
980  if (!arg.color) { this.UseTextColor = true; arg.color = tcolor; }
981 
982  this.DrawText(arg);
983  }
984 
985  this.FinishTextDrawing(text_g, this.FinishPave);
986  }
987 
988  if (draw_header) {
989  var x = Math.round(width*0.25),
990  y = Math.round(-height*0.02),
991  w = Math.round(width*0.5),
992  h = Math.round(height*0.04),
993  lbl_g = text_g.append("svg:g");
994 
995  lbl_g.append("svg:path")
996  .attr("d", "M"+x+","+y + "h"+w + "v"+h + "h-"+w + "z")
997  .call(this.fillatt.func)
998  .call(this.lineatt.func);
999 
1000  this.StartTextDrawing(pt.fTextFont, h/1.5, lbl_g);
1001 
1002  this.DrawText({ align: 22, x: x, y: y, width: w, height: h, text: pt.fLabel, color: tcolor, draw_g: lbl_g });
1003 
1004  this.FinishTextDrawing(lbl_g, this.FinishPave);
1005 
1006  this.UseTextColor = true;
1007  }
1008  }
1009 
1010  TPavePainter.prototype.Format = function(value, fmt) {
1011  // method used to convert value to string according specified format
1012  // format can be like 5.4g or 4.2e or 6.4f
1013  if (!fmt) fmt = "stat";
1014 
1015  var pave = this.GetObject();
1016 
1017  switch(fmt) {
1018  case "stat" : fmt = pave.fStatFormat || JSROOT.gStyle.fStatFormat; break;
1019  case "fit": fmt = pave.fFitFormat || JSROOT.gStyle.fFitFormat; break;
1020  case "entries": if ((Math.abs(value) < 1e9) && (Math.round(value) == value)) return value.toFixed(0); fmt = "14.7g"; break;
1021  case "last": fmt = this.lastformat; break;
1022  }
1023 
1024  delete this.lastformat;
1025 
1026  var res = JSROOT.FFormat(value, fmt || "6.4g");
1027 
1028  this.lastformat = JSROOT.lastFFormat;
1029 
1030  return res;
1031  }
1032 
1033  TPavePainter.prototype.DrawPaveLegend = function(w, h) {
1034 
1035  var legend = this.GetObject(),
1036  nlines = legend.fPrimitives.arr.length,
1037  ncols = legend.fNColumns,
1038  nrows = nlines;
1039 
1040  if (ncols<2) ncols = 1; else { while ((nrows-1)*ncols >= nlines) nrows--; }
1041 
1042  function isEmpty(entry) {
1043  return !entry.fObject && !entry.fOption && (!entry.fLabel || (entry.fLabel == " "));
1044  }
1045 
1046  if (ncols==1) {
1047  for (var i=0;i<nlines;++i)
1048  if (isEmpty(legend.fPrimitives.arr[i])) nrows--;
1049  }
1050 
1051  if (nrows<1) nrows = 1;
1052 
1053  var tcolor = this.get_color(legend.fTextColor),
1054  column_width = Math.round(w/ncols),
1055  padding_x = Math.round(0.03*w/ncols),
1056  padding_y = Math.round(0.03*h),
1057  step_y = (h - 2*padding_y)/nrows,
1058  font_size = 0.9*step_y,
1059  max_font_size = 0, // not limited in the beggining
1060  ph = this.pad_height(),
1061  fsize, any_opt = false, i = -1;
1062 
1063  if (legend.fTextSize && (ph*legend.fTextSize > 2) && (ph*legend.fTextSize < font_size))
1064  font_size = max_font_size = Math.round(ph*legend.fTextSize);
1065 
1066  this.StartTextDrawing(legend.fTextFont, font_size, this.draw_g, max_font_size);
1067 
1068  for (var ii = 0; ii < nlines; ++ii) {
1069  var leg = legend.fPrimitives.arr[ii];
1070 
1071  if (isEmpty(leg)) continue; // let discard empty entry
1072 
1073  if (ncols==1) ++i; else i = ii;
1074 
1075  var lopt = leg.fOption.toLowerCase(),
1076  icol = i % ncols, irow = (i - icol) / ncols,
1077  x0 = icol * column_width,
1078  tpos_x = x0 + Math.round(legend.fMargin*column_width),
1079  pos_y = Math.round(padding_y + irow*step_y), // top corner
1080  mid_y = Math.round(padding_y + (irow+0.5)*step_y), // center line
1081  o_fill = leg, o_marker = leg, o_line = leg,
1082  mo = leg.fObject,
1083  painter = null, isany = false;
1084 
1085  if ((mo !== null) && (typeof mo == 'object')) {
1086  if ('fLineColor' in mo) o_line = mo;
1087  if ('fFillColor' in mo) o_fill = mo;
1088  if ('fMarkerColor' in mo) o_marker = mo;
1089 
1090  painter = this.FindPainterFor(mo);
1091  }
1092 
1093  // Draw fill pattern (in a box)
1094  if (lopt.indexOf('f') != -1) {
1095  var fillatt = (painter && painter.fillatt) ? painter.fillatt : this.createAttFill(o_fill);
1096  // box total height is yspace*0.7
1097  // define x,y as the center of the symbol for this entry
1098  this.draw_g.append("svg:rect")
1099  .attr("x", x0 + padding_x)
1100  .attr("y", Math.round(pos_y+step_y*0.1))
1101  .attr("width", tpos_x - 2*padding_x - x0)
1102  .attr("height", Math.round(step_y*0.8))
1103  .call(fillatt.func);
1104  if (!fillatt.empty()) isany = true;
1105  }
1106 
1107  // Draw line
1108  if (lopt.indexOf('l') != -1) {
1109  var lineatt = (painter && painter.lineatt) ? painter.lineatt : new JSROOT.TAttLineHandler(o_line);
1110  this.draw_g.append("svg:line")
1111  .attr("x1", x0 + padding_x)
1112  .attr("y1", mid_y)
1113  .attr("x2", tpos_x - padding_x)
1114  .attr("y2", mid_y)
1115  .call(lineatt.func);
1116  if (lineatt.color !== 'none') isany = true;
1117  }
1118 
1119  // Draw error
1120  if (lopt.indexOf('e') != -1 && (lopt.indexOf('l') == -1 || lopt.indexOf('f') != -1)) {
1121  }
1122 
1123  // Draw Polymarker
1124  if (lopt.indexOf('p') != -1) {
1125  var marker = (painter && painter.markeratt) ? painter.markeratt : new JSROOT.TAttMarkerHandler(o_marker);
1126  this.draw_g
1127  .append("svg:path")
1128  .attr("d", marker.create((x0 + tpos_x)/2, mid_y))
1129  .call(marker.func);
1130  if (marker.color !== 'none') isany = true;
1131  }
1132 
1133  // special case - nothing draw, try to show rect with line attributes
1134  if (!isany && painter && painter.lineatt && (painter.lineatt.color !== 'none'))
1135  this.draw_g.append("svg:rect")
1136  .attr("x", x0 + padding_x)
1137  .attr("y", Math.round(pos_y+step_y*0.1))
1138  .attr("width", tpos_x - 2*padding_x - x0)
1139  .attr("height", Math.round(step_y*0.8))
1140  .attr("fill", "none")
1141  .call(painter.lineatt.func);
1142 
1143  var pos_x = tpos_x;
1144  if (lopt.length>0) any_opt = true;
1145  else if (!any_opt) pos_x = x0 + padding_x;
1146 
1147  if (leg.fLabel)
1148  this.DrawText({ align: "start", x: pos_x, y: pos_y, width: x0+column_width-pos_x-padding_x, height: step_y, text: leg.fLabel, color: tcolor });
1149  }
1150 
1151  // rescale after all entries are shown
1152  this.FinishTextDrawing(this.draw_g, this.FinishPave);
1153  }
1154 
1155  TPavePainter.prototype.DrawPaletteAxis = function(s_width, s_height, arg) {
1156 
1157  var pthis = this,
1158  palette = this.GetObject(),
1159  axis = palette.fAxis,
1160  can_move = (typeof arg == "string") && (arg.indexOf('can_move') > 0),
1161  postpone_draw = (typeof arg == "string") && (arg.indexOf('postpone') > 0),
1162  nbr1 = axis.fNdiv % 100,
1163  pos_x = parseInt(this.draw_g.attr("x")), // pave position
1164  pos_y = parseInt(this.draw_g.attr("y")),
1165  width = this.pad_width(),
1166  height = this.pad_height(),
1167  axisOffset = axis.fLabelOffset * width,
1168  main = this.main_painter(),
1169  framep = this.frame_painter(),
1170  zmin = 0, zmax = 100,
1171  contour = main.fContour;
1172 
1173  if (nbr1<=0) nbr1 = 8;
1174  axis.fTickSize = 0.6 * s_width / width; // adjust axis ticks size
1175 
1176  if (contour && framep) {
1177  zmin = Math.min(contour[0], framep.zmin);
1178  zmax = Math.max(contour[contour.length-1], framep.zmax);
1179  } else if ((main.gmaxbin!==undefined) && (main.gminbin!==undefined)) {
1180  // this is case of TH2 (needs only for size adjustment)
1181  zmin = main.gminbin; zmax = main.gmaxbin;
1182  } else if ((main.hmin!==undefined) && (main.hmax!==undefined)) {
1183  // this is case of TH1
1184  zmin = main.hmin; zmax = main.hmax;
1185  }
1186 
1187  var z = null, z_kind = "normal";
1188 
1189  if (this.root_pad().fLogz) {
1190  z = d3.scaleLog();
1191  z_kind = "log";
1192  } else {
1193  z = d3.scaleLinear();
1194  }
1195  z.domain([zmin, zmax]).range([s_height,0]);
1196 
1197  this.draw_g.selectAll("rect").style("fill", 'white');
1198 
1199  if (!contour || postpone_draw)
1200  // we need such rect to correctly calculate size
1201  this.draw_g.append("svg:rect")
1202  .attr("x", 0)
1203  .attr("y", 0)
1204  .attr("width", s_width)
1205  .attr("height", s_height)
1206  .style("fill", 'white');
1207  else
1208  for (var i=0;i<contour.length-1;++i) {
1209  var z0 = z(contour[i]),
1210  z1 = z(contour[i+1]),
1211  col = main.getContourColor((contour[i]+contour[i+1])/2);
1212 
1213  var r = this.draw_g.append("svg:rect")
1214  .attr("x", 0)
1215  .attr("y", Math.round(z1))
1216  .attr("width", s_width)
1217  .attr("height", Math.round(z0) - Math.round(z1))
1218  .style("fill", col)
1219  .style("stroke", col)
1220  .property("fill0", col)
1221  .property("fill1", d3.rgb(col).darker(0.5).toString())
1222 
1223  if (this.IsTooltipAllowed())
1224  r.on('mouseover', function() {
1225  d3.select(this).transition().duration(100).style("fill", d3.select(this).property('fill1'));
1226  }).on('mouseout', function() {
1227  d3.select(this).transition().duration(100).style("fill", d3.select(this).property('fill0'));
1228  }).append("svg:title").text(contour[i].toFixed(2) + " - " + contour[i+1].toFixed(2));
1229 
1230  if (JSROOT.gStyle.Zooming)
1231  r.on("dblclick", function() { pthis.frame_painter().Unzoom("z"); });
1232  }
1233 
1234 
1235  this.z_handle.SetAxisConfig("zaxis", z_kind, z, zmin, zmax, zmin, zmax);
1236 
1237  this.z_handle.max_tick_size = Math.round(s_width*0.7);
1238 
1239  this.z_handle.DrawAxis(true, this.draw_g, s_width, s_height, "translate(" + s_width + ", 0)");
1240 
1241  if (can_move && ('getBoundingClientRect' in this.draw_g.node())) {
1242  var rect = this.draw_g.node().getBoundingClientRect();
1243 
1244  var shift = (pos_x + parseInt(rect.width)) - Math.round(0.995*width) + 3;
1245 
1246  if (shift > 0) {
1247  this.draw_g.attr("x", pos_x - shift).attr("y", pos_y)
1248  .attr("transform", "translate(" + (pos_x-shift) + ", " + pos_y + ")");
1249  palette.fX1NDC -= shift/width;
1250  palette.fX2NDC -= shift/width;
1251  }
1252  }
1253 
1254  var evnt = null, doing_zoom = false, sel1 = 0, sel2 = 0, zoom_rect = null;
1255 
1256  function moveRectSel() {
1257 
1258  if (!doing_zoom) return;
1259 
1260  d3.event.preventDefault();
1261  var m = d3.mouse(evnt);
1262 
1263  if (m[1] < sel1) sel1 = m[1]; else sel2 = m[1];
1264 
1265  zoom_rect.attr("y", sel1)
1266  .attr("height", Math.abs(sel2-sel1));
1267  }
1268 
1269  function endRectSel() {
1270  if (!doing_zoom) return;
1271 
1272  d3.event.preventDefault();
1273  d3.select(window).on("mousemove.colzoomRect", null)
1274  .on("mouseup.colzoomRect", null);
1275  zoom_rect.remove();
1276  zoom_rect = null;
1277  doing_zoom = false;
1278 
1279  var zmin = Math.min(z.invert(sel1), z.invert(sel2)),
1280  zmax = Math.max(z.invert(sel1), z.invert(sel2));
1281 
1282  pthis.frame_painter().Zoom("z", zmin, zmax);
1283  }
1284 
1285  function startRectSel() {
1286  // ignore when touch selection is activated
1287  if (doing_zoom) return;
1288  doing_zoom = true;
1289 
1290  d3.event.preventDefault();
1291 
1292  evnt = this;
1293  var origin = d3.mouse(evnt);
1294 
1295  sel1 = sel2 = origin[1];
1296 
1297  zoom_rect = pthis.draw_g
1298  .append("svg:rect")
1299  .attr("class", "zoom")
1300  .attr("id", "colzoomRect")
1301  .attr("x", "0")
1302  .attr("width", s_width)
1303  .attr("y", sel1)
1304  .attr("height", 5);
1305 
1306  d3.select(window).on("mousemove.colzoomRect", moveRectSel)
1307  .on("mouseup.colzoomRect", endRectSel, true);
1308 
1309  d3.event.stopPropagation();
1310  }
1311 
1312  if (JSROOT.gStyle.Zooming)
1313  this.draw_g.select(".axis_zoom")
1314  .on("mousedown", startRectSel)
1315  .on("dblclick", function() { pthis.frame_painter().Unzoom("z"); });
1316 
1317  if (this.FinishPave) this.FinishPave();
1318  }
1319 
1320  TPavePainter.prototype.FillContextMenu = function(menu) {
1321  var pave = this.GetObject();
1322 
1323  menu.add("header: " + pave._typename + "::" + pave.fName);
1324  if (this.IsStats()) {
1325  menu.add("Default position", function() {
1326  pave.fX2NDC = JSROOT.gStyle.fStatX;
1327  pave.fX1NDC = pave.fX2NDC - JSROOT.gStyle.fStatW;
1328  pave.fY2NDC = JSROOT.gStyle.fStatY;
1329  pave.fY1NDC = pave.fY2NDC - JSROOT.gStyle.fStatH;
1330  pave.fInit = 1;
1331  this.InteractiveRedraw(true, "pave_moved")
1332  });
1333 
1334  menu.add("SetStatFormat", function() {
1335  var fmt = prompt("Enter StatFormat", pave.fStatFormat);
1336  if (fmt) {
1337  pave.fStatFormat = fmt;
1338  this.InteractiveRedraw(true, 'exec:SetStatFormat("'+fmt+'")');
1339  }
1340  });
1341  menu.add("SetFitFormat", function() {
1342  var fmt = prompt("Enter FitFormat", pave.fFitFormat);
1343  if (fmt) {
1344  pave.fFitFormat = fmt;
1345  this.InteractiveRedraw(true, 'exec:SetFitFormat("'+fmt+'")');
1346  }
1347  });
1348  menu.add("separator");
1349  menu.add("sub:SetOptStat", function() {
1350  // todo - use jqury dialog here
1351  var fmt = prompt("Enter OptStat", pave.fOptStat);
1352  if (fmt) {
1353  fmt = parseInt(fmt);
1354  if (!isNaN(fmt) && (fmt>=0)) {
1355  pave.fOptStat = parseInt(fmt);
1356  this.InteractiveRedraw(true, "exec:SetOptStat("+fmt+")");
1357  }
1358  }
1359  });
1360  function AddStatOpt(pos, name) {
1361  var opt = (pos<10) ? pave.fOptStat : pave.fOptFit;
1362  opt = parseInt(parseInt(opt) / parseInt(Math.pow(10,pos % 10))) % 10;
1363  menu.addchk(opt, name, opt * 100 + pos, function(arg) {
1364  var newopt = (arg % 100 < 10) ? pave.fOptStat : pave.fOptFit;
1365  var oldopt = parseInt(arg / 100);
1366  newopt -= (oldopt>0 ? oldopt : -1) * parseInt(Math.pow(10, arg % 10));
1367  if (arg % 100 < 10) {
1368  pave.fOptStat = newopt;
1369  this.InteractiveRedraw(true, "exec:SetOptStat("+newopt+")");
1370  } else {
1371  pave.fOptFit = newopt;
1372  this.InteractiveRedraw(true, "exec:SetOptFit("+newopt+")");
1373  }
1374  });
1375  }
1376 
1377  AddStatOpt(0, "Histogram name");
1378  AddStatOpt(1, "Entries");
1379  AddStatOpt(2, "Mean");
1380  AddStatOpt(3, "Std Dev");
1381  AddStatOpt(4, "Underflow");
1382  AddStatOpt(5, "Overflow");
1383  AddStatOpt(6, "Integral");
1384  AddStatOpt(7, "Skewness");
1385  AddStatOpt(8, "Kurtosis");
1386  menu.add("endsub:");
1387 
1388  menu.add("sub:SetOptFit", function() {
1389  // todo - use jqury dialog here
1390  var fmt = prompt("Enter OptStat", pave.fOptFit);
1391  if (fmt) {
1392  fmt = parseInt(fmt);
1393  if (!isNaN(fmt) && (fmt>=0)) {
1394  pave.fOptFit = fmt;
1395  this.InteractiveRedraw(true, "exec:SetOptFit("+fmt+")");
1396  }
1397  }
1398  });
1399  AddStatOpt(10, "Fit parameters");
1400  AddStatOpt(11, "Par errors");
1401  AddStatOpt(12, "Chi square / NDF");
1402  AddStatOpt(13, "Probability");
1403  menu.add("endsub:");
1404 
1405  menu.add("separator");
1406  } else if (pave.fName === "title")
1407  menu.add("Default position", function() {
1408  pave.fX1NDC = 0.28;
1409  pave.fY1NDC = 0.94;
1410  pave.fX2NDC = 0.72;
1411  pave.fY2NDC = 0.99;
1412  pave.fInit = 1;
1413  this.InteractiveRedraw(true, "pave_moved");
1414  });
1415 
1416  if (this.UseTextColor)
1417  this.TextAttContextMenu(menu);
1418 
1419  this.FillAttContextMenu(menu);
1420 
1421  if (menu.size() > 0)
1422  menu.add('Inspect', this.ShowInspector);
1423 
1424  return menu.size() > 0;
1425  }
1426 
1427  TPavePainter.prototype.ShowContextMenu = function(evnt) {
1428 
1429  // for color palette
1430  if (this.z_handle)
1431  return this.frame_painter().ShowContextMenu("z", evnt, this.GetObject().fAxis);
1432 
1433  if (!evnt) {
1434  d3.event.stopPropagation(); // disable main context menu
1435  d3.event.preventDefault(); // disable browser context menu
1436 
1437  // one need to copy event, while after call back event may be changed
1438  evnt = d3.event;
1439  }
1440 
1441  this.ctx_menu_evnt = evnt;
1442 
1443  JSROOT.Painter.createMenu(this, function(menu) {
1444  menu.painter.FillContextMenu(menu);
1445 
1446  menu.painter.FillObjectExecMenu(menu, "title", function() {
1447  menu.show(menu.painter.ctx_menu_evnt);
1448  });
1449  }); // end menu creation
1450  }
1451 
1452  TPavePainter.prototype.IsStats = function() {
1453  return this.MatchObjectType('TPaveStats');
1454  }
1455 
1456  TPavePainter.prototype.ClearPave = function() {
1457  this.GetObject().Clear();
1458  }
1459 
1460  TPavePainter.prototype.AddText = function(txt) {
1461  this.GetObject().AddText(txt);
1462  }
1463 
1464  TPavePainter.prototype.FillFunctionStat = function(f1, dofit) {
1465  if (!dofit || !f1) return false;
1466 
1467  var print_fval = dofit % 10,
1468  print_ferrors = Math.floor(dofit/10) % 10,
1469  print_fchi2 = Math.floor(dofit/100) % 10,
1470  print_fprob = Math.floor(dofit/1000) % 10;
1471 
1472  if (print_fchi2 > 0)
1473  this.AddText("#chi^2 / ndf = " + this.Format(f1.fChisquare,"fit") + " / " + f1.fNDF);
1474  if (print_fprob > 0)
1475  this.AddText("Prob = " + (('Math' in JSROOT) ? this.Format(JSROOT.Math.Prob(f1.fChisquare, f1.fNDF)) : "<not avail>"));
1476  if (print_fval > 0)
1477  for(var n=0;n<f1.GetNumPars();++n) {
1478  var parname = f1.GetParName(n), parvalue = f1.GetParValue(n), parerr = f1.GetParError(n);
1479 
1480  parvalue = (parvalue===undefined) ? "<not avail>" : this.Format(Number(parvalue),"fit");
1481  if (parerr !== undefined) {
1482  parerr = this.Format(parerr,"last");
1483  if ((Number(parerr)===0) && (f1.GetParError(n) != 0)) parerr = this.Format(f1.GetParError(n),"4.2g");
1484  }
1485 
1486  if ((print_ferrors > 0) && parerr)
1487  this.AddText(parname + " = " + parvalue + " #pm " + parerr);
1488  else
1489  this.AddText(parname + " = " + parvalue);
1490  }
1491 
1492  return true;
1493  }
1494 
1495  TPavePainter.prototype.FillStatistic = function() {
1496 
1497  var pp = this.pad_painter();
1498  if (pp && pp._fast_drawing) return false;
1499 
1500  var pave = this.GetObject(),
1501  main = pave.$main_painter || this.main_painter();
1502 
1503  if (pave.fName !== "stats") return false;
1504  if (!main || (typeof main.FillStatistic !== 'function')) return false;
1505 
1506  var dostat = parseInt(pave.fOptStat), dofit = parseInt(pave.fOptFit);
1507  if (isNaN(dostat)) dostat = JSROOT.gStyle.fOptStat;
1508  if (isNaN(dofit)) dofit = JSROOT.gStyle.fOptFit;
1509 
1510  // we take statistic from main painter
1511  if (!main.FillStatistic(this, dostat, dofit)) return false;
1512 
1513  // adjust the size of the stats box with the number of lines
1514  var nlines = pave.fLines.arr.length,
1515  stath = nlines * JSROOT.gStyle.StatFontSize;
1516  if ((stath <= 0) || (JSROOT.gStyle.StatFont % 10 === 3)) {
1517  stath = 0.25 * nlines * JSROOT.gStyle.StatH;
1518  pave.fY1NDC = pave.fY2NDC - stath;
1519  }
1520 
1521  return true;
1522  }
1523 
1524  TPavePainter.prototype.IsDummyPos = function(p) {
1525  if (!p) return true;
1526 
1527  return !p.fInit && !p.fX1 && !p.fX2 && !p.fY1 && !p.fY2 && !p.fX1NDC && !p.fX2NDC && !p.fY1NDC && !p.fY2NDC;
1528  }
1529 
1530  TPavePainter.prototype.UpdateObject = function(obj) {
1531  if (!this.MatchObjectType(obj)) return false;
1532 
1533  var pave = this.GetObject();
1534 
1535  if (!pave.modified_NDC && !this.IsDummyPos(obj)) {
1536  // if position was not modified interactively, update from source object
1537 
1538  if (this.stored && !obj.fInit && (this.stored.fX1 == obj.fX1)
1539  && (this.stored.fX2 == obj.fX2) && (this.stored.fY1 == obj.fY1) && (this.stored.fY2 == obj.fY2)) {
1540  // case when source object not initialized and original coordinates are not changed
1541  // take over only modified NDC coordinate, used in tutorials/graphics/canvas.C
1542  if (this.stored.fX1NDC != obj.fX1NDC) pave.fX1NDC = obj.fX1NDC;
1543  if (this.stored.fX2NDC != obj.fX2NDC) pave.fX2NDC = obj.fX2NDC;
1544  if (this.stored.fY1NDC != obj.fY1NDC) pave.fY1NDC = obj.fY1NDC;
1545  if (this.stored.fY2NDC != obj.fY2NDC) pave.fY2NDC = obj.fY2NDC;
1546  } else {
1547  pave.fInit = obj.fInit;
1548  pave.fX1 = obj.fX1; pave.fX2 = obj.fX2;
1549  pave.fY1 = obj.fY1; pave.fY2 = obj.fY2;
1550  pave.fX1NDC = obj.fX1NDC; pave.fX2NDC = obj.fX2NDC;
1551  pave.fY1NDC = obj.fY1NDC; pave.fY2NDC = obj.fY2NDC;
1552  }
1553 
1554  this.stored = JSROOT.extend({}, obj); // store latest coordinates
1555  }
1556 
1557  pave.fOption = obj.fOption;
1558  pave.fBorderSize = obj.fBorderSize;
1559 
1560  switch (obj._typename) {
1561  case 'TPaveText':
1562  pave.fLines = JSROOT.clone(obj.fLines);
1563  return true;
1564  case 'TPavesText':
1565  pave.fLines = JSROOT.clone(obj.fLines);
1566  pave.fNpaves = obj.fNpaves;
1567  return true;
1568  case 'TPaveLabel':
1569  pave.fLabel = obj.fLabel;
1570  return true;
1571  case 'TPaveStats':
1572  pave.fOptStat = obj.fOptStat;
1573  pave.fOptFit = obj.fOptFit;
1574  return true;
1575  case 'TLegend':
1576  var oldprim = pave.fPrimitives;
1577  pave.fPrimitives = obj.fPrimitives;
1578  pave.fNColumns = obj.fNColumns;
1579  if (oldprim && oldprim.arr && pave.fPrimitives && pave.fPrimitives.arr && (oldprim.arr.length == pave.fPrimitives.arr.length)) {
1580  // try to sync object reference, new object does not displayed automatically
1581  // in ideal case one should use snapids in the entries
1582  for (var k=0;k<oldprim.arr.length;++k) {
1583  var oldobj = oldprim.arr[k].fObject, newobj = pave.fPrimitives.arr[k].fObject;
1584 
1585  if (oldobj && newobj && oldobj._typename == newobj._typename && oldobj.fName == newobj.fName)
1586  pave.fPrimitives.arr[k].fObject = oldobj;
1587  }
1588  }
1589  return true;
1590  case 'TPaletteAxis':
1591  pave.fBorderSize = 1;
1592  pave.fShadowColor = 0;
1593  return true;
1594  }
1595 
1596  return false;
1597  }
1598 
1599  TPavePainter.prototype.Redraw = function() {
1600  this.DrawPave();
1601  }
1602 
1603  TPavePainter.prototype.Cleanup = function() {
1604  if (this.z_handle) {
1605  this.z_handle.Cleanup();
1606  delete this.z_handle;
1607  }
1608 
1609  JSROOT.TObjectPainter.prototype.Cleanup.call(this);
1610  }
1611 
1612  function drawPave(divid, pave, opt) {
1613  // one could force drawing of PaveText on specific sub-pad
1614  var onpad;
1615  if ((typeof opt == 'string') && (opt.indexOf("onpad:")==0)) {
1616  onpad = opt.substr(6);
1617  opt = "";
1618  }
1619 
1620  var painter = new JSROOT.TPavePainter(pave);
1621 
1622  painter.SetDivId(divid, 2, onpad);
1623 
1624  if ((pave.fName === "title") && (pave._typename === "TPaveText")) {
1625  var tpainter = painter.FindPainterFor(null, "title");
1626  if (tpainter && (tpainter !== painter)) {
1627  tpainter.DeleteThis();
1628  } else if ((opt == "postitle") || painter.IsDummyPos(pave)) {
1629  var st = JSROOT.gStyle, fp = painter.frame_painter();
1630  if (st && fp) {
1631  var midx = st.fTitleX, y2 = st.fTitleY, w = st.fTitleW, h = st.fTitleH;
1632  if (!h && fp) h = (y2-fp.fY2NDC)*0.7;
1633  if (!w && fp) w = fp.fX2NDC - fp.fX1NDC;
1634  if (!h || isNaN(h) || (h<0)) h = 0.06;
1635  if (!w || isNaN(w) || (w<0)) w = 0.44;
1636  pave.fX1NDC = midx - w/2;
1637  pave.fY1NDC = y2 - h;
1638  pave.fX2NDC = midx + w/2;
1639  pave.fY2NDC = y2;
1640  pave.fInit = 1;
1641  }
1642  }
1643  } else if (pave._typename === "TPaletteAxis") {
1644  pave.fBorderSize = 1;
1645  pave.fShadowColor = 0;
1646 
1647  // check some default values of TGaxis object, otherwise axis will not be drawn
1648  if (pave.fAxis) {
1649  if (!pave.fAxis.fChopt) pave.fAxis.fChopt = "+";
1650  if (!pave.fAxis.fNdiv) pave.fAxis.fNdiv = 12;
1651  if (!pave.fAxis.fLabelOffset) pave.fAxis.fLabelOffset = 0.005;
1652  }
1653 
1654  painter.z_handle = new JSROOT.TAxisPainter(pave.fAxis, true);
1655  painter.z_handle.SetDivId(divid, -1);
1656 
1657  painter.UseContextMenu = true;
1658  }
1659 
1660  switch (pave._typename) {
1661  case "TPaveLabel":
1662  painter.PaveDrawFunc = painter.DrawPaveLabel;
1663  break;
1664  case "TPaveStats":
1665  painter.PaveDrawFunc = painter.DrawPaveStats;
1666  painter.$secondary = true; // indicates that painter created from others
1667  break;
1668  case "TPaveText":
1669  case "TPavesText":
1670  case "TDiamond":
1671  painter.PaveDrawFunc = painter.DrawPaveText;
1672  break;
1673  case "TLegend":
1674  painter.PaveDrawFunc = painter.DrawPaveLegend;
1675  break;
1676  case "TPaletteAxis":
1677  painter.PaveDrawFunc = painter.DrawPaletteAxis;
1678  break;
1679  }
1680 
1681  painter.DrawPave(opt);
1682 
1683  // drawing ready handled in special painters, if not exists - drawing is done
1684  return painter.PaveDrawFunc ? painter : painter.DrawingReady();
1685  }
1686 
1691  function produceLegend(divid, opt) {
1692  var main_painter = JSROOT.GetMainPainter(divid);
1693  if (!main_painter) return;
1694 
1695  var pp = main_painter.pad_painter(),
1696  pad = main_painter.root_pad();
1697  if (!pp || !pad) return;
1698 
1699  var leg = JSROOT.Create("TLegend");
1700 
1701  for (var k=0;k<pp.painters.length;++k) {
1702  var painter = pp.painters[k],
1703  obj = painter.GetObject();
1704 
1705  if (!obj) continue;
1706 
1707  var entry = JSROOT.Create("TLegendEntry");
1708  entry.fObject = obj;
1709  entry.fLabel = (opt == "all") ? obj.fName : painter.GetItemName();
1710  entry.fOption = "";
1711  if (!entry.fLabel) continue;
1712 
1713  if (painter.lineatt && painter.lineatt.used) entry.fOption+="l";
1714  if (painter.fillatt && painter.fillatt.used) entry.fOption+="f";
1715  if (painter.markeratt && painter.markeratt.used) entry.fOption+="m";
1716  if (!entry.fOption) entry.fOption = "l";
1717 
1718  leg.fPrimitives.Add(entry);
1719  }
1720 
1721  // no entries - no need to draw legend
1722  var szx = 0.4, szy = leg.fPrimitives.arr.length;
1723  if (!szy) return;
1724  if (szy>8) szy = 8;
1725  szy *= 0.1;
1726 
1727  JSROOT.extend(leg, {
1728  fX1NDC: szx*pad.fLeftMargin + (1-szx)*(1-pad.fRightMargin),
1729  fY1NDC: (1-szy)*(1-pad.fTopMargin) + szy*pad.fBottomMargin,
1730  fX2NDC: 0.99-pad.fRightMargin,
1731  fY2NDC: 0.99-pad.fTopMargin
1732  });
1733  leg.fFillStyle = 1001;
1734 
1735  return drawPave(divid, leg);
1736  }
1737 
1738  // ==============================================================================
1739 
1740  function THistDrawOptions(opt) {
1741  this.Reset();
1742  }
1743 
1744  THistDrawOptions.prototype.Reset = function() {
1745  JSROOT.extend(this,
1746  { Axis: 0, RevX: false, RevY: false, Bar: false, BarStyle: 0, Curve: false,
1747  Hist: true, Line: false, Fill: false,
1748  Error: false, ErrorKind: -1, errorX: JSROOT.gStyle.fErrorX,
1749  Mark: false, Same: false, Scat: false, ScatCoef: 1., Func: true,
1750  Arrow: false, Box: false, BoxStyle: 0,
1751  Text: false, TextAngle: 0, TextKind: "", Char: 0, Color: false, Contour: 0,
1752  Lego: 0, Surf: 0, Off: 0, Tri: 0, Proj: 0, AxisPos: 0,
1753  Spec: false, Pie: false, List: false, Zscale: false, PadPalette: false, Candle: "",
1754  GLBox: 0, GLColor: false, Project: "",
1755  System: JSROOT.Painter.Coord.kCARTESIAN,
1756  AutoColor: false, NoStat: false, ForceStat: false, PadStats: false, PadTitle: false, AutoZoom: false,
1757  HighRes: 0, Zero: true, Palette: 0, BaseLine: false,
1758  Optimize: JSROOT.gStyle.OptimizeDraw, Mode3D: false,
1759  FrontBox: true, BackBox: true,
1760  _pmc: false, _plc: false, _pfc: false, need_fillcol: false,
1761  minimum: -1111, maximum: -1111, ymin:0, ymax:0 });
1762  }
1763 
1764  THistDrawOptions.prototype.Decode = function(opt, hdim, histo, pad, painter) {
1765  this.orginal = opt; // will be overwritten by OptionsStore call
1766 
1767  var d = new JSROOT.DrawOptions(opt), check3dbox = "";
1768 
1769  if ((hdim===1) && (histo.fSumw2.length > 0))
1770  for (var n=0;n<histo.fSumw2.length;++n)
1771  if (histo.fSumw2[n] > 0) { this.Error = true; this.Hist = false; this.Zero = false; break; }
1772 
1773  this.ndim = hdim || 1; // keep dimensions, used for now in GED
1774 
1775  this.PadStats = d.check("USE_PAD_STATS");
1776  this.PadPalette = d.check("USE_PAD_PALETTE");
1777  this.PadTitle = d.check("USE_PAD_TITLE");
1778 
1779  if (d.check('PAL', true)) this.Palette = d.partAsInt();
1780  // this is zooming of histo content
1781  if (d.check('MINIMUM:', true)) this.minimum = parseFloat(d.part); else this.minimum = histo.fMinimum;
1782  if (d.check('MAXIMUM:', true)) this.maximum = parseFloat(d.part); else this.maximum = histo.fMaximum;
1783  // this is actual range of data - used by graph drawing
1784  if (d.check('YMIN:', true)) this.ymin = parseFloat(d.part);
1785  if (d.check('YMAX:', true)) this.ymax = parseFloat(d.part);
1786 
1787 
1788  if (d.check('NOOPTIMIZE')) this.Optimize = 0;
1789  if (d.check('OPTIMIZE')) this.Optimize = 2;
1790 
1791  if (d.check('AUTOCOL')) this.AutoColor = true;
1792  if (d.check('AUTOZOOM')) this.AutoZoom = true;
1793 
1794  if (d.check('OPTSTAT',true)) this.optstat = d.partAsInt();
1795  if (d.check('OPTFIT',true)) this.optfit = d.partAsInt();
1796 
1797  if (d.check('NOSTAT')) this.NoStat = true;
1798  if (d.check('STAT')) this.ForceStat = true;
1799 
1800  if (d.check('NOTOOLTIP') && painter) painter.SetTooltipAllowed(false);
1801  if (d.check('TOOLTIP') && painter) painter.SetTooltipAllowed(true);
1802 
1803  if (d.check('LOGX') && pad) { pad.fLogx = 1; pad.fUxmin = 0; pad.fUxmax = 1; pad.fX1 = 0; pad.fX2 = 1; }
1804  if (d.check('LOGY') && pad) { pad.fLogy = 1; pad.fUymin = 0; pad.fUymax = 1; pad.fY1 = 0; pad.fY2 = 1; }
1805  if (d.check('LOGZ') && pad) pad.fLogz = 1;
1806  if (d.check('GRIDXY') && pad) pad.fGridx = pad.fGridy = 1;
1807  if (d.check('GRIDX') && pad) pad.fGridx = 1;
1808  if (d.check('GRIDY') && pad) pad.fGridy = 1;
1809  if (d.check('TICKXY') && pad) pad.fTickx = pad.fTicky = 1;
1810  if (d.check('TICKX') && pad) pad.fTickx = 1;
1811  if (d.check('TICKY') && pad) pad.fTicky = 1;
1812 
1813  if (d.check('FILL_', true)) {
1814  if (d.partAsInt(1)>0) this.histoFillColor = d.partAsInt(); else
1815  for (var col=0;col<8;++col)
1816  if (JSROOT.Painter.root_colors[col].toUpperCase() === d.part) this.histoFillColor = col;
1817  }
1818  if (d.check('LINE_', true)) {
1819  if (d.partAsInt(1)>0) this.histoLineColor = JSROOT.Painter.root_colors[d.partAsInt()]; else
1820  for (var col=0;col<8;++col)
1821  if (JSROOT.Painter.root_colors[col].toUpperCase() === d.part) this.histoLineColor = d.part;
1822  }
1823 
1824  if (d.check('X+')) this.AxisPos = 10;
1825  if (d.check('Y+')) this.AxisPos += 1;
1826 
1827  if (d.check('SAMES')) { this.Same = true; this.ForceStat = true; }
1828  if (d.check('SAME')) { this.Same = true; this.Func = true; }
1829 
1830  if (d.check('SPEC')) this.Spec = true; // not used
1831 
1832  if (d.check('BASE0') || d.check('MIN0')) this.BaseLine = 0; else
1833  if (JSROOT.gStyle.fHistMinimumZero) this.BaseLine = 0;
1834 
1835  if (d.check('PIE')) this.Pie = true; // not used
1836 
1837  if (d.check('CANDLE', true)) this.Candle = d.part;
1838 
1839  if (d.check('GLBOX',true)) this.GLBox = 10 + d.partAsInt();
1840  if (d.check('GLCOL')) this.GLColor = true;
1841 
1842  d.check('GL'); // suppress GL
1843 
1844  if (d.check('LEGO', true)) {
1845  this.Lego = 1;
1846  if (d.part.indexOf('0') >= 0) this.Zero = false;
1847  if (d.part.indexOf('1') >= 0) this.Lego = 11;
1848  if (d.part.indexOf('2') >= 0) this.Lego = 12;
1849  if (d.part.indexOf('3') >= 0) this.Lego = 13;
1850  if (d.part.indexOf('4') >= 0) this.Lego = 14;
1851  check3dbox = d.part;
1852  if (d.part.indexOf('Z') >= 0) this.Zscale = true;
1853  }
1854 
1855  if (d.check('SURF', true)) {
1856  this.Surf = d.partAsInt(10, 1);
1857  check3dbox = d.part;
1858  if (d.part.indexOf('Z')>=0) this.Zscale = true;
1859  }
1860 
1861  if (d.check('TF3', true)) check3dbox = d.part;
1862 
1863  if (d.check('ISO', true)) check3dbox = d.part;
1864 
1865  if (d.check('LIST')) this.List = true; // not used
1866 
1867  if (d.check('CONT', true) && (hdim>1)) {
1868  this.Contour = 1;
1869  if (d.part.indexOf('Z') >= 0) this.Zscale = true;
1870  if (d.part.indexOf('1') >= 0) this.Contour = 11; else
1871  if (d.part.indexOf('2') >= 0) this.Contour = 12; else
1872  if (d.part.indexOf('3') >= 0) this.Contour = 13; else
1873  if (d.part.indexOf('4') >= 0) this.Contour = 14;
1874  }
1875 
1876  // decode bar/hbar option
1877  if (d.check('HBAR', true)) this.BarStyle = 20; else
1878  if (d.check('BAR', true)) this.BarStyle = 10;
1879  if (this.BarStyle > 0) {
1880  this.Hist = false;
1881  this.need_fillcol = true;
1882  this.BarStyle += d.partAsInt();
1883  }
1884 
1885  if (d.check('ARR'))
1886  this.Arrow = true;
1887 
1888  if (d.check('BOX',true))
1889  this.BoxStyle = 10 + d.partAsInt();
1890 
1891  this.Box = this.BoxStyle > 0;
1892 
1893  if (d.check('COL')) this.Color = true;
1894  if (d.check('CHAR')) this.Char = 1;
1895  if (d.check('FUNC')) { this.Func = true; this.Hist = false; }
1896  if (d.check('AXIS')) this.Axis = 1;
1897  if (d.check('AXIG')) this.Axis = 2;
1898 
1899  if (d.check('TEXT', true)) {
1900  this.Text = true;
1901  this.Hist = false;
1902  this.TextAngle = Math.min(d.partAsInt(), 90);
1903  if (d.part.indexOf('N')>=0) this.TextKind = "N";
1904  if (d.part.indexOf('E0')>=0) this.TextLine = true;
1905  if (d.part.indexOf('E')>=0) this.TextKind = "E";
1906  }
1907 
1908  if (d.check('SCAT=', true)) {
1909  this.Scat = true;
1910  this.ScatCoef = parseFloat(d.part);
1911  if (isNaN(this.ScatCoef) || (this.ScatCoef<=0)) this.ScatCoef = 1.;
1912  }
1913 
1914  if (d.check('SCAT')) this.Scat = true;
1915  if (d.check('POL')) this.System = JSROOT.Painter.Coord.kPOLAR;
1916  if (d.check('CYL')) this.System = JSROOT.Painter.Coord.kCYLINDRICAL;
1917  if (d.check('SPH')) this.System = JSROOT.Painter.Coord.kSPHERICAL;
1918  if (d.check('PSR')) this.System = JSROOT.Painter.Coord.kRAPIDITY;
1919 
1920  if (d.check('TRI', true)) {
1921  this.Color = false;
1922  this.Tri = 1;
1923  check3dbox = d.part;
1924  if (d.part.indexOf('ERR') >= 0) this.Error = true;
1925  }
1926 
1927  if (d.check('AITOFF')) this.Proj = 1;
1928  if (d.check('MERCATOR')) this.Proj = 2;
1929  if (d.check('SINUSOIDAL')) this.Proj = 3;
1930  if (d.check('PARABOLIC')) this.Proj = 4;
1931  if (this.Proj > 0) this.Contour = 14;
1932 
1933  if (d.check('PROJX',true)) this.Project = "X" + d.partAsInt(0,1);
1934  if (d.check('PROJY',true)) this.Project = "Y" + d.partAsInt(0,1);
1935 
1936  if (check3dbox) {
1937  if (check3dbox.indexOf('FB') >= 0) this.FrontBox = false;
1938  if (check3dbox.indexOf('BB') >= 0) this.BackBox = false;
1939  }
1940 
1941  if ((hdim==3) && d.check('FB')) this.FrontBox = false;
1942  if ((hdim==3) && d.check('BB')) this.BackBox = false;
1943 
1944  this._pfc = d.check("PFC");
1945  this._plc = d.check("PLC") || this.AutoColor;
1946  this._pmc = d.check("PMC");
1947 
1948  if (d.check('L')) { this.Line = true; this.Hist = false; this.Error = false; }
1949  if (d.check('F')) { this.Fill = true; this.need_fillcol = true; }
1950 
1951  if (d.check('A')) this.Axis = -1;
1952  if (this.Axis && d.check("RX")) this.RevX = true;
1953  if (this.Axis && d.check("RY")) this.RevY = true;
1954 
1955  if (d.check('B1')) { this.BarStyle = 1; this.BaseLine = 0; this.Hist = false; this.need_fillcol = true; }
1956  if (d.check('B')) { this.BarStyle = 1; this.Hist = false; this.need_fillcol = true; }
1957  if (d.check('C')) { this.Curve = true; this.Hist = false; }
1958  if (d.check('][')) { this.Off = 1; this.Hist = true; }
1959 
1960  if (d.check('HIST')) { this.Hist = true; this.Func = true; this.Error = false; }
1961 
1962  this.Bar = (this.BarStyle > 0);
1963 
1964  delete this.MarkStyle; // remove mark style if any
1965 
1966  if (d.check('P0')) { this.Mark = true; this.Hist = false; this.Zero = true; }
1967  if (d.check('P')) { this.Mark = true; this.Hist = false; this.Zero = false; }
1968  if (d.check('Z')) this.Zscale = true;
1969  if (d.check('*')) { this.Mark = true; this.MarkStyle = 3; this.Hist = false; }
1970  if (d.check('H')) this.Hist = true;
1971 
1972  if (d.check('E', true)) {
1973  this.Error = true;
1974  if (hdim == 1) {
1975  this.Zero = false; // do not draw empty bins with erros
1976  this.Hist = false;
1977  if (!isNaN(parseInt(d.part[0]))) this.ErrorKind = parseInt(d.part[0]);
1978  if ((this.ErrorKind === 3) || (this.ErrorKind === 4)) this.need_fillcol = true;
1979  if (this.ErrorKind === 0) this.Zero = true; // enable drawing of empty bins
1980  if (d.part.indexOf('X0')>=0) this.errorX = 0;
1981  }
1982  }
1983  if (d.check('9')) this.HighRes = 1;
1984  if (d.check('0')) this.Zero = false;
1985  if (this.Color && d.check('1')) this.Zero = false;
1986 
1987  // flag identifies 3D drawing mode for histogram
1988  if ((this.Lego > 0) || (hdim == 3) ||
1989  ((this.Surf > 0) || this.Error && (hdim == 2))) this.Mode3D = true;
1990 
1991  //if (this.Surf == 15)
1992  // if (this.System == JSROOT.Painter.Coord.kPOLAR || this.System == JSROOT.Painter.Coord.kCARTESIAN)
1993  // this.Surf = 13;
1994  }
1995 
1996  // function should approx reconstruct draw options
1997  THistDrawOptions.prototype.asString = function(painter) {
1998  var fp = painter ? painter.frame_painter() : null;
1999 
2000  var res = "";
2001  if (this.Mode3D) {
2002 
2003  if (this.Lego) {
2004  res = "LEGO";
2005  if (!this.Zero) res += "0";
2006  if (this.Lego > 10) res += (this.Lego-10);
2007  if (this.Zscale) res+="Z";
2008  } else if (this.Surf) {
2009  res = "SURF" + (this.Surf-10);
2010  if (this.Zscale) res+="Z";
2011  }
2012  if (!this.FrontBox) res+="FB";
2013  if (!this.BackBox) res+="BB";
2014 
2015  } else {
2016  if (this.Scat) {
2017  res = "SCAT";
2018  } else if (this.Color) {
2019  res = "COL";
2020  if (!this.Zero) res+="0";
2021  if (this.Zscale) res+="Z";
2022  if (this.Axis < 0) res+="A";
2023  } else if (this.Contour) {
2024  res = "CONT";
2025  if (this.Contour > 10) res += (this.Contour-10);
2026  if (this.Zscale) res+="Z";
2027  } else if (this.Bar) {
2028  res = (this.BaseLine === false) ? "B" : "B1";
2029  } else if (this.Mark) {
2030  res = this.Zero ? "P0" : "P"; // here invert logic with 0
2031  } else if (this.Scat) {
2032  res = "SCAT";
2033  } else if (this.Error) {
2034  res = "E";
2035  if (this.ErrorKind>=0) res += this.ErrorKind;
2036  } else if (this.Line) {
2037  res += "L";
2038  if (this.Fill) res += "F";
2039  }
2040 
2041  if (this.Text) {
2042  res += "TEXT";
2043  if (this.TextAngle) res += this.TextAngle;
2044  res += this.TextKind;
2045  }
2046 
2047  }
2048  return res;
2049  }
2050 
2051  // ==============================================================================
2052 
2053 
2063  function THistPainter(histo) {
2064  JSROOT.TObjectPainter.call(this, histo);
2065  this.draw_content = true;
2066  this.nbinsx = 0;
2067  this.nbinsy = 0;
2068  this.accept_drops = true; // indicate that one can drop other objects like doing Draw("same")
2069  this.mode3d = false;
2070  this.hist_painter_id = JSROOT.id_counter++; // assign unique identifier for hist painter
2071  }
2072 
2073  THistPainter.prototype = Object.create(JSROOT.TObjectPainter.prototype);
2074 
2075  THistPainter.prototype.GetHisto = function() {
2076  return this.GetObject();
2077  }
2078 
2079  THistPainter.prototype.GetAxis = function(name) {
2080  var histo = this.GetObject();
2081  if (histo)
2082  switch(name) {
2083  case "x": return histo.fXaxis;
2084  case "y": return histo.fYaxis;
2085  case "z": return histo.fZaxis;
2086  }
2087  return null;
2088  }
2089 
2090  THistPainter.prototype.IsTProfile = function() {
2091  return this.MatchObjectType('TProfile');
2092  }
2093 
2094  THistPainter.prototype.IsTH1K = function() {
2095  return this.MatchObjectType('TH1K');
2096  }
2097 
2098  THistPainter.prototype.IsTH2Poly = function() {
2099  return this.MatchObjectType(/^TH2Poly/) || this.MatchObjectType(/^TProfile2Poly/);
2100  }
2101 
2102  THistPainter.prototype.Clear3DScene = function() {
2103  var fp = this.frame_painter();
2104  if (fp && typeof fp.Create3DScene === 'function')
2105  fp.Create3DScene(-1);
2106  this.mode3d = false;
2107  }
2108 
2110  THistPainter.prototype.Cleanup = function() {
2111 
2112  // clear all 3D buffers
2113  this.Clear3DScene();
2114 
2115  delete this.fPalette;
2116  delete this.fContour;
2117  delete this.options;
2118 
2119  JSROOT.TObjectPainter.prototype.Cleanup.call(this);
2120  }
2121 
2122  THistPainter.prototype.Dimension = function() {
2123  var histo = this.GetHisto();
2124  if (!histo) return 0;
2125  if (histo._typename.match(/^TH2/)) return 2;
2126  if (histo._typename.match(/^TProfile2D/)) return 2;
2127  if (histo._typename.match(/^TH3/)) return 3;
2128  return 1;
2129  }
2130 
2133  THistPainter.prototype.DecodeOptions = function(opt) {
2134  var histo = this.GetHisto(),
2135  hdim = this.Dimension(),
2136  pad = this.root_pad();
2137 
2138  if (!this.options)
2139  this.options = new THistDrawOptions;
2140  else
2141  this.options.Reset();
2142 
2143  this.options.Decode(opt || histo.fOption, hdim, histo, pad, this);
2144 
2145  this.OptionsStore(opt); // opt will be return as default draw option, used in webcanvas
2146  }
2147 
2148  THistPainter.prototype.CopyOptionsFrom = function(src) {
2149  if (src === this) return;
2150  var o = this.options, o0 = src.options;
2151 
2152  o.Mode3D = o0.Mode3D;
2153  o.Zero = o0.Zero;
2154  if (o0.Mode3D) {
2155  o.Lego = o0.Lego;
2156  o.Surf = o0.Surf;
2157  } else {
2158  o.Color = o0.Color;
2159  o.Contour = o0.Contour;
2160  }
2161  }
2162 
2164  THistPainter.prototype.CopyOptionsToOthers = function() {
2165  var pthis = this;
2166 
2167  this.ForEachPainter(function(painter) {
2168  if (painter === pthis) return;
2169  if (typeof painter.CopyOptionsFrom == 'function')
2170  painter.CopyOptionsFrom(pthis);
2171  }, "objects");
2172  }
2173 
2174  THistPainter.prototype.ScanContent = function(when_axis_changed) {
2175  // function will be called once new histogram or
2176  // new histogram content is assigned
2177  // one should find min,max,nbins, maxcontent values
2178  // if when_axis_changed === true specified, content will be scanned after axis zoom changed
2179 
2180  alert("HistPainter.prototype.ScanContent not implemented");
2181  }
2182 
2183  THistPainter.prototype.CheckPadRange = function(use_pad) {
2184 
2185  // actual work will be done when frame will draw axes
2186  if (this.is_main_painter())
2187  this.check_pad_range = use_pad ? "pad_range" : true;
2188  }
2189 
2190  THistPainter.prototype.CheckHistDrawAttributes = function() {
2191 
2192  var histo = this.GetHisto(),
2193  pp = this.pad_painter();
2194 
2195  if (pp && (this.options._pfc || this.options._plc || this.options._pmc)) {
2196  var icolor = pp.CreateAutoColor();
2197  if (this.options._pfc) { histo.fFillColor = icolor; delete this.fillatt; }
2198  if (this.options._plc) { histo.fLineColor = icolor; delete this.lineatt; }
2199  if (this.options._pmc) { histo.fMarkerColor = icolor; delete this.markeratt; }
2200  this.options._pfc = this.options._plc = this.options._pmc = false;
2201  }
2202 
2203  this.createAttFill({ attr: histo, color: this.options.histoFillColor, kind: 1 });
2204 
2205  this.createAttLine({ attr: histo, color0: this.options.histoLineColor });
2206  }
2207 
2212  THistPainter.prototype.UpdateObject = function(obj, opt) {
2213 
2214  var histo = this.GetHisto(),
2215  fp = this.frame_painter();
2216 
2217  if (obj !== histo) {
2218 
2219  if (!this.MatchObjectType(obj)) return false;
2220 
2221  // simple replace of object does not help - one can have different
2222  // complex relations between histo and stat box, histo and colz axis,
2223  // one could have THStack or TMultiGraph object
2224  // The only that could be done is update of content
2225 
2226  // check only stats bit, later other settings can be monitored
2227  if (histo.TestBit(JSROOT.TH1StatusBits.kNoStats) != obj.TestBit(JSROOT.TH1StatusBits.kNoStats)) {
2228  histo.fBits = obj.fBits;
2229 
2230  var statpainter = this.FindPainterFor(this.FindStat());
2231  if (statpainter) statpainter.Enabled = !histo.TestBit(JSROOT.TH1StatusBits.kNoStats);
2232  }
2233 
2234  // if (histo.TestBit(JSROOT.TH1StatusBits.kNoStats)) this.ToggleStat();
2235 
2236  // special treatment for webcanvas - also name can be changed
2237  if (this.snapid !== undefined)
2238  histo.fName = obj.fName;
2239 
2240  histo.fFillColor = obj.fFillColor;
2241  histo.fFillStyle = obj.fFillStyle;
2242  histo.fLineColor = obj.fLineColor;
2243  histo.fLineStyle = obj.fLineStyle;
2244  histo.fLineWidth = obj.fLineWidth;
2245 
2246  histo.fEntries = obj.fEntries;
2247  histo.fTsumw = obj.fTsumw;
2248  histo.fTsumwx = obj.fTsumwx;
2249  histo.fTsumwx2 = obj.fTsumwx2;
2250  histo.fXaxis.fNbins = obj.fXaxis.fNbins;
2251  if (this.Dimension() > 1) {
2252  histo.fTsumwy = obj.fTsumwy;
2253  histo.fTsumwy2 = obj.fTsumwy2;
2254  histo.fTsumwxy = obj.fTsumwxy;
2255  histo.fYaxis.fNbins = obj.fYaxis.fNbins;
2256  if (this.Dimension() > 2) {
2257  histo.fTsumwz = obj.fTsumwz;
2258  histo.fTsumwz2 = obj.fTsumwz2;
2259  histo.fTsumwxz = obj.fTsumwxz;
2260  histo.fTsumwyz = obj.fTsumwyz;
2261  histo.fZaxis.fNbins = obj.fZaxis.fNbins;
2262  }
2263  }
2264 
2265  histo.fArray = obj.fArray;
2266  histo.fNcells = obj.fNcells;
2267  histo.fTitle = obj.fTitle;
2268  histo.fMinimum = obj.fMinimum;
2269  histo.fMaximum = obj.fMaximum;
2270  function CopyAxis(tgt,src) {
2271  tgt.fTitle = src.fTitle;
2272  tgt.fLabels = src.fLabels;
2273  tgt.fXmin = src.fXmin;
2274  tgt.fXmax = src.fXmax;
2275  tgt.fTimeDisplay = src.fTimeDisplay;
2276  tgt.fTimeFormat = src.fTimeFormat;
2277  // copy attributes
2278  tgt.fAxisColor = src.fAxisColor;
2279  tgt.fLabelColor = src.fLabelColor;
2280  tgt.fLabelFont = src.fLabelFont;
2281  tgt.fLabelOffset = src.fLabelOffset;
2282  tgt.fLabelSize = src.fLabelSize;
2283  tgt.fNdivisions = src.fNdivisions;
2284  tgt.fTickLength = src.fTickLength;
2285  tgt.fTitleColor = src.fTitleColor;
2286  tgt.fTitleFont = src.fTitleFont;
2287  tgt.fTitleOffset = src.fTitleOffset;
2288  tgt.fTitleSize = src.fTitleSize;
2289  }
2290  CopyAxis(histo.fXaxis, obj.fXaxis);
2291  CopyAxis(histo.fYaxis, obj.fYaxis);
2292  CopyAxis(histo.fZaxis, obj.fZaxis);
2293 
2294  if (this.snapid || !fp || !fp.zoom_changed_interactive) {
2295  function CopyZoom(tgt,src) {
2296  tgt.fFirst = src.fFirst;
2297  tgt.fLast = src.fLast;
2298  tgt.fBits = src.fBits;
2299  }
2300  CopyZoom(histo.fXaxis, obj.fXaxis);
2301  CopyZoom(histo.fYaxis, obj.fYaxis);
2302  CopyZoom(histo.fZaxis, obj.fZaxis);
2303  }
2304  histo.fSumw2 = obj.fSumw2;
2305 
2306  if (this.IsTProfile()) {
2307  histo.fBinEntries = obj.fBinEntries;
2308  } else if (this.IsTH1K()) {
2309  histo.fNIn = obj.fNIn;
2310  histo.fReady = false;
2311  } else if (this.IsTH2Poly()) {
2312  histo.fBins = obj.fBins;
2313  }
2314 
2315  if (this.options.Func) {
2316 
2317  var painters = [], newfuncs = [], pp = this.pad_painter(), pid = this.hist_painter_id;
2318 
2319  // find painters associated with histogram
2320  if (pp)
2321  pp.ForEachPainterInPad(function(objp) {
2322  if (objp.child_painter_id === pid)
2323  painters.push(objp);
2324  }, "objects");
2325 
2326  if (obj.fFunctions)
2327  for (var n=0;n<obj.fFunctions.arr.length;++n) {
2328  var func = obj.fFunctions.arr[n];
2329  if (!func || !func._typename) continue;
2330  var funcpainter = null, func_indx = -1;
2331 
2332  // try to find matching object in associated list of painters
2333  for (var i=0;i<painters.length;++i)
2334  if (painters[i].MatchObjectType(func._typename) && (painters[i].GetObject().fName === func.fName)) {
2335  funcpainter = painters[i];
2336  func_indx = i;
2337  break;
2338  }
2339  // or just in generic list of painted objects
2340  if (!funcpainter && func.fName)
2341  funcpainter = this.FindPainterFor(null, func.fName, func._typename);
2342 
2343  if (funcpainter) {
2344  funcpainter.UpdateObject(func);
2345  if (func_indx >= 0) painters.splice(func_indx, 1);
2346  } else {
2347  newfuncs.push(func);
2348  }
2349  }
2350 
2351  // remove all function which are not found in new list of primitives
2352  if (pp && (painters.length > 0))
2353  pp.CleanPrimitives(function(p) { return painters.indexOf(p) >= 0; });
2354 
2355  // plot new objects on the same pad - will works only for simple drawings already loaded
2356  if (pp && (newfuncs.length > 0)) {
2357  var prev_name = pp.has_canvas ? pp.CurrentPadName(pp.this_pad_name) : undefined;
2358  for (var k=0;k<newfuncs.length;++k)
2359  JSROOT.draw(this.divid, newfuncs[k],"", function (painter) { painter.child_painter_id = pid; } )
2360  pp.CurrentPadName(prev_name);
2361  }
2362  }
2363 
2364  var changed_opt = (histo.fOption != obj.fOption);
2365  histo.fOption = obj.fOption;
2366 
2367  if (((opt !== undefined) && (this.options.original !== opt)) || changed_opt)
2368  this.DecodeOptions(opt || histo.fOption);
2369  }
2370 
2371  if (this.snapid || !fp || !fp.zoom_changed_interactive)
2372  this.CheckPadRange();
2373 
2374  this.ScanContent();
2375 
2376  this.histogram_updated = true; // indicate that object updated
2377 
2378  return true;
2379  }
2380 
2381  THistPainter.prototype.CreateAxisFuncs = function(with_y_axis, with_z_axis) {
2382  // here functions are defined to convert index to axis value and back
2383  // introduced to support non-equidistant bins
2384 
2385  var histo = this.GetHisto();
2386 
2387  function AssignFuncs(axis) {
2388  if (axis.fXbins.length >= axis.fNbins) {
2389  axis.regular = false;
2390  axis.GetBinCoord = function(bin) {
2391  var indx = Math.round(bin);
2392  if (indx <= 0) return this.fXmin;
2393  if (indx > this.fNbins) return this.fXmax;
2394  if (indx==bin) return this.fXbins[indx];
2395  var indx2 = (bin < indx) ? indx - 1 : indx + 1;
2396  return this.fXbins[indx] * Math.abs(bin-indx2) + this.fXbins[indx2] * Math.abs(bin-indx);
2397  };
2398  axis.FindBin = function(x,add) {
2399  for (var k = 1; k < this.fXbins.length; ++k)
2400  if (x < this.fXbins[k]) return Math.floor(k-1+add);
2401  return this.fNbins;
2402  };
2403  } else {
2404  axis.regular = true;
2405  axis.binwidth = (axis.fXmax - axis.fXmin) / (axis.fNbins || 1);
2406  axis.GetBinCoord = function(bin) { return this.fXmin + bin*this.binwidth; };
2407  axis.FindBin = function(x,add) { return Math.floor((x - this.fXmin) / this.binwidth + add); };
2408  }
2409  }
2410 
2411  this.xmin = histo.fXaxis.fXmin;
2412  this.xmax = histo.fXaxis.fXmax;
2413  AssignFuncs(histo.fXaxis);
2414 
2415  this.ymin = histo.fYaxis.fXmin;
2416  this.ymax = histo.fYaxis.fXmax;
2417 
2418  if (!with_y_axis || (this.nbinsy==0)) return;
2419 
2420  AssignFuncs(histo.fYaxis);
2421 
2422  if (!with_z_axis || (this.nbinsz==0)) return;
2423 
2424  AssignFuncs(histo.fZaxis);
2425  }
2426 
2430  THistPainter.prototype.CreateXY = function() {
2431  if (!this.is_main_painter()) return;
2432 
2433  var histo = this.GetHisto(),
2434  fp = this.frame_painter();
2435 
2436  if (!fp)
2437  return console.warn("histogram drawn without frame - not supported");
2438 
2439  // artifically add y range to display axes
2440  if (this.ymin === this.ymax) this.ymax += 1;
2441 
2442  fp.SetAxesRanges(histo.fXaxis, this.xmin, this.xmax, histo.fYaxis, this.ymin, this.ymax, histo.fZaxis, 0, 0);
2443  fp.CreateXY({ ndim: this.Dimension(),
2444  check_pad_range: this.check_pad_range,
2445  create_canvas: this.create_canvas,
2446  zoom_ymin: this.zoom_ymin,
2447  zoom_ymax: this.zoom_ymax,
2448  ymin_nz: this.ymin_nz,
2449  swap_xy: (this.options.BarStyle >= 20),
2450  reverse_x: this.options.RevX,
2451  reverse_y: this.options.RevY,
2452  Proj: this.options.Proj,
2453  extra_y_space: this.options.Text && (this.options.BarStyle > 0) });
2454  delete this.check_pad_range;
2455  }
2456 
2457  THistPainter.prototype.DrawBins = function() {
2458  alert("HistPainter.DrawBins not implemented");
2459  }
2460 
2461  THistPainter.prototype.DrawAxes = function() {
2462  // axes can be drawn only for main histogram
2463 
2464  if (!this.is_main_painter() || this.options.Same) return;
2465 
2466  var fp = this.frame_painter();
2467  if (!fp) return;
2468 
2469  fp.DrawAxes(false, this.options.Axis < 0, this.options.AxisPos, this.options.Zscale);
2470  fp.DrawGrids();
2471  }
2472 
2473  THistPainter.prototype.ToggleTitle = function(arg) {
2474  var histo = this.GetHisto();
2475  if (!this.is_main_painter() || !histo) return false;
2476  if (arg==='only-check') return !histo.TestBit(JSROOT.TH1StatusBits.kNoTitle);
2477  histo.InvertBit(JSROOT.TH1StatusBits.kNoTitle);
2478  this.DrawTitle();
2479  }
2480 
2481  THistPainter.prototype.DrawTitle = function() {
2482 
2483  // case when histogram drawn over other histogram (same option)
2484  if (!this.is_main_painter() || this.options.Same) return;
2485 
2486  var histo = this.GetHisto(), st = JSROOT.gStyle,
2487  tpainter = this.FindPainterFor(null, "title"),
2488  pt = tpainter ? tpainter.GetObject() : null;
2489 
2490  if (!pt) pt = this.FindInPrimitives("title");
2491  if (pt && (pt._typename !== "TPaveText")) pt = null;
2492 
2493  var draw_title = !histo.TestBit(JSROOT.TH1StatusBits.kNoTitle) && (st.fOptTitle > 0);
2494 
2495  if (pt) {
2496  pt.Clear();
2497  if (draw_title) pt.AddText(histo.fTitle);
2498  if (tpainter) tpainter.Redraw();
2499  } else if (draw_title && !tpainter && histo.fTitle && !this.options.PadTitle) {
2500  pt = JSROOT.Create("TPaveText");
2501  pt.fName = "title";
2502  pt.fTextFont = st.fTitleFont;
2503  pt.fTextSize = st.fTitleFontSize;
2504  pt.fTextColor = st.fTitleTextColor;
2505  pt.fTextAlign = st.fTitleAlign;
2506  pt.fFillColor = st.fTitleColor;
2507  pt.fFillStyle = st.fTitleStyle;
2508  pt.fBorderSize = st.fTitleBorderSize;
2509 
2510  pt.AddText(histo.fTitle);
2511  tpainter = JSROOT.Painter.drawPave(this.divid, pt, "postitle");
2512  if (tpainter) tpainter.$secondary = true;
2513  }
2514  }
2515 
2516  THistPainter.prototype.processTitleChange = function(arg) {
2517 
2518  var histo = this.GetHisto(),
2519  tpainter = this.FindPainterFor(null, "title");
2520 
2521  if (!histo || !tpainter) return null;
2522 
2523  if (arg==="check")
2524  return (!this.is_main_painter() || this.options.Same) ? null : histo;
2525 
2526  var pt = tpainter.GetObject();
2527  pt.Clear();
2528  pt.AddText(histo.fTitle);
2529 
2530  tpainter.Redraw();
2531 
2532  this.WebCanvasExec('SetTitle("' + histo.fTitle + '")');
2533  }
2534 
2535  THistPainter.prototype.UpdateStatWebCanvas = function() {
2536  if (!this.snapid) return;
2537 
2538  var stat = this.FindStat(),
2539  statpainter = this.FindPainterFor(stat);
2540 
2541  if (statpainter && !statpainter.snapid) statpainter.Redraw();
2542  }
2543 
2544  THistPainter.prototype.ToggleStat = function(arg) {
2545 
2546  var stat = this.FindStat(), statpainter = null;
2547 
2548  if (!arg) arg = "";
2549 
2550  if (!stat) {
2551  if (arg.indexOf('-check') > 0) return false;
2552  // when statbox created first time, one need to draw it
2553  stat = this.CreateStat(true);
2554  } else {
2555  statpainter = this.FindPainterFor(stat);
2556  }
2557 
2558  if (arg=='only-check') return statpainter ? statpainter.Enabled : false;
2559 
2560  if (arg=='fitpar-check') return stat ? stat.fOptFit : false;
2561 
2562  if (arg=='fitpar-toggle') {
2563  if (!stat) return false;
2564  stat.fOptFit = stat.fOptFit ? 0 : 1111; // for websocket command should be send to server
2565  if (statpainter) statpainter.Redraw();
2566  return true;
2567  }
2568 
2569  if (statpainter) {
2570  statpainter.Enabled = !statpainter.Enabled;
2571  // when stat box is drawn, it always can be drawn individually while it
2572  // should be last for colz RedrawPad is used
2573  statpainter.Redraw();
2574  return statpainter.Enabled;
2575  }
2576 
2577  JSROOT.draw(this.divid, stat, "onpad:" + this.pad_name);
2578 
2579  return true;
2580  }
2581 
2582  THistPainter.prototype.GetSelectIndex = function(axis, side, add) {
2583  // be aware - here indexes starts from 0
2584  var indx = 0,
2585  main = this.frame_painter(),
2586  nbin = this['nbins'+axis] || 0,
2587  taxis = this.GetAxis(axis),
2588  min = main ? main['zoom_' + axis + 'min'] : 0,
2589  max = main ? main['zoom_' + axis + 'max'] : 0;
2590 
2591  if ((min !== max) && taxis) {
2592  if (side == "left")
2593  indx = taxis.FindBin(min, add || 0);
2594  else
2595  indx = taxis.FindBin(max, (add || 0) + 0.5);
2596  if (indx<0) indx = 0; else if (indx>nbin) indx = nbin;
2597  } else {
2598  indx = (side == "left") ? 0 : nbin;
2599  }
2600 
2601  // TAxis object of histogram, where user range can be stored
2602  if (taxis) {
2603  if ((taxis.fFirst === taxis.fLast) || !taxis.TestBit(JSROOT.EAxisBits.kAxisRange) ||
2604  ((taxis.fFirst<=1) && (taxis.fLast>=nbin))) taxis = undefined;
2605  }
2606 
2607  if (side == "left") {
2608  if (indx < 0) indx = 0;
2609  if (taxis && (taxis.fFirst>1) && (indx<taxis.fFirst)) indx = taxis.fFirst-1;
2610  } else {
2611  if (indx > nbin) indx = nbin;
2612  if (taxis && (taxis.fLast <= nbin) && (indx>taxis.fLast)) indx = taxis.fLast;
2613  }
2614 
2615  return indx;
2616  }
2617 
2620  THistPainter.prototype.FindFunction = function(type_name, obj_name) {
2621  var histo = this.GetObject(),
2622  funcs = histo && histo.fFunctions ? histo.fFunctions.arr : null;
2623 
2624  if (!funcs) return null;
2625 
2626  for (var i = 0; i < funcs.length; ++i) {
2627  if (obj_name && (funcs[i].fName !== obj_name)) continue;
2628  if (funcs[i]._typename === type_name) return funcs[i];
2629  }
2630 
2631  return null;
2632  }
2633 
2636  THistPainter.prototype.FindStat = function() {
2637  if (this.options.PadStats) {
2638  var p = this.FindPainterFor(null,"stats", "TPaveStats");
2639  return p ? p.GetObject() : null;
2640  }
2641 
2642  return this.FindFunction('TPaveStats', 'stats');
2643  }
2644 
2645  THistPainter.prototype.IgnoreStatsFill = function() {
2646  return !this.GetObject() || (!this.draw_content && !this.create_stats) || (this.options.Axis>0);
2647  }
2648 
2652  THistPainter.prototype.CreateStat = function(force) {
2653 
2654  var histo = this.GetHisto();
2655 
2656  if (this.options.PadStats || !histo) return null;
2657 
2658  if (!force && !this.options.ForceStat) {
2659  if (this.options.NoStat || histo.TestBit(JSROOT.TH1StatusBits.kNoStats) || !JSROOT.gStyle.AutoStat) return null;
2660 
2661  if (!this.draw_content || !this.is_main_painter()) return null;
2662  }
2663 
2664  var stats = this.FindStat(), st = JSROOT.gStyle,
2665  optstat = this.options.optstat, optfit = this.options.optfit;
2666 
2667  if (optstat !== undefined) {
2668  if (stats) stats.fOptStat = optstat;
2669  delete this.options.optstat;
2670  } else {
2671  optstat = histo.$custom_stat || st.fOptStat;
2672  }
2673 
2674  if (optfit !== undefined) {
2675  if (stats) stats.fOptFit = optfit;
2676  delete this.options.optfit;
2677  } else {
2678  optfit = st.fOptFit;
2679  }
2680 
2681  if (!stats && !optstat && !optfit) return null;
2682 
2683  this.create_stats = true;
2684 
2685  if (stats) return stats;
2686 
2687  stats = JSROOT.Create('TPaveStats');
2688  JSROOT.extend(stats, { fName: 'stats',
2689  fOptStat: optstat,
2690  fOptFit: optfit,
2691  fBorderSize: 1 });
2692 
2693  stats.fX1NDC = st.fStatX - st.fStatW;
2694  stats.fY1NDC = st.fStatY - st.fStatH;
2695  stats.fX2NDC = st.fStatX;
2696  stats.fY2NDC = st.fStatY;
2697 
2698  stats.fFillColor = st.fStatColor;
2699  stats.fFillStyle = st.fStatStyle;
2700 
2701  stats.fTextAngle = 0;
2702  stats.fTextSize = st.fStatFontSize;
2703  stats.fTextAlign = 12;
2704  stats.fTextColor = st.fStatTextColor;
2705  stats.fTextFont = st.fStatFont;
2706 
2707  if (histo._typename.match(/^TProfile/) || histo._typename.match(/^TH2/))
2708  stats.fY1NDC = 0.67;
2709 
2710  stats.AddText(histo.fName);
2711 
2712  this.AddFunction(stats);
2713 
2714  return stats;
2715  }
2716 
2717  THistPainter.prototype.AddFunction = function(obj, asfirst) {
2718  var histo = this.GetObject();
2719  if (!histo || !obj) return;
2720 
2721  if (!histo.fFunctions)
2722  histo.fFunctions = JSROOT.Create("TList");
2723 
2724  if (asfirst)
2725  histo.fFunctions.AddFirst(obj);
2726  else
2727  histo.fFunctions.Add(obj);
2728  }
2729 
2731  THistPainter.prototype.DrawNextFunction = function(indx, callback, painter) {
2732 
2733  if (painter && (typeof painter == "object"))
2734  painter.child_painter_id = this.hist_painter_id;
2735 
2736  var histo = this.GetHisto();
2737  if (!this.options.Func || !histo.fFunctions ||
2738  (indx >= histo.fFunctions.arr.length)) return JSROOT.CallBack(callback);
2739 
2740  var func = histo.fFunctions.arr[indx],
2741  opt = histo.fFunctions.opt[indx],
2742  do_draw = false,
2743  func_painter = this.FindPainterFor(func);
2744 
2745  // no need to do something if painter for object was already done
2746  // object will be redraw automatically
2747  if (func_painter === null) {
2748  if (func._typename === 'TPaveText' || func._typename === 'TPaveStats') {
2749  do_draw = !histo.TestBit(JSROOT.TH1StatusBits.kNoStats) && !this.options.NoStat;
2750  } else if (func._typename === 'TF1') {
2751  do_draw = !func.TestBit(JSROOT.BIT(9));
2752  } else {
2753  do_draw = (func._typename !== 'TPaletteAxis');
2754  }
2755  }
2756 
2757  if (do_draw)
2758  return JSROOT.draw(this.divid, func, opt, this.DrawNextFunction.bind(this, indx+1, callback));
2759 
2760  this.DrawNextFunction(indx+1, callback);
2761  }
2762 
2764  THistPainter.prototype.UnzoomUserRange = function(dox, doy, doz) {
2765 
2766  var res = false, painter = this, histo = this.GetHisto();
2767 
2768  if (!histo) return false;
2769 
2770  function UnzoomTAxis(obj) {
2771  if (!obj) return false;
2772  if (!obj.TestBit(JSROOT.EAxisBits.kAxisRange)) return false;
2773  if (obj.fFirst === obj.fLast) return false;
2774  if ((obj.fFirst <= 1) && (obj.fLast >= obj.fNbins)) return false;
2775  obj.InvertBit(JSROOT.EAxisBits.kAxisRange);
2776  return true;
2777  }
2778 
2779  function UzoomMinMax(ndim, hist) {
2780  if (painter.Dimension()!==ndim) return false;
2781  if ((painter.options.minimum===-1111) && (painter.options.maximum===-1111)) return false;
2782  if (!painter.draw_content) return false; // if not drawing content, not change min/max
2783  painter.options.minimum = painter.options.maximum = -1111;
2784  painter.ScanContent(true); // to reset ymin/ymax
2785  return true;
2786  }
2787 
2788  if (dox && UnzoomTAxis(histo.fXaxis)) res = true;
2789  if (doy && (UnzoomTAxis(histo.fYaxis) || UzoomMinMax(1, histo))) res = true;
2790  if (doz && (UnzoomTAxis(histo.fZaxis) || UzoomMinMax(2, histo))) res = true;
2791 
2792  return res;
2793  }
2794 
2795  THistPainter.prototype.AllowDefaultYZooming = function() {
2796  // return true if default Y zooming should be enabled
2797  // it is typically for 2-Dim histograms or
2798  // when histogram not draw, defined by other painters
2799 
2800  if (this.Dimension()>1) return true;
2801  if (this.draw_content) return false;
2802 
2803  var pad_painter = this.pad_painter();
2804  if (pad_painter && pad_painter.painters)
2805  for (var k = 0; k < pad_painter.painters.length; ++k) {
2806  var subpainter = pad_painter.painters[k];
2807  if ((subpainter!==this) && subpainter.wheel_zoomy!==undefined)
2808  return subpainter.wheel_zoomy;
2809  }
2810 
2811  return false;
2812  }
2813 
2820  THistPainter.prototype.AddInteractive = function() {
2821 
2822  if (this.is_main_painter()) {
2823  var fp = this.frame_painter();
2824  if (fp) fp.AddInteractive();
2825  }
2826  }
2827 
2828  THistPainter.prototype.ShowContextMenu = function(kind, evnt, obj) {
2829 
2830  // this is for debug purposes only, when context menu is where, close is and show normal menu
2831  //if (!evnt && !kind && document.getElementById('root_ctx_menu')) {
2832  // var elem = document.getElementById('root_ctx_menu');
2833  // elem.parentNode.removeChild(elem);
2834  // return;
2835  //}
2836 
2837  var menu_painter = this, frame_corner = false, fp = null; // object used to show context menu
2838 
2839  if (!evnt) {
2840  d3.event.preventDefault();
2841  d3.event.stopPropagation(); // disable main context menu
2842  evnt = d3.event;
2843 
2844  if (kind === undefined) {
2845  var ms = d3.mouse(this.svg_frame().node()),
2846  tch = d3.touches(this.svg_frame().node()),
2847  pp = this.pad_painter(),
2848  pnt = null, sel = null;
2849 
2850  fp = this.frame_painter();
2851 
2852  if (tch.length === 1) pnt = { x: tch[0][0], y: tch[0][1], touch: true }; else
2853  if (ms.length === 2) pnt = { x: ms[0], y: ms[1], touch: false };
2854 
2855  if ((pnt !== null) && (pp !== null)) {
2856  pnt.painters = true; // assign painter for every tooltip
2857  var hints = pp.GetTooltips(pnt), bestdist = 1000;
2858  for (var n=0;n<hints.length;++n)
2859  if (hints[n] && hints[n].menu) {
2860  var dist = ('menu_dist' in hints[n]) ? hints[n].menu_dist : 7;
2861  if (dist < bestdist) { sel = hints[n].painter; bestdist = dist; }
2862  }
2863  }
2864 
2865  if (sel!==null) menu_painter = sel; else
2866  if (fp!==null) kind = "frame";
2867 
2868  if (pnt!==null) frame_corner = (pnt.x>0) && (pnt.x<20) && (pnt.y>0) && (pnt.y<20);
2869 
2870  if (fp) fp.SetLastEventPos(pnt);
2871  }
2872  }
2873 
2874  // one need to copy event, while after call back event may be changed
2875  menu_painter.ctx_menu_evnt = evnt;
2876 
2877  JSROOT.Painter.createMenu(menu_painter, function(menu) {
2878  var domenu = menu.painter.FillContextMenu(menu, kind, obj);
2879 
2880  // fill frame menu by default - or append frame elements when activated in the frame corner
2881  if (fp && (!domenu || (frame_corner && (kind!=="frame"))))
2882  domenu = fp.FillContextMenu(menu);
2883 
2884  if (domenu)
2885  menu.painter.FillObjectExecMenu(menu, kind, function() {
2886  // suppress any running zooming
2887  menu.painter.SwitchTooltip(false);
2888  menu.show(menu.painter.ctx_menu_evnt, menu.painter.SwitchTooltip.bind(menu.painter, true));
2889  });
2890 
2891  }); // end menu creation
2892  }
2893 
2895  THistPainter.prototype.ChangeUserRange = function(arg) {
2896  var histo = this.GetHisto(),
2897  taxis = histo ? histo['f'+arg+"axis"] : null;
2898  if (!taxis) return;
2899 
2900  var curr = "[1," + taxis.fNbins+"]";
2901  if (taxis.TestBit(JSROOT.EAxisBits.kAxisRange))
2902  curr = "[" +taxis.fFirst+"," + taxis.fLast+"]";
2903 
2904  var res = prompt("Enter user range for axis " + arg + " like [1," + taxis.fNbins + "]", curr);
2905  if (!res) return;
2906  res = JSON.parse(res);
2907 
2908  if (!res || (res.length!=2) || isNaN(res[0]) || isNaN(res[1])) return;
2909  taxis.fFirst = parseInt(res[0]);
2910  taxis.fLast = parseInt(res[1]);
2911 
2912  var newflag = (taxis.fFirst < taxis.fLast) && (taxis.fFirst >= 1) && (taxis.fLast<=taxis.fNbins);
2913  if (newflag != taxis.TestBit(JSROOT.EAxisBits.kAxisRange))
2914  taxis.InvertBit(JSROOT.EAxisBits.kAxisRange);
2915 
2916  this.Redraw();
2917  }
2918 
2919  THistPainter.prototype.FillContextMenu = function(menu) {
2920 
2921  var histo = this.GetHisto(),
2922  fp = this.frame_painter();
2923  if (!histo) return;
2924 
2925  menu.add("header:"+ histo._typename + "::" + histo.fName);
2926 
2927  if (this.draw_content) {
2928  menu.addchk(this.ToggleStat('only-check'), "Show statbox", function() { this.ToggleStat(); });
2929  if (this.Dimension() == 1) {
2930  menu.add("User range X", "X", this.ChangeUserRange);
2931  } else {
2932  menu.add("sub:User ranges");
2933  menu.add("X", "X", this.ChangeUserRange);
2934  menu.add("Y", "Y", this.ChangeUserRange);
2935  if (this.Dimension() > 2)
2936  menu.add("Z", "Z", this.ChangeUserRange);
2937  menu.add("endsub:")
2938  }
2939 
2940  if (typeof this.FillHistContextMenu == 'function')
2941  this.FillHistContextMenu(menu);
2942  }
2943 
2944  if (this.options.Mode3D) {
2945  // menu for 3D drawings
2946 
2947  if (menu.size() > 0)
2948  menu.add("separator");
2949 
2950  var main = this.main_painter() || this;
2951 
2952  menu.addchk(main.IsTooltipAllowed(), 'Show tooltips', function() {
2953  main.SetTooltipAllowed("toggle");
2954  });
2955 
2956  menu.addchk(fp.enable_highlight, 'Highlight bins', function() {
2957  fp.enable_highlight = !fp.enable_highlight;
2958  if (!fp.enable_highlight && fp.BinHighlight3D && fp.mode3d) fp.BinHighlight3D(null);
2959  });
2960 
2961  if (fp && fp.Render3D) {
2962  menu.addchk(main.options.FrontBox, 'Front box', function() {
2963  main.options.FrontBox = !main.options.FrontBox;
2964  fp.Render3D();
2965  });
2966  menu.addchk(main.options.BackBox, 'Back box', function() {
2967  main.options.BackBox = !main.options.BackBox;
2968  fp.Render3D();
2969  });
2970  }
2971 
2972  if (this.draw_content) {
2973  menu.addchk(!this.options.Zero, 'Suppress zeros', function() {
2974  this.options.Zero = !this.options.Zero;
2975  this.InteractiveRedraw("pad");
2976  });
2977 
2978  if ((this.options.Lego==12) || (this.options.Lego==14)) {
2979  menu.addchk(this.options.Zscale, "Z scale", function() {
2980  this.ToggleColz();
2981  });
2982  if (this.FillPaletteMenu) this.FillPaletteMenu(menu);
2983  }
2984  }
2985 
2986  if (main.control && typeof main.control.reset === 'function')
2987  menu.add('Reset camera', function() {
2988  main.control.reset();
2989  });
2990  }
2991 
2992  this.FillAttContextMenu(menu);
2993 
2994  if (this.histogram_updated && fp.zoom_changed_interactive)
2995  menu.add('Let update zoom', function() {
2996  fp.zoom_changed_interactive = 0;
2997  });
2998 
2999  return true;
3000  }
3001 
3002  THistPainter.prototype.ButtonClick = function(funcname) {
3003  // TODO: move to frame painter
3004 
3005  var fp = this.frame_painter();
3006 
3007  if (!this.is_main_painter() || !fp) return false;
3008 
3009  switch(funcname) {
3010  case "ToggleZoom":
3011  if ((fp.zoom_xmin !== fp.zoom_xmax) || (fp.zoom_ymin !== fp.zoom_ymax) || (fp.zoom_zmin !== fp.zoom_zmax)) {
3012  fp.Unzoom();
3013  fp.zoom_changed_interactive = 0;
3014  return true;
3015  }
3016  if (this.draw_content && (typeof this.AutoZoom === 'function')) {
3017  this.AutoZoom();
3018  return true;
3019  }
3020  break;
3021  case "ToggleLogX": fp.ToggleLog("x"); break;
3022  case "ToggleLogY": fp.ToggleLog("y"); break;
3023  case "ToggleLogZ": fp.ToggleLog("z"); break;
3024  case "ToggleStatBox": this.ToggleStat(); return true; break;
3025  }
3026  return false;
3027  }
3028 
3029  THistPainter.prototype.FillToolbar = function(not_shown) {
3030  var pp = this.pad_painter();
3031  if (!pp) return;
3032 
3033  pp.AddButton(JSROOT.ToolbarIcons.auto_zoom, 'Toggle between unzoom and autozoom-in', 'ToggleZoom', "Ctrl *");
3034  pp.AddButton(JSROOT.ToolbarIcons.arrow_right, "Toggle log x", "ToggleLogX", "PageDown");
3035  pp.AddButton(JSROOT.ToolbarIcons.arrow_up, "Toggle log y", "ToggleLogY", "PageUp");
3036  if (this.Dimension() > 1)
3037  pp.AddButton(JSROOT.ToolbarIcons.arrow_diag, "Toggle log z", "ToggleLogZ");
3038  if (this.draw_content)
3039  pp.AddButton(JSROOT.ToolbarIcons.statbox, 'Toggle stat box', "ToggleStatBox");
3040  if (!not_shown) pp.ShowButtons();
3041  }
3042 
3044  THistPainter.prototype.Get3DToolTip = function(indx) {
3045  var histo = this.GetHisto(),
3046  tip = { bin: indx, name: histo.fName, title: histo.fTitle };
3047  switch (this.Dimension()) {
3048  case 1:
3049  tip.ix = indx; tip.iy = 1;
3050  tip.value = histo.getBinContent(tip.ix);
3051  tip.error = histo.getBinError(indx);
3052  tip.lines = this.GetBinTips(indx-1);
3053  break;
3054  case 2:
3055  tip.ix = indx % (this.nbinsx + 2);
3056  tip.iy = (indx - tip.ix) / (this.nbinsx + 2);
3057  tip.value = histo.getBinContent(tip.ix, tip.iy);
3058  tip.error = histo.getBinError(indx);
3059  tip.lines = this.GetBinTips(tip.ix-1, tip.iy-1);
3060  break;
3061  case 3:
3062  tip.ix = indx % (this.nbinsx+2);
3063  tip.iy = ((indx - tip.ix) / (this.nbinsx+2)) % (this.nbinsy+2);
3064  tip.iz = (indx - tip.ix - tip.iy * (this.nbinsx+2)) / (this.nbinsx+2) / (this.nbinsy+2);
3065  tip.value = histo.getBinContent(tip.ix, tip.iy, tip.iz);
3066  tip.error = histo.getBinError(indx);
3067  tip.lines = this.GetBinTips(tip.ix-1, tip.iy-1, tip.iz-1);
3068  break;
3069  }
3070 
3071  return tip;
3072  }
3073 
3074  THistPainter.prototype.CreateContour = function(nlevels, zmin, zmax, zminpositive) {
3075 
3076  if (nlevels<1) nlevels = JSROOT.gStyle.fNumberContours;
3077  this.fContour = [];
3078  this.colzmin = zmin;
3079  this.colzmax = zmax;
3080 
3081  if (this.root_pad().fLogz) {
3082  if (this.colzmax <= 0) this.colzmax = 1.;
3083  if (this.colzmin <= 0)
3084  if ((zminpositive===undefined) || (zminpositive <= 0))
3085  this.colzmin = 0.0001*this.colzmax;
3086  else
3087  this.colzmin = ((zminpositive < 3) || (zminpositive>100)) ? 0.3*zminpositive : 1;
3088  if (this.colzmin >= this.colzmax) this.colzmin = 0.0001*this.colzmax;
3089 
3090  var logmin = Math.log(this.colzmin)/Math.log(10),
3091  logmax = Math.log(this.colzmax)/Math.log(10),
3092  dz = (logmax-logmin)/nlevels;
3093  this.fContour.push(this.colzmin);
3094  for (var level=1; level<nlevels; level++)
3095  this.fContour.push(Math.exp((logmin + dz*level)*Math.log(10)));
3096  this.fContour.push(this.colzmax);
3097  this.fCustomContour = true;
3098  } else {
3099  if ((this.colzmin === this.colzmax) && (this.colzmin !== 0)) {
3100  this.colzmax += 0.01*Math.abs(this.colzmax);
3101  this.colzmin -= 0.01*Math.abs(this.colzmin);
3102  }
3103  var dz = (this.colzmax-this.colzmin)/nlevels;
3104  for (var level=0; level<=nlevels; level++)
3105  this.fContour.push(this.colzmin + dz*level);
3106  }
3107 
3108  var fp = this.frame_painter();
3109  if ((this.Dimension() < 3) && fp) {
3110  fp.zmin = this.colzmin;
3111  fp.zmax = this.colzmax;
3112  }
3113 
3114  return this.fContour;
3115  }
3116 
3117  THistPainter.prototype.GetContour = function() {
3118  if (this.fContour) return this.fContour;
3119 
3120  var main = this.main_painter(),
3121  fp = this.frame_painter();
3122  if (main && (main !== this) && main.fContour) {
3123  this.fContour = main.fContour;
3124  this.fCustomContour = main.fCustomContour;
3125  this.colzmin = main.colzmin;
3126  this.colzmax = main.colzmax;
3127  return this.fContour;
3128  }
3129 
3130  // if not initialized, first create contour array
3131  // difference from ROOT - fContour includes also last element with maxbin, which makes easier to build logz
3132  var histo = this.GetObject(), nlevels = JSROOT.gStyle.fNumberContours,
3133  zmin = this.minbin, zmax = this.maxbin, zminpos = this.minposbin;
3134  if (zmin === zmax) { zmin = this.gminbin; zmax = this.gmaxbin; zminpos = this.gminposbin }
3135  if (histo.fContour) nlevels = histo.fContour.length;
3136  if ((this.options.minimum !== -1111) && (this.options.maximum != -1111)) {
3137  zmin = this.options.minimum;
3138  zmax = this.options.maximum;
3139  }
3140  if (fp && (fp.zoom_zmin != fp.zoom_zmax)) {
3141  zmin = fp.zoom_zmin;
3142  zmax = fp.zoom_zmax;
3143  }
3144 
3145  if (histo.fContour && (histo.fContour.length>1) && histo.TestBit(JSROOT.TH1StatusBits.kUserContour)) {
3146  this.fContour = JSROOT.clone(histo.fContour);
3147  this.fCustomContour = true;
3148  this.colzmin = zmin;
3149  this.colzmax = zmax;
3150  if (zmax > this.fContour[this.fContour.length-1]) this.fContour.push(zmax);
3151  if ((this.Dimension()<3) && fp) {
3152  fp.zmin = this.colzmin;
3153  fp.zmax = this.colzmax;
3154  }
3155  return this.fContour;
3156  }
3157 
3158  this.fCustomContour = false;
3159 
3160  return this.CreateContour(nlevels, zmin, zmax, zminpos);
3161  }
3162 
3164  THistPainter.prototype.getContourIndex = function(zc) {
3165 
3166  var cntr = this.GetContour();
3167 
3168  if (this.fCustomContour) {
3169  var l = 0, r = cntr.length-1, mid;
3170  if (zc < cntr[0]) return -1;
3171  if (zc >= cntr[r]) return r;
3172  while (l < r-1) {
3173  mid = Math.round((l+r)/2);
3174  if (cntr[mid] > zc) r = mid; else l = mid;
3175  }
3176  return l;
3177  }
3178 
3179  // bins less than zmin not drawn
3180  if (zc < this.colzmin) return this.options.Zero ? -1 : 0;
3181 
3182  // if bin content exactly zmin, draw it when col0 specified or when content is positive
3183  if (zc===this.colzmin) return ((this.colzmin != 0) || !this.options.Zero || this.IsTH2Poly()) ? 0 : -1;
3184 
3185  return Math.floor(0.01+(zc-this.colzmin)*(cntr.length-1)/(this.colzmax-this.colzmin));
3186  }
3187 
3190  THistPainter.prototype.getContourColor = function(zc, asindx) {
3191  var zindx = this.getContourIndex(zc);
3192  if (zindx < 0) return null;
3193 
3194  var cntr = this.GetContour(),
3195  palette = this.GetPalette(),
3196  indx = palette.calcColorIndex(zindx, cntr.length);
3197 
3198  return asindx ? indx : palette.getColor(indx);
3199  }
3200 
3201  THistPainter.prototype.GetPalette = function(force) {
3202  if (!this.fPalette || force)
3203  this.fPalette = this.get_palette(true, this.options.Palette);
3204  return this.fPalette;
3205  }
3206 
3207  THistPainter.prototype.FillPaletteMenu = function(menu) {
3208 
3209  var curr = this.options.Palette, hpainter = this;
3210  if ((curr===null) || (curr===0)) curr = JSROOT.gStyle.Palette;
3211 
3212  function change(arg) {
3213  hpainter.options.Palette = parseInt(arg);
3214  hpainter.GetPalette(true);
3215  hpainter.Redraw(); // redraw histogram
3216  };
3217 
3218  function add(id, name, more) {
3219  menu.addchk((id===curr) || more, '<nobr>' + name + '</nobr>', id, change);
3220  };
3221 
3222  menu.add("sub:Palette");
3223 
3224  add(50, "ROOT 5", (curr>=10) && (curr<51));
3225  add(51, "Deep Sea");
3226  add(52, "Grayscale", (curr>0) && (curr<10));
3227  add(53, "Dark body radiator");
3228  add(54, "Two-color hue");
3229  add(55, "Rainbow");
3230  add(56, "Inverted dark body radiator");
3231  add(57, "Bird", (curr>113));
3232  add(58, "Cubehelix");
3233  add(59, "Green Red Violet");
3234  add(60, "Blue Red Yellow");
3235  add(61, "Ocean");
3236  add(62, "Color Printable On Grey");
3237  add(63, "Alpine");
3238  add(64, "Aquamarine");
3239  add(65, "Army");
3240  add(66, "Atlantic");
3241 
3242  menu.add("endsub:");
3243  }
3244 
3245  THistPainter.prototype.DrawColorPalette = function(enabled, postpone_draw, can_move) {
3246  // only when create new palette, one could change frame size
3247 
3248  if (!this.is_main_painter()) return null;
3249 
3250  var pal = this.FindFunction('TPaletteAxis'),
3251  pal_painter = this.FindPainterFor(pal);
3252 
3253  if (this._can_move_colz) { can_move = true; delete this._can_move_colz; }
3254 
3255  if (!pal_painter && !pal) {
3256  pal_painter = this.FindPainterFor(undefined, undefined, "TPaletteAxis");
3257  if (pal_painter) {
3258  pal = pal_painter.GetObject();
3259  // add to list of functions
3260  this.AddFunction(pal, true);
3261  }
3262  }
3263 
3264  if (!enabled) {
3265  if (pal_painter) {
3266  pal_painter.Enabled = false;
3267  pal_painter.RemoveDrawG(); // completely remove drawing without need to redraw complete pad
3268  }
3269 
3270  return null;
3271  }
3272 
3273  if (!pal) {
3274 
3275  if (this.options.PadPalette) return null;
3276 
3277  pal = JSROOT.Create('TPave');
3278 
3279  JSROOT.extend(pal, { _typename: "TPaletteAxis", fName: "TPave", fH: null, fAxis: JSROOT.Create('TGaxis'),
3280  fX1NDC: 0.91, fX2NDC: 0.95, fY1NDC: 0.1, fY2NDC: 0.9, fInit: 1 } );
3281 
3282  var zaxis = this.GetHisto().fZaxis;
3283 
3284  JSROOT.extend(pal.fAxis, { fTitle: zaxis.fTitle, fTitleSize: zaxis.fTitleSize, fChopt: "+",
3285  fLineColor: zaxis.fAxisColor, fLineSyle: 1, fLineWidth: 1,
3286  fTextAngle: 0, fTextSize: zaxis.fLabelSize, fTextAlign: 11,
3287  fTextColor: zaxis.fLabelColor, fTextFont: zaxis.fLabelFont });
3288 
3289  // place colz in the beginning, that stat box is always drawn on the top
3290  this.AddFunction(pal, true);
3291 
3292  can_move = true;
3293  }
3294 
3295  var frame_painter = this.frame_painter();
3296 
3297  // keep palette width
3298  if (can_move && frame_painter) {
3299  pal.fX2NDC = frame_painter.fX2NDC + 0.01 + (pal.fX2NDC - pal.fX1NDC);
3300  pal.fX1NDC = frame_painter.fX2NDC + 0.01;
3301  pal.fY1NDC = frame_painter.fY1NDC;
3302  pal.fY2NDC = frame_painter.fY2NDC;
3303  }
3304 
3305  var arg = "";
3306  if (postpone_draw) arg+=";postpone";
3307  if (can_move && !this.do_redraw_palette) arg+=";can_move";
3308 
3309  if (!pal_painter) {
3310  // when histogram drawn on sub pad, let draw new axis object on the same pad
3311  var prev = this.CurrentPadName(this.pad_name);
3312  // CAUTION!!! This is very special place where return value of JSROOT.draw is allowed
3313  // while palette drawing is in same script. Normally callback should be used
3314  pal_painter = JSROOT.draw(this.divid, pal, arg);
3315  this.CurrentPadName(prev);
3316  } else {
3317  pal_painter.Enabled = true;
3318  pal_painter.DrawPave(arg);
3319  }
3320 
3321  // mark painter as secondary - not in list of TCanvas primitives
3322  pal_painter.$secondary = true;
3323 
3324  // make dummy redraw, palette will be updated only from histogram painter
3325  pal_painter.Redraw = function() {};
3326 
3327  // special code to adjust frame position to actual position of palette
3328  if (can_move && frame_painter && (pal.fX1NDC-0.005 < frame_painter.fX2NDC) && !this.do_redraw_palette) {
3329 
3330  this.do_redraw_palette = true;
3331 
3332  frame_painter.fX2NDC = pal.fX1NDC - 0.01;
3333  frame_painter.Redraw();
3334  // here we should redraw main object
3335  if (!postpone_draw) this.Redraw();
3336 
3337  delete this.do_redraw_palette;
3338  }
3339 
3340  return pal_painter;
3341  }
3342 
3343  THistPainter.prototype.ToggleColz = function() {
3344  var can_toggle = this.options.Mode3D ? (this.options.Lego === 12 || this.options.Lego === 14 || this.options.Surf === 11 || this.options.Surf === 12) :
3345  this.options.Color || this.options.Contour;
3346 
3347  if (can_toggle) {
3348  this.options.Zscale = !this.options.Zscale;
3349  this.DrawColorPalette(this.options.Zscale, false, true);
3350  }
3351  }
3352 
3353  THistPainter.prototype.ToggleMode3D = function() {
3354  this.options.Mode3D = !this.options.Mode3D;
3355 
3356  if (this.options.Mode3D) {
3357  if (!this.options.Surf && !this.options.Lego && !this.options.Error) {
3358  if ((this.nbinsx>=50) || (this.nbinsy>=50))
3359  this.options.Lego = this.options.Color ? 14 : 13;
3360  else
3361  this.options.Lego = this.options.Color ? 12 : 1;
3362 
3363  this.options.Zero = false; // do not show zeros by default
3364  }
3365  }
3366 
3367  this.CopyOptionsToOthers();
3368  this.InteractiveRedraw("pad","drawopt");
3369  }
3370 
3371  THistPainter.prototype.PrepareColorDraw = function(args) {
3372 
3373  if (!args) args = { rounding: true, extra: 0, middle: 0 };
3374 
3375  if (args.extra === undefined) args.extra = 0;
3376  if (args.middle === undefined) args.middle = 0;
3377 
3378  var histo = this.GetHisto(),
3379  xaxis = histo.fXaxis, yaxis = histo.fYaxis,
3380  pmain = this.frame_painter(),
3381  hdim = this.Dimension(),
3382  i, j, x, y, binz, binarea,
3383  res = {
3384  i1: this.GetSelectIndex("x", "left", 0 - args.extra),
3385  i2: this.GetSelectIndex("x", "right", 1 + args.extra),
3386  j1: (hdim===1) ? 0 : this.GetSelectIndex("y", "left", 0 - args.extra),
3387  j2: (hdim===1) ? 1 : this.GetSelectIndex("y", "right", 1 + args.extra),
3388  min: 0, max: 0, sumz: 0, xbar1: 0, xbar2: 1, ybar1: 0, ybar2: 1
3389  };
3390 
3391  res.grx = new Float32Array(res.i2+1);
3392  res.gry = new Float32Array(res.j2+1);
3393 
3394  if (typeof histo.fBarOffset == 'number' && typeof histo.fBarWidth == 'number'
3395  && (histo.fBarOffset || histo.fBarWidth !== 1000)) {
3396  if (histo.fBarOffset <= 1000) {
3397  res.xbar1 = res.ybar1 = 0.001*histo.fBarOffset;
3398  } else if (histo.fBarOffset <= 3000) {
3399  res.xbar1 = 0.001*(histo.fBarOffset-2000);
3400  } else if (histo.fBarOffset <= 5000) {
3401  res.ybar1 = 0.001*(histo.fBarOffset-4000);
3402  }
3403 
3404  if (histo.fBarWidth <= 1000) {
3405  res.xbar2 = Math.min(1., res.xbar1 + 0.001*histo.fBarWidth);
3406  res.ybar2 = Math.min(1., res.ybar1 + 0.001*histo.fBarWidth);
3407  } else if (histo.fBarWidth <= 3000) {
3408  res.xbar2 = Math.min(1., res.xbar1 + 0.001*(histo.fBarWidth-2000));
3409  // res.ybar2 = res.ybar1 + 1;
3410  } else if (histo.fBarWidth <= 5000) {
3411  // res.xbar2 = res.xbar1 + 1;
3412  res.ybar2 = Math.min(1., res.ybar1 + 0.001*(histo.fBarWidth-4000));
3413  }
3414  }
3415 
3416  if (args.original) {
3417  res.original = true;
3418  res.origx = new Float32Array(res.i2+1);
3419  res.origy = new Float32Array(res.j2+1);
3420  }
3421 
3422  if (args.pixel_density) args.rounding = true;
3423 
3424  if (!pmain) {
3425  console.warn("cannot draw histogram without frame");
3426  return res;
3427  }
3428 
3429  // calculate graphical coordinates in advance
3430  for (i = res.i1; i <= res.i2; ++i) {
3431  x = xaxis.GetBinCoord(i + args.middle);
3432  if (pmain.logx && (x <= 0)) { res.i1 = i+1; continue; }
3433  if (res.origx) res.origx[i] = x;
3434  res.grx[i] = pmain.grx(x);
3435  if (args.rounding) res.grx[i] = Math.round(res.grx[i]);
3436 
3437  if (args.use3d) {
3438  if (res.grx[i] < -pmain.size_xy3d) { res.i1 = i; res.grx[i] = -pmain.size_xy3d; }
3439  if (res.grx[i] > pmain.size_xy3d) { res.i2 = i; res.grx[i] = pmain.size_xy3d; }
3440  }
3441  }
3442 
3443  if (hdim===1) {
3444  res.gry[0] = pmain.gry(0);
3445  res.gry[1] = pmain.gry(1);
3446  } else
3447  for (j = res.j1; j <= res.j2; ++j) {
3448  y = yaxis.GetBinCoord(j + args.middle);
3449  if (pmain.logy && (y <= 0)) { res.j1 = j+1; continue; }
3450  if (res.origy) res.origy[j] = y;
3451  res.gry[j] = pmain.gry(y);
3452  if (args.rounding) res.gry[j] = Math.round(res.gry[j]);
3453 
3454  if (args.use3d) {
3455  if (res.gry[j] < -pmain.size_xy3d) { res.j1 = j; res.gry[j] = -pmain.size_xy3d; }
3456  if (res.gry[j] > pmain.size_xy3d) { res.j2 = j; res.gry[j] = pmain.size_xy3d; }
3457  }
3458  }
3459 
3460  // find min/max values in selected range
3461 
3462  binz = histo.getBinContent(res.i1 + 1, res.j1 + 1);
3463  this.maxbin = this.minbin = this.minposbin = null;
3464 
3465  for (i = res.i1; i < res.i2; ++i) {
3466  for (j = res.j1; j < res.j2; ++j) {
3467  binz = histo.getBinContent(i + 1, j + 1);
3468  res.sumz += binz;
3469  if (args.pixel_density) {
3470  binarea = (res.grx[i+1]-res.grx[i])*(res.gry[j]-res.gry[j+1]);
3471  if (binarea <= 0) continue;
3472  res.max = Math.max(res.max, binz);
3473  if ((binz>0) && ((binz<res.min) || (res.min===0))) res.min = binz;
3474  binz = binz/binarea;
3475  }
3476  if (this.maxbin===null) {
3477  this.maxbin = this.minbin = binz;
3478  } else {
3479  this.maxbin = Math.max(this.maxbin, binz);
3480  this.minbin = Math.min(this.minbin, binz);
3481  }
3482  if (binz > 0)
3483  if ((this.minposbin===null) || (binz<this.minposbin)) this.minposbin = binz;
3484  }
3485  }
3486 
3487  // force recalculation of z levels
3488  this.fContour = null;
3489  this.fCustomContour = false;
3490 
3491  return res;
3492  }
3493 
3494  // ========================================================================
3495 
3505  function TH1Painter(histo) {
3506  THistPainter.call(this, histo);
3507  }
3508 
3509  TH1Painter.prototype = Object.create(THistPainter.prototype);
3510 
3511  TH1Painter.prototype.ConvertTH1K = function() {
3512  var histo = this.GetObject();
3513 
3514  if (histo.fReady) return;
3515 
3516  var arr = histo.fArray; // array of values
3517  histo.fNcells = histo.fXaxis.fNbins + 2;
3518  histo.fArray = new Float64Array(histo.fNcells);
3519  for (var n=0;n<histo.fNcells;++n) histo.fArray[n] = 0;
3520  for (var n=0;n<histo.fNIn;++n) histo.Fill(arr[n]);
3521  histo.fReady = true;
3522  }
3523 
3527  TH1Painter.prototype.ScanContent = function(when_axis_changed) {
3528 
3529  if (when_axis_changed && !this.nbinsx) when_axis_changed = false;
3530 
3531  if (this.IsTH1K()) this.ConvertTH1K();
3532 
3533  var histo = this.GetHisto();
3534 
3535  if (!when_axis_changed) {
3536  this.nbinsx = histo.fXaxis.fNbins;
3537  this.nbinsy = 0;
3538  this.CreateAxisFuncs(false);
3539  }
3540 
3541  var left = this.GetSelectIndex("x", "left"),
3542  right = this.GetSelectIndex("x", "right");
3543 
3544  if (when_axis_changed) {
3545  if ((left === this.scan_xleft) && (right === this.scan_xright)) return;
3546  }
3547 
3548  // Paint histogram axis only
3549  this.draw_content = !(this.options.Axis > 0);
3550 
3551  this.scan_xleft = left;
3552  this.scan_xright = right;
3553 
3554  var hmin = 0, hmin_nz = 0, hmax = 0, hsum = 0, first = true,
3555  profile = this.IsTProfile(), value, err;
3556 
3557  for (var i = 0; i < this.nbinsx; ++i) {
3558  value = histo.getBinContent(i + 1);
3559  hsum += profile ? histo.fBinEntries[i + 1] : value;
3560 
3561  if ((i<left) || (i>=right)) continue;
3562 
3563  if ((value > 0) && ((hmin_nz == 0) || (value < hmin_nz))) hmin_nz = value;
3564 
3565  if (first) {
3566  hmin = hmax = value;
3567  first = false;
3568  }
3569 
3570  err = this.options.Error ? histo.getBinError(i + 1) : 0;
3571 
3572  hmin = Math.min(hmin, value - err);
3573  hmax = Math.max(hmax, value + err);
3574  }
3575 
3576  // account overflow/underflow bins
3577  if (profile)
3578  hsum += histo.fBinEntries[0] + histo.fBinEntries[this.nbinsx + 1];
3579  else
3580  hsum += histo.getBinContent(0) + histo.getBinContent(this.nbinsx + 1);
3581 
3582  this.stat_entries = hsum;
3583  if (histo.fEntries > 1) this.stat_entries = histo.fEntries;
3584 
3585  this.hmin = hmin;
3586  this.hmax = hmax;
3587 
3588  this.ymin_nz = hmin_nz; // value can be used to show optimal log scale
3589 
3590  if ((this.nbinsx == 0) || ((Math.abs(hmin) < 1e-300) && (Math.abs(hmax) < 1e-300)))
3591  this.draw_content = false;
3592 
3593  var set_zoom = false, set_zoom2 = false;
3594 
3595  if (this.draw_content) {
3596  if (hmin >= hmax) {
3597  if (hmin == 0) { this.ymin = 0; this.ymax = 1; }
3598  else if (hmin < 0) { this.ymin = 2 * hmin; this.ymax = 0; }
3599  else { this.ymin = 0; this.ymax = hmin * 2; }
3600  } else {
3601  var dy = (hmax - hmin) * 0.05;
3602  this.ymin = hmin - dy;
3603  if ((this.ymin < 0) && (hmin >= 0)) this.ymin = 0;
3604  this.ymax = hmax + dy;
3605  }
3606  } else if (this.options.ymin !== this.options.ymax) {
3607  this.ymin = this.options.ymin;
3608  this.ymax = this.options.ymax;
3609  set_zoom2 = true;
3610  }
3611 
3612  hmin = this.options.minimum;
3613  hmax = this.options.maximum;
3614 
3615  if ((hmin === hmax) && (hmin !== -1111)) {
3616  if (hmin < 0) {
3617  hmin *= 2; hmax = 0;
3618  } else {
3619  hmin = 0; hmax*=2; if (!hmax) hmax = 1;
3620  }
3621  }
3622 
3623  if ((hmin != -1111) && (hmax != -1111) && !this.draw_content && !set_zoom2) {
3624  this.ymin = hmin;
3625  this.ymax = hmax;
3626  } else {
3627  if (hmin != -1111) {
3628  if (hmin < this.ymin) this.ymin = hmin; else set_zoom = true;
3629  }
3630  if (hmax != -1111) {
3631  if (hmax > this.ymax) this.ymax = hmax; else set_zoom = true;
3632  }
3633  }
3634 
3635  if (!when_axis_changed) {
3636  if (set_zoom && (this.draw_content || set_zoom2)) {
3637  this.zoom_ymin = (hmin == -1111) ? this.ymin : hmin;
3638  this.zoom_ymax = (hmax == -1111) ? this.ymax : hmax;
3639  } else {
3640  delete this.zoom_ymin;
3641  delete this.zoom_ymax;
3642  }
3643  }
3644 
3645  // used in AllowDefaultYZooming
3646  if (this.Dimension() > 1) this.wheel_zoomy = true; else
3647  if (this.draw_content) this.wheel_zoomy = false;
3648  }
3649 
3651  TH1Painter.prototype.CountStat = function(cond) {
3652  var profile = this.IsTProfile(),
3653  histo = this.GetHisto(), xaxis = histo.fXaxis,
3654  left = this.GetSelectIndex("x", "left"),
3655  right = this.GetSelectIndex("x", "right"),
3656  stat_sumw = 0, stat_sumwx = 0, stat_sumwx2 = 0, stat_sumwy = 0, stat_sumwy2 = 0,
3657  i, xx = 0, w = 0, xmax = null, wmax = null,
3658  fp = this.frame_painter(),
3659  res = { name: histo.fName, meanx: 0, meany: 0, rmsx: 0, rmsy: 0, integral: 0, entries: this.stat_entries, xmax:0, wmax:0 };
3660 
3661  for (i = left; i < right; ++i) {
3662  xx = xaxis.GetBinCoord(i + 0.5);
3663 
3664  if (cond && !cond(xx)) continue;
3665 
3666  if (profile) {
3667  w = histo.fBinEntries[i + 1];
3668  stat_sumwy += histo.fArray[i + 1];
3669  stat_sumwy2 += histo.fSumw2[i + 1];
3670  } else {
3671  w = histo.getBinContent(i + 1);
3672  }
3673 
3674  if ((xmax===null) || (w>wmax)) { xmax = xx; wmax = w; }
3675 
3676  stat_sumw += w;
3677  stat_sumwx += w * xx;
3678  stat_sumwx2 += w * xx * xx;
3679  }
3680 
3681  // when no range selection done, use original statistic from histogram
3682  if (!fp.IsAxisZoomed("x") && (histo.fTsumw>0)) {
3683  stat_sumw = histo.fTsumw;
3684  stat_sumwx = histo.fTsumwx;
3685  stat_sumwx2 = histo.fTsumwx2;
3686  }
3687 
3688  res.integral = stat_sumw;
3689 
3690  if (stat_sumw > 0) {
3691  res.meanx = stat_sumwx / stat_sumw;
3692  res.meany = stat_sumwy / stat_sumw;
3693  res.rmsx = Math.sqrt(Math.abs(stat_sumwx2 / stat_sumw - res.meanx * res.meanx));
3694  res.rmsy = Math.sqrt(Math.abs(stat_sumwy2 / stat_sumw - res.meany * res.meany));
3695  }
3696 
3697  if (xmax!==null) {
3698  res.xmax = xmax;
3699  res.wmax = wmax;
3700  }
3701 
3702  return res;
3703  }
3704 
3706  TH1Painter.prototype.FillStatistic = function(stat, dostat, dofit) {
3707 
3708  // no need to refill statistic if histogram is dummy
3709  if (this.IgnoreStatsFill()) return false;
3710 
3711  var histo = this.GetHisto(),
3712  data = this.CountStat(),
3713  print_name = dostat % 10,
3714  print_entries = Math.floor(dostat / 10) % 10,
3715  print_mean = Math.floor(dostat / 100) % 10,
3716  print_rms = Math.floor(dostat / 1000) % 10,
3717  print_under = Math.floor(dostat / 10000) % 10,
3718  print_over = Math.floor(dostat / 100000) % 10,
3719  print_integral = Math.floor(dostat / 1000000) % 10,
3720  print_skew = Math.floor(dostat / 10000000) % 10,
3721  print_kurt = Math.floor(dostat / 100000000) % 10;
3722 
3723  // make empty at the beginning
3724  stat.ClearPave();
3725 
3726  if (print_name > 0)
3727  stat.AddText(data.name);
3728 
3729  if (this.IsTProfile()) {
3730 
3731  if (print_entries > 0)
3732  stat.AddText("Entries = " + stat.Format(data.entries,"entries"));
3733 
3734  if (print_mean > 0) {
3735  stat.AddText("Mean = " + stat.Format(data.meanx));
3736  stat.AddText("Mean y = " + stat.Format(data.meany));
3737  }
3738 
3739  if (print_rms > 0) {
3740  stat.AddText("Std Dev = " + stat.Format(data.rmsx));
3741  stat.AddText("Std Dev y = " + stat.Format(data.rmsy));
3742  }
3743 
3744  } else {
3745 
3746  if (print_entries > 0)
3747  stat.AddText("Entries = " + stat.Format(data.entries, "entries"));
3748 
3749  if (print_mean > 0)
3750  stat.AddText("Mean = " + stat.Format(data.meanx));
3751 
3752  if (print_rms > 0)
3753  stat.AddText("Std Dev = " + stat.Format(data.rmsx));
3754 
3755  if (print_under > 0)
3756  stat.AddText("Underflow = " + stat.Format((histo.fArray.length > 0) ? histo.fArray[0] : 0, "entries"));
3757 
3758  if (print_over > 0)
3759  stat.AddText("Overflow = " + stat.Format((histo.fArray.length > 0) ? histo.fArray[histo.fArray.length - 1] : 0, "entries"));
3760 
3761  if (print_integral > 0)
3762  stat.AddText("Integral = " + stat.Format(data.integral, "entries"));
3763 
3764  if (print_skew > 0)
3765  stat.AddText("Skew = <not avail>");
3766 
3767  if (print_kurt > 0)
3768  stat.AddText("Kurt = <not avail>");
3769  }
3770 
3771  if (dofit) stat.FillFunctionStat(this.FindFunction('TF1'), dofit);
3772 
3773  return true;
3774  }
3775 
3777  TH1Painter.prototype.DrawBars = function(width, height) {
3778 
3779  this.CreateG(true);
3780 
3781  var left = this.GetSelectIndex("x", "left", -1),
3782  right = this.GetSelectIndex("x", "right", 1),
3783  pmain = this.frame_painter(),
3784  pad = this.root_pad(),
3785  histo = this.GetHisto(), xaxis = histo.fXaxis,
3786  pthis = this,
3787  show_text = this.options.Text, text_col, text_angle, text_size,
3788  i, x1, x2, grx1, grx2, y, gry1, gry2, w,
3789  bars = "", barsl = "", barsr = "",
3790  side = (this.options.BarStyle > 10) ? this.options.BarStyle % 10 : 0;
3791 
3792  if (side>4) side = 4;
3793  gry2 = pmain.swap_xy ? 0 : height;
3794  if ((this.options.BaseLine !== false) && !isNaN(this.options.BaseLine))
3795  if (this.options.BaseLine >= pmain.scale_ymin)
3796  gry2 = Math.round(pmain.gry(this.options.BaseLine));
3797 
3798  if (show_text) {
3799  text_col = this.get_color(histo.fMarkerColor);
3800  text_angle = -1*this.options.TextAngle;
3801  text_size = 20;
3802 
3803  if ((histo.fMarkerSize!==1) && text_angle)
3804  text_size = 0.02*height*histo.fMarkerSize;
3805 
3806  this.StartTextDrawing(42, text_size, this.draw_g, text_size);
3807  }
3808 
3809 
3810  for (i = left; i < right; ++i) {
3811  x1 = xaxis.GetBinLowEdge(i+1);
3812  x2 = xaxis.GetBinLowEdge(i+2);
3813 
3814  if (pmain.logx && (x2 <= 0)) continue;
3815 
3816  grx1 = Math.round(pmain.grx(x1));
3817  grx2 = Math.round(pmain.grx(x2));
3818 
3819  y = histo.getBinContent(i+1);
3820  if (pmain.logy && (y < pmain.scale_ymin)) continue;
3821  gry1 = Math.round(pmain.gry(y));
3822 
3823  w = grx2 - grx1;
3824  grx1 += Math.round(histo.fBarOffset/1000*w);
3825  w = Math.round(histo.fBarWidth/1000*w);
3826 
3827  if (pmain.swap_xy)
3828  bars += "M"+gry2+","+grx1 + "h"+(gry1-gry2) + "v"+w + "h"+(gry2-gry1) + "z";
3829  else
3830  bars += "M"+grx1+","+gry1 + "h"+w + "v"+(gry2-gry1) + "h"+(-w)+ "z";
3831 
3832  if (side > 0) {
3833  grx2 = grx1 + w;
3834  w = Math.round(w * side / 10);
3835  if (pmain.swap_xy) {
3836  barsl += "M"+gry2+","+grx1 + "h"+(gry1-gry2) + "v" + w + "h"+(gry2-gry1) + "z";
3837  barsr += "M"+gry2+","+grx2 + "h"+(gry1-gry2) + "v" + (-w) + "h"+(gry2-gry1) + "z";
3838  } else {
3839  barsl += "M"+grx1+","+gry1 + "h"+w + "v"+(gry2-gry1) + "h"+(-w)+ "z";
3840  barsr += "M"+grx2+","+gry1 + "h"+(-w) + "v"+(gry2-gry1) + "h"+w + "z";
3841  }
3842  }
3843 
3844  if (show_text && y) {
3845  var lbl = (y === Math.round(y)) ? y.toString() : JSROOT.FFormat(y, JSROOT.gStyle.fPaintTextFormat);
3846 
3847  if (pmain.swap_xy)
3848  this.DrawText({ align: 12, x: Math.round(gry1 + text_size/2), y: Math.round(grx1+0.1), height: Math.round(w*0.8), text: lbl, color: text_col, latex: 0 });
3849  else if (text_angle)
3850  this.DrawText({ align: 12, x: grx1+w/2, y: Math.round(gry1 - 2 - text_size/5), width: 0, height: 0, rotate: text_angle, text: lbl, color: text_col, latex: 0 });
3851  else
3852  this.DrawText({ align: 22, x: Math.round(grx1 + w*0.1), y: Math.round(gry1-2-text_size), width: Math.round(w*0.8), height: text_size, text: lbl, color: text_col, latex: 0 });
3853  }
3854  }
3855 
3856  if (bars)
3857  this.draw_g.append("svg:path")
3858  .attr("d", bars)
3859  .call(this.fillatt.func);
3860 
3861  if (barsl)
3862  this.draw_g.append("svg:path")
3863  .attr("d", barsl)
3864  .call(this.fillatt.func)
3865  .style("fill", d3.rgb(this.fillatt.color).brighter(0.5).toString());
3866 
3867  if (barsr)
3868  this.draw_g.append("svg:path")
3869  .attr("d", barsr)
3870  .call(this.fillatt.func)
3871  .style("fill", d3.rgb(this.fillatt.color).darker(0.5).toString());
3872 
3873  if (show_text)
3874  this.FinishTextDrawing(this.draw_g);
3875  }
3876 
3877  TH1Painter.prototype.DrawFilledErrors = function(width, height) {
3878  this.CreateG(true);
3879 
3880  var left = this.GetSelectIndex("x", "left", -1),
3881  right = this.GetSelectIndex("x", "right", 1),
3882  pmain = this.frame_painter(),
3883  histo = this.GetHisto(), xaxis = histo.fXaxis,
3884  i, x, grx, y, yerr, gry1, gry2,
3885  bins1 = [], bins2 = [];
3886 
3887  for (i = left; i < right; ++i) {
3888  x = xaxis.GetBinCoord(i+0.5);
3889  if (pmain.logx && (x <= 0)) continue;
3890  grx = Math.round(pmain.grx(x));
3891 
3892  y = histo.getBinContent(i+1);
3893  yerr = histo.getBinError(i+1);
3894  if (pmain.logy && (y-yerr < pmain.scale_ymin)) continue;
3895 
3896  gry1 = Math.round(pmain.gry(y + yerr));
3897  gry2 = Math.round(pmain.gry(y - yerr));
3898 
3899  bins1.push({ grx:grx, gry: gry1 });
3900  bins2.unshift({ grx:grx, gry: gry2 });
3901  }
3902 
3903  var kind = (this.options.ErrorKind === 4) ? "bezier" : "line",
3904  path1 = JSROOT.Painter.BuildSvgPath(kind, bins1),
3905  path2 = JSROOT.Painter.BuildSvgPath("L"+kind, bins2);
3906 
3907  this.draw_g.append("svg:path")
3908  .attr("d", path1.path + path2.path + "Z")
3909  .style("stroke", "none")
3910  .call(this.fillatt.func);
3911  }
3912 
3913  TH1Painter.prototype.DrawBins = function() {
3914  // new method, create svg:path expression ourself directly from histogram
3915  // all points will be used, compress expression when too large
3916 
3917  this.CheckHistDrawAttributes();
3918 
3919  var width = this.frame_width(), height = this.frame_height();
3920 
3921  if (!this.draw_content || (width<=0) || (height<=0))
3922  return this.RemoveDrawG();
3923 
3924  if (this.options.Bar)
3925  return this.DrawBars(width, height);
3926 
3927  if ((this.options.ErrorKind === 3) || (this.options.ErrorKind === 4))
3928  return this.DrawFilledErrors(width, height);
3929 
3930  var left = this.GetSelectIndex("x", "left", -1),
3931  right = this.GetSelectIndex("x", "right", 2),
3932  pmain = this.frame_painter(),
3933  pad = this.root_pad(),
3934  histo = this.GetHisto(),
3935  xaxis = histo.fXaxis,
3936  pthis = this,
3937  res = "", lastbin = false,
3938  startx, currx, curry, x, grx, y, gry, curry_min, curry_max, prevy, prevx, i, bestimin, bestimax,
3939  exclude_zero = !this.options.Zero,
3940  show_errors = this.options.Error,
3941  show_markers = this.options.Mark,
3942  show_line = this.options.Line || this.options.Curve,
3943  show_text = this.options.Text,
3944  text_profile = show_text && (this.options.TextKind == "E") && this.IsTProfile() && histo.fBinEntries,
3945  path_fill = null, path_err = null, path_marker = null, path_line = null,
3946  do_marker = false, do_err = false,
3947  endx = "", endy = "", dend = 0, my, yerr1, yerr2, bincont, binerr, mx1, mx2, midx, mmx1, mmx2,
3948  mpath = "", text_col, text_angle, text_size;
3949 
3950  if (show_errors && !show_markers && (histo.fMarkerStyle > 1))
3951  show_markers = true;
3952 
3953  if (this.options.ErrorKind === 2) {
3954  if (this.fillatt.empty()) show_markers = true;
3955  else path_fill = "";
3956  } else if (this.options.Error) {
3957  path_err = "";
3958  do_err = true;
3959  }
3960 
3961  if (show_line) path_line = "";
3962 
3963  if (show_markers) {
3964  // draw markers also when e2 option was specified
3965  this.createAttMarker({ attr: histo, style: this.options.MarkStyle }); // when style not configured, it will be ignored
3966  if (this.markeratt.size > 0) {
3967  // simply use relative move from point, can optimize in the future
3968  path_marker = "";
3969  do_marker = true;
3970  this.markeratt.reset_pos();
3971  } else {
3972  show_markers = false;
3973  }
3974  }
3975 
3976  var draw_markers = show_errors || show_markers,
3977  draw_any_but_hist = draw_markers || show_text || show_line,
3978  draw_hist = this.options.Hist && (!this.lineatt.empty() || !this.fillatt.empty());
3979 
3980  if (!draw_hist && !draw_any_but_hist)
3981  return this.RemoveDrawG();
3982 
3983  this.CreateG(true);
3984 
3985  if (show_text) {
3986  text_col = this.get_color(histo.fMarkerColor);
3987  text_angle = -1*this.options.TextAngle;
3988  text_size = 20;
3989 
3990  if ((histo.fMarkerSize!==1) && text_angle)
3991  text_size = 0.02*height*histo.fMarkerSize;
3992 
3993  if (!text_angle && !this.options.TextKind) {
3994  var space = width / (right - left + 1);
3995  if (space < 3 * text_size) {
3996  text_angle = 270;
3997  text_size = Math.round(space*0.7);
3998  }
3999  }
4000 
4001  this.StartTextDrawing(42, text_size, this.draw_g, text_size);
4002  }
4003 
4004  // if there are too many points, exclude many vertical drawings at the same X position
4005  // instead define min and max value and made min-max drawing
4006  var use_minmax = ((right-left) > 3*width);
4007 
4008  if (this.options.ErrorKind === 1) {
4009  var lw = this.lineatt.width + JSROOT.gStyle.fEndErrorSize;
4010  endx = "m0," + lw + "v-" + 2*lw + "m0," + lw;
4011  endy = "m" + lw + ",0h-" + 2*lw + "m" + lw + ",0";
4012  dend = Math.floor((this.lineatt.width-1)/2);
4013  }
4014 
4015  if (draw_any_but_hist) use_minmax = true;
4016 
4017  // just to get correct values for the specified bin
4018  function extract_bin(bin) {
4019  bincont = histo.getBinContent(bin+1);
4020  if (exclude_zero && (bincont===0)) return false;
4021  mx1 = Math.round(pmain.grx(xaxis.GetBinLowEdge(bin+1)));
4022  mx2 = Math.round(pmain.grx(xaxis.GetBinLowEdge(bin+2)));
4023  midx = Math.round((mx1+mx2)/2);
4024  my = Math.round(pmain.gry(bincont));
4025  yerr1 = yerr2 = 20;
4026  if (show_errors) {
4027  binerr = histo.getBinError(bin+1);
4028  yerr1 = Math.round(my - pmain.gry(bincont + binerr)); // up
4029  yerr2 = Math.round(pmain.gry(bincont - binerr) - my); // down
4030  }
4031  return true;
4032  }
4033 
4034  function draw_errbin(bin) {
4035  if (pthis.options.errorX > 0) {
4036  mmx1 = Math.round(midx - (mx2-mx1)*pthis.options.errorX);
4037  mmx2 = Math.round(midx + (mx2-mx1)*pthis.options.errorX);
4038  path_err += "M" + (mmx1+dend) +","+ my + endx + "h" + (mmx2-mmx1-2*dend) + endx;
4039  }
4040  path_err += "M" + midx +"," + (my-yerr1+dend) + endy + "v" + (yerr1+yerr2-2*dend) + endy;
4041  }
4042 
4043  function draw_bin(bin) {
4044  if (extract_bin(bin)) {
4045  if (show_text) {
4046  var cont = text_profile ? histo.fBinEntries[bin+1] : bincont;
4047 
4048  if (cont!==0) {
4049  var lbl = (cont === Math.round(cont)) ? cont.toString() : JSROOT.FFormat(cont, JSROOT.gStyle.fPaintTextFormat);
4050 
4051  if (text_angle)
4052  pthis.DrawText({ align: 12, x: midx, y: Math.round(my - 2 - text_size/5), width: 0, height: 0, rotate: text_angle, text: lbl, color: text_col, latex: 0 });
4053  else
4054  pthis.DrawText({ align: 22, x: Math.round(mx1 + (mx2-mx1)*0.1), y: Math.round(my-2-text_size), width: Math.round((mx2-mx1)*0.8), height: text_size, text: lbl, color: text_col, latex: 0 });
4055  }
4056  }
4057 
4058  if (show_line && (path_line !== null))
4059  path_line += ((path_line.length===0) ? "M" : "L") + midx + "," + my;
4060 
4061  if (draw_markers) {
4062  if ((my >= -yerr1) && (my <= height + yerr2)) {
4063  if (path_fill !== null)
4064  path_fill += "M" + mx1 +","+(my-yerr1) +
4065  "h" + (mx2-mx1) + "v" + (yerr1+yerr2+1) + "h-" + (mx2-mx1) + "z";
4066  if ((path_marker !== null) && do_marker)
4067  path_marker += pthis.markeratt.create(midx, my);
4068  if ((path_err !== null) && do_err)
4069  draw_errbin(bin);
4070  }
4071  }
4072  }
4073  }
4074 
4075  // check if we should draw markers or error marks directly, skipping optimization
4076  if (do_marker || do_err)
4077  if (!JSROOT.gStyle.OptimizeDraw || ((right-left<50000) && (JSROOT.gStyle.OptimizeDraw==1))) {
4078  for (i = left; i < right; ++i) {
4079  if (extract_bin(i)) {
4080  if (path_marker !== null)
4081  path_marker += pthis.markeratt.create(midx, my);
4082  if (path_err !== null)
4083  draw_errbin(i);
4084  }
4085  }
4086  do_err = do_marker = false;
4087  }
4088 
4089 
4090  for (i = left; i <= right; ++i) {
4091 
4092  x = xaxis.GetBinLowEdge(i+1);
4093 
4094  if (this.logx && (x <= 0)) continue;
4095 
4096  grx = Math.round(pmain.grx(x));
4097 
4098  lastbin = (i === right);
4099 
4100  if (lastbin && (left<right)) {
4101  gry = curry;
4102  } else {
4103  y = histo.getBinContent(i+1);
4104  gry = Math.round(pmain.gry(y));
4105  }
4106 
4107  if (res.length === 0) {
4108  bestimin = bestimax = i;
4109  prevx = startx = currx = grx;
4110  prevy = curry_min = curry_max = curry = gry;
4111  res = "M"+currx+","+curry;
4112  } else if (use_minmax) {
4113  if ((grx === currx) && !lastbin) {
4114  if (gry < curry_min) bestimax = i; else
4115  if (gry > curry_max) bestimin = i;
4116 
4117  curry_min = Math.min(curry_min, gry);
4118  curry_max = Math.max(curry_max, gry);
4119  curry = gry;
4120  } else {
4121 
4122  if (draw_any_but_hist) {
4123  if (bestimin === bestimax) { draw_bin(bestimin); } else
4124  if (bestimin < bestimax) { draw_bin(bestimin); draw_bin(bestimax); } else {
4125  draw_bin(bestimax); draw_bin(bestimin);
4126  }
4127  }
4128 
4129  // when several points at same X differs, need complete logic
4130  if (draw_hist && ((curry_min !== curry_max) || (prevy !== curry_min))) {
4131 
4132  if (prevx !== currx)
4133  res += "h"+(currx-prevx);
4134 
4135  if (curry === curry_min) {
4136  if (curry_max !== prevy)
4137  res += "v" + (curry_max - prevy);
4138  if (curry_min !== curry_max)
4139  res += "v" + (curry_min - curry_max);
4140  } else {
4141  if (curry_min !== prevy)
4142  res += "v" + (curry_min - prevy);
4143  if (curry_max !== curry_min)
4144  res += "v" + (curry_max - curry_min);
4145  if (curry !== curry_max)
4146  res += "v" + (curry - curry_max);
4147  }
4148 
4149  prevx = currx;
4150  prevy = curry;
4151  }
4152 
4153  if (lastbin && (prevx !== grx))
4154  res += "h"+(grx-prevx);
4155 
4156  bestimin = bestimax = i;
4157  curry_min = curry_max = curry = gry;
4158  currx = grx;
4159  }
4160  // end of use_minmax
4161  } else if ((gry !== curry) || lastbin) {
4162  if (grx !== currx) res += "h"+(grx-currx);
4163  if (gry !== curry) res += "v"+(gry-curry);
4164  curry = gry;
4165  currx = grx;
4166  }
4167  }
4168 
4169  var close_path = "";
4170  if (!this.fillatt.empty()) {
4171  var h0 = height + 3, gry0 = Math.round(pmain.gry(0));
4172  if (gry0 <= 0) h0 = -3; else if (gry0 < height) h0 = gry0;
4173  close_path = "L"+currx+","+h0 + "L"+startx+","+h0 + "Z";
4174  if (res.length>0) res += close_path;
4175  }
4176 
4177  if (draw_markers || show_line) {
4178  if ((path_fill !== null) && (path_fill.length > 0))
4179  this.draw_g.append("svg:path")
4180  .attr("d", path_fill)
4181  .call(this.fillatt.func);
4182 
4183  if ((path_err !== null) && (path_err.length > 0))
4184  this.draw_g.append("svg:path")
4185  .attr("d", path_err)
4186  .call(this.lineatt.func);
4187 
4188  if ((path_line !== null) && (path_line.length > 0)) {
4189  if (!this.fillatt.empty())
4190  this.draw_g.append("svg:path")
4191  .attr("d", this.options.Fill ? (path_line + close_path) : res)
4192  .attr("stroke", "none")
4193  .call(this.fillatt.func);
4194 
4195  this.draw_g.append("svg:path")
4196  .attr("d", path_line)
4197  .attr("fill", "none")
4198  .call(this.lineatt.func);
4199  }
4200 
4201  if ((path_marker !== null) && (path_marker.length > 0))
4202  this.draw_g.append("svg:path")
4203  .attr("d", path_marker)
4204  .call(this.markeratt.func);
4205 
4206  }
4207 
4208  if ((res.length > 0) && draw_hist) {
4209  this.draw_g.append("svg:path")
4210  .attr("d", res)
4211  .style("stroke-linejoin","miter")
4212  .call(this.lineatt.func)
4213  .call(this.fillatt.func);
4214  }
4215 
4216  if (show_text)
4217  this.FinishTextDrawing(this.draw_g);
4218 
4219  }
4220 
4221  TH1Painter.prototype.GetBinTips = function(bin) {
4222  var tips = [],
4223  name = this.GetTipName(),
4224  pmain = this.frame_painter(),
4225  histo = this.GetHisto(),
4226  x1 = histo.fXaxis.GetBinLowEdge(bin+1),
4227  x2 = histo.fXaxis.GetBinLowEdge(bin+2),
4228  cont = histo.getBinContent(bin+1),
4229  xlbl = "", xnormal = false;
4230 
4231  if (name.length>0) tips.push(name);
4232 
4233  if (pmain.x_kind === 'labels') xlbl = pmain.AxisAsText("x", x1); else
4234  if (pmain.x_kind === 'time') xlbl = pmain.AxisAsText("x", (x1+x2)/2); else
4235  { xnormal = true; xlbl = "[" + pmain.AxisAsText("x", x1) + ", " + pmain.AxisAsText("x", x2) + ")"; }
4236 
4237  if (this.options.Error || this.options.Mark) {
4238  tips.push("x = " + xlbl);
4239  tips.push("y = " + pmain.AxisAsText("y", cont));
4240  if (this.options.Error) {
4241  if (xnormal) tips.push("error x = " + ((x2 - x1) / 2).toPrecision(4));
4242  tips.push("error y = " + histo.getBinError(bin + 1).toPrecision(4));
4243  }
4244  } else {
4245  tips.push("bin = " + (bin+1));
4246  tips.push("x = " + xlbl);
4247  if (histo['$baseh']) cont -= histo['$baseh'].getBinContent(bin+1);
4248  if (cont === Math.round(cont))
4249  tips.push("entries = " + cont);
4250  else
4251  tips.push("entries = " + JSROOT.FFormat(cont, JSROOT.gStyle.fStatFormat));
4252  }
4253 
4254  return tips;
4255  }
4256 
4258  TH1Painter.prototype.ProcessTooltip = function(pnt) {
4259  if ((pnt === null) || !this.draw_content || !this.draw_g || this.options.Mode3D) {
4260  if (this.draw_g !== null)
4261  this.draw_g.select(".tooltip_bin").remove();
4262  return null;
4263  }
4264 
4265  var width = this.frame_width(),
4266  height = this.frame_height(),
4267  pmain = this.frame_painter(),
4268  pad = this.root_pad(),
4269  painter = this,
4270  histo = this.GetHisto(),
4271  findbin = null, show_rect = true,
4272  grx1, midx, grx2, gry1, midy, gry2, gapx = 2,
4273  left = this.GetSelectIndex("x", "left", -1),
4274  right = this.GetSelectIndex("x", "right", 2),
4275  l = left, r = right, pnt_x = pnt.x, pnt_y = pnt.y;
4276 
4277  function GetBinGrX(i) {
4278  var xx = histo.fXaxis.GetBinLowEdge(i+1);
4279  return (pmain.logx && (xx<=0)) ? null : pmain.grx(xx);
4280  }
4281 
4282  function GetBinGrY(i) {
4283  var yy = histo.getBinContent(i + 1);
4284  if (pmain.logy && (yy < pmain.scale_ymin))
4285  return pmain.swap_xy ? -1000 : 10*height;
4286  return Math.round(pmain.gry(yy));
4287  }
4288 
4289  if (pmain.swap_xy) {
4290  var d = pnt.x; pnt_x = pnt_y; pnt_y = d;
4291  d = height; height = width; width = d;
4292  }
4293 
4294  while (l < r-1) {
4295  var m = Math.round((l+r)*0.5), xx = GetBinGrX(m);
4296  if ((xx === null) || (xx < pnt_x - 0.5)) {
4297  if (pmain.swap_xy) r = m; else l = m;
4298  } else if (xx > pnt_x + 0.5) {
4299  if (pmain.swap_xy) l = m; else r = m;
4300  } else { l++; r--; }
4301  }
4302 
4303  findbin = r = l;
4304  grx1 = GetBinGrX(findbin);
4305 
4306  if (pmain.swap_xy) {
4307  while ((l>left) && (GetBinGrX(l-1) < grx1 + 2)) --l;
4308  while ((r<right) && (GetBinGrX(r+1) > grx1 - 2)) ++r;
4309  } else {
4310  while ((l>left) && (GetBinGrX(l-1) > grx1 - 2)) --l;
4311  while ((r<right) && (GetBinGrX(r+1) < grx1 + 2)) ++r;
4312  }
4313 
4314  if (l < r) {
4315  // many points can be assigned with the same cursor position
4316  // first try point around mouse y
4317  var best = height;
4318  for (var m=l;m<=r;m++) {
4319  var dist = Math.abs(GetBinGrY(m) - pnt_y);
4320  if (dist < best) { best = dist; findbin = m; }
4321  }
4322 
4323  // if best distance still too far from mouse position, just take from between
4324  if (best > height/10)
4325  findbin = Math.round(l + (r-l) / height * pnt_y);
4326 
4327  grx1 = GetBinGrX(findbin);
4328  }
4329 
4330  grx1 = Math.round(grx1);
4331  grx2 = Math.round(GetBinGrX(findbin+1));
4332 
4333  if (this.options.Bar) {
4334  var w = grx2 - grx1;
4335  grx1 += Math.round(histo.fBarOffset/1000*w);
4336  grx2 = grx1 + Math.round(histo.fBarWidth/1000*w);
4337  }
4338 
4339  if (grx1 > grx2) { var d = grx1; grx1 = grx2; grx2 = d; }
4340 
4341  midx = Math.round((grx1+grx2)/2);
4342 
4343  midy = gry1 = gry2 = GetBinGrY(findbin);
4344 
4345  if (this.options.Bar) {
4346  show_rect = true;
4347 
4348  gapx = 0;
4349 
4350  gry1 = Math.round(pmain.gry(((this.options.BaseLine!==false) && (this.options.BaseLine > pmain.scale_ymin)) ? this.options.BaseLine : pmain.scale_ymin));
4351 
4352  if (gry1 > gry2) { var d = gry1; gry1 = gry2; gry2 = d; }
4353 
4354  if (!pnt.touch && (pnt.nproc === 1))
4355  if ((pnt_y<gry1) || (pnt_y>gry2)) findbin = null;
4356 
4357  } else if (this.options.Error || this.options.Mark || this.options.Line || this.options.Curve) {
4358 
4359  show_rect = true;
4360 
4361  var msize = 3;
4362  if (this.markeratt) msize = Math.max(msize, this.markeratt.GetFullSize());
4363 
4364  if (this.options.Error) {
4365  var cont = histo.getBinContent(findbin+1),
4366  binerr = histo.getBinError(findbin+1);
4367 
4368  gry1 = Math.round(pmain.gry(cont + binerr)); // up
4369  gry2 = Math.round(pmain.gry(cont - binerr)); // down
4370 
4371  if ((cont==0) && this.IsTProfile()) findbin = null;
4372 
4373  var dx = (grx2-grx1)*this.options.errorX;
4374  grx1 = Math.round(midx - dx);
4375  grx2 = Math.round(midx + dx);
4376  }
4377 
4378  // show at least 6 pixels as tooltip rect
4379  if (grx2 - grx1 < 2*msize) { grx1 = midx-msize; grx2 = midx+msize; }
4380 
4381  gry1 = Math.min(gry1, midy - msize);
4382  gry2 = Math.max(gry2, midy + msize);
4383 
4384  if (!pnt.touch && (pnt.nproc === 1))
4385  if ((pnt_y<gry1) || (pnt_y>gry2)) findbin = null;
4386 
4387  } else {
4388 
4389  // if histogram alone, use old-style with rects
4390  // if there are too many points at pixel, use circle
4391  show_rect = (pnt.nproc === 1) && (right-left < width);
4392 
4393  if (show_rect) {
4394  gry2 = height;
4395 
4396  if (!this.fillatt.empty()) {
4397  gry2 = Math.round(pmain.gry(0));
4398  if (gry2 < 0) gry2 = 0; else if (gry2 > height) gry2 = height;
4399  if (gry2 < gry1) { var d = gry1; gry1 = gry2; gry2 = d; }
4400  }
4401 
4402  // for mouse events pointer should be between y1 and y2
4403  if (((pnt.y < gry1) || (pnt.y > gry2)) && !pnt.touch) findbin = null;
4404  }
4405  }
4406 
4407  if (findbin!==null) {
4408  // if bin on boundary found, check that x position is ok
4409  if ((findbin === left) && (grx1 > pnt_x + gapx)) findbin = null; else
4410  if ((findbin === right-1) && (grx2 < pnt_x - gapx)) findbin = null; else
4411  // if bars option used check that bar is not match
4412  if ((pnt_x < grx1 - gapx) || (pnt_x > grx2 + gapx)) findbin = null; else
4413  // exclude empty bin if empty bins suppressed
4414  if (!this.options.Zero && (histo.getBinContent(findbin+1)===0)) findbin = null;
4415  }
4416 
4417  var ttrect = this.draw_g.select(".tooltip_bin");
4418 
4419  if ((findbin === null) || ((gry2 <= 0) || (gry1 >= height))) {
4420  ttrect.remove();
4421  return null;
4422  }
4423 
4424  var res = { name: histo.fName, title: histo.fTitle,
4425  x: midx, y: midy, exact: true,
4426  color1: this.lineatt ? this.lineatt.color : 'green',
4427  color2: this.fillatt ? this.fillatt.fillcoloralt('blue') : 'blue',
4428  lines: this.GetBinTips(findbin) };
4429 
4430  if (pnt.disabled) {
4431  // case when tooltip should not highlight bin
4432 
4433  ttrect.remove();
4434  res.changed = true;
4435  } else
4436  if (show_rect) {
4437 
4438  if (ttrect.empty())
4439  ttrect = this.draw_g.append("svg:rect")
4440  .attr("class","tooltip_bin h1bin")
4441  .style("pointer-events","none");
4442 
4443  res.changed = ttrect.property("current_bin") !== findbin;
4444 
4445  if (res.changed)
4446  ttrect.attr("x", pmain.swap_xy ? gry1 : grx1)
4447  .attr("width", pmain.swap_xy ? gry2-gry1 : grx2-grx1)
4448  .attr("y", pmain.swap_xy ? grx1 : gry1)
4449  .attr("height", pmain.swap_xy ? grx2-grx1 : gry2-gry1)
4450  .style("opacity", "0.3")
4451  .property("current_bin", findbin);
4452 
4453  res.exact = (Math.abs(midy - pnt_y) <= 5) || ((pnt_y>=gry1) && (pnt_y<=gry2));
4454 
4455  res.menu = true; // one could show context menu
4456  // distance to middle point, use to decide which menu to activate
4457  res.menu_dist = Math.sqrt((midx-pnt_x)*(midx-pnt_x) + (midy-pnt_y)*(midy-pnt_y));
4458 
4459  } else {
4460  var radius = this.lineatt.width + 3;
4461 
4462  if (ttrect.empty())
4463  ttrect = this.draw_g.append("svg:circle")
4464  .attr("class","tooltip_bin")
4465  .style("pointer-events","none")
4466  .attr("r", radius)
4467  .call(this.lineatt.func)
4468  .call(this.fillatt.func);
4469 
4470  res.exact = (Math.abs(midx - pnt.x) <= radius) && (Math.abs(midy - pnt.y) <= radius);
4471 
4472  res.menu = res.exact; // show menu only when mouse pointer exactly over the histogram
4473  res.menu_dist = Math.sqrt((midx-pnt.x)*(midx-pnt.x) + (midy-pnt.y)*(midy-pnt.y));
4474 
4475  res.changed = ttrect.property("current_bin") !== findbin;
4476 
4477  if (res.changed)
4478  ttrect.attr("cx", midx)
4479  .attr("cy", midy)
4480  .property("current_bin", findbin);
4481  }
4482 
4483  if (res.changed)
4484  res.user_info = { obj: histo, name: histo.fName,
4485  bin: findbin, cont: histo.getBinContent(findbin+1),
4486  grx: midx, gry: midy };
4487 
4488  return res;
4489  }
4490 
4491  TH1Painter.prototype.FillHistContextMenu = function(menu) {
4492 
4493  menu.add("Auto zoom-in", this.AutoZoom);
4494 
4495  var sett = JSROOT.getDrawSettings("ROOT." + this.GetObject()._typename, 'nosame');
4496 
4497  menu.addDrawMenu("Draw with", sett.opts, function(arg) {
4498  if (arg==='inspect')
4499  return this.ShowInspector();
4500 
4501  this.DecodeOptions(arg);
4502 
4503  if (this.options.need_fillcol && this.fillatt && this.fillatt.empty())
4504  this.fillatt.Change(5,1001);
4505 
4506  // redraw all objects in pad, inform dependent objects
4507  this.InteractiveRedraw("pad", "drawopt");
4508  });
4509  }
4510 
4511  TH1Painter.prototype.AutoZoom = function() {
4512  var left = this.GetSelectIndex("x", "left", -1),
4513  right = this.GetSelectIndex("x", "right", 1),
4514  dist = right - left, histo = this.GetHisto();
4515 
4516  if ((dist == 0) || !histo) return;
4517 
4518  // first find minimum
4519  var min = histo.getBinContent(left + 1);
4520  for (var indx = left; indx < right; ++indx)
4521  min = Math.min(min, histo.getBinContent(indx+1));
4522  if (min > 0) return; // if all points positive, no chance for autoscale
4523 
4524  while ((left < right) && (histo.getBinContent(left+1) <= min)) ++left;
4525  while ((left < right) && (histo.getBinContent(right) <= min)) --right;
4526 
4527  // if singular bin
4528  if ((left === right-1) && (left > 2) && (right < this.nbinsx-2)) {
4529  --left; ++right;
4530  }
4531 
4532  if ((right - left < dist) && (left < right))
4533  this.frame_painter().Zoom(histo.fXaxis.GetBinLowEdge(left+1), histo.fXaxis.GetBinLowEdge(right+1));
4534  }
4535 
4536  TH1Painter.prototype.CanZoomIn = function(axis,min,max) {
4537  var histo = this.GetHisto();
4538 
4539  if ((axis=="x") && histo && (histo.fXaxis.FindBin(max,0.5) - histo.fXaxis.FindBin(min,0) > 1)) return true;
4540 
4541  if ((axis=="y") && (Math.abs(max-min) > Math.abs(this.ymax-this.ymin)*1e-6)) return true;
4542 
4543  // check if it makes sense to zoom inside specified axis range
4544  return false;
4545  }
4546 
4547  TH1Painter.prototype.CallDrawFunc = function(callback, resize) {
4548 
4549  var main = this.main_painter(),
4550  fp = this.frame_painter();
4551 
4552  if ((main!==this) && fp && (fp.mode3d !== this.options.Mode3D))
4553  this.CopyOptionsFrom(main);
4554 
4555  var funcname = this.options.Mode3D ? "Draw3D" : "Draw2D";
4556 
4557  this[funcname](callback, resize);
4558  }
4559 
4560  TH1Painter.prototype.Draw2D = function(call_back) {
4561  this.Clear3DScene();
4562  this.mode3d = false;
4563 
4564  this.ScanContent(true);
4565 
4566  this.CreateXY();
4567 
4568  if (typeof this.DrawColorPalette === 'function')
4569  this.DrawColorPalette(false);
4570 
4571  this.DrawAxes();
4572  this.DrawBins();
4573  this.DrawTitle();
4574  this.UpdateStatWebCanvas();
4575  this.AddInteractive();
4576  JSROOT.CallBack(call_back);
4577  }
4578 
4579  TH1Painter.prototype.Draw3D = function(call_back, resize) {
4580  this.mode3d = true;
4581  JSROOT.AssertPrerequisites('hist3d', function() {
4582  this.Draw3D(call_back, resize);
4583  }.bind(this));
4584  }
4585 
4586  TH1Painter.prototype.Redraw = function(resize) {
4587  this.CallDrawFunc(null, resize);
4588  }
4589 
4590  function drawHistogram1D(divid, histo, opt) {
4591  // create painter and add it to canvas
4592  var painter = new TH1Painter(histo);
4593 
4594  painter.SetDivId(divid, 1);
4595 
4596  // here we deciding how histogram will look like and how will be shown
4597  painter.DecodeOptions(opt);
4598 
4599  painter.CheckPadRange(!painter.options.Mode3D);
4600 
4601  painter.ScanContent();
4602 
4603  painter.CreateStat(); // only when required
4604 
4605  painter.CallDrawFunc(function() {
4606  painter.DrawNextFunction(0, function() {
4607  if (!painter.options.Mode3D && painter.options.AutoZoom) painter.AutoZoom();
4608  painter.FillToolbar();
4609  painter.DrawingReady();
4610  });
4611  });
4612 
4613  return painter;
4614  }
4615 
4616  // ========================================================================
4617 
4627  function TH2Painter(histo) {
4628  THistPainter.call(this, histo);
4629  this.fContour = null; // contour levels
4630  this.fCustomContour = false; // are this user-defined levels (can be irregular)
4631  this.fPalette = null;
4632  this.wheel_zoomy = true;
4633  }
4634 
4635  TH2Painter.prototype = Object.create(THistPainter.prototype);
4636 
4637  TH2Painter.prototype.Cleanup = function() {
4638  delete this.fCustomContour;
4639  delete this.tt_handle;
4640 
4641  THistPainter.prototype.Cleanup.call(this);
4642  }
4643 
4644  TH2Painter.prototype.ToggleProjection = function(kind, width) {
4645 
4646  if ((kind=="Projections") || (kind=="Off")) kind = "";
4647 
4648  if ((typeof kind == 'string') && (kind.length>1)) {
4649  width = parseInt(kind.substr(1));
4650  kind = kind[0];
4651  }
4652 
4653  if (!width) width = 1;
4654 
4655  if (kind && (this.is_projection==kind)) {
4656  if (this.projection_width === width) {
4657  kind = "";
4658  } else {
4659  this.projection_width = width;
4660  return;
4661  }
4662  }
4663 
4664  delete this.proj_hist;
4665 
4666  var new_proj = (this.is_projection === kind) ? "" : kind;
4667  this.is_projection = ""; // disable projection redraw until callback
4668  this.projection_width = width;
4669 
4670  var canp = this.canv_painter();
4671  if (canp) canp.ToggleProjection(new_proj, this.RedrawProjection.bind(this, "toggling", new_proj));
4672  }
4673 
4674  TH2Painter.prototype.RedrawProjection = function(ii1, ii2, jj1, jj2) {
4675  if (ii1 === "toggling") {
4676  this.is_projection = ii2;
4677  ii1 = ii2 = undefined;
4678  }
4679  if (!this.is_projection) return;
4680 
4681  if (jj2 == undefined) {
4682  if (!this.tt_handle) return;
4683  ii1 = Math.round((this.tt_handle.i1 + this.tt_handle.i2)/2); ii2 = ii1+1;
4684  jj1 = Math.round((this.tt_handle.j1 + this.tt_handle.j2)/2); jj2 = jj1+1;
4685  }
4686 
4687  var canp = this.canv_painter(), histo = this.GetHisto();
4688 
4689  if (canp && !canp._readonly && (this.snapid !== undefined)) {
4690  // this is when projection should be created on the server side
4691  var exec = "EXECANDSEND:D" + this.is_projection + "PROJ:" + this.snapid + ":";
4692  if (this.is_projection == "X")
4693  exec += 'ProjectionX("_projx",' + (jj1+1) + ',' + jj2 + ',"")';
4694  else
4695  exec += 'ProjectionY("_projy",' + (ii1+1) + ',' + ii2 + ',"")';
4696  canp.SendWebsocket(exec);
4697  return;
4698  }
4699 
4700  if (!this.proj_hist) {
4701  if (this.is_projection == "X") {
4702  this.proj_hist = JSROOT.CreateHistogram("TH1D", this.nbinsx);
4703  JSROOT.extend(this.proj_hist.fXaxis, histo.fXaxis);
4704  this.proj_hist.fName = "xproj";
4705  this.proj_hist.fTitle = "X projection";
4706  } else {
4707  this.proj_hist = JSROOT.CreateHistogram("TH1D", this.nbinsy);
4708  JSROOT.extend(this.proj_hist.fXaxis, histo.fYaxis);
4709  this.proj_hist.fName = "yproj";
4710  this.proj_hist.fTitle = "Y projection";
4711  }
4712  }
4713 
4714  if (this.is_projection == "X") {
4715  for (var i=0;i<this.nbinsx;++i) {
4716  var sum=0;
4717  for (var j=jj1;j<jj2;++j) sum += histo.getBinContent(i+1,j+1);
4718  this.proj_hist.setBinContent(i+1, sum);
4719  }
4720  } else {
4721  for (var j=0;j<this.nbinsy;++j) {
4722  var sum = 0;
4723  for (var i=ii1;i<ii2;++i) sum += histo.getBinContent(i+1,j+1);
4724  this.proj_hist.setBinContent(j+1, sum);
4725  }
4726  }
4727 
4728  return canp.DrawProjection(this.is_projection, this.proj_hist);
4729  }
4730 
4731  TH2Painter.prototype.ExecuteMenuCommand = function(method, args) {
4732  if (THistPainter.prototype.ExecuteMenuCommand.call(this,method, args)) return true;
4733 
4734  if ((method.fName == 'SetShowProjectionX') || (method.fName == 'SetShowProjectionY')) {
4735  this.ToggleProjection(method.fName[17], args && parseInt(args) ? parseInt(args) : 1);
4736  return true;
4737  }
4738 
4739  return false;
4740  }
4741 
4742  TH2Painter.prototype.FillHistContextMenu = function(menu) {
4743  // painter automatically bind to menu callbacks
4744 
4745  if (!this.IsTH2Poly()) {
4746  menu.add("sub:Projections", this.ToggleProjection);
4747  var kind = this.is_projection || "";
4748  if (kind) kind += this.projection_width;
4749  var kinds = ["X1", "X2", "X3", "X5", "X10", "Y1", "Y2", "Y3", "Y5", "Y10"];
4750  if (this.is_projection) kinds.push("Off");
4751  for (var k=0;k<kinds.length;++k)
4752  menu.addchk(kind==kinds[k], kinds[k], kinds[k], this.ToggleProjection);
4753  menu.add("endsub:");
4754 
4755  menu.add("Auto zoom-in", this.AutoZoom);
4756  }
4757 
4758  var sett = JSROOT.getDrawSettings("ROOT." + this.GetObject()._typename, 'nosame');
4759 
4760  menu.addDrawMenu("Draw with", sett.opts, function(arg) {
4761  if (arg==='inspect')
4762  return this.ShowInspector();
4763  this.DecodeOptions(arg);
4764  this.InteractiveRedraw("pad", "drawopt");
4765  });
4766 
4767  if (this.options.Color)
4768  this.FillPaletteMenu(menu);
4769  }
4770 
4771  TH2Painter.prototype.ButtonClick = function(funcname) {
4772  if (THistPainter.prototype.ButtonClick.call(this, funcname)) return true;
4773 
4774  if (this !== this.main_painter()) return false;
4775 
4776  switch(funcname) {
4777  case "ToggleColor": this.ToggleColor(); break;
4778  case "ToggleColorZ": this.ToggleColz(); break;
4779  case "Toggle3D": this.ToggleMode3D(); break;
4780  default: return false;
4781  }
4782 
4783  // all methods here should not be processed further
4784  return true;
4785  }
4786 
4787  TH2Painter.prototype.FillToolbar = function() {
4788  THistPainter.prototype.FillToolbar.call(this, true);
4789 
4790  var pp = this.pad_painter();
4791  if (!pp) return;
4792 
4793  if (!this.IsTH2Poly())
4794  pp.AddButton(JSROOT.ToolbarIcons.th2color, "Toggle color", "ToggleColor");
4795  pp.AddButton(JSROOT.ToolbarIcons.th2colorz, "Toggle color palette", "ToggleColorZ");
4796  pp.AddButton(JSROOT.ToolbarIcons.th2draw3d, "Toggle 3D mode", "Toggle3D");
4797  pp.ShowButtons();
4798  }
4799 
4800  TH2Painter.prototype.ToggleColor = function() {
4801 
4802  if (this.options.Mode3D) {
4803  this.options.Mode3D = false;
4804  this.options.Color = true;
4805  } else {
4806  this.options.Color = !this.options.Color;
4807  }
4808 
4809  this._can_move_colz = true; // indicate that next redraw can move Z scale
4810 
4811  this.CopyOptionsToOthers();
4812 
4813  this.RedrawPad();
4814 
4815  // this.DrawColorPalette(this.options.Color && this.options.Zscale);
4816  }
4817 
4818  TH2Painter.prototype.AutoZoom = function() {
4819  if (this.IsTH2Poly()) return; // not implemented
4820 
4821  var i1 = this.GetSelectIndex("x", "left", -1),
4822  i2 = this.GetSelectIndex("x", "right", 1),
4823  j1 = this.GetSelectIndex("y", "left", -1),
4824  j2 = this.GetSelectIndex("y", "right", 1),
4825  i,j, histo = this.GetObject();
4826 
4827  if ((i1 == i2) || (j1 == j2)) return;
4828 
4829  // first find minimum
4830  var min = histo.getBinContent(i1 + 1, j1 + 1);
4831  for (i = i1; i < i2; ++i)
4832  for (j = j1; j < j2; ++j)
4833  min = Math.min(min, histo.getBinContent(i+1, j+1));
4834  if (min > 0) return; // if all points positive, no chance for autoscale
4835 
4836  var ileft = i2, iright = i1, jleft = j2, jright = j1;
4837 
4838  for (i = i1; i < i2; ++i)
4839  for (j = j1; j < j2; ++j)
4840  if (histo.getBinContent(i + 1, j + 1) > min) {
4841  if (i < ileft) ileft = i;
4842  if (i >= iright) iright = i + 1;
4843  if (j < jleft) jleft = j;
4844  if (j >= jright) jright = j + 1;
4845  }
4846 
4847  var xmin, xmax, ymin, ymax, isany = false;
4848 
4849  if ((ileft === iright-1) && (ileft > i1+1) && (iright < i2-1)) { ileft--; iright++; }
4850  if ((jleft === jright-1) && (jleft > j1+1) && (jright < j2-1)) { jleft--; jright++; }
4851 
4852  if ((ileft > i1 || iright < i2) && (ileft < iright - 1)) {
4853  xmin = histo.fXaxis.GetBinLowEdge(ileft+1);
4854  xmax = histo.fXaxis.GetBinLowEdge(iright+1);
4855  isany = true;
4856  }
4857 
4858  if ((jleft > j1 || jright < j2) && (jleft < jright - 1)) {
4859  ymin = histo.fYaxis.GetBinLowEdge(jleft+1);
4860  ymax = histo.fYaxis.GetBinLowEdge(jright+1);
4861  isany = true;
4862  }
4863 
4864  if (isany)
4865  this.frame_painter().Zoom(xmin, xmax, ymin, ymax);
4866  }
4867 
4868  TH2Painter.prototype.ScanContent = function(when_axis_changed) {
4869 
4870  // no need to rescan histogram while result does not depend from axis selection
4871  if (when_axis_changed && this.nbinsx && this.nbinsy) return;
4872 
4873  var i, j, histo = this.GetObject();
4874 
4875  this.nbinsx = histo.fXaxis.fNbins;
4876  this.nbinsy = histo.fYaxis.fNbins;
4877 
4878  // used in CreateXY method
4879 
4880  this.CreateAxisFuncs(true);
4881 
4882  if (this.IsTH2Poly()) {
4883  this.gminposbin = null;
4884  this.gminbin = this.gmaxbin = 0;
4885 
4886  for (var n=0, len=histo.fBins.arr.length; n<len; ++n) {
4887  var bin_content = histo.fBins.arr[n].fContent;
4888  if (n===0) this.gminbin = this.gmaxbin = bin_content;
4889 
4890  if (bin_content < this.gminbin) this.gminbin = bin_content; else
4891  if (bin_content > this.gmaxbin) this.gmaxbin = bin_content;
4892 
4893  if (bin_content > 0)
4894  if ((this.gminposbin===null) || (this.gminposbin > bin_content)) this.gminposbin = bin_content;
4895  }
4896  } else {
4897  // global min/max, used at the moment in 3D drawing
4898  this.gminbin = this.gmaxbin = histo.getBinContent(1, 1);
4899  this.gminposbin = null;
4900  for (i = 0; i < this.nbinsx; ++i) {
4901  for (j = 0; j < this.nbinsy; ++j) {
4902  var bin_content = histo.getBinContent(i+1, j+1);
4903  if (bin_content < this.gminbin) this.gminbin = bin_content; else
4904  if (bin_content > this.gmaxbin) this.gmaxbin = bin_content;
4905  if (bin_content > 0)
4906  if ((this.gminposbin===null) || (this.gminposbin > bin_content)) this.gminposbin = bin_content;
4907  }
4908  }
4909  }
4910 
4911  // this value used for logz scale drawing
4912  if (this.gminposbin === null) this.gminposbin = this.gmaxbin*1e-4;
4913 
4914  if (this.options.Axis > 0) { // Paint histogram axis only
4915  this.draw_content = false;
4916  } else {
4917  this.draw_content = (this.gmaxbin > 0);
4918  if (!this.draw_content && this.options.Zero && this.IsTH2Poly()) {
4919  this.draw_content = true;
4920  this.options.Line = 1;
4921  }
4922  }
4923  }
4924 
4925  TH2Painter.prototype.CountStat = function(cond) {
4926  var histo = this.GetHisto(), xaxis = histo.fXaxis, yaxis = histo.fYaxis,
4927  stat_sum0 = 0, stat_sumx1 = 0, stat_sumy1 = 0,
4928  stat_sumx2 = 0, stat_sumy2 = 0, stat_sumxy = 0,
4929  xside, yside, xx, yy, zz,
4930  fp = this.frame_painter(),
4931  res = { name: histo.fName, entries: 0, integral: 0, meanx: 0, meany: 0, rmsx: 0, rmsy: 0, matrix: [0,0,0,0,0,0,0,0,0], xmax: 0, ymax:0, wmax: null };
4932 
4933  if (this.IsTH2Poly()) {
4934 
4935  var len = histo.fBins.arr.length, i, bin, n, gr, ngr, numgraphs, numpoints,
4936  pmain = this.frame_painter();
4937 
4938  for (i=0;i<len;++i) {
4939  bin = histo.fBins.arr[i];
4940 
4941  xside = 1; yside = 1;
4942 
4943  if (bin.fXmin > pmain.scale_xmax) xside = 2; else
4944  if (bin.fXmax < pmain.scale_xmin) xside = 0;
4945  if (bin.fYmin > pmain.scale_ymax) yside = 2; else
4946  if (bin.fYmax < pmain.scale_ymin) yside = 0;
4947 
4948  xx = yy = numpoints = 0;
4949  gr = bin.fPoly; numgraphs = 1;
4950  if (gr._typename === 'TMultiGraph') { numgraphs = bin.fPoly.fGraphs.arr.length; gr = null; }
4951 
4952  for (ngr=0;ngr<numgraphs;++ngr) {
4953  if (!gr || (ngr>0)) gr = bin.fPoly.fGraphs.arr[ngr];
4954 
4955  for (n=0;n<gr.fNpoints;++n) {
4956  ++numpoints;
4957  xx += gr.fX[n];
4958  yy += gr.fY[n];
4959  }
4960  }
4961 
4962  if (numpoints > 1) {
4963  xx = xx / numpoints;
4964  yy = yy / numpoints;
4965  }
4966 
4967  zz = bin.fContent;
4968 
4969  res.entries += zz;
4970 
4971  res.matrix[yside * 3 + xside] += zz;
4972 
4973  if ((xside != 1) || (yside != 1)) continue;
4974 
4975  if (cond && !cond(xx,yy)) continue;
4976 
4977  if ((res.wmax===null) || (zz>res.wmax)) { res.wmax = zz; res.xmax = xx; res.ymax = yy; }
4978 
4979  stat_sum0 += zz;
4980  stat_sumx1 += xx * zz;
4981  stat_sumy1 += yy * zz;
4982  stat_sumx2 += xx * xx * zz;
4983  stat_sumy2 += yy * yy * zz;
4984  stat_sumxy += xx * yy * zz;
4985  }
4986  } else {
4987  var xleft = this.GetSelectIndex("x", "left"),
4988  xright = this.GetSelectIndex("x", "right"),
4989  yleft = this.GetSelectIndex("y", "left"),
4990  yright = this.GetSelectIndex("y", "right"),
4991  xi, yi;
4992 
4993  for (xi = 0; xi <= this.nbinsx + 1; ++xi) {
4994  xside = (xi <= xleft) ? 0 : (xi > xright ? 2 : 1);
4995  xx = xaxis.GetBinCoord(xi - 0.5);
4996 
4997  for (yi = 0; yi <= this.nbinsy + 1; ++yi) {
4998  yside = (yi <= yleft) ? 0 : (yi > yright ? 2 : 1);
4999  yy = yaxis.GetBinCoord(yi - 0.5);
5000 
5001  zz = histo.getBinContent(xi, yi);
5002 
5003  res.entries += zz;
5004 
5005  res.matrix[yside * 3 + xside] += zz;
5006 
5007  if ((xside != 1) || (yside != 1)) continue;
5008 
5009  if ((cond!=null) && !cond(xx,yy)) continue;
5010 
5011  if ((res.wmax===null) || (zz>res.wmax)) { res.wmax = zz; res.xmax = xx; res.ymax = yy; }
5012 
5013  stat_sum0 += zz;
5014  stat_sumx1 += xx * zz;
5015  stat_sumy1 += yy * zz;
5016  stat_sumx2 += xx * xx * zz;
5017  stat_sumy2 += yy * yy * zz;
5018  stat_sumxy += xx * yy * zz;
5019  }
5020  }
5021  }
5022 
5023  if (!fp.IsAxisZoomed("x") && !fp.IsAxisZoomed("y") && (histo.fTsumw > 0)) {
5024  stat_sum0 = histo.fTsumw;
5025  stat_sumx1 = histo.fTsumwx;
5026  stat_sumx2 = histo.fTsumwx2;
5027  stat_sumy1 = histo.fTsumwy;
5028  stat_sumy2 = histo.fTsumwy2;
5029  stat_sumxy = histo.fTsumwxy;
5030  }
5031 
5032  if (stat_sum0 > 0) {
5033  res.meanx = stat_sumx1 / stat_sum0;
5034  res.meany = stat_sumy1 / stat_sum0;
5035  res.rmsx = Math.sqrt(Math.abs(stat_sumx2 / stat_sum0 - res.meanx * res.meanx));
5036  res.rmsy = Math.sqrt(Math.abs(stat_sumy2 / stat_sum0 - res.meany * res.meany));
5037  }
5038 
5039  if (res.wmax===null) res.wmax = 0;
5040  res.integral = stat_sum0;
5041 
5042  if (histo.fEntries > 1) res.entries = histo.fEntries;
5043 
5044  return res;
5045  }
5046 
5047  TH2Painter.prototype.FillStatistic = function(stat, dostat, dofit) {
5048 
5049  // no need to refill statistic if histogram is dummy
5050  if (this.IgnoreStatsFill()) return false;
5051 
5052  var data = this.CountStat(),
5053  print_name = Math.floor(dostat % 10),
5054  print_entries = Math.floor(dostat / 10) % 10,
5055  print_mean = Math.floor(dostat / 100) % 10,
5056  print_rms = Math.floor(dostat / 1000) % 10,
5057  print_under = Math.floor(dostat / 10000) % 10,
5058  print_over = Math.floor(dostat / 100000) % 10,
5059  print_integral = Math.floor(dostat / 1000000) % 10,
5060  print_skew = Math.floor(dostat / 10000000) % 10,
5061  print_kurt = Math.floor(dostat / 100000000) % 10;
5062 
5063  stat.ClearPave();
5064 
5065  if (print_name > 0)
5066  stat.AddText(data.name);
5067 
5068  if (print_entries > 0)
5069  stat.AddText("Entries = " + stat.Format(data.entries,"entries"));
5070 
5071  if (print_mean > 0) {
5072  stat.AddText("Mean x = " + stat.Format(data.meanx));
5073  stat.AddText("Mean y = " + stat.Format(data.meany));
5074  }
5075 
5076  if (print_rms > 0) {
5077  stat.AddText("Std Dev x = " + stat.Format(data.rmsx));
5078  stat.AddText("Std Dev y = " + stat.Format(data.rmsy));
5079  }
5080 
5081  if (print_integral > 0)
5082  stat.AddText("Integral = " + stat.Format(data.matrix[4],"entries"));
5083 
5084  if (print_skew > 0) {
5085  stat.AddText("Skewness x = <undef>");
5086  stat.AddText("Skewness y = <undef>");
5087  }
5088 
5089  if (print_kurt > 0)
5090  stat.AddText("Kurt = <undef>");
5091 
5092  if ((print_under > 0) || (print_over > 0)) {
5093  var m = data.matrix;
5094 
5095  stat.AddText("" + m[6].toFixed(0) + " | " + m[7].toFixed(0) + " | " + m[7].toFixed(0));
5096  stat.AddText("" + m[3].toFixed(0) + " | " + m[4].toFixed(0) + " | " + m[5].toFixed(0));
5097  stat.AddText("" + m[0].toFixed(0) + " | " + m[1].toFixed(0) + " | " + m[2].toFixed(0));
5098  }
5099 
5100  if (dofit) stat.FillFunctionStat(this.FindFunction('TF1'), dofit);
5101 
5102  return true;
5103  }
5104 
5105  TH2Painter.prototype.DrawBinsColor = function(w,h) {
5106  var histo = this.GetHisto(),
5107  handle = this.PrepareColorDraw(),
5108  colPaths = [], currx = [], curry = [],
5109  colindx, cmd1, cmd2, i, j, binz, x1, dx, y2, dy;
5110 
5111  // now start build
5112  for (i = handle.i1; i < handle.i2; ++i) {
5113 
5114  dx = handle.grx[i+1] - handle.grx[i];
5115  x1 = Math.round(handle.grx[i] + dx*handle.xbar1);
5116  dx = Math.round(dx*(handle.xbar2-handle.xbar1));
5117 
5118  for (j = handle.j1; j < handle.j2; ++j) {
5119  binz = histo.getBinContent(i + 1, j + 1);
5120  colindx = this.getContourColor(binz, true);
5121  if (binz===0) {
5122  if (!this.options.Zero) continue;
5123  if ((colindx === null) && this._show_empty_bins) colindx = 0;
5124  }
5125  if (colindx === null) continue;
5126 
5127  dy = handle.gry[j]-handle.gry[j+1];
5128  y2 = Math.round(handle.gry[j+1] + dy*handle.ybar1);
5129  dy = Math.round(dy*(handle.ybar2-handle.ybar1));
5130 
5131  cmd1 = "M"+x1+","+y2;
5132  if (colPaths[colindx] === undefined) {
5133  colPaths[colindx] = cmd1;
5134  } else{
5135  cmd2 = "m" + (x1-currx[colindx]) + "," + (y2-curry[colindx]);
5136  colPaths[colindx] += (cmd2.length < cmd1.length) ? cmd2 : cmd1;
5137  }
5138 
5139  currx[colindx] = x1;
5140  curry[colindx] = y2;
5141 
5142  colPaths[colindx] += "v"+dy + "h"+dx + "v"+(-dy) + "z";
5143  }
5144  }
5145 
5146  for (colindx=0;colindx<colPaths.length;++colindx)
5147  if (colPaths[colindx] !== undefined)
5148  this.draw_g
5149  .append("svg:path")
5150  .attr("palette-index", colindx)
5151  .attr("fill", this.fPalette.getColor(colindx))
5152  .attr("d", colPaths[colindx]);
5153 
5154  return handle;
5155  }
5156 
5157  TH2Painter.prototype.BuildContour = function(handle, levels, palette, contour_func) {
5158  var histo = this.GetObject(), ddd = 0,
5159  painter = this,
5160  kMAXCONTOUR = 2004,
5161  kMAXCOUNT = 2000,
5162  // arguments used in the PaintContourLine
5163  xarr = new Float32Array(2*kMAXCONTOUR),
5164  yarr = new Float32Array(2*kMAXCONTOUR),
5165  itarr = new Int32Array(2*kMAXCONTOUR),
5166  lj = 0, ipoly, poly, polys = [], np, npmax = 0,
5167  x = [0.,0.,0.,0.], y = [0.,0.,0.,0.], zc = [0.,0.,0.,0.], ir = [0,0,0,0],
5168  i, j, k, n, m, ix, ljfill, count,
5169  xsave, ysave, itars, ix, jx;
5170 
5171  function BinarySearch(zc) {
5172  for (var kk=0;kk<levels.length;++kk)
5173  if (zc<levels[kk]) return kk-1;
5174  return levels.length-1;
5175  }
5176 
5177  function PaintContourLine(elev1, icont1, x1, y1, elev2, icont2, x2, y2) {
5178  /* Double_t *xarr, Double_t *yarr, Int_t *itarr, Double_t *levels */
5179  var vert = (x1 === x2),
5180  tlen = vert ? (y2 - y1) : (x2 - x1),
5181  n = icont1 +1,
5182  tdif = elev2 - elev1,
5183  ii = lj-1,
5184  maxii = kMAXCONTOUR/2 -3 + lj,
5185  icount = 0,
5186  xlen, pdif, diff, elev;
5187 
5188  while (n <= icont2 && ii <= maxii) {
5189 // elev = fH->GetContourLevel(n);
5190  elev = levels[n];
5191  diff = elev - elev1;
5192  pdif = diff/tdif;
5193  xlen = tlen*pdif;
5194  if (vert) {
5195  xarr[ii] = x1;
5196  yarr[ii] = y1 + xlen;
5197  } else {
5198  xarr[ii] = x1 + xlen;
5199  yarr[ii] = y1;
5200  }
5201  itarr[ii] = n;
5202  icount++;
5203  ii +=2;
5204  n++;
5205  }
5206  return icount;
5207  }
5208 
5209  var arrx = handle.original ? handle.origx : handle.grx,
5210  arry = handle.original ? handle.origy : handle.gry;
5211 
5212  for (j = handle.j1; j < handle.j2-1; ++j) {
5213 
5214  y[1] = y[0] = (arry[j] + arry[j+1])/2;
5215  y[3] = y[2] = (arry[j+1] + arry[j+2])/2;
5216 
5217  for (i = handle.i1; i < handle.i2-1; ++i) {
5218 
5219  zc[0] = histo.getBinContent(i+1, j+1);
5220  zc[1] = histo.getBinContent(i+2, j+1);
5221  zc[2] = histo.getBinContent(i+2, j+2);
5222  zc[3] = histo.getBinContent(i+1, j+2);
5223 
5224  for (k=0;k<4;k++)
5225  ir[k] = BinarySearch(zc[k]);
5226 
5227  if ((ir[0] !== ir[1]) || (ir[1] !== ir[2]) || (ir[2] !== ir[3]) || (ir[3] !== ir[0])) {
5228  x[3] = x[0] = (arrx[i] + arrx[i+1])/2;
5229  x[2] = x[1] = (arrx[i+1] + arrx[i+2])/2;
5230 
5231  if (zc[0] <= zc[1]) n = 0; else n = 1;
5232  if (zc[2] <= zc[3]) m = 2; else m = 3;
5233  if (zc[n] > zc[m]) n = m;
5234  n++;
5235  lj=1;
5236  for (ix=1;ix<=4;ix++) {
5237  m = n%4 + 1;
5238  ljfill = PaintContourLine(zc[n-1],ir[n-1],x[n-1],y[n-1],
5239  zc[m-1],ir[m-1],x[m-1],y[m-1]);
5240  lj += 2*ljfill;
5241  n = m;
5242  }
5243 
5244  if (zc[0] <= zc[1]) n = 0; else n = 1;
5245  if (zc[2] <= zc[3]) m = 2; else m = 3;
5246  if (zc[n] > zc[m]) n = m;
5247  n++;
5248  lj=2;
5249  for (ix=1;ix<=4;ix++) {
5250  if (n == 1) m = 4;
5251  else m = n-1;
5252  ljfill = PaintContourLine(zc[n-1],ir[n-1],x[n-1],y[n-1],
5253  zc[m-1],ir[m-1],x[m-1],y[m-1]);
5254  lj += 2*ljfill;
5255  n = m;
5256  }
5257  // Re-order endpoints
5258 
5259  count = 0;
5260  for (ix=1; ix<=lj-5; ix +=2) {
5261  //count = 0;
5262  while (itarr[ix-1] != itarr[ix]) {
5263  xsave = xarr[ix];
5264  ysave = yarr[ix];
5265  itars = itarr[ix];
5266  for (jx=ix; jx<=lj-5; jx +=2) {
5267  xarr[jx] = xarr[jx+2];
5268  yarr[jx] = yarr[jx+2];
5269  itarr[jx] = itarr[jx+2];
5270  }
5271  xarr[lj-3] = xsave;
5272  yarr[lj-3] = ysave;
5273  itarr[lj-3] = itars;
5274  if (count > kMAXCOUNT) break;
5275  count++;
5276  }
5277  }
5278 
5279  if (count > kMAXCOUNT) continue;
5280 
5281  for (ix=1; ix<=lj-2; ix +=2) {
5282 
5283  ipoly = itarr[ix-1];
5284 
5285  if ((ipoly >= 0) && (ipoly < levels.length)) {
5286  poly = polys[ipoly];
5287  if (!poly)
5288  poly = polys[ipoly] = JSROOT.CreateTPolyLine(kMAXCONTOUR*4, true);
5289 
5290  np = poly.fLastPoint;
5291  if (np < poly.fN-2) {
5292  poly.fX[np+1] = Math.round(xarr[ix-1]); poly.fY[np+1] = Math.round(yarr[ix-1]);
5293  poly.fX[np+2] = Math.round(xarr[ix]); poly.fY[np+2] = Math.round(yarr[ix]);
5294  poly.fLastPoint = np+2;
5295  npmax = Math.max(npmax, poly.fLastPoint+1);
5296  } else {
5297  // console.log('reject point??', poly.fLastPoint);
5298  }
5299  }
5300  }
5301  } // end of if (ir[0]
5302  } // end of j
5303  } // end of i
5304 
5305  var polysort = new Int32Array(levels.length), first = 0;
5306  //find first positive contour
5307  for (ipoly=0;ipoly<levels.length;ipoly++) {
5308  if (levels[ipoly] >= 0) { first = ipoly; break; }
5309  }
5310  //store negative contours from 0 to minimum, then all positive contours
5311  k = 0;
5312  for (ipoly=first-1;ipoly>=0;ipoly--) {polysort[k] = ipoly; k++;}
5313  for (ipoly=first;ipoly<levels.length;ipoly++) { polysort[k] = ipoly; k++;}
5314 
5315  var xp = new Float32Array(2*npmax),
5316  yp = new Float32Array(2*npmax);
5317 
5318  for (k=0;k<levels.length;++k) {
5319 
5320  ipoly = polysort[k];
5321  poly = polys[ipoly];
5322  if (!poly) continue;
5323 
5324  var colindx = palette.calcColorIndex(ipoly, levels.length),
5325  xx = poly.fX, yy = poly.fY, np = poly.fLastPoint+1,
5326  istart = 0, iminus, iplus, xmin = 0, ymin = 0, nadd;
5327 
5328  while (true) {
5329 
5330  iminus = npmax;
5331  iplus = iminus+1;
5332  xp[iminus]= xx[istart]; yp[iminus] = yy[istart];
5333  xp[iplus] = xx[istart+1]; yp[iplus] = yy[istart+1];
5334  xx[istart] = xx[istart+1] = xmin;
5335  yy[istart] = yy[istart+1] = ymin;
5336  while (true) {
5337  nadd = 0;
5338  for (i=2;i<np;i+=2) {
5339  if ((iplus < 2*npmax-1) && (xx[i] === xp[iplus]) && (yy[i] === yp[iplus])) {
5340  iplus++;
5341  xp[iplus] = xx[i+1]; yp[iplus] = yy[i+1];
5342  xx[i] = xx[i+1] = xmin;
5343  yy[i] = yy[i+1] = ymin;
5344  nadd++;
5345  }
5346  if ((iminus > 0) && (xx[i+1] === xp[iminus]) && (yy[i+1] === yp[iminus])) {
5347  iminus--;
5348  xp[iminus] = xx[i]; yp[iminus] = yy[i];
5349  xx[i] = xx[i+1] = xmin;
5350  yy[i] = yy[i+1] = ymin;
5351  nadd++;
5352  }
5353  }
5354  if (nadd == 0) break;
5355  }
5356 
5357  if ((iminus+1 < iplus) && (iminus>=0))
5358  contour_func(colindx, xp, yp, iminus, iplus, ipoly);
5359 
5360  istart = 0;
5361  for (i=2;i<np;i+=2) {
5362  if (xx[i] !== xmin && yy[i] !== ymin) {
5363  istart = i;
5364  break;
5365  }
5366  }
5367 
5368  if (istart === 0) break;
5369  }
5370  }
5371  }
5372 
5373  TH2Painter.prototype.DrawBinsContour = function(frame_w,frame_h) {
5374  var handle = this.PrepareColorDraw({ rounding: false, extra: 100, original: this.options.Proj != 0 });
5375 
5376  // get levels
5377  var levels = this.GetContour(),
5378  palette = this.GetPalette(),
5379  painter = this, main = this.frame_painter();
5380 
5381  function BuildPath(xp,yp,iminus,iplus) {
5382  var cmd = "", last = null, pnt = null, i;
5383  for (i=iminus;i<=iplus;++i) {
5384  pnt = null;
5385  switch (main.projection) {
5386  case 1: pnt = main.ProjectAitoff2xy(xp[i], yp[i]); break;
5387  case 2: pnt = main.ProjectMercator2xy(xp[i], yp[i]); break;
5388  case 3: pnt = main.ProjectSinusoidal2xy(xp[i], yp[i]); break;
5389  case 4: pnt = main.ProjectParabolic2xy(xp[i], yp[i]); break;
5390  }
5391  if (pnt) {
5392  pnt.x = main.grx(pnt.x);
5393  pnt.y = main.gry(pnt.y);
5394  } else {
5395  pnt = { x: xp[i], y: yp[i] };
5396  }
5397  pnt.x = Math.round(pnt.x);
5398  pnt.y = Math.round(pnt.y);
5399  if (!cmd) cmd = "M" + pnt.x + "," + pnt.y;
5400  else if ((pnt.x != last.x) && (pnt.y != last.y)) cmd += "l" + (pnt.x - last.x) + "," + (pnt.y - last.y);
5401  else if (pnt.x != last.x) cmd += "h" + (pnt.x - last.x);
5402  else if (pnt.y != last.y) cmd += "v" + (pnt.y - last.y);
5403  last = pnt;
5404  }
5405  return cmd;
5406  }
5407 
5408  if (this.options.Contour===14) {
5409  var dd = "M0,0h"+frame_w+"v"+frame_h+"h-"+frame_w;
5410  if (this.options.Proj) {
5411  var sz = handle.j2 - handle.j1, xd = new Float32Array(sz*2), yd = new Float32Array(sz*2);
5412  for (var i=0;i<sz;++i) {
5413  xd[i] = handle.origx[handle.i1];
5414  yd[i] = (handle.origy[handle.j1]*(i+0.5) + handle.origy[handle.j2]*(sz-0.5-i))/sz;
5415  xd[i+sz] = handle.origx[handle.i2];
5416  yd[i+sz] = (handle.origy[handle.j2]*(i+0.5) + handle.origy[handle.j1]*(sz-0.5-i))/sz;
5417  }
5418  dd = BuildPath(xd,yd,0,2*sz-1);
5419  }
5420 
5421  this.draw_g
5422  .append("svg:path")
5423  .attr("d", dd + "z")
5424  .style('stroke','none')
5425  .style("fill", palette.calcColor(0, levels.length));
5426  }
5427 
5428  this.BuildContour(handle, levels, palette,
5429  function(colindx,xp,yp,iminus,iplus) {
5430  var icol = palette.getColor(colindx),
5431  fillcolor = icol, lineatt = null;
5432 
5433  switch (painter.options.Contour) {
5434  case 1: break;
5435  case 11: fillcolor = 'none'; lineatt = new JSROOT.TAttLineHandler({ color: icol } ); break;
5436  case 12: fillcolor = 'none'; lineatt = new JSROOT.TAttLineHandler({ color: 1, style: (colindx%5 + 1), width: 1 }); break;
5437  case 13: fillcolor = 'none'; lineatt = painter.lineatt; break;
5438  case 14: break;
5439  }
5440 
5441  var elem = painter.draw_g
5442  .append("svg:path")
5443  .attr("class","th2_contour")
5444  .attr("d", BuildPath(xp,yp,iminus,iplus) + (fillcolor == 'none' ? "" : "z"))
5445  .style("fill", fillcolor);
5446 
5447  if (lineatt!==null)
5448  elem.call(lineatt.func);
5449  else
5450  elem.style('stroke','none');
5451  }
5452  );
5453 
5454  handle.hide_only_zeros = true; // text drawing suppress only zeros
5455 
5456  return handle;
5457  }
5458 
5459  TH2Painter.prototype.CreatePolyBin = function(pmain, bin, text_pos) {
5460  var cmd = "", ngr, ngraphs = 1, gr = null;
5461 
5462  if (bin.fPoly._typename=='TMultiGraph')
5463  ngraphs = bin.fPoly.fGraphs.arr.length;
5464  else
5465  gr = bin.fPoly;
5466 
5467  if (text_pos)
5468  bin._sumx = bin._sumy = bin._suml = 0;
5469 
5470  function AddPoint(x1,y1,x2,y2) {
5471  var len = Math.sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));
5472  bin._sumx += (x1+x2)*len/2;
5473  bin._sumy += (y1+y2)*len/2;
5474  bin._suml += len;
5475  }
5476 
5477  for (ngr = 0; ngr < ngraphs; ++ ngr) {
5478  if (!gr || (ngr>0)) gr = bin.fPoly.fGraphs.arr[ngr];
5479 
5480  var npnts = gr.fNpoints, n,
5481  x = gr.fX, y = gr.fY,
5482  grx = Math.round(pmain.grx(x[0])),
5483  gry = Math.round(pmain.gry(y[0])),
5484  nextx, nexty;
5485 
5486  if ((npnts>2) && (x[0]==x[npnts-1]) && (y[0]==y[npnts-1])) npnts--;
5487 
5488  cmd += "M"+grx+","+gry;
5489 
5490  for (n=1;n<npnts;++n) {
5491  nextx = Math.round(pmain.grx(x[n]));
5492  nexty = Math.round(pmain.gry(y[n]));
5493  if (text_pos) AddPoint(grx,gry, nextx, nexty);
5494  if ((grx!==nextx) || (gry!==nexty)) {
5495  if (grx===nextx) cmd += "v" + (nexty - gry); else
5496  if (gry===nexty) cmd += "h" + (nextx - grx); else
5497  cmd += "l" + (nextx - grx) + "," + (nexty - gry);
5498  }
5499  grx = nextx; gry = nexty;
5500  }
5501 
5502  if (text_pos) AddPoint(grx, gry, Math.round(pmain.grx(x[0])), Math.round(pmain.gry(y[0])));
5503  cmd += "z";
5504  }
5505 
5506  if (text_pos) {
5507  if (bin._suml > 0) {
5508  bin._midx = Math.round(bin._sumx / bin._suml);
5509  bin._midy = Math.round(bin._sumy / bin._suml);
5510  } else {
5511  bin._midx = Math.round(pmain.grx((bin.fXmin + bin.fXmax)/2));
5512  bin._midy = Math.round(pmain.gry((bin.fYmin + bin.fYmax)/2));
5513  }
5514  }
5515 
5516  return cmd;
5517  }
5518 
5519  TH2Painter.prototype.DrawPolyBinsColor = function(w,h) {
5520  var histo = this.GetObject(),
5521  pmain = this.frame_painter(),
5522  colPaths = [], textbins = [],
5523  colindx, cmd, bin, item,
5524  i, len = histo.fBins.arr.length;
5525 
5526  // force recalculations of contours
5527  this.fContour = null;
5528  this.fCustomContour = false;
5529 
5530  // use global coordinates
5531  this.maxbin = this.gmaxbin;
5532  this.minbin = this.gminbin;
5533  this.minposbin = this.gminposbin;
5534 
5535  for (i = 0; i < len; ++ i) {
5536  bin = histo.fBins.arr[i];
5537  colindx = this.getContourColor(bin.fContent, true);
5538  if (colindx === null) continue;
5539  if (bin.fContent === 0) {
5540  if (!this.options.Zero || !this.options.Line) continue;
5541  colindx = 0; // make dummy fill color to draw only line
5542  }
5543 
5544  // check if bin outside visible range
5545  if ((bin.fXmin > pmain.scale_xmax) || (bin.fXmax < pmain.scale_xmin) ||
5546  (bin.fYmin > pmain.scale_ymax) || (bin.fYmax < pmain.scale_ymin)) continue;
5547 
5548  cmd = this.CreatePolyBin(pmain, bin, this.options.Text && bin.fContent);
5549 
5550  if (colPaths[colindx] === undefined)
5551  colPaths[colindx] = cmd;
5552  else
5553  colPaths[colindx] += cmd;
5554 
5555  if (this.options.Text && bin.fContent) textbins.push(bin);
5556  }
5557 
5558  for (colindx=0;colindx<colPaths.length;++colindx)
5559  if (colPaths[colindx]) {
5560  item = this.draw_g
5561  .append("svg:path")
5562  .attr("palette-index", colindx)
5563  .attr("fill", colindx ? this.fPalette.getColor(colindx) : 'none')
5564  .attr("d", colPaths[colindx]);
5565  if (this.options.Line)
5566  item.call(this.lineatt.func);
5567  }
5568 
5569  if (textbins.length > 0) {
5570  var text_col = this.get_color(histo.fMarkerColor),
5571  text_angle = -1*this.options.TextAngle,
5572  text_g = this.draw_g.append("svg:g").attr("class","th2poly_text"),
5573  text_size = 12;
5574 
5575  if ((histo.fMarkerSize!==1) && text_angle)
5576  text_size = Math.round(0.02*h*histo.fMarkerSize);
5577 
5578  this.StartTextDrawing(42, text_size, text_g, text_size);
5579 
5580  for (i = 0; i < textbins.length; ++ i) {
5581  bin = textbins[i];
5582 
5583  var lbl = "";
5584 
5585  if (!this.options.TextKind) {
5586  lbl = (Math.round(bin.fContent) === bin.fContent) ? bin.fContent.toString() :
5587  JSROOT.FFormat(bin.fContent, JSROOT.gStyle.fPaintTextFormat);
5588  } else {
5589  if (bin.fPoly) lbl = bin.fPoly.fName;
5590  if (lbl === "Graph") lbl = "";
5591  if (!lbl) lbl = bin.fNumber;
5592  }
5593 
5594  this.DrawText({ align: 22, x: bin._midx, y: bin._midy, rotate: text_angle, text: lbl, color: text_col, latex: 0, draw_g: text_g });
5595  }
5596 
5597  this.FinishTextDrawing(text_g, null);
5598  }
5599 
5600  return { poly: true };
5601  }
5602 
5603  TH2Painter.prototype.DrawBinsText = function(w, h, handle) {
5604  var histo = this.GetObject(),
5605  i,j,binz,errz,colindx,binw,binh,lbl,lble,posx,posy,sizex,sizey,
5606  text_col = this.get_color(histo.fMarkerColor),
5607  text_angle = -1*this.options.TextAngle,
5608  text_g = this.draw_g.append("svg:g").attr("class","th2_text"),
5609  text_size = 20, text_offset = 0,
5610  profile2d = this.MatchObjectType('TProfile2D') && (typeof histo.getBinEntries=='function'),
5611  show_err = (this.options.TextKind == "E"),
5612  use_latex = (show_err && !this.options.TextLine) ? 1 : 0;
5613 
5614  if (handle===null) handle = this.PrepareColorDraw({ rounding: false });
5615 
5616  if ((histo.fMarkerSize!==1) && text_angle)
5617  text_size = Math.round(0.02*h*histo.fMarkerSize);
5618 
5619  if (histo.fBarOffset!==0) text_offset = histo.fBarOffset*1e-3;
5620 
5621  this.StartTextDrawing(42, text_size, text_g, text_size);
5622 
5623  for (i = handle.i1; i < handle.i2; ++i)
5624  for (j = handle.j1; j < handle.j2; ++j) {
5625  binz = histo.getBinContent(i+1, j+1);
5626  if ((binz === 0) && !this._show_empty_bins) continue;
5627 
5628  binw = handle.grx[i+1] - handle.grx[i];
5629  binh = handle.gry[j] - handle.gry[j+1];
5630 
5631  if (profile2d)
5632  binz = histo.getBinEntries(i+1, j+1);
5633 
5634  lbl = (binz === Math.round(binz)) ? binz.toString() :
5635  JSROOT.FFormat(binz, JSROOT.gStyle.fPaintTextFormat);
5636 
5637  if (show_err) {
5638  errz = histo.getBinError(histo.getBin(i+1,j+1));
5639  lble = (errz === Math.round(errz)) ? errz.toString() :
5640  JSROOT.FFormat(errz, JSROOT.gStyle.fPaintTextFormat);
5641  if (this.options.TextLine)
5642  lbl += '\xB1' + lble;
5643  else
5644  lbl = "#splitline{" + lbl + "}{#pm" + lble + "}";
5645  }
5646 
5647  if (text_angle /*|| (histo.fMarkerSize!==1)*/) {
5648  posx = Math.round(handle.grx[i] + binw*0.5);
5649  posy = Math.round(handle.gry[j+1] + binh*(0.5 + text_offset));
5650  sizex = 0;
5651  sizey = 0;
5652  } else {
5653  posx = Math.round(handle.grx[i] + binw*0.1);
5654  posy = Math.round(handle.gry[j+1] + binh*(0.1 + text_offset));
5655  sizex = Math.round(binw*0.8);
5656  sizey = Math.round(binh*0.8);
5657  }
5658 
5659  this.DrawText({ align: 22, x: posx, y: posy, width: sizex, height: sizey, rotate: text_angle, text: lbl, color: text_col, latex: use_latex, draw_g: text_g });
5660  }
5661 
5662  this.FinishTextDrawing(text_g, null);
5663 
5664  handle.hide_only_zeros = true; // text drawing suppress only zeros
5665 
5666  return handle;
5667  }
5668 
5669  TH2Painter.prototype.DrawBinsArrow = function(w, h) {
5670  var histo = this.GetObject(), cmd = "",
5671  i,j,binz,colindx,binw,binh,lbl, loop, dn = 1e-30, dx, dy, xc,yc,
5672  dxn,dyn,x1,x2,y1,y2, anr,si,co,
5673  handle = this.PrepareColorDraw({ rounding: false }),
5674  scale_x = (handle.grx[handle.i2] - handle.grx[handle.i1])/(handle.i2 - handle.i1 + 1-0.03)/2,
5675  scale_y = (handle.gry[handle.j2] - handle.gry[handle.j1])/(handle.j2 - handle.j1 + 1-0.03)/2;
5676 
5677  for (var loop=0;loop<2;++loop)
5678  for (i = handle.i1; i < handle.i2; ++i)
5679  for (j = handle.j1; j < handle.j2; ++j) {
5680 
5681  if (i === handle.i1) {
5682  dx = histo.getBinContent(i+2, j+1) - histo.getBinContent(i+1, j+1);
5683  } else if (i === handle.i2-1) {
5684  dx = histo.getBinContent(i+1, j+1) - histo.getBinContent(i, j+1);
5685  } else {
5686  dx = 0.5*(histo.getBinContent(i+2, j+1) - histo.getBinContent(i, j+1));
5687  }
5688  if (j === handle.j1) {
5689  dy = histo.getBinContent(i+1, j+2) - histo.getBinContent(i+1, j+1);
5690  } else if (j === handle.j2-1) {
5691  dy = histo.getBinContent(i+1, j+1) - histo.getBinContent(i+1, j);
5692  } else {
5693  dy = 0.5*(histo.getBinContent(i+1, j+2) - histo.getBinContent(i+1, j));
5694  }
5695 
5696  if (loop===0) {
5697  dn = Math.max(dn, Math.abs(dx), Math.abs(dy));
5698  } else {
5699  xc = (handle.grx[i] + handle.grx[i+1])/2;
5700  yc = (handle.gry[j] + handle.gry[j+1])/2;
5701  dxn = scale_x*dx/dn;
5702  dyn = scale_y*dy/dn;
5703  x1 = xc - dxn;
5704  x2 = xc + dxn;
5705  y1 = yc - dyn;
5706  y2 = yc + dyn;
5707  dx = Math.round(x2-x1);
5708  dy = Math.round(y2-y1);
5709 
5710  if ((dx!==0) || (dy!==0)) {
5711  cmd += "M"+Math.round(x1)+","+Math.round(y1)+"l"+dx+","+dy;
5712 
5713  if (Math.abs(dx) > 5 || Math.abs(dy) > 5) {
5714  anr = Math.sqrt(2/(dx*dx + dy*dy));
5715  si = Math.round(anr*(dx + dy));
5716  co = Math.round(anr*(dx - dy));
5717  if ((si!==0) && (co!==0))
5718  cmd+="l"+(-si)+","+co + "m"+si+","+(-co) + "l"+(-co)+","+(-si);
5719  }
5720  }
5721  }
5722  }
5723 
5724  this.draw_g
5725  .append("svg:path")
5726  .attr("class","th2_arrows")
5727  .attr("d", cmd)
5728  .style("fill", "none")
5729  .call(this.lineatt.func);
5730 
5731  return handle;
5732  }
5733 
5734 
5735  TH2Painter.prototype.DrawBinsBox = function(w,h) {
5736 
5737  var histo = this.GetObject(),
5738  handle = this.PrepareColorDraw({ rounding: false }),
5739  main = this.main_painter();
5740 
5741  if (main===this) {
5742  if (main.maxbin === main.minbin) {
5743  main.maxbin = main.gmaxbin;
5744  main.minbin = main.gminbin;
5745  main.minposbin = main.gminposbin;
5746  }
5747  if (main.maxbin === main.minbin)
5748  main.minbin = Math.min(0, main.maxbin-1);
5749  }
5750 
5751  var absmax = Math.max(Math.abs(main.maxbin), Math.abs(main.minbin)),
5752  absmin = Math.max(0, main.minbin),
5753  i, j, binz, absz, res = "", cross = "", btn1 = "", btn2 = "",
5754  colindx, zdiff, dgrx, dgry, xx, yy, ww, hh, cmd1, cmd2,
5755  xyfactor = 1, uselogz = false, logmin = 0, logmax = 1;
5756 
5757  if (this.root_pad().fLogz && (absmax>0)) {
5758  uselogz = true;
5759  logmax = Math.log(absmax);
5760  if (absmin>0) logmin = Math.log(absmin); else
5761  if ((main.minposbin>=1) && (main.minposbin<100)) logmin = Math.log(0.7); else
5762  logmin = (main.minposbin > 0) ? Math.log(0.7*main.minposbin) : logmax - 10;
5763  if (logmin >= logmax) logmin = logmax - 10;
5764  xyfactor = 1. / (logmax - logmin);
5765  } else {
5766  xyfactor = 1. / (absmax - absmin);
5767  }
5768 
5769  // now start build
5770  for (i = handle.i1; i < handle.i2; ++i) {
5771  for (j = handle.j1; j < handle.j2; ++j) {
5772  binz = histo.getBinContent(i + 1, j + 1);
5773  absz = Math.abs(binz);
5774  if ((absz === 0) || (absz < absmin)) continue;
5775 
5776  zdiff = uselogz ? ((absz>0) ? Math.log(absz) - logmin : 0) : (absz - absmin);
5777  // area of the box should be proportional to absolute bin content
5778  zdiff = 0.5 * ((zdiff < 0) ? 1 : (1 - Math.sqrt(zdiff * xyfactor)));
5779  // avoid oversized bins
5780  if (zdiff < 0) zdiff = 0;
5781 
5782  ww = handle.grx[i+1] - handle.grx[i];
5783  hh = handle.gry[j] - handle.gry[j+1];
5784 
5785  dgrx = zdiff * ww;
5786  dgry = zdiff * hh;
5787 
5788  xx = Math.round(handle.grx[i] + dgrx);
5789  yy = Math.round(handle.gry[j+1] + dgry);
5790 
5791  ww = Math.max(Math.round(ww - 2*dgrx), 1);
5792  hh = Math.max(Math.round(hh - 2*dgry), 1);
5793 
5794  res += "M"+xx+","+yy + "v"+hh + "h"+ww + "v-"+hh + "z";
5795 
5796  if ((binz<0) && (this.options.BoxStyle === 10))
5797  cross += "M"+xx+","+yy + "l"+ww+","+hh + "M"+(xx+ww)+","+yy + "l-"+ww+","+hh;
5798 
5799  if ((this.options.BoxStyle === 11) && (ww>5) && (hh>5)) {
5800  var pww = Math.round(ww*0.1),
5801  phh = Math.round(hh*0.1),
5802  side1 = "M"+xx+","+yy + "h"+ww + "l"+(-pww)+","+phh + "h"+(2*pww-ww) +
5803  "v"+(hh-2*phh)+ "l"+(-pww)+","+phh + "z",
5804  side2 = "M"+(xx+ww)+","+(yy+hh) + "v"+(-hh) + "l"+(-pww)+","+phh + "v"+(hh-2*phh)+
5805  "h"+(2*pww-ww) + "l"+(-pww)+","+phh + "z";
5806  if (binz<0) { btn2+=side1; btn1+=side2; }
5807  else { btn1+=side1; btn2+=side2; }
5808  }
5809  }
5810  }
5811 
5812  if (res.length > 0) {
5813  var elem = this.draw_g.append("svg:path")
5814  .attr("d", res)
5815  .call(this.fillatt.func);
5816  if ((this.options.BoxStyle === 11) || !this.fillatt.empty())
5817  elem.style('stroke','none');
5818  else
5819  elem.call(this.lineatt.func);
5820  }
5821 
5822  if ((btn1.length>0) && (this.fillatt.color !== 'none'))
5823  this.draw_g.append("svg:path")
5824  .attr("d", btn1)
5825  .style("stroke","none")
5826  .call(this.fillatt.func)
5827  .style("fill", d3.rgb(this.fillatt.color).brighter(0.5).toString());
5828 
5829  if (btn2.length>0)
5830  this.draw_g.append("svg:path")
5831  .attr("d", btn2)
5832  .style("stroke","none")
5833  .call(this.fillatt.func)
5834  .style("fill", this.fillatt.color === 'none' ? 'red' : d3.rgb(this.fillatt.color).darker(0.5).toString());
5835 
5836  if (cross.length > 0) {
5837  var elem = this.draw_g.append("svg:path")
5838  .attr("d", cross)
5839  .style("fill", "none");
5840  if (this.lineatt.color !== 'none')
5841  elem.call(this.lineatt.func);
5842  else
5843  elem.style('stroke','black');
5844  }
5845 
5846  return handle;
5847  }
5848 
5849  TH2Painter.prototype.DrawCandle = function(w,h) {
5850  var histo = this.GetHisto(),
5851  handle = this.PrepareColorDraw(),
5852  pmain = this.frame_painter(), // used for axis values conversions
5853  i, j, y, sum0, sum1, sum2, cont, center, counter, integral, w, pnt,
5854  bars = "", markers = "", posy;
5855 
5856  if (histo.fMarkerColor === 1) histo.fMarkerColor = histo.fLineColor;
5857 
5858  // create attribute only when necessary
5859  this.createAttMarker({ attr: histo, style: 5 });
5860 
5861  // reset absolution position for markers
5862  this.markeratt.reset_pos();
5863 
5864  handle.candle = []; // array of drawn points
5865 
5866  // loop over visible x-bins
5867  for (i = handle.i1; i < handle.i2; ++i) {
5868  sum1 = 0;
5869  //estimate integral
5870  integral = 0;
5871  counter = 0;
5872  for (j = 0; j < this.nbinsy; ++j) {
5873  integral += histo.getBinContent(i+1,j+1);
5874  }
5875  pnt = { bin:i, meany:0, m25y:0, p25y:0, median:0, iqr:0, whiskerp:0, whiskerm:0};
5876  //estimate quantiles... simple function... not so nice as GetQuantiles
5877  for (j = 0; j < this.nbinsy; ++j) {
5878  cont = histo.getBinContent(i+1,j+1);
5879  posy = histo.fYaxis.GetBinCenter(j+1);
5880  if (counter/integral < 0.001 && (counter + cont)/integral >=0.001) pnt.whiskerm = posy; // Lower whisker
5881  if (counter/integral < 0.25 && (counter + cont)/integral >=0.25) pnt.m25y = posy; // Lower edge of box
5882  if (counter/integral < 0.5 && (counter + cont)/integral >=0.5) pnt.median = posy; //Median
5883  if (counter/integral < 0.75 && (counter + cont)/integral >=0.75) pnt.p25y = posy; //Upper edge of box
5884  if (counter/integral < 0.999 && (counter + cont)/integral >=0.999) pnt.whiskerp = posy; // Upper whisker
5885  counter += cont;
5886  y = posy; // center of y bin coordinate
5887  sum1 += cont*y;
5888  }
5889  if (counter > 0) {
5890  pnt.meany = sum1/counter;
5891  }
5892  pnt.iqr = pnt.p25y-pnt.m25y;
5893 
5894  //Whiskers cannot exceed 1.5*iqr from box
5895  if ((pnt.m25y-1.5*pnt.iqr) > pnt.whsikerm) {
5896  pnt.whiskerm = pnt.m25y-1.5*pnt.iqr;
5897  }
5898  if ((pnt.p25y+1.5*pnt.iqr) < pnt.whiskerp) {
5899  pnt.whiskerp = pnt.p25y+1.5*pnt.iqr;
5900  }
5901 
5902  // exclude points with negative y when log scale is specified
5903  if (pmain.logy && (pnt.whiskerm<=0)) continue;
5904 
5905  w = handle.grx[i+1] - handle.grx[i];
5906  w *= 0.66;
5907  center = (handle.grx[i+1] + handle.grx[i]) / 2 + histo.fBarOffset/1000*w;
5908  if (histo.fBarWidth>0) w = w * histo.fBarWidth / 1000;
5909 
5910  pnt.x1 = Math.round(center - w/2);
5911  pnt.x2 = Math.round(center + w/2);
5912  center = Math.round(center);
5913 
5914  pnt.y0 = Math.round(pmain.gry(pnt.median));
5915  // mean line
5916  bars += "M" + pnt.x1 + "," + pnt.y0 + "h" + (pnt.x2-pnt.x1);
5917 
5918  pnt.y1 = Math.round(pmain.gry(pnt.p25y));
5919  pnt.y2 = Math.round(pmain.gry(pnt.m25y));
5920 
5921  // rectangle
5922  bars += "M" + pnt.x1 + "," + pnt.y1 +
5923  "v" + (pnt.y2-pnt.y1) + "h" + (pnt.x2-pnt.x1) + "v-" + (pnt.y2-pnt.y1) + "z";
5924 
5925  pnt.yy1 = Math.round(pmain.gry(pnt.whiskerp));
5926  pnt.yy2 = Math.round(pmain.gry(pnt.whiskerm));
5927 
5928  // upper part
5929  bars += "M" + center + "," + pnt.y1 + "v" + (pnt.yy1-pnt.y1);
5930  bars += "M" + pnt.x1 + "," + pnt.yy1 + "h" + (pnt.x2-pnt.x1);
5931 
5932  // lower part
5933  bars += "M" + center + "," + pnt.y2 + "v" + (pnt.yy2-pnt.y2);
5934  bars += "M" + pnt.x1 + "," + pnt.yy2 + "h" + (pnt.x2-pnt.x1);
5935 
5936  //estimate outliers
5937  for (j = 0; j < this.nbinsy; ++j) {
5938  cont = histo.getBinContent(i+1,j+1);
5939  posy = histo.fYaxis.GetBinCenter(j+1);
5940  if (cont > 0 && posy < pnt.whiskerm) markers += this.markeratt.create(center, posy);
5941  if (cont > 0 && posy > pnt.whiskerp) markers += this.markeratt.create(center, posy);
5942  }
5943 
5944  handle.candle.push(pnt); // keep point for the tooltip
5945  }
5946 
5947  if (bars.length > 0)
5948  this.draw_g.append("svg:path")
5949  .attr("d", bars)
5950  .call(this.lineatt.func)
5951  .call(this.fillatt.func);
5952 
5953  if (markers.length > 0)
5954  this.draw_g.append("svg:path")
5955  .attr("d", markers)
5956  .call(this.markeratt.func);
5957 
5958  return handle;
5959  }
5960 
5961  TH2Painter.prototype.DrawBinsScatter = function(w,h) {
5962  var histo = this.GetObject(),
5963  handle = this.PrepareColorDraw({ rounding: true, pixel_density: true }),
5964  colPaths = [], currx = [], curry = [], cell_w = [], cell_h = [],
5965  colindx, cmd1, cmd2, i, j, binz, cw, ch, factor = 1.,
5966  scale = this.options.ScatCoef * ((this.gmaxbin) > 2000 ? 2000. / this.gmaxbin : 1.);
5967 
5968  JSROOT.seed(handle.sumz);
5969 
5970  if (scale*handle.sumz < 1e5) {
5971  // one can use direct drawing of scatter plot without any patterns
5972 
5973  this.createAttMarker({ attr: histo });
5974 
5975  this.markeratt.reset_pos();
5976 
5977  var path = "", k, npix;
5978  for (i = handle.i1; i < handle.i2; ++i) {
5979  cw = handle.grx[i+1] - handle.grx[i];
5980  for (j = handle.j1; j < handle.j2; ++j) {
5981  ch = handle.gry[j] - handle.gry[j+1];
5982  binz = histo.getBinContent(i + 1, j + 1);
5983 
5984  npix = Math.round(scale*binz);
5985  if (npix<=0) continue;
5986 
5987  for (k=0;k<npix;++k)
5988  path += this.markeratt.create(
5989  Math.round(handle.grx[i] + cw * JSROOT.random()),
5990  Math.round(handle.gry[j+1] + ch * JSROOT.random()));
5991  }
5992  }
5993 
5994  this.draw_g
5995  .append("svg:path")
5996  .attr("d", path)
5997  .call(this.markeratt.func);
5998 
5999  return handle;
6000  }
6001 
6002  // limit filling factor, do not try to produce as many points as filled area;
6003  if (this.maxbin > 0.7) factor = 0.7/this.maxbin;
6004 
6005  var nlevels = Math.round(handle.max - handle.min);
6006  this.CreateContour((nlevels > 50) ? 50 : nlevels, this.minposbin, this.maxbin, this.minposbin);
6007 
6008  // now start build
6009  for (i = handle.i1; i < handle.i2; ++i) {
6010  for (j = handle.j1; j < handle.j2; ++j) {
6011  binz = histo.getBinContent(i + 1, j + 1);
6012  if ((binz <= 0) || (binz < this.minbin)) continue;
6013 
6014  cw = handle.grx[i+1] - handle.grx[i];
6015  ch = handle.gry[j] - handle.gry[j+1];
6016  if (cw*ch <= 0) continue;
6017 
6018  colindx = this.getContourIndex(binz/cw/ch);
6019  if (colindx < 0) continue;
6020 
6021  cmd1 = "M"+handle.grx[i]+","+handle.gry[j+1];
6022  if (colPaths[colindx] === undefined) {
6023  colPaths[colindx] = cmd1;
6024  cell_w[colindx] = cw;
6025  cell_h[colindx] = ch;
6026  } else{
6027  cmd2 = "m" + (handle.grx[i]-currx[colindx]) + "," + (handle.gry[j+1] - curry[colindx]);
6028  colPaths[colindx] += (cmd2.length < cmd1.length) ? cmd2 : cmd1;
6029  cell_w[colindx] = Math.max(cell_w[colindx], cw);
6030  cell_h[colindx] = Math.max(cell_h[colindx], ch);
6031  }
6032 
6033  currx[colindx] = handle.grx[i];
6034  curry[colindx] = handle.gry[j+1];
6035 
6036  colPaths[colindx] += "v"+ch+"h"+cw+"v-"+ch+"z";
6037  }
6038  }
6039 
6040  var layer = this.svg_frame().select('.main_layer'),
6041  defs = layer.select("defs");
6042  if (defs.empty() && (colPaths.length>0))
6043  defs = layer.insert("svg:defs",":first-child");
6044 
6045  this.createAttMarker({ attr: histo });
6046 
6047  for (colindx=0;colindx<colPaths.length;++colindx)
6048  if ((colPaths[colindx] !== undefined) && (colindx<this.fContour.length)) {
6049  var pattern_class = "scatter_" + colindx,
6050  pattern = defs.select('.' + pattern_class);
6051  if (pattern.empty())
6052  pattern = defs.append('svg:pattern')
6053  .attr("class", pattern_class)
6054  .attr("id", "jsroot_scatter_pattern_" + JSROOT.id_counter++)
6055  .attr("patternUnits","userSpaceOnUse");
6056  else
6057  pattern.selectAll("*").remove();
6058 
6059  var npix = Math.round(factor*this.fContour[colindx]*cell_w[colindx]*cell_h[colindx]);
6060  if (npix<1) npix = 1;
6061 
6062  var arrx = new Float32Array(npix), arry = new Float32Array(npix);
6063 
6064  if (npix===1) {
6065  arrx[0] = arry[0] = 0.5;
6066  } else {
6067  for (var n=0;n<npix;++n) {
6068  arrx[n] = JSROOT.random();
6069  arry[n] = JSROOT.random();
6070  }
6071  }
6072 
6073  // arrx.sort();
6074 
6075  this.markeratt.reset_pos();
6076 
6077  var path = "";
6078 
6079  for (var n=0;n<npix;++n)
6080  path += this.markeratt.create(arrx[n] * cell_w[colindx], arry[n] * cell_h[colindx]);
6081 
6082  pattern.attr("width", cell_w[colindx])
6083  .attr("height", cell_h[colindx])
6084  .append("svg:path")
6085  .attr("d",path)
6086  .call(this.markeratt.func);
6087 
6088  this.draw_g
6089  .append("svg:path")
6090  .attr("scatter-index", colindx)
6091  .attr("fill", 'url(#' + pattern.attr("id") + ')')
6092  .attr("d", colPaths[colindx]);
6093  }
6094 
6095  return handle;
6096  }
6097 
6098  TH2Painter.prototype.DrawBins = function() {
6099 
6100  if (!this.draw_content)
6101  return this.RemoveDrawG();
6102 
6103  this.CheckHistDrawAttributes();
6104 
6105  this.CreateG(true);
6106 
6107  var w = this.frame_width(),
6108  h = this.frame_height(),
6109  handle = null;
6110 
6111  if (this.IsTH2Poly()) {
6112  handle = this.DrawPolyBinsColor(w, h);
6113  } else {
6114  if (this.options.Scat)
6115  handle = this.DrawBinsScatter(w, h);
6116  else if (this.options.Color)
6117  handle = this.DrawBinsColor(w, h);
6118  else if (this.options.Box)
6119  handle = this.DrawBinsBox(w, h);
6120  else if (this.options.Arrow)
6121  handle = this.DrawBinsArrow(w, h);
6122  else if (this.options.Contour)
6123  handle = this.DrawBinsContour(w, h);
6124  else if (this.options.Candle)
6125  handle = this.DrawCandle(w, h);
6126 
6127  if (this.options.Text)
6128  handle = this.DrawBinsText(w, h, handle);
6129 
6130  if (!handle)
6131  handle = this.DrawBinsScatter(w, h);
6132  }
6133 
6134  this.tt_handle = handle;
6135  }
6136 
6138  TH2Painter.prototype.GetBinTips = function (i, j) {
6139  var lines = [], pmain = this.frame_painter(),
6140  histo = this.GetHisto(),
6141  binz = histo.getBinContent(i+1,j+1)
6142 
6143  lines.push(this.GetTipName());
6144 
6145  if (pmain.x_kind == 'labels')
6146  lines.push("x = " + pmain.AxisAsText("x", histo.fXaxis.GetBinLowEdge(i+1)));
6147  else
6148  lines.push("x = [" + pmain.AxisAsText("x", histo.fXaxis.GetBinLowEdge(i+1)) + ", " + pmain.AxisAsText("x", histo.fXaxis.GetBinLowEdge(i+2)) + ")");
6149 
6150  if (pmain.y_kind == 'labels')
6151  lines.push("y = " + pmain.AxisAsText("y", histo.fYaxis.GetBinLowEdge(j+1)));
6152  else
6153  lines.push("y = [" + pmain.AxisAsText("y", histo.fYaxis.GetBinLowEdge(j+1)) + ", " + pmain.AxisAsText("y", histo.fYaxis.GetBinLowEdge(j+2)) + ")");
6154 
6155  lines.push("bin = " + i + ", " + j);
6156 
6157  if (histo.$baseh) binz -= histo.$baseh.getBinContent(i+1,j+1);
6158 
6159  lines.push("entries = " + ((binz === Math.round(binz)) ? binz : JSROOT.FFormat(binz, JSROOT.gStyle.fStatFormat)));
6160 
6161  if ((this.options.TextKind == "E") || this.MatchObjectType('TProfile2D')) {
6162  var errz = histo.getBinError(histo.getBin(i+1,j+1));
6163  lines.push("error = " + ((errz === Math.round(errz)) ? errz.toString() : JSROOT.FFormat(errz, JSROOT.gStyle.fPaintTextFormat)));
6164  }
6165 
6166  return lines;
6167  }
6168 
6169  TH2Painter.prototype.GetCandleTips = function(p) {
6170  var lines = [], main = this.frame_painter(), histo = this.GetHisto();
6171 
6172  lines.push(this.GetTipName());
6173 
6174  lines.push("x = " + main.AxisAsText("x", histo.fXaxis.GetBinLowEdge(p.bin+1)));
6175 
6176  lines.push('mean y = ' + JSROOT.FFormat(p.meany, JSROOT.gStyle.fStatFormat))
6177  lines.push('m25 = ' + JSROOT.FFormat(p.m25y, JSROOT.gStyle.fStatFormat))
6178  lines.push('p25 = ' + JSROOT.FFormat(p.p25y, JSROOT.gStyle.fStatFormat))
6179 
6180  return lines;
6181  }
6182 
6183  TH2Painter.prototype.ProvidePolyBinHints = function(binindx, realx, realy) {
6184 
6185  var histo = this.GetHisto(),
6186  bin = histo.fBins.arr[binindx],
6187  pmain = this.frame_painter(),
6188  binname = bin.fPoly.fName,
6189  lines = [], numpoints = 0;
6190 
6191  if (binname === "Graph") binname = "";
6192  if (binname.length === 0) binname = bin.fNumber;
6193 
6194  if ((realx===undefined) && (realy===undefined)) {
6195  realx = realy = 0;
6196  var gr = bin.fPoly, numgraphs = 1;
6197  if (gr._typename === 'TMultiGraph') { numgraphs = bin.fPoly.fGraphs.arr.length; gr = null; }
6198 
6199  for (var ngr=0;ngr<numgraphs;++ngr) {
6200  if (!gr || (ngr>0)) gr = bin.fPoly.fGraphs.arr[ngr];
6201 
6202  for (var n=0;n<gr.fNpoints;++n) {
6203  ++numpoints;
6204  realx += gr.fX[n];
6205  realy += gr.fY[n];
6206  }
6207  }
6208 
6209  if (numpoints > 1) {
6210  realx = realx / numpoints;
6211  realy = realy / numpoints;
6212  }
6213  }
6214 
6215  lines.push(this.GetTipName());
6216  lines.push("x = " + pmain.AxisAsText("x", realx));
6217  lines.push("y = " + pmain.AxisAsText("y", realy));
6218  if (numpoints > 0) lines.push("npnts = " + numpoints);
6219  lines.push("bin = " + binname);
6220  if (bin.fContent === Math.round(bin.fContent))
6221  lines.push("content = " + bin.fContent);
6222  else
6223  lines.push("content = " + JSROOT.FFormat(bin.fContent, JSROOT.gStyle.fStatFormat));
6224  return lines;
6225  }
6226 
6227  TH2Painter.prototype.ProcessTooltip = function(pnt) {
6228  if (!pnt || !this.draw_content || !this.draw_g || !this.tt_handle || this.options.Proj) {
6229  if (this.draw_g !== null)
6230  this.draw_g.select(".tooltip_bin").remove();
6231  return null;
6232  }
6233 
6234  var histo = this.GetHisto(),
6235  h = this.tt_handle,
6236  ttrect = this.draw_g.select(".tooltip_bin");
6237 
6238  if (h.poly) {
6239  // process tooltips from TH2Poly
6240 
6241  var pmain = this.frame_painter(),
6242  realx, realy, foundindx = -1;
6243 
6244  if (pmain.grx === pmain.x) realx = pmain.x.invert(pnt.x);
6245  if (pmain.gry === pmain.y) realy = pmain.y.invert(pnt.y);
6246 
6247  if ((realx!==undefined) && (realy!==undefined)) {
6248  var i, len = histo.fBins.arr.length, bin;
6249 
6250  for (i = 0; (i < len) && (foundindx < 0); ++ i) {
6251  bin = histo.fBins.arr[i];
6252 
6253  // found potential bins candidate
6254  if ((realx < bin.fXmin) || (realx > bin.fXmax) ||
6255  (realy < bin.fYmin) || (realy > bin.fYmax)) continue;
6256 
6257  // ignore empty bins with col0 option
6258  if ((bin.fContent === 0) && !this.options.Zero) continue;
6259 
6260  var gr = bin.fPoly, numgraphs = 1;
6261  if (gr._typename === 'TMultiGraph') { numgraphs = bin.fPoly.fGraphs.arr.length; gr = null; }
6262 
6263  for (var ngr=0;ngr<numgraphs;++ngr) {
6264  if (!gr || (ngr>0)) gr = bin.fPoly.fGraphs.arr[ngr];
6265  if (gr.IsInside(realx,realy)) {
6266  foundindx = i;
6267  break;
6268  }
6269  }
6270  }
6271  }
6272 
6273  if (foundindx < 0) {
6274  ttrect.remove();
6275  return null;
6276  }
6277 
6278  var res = { name: histo.fName, title: histo.fTitle,
6279  x: pnt.x, y: pnt.y,
6280  color1: this.lineatt ? this.lineatt.color : 'green',
6281  color2: this.fillatt ? this.fillatt.fillcoloralt('blue') : "blue",
6282  exact: true, menu: true,
6283  lines: this.ProvidePolyBinHints(foundindx, realx, realy) };
6284 
6285  if (pnt.disabled) {
6286  ttrect.remove();
6287  res.changed = true;
6288  } else {
6289 
6290  if (ttrect.empty())
6291  ttrect = this.draw_g.append("svg:path")
6292  .attr("class","tooltip_bin h1bin")
6293  .style("pointer-events","none");
6294 
6295  res.changed = ttrect.property("current_bin") !== foundindx;
6296 
6297  if (res.changed)
6298  ttrect.attr("d", this.CreatePolyBin(pmain, bin))
6299  .style("opacity", "0.7")
6300  .property("current_bin", foundindx);
6301  }
6302 
6303  if (res.changed)
6304  res.user_info = { obj: histo, name: histo.fName,
6305  bin: foundindx,
6306  cont: bin.fContent,
6307  grx: pnt.x, gry: pnt.y };
6308 
6309  return res;
6310 
6311  } else if (h.candle) {
6312  // process tooltips for candle
6313 
6314  var p, i;
6315 
6316  for (i=0;i<h.candle.length;++i) {
6317  p = h.candle[i];
6318  if ((p.x1 <= pnt.x) && (pnt.x <= p.x2) && (p.yy1 <= pnt.y) && (pnt.y <= p.yy2)) break;
6319  }
6320 
6321  if (i>=h.candle.length) {
6322  ttrect.remove();
6323  return null;
6324  }
6325 
6326  var res = { name: histo.fName, title: histo.fTitle,
6327  x: pnt.x, y: pnt.y,
6328  color1: this.lineatt ? this.lineatt.color : 'green',
6329  color2: this.fillatt ? this.fillatt.fillcoloralt('blue') : "blue",
6330  lines: this.GetCandleTips(p), exact: true, menu: true };
6331 
6332  if (pnt.disabled) {
6333  ttrect.remove();
6334  res.changed = true;
6335  } else {
6336 
6337  if (ttrect.empty())
6338  ttrect = this.draw_g.append("svg:rect")
6339  .attr("class","tooltip_bin h1bin")
6340  .style("pointer-events","none");
6341 
6342  res.changed = ttrect.property("current_bin") !== i;
6343 
6344  if (res.changed)
6345  ttrect.attr("x", p.x1)
6346  .attr("width", p.x2-p.x1)
6347  .attr("y", p.yy1)
6348  .attr("height", p.yy2- p.yy1)
6349  .style("opacity", "0.7")
6350  .property("current_bin", i);
6351  }
6352 
6353  if (res.changed)
6354  res.user_info = { obj: histo, name: histo.fName,
6355  bin: i+1, cont: p.median, binx: i+1, biny: 1,
6356  grx: pnt.x, gry: pnt.y };
6357 
6358  return res;
6359  }
6360 
6361  var i, j, binz = 0, colindx = null,
6362  i1, i2, j1, j2, x1, x2, y1, y2;
6363 
6364  // search bins position
6365  for (i = h.i1; i < h.i2; ++i)
6366  if ((pnt.x>=h.grx[i]) && (pnt.x<=h.grx[i+1])) break;
6367 
6368  for (j = h.j1; j < h.j2; ++j)
6369  if ((pnt.y>=h.gry[j+1]) && (pnt.y<=h.gry[j])) break;
6370 
6371  if ((i < h.i2) && (j < h.j2)) {
6372 
6373  i1 = i; i2 = i+1; j1 = j; j2 = j+1;
6374  x1 = h.grx[i1]; x2 = h.grx[i2];
6375  y1 = h.gry[j2]; y2 = h.gry[j1];
6376 
6377  var match = true;
6378 
6379  if (this.options.Color) {
6380  // take into account bar settings
6381  var dx = x2 - x1, dy = y2 - y1;
6382  x2 = Math.round(x1 + dx*h.xbar2);
6383  x1 = Math.round(x1 + dx*h.xbar1);
6384  y2 = Math.round(y1 + dy*h.ybar2);
6385  y1 = Math.round(y1 + dy*h.ybar1);
6386  if ((pnt.x<x1) || (pnt.x>=x2) || (pnt.y<y1) || (pnt.y>=y2)) match = false;
6387  }
6388 
6389  binz = histo.getBinContent(i+1,j+1);
6390  if (this.is_projection) {
6391  colindx = 0; // just to avoid hide
6392  } else if (!match) {
6393  colindx = null;
6394  } else if (h.hide_only_zeros) {
6395  colindx = (binz === 0) && !this._show_empty_bins ? null : 0;
6396  } else {
6397  colindx = this.getContourColor(binz, true);
6398  if ((colindx === null) && (binz === 0) && this._show_empty_bins) colindx = 0;
6399  }
6400  }
6401 
6402  if (colindx === null) {
6403  ttrect.remove();
6404  return null;
6405  }
6406 
6407  var res = { name: histo.fName, title: histo.fTitle,
6408  x: pnt.x, y: pnt.y,
6409  color1: this.lineatt ? this.lineatt.color : 'green',
6410  color2: this.fillatt ? this.fillatt.fillcoloralt('blue') : "blue",
6411  lines: this.GetBinTips(i, j), exact: true, menu: true };
6412 
6413  if (this.options.Color) res.color2 = this.GetPalette().getColor(colindx);
6414 
6415  if (pnt.disabled && !this.is_projection) {
6416  ttrect.remove();
6417  res.changed = true;
6418  } else {
6419  if (ttrect.empty())
6420  ttrect = this.draw_g.append("svg:rect")
6421  .attr("class","tooltip_bin h1bin")
6422  .style("pointer-events","none");
6423 
6424  var binid = i*10000 + j;
6425 
6426  if (this.is_projection == "X") {
6427  x1 = 0; x2 = this.frame_width();
6428  if (this.projection_width > 1) {
6429  var dd = (this.projection_width-1)/2;
6430  if (j2+dd >= h.j2) { j2 = Math.min(Math.round(j2+dd), h.j2); j1 = Math.max(j2 - this.projection_width, h.j1); }
6431  else { j1 = Math.max(Math.round(j1-dd), h.j1); j2 = Math.min(j1 + this.projection_width, h.j2); }
6432  }
6433  y1 = h.gry[j2]; y2 = h.gry[j1];
6434  binid = j1*777 + j2*333;
6435  } else if (this.is_projection == "Y") {
6436  y1 = 0; y2 = this.frame_height();
6437  if (this.projection_width > 1) {
6438  var dd = (this.projection_width-1)/2;
6439  if (i2+dd >= h.i2) { i2 = Math.min(Math.round(i2+dd), h.i2); i1 = Math.max(i2 - this.projection_width, h.i1); }
6440  else { i1 = Math.max(Math.round(i1-dd), h.i1); i2 = Math.min(i1 + this.projection_width, h.i2); }
6441  }
6442  x1 = h.grx[i1], x2 = h.grx[i2],
6443  binid = i1*777 + i2*333;
6444  }
6445 
6446  res.changed = ttrect.property("current_bin") !== binid;
6447 
6448  if (res.changed)
6449  ttrect.attr("x", x1)
6450  .attr("width", x2 - x1)
6451  .attr("y", y1)
6452  .attr("height", y2 - y1)
6453  .style("opacity", "0.7")
6454  .property("current_bin", binid);
6455 
6456  if (this.is_projection && res.changed)
6457  this.RedrawProjection(i1, i2, j1, j2);
6458  }
6459 
6460  if (res.changed)
6461  res.user_info = { obj: histo, name: histo.fName,
6462  bin: histo.getBin(i+1, j+1), cont: binz, binx: i+1, biny: j+1,
6463  grx: pnt.x, gry: pnt.y };
6464 
6465  return res;
6466  }
6467 
6468  TH2Painter.prototype.CanZoomIn = function(axis,min,max) {
6469  // check if it makes sense to zoom inside specified axis range
6470  if (axis=="z") return true;
6471 
6472  var obj = this.GetHisto();
6473  if (obj) obj = (axis=="y") ? obj.fYaxis : obj.fXaxis;
6474 
6475  return !obj || (obj.FindBin(max,0.5) - obj.FindBin(min,0) > 1);
6476  }
6477 
6478  TH2Painter.prototype.Draw2D = function(call_back, resize) {
6479 
6480  this.Clear3DScene();
6481  this.mode3d = false;
6482 
6483  this.CreateXY();
6484 
6485  // draw new palette, resize frame if required
6486  var pp = this.DrawColorPalette(this.options.Zscale && (this.options.Color || this.options.Contour), true);
6487 
6488  this.DrawAxes();
6489 
6490  this.DrawBins();
6491 
6492  // redraw palette till the end when contours are available
6493  if (pp) pp.DrawPave();
6494 
6495  this.DrawTitle();
6496 
6497  this.UpdateStatWebCanvas();
6498 
6499  this.AddInteractive();
6500 
6501  JSROOT.CallBack(call_back);
6502  }
6503 
6504  TH2Painter.prototype.Draw3D = function(call_back, resize) {
6505  this.mode3d = true;
6506  JSROOT.AssertPrerequisites('hist3d', function() {
6507  this.Draw3D(call_back, resize);
6508  }.bind(this));
6509  }
6510 
6511  TH2Painter.prototype.CallDrawFunc = function(callback, resize) {
6512 
6513  var main = this.main_painter(),
6514  fp = this.frame_painter();
6515 
6516  if ((main!==this) && fp && (fp.mode3d !== this.options.Mode3D))
6517  this.CopyOptionsFrom(main);
6518 
6519  var funcname = this.options.Mode3D ? "Draw3D" : "Draw2D";
6520  this[funcname](callback, resize);
6521  }
6522 
6523  TH2Painter.prototype.Redraw = function(resize) {
6524  this.CallDrawFunc(null, resize);
6525  }
6526 
6527  function drawHistogram2D(divid, histo, opt) {
6528  // create painter and add it to canvas
6529  var painter = new JSROOT.TH2Painter(histo);
6530 
6531  painter.SetDivId(divid, 1);
6532 
6533  // here we deciding how histogram will look like and how will be shown
6534  painter.DecodeOptions(opt);
6535 
6536  if (painter.IsTH2Poly()) {
6537  if (painter.options.Mode3D) painter.options.Lego = 12; // lego always 12
6538  else if (!painter.options.Color) painter.options.Color = true; // default is color
6539  }
6540 
6541  painter._show_empty_bins = false;
6542 
6543  painter._can_move_colz = true;
6544 
6545  // special case for root 3D drawings - pad range is wired
6546  painter.CheckPadRange(!painter.options.Mode3D && (painter.options.Contour != 14));
6547 
6548  painter.ScanContent();
6549 
6550  painter.CreateStat(); // only when required
6551 
6552  painter.CallDrawFunc(function() {
6553  this.DrawNextFunction(0, function() {
6554  if (!this.Mode3D && this.options.AutoZoom) this.AutoZoom();
6555  this.FillToolbar();
6556  if (this.options.Project && !this.mode3d)
6557  this.ToggleProjection(this.options.Project);
6558  this.DrawingReady();
6559  }.bind(this));
6560  }.bind(painter));
6561 
6562  return painter;
6563  }
6564 
6565  // =================================================================================
6566 
6567 
6568  function createTF2Histogram(func, nosave, hist) {
6569  var nsave = 0, npx = 0, npy = 0;
6570  if (!nosave) {
6571  nsave = func.fSave.length;
6572  npx = Math.round(func.fSave[nsave-2]);
6573  npy = Math.round(func.fSave[nsave-1]);
6574  if (nsave !== (npx+1)*(npy+1) + 6) nsave = 0;
6575  }
6576 
6577  if (nsave > 6) {
6578  var dx = (func.fSave[nsave-5] - func.fSave[nsave-6]) / npx / 2,
6579  dy = (func.fSave[nsave-3] - func.fSave[nsave-4]) / npy / 2;
6580 
6581  if (!hist) hist = JSROOT.CreateHistogram("TH2F", npx+1, npy+1);
6582 
6583  hist.fXaxis.fXmin = func.fSave[nsave-6] - dx;
6584  hist.fXaxis.fXmax = func.fSave[nsave-5] + dx;
6585 
6586  hist.fYaxis.fXmin = func.fSave[nsave-4] - dy;
6587  hist.fYaxis.fXmax = func.fSave[nsave-3] + dy;
6588 
6589  for (var k=0,j=0;j<=npy;++j)
6590  for (var i=0;i<=npx;++i)
6591  hist.setBinContent(hist.getBin(i+1,j+1), func.fSave[k++]);
6592 
6593  } else {
6594  npx = Math.max(func.fNpx, 2);
6595  npy = Math.max(func.fNpy, 2);
6596 
6597  if (!hist) hist = JSROOT.CreateHistogram("TH2F", npx, npy);
6598 
6599  hist.fXaxis.fXmin = func.fXmin;
6600  hist.fXaxis.fXmax = func.fXmax;
6601 
6602  hist.fYaxis.fXmin = func.fYmin;
6603  hist.fYaxis.fXmax = func.fYmax;
6604 
6605  for (var j=0;j<npy;++j)
6606  for (var i=0;i<npx;++i) {
6607  var x = func.fXmin + (i + 0.5) * (func.fXmax - func.fXmin) / npx,
6608  y = func.fYmin + (j + 0.5) * (func.fYmax - func.fYmin) / npy;
6609 
6610  hist.setBinContent(hist.getBin(i+1,j+1), func.evalPar(x,y));
6611  }
6612  }
6613 
6614  hist.fName = "Func";
6615  hist.fTitle = func.fTitle;
6616  hist.fMinimum = func.fMinimum;
6617  hist.fMaximum = func.fMaximum;
6618  //fHistogram->SetContour(fContour.fN, levels);
6619  hist.fLineColor = func.fLineColor;
6620  hist.fLineStyle = func.fLineStyle;
6621  hist.fLineWidth = func.fLineWidth;
6622  hist.fFillColor = func.fFillColor;
6623  hist.fFillStyle = func.fFillStyle;
6624  hist.fMarkerColor = func.fMarkerColor;
6625  hist.fMarkerStyle = func.fMarkerStyle;
6626  hist.fMarkerSize = func.fMarkerSize;
6627 
6628  hist.fBits |= JSROOT.TH1StatusBits.kNoStats;
6629 
6630  // only for testing - unfortunately, axis settings are not stored with TF2
6631  // hist.fXaxis.fTitle = "axis X";
6632  // hist.fXaxis.InvertBit(JSROOT.EAxisBits.kCenterTitle);
6633  // hist.fYaxis.fTitle = "axis Y";
6634  // hist.fYaxis.InvertBit(JSROOT.EAxisBits.kCenterTitle);
6635  // hist.fZaxis.fTitle = "axis Z";
6636  // hist.fZaxis.InvertBit(JSROOT.EAxisBits.kCenterTitle);
6637 
6638  return hist;
6639  }
6640 
6641  // TF2 always drawn via temporary TH2 object,
6642  // therefore there is no special painter class
6643 
6644  function drawTF2(divid, func, opt) {
6645 
6646  var d = new JSROOT.DrawOptions(opt);
6647 
6648  var hist = createTF2Histogram(func, d.check('NOSAVE'));
6649 
6650  if (d.empty()) opt = "cont3"; else
6651  if (d.opt === "SAME") opt = "cont2 same";
6652  else opt = d.opt;
6653 
6654  var hpainter = drawHistogram2D(divid, hist, opt);
6655 
6656  hpainter.tf2_typename = func._typename;
6657  hpainter.tf2_nosave = d.check('NOSAVE');
6658 
6659  hpainter.UpdateObject = function(obj, opt) {
6660  if (!obj || (this.tf2_typename != obj._typename)) return false;
6661 
6662  createTF2Histogram(obj, this.tf2_nosave, this.GetHisto());
6663 
6664  return true;
6665  }
6666 
6667  return hpainter;
6668  }
6669 
6670  // ====================================================================
6671 
6672  function THStackPainter(stack, opt) {
6673  JSROOT.TObjectPainter.call(this, stack, opt);
6674 
6675  this.firstpainter = null;
6676  this.painters = []; // keep painters to be able update objects
6677  }
6678 
6679  THStackPainter.prototype = Object.create(JSROOT.TObjectPainter.prototype);
6680 
6681  THStackPainter.prototype.Cleanup = function() {
6682  var pp = this.pad_painter();
6683  if (pp) pp.CleanPrimitives(this.Selector.bind(this, true));
6684  delete this.firstpainter;
6685  delete this.painters;
6686  JSROOT.TObjectPainter.prototype.Cleanup.call(this);
6687  }
6688 
6689  THStackPainter.prototype.HasErrors = function(hist) {
6690  if (hist.fSumw2 && (hist.fSumw2.length > 0))
6691  for (var n=0;n<hist.fSumw2.length;++n)
6692  if (hist.fSumw2[n] > 0) return true;
6693  return false;
6694  }
6695 
6696  THStackPainter.prototype.BuildStack = function(stack) {
6697  // build sum of all histograms
6698  // Build a separate list fStack containing the running sum of all histograms
6699 
6700  if (!stack.fHists) return false;
6701  var nhists = stack.fHists.arr.length;
6702  if (nhists <= 0) return false;
6703  var lst = JSROOT.Create("TList");
6704  lst.Add(JSROOT.clone(stack.fHists.arr[0]), stack.fHists.opt[0]);
6705  for (var i=1;i<nhists;++i) {
6706  var hnext = JSROOT.clone(stack.fHists.arr[i]),
6707  hnextopt = stack.fHists.opt[i],
6708  hprev = lst.arr[i-1];
6709 
6710  if ((hnext.fNbins != hprev.fNbins) ||
6711  (hnext.fXaxis.fXmin != hprev.fXaxis.fXmin) ||
6712  (hnext.fXaxis.fXmax != hprev.fXaxis.fXmax)) {
6713  JSROOT.console("When drawing THStack, cannot sum-up histograms " + hnext.fName + " and " + hprev.fName);
6714  lst.Clear();
6715  return false;
6716  }
6717 
6718  // trivial sum of histograms
6719  for (var n = 0; n < hnext.fArray.length; ++n)
6720  hnext.fArray[n] += hprev.fArray[n];
6721 
6722  lst.Add(hnext, hnextopt);
6723  }
6724  stack.fStack = lst;
6725  return true;
6726  }
6727 
6728  THStackPainter.prototype.GetHistMinMax = function(hist, witherr) {
6729  var res = { min : 0, max : 0 },
6730  domin = true, domax = true;
6731  if (hist.fMinimum !== -1111) {
6732  res.min = hist.fMinimum;
6733  domin = false;
6734  }
6735  if (hist.fMaximum !== -1111) {
6736  res.max = hist.fMaximum;
6737  domax = false;
6738  }
6739 
6740  if (!domin && !domax) return res;
6741 
6742  var i1 = 1, i2 = hist.fXaxis.fNbins, j1 = 1, j2 = 1, first = true;
6743 
6744  if (hist.fXaxis.TestBit(JSROOT.EAxisBits.kAxisRange)) {
6745  i1 = hist.fXaxis.fFirst;
6746  i2 = hist.fXaxis.fLast;
6747  }
6748 
6749  if (hist._typename.indexOf("TH2")===0) {
6750  j2 = hist.fYaxis.fNbins;
6751  if (hist.fYaxis.TestBit(JSROOT.EAxisBits.kAxisRange)) {
6752  j1 = hist.fYaxis.fFirst;
6753  j2 = hist.fYaxis.fLast;
6754  }
6755  }
6756  for (var j=j1; j<=j2;++j)
6757  for (var i=i1; i<=i2;++i) {
6758  var val = hist.getBinContent(i, j),
6759  err = witherr ? hist.getBinError(hist.getBin(i,j)) : 0;
6760  if (domin && (first || (val-err < res.min))) res.min = val-err;
6761  if (domax && (first || (val+err > res.max))) res.max = val+err;
6762  first = false;
6763  }
6764 
6765  return res;
6766  }
6767 
6768  THStackPainter.prototype.GetMinMax = function(iserr, pad) {
6769  var res = { min: 0, max: 0 },
6770  stack = this.GetObject();
6771 
6772  if (this.options.nostack) {
6773  for (var i = 0; i < stack.fHists.arr.length; ++i) {
6774  var resh = this.GetHistMinMax(stack.fHists.arr[i], iserr);
6775  if (i==0) {
6776  res = resh;
6777  } else {
6778  res.min = Math.min(res.min, resh.min);
6779  res.max = Math.max(res.max, resh.max);
6780  }
6781  }
6782  } else {
6783  res.min = this.GetHistMinMax(stack.fStack.arr[0], iserr).min;
6784  res.max = this.GetHistMinMax(stack.fStack.arr[stack.fStack.arr.length-1], iserr).max;
6785  }
6786 
6787  if (stack.fMaximum != -1111) res.max = stack.fMaximum;
6788  res.max *= 1.05;
6789  if (stack.fMinimum != -1111) res.min = stack.fMinimum;
6790 
6791  if (pad && (this.options.ndim == 1 ? pad.fLogy : pad.fLogz)) {
6792  if (res.max<=0) res.max = 1;
6793  if (res.min<=0) res.min = 1e-4*res.max;
6794  var kmin = 1/(1 + 0.5*JSROOT.log10(res.max / res.min)),
6795  kmax = 1 + 0.2*JSROOT.log10(res.max / res.min);
6796  res.min *= kmin;
6797  res.max *= kmax;
6798  } else {
6799  if ((res.min>0) && (res.min < 0.05*res.max)) res.min = 0;
6800  }
6801 
6802  return res;
6803  }
6804 
6805  THStackPainter.prototype.DrawNextHisto = function(indx, mm, subp, reenter) {
6806  if (mm === "callback") {
6807  mm = null; // just misuse min/max argument to indicate callback
6808  if (indx<0) this.firstpainter = subp;
6809  else this.painters.push(subp);
6810  indx++;
6811  }
6812 
6813  var stack = this.GetObject(),
6814  hist = stack.fHistogram, hopt = "",
6815  hlst = this.options.nostack ? stack.fHists : stack.fStack,
6816  nhists = (hlst && hlst.arr) ? hlst.arr.length : 0, rindx = 0;
6817 
6818  if (indx>=nhists) return this.DrawingReady();
6819 
6820  if ((indx % 500 === 499) && !reenter)
6821  return setTimeout(this.DrawNextHisto.bind(this, indx, mm, subp, true), 0);
6822 
6823  if (indx>=0) {
6824  rindx = this.options.horder ? indx : nhists-indx-1;
6825  hist = hlst.arr[rindx];
6826  hopt = hlst.opt[rindx] || hist.fOption || this.options.hopt;
6827  if (hopt.toUpperCase().indexOf(this.options.hopt)<0) hopt += this.options.hopt;
6828  if (this.options.draw_errors && !hopt) hopt = "E";
6829  hopt += " same nostat";
6830 
6831  // if there is auto colors assignment, try to provide it
6832  if (this.options._pfc || this.options._plc || this.options._pmc) {
6833  if (!this.pallette && JSROOT.Painter.GetColorPalette)
6834  this.palette = JSROOT.Painter.GetColorPalette();
6835  if (this.palette) {
6836  var color = this.palette.calcColor(rindx, nhists+1);
6837  var icolor = this.add_color(color);
6838 
6839  if (this.options._pfc) hist.fFillColor = icolor;
6840  if (this.options._plc) hist.fLineColor = icolor;
6841  if (this.options._pmc) hist.fMarkerColor = icolor;
6842  }
6843  }
6844 
6845  } else {
6846  hopt = this.options.hopt + " axis";
6847  // if (mm && (!this.options.nostack || (hist.fMinimum==-1111 && hist.fMaximum==-1111))) hopt += ";minimum:" + mm.min + ";maximum:" + mm.max;
6848  if (mm) hopt += ";minimum:" + mm.min + ";maximum:" + mm.max;
6849  }
6850 
6851  // special handling of stacked histograms - set $baseh object for correct drawing
6852  // also used to provide tooltips
6853  if ((rindx > 0) && !this.options.nostack) hist.$baseh = hlst.arr[rindx - 1];
6854 
6855  JSROOT.draw(this.divid, hist, hopt, this.DrawNextHisto.bind(this, indx, "callback"));
6856  }
6857 
6858  THStackPainter.prototype.DecodeOptions = function(opt) {
6859  if (!this.options) this.options = {};
6860  JSROOT.extend(this.options, { ndim: 1, nostack: false, same: false, horder: true, has_errors: false, draw_errors: false, hopt: "" });
6861 
6862  var stack = this.GetObject(),
6863  hist = stack.fHistogram || (stack.fHists ? stack.fHists.arr[0] : null) || (stack.fStack ? stack.fStack.arr[0] : null);
6864 
6865  if (hist && (hist._typename.indexOf("TH2")==0)) this.options.ndim = 2;
6866 
6867  if ((this.options.ndim==2) && !opt) opt = "lego1";
6868 
6869  if (stack.fHists && !this.options.nostack) {
6870  for (var k=0;k<stack.fHists.arr.length;++k)
6871  this.options.has_errors = this.options.has_errors || this.HasErrors(stack.fHists.arr[k]);
6872  }
6873 
6874  var d = new JSROOT.DrawOptions(opt);
6875 
6876  this.options.nostack = d.check("NOSTACK");
6877  if (d.check("STACK")) this.options.nostack = false;
6878  this.options.same = d.check("SAME");
6879 
6880  this.options._pfc = d.check("PFC");
6881  this.options._plc = d.check("PLC");
6882  this.options._pmc = d.check("PMC");
6883 
6884  this.options.hopt = d.remain(); // use remaining draw options for histogram draw
6885 
6886  var dolego = d.check("LEGO");
6887 
6888  this.options.errors = d.check("E");
6889 
6890  // if any histogram appears with pre-calculated errors, use E for all histograms
6891  if (!this.options.nostack && this.options.has_errors && !dolego && !d.check("HIST") && (this.options.hopt.indexOf("E")<0)) this.options.draw_errors = true;
6892 
6893  this.options.horder = this.options.nostack || dolego;
6894  }
6895 
6896  THStackPainter.prototype.CreateHistogram = function(stack) {
6897  var histos = stack.fHists,
6898  numhistos = histos ? histos.arr.length : 0;
6899 
6900  if (!numhistos) {
6901  var histo = JSROOT.CreateHistogram("TH1I", 100);
6902  histo.fTitle = stack.fTitle;
6903  return histo;
6904  }
6905 
6906  var h0 = histos.arr[0];
6907  var histo = JSROOT.CreateHistogram((this.options.ndim==1) ? "TH1I" : "TH2I", h0.fXaxis.fNbins, h0.fYaxis.fNbins);
6908  histo.fName = "axis_hist";
6909  JSROOT.extend(histo.fXaxis, h0.fXaxis);
6910  if (this.options.ndim==2)
6911  JSROOT.extend(histo.fYaxis, h0.fYaxis);
6912 
6913  // this code is not exists in ROOT painter, can be skipped?
6914  for (var n=1;n<numhistos;++n) {
6915  var h = histos.arr[n];
6916 
6917  if (!histo.fXaxis.fLabels) {
6918  histo.fXaxis.fXmin = Math.min(histo.fXaxis.fXmin, h.fXaxis.fXmin);
6919  histo.fXaxis.fXmax = Math.max(histo.fXaxis.fXmax, h.fXaxis.fXmax);
6920  }
6921 
6922  if ((this.options.ndim==2) && !histo.fYaxis.fLabels) {
6923  histo.fYaxis.fXmin = Math.min(histo.fYaxis.fXmin, h.fYaxis.fXmin);
6924  histo.fYaxis.fXmax = Math.max(histo.fYaxis.fXmax, h.fYaxis.fXmax);
6925  }
6926  }
6927 
6928  histo.fTitle = stack.fTitle;
6929 
6930  return histo;
6931  }
6932 
6933  THStackPainter.prototype.drawStack = function() {
6934 
6935  var stack = this.GetObject();
6936 
6937  // when building stack, one could fail to sum up histograms
6938  if (!this.options.nostack)
6939  this.options.nostack = ! this.BuildStack(stack);
6940 
6941  if (!stack.fHistogram && !this.options.same)
6942  stack.fHistogram = this.CreateHistogram(stack);
6943 
6944  var mm = this.GetMinMax(this.options.errors || this.options.draw_errors, this.root_pad());
6945 
6946  this.DrawNextHisto(this.options.same ? 0 : -1, mm);
6947  return this;
6948  }
6949 
6950  THStackPainter.prototype.UpdateObject = function(obj) {
6951  if (!this.MatchObjectType(obj)) return false;
6952 
6953  var stack = this.GetObject();
6954 
6955  stack.fHists = obj.fHists;
6956  stack.fStack = obj.fStack;
6957 
6958  if (!this.options.nostack) {
6959  this.options.nostack = !this.BuildStack(stack);
6960  }
6961 
6962  var isany = false;
6963  if (this.firstpainter) {
6964  var src = obj.fHistogram;
6965  if (!src)
6966  src = stack.fHistogram = this.CreateHistogram(stack);
6967 
6968  this.firstpainter.UpdateObject(src);
6969 
6970  var mm = this.GetMinMax(this.options.errors || this.options.draw_errors, this.root_pad());
6971 
6972  this.firstpainter.options.minimum = mm.min;
6973  this.firstpainter.options.maximum = mm.max;
6974 
6975  if (this.options.ndim == 1) {
6976  this.firstpainter.ymin = mm.min;
6977  this.firstpainter.ymax = mm.max;
6978  }
6979 
6980  isany = true;
6981  }
6982 
6983  // try fully remove old histogram painters
6984  var pp = this.pad_painter();
6985  if (pp) pp.CleanPrimitives(this.Selector.bind(this, false));
6986  this.painters = [];
6987 
6988  this.did_update = isany;
6989 
6990  return isany;
6991  }
6992 
6994  THStackPainter.prototype.Selector = function(fullclear, painter) {
6995  if (fullclear && (painter===this.firstpainter)) return true;
6996  return this.painters.indexOf(painter) >= 0;
6997  }
6998 
7000  THStackPainter.prototype.Redraw = function() {
7001  // do nothing in case of simple redraw
7002  if (!this.did_update) return;
7003 
7004  // remove flag set in update
7005  delete this.did_update;
7006 
7007  if (this.firstpainter)
7008  this.firstpainter.Redraw();
7009 
7010  this.DrawNextHisto(0, null);
7011  }
7012 
7013  function drawHStack(divid, stack, opt) {
7014  // paint the list of histograms
7015  // By default, histograms are shown stacked.
7016  // - the first histogram is paint
7017  // - then the sum of the first and second, etc
7018 
7019  var painter = new THStackPainter(stack, opt);
7020  painter.SetDivId(divid, -1); // it maybe no element to set divid
7021  painter.DecodeOptions(opt);
7022 
7023  if (!stack.fHists || (stack.fHists.arr.length == 0)) return painter.DrawingReady();
7024 
7025  painter.drawStack();
7026 
7027  painter.SetDivId(divid); // only when first histogram drawn, we could assign divid
7028 
7029  return painter;
7030  }
7031 
7032  // =================================================================================
7033 
7034  // kept for backward compatibility, will be removed in future JSROOT versions
7035  JSROOT.Painter.drawLegend = drawPave;
7036  JSROOT.Painter.drawPaveText = drawPave;
7037 
7038  JSROOT.Painter.drawPave = drawPave;
7039  JSROOT.Painter.produceLegend = produceLegend;
7040  JSROOT.Painter.drawHistogram1D = drawHistogram1D;
7041  JSROOT.Painter.drawHistogram2D = drawHistogram2D;
7042  JSROOT.Painter.drawTF2 = drawTF2;
7043  JSROOT.Painter.drawHStack = drawHStack;
7044 
7045  JSROOT.TPavePainter = TPavePainter;
7046  JSROOT.THistPainter = THistPainter;
7047  JSROOT.TH1Painter = TH1Painter;
7048  JSROOT.TH2Painter = TH2Painter;
7049  JSROOT.THStackPainter = THStackPainter;
7050 
7051  return JSROOT;
7052 
7053 }));