cione-comp-lib
Version:
`$ yarn add cione-comp-lib` 或者 `$ npm i cione-comp-lib -S`
421 lines (420 loc) • 15.6 kB
JavaScript
import "../../../../echarts/lib/echarts.mjs";
import graphicGL from "../../util/graphicGL.mjs";
import spriteUtil from "../../util/sprite.mjs";
import PointsMesh from "./PointsMesh.mjs";
import LabelsBuilder from "../../component/common/LabelsBuilder.mjs";
import Matrix4 from "../../../../claygl/src/math/Matrix4.mjs";
import retrieve from "../../util/retrieve.mjs";
import { getVisualColor, getVisualOpacity, getItemVisualColor, getItemVisualOpacity } from "../../util/visual.mjs";
import { lift } from "../../../../zrender/lib/tool/color.mjs";
var SDF_RANGE = 20;
var Z_2D = -10;
function isSymbolSizeSame(a, b) {
return a && b && a[0] === b[0] && a[1] === b[1];
}
function PointsBuilder(is2D, api) {
this.rootNode = new graphicGL.Node();
this.is2D = is2D;
this._labelsBuilder = new LabelsBuilder(256, 256, api);
this._labelsBuilder.getMesh().renderOrder = 100;
this.rootNode.add(this._labelsBuilder.getMesh());
this._api = api;
this._spriteImageCanvas = document.createElement("canvas");
this._startDataIndex = 0;
this._endDataIndex = 0;
this._sizeScale = 1;
}
PointsBuilder.prototype = {
constructor: PointsBuilder,
highlightOnMouseover: true,
update: function(seriesModel, ecModel, api, start, end) {
var tmp = this._prevMesh;
this._prevMesh = this._mesh;
this._mesh = tmp;
var data = seriesModel.getData();
if (start == null) {
start = 0;
}
if (end == null) {
end = data.count();
}
this._startDataIndex = start;
this._endDataIndex = end - 1;
if (!this._mesh) {
var material = this._prevMesh && this._prevMesh.material;
this._mesh = new PointsMesh({
renderOrder: 10,
frustumCulling: false
});
if (material) {
this._mesh.material = material;
}
}
var material = this._mesh.material;
var geometry = this._mesh.geometry;
var attributes = geometry.attributes;
this.rootNode.remove(this._prevMesh);
this.rootNode.add(this._mesh);
this._setPositionTextureToMesh(this._mesh, this._positionTexture);
var symbolInfo = this._getSymbolInfo(seriesModel, start, end);
var dpr = api.getDevicePixelRatio();
var itemStyle = seriesModel.getModel("itemStyle").getItemStyle();
var largeMode = seriesModel.get("large");
var pointSizeScale = 1;
if (symbolInfo.maxSize > 2) {
pointSizeScale = this._updateSymbolSprite(seriesModel, itemStyle, symbolInfo, dpr);
material.enableTexture("sprite");
} else {
material.disableTexture("sprite");
}
attributes.position.init(end - start);
var rgbaArr = [];
if (largeMode) {
material.undefine("VERTEX_SIZE");
material.undefine("VERTEX_COLOR");
var color = getVisualColor(data);
var opacity = getVisualOpacity(data);
graphicGL.parseColor(color, rgbaArr);
rgbaArr[3] *= opacity;
material.set({
color: rgbaArr,
"u_Size": symbolInfo.maxSize * this._sizeScale
});
} else {
material.set({
color: [1, 1, 1, 1]
});
material.define("VERTEX_SIZE");
material.define("VERTEX_COLOR");
attributes.size.init(end - start);
attributes.color.init(end - start);
this._originalOpacity = new Float32Array(end - start);
}
var points = data.getLayout("points");
var positionArr = attributes.position.value;
for (var i = 0; i < end - start; i++) {
var i3 = i * 3;
var i2 = i * 2;
if (this.is2D) {
positionArr[i3] = points[i2];
positionArr[i3 + 1] = points[i2 + 1];
positionArr[i3 + 2] = Z_2D;
} else {
positionArr[i3] = points[i3];
positionArr[i3 + 1] = points[i3 + 1];
positionArr[i3 + 2] = points[i3 + 2];
}
if (!largeMode) {
var color = getItemVisualColor(data, i);
var opacity = getItemVisualOpacity(data, i);
graphicGL.parseColor(color, rgbaArr);
rgbaArr[3] *= opacity;
attributes.color.set(i, rgbaArr);
if (rgbaArr[3] < 0.99)
;
var symbolSize = data.getItemVisual(i, "symbolSize");
symbolSize = symbolSize instanceof Array ? Math.max(symbolSize[0], symbolSize[1]) : symbolSize;
if (isNaN(symbolSize)) {
symbolSize = 0;
}
attributes.size.value[i] = symbolSize * pointSizeScale * this._sizeScale;
this._originalOpacity[i] = rgbaArr[3];
}
}
this._mesh.sizeScale = pointSizeScale;
geometry.updateBoundingBox();
geometry.dirty();
this._updateMaterial(seriesModel, itemStyle);
var coordSys = seriesModel.coordinateSystem;
if (coordSys && coordSys.viewGL) {
var methodName = coordSys.viewGL.isLinearSpace() ? "define" : "undefine";
material[methodName]("fragment", "SRGB_DECODE");
}
if (!largeMode) {
this._updateLabelBuilder(seriesModel, start, end);
}
this._updateHandler(seriesModel, ecModel, api);
this._updateAnimation(seriesModel);
this._api = api;
},
getPointsMesh: function() {
return this._mesh;
},
updateLabels: function(highlightDataIndices) {
this._labelsBuilder.updateLabels(highlightDataIndices);
},
hideLabels: function() {
this.rootNode.remove(this._labelsBuilder.getMesh());
},
showLabels: function() {
this.rootNode.add(this._labelsBuilder.getMesh());
},
dispose: function() {
this._labelsBuilder.dispose();
},
_updateSymbolSprite: function(seriesModel, itemStyle, symbolInfo, dpr) {
symbolInfo.maxSize = Math.min(symbolInfo.maxSize * 2, 200);
var symbolSize = [];
if (symbolInfo.aspect > 1) {
symbolSize[0] = symbolInfo.maxSize;
symbolSize[1] = symbolInfo.maxSize / symbolInfo.aspect;
} else {
symbolSize[1] = symbolInfo.maxSize;
symbolSize[0] = symbolInfo.maxSize * symbolInfo.aspect;
}
symbolSize[0] = symbolSize[0] || 1;
symbolSize[1] = symbolSize[1] || 1;
if (this._symbolType !== symbolInfo.type || !isSymbolSizeSame(this._symbolSize, symbolSize) || this._lineWidth !== itemStyle.lineWidth) {
spriteUtil.createSymbolSprite(symbolInfo.type, symbolSize, {
fill: "#fff",
lineWidth: itemStyle.lineWidth,
stroke: "transparent",
shadowColor: "transparent",
minMargin: Math.min(symbolSize[0] / 2, 10)
}, this._spriteImageCanvas);
spriteUtil.createSDFFromCanvas(this._spriteImageCanvas, Math.min(this._spriteImageCanvas.width, 32), SDF_RANGE, this._mesh.material.get("sprite").image);
this._symbolType = symbolInfo.type;
this._symbolSize = symbolSize;
this._lineWidth = itemStyle.lineWidth;
}
return this._spriteImageCanvas.width / symbolInfo.maxSize * dpr;
},
_updateMaterial: function(seriesModel, itemStyle) {
var blendFunc = seriesModel.get("blendMode") === "lighter" ? graphicGL.additiveBlend : null;
var material = this._mesh.material;
material.blend = blendFunc;
material.set("lineWidth", itemStyle.lineWidth / SDF_RANGE);
var strokeColor = graphicGL.parseColor(itemStyle.stroke);
material.set("strokeColor", strokeColor);
material.transparent = true;
material.depthMask = false;
material.depthTest = !this.is2D;
material.sortVertices = !this.is2D;
},
_updateLabelBuilder: function(seriesModel, start, end) {
var data = seriesModel.getData();
var geometry = this._mesh.geometry;
var positionArr = geometry.attributes.position.value;
var start = this._startDataIndex;
var pointSizeScale = this._mesh.sizeScale;
this._labelsBuilder.updateData(data, start, end);
this._labelsBuilder.getLabelPosition = function(dataIndex, positionDesc, distance) {
var idx3 = (dataIndex - start) * 3;
return [positionArr[idx3], positionArr[idx3 + 1], positionArr[idx3 + 2]];
};
this._labelsBuilder.getLabelDistance = function(dataIndex, positionDesc, distance) {
var size = geometry.attributes.size.get(dataIndex - start) / pointSizeScale;
return size / 2 + distance;
};
this._labelsBuilder.updateLabels();
},
_updateAnimation: function(seriesModel) {
graphicGL.updateVertexAnimation([["prevPosition", "position"], ["prevSize", "size"]], this._prevMesh, this._mesh, seriesModel);
},
_updateHandler: function(seriesModel, ecModel, api) {
var data = seriesModel.getData();
var pointsMesh = this._mesh;
var self = this;
var lastDataIndex = -1;
var isCartesian3D = seriesModel.coordinateSystem && seriesModel.coordinateSystem.type === "cartesian3D";
var grid3DModel;
if (isCartesian3D) {
grid3DModel = seriesModel.coordinateSystem.model;
}
pointsMesh.seriesIndex = seriesModel.seriesIndex;
pointsMesh.off("mousemove");
pointsMesh.off("mouseout");
pointsMesh.on("mousemove", function(e) {
var dataIndex = e.vertexIndex + self._startDataIndex;
if (dataIndex !== lastDataIndex) {
if (this.highlightOnMouseover) {
this.downplay(data, lastDataIndex);
this.highlight(data, dataIndex);
this._labelsBuilder.updateLabels([dataIndex]);
}
if (isCartesian3D) {
api.dispatchAction({
type: "grid3DShowAxisPointer",
value: [data.get(seriesModel.coordDimToDataDim("x")[0], dataIndex), data.get(seriesModel.coordDimToDataDim("y")[0], dataIndex), data.get(seriesModel.coordDimToDataDim("z")[0], dataIndex)],
grid3DIndex: grid3DModel.componentIndex
});
}
}
pointsMesh.dataIndex = dataIndex;
lastDataIndex = dataIndex;
}, this);
pointsMesh.on("mouseout", function(e) {
var dataIndex = e.vertexIndex + self._startDataIndex;
if (this.highlightOnMouseover) {
this.downplay(data, dataIndex);
this._labelsBuilder.updateLabels();
}
lastDataIndex = -1;
pointsMesh.dataIndex = -1;
if (isCartesian3D) {
api.dispatchAction({
type: "grid3DHideAxisPointer",
grid3DIndex: grid3DModel.componentIndex
});
}
}, this);
},
updateLayout: function(seriesModel, ecModel, api) {
var data = seriesModel.getData();
if (!this._mesh) {
return;
}
var positionArr = this._mesh.geometry.attributes.position.value;
var points = data.getLayout("points");
if (this.is2D) {
for (var i = 0; i < points.length / 2; i++) {
var i3 = i * 3;
var i2 = i * 2;
positionArr[i3] = points[i2];
positionArr[i3 + 1] = points[i2 + 1];
positionArr[i3 + 2] = Z_2D;
}
} else {
for (var i = 0; i < points.length; i++) {
positionArr[i] = points[i];
}
}
this._mesh.geometry.dirty();
api.getZr().refresh();
},
updateView: function(camera) {
if (!this._mesh) {
return;
}
var worldViewProjection = new Matrix4();
Matrix4.mul(worldViewProjection, camera.viewMatrix, this._mesh.worldTransform);
Matrix4.mul(worldViewProjection, camera.projectionMatrix, worldViewProjection);
this._mesh.updateNDCPosition(worldViewProjection, this.is2D, this._api);
},
highlight: function(data, dataIndex) {
if (dataIndex > this._endDataIndex || dataIndex < this._startDataIndex) {
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 = lift(color, -0.4);
}
if (emphasisOpacity == null) {
emphasisOpacity = getItemVisualOpacity(data, dataIndex);
}
var colorArr = graphicGL.parseColor(emphasisColor);
colorArr[3] *= emphasisOpacity;
this._mesh.geometry.attributes.color.set(dataIndex - this._startDataIndex, colorArr);
this._mesh.geometry.dirtyAttribute("color");
this._api.getZr().refresh();
},
downplay: function(data, dataIndex) {
if (dataIndex > this._endDataIndex || dataIndex < this._startDataIndex) {
return;
}
var color = getItemVisualColor(data, dataIndex);
var opacity = getItemVisualOpacity(data, dataIndex);
var colorArr = graphicGL.parseColor(color);
colorArr[3] *= opacity;
this._mesh.geometry.attributes.color.set(dataIndex - this._startDataIndex, colorArr);
this._mesh.geometry.dirtyAttribute("color");
this._api.getZr().refresh();
},
fadeOutAll: function(fadeOutPercent) {
if (this._originalOpacity) {
var geo = this._mesh.geometry;
for (var i = 0; i < geo.vertexCount; i++) {
var fadeOutOpacity = this._originalOpacity[i] * fadeOutPercent;
geo.attributes.color.value[i * 4 + 3] = fadeOutOpacity;
}
geo.dirtyAttribute("color");
this._api.getZr().refresh();
}
},
fadeInAll: function() {
this.fadeOutAll(1);
},
setPositionTexture: function(texture) {
if (this._mesh) {
this._setPositionTextureToMesh(this._mesh, texture);
}
this._positionTexture = texture;
},
removePositionTexture: function() {
this._positionTexture = null;
if (this._mesh) {
this._setPositionTextureToMesh(this._mesh, null);
}
},
setSizeScale: function(sizeScale) {
if (sizeScale !== this._sizeScale) {
if (this._mesh) {
var originalSize = this._mesh.material.get("u_Size");
this._mesh.material.set("u_Size", originalSize / this._sizeScale * sizeScale);
var attributes = this._mesh.geometry.attributes;
if (attributes.size.value) {
for (var i = 0; i < attributes.size.value.length; i++) {
attributes.size.value[i] = attributes.size.value[i] / this._sizeScale * sizeScale;
}
}
}
this._sizeScale = sizeScale;
}
},
_setPositionTextureToMesh: function(mesh, texture) {
if (texture) {
mesh.material.set("positionTexture", texture);
}
mesh.material[texture ? "enableTexture" : "disableTexture"]("positionTexture");
},
_getSymbolInfo: function(seriesModel, start, end) {
if (seriesModel.get("large")) {
var symbolSize = retrieve.firstNotNull(seriesModel.get("symbolSize"), 1);
var maxSymbolSize;
var symbolAspect;
if (symbolSize instanceof Array) {
maxSymbolSize = Math.max(symbolSize[0], symbolSize[1]);
symbolAspect = symbolSize[0] / symbolSize[1];
} else {
maxSymbolSize = symbolSize;
symbolAspect = 1;
}
return {
maxSize: symbolSize,
type: seriesModel.get("symbol"),
aspect: symbolAspect
};
}
var data = seriesModel.getData();
var symbolAspect;
var symbolType = data.getItemVisual(0, "symbol") || "circle";
var maxSymbolSize = 0;
for (var idx = start; idx < end; idx++) {
var symbolSize = data.getItemVisual(idx, "symbolSize");
var currentSymbolType = data.getItemVisual(idx, "symbol");
var currentSymbolAspect;
if (!(symbolSize instanceof Array)) {
if (isNaN(symbolSize)) {
continue;
}
currentSymbolAspect = 1;
maxSymbolSize = Math.max(symbolSize, maxSymbolSize);
} else {
currentSymbolAspect = symbolSize[0] / symbolSize[1];
maxSymbolSize = Math.max(Math.max(symbolSize[0], symbolSize[1]), maxSymbolSize);
}
symbolType = currentSymbolType;
symbolAspect = currentSymbolAspect;
}
return {
maxSize: maxSymbolSize,
type: symbolType,
aspect: symbolAspect
};
}
};
export { PointsBuilder as default };