mirror of https://gitee.com/antv-l7/antv-l7
Merge branch 'heatmap-fix' into 'master'
Heatmap fix render 根据地图变化在执行渲染 text 重构文本渲染方式 See merge request !22
This commit is contained in:
commit
f7fb51360d
|
@ -25,22 +25,24 @@ const scene = new L7.Scene({
|
||||||
mapStyle: 'dark', // 样式URL
|
mapStyle: 'dark', // 样式URL
|
||||||
center: [ 120.19382669582967, 30.258134 ],
|
center: [ 120.19382669582967, 30.258134 ],
|
||||||
pitch: 0,
|
pitch: 0,
|
||||||
zoom: 3
|
zoom: 1
|
||||||
});
|
});
|
||||||
window.scene = scene;
|
window.scene = scene;
|
||||||
scene.on('loaded', () => {
|
scene.on('loaded', () => {
|
||||||
$.get('./data/provincePoint.geojson', data => {
|
$.get('./data/provincePoint.geojson', data => {
|
||||||
|
// data.features = data.features.slice(0,1);
|
||||||
scene.PointLayer({
|
scene.PointLayer({
|
||||||
zIndex: 2
|
zIndex: 2
|
||||||
})
|
})
|
||||||
.source(data)
|
.source(data)
|
||||||
.shape('name', 'text')
|
.shape('name', 'text')
|
||||||
|
.active(true)
|
||||||
.size(12) // default 1
|
.size(12) // default 1
|
||||||
.color('#fff')
|
.color('name')
|
||||||
.style({
|
.style({
|
||||||
stroke: '#999',
|
stroke: '#999',
|
||||||
strokeWidth: 2,
|
strokeWidth: 0,
|
||||||
opacity: 0.85
|
opacity: 1.0
|
||||||
})
|
})
|
||||||
.render();
|
.render();
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@antv/l7",
|
"name": "@antv/l7",
|
||||||
"version": "1.1.6",
|
"version": "1.1.7",
|
||||||
"description": "Large-scale WebGL-powered Geospatial Data Visualization",
|
"description": "Large-scale WebGL-powered Geospatial Data Visualization",
|
||||||
"main": "build/l7.js",
|
"main": "build/l7.js",
|
||||||
"browser": "build/l7.js",
|
"browser": "build/l7.js",
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
|
|
||||||
export class layerControl {
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
|
|
||||||
import { getMap } from '../../map';
|
|
||||||
import Base from '../base';
|
|
||||||
export default class MapContorller extends Base {
|
|
||||||
constructor(cfg, engine, scene) {
|
|
||||||
super(cfg);
|
|
||||||
this._engine = engine;
|
|
||||||
this.scene = scene;
|
|
||||||
}
|
|
||||||
_init() {
|
|
||||||
const mapType = this.get('mapType');
|
|
||||||
const mapCfg = this.get('mapCfg');
|
|
||||||
this.map = new getMap(mapType)(mapCfg);
|
|
||||||
this.map('mapLoad', this._mapload.bind(this));
|
|
||||||
}
|
|
||||||
_mapload() {
|
|
||||||
this.map.asyncCamera(this._engine);
|
|
||||||
this.emit('loaded');
|
|
||||||
}
|
|
||||||
_bindMapMethod() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -31,7 +31,6 @@ export default class Engine extends EventEmitter {
|
||||||
|
|
||||||
}
|
}
|
||||||
run() {
|
run() {
|
||||||
|
|
||||||
this.update();
|
this.update();
|
||||||
this.engineID = requestAnimationFrame(this.run.bind(this));
|
this.engineID = requestAnimationFrame(this.run.bind(this));
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,6 +112,8 @@ export default class Layer extends Base {
|
||||||
if (type === 'fill') {
|
if (type === 'fill') {
|
||||||
this._addPickMesh(object);// 不对边界线进行拾取
|
this._addPickMesh(object);// 不对边界线进行拾取
|
||||||
}
|
}
|
||||||
|
this.scene._engine.update();
|
||||||
|
setTimeout(() => this.scene._engine.update(), 500);
|
||||||
}
|
}
|
||||||
remove(object) {
|
remove(object) {
|
||||||
if (object.type === 'composer') {
|
if (object.type === 'composer') {
|
||||||
|
|
|
@ -2,6 +2,7 @@ import Engine from './engine';
|
||||||
import { LAYER_MAP } from '../layer';
|
import { LAYER_MAP } from '../layer';
|
||||||
import Base from './base';
|
import Base from './base';
|
||||||
import LoadImage from './image';
|
import LoadImage from './image';
|
||||||
|
import FontAtlasManager from '../geom/buffer/point/text/font-manager';
|
||||||
// import WorkerPool from './worker';
|
// import WorkerPool from './worker';
|
||||||
// import { MapProvider } from '../map/AMap';
|
// import { MapProvider } from '../map/AMap';
|
||||||
import { getMap } from '../map/index';
|
import { getMap } from '../map/index';
|
||||||
|
@ -16,12 +17,14 @@ export default class Scene extends Base {
|
||||||
this._initMap();
|
this._initMap();
|
||||||
// this._initAttribution(); // 暂时取消,后面作为组件去加载
|
// this._initAttribution(); // 暂时取消,后面作为组件去加载
|
||||||
this.addImage();
|
this.addImage();
|
||||||
|
this.fontAtlasManager = new FontAtlasManager();
|
||||||
this._layers = [];
|
this._layers = [];
|
||||||
|
this.animateCount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
_initEngine(mapContainer) {
|
_initEngine(mapContainer) {
|
||||||
this._engine = new Engine(mapContainer, this);
|
this._engine = new Engine(mapContainer, this);
|
||||||
this._engine.run();
|
this.registerMapEvent();
|
||||||
// this.workerPool = new WorkerPool();
|
// this.workerPool = new WorkerPool();
|
||||||
compileBuiltinModules();
|
compileBuiltinModules();
|
||||||
}
|
}
|
||||||
|
@ -38,8 +41,8 @@ export default class Scene extends Base {
|
||||||
this._container = document.getElementById(Map.container);
|
this._container = document.getElementById(Map.container);
|
||||||
// const Map = new MapProvider(this.mapContainer, this._attrs);
|
// const Map = new MapProvider(this.mapContainer, this._attrs);
|
||||||
Map.on('mapLoad', () => {
|
Map.on('mapLoad', () => {
|
||||||
this._initEngine(Map.renderDom);
|
|
||||||
this.map = Map.map;
|
this.map = Map.map;
|
||||||
|
this._initEngine(Map.renderDom);
|
||||||
Map.asyncCamera(this._engine);
|
Map.asyncCamera(this._engine);
|
||||||
this.initLayer();
|
this.initLayer();
|
||||||
this._registEvents();
|
this._registEvents();
|
||||||
|
@ -115,5 +118,31 @@ export default class Scene extends Base {
|
||||||
layer.destroy();
|
layer.destroy();
|
||||||
layer = null;
|
layer = null;
|
||||||
}
|
}
|
||||||
|
startAnimate() {
|
||||||
|
if (this.animateCount === 0) {
|
||||||
|
this.unRegsterMapEvent();
|
||||||
|
this._engine.run();
|
||||||
|
}
|
||||||
|
this.animateCount++;
|
||||||
|
}
|
||||||
|
stopAnimate() {
|
||||||
|
if (this.animateCount === 1) {
|
||||||
|
this._engine.stop();
|
||||||
|
this.registerMapEvent();
|
||||||
|
}
|
||||||
|
this.animateCount++;
|
||||||
|
}
|
||||||
|
// 地图状态变化时更新可视化渲染
|
||||||
|
registerMapEvent() {
|
||||||
|
this._updateRender = () => this._engine.update();
|
||||||
|
this.map.on('mousemove', this._updateRender);
|
||||||
|
this.map.on('mapmove', this._updateRender);
|
||||||
|
this.map.on('camerachange', this._updateRender);
|
||||||
|
}
|
||||||
|
unRegsterMapEvent() {
|
||||||
|
this.map.off('mousemove', this._updateRender);
|
||||||
|
this.map.off('mapmove', this._updateRender);
|
||||||
|
this.map.off('camerachange', this._updateRender);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,9 @@ export { WebGLRenderTarget } from 'three/src/renderers/WebGLRenderTarget.js';
|
||||||
export { PerspectiveCamera } from 'three/src/cameras/PerspectiveCamera.js';
|
export { PerspectiveCamera } from 'three/src/cameras/PerspectiveCamera.js';
|
||||||
export { OrthographicCamera } from 'three/src/cameras/OrthographicCamera.js';
|
export { OrthographicCamera } from 'three/src/cameras/OrthographicCamera.js';
|
||||||
export { BufferGeometry } from 'three/src/core/BufferGeometry.js';
|
export { BufferGeometry } from 'three/src/core/BufferGeometry.js';
|
||||||
|
export { InstancedBufferGeometry } from 'three/src/core/InstancedBufferGeometry';
|
||||||
export { PlaneBufferGeometry } from 'three/src/geometries/PlaneGeometry.js';
|
export { PlaneBufferGeometry } from 'three/src/geometries/PlaneGeometry.js';
|
||||||
|
export { BoxBufferGeometry } from 'three/src/geometries/BoxGeometry.js';
|
||||||
export { Raycaster } from 'three/src/core/Raycaster.js';
|
export { Raycaster } from 'three/src/core/Raycaster.js';
|
||||||
export { UniformsUtils } from 'three/src/renderers/shaders/UniformsUtils.js';
|
export { UniformsUtils } from 'three/src/renderers/shaders/UniformsUtils.js';
|
||||||
export { Matrix4 } from 'three/src/math/Matrix4.js';
|
export { Matrix4 } from 'three/src/math/Matrix4.js';
|
||||||
|
@ -39,4 +41,5 @@ export {
|
||||||
BufferAttribute
|
BufferAttribute
|
||||||
} from 'three/src/core/BufferAttribute.js';
|
} from 'three/src/core/BufferAttribute.js';
|
||||||
|
|
||||||
|
export { InstancedBufferAttribute } from 'three/src/core/InstancedBufferAttribute'
|
||||||
// export * from '../../build/three.js';
|
// export * from '../../build/three.js';
|
||||||
|
|
|
@ -1,246 +0,0 @@
|
||||||
// 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;
|
|
|
@ -0,0 +1,131 @@
|
||||||
|
export default function TextBuffer(layerData, fontAtlasManager) {
|
||||||
|
const characterSet = [];
|
||||||
|
layerData.forEach(element => {
|
||||||
|
let text = element.shape || '';
|
||||||
|
text = text.toString();
|
||||||
|
for (let j = 0; j < text.length; j++) {
|
||||||
|
if (characterSet.indexOf(text[j]) === -1) {
|
||||||
|
characterSet.push(text[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
fontAtlasManager.setProps({
|
||||||
|
characterSet
|
||||||
|
});
|
||||||
|
const attr = drawGlyph(layerData, fontAtlasManager);
|
||||||
|
return attr;
|
||||||
|
}
|
||||||
|
function drawGlyph(layerData, fontAtlasManager) {
|
||||||
|
const attributes = {
|
||||||
|
originPoints: [],
|
||||||
|
textSizes: [],
|
||||||
|
textOffsets: [],
|
||||||
|
colors: [],
|
||||||
|
textureElements: [],
|
||||||
|
pickingIds: []
|
||||||
|
};
|
||||||
|
const { texture, fontAtlas, mapping, scale } = fontAtlasManager;
|
||||||
|
layerData.forEach(function(element) {
|
||||||
|
const size = element.size;
|
||||||
|
const pos = element.coordinates;
|
||||||
|
let text = element.shape || '';
|
||||||
|
const pen = {
|
||||||
|
x: (-text.length * size) / 2,
|
||||||
|
y: 0
|
||||||
|
};
|
||||||
|
text = text.toString();
|
||||||
|
|
||||||
|
for (let i = 0; i < text.length; i++) {
|
||||||
|
const metric = mapping[text[i]];
|
||||||
|
const { x, y, width, height } = metric;
|
||||||
|
const color = element.color;
|
||||||
|
const offsetX = pen.x;
|
||||||
|
const offsetY = pen.y;
|
||||||
|
attributes.pickingIds.push(
|
||||||
|
element.id,
|
||||||
|
element.id,
|
||||||
|
element.id,
|
||||||
|
element.id,
|
||||||
|
element.id,
|
||||||
|
element.id
|
||||||
|
);
|
||||||
|
attributes.textOffsets.push(
|
||||||
|
// 文字在词语的偏移量
|
||||||
|
offsetX,
|
||||||
|
offsetY,
|
||||||
|
offsetX,
|
||||||
|
offsetY,
|
||||||
|
offsetX,
|
||||||
|
offsetY,
|
||||||
|
offsetX,
|
||||||
|
offsetY,
|
||||||
|
offsetX,
|
||||||
|
offsetY,
|
||||||
|
offsetX,
|
||||||
|
offsetY
|
||||||
|
);
|
||||||
|
attributes.originPoints.push(
|
||||||
|
// 词语的经纬度坐标
|
||||||
|
pos[0],
|
||||||
|
pos[1],
|
||||||
|
0,
|
||||||
|
pos[0],
|
||||||
|
pos[1],
|
||||||
|
0,
|
||||||
|
pos[0],
|
||||||
|
pos[1],
|
||||||
|
0,
|
||||||
|
pos[0],
|
||||||
|
pos[1],
|
||||||
|
0,
|
||||||
|
pos[0],
|
||||||
|
pos[1],
|
||||||
|
0,
|
||||||
|
pos[0],
|
||||||
|
pos[1],
|
||||||
|
0
|
||||||
|
);
|
||||||
|
attributes.textSizes.push(
|
||||||
|
size,
|
||||||
|
size * scale,
|
||||||
|
0,
|
||||||
|
size * scale,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
size,
|
||||||
|
size * scale,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
size,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
attributes.colors.push(
|
||||||
|
...color,
|
||||||
|
...color,
|
||||||
|
...color,
|
||||||
|
...color,
|
||||||
|
...color,
|
||||||
|
...color
|
||||||
|
);
|
||||||
|
attributes.textureElements.push(
|
||||||
|
// 文字纹理坐标
|
||||||
|
x + width,
|
||||||
|
y,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
x,
|
||||||
|
y + height,
|
||||||
|
x + width,
|
||||||
|
y,
|
||||||
|
x,
|
||||||
|
y + height,
|
||||||
|
x + width,
|
||||||
|
y + height
|
||||||
|
);
|
||||||
|
pen.x = pen.x + size;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
attributes.texture = texture;
|
||||||
|
attributes.fontAtlas = fontAtlas;
|
||||||
|
return attributes;
|
||||||
|
}
|
|
@ -0,0 +1,227 @@
|
||||||
|
import TinySDF from '@mapbox/tiny-sdf';
|
||||||
|
import { buildMapping } from '../../../../util/font-util';
|
||||||
|
import * as THREE from '../../../../core/three';
|
||||||
|
import LRUCache from './lru-cache';
|
||||||
|
export const DEFAULT_CHAR_SET = getDefaultCharacterSet();
|
||||||
|
export const DEFAULT_FONT_FAMILY = 'sans-serif';
|
||||||
|
export const DEFAULT_FONT_WEIGHT = 'normal';
|
||||||
|
export const DEFAULT_FONT_SIZE = 24;
|
||||||
|
export const DEFAULT_BUFFER = 3;
|
||||||
|
export const DEFAULT_CUTOFF = 0.25;
|
||||||
|
export const DEFAULT_RADIUS = 8;
|
||||||
|
const MAX_CANVAS_WIDTH = 1024;
|
||||||
|
const BASELINE_SCALE = 0.9;
|
||||||
|
const HEIGHT_SCALE = 1.2;
|
||||||
|
const CACHE_LIMIT = 3;
|
||||||
|
const cache = new LRUCache(CACHE_LIMIT);
|
||||||
|
|
||||||
|
const VALID_PROPS = [
|
||||||
|
'fontFamily',
|
||||||
|
'fontWeight',
|
||||||
|
'characterSet',
|
||||||
|
'fontSize',
|
||||||
|
'sdf',
|
||||||
|
'buffer',
|
||||||
|
'cutoff',
|
||||||
|
'radius'
|
||||||
|
];
|
||||||
|
|
||||||
|
function getDefaultCharacterSet() {
|
||||||
|
const charSet = [];
|
||||||
|
for (let i = 32; i < 128; i++) {
|
||||||
|
charSet.push(String.fromCharCode(i));
|
||||||
|
}
|
||||||
|
return charSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setTextStyle(ctx, fontFamily, fontSize, fontWeight) {
|
||||||
|
ctx.font = `${fontWeight} ${fontSize}px ${fontFamily}`;
|
||||||
|
ctx.fillStyle = '#000';
|
||||||
|
ctx.textBaseline = 'baseline';
|
||||||
|
ctx.textAlign = 'left';
|
||||||
|
}
|
||||||
|
function getNewChars(key, characterSet) {
|
||||||
|
const cachedFontAtlas = cache.get(key);
|
||||||
|
if (!cachedFontAtlas) {
|
||||||
|
return characterSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newChars = [];
|
||||||
|
const cachedMapping = cachedFontAtlas.mapping;
|
||||||
|
let cachedCharSet = Object.keys(cachedMapping);
|
||||||
|
cachedCharSet = new Set(cachedCharSet);
|
||||||
|
|
||||||
|
let charSet = characterSet;
|
||||||
|
if (charSet instanceof Array) {
|
||||||
|
charSet = new Set(charSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
charSet.forEach(char => {
|
||||||
|
if (!cachedCharSet.has(char)) {
|
||||||
|
newChars.push(char);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return newChars;
|
||||||
|
}
|
||||||
|
|
||||||
|
function populateAlphaChannel(alphaChannel, imageData) {
|
||||||
|
// populate distance value from tinySDF to image alpha channel
|
||||||
|
for (let i = 0; i < alphaChannel.length; i++) {
|
||||||
|
imageData.data[4 * i + 3] = alphaChannel[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class FontAtlasManager {
|
||||||
|
constructor() {
|
||||||
|
|
||||||
|
// font settings
|
||||||
|
this.props = {
|
||||||
|
fontFamily: DEFAULT_FONT_FAMILY,
|
||||||
|
fontWeight: DEFAULT_FONT_WEIGHT,
|
||||||
|
characterSet: DEFAULT_CHAR_SET,
|
||||||
|
fontSize: DEFAULT_FONT_SIZE,
|
||||||
|
buffer: DEFAULT_BUFFER,
|
||||||
|
// sdf only props
|
||||||
|
// https://github.com/mapbox/tiny-sdf
|
||||||
|
sdf: true,
|
||||||
|
cutoff: DEFAULT_CUTOFF,
|
||||||
|
radius: DEFAULT_RADIUS
|
||||||
|
};
|
||||||
|
|
||||||
|
// key is used for caching generated fontAtlas
|
||||||
|
this._key = null;
|
||||||
|
this._texture = new THREE.Texture();
|
||||||
|
}
|
||||||
|
|
||||||
|
get texture() {
|
||||||
|
return this._texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
get mapping() {
|
||||||
|
const data = cache.get(this._key);
|
||||||
|
return data && data.mapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
get scale() {
|
||||||
|
return HEIGHT_SCALE;
|
||||||
|
}
|
||||||
|
|
||||||
|
get fontAtlas() {
|
||||||
|
return this._fontAtlas;
|
||||||
|
}
|
||||||
|
|
||||||
|
setProps(props = {}) {
|
||||||
|
VALID_PROPS.forEach(prop => {
|
||||||
|
if (prop in props) {
|
||||||
|
this.props[prop] = props[prop];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// update cache key
|
||||||
|
const oldKey = this._key;
|
||||||
|
this._key = this._getKey();
|
||||||
|
|
||||||
|
const charSet = getNewChars(this._key, this.props.characterSet);
|
||||||
|
const cachedFontAtlas = cache.get(this._key);
|
||||||
|
|
||||||
|
// if a fontAtlas associated with the new settings is cached and
|
||||||
|
// there are no new chars
|
||||||
|
if (cachedFontAtlas && charSet.length === 0) {
|
||||||
|
// update texture with cached fontAtlas
|
||||||
|
if (this._key !== oldKey) {
|
||||||
|
this._updateTexture(cachedFontAtlas);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update fontAtlas with new settings
|
||||||
|
const fontAtlas = this._generateFontAtlas(this._key, charSet, cachedFontAtlas);
|
||||||
|
this._fontAtlas = fontAtlas;
|
||||||
|
this._updateTexture(fontAtlas);
|
||||||
|
|
||||||
|
// update cache
|
||||||
|
cache.set(this._key, fontAtlas);
|
||||||
|
}
|
||||||
|
|
||||||
|
_updateTexture({ data: canvas }) {
|
||||||
|
this._texture = new THREE.CanvasTexture(canvas);
|
||||||
|
this._texture.wrapS = THREE.ClampToEdgeWrapping;
|
||||||
|
this._texture.wrapT = THREE.ClampToEdgeWrapping;
|
||||||
|
this._texture.minFilter = THREE.LinearFilter;
|
||||||
|
this._texture.flipY = false;
|
||||||
|
this._texture.needUpdate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_generateFontAtlas(key, characterSet, cachedFontAtlas) {
|
||||||
|
const { fontFamily, fontWeight, fontSize, buffer, sdf, radius, cutoff } = this.props;
|
||||||
|
let canvas = cachedFontAtlas && cachedFontAtlas.data;
|
||||||
|
if (!canvas) {
|
||||||
|
canvas = document.createElement('canvas');
|
||||||
|
canvas.width = MAX_CANVAS_WIDTH;
|
||||||
|
}
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
|
||||||
|
setTextStyle(ctx, fontFamily, fontSize, fontWeight);
|
||||||
|
|
||||||
|
// 1. build mapping
|
||||||
|
const { mapping, canvasHeight, xOffset, yOffset } = buildMapping(
|
||||||
|
Object.assign(
|
||||||
|
{
|
||||||
|
getFontWidth: char => ctx.measureText(char).width,
|
||||||
|
fontHeight: fontSize * HEIGHT_SCALE,
|
||||||
|
buffer,
|
||||||
|
characterSet,
|
||||||
|
maxCanvasWidth: MAX_CANVAS_WIDTH
|
||||||
|
},
|
||||||
|
cachedFontAtlas && {
|
||||||
|
mapping: cachedFontAtlas.mapping,
|
||||||
|
xOffset: cachedFontAtlas.xOffset,
|
||||||
|
yOffset: cachedFontAtlas.yOffset
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// 2. update canvas
|
||||||
|
// copy old canvas data to new canvas only when height changed
|
||||||
|
if (canvas.height !== canvasHeight) {
|
||||||
|
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
||||||
|
canvas.height = canvasHeight;
|
||||||
|
ctx.putImageData(imageData, 0, 0);
|
||||||
|
}
|
||||||
|
setTextStyle(ctx, fontFamily, fontSize, fontWeight);
|
||||||
|
|
||||||
|
// 3. layout characters
|
||||||
|
if (sdf) {
|
||||||
|
const tinySDF = new TinySDF(fontSize, buffer, radius, cutoff, fontFamily, fontWeight);
|
||||||
|
// used to store distance values from tinySDF
|
||||||
|
// tinySDF.size equals `fontSize + buffer * 2`
|
||||||
|
const imageData = ctx.getImageData(0, 0, tinySDF.size, tinySDF.size);
|
||||||
|
|
||||||
|
for (const char of characterSet) {
|
||||||
|
populateAlphaChannel(tinySDF.draw(char), imageData);
|
||||||
|
ctx.putImageData(imageData, mapping[char].x - buffer, mapping[char].y - buffer);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (const char of characterSet) {
|
||||||
|
ctx.fillText(char, mapping[char].x, mapping[char].y + fontSize * BASELINE_SCALE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
xOffset,
|
||||||
|
yOffset,
|
||||||
|
mapping,
|
||||||
|
data: canvas,
|
||||||
|
width: canvas.width,
|
||||||
|
height: canvas.height
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
_getKey() {
|
||||||
|
const { fontFamily, fontWeight, fontSize, buffer, sdf, radius, cutoff } = this.props;
|
||||||
|
if (sdf) {
|
||||||
|
return `${fontFamily} ${fontWeight} ${fontSize} ${buffer} ${radius} ${cutoff}`;
|
||||||
|
}
|
||||||
|
return `${fontFamily} ${fontWeight} ${fontSize} ${buffer}`;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
/**
|
||||||
|
* LRU Cache class with limit
|
||||||
|
*
|
||||||
|
* Update order for each get/set operation
|
||||||
|
* Delete oldest when reach given limit
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default class LRUCache {
|
||||||
|
constructor(limit = 5) {
|
||||||
|
this.limit = limit;
|
||||||
|
|
||||||
|
this.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
clear() {
|
||||||
|
this._cache = {};
|
||||||
|
// access/update order, first item is oldest, last item is newest
|
||||||
|
this._order = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
get(key) {
|
||||||
|
const value = this._cache[key];
|
||||||
|
if (value) {
|
||||||
|
// update order
|
||||||
|
this._deleteOrder(key);
|
||||||
|
this._appendOrder(key);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
set(key, value) {
|
||||||
|
if (!this._cache[key]) {
|
||||||
|
// if reach limit, delete the oldest
|
||||||
|
if (Object.keys(this._cache).length === this.limit) {
|
||||||
|
this.delete(this._order[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._cache[key] = value;
|
||||||
|
this._appendOrder(key);
|
||||||
|
} else {
|
||||||
|
// if found in cache, delete the old one, insert new one to the first of list
|
||||||
|
this.delete(key);
|
||||||
|
|
||||||
|
this._cache[key] = value;
|
||||||
|
this._appendOrder(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(key) {
|
||||||
|
const value = this._cache[key];
|
||||||
|
if (value) {
|
||||||
|
this._deleteCache(key);
|
||||||
|
this._deleteOrder(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_deleteCache(key) {
|
||||||
|
delete this._cache[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
_deleteOrder(key) {
|
||||||
|
const index = this._order.findIndex(o => o === key);
|
||||||
|
if (index >= 0) {
|
||||||
|
this._order.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_appendOrder(key) {
|
||||||
|
this._order.push(key);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,181 +0,0 @@
|
||||||
|
|
||||||
|
|
||||||
import { getJSON } from '../../../util/ajax';
|
|
||||||
import EventEmitter from 'wolfy87-eventemitter';
|
|
||||||
import Global from '../../../global';
|
|
||||||
// const Space = 1;
|
|
||||||
const metrics = {
|
|
||||||
buffer: 3,
|
|
||||||
family: 'ios9',
|
|
||||||
size: 24
|
|
||||||
};
|
|
||||||
export default function TextBuffer(layerData, style) {
|
|
||||||
EventEmitter.call(this);
|
|
||||||
const attributes = {
|
|
||||||
originPoints: [],
|
|
||||||
textSizes: [],
|
|
||||||
textOffsets: [],
|
|
||||||
colors: [],
|
|
||||||
textureElements: []
|
|
||||||
};
|
|
||||||
const { textOffset = [ 0, 0 ] } = style;
|
|
||||||
const chars = [];
|
|
||||||
const textChars = {};
|
|
||||||
layerData.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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
loadTextInfo(chars, (chars, texture) => {
|
|
||||||
layerData.forEach(element => {
|
|
||||||
const size = element.size;
|
|
||||||
const pos = layerData.coordinates;
|
|
||||||
const pen = { x: textOffset[0], y: textOffset[1] };
|
|
||||||
let text = element.shape || '';
|
|
||||||
text = text.toString();
|
|
||||||
for (let i = 0; i < text.length; i++) {
|
|
||||||
const color = element.color;
|
|
||||||
drawGlyph(chars, pos, text[i], pen, size, attributes.colors, attributes.textureElements, attributes.originPoints, attributes.textSizes, attributes.textOffsets, color);
|
|
||||||
}
|
|
||||||
this.emit('completed', { attributes, texture });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadTextInfo(chars, done) {
|
|
||||||
getJSON({
|
|
||||||
url: `${Global.sdfHomeUrl}/getsdfdata?chars=${chars.join('|')}`
|
|
||||||
}, (e, info) => {
|
|
||||||
loadTextTexture(info.url, texture => {
|
|
||||||
done(info.info, texture);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
function loadTextTexture(url, cb) {
|
|
||||||
|
|
||||||
|
|
||||||
const img = new Image();
|
|
||||||
img.crossOrigin = 'anonymous';
|
|
||||||
|
|
||||||
img.onload = () => {
|
|
||||||
const textTexture = this._creatTexture(img);
|
|
||||||
cb(textTexture);
|
|
||||||
};
|
|
||||||
img.src = url;
|
|
||||||
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* 计算每个标注词语的位置
|
|
||||||
* @param {*} chars 文本信息
|
|
||||||
* @param {*} pos 文字三维空间坐标
|
|
||||||
* @param {*} text 字符
|
|
||||||
* @param {*} pen 字符在词语的偏移量
|
|
||||||
* @param {*} size 字体大小
|
|
||||||
* @param {*} colors 颜色
|
|
||||||
* @param {*} textureElements 纹理坐标
|
|
||||||
* @param {*} originPoints 初始位置数据
|
|
||||||
* @param {*} textSizes 文字大小数组
|
|
||||||
* @param {*} textOffsets 字体偏移量数据
|
|
||||||
* @param {*} color 文字颜色
|
|
||||||
*/
|
|
||||||
function drawGlyph(chars, pos, text, pen, size, colors, textureElements, originPoints, textSizes, textOffsets, color) {
|
|
||||||
const chr = text.charCodeAt(0);
|
|
||||||
const metric = chars[chr];
|
|
||||||
if (!metric) return;
|
|
||||||
const scale = size / metrics.size;
|
|
||||||
|
|
||||||
let width = metric[0];
|
|
||||||
let height = metric[1];
|
|
||||||
const posX = metric[5];
|
|
||||||
const posY = metric[6];
|
|
||||||
const buffer = metrics.buffer;
|
|
||||||
if (width > 0 && height > 0) {
|
|
||||||
width += buffer * 2;
|
|
||||||
height += buffer * 2;
|
|
||||||
const originX = 0;
|
|
||||||
const originY = 0;
|
|
||||||
const offsetX = pen.x;
|
|
||||||
const offsetY = pen.y;
|
|
||||||
originPoints.push(
|
|
||||||
pos[0] + originX, pos[1] + originY, 0,
|
|
||||||
pos[0] + originX, pos[1] + originY, 0,
|
|
||||||
pos[0] + originX, pos[1] + originY, 0,
|
|
||||||
pos[0] + originX, pos[1] + originY, 0,
|
|
||||||
pos[0] + originX, pos[1] + originY, 0,
|
|
||||||
pos[0] + originX, pos[1] + originY, 0,
|
|
||||||
);
|
|
||||||
const bx = 0;
|
|
||||||
const by = metrics.size / 2 + buffer;
|
|
||||||
textSizes.push(
|
|
||||||
((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,
|
|
||||||
offsetX, offsetY,
|
|
||||||
offsetX, offsetY,
|
|
||||||
offsetX, offsetY,
|
|
||||||
offsetX, offsetY,
|
|
||||||
);
|
|
||||||
|
|
||||||
colors.push(
|
|
||||||
...color,
|
|
||||||
...color,
|
|
||||||
...color,
|
|
||||||
...color,
|
|
||||||
...color,
|
|
||||||
...color,
|
|
||||||
);
|
|
||||||
textureElements.push(
|
|
||||||
|
|
||||||
posX + width, posY,
|
|
||||||
posX, posY,
|
|
||||||
posX, posY + height,
|
|
||||||
|
|
||||||
posX + width, posY,
|
|
||||||
posX, posY + height,
|
|
||||||
posX + width, posY + height
|
|
||||||
);
|
|
||||||
}
|
|
||||||
pen.x = pen.x + size * 1.8;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// function measureText(text, size) {
|
|
||||||
// const dimensions = {
|
|
||||||
// advance: 0
|
|
||||||
// };
|
|
||||||
// const metrics = this.metrics;
|
|
||||||
// const scale = size / metrics.size;
|
|
||||||
// for (let i = 0; i < text.length; i++) {
|
|
||||||
// const code = text.charCodeAt(i);
|
|
||||||
// const horiAdvance = metrics.chars[code][4];
|
|
||||||
|
|
||||||
// dimensions.advance += (horiAdvance + Space) * scale;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return dimensions;
|
|
||||||
// }
|
|
||||||
// function creatTexture(image) {
|
|
||||||
// this.bufferStruct.textSize = [ image.width, image.height ];
|
|
||||||
// const texture = new THREE.Texture(image);
|
|
||||||
// texture.minFilter = THREE.LinearFilter;
|
|
||||||
// texture.magFilter = THREE.ClampToEdgeWrapping;
|
|
||||||
// texture.needsUpdate = true;
|
|
||||||
// return texture;
|
|
||||||
// }
|
|
|
@ -1,276 +0,0 @@
|
||||||
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 {
|
|
||||||
|
|
||||||
geometryBuffer() {
|
|
||||||
this.metrics = {
|
|
||||||
buffer: 3,
|
|
||||||
family: 'ios9',
|
|
||||||
size: 24
|
|
||||||
};
|
|
||||||
const layerData = this.get('layerData');
|
|
||||||
const { textOffset = [ 0, 0 ] } = this.get('style');
|
|
||||||
const chars = [];
|
|
||||||
const textChars = {};
|
|
||||||
layerData.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 = [];
|
|
||||||
const colors = [];
|
|
||||||
const originPoints = [];
|
|
||||||
const textSizes = [];
|
|
||||||
const textOffsets = [];
|
|
||||||
layerData.forEach(element => {
|
|
||||||
const size = element.size;
|
|
||||||
const pos = element.coordinates;
|
|
||||||
// const pen = { x: pos[0] - dimensions.advance / 2, y: pos[1] };
|
|
||||||
const pen = { x: textOffset[0], y: textOffset[1] };
|
|
||||||
let text = element.shape || '';
|
|
||||||
text = text.toString();
|
|
||||||
for (let i = 0; i < text.length; i++) {
|
|
||||||
|
|
||||||
|
|
||||||
const color = element.color;
|
|
||||||
this._drawGlyph(pos, text[i], pen, size, colors, textureElements, originPoints, textSizes, textOffsets, color);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.bufferStruct.style = layerData;
|
|
||||||
this.attributes = {
|
|
||||||
originPoints,
|
|
||||||
textSizes,
|
|
||||||
textOffsets,
|
|
||||||
colors,
|
|
||||||
textureElements
|
|
||||||
};
|
|
||||||
this.emit('completed');
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
_loadTextInfo(chars) {
|
|
||||||
getJSON({
|
|
||||||
url: `${Global.sdfHomeUrl}/getsdfdata?chars=${chars.join('|')}`
|
|
||||||
}, (e, info) => {
|
|
||||||
this.metrics.chars = info.info;
|
|
||||||
|
|
||||||
this._loadTextTexture(info.url);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
_loadTextTexture(url) {
|
|
||||||
|
|
||||||
|
|
||||||
const img = new Image();
|
|
||||||
img.crossOrigin = 'anonymous';
|
|
||||||
|
|
||||||
|
|
||||||
img.onload = () => {
|
|
||||||
this.bufferStruct.textTexture = this._creatTexture(this.sdfTexture.texure);
|
|
||||||
this.emit('SourceLoaded');
|
|
||||||
};
|
|
||||||
img.src = url;
|
|
||||||
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* 计算每个标注词语的位置
|
|
||||||
* @param {*} pos 文字三维空间坐标
|
|
||||||
* @param {*} text 字符
|
|
||||||
* @param {*} pen 字符在词语的偏移量
|
|
||||||
* @param {*} size 字体大小
|
|
||||||
* @param {*} colors 颜色
|
|
||||||
* @param {*} textureElements 纹理坐标
|
|
||||||
* @param {*} originPoints 初始位置数据
|
|
||||||
* @param {*} textSizes 文字大小数组
|
|
||||||
* @param {*} textOffsets 字体偏移量数据
|
|
||||||
* @param {*} 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 = 24; // metric[0];
|
|
||||||
let height = 24;// metric[1];
|
|
||||||
|
|
||||||
// const horiBearingX = metric[2];
|
|
||||||
// const horiBearingY = metric[3];
|
|
||||||
|
|
||||||
// const horiAdvance = metric[4];
|
|
||||||
// const posX = metric[5];
|
|
||||||
// const posY = metric[6];
|
|
||||||
const posX = x;
|
|
||||||
const posY = y;
|
|
||||||
|
|
||||||
const buffer = metrics.buffer;
|
|
||||||
|
|
||||||
if (width > 0 && height > 0) {
|
|
||||||
width += buffer * 2;
|
|
||||||
height += buffer * 2;
|
|
||||||
|
|
||||||
// Add a quad (= two triangles) per glyph.
|
|
||||||
// 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;
|
|
||||||
originPoints.push(
|
|
||||||
pos[0] + originX, pos[1] + originY, 0,
|
|
||||||
pos[0] + originX, pos[1] + originY, 0,
|
|
||||||
pos[0] + originX, pos[1] + originY, 0,
|
|
||||||
pos[0] + originX, pos[1] + originY, 0,
|
|
||||||
pos[0] + originX, pos[1] + originY, 0,
|
|
||||||
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(
|
|
||||||
|
|
||||||
|
|
||||||
((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,
|
|
||||||
offsetX, offsetY,
|
|
||||||
offsetX, offsetY,
|
|
||||||
offsetX, offsetY,
|
|
||||||
offsetX, offsetY,
|
|
||||||
);
|
|
||||||
|
|
||||||
colors.push(
|
|
||||||
...color,
|
|
||||||
...color,
|
|
||||||
...color,
|
|
||||||
...color,
|
|
||||||
...color,
|
|
||||||
...color,
|
|
||||||
);
|
|
||||||
textureElements.push(
|
|
||||||
|
|
||||||
posX + width, posY,
|
|
||||||
posX, posY,
|
|
||||||
posX, posY + height,
|
|
||||||
|
|
||||||
posX + width, posY,
|
|
||||||
posX, posY + height,
|
|
||||||
posX + width, posY + height
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// pen.x = pen.x + (horiAdvance + Space) * scale;
|
|
||||||
pen.x = pen.x + size * 1.8;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
_measureText(text, size) {
|
|
||||||
const dimensions = {
|
|
||||||
advance: 0
|
|
||||||
};
|
|
||||||
const metrics = this.metrics;
|
|
||||||
const scale = size / metrics.size;
|
|
||||||
for (let i = 0; i < text.length; i++) {
|
|
||||||
const code = text.charCodeAt(i);
|
|
||||||
const horiAdvance = metrics.chars[code][4];
|
|
||||||
|
|
||||||
dimensions.advance += (horiAdvance + Space) * scale;
|
|
||||||
}
|
|
||||||
|
|
||||||
return dimensions;
|
|
||||||
}
|
|
||||||
_creatTexture(image) {
|
|
||||||
this.bufferStruct.textSize = [ image.width, image.height ];
|
|
||||||
const texture = new THREE.Texture(image);
|
|
||||||
texture.minFilter = 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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,91 +0,0 @@
|
||||||
import { UniformSemantic, DataType, RenderState, BlendFunc } from '@ali/r3-base';
|
|
||||||
import { Material, RenderTechnique } from '@ali/r3-material';
|
|
||||||
import point_frag from '../shader/rainPass_frag.glsl';
|
|
||||||
import point_vert from '../shader/rainPass_vert.glsl';
|
|
||||||
export class RainPassMaterial extends Material {
|
|
||||||
constructor(opt) {
|
|
||||||
super(opt.name);
|
|
||||||
|
|
||||||
// this._generateTechnique();
|
|
||||||
|
|
||||||
for (const item in opt) {
|
|
||||||
if (item.substr(0, 2) === 'u_') {
|
|
||||||
this.setValue(item, opt[item]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_generateTechnique() {
|
|
||||||
|
|
||||||
const VERT_SHADER = point_vert;
|
|
||||||
// 片元着色器
|
|
||||||
const FRAG_SHADER = point_frag;
|
|
||||||
// Technique 配置信息
|
|
||||||
const cfg = {
|
|
||||||
attributes: {
|
|
||||||
a_position: {
|
|
||||||
name: 'a_position',
|
|
||||||
semantic: 'POSITION',
|
|
||||||
type: DataType.FLOAT_VEC3
|
|
||||||
},
|
|
||||||
a_uv: {
|
|
||||||
name: 'a_uv',
|
|
||||||
semantic: 'TEXCOORD_0',
|
|
||||||
type: DataType.FLOAT_VEC2
|
|
||||||
}
|
|
||||||
},
|
|
||||||
uniforms: {
|
|
||||||
matModelViewProjection: {
|
|
||||||
name: 'matModelViewProjection',
|
|
||||||
semantic: UniformSemantic.MODELVIEWPROJECTION,
|
|
||||||
type: DataType.FLOAT_MAT4
|
|
||||||
},
|
|
||||||
u_texture: {
|
|
||||||
name: 'u_texture',
|
|
||||||
type: DataType.SAMPLER_2D
|
|
||||||
},
|
|
||||||
u_colorTexture: {
|
|
||||||
name: 'u_colorTexture',
|
|
||||||
type: DataType.SAMPLER_2D
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 创建 Technique
|
|
||||||
const tech = new RenderTechnique('PointMaterial');
|
|
||||||
tech.states = {
|
|
||||||
disable: [ RenderState.CULL_FACE, RenderState.DEPTH_TEST ],
|
|
||||||
enable: [ RenderState.BLEND ],
|
|
||||||
functions: { blendFunc: [ BlendFunc.SRC_ALPHA, BlendFunc.ONE_MINUS_SRC_ALPHA ] }
|
|
||||||
};
|
|
||||||
tech.isValid = true;
|
|
||||||
tech.uniforms = cfg.uniforms;
|
|
||||||
tech.attributes = cfg.attributes;
|
|
||||||
tech.vertexShader = VERT_SHADER;
|
|
||||||
tech.fragmentShader = FRAG_SHADER;
|
|
||||||
tech.customMacros = this._macros;
|
|
||||||
this._technique = tech;
|
|
||||||
}
|
|
||||||
|
|
||||||
prepareDrawing(camera, component, primitive) {
|
|
||||||
|
|
||||||
this.getAttributeDefines(camera, component, primitive);
|
|
||||||
if (!this._technique) { this._generateTechnique(); }
|
|
||||||
super.prepareDrawing(camera, component, primitive);
|
|
||||||
|
|
||||||
}
|
|
||||||
getAttributeDefines(camera, component, primitive) {
|
|
||||||
this._macros = [];
|
|
||||||
if (!primitive) return this._macros;
|
|
||||||
|
|
||||||
const attribNames = Object.keys(primitive.vertexAttributes);
|
|
||||||
if (attribNames.indexOf('SHAPE') !== -1) {
|
|
||||||
this._macros.push('SHAPE');
|
|
||||||
}
|
|
||||||
if (attribNames.indexOf('TEXCOORD_0') !== -1) {
|
|
||||||
this._macros.push('TEXCOORD_0');
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -9,12 +9,13 @@ export default function TextMaterial(options) {
|
||||||
u_texture: { value: options.u_texture },
|
u_texture: { value: options.u_texture },
|
||||||
u_strokeWidth: { value: options.u_strokeWidth },
|
u_strokeWidth: { value: options.u_strokeWidth },
|
||||||
u_stroke: { value: options.u_stroke },
|
u_stroke: { value: options.u_stroke },
|
||||||
u_textSize: { value: options.u_textSize },
|
u_textTextureSize: { value: options.u_textTextureSize },
|
||||||
u_scale: { value: options.u_scale },
|
u_scale: { value: options.u_scale },
|
||||||
u_gamma: { value: options.u_gamma },
|
u_gamma: { value: options.u_gamma },
|
||||||
u_buffer: { value: options.u_buffer },
|
u_buffer: { value: options.u_buffer },
|
||||||
u_color: { value: options.u_color },
|
u_glSize: { value: options.u_glSize },
|
||||||
u_glSize: { value: options.u_glSize }
|
u_activeId: { value: options.u_activeId || 0 },
|
||||||
|
u_activeColor: { value: options.u_activeColor }
|
||||||
|
|
||||||
},
|
},
|
||||||
vertexShader: vs,
|
vertexShader: vs,
|
||||||
|
|
|
@ -1,48 +0,0 @@
|
||||||
precision highp float;
|
|
||||||
#define ambientRatio 0.5
|
|
||||||
#define diffuseRatio 0.4
|
|
||||||
#define specularRatio 0.1
|
|
||||||
attribute vec4 a_color;
|
|
||||||
attribute vec4 a_idColor;
|
|
||||||
attribute vec2 faceUv;
|
|
||||||
attribute vec3 a_shape;
|
|
||||||
attribute vec3 a_size;
|
|
||||||
uniform float u_zoom;
|
|
||||||
varying vec2 v_texCoord;
|
|
||||||
varying vec4 v_color;
|
|
||||||
varying float v_lightWeight;
|
|
||||||
varying float v_size;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
float scale = pow(2.0,(20.0 - u_zoom));
|
|
||||||
mat4 matModelViewProjection = projectionMatrix * modelViewMatrix;
|
|
||||||
vec3 newposition = position;
|
|
||||||
#ifdef SHAPE
|
|
||||||
newposition =position + a_size * scale* a_shape;
|
|
||||||
#endif
|
|
||||||
v_texCoord = faceUv;
|
|
||||||
if(normal == vec3(0.,0.,1.)){
|
|
||||||
v_color = a_color;
|
|
||||||
gl_Position = matModelViewProjection * vec4(newposition, 1.0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
vec3 worldPos = vec3(vec4(newposition,1.0) * modelMatrix);
|
|
||||||
vec3 worldNormal = vec3(vec4(normal,1.0) * modelMatrix);
|
|
||||||
// //cal light weight
|
|
||||||
vec3 viewDir = normalize(cameraPosition - worldPos);
|
|
||||||
//vec3 lightDir = normalize(vec3(1, -10.5, 12));
|
|
||||||
vec3 lightDir = normalize(vec3(0.,-10.,1.));
|
|
||||||
vec3 halfDir = normalize(viewDir+lightDir);
|
|
||||||
// //lambert
|
|
||||||
float lambert = dot(worldNormal, lightDir);
|
|
||||||
//specular
|
|
||||||
float specular = pow( max(0.0, dot(worldNormal, halfDir)), 32.0);
|
|
||||||
//sum to light weight
|
|
||||||
float lightWeight = ambientRatio + diffuseRatio * lambert + specularRatio * specular;
|
|
||||||
v_texCoord = faceUv;
|
|
||||||
v_lightWeight = lightWeight;
|
|
||||||
// v_size = a_size;
|
|
||||||
v_color =vec4(a_color.rgb*lightWeight, a_color.w);
|
|
||||||
gl_Position = matModelViewProjection * vec4(newposition, 1.0);
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
precision mediump float;
|
|
||||||
uniform sampler2D u_texture;
|
|
||||||
varying float v_time;
|
|
||||||
varying vec2 v_texCoord;
|
|
||||||
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
vec4 color = texture2D(u_texture, v_texCoord);
|
|
||||||
if(color.w ==0.)
|
|
||||||
discard;
|
|
||||||
gl_FragColor = texture2D(u_texture, v_texCoord);
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
precision highp float;
|
|
||||||
attribute vec3 a_position;
|
|
||||||
attribute vec2 a_uv;
|
|
||||||
uniform mat4 matModelViewProjection;
|
|
||||||
uniform float u_time;
|
|
||||||
// varying float v_time;
|
|
||||||
|
|
||||||
varying vec2 v_texCoord;
|
|
||||||
varying vec4 v_color;
|
|
||||||
float random (in float x) {
|
|
||||||
return fract(sin(x)*1e4);
|
|
||||||
}
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
v_texCoord = a_uv;
|
|
||||||
float z = a_position.z;
|
|
||||||
z = z - mod(u_time * 1000000.0, 5000000.0);
|
|
||||||
gl_Position = matModelViewProjection * vec4(vec2(a_position), z, 1.0);
|
|
||||||
gl_PointSize = 3.0;
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
precision mediump float;
|
|
||||||
uniform sampler2D u_texture;
|
|
||||||
uniform sampler2D u_colorTexture;
|
|
||||||
uniform vec2 u_wind_min;
|
|
||||||
uniform vec2 u_wind_max;
|
|
||||||
varying float v_time;
|
|
||||||
varying vec2 v_texCoord;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
vec2 velocity = mix(u_wind_min, u_wind_max, texture2D(u_texture, v_texCoord).rg);
|
|
||||||
float speed_t = length(velocity) / length(u_wind_max);
|
|
||||||
vec2 ramp_pos = vec2(
|
|
||||||
fract(16.0 * speed_t),
|
|
||||||
floor(16.0 * speed_t) / 16.0);
|
|
||||||
gl_FragColor = texture2D(u_colorTexture, ramp_pos);
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
precision highp float;
|
|
||||||
attribute vec3 a_position;
|
|
||||||
attribute vec2 a_uv;
|
|
||||||
uniform mat4 matModelViewProjection;
|
|
||||||
uniform float u_time;
|
|
||||||
// varying float v_time;
|
|
||||||
|
|
||||||
varying vec2 v_texCoord;
|
|
||||||
varying vec4 v_color;
|
|
||||||
float random (in float x) {
|
|
||||||
return fract(sin(x)*1e4);
|
|
||||||
}
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
v_texCoord = a_uv;
|
|
||||||
gl_Position = matModelViewProjection * vec4(a_position, 1.0);
|
|
||||||
gl_PointSize = 1.0;
|
|
||||||
}
|
|
|
@ -1,32 +1,28 @@
|
||||||
precision mediump float;
|
precision mediump float;
|
||||||
uniform sampler2D u_texture;
|
uniform sampler2D u_texture;
|
||||||
varying vec4 v_color;
|
varying vec4 v_color;
|
||||||
uniform vec4 u_stroke;
|
uniform vec4 u_stroke;
|
||||||
uniform float u_strokeWidth;
|
uniform float u_strokeWidth;
|
||||||
uniform float u_buffer;
|
uniform float u_buffer;
|
||||||
uniform float u_gamma;
|
uniform float u_gamma;
|
||||||
|
uniform float u_opacity;
|
||||||
varying vec2 v_texcoord;
|
varying vec2 v_texcoord;
|
||||||
|
void main(){
|
||||||
void main() {
|
float dist=texture2D(u_texture,vec2(v_texcoord.x,v_texcoord.y)).a;
|
||||||
|
|
||||||
float dist =texture2D(u_texture, vec2(v_texcoord.x,1.0-v_texcoord.y)).r;
|
|
||||||
float alpha;
|
float alpha;
|
||||||
if(u_strokeWidth == 0.0){
|
if(u_strokeWidth==0.){
|
||||||
alpha = smoothstep(u_buffer - u_gamma, u_buffer + u_gamma, dist);
|
alpha=smoothstep(u_buffer-u_gamma,u_buffer+u_gamma,dist);
|
||||||
gl_FragColor = vec4(v_color.rgb, alpha * v_color.a);
|
gl_FragColor=vec4(v_color.rgb,alpha*v_color.a);
|
||||||
}else{
|
}else{
|
||||||
|
if(dist<=u_buffer-u_gamma){
|
||||||
if(dist <= u_buffer - u_gamma){
|
alpha=smoothstep(u_strokeWidth-u_gamma,u_strokeWidth+u_gamma,dist);
|
||||||
|
gl_FragColor=vec4(u_stroke.rgb,alpha*u_stroke.a);
|
||||||
alpha = smoothstep(u_strokeWidth - u_gamma, u_strokeWidth+ u_gamma, dist);
|
}else if(dist<u_buffer){
|
||||||
gl_FragColor = vec4(u_stroke.rgb, alpha * u_stroke.a);
|
alpha=smoothstep(u_buffer-u_gamma,u_buffer+u_gamma,dist);
|
||||||
}else if(dist < u_buffer){
|
gl_FragColor=vec4(alpha*v_color.rgb+(1.-alpha)*u_stroke.rgb,1.*v_color.a*alpha+(1.-alpha)*u_stroke.a);
|
||||||
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{
|
}else{
|
||||||
alpha = 1.0;
|
alpha=1.;
|
||||||
gl_FragColor = vec4(v_color.rgb, alpha * v_color.a);
|
gl_FragColor=vec4(v_color.rgb,alpha*v_color.a);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +1,24 @@
|
||||||
precision mediump float;
|
precision mediump float;
|
||||||
attribute vec2 a_txtsize;
|
attribute vec2 a_txtsize;
|
||||||
attribute vec2 a_txtOffsets;
|
attribute vec2 a_txtOffsets;
|
||||||
|
uniform float u_opacity;
|
||||||
attribute vec4 a_color;
|
attribute vec4 a_color;
|
||||||
uniform mat4 u_model;
|
uniform vec2 u_textTextureSize;// 纹理大小
|
||||||
uniform mat4 u_view;
|
|
||||||
uniform mat4 u_scale;
|
|
||||||
uniform vec2 u_textSize;
|
|
||||||
uniform vec2 u_glSize;
|
uniform vec2 u_glSize;
|
||||||
varying vec2 v_texcoord;
|
varying vec2 v_texcoord;
|
||||||
varying vec4 v_color;
|
varying vec4 v_color;
|
||||||
|
uniform float u_activeId;
|
||||||
|
uniform vec4 u_activeColor;
|
||||||
|
|
||||||
void main() {
|
void main(){
|
||||||
mat4 matModelViewProjection = projectionMatrix * modelViewMatrix;
|
mat4 matModelViewProjection=projectionMatrix*modelViewMatrix;
|
||||||
vec4 cur_position = matModelViewProjection * vec4(position.xy, 0, 1);
|
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);
|
gl_Position=cur_position/cur_position.w+vec4((a_txtOffsets+a_txtsize)/u_glSize*2.,0.,0.);
|
||||||
highp float camera_to_anchor_distance = gl_Position.w;
|
v_color=vec4(a_color.rgb,a_color.a*u_opacity);
|
||||||
// highp float perspective_ratio = clamp(
|
if(pickingId==u_activeId){
|
||||||
// 0.5 + 0.5 * distance_ratio,
|
v_color=u_activeColor;
|
||||||
// 0.0, // Prevents oversized near-field symbols in pitched/overzoomed tiles
|
}
|
||||||
// 4.0);
|
v_texcoord=uv/u_textTextureSize;
|
||||||
v_color = a_color;
|
worldId=id_toPickColor(pickingId);
|
||||||
v_color.a = v_color.a * camera_to_anchor_distance;
|
|
||||||
v_texcoord = uv / u_textSize;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
precision mediump float;
|
|
||||||
|
|
||||||
attribute vec3 a_position;
|
|
||||||
|
|
||||||
varying vec2 v_tex_pos;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
v_tex_pos = vec2(a_position);
|
|
||||||
gl_Position = vec4(1.0 - 2.0 * vec2(a_position), 0, 1);
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
precision mediump float;
|
|
||||||
|
|
||||||
uniform sampler2D u_screen;
|
|
||||||
uniform float u_opacity;
|
|
||||||
|
|
||||||
varying vec2 v_tex_pos;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
vec4 color = texture2D(u_screen, 1.0 - v_tex_pos);
|
|
||||||
// a hack to guarantee opacity fade out even with a value close to 1.0
|
|
||||||
gl_FragColor = vec4(1.0,0.,0.,1.0);
|
|
||||||
}
|
|
|
@ -61,6 +61,7 @@ export default class LineLayer extends Layer {
|
||||||
if (animateOptions.enable) {
|
if (animateOptions.enable) {
|
||||||
|
|
||||||
material.setDefinesvalue('ANIMATE', true);
|
material.setDefinesvalue('ANIMATE', true);
|
||||||
|
this.scene.startAnimate();
|
||||||
const { duration, interval, trailLength, repeat = Infinity } = animateOptions;
|
const { duration, interval, trailLength, repeat = Infinity } = animateOptions;
|
||||||
this.animateDuration = this.scene._engine.clock.getElapsedTime() + duration * repeat;
|
this.animateDuration = this.scene._engine.clock.getElapsedTime() + duration * repeat;
|
||||||
material.upDateUninform({
|
material.upDateUninform({
|
||||||
|
@ -92,6 +93,7 @@ export default class LineLayer extends Layer {
|
||||||
});
|
});
|
||||||
if (animateOptions.enable) {
|
if (animateOptions.enable) {
|
||||||
material.setDefinesvalue('ANIMATE', true);
|
material.setDefinesvalue('ANIMATE', true);
|
||||||
|
this.scene.startAnimate();
|
||||||
}
|
}
|
||||||
|
|
||||||
const mesh = new THREE.LineSegments(geometry, material);
|
const mesh = new THREE.LineSegments(geometry, material);
|
||||||
|
@ -99,10 +101,11 @@ export default class LineLayer extends Layer {
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
_preRender() {
|
preRender() {
|
||||||
if (this.animateDuration > 0 && this.animateDuration < this.scene._engine.clock.getElapsedTime()) {
|
if (this.animateDuration > 0 && this.animateDuration < this.scene._engine.clock.getElapsedTime()) {
|
||||||
this.layerMesh.material.setDefinesvalue('ANIMATE', false);
|
this.layerMesh.material.setDefinesvalue('ANIMATE', false);
|
||||||
this.emit('animateEnd');
|
this.emit('animateEnd');
|
||||||
|
this.scene.stopAnimate();
|
||||||
this.animateDuration = Infinity;
|
this.animateDuration = Infinity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
import Layer from '../core/layer';
|
import Layer from '../core/layer';
|
||||||
import * as THREE from '../core/three';
|
|
||||||
import * as drawPoint from '../layer/render/point';
|
import * as drawPoint from '../layer/render/point';
|
||||||
|
import TextBuffer from '../geom/buffer/point/text';
|
||||||
|
import DrawText from './render/point/drawText';
|
||||||
import Global from '../global';
|
import Global from '../global';
|
||||||
// import PointBuffer from '../geom/buffer/point';
|
// import PointBuffer from '../geom/buffer/point';
|
||||||
import TextBuffer from '../geom/buffer/text';
|
|
||||||
import TextMaterial from '../geom/material/textMaterial';
|
|
||||||
import * as PointBuffer from '../geom/buffer/point/index';
|
import * as PointBuffer from '../geom/buffer/point/index';
|
||||||
const { pointShape } = Global;
|
const { pointShape } = Global;
|
||||||
/**
|
/**
|
||||||
|
@ -15,26 +14,22 @@ const { pointShape } = Global;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export default class PointLayer extends Layer {
|
export default class PointLayer extends Layer {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
this.type = 'point';
|
this.type = 'point';
|
||||||
|
|
||||||
this.init();
|
this.init();
|
||||||
if (!this._hasRender) {
|
if (!this._hasRender) {
|
||||||
this._prepareRender(this.shapeType);
|
this._prepareRender(this.shapeType);
|
||||||
this._hasRender = true;
|
this._hasRender = true;
|
||||||
} else {
|
} else {
|
||||||
this._initAttrs();
|
this._initAttrs();
|
||||||
(this._needUpdateFilter || this._needUpdateColor) ? this._updateFilter() : null;
|
this._needUpdateFilter || this._needUpdateColor
|
||||||
|
? this._updateFilter()
|
||||||
|
: null;
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
_prepareRender() {
|
_prepareRender() {
|
||||||
const { stroke, fill } = this.get('styleOptions');
|
const { stroke, fill } = this.get('styleOptions');
|
||||||
if (this.shapeType === 'text') { // 绘制文本图层
|
|
||||||
this._textPoint();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const style = this.get('styleOptions');
|
const style = this.get('styleOptions');
|
||||||
const activeOption = this.get('activedOptions');
|
const activeOption = this.get('activedOptions');
|
||||||
const config = {
|
const config = {
|
||||||
|
@ -42,36 +37,55 @@ export default class PointLayer extends Layer {
|
||||||
activeColor: activeOption.fill
|
activeColor: activeOption.fill
|
||||||
};
|
};
|
||||||
const pointShapeType = this._getShape();
|
const pointShapeType = this._getShape();
|
||||||
|
|
||||||
switch (pointShapeType) {
|
switch (pointShapeType) {
|
||||||
case 'fill' :// 填充图形
|
case 'fill': { // 填充图形
|
||||||
{
|
if (fill !== 'none') {
|
||||||
if (fill !== 'none') { // 是否填充
|
// 是否填充
|
||||||
const attributes = PointBuffer.FillBuffer(this.layerData, style);
|
const attributes = PointBuffer.FillBuffer(this.layerData, style);
|
||||||
const meshfill = drawPoint.DrawFill(attributes, config);
|
const meshfill = drawPoint.DrawFill(attributes, config);
|
||||||
this.add(meshfill);
|
this.add(meshfill);
|
||||||
}
|
|
||||||
if (stroke !== 'none') { // 是否绘制边界
|
|
||||||
const lineAttribute = PointBuffer.StrokeBuffer(this.layerData, style);
|
|
||||||
const meshStroke = drawPoint.DrawStroke(lineAttribute, config);
|
|
||||||
this.add(meshStroke, 'line');
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case 'image':// 绘制图片标注
|
if (stroke !== 'none') {
|
||||||
{
|
// 是否绘制边界
|
||||||
const imageAttribute = PointBuffer.ImageBuffer(this.layerData, { imagePos: this.scene.image.imagePos });
|
const lineAttribute = PointBuffer.StrokeBuffer(this.layerData, style);
|
||||||
const imageMesh = drawPoint.DrawImage(imageAttribute, { ...style, texture: this.scene.image.texture });
|
const meshStroke = drawPoint.DrawStroke(lineAttribute, config);
|
||||||
this.add(imageMesh);
|
this.add(meshStroke, 'line');
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'normal' : // 原生点
|
|
||||||
{
|
|
||||||
const normalAttribute = PointBuffer.NormalBuffer(this.layerData, style);
|
|
||||||
const normalPointMesh = drawPoint.DrawNormal(normalAttribute, config);
|
|
||||||
this.add(normalPointMesh);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'image': { // 绘制图片标注
|
||||||
|
const imageAttribute = PointBuffer.ImageBuffer(this.layerData, {
|
||||||
|
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(this.layerData, style);
|
||||||
|
const normalPointMesh = drawPoint.DrawNormal(normalAttribute, config);
|
||||||
|
this.add(normalPointMesh);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'text': { // 原生点
|
||||||
|
const { width, height } = this.scene.getSize();
|
||||||
|
const textCfg = {
|
||||||
|
...style,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
activeColor: activeOption.fill
|
||||||
|
};
|
||||||
|
const buffer = new TextBuffer(
|
||||||
|
this.layerData,
|
||||||
|
this.scene.fontAtlasManager
|
||||||
|
);
|
||||||
|
const mesh = new DrawText(buffer, textCfg);
|
||||||
|
this.add(mesh);
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -88,51 +102,14 @@ export default class PointLayer extends Layer {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (pointShape['2d'].indexOf(shape) !== -1 || pointShape['3d'].indexOf(shape) !== -1) {
|
if (
|
||||||
|
pointShape['2d'].indexOf(shape) !== -1 ||
|
||||||
|
pointShape['3d'].indexOf(shape) !== -1
|
||||||
|
) {
|
||||||
return 'fill';
|
return 'fill';
|
||||||
} else if (shape === 'text') {
|
|
||||||
return 'text';
|
|
||||||
} else if (this.scene.image.imagesIds.indexOf(shape) !== -1) {
|
} else if (this.scene.image.imagesIds.indexOf(shape) !== -1) {
|
||||||
return 'image';
|
return 'image';
|
||||||
}
|
}
|
||||||
return 'normal';
|
return 'text';
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
_textPoint() {
|
|
||||||
const styleOptions = this.get('styleOptions');
|
|
||||||
const buffer = new TextBuffer({
|
|
||||||
type: this.shapeType,
|
|
||||||
layerData: this.layerData,
|
|
||||||
style: this.get('styleOptions')
|
|
||||||
});
|
|
||||||
|
|
||||||
buffer.on('completed', () => {
|
|
||||||
const { color, stroke } = styleOptions;
|
|
||||||
const geometry = new THREE.BufferGeometry();
|
|
||||||
geometry.addAttribute('position', new THREE.Float32BufferAttribute(buffer.attributes.originPoints, 3));
|
|
||||||
geometry.addAttribute('uv', new THREE.Float32BufferAttribute(buffer.attributes.textureElements, 2));
|
|
||||||
geometry.addAttribute('a_txtsize', new THREE.Float32BufferAttribute(buffer.attributes.textSizes, 2));
|
|
||||||
geometry.addAttribute('a_txtOffsets', new THREE.Float32BufferAttribute(buffer.attributes.textOffsets, 2));
|
|
||||||
geometry.addAttribute('a_color', new THREE.Float32BufferAttribute(buffer.attributes.colors, 4));
|
|
||||||
const { width, height } = this.scene.getSize();
|
|
||||||
const material = new TextMaterial({
|
|
||||||
name: this.layerId,
|
|
||||||
u_texture: buffer.bufferStruct.textTexture,
|
|
||||||
u_strokeWidth: styleOptions.strokeWidth,
|
|
||||||
u_stroke: stroke,
|
|
||||||
u_textSize: buffer.bufferStruct.textSize,
|
|
||||||
u_gamma: 2 * 1.4142 / 64,
|
|
||||||
u_buffer: 0.65,
|
|
||||||
u_color: color,
|
|
||||||
u_glSize: [ width, height ]
|
|
||||||
});
|
|
||||||
const mesh = new THREE.Mesh(geometry, material);
|
|
||||||
this.add(mesh);
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,7 @@ export default class PolygonLayer extends Layer {
|
||||||
return drawPolygon.DrawLine(attributes, style);
|
return drawPolygon.DrawLine(attributes, style);
|
||||||
} else if (animateOptions.enable) {
|
} else if (animateOptions.enable) {
|
||||||
const { near, far } = this.map.getCameraState();
|
const { near, far } = this.map.getCameraState();
|
||||||
|
this.scene.startAnimate();
|
||||||
return drawPolygon.DrawAnimate(attributes, { ...style, near, far });
|
return drawPolygon.DrawAnimate(attributes, { ...style, near, far });
|
||||||
}
|
}
|
||||||
return drawPolygon.DrawFill(attributes, config);
|
return drawPolygon.DrawFill(attributes, config);
|
||||||
|
|
|
@ -1,23 +1,48 @@
|
||||||
// import * as THREE from '../../../core/three';
|
import * as THREE from '../../../core/three';
|
||||||
// import TextMaterial from '../../../geom/material/textMaterial';
|
import TextMaterial from '../../../geom/material/textMaterial';
|
||||||
// export default function DawText(attributes, texture, style) {
|
|
||||||
// const geometry = new THREE.BufferGeometry();
|
export default function DrawText(attributes, style) {
|
||||||
// const { strokeWidth, stroke, opacity } = style;
|
const geometry = new THREE.BufferGeometry();
|
||||||
// geometry.addAttribute('position', new THREE.Float32BufferAttribute(attributes.originPoints, 3));
|
geometry.addAttribute(
|
||||||
// geometry.addAttribute('uv', new THREE.Float32BufferAttribute(attributes.textureElements, 2));
|
'position',
|
||||||
// geometry.addAttribute('a_txtsize', new THREE.Float32BufferAttribute(attributes.textSizes, 2));
|
new THREE.Float32BufferAttribute(attributes.originPoints, 3)
|
||||||
// geometry.addAttribute('a_txtOffsets', new THREE.Float32BufferAttribute(attributes.textOffsets, 2));
|
);
|
||||||
// geometry.addAttribute('a_color', new THREE.Float32BufferAttribute(attributes.colors, 4));
|
geometry.addAttribute(
|
||||||
// const material = new TextMaterial({
|
'uv',
|
||||||
// name: this.layerId,
|
new THREE.Float32BufferAttribute(attributes.textureElements, 2)
|
||||||
// u_texture: texture,
|
);
|
||||||
// u_strokeWidth: 1,
|
geometry.addAttribute(
|
||||||
// u_stroke: stroke,
|
'a_txtsize',
|
||||||
// u_textSize: buffer.bufferStruct.textSize,
|
new THREE.Float32BufferAttribute(attributes.textSizes, 2)
|
||||||
// u_gamma: 0.11,
|
);
|
||||||
// u_buffer: 0.8,
|
geometry.addAttribute(
|
||||||
// u_color: color,
|
'a_txtOffsets',
|
||||||
// u_glSize: [ width, height ]
|
new THREE.Float32BufferAttribute(attributes.textOffsets, 2)
|
||||||
// });
|
);
|
||||||
// const mesh = new THREE.Mesh(geometry, material);
|
geometry.addAttribute(
|
||||||
// }
|
'a_color',
|
||||||
|
new THREE.Float32BufferAttribute(attributes.colors, 4)
|
||||||
|
);
|
||||||
|
geometry.addAttribute(
|
||||||
|
'pickingId',
|
||||||
|
new THREE.Float32BufferAttribute(attributes.pickingIds, 1)
|
||||||
|
);
|
||||||
|
const { strokeWidth, width, stroke, height, opacity, activeColor } = style;
|
||||||
|
const material = new TextMaterial({
|
||||||
|
name: this.layerId,
|
||||||
|
u_texture: attributes.texture,
|
||||||
|
u_strokeWidth: strokeWidth,
|
||||||
|
u_stroke: stroke,
|
||||||
|
u_textTextureSize: [
|
||||||
|
attributes.fontAtlas.width,
|
||||||
|
attributes.fontAtlas.height
|
||||||
|
],
|
||||||
|
u_gamma: 0.2,
|
||||||
|
u_buffer: 0.75,
|
||||||
|
u_opacity: opacity,
|
||||||
|
u_glSize: [ width, height ],
|
||||||
|
u_activeColor: activeColor
|
||||||
|
});
|
||||||
|
const mesh = new THREE.Mesh(geometry, material);
|
||||||
|
return mesh;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
export function nextPowOfTwo(number) {
|
||||||
|
return Math.pow(2, Math.ceil(Math.log2(number)));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildMapping({
|
||||||
|
characterSet,
|
||||||
|
getFontWidth,
|
||||||
|
fontHeight,
|
||||||
|
buffer,
|
||||||
|
maxCanvasWidth,
|
||||||
|
mapping = {},
|
||||||
|
xOffset = 0,
|
||||||
|
yOffset = 0
|
||||||
|
}) {
|
||||||
|
let row = 0;
|
||||||
|
let x = xOffset;
|
||||||
|
Array.from(characterSet).forEach((char, i) => {
|
||||||
|
if (!mapping[char]) {
|
||||||
|
const width = getFontWidth(char, i);
|
||||||
|
if (x + width + buffer * 2 > maxCanvasWidth) {
|
||||||
|
x = 0;
|
||||||
|
row++;
|
||||||
|
}
|
||||||
|
mapping[char] = {
|
||||||
|
x: x + buffer,
|
||||||
|
y: yOffset + row * (fontHeight + buffer * 2) + buffer,
|
||||||
|
width,
|
||||||
|
height: fontHeight,
|
||||||
|
mask: true
|
||||||
|
};
|
||||||
|
x += width + buffer * 2;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const rowHeight = fontHeight + buffer * 2;
|
||||||
|
|
||||||
|
return {
|
||||||
|
mapping,
|
||||||
|
xOffset: x,
|
||||||
|
yOffset: yOffset + row * rowHeight,
|
||||||
|
canvasHeight: nextPowOfTwo(yOffset + (row + 1) * rowHeight)
|
||||||
|
};
|
||||||
|
}
|
Loading…
Reference in New Issue