text: // Calculates all necessary s*it to build the chart
  // Heights, widths, transforms, ... = everything
  // Why such a mess you ask? Well, that's because
  // one components size can depend on other component
  // data formatting (e.g. grid width depends on sidebar precision)
  // So it's better to calc all in one place.
  
  import GridMaker from './grid_maker.js'
  import Utils from '../../stuff/utils.js'
  import math from '../../stuff/math.js'
  import log_scale from './log_scale.js'
  
  function Layout(params) {
  
      let {
          chart, sub, offsub, interval, range, ctx, layers_meta,
          ti_map, $props:$p, y_transforms: y_ts
      } = params
  
      let mgrid = chart.grid || {}
  
      offsub = offsub.filter((x, i) => {
          // Skip offchart overlays with custom grid id,
          // because they will be mergred with the existing grids
          return !(x.grid && x.grid.id)
      })
  
      // Splits space between main chart
      // and offchart indicator grids
      function grid_hs() {
  
          const height = $p.height - $p.config.BOTBAR
  
          // When at least one height defined (default = 1),
          // Pxs calculated as: (sum of weights) / number
          if (mgrid.height || offsub.find(x => x.grid.height)) {
              return weighted_hs(mgrid, height)
          }
  
          const n = offsub.length
          const off_h = (2 * Math.sqrt(n) / 7) / (n || 1)
  
          // Offchart grid height
          const px = Math.floor(height * off_h)
  
          // Main grid height
          const m = height - px * n
          return [m].concat(Array(n).fill(px))
  
      }
  
      function weighted_hs(grid, height) {
          let hs = [{grid}, ...offsub].map(x => x.grid.height || 1)
          let sum = hs.reduce((a, b) => a + b, 0)
          hs = hs.map(x => Math.floor((x / sum) * height))
  
          // Refine the height if Math.floor decreased px sum
          sum = hs.reduce((a, b) => a + b, 0)
          for (var i = 0; i < height - sum; i++) hs[i % hs.length]++
          return hs
      }
  
      function candles_n_vol() {
  
          self.candles = []
          self.volume = []
  
          let maxv = Math.max(...sub.map(x => x[5]))
          let vs = $p.config.VOLSCALE * $p.height / maxv
          var x1, x2, mid, prev = undefined
  
          let splitter = self.px_step > 5 ? 1 : 0
  
          for (var i = 0; i < sub.length; i++) {
              let p = sub[i]
              mid = self.t2screen(p[0]) + 0.5
              self.candles.push(mgrid.logScale ?
                  log_scale.candle(self, mid, p, $p): {
                  x: mid,
                  w: self.px_step * $p.config.CANDLEW,
                  o: Math.floor(p[1] * self.A + self.B),
                  h: Math.floor(p[2] * self.A + self.B),
                  l: Math.floor(p[3] * self.A + self.B),
                  c: Math.floor(p[4] * self.A + self.B),
                  raw: p
              })
              // Clear volume bar if there is a time gap
              if (sub[i-1] && p[0] - sub[i-1][0] > interval) {
                  prev = null
              }
              x1 = prev || Math.floor(mid - self.px_step * 0.5)
              x2 = Math.floor(mid + self.px_step * 0.5) - 0.5
              self.volume.push({
                  x1: x1,
                  x2: x2,
                  h: p[5] * vs,
                  green: p[4] >= p[1],
                  raw: p
              })
              prev = x2 + splitter
          }
      }
  
      // Main grid
      const hs = grid_hs()
      let specs = {
          sub, interval, range, ctx, $p, layers_meta,
          ti_map, height: hs[0], y_t: y_ts[0],
          grid: mgrid
      }
      let gms = [new GridMaker(0, specs)]
  
      // Sub grids
      for (var [i, { data, grid }] of offsub.entries()) {
          specs.sub = data
          specs.height = hs[i + 1]
          specs.y_t = y_ts[i + 1]
          specs.grid = grid || {}
          gms.push(new GridMaker(i + 1, specs, gms[0].get_layout()))
      }
  
      // Max sidebar among all grinds
      let sb = Math.max(...gms.map(x => x.get_sidebar()))
  
      let grids = [], offset = 0
  
      for (i = 0; i < gms.length; i++) {
          gms[i].set_sidebar(sb)
          grids.push(gms[i].create())
          grids[i].id = i
          grids[i].offset = offset
          offset += grids[i].height
      }
  
      let self = grids[0]
  
      candles_n_vol()
  
      return {
          grids: grids,
          botbar: {
              width: $p.width,
              height: $p.config.BOTBAR,
              offset: offset,
              xs: grids[0] ? grids[0].xs : []
          }
      }
  }
  
  export default Layout
  