713 lines
23 KiB
JavaScript
713 lines
23 KiB
JavaScript
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), itemStyleModel.get('opacity'), 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 itemStyleModel = data.getItemModel(dataIndex);
|
|
var color = retrieve.firstNotNull(getItemVisualColor(data, dataIndex), itemStyleModel.get(['itemStyle', 'color']), '#fff');
|
|
var opacity = retrieve.firstNotNull(getItemVisualOpacity(data, dataIndex), itemStyleModel.get(['itemStyle', 'opacity']), 1);
|
|
var colorArr = graphicGL.parseColor(color);
|
|
colorArr[3] *= opacity;
|
|
|
|
this._setColorOfDataIndex(data, dataIndex, colorArr);
|
|
},
|
|
dispose: function () {
|
|
this._labelsBuilder.dispose();
|
|
},
|
|
_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; |