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"));
10 if (typeof d3 !=
'object')
11 throw new Error(
'This extension requires d3.js',
'JSRootPainter.v6.js');
12 if (typeof JSROOT ==
'undefined')
13 throw new Error(
'JSROOT is not defined',
'JSRootPainter.v6.js');
14 if (typeof JSROOT.Painter !=
'object')
15 throw new Error(
'JSROOT.Painter not defined',
'JSRootPainter.v6.js');
18 } (
function(JSROOT, d3) {
22 JSROOT.sources.push(
"v6");
25 JSROOT.WebSnapIds = { kNone: 0, kObject: 1, kSVG: 2, kSubPad: 3, kColors: 4, kStyle: 5 };
40 function TAxisPainter(axis, embedded) {
41 JSROOT.TObjectPainter.call(
this, axis);
43 this.embedded = embedded;
55 this.invert_side =
false;
56 this.lbls_both_sides =
false;
59 TAxisPainter.prototype = Object.create(JSROOT.TObjectPainter.prototype);
61 TAxisPainter.prototype.Cleanup =
function() {
67 JSROOT.TObjectPainter.prototype.Cleanup.call(
this);
70 TAxisPainter.prototype.SetAxisConfig =
function(name, kind, func, min, max, smin, smax) {
77 this.scale_min = smin;
78 this.scale_max = smax;
81 TAxisPainter.prototype.format10Exp =
function(order, value) {
84 value = Math.round(value/Math.pow(10,order));
85 if ((value!=0) && (value!=1)) res = value.toString() + (JSROOT.gStyle.Latex ?
"#times" :
"x");
88 if (JSROOT.gStyle.Latex > 1)
return res +
"^{" + order +
"}";
89 var superscript_symbols = {
90 '0':
'\u2070',
'1':
'\xB9',
'2':
'\xB2',
'3':
'\xB3',
'4':
'\u2074',
'5':
'\u2075',
91 '6':
'\u2076',
'7':
'\u2077',
'8':
'\u2078',
'9':
'\u2079',
'-':
'\u207B'
93 var str = order.toString();
94 for (var n=0;n<str.length;++n) res += superscript_symbols[str[n]];
98 TAxisPainter.prototype.CreateFormatFuncs =
function() {
100 var axis = this.GetObject(),
101 is_gaxis = (axis && axis._typename ===
'TGaxis');
106 if (is_gaxis) ndiv = axis.fNdiv;
else
107 if (axis) ndiv = Math.max(axis.fNdivisions, 4);
109 this.nticks = ndiv % 100;
110 this.nticks2 = (ndiv % 10000 - this.nticks) / 100;
111 this.nticks3 = Math.floor(ndiv/10000);
113 if (axis && !is_gaxis && (this.nticks > 7)) this.nticks = 7;
115 var gr_range = Math.abs(this.func.range()[1] - this.func.range()[0]);
116 if (gr_range<=0) gr_range = 100;
118 if (this.kind ==
'time') {
119 if (this.nticks > 8) this.nticks = 8;
121 var scale_range = this.scale_max - this.scale_min,
122 tf1 = JSROOT.Painter.getTimeFormat(axis),
123 tf2 = JSROOT.Painter.chooseTimeFormat(scale_range / gr_range,
false);
125 if ((tf1.length == 0) || (scale_range < 0.1 * (
this.full_max -
this.full_min)))
126 tf1 = JSROOT.Painter.chooseTimeFormat(scale_range / this.nticks,
true);
128 this.tfunc1 = this.tfunc2 = d3.timeFormat(tf1);
130 this.tfunc2 = d3.timeFormat(tf2);
132 this.format =
function(d, asticks) {
133 return asticks ? this.tfunc1(d) : this.tfunc2(d);
136 }
else if (this.kind ==
'log') {
137 if (this.nticks2 > 1) {
138 this.nticks *= this.nticks2;
141 this.noexp = axis ? axis.TestBit(JSROOT.EAxisBits.kNoExponent) :
false;
142 if ((this.scale_max < 300) && (this.scale_min > 0.3)) this.noexp =
true;
143 this.moreloglabels = axis ? axis.TestBit(JSROOT.EAxisBits.kMoreLogLabels) :
false;
145 this.format =
function(d, asticks, notickexp_fmt) {
146 var val = parseFloat(d), rnd = Math.round(val);
148 return ((rnd === val) && (Math.abs(rnd)<1e9)) ? rnd.toString() : JSROOT.FFormat(val, notickexp_fmt || JSROOT.gStyle.fStatFormat);
150 if (val <= 0)
return null;
151 var vlog = JSROOT.log10(val);
152 if (this.moreloglabels || (Math.abs(vlog - Math.round(vlog))<0.001)) {
153 if (!this.noexp && !notickexp_fmt)
154 return this.format10Exp(Math.floor(vlog+0.01), val);
156 return (vlog<0) ? val.toFixed(Math.round(-vlog+0.5)) : val.toFixed(0);
160 }
else if (this.kind ==
'labels') {
162 var scale_range = this.scale_max - this.scale_min;
163 if (this.nticks > scale_range)
164 this.nticks = Math.round(scale_range);
166 this.regular_labels =
true;
168 if (axis && axis.fNbins && axis.fLabels) {
169 if ((axis.fNbins != Math.round(axis.fXmax - axis.fXmin)) ||
170 (axis.fXmin != 0) || (axis.fXmax != axis.fNbins)) {
171 this.regular_labels =
false;
179 this.format =
function(d) {
180 var indx = parseFloat(d);
181 if (!this.regular_labels)
182 indx = (indx - this.axis.fXmin)/(this.axis.fXmax -
this.axis.fXmin) * this.axis.fNbins;
183 indx = Math.round(indx);
184 if ((indx<0) || (indx>=this.axis.fNbins))
return null;
185 for (var i = 0; i < this.axis.fLabels.arr.length; ++i) {
186 var tstr = this.axis.fLabels.arr[i];
187 if (tstr.fUniqueID === indx+1)
return tstr.fString;
196 this.format =
function(d, asticks, fmt) {
197 var val = parseFloat(d);
198 if (asticks && this.order) val = val / Math.pow(10, this.order);
200 if (val === Math.round(val))
201 return (Math.abs(val)<1e9) ? val.toFixed(0) : val.toExponential(4);
203 if (asticks)
return (this.ndig>10) ? val.toExponential(this.ndig-11) : val.toFixed(this.ndig);
205 return JSROOT.FFormat(val, fmt || JSROOT.gStyle.fStatFormat);
210 TAxisPainter.prototype.ProduceTicks =
function(ndiv, ndiv2) {
211 if (!this.noticksopt)
return this.func.ticks(ndiv * (ndiv2 || 1));
213 if (ndiv2) ndiv = (ndiv-1) * ndiv2;
214 var dom = this.func.domain(), ticks = [];
215 for (var n=0;n<=ndiv;++n)
216 ticks.push((dom[0]*(ndiv-n) + dom[1]*n)/ndiv);
220 TAxisPainter.prototype.CreateTicks =
function(only_major_as_array, optionNoexp, optionNoopt, optionInt) {
223 if (optionNoopt && this.nticks && (this.kind ==
"normal")) this.noticksopt =
true;
225 var handle = { nminor: 0, nmiddle: 0, nmajor: 0, func: this.func };
227 handle.minor = handle.middle = handle.major = this.ProduceTicks(this.nticks);
229 if (only_major_as_array) {
230 var res = handle.major, delta = (this.scale_max - this.scale_min)*1e-5;
231 if (res[0] > this.scale_min + delta) res.unshift(this.scale_min);
232 if (res[res.length-1] <
this.scale_max - delta) res.push(this.scale_max);
236 if ((this.kind ==
'labels') && !this.regular_labels) {
238 for (var n=0;n<this.axis.fNbins;++n) {
239 var x = this.axis.fXmin + n / this.axis.fNbins * (this.axis.fXmax - this.axis.fXmin);
240 if ((x >= this.scale_min) && (x < this.scale_max)) handle.lbl_pos.push(x);
244 if (this.nticks2 > 1) {
245 handle.minor = handle.middle = this.ProduceTicks(handle.major.length,
this.nticks2);
247 var gr_range = Math.abs(this.func.range()[1] - this.func.range()[0]);
250 if ((handle.middle.length <= handle.major.length) || (handle.middle.length > gr_range/3.5)) {
251 handle.minor = handle.middle = handle.major;
253 if ((this.nticks3 > 1) && (this.kind !==
'log')) {
254 handle.minor = this.ProduceTicks(handle.middle.length,
this.nticks3);
255 if ((handle.minor.length <= handle.middle.length) || (handle.minor.length > gr_range/1.7)) handle.minor = handle.middle;
259 handle.reset =
function() {
260 this.nminor = this.nmiddle = this.nmajor = 0;
263 handle.next =
function(doround) {
264 if (this.nminor >= this.minor.length)
return false;
266 this.tick = this.minor[this.nminor++];
267 this.grpos = this.func(this.tick);
268 if (doround) this.grpos = Math.round(this.grpos);
271 if ((this.nmiddle < this.middle.length) && (Math.abs(
this.grpos -
this.func(
this.middle[
this.nmiddle])) < 1)) {
276 if ((this.nmajor < this.major.length) && (Math.abs(this.grpos - this.func(this.major[this.nmajor])) < 1) ) {
283 handle.last_major =
function() {
284 return (this.kind !== 1) ?
false : this.nmajor == this.major.length;
287 handle.next_major_grpos =
function() {
288 if (this.nmajor >= this.major.length)
return null;
289 return this.func(this.major[this.nmajor]);
297 if ((this.kind ==
"normal") && (handle.major.length > 0)) {
299 var maxorder = 0, minorder = 0, exclorder3 =
false;
302 var maxtick = Math.max(Math.abs(handle.major[0]),Math.abs(handle.major[handle.major.length-1])),
303 mintick = Math.min(Math.abs(handle.major[0]),Math.abs(handle.major[handle.major.length-1])),
304 ord1 = (maxtick > 0) ? Math.round(JSROOT.log10(maxtick)/3)*3 : 0,
305 ord2 = (mintick > 0) ? Math.round(JSROOT.log10(mintick)/3)*3 : 0;
307 exclorder3 = (maxtick < 2e4);
309 if (maxtick || mintick) {
310 maxorder = Math.max(ord1,ord2) + 3;
311 minorder = Math.min(ord1,ord2) - 3;
317 var bestorder = 0, bestndig = this.ndig, bestlen = 1e10;
319 for (var order = minorder; order <= maxorder; order+=3) {
320 if (exclorder3 && (order===3))
continue;
323 var lbls = [], indx = 0, totallen = 0;
324 while (indx<handle.major.length) {
325 var lbl = this.format(handle.major[indx],
true);
326 if (lbls.indexOf(lbl)<0) {
328 totallen += lbl.length;
332 if (++this.ndig > 15)
break;
333 lbls = []; indx = 0; totallen = 0;
337 if (!order && (this.ndig<4)) totallen-=(handle.major.length*2+3);
339 if (totallen < bestlen) {
341 bestorder = this.order;
342 bestndig = this.ndig;
346 this.order = bestorder;
347 this.ndig = bestndig;
350 if (this.order) console.warn(
'Axis painter - integer labels are configured, but axis order ' + this.order +
' is preferable');
351 if (this.ndig) console.warn(
'Axis painter - integer labels are configured, but ' + this.ndig +
' decimal digits are required');
360 TAxisPainter.prototype.IsCenterLabels =
function() {
361 if (this.kind ===
'labels')
return true;
362 if (this.kind ===
'log')
return false;
363 var axis = this.GetObject();
364 return axis && axis.TestBit(JSROOT.EAxisBits.kCenterLabels);
367 TAxisPainter.prototype.AddTitleDrag =
function(title_g, vertical, offset_k, reverse, axis_length) {
368 if (!JSROOT.gStyle.MoveResize)
return;
370 var pthis =
this, drag_rect = null, prefix =
"", drag_move,
371 acc_x, acc_y, new_x, new_y, sign_0, center_0, alt_pos;
372 if (JSROOT._test_d3_ === 3) {
374 drag_move = d3.behavior.drag().origin(Object);
376 drag_move = d3.drag().subject(Object);
380 .on(prefix+
"start",
function() {
382 d3.event.sourceEvent.preventDefault();
383 d3.event.sourceEvent.stopPropagation();
385 var box = title_g.node().getBBox(),
386 axis = pthis.GetObject();
388 new_x = acc_x = title_g.property(
'shift_x');
389 new_y = acc_y = title_g.property(
'shift_y');
391 sign_0 = vertical ? (acc_x>0) : (acc_y>0);
393 if (axis.TestBit(JSROOT.EAxisBits.kCenterTitle))
394 alt_pos = (reverse === vertical) ? axis_length : 0;
396 alt_pos = Math.round(axis_length/2);
398 drag_rect = title_g.append(
"rect")
399 .classed(
"zoom",
true)
402 .attr(
"width", box.width)
403 .attr(
"height", box.height)
404 .style(
"cursor",
"move");
406 }).on(
"drag",
function() {
407 if (!drag_rect)
return;
409 d3.event.sourceEvent.preventDefault();
410 d3.event.sourceEvent.stopPropagation();
412 acc_x += d3.event.dx;
413 acc_y += d3.event.dy;
415 var set_x = title_g.property(
'shift_x'),
416 set_y = title_g.property(
'shift_y');
420 if (Math.abs(acc_y - set_y) > Math.abs(acc_y - alt_pos)) set_y = alt_pos;
423 if (Math.abs(acc_x - set_x) > Math.abs(acc_x - alt_pos)) set_x = alt_pos;
426 if (sign_0 === (vertical ? (set_x>0) : (set_y>0))) {
427 new_x = set_x; new_y = set_y;
428 title_g.attr(
'transform',
'translate(' + new_x +
',' + new_y +
')');
431 }).on(prefix+
"end",
function() {
432 if (!drag_rect)
return;
434 d3.event.sourceEvent.preventDefault();
435 d3.event.sourceEvent.stopPropagation();
437 title_g.property(
'shift_x', new_x)
438 .property(
'shift_y', new_y);
440 var axis = pthis.GetObject();
442 axis.fTitleOffset = (vertical ? new_x : new_y) / offset_k;
443 if ((vertical ? new_y : new_x) === alt_pos) axis.InvertBit(JSROOT.EAxisBits.kCenterTitle);
449 title_g.style(
"cursor",
"move").call(drag_move);
452 TAxisPainter.prototype.DrawAxis =
function(vertical, layer, w, h, transform, reverse, second_shift, disable_axis_drawing, max_text_width) {
455 var axis = this.GetObject(), chOpt =
"",
456 is_gaxis = (axis && axis._typename ===
'TGaxis'),
457 axis_g = layer, tickSize = 0.03,
458 scaling_size = 100, draw_lines =
true,
459 pad_w = this.pad_width() || 10,
460 pad_h = this.pad_height() || 10;
462 this.vertical = vertical;
464 function myXor(a,b) {
return ( a && !b ) || (!a && b); }
467 if (!second_shift) second_shift = 0;
else
468 if (this.invert_side) second_shift = -second_shift;
471 this.createAttLine({ attr: axis });
472 draw_lines = axis.fLineColor != 0;
474 tickSize = axis.fTickSize;
475 scaling_size = (vertical ? 1.7*h : 0.6*w);
477 this.createAttLine({ color: axis.fAxisColor, width: 1, style: 1 });
478 chOpt = myXor(vertical, this.invert_side) ?
"-S" :
"+S";
479 tickSize = axis.fTickLength;
480 scaling_size = (vertical ? pad_w : pad_h);
484 this.lineatt.not_standard =
true;
486 if (!is_gaxis || (this.name ===
"zaxis")) {
487 axis_g = layer.select(
"." + this.name +
"_container");
489 axis_g = layer.append(
"svg:g").attr(
"class",this.name +
"_container");
491 axis_g.selectAll(
"*").remove();
494 if (!disable_axis_drawing && draw_lines)
495 axis_g.append(
"svg:line")
496 .attr(
"x1",0).attr(
"y1",0)
497 .attr(
"x2",vertical ? 0 : w)
498 .attr(
"y2", vertical ? h : 0)
499 .call(this.lineatt.func);
501 axis_g.attr(
"transform", transform || null);
503 var side = 1, ticks_plusminus = 0,
504 text_scaling_size = Math.min(pad_w, pad_h),
505 optionPlus = (chOpt.indexOf(
"+")>=0),
506 optionMinus = (chOpt.indexOf(
"-")>=0),
507 optionSize = (chOpt.indexOf(
"S")>=0),
508 optionY = (chOpt.indexOf(
"Y")>=0),
509 optionUp = (chOpt.indexOf(
"0")>=0),
510 optionDown = (chOpt.indexOf(
"O")>=0),
511 optionUnlab = (chOpt.indexOf(
"U")>=0),
512 optionNoopt = (chOpt.indexOf(
"N")>=0),
513 optionInt = (chOpt.indexOf(
"I")>=0),
514 optionNoexp = axis.TestBit(JSROOT.EAxisBits.kNoExponent);
516 if (is_gaxis && axis.TestBit(JSROOT.EAxisBits.kTickPlus)) optionPlus =
true;
517 if (is_gaxis && axis.TestBit(JSROOT.EAxisBits.kTickMinus)) optionMinus =
true;
519 if (optionPlus && optionMinus) { side = 1; ticks_plusminus = 1; }
else
520 if (optionMinus) { side = myXor(reverse,vertical) ? 1 : -1; }
else
521 if (optionPlus) { side = myXor(reverse,vertical) ? -1 : 1; }
523 tickSize = Math.round((optionSize ? tickSize : 0.03) * scaling_size);
525 if (this.max_tick_size && (tickSize > this.max_tick_size)) tickSize = this.max_tick_size;
527 this.CreateFormatFuncs();
529 var res =
"", res2 =
"", lastpos = 0, lasth = 0;
535 var handle = this.CreateTicks(
false, optionNoexp, optionNoopt, optionInt);
537 while (handle.next(
true)) {
539 var h1 = Math.round(tickSize/4), h2 = 0;
542 h1 = Math.round(tickSize/2);
544 if (handle.kind == 1) {
546 if (!(
'format' in
this) || (this.format(handle.tick,
true)!==null)) h1 = tickSize;
547 this.ticks.push(handle.grpos);
550 if (ticks_plusminus > 0) h2 = -h1;
else
551 if (side < 0) { h2 = -h1; h1 = 0; }
else { h2 = 0; }
553 if (res.length == 0) {
554 res = vertical ? (
"M"+h1+
","+handle.grpos) : (
"M"+handle.grpos+
","+(-h1));
555 res2 = vertical ? (
"M"+(second_shift-h1)+
","+handle.grpos) : (
"M"+handle.grpos+
","+(second_shift+h1));
557 res += vertical ? (
"m"+(h1-lasth)+
","+(handle.grpos-lastpos)) : (
"m"+(handle.grpos-lastpos)+
","+(lasth-h1));
558 res2 += vertical ? (
"m"+(lasth-h1)+
","+(handle.grpos-lastpos)) : (
"m"+(handle.grpos-lastpos)+
","+(h1-lasth));
561 res += vertical ? (
"h"+ (h2-h1)) : (
"v"+ (h1-h2));
562 res2 += vertical ? (
"h"+ (h1-h2)) : (
"v"+ (h2-h1));
564 lastpos = handle.grpos;
568 if ((res.length > 0) && !disable_axis_drawing && draw_lines)
569 axis_g.append(
"svg:path").attr(
"d", res).call(this.lineatt.func);
571 if ((second_shift!==0) && (res2.length>0) && !disable_axis_drawing && draw_lines)
572 axis_g.append(
"svg:path").attr(
"d", res2).call(this.lineatt.func);
574 var labelsize = Math.round( (axis.fLabelSize < 1) ? axis.fLabelSize * text_scaling_size : axis.fLabelSize);
575 if ((labelsize <= 0) || (Math.abs(axis.fLabelOffset) > 1.1)) optionUnlab =
true;
578 if (!disable_axis_drawing && !optionUnlab) {
580 var label_color = this.get_color(axis.fLabelColor),
581 labeloffset = Math.round(Math.abs(axis.fLabelOffset)*text_scaling_size),
582 center_lbls = this.IsCenterLabels(),
583 rotate_lbls = axis.TestBit(JSROOT.EAxisBits.kLabelsVert),
584 textscale = 1, maxtextlen = 0, lbls_tilt =
false, labelfont = null,
585 label_g = [ axis_g.append(
"svg:g").attr(
"class",
"axis_labels") ],
586 lbl_pos = handle.lbl_pos || handle.major;
588 if (this.lbls_both_sides)
589 label_g.push(axis_g.append(
"svg:g").attr(
"class",
"axis_labels").attr(
"transform", vertical ?
"translate(" + w +
",0)" :
"translate(0," + (-h) +
")"));
591 for (var lcnt = 0; lcnt < label_g.length; ++lcnt) {
593 if (lcnt > 0) side = -side;
596 fix_coord = vertical ? -labeloffset*side : (labeloffset+2)*side + ticks_plusminus*tickSize;
598 labelfont = JSROOT.Painter.getFontDetails(axis.fLabelFont, labelsize);
600 this.StartTextDrawing(labelfont,
'font', label_g[lcnt]);
602 for (var nmajor=0;nmajor<lbl_pos.length;++nmajor) {
604 var lbl = this.format(lbl_pos[nmajor],
true);
605 if (lbl === null)
continue;
607 var pos = Math.round(this.func(lbl_pos[nmajor])),
608 gap_before = (nmajor>0) ? Math.abs(Math.round(pos -
this.func(lbl_pos[nmajor-1]))) : 0,
609 gap_after = (nmajor<lbl_pos.length-1) ? Math.abs(Math.round(
this.func(lbl_pos[nmajor+1])-pos)) : 0;
612 var gap = gap_after || gap_before;
613 pos = Math.round(pos - (vertical ? 0.5*gap : -0.5*gap));
614 if ((pos < -5) || (pos > (vertical ? h : w) + 5))
continue;
617 var arg = { text: lbl, color: label_color, latex: 1, draw_g: label_g[lcnt] };
619 maxtextlen = Math.max(maxtextlen, lbl.length);
624 arg.align = rotate_lbls ? ((side<0) ? 23 : 20) : ((side<0) ? 12 : 32);
628 arg.align = rotate_lbls ? ((side<0) ? 12 : 32) : ((side<0) ? 20 : 23);
631 if (rotate_lbls) arg.rotate = 270;
633 var textwidth = this.DrawText(arg);
635 if (textwidth && ((!vertical && !rotate_lbls) || (vertical && rotate_lbls)) && (this.kind !=
'log')) {
636 var maxwidth = gap_before*0.45 + gap_after*0.45;
637 if (!gap_before) maxwidth = 0.9*gap_after;
else
638 if (!gap_after) maxwidth = 0.9*gap_before;
639 textscale = Math.min(textscale, maxwidth / textwidth);
640 }
else if (vertical && max_text_width && !lcnt && (max_text_width - labeloffset > 20) && (textwidth > max_text_width - labeloffset)) {
641 textscale = Math.min(textscale, (max_text_width - labeloffset) / textwidth);
644 if (lastpos && (pos!=lastpos) && ((vertical && !rotate_lbls) || (!vertical && rotate_lbls))) {
645 var axis_step = Math.abs(pos-lastpos);
646 textscale = Math.min(textscale, 0.9*axis_step/labelsize);
653 this.DrawText({ color: label_color,
654 x: vertical ? side*5 : w+5,
655 y: this.has_obstacle ? fix_coord : (vertical ? -3 : -3*side),
656 align: vertical ? ((side<0) ? 30 : 10) : ( myXor(this.has_obstacle, (side<0)) ? 13 : 10 ),
658 text:
'#times' + this.format10Exp(this.order),
659 draw_g: label_g[lcnt]
663 if ((textscale > 0.01) && (textscale < 0.7) && !vertical && !rotate_lbls && (maxtextlen > 5) && !this.lbls_both_sides) {
668 for (var lcnt = 0; lcnt < label_g.length; ++lcnt) {
669 if ((textscale > 0.01) && (textscale < 1))
670 this.TextScaleFactor(1/textscale, label_g[lcnt]);
672 this.FinishTextDrawing(label_g[lcnt]);
674 label_g[lcnt].selectAll(
"text").each(
function() {
675 var txt = d3.select(
this), tr = txt.attr(
"transform");
676 txt.attr(
"transform", tr +
" rotate(25)").style(
"text-anchor",
"start");
680 if (label_g.length > 1) side = -side;
682 if (labelfont) labelsize = labelfont.size;
685 if (JSROOT.gStyle.Zooming && !
this.disable_zooming) {
686 var r = axis_g.append(
"svg:rect")
687 .attr(
"class",
"axis_zoom")
688 .style(
"opacity",
"0")
689 .style(
"cursor",
"crosshair");
692 r.attr(
"x", (side>0) ? (-2*labelsize - 3) : 3)
694 .attr(
"width", 2*labelsize + 3)
697 r.attr(
"x", 0).attr(
"y", (side>0) ? 0 : -labelsize - 3)
698 .attr(
"width", w).attr(
"height", labelsize + 3);
701 if ((axis.fTitle.length > 0) && !disable_axis_drawing) {
702 var title_g = axis_g.append(
"svg:g").attr(
"class",
"axis_title"),
703 title_fontsize = (axis.fTitleSize >= 1) ? axis.fTitleSize : Math.round(axis.fTitleSize * text_scaling_size),
704 title_offest_k = 1.6*(axis.fTitleSize<1 ? axis.fTitleSize : axis.fTitleSize/(this.pad_height(
"") || 10)),
705 center = axis.TestBit(JSROOT.EAxisBits.kCenterTitle),
706 rotate = axis.TestBit(JSROOT.EAxisBits.kRotateTitle) ? -1 : 1,
707 title_color = this.get_color(is_gaxis ? axis.fTextColor : axis.fTitleColor),
708 shift_x = 0, shift_y = 0;
710 this.StartTextDrawing(axis.fTitleFont, title_fontsize, title_g);
712 var myxor = ((rotate<0) && !reverse) || ((rotate>=0) && reverse);
715 title_offest_k *= -side*pad_w;
717 shift_x = Math.round(title_offest_k*axis.fTitleOffset);
719 if ((this.name ==
"zaxis") && is_gaxis && (
'getBoundingClientRect' in axis_g.node())) {
721 var rect = axis_g.node().getBoundingClientRect();
722 if (shift_x < rect.width - tickSize) shift_x = Math.round(rect.width - tickSize);
725 shift_y = Math.round(center ? h/2 : (reverse ? h : 0));
727 this.DrawText({ align: (center ?
"middle" : (myxor ?
"begin" :
"end" )) +
";middle",
728 rotate: (rotate<0) ? 90 : 270,
729 text: axis.fTitle, color: title_color, draw_g: title_g });
731 title_offest_k *= side*pad_h;
733 shift_x = Math.round(center ? w/2 : (reverse ? 0 : w));
734 shift_y = Math.round(title_offest_k*axis.fTitleOffset);
735 this.DrawText({ align: (center ?
'middle' : (myxor ?
'begin' :
'end')) +
";middle",
736 rotate: (rotate<0) ? 180 : 0,
737 text: axis.fTitle, color: title_color, draw_g: title_g });
740 var axis_rect = null;
741 if (vertical && (axis.fTitleOffset == 0) && (
'getBoundingClientRect' in axis_g.node()))
742 axis_rect = axis_g.node().getBoundingClientRect();
744 this.FinishTextDrawing(title_g,
function() {
746 var title_rect = title_g.node().getBoundingClientRect();
747 shift_x = (side>0) ? Math.round(axis_rect.left - title_rect.right - title_fontsize*0.3) :
748 Math.round(axis_rect.right - title_rect.left + title_fontsize*0.3);
751 title_g.attr(
'transform',
'translate(' + shift_x +
',' + shift_y +
')')
752 .property(
'shift_x', shift_x)
753 .property(
'shift_y', shift_y);
757 this.AddTitleDrag(title_g, vertical, title_offest_k, reverse, vertical ? h : w);
762 if (!disable_axis_drawing && (
'getBoundingClientRect' in axis_g.node())) {
763 var rect1 = axis_g.node().getBoundingClientRect(),
764 rect2 = this.svg_pad().node().getBoundingClientRect();
766 this.position = rect1.left - rect2.left;
770 TAxisPainter.prototype.Redraw =
function() {
772 var gaxis = this.GetObject(),
773 x1 = this.AxisToSvg(
"x", gaxis.fX1),
774 y1 = this.AxisToSvg(
"y", gaxis.fY1),
775 x2 = this.AxisToSvg(
"x", gaxis.fX2),
776 y2 = this.AxisToSvg(
"y", gaxis.fY2),
777 w = x2 - x1, h = y1 - y2,
778 vertical = Math.abs(w) < Math.abs(h),
779 func = null, reverse =
false, kind =
"normal",
780 min = gaxis.fWmin, max = gaxis.fWmax,
781 domain_min = min, domain_max = max;
783 if (gaxis.fChopt.indexOf(
"t")>=0) {
784 func = d3.scaleTime();
786 this.toffset = JSROOT.Painter.getTimeOffset(gaxis);
787 domain_min =
new Date(this.toffset + min*1000);
788 domain_max =
new Date(this.toffset + max*1000);
789 }
else if (gaxis.fChopt.indexOf(
"G")>=0) {
790 func = d3.scaleLog();
793 func = d3.scaleLinear();
797 func.domain([domain_min, domain_max]);
803 var d = y1; y1 = y2; y2 = d;
804 h = -h; reverse =
true;
811 var d = x1; x1 = x2; x2 = d;
812 w = -w; reverse =
true;
817 this.SetAxisConfig(vertical ?
"yaxis" :
"xaxis", kind, func, min, max, min, max);
821 this.DrawAxis(vertical, this.draw_g, w, h,
"translate(" + x1 +
"," + y2 +
")", reverse);
824 JSROOT.Painter.drawGaxis =
function(divid, obj, opt) {
825 var painter =
new JSROOT.TAxisPainter(obj,
false);
827 painter.SetDivId(divid);
829 painter.disable_zooming =
true;
833 return painter.DrawingReady();
847 function TFramePainter(tframe) {
848 if (tframe && tframe.$dummy) tframe = null;
849 JSROOT.TooltipHandler.call(
this, tframe);
852 this.shrink_frame_left = 0.;
853 this.x_kind =
'normal';
854 this.y_kind =
'normal';
855 this.xmin = this.xmax = 0;
856 this.ymin = this.ymax = 0;
857 this.ranges_set =
false;
858 this.axes_drawn =
false;
859 this.keys_handler = null;
863 TFramePainter.prototype = Object.create(JSROOT.TooltipHandler.prototype);
867 TFramePainter.prototype.frame_painter =
function() {
873 TFramePainter.prototype.SetActive =
function(on) {
877 TFramePainter.prototype.GetTipName =
function(append) {
878 var res = JSROOT.TooltipHandler.prototype.GetTipName.call(
this) ||
"TFrame";
879 if (append) res+=append;
883 TFramePainter.prototype.Shrink =
function(shrink_left, shrink_right) {
884 this.fX1NDC += shrink_left;
885 this.fX2NDC -= shrink_right;
888 TFramePainter.prototype.SetLastEventPos =
function(pnt) {
890 this.fLastEventPnt = pnt;
893 TFramePainter.prototype.GetLastEventPos =
function() {
895 return this.fLastEventPnt;
898 TFramePainter.prototype.ProjectAitoff2xy =
function(l, b) {
899 var DegToRad = Math.PI/180,
900 alpha2 = (l/2)*DegToRad,
904 cdec = Math.cos(delta),
905 denom = Math.sqrt(1. + cdec*Math.cos(alpha2)),
907 x: cdec*Math.sin(alpha2)*2.*r2/denom/f/DegToRad,
908 y: Math.sin(delta)*r2/denom/f/DegToRad
914 TFramePainter.prototype.ProjectMercator2xy =
function(l, b) {
915 var aid = Math.tan((Math.PI/2 + b/180*Math.PI)/2);
916 return { x: l, y: Math.log(aid) };
919 TFramePainter.prototype.ProjectSinusoidal2xy =
function(l, b) {
920 return { x: l*Math.cos(b/180*Math.PI), y: b };
923 TFramePainter.prototype.ProjectParabolic2xy =
function(l, b) {
925 x: l*(2.*Math.cos(2*b/180*Math.PI/3) - 1),
926 y: 180*Math.sin(b/180*Math.PI/3)
930 TFramePainter.prototype.RecalculateRange =
function(Proj) {
933 this.projection = Proj || 0;
935 if (!this.projection)
return;
938 if (this.projection == 1) {
940 pnts.push(this.ProjectAitoff2xy(this.scale_xmin, this.scale_ymin));
941 pnts.push(this.ProjectAitoff2xy(this.scale_xmin, this.scale_ymax));
942 pnts.push(this.ProjectAitoff2xy(this.scale_xmax, this.scale_ymax));
943 pnts.push(this.ProjectAitoff2xy(this.scale_xmax, this.scale_ymin));
944 if (this.scale_ymin<0 && this.scale_ymax>0) {
946 pnts.push(this.ProjectAitoff2xy(this.scale_xmin*0.9999, 0));
947 pnts.push(this.ProjectAitoff2xy(this.scale_xmax*0.9999, 0));
949 if (this.scale_xmin<0 && this.scale_xmax>0) {
950 pnts.push(this.ProjectAitoff2xy(0, this.scale_ymin));
951 pnts.push(this.ProjectAitoff2xy(0, this.scale_ymax));
953 }
else if (this.projection == 2) {
954 if (this.scale_ymin <= -90 || this.scale_ymax >=90) {
955 console.warn(
"Mercator Projection",
"Latitude out of range", this.scale_ymin, this.scale_ymax);
959 pnts.push(this.ProjectMercator2xy(this.scale_xmin, this.scale_ymin));
960 pnts.push(this.ProjectMercator2xy(this.scale_xmax, this.scale_ymax));
962 }
else if (this.projection == 3) {
963 pnts.push(this.ProjectSinusoidal2xy(this.scale_xmin, this.scale_ymin));
964 pnts.push(this.ProjectSinusoidal2xy(this.scale_xmin, this.scale_ymax));
965 pnts.push(this.ProjectSinusoidal2xy(this.scale_xmax, this.scale_ymax));
966 pnts.push(this.ProjectSinusoidal2xy(this.scale_xmax, this.scale_ymin));
967 if (this.scale_ymin<0 && this.scale_ymax>0) {
968 pnts.push(this.ProjectSinusoidal2xy(this.scale_xmin, 0));
969 pnts.push(this.ProjectSinusoidal2xy(this.scale_xmax, 0));
971 if (this.scale_xmin<0 && this.scale_xmax>0) {
972 pnts.push(this.ProjectSinusoidal2xy(0, this.scale_ymin));
973 pnts.push(this.ProjectSinusoidal2xy(0, this.scale_ymax));
975 }
else if (this.projection == 4) {
976 pnts.push(this.ProjectParabolic2xy(this.scale_xmin, this.scale_ymin));
977 pnts.push(this.ProjectParabolic2xy(this.scale_xmin, this.scale_ymax));
978 pnts.push(this.ProjectParabolic2xy(this.scale_xmax, this.scale_ymax));
979 pnts.push(this.ProjectParabolic2xy(this.scale_xmax, this.scale_ymin));
980 if (this.scale_ymin<0 && this.scale_ymax>0) {
981 pnts.push(this.ProjectParabolic2xy(this.scale_xmin, 0));
982 pnts.push(this.ProjectParabolic2xy(this.scale_xmax, 0));
984 if (this.scale_xmin<0 && this.scale_xmax>0) {
985 pnts.push(this.ProjectParabolic2xy(0, this.scale_ymin));
986 pnts.push(this.ProjectParabolic2xy(0, this.scale_ymax));
990 this.original_xmin = this.scale_xmin;
991 this.original_xmax = this.scale_xmax;
992 this.original_ymin = this.scale_ymin;
993 this.original_ymax = this.scale_ymax;
995 this.scale_xmin = this.scale_xmax = pnts[0].x;
996 this.scale_ymin = this.scale_ymax = pnts[0].y;
998 for (var n=1;n<pnts.length;++n) {
999 this.scale_xmin = Math.min(this.scale_xmin, pnts[n].x);
1000 this.scale_xmax = Math.max(this.scale_xmax, pnts[n].x);
1001 this.scale_ymin = Math.min(this.scale_ymin, pnts[n].y);
1002 this.scale_ymax = Math.max(this.scale_ymax, pnts[n].y);
1006 TFramePainter.prototype.SetAxesRanges =
function(xaxis, xmin, xmax, yaxis, ymin, ymax, zaxis, zmin, zmax) {
1009 this.ranges_set =
true;
1024 TFramePainter.prototype.GetAxis =
function(name) {
1026 case "x":
return this.xaxis;
1027 case "y":
return this.yaxis;
1028 case "z":
return this.zaxis;
1033 TFramePainter.prototype.CheckAxisZoom =
function(name) {
1034 var axis = this.GetAxis(name);
1035 if (axis && axis.TestBit(JSROOT.EAxisBits.kAxisRange)) {
1036 if ((axis.fFirst !== axis.fLast) && ((axis.fFirst > 1) || (axis.fLast < axis.fNbins))) {
1037 this[
'zoom_' + name +
'min'] = axis.fFirst > 1 ? axis.GetBinLowEdge(axis.fFirst) : axis.fXmin;
1038 this[
'zoom_' + name +
'max'] = axis.fLast < axis.fNbins ? axis.GetBinLowEdge(axis.fLast+1) : axis.fXmax;
1040 axis.InvertBit(JSROOT.EAxisBits.kAxisRange);
1041 axis.fFirst = 1; axis.fLast = axis.fNbins
1046 TFramePainter.prototype.CheckPadUserRange =
function(pad, name) {
1050 var umin = pad[
'fU' + name +
'min'],
1051 umax = pad[
'fU' + name +
'max'],
1055 if ((Math.abs(pad.fX1) > eps) || (Math.abs(pad.fX2-1) > eps)) {
1056 var dx = pad.fX2 - pad.fX1;
1057 umin = pad.fX1 + dx*pad.fLeftMargin;
1058 umax = pad.fX2 - dx*pad.fRightMargin;
1061 if ((Math.abs(pad.fY1) > eps) || (Math.abs(pad.fY2-1) > eps)) {
1062 var dy = pad.fY2 - pad.fY1;
1063 umin = pad.fY1 + dy*pad.fBottomMargin;
1064 umax = pad.fY2 - dy*pad.fTopMargin;
1068 if ((umin>=umax) || (Math.abs(umin)<eps && Math.abs(umax-1)<eps))
return;
1070 if (pad[
'fLog' + name] > 0) {
1071 umin = Math.exp(umin * Math.log(10));
1072 umax = Math.exp(umax * Math.log(10));
1076 if (this.swap_xy) aname = (name==
"x") ?
"y" :
"x";
1077 var smin =
'scale_' + aname +
'min',
1078 smax =
'scale_' + aname +
'max';
1080 var eps = (
this[smax] -
this[smin]) * 1e-7;
1082 if ((Math.abs(umin -
this[smin]) > eps) || (Math.abs(umax -
this[smax]) > eps)) {
1083 this[
"zoom_" + aname +
"min"] = umin;
1084 this[
"zoom_" + aname +
"max"] = umax;
1088 TFramePainter.prototype.CreateXY =
function(opts) {
1092 if (!opts) opts = {};
1094 this.swap_xy = opts.swap_xy ||
false;
1095 this.reverse_x = opts.reverse_x ||
false;
1096 this.reverse_y = opts.reverse_y ||
false;
1098 this.logx = this.logy =
false;
1100 var w = this.frame_width(), h = this.frame_height(), pad = this.root_pad();
1102 this.scale_xmin = this.xmin;
1103 this.scale_xmax = this.xmax;
1105 this.scale_ymin = this.ymin;
1106 this.scale_ymax = this.ymax;
1108 if (opts.extra_y_space) {
1109 var log_scale = this.swap_xy ? pad.fLogx : pad.fLogy;
1110 if (log_scale && (this.scale_ymax > 0))
1111 this.scale_ymax = Math.exp(Math.log(
this.scale_ymax)*1.1);
1113 this.scale_ymax += (this.scale_ymax - this.scale_ymin) * 0.1;
1116 if (opts.check_pad_range) {
1119 this.zoom_xmin = this.zoom_xmax = 0;
1120 this.zoom_ymin = this.zoom_ymax = 0;
1121 this.zoom_zmin = this.zoom_zmax = 0;
1123 this.CheckAxisZoom(
'x');
1124 if (opts.ndim && (opts.ndim > 1)) this.CheckAxisZoom(
'y');
1125 if (opts.ndim && (opts.ndim > 2)) this.CheckAxisZoom(
'z');
1127 if (opts.check_pad_range ===
"pad_range") {
1128 var canp = this.canv_painter();
1130 if (!canp || !canp.online_canvas) {
1131 this.CheckPadUserRange(pad,
'x');
1132 this.CheckPadUserRange(pad,
'y');
1137 if ((this.zoom_ymin == this.zoom_ymax) && (opts.zoom_ymin != opts.zoom_ymax) && !
this.zoom_changed_interactive) {
1138 this.zoom_ymin = opts.zoom_ymin;
1139 this.zoom_ymax = opts.zoom_ymax;
1142 if (this.zoom_xmin != this.zoom_xmax) {
1143 this.scale_xmin = this.zoom_xmin;
1144 this.scale_xmax = this.zoom_xmax;
1147 if (this.zoom_ymin != this.zoom_ymax) {
1148 this.scale_ymin = this.zoom_ymin;
1149 this.scale_ymax = this.zoom_ymax;
1153 this.RecalculateRange(opts.Proj);
1155 if (this.xaxis.fTimeDisplay) {
1156 this.x_kind =
'time';
1157 this.timeoffsetx = JSROOT.Painter.getTimeOffset(this.xaxis);
1158 this.ConvertX =
function(x) {
return new Date(this.timeoffsetx + x*1000); };
1159 this.RevertX =
function(grx) {
return (this.x.invert(grx) - this.timeoffsetx) / 1000; };
1161 this.x_kind = !this.xaxis.fLabels ?
'normal' :
'labels';
1162 this.ConvertX =
function(x) {
return x; };
1163 this.RevertX =
function(grx) {
return this.x.invert(grx); };
1166 if (this.x_kind ==
'time') {
1167 this.x = d3.scaleTime();
1168 }
else if (this.swap_xy ? pad.fLogy : pad.fLogx) {
1171 if (this.scale_xmax <= 0) this.scale_xmax = 0;
1173 if ((this.scale_xmin <= 0) && this.xaxis && !this.swap_xy)
1174 for (var i=0;i<this.xaxis.fNbins;++i) {
1175 this.scale_xmin = Math.max(this.scale_xmin, this.xaxis.GetBinLowEdge(i+1));
1176 if (this.scale_xmin>0)
break;
1179 if ((this.scale_xmin <= 0) || (this.scale_xmin >= this.scale_xmax))
1180 this.scale_xmin = this.scale_xmax * 0.0001;
1182 this.x = d3.scaleLog();
1184 this.x = d3.scaleLinear();
1187 var gr_range_x = this.reverse_x ? [ w, 0 ] : [ 0, w ],
1188 gr_range_y = this.reverse_y ? [ 0, h ] : [ h, 0 ];
1190 this.x.domain([this.ConvertX(this.scale_xmin), this.ConvertX(this.scale_xmax)])
1191 .range(this.swap_xy ? gr_range_y : gr_range_x);
1193 if (this.x_kind ==
'time') {
1195 this.grx =
function(val) {
return this.x(this.ConvertX(val)); }
1196 }
else if (this.logx) {
1197 this.grx =
function(val) {
return (val < this.scale_xmin) ? (this.swap_xy ? this.x.range()[0]+5 : -5) : this.x(val); }
1202 if (this.yaxis.fTimeDisplay) {
1203 this.y_kind =
'time';
1204 this.timeoffsety = JSROOT.Painter.getTimeOffset(this.yaxis);
1205 this.ConvertY =
function(y) {
return new Date(this.timeoffsety + y*1000); };
1206 this.RevertY =
function(gry) {
return (this.y.invert(gry) - this.timeoffsety) / 1000; };
1208 this.y_kind = !this.yaxis.fLabels ?
'normal' :
'labels';
1209 this.ConvertY =
function(y) {
return y; };
1210 this.RevertY =
function(gry) {
return this.y.invert(gry); };
1213 if (this.swap_xy ? pad.fLogx : pad.fLogy) {
1215 if (this.scale_ymax <= 0)
1216 this.scale_ymax = 1;
1218 if ((this.zoom_ymin === this.zoom_ymax) && (opts.ndim==1) &&
this.draw_content)
1219 this.scale_ymax*=1.8;
1222 if ((this.scale_ymin <= 0) && this.yaxis && (opts.ndim>1) && !
this.swap_xy)
1223 for (var i=0;i<this.yaxis.fNbins;++i) {
1224 this.scale_ymin = Math.max(this.scale_ymin, this.yaxis.GetBinLowEdge(i+1));
1225 if (this.scale_ymin>0)
break;
1228 if ((this.scale_ymin <= 0) && (opts.ymin_nz) && (opts.ymin_nz < 1e-2*
this.ymax))
1229 this.scale_ymin = 0.3 * opts.ymin_nz;
1231 if ((this.scale_ymin <= 0) || (this.scale_ymin >= this.scale_ymax))
1232 this.scale_ymin = 3e-4 * this.scale_ymax;
1234 this.y = d3.scaleLog();
1235 }
else if (this.y_kind ==
'time') {
1236 this.y = d3.scaleTime();
1238 this.y = d3.scaleLinear()
1241 this.y.domain([ this.ConvertY(this.scale_ymin), this.ConvertY(this.scale_ymax) ])
1242 .range(this.swap_xy ? gr_range_x : gr_range_y);
1244 if (this.y_kind ==
'time') {
1246 this.gry =
function(val) {
return this.y(this.ConvertY(val)); }
1247 }
else if (this.logy) {
1249 this.gry =
function(val) {
return (val < this.scale_ymin) ? (this.swap_xy ? -5 : this.y.range()[0]+5) : this.y(val); }
1254 this.SetRootPadRange(pad);
1258 TFramePainter.prototype.SetRootPadRange =
function(pad, is3d) {
1259 if (!pad || !this.ranges_set)
return;
1264 pad.fUxmin = pad.fUymin = -0.9;
1265 pad.fUxmax = pad.fUymax = 0.9;
1267 pad.fLogx = (this.swap_xy ? this.logy : this.logx) ? 1 : 0;
1268 pad.fUxmin = pad.fLogx ? JSROOT.log10(this.scale_xmin) : this.scale_xmin;
1269 pad.fUxmax = pad.fLogx ? JSROOT.log10(this.scale_xmax) : this.scale_xmax;
1270 pad.fLogy = (this.swap_xy ? this.logx : this.logy) ? 1 : 0;
1271 pad.fUymin = pad.fLogy ? JSROOT.log10(this.scale_ymin) : this.scale_ymin;
1272 pad.fUymax = pad.fLogy ? JSROOT.log10(this.scale_ymax) : this.scale_ymax;
1275 var rx = pad.fUxmax - pad.fUxmin,
1276 mx = 1 - pad.fLeftMargin - pad.fRightMargin,
1277 ry = pad.fUymax - pad.fUymin,
1278 my = 1 - pad.fBottomMargin - pad.fTopMargin;
1280 if (mx <= 0) mx = 0.01;
1281 if (my <= 0) my = 0.01;
1283 pad.fX1 = pad.fUxmin - rx/mx*pad.fLeftMargin;
1284 pad.fX2 = pad.fUxmax + rx/mx*pad.fRightMargin;
1285 pad.fY1 = pad.fUymin - ry/my*pad.fBottomMargin;
1286 pad.fY2 = pad.fUymax + ry/my*pad.fTopMargin;
1290 TFramePainter.prototype.DrawGrids =
function() {
1293 var layer = this.svg_frame().select(
".grid_layer");
1295 layer.selectAll(
".xgrid").remove();
1296 layer.selectAll(
".ygrid").remove();
1298 var pad = this.root_pad(),
1299 h = this.frame_height(),
1300 w = this.frame_width(),
1301 grid_style = JSROOT.gStyle.fGridStyle;
1303 if ((grid_style < 0) || (grid_style >= JSROOT.Painter.root_line_styles.length)) grid_style = 11;
1306 if (pad && pad.fGridx &&
this.x_handle) {
1308 for (var n=0;n<this.x_handle.ticks.length;++n)
1310 gridx +=
"M0,"+this.x_handle.ticks[n]+
"h"+w;
1312 gridx +=
"M"+this.x_handle.ticks[n]+
",0v"+h;
1314 var colid = (JSROOT.gStyle.fGridColor > 0) ? JSROOT.gStyle.fGridColor : (
this.GetAxis(
"x") ? this.GetAxis(
"x").fAxisColor : 1),
1315 grid_color = this.get_color(colid) ||
"black";
1317 if (gridx.length > 0)
1318 layer.append(
"svg:path")
1319 .attr(
"class",
"xgrid")
1321 .style(
'stroke', grid_color)
1322 .style(
"stroke-width", JSROOT.gStyle.fGridWidth)
1323 .style(
"stroke-dasharray", JSROOT.Painter.root_line_styles[grid_style]);
1327 if (pad && pad.fGridy &&
this.y_handle) {
1329 for (var n=0;n<this.y_handle.ticks.length;++n)
1331 gridy +=
"M"+this.y_handle.ticks[n]+
",0v"+h;
1333 gridy +=
"M0,"+this.y_handle.ticks[n]+
"h"+w;
1335 var colid = (JSROOT.gStyle.fGridColor > 0) ? JSROOT.gStyle.fGridColor : (
this.GetAxis(
"y") ? this.GetAxis(
"y").fAxisColor : 1),
1336 grid_color = this.get_color(colid) ||
"black";
1338 if (gridy.length > 0)
1339 layer.append(
"svg:path")
1340 .attr(
"class",
"ygrid")
1342 .style(
'stroke',grid_color)
1343 .style(
"stroke-width",JSROOT.gStyle.fGridWidth)
1344 .style(
"stroke-dasharray", JSROOT.Painter.root_line_styles[grid_style]);
1348 TFramePainter.prototype.AxisAsText =
function(axis, value) {
1350 if (this.x_kind ==
'time')
1351 value = this.ConvertX(value);
1352 if (this.x_handle && (
'format' in this.x_handle))
1353 return this.x_handle.format(value,
false, JSROOT.gStyle.XValuesFormat);
1354 }
else if (axis ==
"y") {
1355 if (this.y_kind ==
'time')
1356 value = this.ConvertY(value);
1357 if (this.y_handle && (
'format' in this.y_handle))
1358 return this.y_handle.format(value,
false,
false, JSROOT.gStyle.YValuesFormat);
1360 if (this.z_handle && (
'format' in this.z_handle))
1361 return this.z_handle.format(value,
false,
false, JSROOT.gStyle.ZValuesFormat);
1364 return value.toPrecision(4);
1367 TFramePainter.prototype.DrawAxes =
function(shrink_forbidden, disable_axis_draw, AxisPos, has_x_obstacle) {
1374 if ((this.xmin==this.xmax) || (this.ymin==this.ymax))
return false;
1376 if (AxisPos === undefined) AxisPos = 0;
1378 var layer = this.svg_frame().select(
".axis_layer"),
1379 w = this.frame_width(),
1380 h = this.frame_height(),
1381 pad = this.root_pad();
1383 this.x_handle =
new JSROOT.TAxisPainter(this.xaxis,
true);
1384 this.x_handle.SetDivId(this.divid, -1);
1385 this.x_handle.pad_name = this.pad_name;
1387 this.x_handle.SetAxisConfig(
"xaxis",
1388 (this.logx && (this.x_kind !==
"time")) ?
"log" : this.x_kind,
1389 this.x, this.xmin, this.xmax, this.scale_xmin, this.scale_xmax);
1390 this.x_handle.invert_side = (AxisPos >= 10);
1391 this.x_handle.lbls_both_sides = !this.x_handle.invert_side && (pad.fTickx > 1);
1392 this.x_handle.has_obstacle = has_x_obstacle;
1394 this.y_handle =
new JSROOT.TAxisPainter(this.yaxis,
true);
1395 this.y_handle.SetDivId(this.divid, -1);
1396 this.y_handle.pad_name = this.pad_name;
1398 this.y_handle.SetAxisConfig(
"yaxis",
1399 (this.logy && this.y_kind !==
"time") ?
"log" : this.y_kind,
1400 this.y, this.ymin, this.ymax, this.scale_ymin, this.scale_ymax);
1401 this.y_handle.invert_side = ((AxisPos % 10) === 1);
1402 this.y_handle.lbls_both_sides = !this.y_handle.invert_side && (pad.fTicky > 1);
1404 var draw_horiz = this.swap_xy ? this.y_handle : this.x_handle,
1405 draw_vertical = this.swap_xy ? this.x_handle : this.y_handle;
1407 if (!disable_axis_draw) {
1408 var pp = this.pad_painter();
1409 if (pp && pp._fast_drawing) disable_axis_draw =
true;
1412 if (!disable_axis_draw) {
1413 draw_horiz.DrawAxis(
false, layer, w, h,
1414 draw_horiz.invert_side ? undefined :
"translate(0," + h +
")",
1415 false, pad.fTickx ? -h : 0, disable_axis_draw);
1417 draw_vertical.DrawAxis(
true, layer, w, h,
1418 draw_vertical.invert_side ?
"translate(" + w +
",0)" : undefined,
1419 false, pad.fTicky ? w : 0, disable_axis_draw,
1420 draw_vertical.invert_side ? 0 :
this.frame_x());
1425 if (!shrink_forbidden && JSROOT.gStyle.CanAdjustFrame && !disable_axis_draw) {
1427 var shrink = 0., ypos = draw_vertical.position;
1429 if ((-0.2*w < ypos) && (ypos < 0)) {
1430 shrink = -ypos/w + 0.001;
1431 this.shrink_frame_left += shrink;
1432 }
else if ((ypos>0) && (ypos<0.3*w) && (this.shrink_frame_left > 0) && (ypos/w > this.shrink_frame_left)) {
1433 shrink = -this.shrink_frame_left;
1434 this.shrink_frame_left = 0.;
1438 this.Shrink(shrink, 0);
1440 this.DrawAxes(
true);
1444 this.axes_drawn =
true;
1449 TFramePainter.prototype.UpdateAttributes =
function(force) {
1450 var pad = this.root_pad(),
1451 tframe = this.GetObject();
1453 if ((this.fX1NDC === undefined) || (force && !this.modified_NDC)) {
1454 if (!pad || (pad.fLeftMargin===undefined)) {
1455 JSROOT.extend(
this, JSROOT.gStyle.FrameNDC);
1457 JSROOT.extend(
this, {
1458 fX1NDC: pad.fLeftMargin,
1459 fX2NDC: 1 - pad.fRightMargin,
1460 fY1NDC: pad.fBottomMargin,
1461 fY2NDC: 1 - pad.fTopMargin
1466 if (this.fillatt === undefined) {
1467 if (tframe) this.createAttFill({ attr: tframe });
1468 else if (pad && pad.fFrameFillColor) this.createAttFill({ pattern: pad.fFrameFillStyle, color: pad.fFrameFillColor });
1469 else if (pad) this.createAttFill({ attr: pad });
1470 else this.createAttFill({ pattern: 1001, color: 0});
1473 if (!tframe && this.fillatt.empty() && this.pad_painter() && this.pad_painter().iscan)
1474 this.fillatt.SetSolidColor(
'white');
1477 if (!tframe && pad && (pad.fFrameLineColor!==undefined))
1478 this.createAttLine({ color: pad.fFrameLineColor, width: pad.fFrameLineWidth, style: pad.fFrameLineStyle });
1480 this.createAttLine({ attr: tframe, color:
'black' });
1486 TFramePainter.prototype.SizeChanged =
function() {
1488 var pad = this.root_pad();
1491 pad.fLeftMargin = this.fX1NDC;
1492 pad.fRightMargin = 1 - this.fX2NDC;
1493 pad.fBottomMargin = this.fY1NDC;
1494 pad.fTopMargin = 1 - this.fY2NDC;
1495 this.SetRootPadRange(pad);
1498 this.InteractiveRedraw(
"pad",
"frame");
1501 TFramePainter.prototype.CleanXY =
function() {
1503 delete this.x;
delete this.grx;
1504 delete this.ConvertX;
delete this.RevertX;
1505 delete this.y;
delete this.gry;
1506 delete this.ConvertY;
delete this.RevertY;
1507 delete this.z;
delete this.grz;
1510 TFramePainter.prototype.CleanupAxes =
function() {
1512 if (this.x_handle) {
1513 this.x_handle.Cleanup();
1514 delete this.x_handle;
1517 if (this.y_handle) {
1518 this.y_handle.Cleanup();
1519 delete this.y_handle;
1522 if (this.z_handle) {
1523 this.z_handle.Cleanup();
1524 delete this.z_handle;
1527 this.draw_g.select(
".grid_layer").selectAll(
"*").remove();
1528 this.draw_g.select(
".axis_layer").selectAll(
"*").remove();
1530 this.axes_drawn =
false;
1534 TFramePainter.prototype.CleanFrameDrawings =
function() {
1537 if (typeof this.Create3DScene ===
'function')
1538 this.Create3DScene(-1);
1543 this.ranges_set =
false;
1545 this.xmin = this.xmax = 0;
1546 this.ymin = this.ymax = 0;
1547 this.zmin = this.zmax = 0;
1549 this.zoom_xmin = this.zoom_xmax = 0;
1550 this.zoom_ymin = this.zoom_ymax = 0;
1551 this.zoom_zmin = this.zoom_zmax = 0;
1553 this.scale_xmin = this.scale_xmax = 0;
1554 this.scale_ymin = this.scale_ymax = 0;
1555 this.scale_zmin = this.scale_zmax = 0;
1558 this.draw_g.select(
".main_layer").selectAll(
"*").remove();
1559 this.draw_g.select(
".upper_layer").selectAll(
"*").remove();
1567 this.draw_g.selectAll(
"*").remove();
1568 this.draw_g.on(
"mousedown", null)
1569 .on(
"dblclick", null)
1571 .on(
"contextmenu", null)
1572 .property(
'interactive_set', null);
1573 this.draw_g.remove();
1578 if (this.keys_handler) {
1579 window.removeEventListener(
'keydown', this.keys_handler,
false);
1580 this.keys_handler = null;
1584 TFramePainter.prototype.Cleanup =
function() {
1585 this.CleanFrameDrawings();
1586 delete this._click_handler;
1587 delete this._dblclick_handler;
1589 JSROOT.TooltipHandler.prototype.Cleanup.call(
this);
1592 TFramePainter.prototype.Redraw =
function() {
1594 var pp = this.pad_painter();
1595 if (pp) pp.frame_painter_ref =
this;
1597 if (this.mode3d)
return;
1600 this.UpdateAttributes();
1602 var width = this.pad_width(),
1603 height = this.pad_height(),
1604 lm = Math.round(width * this.fX1NDC),
1605 w = Math.round(width * (this.fX2NDC - this.fX1NDC)),
1606 tm = Math.round(height * (1 - this.fY2NDC)),
1607 h = Math.round(height * (this.fY2NDC - this.fY1NDC)),
1608 rotate =
false, fixpos =
false;
1610 if (pp && pp.options) {
1611 if (pp.options.RotateFrame) rotate =
true;
1612 if (pp.options.FixFrame) fixpos =
true;
1616 this.draw_g = this.svg_layer(
"primitives_layer").select(
".root_frame");
1618 var top_rect, main_svg;
1620 if (this.draw_g.empty()) {
1622 var layer = this.svg_layer(
"primitives_layer");
1624 this.draw_g = layer.append(
"svg:g").attr(
"class",
"root_frame");
1626 this.draw_g.append(
"svg:title").text(
"");
1628 top_rect = this.draw_g.append(
"svg:rect");
1631 this.draw_g.append(
'svg:g').attr(
'class',
'grid_layer');
1633 main_svg = this.draw_g.append(
'svg:svg')
1634 .attr(
'class',
'main_layer')
1637 .attr(
'overflow',
'hidden');
1639 this.draw_g.append(
'svg:g').attr(
'class',
'axis_layer');
1640 this.draw_g.append(
'svg:g').attr(
'class',
'upper_layer');
1642 top_rect = this.draw_g.select(
"rect");
1643 main_svg = this.draw_g.select(
".main_layer");
1646 this.axes_drawn =
false;
1648 var trans =
"translate(" + lm +
"," + tm +
")";
1650 trans +=
" rotate(-90) " +
"translate(" + -h +
",0)";
1651 var d = w; w = h; h = d;
1656 this._frame_width = w;
1657 this._frame_height = h;
1659 this.draw_g.attr(
"transform", trans);
1661 top_rect.attr(
"x", 0)
1665 .call(this.fillatt.func)
1666 .call(this.lineatt.func);
1668 main_svg.attr(
"width", w)
1670 .attr(
"viewBox",
"0 0 " + w +
" " + h);
1675 if (JSROOT.BatchMode)
return;
1677 this.draw_g.attr(
"x", lm)
1682 if (!rotate && !fixpos)
1683 this.AddDrag({ obj:
this, only_resize:
true, minwidth: 20, minheight: 20,
1684 redraw: this.SizeChanged.bind(
this) });
1686 var tooltip_rect = main_svg;
1687 tooltip_rect.style(
"pointer-events",
"visibleFill")
1688 .property(
'handlers_set', 0);
1700 var handlers_set = (pp && pp._fast_drawing) ? 0 : 1;
1702 if (tooltip_rect.property(
'handlers_set') != handlers_set) {
1703 var close_handler = handlers_set ? this.ProcessTooltipEvent.bind(
this, null) : null,
1704 mouse_handler = handlers_set ? this.ProcessTooltipEvent.bind(
this, { handler:
true, touch:
false }) : null;
1706 tooltip_rect.property(
'handlers_set', handlers_set)
1707 .on(
'mouseenter', mouse_handler)
1708 .on(
'mousemove', mouse_handler)
1709 .on(
'mouseleave', close_handler);
1711 if (JSROOT.touches) {
1712 var touch_handler = handlers_set ? this.ProcessTooltipEvent.bind(
this, { handler:
true, touch:
true }) : null;
1714 tooltip_rect.on(
"touchstart", touch_handler)
1715 .on(
"touchmove", touch_handler)
1716 .on(
"touchend", close_handler)
1717 .on(
"touchcancel", close_handler);
1721 tooltip_rect.attr(
"x", 0)
1726 var hintsg = this.hints_layer().select(
".objects_hints");
1728 if (!hintsg.empty() && this.IsTooltipAllowed() && (hintsg.property(
"hints_pad") == this.pad_name))
1729 setTimeout(this.ProcessTooltipEvent.bind(
this, hintsg.property(
'last_point')), 10);
1732 TFramePainter.prototype.ToggleLog =
function(axis) {
1733 var pad = this.root_pad();
1737 if (!pad[
"fLog" + axis]) {
1738 var kind =
this[axis+
"_kind"];
1739 if (this.swap_xy && axis===
"x") kind = this.y_kind;
else
1740 if (this.swap_xy && axis===
"y") kind = this.x_kind;
1741 if (kind ===
"labels")
return;
1745 pad[
"fLog" + axis] = pad[
"fLog" + axis] ? 0 : 1;
1747 this.InteractiveRedraw(
"pad",
"log"+axis);
1750 TFramePainter.prototype.FillContextMenu =
function(menu, kind, obj) {
1754 var main = this.main_painter(), pad = this.root_pad();
1756 if ((kind==
"x") || (kind==
"y") || (kind==
"z")) {
1757 var faxis = obj ||
this[kind+
'axis'];
1758 menu.add(
"header: " + kind.toUpperCase() +
" axis");
1759 menu.add(
"Unzoom", this.Unzoom.bind(
this, kind));
1760 menu.addchk(pad[
"fLog" + kind],
"SetLog"+kind, this.ToggleLog.bind(
this, kind) );
1761 menu.addchk(faxis.TestBit(JSROOT.EAxisBits.kMoreLogLabels),
"More log",
1762 function() { faxis.InvertBit(JSROOT.EAxisBits.kMoreLogLabels); this.RedrawPad(); });
1763 menu.addchk(faxis.TestBit(JSROOT.EAxisBits.kNoExponent),
"No exponent",
1764 function() { faxis.InvertBit(JSROOT.EAxisBits.kNoExponent); this.RedrawPad(); });
1766 if ((kind ===
"z") && main && main.options && main.options.Zscale)
1767 if (typeof main.FillPaletteMenu ==
'function') main.FillPaletteMenu(menu);
1770 menu.add(
"sub:Labels");
1771 menu.addchk(faxis.TestBit(JSROOT.EAxisBits.kCenterLabels),
"Center",
1772 function() { faxis.InvertBit(JSROOT.EAxisBits.kCenterLabels); this.RedrawPad(); });
1773 menu.addchk(faxis.TestBit(JSROOT.EAxisBits.kLabelsVert),
"Rotate",
1774 function() { faxis.InvertBit(JSROOT.EAxisBits.kLabelsVert); this.RedrawPad(); });
1775 this.AddColorMenuEntry(menu,
"Color", faxis.fLabelColor,
1776 function(arg) { faxis.fLabelColor = parseInt(arg); this.InteractiveRedraw(
"pad", this.GetColorExec(parseInt(arg),
"SetLabelColor"), kind); }.bind(main ||
this));
1777 this.AddSizeMenuEntry(menu,
"Offset", 0, 0.1, 0.01, faxis.fLabelOffset,
1778 function(arg) { faxis.fLabelOffset = parseFloat(arg); this.RedrawPad(); } );
1779 this.AddSizeMenuEntry(menu,
"Size", 0.02, 0.11, 0.01, faxis.fLabelSize,
1780 function(arg) { faxis.fLabelSize = parseFloat(arg); this.RedrawPad(); } );
1781 menu.add(
"endsub:");
1782 menu.add(
"sub:Title");
1783 menu.add(
"SetTitle",
function() {
1784 var t = prompt(
"Enter axis title", faxis.fTitle);
1785 if (t!==null) { faxis.fTitle = t; this.RedrawPad(); }
1787 menu.addchk(faxis.TestBit(JSROOT.EAxisBits.kCenterTitle),
"Center",
1788 function() { faxis.InvertBit(JSROOT.EAxisBits.kCenterTitle); this.RedrawPad(); });
1789 menu.addchk(faxis.TestBit(JSROOT.EAxisBits.kRotateTitle),
"Rotate",
1790 function() { faxis.InvertBit(JSROOT.EAxisBits.kRotateTitle); this.RedrawPad(); });
1791 this.AddColorMenuEntry(menu,
"Color", faxis.fTitleColor,
1792 function(arg) { faxis.fTitleColor = parseInt(arg); this.InteractiveRedraw(
"pad", this.GetColorExec(parseInt(arg),
"SetTitleColor"), kind); }.bind(main ||
this));
1793 this.AddSizeMenuEntry(menu,
"Offset", 0, 3, 0.2, faxis.fTitleOffset,
1794 function(arg) { faxis.fTitleOffset = parseFloat(arg); this.RedrawPad(); } );
1795 this.AddSizeMenuEntry(menu,
"Size", 0.02, 0.11, 0.01, faxis.fTitleSize,
1796 function(arg) { faxis.fTitleSize = parseFloat(arg); this.RedrawPad(); } );
1797 menu.add(
"endsub:");
1798 menu.add(
"sub:Ticks");
1799 if (faxis._typename ==
"TGaxis") {
1800 this.AddColorMenuEntry(menu,
"Color", faxis.fLineColor,
1801 function(arg) { faxis.fLineColor = parseInt(arg); this.RedrawPad(); });
1802 this.AddSizeMenuEntry(menu,
"Size", -0.05, 0.055, 0.01, faxis.fTickSize,
1803 function(arg) { faxis.fTickSize = parseFloat(arg); this.RedrawPad(); } );
1805 this.AddColorMenuEntry(menu,
"Color", faxis.fAxisColor,
1806 function(arg) { faxis.fAxisColor = parseInt(arg); this.InteractiveRedraw(
"pad", this.GetColorExec(parseInt(arg),
"SetAxisColor"), kind); }.bind(main ||
this));
1807 this.AddSizeMenuEntry(menu,
"Size", -0.05, 0.055, 0.01, faxis.fTickLength,
1808 function(arg) { faxis.fTickLength = parseFloat(arg); this.RedrawPad(); } );
1810 menu.add(
"endsub:");
1815 var alone = menu.size() == 0;
1818 menu.add(
"header:Frame");
1820 menu.add(
"separator");
1822 if (this.zoom_xmin !== this.zoom_xmax)
1823 menu.add(
"Unzoom X", this.Unzoom.bind(
this,
"x"));
1824 if (this.zoom_ymin !== this.zoom_ymax)
1825 menu.add(
"Unzoom Y", this.Unzoom.bind(
this,
"y"));
1826 if (this.zoom_zmin !== this.zoom_zmax)
1827 menu.add(
"Unzoom Z", this.Unzoom.bind(
this,
"z"));
1828 menu.add(
"Unzoom all", this.Unzoom.bind(
this,
"xyz"));
1831 menu.addchk(pad.fLogx,
"SetLogx",
this.ToggleLog.bind(
this,
"x"));
1833 menu.addchk(pad.fLogy,
"SetLogy",
this.ToggleLog.bind(
this,
"y"));
1835 if (main && (typeof main.Dimension ===
'function') && (main.Dimension() > 1))
1836 menu.addchk(pad.fLogz,
"SetLogz",
this.ToggleLog.bind(
this,
"z"));
1837 menu.add(
"separator");
1840 menu.addchk(this.IsTooltipAllowed(),
"Show tooltips",
function() {
1841 this.SetTooltipAllowed(
"toggle");
1843 this.FillAttContextMenu(menu, alone ?
"" :
"Frame ");
1844 menu.add(
"separator");
1845 menu.add(
"Save as frame.png",
function() { this.pad_painter().SaveAs(
"png",
'frame',
'frame.png'); });
1846 menu.add(
"Save as frame.svg",
function() { this.pad_painter().SaveAs(
"svg",
'frame',
'frame.svg'); });
1851 TFramePainter.prototype.FillWebObjectOptions =
function(res) {
1852 res.fcust =
"frame";
1853 res.fopt = [this.scale_xmin || 0, this.scale_ymin || 0, this.scale_xmax || 0, this.scale_ymax || 0];
1857 TFramePainter.prototype.GetFrameRect =
function() {
1863 width: this.frame_width(),
1864 height: this.frame_height(),
1865 transform: this.draw_g ? this.draw_g.attr(
"transform") :
"",
1871 TFramePainter.prototype.ProcessFrameClick =
function(pnt, dblckick) {
1875 var pp = this.pad_painter();
1878 pnt.painters =
true;
1879 pnt.disabled =
true;
1882 var hints = pp.GetTooltips(pnt), exact = null;
1883 for (var k=0; (k<hints.length) && !exact; ++k)
1884 if (hints[k] && hints[k].exact) exact = hints[k];
1891 var handler = dblckick ? this._dblclick_handler : this._click_handler;
1892 if (handler) res = handler(exact.user_info, pnt);
1896 pp.SelectObjectPainter(exact ? exact.painter :
this,
1897 { x: pnt.x + (
this._frame_x || 0), y: pnt.y + (
this._frame_y || 0) });
1902 TFramePainter.prototype.AddKeysHandler =
function() {
1903 if (this.keys_handler || JSROOT.BatchMode || (typeof window ==
'undefined'))
return;
1905 this.keys_handler = this.ProcessKeyPress.bind(
this);
1907 window.addEventListener(
'keydown', this.keys_handler,
false);
1910 TFramePainter.prototype.ProcessKeyPress =
function(evnt) {
1912 var main = this.select_main();
1913 if (main.empty())
return;
1916 switch (evnt.keyCode) {
1917 case 33: key =
"PageUp";
break;
1918 case 34: key =
"PageDown";
break;
1919 case 37: key =
"ArrowLeft";
break;
1920 case 38: key =
"ArrowUp";
break;
1921 case 39: key =
"ArrowRight";
break;
1922 case 40: key =
"ArrowDown";
break;
1923 case 42: key =
"PrintScreen";
break;
1924 case 106: key =
"*";
break;
1925 default:
return false;
1928 var pp = this.pad_painter();
1929 if (JSROOT.Painter.GetActivePad() !== pp)
return;
1931 if (evnt.shiftKey) key =
"Shift " + key;
1932 if (evnt.altKey) key =
"Alt " + key;
1933 if (evnt.ctrlKey) key =
"Ctrl " + key;
1935 var zoom = { name:
"x", dleft: 0, dright: 0 };
1938 case "ArrowLeft": zoom.dleft = -1; zoom.dright = 1;
break;
1939 case "ArrowRight": zoom.dleft = 1; zoom.dright = -1;
break;
1940 case "Ctrl ArrowLeft": zoom.dleft = zoom.dright = -1;
break;
1941 case "Ctrl ArrowRight": zoom.dleft = zoom.dright = 1;
break;
1942 case "ArrowUp": zoom.name =
"y"; zoom.dleft = 1; zoom.dright = -1;
break;
1943 case "ArrowDown": zoom.name =
"y"; zoom.dleft = -1; zoom.dright = 1;
break;
1944 case "Ctrl ArrowUp": zoom.name =
"y"; zoom.dleft = zoom.dright = 1;
break;
1945 case "Ctrl ArrowDown": zoom.name =
"y"; zoom.dleft = zoom.dright = -1;
break;
1948 if (zoom.dleft || zoom.dright) {
1949 if (!JSROOT.gStyle.Zooming)
return false;
1951 if (this.mode3d && (key.indexOf(
"Ctrl")!==0))
return false;
1952 this.AnalyzeMouseWheelEvent(null, zoom, 0.5);
1953 this.Zoom(zoom.name, zoom.min, zoom.max);
1954 if (zoom.changed) this.zoom_changed_interactive = 2;
1955 evnt.stopPropagation();
1956 evnt.preventDefault();
1958 var func = pp ? pp.FindButton(key) :
"";
1960 pp.PadButtonClick(func);
1961 evnt.stopPropagation();
1962 evnt.preventDefault();
1969 TFramePainter.prototype.clearInteractiveElements =
function() {
1970 JSROOT.Painter.closeMenu();
1971 if (this.zoom_rect) { this.zoom_rect.remove(); this.zoom_rect = null; }
1975 this.SwitchTooltip(
true);
1978 TFramePainter.prototype.startRectSel =
function() {
1981 if (this.zoom_kind > 100)
return;
1984 if ((d3.event.which || d3.event.button) !== 1)
return;
1986 d3.event.preventDefault();
1988 var pos = d3.mouse(this.svg_frame().node());
1990 this.clearInteractiveElements();
1991 this.zoom_origin = pos;
1993 var w = this.frame_width(), h = this.frame_height();
1995 this.zoom_curr = [ Math.max(0, Math.min(w,
this.zoom_origin[0])),
1996 Math.max(0, Math.min(h,
this.zoom_origin[1])) ];
1998 if ((this.zoom_origin[0] < 0) || (this.zoom_origin[0] > w)) {
2000 this.zoom_origin[0] = 0;
2001 this.zoom_origin[1] = this.zoom_curr[1];
2002 this.zoom_curr[0] = w;
2003 this.zoom_curr[1] += 1;
2004 }
else if ((this.zoom_origin[1] < 0) || (this.zoom_origin[1] > h)) {
2006 this.zoom_origin[0] = this.zoom_curr[0];
2007 this.zoom_origin[1] = 0;
2008 this.zoom_curr[0] += 1;
2009 this.zoom_curr[1] = h;
2012 this.zoom_origin[0] = this.zoom_curr[0];
2013 this.zoom_origin[1] = this.zoom_curr[1];
2016 d3.select(window).on(
"mousemove.zoomRect", this.moveRectSel.bind(
this))
2017 .on(
"mouseup.zoomRect", this.endRectSel.bind(
this),
true);
2019 this.zoom_rect = null;
2022 this.SwitchTooltip(
false);
2024 d3.event.stopPropagation();
2027 TFramePainter.prototype.moveRectSel =
function() {
2029 if ((this.zoom_kind == 0) || (this.zoom_kind > 100))
return;
2031 d3.event.preventDefault();
2032 var m = d3.mouse(this.svg_frame().node());
2034 m[0] = Math.max(0, Math.min(
this.frame_width(), m[0]));
2035 m[1] = Math.max(0, Math.min(
this.frame_height(), m[1]));
2037 switch (this.zoom_kind) {
2038 case 1: this.zoom_curr[0] = m[0]; this.zoom_curr[1] = m[1];
break;
2039 case 2: this.zoom_curr[0] = m[0];
break;
2040 case 3: this.zoom_curr[1] = m[1];
break;
2043 if (this.zoom_rect===null)
2044 this.zoom_rect = this.svg_frame()
2046 .attr(
"class",
"zoom")
2047 .attr(
"pointer-events",
"none");
2049 this.zoom_rect.attr(
"x", Math.min(
this.zoom_origin[0],
this.zoom_curr[0]))
2050 .attr(
"y", Math.min(
this.zoom_origin[1],
this.zoom_curr[1]))
2051 .attr(
"width", Math.abs(
this.zoom_curr[0] -
this.zoom_origin[0]))
2052 .attr(
"height", Math.abs(
this.zoom_curr[1] -
this.zoom_origin[1]));
2055 TFramePainter.prototype.endRectSel =
function() {
2056 if ((this.zoom_kind == 0) || (this.zoom_kind > 100))
return;
2058 d3.event.preventDefault();
2060 d3.select(window).on(
"mousemove.zoomRect", null)
2061 .on(
"mouseup.zoomRect", null);
2063 var m = d3.mouse(this.svg_frame().node()), changed = [
true,
true];
2064 m[0] = Math.max(0, Math.min(
this.frame_width(), m[0]));
2065 m[1] = Math.max(0, Math.min(
this.frame_height(), m[1]));
2067 switch (this.zoom_kind) {
2068 case 1: this.zoom_curr[0] = m[0]; this.zoom_curr[1] = m[1];
break;
2069 case 2: this.zoom_curr[0] = m[0]; changed[1] =
false;
break;
2070 case 3: this.zoom_curr[1] = m[1]; changed[0] =
false;
break;
2073 var xmin, xmax, ymin, ymax, isany =
false,
2074 idx = this.swap_xy ? 1 : 0, idy = 1 - idx;
2076 if (changed[idx] && (Math.abs(
this.zoom_curr[idx] -
this.zoom_origin[idx]) > 10)) {
2077 xmin = Math.min(this.RevertX(this.zoom_origin[idx]), this.RevertX(this.zoom_curr[idx]));
2078 xmax = Math.max(this.RevertX(this.zoom_origin[idx]), this.RevertX(this.zoom_curr[idx]));
2082 if (changed[idy] && (Math.abs(
this.zoom_curr[idy] -
this.zoom_origin[idy]) > 10)) {
2083 ymin = Math.min(this.RevertY(this.zoom_origin[idy]), this.RevertY(this.zoom_curr[idy]));
2084 ymax = Math.max(this.RevertY(this.zoom_origin[idy]), this.RevertY(this.zoom_curr[idy]));
2088 var kind = this.zoom_kind, pnt = (kind===1) ? { x: this.zoom_origin[0], y: this.zoom_origin[1] } : null;
2090 this.clearInteractiveElements();
2093 this.zoom_changed_interactive = 2;
2094 this.Zoom(xmin, xmax, ymin, ymax);
2098 var fp = this.frame_painter();
2099 if (fp) fp.ProcessFrameClick(pnt);
2102 var pp = this.pad_painter();
2103 if (pp) pp.SelectObjectPainter(this.x_handle);
2106 var pp = this.pad_painter();
2107 if (pp) pp.SelectObjectPainter(this.y_handle);
2115 TFramePainter.prototype.mouseDoubleClick =
function() {
2116 d3.event.preventDefault();
2117 var m = d3.mouse(this.svg_frame().node());
2118 this.clearInteractiveElements();
2120 var valid_x = (m[0] >= 0) && (m[0] <= this.frame_width()),
2121 valid_y = (m[1] >= 0) && (m[1] <= this.frame_height());
2123 if (valid_x && valid_y && this._dblclick_handler)
2124 if (this.ProcessFrameClick({ x: m[0], y: m[1] },
true))
return;
2127 if (!valid_x) kind = this.swap_xy ?
"x" :
"y";
else
2128 if (!valid_y) kind = this.swap_xy ?
"y" :
"x";
2129 if (this.Unzoom(kind))
return;
2131 var pp = this.pad_painter();
2132 if (pp) pp.SelectObjectPainter(pp, { x: m[0]+this.frame_x(), y: m[1]+this.frame_y(), dbl:
true });
2135 TFramePainter.prototype.ConfigureUserClickHandler =
function(handler) {
2136 this._click_handler = handler && (typeof handler ==
'function') ? handler : null;
2139 TFramePainter.prototype.ConfigureUserDblclickHandler =
function(handler) {
2140 this._dblclick_handler = handler && (typeof handler ==
'function') ? handler : null;
2143 TFramePainter.prototype.Zoom =
function(xmin, xmax, ymin, ymax, zmin, zmax) {
2148 if (this.projection)
return false;
2150 if (xmin===
"x") { xmin = xmax; xmax = ymin; ymin = undefined; }
else
2151 if (xmin===
"y") { ymax = ymin; ymin = xmax; xmin = xmax = undefined; }
else
2152 if (xmin===
"z") { zmin = xmax; zmax = ymin; xmin = xmax = ymin = undefined; }
2154 var zoom_x = (xmin !== xmax), zoom_y = (ymin !== ymax), zoom_z = (zmin !== zmax),
2155 unzoom_x =
false, unzoom_y =
false, unzoom_z =
false;
2159 if (xmin <= this.xmin) { xmin = this.xmin; cnt++; }
2160 if (xmax >= this.xmax) { xmax = this.xmax; cnt++; }
2161 if (cnt === 2) { zoom_x =
false; unzoom_x =
true; }
2163 unzoom_x = (xmin === xmax) && (xmin === 0);
2168 if (ymin <= this.ymin) { ymin = this.ymin; cnt++; }
2169 if (ymax >= this.ymax) { ymax = this.ymax; cnt++; }
2170 if (cnt === 2) { zoom_y =
false; unzoom_y =
true; }
2172 unzoom_y = (ymin === ymax) && (ymin === 0);
2177 if (zmin <= this.zmin) { zmin = this.zmin; cnt++; }
2178 if (zmax >= this.zmax) { zmax = this.zmax; cnt++; }
2179 if (cnt === 2) { zoom_z =
false; unzoom_z =
true; }
2181 unzoom_z = (zmin === zmax) && (zmin === 0);
2184 var changed =
false, main =
this;
2187 if (zoom_x || zoom_y || zoom_z)
2188 main.ForEachPainter(
function(obj) {
2189 if (zoom_x && obj.CanZoomIn(
"x", xmin, xmax)) {
2190 main.zoom_xmin = xmin;
2191 main.zoom_xmax = xmax;
2195 if (zoom_y && obj.CanZoomIn(
"y", ymin, ymax)) {
2196 main.zoom_ymin = ymin;
2197 main.zoom_ymax = ymax;
2201 if (zoom_z && obj.CanZoomIn(
"z", zmin, zmax)) {
2202 main.zoom_zmin = zmin;
2203 main.zoom_zmax = zmax;
2210 if (unzoom_x || unzoom_y || unzoom_z) {
2212 if (this.zoom_xmin !== this.zoom_xmax) changed =
true;
2213 this.zoom_xmin = this.zoom_xmax = 0;
2216 if (this.zoom_ymin !== this.zoom_ymax) changed =
true;
2217 this.zoom_ymin = this.zoom_ymax = 0;
2220 if (this.zoom_zmin !== this.zoom_zmax) changed =
true;
2221 this.zoom_zmin = this.zoom_zmax = 0;
2226 var pp = this.pad_painter();
2227 if (pp && pp.painters)
2228 pp.painters.forEach(
function(painter){
2229 if (painter && (typeof painter.UnzoomUserRange ==
'function'))
2230 if (painter.UnzoomUserRange(unzoom_x, unzoom_y, unzoom_z)) changed =
true;
2236 this.InteractiveRedraw(
"pad",
"zoom");
2241 TFramePainter.prototype.IsAxisZoomed =
function(axis) {
2242 return this[
'zoom_'+axis+
'min'] !==
this[
'zoom_'+axis+
'max'];
2245 TFramePainter.prototype.Unzoom =
function(dox, doy, doz) {
2246 if (typeof dox ===
'undefined') { dox =
true; doy =
true; doz =
true; }
else
2247 if (typeof dox ===
'string') { doz = dox.indexOf(
"z")>=0; doy = dox.indexOf(
"y")>=0; dox = dox.indexOf(
"x")>=0; }
2249 var last = this.zoom_changed_interactive;
2251 if (dox || doy || doz) this.zoom_changed_interactive = 1;
2253 var changed = this.Zoom(dox ? 0 : undefined, dox ? 0 : undefined,
2254 doy ? 0 : undefined, doy ? 0 : undefined,
2255 doz ? 0 : undefined, doz ? 0 : undefined);
2258 if ((dox || doy || doz) && !changed)
2259 this.zoom_changed_interactive = (!isNaN(last) && (last>0)) ? last - 1 : 0;
2264 TFramePainter.prototype.AnalyzeMouseWheelEvent =
function(event, item, dmin, ignore) {
2266 item.min = item.max = undefined;
2267 item.changed =
false;
2268 if (ignore && item.ignore)
return;
2270 var delta = 0, delta_left = 1, delta_right = 1;
2272 if (
'dleft' in item) { delta_left = item.dleft; delta = 1; }
2273 if (
'dright' in item) { delta_right = item.dright; delta = 1; }
2275 if (
'delta' in item) {
2277 }
else if (event && event.wheelDelta !== undefined ) {
2279 delta = -
event.wheelDelta;
2280 }
else if (event && event.deltaY !== undefined ) {
2282 delta =
event.deltaY;
2283 }
else if (event && event.detail !== undefined) {
2284 delta =
event.detail;
2287 if (delta===0)
return;
2288 delta = (delta<0) ? -0.2 : 0.2;
2291 delta_right *= delta;
2293 var lmin = item.min =
this[
"scale_"+item.name+
"min"],
2294 lmax = item.max =
this[
"scale_"+item.name+
"max"],
2295 gmin =
this[item.name+
"min"],
2296 gmax =
this[item.name+
"max"];
2298 if ((item.min === item.max) && (delta<0)) {
2303 if (item.min >= item.max)
return;
2305 if ((dmin>0) && (dmin<1)) {
2306 if (
this[
'log'+item.name]) {
2307 var factor = (item.min>0) ? JSROOT.log10(item.max/item.min) : 2;
2308 if (factor>10) factor = 10;
else if (factor<0.01) factor = 0.01;
2309 item.min = item.min / Math.pow(10, factor*delta_left*dmin);
2310 item.max = item.max * Math.pow(10, factor*delta_right*(1-dmin));
2312 var rx_left = (item.max - item.min), rx_right = rx_left;
2313 if (delta_left>0) rx_left = 1.001 * rx_left / (1-delta_left);
2314 item.min += -delta_left*dmin*rx_left;
2316 if (delta_right>0) rx_right = 1.001 * rx_right / (1-delta_right);
2318 item.max -= -delta_right*(1-dmin)*rx_right;
2320 if (item.min >= item.max)
2321 item.min = item.max = undefined;
2323 if (delta_left !== delta_right) {
2325 if (((item.min < gmin) && (lmin===gmin)) ||
2326 ((item.max > gmax) && (lmax==gmax)))
2327 item.min = item.max = undefined;
2331 item.min = item.max = undefined;
2334 item.changed = ((item.min !== undefined) && (item.max !== undefined));
2337 TFramePainter.prototype.AllowDefaultYZooming =
function() {
2342 var pad_painter = this.pad_painter();
2343 if (pad_painter && pad_painter.painters)
2344 for (var k = 0; k < pad_painter.painters.length; ++k) {
2345 var subpainter = pad_painter.painters[k];
2346 if (subpainter && (subpainter.wheel_zoomy !== undefined))
2347 return subpainter.wheel_zoomy;
2353 TFramePainter.prototype.mouseWheel =
function() {
2354 d3.event.stopPropagation();
2356 d3.event.preventDefault();
2357 this.clearInteractiveElements();
2359 var itemx = { name:
"x", ignore:
false },
2360 itemy = { name:
"y", ignore: !this.AllowDefaultYZooming() },
2361 cur = d3.mouse(this.svg_frame().node()),
2362 w = this.frame_width(), h = this.frame_height();
2364 this.AnalyzeMouseWheelEvent(d3.event,
this.swap_xy ? itemy : itemx, cur[0] / w, (cur[1] >=0) && (cur[1] <= h));
2366 this.AnalyzeMouseWheelEvent(d3.event,
this.swap_xy ? itemx : itemy, 1 - cur[1] / h, (cur[0] >= 0) && (cur[0] <= w));
2368 this.Zoom(itemx.min, itemx.max, itemy.min, itemy.max);
2370 if (itemx.changed || itemy.changed) this.zoom_changed_interactive = 2;
2373 TFramePainter.prototype.startTouchZoom =
function() {
2375 if (this.zoom_kind != 0) {
2376 d3.event.preventDefault();
2377 d3.event.stopPropagation();
2381 var arr = d3.touches(this.svg_frame().node());
2386 if (arr.length == 1) {
2389 var now =
new Date(), diff = now.getTime() - this.last_touch.getTime();
2390 this.last_touch = now;
2392 if ((diff < 300) && this.zoom_curr
2393 && (Math.abs(
this.zoom_curr[0] - arr[0][0]) < 30)
2394 && (Math.abs(
this.zoom_curr[1] - arr[0][1]) < 30)) {
2396 d3.event.preventDefault();
2397 d3.event.stopPropagation();
2399 this.clearInteractiveElements();
2402 this.last_touch =
new Date(0);
2404 this.svg_frame().on(
"touchcancel", null)
2405 .on(
"touchend", null,
true);
2407 if (JSROOT.gStyle.ContextMenu) {
2408 this.zoom_curr = arr[0];
2409 this.svg_frame().on(
"touchcancel", this.endTouchSel.bind(
this))
2410 .on(
"touchend", this.endTouchSel.bind(
this));
2411 d3.event.preventDefault();
2412 d3.event.stopPropagation();
2416 if ((arr.length != 2) || !JSROOT.gStyle.Zooming || !JSROOT.gStyle.ZoomTouch)
return;
2418 d3.event.preventDefault();
2419 d3.event.stopPropagation();
2421 this.clearInteractiveElements();
2423 this.svg_frame().on(
"touchcancel", null)
2424 .on(
"touchend", null);
2426 var pnt1 = arr[0], pnt2 = arr[1], w = this.frame_width(), h = this.frame_height();
2428 this.zoom_curr = [ Math.min(pnt1[0], pnt2[0]), Math.min(pnt1[1], pnt2[1]) ];
2429 this.zoom_origin = [ Math.max(pnt1[0], pnt2[0]), Math.max(pnt1[1], pnt2[1]) ];
2431 if ((this.zoom_curr[0] < 0) || (this.zoom_curr[0] > w)) {
2432 this.zoom_kind = 103;
2433 this.zoom_curr[0] = 0;
2434 this.zoom_origin[0] = w;
2435 }
else if ((this.zoom_origin[1] > h) || (this.zoom_origin[1] < 0)) {
2436 this.zoom_kind = 102;
2437 this.zoom_curr[1] = 0;
2438 this.zoom_origin[1] = h;
2440 this.zoom_kind = 101;
2443 this.SwitchTooltip(
false);
2445 this.zoom_rect = this.svg_frame().append(
"rect")
2446 .attr(
"class",
"zoom")
2447 .attr(
"id",
"zoomRect")
2448 .attr(
"x", this.zoom_curr[0])
2449 .attr(
"y", this.zoom_curr[1])
2450 .attr(
"width", this.zoom_origin[0] - this.zoom_curr[0])
2451 .attr(
"height", this.zoom_origin[1] - this.zoom_curr[1]);
2453 d3.select(window).on(
"touchmove.zoomRect", this.moveTouchSel.bind(
this))
2454 .on(
"touchcancel.zoomRect", this.endTouchSel.bind(
this))
2455 .on(
"touchend.zoomRect", this.endTouchSel.bind(
this));
2458 TFramePainter.prototype.moveTouchSel =
function() {
2459 if (this.zoom_kind < 100)
return;
2461 d3.event.preventDefault();
2463 var arr = d3.touches(this.svg_frame().node());
2465 if (arr.length != 2)
2466 return this.clearInteractiveElements();
2468 var pnt1 = arr[0], pnt2 = arr[1];
2470 if (this.zoom_kind != 103) {
2471 this.zoom_curr[0] = Math.min(pnt1[0], pnt2[0]);
2472 this.zoom_origin[0] = Math.max(pnt1[0], pnt2[0]);
2474 if (this.zoom_kind != 102) {
2475 this.zoom_curr[1] = Math.min(pnt1[1], pnt2[1]);
2476 this.zoom_origin[1] = Math.max(pnt1[1], pnt2[1]);
2479 this.zoom_rect.attr(
"x", this.zoom_curr[0])
2480 .attr(
"y", this.zoom_curr[1])
2481 .attr(
"width", this.zoom_origin[0] - this.zoom_curr[0])
2482 .attr(
"height", this.zoom_origin[1] - this.zoom_curr[1]);
2484 if ((this.zoom_origin[0] - this.zoom_curr[0] > 10)
2485 || (this.zoom_origin[1] - this.zoom_curr[1] > 10))
2486 this.SwitchTooltip(
false);
2488 d3.event.stopPropagation();
2491 TFramePainter.prototype.endTouchSel =
function() {
2493 this.svg_frame().on(
"touchcancel", null)
2494 .on(
"touchend", null);
2496 if (this.zoom_kind === 0) {
2499 d3.event.preventDefault();
2501 var now =
new Date();
2503 var diff = now.getTime() - this.last_touch.getTime();
2505 if ((diff > 500) && (diff<2000) && !this.frame_painter().IsTooltipShown()) {
2506 this.ShowContextMenu(
'main', { clientX: this.zoom_curr[0], clientY: this.zoom_curr[1] });
2507 this.last_touch =
new Date(0);
2509 this.clearInteractiveElements();
2513 if (this.zoom_kind < 100)
return;
2515 d3.event.preventDefault();
2516 d3.select(window).on(
"touchmove.zoomRect", null)
2517 .on(
"touchend.zoomRect", null)
2518 .on(
"touchcancel.zoomRect", null);
2520 var xmin, xmax, ymin, ymax, isany =
false,
2521 xid = this.swap_xy ? 1 : 0, yid = 1 - xid,
2522 changed = [
true,
true];
2523 if (this.zoom_kind === 102) changed[1] =
false;
2524 if (this.zoom_kind === 103) changed[0] =
false;
2526 if (changed[xid] && (Math.abs(
this.zoom_curr[xid] -
this.zoom_origin[xid]) > 10)) {
2527 xmin = Math.min(this.RevertX(this.zoom_origin[xid]), this.RevertX(this.zoom_curr[xid]));
2528 xmax = Math.max(this.RevertX(this.zoom_origin[xid]), this.RevertX(this.zoom_curr[xid]));
2532 if (changed[yid] && (Math.abs(
this.zoom_curr[yid] -
this.zoom_origin[yid]) > 10)) {
2533 ymin = Math.min(this.RevertY(this.zoom_origin[yid]), this.RevertY(this.zoom_curr[yid]));
2534 ymax = Math.max(this.RevertY(this.zoom_origin[yid]), this.RevertY(this.zoom_curr[yid]));
2538 this.clearInteractiveElements();
2539 this.last_touch =
new Date(0);
2542 this.zoom_changed_interactive = 2;
2543 this.Zoom(xmin, xmax, ymin, ymax);
2546 d3.event.stopPropagation();
2549 TFramePainter.prototype.ShowContextMenu =
function(kind, evnt, obj) {
2552 if ((
'zoom_kind' in
this) && (this.zoom_kind > 100))
return;
2561 var menu_painter =
this, exec_painter = null, frame_corner =
false, fp = null;
2564 d3.event.preventDefault();
2565 d3.event.stopPropagation();
2568 if (kind === undefined) {
2569 var ms = d3.mouse(this.svg_frame().node()),
2570 tch = d3.touches(this.svg_frame().node()),
2571 pp = this.pad_painter(),
2572 pnt = null, sel = null;
2576 if (tch.length === 1) pnt = { x: tch[0][0], y: tch[0][1], touch:
true };
else
2577 if (ms.length === 2) pnt = { x: ms[0], y: ms[1], touch:
false };
2579 if ((pnt !== null) && (pp !== null)) {
2580 pnt.painters =
true;
2581 var hints = pp.GetTooltips(pnt), bestdist = 1000;
2582 for (var n=0;n<hints.length;++n)
2583 if (hints[n] && hints[n].menu) {
2584 var dist = (
'menu_dist' in hints[n]) ? hints[n].menu_dist : 7;
2585 if (dist < bestdist) { sel = hints[n].painter; bestdist = dist; }
2589 if (sel) menu_painter = sel;
else kind =
"frame";
2591 if (pnt) frame_corner = (pnt.x>0) && (pnt.x<20) && (pnt.y>0) && (pnt.y<20);
2593 fp.SetLastEventPos(pnt);
2594 }
else if ((kind==
"x") || (kind==
"y") || (kind==
"z")) {
2595 exec_painter = this.main_painter();
2600 menu_painter.ctx_menu_evnt = evnt;
2602 if (!exec_painter) exec_painter = menu_painter;
2604 JSROOT.Painter.createMenu(menu_painter,
function(menu) {
2605 var domenu = menu.painter.FillContextMenu(menu, kind, obj);
2608 if (fp && (!domenu || (frame_corner && (kind!==
"frame"))))
2609 domenu = fp.FillContextMenu(menu);
2612 exec_painter.FillObjectExecMenu(menu, kind,
function() {
2614 menu.painter.SwitchTooltip(
false);
2615 menu.show(menu.painter.ctx_menu_evnt, menu.painter.SwitchTooltip.bind(menu.painter,
true));
2626 TFramePainter.prototype.ShowAxisStatus =
function(axis_name) {
2629 var status_func = this.GetShowStatusFunc();
2631 if (typeof status_func !=
"function")
return;
2633 var taxis = this.GetAxis(axis_name), hint_name = axis_name, hint_title =
"TAxis",
2634 m = d3.mouse(this.svg_frame().node()),
id = (axis_name==
"x") ? 0 : 1;
2636 if (taxis) { hint_name = taxis.fName; hint_title = taxis.fTitle || (
"TAxis object for " + axis_name); }
2637 if (this.swap_xy)
id = 1-id;
2639 var axis_value = (axis_name==
"x") ? this.RevertX(m[
id]) : this.RevertY(m[
id]);
2641 status_func(hint_name, hint_title, axis_name +
" : " + this.AxisAsText(axis_name, axis_value), m[0]+
","+m[1]);
2644 TFramePainter.prototype.AddInteractive =
function() {
2646 if (JSROOT.BatchMode || (!JSROOT.gStyle.Zooming && !JSROOT.gStyle.ContextMenu))
return;
2648 var pp = this.pad_painter();
2649 if (pp && pp._fast_drawing)
return;
2651 var svg = this.svg_frame();
2653 if (svg.empty())
return;
2655 var svg_x = svg.selectAll(
".xaxis_container"),
2656 svg_y = svg.selectAll(
".yaxis_container");
2658 if (!svg.property(
'interactive_set')) {
2659 this.AddKeysHandler();
2661 this.last_touch =
new Date(0);
2663 this.zoom_rect = null;
2664 this.zoom_origin = null;
2665 this.zoom_curr = null;
2669 if (JSROOT.gStyle.Zooming && !
this.projection) {
2670 if (JSROOT.gStyle.ZoomMouse) {
2671 svg.on(
"mousedown", this.startRectSel.bind(
this));
2672 svg.on(
"dblclick", this.mouseDoubleClick.bind(
this));
2674 if (JSROOT.gStyle.ZoomWheel) {
2675 svg.on(
"wheel", this.mouseWheel.bind(
this));
2679 if (JSROOT.touches && ((JSROOT.gStyle.Zooming && JSROOT.gStyle.ZoomTouch && !
this.projection) || JSROOT.gStyle.ContextMenu))
2680 svg.on(
"touchstart",
this.startTouchZoom.bind(
this));
2682 if (JSROOT.gStyle.ContextMenu) {
2683 if (JSROOT.touches) {
2684 svg_x.on(
"touchstart", this.startTouchMenu.bind(
this,
"x"));
2685 svg_y.on(
"touchstart", this.startTouchMenu.bind(
this,
"y"));
2687 svg.on(
"contextmenu", this.ShowContextMenu.bind(
this));
2688 svg_x.on(
"contextmenu", this.ShowContextMenu.bind(
this,
"x"));
2689 svg_y.on(
"contextmenu", this.ShowContextMenu.bind(
this,
"y"));
2692 svg_x.on(
"mousemove", this.ShowAxisStatus.bind(
this,
"x"));
2693 svg_y.on(
"mousemove", this.ShowAxisStatus.bind(
this,
"y"));
2695 svg.property(
'interactive_set',
true);
2698 function drawFrame(divid, obj, opt) {
2699 var p =
new TFramePainter(obj);
2700 if (opt ==
"3d") p.mode3d =
true;
2701 p.SetDivId(divid, 2);
2703 return p.DrawingReady();
2718 function TPadPainter(pad, iscan) {
2719 JSROOT.TObjectPainter.call(
this, pad);
2722 this.this_pad_name =
"";
2723 if (!this.iscan && (pad !== null) && (
'fName' in pad)) {
2724 this.this_pad_name = pad.fName.replace(
" ",
"_");
2725 var regexp =
new RegExp(
"^[A-Za-z][A-Za-z0-9_]*$");
2726 if (!regexp.test(
this.this_pad_name)) this.this_pad_name =
'jsroot_pad_' + JSROOT.id_counter++;
2729 this.has_canvas =
true;
2732 TPadPainter.prototype = Object.create(JSROOT.TObjectPainter.prototype);
2734 TPadPainter.prototype.Cleanup =
function() {
2737 for (var k=0;k<this.painters.length;++k)
2738 this.painters[k].Cleanup();
2740 var svg_p = this.svg_pad(this.this_pad_name);
2741 if (!svg_p.empty()) {
2742 svg_p.property(
'pad_painter', null);
2743 svg_p.property(
'mainpainter', null);
2744 if (!this.iscan) svg_p.remove();
2747 delete this.frame_painter_ref;
2748 delete this.pads_cache;
2749 delete this.custom_palette;
2753 this.this_pad_name =
"";
2754 this.has_canvas =
false;
2756 JSROOT.Painter.SelectActivePad({ pp:
this, active:
false });
2758 JSROOT.TObjectPainter.prototype.Cleanup.call(
this);
2765 TPadPainter.prototype.CleanPrimitives =
function(selector) {
2766 if (!selector || (typeof selector !==
'function'))
return;
2768 for (var k = this.painters.length-1; k >= 0; --k)
2769 if (selector(this.painters[k])) {
2770 this.painters[k].Cleanup();
2771 this.painters.splice(k, 1);
2778 TPadPainter.prototype.CreateAutoColor =
function() {
2779 var pp = this.canv_painter(),
2780 pad = this.root_pad(),
2781 numprimitives = pad && pad.fPrimitves ? pad.fPrimitves.arr.length : 5;
2783 var pal = this.get_palette(
true);
2785 var indx = this._auto_color || 0;
2786 this._auto_color = indx+1;
2789 if (numprimitives<2) numprimitives = 2;
2790 if (indx >= numprimitives) indx = numprimitives - 1;
2791 var palindx = Math.round(indx * (pal.getLength()-3) / (numprimitives-1));
2792 var colvalue = pal.getColor(palindx);
2793 var colindx = this.add_color(colvalue);
2797 this._auto_color = this._auto_color % 8;
2805 TPadPainter.prototype.ForEachPainterInPad =
function(userfunc, kind) {
2806 if (!kind) kind =
"all";
2807 if (kind!=
"objects") userfunc(
this);
2808 for (var k = 0; k < this.painters.length; ++k) {
2809 var sub = this.painters[k];
2810 if (typeof sub.ForEachPainterInPad ===
'function') {
2811 if (kind!=
"objects") sub.ForEachPainterInPad(userfunc, kind);
2812 }
else if (kind !=
"pads") userfunc(sub);
2816 TPadPainter.prototype.ButtonSize =
function(fact) {
2817 return Math.round((!fact ? 1 : fact) * (this.iscan || !this.has_canvas ? 16 : 12));
2821 TPadPainter.prototype.RegisterForPadEvents =
function(receiver) {
2822 this.pad_events_receiver = receiver;
2826 TPadPainter.prototype.SelectObjectPainter =
function(_painter, pos) {
2827 var istoppad = (this.iscan || !this.has_canvas),
2828 canp = istoppad ?
this : this.canv_painter(),
2829 pp = _painter instanceof TPadPainter ? _painter : _painter.pad_painter();
2831 if (pos && !istoppad)
2832 this.CalcAbsolutePosition(this.svg_pad(this.this_pad_name), pos);
2834 JSROOT.Painter.SelectActivePad({ pp: pp, active:
true });
2836 if (typeof canp.SelectActivePad ==
"function")
2837 canp.SelectActivePad(pp, _painter, pos);
2839 if (canp.pad_events_receiver)
2840 canp.pad_events_receiver({ what:
"select", padpainter: pp, painter: _painter, position: pos });
2844 TPadPainter.prototype.InteractiveObjectRedraw =
function(_painter) {
2845 var canp = (this.iscan || !this.has_canvas) ?
this : this.canv_painter(),
2846 pp = _painter instanceof TPadPainter ? _painter : _painter.pad_painter();
2848 if (canp && canp.pad_events_receiver)
2849 canp.pad_events_receiver({ what:
"redraw", padpainter: pp, painter: _painter });
2854 TPadPainter.prototype.SetActive =
function(on) {
2855 var fp = this.frame_painter();
2856 if (fp && (typeof fp.SetActive ==
'function')) fp.SetActive(on);
2861 TPadPainter.prototype.DrawActiveBorder =
function(svg_rect, is_active) {
2862 if (is_active !== undefined) {
2863 if (this.is_active_pad === is_active)
return;
2864 this.is_active_pad = is_active;
2867 if (this.is_active_pad === undefined)
return;
2870 svg_rect = this.iscan ? this.svg_canvas().select(
".canvas_fillrect") :
2871 this.svg_pad(this.this_pad_name).select(
".root_pad_border");
2873 var lineatt = this.is_active_pad ?
new JSROOT.TAttLineHandler({ style: 1, width: 1, color:
"red" }) : this.lineatt;
2875 if (!lineatt) lineatt =
new JSROOT.TAttLineHandler({ color:
"none" });
2877 svg_rect.call(lineatt.func);
2880 TPadPainter.prototype.CreateCanvasSvg =
function(check_resize, new_size) {
2882 var factor = null, svg = null, lmt = 5, rect = null, btns;
2884 if (check_resize > 0) {
2886 if (this._fixed_size)
return (check_resize > 1);
2888 svg = this.svg_canvas();
2890 if (svg.empty())
return false;
2892 factor = svg.property(
'height_factor');
2894 rect = this.check_main_resize(check_resize, null, factor);
2896 if (!rect.changed)
return false;
2898 btns = this.svg_layer(
"btns_layer");
2902 var render_to = this.select_main();
2904 if (render_to.style(
'position')==
'static')
2905 render_to.style(
'position',
'relative');
2907 svg = render_to.append(
"svg")
2908 .attr(
"class",
"jsroot root_canvas")
2909 .property(
'pad_painter',
this)
2910 .property(
'mainpainter', null)
2911 .property(
'current_pad',
"")
2912 .property(
'redraw_by_resize',
false);
2914 if (JSROOT.BatchMode) {
2915 svg.attr(
"xmlns",
"http://www.w3.org/2000/svg");
2916 svg.attr(
"xmlns:xlink",
"http://www.w3.org/1999/xlink");
2919 svg.append(
"svg:title").text(
"ROOT canvas");
2920 var frect = svg.append(
"svg:rect").attr(
"class",
"canvas_fillrect")
2921 .attr(
"x",0).attr(
"y",0);
2922 if (!JSROOT.BatchMode)
2923 frect.style(
"pointer-events",
"visibleFill")
2924 .on(
"dblclick", this.EnlargePad.bind(
this))
2925 .on(
"click", this.SelectObjectPainter.bind(
this,
this))
2926 .on(
"mouseenter", this.ShowObjectStatus.bind(
this));
2928 svg.append(
"svg:g").attr(
"class",
"primitives_layer");
2929 svg.append(
"svg:g").attr(
"class",
"info_layer");
2930 btns = svg.append(
"svg:g").attr(
"class",
"btns_layer")
2931 .property(
'leftside', JSROOT.gStyle.ToolBarSide ==
'left')
2932 .property(
'vertical', JSROOT.gStyle.ToolBarVert);
2934 if (JSROOT.gStyle.ContextMenu)
2935 svg.select(
".canvas_fillrect").on(
"contextmenu", this.ShowContextMenu.bind(
this));
2938 if (this.pad && this.pad.fCw &&
this.pad.fCh && (
this.pad.fCw > 0)) {
2939 factor = this.pad.fCh / this.pad.fCw;
2940 if ((factor < 0.1) || (factor > 10)) factor = 0.66;
2943 if (this._fixed_size) {
2944 render_to.style(
"overflow",
"auto");
2945 rect = { width: this.pad.fCw, height: this.pad.fCh };
2947 rect = this.check_main_resize(2, new_size, factor);
2951 this.createAttFill({ attr: this.pad });
2953 if ((rect.width<=lmt) || (rect.height<=lmt)) {
2954 svg.style(
"display",
"none");
2955 console.warn(
"Hide canvas while geometry too small w=" + rect.width +
" h=" + rect.height);
2956 rect.width = 200; rect.height = 100;
2958 svg.style(
"display", null);
2961 if (this._fixed_size) {
2964 .attr(
"width", rect.width)
2965 .attr(
"height", rect.height)
2966 .style(
"position",
"absolute");
2970 .style(
"width",
"100%")
2971 .style(
"height",
"100%")
2972 .style(
"position",
"absolute")
2976 .style(
"bottom", 0);
2981 svg.attr(
"viewBox",
"0 0 " + rect.width +
" " + rect.height)
2982 .attr(
"preserveAspectRatio",
"none")
2983 .property(
'height_factor', factor)
2984 .property(
'draw_x', 0)
2985 .property(
'draw_y', 0)
2986 .property(
'draw_width', rect.width)
2987 .property(
'draw_height', rect.height);
2989 var fill_rect = svg.select(
".canvas_fillrect")
2990 .attr(
"width", rect.width)
2991 .attr(
"height", rect.height)
2992 .call(this.fillatt.func);
2994 this._fast_drawing = JSROOT.gStyle.SmallPad && ((rect.width < JSROOT.gStyle.SmallPad.width) || (rect.height < JSROOT.gStyle.SmallPad.height));
2996 this.DrawActiveBorder(fill_rect);
2998 this.AlignBtns(btns, rect.width, rect.height, svg);
3003 TPadPainter.prototype.EnlargePad =
function() {
3006 d3.event.preventDefault();
3007 d3.event.stopPropagation();
3010 var svg_can = this.svg_canvas(),
3011 pad_enlarged = svg_can.property(
"pad_enlarged");
3013 if (this.iscan || !this.has_canvas || (!pad_enlarged && !this.HasObjectsToDraw() && !this.painters)) {
3014 if (this._fixed_size)
return;
3015 if (!this.enlarge_main(
'toggle'))
return;
3016 if (this.enlarge_main(
'state')==
'off') svg_can.property(
"pad_enlarged", null);
3017 }
else if (!pad_enlarged) {
3018 this.enlarge_main(
true,
true);
3019 svg_can.property(
"pad_enlarged", this.pad);
3020 }
else if (pad_enlarged === this.pad) {
3021 this.enlarge_main(
false);
3022 svg_can.property(
"pad_enlarged", null);
3024 console.error(
'missmatch with pad double click events');
3027 var was_fast = this._fast_drawing;
3029 this.CheckResize({ force:
true });
3031 if (this._fast_drawing != was_fast)
3035 TPadPainter.prototype.CreatePadSvg =
function(only_resize) {
3038 if (!this.has_canvas) {
3039 this.CreateCanvasSvg(only_resize ? 2 : 0);
3043 var svg_can = this.svg_canvas(),
3044 width = svg_can.property(
"draw_width"),
3045 height = svg_can.property(
"draw_height"),
3046 pad_enlarged = svg_can.property(
"pad_enlarged"),
3047 pad_visible = !pad_enlarged || (pad_enlarged === this.pad),
3048 w = Math.round(
this.pad.fAbsWNDC * width),
3049 h = Math.round(this.pad.fAbsHNDC * height),
3050 x = Math.round(this.pad.fAbsXlowNDC * width),
3051 y = Math.round(height * (1 - this.pad.fAbsYlowNDC)) - h,
3052 svg_pad = null, svg_rect = null, btns = null;
3054 if (pad_enlarged === this.pad) { w = width; h = height; x = y = 0; }
3057 svg_pad = this.svg_pad(this.this_pad_name);
3058 svg_rect = svg_pad.select(
".root_pad_border");
3059 btns = this.svg_layer(
"btns_layer", this.this_pad_name);
3061 svg_pad = svg_can.select(
".primitives_layer")
3063 .classed(
"__root_pad_" + this.this_pad_name,
true)
3064 .attr(
"pad", this.this_pad_name)
3065 .property(
'pad_painter',
this)
3066 .property(
'mainpainter', null);
3067 svg_rect = svg_pad.append(
"svg:rect").attr(
"class",
"root_pad_border");
3069 svg_pad.append(
"svg:g").attr(
"class",
"primitives_layer");
3070 btns = svg_pad.append(
"svg:g").attr(
"class",
"btns_layer")
3071 .property(
'leftside', JSROOT.gStyle.ToolBarSide !=
'left')
3072 .property(
'vertical', JSROOT.gStyle.ToolBarVert);
3074 if (JSROOT.gStyle.ContextMenu)
3075 svg_rect.on(
"contextmenu", this.ShowContextMenu.bind(
this));
3077 if (!JSROOT.BatchMode)
3078 svg_rect.attr(
"pointer-events",
"visibleFill")
3079 .on(
"dblclick", this.EnlargePad.bind(
this))
3080 .on(
"click", this.SelectObjectPainter.bind(
this,
this))
3081 .on(
"mouseenter", this.ShowObjectStatus.bind(
this));
3084 this.createAttFill({ attr: this.pad });
3085 this.createAttLine({ attr: this.pad, color0: this.pad.fBorderMode == 0 ?
'none' :
'' });
3089 .attr(
"display", pad_visible ? null :
"none")
3090 .attr(
"viewBox",
"0 0 " + w +
" " + h)
3091 .attr(
"preserveAspectRatio",
"none")
3096 .property(
'draw_x', x)
3097 .property(
'draw_y', y)
3098 .property(
'draw_width', w)
3099 .property(
'draw_height', h);
3101 svg_rect.attr(
"x", 0)
3105 .call(this.fillatt.func)
3106 .call(this.lineatt.func);
3108 this.DrawActiveBorder(svg_rect);
3110 this._fast_drawing = JSROOT.gStyle.SmallPad && ((w < JSROOT.gStyle.SmallPad.width) || (h < JSROOT.gStyle.SmallPad.height));
3112 if (svg_pad.property(
'can3d') === 1)
3115 .select(
".draw3d_" + this.this_pad_name)
3116 .style(
'display', pad_visible ?
'' :
'none');
3118 this.AlignBtns(btns, w, h);
3123 TPadPainter.prototype.CheckSpecial =
function(obj) {
3125 if (!obj || (obj._typename!==
"TObjArray"))
return false;
3127 if (obj.name ==
"ListOfColors") {
3129 if (this.options && this.options.CreatePalette) {
3131 for (var n = obj.arr.length -
this.options.CreatePalette; n<obj.arr.length; ++n) {
3132 var col = JSROOT.Painter.MakeColorRGB(obj.arr[n]);
3133 if (!col) { console.log(
'Fail to create color for palette'); arr = null;
break; }
3136 if (arr) this.custom_palette =
new JSROOT.ColorPalette(arr);
3139 if (!this.options || this.options.GlobalColors)
3140 JSROOT.Painter.adoptRootColors(obj);
3143 if (this.options && this.options.LocalColors)
3144 this.root_colors = JSROOT.Painter.extendRootColors(null, obj);
3148 if (obj.name ==
"CurrentColorPalette") {
3149 var arr = [], missing =
false;
3150 for (var n = 0; n < obj.arr.length; ++n) {
3151 var col = obj.arr[n];
3152 if (col && (col._typename ==
'TColor')) {
3153 arr[n] = JSROOT.Painter.MakeColorRGB(col);
3155 console.log(
'Missing color with index ' + n); missing =
true;
3158 if (!this.options || (!missing && !this.options.IgnorePalette))
3159 this.custom_palette =
new JSROOT.ColorPalette(arr);
3166 TPadPainter.prototype.CheckSpecialsInPrimitives =
function(can) {
3167 var lst = can ? can.fPrimitives : null;
3169 for (var i = 0; i < lst.arr.length; ++i) {
3170 if (this.CheckSpecial(lst.arr[i])) {
3171 lst.arr.splice(i,1);
3172 lst.opt.splice(i,1);
3178 TPadPainter.prototype.RemovePrimitive =
function(obj) {
3179 if (!this.pad || !this.pad.fPrimitives)
return;
3180 var indx = this.pad.fPrimitives.arr.indexOf(obj);
3181 if (indx>=0) this.pad.fPrimitives.RemoveAt(indx);
3184 TPadPainter.prototype.FindPrimitive =
function(exact_obj, classname, name) {
3185 if (!this.pad || !this.pad.fPrimitives)
return null;
3187 for (var i=0; i < this.pad.fPrimitives.arr.length; i++) {
3188 var obj = this.pad.fPrimitives.arr[i];
3190 if ((exact_obj !== null) && (obj !== exact_obj))
continue;
3192 if ((classname !== undefined) && (classname !== null))
3193 if (obj._typename !== classname)
continue;
3195 if ((name !== undefined) && (name !== null))
3196 if (obj.fName !== name)
continue;
3205 TPadPainter.prototype.HasObjectsToDraw =
function() {
3207 if (!this.pad || !this.pad.fPrimitives)
return false;
3209 for (var n=0;n<this.pad.fPrimitives.arr.length;++n)
3210 if (this.pad.fPrimitives.arr[n] &&
this.pad.fPrimitives.arr[n]._typename !=
"TPad")
return true;
3215 TPadPainter.prototype.DrawPrimitives =
function(indx, callback, ppainter) {
3219 this._doing_pad_draw =
true;
3222 this._start_tm = this._lasttm_tm =
new Date().getTime();
3225 this._num_primitives = this.pad && this.pad.fPrimitives ? this.pad.fPrimitives.arr.length : 0;
3229 if (ppainter && (typeof ppainter==
'object')) ppainter._primitive =
true;
3231 if (!this.pad || (indx >= this.pad.fPrimitives.arr.length)) {
3232 delete this._doing_pad_draw;
3233 delete this._current_primitive_indx;
3234 if (this._start_tm) {
3235 var spenttm =
new Date().getTime() - this._start_tm;
3236 if (spenttm > 1000) console.log(
"Canvas drawing took " + (spenttm*1e-3).toFixed(2) +
"s");
3237 delete this._start_tm;
3238 delete this._lasttm_tm;
3241 return JSROOT.CallBack(callback);
3245 var handle = { func: this.DrawPrimitives.bind(
this, indx+1, callback) };
3248 this._current_primitive_indx = indx;
3250 ppainter = JSROOT.draw(this.divid, this.pad.fPrimitives.arr[indx],
this.pad.fPrimitives.opt[indx], handle);
3254 if (!handle.completed)
return;
3256 if (!JSROOT.BatchMode &&
this.iscan) {
3257 var curtm =
new Date().getTime();
3258 if (curtm > this._lasttm_tm + 15000) {
3259 this._lasttm_tm = curtm;
3260 ppainter._primitive =
true;
3261 return requestAnimationFrame(handle.func);
3267 TPadPainter.prototype.GetTooltips =
function(pnt) {
3268 var painters = [], hints = [];
3271 if (this.painters !== null)
3272 this.painters.forEach(
function(obj) {
3273 if (
'ProcessTooltip' in obj) painters.push(obj);
3276 if (pnt) pnt.nproc = painters.length;
3278 painters.forEach(
function(obj) {
3279 var hint = obj.ProcessTooltip(pnt);
3280 if (!hint) hint = { user_info: null };
3282 if (hint && pnt && pnt.painters) hint.painter = obj;
3288 TPadPainter.prototype.FillContextMenu =
function(menu) {
3291 menu.add(
"header: " + this.pad._typename +
"::" +
this.pad.fName);
3293 menu.add(
"header: Canvas");
3295 menu.addchk(this.IsTooltipAllowed(),
"Show tooltips", this.SetTooltipAllowed.bind(
this,
"toggle"));
3297 if (!this._websocket) {
3299 function SetPadField(arg) {
3300 this.pad[arg.substr(1)] = parseInt(arg[0]);
3301 this.InteractiveRedraw(
"axes", arg.substr(1));
3304 menu.addchk(this.pad.fGridx,
'Grid x', (
this.pad.fGridx ?
'0' :
'1') +
'fGridx', SetPadField);
3305 menu.addchk(this.pad.fGridy,
'Grid y', (
this.pad.fGridy ?
'0' :
'1') +
'fGridy', SetPadField);
3306 menu.add(
"sub:Ticks x");
3307 menu.addchk(this.pad.fTickx == 0,
"normal",
"0fTickx", SetPadField);
3308 menu.addchk(this.pad.fTickx == 1,
"ticks on both sides",
"1fTickx", SetPadField);
3309 menu.addchk(this.pad.fTickx == 2,
"labels on both sides",
"2fTickx", SetPadField);
3310 menu.add(
"endsub:");
3311 menu.add(
"sub:Ticks y");
3312 menu.addchk(this.pad.fTicky == 0,
"normal",
"0fTicky", SetPadField);
3313 menu.addchk(this.pad.fTicky == 1,
"ticks on both sides",
"1fTicky", SetPadField);
3314 menu.addchk(this.pad.fTicky == 2,
"labels on both sides",
"2fTicky", SetPadField);
3315 menu.add(
"endsub:");
3317 this.FillAttContextMenu(menu);
3320 menu.add(
"separator");
3322 if (this.ActivateStatusBar)
3323 menu.addchk(this.HasEventStatus(),
"Event status", this.ActivateStatusBar.bind(
this,
'toggle'));
3325 if (this.enlarge_main() || (this.has_canvas && this.HasObjectsToDraw()))
3326 menu.addchk((this.enlarge_main(
'state')==
'on'),
"Enlarge " + (this.iscan ?
"canvas" :
"pad"), this.EnlargePad.bind(
this));
3328 var fname = this.this_pad_name;
3329 if (fname.length===0) fname = this.iscan ?
"canvas" :
"pad";
3331 menu.add(
"Save as "+ fname+
".png", fname+
".png", this.SaveAs.bind(
this,
"png",
false));
3332 menu.add(
"Save as "+ fname+
".svg", fname+
".svg", this.SaveAs.bind(
this,
"svg",
false));
3337 TPadPainter.prototype.ShowContextMenu =
function(evnt) {
3340 var pos = d3.mouse(this.svg_pad(this.this_pad_name).node());
3342 if (pos && (pos.length==2) && (pos[0]>0) && (pos[0]<10) && (pos[1]>0) && pos[1]<10)
return;
3344 d3.event.stopPropagation();
3345 d3.event.preventDefault();
3350 var fp = this.frame_painter();
3351 if (fp) fp.SetLastEventPos();
3354 JSROOT.Painter.createMenu(
this,
function(menu) {
3356 menu.painter.FillContextMenu(menu);
3358 menu.painter.FillObjectExecMenu(menu,
"",
function() { menu.show(evnt); });
3362 TPadPainter.prototype.Redraw =
function(resize) {
3365 if (this._doing_pad_draw)
3366 return console.log(
'Prevent redrawing', this.pad.fName);
3368 var showsubitems =
true;
3371 this.CreateCanvasSvg(2);
3373 showsubitems = this.CreatePadSvg(
true);
3377 for (var i = 0; i < this.painters.length; ++i) {
3378 var sub = this.painters[i];
3379 if (showsubitems || sub.this_pad_name) sub.Redraw(resize);
3383 TPadPainter.prototype.NumDrawnSubpads =
function() {
3384 if (this.painters === undefined)
return 0;
3388 for (var i = 0; i < this.painters.length; ++i) {
3389 var obj = this.painters[i].GetObject();
3390 if (obj && (obj._typename ===
"TPad")) num++;
3396 TPadPainter.prototype.RedrawByResize =
function() {
3397 if (this.access_3d_kind() === 1)
return true;
3399 for (var i = 0; i < this.painters.length; ++i)
3400 if (typeof this.painters[i].RedrawByResize ===
'function')
3401 if (this.painters[i].RedrawByResize())
return true;
3406 TPadPainter.prototype.CheckCanvasResize =
function(size, force) {
3408 if (!this.iscan && this.has_canvas)
return false;
3410 if ((size ===
true) || (size ===
false)) { force = size; size = null; }
3412 if (size && (typeof size ===
'object') && size.force) force =
true;
3414 if (!force) force = this.RedrawByResize();
3416 var changed = this.CreateCanvasSvg(force ? 2 : 1, size);
3421 for (var i = 0; i < this.painters.length; ++i)
3422 this.painters[i].Redraw(force ?
false :
true);
3427 TPadPainter.prototype.UpdateObject =
function(obj) {
3428 if (!obj)
return false;
3430 this.pad.fBits = obj.fBits;
3431 this.pad.fTitle = obj.fTitle;
3433 this.pad.fGridx = obj.fGridx;
3434 this.pad.fGridy = obj.fGridy;
3435 this.pad.fTickx = obj.fTickx;
3436 this.pad.fTicky = obj.fTicky;
3437 this.pad.fLogx = obj.fLogx;
3438 this.pad.fLogy = obj.fLogy;
3439 this.pad.fLogz = obj.fLogz;
3441 this.pad.fUxmin = obj.fUxmin;
3442 this.pad.fUxmax = obj.fUxmax;
3443 this.pad.fUymin = obj.fUymin;
3444 this.pad.fUymax = obj.fUymax;
3446 this.pad.fX1 = obj.fX1;
3447 this.pad.fX2 = obj.fX2;
3448 this.pad.fY1 = obj.fY1;
3449 this.pad.fY2 = obj.fY2;
3451 this.pad.fLeftMargin = obj.fLeftMargin;
3452 this.pad.fRightMargin = obj.fRightMargin;
3453 this.pad.fBottomMargin = obj.fBottomMargin
3454 this.pad.fTopMargin = obj.fTopMargin;
3456 this.pad.fFillColor = obj.fFillColor;
3457 this.pad.fFillStyle = obj.fFillStyle;
3458 this.pad.fLineColor = obj.fLineColor;
3459 this.pad.fLineStyle = obj.fLineStyle;
3460 this.pad.fLineWidth = obj.fLineWidth;
3462 this.pad.fPhi = obj.fPhi;
3463 this.pad.fTheta = obj.fTheta;
3465 if (this.iscan) this.CheckSpecialsInPrimitives(obj);
3467 var fp = this.frame_painter();
3468 if (fp) fp.UpdateAttributes(!fp.modified_NDC);
3470 if (!obj.fPrimitives)
return false;
3472 var isany =
false, p = 0;
3473 for (var n = 0; n < obj.fPrimitives.arr.length; ++n) {
3474 while (p < this.painters.length) {
3475 var pp = this.painters[p++];
3476 if (!pp._primitive)
continue;
3477 if (pp.UpdateObject(obj.fPrimitives.arr[n])) isany =
true;
3487 TPadPainter.prototype.DrawNextSnap =
function(lst, indx, call_back, objpainter) {
3491 this._doing_pad_draw =
true;
3492 this._snaps_map = {};
3493 this._num_primitives = lst ? lst.length : 0;
3498 if (objpainter && lst && lst[indx] && (objpainter.snapid === undefined)) {
3500 var pi = this.painters.indexOf(objpainter);
3501 if (pi<0) this.painters.push(objpainter);
3502 objpainter.snapid = lst[indx].fObjectID;
3503 if (objpainter.$primary && (pi>0) &&
this.painters[pi-1].$secondary) {
3504 this.painters[pi-1].snapid = objpainter.snapid +
"#hist";
3505 console.log(
'ASSIGN SECONDARY HIST ID', this.painters[pi-1].snapid);
3513 if (!lst || (indx >= lst.length)) {
3514 delete this._doing_pad_draw;
3515 delete this._snaps_map;
3516 delete this._current_primitive_indx;
3517 return JSROOT.CallBack(call_back,
this);
3520 var snap = lst[indx],
3521 snapid = snap.fObjectID,
3522 cnt = this._snaps_map[snapid];
3524 if (cnt) cnt++;
else cnt=1;
3525 this._snaps_map[snapid] = cnt;
3527 this._current_primitive_indx = indx;
3531 for (var k=0; k<this.painters.length; ++k) {
3532 if (this.painters[k].snapid === snapid)
3533 if (--cnt === 0) { objpainter = this.painters[k];
break; }
3537 var draw_callback = this.DrawNextSnap.bind(
this, lst, indx, call_back);
3541 if (snap.fKind === JSROOT.WebSnapIds.kObject) {
3542 if (objpainter.UpdateObject(snap.fSnapshot, snap.fOption)) objpainter.Redraw();
3546 if (snap.fKind === JSROOT.WebSnapIds.kSVG) {
3547 if (objpainter.UpdateObject(snap.fSnapshot)) objpainter.Redraw();
3551 if (snap.fKind === JSROOT.WebSnapIds.kSubPad) {
3552 return objpainter.RedrawPadSnap(snap, draw_callback);
3559 if (snap.fKind === JSROOT.WebSnapIds.kStyle) {
3560 JSROOT.extend(JSROOT.gStyle, snap.fSnapshot);
3565 if (snap.fKind === JSROOT.WebSnapIds.kColors) {
3567 var ListOfColors = [], arr = snap.fSnapshot.fOper.split(
";");
3568 for (var n=0;n<arr.length;++n) {
3569 var name = arr[n], p = name.indexOf(
":");
3571 ListOfColors[parseInt(name.substr(0,p))] =
"rgb(" + name.substr(p+1) +
")";
3573 p = name.indexOf(
"=");
3574 ListOfColors[parseInt(name.substr(0,p))] =
"rgba(" + name.substr(p+1) +
")";
3579 if (!this.options || this.options.GlobalColors)
3580 JSROOT.Painter.adoptRootColors(ListOfColors);
3583 if (this.options && this.options.LocalColors)
3584 this.root_colors = JSROOT.Painter.extendRootColors(null, ListOfColors);
3587 if (snap.fSnapshot.fBuf && (!
this.options || !
this.options.IgnorePalette)) {
3589 for (var n=0;n<snap.fSnapshot.fBuf.length;++n)
3590 palette[n] = ListOfColors[Math.round(snap.fSnapshot.fBuf[n])];
3592 this.custom_palette =
new JSROOT.ColorPalette(palette);
3598 if (snap.fKind === JSROOT.WebSnapIds.kSubPad) {
3600 var subpad = snap.fSnapshot;
3602 subpad.fPrimitives = null;
3604 var padpainter =
new TPadPainter(subpad,
false);
3605 padpainter.DecodeOptions(snap.fOption);
3606 padpainter.SetDivId(this.divid);
3607 padpainter.snapid = snap.fObjectID;
3609 padpainter.CreatePadSvg();
3611 if (padpainter.MatchObjectType(
"TPad") && snap.fPrimitives.length > 0) {
3612 padpainter.AddButton(JSROOT.ToolbarIcons.camera,
"Create PNG",
"PadSnapShot");
3613 padpainter.AddButton(JSROOT.ToolbarIcons.circle,
"Enlarge pad",
"EnlargePad");
3615 if (JSROOT.gStyle.ContextMenu)
3616 padpainter.AddButton(JSROOT.ToolbarIcons.question,
"Access context menus",
"PadContextMenus");
3620 var prev_name = padpainter.CurrentPadName(padpainter.this_pad_name);
3621 padpainter.DrawNextSnap(snap.fPrimitives, -1,
function() {
3622 padpainter.CurrentPadName(prev_name);
3623 draw_callback(padpainter);
3628 var handle = { func: draw_callback };
3631 if (snap.fKind === JSROOT.WebSnapIds.kObject)
3632 objpainter = JSROOT.draw(this.divid, snap.fSnapshot, snap.fOption, handle);
3634 if (snap.fKind === JSROOT.WebSnapIds.kSVG)
3635 objpainter = JSROOT.draw(this.divid, snap.fSnapshot, snap.fOption, handle);
3637 if (!handle.completed)
return;
3641 TPadPainter.prototype.FindSnap =
function(snapid) {
3643 if (this.snapid === snapid)
return this;
3645 if (!this.painters)
return null;
3647 for (var k=0;k<this.painters.length;++k) {
3648 var sub = this.painters[k];
3650 if (typeof sub.FindSnap ===
'function') sub = sub.FindSnap(snapid);
3651 else if (sub.snapid !== snapid) sub = null;
3653 if (sub)
return sub;
3659 TPadPainter.prototype.AddOnlineButtons =
function() {
3660 this.AddButton(JSROOT.ToolbarIcons.camera,
"Create PNG",
"CanvasSnapShot",
"Ctrl PrintScreen");
3661 if (JSROOT.gStyle.ContextMenu)
3662 this.AddButton(JSROOT.ToolbarIcons.question,
"Access context menus",
"PadContextMenus");
3664 if (this.enlarge_main(
'verify'))
3665 this.AddButton(JSROOT.ToolbarIcons.circle,
"Enlarge canvas",
"EnlargePad");
3667 if (this.brlayout) {
3668 this.AddButton(JSROOT.ToolbarIcons.diamand,
"Toggle Ged",
"ToggleGed");
3669 this.AddButton(JSROOT.ToolbarIcons.three_circles,
"Toggle Status",
"ToggleStatus");
3673 TPadPainter.prototype.RedrawPadSnap =
function(snap, call_back) {
3678 if (!snap || !snap.fPrimitives)
return;
3680 this.is_active_pad = !!snap.fActive;
3681 this._readonly = (snap.fReadOnly === undefined) ?
true : snap.fReadOnly;
3683 var first = snap.fSnapshot;
3684 first.fPrimitives = null;
3686 if (this.snapid === undefined) {
3689 this.snapid = snap.fObjectID;
3691 this.draw_object = first;
3696 if (this.batch_mode && (!first.fCw || !first.fCh)) { first.fCw = 900; first.fCh = 700; }
3699 if (!first.fCw || !first.fCh) this._fixed_size =
false;
3701 if (JSROOT.BrowserLayout && !
this.batch_mode && !
this.use_openui && !
this.brlayout) {
3702 var mainid = this.divid;
3703 if (mainid && (typeof mainid ==
'object'))
3704 mainid = d3.select(mainid).attr(
"id");
3705 if (mainid && (typeof mainid ==
"string")) {
3706 this.brlayout =
new JSROOT.BrowserLayout(mainid, null,
this);
3707 this.brlayout.Create(mainid,
true);
3709 this.SetDivId(this.brlayout.drawing_divid(), -1);
3710 JSROOT.RegisterForResize(this.brlayout);
3714 this.CreateCanvasSvg(0);
3715 this.SetDivId(this.divid);
3716 if (!this.batch_mode)
3717 this.AddOnlineButtons();
3719 if (snap.fScripts && (typeof snap.fScripts ==
"string")) {
3722 if (snap.fScripts.indexOf(
"load:") == 0) arg = snap.fScripts;
else
3723 if (snap.fScripts.indexOf(
"assert:") == 0) arg = snap.fScripts.substr(7);
3726 JSROOT.AssertPrerequisites(arg,
function() {
3727 painter.DrawNextSnap(snap.fPrimitives, -1, call_back);
3730 console.log(
'Calling eval ' + snap.fScripts.length);
3731 eval(snap.fScripts);
3732 console.log(
'Calling eval done');
3733 this.DrawNextSnap(snap.fPrimitives, -1, call_back);
3736 this.DrawNextSnap(snap.fPrimitives, -1, call_back);
3742 this.UpdateObject(first);
3746 this.CreateCanvasSvg(2);
3748 this.CreatePadSvg(
true);
3751 var isanyfound =
false, isanyremove =
false;
3755 function MatchPrimitive(painters, primitives, class_name, obj_name) {
3756 var painter, primitive;
3757 for (var k=0;k<painters.length;++k) {
3758 if (painters[k].snapid === undefined)
continue;
3759 if (!painters[k].MatchObjectType(class_name))
continue;
3760 if (obj_name && (!painters[k].GetObject() || (painters[k].GetObject().fName !== obj_name)))
continue;
3761 painter = painters[k];
3764 if (!painter)
return;
3765 for (var k=0;k<primitives.length;++k) {
3766 if ((primitives[k].fKind !== 1) || !primitives[k].fSnapshot || (primitives[k].fSnapshot._typename !== class_name))
continue;
3767 if (obj_name && (primitives[k].fSnapshot.fName !== obj_name))
continue;
3768 primitive = primitives[k];
3771 if (!primitive)
return;
3774 if (painter.snapid !== primitive.fObjectID)
3775 painter.snapid = primitive.fObjectID;
3779 MatchPrimitive(this.painters, snap.fPrimitives,
"TFrame");
3780 MatchPrimitive(this.painters, snap.fPrimitives,
"TPaveText",
"title");
3783 for (var k=0;k<this.painters.length;++k) {
3784 var sub = this.painters[k];
3785 if ((sub.snapid===undefined) || sub.$secondary)
continue;
3787 for (var i=0;i<snap.fPrimitives.length;++i)
3788 if (snap.fPrimitives[i].fObjectID === sub.snapid) { sub = null; isanyfound =
true;
break; }
3791 console.log(
'Remove painter' + k +
' from ' + this.painters.length +
' ' + sub.GetObject()._typename);
3793 this.painters.splice(k--,1);
3800 delete this.pads_cache;
3804 var svg_p = this.svg_pad(this.this_pad_name),
3805 fp = this.frame_painter();
3806 if (svg_p && !svg_p.empty())
3807 svg_p.property(
'mainpainter', null);
3808 for (var k=0;k<this.painters.length;++k)
3809 if (fp !== this.painters[k])
3810 this.painters[k].Cleanup();
3813 this.painters.push(fp);
3814 fp.CleanFrameDrawings();
3816 this.RemoveButtons();
3817 this.AddOnlineButtons();
3820 var padpainter =
this,
3821 prev_name = padpainter.CurrentPadName(padpainter.this_pad_name);
3823 padpainter.DrawNextSnap(snap.fPrimitives, -1,
function() {
3824 padpainter.CurrentPadName(prev_name);
3825 JSROOT.CallBack(call_back, padpainter);
3829 TPadPainter.prototype.CreateImage =
function(format, call_back) {
3830 if (format==
"pdf") {
3832 JSROOT.CallBack(call_back, btoa(
"dummy PDF file"));
3833 }
else if ((format==
"png") || (format==
"jpeg") || (format==
"svg")) {
3834 this.ProduceImage(
true, format,
function(res) {
3835 if ((format==
"svg") || !res)
3836 return JSROOT.CallBack(call_back, res);
3837 var separ = res.indexOf(
"base64,");
3838 JSROOT.CallBack(call_back, (separ>0) ? res.substr(separ+7) :
"");
3841 JSROOT.CallBack(call_back,
"");
3846 TPadPainter.prototype.GetWebPadOptions =
function(arg) {
3847 var is_top = (arg === undefined), elem = null, scan_subpads =
true;
3849 if (is_top && this._readonly)
return "";
3850 if (arg ===
"only_this") { is_top =
true; scan_subpads =
false; }
3851 if (is_top) arg = [];
3854 elem = { _typename:
"TWebPadOptions", snapid: this.snapid.toString(),
3855 active: !!this.is_active_pad,
3856 bits: 0, primitives: [],
3857 logx: this.pad.fLogx, logy: this.pad.fLogy, logz: this.pad.fLogz,
3858 gridx: this.pad.fGridx, gridy: this.pad.fGridy,
3859 tickx: this.pad.fTickx, ticky: this.pad.fTicky,
3860 mleft: this.pad.fLeftMargin, mright: this.pad.fRightMargin,
3861 mtop: this.pad.fTopMargin, mbottom: this.pad.fBottomMargin,
3862 zx1:0, zx2:0, zy1:0, zy2:0, zz1:0, zz2:0 };
3864 if (this.iscan) elem.bits = this.GetStatusBits();
3866 if (this.GetPadRanges(elem))
3869 console.log(
'fail to get ranges for pad ' + this.pad.fName);
3872 for (var k=0; k<this.painters.length; ++k) {
3873 var sub = this.painters[k];
3874 if (typeof sub.GetWebPadOptions ==
"function") {
3875 if (scan_subpads) sub.GetWebPadOptions(arg);
3876 }
else if (sub.snapid) {
3877 var opt = { _typename:
"TWebObjectOptions", snapid: sub.snapid.toString(), opt: sub.OptionsAsString(), fcust:
"", fopt: [] };
3878 if (typeof sub.FillWebObjectOptions ==
"function")
3879 opt = sub.FillWebObjectOptions(opt);
3880 elem.primitives.push(opt);
3884 if (is_top)
return JSROOT.toJSON(arg);
3887 TPadPainter.prototype.GetPadRanges =
function(r) {
3890 if (!r)
return false;
3892 var main = this.frame_painter_ref,
3893 p = this.svg_pad(this.this_pad_name);
3895 r.ranges = main && main.ranges_set ?
true :
false;
3897 r.ux1 = r.px1 = r.ranges ? main.scale_xmin : 0;
3898 r.uy1 = r.py1 = r.ranges ? main.scale_ymin : 0;
3899 r.ux2 = r.px2 = r.ranges ? main.scale_xmax : 0;
3900 r.uy2 = r.py2 = r.ranges ? main.scale_ymax : 0;
3903 if (main.zoom_xmin !== main.zoom_xmax) {
3904 r.zx1 = main.zoom_xmin; r.zx2 = main.zoom_xmax;
3907 if (main.zoom_ymin !== main.zoom_ymax) {
3908 r.zy1 = main.zoom_ymin; r.zy2 = main.zoom_ymax;
3911 if (main.zoom_zmin !== main.zoom_zmax) {
3912 r.zz1 = main.zoom_zmin; r.zz2 = main.zoom_zmax;
3916 if (!r.ranges || p.empty())
return true;
3919 var same =
function(x) {
return x; },
3920 exp10 =
function(x) {
return Math.pow(10, x); },
3921 func = main.logx ? JSROOT.log10 : same,
3922 func2 = main.logx ? exp10 : same,
3923 k = (func(main.scale_xmax) - func(main.scale_xmin))/p.property(
"draw_width"),
3924 x1 = func(main.scale_xmin) - k*p.property(
"draw_x"),
3925 x2 = x1 + k*p.property(
"draw_width");
3928 function match(v1, v0, range) {
3929 return (Math.abs(v0-v1)<Math.abs(range)*1e-10) ? v0 : v1;
3932 r.ux1 = match( func2(x1), r.ux1, r.px2-r.px1);
3933 r.ux2 = match( func2(x2), r.ux2, r.px2-r.px1);
3935 func = main.logy ? JSROOT.log10 : same;
3936 func2 = main.logy ? exp10 : same;
3938 k = (func(main.scale_ymax) - func(main.scale_ymin))/p.property(
"draw_height");
3939 var y2 = func(main.scale_ymax) + k*p.property(
"draw_y"),
3940 y1 = y2 - k*p.property(
"draw_height");
3942 r.uy1 = match( func2(y1), r.uy1, r.py2-r.py1);
3943 r.uy2 = match( func2(y2), r.uy2, r.py2-r.py1);
3948 TPadPainter.prototype.ItemContextMenu =
function(name) {
3949 var rrr = this.svg_pad(this.this_pad_name).node().getBoundingClientRect();
3950 var evnt = { clientX: rrr.left+10, clientY: rrr.top + 10 };
3954 return setTimeout(this.ShowContextMenu.bind(
this, evnt), 50);
3956 var selp = null, selkind;
3962 selp = this.frame_painter_ref;
3966 selp = this.frame_painter_ref;
3969 var indx = parseInt(name);
3970 if (!isNaN(indx)) selp = this.painters[indx];
3974 if (!selp || (typeof selp.FillContextMenu !==
'function'))
return;
3976 JSROOT.Painter.createMenu(selp,
function(menu) {
3977 if (selp.FillContextMenu(menu, selkind))
3978 setTimeout(menu.show.bind(menu, evnt), 50);
3982 TPadPainter.prototype.SaveAs =
function(kind, full_canvas, filename) {
3984 filename = this.this_pad_name;
3985 if (filename.length === 0) filename = this.iscan ?
"canvas" :
"pad";
3986 filename +=
"." + kind;
3988 this.ProduceImage(full_canvas, kind,
function(imgdata) {
3989 var a = document.createElement(
'a');
3990 a.download = filename;
3991 a.href = (kind !=
"svg") ? imgdata :
"data:image/svg+xml;charset=utf-8,"+encodeURIComponent(imgdata);
3992 document.body.appendChild(a);
3993 a.addEventListener(
"click",
function(e) {
3994 a.parentNode.removeChild(a);
4000 TPadPainter.prototype.ProduceImage =
function(full_canvas, file_format, call_back) {
4002 var use_frame = (full_canvas ===
"frame");
4004 var elem = use_frame ? this.svg_frame() : (full_canvas ? this.svg_canvas() : this.svg_pad(this.this_pad_name));
4006 if (elem.empty())
return JSROOT.CallBack(call_back);
4008 var painter = (full_canvas && !use_frame) ? this.canv_painter() :
this;
4015 painter.ForEachPainterInPad(
function(pp) {
4019 var item = { prnt: pp.svg_pad(pp.this_pad_name) };
4023 var btns = pp.svg_layer(
"btns_layer", pp.this_pad_name);
4024 item.btns_node = btns.node();
4025 if (item.btns_node) {
4026 item.btns_prnt = item.btns_node.parentNode;
4027 item.btns_next = item.btns_node.nextSibling;
4031 var main = pp.frame_painter_ref;
4032 if (!main || (typeof main.Render3D !==
'function'))
return;
4034 var can3d = main.access_3d_kind();
4036 if ((can3d !== 1) && (can3d !== 2))
return;
4038 var sz2 = main.size_for_3d(2);
4040 var sz = (can3d == 2) ? sz : main.size_for_3d(1);
4042 var canvas = main.renderer.domElement;
4044 var dataUrl = canvas.toDataURL(
"image/png");
4048 item.foreign = item.prnt.select(
"." + sz2.clname);
4049 item.foreign.remove();
4052 var svg_frame = main.svg_frame();
4053 item.frame_node = svg_frame.node();
4054 if (item.frame_node) {
4055 item.frame_next = item.frame_node.nextSibling;
4063 item.img = item.prnt.insert(
"image",
".primitives_layer")
4066 .attr(
"width", canvas.width)
4067 .attr(
"height", canvas.height)
4068 .attr(
"href", dataUrl);
4072 function reEncode(data) {
4073 data = encodeURIComponent(data);
4074 data = data.replace(/%([0-9A-F]{2})/g,
function(match, p1) {
4075 var c = String.fromCharCode(
'0x'+p1);
4076 return c ===
'%' ?
'%25' : c;
4078 return decodeURIComponent(data);
4081 function reconstruct(res) {
4082 for (var k=0;k<items.length;++k) {
4083 var item = items[k];
4088 var prim = item.prnt.select(
".primitives_layer");
4091 item.prnt.node().insertBefore(item.foreign.node(), prim.node());
4093 if (item.frame_node)
4094 prim.node().insertBefore(item.frame_node, item.frame_next);
4097 item.btns_prnt.insertBefore(item.btns_node, item.btns_next);
4100 JSROOT.CallBack(call_back, res);
4103 var width = elem.property(
'draw_width'), height = elem.property(
'draw_height');
4104 if (use_frame) { width = this.frame_width(); height = this.frame_height(); }
4106 var svg =
'<svg width="' + width +
'" height="' + height +
'" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">' +
4107 elem.node().innerHTML +
4110 if (file_format ==
"svg")
4111 return reconstruct(svg);
4113 var doctype =
'<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">';
4115 var image =
new Image();
4116 image.onload =
function() {
4117 var canvas = document.createElement(
'canvas');
4118 canvas.width = image.width;
4119 canvas.height = image.height;
4120 var context = canvas.getContext(
'2d');
4121 context.drawImage(image, 0, 0);
4123 reconstruct(canvas.toDataURL(
'image/' + file_format));
4126 image.onerror =
function(arg) {
4127 console.log(
'IMAGE ERROR', arg);
4131 image.src =
'data:image/svg+xml;base64,' + window.btoa(reEncode(doctype + svg));
4134 TPadPainter.prototype.PadButtonClick =
function(funcname) {
4136 if (funcname ==
"CanvasSnapShot")
return this.SaveAs(
"png",
true);
4138 if (funcname ==
"EnlargePad")
return this.EnlargePad();
4140 if (funcname ==
"PadSnapShot")
return this.SaveAs(
"png",
false);
4142 if (funcname ==
"PadContextMenus") {
4144 d3.event.preventDefault();
4145 d3.event.stopPropagation();
4147 if (JSROOT.Painter.closeMenu())
return;
4149 var pthis =
this, evnt = d3.event;
4151 JSROOT.Painter.createMenu(pthis,
function(menu) {
4152 menu.add(
"header:Menus");
4155 menu.add(
"Canvas",
"pad", pthis.ItemContextMenu);
4157 menu.add(
"Pad",
"pad", pthis.ItemContextMenu);
4159 if (pthis.frame_painter())
4160 menu.add(
"Frame",
"frame", pthis.ItemContextMenu);
4162 var main = pthis.main_painter();
4165 menu.add(
"X axis",
"xaxis", pthis.ItemContextMenu);
4166 menu.add(
"Y axis",
"yaxis", pthis.ItemContextMenu);
4167 if ((typeof main.Dimension ===
'function') && (main.Dimension() > 1))
4168 menu.add(
"Z axis",
"zaxis", pthis.ItemContextMenu);
4171 if (pthis.painters && (pthis.painters.length>0)) {
4172 menu.add(
"separator");
4174 for (var n=0;n<pthis.painters.length;++n) {
4175 var pp = pthis.painters[n];
4176 var obj = pp ? pp.GetObject() : null;
4177 if (!obj || (shown.indexOf(obj)>=0))
continue;
4179 var name = (
'_typename' in obj) ? (obj._typename +
"::") :
"";
4180 if (
'fName' in obj) name += obj.fName;
4181 if (name.length==0) name =
"item" + n;
4182 menu.add(name, n, pthis.ItemContextMenu);
4196 for (var i = 0; i < this.painters.length; ++i) {
4197 var pp = this.painters[i];
4199 if (typeof pp.PadButtonClick ==
'function')
4200 pp.PadButtonClick(funcname);
4202 if (!done && (typeof pp.ButtonClick ==
'function'))
4203 done = pp.ButtonClick(funcname);
4207 TPadPainter.prototype.FindButton =
function(keyname) {
4208 var group = this.svg_layer(
"btns_layer", this.this_pad_name), found_func =
"";
4210 group.selectAll(
"svg").each(
function() {
4211 if (d3.select(
this).attr(
"key") === keyname)
4212 found_func = d3.select(
this).attr(
"name");
4218 TPadPainter.prototype.toggleButtonsVisibility =
function(action) {
4219 var group = this.svg_layer(
"btns_layer", this.this_pad_name),
4220 btn = group.select(
"[name='Toggle']");
4222 if (btn.empty())
return;
4224 var state = btn.property(
'buttons_state');
4226 if (btn.property(
'timout_handler')) {
4227 if (action!==
'timeout') clearTimeout(btn.property(
'timout_handler'));
4228 btn.property(
'timout_handler', null);
4231 var is_visible =
false;
4233 case 'enable': is_visible =
true;
break;
4234 case 'enterbtn':
return;
4235 case 'timeout': is_visible =
false;
break;
4238 btn.property(
'buttons_state', state);
4243 if (!state) btn.property(
'timout_handler', setTimeout(this.toggleButtonsVisibility.bind(
this,
'timeout'), 500));
4247 group.selectAll(
'svg').each(
function() {
4248 if (
this===btn.node())
return;
4249 d3.select(
this).style(
'display', is_visible ?
"" :
"none");
4253 TPadPainter.prototype.RemoveButtons =
function() {
4254 var group = this.svg_layer(
"btns_layer", this.this_pad_name);
4255 if (!group.empty()) {
4256 group.selectAll(
"*").remove();
4257 group.property(
"nextx", null);
4261 TPadPainter.prototype.AddButton =
function(_btn, _tooltip, _funcname, _keyname) {
4262 if (!JSROOT.gStyle.ToolBar)
return;
4264 if (!this._buttons) this._buttons = [];
4267 for (var k=0;k<this._buttons.length;++k)
4268 if (this._buttons[k].funcname == _funcname)
return;
4270 this._buttons.push({ btn: _btn, tooltip: _tooltip, funcname: _funcname, keyname: _keyname });
4272 var iscan = this.iscan || !this.has_canvas;
4273 if (!iscan && (_funcname.indexOf(
"Pad")!=0) && (_funcname !==
"EnlargePad")) {
4274 var cp = this.canv_painter();
4275 if (cp && (cp!==
this)) cp.AddButton(_btn, _tooltip, _funcname);
4279 TPadPainter.prototype.ShowButtons =
function() {
4281 if (!this._buttons)
return;
4283 var group = this.svg_layer(
"btns_layer", this.this_pad_name);
4284 if (group.empty())
return;
4287 group.selectAll(
"*").remove();
4289 var iscan = this.iscan || !this.has_canvas, ctrl,
4290 x = group.property(
'leftside') ? this.ButtonSize(1.25) : 0, y = 0;
4292 if (this._fast_drawing) {
4293 ctrl = JSROOT.ToolbarIcons.CreateSVG(group, JSROOT.ToolbarIcons.circle,
this.ButtonSize(),
"EnlargePad");
4294 ctrl.attr(
"name",
"Enlarge").attr(
"x", 0).attr(
"y", 0)
4296 .on(
"click", this.PadButtonClick.bind(
this,
"EnlargePad"));
4298 ctrl = JSROOT.ToolbarIcons.CreateSVG(group, JSROOT.ToolbarIcons.rect,
this.ButtonSize(),
"Toggle tool buttons");
4300 ctrl.attr(
"name",
"Toggle").attr(
"x", 0).attr(
"y", 0)
4301 .property(
"buttons_state", (JSROOT.gStyle.ToolBar!==
'popup'))
4302 .on(
"click", this.toggleButtonsVisibility.bind(
this,
'toggle'))
4303 .on(
"mouseenter", this.toggleButtonsVisibility.bind(
this,
'enable'))
4304 .on(
"mouseleave", this.toggleButtonsVisibility.bind(
this,
'disable'));
4306 for (var k=0;k<this._buttons.length;++k) {
4307 var item = this._buttons[k];
4309 var svg = JSROOT.ToolbarIcons.CreateSVG(group, item.btn,
this.ButtonSize(),
4310 item.tooltip + (iscan ?
"" : (
" on pad " + this.this_pad_name)) + (item.keyname ?
" (keyshortcut " + item.keyname +
")" :
""));
4312 if (group.property(
'vertical'))
4313 svg.attr(
"x", y).attr(
"y", x);
4315 svg.attr(
"x", x).attr(
"y", y);
4317 svg.attr(
"name", item.funcname)
4318 .style(
'display', (ctrl.property(
"buttons_state") ?
'' :
'none'))
4319 .on(
"mouseenter", this.toggleButtonsVisibility.bind(
this,
'enterbtn'))
4320 .on(
"mouseleave", this.toggleButtonsVisibility.bind(
this,
'leavebtn'));
4322 if (item.keyname) svg.attr(
"key", item.keyname);
4324 svg.on(
"click", this.PadButtonClick.bind(
this, item.funcname));
4326 x += this.ButtonSize(1.25);
4330 group.property(
"nextx", x);
4332 this.AlignBtns(group, this.pad_width(this.this_pad_name), this.pad_height(this.this_pad_name));
4334 if (group.property(
'vertical')) ctrl.attr(
"y", x);
4335 else if (!group.property(
'leftside')) ctrl.attr(
"x", x);
4338 TPadPainter.prototype.AlignBtns =
function(btns, width, height, svg) {
4339 var sz0 = this.ButtonSize(1.25), nextx = (btns.property(
'nextx') || 0) + sz0, btns_x, btns_y;
4341 if (btns.property(
'vertical')) {
4342 btns_x = btns.property(
'leftside') ? 2 : (width - sz0);
4343 btns_y = height - nextx;
4345 btns_x = btns.property(
'leftside') ? 2 : (width - nextx);
4346 btns_y = height - sz0;
4349 btns.attr(
"transform",
"translate("+btns_x+
","+btns_y+
")");
4352 TPadPainter.prototype.DrawingReady =
function(res_painter) {
4354 var main = this.frame_painter_ref;
4356 if (main && main.mode3d && typeof main.Render3D ==
'function') main.Render3D(-2222);
4358 JSROOT.TObjectPainter.prototype.DrawingReady.call(
this, res_painter);
4361 TPadPainter.prototype.DecodeOptions =
function(opt) {
4362 var pad = this.GetObject();
4365 var d =
new JSROOT.DrawOptions(opt);
4367 if (d.check(
'WEBSOCKET')) this.OpenWebsocket();
4368 if (!this.options) this.options = {};
4370 JSROOT.extend(this.options, { GlobalColors:
true, LocalColors:
false, CreatePalette: 0, IgnorePalette:
false, RotateFrame:
false, FixFrame:
false });
4372 if (d.check(
'NOCOLORS') || d.check(
'NOCOL')) this.options.GlobalColors =
this.options.LocalColors =
false;
4373 if (d.check(
'LCOLORS') || d.check(
'LCOL')) { this.options.GlobalColors =
false; this.options.LocalColors =
true; }
4374 if (d.check(
'NOPALETTE') || d.check(
'NOPAL')) this.options.IgnorePalette =
true;
4375 if (d.check(
'ROTATE')) this.options.RotateFrame =
true;
4376 if (d.check(
'FIXFRAME')) this.options.FixFrame =
true;
4378 if (d.check(
"CP",
true)) this.options.CreatePalette = d.partAsInt(0,0);
4380 if (d.check(
'WHITE')) pad.fFillColor = 0;
4381 if (d.check(
'LOGX')) { pad.fLogx = 1; pad.fUxmin = 0; pad.fUxmax = 1; pad.fX1 = 0; pad.fX2 = 1; }
4382 if (d.check(
'LOGY')) { pad.fLogy = 1; pad.fUymin = 0; pad.fUymax = 1; pad.fY1 = 0; pad.fY2 = 1; }
4383 if (d.check(
'LOGZ')) pad.fLogz = 1;
4384 if (d.check(
'LOG')) pad.fLogx = pad.fLogy = pad.fLogz = 1;
4385 if (d.check(
'GRIDX')) pad.fGridx = 1;
4386 if (d.check(
'GRIDY')) pad.fGridy = 1;
4387 if (d.check(
'GRID')) pad.fGridx = pad.fGridy = 1;
4388 if (d.check(
'TICKX')) pad.fTickx = 1;
4389 if (d.check(
'TICKY')) pad.fTicky = 1;
4390 if (d.check(
'TICK')) pad.fTickx = pad.fTicky = 1;
4392 this.OptionsStore(opt);
4395 function drawPad(divid, pad, opt) {
4396 var painter =
new TPadPainter(pad,
false);
4397 painter.DecodeOptions(opt);
4399 painter.SetDivId(divid);
4401 if (painter.svg_canvas().empty()) {
4402 painter.has_canvas =
false;
4403 painter.this_pad_name =
"";
4406 painter.CreatePadSvg();
4408 if (painter.MatchObjectType(
"TPad") && (!painter.has_canvas || painter.HasObjectsToDraw())) {
4409 painter.AddButton(JSROOT.ToolbarIcons.camera,
"Create PNG",
"PadSnapShot");
4411 if ((painter.has_canvas && painter.HasObjectsToDraw()) || painter.enlarge_main(
'verify'))
4412 painter.AddButton(JSROOT.ToolbarIcons.circle,
"Enlarge pad",
"EnlargePad");
4414 if (JSROOT.gStyle.ContextMenu)
4415 painter.AddButton(JSROOT.ToolbarIcons.question,
"Access context menus",
"PadContextMenus");
4419 var prev_name = painter.has_canvas ? painter.CurrentPadName(painter.this_pad_name) : undefined;
4422 JSROOT.Painter.SelectActivePad({ pp: painter, active:
true });
4425 painter.DrawPrimitives(0,
function() {
4426 painter.ShowButtons();
4428 painter.CurrentPadName(prev_name);
4429 painter.DrawingReady();
4446 function TCanvasPainter(canvas) {
4447 TPadPainter.call(
this, canvas,
true);
4448 this._websocket = null;
4449 this.tooltip_allowed = (JSROOT.gStyle.Tooltip > 0);
4452 TCanvasPainter.prototype = Object.create(TPadPainter.prototype);
4454 TCanvasPainter.prototype.ChangeLayout =
function(layout_kind, call_back) {
4455 var current = this.get_layout_kind();
4456 if (current == layout_kind)
return JSROOT.CallBack(call_back,
true);
4458 var origin = this.select_main(
'origin'),
4459 sidebar = origin.select(
'.side_panel'),
4460 main = this.select_main(), lst = [];
4462 while (main.node().firstChild)
4463 lst.push(main.node().removeChild(main.node().firstChild));
4465 if (!sidebar.empty()) JSROOT.cleanup(sidebar.node());
4467 this.set_layout_kind(
"simple");
4470 if (layout_kind ==
'simple') {
4472 for (var k=0;k<lst.length;++k)
4473 main.node().appendChild(lst[k]);
4474 this.set_layout_kind(layout_kind);
4475 JSROOT.resize(main.node());
4476 return JSROOT.CallBack(call_back,
true);
4481 JSROOT.AssertPrerequisites(
"jq2d",
function() {
4483 var grid =
new JSROOT.GridDisplay(origin.node(), layout_kind);
4485 if (layout_kind.indexOf(
"vert")==0) {
4486 main = d3.select(grid.GetFrame(0));
4487 sidebar = d3.select(grid.GetFrame(1));
4489 main = d3.select(grid.GetFrame(1));
4490 sidebar = d3.select(grid.GetFrame(0));
4493 main.classed(
"central_panel",
true).style(
'position',
'relative');
4494 sidebar.classed(
"side_panel",
true).style(
'position',
'relative');
4497 for (var k=0;k<lst.length;++k)
4498 main.node().appendChild(lst[k]);
4500 pthis.set_layout_kind(layout_kind,
".central_panel");
4503 origin.property(
'mdi', null);
4506 JSROOT.resize(main.node());
4508 JSROOT.CallBack(call_back,
true);
4512 TCanvasPainter.prototype.ToggleProjection =
function(kind, call_back) {
4513 delete this.proj_painter;
4515 if (kind) this.proj_painter = 1;
4517 if (this.ShowUI5ProjectionArea)
4518 return this.ShowUI5ProjectionArea(kind, call_back);
4520 var layout =
'simple';
4522 if (kind ==
"X") layout =
'vert2_31';
else
4523 if (kind ==
"Y") layout =
'horiz2_13';
4525 this.ChangeLayout(layout, call_back);
4528 TCanvasPainter.prototype.DrawProjection =
function(kind,hist) {
4530 if (!this.proj_painter)
return;
4532 if (this.proj_painter === 1) {
4534 var canv = JSROOT.Create(
"TCanvas"),
4535 pthis =
this, pad = this.root_pad(),
4536 main = this.frame_painter_ref, drawopt;
4539 canv.fLeftMargin = pad.fLeftMargin;
4540 canv.fRightMargin = pad.fRightMargin;
4541 canv.fLogx = main.logx ? 1 : 0;
4542 canv.fUxmin = main.logx ? JSROOT.log10(main.scale_xmin) : main.scale_xmin;
4543 canv.fUxmax = main.logx ? JSROOT.log10(main.scale_xmax) : main.scale_xmax;
4544 drawopt =
"fixframe";
4546 canv.fBottomMargin = pad.fBottomMargin;
4547 canv.fTopMargin = pad.fTopMargin;
4548 canv.fLogx = main.logy ? 1 : 0;
4549 canv.fUxmin = main.logy ? JSROOT.log10(main.scale_ymin) : main.scale_ymin;
4550 canv.fUxmax = main.logy ? JSROOT.log10(main.scale_ymax) : main.scale_ymax;
4554 canv.fPrimitives.Add(hist,
"hist");
4556 if (this.DrawInUI5ProjectionArea) {
4558 this.DrawInUI5ProjectionArea(canv, drawopt,
function(painter) { pthis.proj_painter = painter; })
4560 this.DrawInSidePanel(canv, drawopt,
function(painter) { pthis.proj_painter = painter; })
4563 var hp = this.proj_painter.main_painter();
4564 if (hp) hp.UpdateObject(hist,
"hist");
4565 this.proj_painter.RedrawPad();
4569 TCanvasPainter.prototype.DrawInSidePanel =
function(canv, opt, call_back) {
4570 var side = this.select_main(
'origin').select(
".side_panel");
4571 if (side.empty())
return JSROOT.CallBack(call_back, null);
4572 JSROOT.draw(side.node(), canv, opt, call_back);
4575 TCanvasPainter.prototype.ShowMessage =
function(msg) {
4576 if (this.testUI5())
return;
4577 JSROOT.progress(msg, 7000);
4581 TCanvasPainter.prototype.SaveCanvasAsFile =
function(fname) {
4582 var pthis =
this, pnt = fname.indexOf(
".");
4583 this.CreateImage(fname.substr(pnt+1),
function(res) {
4584 pthis.SendWebsocket(
"SAVE:" + fname +
":" + res);
4588 TCanvasPainter.prototype.SendSaveCommand =
function(fname) {
4589 this.SendWebsocket(
"PRODUCE:" + fname);
4592 TCanvasPainter.prototype.WindowBeforeUnloadHanlder =
function() {
4594 this.CloseWebsocket(
true);
4597 TCanvasPainter.prototype.SendWebsocket =
function(msg) {
4598 if (!this._websocket)
return;
4599 if (this._websocket.CanSend())
4600 this._websocket.Send(msg);
4602 console.warn(
"DROP SEND: " + msg);
4605 TCanvasPainter.prototype.CloseWebsocket =
function(force) {
4606 if (this._websocket) {
4607 this._websocket.Close(force);
4608 this._websocket.Cleanup();
4609 delete this._websocket;
4613 TCanvasPainter.prototype.OpenWebsocket =
function(socket_kind) {
4617 this.CloseWebsocket();
4619 this._websocket =
new JSROOT.WebWindowHandle(socket_kind);
4620 this._websocket.SetReceiver(
this);
4621 this._websocket.Connect();
4624 TCanvasPainter.prototype.UseWebsocket =
function(handle, href) {
4625 this.CloseWebsocket();
4627 this._websocket = handle;
4628 this._websocket.SetReceiver(
this);
4629 this._websocket.Connect(href);
4632 TCanvasPainter.prototype.OnWebsocketOpened =
function(handle) {
4636 TCanvasPainter.prototype.OnWebsocketClosed =
function(handle) {
4637 JSROOT.CloseCurrentWindow();
4640 TCanvasPainter.prototype.OnWebsocketMsg =
function(handle, msg) {
4641 console.log(
"GET MSG len:" + msg.length +
" " + msg.substr(0,60));
4643 if (msg ==
"CLOSE") {
4644 this.OnWebsocketClosed();
4645 this.CloseWebsocket(
true);
4646 }
else if (msg.substr(0,6)==
'SNAP6:') {
4649 var snap = JSROOT.parse(msg.substr(6)), pthis =
this;
4651 this.RedrawPadSnap(snap,
function() {
4652 pthis.CompeteCanvasSnapDrawing();
4653 var ranges = pthis.GetWebPadOptions();
4654 if (ranges) ranges =
":" + ranges;
4655 handle.Send(
"READY6:" + snap.fVersion + ranges);
4657 }
else if (msg.substr(0,5)==
'MENU:') {
4659 var lst = JSROOT.parse(msg.substr(5));
4660 if (typeof this._getmenu_callback ==
'function')
4661 this._getmenu_callback(lst);
4662 }
else if (msg.substr(0,4)==
'CMD:') {
4663 msg = msg.substr(4);
4664 var p1 = msg.indexOf(
":"),
4665 cmdid = msg.substr(0,p1),
4666 cmd = msg.substr(p1+1),
4667 reply =
"REPLY:" + cmdid +
":";
4668 if ((cmd ==
"SVG") || (cmd ==
"PNG") || (cmd ==
"JPEG")) {
4669 this.CreateImage(cmd.toLowerCase(),
function(res) {
4670 handle.Send(reply + res);
4673 console.log(
'Unrecognized command ' + cmd);
4676 }
else if ((msg.substr(0,7)==
'DXPROJ:') || (msg.substr(0,7)==
'DYPROJ:')) {
4678 hist = JSROOT.parse(msg.substr(7));
4679 this.DrawProjection(kind, hist);
4680 }
else if (msg.substr(0,5)==
'SHOW:') {
4681 var that = msg.substr(5),
4682 on = (that[that.length-1] ==
'1');
4683 this.ShowSection(that.substr(0,that.length-2), on);
4684 }
else if (msg.substr(0,5) ==
"EDIT:") {
4685 var obj_painter = this.FindSnap(msg.substr(5));
4686 console.log(
'GET EDIT ' + msg.substr(5) +
' found ' + !!obj_painter);
4688 this.ShowSection(
"Editor",
true,
function() {
4689 if (this.pad_events_receiver)
4690 this.pad_events_receiver({ what:
"select", padpainter:
this, painter: obj_painter });
4694 console.log(
"unrecognized msg " + msg);
4698 TCanvasPainter.prototype.PadButtonClick =
function(funcname) {
4699 if (funcname ==
"ToggleGed")
return this.ActivateGed(
this, null,
"toggle");
4700 if (funcname ==
"ToggleStatus")
return this.ActivateStatusBar(
"toggle");
4701 TPadPainter.prototype.PadButtonClick.call(
this, funcname);
4704 TCanvasPainter.prototype.testUI5 =
function() {
4705 if (!this.use_openui)
return false;
4706 console.warn(
"full ui5 should be used - not loaded yet? Please check!!");
4710 TCanvasPainter.prototype.HasEventStatus =
function() {
4711 if (this.testUI5())
return false;
4712 return this.brlayout ? this.brlayout.HasStatus() :
false;
4715 TCanvasPainter.prototype.ActivateStatusBar =
function(state) {
4716 if (this.testUI5())
return;
4718 this.brlayout.CreateStatusLine(23, state);
4719 this.ProcessChanges(
"sbits",
this);
4723 TCanvasPainter.prototype.HasGed =
function() {
4724 if (this.testUI5())
return false;
4725 return this.brlayout ? this.brlayout.HasContent() :
false;
4729 TCanvasPainter.prototype.RemoveGed =
function() {
4730 if (this.testUI5())
return;
4732 this.RegisterForPadEvents(null);
4734 if (this.ged_view) {
4735 this.ged_view.getController().cleanupGed();
4736 this.ged_view.destroy();
4737 delete this.ged_view;
4740 this.brlayout.DeleteContent();
4742 this.ProcessChanges(
"sbits",
this);
4746 TCanvasPainter.prototype.ActivateGed =
function(objpainter, kind, mode, callback) {
4747 if (this.testUI5() || !this.brlayout)
4748 return JSROOT.CallBack(callback);
4750 if (this.brlayout.HasContent()) {
4751 if ((mode ===
"toggle") || (mode ===
false))
4754 this.SelectObjectPainter(objpainter);
4756 JSROOT.CallBack(callback,
true);
4760 return JSROOT.CallBack(callback);
4763 if (this._ged_callbacks !== undefined) {
4764 this._ged_callbacks.push(callback);
4768 this._ged_callbacks = [ callback ];
4770 var btns = this.brlayout.CreateBrowserBtns();
4772 JSROOT.ToolbarIcons.CreateSVG(btns, JSROOT.ToolbarIcons.diamand, 15,
"toggle fix-pos mode")
4773 .style(
"margin",
"3px").on(
"click", this.brlayout.Toggle.bind(
this.brlayout,
'fix'));
4775 JSROOT.ToolbarIcons.CreateSVG(btns, JSROOT.ToolbarIcons.circle, 15,
"toggle float mode")
4776 .style(
"margin",
"3px").on(
"click", this.brlayout.Toggle.bind(
this.brlayout,
'float'));
4778 JSROOT.ToolbarIcons.CreateSVG(btns, JSROOT.ToolbarIcons.cross, 15,
"delete GED")
4779 .style(
"margin",
"3px").on(
"click", this.RemoveGed.bind(
this));
4782 this.brlayout.SetBrowserContent(
"<div class='jsroot_browser_hierarchy' id='ged_placeholder'>Loading GED ...</div>");
4783 this.brlayout.SetBrowserTitle(
"GED");
4784 this.brlayout.ToggleBrowserKind(kind ||
"float");
4788 JSROOT.AssertPrerequisites(
'openui5',
function() {
4790 d3.select(
"#ged_placeholder").text(
"");
4792 sap.ui.define([
"sap/ui/model/json/JSONModel",
"sap/ui/core/mvc/XMLView"],
4793 function(JSONModel,XMLView) {
4795 var oModel =
new JSONModel({ handle: null });
4798 viewName :
"rootui5.canv.view.Ged"
4799 }).then(
function(oGed) {
4801 oGed.setModel(oModel);
4803 oGed.placeAt(
"ged_placeholder");
4805 pthis.ged_view = oGed;
4808 pthis.RegisterForPadEvents(oGed.getController().padEventsReceiver.bind(oGed.getController()));
4810 pthis.SelectObjectPainter(objpainter);
4812 pthis.ProcessChanges(
"sbits", pthis);
4815 var arr = pthis._ged_callbacks;
4816 delete pthis._ged_callbacks;
4817 arr.forEach(
function(func) { JSROOT.CallBack(func,
true) });
4823 TCanvasPainter.prototype.ShowSection =
function(that, on, callback) {
4825 return JSROOT.CallBack(callback);
4827 console.log(
'Show section ' + that +
' flag = ' + on);
4831 case "StatusBar": this.ActivateStatusBar(on);
break;
4832 case "Editor": this.ActivateGed(
this, null, !!on, callback); callback = null;
break;
4833 case "ToolBar":
break;
4834 case "ToolTips": this.SetTooltipAllowed(on);
break;
4837 JSROOT.CallBack(callback,
true);
4840 TCanvasPainter.prototype.CompeteCanvasSnapDrawing =
function() {
4841 if (!this.pad)
return;
4843 if (document) document.title = this.pad.fTitle;
4845 if (this._all_sections_showed)
return;
4846 this._all_sections_showed =
true;
4847 this.ShowSection(
"Menu", this.pad.TestBit(JSROOT.TCanvasStatusBits.kMenuBar));
4848 this.ShowSection(
"StatusBar", this.pad.TestBit(JSROOT.TCanvasStatusBits.kShowEventStatus));
4849 this.ShowSection(
"ToolBar", this.pad.TestBit(JSROOT.TCanvasStatusBits.kShowToolBar));
4850 this.ShowSection(
"Editor", this.pad.TestBit(JSROOT.TCanvasStatusBits.kShowEditor));
4851 this.ShowSection(
"ToolTips", this.pad.TestBit(JSROOT.TCanvasStatusBits.kShowToolTips));
4857 TCanvasPainter.prototype.ProcessChanges =
function(kind, painter, subelem) {
4859 if (!this._websocket || this._readonly || !this._websocket.CanSend(2) || (typeof kind !==
"string"))
return;
4862 if (!painter) painter =
this;
4865 msg =
"STATUSBITS:" + this.GetStatusBits();
4869 if (!painter.GetWebPadOptions)
4870 painter = painter.pad_painter();
4871 if (typeof painter.GetWebPadOptions ==
"function")
4872 msg =
"OPTIONS6:" + painter.GetWebPadOptions(
"only_this");
4875 if (painter.FillWebObjectOptions) {
4876 var info = painter.FillWebObjectOptions();
4877 if (info) msg =
"PRIMIT6:" + JSROOT.toJSON(info);
4881 if ((kind.substr(0,5) ==
"exec:") && painter && painter.snapid) {
4882 msg =
"PRIMIT6:" + JSROOT.toJSON({
4883 _typename:
"TWebObjectOptions",
4884 snapid: painter.snapid.toString() + (subelem ?
"#"+subelem :
""),
4885 opt: kind.substr(5),
4890 console.log(
"UNPROCESSED CHANGES", kind);
4895 console.log(
"Sending " + msg.length +
" " + msg.substr(0,40));
4896 this._websocket.Send(msg);
4901 TCanvasPainter.prototype.SelectActivePad =
function(pad_painter, obj_painter, click_pos) {
4902 if ((this.snapid === undefined) || !pad_painter)
return;
4904 var arg = null, ischanged =
false;
4906 if ((pad_painter.snapid !== undefined) && this._websocket)
4907 arg = { _typename:
"TWebPadClick", padid: pad_painter.snapid.toString(), objid:
"", x: -1, y: -1, dbl:
false };
4909 if (!pad_painter.is_active_pad) {
4911 this.ForEachPainterInPad(
function(pp) {
4912 pp.DrawActiveBorder(null, pp === pad_painter);
4916 if (obj_painter && (obj_painter.snapid!==undefined) && arg) {
4918 arg.objid = obj_painter.snapid.toString();
4921 if (click_pos && arg) {
4923 arg.x = Math.round(click_pos.x || 0);
4924 arg.y = Math.round(click_pos.y || 0);
4925 if (click_pos.dbl) arg.dbl =
true;
4928 if (arg && ischanged)
4929 this.SendWebsocket(
"PADCLICKED:" + JSROOT.toJSON(arg));
4932 TCanvasPainter.prototype.GetStatusBits =
function() {
4934 if (this.HasEventStatus()) bits |= JSROOT.TCanvasStatusBits.kShowEventStatus;
4935 if (this.HasGed()) bits |= JSROOT.TCanvasStatusBits.kShowEditor;
4936 if (this.IsTooltipAllowed()) bits |= JSROOT.TCanvasStatusBits.kShowToolTips;
4937 if (this.use_openui) bits |= JSROOT.TCanvasStatusBits.kMenuBar;
4942 TCanvasPainter.prototype.ProduceJSON =
function() {
4944 var canv = this.GetObject(),
4945 fill0 = (canv.fFillStyle == 0);
4947 if (fill0) canv.fFillStyle = 1001;
4949 if (!this.normal_canvas) {
4952 this.ForEachPainterInPad(
function(p) {
4953 if (p.$secondary)
return;
4955 var subobj = p.GetObject();
4956 if (subobj && subobj._typename)
4957 canv.fPrimitives.Add(subobj, p.OptionsAsString());
4961 var res = JSROOT.toJSON(canv);
4963 if (fill0) canv.fFillStyle = 0;
4965 if (!this.normal_canvas)
4966 canv.fPrimitives.Clear();
4972 TCanvasPainter.prototype.DirectGeoDraw =
function() {
4973 var lst = this.pad ? this.pad.fPrimitives : null;
4974 if (!lst || (lst.arr.length != 1))
return;
4976 var obj = lst.arr[0];
4977 if (obj && obj._typename && (obj._typename.indexOf(
"TGeo")==0))
4978 return JSROOT.draw(this.divid, obj, lst.opt[0]);
4981 function drawCanvas(divid, can, opt) {
4982 var nocanvas = !can;
4983 if (nocanvas) can = JSROOT.Create(
"TCanvas");
4985 var painter =
new TCanvasPainter(can);
4986 painter.SetDivId(divid, -1);
4988 if (!nocanvas && can.fCw && can.fCh && !JSROOT.BatchMode) {
4989 var rect0 = painter.select_main().node().getBoundingClientRect();
4990 if (!rect0.height && (rect0.width > 0.1*can.fCw)) {
4991 painter.select_main().style(
"width", can.fCw+
"px").style(
"height", can.fCh+
"px");
4992 painter._fixed_size =
true;
4996 var direct = painter.DirectGeoDraw();
4997 if (direct)
return direct;
4999 painter.DecodeOptions(opt);
5000 painter.normal_canvas = !nocanvas;
5001 painter.CheckSpecialsInPrimitives(can);
5002 painter.CreateCanvasSvg(0);
5003 painter.SetDivId(divid);
5005 painter.AddButton(JSROOT.ToolbarIcons.camera,
"Create PNG",
"CanvasSnapShot",
"Ctrl PrintScreen");
5006 if (JSROOT.gStyle.ContextMenu)
5007 painter.AddButton(JSROOT.ToolbarIcons.question,
"Access context menus",
"PadContextMenus");
5009 if (painter.enlarge_main(
'verify'))
5010 painter.AddButton(JSROOT.ToolbarIcons.circle,
"Enlarge canvas",
"EnlargePad");
5012 if (nocanvas && opt.indexOf(
"noframe") < 0)
5013 JSROOT.Painter.drawFrame(divid, null);
5016 JSROOT.Painter.SelectActivePad({ pp: painter, active:
true });
5018 painter.DrawPrimitives(0,
function() { painter.ShowButtons(); painter.DrawingReady(); });
5022 function drawPadSnapshot(divid, snap, opt) {
5025 var can = JSROOT.Create(
"TCanvas");
5027 var painter =
new TCanvasPainter(can);
5028 painter.normal_canvas =
false;
5030 painter.SetDivId(divid, -1);
5032 painter.AddButton(JSROOT.ToolbarIcons.camera,
"Create PNG",
"CanvasSnapShot",
"Ctrl PrintScreen");
5033 if (JSROOT.gStyle.ContextMenu)
5034 painter.AddButton(JSROOT.ToolbarIcons.question,
"Access context menus",
"PadContextMenus");
5036 if (painter.enlarge_main(
'verify'))
5037 painter.AddButton(JSROOT.ToolbarIcons.circle,
"Enlarge canvas",
"EnlargePad");
5039 painter.RedrawPadSnap(snap,
function() { painter.ShowButtons(); painter.DrawingReady(); });
5045 JSROOT.TAxisPainter = TAxisPainter;
5046 JSROOT.TFramePainter = TFramePainter;
5047 JSROOT.TPadPainter = TPadPainter;
5048 JSROOT.TCanvasPainter = TCanvasPainter;
5050 JSROOT.Painter.drawFrame = drawFrame;
5051 JSROOT.Painter.drawPad = drawPad;
5052 JSROOT.Painter.drawCanvas = drawCanvas;
5053 JSROOT.Painter.drawPadSnapshot = drawPadSnapshot;