Current File : /home/tradevaly/www/node_modules/apexcharts/src/charts/Radar.js
import Fill from '../modules/Fill'
import Graphics from '../modules/Graphics'
import Markers from '../modules/Markers'
import DataLabels from '../modules/DataLabels'
import Filters from '../modules/Filters'
import Utils from '../utils/Utils'
import Helpers from './common/circle/Helpers'
import CoreUtils from '../modules/CoreUtils'

/**
 * ApexCharts Radar Class for Spider/Radar Charts.
 * @module Radar
 **/

class Radar {
  constructor(ctx) {
    this.ctx = ctx
    this.w = ctx.w

    this.chartType = this.w.config.chart.type

    this.initialAnim = this.w.config.chart.animations.enabled
    this.dynamicAnim =
      this.initialAnim &&
      this.w.config.chart.animations.dynamicAnimation.enabled

    this.animDur = 0

    const w = this.w
    this.graphics = new Graphics(this.ctx)

    this.lineColorArr =
      w.globals.stroke.colors !== undefined
        ? w.globals.stroke.colors
        : w.globals.colors

    this.defaultSize =
      w.globals.svgHeight < w.globals.svgWidth
        ? w.globals.gridHeight + w.globals.goldenPadding * 1.5
        : w.globals.gridWidth

    this.isLog = w.config.yaxis[0].logarithmic

    this.coreUtils = new CoreUtils(this.ctx)
    this.maxValue = this.isLog
      ? this.coreUtils.getLogVal(w.globals.maxY, 0)
      : w.globals.maxY
    this.minValue = this.isLog
      ? this.coreUtils.getLogVal(this.w.globals.minY, 0)
      : w.globals.minY

    this.polygons = w.config.plotOptions.radar.polygons

    this.strokeWidth = w.config.stroke.show ? w.config.stroke.width : 0

    this.size =
      this.defaultSize / 2.1 - this.strokeWidth - w.config.chart.dropShadow.blur

    if (w.config.xaxis.labels.show) {
      this.size = this.size - w.globals.xAxisLabelsWidth / 1.75
    }

    if (w.config.plotOptions.radar.size !== undefined) {
      this.size = w.config.plotOptions.radar.size
    }

    this.dataRadiusOfPercent = []
    this.dataRadius = []
    this.angleArr = []

    this.yaxisLabelsTextsPos = []
  }

  draw(series) {
    let w = this.w
    const fill = new Fill(this.ctx)

    const allSeries = []
    const dataLabels = new DataLabels(this.ctx)

    if (series.length) {
      this.dataPointsLen = series[w.globals.maxValsInArrayIndex].length
    }
    this.disAngle = (Math.PI * 2) / this.dataPointsLen

    let halfW = w.globals.gridWidth / 2
    let halfH = w.globals.gridHeight / 2
    let translateX = halfW + w.config.plotOptions.radar.offsetX
    let translateY = halfH + w.config.plotOptions.radar.offsetY

    let ret = this.graphics.group({
      class: 'apexcharts-radar-series apexcharts-plot-series',
      transform: `translate(${translateX || 0}, ${translateY || 0})`
    })

    let dataPointsPos = []
    let elPointsMain = null
    let elDataPointsMain = null

    this.yaxisLabels = this.graphics.group({
      class: 'apexcharts-yaxis'
    })

    series.forEach((s, i) => {
      let longestSeries = s.length === w.globals.dataPoints

      // el to which series will be drawn
      let elSeries = this.graphics.group().attr({
        class: `apexcharts-series`,
        'data:longestSeries': longestSeries,
        seriesName: Utils.escapeString(w.globals.seriesNames[i]),
        rel: i + 1,
        'data:realIndex': i
      })

      this.dataRadiusOfPercent[i] = []
      this.dataRadius[i] = []
      this.angleArr[i] = []

      s.forEach((dv, j) => {
        const range = Math.abs(this.maxValue - this.minValue)
        dv = dv + Math.abs(this.minValue)

        if (this.isLog) {
          dv = this.coreUtils.getLogVal(dv, 0)
        }

        this.dataRadiusOfPercent[i][j] = dv / range

        this.dataRadius[i][j] = this.dataRadiusOfPercent[i][j] * this.size
        this.angleArr[i][j] = j * this.disAngle
      })

      dataPointsPos = this.getDataPointsPos(
        this.dataRadius[i],
        this.angleArr[i]
      )
      const paths = this.createPaths(dataPointsPos, {
        x: 0,
        y: 0
      })

      // points
      elPointsMain = this.graphics.group({
        class: 'apexcharts-series-markers-wrap apexcharts-element-hidden'
      })

      // datapoints
      elDataPointsMain = this.graphics.group({
        class: `apexcharts-datalabels`,
        'data:realIndex': i
      })

      w.globals.delayedElements.push({
        el: elPointsMain.node,
        index: i
      })

      const defaultRenderedPathOptions = {
        i,
        realIndex: i,
        animationDelay: i,
        initialSpeed: w.config.chart.animations.speed,
        dataChangeSpeed: w.config.chart.animations.dynamicAnimation.speed,
        className: `apexcharts-radar`,
        shouldClipToGrid: false,
        bindEventsOnPaths: false,
        stroke: w.globals.stroke.colors[i],
        strokeLineCap: w.config.stroke.lineCap
      }

      let pathFrom = null

      if (w.globals.previousPaths.length > 0) {
        pathFrom = this.getPreviousPath(i)
      }

      for (let p = 0; p < paths.linePathsTo.length; p++) {
        let renderedLinePath = this.graphics.renderPaths({
          ...defaultRenderedPathOptions,
          pathFrom: pathFrom === null ? paths.linePathsFrom[p] : pathFrom,
          pathTo: paths.linePathsTo[p],
          strokeWidth: Array.isArray(this.strokeWidth)
            ? this.strokeWidth[i]
            : this.strokeWidth,
          fill: 'none',
          drawShadow: false
        })

        elSeries.add(renderedLinePath)

        let pathFill = fill.fillPath({
          seriesNumber: i
        })

        let renderedAreaPath = this.graphics.renderPaths({
          ...defaultRenderedPathOptions,
          pathFrom: pathFrom === null ? paths.areaPathsFrom[p] : pathFrom,
          pathTo: paths.areaPathsTo[p],
          strokeWidth: 0,
          fill: pathFill,
          drawShadow: false
        })

        if (w.config.chart.dropShadow.enabled) {
          const filters = new Filters(this.ctx)

          const shadow = w.config.chart.dropShadow
          filters.dropShadow(
            renderedAreaPath,
            Object.assign({}, shadow, { noUserSpaceOnUse: true }),
            i
          )
        }

        elSeries.add(renderedAreaPath)
      }

      s.forEach((sj, j) => {
        let markers = new Markers(this.ctx)

        let opts = markers.getMarkerConfig('apexcharts-marker', i, j)

        let point = this.graphics.drawMarker(
          dataPointsPos[j].x,
          dataPointsPos[j].y,
          opts
        )

        point.attr('rel', j)
        point.attr('j', j)
        point.attr('index', i)
        point.node.setAttribute('default-marker-size', opts.pSize)

        let elPointsWrap = this.graphics.group({
          class: 'apexcharts-series-markers'
        })

        if (elPointsWrap) {
          elPointsWrap.add(point)
        }

        elPointsMain.add(elPointsWrap)

        elSeries.add(elPointsMain)

        const dataLabelsConfig = w.config.dataLabels

        if (dataLabelsConfig.enabled) {
          let text = dataLabelsConfig.formatter(w.globals.series[i][j], {
            seriesIndex: i,
            dataPointIndex: j,
            w
          })

          dataLabels.plotDataLabelsText({
            x: dataPointsPos[j].x,
            y: dataPointsPos[j].y,
            text,
            textAnchor: 'middle',
            i,
            j: i,
            parent: elDataPointsMain,
            offsetCorrection: false,
            dataLabelsConfig: {
              ...dataLabelsConfig
            }
          })
        }
        elSeries.add(elDataPointsMain)
      })

      allSeries.push(elSeries)
    })

    this.drawPolygons({
      parent: ret
    })

    if (w.config.xaxis.labels.show) {
      const xaxisTexts = this.drawXAxisTexts()
      ret.add(xaxisTexts)
    }

    allSeries.forEach((elS) => {
      ret.add(elS)
    })

    ret.add(this.yaxisLabels)

    return ret
  }

  drawPolygons(opts) {
    const w = this.w
    const { parent } = opts
    const helpers = new Helpers(this.ctx)

    const yaxisTexts = w.globals.yAxisScale[0].result.reverse()
    const layers = yaxisTexts.length

    let radiusSizes = []
    let layerDis = this.size / (layers - 1)
    for (let i = 0; i < layers; i++) {
      radiusSizes[i] = layerDis * i
    }
    radiusSizes.reverse()

    let polygonStrings = []
    let lines = []

    radiusSizes.forEach((radiusSize, r) => {
      const polygon = Utils.getPolygonPos(radiusSize, this.dataPointsLen)
      let string = ''

      polygon.forEach((p, i) => {
        if (r === 0) {
          const line = this.graphics.drawLine(
            p.x,
            p.y,
            0,
            0,
            Array.isArray(this.polygons.connectorColors)
              ? this.polygons.connectorColors[i]
              : this.polygons.connectorColors
          )

          lines.push(line)
        }

        if (i === 0) {
          this.yaxisLabelsTextsPos.push({
            x: p.x,
            y: p.y
          })
        }

        string += p.x + ',' + p.y + ' '
      })

      polygonStrings.push(string)
    })

    polygonStrings.forEach((p, i) => {
      const strokeColors = this.polygons.strokeColors
      const strokeWidth = this.polygons.strokeWidth
      const polygon = this.graphics.drawPolygon(
        p,
        Array.isArray(strokeColors) ? strokeColors[i] : strokeColors,
        Array.isArray(strokeWidth) ? strokeWidth[i] : strokeWidth,
        w.globals.radarPolygons.fill.colors[i]
      )
      parent.add(polygon)
    })

    lines.forEach((l) => {
      parent.add(l)
    })

    if (w.config.yaxis[0].show) {
      this.yaxisLabelsTextsPos.forEach((p, i) => {
        const yText = helpers.drawYAxisTexts(p.x, p.y, i, yaxisTexts[i])
        this.yaxisLabels.add(yText)
      })
    }
  }

  drawXAxisTexts() {
    const w = this.w

    const xaxisLabelsConfig = w.config.xaxis.labels
    let elXAxisWrap = this.graphics.group({
      class: 'apexcharts-xaxis'
    })

    let polygonPos = Utils.getPolygonPos(this.size, this.dataPointsLen)

    w.globals.labels.forEach((label, i) => {
      let formatter = w.config.xaxis.labels.formatter
      let dataLabels = new DataLabels(this.ctx)

      if (polygonPos[i]) {
        let textPos = this.getTextPos(polygonPos[i], this.size)

        let text = formatter(label, {
          seriesIndex: -1,
          dataPointIndex: i,
          w
        })

        dataLabels.plotDataLabelsText({
          x: textPos.newX,
          y: textPos.newY,
          text,
          textAnchor: textPos.textAnchor,
          i,
          j: i,
          parent: elXAxisWrap,
          color:
            Array.isArray(xaxisLabelsConfig.style.colors) &&
            xaxisLabelsConfig.style.colors[i]
              ? xaxisLabelsConfig.style.colors[i]
              : '#a8a8a8',
          dataLabelsConfig: {
            textAnchor: textPos.textAnchor,
            dropShadow: { enabled: false },
            ...xaxisLabelsConfig
          },
          offsetCorrection: false
        })
      }
    })

    return elXAxisWrap
  }

  createPaths(pos, origin) {
    let linePathsTo = []
    let linePathsFrom = []
    let areaPathsTo = []
    let areaPathsFrom = []

    if (pos.length) {
      linePathsFrom = [this.graphics.move(origin.x, origin.y)]
      areaPathsFrom = [this.graphics.move(origin.x, origin.y)]

      let linePathTo = this.graphics.move(pos[0].x, pos[0].y)
      let areaPathTo = this.graphics.move(pos[0].x, pos[0].y)

      pos.forEach((p, i) => {
        linePathTo += this.graphics.line(p.x, p.y)
        areaPathTo += this.graphics.line(p.x, p.y)
        if (i === pos.length - 1) {
          linePathTo += 'Z'
          areaPathTo += 'Z'
        }
      })

      linePathsTo.push(linePathTo)
      areaPathsTo.push(areaPathTo)
    }

    return {
      linePathsFrom,
      linePathsTo,
      areaPathsFrom,
      areaPathsTo
    }
  }

  getTextPos(pos, polygonSize) {
    let limit = 10
    let textAnchor = 'middle'

    let newX = pos.x
    let newY = pos.y

    if (Math.abs(pos.x) >= limit) {
      if (pos.x > 0) {
        textAnchor = 'start'
        newX += 10
      } else if (pos.x < 0) {
        textAnchor = 'end'
        newX -= 10
      }
    } else {
      textAnchor = 'middle'
    }
    if (Math.abs(pos.y) >= polygonSize - limit) {
      if (pos.y < 0) {
        newY -= 10
      } else if (pos.y > 0) {
        newY += 10
      }
    }

    return {
      textAnchor,
      newX,
      newY
    }
  }

  getPreviousPath(realIndex) {
    let w = this.w
    let pathFrom = null
    for (let pp = 0; pp < w.globals.previousPaths.length; pp++) {
      let gpp = w.globals.previousPaths[pp]

      if (
        gpp.paths.length > 0 &&
        parseInt(gpp.realIndex, 10) === parseInt(realIndex, 10)
      ) {
        if (typeof w.globals.previousPaths[pp].paths[0] !== 'undefined') {
          pathFrom = w.globals.previousPaths[pp].paths[0].d
        }
      }
    }
    return pathFrom
  }

  getDataPointsPos(
    dataRadiusArr,
    angleArr,
    dataPointsLen = this.dataPointsLen
  ) {
    dataRadiusArr = dataRadiusArr || []
    angleArr = angleArr || []
    let dataPointsPosArray = []
    for (let j = 0; j < dataPointsLen; j++) {
      let curPointPos = {}
      curPointPos.x = dataRadiusArr[j] * Math.sin(angleArr[j])
      curPointPos.y = -dataRadiusArr[j] * Math.cos(angleArr[j])
      dataPointsPosArray.push(curPointPos)
    }
    return dataPointsPosArray
  }
}

export default Radar