fix(meshline):修复动画流动方向颠倒的问题

This commit is contained in:
李正学 2018-12-24 10:48:03 +08:00
parent bb342b5094
commit c968530976
53 changed files with 1323 additions and 221 deletions

View File

@ -35,7 +35,9 @@ const scene = new L7.Scene({
mapStyle: 'light', // 样式URL
center: [ 120.19382669582967, 30.258134 ],
pitch: 0,
zoom: 12
zoom: 12,
maxZoom:14,
minZoom:11,
});
window.scene = scene;
scene.on('loaded', () => {
@ -53,7 +55,7 @@ scene.on('loaded', () => {
.shape('2d:circle')
.size('value', [ 2, 80]) // default 1
//.size('value', [ 10, 300]) // default 1
.active(true)
.active(false)
.filter('value', field_8 => {
return field_8 * 1 > 500;
})
@ -65,7 +67,9 @@ scene.on('loaded', () => {
})
.render();
circleLayer.on('click',(e)=>{
console.log(e);
})
});
});

View File

@ -38,7 +38,7 @@ scene.on('loaded', () => {
x: 'j',
y: 'w',
})
.shape('3d:circle')
.shape('cylinder')
.size('t',(level)=> {
return [2,2,(level*3+20)];
})

View File

@ -29,8 +29,6 @@ const scene = new L7.Scene({
scene.on('loaded', () => {
scene.image.addImage('local', 'https://gw.alipayobjects.com/zos/rmsportal/xZXhTxbglnuTmZEwqQrE.png');
$.getJSON('https://gw.alipayobjects.com/os/rmsportal/UpapMomPYUeiBjbHNAma.json', region => {
const color = [ 'rgb(22,32,101)', 'rgb(28,43,127)', 'rgb(36,68,142)', 'rgb(45,94,158)', 'rgb(53,119,174)', 'rgb(61,145,190)', 'rgb(70,170,206)', 'rgb(98,190,210)', 'rgb(138,205,206)', 'rgb(179,221,204)', 'rgb(220,236,201)' ];
var points = region.features.map((feature)=>{
@ -70,7 +68,7 @@ $.getJSON('https://gw.alipayobjects.com/os/rmsportal/dzpMOiLYBKxpdmsgBLoE.json',
.size([ 1.5, 0 ])
.shape('line')
.style({
// 'lineType':'solid'
'lineType':'dash'
})
.render();
});

View File

@ -86,6 +86,7 @@ scene.on('loaded', () => {
opacity: 1
})
.render();
/**
const citylayer2 = scene.PolygonLayer()
.source(city)
.shape('line')
@ -93,7 +94,8 @@ scene.on('loaded', () => {
.style({
opacity: 0.1
})
.render();
// .render();
**/
citylayer.on('click',(e)=>{
$("#info").css({'left': e.pixel.x,'top':e.pixel.y, display:'block'});

View File

@ -35,7 +35,7 @@ scene.on('loaded', () => {
})
.source(data)
.shape('name', 'text')
.size(10) // default 1
.size(12) // default 1
.color('#fff')
.style({
stroke: '#999',

View File

@ -38,6 +38,7 @@ scene.on('loaded', () => {
})
.source(data)
.shape('fill')
.active({fill:'blue'})
.color('rgb(79,174,234)')
.render();
});
@ -71,10 +72,12 @@ scene.on('loaded', () => {
.size('floor',[10,2000])
.color('rgba(242,246,250,0.96)')
.render();
/**
citylayer.on('click',(e)=>{
$("#info").css({'left': e.pixel.x,'top':e.pixel.y, display:'block'});
$("#info").html(`<p>楼高<span">${e.feature.properties.floor || 0}</span></p>`);
})
**/
});
});

85
demos/line.html Normal file

File diff suppressed because one or more lines are too long

57
demos/point.html Normal file
View File

@ -0,0 +1,57 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<meta name="geometry" content="diagram">
<link rel="stylesheet" href="./assets/common.css">
<title>point_circle</title>
<style>
#map { position:absolute; top:0; bottom:0; width:100%; }
</style>
</head>
<body>
<div id="map"></div>
<script src="https://webapi.amap.com/maps?v=1.4.8&key=15cd8a57710d40c9b7c0e3cc120f1200&plugin=Map3D"></script>
<script src="./assets/jquery-3.2.1.min.js"></script>
<script src="./assets/dat.gui.min.js"></script>
<script src="../build/L7.js"></script>
<script>
const scene = new L7.Scene({
id: 'map',
mapStyle: 'dark', // 样式URL
center: [104.838088,34.075889 ],
pitch: 0,
zoom: 9,
minZoom: 8,
rotation:0
});
scene.on('loaded', () => {
scene.image.addImage('local', 'https://gw.alipayobjects.com/zos/rmsportal/xZXhTxbglnuTmZEwqQrE.png');
$.get('https://gw.alipayobjects.com/os/rmsportal/oVTMqfzuuRFKiDwhPSFL.json', data => {
scene.PointLayer({
zIndex: 2
})
.source(data.list, {
type: 'array',
x: 'j',
y: 'w',
})
.shape('m','text')
.size(20)
.color('#fff')
.style({
stroke:'#fff',
strokeWidth:1.0,
opacity:1.0,
})
.render();
});
});
</script>
</body>
</html>

View File

@ -41,7 +41,7 @@ scene.on('loaded', () => {
.animate({enable:true})
.render();
});
$.get('./data/2.geojson', data => {
$.get('https://gw.alipayobjects.com/os/rmsportal/vmvAxgsEwbpoSWbSYvix.json', data => {
buildLayer = scene.PolygonLayer({
zIndex: 2
})

View File

@ -1,6 +1,6 @@
{
"name": "@antv/l7",
"version": "1.0.0",
"version": "1.0.1",
"description": "Large-scale WebGL-powered Geospatial Data Visualization",
"main": "build/l7.js",
"browser": "build/l7.js",

View File

@ -48,12 +48,12 @@ class Picking {
// return;
// }
const point = { x: event.clientX, y: event.clientY };
const point = { x: event.clientX, y: event.clientY,type:event.type};
const normalisedPoint = { x: 0, y: 0 };
normalisedPoint.x = (point.x / this._width) * 2 - 1;
normalisedPoint.y = -(point.y / this._height) * 2 + 1;
this._pick(point, normalisedPoint);
this._pickAllObject(point, normalisedPoint);
// this._pick(point, normalisedPoint);
}
_onWorldMove() {
@ -76,10 +76,10 @@ class Picking {
_update(point) {
const texture = this._pickingTexture;
if (this._needUpdate) {
// if (this._needUpdate) {
this._renderer.render(this._pickingScene, this._camera, this._pickingTexture);
this._needUpdate = false;
}
// this._needUpdate = false;
// }
this.pixelBuffer = new Uint8Array(4);
this._renderer.readRenderTargetPixels(texture, point.x, this._height - point.y, 1, 1, this.pixelBuffer);
@ -99,17 +99,33 @@ class Picking {
});
}
_filterObject(id) {
this._pickingScene.children.forEach((object, index) => {
index === id ? object.visible = true : object.visible = false;
});
}
_pickAllObject(point, normalisedPoint) {
this._pickingScene.children.forEach((object, index) => {
this._filterObject(index);
const item = this._pick(point, normalisedPoint, object.name);
item.type = point.type;
this._world.emit('pick', item);
this._world.emit('pick-' + object.name, item);
});
}
_updateRender() {
this._renderer.render(this._pickingScene, this._camera, this._pickingTexture);
}
_pick(point, normalisedPoint) {
_pick(point, normalisedPoint, layerId) {
this._update(point);
// Interpret the pixel as an ID
const id = (this.pixelBuffer[2] * 255 * 255) + (this.pixelBuffer[1] * 255) + (this.pixelBuffer[0]);
let id = (this.pixelBuffer[2] * 255 * 255) + (this.pixelBuffer[1] * 255) + (this.pixelBuffer[0]);
// Skip if ID is 16646655 (white) as the background returns this
if (id === 16646655 || this.pixelBuffer[3] === 0) {
return;
id = -999;
// return;
}
this._raycaster.setFromCamera(normalisedPoint, this._camera);
@ -118,7 +134,6 @@ class Picking {
//
// TODO: Only perform intersection test on the relevant picking mesh
const intersects = this._raycaster.intersectObjects(this._pickingScene.children, true);
const _point2d = { x: point.x, y: point.y };
let _point3d;
@ -131,13 +146,14 @@ class Picking {
//
// TODO: Look into the leak potential for passing so much by reference here
const item = {
layerId,
featureId: id - 1,
point2d: _point2d,
point3d: _point3d,
intersects
};
this._world.emit('pick', item);
// this._world.emit('pick-' + id, _point2d, _point3d, intersects);
return item
}
// Add mesh to picking scene

View File

@ -5,30 +5,32 @@ import { getImage } from '../util/ajax';
export default class LoadImage extends EventEmitter {
constructor() {
super();
this.imageWidth = 64;
const pixelRatio = window.devicePixelRatio || 1;
this.imageWidth = 64 * pixelRatio;
this.canvas = document.createElement('canvas');
this.canvas.style.cssText += 'height: 512px;width: 512px;';
this.canvas.width = this.imageWidth * 8;
this.canvas.height = this.imageWidth * 8;
this.ctx = this.canvas.getContext('2d');
this.images = [];
this.imagesCount = 0;
this.imagePos = {};
this.imagesIds = [];
}
addImage(id, opt) {
this.imagesCount ++;
this.imagesIds.push(id);
const imageCount = this.imagesCount;
const x = imageCount % 8 * 64;
const y = parseInt(imageCount / 8) * 64;
this.imagePos[id] = { x: x / 512, y: y / 512 };
const x = imageCount % 8 * this.imageWidth;
const y = parseInt(imageCount / 8) * this.imageWidth;
this.imagePos[id] = { x: x / this.canvas.width, y: y / this.canvas.height };
this.texture = new THREE.Texture(this.canvas);
if (typeof opt === 'string') {
getImage({ url: opt }, (err, img) => {
img.id = id;
this.images.push(img);
this.ctx.drawImage(img, x, y, 64, 64);
this.ctx.drawImage(img, x, y, this.imageWidth, this.imageWidth);
this.texture.magFilter = THREE.LinearFilter;
this.texture.minFilter = THREE.LinearFilter;
@ -47,7 +49,7 @@ export default class LoadImage extends EventEmitter {
image.data = data;
image.id = id;
this.images.push(image);
this.ctx.drawImage(image, x, y, 64, 64);
this.ctx.drawImage(image, x, y, this.imageWidth, this.imageWidth);
this.texture = new THREE.CanvasTexture(this.canvas);
this.imagePos[id] = { x: x >> 9, y: y >> 9 };
if (this.images.length === this.imagesCount) {

View File

@ -36,9 +36,10 @@ export default class Layer extends Base {
attrs: {},
// 样式配置项
styleOptions: {
stroke: [ 1.0, 1.0, 1.0, 1.0 ],
stroke: 'none',
strokeWidth: 1.0,
opacity: 1.0,
strokeOpacity: 1.0,
texture: false
},
// 选中时的配置项
@ -63,31 +64,35 @@ export default class Layer extends Base {
this._activeIds = null;
scene._engine._scene.add(this._object3D);
this.layerMesh = null;
this.layerLineMesh = null;
}
/**
* 将图层添加加到 Object
* @param {*} object three 物体
* @param {*} type mesh类型是区别是填充还是边线
*/
add(object) {
this.layerMesh = object;
add(object, type = 'fill') {
type === 'fill' ? this.layerMesh = object : this.layerLineMesh = object;
this._visibleWithZoom();
this.scene.on('zoomchange', () => {
this._visibleWithZoom();
});
this.layerMesh.onBeforeRender = () => {
object.onBeforeRender = () => {
const zoom = this.scene.getZoom();
this.layerMesh.material.setUniformsValue('u_time', this.scene._engine.clock.getElapsedTime());
this.layerMesh.material.setUniformsValue('u_zoom', zoom);
object.material.setUniformsValue('u_time', this.scene._engine.clock.getElapsedTime());
object.material.setUniformsValue('u_zoom', zoom);
this._preRender();
};
// 更新
if (this._needUpdateFilter) {
this._updateFilter();
this._updateFilter(object);
}
this._object3D.add(object);
this._addPickMesh(object);
if (type === 'fill') { this._addPickMesh(object); }
}
remove(object) {
this._object3D.remove(object);
@ -131,7 +136,7 @@ export default class Layer extends Base {
}
values === 'text' ? this.shapeType = values : null;
this._createAttrOption('shape', field, values, Global.sizes);
this._createAttrOption('shape', field, values, Global.shape);
return this;
}
/**
@ -171,7 +176,7 @@ export default class Layer extends Base {
styleOptions.fields = fields;
Util.assign(styleOptions, cfg);
for (const item in cfg) {
if (colorItem.indexOf(item) !== -1) {
if (colorItem.indexOf(item) !== -1 && styleOptions[item] !== 'none') {
styleOptions[item] = ColorUtil.color2RGBA(styleOptions[item]);
}
styleOptions[item] = styleOptions[item];
@ -252,20 +257,24 @@ export default class Layer extends Base {
this._mapping();
const activeHander = this._addActiveFeature.bind(this);
const resetHander = this._resetStyle.bind(this);
if (this.get('allowActive')) {
this.scene.on('pick', activeHander);
this.on('active', activeHander);
this.on('mouseleave', resetHander);
} else {
this.scene.off('pick', activeHander);
this.off('active', activeHander);
this.off('mouseleave', resetHander);
}
}
_addActiveFeature(e) {
const { featureId } = e;
if (featureId < 0) return;
const activeStyle = this.get('activedOptions');
const selectFeatureIds = this.layerSource.getSelectFeatureId(featureId);
// 如果数据不显示状态则不进行高亮
if (this.StyleData[selectFeatureIds[0]].hasOwnProperty('filter') && this.StyleData[selectFeatureIds[0]].filter === false) { return; }
const style = Util.assign({}, this.StyleData[featureId]);
style.color = ColorUtil.toRGB(activeStyle.fill).map(e => e / 255);
@ -434,6 +443,7 @@ export default class Layer extends Base {
}
_addPickMesh(mesh) {
this._pickingMesh = new THREE.Object3D();
this._pickingMesh.name = this.layerId;
this._visibleWithZoom();
this.scene.on('zoomchange', () => {
this._visibleWithZoom();
@ -445,6 +455,7 @@ export default class Layer extends Base {
});
const pickingMesh = new THREE[mesh.type](mesh.geometry, pickmaterial);
pickingMesh.name = this.layerId;
pickmaterial.setDefinesvalue(this.type, true);
pickingMesh.onBeforeRender = () => {
const zoom = this.scene.getZoom();
@ -457,20 +468,40 @@ export default class Layer extends Base {
}
_addPickingEvents() {
// TODO: Find a way to properly remove this listener on destroy
this.scene.on('pick', e => {
// Re-emit click event from the layer
const { featureId, point2d, intersects } = e;
if (intersects.length === 0) { return; }
const source = this.layerSource.get('data');
const feature = source.features[featureId];
this.scene.on('pick-' + this.layerId, e => {
const { featureId, point2d,type} = e;
if (featureId < -100 && this._activeIds !== null) {
this.emit('mouseleave');
return;
}
// if (intersects.length === 0) { return; }
const feature = this.layerSource.getSelectFeature(featureId);
const lnglat = this.scene.containerToLngLat(point2d);
const target = {
featureId,
feature,
pixel: point2d,
lnglat: { lng: lnglat.lng, lat: lnglat.lat }
};
if(featureId>=0) {
switch(type) {
case 'mouseup':
this.emit('click', target);
// this.emit('move', target);
break;
case 'mousemove':
this.emit('mousemove', target);
this.emit('active', target);
break;
default:
//this.emit('click', target);
}
}
});
}
/**
@ -480,7 +511,7 @@ export default class Layer extends Base {
*/
updateStyle(featureStyleId, style) {
if (this._activeIds) {
this.resetStyle();
this._resetStyle();
}
this._activeIds = featureStyleId;
const pickingId = this.layerMesh.geometry.attributes.pickingId.array;
@ -508,14 +539,14 @@ export default class Layer extends Base {
}
/**
* 用于过滤数据
* @param {*} filterData 数据过滤标识符
* @param {*} object 需要过滤的mesh
*/
_updateFilter() {
_updateFilter(object) {
this._updateMaping();
const filterData = this.StyleData;
this._activeIds = null; // 清空选中元素
const colorAttr = this.layerMesh.geometry.attributes.a_color;
const pickAttr = this.layerMesh.geometry.attributes.pickingId;
const colorAttr = object.geometry.attributes.a_color;
const pickAttr = object.geometry.attributes.pickingId;
pickAttr.array.forEach((id, index) => {
id = Math.abs(id);
const color = [ ...this.StyleData[id - 1].color ];
@ -526,7 +557,7 @@ export default class Layer extends Base {
colorAttr.array[index * 4 + 1] = 0;
colorAttr.array[index * 4 + 2] = 0;
colorAttr.array[index * 4 + 3] = 0;
pickAttr.array[index] = -id;
pickAttr.array[index] = -id; // 通过Id数据过滤 id<0 不显示
} else {
colorAttr.array[index * 4 + 0] = color[0];
colorAttr.array[index * 4 + 1] = color[1];
@ -537,8 +568,8 @@ export default class Layer extends Base {
});
colorAttr.needsUpdate = true;
pickAttr.needsUpdate = true;
this._needUpdateFilter = false;
this._needUpdateColor = false;
// this._needUpdateFilter = false;
// this._needUpdateColor = false;
}
_visibleWithZoom() {
const zoom = this.scene.getZoom();
@ -561,7 +592,8 @@ export default class Layer extends Base {
/**
* 重置高亮要素
*/
resetStyle() {
_resetStyle() {
const pickingId = this.layerMesh.geometry.attributes.pickingId.array;
const colorAttr = this.layerMesh.geometry.attributes.a_color;
this._activeIds.forEach(index => {
@ -577,6 +609,7 @@ export default class Layer extends Base {
}
});
colorAttr.needsUpdate = true;
this._activeIds = null;
}
/**
* 销毁Layer对象
@ -609,5 +642,8 @@ export default class Layer extends Base {
this._object3D = null;
this.scene = null;
}
_preRender(){
}
}

View File

@ -1,5 +1,4 @@
import Engine from './engine';
import * as THREE from './three';
import * as layers from '../layer';
import Base from './base';
import LoadImage from './image';
@ -19,6 +18,7 @@ export default class Scene extends Base {
}
_initEngine(mapContainer) {
this._el = mapContainer;
this._engine = new Engine(mapContainer, this);
this._engine.run();
}
@ -77,16 +77,34 @@ export default class Scene extends Base {
return this._layers;
}
_addLight() {
const scene = this._engine._scene;
const ambientLight = new THREE.AmbientLight(0xaaaaaa);
scene.add(ambientLight);
// const scene = this._engine._scene;
// //const ambientLight = new THREE.AmbientLight(0xaaaaaa);
// scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5);
scene.add(directionalLight);
// const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5);
// scene.add(directionalLight);
}
_addLayer() {
}
_registEvents(){
const events = [
'mouseout',
'mouseover',
'mousemove',
'mousedown',
'mouseleave',
'mouseup',
'click',
'dblclick'
];
events.forEach((event)=>{
this._el.addEventListener(event, e => {
}, false);
})
}
// 代理map事件
removeLayer(layer) {
const layerIndex = this._layers.indexOf(layer);
if (layerIndex > -1) {

View File

@ -9,8 +9,8 @@ export { Points } from 'three/src/objects/Points.js';
export { LineSegments } from 'three/src/objects/LineSegments.js';
export { Mesh } from 'three/src/objects/Mesh.js';
export { Texture } from 'three/src/textures/Texture.js';
export { DirectionalLight } from 'three/src/lights/DirectionalLight.js';
export { AmbientLight } from 'three/src/lights/AmbientLight.js';
//export { DirectionalLight } from 'three/src/lights/DirectionalLight.js';
//export { AmbientLight } from 'three/src/lights/AmbientLight.js';
export { WebGLRenderTarget } from 'three/src/renderers/WebGLRenderTarget.js';
export { PerspectiveCamera } from 'three/src/cameras/PerspectiveCamera.js';
export { BufferGeometry } from 'three/src/core/BufferGeometry.js';
@ -18,15 +18,15 @@ export { Raycaster } from 'three/src/core/Raycaster.js';
export { Matrix4 } from 'three/src/math/Matrix4.js';
export { Matrix3 } from 'three/src/math/Matrix3.js';
export { Line } from 'three/src/objects/Line.js';
export { LineLoop } from 'three/src/objects/LineLoop.js';
// export { LineLoop } from 'three/src/objects/LineLoop.js';
export { Vector4 } from 'three/src/math/Vector4.js';
export { Vector3 } from 'three/src/math/Vector3.js';
export { Vector2 } from 'three/src/math/Vector2.js';
export { TextureLoader } from 'three/src/loaders/TextureLoader.js';
export { LineDashedMaterial } from 'three/src/materials/LineDashedMaterial.js';
// export { TextureLoader } from 'three/src/loaders/TextureLoader.js';
// export { LineDashedMaterial } from 'three/src/materials/LineDashedMaterial.js';
export { ShaderMaterial } from 'three/src/materials/ShaderMaterial.js';
export { PointsMaterial } from 'three/src/materials/PointsMaterial.js';
export { VideoTexture } from 'three/src/textures/VideoTexture.js';
// export { PointsMaterial } from 'three/src/materials/PointsMaterial.js';
// export { VideoTexture } from 'three/src/textures/VideoTexture.js';
export { DataTexture } from 'three/src/textures/DataTexture.js';
export {
Float64BufferAttribute,

0
src/geo/bound.js Normal file
View File

20
src/geo/lngLat.js Normal file
View File

@ -0,0 +1,20 @@
export class LatLng {
constructor(lat, lng, alt) {
if (isNaN(lat) || isNaN(lng)) {
throw new Error('Invalid LatLng object: (' + lat + ', ' + lng + ')');
}
this.lat = +lat;
this.lng = +lng;
if (alt !== undefined) {
this.alt = +alt;
}
}
equal() {
}
distanceTo() {
}
}

View File

@ -1,5 +1,9 @@
import BufferBase from './bufferBase';
import { regularShape } from '../shape/index';
import * as polygonPath from '../shape/path';
import * as polygonShape from '../shape/polygon';
import * as lineShape from '../shape/line';
import { pointShape } from '../../global';
import Util from '../../util';
export default class PointBuffer extends BufferBase {
geometryBuffer() {
@ -43,8 +47,17 @@ export default class PointBuffer extends BufferBase {
this.attributes = this._toPointsAttributes(this.bufferStruct);
}
_3dRegularBuffer() {
const lineAttribute = {
shapes: [],
normal: [],
miter: [],
indexArray: [],
sizes: [],
positions: []
};
const coordinates = this.get('coordinates');
const properties = this.get('properties');
const style = this.get('style');
const type = this.get('type');
const positions = [];
const shapes = [];
@ -55,7 +68,7 @@ export default class PointBuffer extends BufferBase {
this.bufferStruct.style = properties;
coordinates.forEach((geo, index) => {
let { size, shape } = properties[index];
let shapeType = 'extrude';
let shapeType = '';
if (type === '2d' || (type === '3d' && size[2] === 0)) {
shapeType = 'fill';
@ -67,7 +80,20 @@ export default class PointBuffer extends BufferBase {
uvs.push(0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0);
shape = 'square';
}
const vert = regularShape[shape](shapeType);
// const vert = regularShape[shape](shapeType);
properties[index].size = size;
const [ vert, polygonLine ] = this._getShape(properties[index], style, lineAttribute.miter.length);
polygonLine.miter.forEach(() => {
lineAttribute.positions.push(...geo);
lineAttribute.sizes.push(...size);
});
lineAttribute.shapes.push(...polygonLine.positions);
lineAttribute.normal.push(...polygonLine.normal);
lineAttribute.miter.push(...polygonLine.miter);
lineAttribute.indexArray.push(...polygonLine.indexArray);
shapes.push(vert.positions);
positions.push(geo);
sizes.push(size);
@ -81,6 +107,21 @@ export default class PointBuffer extends BufferBase {
this.bufferStruct.sizes = sizes;
this.bufferStruct.faceUv = uvs;
this.attributes = this._toPointShapeAttributes(this.bufferStruct);
this.lineAttribute = lineAttribute;
}
_getShape(props, style, positionsIndex) {
const { shape } = props;
const { stroke, strokeWidth } = style;
const path = polygonPath[shape]();
let polygon = null;
let polygonLine = null;
if (pointShape['3d'].indexOf(shape) === -1) {
polygon = polygonShape.fill([ path ]);
polygonLine = lineShape.Line(path, { size: [ strokeWidth, 0 ], color: stroke }, positionsIndex);
} else {
polygon = polygonShape.extrude([ path ]);
}
return [ polygon, polygonLine ];
}
}

View File

@ -0,0 +1,81 @@
import { pointShape } from '../../../global';
import * as THREE from '../../../core/three';
import * as polygonShape from '../../shape/polygon';
import * as polygonPath from '../../shape/path';
import Util from '../../../util';
export default function fillBuffer(coordinates, properties) {
const attribute = {
vertices: [],
normals: [],
colors: [],
pickingIds: [],
shapePositions: [],
a_size: [],
faceUv: []
};
coordinates.forEach((geo, index) => {
let { size, shape, color, id } = properties[index];
let polygon = null;
const path = polygonPath[shape]();
if (pointShape['2d'].indexOf(shape) !== -1) {
Util.isArray(size) || (size = [ size, size, 0 ]);
polygon = polygonShape.fill([ path ]);
} else if (pointShape['3d'].indexOf(shape) !== -1) {
Util.isArray(size) || (size = [ size, size, size ]);
polygon = polygonShape.extrude([ path ]);
} else {
throw new Error('Invalid shape type: ' + shape);
}
toPointShapeAttributes(polygon, geo, { size, shape, color, id }, attribute);
});
return attribute;
}
function toPointShapeAttributes(polygon, geo, style, attribute) {
const { positionsIndex, positions } = polygon;
const pA = new THREE.Vector3();
const pB = new THREE.Vector3();
const pC = new THREE.Vector3();
const cb = new THREE.Vector3();
const ab = new THREE.Vector3();
for (let i = 0; i < positionsIndex.length / 3; i++) {
let index = positionsIndex[i * 3];
const { color, size, id } = style;
const ax = positions[index][0];
const ay = positions[index][1];
const az = positions[index][2];
index = positionsIndex[i * 3 + 1];
const bx = positions[index][0];
const by = positions[index][1];
const bz = positions[index][2];
index = positionsIndex[i * 3 + 2];
const cx = positions[index][0];
const cy = positions[index][1];
const cz = positions[index][2];
pA.set(ax, ay, az);
pB.set(bx, by, bz);
pC.set(cx, cy, cz);
cb.subVectors(pC, pB);
ab.subVectors(pA, pB);
cb.cross(ab);
cb.normalize();
const nx = cb.x;
const ny = cb.y;
const nz = cb.z;
attribute.vertices.push(...geo, ...geo, ...geo);
attribute.shapePositions.push(ax, ay, az, bx, by, bz, cx, cy, cz);
attribute.a_size.push(...size, ...size, ...size);
attribute.normals.push(nx, ny, nz, nx, ny, nz, nx, ny, nz);
attribute.colors.push(...color, ...color, ...color);
attribute.pickingIds.push(id, id, id);
}
}

View File

@ -0,0 +1,23 @@
export default function ImageBuffer(coordinates, properties, opt) {
const attributes = {
vertices: [],
colors: [],
sizes: [],
shapes: [],
pickingIds: [],
uv: []
};
coordinates.forEach((pos, index) => {
const { color, size, id, shape } = properties[index];
const { x, y } = opt.imagePos[shape];
attributes.vertices.push(...pos);
attributes.colors.push(...color);
attributes.pickingIds.push(id);
attributes.sizes.push(size * window.devicePixelRatio); //
attributes.uv.push(x, y);
attributes.shapes.push(shape);
});
return attributes;
}

View File

@ -0,0 +1,4 @@
export { default as FillBuffer } from './fillBuffer';
export { default as StrokeBuffer } from './strokeBuffer';
export { default as ImageBuffer } from './imageBuffer';
export { default as NormalBuffer } from './normalBuffer';

View File

@ -0,0 +1,19 @@
export default function NormalBuffer(coordinates, properties) {
const attributes = {
vertices: [],
colors: [],
sizes: [],
pickingIds: []
};
coordinates.forEach((pos, index) => {
const { color, size, id } = properties[index];
attributes.vertices.push(...pos);
attributes.colors.push(...color);
attributes.pickingIds.push(id);
attributes.sizes.push(size);
});
return attributes;
}

View File

@ -0,0 +1,246 @@
const SDFCommonWordsKey = '_AMap_sdf_com_words';
/**
* SDF 常用字获取/存储/check
*
*/
const SDFCommonWords = {
store() {
},
/**
* 检查一个字符是否在常用字中
* @param {*} charcode 汉字
*/
check(charcode) {
const range = this.range || [];
const info = this.info || {};
if (typeof charcode !== 'number') {
charcode = charcode.substr(0).charCodeAt(0);
}
for (let i = 0; i < range.length; i++) {
const curRange = range[i];
const [ rangeStart, rangeEnd ] = curRange.split('-');
if (charcode >= rangeStart && charcode <= rangeEnd) {
const curInfo = info[curRange] && info[curRange].info || {};
if (curInfo[charcode]) {
return true;
}
}
}
return false;
},
/**
* 获取纹理和位置信息
* @param list
* @param cb
*/
getImagesAndInfo(list, cb) {
const range = this.range;
},
loadCanvas(url, range, done) {
try {
const xhr = new XMLHttpRequest();
xhr.open('GET', url);
// 直接用 blob 格式 load 图片文件,方便直接转换成 base64
// 转成 base64 便于存储
// 使用 canvas 转换 base64 容易有损
xhr.responseType = 'blob';
xhr.onerror = function() {
done({ code: 0 });
};
xhr.onload = function() {
if (xhr.status === 200) {
const reader = new FileReader();
reader.onload = () => {
done(reader.result, range);
};
reader.readAsDataURL(xhr.response);
} else {
done({ code: 0 });
}
};
xhr.send();
} catch (err) {
done({ code: 0 });
}
},
loadImages(urls = []) {
const deferred = $.Deferred();
const totalNumbers = urls.length;
const localInfo = this.info;
let loadPicNum = 0;
for (let i = 0; i < urls.length; i++) {
const { url, range } = urls[i];
this.loadCanvas(url, range, (base64, range) => {
// image to base64
loadPicNum++;
!localInfo[range] && (localInfo[range] = {});
localInfo[range].pic = base64;
this.info = localInfo;
// todo: temp 暂时用 localstorage 存储,因为数据比较大,最好使用 indexDB
localStorage.setItem(SDFCommonWordsKey, JSON.stringify(localInfo));
if (loadPicNum === totalNumbers) {
deferred.resolve();
}
});
}
return deferred;
},
loadInfo(urls) {
const deferred = $.Deferred();
const totalNumbers = urls.length;
const localInfo = this.info;
let loadInfoNum = 0;
for (let i = 0; i < urls.length; i++) {
const { url, range } = urls[i];
$.ajax({
url,
dataType: 'json',
success: data => {
loadInfoNum++;
!localInfo[range] && (localInfo[range] = {});
localInfo[range].info = data;
this.info = localInfo;
localStorage.setItem(SDFCommonWordsKey, JSON.stringify(localInfo));
if (loadInfoNum === totalNumbers) {
deferred.resolve();
}
},
error: () => {
}
});
}
return deferred;
},
getTotalAssets(info, cb) {
const { range = [], urlPrefix } = info;
const picUrls = [];
const infoUrls = [];
this.range = range;
for (let i = 0; i < range.length; i++) {
const curRange = range[i];
const baseUrl = urlPrefix + curRange;
const picUrl = baseUrl + '.png';
const infoUrl = baseUrl + '.json';
picUrls.push({ range: curRange, url: picUrl });
infoUrls.push({ range: curRange, url: infoUrl });
}
const imageDeferred = this.loadImages(picUrls);
const infoDeferred = this.loadInfo(infoUrls);
$.when(imageDeferred, infoDeferred)
.then(() => {
// all info load complete
// console.log("all info load complete", " -- ", 1);
cb && cb(this.info);
}, () => {
// fail
});
},
// 获取数据
getData(cb) {
if (!_.isEmpty(this.info)) {
cb && cb(this.info);
} else {
this.getRemoteData(cb);
}
},
/**
* 从服务获取数据什么时候强制去取一回数据过期
* @param cb
*/
getRemoteData(cb) {
const self = this;
$.ajax({
url: '/getcommonwords',
dataType: 'json',
success: data => {
if (data.code == 1) {
const info = data.data;
self.getTotalAssets(info, cb);
}
}
});
},
destroy() {
},
init() {
let info = localStorage.getItem(SDFCommonWordsKey);
this.range = [];
this.info = {};
if (info) {
info = JSON.parse(info);
this.range = Object.keys(info);
this.info = info;
}
this.info = info || {};
}
};
export default SDFCommonWords;

View File

@ -0,0 +1,53 @@
import * as polygonPath from '../../shape/path';
import * as polygonShape from '../../shape/polygon';
import * as lineShape from '../../shape/line';
import { pointShape } from '../../../global';
import Util from '../../../util';
export default function StrokeBuffer(coordinates, properties, style) {
const attribute = {
shapes: [],
normal: [],
miter: [],
indexArray: [],
sizes: [],
positions: [],
pickingIds: [],
colors: []
};
const { stroke, strokeWidth } = style;
coordinates.forEach((geo, index) => {
let { size, shape, id } = properties[index];
const path = polygonPath[shape]();
const positionsIndex = attribute.miter.length;
let polygon = null;
if (pointShape['2d'].indexOf(shape) !== -1) {
Util.isArray(size) || (size = [ size, size, 0 ]);
polygon = lineShape.Line([ path ], { size: [ strokeWidth, 0 ], color: stroke, id }, positionsIndex);
} else if (pointShape['3d'].indexOf(shape) !== -1) {
Util.isArray(size) || (size = [ size, size, size ]);
const polygonExtrudePath = polygonShape.extrudeline([ path ]);
// TODO 3d line
polygon = lineShape.Line([ polygonExtrudePath ], { size: [ strokeWidth, 0 ], color: stroke, id }, positionsIndex);
} else {
throw new Error('Invalid shape type: ' + shape);
}
polygonLineBuffer(polygon, geo, size, attribute);
});
return attribute;
}
function polygonLineBuffer(polygon, geo, size, attribute) {
attribute.shapes.push(...polygon.positions);
attribute.normal.push(...polygon.normal);
attribute.miter.push(...polygon.miter);
attribute.pickingIds.push(...polygon.pickingIds);
attribute.indexArray.push(...polygon.indexArray);
attribute.colors.push(...polygon.colors);
polygon.miter.forEach(() => {
attribute.positions.push(...geo); // 多边形位置
attribute.sizes.push(...size); // 多边形大小
});
}

View File

@ -0,0 +1,9 @@
import { getJSON } from '../../../util/ajax';
import * as THREE from '../../../core/three';
import Global from '../../../global';
const Space = 1;
export default function TextBuffer(coordinates, properties, style) {
}

View File

@ -1,6 +1,8 @@
import BufferBase from './bufferBase';
import { getJSON } from '../../util/ajax';
import * as THREE from '../../core/three';
import TinySDF from '@mapbox/tiny-sdf';
import Global from '../../global';
const Space = 1;
export default class TextBuffer extends BufferBase {
@ -15,16 +17,21 @@ export default class TextBuffer extends BufferBase {
const properties = this.get('properties');
const { textOffset = [ 0, 0 ] } = this.get('style');
const chars = [];
const textChars = {};
properties.forEach(element => {
let text = element.shape || '';
text = text.toString();
for (let j = 0; j < text.length; j++) {
const code = text.charCodeAt(j);
textChars[text] = 0;
if (chars.indexOf(code) === -1) {
chars.push(text.charCodeAt(j));
}
}
});
const sdfTexture = this._updateSdf(Object.keys(textChars).join(''));
this.sdfTexture = sdfTexture;
this._loadTextInfo(chars);
this.on('SourceLoaded', () => {
const textureElements = [];
@ -40,10 +47,10 @@ export default class TextBuffer extends BufferBase {
let text = element.shape || '';
text = text.toString();
for (let i = 0; i < text.length; i++) {
const chr = text.charCodeAt(i);
const color = element.color;
this._drawGlyph(pos, chr, pen, size, colors, textureElements, originPoints, textSizes, textOffsets, color);
this._drawGlyph(pos, text[i], pen, size, colors, textureElements, originPoints, textSizes, textOffsets, color);
}
});
this.bufferStruct.style = properties;
@ -64,6 +71,7 @@ export default class TextBuffer extends BufferBase {
url: `${Global.sdfHomeUrl}/getsdfdata?chars=${chars.join('|')}`
}, (e, info) => {
this.metrics.chars = info.info;
this._loadTextTexture(info.url);
});
}
@ -73,8 +81,9 @@ export default class TextBuffer extends BufferBase {
const img = new Image();
img.crossOrigin = 'anonymous';
img.onload = () => {
this.bufferStruct.textTexture = this._creatTexture(img);
this.bufferStruct.textTexture = this._creatTexture(this.sdfTexture.texure);
this.emit('SourceLoaded');
};
img.src = url;
@ -93,22 +102,26 @@ export default class TextBuffer extends BufferBase {
* @param {*} textOffsets 字体偏移量数据
* @param {*} color 文字颜色
*/
_drawGlyph(pos, chr, pen, size, colors, textureElements, originPoints, textSizes, textOffsets, color) {
_drawGlyph(pos, text, pen, size, colors, textureElements, originPoints, textSizes, textOffsets, color) {
const metrics = this.metrics;
const chr = text.charCodeAt(0);
const metric = metrics.chars[chr];
if (!metric) return;
const info = this.sdfTexture.info;
const { x, y } = info[text];
const scale = size / metrics.size;
let width = metric[0];
let height = metric[1];
let width = 24; // metric[0];
let height = 24;// metric[1];
const horiBearingX = metric[2];
const horiBearingY = metric[3];
// const horiBearingY = metric[3];
const horiAdvance = metric[4];
const posX = metric[5];
const posY = metric[6];
// const posX = metric[5];
// const posY = metric[6];
const posX = x;
const posY = y;
const buffer = metrics.buffer;
@ -117,12 +130,17 @@ export default class TextBuffer extends BufferBase {
height += buffer * 2;
// Add a quad (= two triangles) per glyph.
const originX = (horiBearingX - buffer + width / 2) * scale;
const originY = -(height / 2 - horiBearingY) * scale;
// const originY = (height / 2 - horiBearingY) * scale;
// const originY = 0;
const offsetWidth = width / 2 * scale / (1.0 - horiBearingX * 1.5 / horiAdvance);
const offsetHeight = (horiAdvance / 2) * scale;
// const originX = (horiBearingX - buffer + width / 2) * scale;
// const originY = -(height - horiBearingY) * scale;
const originX = 0;
const originY = 0;
// const offsetWidth = width / 2 * scale / (1.0 - horiBearingX * 1.5 / horiAdvance);
// const offsetHeight = (horiAdvance / 2) * scale;
// const offsetWidth = width/2 * scale;
// const offsetHeight = height / 2 * scale;
// const offsetHeight = height * scale;
const offsetX = pen.x;
const offsetY = pen.y;
@ -135,14 +153,31 @@ export default class TextBuffer extends BufferBase {
pos[0] + originX, pos[1] + originY, 0,
);
// textSizes.push(
// offsetWidth, offsetHeight,
// -offsetWidth, offsetHeight,
// -offsetWidth, -offsetHeight,
// offsetWidth, offsetHeight,
// -offsetWidth, -offsetHeight,
// offsetWidth, -offsetHeight,
// );
const bx = 0;
const by = metrics.size / 2 + buffer;
textSizes.push(
offsetWidth, offsetHeight,
-offsetWidth, offsetHeight,
-offsetWidth, -offsetHeight,
offsetWidth, offsetHeight,
-offsetWidth, -offsetHeight,
offsetWidth, -offsetHeight,
((bx - buffer + width) * scale), (height - by) * scale,
((bx - buffer) * scale), (height - by) * scale,
((bx - buffer) * scale), -by * scale,
((bx - buffer + width) * scale), (height - by) * scale,
((bx - buffer) * scale), -by * scale,
((bx - buffer + width) * scale), -by * scale,
);
textOffsets.push(
offsetX, offsetY,
offsetX, offsetY,
@ -172,7 +207,8 @@ export default class TextBuffer extends BufferBase {
);
}
pen.x = pen.x + (horiAdvance + Space) * scale;
// pen.x = pen.x + (horiAdvance + Space) * scale;
pen.x = pen.x + size * 1.8;
}
@ -196,8 +232,46 @@ export default class TextBuffer extends BufferBase {
this.bufferStruct.textSize = [ image.width, image.height ];
const texture = new THREE.Texture(image);
texture.minFilter = THREE.LinearFilter;
texture.magFilter = THREE.LinearFilter;
texture.magFilter = THREE.ClampToEdgeWrapping;
texture.needsUpdate = true;
return texture;
}
_updateSdf(chars) {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const sdfs = {};
const fontSize = 24;
const fontWeight = 100;
const buffer = fontSize / 8;
const radius = fontSize / 3;
const canvasSize = Math.floor(Math.pow(chars.length, 0.5)) * (fontSize + buffer + radius);
canvas.width = canvasSize;
canvas.height = canvasSize;
ctx.clearRect(0, 0, canvas.width, canvas.height);
const sdf = new TinySDF(fontSize, buffer, radius, null, null, fontWeight);
for (let y = 0, i = 0; y + sdf.size <= canvas.height && i < chars.length; y += sdf.size) {
for (let x = 0; x + sdf.size <= canvas.width && i < chars.length; x += sdf.size) {
ctx.putImageData(this._makeRGBAImageData(ctx, sdf.draw(chars[i]), sdf.size), x, y);
sdfs[chars[i]] = { x, y };
i++;
}
}
return {
info: sdfs,
texure: canvas
};
}
_makeRGBAImageData(ctx, alphaChannel, size) {
const imageData = ctx.createImageData(size, size);
const data = imageData.data;
for (let i = 0; i < alphaChannel.length; i++) {
data[4 * i + 0] = alphaChannel[i];
data[4 * i + 1] = alphaChannel[i];
data[4 * i + 2] = alphaChannel[i];
data[4 * i + 3] = 255;
}
return imageData;
}
}

View File

@ -45,7 +45,10 @@ export function MeshLineMaterial(options) {
uniforms: {
u_opacity: { value: options.u_opacity || 1.0 },
u_time: { value: options.u_time || 0 },
u_zoom: { value: options.u_zoom }
u_zoom: { value: options.u_zoom },
u_duration: { value: options.u_duration || 2.0 },
u_interval: { value: options.u_interval || 1.0 },
u_trailLength: { value: options.u_trailLength || 0.2 }
},
vertexShader: meshline_vert,
fragmentShader: meshline_frag,

View File

@ -0,0 +1,29 @@
import Material from './material';
import point_frag from '../shader/point_meshLine_frag.glsl';
import point_vert from '../shader/point_meshLine_vert.glsl';
export default class PointLineMaterial extends Material {
getDefaultParameters() {
return {
uniforms: {
u_strokeOpacity: { value: 1 },
u_stroke: { value: [ 1.0, 1.0, 1.0, 1.0 ] },
u_strokeWidth: { value: 1.0 },
u_zoom: { value: 10 }
}
};
}
constructor(_uniforms, _defines, parameters) {
super(parameters);
const { uniforms } = this.getDefaultParameters();
this.uniforms = Object.assign(uniforms, this.setUniform(_uniforms));
this.type = 'PointLineMaterial';
this.vertexShader = point_vert;
this.fragmentShader = point_frag;
this.transparent = true;
}
}

View File

@ -1,7 +1,11 @@
precision highp float;
uniform float u_opacity;
varying vec4 v_color;
varying float vTime;
void main() {
gl_FragColor = v_color;
gl_FragColor.a = v_color.a * u_opacity;
#ifdef ANIMATE
gl_FragColor.a *= vTime;
#endif
}

View File

@ -2,12 +2,30 @@ precision highp float;
attribute float a_miter;
attribute vec4 a_color;
attribute float a_size;
attribute float a_distance;
uniform float u_zoom;
varying vec4 v_color;
uniform float u_time;
varying float vTime;
// animate
#ifdef ANIMATE
uniform float u_duration; // 动画持续时间
uniform float u_interval;
uniform float u_repeat;
uniform float u_trailLength;
#endif
void main() {
mat4 matModelViewProjection = projectionMatrix * modelViewMatrix;
vec3 pointPos = position.xyz + vec3(normal * a_size * pow(2.0,20.0-u_zoom) / 2.0 * a_miter);
v_color = a_color;
#ifdef ANIMATE
//mod(a_distance,0.2) * 5.
float alpa =1.0 - fract( mod(1.0- a_distance,u_interval)* (1.0/u_interval) + u_time / u_duration);
alpa = (alpa + u_trailLength -1.0) / u_trailLength;
vTime = clamp(alpa,0.,1.);
#endif
gl_Position = matModelViewProjection * vec4(pointPos, 1.0);
}

View File

@ -0,0 +1,17 @@
precision highp float;
uniform float u_strokeOpacity;
uniform vec4 u_stroke;
varying float v_pickingId;
void main() {
if(v_pickingId < -0.1) {
discard;
}
#ifdef ANIMATE
if (vTime > 1.0 || vTime < 0.0) {
discard;
}
#endif
gl_FragColor = u_stroke;
gl_FragColor.a = u_stroke.a * u_strokeOpacity ;
}

View File

@ -0,0 +1,23 @@
precision highp float;
attribute float a_miter;
attribute vec3 a_size;
attribute vec3 a_shape;
attribute float pickingId;
uniform float u_strokeWidth;
uniform float u_zoom;
varying float v_pickingId;
uniform float u_time;
varying float vTime;
void main() {
mat4 matModelViewProjection = projectionMatrix * modelViewMatrix;
float scale = pow(2.0,(20.0 - u_zoom));
vec3 newposition = position + (a_size + vec3(u_strokeWidth/2.,u_strokeWidth/2.,0)) * scale* a_shape;
v_pickingId = pickingId;
#ifdef ANIMATE
vTime = 1.0- (mod(u_time*50.,3600.)- position.z) / 100.;
#endif
//vec3 pointPos = newposition.xyz + vec3(normal * u_strokeWidth * pow(2.0,20.0-u_zoom) / 2.0 * a_miter);
vec3 pointPos = newposition.xyz + vec3(normal * u_strokeWidth * scale / 2.0 * a_miter);
gl_Position = matModelViewProjection * vec4(pointPos, 1.0);
}

View File

@ -13,17 +13,16 @@ void main() {
float dist =texture2D(u_texture, vec2(v_texcoord.x,1.0-v_texcoord.y)).r;
float alpha;
if(u_strokeWidth == 0.0){
alpha = smoothstep(u_buffer - u_gamma, u_buffer, dist);
alpha = smoothstep(u_buffer - u_gamma, u_buffer + u_gamma, dist);
gl_FragColor = vec4(v_color.rgb, alpha * v_color.a);
}else{
if(dist <= u_buffer - u_gamma){
alpha = smoothstep(u_strokeWidth - u_gamma, u_strokeWidth, dist);
alpha = smoothstep(u_strokeWidth - u_gamma, u_strokeWidth+ u_gamma, dist);
gl_FragColor = vec4(u_stroke.rgb, alpha * u_stroke.a);
}else if(dist < u_buffer){
alpha = smoothstep(u_buffer - u_gamma, u_buffer, dist);
alpha = smoothstep(u_buffer - u_gamma, u_buffer+u_gamma, dist);
gl_FragColor = vec4(alpha * v_color.rgb + (1.0 - alpha) * u_stroke.rgb, 1.0 * v_color.a * alpha + (1.0 - alpha) * u_stroke.a);
}else{
alpha = 1.0;

View File

@ -14,7 +14,13 @@ void main() {
mat4 matModelViewProjection = projectionMatrix * modelViewMatrix;
vec4 cur_position = matModelViewProjection * vec4(position.xy, 0, 1);
gl_Position = cur_position / cur_position.w + vec4((a_txtOffsets + a_txtsize)/ u_glSize * 2.0,0.0, 0.0) +vec4(abs(a_txtsize.x)/u_glSize.x *2.0, -abs(a_txtsize.y)/u_glSize.y* 2.0, 0.0, 0.0);
highp float camera_to_anchor_distance = gl_Position.w;
// highp float perspective_ratio = clamp(
// 0.5 + 0.5 * distance_ratio,
// 0.0, // Prevents oversized near-field symbols in pitched/overzoomed tiles
// 4.0);
v_color = a_color;
v_color.a = v_color.a * camera_to_anchor_distance;
v_texcoord = uv / u_textSize;
}

View File

@ -120,7 +120,7 @@ export function defaultLine(geo, index) {
return { positions, indexes: indexArray };
}
// mesh line
export function Line(path, props, positionsIndex, dash = false) {
export function Line(path, props, positionsIndex) {
if (path.length === 1) path = path[0];// 面坐标转线坐标
const positions = [];
const pickingIds = [];
@ -129,7 +129,7 @@ export function Line(path, props, positionsIndex, dash = false) {
const colors = [];
const indexArray = [];
const normals = getNormal(path);
const attrDistance = [];
let attrDistance = [];
const sizes = [];
let c = 0;
let index = positionsIndex;
@ -153,10 +153,14 @@ export function Line(path, props, positionsIndex, dash = false) {
point[2] = size[1];
positions.push(...point);
positions.push(...point);
if (dash) {
const d = pointIndex / (list.length - 1);
if(pointIndex===0){
attrDistance.push(0, 0);
} else{
const d = attrDistance[pointIndex * 2-1] + lineSegmentDistance(path[pointIndex-1],path[pointIndex]);
attrDistance.push(d,d);
}
index += 2;
});
normals.forEach(n => {
@ -167,6 +171,9 @@ export function Line(path, props, positionsIndex, dash = false) {
miter.push(-m);
miter.push(m);
});
attrDistance = attrDistance.map((d)=>{
return d / attrDistance[attrDistance.length-1];
})
return {
positions,
normal,
@ -179,3 +186,9 @@ export function Line(path, props, positionsIndex, dash = false) {
};
}
function lineSegmentDistance(end,start) {
const dx = start[0] - end[0];
const dy = start[1] - end[1];
const dz = start[2] - end[2];
return Math.sqrt(dx * dx + dy * dy + dz * dz);
}

45
src/geom/shape/path.js Normal file
View File

@ -0,0 +1,45 @@
/**
* @author lzxue
* @email lzx199065@gmail.com
* @create date 2018-11-28 11:01:33
* @modify date 2018-11-28 11:01:33
* @desc ,线, coordinates
*/
function circle() {
return polygonPath(30);
}
function square() {
return polygonPath(4);
}
function triangle() {
return polygonPath(3);
}
function hexagon() {
return polygonPath(6);
}
export {
circle,
square,
triangle,
hexagon,
circle as cylinder,
triangle as triangleColumn,
hexagon as hexagonColumn,
square as squareColumn
};
export function polygonPath(pointCount) {
const step = Math.PI * 2 / pointCount;
const line = [];
for (let i = 0; i < pointCount; i++) {
line.push(step * i);
}
const path = line.map(t => {
const x = Math.sin(t + Math.PI / 4),
y = Math.cos(t + Math.PI / 4);
return [ x, y, 0 ];
});
path.push(path[0]);
return path;
}

View File

@ -1,12 +1,12 @@
import * as polygonShape from './polygon';
import { polygonPath } from './path';
/**
* shape circle
* @param {enum} type 渲染类型
* @return {object} 顶点坐标和索引坐标
*/
export function circle(type) {
const points = polygonPoint(30);
const points = polygonPath(30);
return polygonShape[type]([ points ]);
}
/**
@ -15,7 +15,7 @@ export function circle(type) {
* @return {object} 顶点坐标和索引坐标
*/
export function triangle(type) {
const points = polygonPoint(3);
const points = polygonPath(3);
return polygonShape[type]([ points ]);
}
@ -25,7 +25,7 @@ export function triangle(type) {
* @return {object} 顶点坐标和索引坐标
*/
export function diamond(type) {
const points = polygonPoint(4);
const points = polygonPath(4);
return polygonShape[type]([ points ]);
}
@ -39,26 +39,6 @@ export function square(type) {
* @return {object} 顶点坐标和索引坐标
*/
export function hexagon(type) {
const points = polygonPoint(6);
const points = polygonPath(6);
return polygonShape[type]([ points ]);
}
/**
* 规则多边形
* @param {*} pointCount 顶点个数
* @param {*} extrude 是否拔高
* @return {Array} 顶点坐标
*/
function polygonPoint(pointCount) {
const step = Math.PI * 2 / pointCount;
const line = [];
for (let i = 0; i < pointCount; i++) {
line.push(step * i);
}
// debugger
const points = line.map(t => {
const x = Math.sin(t + Math.PI / 4),
y = Math.cos(t + Math.PI / 4);
return [ x, y, 0 ];
});
return points;
}

View File

@ -55,8 +55,9 @@ export function extrudeline(points) {
vertIndex.push(vertCount, 0);
vertIndex.push(vertCount, vertCount + pointCount);
vertIndex.push(vertCount + pointCount, pointCount);
return {
positions,
positionsIndex: vertIndex
};
const newPositions = [];
vertIndex.forEach(index => {
newPositions.push(positions[index]);
});
return newPositions;
}

View File

@ -4,7 +4,7 @@
*/
// const Global = {};
const Global = {
version: '____L7_VERSION____',
version: '1.0.0',
scene: {
mapType: 'AMAP',
zoom: 5,
@ -22,6 +22,11 @@ const Global = {
// 指定固定 tick 数的逼近值
snapCountArray: [ 0, 1, 1.2, 1.5, 1.6, 2, 2.2, 2.4, 2.5, 3, 4, 5, 6, 7.5, 8, 10 ],
size: 10000,
shape: 'circle',
pointShape: {
'2d': [ 'circle', 'square', 'hexagon', 'triangle' ],
'3d': [ 'cylinder', 'triangleColumn', 'hexagonColumn', 'squareColumn' ]
},
sdfHomeUrl: 'https://sdf.amap.com',
scales: {
}

View File

@ -1,8 +1,17 @@
// import Util from './util';
import Scene from './core/scene';
const version = '0.0.1';
import Global from './global';
const version = Global.version;
const track = function(enable) {
Global.trackable = enable;
};
require('./track');
export {
version,
Scene
Scene,
track
};

View File

@ -46,6 +46,7 @@ export default class LineLayer extends Layer {
geometry.addAttribute('a_size', new THREE.Float32BufferAttribute(attributes.sizes, 1));
geometry.addAttribute('normal', new THREE.Float32BufferAttribute(attributes.normal, 3));
geometry.addAttribute('a_miter', new THREE.Float32BufferAttribute(attributes.miter, 1));
geometry.addAttribute('a_distance', new THREE.Float32BufferAttribute(attributes.attrDistance, 1));
const lineType = style.lineType;
let material;
@ -55,6 +56,20 @@ export default class LineLayer extends Layer {
u_opacity: opacity,
u_zoom: this.scene.getZoom()
});
if (animateOptions.enable) {
material.setDefinesvalue('ANIMATE', true);
const {duration,interval,trailLength,repeat = Infinity} = animateOptions;
this.animateDuration = this.scene._engine.clock.getElapsedTime() + duration * repeat;
material.upDateUninform({
u_duration: duration,
u_interval: interval,
u_trailLength:trailLength,
});
}
} else {
geometry.addAttribute('a_distance', new THREE.Float32BufferAttribute(attributes.attrDistance, 1));
material = new DashLineMaterial({
@ -80,4 +95,11 @@ export default class LineLayer extends Layer {
}
return this;
}
_preRender(){
if(this.animateDuration>0 && this.animateDuration< this.scene._engine.clock.getElapsedTime()){
this.layerMesh.material.setDefinesvalue('ANIMATE', false);
this.emit('animateEnd');
this.animateDuration = Infinity;
}
}
}

View File

@ -1,12 +1,11 @@
import Layer from '../core/layer';
import * as THREE from '../core/three';
import PointBuffer from '../geom/buffer/point';
import PointMaterial from '../geom/material/pointMaterial';
import PolygonMaterial from '../geom/material/polygonMaterial';
import * as drawPoint from '../layer/render/point';
import { pointShape } from '../global';
// import PointBuffer from '../geom/buffer/point';
import TextBuffer from '../geom/buffer/text';
import TextMaterial from '../geom/material/textMaterial';
import radar from '../geom/shader/radar_frag.glsl';
import warn from '../geom/shader/warn_frag.glsl';
import * as PointBuffer from '../geom/buffer/point/index';
/**
@ -33,83 +32,64 @@ export default class PointLayer extends Layer {
return this;
}
_prepareRender() {
const { opacity, strokeWidth, stroke, strokeOpacity, shape, fill } = this.get('styleOptions');
if (this.shapeType === 'text') { // 绘制文本图层
this._textPoint();
return;
}
const source = this.layerSource;
const { opacity, strokeWidth, stroke, shape } = this.get('styleOptions');
this._buffer = new PointBuffer({
type: this.shapeType,
imagePos: this.scene.image.imagePos,
coordinates: source.geoData,
properties: this.StyleData
});
const geometry = this.geometry = new THREE.BufferGeometry();
let mtl;
if (this.shapeType === '2d' || this.shapeType === '3d') {
mtl = new PolygonMaterial({
u_opacity: opacity,
u_zoom: this.scene.getZoom()
});
// mtl= new pickingMaterial({
// u_opacity: opacity,
// u_zoom: this.scene.getZoom()
// })
// mtl.setDefinesvalue('point', true);
mtl.setDefinesvalue('SHAPE', true);
if (shape === 'radar') {
mtl.fragmentShader = radar;
const style = this.get('styleOptions');
const pointShapeType = this._getShape();
switch (pointShapeType) {
case 'fill' :// 填充图形
{
if (fill !== 'none') { // 是否填充
const attributes = PointBuffer.FillBuffer(source.geoData, this.StyleData, style);
const meshfill = drawPoint.DrawFill(attributes, this.get('styleOptions'));
this.add(meshfill);
}
if (shape === 'warn') {
mtl.fragmentShader = warn;
}
} else { // sdf 绘制点
mtl = new PointMaterial({
u_opacity: opacity,
u_strokeWidth: strokeWidth,
u_stroke: stroke,
shape: this.shapeType || false,
u_texture: this.scene.image.texture
}, {
SHAPE: (this.shapeType !== 'image'),
TEXCOORD_0: (this.shapeType === 'image')
});
}
const { attributes } = this._buffer;
geometry.addAttribute('position', new THREE.Float32BufferAttribute(attributes.vertices, 3));
geometry.addAttribute('a_color', new THREE.Float32BufferAttribute(attributes.colors, 4));
geometry.addAttribute('pickingId', new THREE.Float32BufferAttribute(attributes.pickingIds, 1));
if (this.shapeType === 'image') {
geometry.addAttribute('uv', new THREE.Float32BufferAttribute(attributes.uvs, 2));
geometry.addAttribute('a_size', new THREE.Float32BufferAttribute(attributes.sizes, 1));
} else if (this.shapeType === undefined) {
geometry.addAttribute('a_size', new THREE.Float32BufferAttribute(attributes.sizes, 1));
} else { // 多边形面
geometry.addAttribute('normal', new THREE.Float32BufferAttribute(attributes.normals, 3));
geometry.addAttribute('a_shape', new THREE.Float32BufferAttribute(attributes.shapePositions, 3));
geometry.addAttribute('a_size', new THREE.Float32BufferAttribute(attributes.a_size, 3));
if (shape) {
geometry.addAttribute('faceUv', new THREE.Float32BufferAttribute(attributes.faceUv, 2));
if (stroke !== 'none') { // 是否绘制边界
const lineAttribute = PointBuffer.StrokeBuffer(source.geoData, this.StyleData, style);
const meshStroke = drawPoint.DrawStroke(lineAttribute, this.get('styleOptions'));
this.add(meshStroke, 'line');
}
}
let mesh;
if (this.shapeType === 'image') {
mesh = new THREE.Points(geometry, mtl);
} else if (this.shapeType === undefined) { // 散点图
mesh = new THREE.Points(geometry, mtl);
} else {
mesh = new THREE.Mesh(geometry, mtl);
break;
case 'image':// 绘制图片标注
const imageAttribute = PointBuffer.ImageBuffer(source.geoData, this.StyleData, { imagePos: this.scene.image.imagePos });
const imageMesh = drawPoint.DrawImage(imageAttribute, { ...style, texture: this.scene.image.texture });
this.add(imageMesh);
break;
case 'normal' : // 原生点
const normalAttribute = PointBuffer.NormalBuffer(source.geoData, this.StyleData, style);
const normalPointMesh = drawPoint.DrawNormal(normalAttribute, style);
this.add(normalPointMesh);
}
}
_getShape() {
let shape = null;
if (!this.StyleData[0].hasOwnProperty('shape')) {
return 'normal';
}
for (let i = 0; i < this.StyleData.length; i++) {
shape = this.StyleData[i].shape;
if (shape !== undefined) {
break;
}
}
this.add(mesh);
if (pointShape['2d'].indexOf(shape) !== -1 || pointShape['3d'].indexOf(shape) !== -1) {
return 'fill';
} else if (shape == 'text') {
return 'text';
} else if (this.scene.image.imagesIds.indexOf(shape) !== -1) {
return 'image';
}
return 'normal';
}
_textPoint() {
const source = this.layerSource;
@ -133,11 +113,11 @@ export default class PointLayer extends Layer {
const material = new TextMaterial({
name: this.layerId,
u_texture: buffer.bufferStruct.textTexture,
u_strokeWidth: 1,
u_strokeWidth: styleOptions.strokeWidth,
u_stroke: stroke,
u_textSize: buffer.bufferStruct.textSize,
u_gamma: 0.11,
u_buffer: 0.8,
u_gamma: 2 * 1.4142 / 64,
u_buffer: 0.65,
u_color: color,
u_glSize: [ width, height ]
});

View File

@ -0,0 +1,28 @@
/**
* @author lzxue
* @email lzx199065@gmail.com
* @create date 2018-11-29 16:07:24
* @modify date 2018-11-29 16:07:24
* @desc [description] 绘制点图层的面状填充三角形六边形
*/
import * as THREE from '../../../core/three';
import PolygonMaterial from '../../../geom/material/polygonMaterial';
export default function DrawFill(attributes, style) {
const { opacity } = style;
const geometry = new THREE.BufferGeometry();
geometry.addAttribute('position', new THREE.Float32BufferAttribute(attributes.vertices, 3));
geometry.addAttribute('a_color', new THREE.Float32BufferAttribute(attributes.colors, 4));
geometry.addAttribute('pickingId', new THREE.Float32BufferAttribute(attributes.pickingIds, 1));
geometry.addAttribute('normal', new THREE.Float32BufferAttribute(attributes.normals, 3));
geometry.addAttribute('a_shape', new THREE.Float32BufferAttribute(attributes.shapePositions, 3));
geometry.addAttribute('a_size', new THREE.Float32BufferAttribute(attributes.a_size, 3));
const material = new PolygonMaterial({
u_opacity: opacity
}, {
SHAPE: true
});
material.setDefinesvalue('SHAPE', true);
const fillMesh = new THREE.Mesh(geometry, material);
return fillMesh;
}

View File

@ -0,0 +1,23 @@
import * as THREE from '../../../core/three';
import PointMaterial from '../../../geom/material/pointMaterial';
export default function DrawImage(attributes, style) {
const geometry = new THREE.BufferGeometry();
const { strokeWidth, stroke, opacity, texture } = style;
geometry.addAttribute('position', new THREE.Float32BufferAttribute(attributes.vertices, 3));
geometry.addAttribute('a_color', new THREE.Float32BufferAttribute(attributes.colors, 4));
geometry.addAttribute('pickingId', new THREE.Float32BufferAttribute(attributes.pickingIds, 1));
geometry.addAttribute('uv', new THREE.Float32BufferAttribute(attributes.uv, 2));
geometry.addAttribute('a_size', new THREE.Float32BufferAttribute(attributes.sizes, 1));
const material = new PointMaterial({
u_opacity: opacity,
u_strokeWidth: strokeWidth,
u_stroke: stroke,
u_texture: texture
}, {
SHAPE: false,
TEXCOORD_0: true
});
const strokeMesh = new THREE.Points(geometry, material);
return strokeMesh;
}

View File

@ -0,0 +1,19 @@
import * as THREE from '../../../core/three';
import PointMaterial from '../../../geom/material/pointMaterial';
export default function DrawNormal(attributes, style) {
const geometry = new THREE.BufferGeometry();
const { opacity } = style;
geometry.addAttribute('position', new THREE.Float32BufferAttribute(attributes.vertices, 3));
geometry.addAttribute('a_color', new THREE.Float32BufferAttribute(attributes.colors, 4));
geometry.addAttribute('pickingId', new THREE.Float32BufferAttribute(attributes.pickingIds, 1));
geometry.addAttribute('a_size', new THREE.Float32BufferAttribute(attributes.sizes, 1));
const material = new PointMaterial({
u_opacity: opacity
}, {
SHAPE: false,
TEXCOORD_0: false
});
const strokeMesh = new THREE.Points(geometry, material);
return strokeMesh;
}

View File

@ -0,0 +1,29 @@
/**
* @author lzxue
* @email lzx199065@gmail.com
* @create date 2018-11-29 16:35:34
* @modify date 2018-11-29 16:35:34
* @desc [description] 绘制图形的边框
*/
import PointLineMaterial from '../../../geom/material/pointLineMaterial';
import * as THREE from '../../../core/three';
export default function DrawStroke(attributes, style) {
const { strokeWidth, stroke, strokeOpacity } = style;
const geometry = new THREE.BufferGeometry();
geometry.setIndex(attributes.indexArray);
geometry.addAttribute('position', new THREE.Float32BufferAttribute(attributes.positions, 3));
geometry.addAttribute('a_shape', new THREE.Float32BufferAttribute(attributes.shapes, 3));
geometry.addAttribute('a_size', new THREE.Float32BufferAttribute(attributes.sizes, 3));
geometry.addAttribute('normal', new THREE.Float32BufferAttribute(attributes.normal, 3));
geometry.addAttribute('a_miter', new THREE.Float32BufferAttribute(attributes.miter, 1));
geometry.addAttribute('pickingId', new THREE.Float32BufferAttribute(attributes.pickingIds, 1));
geometry.addAttribute('a_color', new THREE.Float32BufferAttribute(attributes.colors, 4));
const material = new PointLineMaterial({
u_strokeOpacity: strokeOpacity,
u_stroke: stroke,
u_strokeWidth: strokeWidth
});
const strokeMesh = new THREE.Mesh(geometry, material);
return strokeMesh;
}

View File

@ -0,0 +1,23 @@
import * as THREE from '../../../core/three';
import TextMaterial from '../../../geom/material/textMaterial';
export default function DawText(attributes, style) {
const geometry = new THREE.BufferGeometry();
const { strokeWidth, stroke, opacity } = style;
geometry.addAttribute('position', new THREE.Float32BufferAttribute(attributes.originPoints, 3));
geometry.addAttribute('uv', new THREE.Float32BufferAttribute(attributes.textureElements, 2));
geometry.addAttribute('a_txtsize', new THREE.Float32BufferAttribute(attributes.textSizes, 2));
geometry.addAttribute('a_txtOffsets', new THREE.Float32BufferAttribute(attributes.textOffsets, 2));
geometry.addAttribute('a_color', new THREE.Float32BufferAttribute(attributes.colors, 4));
const material = new TextMaterial({
name: this.layerId,
u_texture: buffer.bufferStruct.textTexture,
u_strokeWidth: 1,
u_stroke: stroke,
u_textSize: buffer.bufferStruct.textSize,
u_gamma: 0.11,
u_buffer: 0.8,
u_color: color,
u_glSize: [ width, height ]
});
const mesh = new THREE.Mesh(geometry, material);
}

View File

@ -0,0 +1,4 @@
export { default as DrawFill } from './drawFill';
export { default as DrawImage } from './drawImage';
export { default as DrawStroke } from './drawStroke';
export { default as DrawNormal } from './drawNormal';

View File

@ -23,6 +23,7 @@ export class MapProvider extends Base {
initMap() {
const mapStyle = this.get('mapStyle');
switch (mapStyle) {
case 'dark' :
this.set('mapStyle', Theme.DarkTheme.mapStyle);
@ -31,7 +32,7 @@ export class MapProvider extends Base {
this.set('mapStyle', Theme.LightTheme.mapStyle);
break;
default:
this.set('mapStyle', Theme.LightTheme.mapStyle);
this.set('mapStyle', mapStyle);
}
this.set('zooms', [ this.get('minZoom'), this.get('maxZoom') ]);
this.map = new AMap.Map(this.container, this._attrs);

View File

@ -39,6 +39,10 @@ export default class CSVSource extends Source {
getSelectFeatureId(featureId) {
return [ featureId ];
}
getSelectFeature(featureId) {
return this.propertiesData[featureId];
}
_getCoord(geo) {
if (geo.geometry) {
// GeoJSON feature

View File

@ -39,5 +39,9 @@ export default class GeojsonSource extends Source {
return selectFeatureIds;
}
getSelectFeature(featureId){
const data = this.get('data');
return data.features[featureId];
}
}

22
src/track.js Normal file
View File

@ -0,0 +1,22 @@
/**
* @fileOverview track g2
* @author dxq613@gmail.com
*/
const Global = require('./global');
const SERVER_URL = 'https://kcart.alipay.com/web/bi.do';
// 延迟发送请求
setTimeout(function() {
if (Global.trackable) {
const image = new Image();
const newObj = {
pg: document.URL,
r: new Date().getTime(),
l7: true,
version: Global.version,
page_type: 'syslog'
};
const d = encodeURIComponent(JSON.stringify([ newObj ]));
image.src = `${SERVER_URL}?BIProfile=merge&d=${d}`;
}
}, 3000);

View File

@ -4,7 +4,7 @@ const pkg = require('./package.json');
module.exports = {
devtool: 'cheap-source-map',
entry: {
L7: './src/index.js'
l7: './src/index.js'
},
output: {
filename: '[name].js',