import * as echarts from 'echarts/lib/echarts'; import graphicGL from '../../util/graphicGL'; import retrieve from '../../util/retrieve'; import format from '../../util/format'; import BarsGeometry from '../../util/geometry/Bars3DGeometry'; import LabelsBuilder from '../../component/common/LabelsBuilder'; import glmatrix from 'claygl/src/dep/glmatrix'; import { getItemVisualColor, getItemVisualOpacity } from '../../util/visual'; var vec3 = glmatrix.vec3; export default echarts.ChartView.extend({ type: 'bar3D', __ecgl__: true, init: function (ecModel, api) { this.groupGL = new graphicGL.Node(); this._api = api; this._labelsBuilder = new LabelsBuilder(256, 256, api); var self = this; this._labelsBuilder.getLabelPosition = function (dataIndex, position, distance) { if (self._data) { var layout = self._data.getItemLayout(dataIndex); var start = layout[0]; var dir = layout[1]; var height = layout[2][1]; return vec3.scaleAndAdd([], start, dir, distance + height); } else { return [0, 0]; } }; // Give a large render order. this._labelsBuilder.getMesh().renderOrder = 100; }, render: function (seriesModel, ecModel, api) { // Swap barMesh var tmp = this._prevBarMesh; this._prevBarMesh = this._barMesh; this._barMesh = tmp; if (!this._barMesh) { this._barMesh = new graphicGL.Mesh({ geometry: new BarsGeometry(), shadowDepthMaterial: new graphicGL.Material({ shader: new graphicGL.Shader(graphicGL.Shader.source('ecgl.sm.depth.vertex'), graphicGL.Shader.source('ecgl.sm.depth.fragment')) }), // Only cartesian3D enable culling // FIXME Performance culling: seriesModel.coordinateSystem.type === 'cartesian3D', // Render after axes renderOrder: 10, // Render normal in normal pass renderNormal: true }); } this.groupGL.remove(this._prevBarMesh); this.groupGL.add(this._barMesh); this.groupGL.add(this._labelsBuilder.getMesh()); var coordSys = seriesModel.coordinateSystem; this._doRender(seriesModel, api); if (coordSys && coordSys.viewGL) { coordSys.viewGL.add(this.groupGL); var methodName = coordSys.viewGL.isLinearSpace() ? 'define' : 'undefine'; this._barMesh.material[methodName]('fragment', 'SRGB_DECODE'); } this._data = seriesModel.getData(); this._labelsBuilder.updateData(this._data); this._labelsBuilder.updateLabels(); this._updateAnimation(seriesModel); }, _updateAnimation: function (seriesModel) { graphicGL.updateVertexAnimation([['prevPosition', 'position'], ['prevNormal', 'normal']], this._prevBarMesh, this._barMesh, seriesModel); }, _doRender: function (seriesModel, api) { var data = seriesModel.getData(); var shading = seriesModel.get('shading'); var enableNormal = shading !== 'color'; var self = this; var barMesh = this._barMesh; var shadingPrefix = 'ecgl.' + shading; if (!barMesh.material || barMesh.material.shader.name !== shadingPrefix) { barMesh.material = graphicGL.createMaterial(shadingPrefix, ['VERTEX_COLOR']); } graphicGL.setMaterialFromModel(shading, barMesh.material, seriesModel, api); barMesh.geometry.enableNormal = enableNormal; barMesh.geometry.resetOffset(); // Bevel settings var bevelSize = seriesModel.get('bevelSize'); var bevelSegments = seriesModel.get('bevelSmoothness'); barMesh.geometry.bevelSegments = bevelSegments; barMesh.geometry.bevelSize = bevelSize; var colorArr = []; var vertexColors = new Float32Array(data.count() * 4); var colorOffset = 0; var barCount = 0; var hasTransparent = false; data.each(function (idx) { if (!data.hasValue(idx)) { return; } var color = getItemVisualColor(data, idx); var opacity = getItemVisualOpacity(data, idx); if (opacity == null) { opacity = 1; } graphicGL.parseColor(color, colorArr); colorArr[3] *= opacity; vertexColors[colorOffset++] = colorArr[0]; vertexColors[colorOffset++] = colorArr[1]; vertexColors[colorOffset++] = colorArr[2]; vertexColors[colorOffset++] = colorArr[3]; if (colorArr[3] > 0) { barCount++; if (colorArr[3] < 0.99) { hasTransparent = true; } } }); barMesh.geometry.setBarCount(barCount); var orient = data.getLayout('orient'); // Map of dataIndex and barIndex. var barIndexOfData = this._barIndexOfData = new Int32Array(data.count()); var barCount = 0; data.each(function (idx) { if (!data.hasValue(idx)) { barIndexOfData[idx] = -1; return; } var layout = data.getItemLayout(idx); var start = layout[0]; var dir = layout[1]; var size = layout[2]; var idx4 = idx * 4; colorArr[0] = vertexColors[idx4++]; colorArr[1] = vertexColors[idx4++]; colorArr[2] = vertexColors[idx4++]; colorArr[3] = vertexColors[idx4++]; if (colorArr[3] > 0) { self._barMesh.geometry.addBar(start, dir, orient, size, colorArr, idx); barIndexOfData[idx] = barCount++; } }); barMesh.geometry.dirty(); barMesh.geometry.updateBoundingBox(); var material = barMesh.material; material.transparent = hasTransparent; material.depthMask = !hasTransparent; barMesh.geometry.sortTriangles = hasTransparent; this._initHandler(seriesModel, api); }, _initHandler: function (seriesModel, api) { var data = seriesModel.getData(); var barMesh = this._barMesh; var isCartesian3D = seriesModel.coordinateSystem.type === 'cartesian3D'; barMesh.seriesIndex = seriesModel.seriesIndex; var lastDataIndex = -1; barMesh.off('mousemove'); barMesh.off('mouseout'); barMesh.on('mousemove', function (e) { var dataIndex = barMesh.geometry.getDataIndexOfVertex(e.triangle[0]); if (dataIndex !== lastDataIndex) { this._downplay(lastDataIndex); this._highlight(dataIndex); this._labelsBuilder.updateLabels([dataIndex]); if (isCartesian3D) { api.dispatchAction({ type: 'grid3DShowAxisPointer', value: [data.get('x', dataIndex), data.get('y', dataIndex), data.get('z', dataIndex, true)] }); } } lastDataIndex = dataIndex; barMesh.dataIndex = dataIndex; }, this); barMesh.on('mouseout', function (e) { this._downplay(lastDataIndex); this._labelsBuilder.updateLabels(); lastDataIndex = -1; barMesh.dataIndex = -1; if (isCartesian3D) { api.dispatchAction({ type: 'grid3DHideAxisPointer' }); } }, this); }, _highlight: function (dataIndex) { var data = this._data; if (!data) { return; } var barIndex = this._barIndexOfData[dataIndex]; if (barIndex < 0) { return; } var itemModel = data.getItemModel(dataIndex); var emphasisItemStyleModel = itemModel.getModel('emphasis.itemStyle'); var emphasisColor = emphasisItemStyleModel.get('color'); var emphasisOpacity = emphasisItemStyleModel.get('opacity'); 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._barMesh.geometry.setColor(barIndex, colorArr); this._api.getZr().refresh(); }, _downplay: function (dataIndex) { var data = this._data; if (!data) { return; } var barIndex = this._barIndexOfData[dataIndex]; if (barIndex < 0) { return; } var color = getItemVisualColor(data, dataIndex); var opacity = getItemVisualOpacity(data, dataIndex); var colorArr = graphicGL.parseColor(color); colorArr[3] *= opacity; this._barMesh.geometry.setColor(barIndex, colorArr); this._api.getZr().refresh(); }, highlight: function (seriesModel, ecModel, api, payload) { this._toggleStatus('highlight', seriesModel, ecModel, api, payload); }, downplay: function (seriesModel, ecModel, api, payload) { this._toggleStatus('downplay', seriesModel, ecModel, api, payload); }, _toggleStatus: function (status, seriesModel, ecModel, api, payload) { var data = seriesModel.getData(); var dataIndex = retrieve.queryDataIndex(data, payload); var self = this; if (dataIndex != null) { echarts.util.each(format.normalizeToArray(dataIndex), function (dataIdx) { status === 'highlight' ? this._highlight(dataIdx) : this._downplay(dataIdx); }, this); } else { data.each(function (dataIdx) { status === 'highlight' ? self._highlight(dataIdx) : self._downplay(dataIdx); }); } }, remove: function () { this.groupGL.removeAll(); }, dispose: function () { this._labelsBuilder.dispose(); this.groupGL.removeAll(); } });