GSI Object Oriented Online Offline (Go4)  GO4-6.2.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
condition.js
Go to the documentation of this file.
1 // $Id: condition.js 3521 2022-01-27 09:57:00Z linev $
2 
3 JSROOT.define([], () => {
4 
5  "use strict";
6 
7  if (typeof GO4 != "object") {
8  let e1 = new Error("condition.js requires GO4 to be already loaded");
9  e1.source = "condition.js";
10  throw e1;
11  }
12 
13  // =========================================================================================
14 
15  class ConditionEditor extends JSROOT.BasePainter {
16 
17  constructor(dom, cond) {
18  super(dom);
19  this.cond = cond;
20  this.changes = ["dummy", "init"];
21  this.clearChanges();
22  }
23 
24  isPolyCond() {
25  return ((this.cond._typename == "TGo4PolyCond") || (this.cond._typename == "TGo4ShapedCond"));
26  }
27 
28  isEllipseCond() {
29  return (this.cond._typename == "TGo4ShapedCond");
30  }
31 
33  markChanged(key) {
34  // first avoid duplicate keys:
35  for (let index = 0; index < this.changes.length; index++) {
36  if (this.changes[index]== key) return;
37  }
38  this.changes.push(key);
39  console.log("Mark changed :%s", key);
40  this.selectDom().select(".buttonChangeLabel").style("display", null);// show warning sign
41  }
42 
44  clearChanges() {
45  for (let index = 0; index < this.changes.length ; index++) {
46  let removed = this.changes.pop();
47  console.log("Clear changes removed :%s", removed);
48  }
49  this.selectDom().select(".buttonChangeLabel").style("display", "none"); // hide warning sign
50  }
51 
53  evaluateChanges(optionstring) {
54  let dom = this.selectDom(),
55  len = this.changes.length;
56  for (let index = 0; index < len ; index++) {
57  //let cursor=changes.pop();
58  let key = this.changes[index];
59  //console.log("Evaluate change key:%s", key);
60 
61  // here mapping of key to editor field:
62  if(key=="limits")
63  {
64  let xmin = dom.select(".cond_xmin").property("value"),
65  xmax = dom.select(".cond_xmax").property("value");
66  optionstring += `&xmin=${xmin}&xmax=${xmax}`;
67  this.cond.fLow1 = xmin;
68  this.cond.fUp1 = xmax;
69  if (this.cond.fiDim == 2) {
70  let ymin = dom.select(".cond_ymin").property("value"),
71  ymax = dom.select(".cond_ymax").property("value");
72  this.cond.fLow2 = ymin;
73  this.cond.fUp2 = ymax;
74  optionstring += `&ymin=${ymin}&ymax=${ymax}`;
75  }
76  } else if (key == "polygon") {
77  let npoints = dom.select(".cut_points").property("value");
78  optionstring += "&npolygon="+npoints;
79  let values = dom.selectAll(".cut_values input").nodes();
80  // TODO: set display of polygon points
81  if (values.length != npoints*2)
82  return console.error('mismatch', values.length, npoints*2);
83 
84  // always copy last point!
85  values[npoints*2-2].value = values[0].value;
86  values[npoints*2-1].value = values[1].value;
87 
88  for(let i = 0; i < npoints; ++i) {
89  let x = values[i*2].value,
90  y = values[i*2+1].value;
91  optionstring += `&x${i}=${x}&y${i}=${y}`;
92  }
93  } else if (key == "ellinpts") {
94  let val = dom.select(".cond_ellipse_points").property("value");
95  optionstring += `&${key}=${val}`;
96  } else if (key == "ellicx") {
97  let val = dom.select(".cond_ellipse_cx").property("value");
98  optionstring += `&${key}=${val}`;
99  } else if (key == "ellicy") {
100  let val = dom.select(".cond_ellipse_cy").property("value");
101  optionstring += `&${key}=${val}`;
102  } else if (key == "ellia1") {
103  let val = dom.select(".cond_ellipse_a1").property("value");
104  optionstring += `&${key}=${val}`;
105  } else if (key == "ellia2") {
106  let val = dom.select(".cond_ellipse_a2").property("value");
107  optionstring += `&${key}=${val}`;
108  } else if (key == "ellishape") {
109  let val = dom.select(".cond_ellipse_iscircle").property("value");
110  optionstring += `&${key}=${val}`;
111  } else if (key == "ellith") {
112  let val = dom.select(".cond_ellipse_theta").property("value");
113  optionstring += `&${key}=${val}`;
114  } else if (key == "resultmode") {
115  let val = dom.select(".cond_execmode").property("value");
116  optionstring += `&${key}=${val}`;
117  } else if (key == "invertmode") {
118  let val = dom.select(".cond_invertmode").property("value");
119  optionstring += `&${key}=${val}`;
120  } else if (key == "visible") {
121  let arg = dom.select(".cond_visible").property("checked") ? 1 : 0;
122  optionstring += `&${key}=${arg}`;
123  } else if (key == "labeldraw") {
124  let arg = dom.select(".cond_label").property("checked") ? 1 : 0;
125  this.cond.fbLabelDraw = arg;
126  optionstring += `&${key}=${arg}`;
127  } else if (key == "limitsdraw"){
128  let arg = dom.select(".cond_limits").property("checked") ? 1 : 0;
129  this.cond.fbLimitsDraw = arg;
130  optionstring += `&${key}=${arg}`;
131  } else if (key == "intdraw") {
132  let arg = dom.select(".cond_integr").property("checked") ? 1 : 0;
133  this.cond.fbIntDraw = arg;
134  optionstring += `&${key}=${arg}`;
135  } else if (key == "xmeandraw") {
136  let arg = dom.select(".cond_xmean").property("checked") ? 1 : 0;
137  this.cond.fbXMeanDraw = arg;
138  optionstring += `&${key}=${arg}`;
139  } else if (key == "xrmsdraw") {
140  let arg = dom.select(".cond_xrms").property("checked") ? 1 : 0;
141  this.cond.fbXRMSDraw = arg;
142  optionstring += `&${key}=${arg}`;
143  } else if (key == "ymeandraw") {
144  let arg = dom.select(".cond_ymean").property("checked") ? 1 : 0;
145  this.cond.fbYMeanDraw = arg;
146  optionstring += `&${key}=${arg}`;
147  } else if (key == "yrmsdraw") {
148  let arg = dom.select(".cond_yrms").property("checked") ? 1 : 0;
149  this.cond.fbYRMSDraw = arg;
150  optionstring += `&${key}=${arg}`;
151  } else if (key == "xmaxdraw") {
152  let arg = dom.select(".cond_maxx").property("checked") ? 1 : 0;
153  this.cond.fbXMaxDraw = arg;
154  optionstring += `&${key}=${arg}`;
155  } else if (key=="ymaxdraw") {
156  let arg = dom.select(".cond_maxy").property("checked") ? 1 : 0;
157  this.cond.fbYMaxDraw = arg;
158  optionstring += `&${key}=${arg}`;
159  } else if (key=="cmaxdraw") {
160  let arg = dom.select(".cond_max").property("checked") ? 1 : 0;
161  this.cond.fbCMaxDraw = arg;
162  optionstring += `&${key}=${arg}`;
163  } else {
164  console.log(`Warning: evaluateChanges found unknown key: ${key}`);
165  }
166  }
167  console.log(`Resulting option string: ${optionstring}`);
168  return optionstring;
169  }
170 
171  checkResize() {}
172 
177  changePolygonDimension() {
178  if(!this.isPolyCond()) return;
179 
180  let dom = this.selectDom(),
181  oldpoints = this.cond.fxCut.fNpoints,
182  npoints = dom.select(".cut_points").property("value");
183 
184  //if(npoints==oldpoints) return; // no dimension change, do nothing - disabled, error if we again go back to original condition dimension
185  if (this.cond.fxCut) {
186  let body = dom.select(".cut_values tbody").html(""); // clear old contents
187  if (npoints > oldpoints) {
188  // insert last but one point into table:
189  // first points are unchanged:
190  for (let i = 0; i < oldpoints - 1; i++) {
191  let x = this.cond.fxCut.fX[i], y = this.cond.fxCut.fY[i];
192  body.append("tr").html(`<td><input type="text" value="${x}"/></td><td><input type="text" value="${y}"/></td>`);
193  }
194  // inserted points will reproduce values of last but one point:
195  let insx = this.cond.fxCut.fX[oldpoints - 2],
196  insy = this.cond.fxCut.fY[oldpoints - 2];
197  for (let i = oldpoints - 1; i < npoints - 1; i++)
198  body.append("tr").html(`<td><input type="text" value="${insx}"/></td><td><input type="text" value="${insy}"/></td>`);
199  // final point is kept as last point of old polygon, should
200  // match first point for closed tcutg:
201  let lastx = this.cond.fxCut.fX[oldpoints - 1],
202  lasty = this.cond.fxCut.fY[oldpoints - 1];
203  body.append("tr").html(`<td><input type="text" value="${lastx}" disabled/></td><td><input type="text" value="${lasty}" disabled/></td>`);
204  } else {
205  // remove last but one point from table:
206  for (let i = 0; i < npoints - 1; i++) {
207  let x = this.cond.fxCut.fX[i], y = this.cond.fxCut.fY[i];
208  body.append("tr").html(`<td><input type="text" value="${x}"/></td><td><input type="text" value="${y}"/></td>`);
209  }
210  // final point is kept as last point of old polygon, should
211  // match first point for closed tcutg:
212  let lastx = this.cond.fxCut.fX[oldpoints - 1],
213  lasty = this.cond.fxCut.fY[oldpoints - 1];
214  body.append("tr").html(`<td><input type="text" value="${lastx}" disabled/></td><td><input type="text" value="${lasty}" disabled/> </td>`);
215  }
216  }
217  this.markChanged("polygon");
218  }
219 
221  changeTab(action, indx) {
222 
223  let dom = this.selectDom(),
224  btns = dom.select('.cond_tabs_header').selectAll("button").nodes(),
225  tabs = dom.selectAll('.tabs_body>div').nodes();
226 
227  if (btns.length != tabs.length)
228  return console.error('mismatch in tabs sizes', btns.length, tabs.length);
229 
230  d3.select(btns[indx]).attr('disabled', (action=="enable") ? null : "true");
231 
232  // if that tab selected, find any other suitable
233  if ((action == "disable") && d3.select(btns[indx]).classed("active_btn")) {
234  let best = -1;
235  btns.forEach((btn,k) => {
236  if (!d3.select(btn).attr('disabled') && (k != indx) && (best < 0)) best = k;
237  });
238 
239  if (best >= 0) {
240  d3.select(tabs[indx]).style("display", "none");
241  d3.select(tabs[best]).style("display", null);
242  d3.select(btns[indx]).classed("active_btn", false);
243  d3.select(btns[best]).classed("active_btn", true);
244  }
245  }
246  }
247 
249  refreshEditor() {
250  let editor = this,
251  cond = this.cond,
252  dom = this.selectDom();
253 
254  dom.select(".cond_name").text(cond.fName);
255  dom.select(".cond_type").text(cond._typename);
256 
257  dom.select(".cond_execmode").node().value = cond.fbEnabled ? 0 : (cond.fbResult ? 1 : 2);
258 
259  dom.select(".cond_invertmode").node().value = cond.fbTrue ? 0 : 1;
260 
261  dom.select(".cond_xmin").property("value", cond.fLow1).on("change", () => this.markChanged("limits"));
262  dom.select(".cond_xmax").property("value", cond.fUp1).on("change", () => this.markChanged("limits"));
263  if (cond.fiDim==2) {
264  dom.select(".cond_ymin").property("value", cond.fLow2).on("change", () => this.markChanged("limits"));
265  dom.select(".cond_ymax").property("value", cond.fUp2).on("change", () => this.markChanged("limits"));
266  } else {
267  dom.select(".cond_ymin").attr('disabled', true);
268  dom.select(".cond_ymax").attr('disabled', true);
269  }
270 
271  if(this.isPolyCond()) {
272  this.changeTab("enable", 1); // enable/disable by tab index
273  this.changeTab("disable", 0); // enable/disable by tab index
274  if (this.cond.fxCut) {
275  let numpoints = this.cond.fxCut.fNpoints;
276  dom.select(".cut_points").property("value", numpoints);
277  let body = dom.select(".cut_values tbody").html("").on("change", () => editor.markChanged("polygon"));
278 
279  for(let i = 0; i < numpoints; i++) {
280  let x = this.cond.fxCut.fX[i];
281  let y = this.cond.fxCut.fY[i];
282  let row = body.append("tr").html(`<td><input type="text" value="${x}"/></td><td><input type="text" value="${y}"/> </td>`);
283  if ((i == numpoints-1) && (numpoints > 1)) row.selectAll("input").property("disabled", true); // disable last row
284  }
285  }
286  if(this.isEllipseCond()) {
287  this.changeTab( "enable", 2 ); // enable/disable by tab index
288  let numpoints = this.cond.fiResolution;
289  dom.select(".cond_ellipse_points").property("value", numpoints);
290  dom.select(".cond_ellipse_cx").property("value", cond.fdCenterX).on("change", () => this.markChanged("ellicx"));
291  dom.select(".cond_ellipse_cy").property("value", cond.fdCenterY).on("change", () => this.markChanged("ellicy"));
292  dom.select(".cond_ellipse_a1").property("value", cond.fdRadius1).on("change", () => this.markChanged("ellia1"));
293  dom.select(".cond_ellipse_a2").property("value", cond.fdRadius2).property('disabled', cond.fbIsCircle).on("change", () => this.markChanged("ellia2"));
294  dom.select(".cond_ellipse_theta").property("value", cond.fdTheta).property('disabled', cond.fbIsCircle).on("change", () => {
295  this.markChanged("ellith");
296  dom.select(".cond_ellipse_theta_slider").property("value", dom.select(".cond_ellipse_theta").property("value") % 360);
297  });
298 
299  let options = dom.select(".cond_ellipse_iscircle").node().options;
300  for (let i = 0; i < options.length; i++)
301  options[i].selected = (options[i].value == cond.fiShapeType);
302 
303  dom.select(".cond_ellipse_theta_slider")
304  .property("value",cond.fdTheta)
305  .attr("disbaled", cond.fbIsCircle)
306  .on("change", () => {
307  this.markChanged("ellith");
308  dom.select(".cond_ellipse_theta").property("value", dom.select(".cond_ellipse_theta_slider").property("value"));
309  });
310  }
311 
312  } else {
313  this.changeTab( "enable", 0 );
314  this.changeTab( "disable", 1 ); // enable/disable by tab index
315  this.changeTab( "disable", 2 ); // enable/disable by tab index
316  }
317 
318  dom.select(".cond_counts").text(cond.fiCounts);
319  dom.select(".cond_true").text(cond.fiTrueCounts);
320  dom.select(".cond_percent").text((cond.fiCounts > 0 ? 100. * cond.fiTrueCounts / cond.fiCounts : 0.).toFixed(2) + "%");
321 
322  // todo: get keywords from condition class definition
323  // problem: static letiables are not streamed by default
324 
325  dom.select(".cond_visible")
326  .property('checked', cond.fbVisible)
327  .on("click", function() { cond.fbVisible = this.checked; editor.markChanged("visible")});
328 
329  dom.select(".cond_limits")
330  .property('checked', cond.fbLimitsDraw)
331  .on("click", function() { cond.fbLimitsDraw = this.checked; editor.markChanged("limitsdraw")});
332 
333  dom.select(".cond_label")
334  .property('checked', cond.fbLabelDraw)
335  .on("click", function() { cond.fbLabelDraw = this.checked; editor.markChanged("labeldraw")});
336 
337  dom.select(".cond_integr")
338  .property('checked', cond.fbIntDraw)
339  .on("click", function() { cond.fbIntDraw = this.checked; editor.markChanged("intdraw")});
340 
341  dom.select(".cond_maxx")
342  .property('checked', cond.fbXMaxDraw)
343  .on("click", function() { cond.fbXMaxDraw = this.checked; editor.markChanged("xmaxdraw")});
344 
345  dom.select(".cond_max")
346  .property('checked', cond.fbCMaxDraw)
347  .on("click", function() { cond.fbCMaxDraw = this.checked; editor.markChanged("cmaxdraw")});
348 
349  dom.select(".cond_maxy")
350  .property('checked', cond.fbYMaxDraw)
351  .on("click", function() { cond.fbYMaxDraw = this.checked; editor.markChanged("ymaxdraw")});
352 
353  dom.select(".cond_xmean")
354  .property('checked', cond.fbXMeanDraw)
355  .on("click", function() { cond.fbXMeanDraw = this.checked; editor.markChanged("xmeandraw")});
356 
357  dom.select(".cond_xrms")
358  .property('checked', cond.fbXRMSDraw)
359  .on("click", function() { cond.fbXRMSDraw = this.checked; editor.markChanged("xrmsdraw")});
360 
361  dom.select(".cond_ymean")
362  .property('checked', cond.fbYMeanDraw)
363  .on("click", function() { cond.fbYMeanDraw = this.checked; editor.markChanged("ymeandraw")});
364 
365  dom.select(".cond_yrms")
366  .property('checked', cond.fbYRMSDraw)
367  .on("click", function() { cond.fbYRMSDraw = this.checked; editor.markChanged("yrmsdraw")});
368 
369  editor.clearChanges();
370  }
371 
373  fillEditor() {
374  this.setTopPainter();
375 
376  let editor = this,
377  cond = this.cond,
378  dom = this.selectDom();
379 
380  // assign tabs buttons handlers
381  dom.select('.cond_tabs_header').selectAll("button").on("click", function() {
382  let btn = d3.select(this);
383 
384  dom.select('.cond_tabs_header').selectAll("button").each(function() {
385  d3.select(this).classed("active_btn", false);
386  });
387 
388  btn.classed("active_btn", true);
389 
390  dom.selectAll('.tabs_body>div').each(function() {
391  let tab = d3.select(this);
392  tab.style('display', tab.attr('id') == btn.attr("for") ? null : "none");
393  });
394  });
395 
396  // mark first tab as active
397  dom.select('.cond_tabs_header').select("button").classed("active_btn", true);
398 
399  dom.select(".cond_execmode").on("change", () => this.markChanged("resultmode"));
400 
401  dom.select(".cond_invertmode").on("change", () => this.markChanged("invertmode"));
402 
403  if(this.isEllipseCond()) {
404  dom.select(".cond_ellipse_iscircle").on("change", () => {
405  cond.fiShapeType = parseInt(dom.select(".cond_ellipse_iscircle").property("value"));
406  let flags;
407  switch(cond.fiShapeType) {
408  case 2: flags = [true, true, true, null]; break; // circle
409  case 3: flags = [null, null, null, null]; break; // ellipse
410  case 4: flags = [null, null, null, true]; break; // box
411  default: flags = [true, true, true, null]; // free style
412  }
413  dom.select(".cond_ellipse_a2").attr('disabled', flags[0]);
414  dom.select(".cond_ellipse_theta").attr('disabled', flags[1]);
415  dom.select(".cond_ellipse_theta_slider").attr('disabled', flags[2]);
416  dom.select(".cond_ellipse_points").attr('disabled', flags[3]);
417  this.markChanged("ellishape");
418  })
419  }
420 
421  dom.select(".buttonGetCondition")
422  .style('background-image', "url(" + GO4.source_dir + "icons/right.png)")
423  .on("click", () => {
424  if (JSROOT.hpainter) JSROOT.hpainter.display(this.getItemName());
425  else console.log("hierarhy painter object not found!");
426  });
427 
428  dom.select(".buttonSetCondition")
429  .style('background-image', "url(" + GO4.source_dir + "icons/left.png)")
430  .on("click", () => {
431  let options = this.evaluateChanges(""); // complete option string from all changed elements
432  console.log("set condition " + this.getItemName() + ", options="+options);
433  GO4.ExecuteMethod(this, "UpdateFromUrl",options)
434  .then(() => {
435  console.log("set condition done.");
436  this.clearChanges();
437  }).catch(err => {
438  console.log("set condition FAILED.", err);
439  });
440  });
441 
442  dom.select(".buttonChangeLabel")
443  .style('background-image', "url(" + GO4.source_dir + "icons/info1.png)");
444 
445  dom.select(".buttonDrawCondition")
446  .style('background-image', "url(" + GO4.source_dir + "icons/chart.png)")
447  .on("click", () => {
448  // TODO: implement correctly after MDI is improved, need to find out active frame and location of bound histogram
449 
450  if (JSROOT.hpainter) {
451  this.evaluateChanges("");
452 
453  if (JSROOT.hpainter.updateOnOtherFrames(this, this.cond)) return;
454 
455  JSROOT.hpainter.drawOnSuitableHistogram(this, this.cond, editor.cond.fiDim==2);
456 
457  return;
458  }
459 
460  let baseurl = editor.getItemName() + "/",
461  drawurl = baseurl + "draw.htm";
462 
463  console.log("draw condition to next window with url="+drawurl);
464  //window.open(drawurl);
465  window.open(drawurl,'_blank');
466 
467  });
468 
469  dom.select(".buttonClearCondition")
470  .style('background-image', "url(" + GO4.source_dir + "icons/clear.png)")
471  .on("click", () => {
472  GO4.ExecuteMethod(this, "UpdateFromUrl", "&resetcounters=1")
473  .then(() => {
474  console.log("reset condition counters done.");
475  if(JSROOT.hpainter) JSROOT.hpainter.display(editor.getItemName());
476  }).catch(err => {
477  console.log("reset condition counters FAILED.", err);
478  });
479  });
480 
481  dom.select(".cut_points").on("change", () => this.changePolygonDimension());
482 
483  dom.select(".cond_ellipse_points").on("change", () => this.markChanged("ellinpts"));
484 
485  this.refreshEditor();
486  }
487 
488  drawEditor() {
489  return JSROOT.httpRequest(GO4.source_dir + "html/condeditor.htm", "text").then(src => {
490  this.selectDom().html(src);
491  this.fillEditor();
492  return this;
493  });
494  }
495 
496  redrawObject(obj/*, opt */) {
497  if (obj._typename != this.cond._typename) return false;
498  this.cond = JSROOT.clone(obj); // does this also work with polygon condition?
499  this.refreshEditor();
500  return true;
501  }
502  }
503 
504  GO4.ConditionEditor = ConditionEditor;
505 
506 })