import * as echarts from 'echarts/lib/echarts';
import graphicGL from '../../util/graphicGL';
import earcut from '../../util/earcut';
import LinesGeo from '../../util/geometry/Lines3D';
import retrieve from '../../util/retrieve';
import glmatrix from 'claygl/src/dep/glmatrix';
import trianglesSortMixin from '../../util/geometry/trianglesSortMixin';
import LabelsBuilder from './LabelsBuilder';
import lines3DGLSL from '../../util/shader/lines3D.glsl.js';
import { getItemVisualColor, getItemVisualOpacity } from '../../util/visual';
var vec3 = glmatrix.vec3;
graphicGL.Shader.import(lines3DGLSL);

function Geo3DBuilder(api) {
  this.rootNode = new graphicGL.Node(); // Cache triangulation result

  this._triangulationResults = {};
  this._shadersMap = graphicGL.COMMON_SHADERS.filter(function (shaderName) {
    return shaderName !== 'shadow';
  }).reduce(function (obj, shaderName) {
    obj[shaderName] = graphicGL.createShader('ecgl.' + shaderName);
    return obj;
  }, {});
  this._linesShader = graphicGL.createShader('ecgl.meshLines3D');
  var groundMaterials = {};
  graphicGL.COMMON_SHADERS.forEach(function (shading) {
    groundMaterials[shading] = new graphicGL.Material({
      shader: graphicGL.createShader('ecgl.' + shading)
    });
  });
  this._groundMaterials = groundMaterials;
  this._groundMesh = new graphicGL.Mesh({
    geometry: new graphicGL.PlaneGeometry({
      dynamic: true
    }),
    castShadow: false,
    renderNormal: true,
    $ignorePicking: true
  });

  this._groundMesh.rotation.rotateX(-Math.PI / 2);

  this._labelsBuilder = new LabelsBuilder(512, 512, api); // Give a large render order.

  this._labelsBuilder.getMesh().renderOrder = 100;
  this._labelsBuilder.getMesh().material.depthTest = false;
  this.rootNode.add(this._labelsBuilder.getMesh());

  this._initMeshes();

  this._api = api;
}

Geo3DBuilder.prototype = {
  constructor: Geo3DBuilder,
  // Which dimension to extrude. Y or Z
  extrudeY: true,
  update: function (componentModel, ecModel, api, start, end) {
    var data = componentModel.getData();

    if (start == null) {
      start = 0;
    }

    if (end == null) {
      end = data.count();
    }

    this._startIndex = start;
    this._endIndex = end - 1;

    this._triangulation(componentModel, start, end);

    var shader = this._getShader(componentModel.get('shading'));

    this._prepareMesh(componentModel, shader, api, start, end);

    this.rootNode.updateWorldTransform();

    this._updateRegionMesh(componentModel, api, start, end);

    var coordSys = componentModel.coordinateSystem; // PENDING

    if (coordSys.type === 'geo3D') {
      this._updateGroundPlane(componentModel, coordSys, api);
    }

    var self = this;

    this._labelsBuilder.updateData(data, start, end);

    this._labelsBuilder.getLabelPosition = function (dataIndex, positionDesc, distance) {
      var name = data.getName(dataIndex);
      var center;
      var height = distance;

      if (coordSys.type === 'geo3D') {
        var region = coordSys.getRegion(name);

        if (!region) {
          return [NaN, NaN, NaN];
        }

        center = region.getCenter();
        var pos = coordSys.dataToPoint([center[0], center[1], height]);
        return pos;
      } else {
        var tmp = self._triangulationResults[dataIndex - self._startIndex];
        var center = self.extrudeY ? [(tmp.max[0] + tmp.min[0]) / 2, tmp.max[1] + height, (tmp.max[2] + tmp.min[2]) / 2] : [(tmp.max[0] + tmp.min[0]) / 2, (tmp.max[1] + tmp.min[1]) / 2, tmp.max[2] + height];
      }
    };

    this._data = data;

    this._labelsBuilder.updateLabels();

    this._updateDebugWireframe(componentModel); // Reset some state.


    this._lastHoverDataIndex = 0;
  },
  _initMeshes: function () {
    var self = this;

    function createPolygonMesh() {
      var mesh = new graphicGL.Mesh({
        name: 'Polygon',
        material: new graphicGL.Material({
          shader: self._shadersMap.lambert
        }),
        geometry: new graphicGL.Geometry({
          sortTriangles: true,
          dynamic: true
        }),
        // TODO Disable culling
        culling: false,
        ignorePicking: true,
        // Render normal in normal pass
        renderNormal: true
      });
      Object.assign(mesh.geometry, trianglesSortMixin);
      return mesh;
    }

    var polygonMesh = createPolygonMesh();
    var linesMesh = new graphicGL.Mesh({
      material: new graphicGL.Material({
        shader: this._linesShader
      }),
      castShadow: false,
      ignorePicking: true,
      $ignorePicking: true,
      geometry: new LinesGeo({
        useNativeLine: false
      })
    });
    this.rootNode.add(polygonMesh);
    this.rootNode.add(linesMesh);
    polygonMesh.material.define('both', 'VERTEX_COLOR');
    polygonMesh.material.define('fragment', 'DOUBLE_SIDED');
    this._polygonMesh = polygonMesh;
    this._linesMesh = linesMesh;
    this.rootNode.add(this._groundMesh);
  },
  _getShader: function (shading) {
    var shader = this._shadersMap[shading];

    if (!shader) {
      if (process.env.NODE_ENV !== 'production') {
        console.warn('Unkown shading ' + shading);
      } // Default use lambert shader.


      shader = this._shadersMap.lambert;
    }

    shader.__shading = shading;
    return shader;
  },
  _prepareMesh: function (componentModel, shader, api, start, end) {
    var polygonVertexCount = 0;
    var polygonTriangleCount = 0;
    var linesVertexCount = 0;
    var linesTriangleCount = 0; // TODO Lines

    for (var idx = start; idx < end; idx++) {
      var polyInfo = this._getRegionPolygonInfo(idx);

      var lineInfo = this._getRegionLinesInfo(idx, componentModel, this._linesMesh.geometry);

      polygonVertexCount += polyInfo.vertexCount;
      polygonTriangleCount += polyInfo.triangleCount;
      linesVertexCount += lineInfo.vertexCount;
      linesTriangleCount += lineInfo.triangleCount;
    }

    var polygonMesh = this._polygonMesh;
    var polygonGeo = polygonMesh.geometry;
    ['position', 'normal', 'texcoord0', 'color'].forEach(function (attrName) {
      polygonGeo.attributes[attrName].init(polygonVertexCount);
    });
    polygonGeo.indices = polygonVertexCount > 0xffff ? new Uint32Array(polygonTriangleCount * 3) : new Uint16Array(polygonTriangleCount * 3);

    if (polygonMesh.material.shader !== shader) {
      polygonMesh.material.attachShader(shader, true);
    }

    graphicGL.setMaterialFromModel(shader.__shading, polygonMesh.material, componentModel, api);

    if (linesVertexCount > 0) {
      this._linesMesh.geometry.resetOffset();

      this._linesMesh.geometry.setVertexCount(linesVertexCount);

      this._linesMesh.geometry.setTriangleCount(linesTriangleCount);
    } // Indexing data index from vertex index.


    this._dataIndexOfVertex = new Uint32Array(polygonVertexCount); // Indexing vertex index range from data index

    this._vertexRangeOfDataIndex = new Uint32Array((end - start) * 2);
  },
  _updateRegionMesh: function (componentModel, api, start, end) {
    var data = componentModel.getData();
    var vertexOffset = 0;
    var triangleOffset = 0; // Materials configurations.

    var hasTranparentRegion = false;
    var polygonMesh = this._polygonMesh;
    var linesMesh = this._linesMesh;

    for (var dataIndex = start; dataIndex < end; dataIndex++) {
      // Get bunch of visual properties.
      var regionModel = componentModel.getRegionModel(dataIndex);
      var itemStyleModel = regionModel.getModel('itemStyle');
      var color = retrieve.firstNotNull(getItemVisualColor(data, dataIndex), itemStyleModel.get('color'), '#fff');
      var opacity = retrieve.firstNotNull(getItemVisualOpacity(data, dataIndex), 1);
      var colorArr = graphicGL.parseColor(color);
      var borderColorArr = graphicGL.parseColor(itemStyleModel.get('borderColor'));
      colorArr[3] *= opacity;
      borderColorArr[3] *= opacity;
      var isTransparent = colorArr[3] < 0.99;
      polygonMesh.material.set('color', [1, 1, 1, 1]);
      hasTranparentRegion = hasTranparentRegion || isTransparent;
      var regionHeight = retrieve.firstNotNull(regionModel.get('height', true), componentModel.get('regionHeight'));

      var newOffsets = this._updatePolygonGeometry(componentModel, polygonMesh.geometry, dataIndex, regionHeight, vertexOffset, triangleOffset, colorArr);

      for (var i = vertexOffset; i < newOffsets.vertexOffset; i++) {
        this._dataIndexOfVertex[i] = dataIndex;
      }

      this._vertexRangeOfDataIndex[(dataIndex - start) * 2] = vertexOffset;
      this._vertexRangeOfDataIndex[(dataIndex - start) * 2 + 1] = newOffsets.vertexOffset;
      vertexOffset = newOffsets.vertexOffset;
      triangleOffset = newOffsets.triangleOffset; // Update lines.

      var lineWidth = itemStyleModel.get('borderWidth');
      var hasLine = lineWidth > 0;

      if (hasLine) {
        lineWidth *= api.getDevicePixelRatio();

        this._updateLinesGeometry(linesMesh.geometry, componentModel, dataIndex, regionHeight, lineWidth, componentModel.coordinateSystem.transform);
      }

      linesMesh.invisible = !hasLine;
      linesMesh.material.set({
        color: borderColorArr
      });
    }

    var polygonMesh = this._polygonMesh;
    polygonMesh.material.transparent = hasTranparentRegion;
    polygonMesh.material.depthMask = !hasTranparentRegion;
    polygonMesh.geometry.updateBoundingBox();
    polygonMesh.frontFace = this.extrudeY ? graphicGL.Mesh.CCW : graphicGL.Mesh.CW; // Update tangents

    if (polygonMesh.material.get('normalMap')) {
      polygonMesh.geometry.generateTangents();
    }

    polygonMesh.seriesIndex = componentModel.seriesIndex;
    polygonMesh.on('mousemove', this._onmousemove, this);
    polygonMesh.on('mouseout', this._onmouseout, this);
  },
  _updateDebugWireframe: function (componentModel) {
    var debugWireframeModel = componentModel.getModel('debug.wireframe'); // TODO Unshow

    if (debugWireframeModel.get('show')) {
      var color = graphicGL.parseColor(debugWireframeModel.get('lineStyle.color') || 'rgba(0,0,0,0.5)');
      var width = retrieve.firstNotNull(debugWireframeModel.get('lineStyle.width'), 1); // TODO  Will cause highlight wrong

      var mesh = this._polygonMesh;
      mesh.geometry.generateBarycentric();
      mesh.material.define('both', 'WIREFRAME_TRIANGLE');
      mesh.material.set('wireframeLineColor', color);
      mesh.material.set('wireframeLineWidth', width);
    }
  },
  _onmousemove: function (e) {
    var dataIndex = this._dataIndexOfVertex[e.triangle[0]];

    if (dataIndex == null) {
      dataIndex = -1;
    }

    if (dataIndex !== this._lastHoverDataIndex) {
      this.downplay(this._lastHoverDataIndex);
      this.highlight(dataIndex);

      this._labelsBuilder.updateLabels([dataIndex]);
    }

    this._lastHoverDataIndex = dataIndex;
    this._polygonMesh.dataIndex = dataIndex;
  },
  _onmouseout: function (e) {
    if (e.target) {
      this.downplay(this._lastHoverDataIndex);
      this._lastHoverDataIndex = -1;
      this._polygonMesh.dataIndex = -1;
    }

    this._labelsBuilder.updateLabels([]);
  },
  _updateGroundPlane: function (componentModel, geo3D, api) {
    var groundModel = componentModel.getModel('groundPlane', componentModel);
    this._groundMesh.invisible = !groundModel.get('show', true);

    if (this._groundMesh.invisible) {
      return;
    }

    var shading = componentModel.get('shading');
    var material = this._groundMaterials[shading];

    if (!material) {
      if (process.env.NODE_ENV !== 'production') {
        console.warn('Unkown shading ' + shading);
      }

      material = this._groundMaterials.lambert;
    }

    graphicGL.setMaterialFromModel(shading, material, groundModel, api);

    if (material.get('normalMap')) {
      this._groundMesh.geometry.generateTangents();
    }

    this._groundMesh.material = material;

    this._groundMesh.material.set('color', graphicGL.parseColor(groundModel.get('color')));

    this._groundMesh.scale.set(geo3D.size[0], geo3D.size[2], 1);
  },
  _triangulation: function (componentModel, start, end) {
    this._triangulationResults = [];
    var minAll = [Infinity, Infinity, Infinity];
    var maxAll = [-Infinity, -Infinity, -Infinity];
    var coordSys = componentModel.coordinateSystem;

    for (var idx = start; idx < end; idx++) {
      var polygons = [];
      var polygonCoords = componentModel.getRegionPolygonCoords(idx);

      for (var i = 0; i < polygonCoords.length; i++) {
        var exterior = polygonCoords[i].exterior;
        var interiors = polygonCoords[i].interiors;
        var points = [];
        var holes = [];

        if (exterior.length < 3) {
          continue;
        }

        var offset = 0;

        for (var j = 0; j < exterior.length; j++) {
          var p = exterior[j];
          points[offset++] = p[0];
          points[offset++] = p[1];
        }

        for (var j = 0; j < interiors.length; j++) {
          if (interiors[j].length < 3) {
            continue;
          }

          var startIdx = points.length / 2;

          for (var k = 0; k < interiors[j].length; k++) {
            var p = interiors[j][k];
            points.push(p[0]);
            points.push(p[1]);
          }

          holes.push(startIdx);
        }

        var triangles = earcut(points, holes);
        var points3 = new Float64Array(points.length / 2 * 3);
        var pos = [];
        var min = [Infinity, Infinity, Infinity];
        var max = [-Infinity, -Infinity, -Infinity];
        var off3 = 0;

        for (var j = 0; j < points.length;) {
          vec3.set(pos, points[j++], 0, points[j++]);

          if (coordSys && coordSys.transform) {
            vec3.transformMat4(pos, pos, coordSys.transform);
          }

          vec3.min(min, min, pos);
          vec3.max(max, max, pos);
          points3[off3++] = pos[0];
          points3[off3++] = pos[1];
          points3[off3++] = pos[2];
        }

        vec3.min(minAll, minAll, min);
        vec3.max(maxAll, maxAll, max);
        polygons.push({
          points: points3,
          indices: triangles,
          min: min,
          max: max
        });
      }

      this._triangulationResults.push(polygons);
    }

    this._geoBoundingBox = [minAll, maxAll];
  },

  /**
   * Get region vertex and triangle count
   */
  _getRegionPolygonInfo: function (idx) {
    var polygons = this._triangulationResults[idx - this._startIndex];
    var sideVertexCount = 0;
    var sideTriangleCount = 0;

    for (var i = 0; i < polygons.length; i++) {
      sideVertexCount += polygons[i].points.length / 3;
      sideTriangleCount += polygons[i].indices.length / 3;
    }

    var vertexCount = sideVertexCount * 2 + sideVertexCount * 4;
    var triangleCount = sideTriangleCount * 2 + sideVertexCount * 2;
    return {
      vertexCount: vertexCount,
      triangleCount: triangleCount
    };
  },
  _updatePolygonGeometry: function (componentModel, geometry, dataIndex, regionHeight, vertexOffset, triangleOffset, color) {
    // FIXME
    var projectUVOnGround = componentModel.get('projectUVOnGround');
    var positionAttr = geometry.attributes.position;
    var normalAttr = geometry.attributes.normal;
    var texcoordAttr = geometry.attributes.texcoord0;
    var colorAttr = geometry.attributes.color;
    var polygons = this._triangulationResults[dataIndex - this._startIndex];
    var hasColor = colorAttr.value && color;
    var indices = geometry.indices;
    var extrudeCoordIndex = this.extrudeY ? 1 : 2;
    var sideCoordIndex = this.extrudeY ? 2 : 1;
    var scale = [this.rootNode.worldTransform.x.len(), this.rootNode.worldTransform.y.len(), this.rootNode.worldTransform.z.len()];
    var min = vec3.mul([], this._geoBoundingBox[0], scale);
    var max = vec3.mul([], this._geoBoundingBox[1], scale);
    var maxDimSize = Math.max(max[0] - min[0], max[2] - min[2]);

    function addVertices(polygon, y, insideOffset) {
      var points = polygon.points;
      var pointsLen = points.length;
      var currentPosition = [];
      var uv = [];

      for (var k = 0; k < pointsLen; k += 3) {
        currentPosition[0] = points[k];
        currentPosition[extrudeCoordIndex] = y;
        currentPosition[sideCoordIndex] = points[k + 2];
        uv[0] = (points[k] * scale[0] - min[0]) / maxDimSize;
        uv[1] = (points[k + 2] * scale[sideCoordIndex] - min[2]) / maxDimSize;
        positionAttr.set(vertexOffset, currentPosition);

        if (hasColor) {
          colorAttr.set(vertexOffset, color);
        }

        texcoordAttr.set(vertexOffset++, uv);
      }
    }

    function buildTopBottom(polygon, y, insideOffset) {
      var startVertexOffset = vertexOffset;
      addVertices(polygon, y, insideOffset);
      var len = polygon.indices.length;

      for (var k = 0; k < len; k++) {
        indices[triangleOffset * 3 + k] = polygon.indices[k] + startVertexOffset;
      }

      triangleOffset += polygon.indices.length / 3;
    }

    var normalTop = this.extrudeY ? [0, 1, 0] : [0, 0, 1];
    var normalBottom = vec3.negate([], normalTop);

    for (var p = 0; p < polygons.length; p++) {
      var startVertexOffset = vertexOffset;
      var polygon = polygons[p]; // BOTTOM

      buildTopBottom(polygon, 0, 0); // TOP

      buildTopBottom(polygon, regionHeight, 0);
      var ringVertexCount = polygon.points.length / 3;

      for (var v = 0; v < ringVertexCount; v++) {
        normalAttr.set(startVertexOffset + v, normalBottom);
        normalAttr.set(startVertexOffset + v + ringVertexCount, normalTop);
      }

      var quadToTriangle = [0, 3, 1, 1, 3, 2];
      var quadPos = [[], [], [], []];
      var a = [];
      var b = [];
      var normal = [];
      var uv = [];
      var len = 0;

      for (var v = 0; v < ringVertexCount; v++) {
        var next = (v + 1) % ringVertexCount;
        var dx = (polygon.points[next * 3] - polygon.points[v * 3]) * scale[0];
        var dy = (polygon.points[next * 3 + 2] - polygon.points[v * 3 + 2]) * scale[sideCoordIndex];
        var sideLen = Math.sqrt(dx * dx + dy * dy); // 0----1
        // 3----2

        for (var k = 0; k < 4; k++) {
          var isCurrent = k === 0 || k === 3;
          var idx3 = (isCurrent ? v : next) * 3;
          quadPos[k][0] = polygon.points[idx3];
          quadPos[k][extrudeCoordIndex] = k > 1 ? regionHeight : 0;
          quadPos[k][sideCoordIndex] = polygon.points[idx3 + 2];
          positionAttr.set(vertexOffset + k, quadPos[k]);

          if (projectUVOnGround) {
            uv[0] = (polygon.points[idx3] * scale[0] - min[0]) / maxDimSize;
            uv[1] = (polygon.points[idx3 + 2] * scale[sideCoordIndex] - min[sideCoordIndex]) / maxDimSize;
          } else {
            uv[0] = (isCurrent ? len : len + sideLen) / maxDimSize;
            uv[1] = (quadPos[k][extrudeCoordIndex] * scale[extrudeCoordIndex] - min[extrudeCoordIndex]) / maxDimSize;
          }

          texcoordAttr.set(vertexOffset + k, uv);
        }

        vec3.sub(a, quadPos[1], quadPos[0]);
        vec3.sub(b, quadPos[3], quadPos[0]);
        vec3.cross(normal, a, b);
        vec3.normalize(normal, normal);

        for (var k = 0; k < 4; k++) {
          normalAttr.set(vertexOffset + k, normal);

          if (hasColor) {
            colorAttr.set(vertexOffset + k, color);
          }
        }

        for (var k = 0; k < 6; k++) {
          indices[triangleOffset * 3 + k] = quadToTriangle[k] + vertexOffset;
        }

        vertexOffset += 4;
        triangleOffset += 2;
        len += sideLen;
      }
    }

    geometry.dirty();
    return {
      vertexOffset: vertexOffset,
      triangleOffset: triangleOffset
    };
  },
  _getRegionLinesInfo: function (idx, componentModel, geometry) {
    var vertexCount = 0;
    var triangleCount = 0;
    var regionModel = componentModel.getRegionModel(idx);
    var itemStyleModel = regionModel.getModel('itemStyle');
    var lineWidth = itemStyleModel.get('borderWidth');

    if (lineWidth > 0) {
      var polygonCoords = componentModel.getRegionPolygonCoords(idx);
      polygonCoords.forEach(function (coords) {
        var exterior = coords.exterior;
        var interiors = coords.interiors;
        vertexCount += geometry.getPolylineVertexCount(exterior);
        triangleCount += geometry.getPolylineTriangleCount(exterior);

        for (var i = 0; i < interiors.length; i++) {
          vertexCount += geometry.getPolylineVertexCount(interiors[i]);
          triangleCount += geometry.getPolylineTriangleCount(interiors[i]);
        }
      }, this);
    }

    return {
      vertexCount: vertexCount,
      triangleCount: triangleCount
    };
  },
  _updateLinesGeometry: function (geometry, componentModel, dataIndex, regionHeight, lineWidth, transform) {
    function convertToPoints3(polygon) {
      var points = new Float64Array(polygon.length * 3);
      var offset = 0;
      var pos = [];

      for (var i = 0; i < polygon.length; i++) {
        pos[0] = polygon[i][0]; // Add a offset to avoid z-fighting

        pos[1] = regionHeight + 0.1;
        pos[2] = polygon[i][1];

        if (transform) {
          vec3.transformMat4(pos, pos, transform);
        }

        points[offset++] = pos[0];
        points[offset++] = pos[1];
        points[offset++] = pos[2];
      }

      return points;
    }

    var whiteColor = [1, 1, 1, 1];
    var coords = componentModel.getRegionPolygonCoords(dataIndex);
    coords.forEach(function (geo) {
      var exterior = geo.exterior;
      var interiors = geo.interiors;
      geometry.addPolyline(convertToPoints3(exterior), whiteColor, lineWidth);

      for (var i = 0; i < interiors.length; i++) {
        geometry.addPolyline(convertToPoints3(interiors[i]), whiteColor, lineWidth);
      }
    });
  },
  highlight: function (dataIndex) {
    var data = this._data;

    if (!data) {
      return;
    }

    var itemModel = data.getItemModel(dataIndex);
    var emphasisItemStyleModel = itemModel.getModel(['emphasis', 'itemStyle']);
    var emphasisColor = emphasisItemStyleModel.get('color');
    var emphasisOpacity = retrieve.firstNotNull(emphasisItemStyleModel.get('opacity'), getItemVisualOpacity(data, dataIndex), 1);

    if (emphasisColor == null) {
      var color = getItemVisualColor(data, dataIndex);
      emphasisColor = echarts.color.lift(color, -0.4);
    }

    if (emphasisOpacity == null) {
      emphasisOpacity = getItemVisualOpacity(data, dataIndex);
    }

    var colorArr = graphicGL.parseColor(emphasisColor);
    colorArr[3] *= emphasisOpacity;

    this._setColorOfDataIndex(data, dataIndex, colorArr);
  },
  downplay: function (dataIndex) {
    var data = this._data;

    if (!data) {
      return;
    }

    var color = retrieve.firstNotNull(getItemVisualColor(data, dataIndex), data.getItemModel(dataIndex).get(['itemStyle', 'color']), '#fff');
    var opacity = retrieve.firstNotNull(getItemVisualOpacity(data, dataIndex), 1);
    var colorArr = graphicGL.parseColor(color);
    colorArr[3] *= opacity;

    this._setColorOfDataIndex(data, dataIndex, colorArr);
  },
  _setColorOfDataIndex: function (data, dataIndex, colorArr) {
    if (dataIndex < this._startIndex && dataIndex > this._endIndex) {
      return;
    }

    dataIndex -= this._startIndex;

    for (var i = this._vertexRangeOfDataIndex[dataIndex * 2]; i < this._vertexRangeOfDataIndex[dataIndex * 2 + 1]; i++) {
      this._polygonMesh.geometry.attributes.color.set(i, colorArr);
    }

    this._polygonMesh.geometry.dirty();

    this._api.getZr().refresh();
  }
};
export default Geo3DBuilder;