147 lines
4.5 KiB
JavaScript
147 lines
4.5 KiB
JavaScript
|
|
import * as echarts from 'echarts/lib/echarts';
|
||
|
|
import glmatrix from 'claygl/src/dep/glmatrix';
|
||
|
|
var vec3 = glmatrix.vec3;
|
||
|
|
var vec2 = glmatrix.vec2;
|
||
|
|
var normalize = vec3.normalize;
|
||
|
|
var cross = vec3.cross;
|
||
|
|
var sub = vec3.sub;
|
||
|
|
var add = vec3.add;
|
||
|
|
var create = vec3.create;
|
||
|
|
var normal = create();
|
||
|
|
var tangent = create();
|
||
|
|
var bitangent = create();
|
||
|
|
var halfVector = create();
|
||
|
|
var coord0 = [];
|
||
|
|
var coord1 = [];
|
||
|
|
|
||
|
|
function getCubicPointsOnGlobe(coords, coordSys) {
|
||
|
|
vec2.copy(coord0, coords[0]);
|
||
|
|
vec2.copy(coord1, coords[1]);
|
||
|
|
var pts = [];
|
||
|
|
var p0 = pts[0] = create();
|
||
|
|
var p1 = pts[1] = create();
|
||
|
|
var p2 = pts[2] = create();
|
||
|
|
var p3 = pts[3] = create();
|
||
|
|
coordSys.dataToPoint(coord0, p0);
|
||
|
|
coordSys.dataToPoint(coord1, p3); // Get p1
|
||
|
|
|
||
|
|
normalize(normal, p0); // TODO p0-p3 is parallel with normal
|
||
|
|
|
||
|
|
sub(tangent, p3, p0);
|
||
|
|
normalize(tangent, tangent);
|
||
|
|
cross(bitangent, tangent, normal);
|
||
|
|
normalize(bitangent, bitangent);
|
||
|
|
cross(tangent, normal, bitangent); // p1 is half vector of p0 and tangent on p0
|
||
|
|
|
||
|
|
add(p1, normal, tangent);
|
||
|
|
normalize(p1, p1); // Get p2
|
||
|
|
|
||
|
|
normalize(normal, p3);
|
||
|
|
sub(tangent, p0, p3);
|
||
|
|
normalize(tangent, tangent);
|
||
|
|
cross(bitangent, tangent, normal);
|
||
|
|
normalize(bitangent, bitangent);
|
||
|
|
cross(tangent, normal, bitangent); // p2 is half vector of p3 and tangent on p3
|
||
|
|
|
||
|
|
add(p2, normal, tangent);
|
||
|
|
normalize(p2, p2); // Project distance of p0 on halfVector
|
||
|
|
|
||
|
|
add(halfVector, p0, p3);
|
||
|
|
normalize(halfVector, halfVector);
|
||
|
|
var projDist = vec3.dot(p0, halfVector); // Angle of halfVector and p1
|
||
|
|
|
||
|
|
var cosTheta = vec3.dot(halfVector, p1);
|
||
|
|
var len = (Math.max(vec3.len(p0), vec3.len(p3)) - projDist) / cosTheta * 2;
|
||
|
|
vec3.scaleAndAdd(p1, p0, p1, len);
|
||
|
|
vec3.scaleAndAdd(p2, p3, p2, len);
|
||
|
|
return pts;
|
||
|
|
}
|
||
|
|
|
||
|
|
function getCubicPointsOnPlane(coords, coordSys, up) {
|
||
|
|
var pts = [];
|
||
|
|
var p0 = pts[0] = vec3.create();
|
||
|
|
var p1 = pts[1] = vec3.create();
|
||
|
|
var p2 = pts[2] = vec3.create();
|
||
|
|
var p3 = pts[3] = vec3.create();
|
||
|
|
coordSys.dataToPoint(coords[0], p0);
|
||
|
|
coordSys.dataToPoint(coords[1], p3);
|
||
|
|
var len = vec3.dist(p0, p3);
|
||
|
|
vec3.lerp(p1, p0, p3, 0.3);
|
||
|
|
vec3.lerp(p2, p0, p3, 0.3);
|
||
|
|
vec3.scaleAndAdd(p1, p1, up, Math.min(len * 0.1, 10));
|
||
|
|
vec3.scaleAndAdd(p2, p2, up, Math.min(len * 0.1, 10));
|
||
|
|
return pts;
|
||
|
|
}
|
||
|
|
|
||
|
|
function getPolylinePoints(coords, coordSys) {
|
||
|
|
var pts = new Float32Array(coords.length * 3);
|
||
|
|
var off = 0;
|
||
|
|
var pt = [];
|
||
|
|
|
||
|
|
for (var i = 0; i < coords.length; i++) {
|
||
|
|
coordSys.dataToPoint(coords[i], pt);
|
||
|
|
pts[off++] = pt[0];
|
||
|
|
pts[off++] = pt[1];
|
||
|
|
pts[off++] = pt[2];
|
||
|
|
}
|
||
|
|
|
||
|
|
return pts;
|
||
|
|
}
|
||
|
|
|
||
|
|
function prepareCoords(data) {
|
||
|
|
var coordsList = [];
|
||
|
|
data.each(function (idx) {
|
||
|
|
var itemModel = data.getItemModel(idx);
|
||
|
|
var coords = itemModel.option instanceof Array ? itemModel.option : itemModel.getShallow('coords', true);
|
||
|
|
|
||
|
|
if (process.env.NODE_ENV !== 'production') {
|
||
|
|
if (!(coords instanceof Array && coords.length > 0 && coords[0] instanceof Array)) {
|
||
|
|
throw new Error('Invalid coords ' + JSON.stringify(coords) + '. Lines must have 2d coords array in data item.');
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
coordsList.push(coords);
|
||
|
|
});
|
||
|
|
return {
|
||
|
|
coordsList: coordsList
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
function layoutGlobe(seriesModel, coordSys) {
|
||
|
|
var data = seriesModel.getData();
|
||
|
|
var isPolyline = seriesModel.get('polyline');
|
||
|
|
data.setLayout('lineType', isPolyline ? 'polyline' : 'cubicBezier');
|
||
|
|
var res = prepareCoords(data);
|
||
|
|
data.each(function (idx) {
|
||
|
|
var coords = res.coordsList[idx];
|
||
|
|
var getPointsMethod = isPolyline ? getPolylinePoints : getCubicPointsOnGlobe;
|
||
|
|
data.setItemLayout(idx, getPointsMethod(coords, coordSys));
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
function layoutOnPlane(seriesModel, coordSys, normal) {
|
||
|
|
var data = seriesModel.getData();
|
||
|
|
var isPolyline = seriesModel.get('polyline');
|
||
|
|
var res = prepareCoords(data);
|
||
|
|
data.setLayout('lineType', isPolyline ? 'polyline' : 'cubicBezier');
|
||
|
|
data.each(function (idx) {
|
||
|
|
var coords = res.coordsList[idx];
|
||
|
|
var pts = isPolyline ? getPolylinePoints(coords, coordSys) : getCubicPointsOnPlane(coords, coordSys, normal);
|
||
|
|
data.setItemLayout(idx, pts);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
export default function lines3DLayout(ecModel, api) {
|
||
|
|
ecModel.eachSeriesByType('lines3D', function (seriesModel) {
|
||
|
|
var coordSys = seriesModel.coordinateSystem;
|
||
|
|
|
||
|
|
if (coordSys.type === 'globe') {
|
||
|
|
layoutGlobe(seriesModel, coordSys);
|
||
|
|
} else if (coordSys.type === 'geo3D') {
|
||
|
|
layoutOnPlane(seriesModel, coordSys, [0, 1, 0]);
|
||
|
|
} else if (coordSys.type === 'mapbox3D' || coordSys.type === 'maptalks3D') {
|
||
|
|
layoutOnPlane(seriesModel, coordSys, [0, 0, 1]);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
;
|