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