5JSROOT.require("painter").then(jsrp => {
6 // cannot use JSROOT.define syntax here
8 if (typeof GO4 == 'undefined')
9 globalThis.GO4 = { version: "6.2.99", web_canvas: true, id_counter: 1 };
11 function findPainter(painter, obj, name, typ) {
12 let pp = painter.getPadPainter();
13 return pp ? pp.findPainterFor(obj, name, typ) : null;
16 class MarkerPainter extends JSROOT.ObjectPainter{
17 constructor(dom, marker) {
19 this.pave = null; // drawing of stat
25 this.draw_g.select('path').attr("d",this.markeratt.create(this.grx, this.gry));
29 let marker = this.getObject();
30 marker.fX = this.svgToAxis("x", this.grx);
31 marker.fY = this.svgToAxis("y", this.gry);
32 let exec = `SetXY(${marker.fX},${marker.fY})`;
33 this.submitCanvExec(exec);
38 let g = this.createG(), // can draw in complete pad
39 marker = this.getObject();
41 this.createAttMarker({ attr: marker });
43 this.grx = this.axisToSvg("x", marker.fX);
44 this.gry = this.axisToSvg("y", marker.fY);
46 let path = this.markeratt.create(this.grx, this.gry);
51 .call(this.markeratt.func);
53 if (typeof this.AddMove == 'function')
56 JSROOT.require(['interactive'])
57 .then(inter => inter.addMoveHandler(this));
62 rect = this.getFramePainter().getFrameRect(),
63 main = this.getMainPainter(), hint = null;
65 if (main && typeof main.processTooltipEvent == 'function')
66 hint = main.processTooltipEvent({ enabled: false, x: this.grx - rect.x, y: this.gry - rect.y });
68 lbls.push(marker.fxName + ((hint && hint.name) ? (" : " + hint.name) : ""));
71 lbls.push("X = " + jsrp.floatToString(marker.fX, "6.4g"));
74 lbls.push("Y = " + jsrp.floatToString(marker.fY, "6.4g"));
76 if (hint && hint.user_info) {
77 if (marker.fbXbinDraw) {
79 if (hint.user_info.binx !== undefined) bin = hint.user_info.binx; else
80 if (hint.user_info.bin !== undefined) bin = hint.user_info.bin;
81 lbls.push("Xbin = " + bin);
84 if (marker.fbYbinDraw) {
85 lbls.push("Ybin = " + ((hint.user_info.biny !== undefined) ? hint.user_info.biny : "<undef>"));
88 if (marker.fbContDraw)
89 lbls.push("Cont = " + hint.user_info.cont);
97 let marker = this.getObject();
99 if (!marker.fbHasLabel)
100 return Promise.resolve(this);
102 let pave_painter = findPainter(this, this.pave);
105 this.pave = JSROOT.create("TPaveStats");
106 this.pave.fName = "stats_" + marker.fName;
108 let pp = this.getPadPainter(),
109 pad_width = pp.getPadWidth(),
110 pad_height = pp.getPadHeight();
112 let px = this.grx / pad_width + 0.02,
113 py = this.gry / pad_height - 0.02;
114 JSROOT.extend(this.pave, { fX1NDC: px, fY1NDC: py - 0.15, fX2NDC: px + 0.2, fY2NDC: py, fBorderSize: 1, fFillColor: 0, fFillStyle: 1001 });
116 let st = JSROOT.gStyle;
117 JSROOT.extend(this.pave, { fFillColor: st.fStatColor, fFillStyle: st.fStatStyle, fTextAngle: 0, fTextSize: st.fStatFontSize,
118 fTextAlign: 12, fTextColor: st.fStatTextColor, fTextFont: st.fStatFont });
123 let lbls = this.fillLabels(marker);
124 for (let k = 0; k < lbls.length; ++k)
125 this.pave.AddText(lbls[k]);
127 let pr = pave_painter ? pave_painter.redraw() :
128 JSROOT.draw(this.divid, this.pave, "").then(p => { if (p) p.$secondary = true; });
129 return pr.then(() => this);
133 if (!this.updateObject(obj)) return false;
134 this.redraw(); // no need to redraw complete pad
140 let pp = findPainter(this, this.pave);
142 pp.removeFromPadPrimitives();
153 return this.drawLabel();
156 // fillContextMenu(menu) {
157 // let marker = this.getObject();
158 // menu.add("header:"+ marker._typename + "::" + marker.fxName);
159 // function select(name,exec) {
160 // let marker = this.getObject();
161 // marker[name] = !marker[name];
162 // this.submitCanvExec(exec + (marker[name] ? '(true)' : '(false)'));
165 // menu.addchk(marker.fbHasLabel, 'Label', select.bind(this, 'fbHasLabel', 'SetLabelDraw'));
166 // menu.addchk(marker.fbHasConnector, 'Connector', select.bind(this, 'fbHasConnector', 'SetLineDraw'));
167 // menu.addchk(marker.fbXDraw, 'Draw X', select.bind(this, 'fbXDraw', 'SetXDraw'));
168 // menu.addchk(marker.fbYDraw, 'Draw Y', select.bind(this, 'fbYDraw', 'SetYDraw'));
169 // menu.addchk(marker.fbXbinDraw, 'Draw X bin', select.bind(this, 'fbXbinDraw', 'SetXbinDraw'));
170 // menu.addchk(marker.fbYbinDraw, 'Draw Y bin', select.bind(this, 'fbYbinDraw', 'SetYbinDraw'));
171 // menu.addchk(marker.fbContDraw, 'Draw content', select.bind(this, 'fbContDraw', 'SetContDraw'));
175 processTooltipEvent(pnt) {
176 if (!pnt) return null;
178 let marker = this.getObject(),
179 rect = this.getFramePainter().getFrameRect(),
182 marker_sz = this.markeratt.getFullSize();
184 let hint = { name: marker.fxName,
185 title: marker.fxName,
190 color1: this.markeratt.color };
192 let dist = Math.sqrt(Math.pow(pnt.x - hint.x, 2) + Math.pow(pnt.y - hint.y, 2));
194 hint.menu_dist = dist;
196 if (dist < 2.5 * marker_sz) hint.exact = true;
199 hint.lines = this.fillLabels(marker);
201 // res.menu = res.exact; // activate menu only when exactly locate bin
202 // res.menu_dist = 3; // distance always fixed
210 GO4.drawGo4Marker = function(dom, obj /*, option */) {
211 let painter = new MarkerPainter(dom, obj);
212 painter.drawMarker();
213 painter.addToPadPrimitives();
215 return painter.drawLabel();
218 // =========================================================================
220 class ConditionPainter extends JSROOT.ObjectPainter {
221 constructor(dom, cond) {
223 this.pave = null; // drawing of stat
227 // JAM: need to put this here, since condition object will lose internal definition after cloning it again!
228 let cond = this.getObject();
230 return cond.fbResult;
233 return cond.fxCut.IsInside(x,y) ? cond.fbTrue : cond.fbFalse;
235 if ((x < cond.fLow1) || (x > cond.fUp1)) return cond.fbFalse;
237 if ((cond.fiDim==2) && ((y < cond.fLow2) || (y > cond.fUp2))) return cond.fbFalse;
244 return this.matchObjectType("TGo4PolyCond") || this.matchObjectType("TGo4ShapedCond");
248 return this.matchObjectType("TGo4ShapedCond");
252 if (!p || !this.snapid || p._oldexec) return;
253 p.snapid = this.snapid + "#member_fxCut";
254 p._condpainter = this;
256 // catch TCutG exec and mark condition as modified
257 p._oldexec = p.submitCanvExec;
258 p.submitCanvExec = function(exec, arg) {
259 this._oldexec(exec, arg);
260 p._condpainter.submitCanvExec("SetChanged()");
264 drawCondition(interactive) {
266 let cond = this.getObject();
268 if (!cond || !cond.fbVisible) return;
270 if (this.isPolyCond()) {
272 // look here if cut is already drawn in divid:
273 let cutpaint = findPainter(this, null, cond.fName, 'TCutG');
276 if (cutpaint.updateObject(cond.fxCut)) cutpaint.redraw();
277 this.afterCutDraw(cutpaint);
279 cond.fxCut.fFillStyle = 3006;
280 cond.fxCut.fFillColor = 2;
281 JSROOT.draw(this.divid, cond.fxCut, "LF").then(p => this.afterCutDraw(p));
289 let g = this.createG(true); // drawing performed inside frame
291 if ((cond.fFillStyle==1001) && (cond.fFillColor==19)) {
292 cond.fFillStyle = 3006;
296 this.createAttFill({attr: cond});
297 this.createAttLine({attr: cond});
299 this.grx1 = this.axisToSvg("x", cond.fLow1);
300 this.grx2 = this.axisToSvg("x", cond.fUp1);
302 if (cond.fiDim == 2) {
303 this.gry1 = this.axisToSvg("y", cond.fUp2);
304 this.gry2 = this.axisToSvg("y", cond.fLow2);
308 this.gry2 = this.getFramePainter().getFrameHeight();
313 .attr("x", this.grx1)
314 .attr("y", this.gry1)
315 .attr("width", this.grx2 - this.grx1)
316 .attr("height", this.gry2 - this.gry1)
317 .call(this.lineatt.func)
318 .call(this.fillatt.func);
320 if (typeof this.AddMove == 'function')
323 JSROOT.require(['interactive'])
324 .then(inter => inter.addMoveHandler(this));
328 this.swapx = this.swapy = false;
329 this.dx1 = Math.abs(x-this.grx1) < 5;
330 this.dx2 = Math.abs(x-this.grx2) < 5;
331 this.dy1 = Math.abs(y-this.gry1) < 5;
332 this.dy2 = Math.abs(y-this.gry2) < 5;
333 if (!this.dx1 && !this.dx2 && !this.dy1 && !this.dy2)
334 this.dx1 = this.dx2 = this.dy1 = this.dy2 = true;
335 if (!this.candy) this.dy1 = this.dy2 = false;
339 if (this.dx1) this.grx1 += dx;
340 if (this.dx2) this.grx2 += dx;
341 if (this.grx1 > this.grx2) {
343 let tempx = this.grx1; this.grx1 = this.grx2; this.grx2 = tempx;
344 tempx = this.dx1; this.dx1 = this.dx2; this.dx2 = tempx;
346 if (this.dy1) this.gry1 += dy;
347 if (this.dy2) this.gry2 += dy;
348 if (this.gry1 > this.gry2) {
350 let tempy = this.gry1; this.gry1 = this.gry2; this.gry2 = tempy;
351 tempy = this.dy1; this.dy1 = this.dy2; this.dy2 = tempy;
353 this.draw_g.select('rect').attr("x",this.grx1).attr("y", this.gry1)
354 .attr("width", this.grx2 - this.grx1).attr("height", this.gry2 - this.gry1);
358 let cond = this.getObject(), exec = "";
359 if (this.dx1 || this.swapx) { cond.fLow1 = this.svgToAxis("x", this.grx1); exec += "SetXLow(" + cond.fLow1 + ");;"; }
360 if (this.dx2 || this.swapx) { cond.fUp1 = this.svgToAxis("x", this.grx2); exec += "SetXUp(" + cond.fUp1 + ");;"; }
361 if (this.dy2 || this.swapy) { cond.fLow2 = this.svgToAxis("y", this.gry2); exec += "SetYLow(" + cond.fLow2 + ");;"; }
362 if (this.dy1 || this.swapy) { cond.fUp2 = this.svgToAxis("y", this.gry1); exec += "SetYUp(" + cond.fUp2 + ");;"; }
364 this.submitCanvExec(exec + "SetChanged()");
371 let cond = this.getObject(), painter = this, stat = {};
373 if (!cond.fbLabelDraw || !cond.fbVisible)
374 return Promise.resolve(this);
376 let pave_painter = findPainter(this, this.pave);
379 this.pave = JSROOT.create("TPaveStats");
380 this.pave.fName = "stats_" + cond.fName;
381 JSROOT.extend(this.pave, { fX1NDC: 0.1, fY1NDC: 0.4, fX2NDC: 0.4, fY2NDC: 0.65, fBorderSize: 1, fFillColor: 0, fFillStyle: 1001 });
383 let st = JSROOT.gStyle;
384 JSROOT.extend(this.pave, { fFillColor: st.fStatColor, fFillStyle: st.fStatStyle, fTextAngle: 0, fTextSize: st.fStatFontSize,
385 fTextAlign: 12, fTextColor: st.fStatTextColor, fTextFont: st.fStatFont});
390 this.pave.AddText(cond.fName);
392 this.pave.AddText("Counts = " + cond.fiCounts);
394 if (cond.fbLimitsDraw)
395 if (this.isPolyCond()) {
396 let res = { xmin: 0, xmax: 0, ymin: 0, ymax: 0 };
397 if (cond.fxCut.fNpoints > 0) {
398 res.xmin = res.xmax = cond.fxCut.fX[0];
399 res.ymin = res.ymax = cond.fxCut.fY[0];
400 for (let i=1; i<cond.fxCut.fNpoints; i++) {
401 res.xmin = Math.min(res.xmin, cond.fxCut.fX[i]);
402 res.xmax = Math.max(res.xmax, cond.fxCut.fX[i]);
403 res.ymin = Math.min(res.ymin, cond.fxCut.fY[i]);
404 res.ymax = Math.max(res.ymax, cond.fxCut.fY[i]);
407 this.pave.AddText("Xmin = " + res.xmin);
408 this.pave.AddText("Xmax = " + res.xmax);
409 this.pave.AddText("Ymin = " + res.ymin);
410 this.pave.AddText("Ymax = " + res.ymax);
412 this.pave.AddText("Xmin = " + cond.fLow1);
413 this.pave.AddText("Xmax = " + cond.fUp1);
415 this.pave.AddText("Ymin = " + cond.fLow2);
416 this.pave.AddText("Ymax = " + cond.fUp2);
420 stat = this.getMainPainter().countStat((x,y) => painter.Test(x,y));
422 if (cond.fbIntDraw) this.pave.AddText("Integral = " + jsrp.floatToString(stat.integral, "14.7g"));
424 if (cond.fbXMeanDraw) this.pave.AddText("Mean x = " + jsrp.floatToString(stat.meanx, "6.4g"));
426 if (cond.fbXRMSDraw) this.pave.AddText("RMS x = " + jsrp.floatToString(stat.rmsx, "6.4g"));
429 if (cond.fbYMeanDraw) this.pave.AddText("Mean y = " + jsrp.floatToString(stat.meany, "6.4g"));
430 if (cond.fbYRMSDraw) this.pave.AddText("RMS y = " + jsrp.floatToString(stat.rmsy, "6.4g"));
433 if (cond.fbXMaxDraw) this.pave.AddText("X max = " + jsrp.floatToString(stat.xmax, "6.4g"));
436 if (cond.fbYMaxDraw) this.pave.AddText("Y max = " + jsrp.floatToString(stat.ymax, "6.4g"));
439 if (cond.fbCMaxDraw) this.pave.AddText("C max = " + jsrp.floatToString(stat.wmax, "14.7g"));
441 let pr = pave_painter ? pave_painter.redraw() :
442 JSROOT.draw(this.divid, this.pave, "nofillstats");
443 return pr.then(() => this); // ensure that condition painter is returned
446 // fillContextMenu(menu) {
447 // let cond = this.getObject();
448 // menu.add("header:"+ cond._typename + "::" + cond.fName);
449 // function select(name,exec) {
450 // let cond = this.getObject();
451 // cond[name] = !cond[name];
452 // this.submitCanvExec(exec + (cond[name] ? '(true)' : '(false)'));
455 // menu.addchk(cond.fbLabelDraw, 'Label', select.bind(this, 'fbLabelDraw', 'SetLabelDraw'));
456 // menu.addchk(cond.fbLimitsDraw, 'Limits', select.bind(this, 'fbLimitsDraw', 'SetLimitsDraw'));
457 // menu.addchk(cond.fbIntDraw, 'Integral', select.bind(this, 'fbIntDraw', 'SetIntDraw'));
458 // menu.addchk(cond.fbXMeanDraw, 'X mean', select.bind(this, 'fbXMeanDraw', 'SetXMeanDraw'));
459 // menu.addchk(cond.fbXRMSDraw, 'X rms', select.bind(this, 'fbXRMSDraw', 'SetXRMSDraw'));
460 // menu.addchk(cond.fbXMaxDraw, 'X max', select.bind(this, 'fbXMaxDraw', 'SetXMaxDraw'));
461 // menu.addchk(cond.fbYMeanDraw, 'Y mean', select.bind(this, 'fbYMeanDraw', 'SetYMeanDraw'));
462 // menu.addchk(cond.fbYRMSDraw, 'Y rms', select.bind(this, 'fbYRMSDraw', 'SetYRMSDraw'));
463 // menu.addchk(cond.fbYMaxDraw, 'Y max', select.bind(this, 'fbYMaxDraw', 'SetYMaxDraw'));
467 processTooltipEvent(pnt) {
468 if (!pnt) return null;
470 let cond = this.getObject(),
471 hint = { name: cond.fName, title: cond.fTitle, painter: this, menu: true, x: pnt.x, y: pnt.y };
473 if (!this.isPolyCond()) {
475 hint.color1 = this.fillatt.color;
476 hint.color2 = this.lineatt.color;
478 hint.menu_dist = Math.sqrt(Math.pow(pnt.x - (this.grx1 + this.grx2)/2, 2) + Math.pow(pnt.y - (this.gry1 + this.gry2)/2, 2));
479 hint.exact = (this.grx1 <= pnt.x) && (pnt.x <= this.grx2) && (this.gry1 <= pnt.y) && (pnt.y <= this.gry2);
480 if (Math.abs(this.grx1 - pnt.x) < 5) hint.sidex = -1;
481 if (Math.abs(this.grx2 - pnt.x) < 5) hint.sidex = 1;
482 if (cond.fiDim == 2) {
483 if (Math.abs(this.gry1 - pnt.y) < 5) hint.sidey = 1;
484 if (Math.abs(this.gry2 - pnt.y) < 5) hint.sidey = -1;
489 hint.lines = ["condition", cond.fName ];
495 if (!this.updateObject(obj)) return false;
496 this.redraw(); // no need to redraw complete pad
502 let pp = findPainter(this, this.pave);
504 pp.removeFromPadPrimitives();
513 this.drawCondition();
514 return this.drawLabel();
518 let elem = this.selectDom();
519 if (elem.empty()) return "";
520 let id = elem.attr("id");
522 id = "go4_element_" + GO4.id_counter++;
530 GO4.drawGo4Cond = function(dom, cond, option) {
532 if (!option) option = "";
534 let condpainter = new ConditionPainter(dom, cond),
535 elem = condpainter.selectDom(),
536 main = condpainter.getMainPainter();
538 if (GO4.web_canvas || (option.indexOf('same') >= 0) || main) {
539 // if no hist painter, do nothing, just return dummy
541 return Promise.resolve(condpainter);
542 condpainter.drawCondition();
543 condpainter.addToPadPrimitives();
544 return condpainter.drawLabel();
547 // from here normal code for plain THttpServer
548 if ((option=='editor') || !cond.fxHistoName) {
549 // failure, should never happens!
551 return Promise.resolve(null);
553 let rect = elem.node().getBoundingClientRect();
554 if ((rect.height < 10) && (rect.width > 10))
555 elem.style("height", Math.round(rect.width*0.4) + "px");
556 return JSROOT.require(GO4.source_dir + 'html/condition.js').then(() => {
557 let editor = new GO4.ConditionEditor(dom, cond);
558 return editor.drawEditor();
562 if (!JSROOT.hpainter) return;
564 let histofullpath = null;
566 JSROOT.hpainter.forEachItem(h => {
567 if ((h._name == cond.fxHistoName) && h._kind && (h._kind.indexOf("ROOT.TH") == 0))
568 histofullpath = JSROOT.hpainter.itemFullName(h);
571 if (histofullpath === null) {
572 histofullpath = "../../Histograms/" + cond.fxHistoName;
574 let hitem = JSROOT.hpainter.findItem({ name: histofullpath, force: true });
576 hitem._kind = "ROOT.TH1I";
579 function drawCond(hpainter) {
580 if (!hpainter) return console.log("fail to draw histogram " + histofullpath);
581 condpainter.drawCondition();
582 condpainter.addToPadPrimitives();
583 return condpainter.drawLabel();
586 return JSROOT.hpainter.display(histofullpath, "divid:" + condpainter.getDomId()).then(hp => drawCond(hp));
589 GO4.drawCondArray = function(divid, obj, option) {
590 let arr = obj.condarr.arr,
593 for (let k = 0; k < num; ++k) {
594 let p = GO4.drawGo4Cond(divid, arr[k], "");
595 if (k == 0) first = p;
597 return first; // return first condition as result of drawing
600 // =======================================================================
602 if (GO4.web_canvas) {
603 jsrp.addDrawFunc({ name: "TGo4Marker", func: GO4.drawGo4Marker });
604 jsrp.addDrawFunc({ name: "TGo4WinCond", func: GO4.drawGo4Cond });
605 jsrp.addDrawFunc({ name: "TGo4PolyCond", func: GO4.drawGo4Cond });
606 jsrp.addDrawFunc({ name: "TGo4ShapedCond", func: GO4.drawGo4Cond });
607 jsrp.addDrawFunc({ name: "TGo4CondArray", func: GO4.drawCondArray });