1import { ObjectPainter, getHPainter, addDrawFunc, create, gStyle, draw, floatToString, getElementMainPainter } from 'jsroot';
 
    3import { addMoveHandler } from 'jsrootsys/modules/gui/utils.mjs';
 
    5import { GO4 } from './core.mjs';
 
    7import { ConditionEditor } from './condition.mjs';
 
   11function findPainter(painter, obj, name, typ) {
 
   12   let pp = painter.getPadPainter();
 
   13   return pp ? pp.findPainterFor(obj, name, typ) : null;
 
   16class MarkerPainter extends 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);
 
   58            rect = this.getFramePainter().getFrameRect(),
 
   59            main = this.getMainPainter(), hint = null;
 
   61      if (main && typeof main.processTooltipEvent == 'function')
 
   62         hint = main.processTooltipEvent({ enabled: false, x: this.grx - rect.x, y: this.gry - rect.y });
 
   64      lbls.push(marker.fxName + ((hint && hint.name) ? (" : " + hint.name) : ""));
 
   67            lbls.push("X = " + floatToString(marker.fX, "6.4g"));
 
   70         lbls.push("Y = " + floatToString(marker.fY, "6.4g"));
 
   72      if (hint && hint.user_info) {
 
   73         if (marker.fbXbinDraw) {
 
   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);
 
   80         if (marker.fbYbinDraw) {
 
   81            lbls.push("Ybin = " + ((hint.user_info.biny !== undefined) ? hint.user_info.biny : "<undef>"));
 
   84         if (marker.fbContDraw)
 
   85            lbls.push("Cont = " + hint.user_info.cont);
 
   93      let marker = this.getObject();
 
   95      if (!marker.fbHasLabel)
 
   96         return Promise.resolve(this);
 
   98      let pave_painter = findPainter(this, this.pave);
 
  101         this.pave = create("TPaveStats");
 
  102         this.pave.fName = "stats_" + marker.fName;
 
  104         let pp = this.getPadPainter(),
 
  105               pad_width = pp.getPadWidth(),
 
  106               pad_height = pp.getPadHeight();
 
  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 });
 
  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 });
 
  119      let lbls = this.fillLabels(marker);
 
  120      for (let k = 0; k < lbls.length; ++k)
 
  121         this.pave.AddText(lbls[k]);
 
  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);
 
  129      if (!this.updateObject(obj)) return false;
 
  130      this.redraw(); // no need to redraw complete pad
 
  136         let pp = findPainter(this, this.pave);
 
  138            pp.removeFromPadPrimitives();
 
  149      return this.drawLabel();
 
  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)'));
 
  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'));
 
  171   processTooltipEvent(pnt) {
 
  172      if (!pnt) return null;
 
  174      let marker = this.getObject(),
 
  175            rect = this.getFramePainter().getFrameRect(),
 
  178            marker_sz = this.markeratt.getFullSize();
 
  180      let hint = { name: marker.fxName,
 
  181                     title: marker.fxName,
 
  186                     color1: this.markeratt.color };
 
  188      let dist = Math.sqrt(Math.pow(pnt.x - hint.x, 2) + Math.pow(pnt.y - hint.y, 2));
 
  190      hint.menu_dist = dist;
 
  192      if (dist < 2.5 * marker_sz) hint.exact = true;
 
  195         hint.lines = this.fillLabels(marker);
 
  197      // res.menu = res.exact; // activate menu only when exactly locate bin
 
  198      // res.menu_dist = 3; // distance always fixed
 
  205   static draw(dom, obj, /*, option */) {
 
  206      const painter = new MarkerPainter(dom, obj);
 
  207      painter.drawMarker();
 
  208      painter.addToPadPrimitives();
 
  210      return painter.drawLabel();
 
  213} // class MarkerPainter
 
  216// =========================================================================
 
  218class ConditionPainter extends ObjectPainter {
 
  219   constructor(dom, cond) {
 
  221      this.pave = null; // drawing of stat
 
  225      //  JAM: need to put this here, since condition object will lose internal definition after cloning it again!
 
  226      let cond = this.getObject();
 
  228         return cond.fbResult;
 
  231         return cond.fxCut.IsInside(x,y) ? cond.fbTrue : cond.fbFalse;
 
  233      if ((x < cond.fLow1) || (x > cond.fUp1)) return cond.fbFalse;
 
  235      if ((cond.fiDim==2) && ((y < cond.fLow2) || (y > cond.fUp2))) return cond.fbFalse;
 
  242      return this.matchObjectType("TGo4PolyCond") || this.matchObjectType("TGo4ShapedCond");
 
  246      return this.matchObjectType("TGo4ShapedCond");
 
  250      if (!p || !this.snapid || p._oldexec) return;
 
  251      p.snapid = this.snapid + "#member_fxCut";
 
  252      p._condpainter = this;
 
  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()");
 
  262   drawCondition(interactive) {
 
  264      let cond = this.getObject();
 
  266      if (!cond || !cond.fbVisible) return;
 
  268      if (this.isPolyCond()) {
 
  270            // look here if cut is already drawn
 
  271            let cutpaint = findPainter(this, null, cond.fName, 'TCutG');
 
  274               if (cutpaint.updateObject(cond.fxCut)) cutpaint.redraw();
 
  275               this.afterCutDraw(cutpaint);
 
  277               cond.fxCut.fFillStyle = 3006;
 
  278               cond.fxCut.fFillColor = 2;
 
  279               draw(this.getDrawDom(), cond.fxCut, "LF").then(p => this.afterCutDraw(p));
 
  286      let g = this.createG(true); // drawing performed inside frame
 
  288      if ((cond.fFillStyle==1001) && (cond.fFillColor==19)) {
 
  289         cond.fFillStyle = 3006;
 
  293      this.createAttFill({attr: cond});
 
  294      this.createAttLine({attr: cond});
 
  296      this.grx1 = this.axisToSvg("x", cond.fLow1);
 
  297      this.grx2 = this.axisToSvg("x", cond.fUp1);
 
  299      if (cond.fiDim == 2) {
 
  300         this.gry1 = this.axisToSvg("y", cond.fUp2);
 
  301         this.gry2 = this.axisToSvg("y", cond.fLow2);
 
  305         this.gry2 = this.getFramePainter().getFrameHeight();
 
  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);
 
  317      addMoveHandler(this);
 
  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;
 
  332      if (this.dx1) this.grx1 += dx;
 
  333      if (this.dx2) this.grx2 += dx;
 
  334      if (this.grx1 > this.grx2) {
 
  336         let tempx = this.grx1; this.grx1 = this.grx2; this.grx2 = tempx;
 
  337         tempx = this.dx1; this.dx1 = this.dx2; this.dx2 = tempx;
 
  339      if (this.dy1) this.gry1 += dy;
 
  340      if (this.dy2) this.gry2 += dy;
 
  341      if (this.gry1 > this.gry2) {
 
  343         let tempy = this.gry1; this.gry1 = this.gry2; this.gry2 = tempy;
 
  344         tempy = this.dy1; this.dy1 = this.dy2; this.dy2 = tempy;
 
  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);
 
  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 + ");;"; }
 
  357         this.submitCanvExec(exec + "SetChanged()");
 
  364      let cond = this.getObject(), painter = this, stat = {};
 
  366      if (!cond.fbLabelDraw || !cond.fbVisible)
 
  367         return Promise.resolve(this);
 
  369      let pave_painter = findPainter(this, this.pave);
 
  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 });
 
  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});
 
  383      this.pave.AddText(cond.fName);
 
  385      this.pave.AddText("Counts = " + cond.fiCounts);
 
  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]);
 
  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);
 
  405            this.pave.AddText("Xmin = " + cond.fLow1);
 
  406            this.pave.AddText("Xmax = " + cond.fUp1);
 
  408               this.pave.AddText("Ymin = " + cond.fLow2);
 
  409               this.pave.AddText("Ymax = " + cond.fUp2);
 
  413      stat = this.getMainPainter().countStat((x,y) => painter.Test(x,y));
 
  415      if (cond.fbIntDraw) this.pave.AddText("Integral = " + floatToString(stat.integral, "14.7g"));
 
  417      if (cond.fbXMeanDraw) this.pave.AddText("Mean x = " + floatToString(stat.meanx, "6.4g"));
 
  419      if (cond.fbXRMSDraw) this.pave.AddText("RMS x = " + floatToString(stat.rmsx, "6.4g"));
 
  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"));
 
  426      if (cond.fbXMaxDraw) this.pave.AddText("X max = " + floatToString(stat.xmax, "6.4g"));
 
  429         if (cond.fbYMaxDraw) this.pave.AddText("Y max = " + floatToString(stat.ymax, "6.4g"));
 
  432      if (cond.fbCMaxDraw) this.pave.AddText("C max = " + floatToString(stat.wmax, "14.7g"));
 
  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
 
  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)'));
 
  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'));
 
  459   processTooltipEvent(pnt) {
 
  460      if (!pnt) return null;
 
  462      let cond = this.getObject(),
 
  463            hint = { name: cond.fName, title: cond.fTitle, painter: this, menu: true, x: pnt.x, y: pnt.y };
 
  465      if (!this.isPolyCond()) {
 
  467         hint.color1 = this.fillatt.color;
 
  468         hint.color2 = this.lineatt.color;
 
  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;
 
  481         hint.lines = ["condition", cond.fName ];
 
  487      if (!this.updateObject(obj)) return false;
 
  488      this.redraw(); // no need to redraw complete pad
 
  494         let pp = findPainter(this, this.pave);
 
  496            pp.removeFromPadPrimitives();
 
  505      this.drawCondition();
 
  506      return this.drawLabel();
 
  509   static async draw(dom, cond, option) {
 
  511      if (!option) option = "";
 
  513      let condpainter = new ConditionPainter(dom, cond),
 
  514            elem = condpainter.selectDom(),
 
  515            main = condpainter.getMainPainter();
 
  517      if (GO4.web_canvas || (option.indexOf('same') >= 0) || main) {
 
  518         // if no hist painter, do nothing, just return dummy
 
  521         condpainter.drawCondition();
 
  522         condpainter.addToPadPrimitives();
 
  523         return condpainter.drawLabel();
 
  526      // from here normal code for plain THttpServer
 
  527      if ((option=='editor') || !cond.fxHistoName) {
 
  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();
 
  536      const hpainter = getHPainter();
 
  537      if (!hpainter) return;
 
  539      let histofullpath = null;
 
  541      hpainter.forEachItem(h => {
 
  542         if ((h._name == cond.fxHistoName) && h._kind && (h._kind.indexOf("ROOT.TH") == 0))
 
  543            histofullpath = hpainter.itemFullName(h);
 
  546      if (histofullpath === null) {
 
  547         histofullpath = "../../Histograms/" + cond.fxHistoName;
 
  549         let hitem = hpainter.findItem({ name: histofullpath, force: true });
 
  551         hitem._kind = "ROOT.TH1I";
 
  554      return hpainter.display(histofullpath, '', condpainter.getDrawDom()).then(hist_painter => {
 
  556            return console.log('fail to draw histogram ' + histofullpath);
 
  557         condpainter.drawCondition();
 
  558         condpainter.addToPadPrimitives();
 
  559         return condpainter.drawLabel();
 
  563} // class ConditionPainter
 
  565function drawCondArray(dom, obj, option) {
 
  566   const arr = obj.condarr.arr,
 
  568         main = getElementMainPainter(dom);
 
  571   function drawNext(i) {
 
  577      if ((i === 0) && !cond.fxHistoName && !main) {
 
  578         cond.fxHistoName = obj.fxHistoName;
 
  579         if (!cond.fxHistoName)
 
  583      return ConditionPainter.draw(dom, cond, ((i > 0) || main) ? 'same' : '').then(p => {
 
  586         return drawNext(i + 1);
 
  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 });
 
  599export { MarkerPainter, ConditionPainter, drawCondArray };