cione-comp-lib
Version:
`$ yarn add cione-comp-lib` 或者 `$ npm i cione-comp-lib -S`
275 lines (274 loc) • 10.9 kB
JavaScript
import "../../../../echarts/lib/echarts.mjs";
import graphicGL from "../../util/graphicGL.mjs";
import retrieve from "../../util/retrieve.mjs";
import ViewGL from "../../core/ViewGL.mjs";
import VectorFieldParticleSurface from "./VectorFieldParticleSurface.mjs";
import ChartView from "../../../../echarts/lib/view/Chart.mjs";
var FlowGLView = ChartView.extend({
type: "flowGL",
__ecgl__: true,
init: function(ecModel, api) {
this.viewGL = new ViewGL("orthographic");
this.groupGL = new graphicGL.Node();
this.viewGL.add(this.groupGL);
this._particleSurface = new VectorFieldParticleSurface();
var planeMesh = new graphicGL.Mesh({
geometry: new graphicGL.PlaneGeometry(),
material: new graphicGL.Material({
shader: new graphicGL.Shader({
vertex: graphicGL.Shader.source("ecgl.color.vertex"),
fragment: graphicGL.Shader.source("ecgl.color.fragment")
}),
transparent: true
})
});
planeMesh.material.enableTexture("diffuseMap");
this.groupGL.add(planeMesh);
this._planeMesh = planeMesh;
},
render: function(seriesModel, ecModel, api) {
var particleSurface = this._particleSurface;
particleSurface.setParticleType(seriesModel.get("particleType"));
particleSurface.setSupersampling(seriesModel.get("supersampling"));
this._updateData(seriesModel, api);
this._updateCamera(api.getWidth(), api.getHeight(), api.getDevicePixelRatio());
var particleDensity = retrieve.firstNotNull(seriesModel.get("particleDensity"), 128);
particleSurface.setParticleDensity(particleDensity, particleDensity);
var planeMesh = this._planeMesh;
var time = +new Date();
var self = this;
var firstFrame = true;
planeMesh.__percent = 0;
planeMesh.stopAnimation();
planeMesh.animate("", {
loop: true
}).when(1e5, {
__percent: 1
}).during(function() {
var timeNow = +new Date();
var dTime = Math.min(timeNow - time, 20);
time = time + dTime;
if (self._renderer) {
particleSurface.update(self._renderer, api, dTime / 1e3, firstFrame);
planeMesh.material.set("diffuseMap", particleSurface.getSurfaceTexture());
}
firstFrame = false;
}).start();
var itemStyleModel = seriesModel.getModel("itemStyle");
var color = graphicGL.parseColor(itemStyleModel.get("color"));
color[3] *= retrieve.firstNotNull(itemStyleModel.get("opacity"), 1);
planeMesh.material.set("color", color);
particleSurface.setColorTextureImage(seriesModel.get("colorTexture"), api);
particleSurface.setParticleSize(seriesModel.get("particleSize"));
particleSurface.particleSpeedScaling = seriesModel.get("particleSpeed");
particleSurface.motionBlurFactor = 1 - Math.pow(0.1, seriesModel.get("particleTrail"));
},
updateTransform: function(seriesModel, ecModel, api) {
this._updateData(seriesModel, api);
},
afterRender: function(globeModel, ecModel, api, layerGL) {
var renderer = layerGL.renderer;
this._renderer = renderer;
},
_updateData: function(seriesModel, api) {
var coordSys = seriesModel.coordinateSystem;
var dims = coordSys.dimensions.map(function(coordDim) {
return seriesModel.coordDimToDataDim(coordDim)[0];
});
var data = seriesModel.getData();
var xExtent = data.getDataExtent(dims[0]);
var yExtent = data.getDataExtent(dims[1]);
var gridWidth = seriesModel.get("gridWidth");
var gridHeight = seriesModel.get("gridHeight");
if (gridWidth == null || gridWidth === "auto") {
var aspect = (xExtent[1] - xExtent[0]) / (yExtent[1] - yExtent[0]);
gridWidth = Math.round(Math.sqrt(aspect * data.count()));
}
if (gridHeight == null || gridHeight === "auto") {
gridHeight = Math.ceil(data.count() / gridWidth);
}
var vectorFieldTexture = this._particleSurface.vectorFieldTexture;
var pixels = vectorFieldTexture.pixels;
if (!pixels || pixels.length !== gridHeight * gridWidth * 4) {
pixels = vectorFieldTexture.pixels = new Float32Array(gridWidth * gridHeight * 4);
} else {
for (var i = 0; i < pixels.length; i++) {
pixels[i] = 0;
}
}
var maxMag = 0;
var minMag = Infinity;
var points = new Float32Array(data.count() * 2);
var offset = 0;
var bbox = [[Infinity, Infinity], [-Infinity, -Infinity]];
data.each([dims[0], dims[1], "vx", "vy"], function(x, y, vx, vy) {
var pt = coordSys.dataToPoint([x, y]);
points[offset++] = pt[0];
points[offset++] = pt[1];
bbox[0][0] = Math.min(pt[0], bbox[0][0]);
bbox[0][1] = Math.min(pt[1], bbox[0][1]);
bbox[1][0] = Math.max(pt[0], bbox[1][0]);
bbox[1][1] = Math.max(pt[1], bbox[1][1]);
var mag = Math.sqrt(vx * vx + vy * vy);
maxMag = Math.max(maxMag, mag);
minMag = Math.min(minMag, mag);
});
data.each(["vx", "vy"], function(vx, vy, i2) {
var xPix = Math.round((points[i2 * 2] - bbox[0][0]) / (bbox[1][0] - bbox[0][0]) * (gridWidth - 1));
var yPix = gridHeight - 1 - Math.round((points[i2 * 2 + 1] - bbox[0][1]) / (bbox[1][1] - bbox[0][1]) * (gridHeight - 1));
var idx = (yPix * gridWidth + xPix) * 4;
pixels[idx] = vx / maxMag * 0.5 + 0.5;
pixels[idx + 1] = vy / maxMag * 0.5 + 0.5;
pixels[idx + 3] = 1;
});
vectorFieldTexture.width = gridWidth;
vectorFieldTexture.height = gridHeight;
if (seriesModel.get("coordinateSystem") === "bmap") {
this._fillEmptyPixels(vectorFieldTexture);
}
vectorFieldTexture.dirty();
this._updatePlanePosition(bbox[0], bbox[1], seriesModel, api);
this._updateGradientTexture(data.getVisual("visualMeta"), [minMag, maxMag]);
},
_fillEmptyPixels: function(texture) {
var pixels = texture.pixels;
var width = texture.width;
var height = texture.height;
function fetchPixel(x2, y2, rg) {
x2 = Math.max(Math.min(x2, width - 1), 0);
y2 = Math.max(Math.min(y2, height - 1), 0);
var idx2 = (y2 * (width - 1) + x2) * 4;
if (pixels[idx2 + 3] === 0) {
return false;
}
rg[0] = pixels[idx2];
rg[1] = pixels[idx2 + 1];
return true;
}
function addPixel(a, b, out) {
out[0] = a[0] + b[0];
out[1] = a[1] + b[1];
}
var center = [], left = [], right = [], top = [], bottom = [];
var weight = 0;
for (var y = 0; y < height; y++) {
for (var x = 0; x < width; x++) {
var idx = (y * (width - 1) + x) * 4;
if (pixels[idx + 3] === 0) {
weight = center[0] = center[1] = 0;
if (fetchPixel(x - 1, y, left)) {
weight++;
addPixel(left, center, center);
}
if (fetchPixel(x + 1, y, right)) {
weight++;
addPixel(right, center, center);
}
if (fetchPixel(x, y - 1, top)) {
weight++;
addPixel(top, center, center);
}
if (fetchPixel(x, y + 1, bottom)) {
weight++;
addPixel(bottom, center, center);
}
center[0] /= weight;
center[1] /= weight;
pixels[idx] = center[0];
pixels[idx + 1] = center[1];
}
pixels[idx + 3] = 1;
}
}
},
_updateGradientTexture: function(visualMeta, magExtent) {
if (!visualMeta || !visualMeta.length) {
this._particleSurface.setGradientTexture(null);
return;
}
this._gradientTexture = this._gradientTexture || new graphicGL.Texture2D({
image: document.createElement("canvas")
});
var gradientTexture = this._gradientTexture;
var canvas = gradientTexture.image;
canvas.width = 200;
canvas.height = 1;
var ctx = canvas.getContext("2d");
var gradient = ctx.createLinearGradient(0, 0.5, canvas.width, 0.5);
visualMeta[0].stops.forEach(function(stop) {
var offset;
if (magExtent[1] === magExtent[0]) {
offset = 0;
} else {
offset = stop.value / magExtent[1];
offset = Math.min(Math.max(offset, 0), 1);
}
gradient.addColorStop(offset, stop.color);
});
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, canvas.width, canvas.height);
gradientTexture.dirty();
this._particleSurface.setGradientTexture(this._gradientTexture);
},
_updatePlanePosition: function(leftTop, rightBottom, seriesModel, api) {
var limitedResult = this._limitInViewportAndFullFill(leftTop, rightBottom, seriesModel, api);
leftTop = limitedResult.leftTop;
rightBottom = limitedResult.rightBottom;
this._particleSurface.setRegion(limitedResult.region);
this._planeMesh.position.set((leftTop[0] + rightBottom[0]) / 2, api.getHeight() - (leftTop[1] + rightBottom[1]) / 2, 0);
var width = rightBottom[0] - leftTop[0];
var height = rightBottom[1] - leftTop[1];
this._planeMesh.scale.set(width / 2, height / 2, 1);
this._particleSurface.resize(Math.max(Math.min(width, 2048), 1), Math.max(Math.min(height, 2048), 1));
if (this._renderer) {
this._particleSurface.clearFrame(this._renderer);
}
},
_limitInViewportAndFullFill: function(leftTop, rightBottom, seriesModel, api) {
var newLeftTop = [Math.max(leftTop[0], 0), Math.max(leftTop[1], 0)];
var newRightBottom = [Math.min(rightBottom[0], api.getWidth()), Math.min(rightBottom[1], api.getHeight())];
if (seriesModel.get("coordinateSystem") === "bmap") {
var lngRange = seriesModel.getData().getDataExtent(seriesModel.coordDimToDataDim("lng")[0]);
var isContinuous = Math.floor(lngRange[1] - lngRange[0]) >= 359;
if (isContinuous) {
if (newLeftTop[0] > 0) {
newLeftTop[0] = 0;
}
if (newRightBottom[0] < api.getWidth()) {
newRightBottom[0] = api.getWidth();
}
}
}
var width = rightBottom[0] - leftTop[0];
var height = rightBottom[1] - leftTop[1];
var newWidth = newRightBottom[0] - newLeftTop[0];
var newHeight = newRightBottom[1] - newLeftTop[1];
var region = [(newLeftTop[0] - leftTop[0]) / width, 1 - newHeight / height - (newLeftTop[1] - leftTop[1]) / height, newWidth / width, newHeight / height];
return {
leftTop: newLeftTop,
rightBottom: newRightBottom,
region
};
},
_updateCamera: function(width, height, dpr) {
this.viewGL.setViewport(0, 0, width, height, dpr);
var camera = this.viewGL.camera;
camera.left = camera.bottom = 0;
camera.top = height;
camera.right = width;
camera.near = 0;
camera.far = 100;
camera.position.z = 10;
},
remove: function() {
this._planeMesh.stopAnimation();
this.groupGL.removeAll();
},
dispose: function() {
if (this._renderer) {
this._particleSurface.dispose(this._renderer);
}
this.groupGL.removeAll();
}
});
export { FlowGLView as default };