hist/TGraphTimePainter.mjs

import { internals } from '../core.mjs';
import { DrawOptions } from '../base/BasePainter.mjs';
import { ObjectPainter } from '../base/ObjectPainter.mjs';
import { TH1Painter } from '../hist2d/TH1Painter.mjs';
import { draw } from '../draw.mjs';


/**
 * @summary Painter for TGraphTime object
 *
 * @private
 */

class TGraphTimePainter extends ObjectPainter {

   /** @summary Redraw object */
   redraw() {
      if (this.step === undefined)
         this.startDrawing();
   }

   /** @summary Decode drawing options */
   decodeOptions(opt) {
      const d = new DrawOptions(opt || 'REPEAT');

      if (!this.options) this.options = {};

      Object.assign(this.options, {
          once: d.check('ONCE'),
          repeat: d.check('REPEAT'),
          first: d.check('FIRST')
      });

      this.storeDrawOpt(opt);
   }

   /** @summary Draw primitives */
   async drawPrimitives(indx) {
      if (!indx) {
         indx = 0;
         this._doing_primitives = true;
      }

      const lst = this.getObject()?.fSteps.arr[this.step];

      if (!lst || (indx >= lst.arr.length)) {
         delete this._doing_primitives;
         return;
      }

      return draw(this.getPadPainter(), lst.arr[indx], lst.opt[indx]).then(p => {
         if (p) {
            p.$grtimeid = this.selfid; // indicator that painter created by ourself
            p.$grstep = this.step; // remember step
         }
         return this.drawPrimitives(indx+1);
      });
   }

   /** @summary Continue drawing */
   continueDrawing() {
      if (!this.options) return;

      const gr = this.getObject();

      if (this.options.first) {
         // draw only single frame, cancel all others
         delete this.step;
         return;
      }

      if (this.wait_animation_frame) {
         delete this.wait_animation_frame;

         // clear pad
         const pp = this.getPadPainter();
         if (!pp) {
            // most probably, pad is cleared
            delete this.step;
            return;
         }

         // draw primitives again
         this.drawPrimitives().then(() => {
            // clear primitives produced by previous drawing to avoid flicking
            pp.cleanPrimitives(p => { return (p.$grtimeid === this.selfid) && (p.$grstep !== this.step); });

            this.continueDrawing();
         });
      } else if (this.running_timeout) {
         clearTimeout(this.running_timeout);
         delete this.running_timeout;

         this.wait_animation_frame = true;
         // use animation frame to disable update in inactive form
         requestAnimationFrame(() => this.continueDrawing());
      } else {
         let sleeptime = Math.max(gr.fSleepTime, 10);

         if (++this.step > gr.fSteps.arr.length) {
            if (this.options.repeat) {
               this.step = 0; // start again
               sleeptime = Math.max(5000, 5*sleeptime); // increase sleep time
            } else {
               delete this.step;    // clear indicator that animation running
               return;
            }
         }

         this.running_timeout = setTimeout(() => this.continueDrawing(), sleeptime);
      }
   }

   /** @summary Start drawing of graph time */
   startDrawing() {
      this.step = 0;

      return this.drawPrimitives().then(() => {
         this.continueDrawing();
         return this;
      });
   }

   /** @summary Draw TGraphTime object */
   static async draw(dom, gr, opt) {
      if (!gr.fFrame) {
        console.error('Frame histogram not exists');
        return null;
      }

      const painter = new TGraphTimePainter(dom, gr);

      if (painter.getMainPainter()) {
         console.error('Cannot draw graph time on top of other histograms');
         return null;
      }

      painter.decodeOptions(opt);

      if (!gr.fFrame.fTitle && gr.fTitle) {
         const arr = gr.fTitle.split(';');
         gr.fFrame.fTitle = arr[0];
         if (arr[1]) gr.fFrame.fXaxis.fTitle = arr[1];
         if (arr[2]) gr.fFrame.fYaxis.fTitle = arr[2];
      }

      painter.selfid = 'grtime_' + internals.id_counter++; // use to identify primitives which should be clean

      return TH1Painter.draw(dom, gr.fFrame, '').then(() => {
         painter.addToPadPrimitives();
         return painter.startDrawing();
      });
   }

} // class TGraphTimePainter


/** @summary Draw TRooPlot
  * @private */
async function drawRooPlot(dom, plot) {
   return draw(dom, plot._hist, 'hist').then(async hp => {
      const arr = [];
      for (let i = 0; i < plot._items.arr.length; ++i)
         arr.push(draw(dom, plot._items.arr[i], plot._items.opt[i]));
      return Promise.all(arr).then(() => hp);
   });
}

export { TGraphTimePainter, drawRooPlot };