text: /*!
   * The MIT License (MIT)
   *
   * Copyright (c) 2016 Mykhailo Stadnyk <mikhus@gmail.com>
   *
   * Permission is hereby granted, free of charge, to any person obtaining a copy
   * of this software and associated documentation files (the "Software"), to deal
   * in the Software without restriction, including without limitation the rights
   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   * copies of the Software, and to permit persons to whom the Software is
   * furnished to do so, subject to the following conditions:
   *
   * The above copyright notice and this permission notice shall be included in
   * all copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
   * SOFTWARE.
   */
  require('./polyfill');
  
  const GenericOptions = require('./GenericOptions');
  const BaseGauge = require('./BaseGauge');
  const SmartCanvas = require('./SmartCanvas');
  const drawings = require('./drawings');
  
  const PI = Math.PI;
  const HPI = PI / 2;
  
  /**
   * Gauge configuration options
   *
   * @typedef {GenericOptions|{exactTicks: boolean, ticksAngle: number, startAngle: number, colorNeedleCircleOuter: string, colorNeedleCircleOuterEnd: string, colorNeedleCircleInner: string, colorNeedleCircleInnerEnd: string, needleCircleSize: number, needleCircleInner: boolean, needleCircleOuter: boolean, animationTarget: string, useMinPath: boolean}} RadialGaugeOptions
   */
  
  /**
   * Default gauge configuration options
   *
   * @access private
   * @type {RadialGaugeOptions}
   */
  const defaultRadialGaugeOptions = Object.assign({}, GenericOptions, {
      // basic options
      ticksAngle: 270,
      startAngle: 45,
  
      // colors
      colorNeedleCircleOuter: '#f0f0f0',
      colorNeedleCircleOuterEnd: '#ccc',
      colorNeedleCircleInner: '#e8e8e8',
      colorNeedleCircleInnerEnd: '#f5f5f5',
  
      // needle
      needleCircleSize: 10,
      needleCircleInner: true,
      needleCircleOuter: true,
      needleStart: 20,
  
      // custom animations
      animationTarget: 'needle', // 'needle' or 'plate'
      useMinPath: false,
  
      barWidth: 0
  });
  
  /* istanbul ignore next: private, not testable */
  /**
   * Draws gradient-filled circle on a canvas
   *
   * @access private
   * @param {number} radius
   * @param {number} width
   * @param {Canvas2DContext} context
   * @param {string} start gradient start color
   * @param {string} end gradient end color
   */
  function drawRadialBorder(radius, width, context, start, end) {
      context.beginPath();
      //noinspection JSUnresolvedFunction
      context.arc(0, 0, abs(radius), 0, PI * 2, true);
      context.lineWidth = width;
      context.strokeStyle = end ?
          drawings.linearGradient(context, start, end, radius) :
          start;
      context.stroke();
      context.closePath();
  }
  
  /* istanbul ignore next: private, not testable */
  /**
   * Returns max radius without borders for the gauge
   *
   * @param {Canvas2DContext} context
   * @param {RadialGaugeOptions} options
   * @return {number}
   */
  function maxRadialRadius(context, options) {
      let pxRatio = SmartCanvas.pixelRatio;
  
      if (!context.maxRadius) {
          context.maxRadius = context.max
              - options.borderShadowWidth
              - options.borderOuterWidth * pxRatio
              - options.borderMiddleWidth * pxRatio
              - options.borderInnerWidth * pxRatio
              + (options.borderOuterWidth ? 0.5 : 0)
              + (options.borderMiddleWidth ? 0.5 : 0)
              + (options.borderInnerWidth ? 0.5 : 0);
      }
  
      return context.maxRadius;
  }
  
  /* istanbul ignore next: private, not testable */
  /**
   * Draws gauge plate on the canvas
   *
   * @access private
   * @param {Canvas2DContext} context
   * @param {RadialGaugeOptions} options
   */
  function drawRadialPlate(context, options) {
      let pxRatio = SmartCanvas.pixelRatio;
      let d0 = options.borderShadowWidth * pxRatio;
      let r0 = context.max - d0 - (options.borderOuterWidth * pxRatio) / 2;
      let r1 = r0 - (options.borderOuterWidth * pxRatio) / 2 -
          (options.borderMiddleWidth * pxRatio) / 2 + 0.5;
      let r2 = r1 - (options.borderMiddleWidth * pxRatio) / 2 -
          (options.borderInnerWidth * pxRatio) / 2 + 0.5;
      let r3 = maxRadialRadius(context, options);
      let grad;
      let shadowDrawn = false;
  
      context.save();
  
      if (options.borderOuterWidth) {
          shadowDrawn = drawings.drawShadow(context, options, shadowDrawn);
          drawRadialBorder(r0,
              options.borderOuterWidth * pxRatio,
              context,
              options.colorBorderOuter,
              options.colorBorderOuterEnd);
      }
  
      if (options.borderMiddleWidth) {
          shadowDrawn = drawings.drawShadow(context, options, shadowDrawn);
          drawRadialBorder(r1,
              options.borderMiddleWidth * pxRatio,
              context,
              options.colorBorderMiddle,
              options.colorBorderMiddleEnd);
      }
  
      if (options.borderInnerWidth) {
          shadowDrawn = drawings.drawShadow(context, options, shadowDrawn);
          drawRadialBorder(r2,
              options.borderInnerWidth * pxRatio,
              context,
              options.colorBorderInner,
              options.colorBorderInnerEnd);
      }
  
      drawings.drawShadow(context, options, shadowDrawn);
  
      context.beginPath();
      //noinspection JSUnresolvedFunction
      context.arc(0, 0, abs(r3), 0, PI * 2, true);
  
      if (options.colorPlateEnd) {
          grad = context.createRadialGradient(0, 0, r3 / 2, 0, 0, r3);
          grad.addColorStop(0, options.colorPlate);
          grad.addColorStop(1, options.colorPlateEnd);
      }
  
      else  {
          grad = options.colorPlate;
      }
  
      context.fillStyle = grad;
  
      context.fill();
      context.closePath();
  
      context.restore();
  }
  
  /* istanbul ignore next: private, not testable */
  /**
   * Draws gauge highlight areas on a canvas
   *
   * @access private
   * @param {Canvas2DContext} context
   * @param {RadialGaugeOptions} options
   */
  function drawRadialHighlights(context, options) {
      let hlWidth = context.max *
          (parseFloat(options.highlightsWidth) || 0) / 100;
  
      if (!hlWidth) return;
  
      //noinspection JSUnresolvedFunction
      let r = abs(radialTicksRadius(context, options) - hlWidth / 2);
      let i = 0, s = options.highlights.length;
      let vd = (options.maxValue - options.minValue) / options.ticksAngle;
  
      context.save();
  
      for (; i < s; i++) {
          let hlt = options.highlights[i];
  
          context.beginPath();
  
          context.rotate(HPI);
          context.arc(0, 0, r,
              drawings.radians(options.startAngle +
                  (hlt.from - options.minValue) / vd),
              drawings.radians(options.startAngle +
                  (hlt.to - options.minValue) / vd),
              false
          );
          context.strokeStyle = hlt.color;
          context.lineWidth = hlWidth;
          context.stroke();
          context.closePath();
  
          context.restore();
          context.save();
      }
  }
  
  /* istanbul ignore next: private, not testable */
  /**
   * Draws minor ticks bar on a canvas
   *
   * @access private
   * @param {Canvas2DContext} context
   * @param {RadialGaugeOptions} options
   */
  function drawRadialMinorTicks(context, options) {
      let radius = radialTicksRadius(context, options);
      let s, range, angle;
      let i = 0;
      let delta = 0;
      let minTicks = Math.abs(options.minorTicks) || 0;
      let ratio = options.ticksAngle / (options.maxValue - options.minValue);
  
      context.lineWidth = SmartCanvas.pixelRatio;
      context.strokeStyle = options.colorMinorTicks || options.colorStrokeTicks;
  
      context.save();
  
      if (options.exactTicks) {
          range = options.maxValue - options.minValue;
          s = minTicks ? range / minTicks : 0;
          delta = (BaseGauge.mod(options.majorTicks[0], minTicks) || 0)  * ratio;
      }
  
      else {
          s = minTicks * (options.majorTicks.length - 1);
      }
  
      for (; i < s; ++i) {
          angle = options.startAngle + delta + i * (options.ticksAngle / s);
          if (angle <= (options.ticksAngle + options.startAngle )) {
              context.rotate(drawings.radians(angle));
  
              context.beginPath();
              context.moveTo(0, radius);
              context.lineTo(0, radius - context.max * 0.075);
              closeStrokedPath(context);
          }
      }
  }
  
  /* istanbul ignore next: private, not testable */
  /**
   * Returns ticks radius
   *
   * @access private
   * @param context
   * @param options
   * @return {number}
   */
  function radialTicksRadius(context, options) {
      let unit = context.max / 100;
  
      return maxRadialRadius(context, options) - 5 * unit -
          (options.barWidth ?
              ((parseFloat(options.barStrokeWidth) || 0) * 2 +
              ((parseFloat(options.barWidth) || 0) + 5) * unit) :
          0);
  }
  
  /* istanbul ignore next: private, not testable */
  /**
   * Draws gauge major ticks bar on a canvas
   *
   * @param {Canvas2DContext} context
   * @param {RadialGaugeOptions} options
   */
  function drawRadialMajorTicks(context, options) {
      drawings.prepareTicks(options);
  
      //noinspection JSUnresolvedFunction
      let r = abs(radialTicksRadius(context, options));
      let i, colors;
      let s = options.majorTicks.length;
      let pixelRatio = SmartCanvas.pixelRatio;
  
      context.lineWidth = 2 * pixelRatio;
      context.save();
  
      colors = options.colorMajorTicks instanceof Array ?
          options.colorMajorTicks : new Array(s).fill(options.colorStrokeTicks ||
              options.colorMajorTicks);
  
      i = 0;
      for (; i < s; ++i) {
          context.strokeStyle = colors[i];
          context.rotate(drawings.radians(radialNextAngle(
              options,
              options.exactTicks ? options.majorTicks[i] : i,
              s
          )));
  
          context.beginPath();
          context.moveTo(0, r);
          context.lineTo(0, r - context.max * 0.15);
          closeStrokedPath(context);
      }
  
      if (options.strokeTicks) {
          context.strokeStyle = options.colorStrokeTicks || colors[0];
          context.rotate(HPI);
  
          context.beginPath();
          context.arc(0, 0, r,
              drawings.radians(options.startAngle),
              drawings.radians(options.startAngle + options.ticksAngle),
              false
          );
          closeStrokedPath(context);
      }
  }
  
  /* istanbul ignore next: private, not testable */
  function radialNextAngle(options, i, s) {
      if (options.exactTicks) {
          let ratio = options.ticksAngle / (options.maxValue - options.minValue);
          return options.startAngle + ratio * (i - options.minValue);
      }
  
      return options.startAngle + i * (options.ticksAngle / (s - 1));
  }
  
  /* istanbul ignore next: private, not testable */
  /**
   * Strokes, closes path and restores previous context state
   *
   * @param {Canvas2DContext} context
   */
  function closeStrokedPath(context) {
      context.stroke();
      context.restore();
      context.closePath();
      context.save();
  }
  
  /* istanbul ignore next: private, not testable */
  /**
   * Draws gauge bar numbers
   *
   * @access private
   * @param {Canvas2DContext} context
   * @param {RadialGaugeOptions} options
   */
  function drawRadialNumbers(context, options) {
      let radius = radialTicksRadius(context, options) - context.max * 0.15;
      let points = {};
      let i = 0;
      let s = options.majorTicks.length;
      let isAnimated = options.animationTarget !== 'needle';
      let colors = options.colorNumbers instanceof Array ?
          options.colorNumbers : new Array(s).fill(options.colorNumbers);
  
      let plateValueAngle = isAnimated ? -(options.value - options.minValue) /
          (options.maxValue - options.minValue) * options.ticksAngle : 0;
  
      if (isAnimated) {
          context.save();
          context.rotate(-drawings.radians(plateValueAngle));
      }
  
      context.font = drawings.font(options, 'Numbers', context.max / 200);
      context.lineWidth = 0;
      context.textAlign = 'center';
      context.textBaseline = 'middle';
  
      for (; i < s; ++i) {
          let angle = plateValueAngle + radialNextAngle(options,
              options.exactTicks ? options.majorTicks[i] : i, s);
          let textWidth = context.measureText(options.majorTicks[i]).width;
          let textHeight = options.fontNumbersSize;
          let textRadius = Math.sqrt(textWidth * textWidth +
              textHeight * textHeight) / 2;
          let point = drawings.radialPoint(radius - textRadius -
              options.numbersMargin / 100 * context.max,
              drawings.radians(angle));
  
          if (angle === 360) angle = 0;
  
          if (points[angle]) {
              continue; //already drawn at this place, skipping
          }
  
          points[angle] = true;
  
          context.fillStyle = colors[i];
          context.fillText(options.majorTicks[i], point.x, point.y);
      }
  
      isAnimated && context.restore();
  }
  
  /* istanbul ignore next: private, not testable */
  /**
   * Draws gauge title
   *
   * @access private
   * @param {Canvas2DContext} context
   * @param {RadialGaugeOptions} options
   */
  function drawRadialTitle(context, options) {
      if (!options.title) return;
  
      context.save();
      context.font = drawings.font(options, 'Title', context.max / 200);
      context.fillStyle = options.colorTitle;
      context.textAlign = 'center';
      context.fillText(options.title, 0, -context.max / 4.25, context.max * 0.8);
      context.restore();
  }
  
  /* istanbul ignore next: private, not testable */
  /**
   * Draws units name on the gauge
   *
   * @access private
   * @param {Canvas2DContext} context
   * @param {RadialGaugeOptions} options
   */
  function drawRadialUnits(context, options) {
      if (!options.units) return;
  
      context.save();
      context.font = drawings.font(options, 'Units', context.max / 200);
      context.fillStyle = options.colorUnits;
      context.textAlign = 'center';
      context.fillText(options.units, 0, context.max / 3.25, context.max * 0.8);
      context.restore();
  }
  
  /* istanbul ignore next: private, not testable */
  /**
   * Draws gauge needle
   *
   * @access private
   * @param {Canvas2DContext} context
   * @param {RadialGaugeOptions} options
   */
  function drawRadialNeedle(context, options) {
      if (!options.needle) return;
  
      let value = options.ticksAngle < 360 ?
          drawings.normalizedValue(options).indented : options.value;
      let max = maxRadialRadius(context, options);
      //noinspection JSUnresolvedFunction
      let r1 = abs(max / 100 * options.needleCircleSize);
      //noinspection JSUnresolvedFunction
      let r2 = abs(max / 100 * options.needleCircleSize * 0.75);
      //noinspection JSUnresolvedFunction
      let rIn = abs(max / 100 * options.needleEnd);
      //noinspection JSUnresolvedFunction
      let rStart = abs(options.needleStart ?
              max / 100 * options.needleStart : 0);
      //noinspection JSUnresolvedFunction
      let pad1 = max / 100 * options.needleWidth;
      let pad2 = max / 100 * options.needleWidth / 2;
      let pixelRatio = SmartCanvas.pixelRatio;
      let isFixed = options.animationTarget !== 'needle';
  
      context.save();
  
      drawings.drawNeedleShadow(context, options);
  
      context.rotate(drawings.radians(isFixed ? options.startAngle :
          (options.startAngle + (value -
              options.minValue) / (options.maxValue - options.minValue) *
              options.ticksAngle)));
  
      context.fillStyle = drawings.linearGradient(
          context,
          options.colorNeedle,
          options.colorNeedleEnd,
          rIn - rStart);
  
      if (options.needleType === 'arrow') {
          context.beginPath();
          context.moveTo(-pad2, -rStart);
          context.lineTo(-pad1, 0);
          context.lineTo(-1 * pixelRatio, rIn);
          context.lineTo(pixelRatio, rIn);
          context.lineTo(pad1, 0);
          context.lineTo(pad2, -rStart);
          context.closePath();
          context.fill();
  
          context.beginPath();
          context.lineTo(-0.5 * pixelRatio, rIn);
          context.lineTo(-1 * pixelRatio, rIn);
          context.lineTo(-pad1, 0);
          context.lineTo(-pad2, -rStart);
          context.lineTo(pad2 / 2 * pixelRatio - 2 * pixelRatio, -rStart);
          context.closePath();
          context.fillStyle = options.colorNeedleShadowUp;
          context.fill();
      }
  
      else { // simple line needle
          context.beginPath();
          context.moveTo(-pad2, rIn);
          context.lineTo(-pad2, rStart);
          context.lineTo(pad2, rStart);
          context.lineTo(pad2, rIn);
          context.closePath();
          context.fill();
      }
  
      if (options.needleCircleSize) {
          context.restore();
  
          drawings.drawNeedleShadow(context, options);
  
          if (options.needleCircleOuter) {
              context.beginPath();
              context.arc(0, 0, r1, 0, PI * 2, true);
              context.fillStyle = drawings.linearGradient(
                  context,
                  options.colorNeedleCircleOuter,
                  options.colorNeedleCircleOuterEnd,
                  r1
              );
              context.fill();
              context.closePath();
          }
  
          if (options.needleCircleInner) {
              context.beginPath();
              context.arc(0, 0, r2, 0, PI * 2, true);
              context.fillStyle = drawings.linearGradient(
                  context,
                  options.colorNeedleCircleInner,
                  options.colorNeedleCircleInnerEnd,
                  r2
              );
              context.fill();
              context.closePath();
          }
  
          context.restore();
      }
  }
  
  /* istanbul ignore next: private, not testable */
  /**
   * Draws gauge value box
   *
   * @param {Canvas2DContext} context
   * @param {RadialGaugeOptions} options
   * @param {number} value
   */
  function drawRadialValueBox(context, options, value) {
      drawings.drawValueBox(context, options, value, 0,
          context.max - context.max * 0.33, context.max);
  }
  
  /* istanbul ignore next: private, not testable */
  /**
   * Draws gauge progress bar
   *
   * @param {Canvas2DContext} context
   * @param {RadialGaugeOptions} options
   */
  function drawRadialProgressBar(context, options) {
      let unit = context.max / 100;
      let rMax = maxRadialRadius(context, options) - 5 * unit;
      let sw = (parseFloat(options.barStrokeWidth) || 0);
      let w = (parseFloat(options.barWidth) || 0) * unit;
      let rMin = rMax - sw * 2 - w;
      let half = (rMax- rMin) / 2;
      let r = rMin + half;
      let delta = sw / r;
      let sa = options.startAngle;
      let ea = options.startAngle + options.ticksAngle;
  
      context.save();
      context.rotate(HPI);
  
      if (sw) {
          // draw stroke
          context.beginPath();
          context.arc(0, 0, r, drawings.radians(sa) - delta,
              drawings.radians(ea) + delta, false);
          context.strokeStyle = options.colorBarStroke;
          context.lineWidth = half * 2;
          context.stroke();
          context.closePath();
      }
  
      if (w) {
          // draw bar
          context.beginPath();
          context.arc(0, 0, r, drawings.radians(sa), drawings.radians(ea), false);
          context.strokeStyle = options.colorBar;
          context.lineWidth = w;
          context.stroke();
          context.closePath();
  
          if  (options.barShadow) {
              // draw shadow
              context.beginPath();
              context.arc(0, 0, rMax, drawings.radians(sa), drawings.radians(ea),
                  false);
              context.clip();
  
              context.beginPath();
              context.strokeStyle = options.colorBar;
              context.lineWidth = 1;
              context.shadowBlur = options.barShadow;
              context.shadowColor = options.colorBarShadow;
              context.shadowOffsetX = 0;
              context.shadowOffsetY = 0;
              context.arc(0, 0, rMax,
                  drawings.radians(options.startAngle),
                  drawings.radians(options.startAngle + options.ticksAngle),
                  false);
              context.stroke();
              context.closePath();
  
              context.restore();
              context.rotate(HPI);
          }
  
          // draw bar progress
          if (options.barProgress) {
              context.beginPath();
              context.arc(0, 0, r,
                  drawings.radians(sa),
                  drawings.radians(sa +
                      (drawings.normalizedValue(options).normal -
                      options.minValue) / (options.maxValue - options.minValue) *
                      options.ticksAngle),
                  false);
              context.strokeStyle = options.colorBarProgress;
              context.lineWidth = w;
              context.stroke();
              context.closePath();
          }
      }
  
      context.restore();
  }
  
  /**
   * Find and return gauge value to display
   *
   * @param {RadialGauge} gauge
   */
  function displayValue(gauge) {
      if (gauge.options.animatedValue) {
          return gauge.options.value;
      }
  
      return gauge.value;
  }
  
  /**
   * Minimalistic HTML5 Canvas Gauge
   * @example
   *  var gauge = new RadialGauge({
   *     renderTo: 'gauge-id', // identifier of HTML canvas element or element itself
   *     width: 400,
   *     height: 400,
   *     units: 'Km/h',
   *     title: false,
   *     value: 0,
   *     minValue: 0,
   *     maxValue: 220,
   *     majorTicks: [
   *         '0','20','40','60','80','100','120','140','160','180','200','220'
   *     ],
   *     minorTicks: 2,
   *     strokeTicks: false,
   *     highlights: [
   *         { from: 0, to: 50, color: 'rgba(0,255,0,.15)' },
   *         { from: 50, to: 100, color: 'rgba(255,255,0,.15)' },
   *         { from: 100, to: 150, color: 'rgba(255,30,0,.25)' },
   *         { from: 150, to: 200, color: 'rgba(255,0,225,.25)' },
   *         { from: 200, to: 220, color: 'rgba(0,0,255,.25)' }
   *     ],
   *     colorPlate: '#222',
   *     colorMajorTicks: '#f5f5f5',
   *     colorMinorTicks: '#ddd',
   *     colorTitle: '#fff',
   *     colorUnits: '#ccc',
   *     colorNumbers: '#eee',
   *     colorNeedleStart: 'rgba(240, 128, 128, 1)',
   *     colorNeedleEnd: 'rgba(255, 160, 122, .9)',
   *     valueBox: true,
   *     animationRule: 'bounce'
   * });
   * // draw initially
   * gauge.draw();
   * // animate
   * setInterval(() => {
   *    gauge.value = Math.random() * -220 + 220;
   * }, 1000);
   */
  export default class RadialGauge extends BaseGauge {
  
      /**
       * Fired each time before gauge plate is drawn
       *
       * @event RadialGauge#beforePlate
       */
  
      /**
       * Fired each time before gauge highlight areas are drawn
       *
       * @event RadialGauge#beforeHighlights
       */
  
      /**
       * Fired each time before gauge minor ticks are drawn
       *
       * @event RadialGauge#beforeMinorTicks
       */
  
      /**
       * Fired each time before gauge major ticks are drawn
       *
       * @event RadialGauge#beforeMajorTicks
       */
  
      /**
       * Fired each time before gauge tick numbers are drawn
       *
       * @event RadialGauge#beforeNumbers
       */
  
      /**
       * Fired each time before gauge title is drawn
       *
       * @event RadialGauge#beforeTitle
       */
  
      /**
       * Fired each time before gauge units text is drawn
       *
       * @event RadialGauge#beforeUnits
       */
  
      /**
       * Fired each time before gauge progress bar is drawn
       *
       * @event RadialGauge#beforeProgressBar
       */
  
      /**
       * Fired each time before gauge value box is drawn
       *
       * @event RadialGauge#beforeValueBox
       */
  
      /**
       * Fired each time before gauge needle is drawn
       *
       * @event RadialGauge#beforeNeedle
       */
  
      /**
       * @constructor
       * @param {RadialGaugeOptions} options
       */
      constructor(options) {
          options = Object.assign({}, defaultRadialGaugeOptions, options || {});
          super(RadialGauge.configure(options));
      }
  
      /**
       * Checks and updates gauge options properly
       *
       * @param {*} options
       * @return {*}
       * @access protected
       */
      static configure(options) {
          if (options.barWidth > 50) options.barWidth = 50;
  
          /* istanbul ignore if */
          if (isNaN(options.startAngle)) options.startAngle = 45;
          /* istanbul ignore if */
          if (isNaN(options.ticksAngle)) options.ticksAngle = 270;
  
          /* istanbul ignore if */
          if (options.ticksAngle > 360) options.ticksAngle = 360;
          /* istanbul ignore if */
          if (options.ticksAngle < 0) options.ticksAngle = 0;
  
          /* istanbul ignore if */
          if (options.startAngle < 0) options.startAngle = 0;
          /* istanbul ignore if */
          if (options.startAngle > 360) options.startAngle = 360;
  
          return options;
      }
  
      /**
       * Sets the value for radial gauge
       *
       * @param {number} value
       */
      set value(value) {
          value = BaseGauge.ensureValue(value, this.options.minValue);
  
          if (this.options.animation &&
              this.options.ticksAngle === 360 &&
              this.options.useMinPath
          ) {
              this._value = value;
              value = this.options.value +
                  ((((value - this.options.value) % 360) + 540) % 360) - 180;
          }
  
          super.value = value;
      }
  
      /**
       * Returns current gauge value
       *
       * @return {number}
       */
      get value() {
          return super.value;
      }
  
      /**
       * Triggering gauge render on a canvas.
       *
       * @returns {RadialGauge}
       */
      draw() {
          try {
              let canvas = this.canvas;
              let [x, y, w, h] = [
                  -canvas.drawX,
                  -canvas.drawY,
                  canvas.drawWidth,
                  canvas.drawHeight
              ];
              let options = this.options;
  
              if (options.animationTarget === 'needle') {
                  if (!canvas.elementClone.initialized) {
                      let context = canvas.contextClone;
  
                      // clear the cache
                      context.clearRect(x, y, w, h);
                      context.save();
  
                      this.emit('beforePlate');
                      drawRadialPlate(context, options);
                      this.emit('beforeHighlights');
                      drawRadialHighlights(context, options);
                      this.emit('beforeMinorTicks');
                      drawRadialMinorTicks(context, options);
                      this.emit('beforeMajorTicks');
                      drawRadialMajorTicks(context, options);
                      this.emit('beforeNumbers');
                      drawRadialNumbers(context, options);
                      this.emit('beforeTitle');
                      drawRadialTitle(context, options);
                      this.emit('beforeUnits');
                      drawRadialUnits(context, options);
  
                      canvas.elementClone.initialized = true;
                  }
  
                  this.canvas.commit();
  
                  // clear the canvas
                  canvas.context.clearRect(x, y, w, h);
                  canvas.context.save();
  
                  canvas.context.drawImage(canvas.elementClone, x, y, w, h);
                  canvas.context.save();
  
                  this.emit('beforeProgressBar');
                  drawRadialProgressBar(canvas.context, options);
                  this.emit('beforeValueBox');
                  drawRadialValueBox(canvas.context, options, displayValue(this));
                  this.emit('beforeNeedle');
                  drawRadialNeedle(canvas.context, options);
              }
  
              else {
                  let plateValueAngle = -drawings.radians(
                      (options.value - options.minValue) /
                      (options.maxValue - options.minValue) *
                      options.ticksAngle);
  
                  // clear the canvas
                  canvas.context.clearRect(x, y, w, h);
                  canvas.context.save();
  
                  this.emit('beforePlate');
                  drawRadialPlate(canvas.context, options);
  
                  canvas.context.rotate(plateValueAngle);
  
                  // animated
                  this.emit('beforeHighlights');
                  drawRadialHighlights(canvas.context, options);
                  this.emit('beforeMinorTicks');
                  drawRadialMinorTicks(canvas.context, options);
                  this.emit('beforeMajorTicks');
                  drawRadialMajorTicks(canvas.context, options);
                  this.emit('beforeNumbers');
                  drawRadialNumbers(canvas.context, options);
                  this.emit('beforeProgressBar');
                  drawRadialProgressBar(canvas.context, options);
  
                  // non-animated
                  canvas.context.rotate(-plateValueAngle);
                  canvas.context.save();
  
                  if (!canvas.elementClone.initialized) {
                      let context = canvas.contextClone;
  
                      // clear the cache
                      context.clearRect(x, y, w, h);
                      context.save();
  
                      this.emit('beforeTitle');
                      drawRadialTitle(context, options);
                      this.emit('beforeUnits');
                      drawRadialUnits(context, options);
                      this.emit('beforeNeedle');
                      drawRadialNeedle(context, options);
  
                      canvas.elementClone.initialized = true;
                  }
  
                  canvas.context.drawImage(canvas.elementClone, x, y, w, h);
              }
  
              // value box animations
              this.emit('beforeValueBox');
              drawRadialValueBox(canvas.context, options, displayValue(this));
  
              super.draw();
          }
  
          catch (err) {
             drawings.verifyError(err);
          }
  
          return this;
      }
  }
  
  
  /**
   * @ignore
   * @typedef {object} ns
   */
  /* istanbul ignore if */
  if (typeof ns !== 'undefined') {
      ns['RadialGauge'] = RadialGauge;
  }
  
  BaseGauge.initialize('RadialGauge', defaultRadialGaugeOptions);
  
  module.exports = RadialGauge;
  