GSI Object Oriented Online Offline (Go4) GO4-6.4.0
Loading...
Searching...
No Matches
go4canvas.mjs
Go to the documentation of this file.
1import { ObjectPainter, getHPainter, addDrawFunc, create, gStyle, draw, floatToString, getElementMainPainter } from 'jsroot';
2
3import { addMoveHandler } from 'jsrootsys/modules/gui/utils.mjs';
4
5import { GO4 } from './core.mjs';
6
7import { ConditionEditor } from './condition.mjs';
8
9GO4.web_canvas = true;
10
11function findPainter(painter, obj, name, typ) {
12 let pp = painter.getPadPainter();
13 return pp ? pp.findPainterFor(obj, name, typ) : null;
14}
15
16class MarkerPainter extends ObjectPainter{
17 constructor(dom, marker) {
18 super(dom, marker);
19 this.pave = null; // drawing of stat
20 }
21
22 moveDrag(dx,dy) {
23 this.grx += dx;
24 this.gry += dy;
25 this.draw_g.select('path').attr("d",this.markeratt.create(this.grx, this.gry));
26 }
27
28 moveEnd() {
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);
34 this.drawLabel();
35 }
36
37 drawMarker() {
38 let g = this.createG(), // can draw in complete pad
39 marker = this.getObject();
40
41 this.createAttMarker({ attr: marker });
42
43 this.grx = this.axisToSvg("x", marker.fX);
44 this.gry = this.axisToSvg("y", marker.fY);
45
46 let path = this.markeratt.create(this.grx, this.gry);
47
48 if (path)
49 g.append("svg:path")
50 .attr("d", path)
51 .call(this.markeratt.func);
52
53 addMoveHandler(this);
54 }
55
56 fillLabels(marker) {
57 let lbls = [],
58 rect = this.getFramePainter().getFrameRect(),
59 main = this.getMainPainter(), hint = null;
60
61 if (main && typeof main.processTooltipEvent == 'function')
62 hint = main.processTooltipEvent({ enabled: false, x: this.grx - rect.x, y: this.gry - rect.y });
63
64 lbls.push(marker.fxName + ((hint && hint.name) ? (" : " + hint.name) : ""));
65
66 if (marker.fbXDraw)
67 lbls.push("X = " + floatToString(marker.fX, "6.4g"));
68
69 if (marker.fbYDraw)
70 lbls.push("Y = " + floatToString(marker.fY, "6.4g"));
71
72 if (hint && hint.user_info) {
73 if (marker.fbXbinDraw) {
74 let bin = "<undef>";
75 if (hint.user_info.binx !== undefined) bin = hint.user_info.binx; else
76 if (hint.user_info.bin !== undefined) bin = hint.user_info.bin;
77 lbls.push("Xbin = " + bin);
78 }
79
80 if (marker.fbYbinDraw) {
81 lbls.push("Ybin = " + ((hint.user_info.biny !== undefined) ? hint.user_info.biny : "<undef>"));
82 }
83
84 if (marker.fbContDraw)
85 lbls.push("Cont = " + hint.user_info.cont);
86 }
87
88 return lbls;
89 }
90
91 drawLabel() {
92
93 let marker = this.getObject();
94
95 if (!marker.fbHasLabel)
96 return Promise.resolve(this);
97
98 let pave_painter = findPainter(this, this.pave);
99
100 if (!pave_painter) {
101 this.pave = create("TPaveStats");
102 this.pave.fName = "stats_" + marker.fName;
103
104 let pp = this.getPadPainter(),
105 pad_width = pp.getPadWidth(),
106 pad_height = pp.getPadHeight();
107
108 let px = this.grx / pad_width + 0.02,
109 py = this.gry / pad_height - 0.02;
110 Object.assign(this.pave, { fX1NDC: px, fY1NDC: py - 0.15, fX2NDC: px + 0.2, fY2NDC: py, fBorderSize: 1, fFillColor: 0, fFillStyle: 1001 });
111
112 const st = gStyle;
113 Object.assign(this.pave, { fFillColor: st.fStatColor, fFillStyle: st.fStatStyle, fTextAngle: 0, fTextSize: st.fStatFontSize,
114 fTextAlign: 12, fTextColor: st.fStatTextColor, fTextFont: st.fStatFont });
115 } else {
116 this.pave.Clear();
117 }
118
119 let lbls = this.fillLabels(marker);
120 for (let k = 0; k < lbls.length; ++k)
121 this.pave.AddText(lbls[k]);
122
123 let pr = pave_painter ? pave_painter.redraw() :
124 draw(this.getDrawDom(), this.pave, "").then(p => { if (p) p.$secondary = true; });
125 return pr.then(() => this);
126 }
127
128 redrawObject(obj) {
129 if (!this.updateObject(obj)) return false;
130 this.redraw(); // no need to redraw complete pad
131 return true;
132 }
133
134 cleanup(arg) {
135 if (this.pave) {
136 let pp = findPainter(this, this.pave);
137 if (pp) {
138 pp.removeFromPadPrimitives();
139 pp.cleanup();
140 }
141 delete this.pave;
142 }
143
144 super.cleanup(arg);
145 }
146
147 redraw() {
148 this.drawMarker();
149 return this.drawLabel();
150 }
151
152// fillContextMenu(menu) {
153// let marker = this.getObject();
154// menu.add("header:"+ marker._typename + "::" + marker.fxName);
155// function select(name,exec) {
156// let marker = this.getObject();
157// marker[name] = !marker[name];
158// this.submitCanvExec(exec + (marker[name] ? '(true)' : '(false)'));
159// this.redraw();
160// }
161// menu.addchk(marker.fbHasLabel, 'Label', select.bind(this, 'fbHasLabel', 'SetLabelDraw'));
162// menu.addchk(marker.fbHasConnector, 'Connector', select.bind(this, 'fbHasConnector', 'SetLineDraw'));
163// menu.addchk(marker.fbXDraw, 'Draw X', select.bind(this, 'fbXDraw', 'SetXDraw'));
164// menu.addchk(marker.fbYDraw, 'Draw Y', select.bind(this, 'fbYDraw', 'SetYDraw'));
165// menu.addchk(marker.fbXbinDraw, 'Draw X bin', select.bind(this, 'fbXbinDraw', 'SetXbinDraw'));
166// menu.addchk(marker.fbYbinDraw, 'Draw Y bin', select.bind(this, 'fbYbinDraw', 'SetYbinDraw'));
167// menu.addchk(marker.fbContDraw, 'Draw content', select.bind(this, 'fbContDraw', 'SetContDraw'));
168// return true;
169// }
170
171 processTooltipEvent(pnt) {
172 if (!pnt) return null;
173
174 let marker = this.getObject(),
175 rect = this.getFramePainter().getFrameRect(),
176 fx = rect.x,
177 fy = rect.y,
178 marker_sz = this.markeratt.getFullSize();
179
180 let hint = { name: marker.fxName,
181 title: marker.fxName,
182 painter: this,
183 menu: true,
184 x: this.grx - fx,
185 y: this.gry - fy,
186 color1: this.markeratt.color };
187
188 let dist = Math.sqrt(Math.pow(pnt.x - hint.x, 2) + Math.pow(pnt.y - hint.y, 2));
189
190 hint.menu_dist = dist;
191
192 if (dist < 2.5 * marker_sz) hint.exact = true;
193
194 if (hint.exact)
195 hint.lines = this.fillLabels(marker);
196
197 // res.menu = res.exact; // activate menu only when exactly locate bin
198 // res.menu_dist = 3; // distance always fixed
199
200 return hint;
201 }
202
203 ShowTooltip(hint) {}
204
205 static draw(dom, obj, /*, option */) {
206 const painter = new MarkerPainter(dom, obj);
207 painter.drawMarker();
208 painter.addToPadPrimitives();
209 painter.drawLabel();
210 return painter.drawLabel();
211 }
212
213} // class MarkerPainter
214
215
216// =========================================================================
217
218class ConditionPainter extends ObjectPainter {
219 constructor(dom, cond) {
220 super(dom, cond);
221 this.pave = null; // drawing of stat
222 }
223
224 Test(x,y) {
225 // JAM: need to put this here, since condition object will lose internal definition after cloning it again!
226 let cond = this.getObject();
227 if (!cond.fbEnabled)
228 return cond.fbResult;
229
230 if (cond.fxCut)
231 return cond.fxCut.IsInside(x,y) ? cond.fbTrue : cond.fbFalse;
232
233 if ((x < cond.fLow1) || (x > cond.fUp1)) return cond.fbFalse;
234
235 if ((cond.fiDim==2) && ((y < cond.fLow2) || (y > cond.fUp2))) return cond.fbFalse;
236
237 return cond.fbTrue;
238 }
239
240
241 isPolyCond() {
242 return this.matchObjectType("TGo4PolyCond") || this.matchObjectType("TGo4ShapedCond");
243 }
244
245 isEllipseCond() {
246 return this.matchObjectType("TGo4ShapedCond");
247 }
248
249 afterCutDraw(p) {
250 if (!p || !this.snapid || p._oldexec) return;
251 p.snapid = this.snapid + "#member_fxCut";
252 p._condpainter = this;
253
254 // catch TCutG exec and mark condition as modified
255 p._oldexec = p.submitCanvExec;
256 p.submitCanvExec = function(exec, arg) {
257 this._oldexec(exec, arg);
258 p._condpainter.submitCanvExec("SetChanged()");
259 }
260 }
261
262 drawCondition(interactive) {
263
264 let cond = this.getObject();
265
266 if (!cond || !cond.fbVisible) return;
267
268 if (this.isPolyCond()) {
269 if (cond.fxCut) {
270 // look here if cut is already drawn
271 let cutpaint = findPainter(this, null, cond.fName, 'TCutG');
272
273 if (cutpaint) {
274 if (cutpaint.updateObject(cond.fxCut)) cutpaint.redraw();
275 this.afterCutDraw(cutpaint);
276 } else {
277 cond.fxCut.fFillStyle = 3006;
278 cond.fxCut.fFillColor = 2;
279 draw(this.getDrawDom(), cond.fxCut, "LF").then(p => this.afterCutDraw(p));
280 }
281
282 }
283 return;
284 }
285
286 let g = this.createG(true); // drawing performed inside frame
287
288 if ((cond.fFillStyle==1001) && (cond.fFillColor==19)) {
289 cond.fFillStyle = 3006;
290 cond.fFillColor = 2;
291 }
292
293 this.createAttFill({attr: cond});
294 this.createAttLine({attr: cond});
295
296 this.grx1 = this.axisToSvg("x", cond.fLow1);
297 this.grx2 = this.axisToSvg("x", cond.fUp1);
298
299 if (cond.fiDim == 2) {
300 this.gry1 = this.axisToSvg("y", cond.fUp2);
301 this.gry2 = this.axisToSvg("y", cond.fLow2);
302 this.candy = true;
303 } else {
304 this.gry1 = 0;
305 this.gry2 = this.getFramePainter().getFrameHeight();
306 this.candy = false;
307 }
308
309 g.append("svg:rect")
310 .attr("x", this.grx1)
311 .attr("y", this.gry1)
312 .attr("width", this.grx2 - this.grx1)
313 .attr("height", this.gry2 - this.gry1)
314 .call(this.lineatt.func)
315 .call(this.fillatt.func);
316
317 addMoveHandler(this);
318 }
319
320 moveStart(x,y) {
321 this.swapx = this.swapy = false;
322 this.dx1 = Math.abs(x-this.grx1) < 5;
323 this.dx2 = Math.abs(x-this.grx2) < 5;
324 this.dy1 = Math.abs(y-this.gry1) < 5;
325 this.dy2 = Math.abs(y-this.gry2) < 5;
326 if (!this.dx1 && !this.dx2 && !this.dy1 && !this.dy2)
327 this.dx1 = this.dx2 = this.dy1 = this.dy2 = true;
328 if (!this.candy) this.dy1 = this.dy2 = false;
329 }
330
331 moveDrag(dx,dy) {
332 if (this.dx1) this.grx1 += dx;
333 if (this.dx2) this.grx2 += dx;
334 if (this.grx1 > this.grx2) {
335 this.swapx = true;
336 let tempx = this.grx1; this.grx1 = this.grx2; this.grx2 = tempx;
337 tempx = this.dx1; this.dx1 = this.dx2; this.dx2 = tempx;
338 }
339 if (this.dy1) this.gry1 += dy;
340 if (this.dy2) this.gry2 += dy;
341 if (this.gry1 > this.gry2) {
342 this.swapy = true;
343 let tempy = this.gry1; this.gry1 = this.gry2; this.gry2 = tempy;
344 tempy = this.dy1; this.dy1 = this.dy2; this.dy2 = tempy;
345 }
346 this.draw_g.select('rect').attr("x",this.grx1).attr("y", this.gry1)
347 .attr("width", this.grx2 - this.grx1).attr("height", this.gry2 - this.gry1);
348 }
349
350 moveEnd() {
351 let cond = this.getObject(), exec = "";
352 if (this.dx1 || this.swapx) { cond.fLow1 = this.svgToAxis("x", this.grx1); exec += "SetXLow(" + cond.fLow1 + ");;"; }
353 if (this.dx2 || this.swapx) { cond.fUp1 = this.svgToAxis("x", this.grx2); exec += "SetXUp(" + cond.fUp1 + ");;"; }
354 if (this.dy2 || this.swapy) { cond.fLow2 = this.svgToAxis("y", this.gry2); exec += "SetYLow(" + cond.fLow2 + ");;"; }
355 if (this.dy1 || this.swapy) { cond.fUp2 = this.svgToAxis("y", this.gry1); exec += "SetYUp(" + cond.fUp2 + ");;"; }
356 if (exec) {
357 this.submitCanvExec(exec + "SetChanged()");
358 this.drawLabel();
359 }
360 }
361
362 drawLabel() {
363
364 let cond = this.getObject(), painter = this, stat = {};
365
366 if (!cond.fbLabelDraw || !cond.fbVisible)
367 return Promise.resolve(this);
368
369 let pave_painter = findPainter(this, this.pave);
370
371 if (!pave_painter) {
372 this.pave = create('TPaveStats');
373 this.pave.fName = 'stats_' + cond.fName;
374 Object.assign(this.pave, { fX1NDC: 0.1, fY1NDC: 0.4, fX2NDC: 0.4, fY2NDC: 0.65, fBorderSize: 1, fFillColor: 0, fFillStyle: 1001 });
375
376 const st = gStyle;
377 Object.assign(this.pave, { fFillColor: st.fStatColor, fFillStyle: st.fStatStyle, fTextAngle: 0, fTextSize: st.fStatFontSize,
378 fTextAlign: 12, fTextColor: st.fStatTextColor, fTextFont: st.fStatFont});
379 } else {
380 this.pave.Clear();
381 }
382
383 this.pave.AddText(cond.fName);
384
385 this.pave.AddText("Counts = " + cond.fiCounts);
386
387 if (cond.fbLimitsDraw)
388 if (this.isPolyCond()) {
389 let res = { xmin: 0, xmax: 0, ymin: 0, ymax: 0 };
390 if (cond.fxCut.fNpoints > 0) {
391 res.xmin = res.xmax = cond.fxCut.fX[0];
392 res.ymin = res.ymax = cond.fxCut.fY[0];
393 for (let i=1; i<cond.fxCut.fNpoints; i++) {
394 res.xmin = Math.min(res.xmin, cond.fxCut.fX[i]);
395 res.xmax = Math.max(res.xmax, cond.fxCut.fX[i]);
396 res.ymin = Math.min(res.ymin, cond.fxCut.fY[i]);
397 res.ymax = Math.max(res.ymax, cond.fxCut.fY[i]);
398 }
399 }
400 this.pave.AddText("Xmin = " + res.xmin);
401 this.pave.AddText("Xmax = " + res.xmax);
402 this.pave.AddText("Ymin = " + res.ymin);
403 this.pave.AddText("Ymax = " + res.ymax);
404 } else {
405 this.pave.AddText("Xmin = " + cond.fLow1);
406 this.pave.AddText("Xmax = " + cond.fUp1);
407 if (cond.fiDim==2) {
408 this.pave.AddText("Ymin = " + cond.fLow2);
409 this.pave.AddText("Ymax = " + cond.fUp2);
410 }
411 }
412
413 stat = this.getMainPainter().countStat((x,y) => painter.Test(x,y));
414
415 if (cond.fbIntDraw) this.pave.AddText("Integral = " + floatToString(stat.integral, "14.7g"));
416
417 if (cond.fbXMeanDraw) this.pave.AddText("Mean x = " + floatToString(stat.meanx, "6.4g"));
418
419 if (cond.fbXRMSDraw) this.pave.AddText("RMS x = " + floatToString(stat.rmsx, "6.4g"));
420
421 if (cond.fiDim==2) {
422 if (cond.fbYMeanDraw) this.pave.AddText("Mean y = " + floatToString(stat.meany, "6.4g"));
423 if (cond.fbYRMSDraw) this.pave.AddText("RMS y = " + floatToString(stat.rmsy, "6.4g"));
424 }
425
426 if (cond.fbXMaxDraw) this.pave.AddText("X max = " + floatToString(stat.xmax, "6.4g"));
427
428 if (cond.fiDim==2) {
429 if (cond.fbYMaxDraw) this.pave.AddText("Y max = " + floatToString(stat.ymax, "6.4g"));
430 }
431
432 if (cond.fbCMaxDraw) this.pave.AddText("C max = " + floatToString(stat.wmax, "14.7g"));
433
434 let pr = pave_painter ? pave_painter.redraw() : draw(this.getDrawDom(), this.pave, "nofillstats");
435 return pr.then(() => this); // ensure that condition painter is returned
436 }
437
438// fillContextMenu(menu) {
439// let cond = this.getObject();
440// menu.add("header:"+ cond._typename + "::" + cond.fName);
441// function select(name,exec) {
442// let cond = this.getObject();
443// cond[name] = !cond[name];
444// this.submitCanvExec(exec + (cond[name] ? '(true)' : '(false)'));
445// this.redraw();
446// }
447// menu.addchk(cond.fbLabelDraw, 'Label', select.bind(this, 'fbLabelDraw', 'SetLabelDraw'));
448// menu.addchk(cond.fbLimitsDraw, 'Limits', select.bind(this, 'fbLimitsDraw', 'SetLimitsDraw'));
449// menu.addchk(cond.fbIntDraw, 'Integral', select.bind(this, 'fbIntDraw', 'SetIntDraw'));
450// menu.addchk(cond.fbXMeanDraw, 'X mean', select.bind(this, 'fbXMeanDraw', 'SetXMeanDraw'));
451// menu.addchk(cond.fbXRMSDraw, 'X rms', select.bind(this, 'fbXRMSDraw', 'SetXRMSDraw'));
452// menu.addchk(cond.fbXMaxDraw, 'X max', select.bind(this, 'fbXMaxDraw', 'SetXMaxDraw'));
453// menu.addchk(cond.fbYMeanDraw, 'Y mean', select.bind(this, 'fbYMeanDraw', 'SetYMeanDraw'));
454// menu.addchk(cond.fbYRMSDraw, 'Y rms', select.bind(this, 'fbYRMSDraw', 'SetYRMSDraw'));
455// menu.addchk(cond.fbYMaxDraw, 'Y max', select.bind(this, 'fbYMaxDraw', 'SetYMaxDraw'));
456// return true;
457// }
458
459 processTooltipEvent(pnt) {
460 if (!pnt) return null;
461
462 let cond = this.getObject(),
463 hint = { name: cond.fName, title: cond.fTitle, painter: this, menu: true, x: pnt.x, y: pnt.y };
464
465 if (!this.isPolyCond()) {
466
467 hint.color1 = this.fillatt.color;
468 hint.color2 = this.lineatt.color;
469
470 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));
471 hint.exact = (this.grx1 <= pnt.x) && (pnt.x <= this.grx2) && (this.gry1 <= pnt.y) && (pnt.y <= this.gry2);
472 if (Math.abs(this.grx1 - pnt.x) < 5) hint.sidex = -1;
473 if (Math.abs(this.grx2 - pnt.x) < 5) hint.sidex = 1;
474 if (cond.fiDim == 2) {
475 if (Math.abs(this.gry1 - pnt.y) < 5) hint.sidey = 1;
476 if (Math.abs(this.gry2 - pnt.y) < 5) hint.sidey = -1;
477 }
478 }
479
480 if (hint.exact)
481 hint.lines = ["condition", cond.fName ];
482
483 return hint;
484 }
485
486 redrawObject(obj) {
487 if (!this.updateObject(obj)) return false;
488 this.redraw(); // no need to redraw complete pad
489 return true;
490 }
491
492 cleanup(arg) {
493 if (this.pave) {
494 let pp = findPainter(this, this.pave);
495 if (pp) {
496 pp.removeFromPadPrimitives();
497 pp.cleanup();
498 }
499 delete this.pave;
500 }
501 super.cleanup(arg);
502 }
503
504 redraw() {
505 this.drawCondition();
506 return this.drawLabel();
507 }
508
509 static async draw(dom, cond, option) {
510
511 if (!option) option = "";
512
513 let condpainter = new ConditionPainter(dom, cond),
514 elem = condpainter.selectDom(),
515 main = condpainter.getMainPainter();
516
517 if (GO4.web_canvas || (option.indexOf('same') >= 0) || main) {
518 // if no hist painter, do nothing, just return dummy
519 if (!main)
520 return condpainter;
521 condpainter.drawCondition();
522 condpainter.addToPadPrimitives();
523 return condpainter.drawLabel();
524 }
525
526 // from here normal code for plain THttpServer
527 if ((option=='editor') || !cond.fxHistoName) {
528
529 const rect = elem.node().getBoundingClientRect();
530 if ((rect.height < 10) && (rect.width > 10))
531 elem.style("height", Math.round(rect.width*0.4) + "px");
532 const editor = new ConditionEditor(dom, cond);
533 return editor.drawEditor();
534 }
535
536 const hpainter = getHPainter();
537 if (!hpainter) return;
538
539 let histofullpath = null;
540
541 hpainter.forEachItem(h => {
542 if ((h._name == cond.fxHistoName) && h._kind && (h._kind.indexOf("ROOT.TH") == 0))
543 histofullpath = hpainter.itemFullName(h);
544 });
545
546 if (histofullpath === null) {
547 histofullpath = "../../Histograms/" + cond.fxHistoName;
548
549 let hitem = hpainter.findItem({ name: histofullpath, force: true });
550
551 hitem._kind = "ROOT.TH1I";
552 }
553
554 return hpainter.display(histofullpath, '', condpainter.getDrawDom()).then(hist_painter => {
555 if (!hist_painter)
556 return console.log('fail to draw histogram ' + histofullpath);
557 condpainter.drawCondition();
558 condpainter.addToPadPrimitives();
559 return condpainter.drawLabel();
560 });
561 }
562
563} // class ConditionPainter
564
565function drawCondArray(dom, obj, option) {
566 const arr = obj.condarr.arr,
567 num = obj.fiNumCond,
568 main = getElementMainPainter(dom);
569 let first = null;
570
571 function drawNext(i) {
572 if (i >= num)
573 return first;
574
575 const cond = arr[i];
576
577 if ((i === 0) && !cond.fxHistoName && !main) {
578 cond.fxHistoName = obj.fxHistoName;
579 if (!cond.fxHistoName)
580 return null;
581 }
582
583 return ConditionPainter.draw(dom, cond, ((i > 0) || main) ? 'same' : '').then(p => {
584 if (!first)
585 first = p;
586 return drawNext(i + 1);
587 });
588 }
589
590 return drawNext(0);
591}
592
593addDrawFunc({ name: 'TGo4Marker', func: MarkerPainter.draw });
594addDrawFunc({ name: 'TGo4WinCond', func: ConditionPainter.draw });
595addDrawFunc({ name: 'TGo4PolyCond', func: ConditionPainter.draw });
596addDrawFunc({ name: 'TGo4ShapedCond', func: ConditionPainter.draw });
597addDrawFunc({ name: 'TGo4CondArray', func: drawCondArray });
598
599export { MarkerPainter, ConditionPainter, drawCondArray };