UNPKG

cione-comp-lib

Version:

`$ yarn add cione-comp-lib` 或者 `$ npm i cione-comp-lib -S`

375 lines (374 loc) 13.6 kB
import "../../../../echarts/lib/echarts.mjs"; import graphicGL from "../../util/graphicGL.mjs"; import glmatrix from "../../../../claygl/src/glmatrix/index.mjs"; import trianglesSortMixin from "../../util/geometry/trianglesSortMixin.mjs"; import { getItemVisualColor, getItemVisualOpacity } from "../../util/visual.mjs"; import ChartView from "../../../../echarts/lib/view/Chart.mjs"; var vec3 = glmatrix.vec3; function isPointsNaN(pt) { return isNaN(pt[0]) || isNaN(pt[1]) || isNaN(pt[2]); } var SurfaceView = ChartView.extend({ type: "surface", __ecgl__: true, init: function(ecModel, api) { this.groupGL = new graphicGL.Node(); }, render: function(seriesModel, ecModel, api) { var tmp = this._prevSurfaceMesh; this._prevSurfaceMesh = this._surfaceMesh; this._surfaceMesh = tmp; if (!this._surfaceMesh) { this._surfaceMesh = this._createSurfaceMesh(); } this.groupGL.remove(this._prevSurfaceMesh); this.groupGL.add(this._surfaceMesh); var coordSys = seriesModel.coordinateSystem; var shading = seriesModel.get("shading"); var data = seriesModel.getData(); var shadingPrefix = "ecgl." + shading; if (!this._surfaceMesh.material || this._surfaceMesh.material.shader.name !== shadingPrefix) { this._surfaceMesh.material = graphicGL.createMaterial(shadingPrefix, ["VERTEX_COLOR", "DOUBLE_SIDED"]); } graphicGL.setMaterialFromModel(shading, this._surfaceMesh.material, seriesModel, api); if (coordSys && coordSys.viewGL) { coordSys.viewGL.add(this.groupGL); var methodName = coordSys.viewGL.isLinearSpace() ? "define" : "undefine"; this._surfaceMesh.material[methodName]("fragment", "SRGB_DECODE"); } var isParametric = seriesModel.get("parametric"); var dataShape = seriesModel.get("dataShape"); if (!dataShape) { dataShape = this._getDataShape(data, isParametric); } var wireframeModel = seriesModel.getModel("wireframe"); var wireframeLineWidth = wireframeModel.get("lineStyle.width"); var showWireframe = wireframeModel.get("show") && wireframeLineWidth > 0; this._updateSurfaceMesh(this._surfaceMesh, seriesModel, dataShape, showWireframe); var material = this._surfaceMesh.material; if (showWireframe) { material.define("WIREFRAME_QUAD"); material.set("wireframeLineWidth", wireframeLineWidth); material.set("wireframeLineColor", graphicGL.parseColor(wireframeModel.get("lineStyle.color"))); } else { material.undefine("WIREFRAME_QUAD"); } this._initHandler(seriesModel, api); this._updateAnimation(seriesModel); }, _updateAnimation: function(seriesModel) { graphicGL.updateVertexAnimation([["prevPosition", "position"], ["prevNormal", "normal"]], this._prevSurfaceMesh, this._surfaceMesh, seriesModel); }, _createSurfaceMesh: function() { var mesh = new graphicGL.Mesh({ geometry: new graphicGL.Geometry({ dynamic: true, sortTriangles: true }), shadowDepthMaterial: new graphicGL.Material({ shader: new graphicGL.Shader(graphicGL.Shader.source("ecgl.sm.depth.vertex"), graphicGL.Shader.source("ecgl.sm.depth.fragment")) }), culling: false, renderOrder: 10, renderNormal: true }); mesh.geometry.createAttribute("barycentric", "float", 4); mesh.geometry.createAttribute("prevPosition", "float", 3); mesh.geometry.createAttribute("prevNormal", "float", 3); Object.assign(mesh.geometry, trianglesSortMixin); return mesh; }, _initHandler: function(seriesModel, api) { var data = seriesModel.getData(); var surfaceMesh = this._surfaceMesh; var coordSys = seriesModel.coordinateSystem; function getNearestPointIdx(triangle, point) { var nearestDist = Infinity; var nearestIdx = -1; var pos = []; for (var i = 0; i < triangle.length; i++) { surfaceMesh.geometry.attributes.position.get(triangle[i], pos); var dist = vec3.dist(point.array, pos); if (dist < nearestDist) { nearestDist = dist; nearestIdx = triangle[i]; } } return nearestIdx; } surfaceMesh.seriesIndex = seriesModel.seriesIndex; var lastDataIndex = -1; surfaceMesh.off("mousemove"); surfaceMesh.off("mouseout"); surfaceMesh.on("mousemove", function(e) { var idx = getNearestPointIdx(e.triangle, e.point); if (idx >= 0) { var point = []; surfaceMesh.geometry.attributes.position.get(idx, point); var value = coordSys.pointToData(point); var minDist = Infinity; var dataIndex = -1; var item = []; for (var i = 0; i < data.count(); i++) { item[0] = data.get("x", i); item[1] = data.get("y", i); item[2] = data.get("z", i); var dist = vec3.squaredDistance(item, value); if (dist < minDist) { dataIndex = i; minDist = dist; } } if (dataIndex !== lastDataIndex) { api.dispatchAction({ type: "grid3DShowAxisPointer", value }); } lastDataIndex = dataIndex; surfaceMesh.dataIndex = dataIndex; } else { surfaceMesh.dataIndex = -1; } }, this); surfaceMesh.on("mouseout", function(e) { lastDataIndex = -1; surfaceMesh.dataIndex = -1; api.dispatchAction({ type: "grid3DHideAxisPointer" }); }, this); }, _updateSurfaceMesh: function(surfaceMesh, seriesModel, dataShape, showWireframe) { var geometry = surfaceMesh.geometry; var data = seriesModel.getData(); var pointsArr = data.getLayout("points"); var invalidDataCount = 0; data.each(function(idx2) { if (!data.hasValue(idx2)) { invalidDataCount++; } }); var needsSplitQuad = invalidDataCount || showWireframe; var positionAttr = geometry.attributes.position; var normalAttr = geometry.attributes.normal; var texcoordAttr = geometry.attributes.texcoord0; var barycentricAttr = geometry.attributes.barycentric; var colorAttr = geometry.attributes.color; var row = dataShape[0]; var column = dataShape[1]; var shading = seriesModel.get("shading"); var needsNormal = shading !== "color"; if (needsSplitQuad) { var vertexCount = (row - 1) * (column - 1) * 4; positionAttr.init(vertexCount); if (showWireframe) { barycentricAttr.init(vertexCount); } } else { positionAttr.value = new Float32Array(pointsArr); } colorAttr.init(geometry.vertexCount); texcoordAttr.init(geometry.vertexCount); var quadToTriangle = [0, 3, 1, 1, 3, 2]; var quadBarycentric = [[1, 1, 0, 0], [0, 1, 0, 1], [1, 0, 0, 1], [1, 0, 1, 0]]; var indices = geometry.indices = new (geometry.vertexCount > 65535 ? Uint32Array : Uint16Array)((row - 1) * (column - 1) * 6); var getQuadIndices = function(i2, j2, out) { out[1] = i2 * column + j2; out[0] = i2 * column + j2 + 1; out[3] = (i2 + 1) * column + j2 + 1; out[2] = (i2 + 1) * column + j2; }; var isTransparent = false; if (needsSplitQuad) { var quadIndices = []; var pos = []; var faceOffset = 0; if (needsNormal) { normalAttr.init(geometry.vertexCount); } else { normalAttr.value = null; } var pts = [[], [], []]; var v21 = [], v32 = []; var normal = vec3.create(); var getFromArray = function(arr, idx2, out) { var idx32 = idx2 * 3; out[0] = arr[idx32]; out[1] = arr[idx32 + 1]; out[2] = arr[idx32 + 2]; return out; }; var vertexNormals = new Float32Array(pointsArr.length); var vertexColors = new Float32Array(pointsArr.length / 3 * 4); for (var i = 0; i < data.count(); i++) { if (data.hasValue(i)) { var rgbaArr = graphicGL.parseColor(getItemVisualColor(data, i)); var opacity = getItemVisualOpacity(data, i); opacity != null && (rgbaArr[3] *= opacity); if (rgbaArr[3] < 0.99) { isTransparent = true; } for (var k = 0; k < 4; k++) { vertexColors[i * 4 + k] = rgbaArr[k]; } } } var farPoints = [1e7, 1e7, 1e7]; for (var i = 0; i < row - 1; i++) { for (var j = 0; j < column - 1; j++) { var dataIndex = i * (column - 1) + j; var vertexOffset = dataIndex * 4; getQuadIndices(i, j, quadIndices); var invisibleQuad = false; for (var k = 0; k < 4; k++) { getFromArray(pointsArr, quadIndices[k], pos); if (isPointsNaN(pos)) { invisibleQuad = true; } } for (var k = 0; k < 4; k++) { if (invisibleQuad) { positionAttr.set(vertexOffset + k, farPoints); } else { getFromArray(pointsArr, quadIndices[k], pos); positionAttr.set(vertexOffset + k, pos); } if (showWireframe) { barycentricAttr.set(vertexOffset + k, quadBarycentric[k]); } } for (var k = 0; k < 6; k++) { indices[faceOffset++] = quadToTriangle[k] + vertexOffset; } if (needsNormal && !invisibleQuad) { for (var k = 0; k < 2; k++) { var k3 = k * 3; for (var m = 0; m < 3; m++) { var idx = quadIndices[quadToTriangle[k3] + m]; getFromArray(pointsArr, idx, pts[m]); } vec3.sub(v21, pts[0], pts[1]); vec3.sub(v32, pts[1], pts[2]); vec3.cross(normal, v21, v32); for (var m = 0; m < 3; m++) { var idx3 = quadIndices[quadToTriangle[k3] + m] * 3; vertexNormals[idx3] = vertexNormals[idx3] + normal[0]; vertexNormals[idx3 + 1] = vertexNormals[idx3 + 1] + normal[1]; vertexNormals[idx3 + 2] = vertexNormals[idx3 + 2] + normal[2]; } } } } } if (needsNormal) { for (var i = 0; i < vertexNormals.length / 3; i++) { getFromArray(vertexNormals, i, normal); vec3.normalize(normal, normal); vertexNormals[i * 3] = normal[0]; vertexNormals[i * 3 + 1] = normal[1]; vertexNormals[i * 3 + 2] = normal[2]; } } var rgbaArr = []; var uvArr = []; for (var i = 0; i < row - 1; i++) { for (var j = 0; j < column - 1; j++) { var dataIndex = i * (column - 1) + j; var vertexOffset = dataIndex * 4; getQuadIndices(i, j, quadIndices); for (var k = 0; k < 4; k++) { for (var m = 0; m < 4; m++) { rgbaArr[m] = vertexColors[quadIndices[k] * 4 + m]; } colorAttr.set(vertexOffset + k, rgbaArr); if (needsNormal) { getFromArray(vertexNormals, quadIndices[k], normal); normalAttr.set(vertexOffset + k, normal); } var idx = quadIndices[k]; uvArr[0] = idx % column / (column - 1); uvArr[1] = Math.floor(idx / column) / (row - 1); texcoordAttr.set(vertexOffset + k, uvArr); } dataIndex++; } } } else { var uvArr = []; for (var i = 0; i < data.count(); i++) { uvArr[0] = i % column / (column - 1); uvArr[1] = Math.floor(i / column) / (row - 1); var rgbaArr = graphicGL.parseColor(getItemVisualColor(data, i)); var opacity = getItemVisualOpacity(data, i); opacity != null && (rgbaArr[3] *= opacity); if (rgbaArr[3] < 0.99) { isTransparent = true; } colorAttr.set(i, rgbaArr); texcoordAttr.set(i, uvArr); } var quadIndices = []; var cursor = 0; for (var i = 0; i < row - 1; i++) { for (var j = 0; j < column - 1; j++) { getQuadIndices(i, j, quadIndices); for (var k = 0; k < 6; k++) { indices[cursor++] = quadIndices[quadToTriangle[k]]; } } } if (needsNormal) { geometry.generateVertexNormals(); } else { normalAttr.value = null; } } if (surfaceMesh.material.get("normalMap")) { geometry.generateTangents(); } geometry.updateBoundingBox(); geometry.dirty(); surfaceMesh.material.transparent = isTransparent; surfaceMesh.material.depthMask = !isTransparent; }, _getDataShape: function(data, isParametric) { var prevX = -Infinity; var rowCount = 0; var columnCount = 0; var mayInvalid = false; var rowDim = isParametric ? "u" : "x"; var dataCount = data.count(); for (var i = 0; i < dataCount; i++) { var x = data.get(rowDim, i); if (x < prevX) { columnCount = 0; rowCount++; } prevX = x; columnCount++; } if (!rowCount || columnCount === 1) { mayInvalid = true; } if (!mayInvalid) { return [rowCount + 1, columnCount]; } var rows = Math.floor(Math.sqrt(dataCount)); while (rows > 0) { if (Math.floor(dataCount / rows) === dataCount / rows) { return [rows, dataCount / rows]; } rows--; } rows = Math.floor(Math.sqrt(dataCount)); return [rows, rows]; }, dispose: function() { this.groupGL.removeAll(); }, remove: function() { this.groupGL.removeAll(); } }); export { SurfaceView as default };