'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };

var _deck = require('deck.gl');

var _luma = require('luma.gl');

var _polygonTesselator = require('./polygon-tesselator');

var _solidPolygonLayerVertex = require('./solid-polygon-layer-vertex.glsl');

var _solidPolygonLayerVertex2 = _interopRequireDefault(_solidPolygonLayerVertex);

var _solidPolygonLayerVertex3 = require('./solid-polygon-layer-vertex-64.glsl');

var _solidPolygonLayerVertex4 = _interopRequireDefault(_solidPolygonLayerVertex3);

var _solidPolygonLayerFragment = require('./solid-polygon-layer-fragment.glsl');

var _solidPolygonLayerFragment2 = _interopRequireDefault(_solidPolygonLayerFragment);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } // Copyright (c) 2015 - 2017 Uber Technologies, Inc.
//
// 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.

var enable64bitSupport = _deck.experimental.enable64bitSupport,
    get = _deck.experimental.get;

// Polygon geometry generation is managed by the polygon tesselator

var defaultLineColor = [0x0, 0x0, 0x0, 0xff];
var defaultFillColor = [0x0, 0x0, 0x0, 0xff];

var defaultProps = {
  filled: true,
  // Whether to extrude
  extruded: false,
  // Whether to draw a GL.LINES wireframe of the polygon
  wireframe: false,
  fp64: false,

  // elevation multiplier
  elevationScale: 1,

  // Accessor for polygon geometry
  getPolygon: function getPolygon(f) {
    return get(f, 'polygon') || get(f, 'geometry.coordinates');
  },
  // Accessor for extrusion height
  getElevation: function getElevation(f) {
    return get(f, 'elevation') || get(f, 'properties.height') || 0;
  },
  // Accessor for colors
  getFillColor: function getFillColor(f) {
    return get(f, 'fillColor') || get(f, 'properties.color') || defaultFillColor;
  },
  getLineColor: function getLineColor(f) {
    return get(f, 'lineColor') || get(f, 'properties.color') || defaultLineColor;
  },

  // Optional settings for 'lighting' shader module
  lightSettings: {}
};

// Side model attributes
var SIDE_FILL_POSITIONS = new Float32Array([
// top left corner
0, 1,
// bottom left corner
0, 0,
// top right corner
1, 1,
// bottom right corner
1, 0]);
var SIDE_WIRE_POSITIONS = new Float32Array([
// top right corner
1, 1,
// top left corner
0, 1,
// bottom left corner
0, 0,
// bottom right corner
1, 0]);

// Model types
var ATTRIBUTE_MAPS = {
  TOP: {
    indices: { instanced: 0 },
    positions: { instanced: 0 },
    positions64xyLow: { instanced: 0 },
    elevations: { instanced: 0 },
    fillColors: { name: 'colors', instanced: 0 },
    pickingColors: { instanced: 0 }
  },
  SIDE: {
    positions: { instanced: 1 },
    positions64xyLow: { instanced: 1 },
    nextPositions: { instanced: 1 },
    nextPositions64xyLow: { instanced: 1 },
    elevations: { instanced: 1 },
    fillColors: { name: 'colors', instanced: 1 },
    pickingColors: { instanced: 1 }
  },
  WIRE: {
    positions: { instanced: 1 },
    positions64xyLow: { instanced: 1 },
    nextPositions: { instanced: 1 },
    nextPositions64xyLow: { instanced: 1 },
    elevations: { instanced: 1 },
    lineColors: { name: 'colors', instanced: 1 },
    pickingColors: { instanced: 1 }
  }
};

var SolidPolygonLayer = function (_Layer) {
  _inherits(SolidPolygonLayer, _Layer);

  function SolidPolygonLayer() {
    _classCallCheck(this, SolidPolygonLayer);

    return _possibleConstructorReturn(this, (SolidPolygonLayer.__proto__ || Object.getPrototypeOf(SolidPolygonLayer)).apply(this, arguments));
  }

  _createClass(SolidPolygonLayer, [{
    key: 'getShaders',
    value: function getShaders() {
      return enable64bitSupport(this.props) ? { vs: _solidPolygonLayerVertex4.default, fs: _solidPolygonLayerFragment2.default, modules: ['project64', 'lighting', 'picking'] } : { vs: _solidPolygonLayerVertex2.default, fs: _solidPolygonLayerFragment2.default, modules: ['lighting', 'picking'] }; // 'project' module added by default.
    }
  }, {
    key: 'initializeState',
    value: function initializeState() {
      var gl = this.context.gl;

      this.setState({
        numInstances: 0,
        IndexType: gl.getExtension('OES_element_index_uint') ? Uint32Array : Uint16Array
      });

      var attributeManager = this.getAttributeManager();
      var noAlloc = true;
      /* eslint-disable max-len */
      attributeManager.add({
        indices: { size: 1, isIndexed: true, update: this.calculateIndices, noAlloc: noAlloc },
        positions: {
          size: 3,
          accessor: ['extruded', 'fp64'],
          update: this.calculatePositions,
          noAlloc: noAlloc
        },
        nextPositions: {
          size: 3,
          accessor: ['extruded', 'fp64'],
          update: this.calculateNextPositions,
          noAlloc: noAlloc
        },
        elevations: {
          size: 1,
          accessor: ['extruded', 'getElevation'],
          update: this.calculateElevations,
          noAlloc: noAlloc
        },
        fillColors: {
          alias: 'colors',
          size: 4,
          type: _luma.GL.UNSIGNED_BYTE,
          accessor: 'getFillColor',
          update: this.calculateFillColors,
          noAlloc: noAlloc
        },
        lineColors: {
          alias: 'colors',
          size: 4,
          type: _luma.GL.UNSIGNED_BYTE,
          accessor: 'getLineColor',
          update: this.calculateLineColors,
          noAlloc: noAlloc
        },
        pickingColors: { size: 3, type: _luma.GL.UNSIGNED_BYTE, update: this.calculatePickingColors, noAlloc: noAlloc }
      });
      /* eslint-enable max-len */
    }
  }, {
    key: 'updateAttribute',
    value: function updateAttribute(_ref) {
      var props = _ref.props,
          oldProps = _ref.oldProps;

      if (props.fp64 !== oldProps.fp64) {
        var attributeManager = this.getAttributeManager();

        if (props.fp64 && props.coordinateSystem === _deck.COORDINATE_SYSTEM.LNGLAT) {
          /* eslint-disable max-len */
          attributeManager.add({
            positions64xyLow: { size: 2, accessor: 'fp64', update: this.calculatePositionsLow },
            nextPositions64xyLow: { size: 2, accessor: 'fp64', update: this.calculateNextPositionsLow }
          });
          /* eslint-enable max-len */
        } else {
          attributeManager.remove(['positions64xyLow', 'nextPositions64xyLow']);
        }
      }
    }
  }, {
    key: 'draw',
    value: function draw(_ref2) {
      var uniforms = _ref2.uniforms;
      var _props = this.props,
          extruded = _props.extruded,
          elevationScale = _props.elevationScale;


      var renderUniforms = Object.assign({}, uniforms, {
        extruded: extruded ? 1.0 : 0.0,
        elevationScale: elevationScale
      });

      this.state.models.forEach(function (model) {
        model.render(renderUniforms);
      });
    }
  }, {
    key: 'updateState',
    value: function updateState(updateParams) {
      _get(SolidPolygonLayer.prototype.__proto__ || Object.getPrototypeOf(SolidPolygonLayer.prototype), 'updateState', this).call(this, updateParams);

      this.updateGeometry(updateParams);
      this.updateAttribute(updateParams);

      var props = updateParams.props,
          oldProps = updateParams.oldProps;


      var regenerateModels = props.fp64 !== oldProps.fp64 || props.filled !== oldProps.filled || props.extruded !== oldProps.extruded || props.wireframe !== oldProps.wireframe;

      if (regenerateModels) {
        this.setState(Object.assign({
          // Set a flag to set attributes to new models
          modelsChanged: true
        }, this._getModels(this.context.gl)));
      }

      if (props.extruded !== oldProps.extruded) {
        this.state.attributeManager.invalidate('extruded');
      }
      if (props.fp64 !== oldProps.fp64) {
        this.state.attributeManager.invalidate('fp64');
      }
    }
  }, {
    key: 'updateGeometry',
    value: function updateGeometry(_ref3) {
      var props = _ref3.props,
          oldProps = _ref3.oldProps,
          changeFlags = _ref3.changeFlags;

      var geometryConfigChanged = changeFlags.dataChanged || changeFlags.updateTriggersChanged && (changeFlags.updateTriggersChanged.all || changeFlags.updateTriggersChanged.getPolygon);

      // When the geometry config  or the data is changed,
      // tessellator needs to be invoked
      if (geometryConfigChanged) {
        // TODO - avoid creating a temporary array here: let the tesselator iterate
        var polygons = props.data.map(props.getPolygon);

        this.setState({
          polygonTesselator: new _polygonTesselator.PolygonTesselator({ polygons: polygons, IndexType: this.state.IndexType })
        });

        this.state.attributeManager.invalidateAll();
      }

      if (geometryConfigChanged || props.extruded !== oldProps.extruded || props.fp64 !== oldProps.fp64) {
        this.state.polygonTesselator.updatePositions({
          fp64: props.fp64,
          extruded: props.extruded
        });
      }
    }
  }, {
    key: 'updateAttributes',
    value: function updateAttributes(props) {
      var _state = this.state,
          attributeManager = _state.attributeManager,
          modelsChanged = _state.modelsChanged;

      // Figure out data length

      attributeManager.update({
        data: props.data,
        numInstances: 0,
        props: props,
        buffers: props,
        context: this,
        // Don't worry about non-attribute props
        ignoreUnknownAttributes: true
      });

      if (modelsChanged) {
        this._updateAttributes(attributeManager.attributes);
        // clear the flag
        this.setState({ modelsChanged: false });
      } else {
        var changedAttributes = attributeManager.getChangedAttributes({ clearChangedFlags: true });
        this._updateAttributes(changedAttributes);
      }
    }
  }, {
    key: '_updateAttributes',
    value: function _updateAttributes(attributes) {
      var modelsByName = this.state.modelsByName;


      for (var modelName in modelsByName) {
        var model = modelsByName[modelName];

        if (modelName === 'TOP') {
          model.setVertexCount(this.state.numVertex);
        } else {
          model.setInstanceCount(this.state.numInstances);
        }

        var attributeMap = ATTRIBUTE_MAPS[modelName];
        var newAttributes = {};
        for (var attributeName in attributes) {
          var attribute = attributes[attributeName];
          var attributeOverride = attributeMap[attributeName];

          if (attributeOverride) {
            var newAttribute = Object.assign({}, attribute, attributeOverride);
            newAttributes[attributeOverride.name || attributeName] = newAttribute;
          }
        }
        model.setAttributes(newAttributes);
      }
    }
  }, {
    key: '_getModels',
    value: function _getModels(gl) {
      var _props2 = this.props,
          id = _props2.id,
          filled = _props2.filled,
          extruded = _props2.extruded,
          wireframe = _props2.wireframe;


      var models = {};

      if (filled) {
        models.TOP = new _luma.Model(gl, Object.assign({}, this.getShaders(), {
          id: id + '-top',
          geometry: new _luma.Geometry({
            drawMode: _luma.GL.TRIANGLES,
            attributes: {
              vertexPositions: { size: 2, isGeneric: true, value: new Float32Array([0, 1]) },
              nextPositions: { size: 3, isGeneric: true, value: new Float32Array(3) },
              nextPositions64xyLow: { size: 2, isGeneric: true, value: new Float32Array(2) }
            }
          }),
          uniforms: {
            isSideVertex: 0
          },
          vertexCount: 0,
          isIndexed: true,
          shaderCache: this.context.shaderCache
        }));
      }
      if (filled && extruded) {
        models.SIDE = new _luma.Model(gl, Object.assign({}, this.getShaders(), {
          id: id + '-side',
          geometry: new _luma.Geometry({
            drawMode: _luma.GL.TRIANGLE_STRIP,
            vertexCount: 4,
            attributes: {
              vertexPositions: { size: 2, value: SIDE_FILL_POSITIONS }
            }
          }),
          uniforms: {
            isSideVertex: 1
          },
          isInstanced: 1,
          shaderCache: this.context.shaderCache
        }));
      }
      if (extruded && wireframe) {
        models.WIRE = new _luma.Model(gl, Object.assign({}, this.getShaders(), {
          id: id + '-wire',
          geometry: new _luma.Geometry({
            drawMode: _luma.GL.LINE_STRIP,
            vertexCount: 4,
            attributes: {
              vertexPositions: { size: 2, value: SIDE_WIRE_POSITIONS }
            }
          }),
          uniforms: {
            isSideVertex: 1
          },
          isInstanced: 1,
          shaderCache: this.context.shaderCache
        }));
      }

      return {
        models: [models.WIRE, models.SIDE, models.TOP].filter(Boolean),
        modelsByName: models
      };
    }
  }, {
    key: 'calculateIndices',
    value: function calculateIndices(attribute) {
      attribute.value = this.state.polygonTesselator.indices();
      attribute.target = _luma.GL.ELEMENT_ARRAY_BUFFER;
      var numVertex = attribute.value.length / attribute.size;
      this.setState({ numVertex: numVertex });
    }
  }, {
    key: 'calculatePositions',
    value: function calculatePositions(attribute) {
      attribute.value = this.state.polygonTesselator.positions();
      var numInstances = attribute.value.length / attribute.size;
      this.setState({ numInstances: numInstances });
    }
  }, {
    key: 'calculatePositionsLow',
    value: function calculatePositionsLow(attribute) {
      attribute.value = this.state.polygonTesselator.positions64xyLow();
    }
  }, {
    key: 'calculateNextPositions',
    value: function calculateNextPositions(attribute) {
      attribute.value = this.state.polygonTesselator.nextPositions();
    }
  }, {
    key: 'calculateNextPositionsLow',
    value: function calculateNextPositionsLow(attribute) {
      attribute.value = this.state.polygonTesselator.nextPositions64xyLow();
    }
  }, {
    key: 'calculateElevations',
    value: function calculateElevations(attribute) {
      var _this2 = this;

      if (this.props.extruded) {
        attribute.isGeneric = false;
        attribute.value = this.state.polygonTesselator.elevations({
          getElevation: function getElevation(polygonIndex) {
            return _this2.props.getElevation(_this2.props.data[polygonIndex]);
          }
        });
      } else {
        attribute.isGeneric = true;
        attribute.value = new Float32Array(1);
      }
    }
  }, {
    key: 'calculateFillColors',
    value: function calculateFillColors(attribute) {
      var _this3 = this;

      attribute.value = this.state.polygonTesselator.colors({
        key: 'fillColors',
        getColor: function getColor(polygonIndex) {
          return _this3.props.getFillColor(_this3.props.data[polygonIndex]);
        }
      });
    }
  }, {
    key: 'calculateLineColors',
    value: function calculateLineColors(attribute) {
      var _this4 = this;

      attribute.value = this.state.polygonTesselator.colors({
        key: 'lineColors',
        getColor: function getColor(polygonIndex) {
          return _this4.props.getLineColor(_this4.props.data[polygonIndex]);
        }
      });
    }

    // Override the default picking colors calculation

  }, {
    key: 'calculatePickingColors',
    value: function calculatePickingColors(attribute) {
      attribute.value = this.state.polygonTesselator.pickingColors();
    }
  }]);

  return SolidPolygonLayer;
}(_deck.Layer);

exports.default = SolidPolygonLayer;


SolidPolygonLayer.layerName = 'SolidPolygonLayer';
SolidPolygonLayer.defaultProps = defaultProps;
//# sourceMappingURL=solid-polygon-layer.js.map