diff --git a/demos/06_text.html b/demos/06_text.html index 52c44ea774..79f636e663 100644 --- a/demos/06_text.html +++ b/demos/06_text.html @@ -25,22 +25,24 @@ const scene = new L7.Scene({ mapStyle: 'dark', // 样式URL center: [ 120.19382669582967, 30.258134 ], pitch: 0, - zoom: 3 + zoom: 1 }); window.scene = scene; scene.on('loaded', () => { $.get('./data/provincePoint.geojson', data => { + // data.features = data.features.slice(0,1); scene.PointLayer({ zIndex: 2 }) .source(data) .shape('name', 'text') + .active(true) .size(12) // default 1 - .color('#fff') + .color('name') .style({ stroke: '#999', - strokeWidth: 2, - opacity: 0.85 + strokeWidth: 0, + opacity: 1.0 }) .render(); }); diff --git a/package.json b/package.json index 94993685ce..7dc407177f 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@antv/l7", - "version": "1.1.6", + "version": "1.1.7", "description": "Large-scale WebGL-powered Geospatial Data Visualization", "main": "build/l7.js", "browser": "build/l7.js", diff --git a/src/core/controller/layer.js b/src/core/controller/layer.js deleted file mode 100644 index 9002769ca4..0000000000 --- a/src/core/controller/layer.js +++ /dev/null @@ -1,3 +0,0 @@ - -export class layerControl { -} diff --git a/src/core/controller/map.js b/src/core/controller/map.js deleted file mode 100644 index 3068e61cfe..0000000000 --- a/src/core/controller/map.js +++ /dev/null @@ -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() { - - } - -} diff --git a/src/core/engine/index.js b/src/core/engine/index.js index a08bc77d76..9f04690f43 100644 --- a/src/core/engine/index.js +++ b/src/core/engine/index.js @@ -31,7 +31,6 @@ export default class Engine extends EventEmitter { } run() { - this.update(); this.engineID = requestAnimationFrame(this.run.bind(this)); } diff --git a/src/core/layer.js b/src/core/layer.js index 4852e0afb7..1403fee10e 100644 --- a/src/core/layer.js +++ b/src/core/layer.js @@ -112,6 +112,8 @@ export default class Layer extends Base { if (type === 'fill') { this._addPickMesh(object);// 不对边界线进行拾取 } + this.scene._engine.update(); + setTimeout(() => this.scene._engine.update(), 500); } remove(object) { if (object.type === 'composer') { diff --git a/src/core/scene.js b/src/core/scene.js index 0ab5e653f5..5d22c4e67d 100644 --- a/src/core/scene.js +++ b/src/core/scene.js @@ -2,6 +2,7 @@ import Engine from './engine'; import { LAYER_MAP } from '../layer'; import Base from './base'; import LoadImage from './image'; +import FontAtlasManager from '../geom/buffer/point/text/font-manager'; // import WorkerPool from './worker'; // import { MapProvider } from '../map/AMap'; import { getMap } from '../map/index'; @@ -16,12 +17,14 @@ export default class Scene extends Base { this._initMap(); // this._initAttribution(); // 暂时取消,后面作为组件去加载 this.addImage(); + this.fontAtlasManager = new FontAtlasManager(); this._layers = []; + this.animateCount = 0; } _initEngine(mapContainer) { this._engine = new Engine(mapContainer, this); - this._engine.run(); + this.registerMapEvent(); // this.workerPool = new WorkerPool(); compileBuiltinModules(); } @@ -38,8 +41,8 @@ export default class Scene extends Base { this._container = document.getElementById(Map.container); // const Map = new MapProvider(this.mapContainer, this._attrs); Map.on('mapLoad', () => { - this._initEngine(Map.renderDom); this.map = Map.map; + this._initEngine(Map.renderDom); Map.asyncCamera(this._engine); this.initLayer(); this._registEvents(); @@ -115,5 +118,31 @@ export default class Scene extends Base { layer.destroy(); 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); + } } diff --git a/src/core/three.js b/src/core/three.js index 63c37b9ac8..f832dfbf66 100644 --- a/src/core/three.js +++ b/src/core/three.js @@ -14,7 +14,9 @@ export { WebGLRenderTarget } from 'three/src/renderers/WebGLRenderTarget.js'; export { PerspectiveCamera } from 'three/src/cameras/PerspectiveCamera.js'; export { OrthographicCamera } from 'three/src/cameras/OrthographicCamera.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 { BoxBufferGeometry } from 'three/src/geometries/BoxGeometry.js'; export { Raycaster } from 'three/src/core/Raycaster.js'; export { UniformsUtils } from 'three/src/renderers/shaders/UniformsUtils.js'; export { Matrix4 } from 'three/src/math/Matrix4.js'; @@ -39,4 +41,5 @@ export { BufferAttribute } from 'three/src/core/BufferAttribute.js'; +export { InstancedBufferAttribute } from 'three/src/core/InstancedBufferAttribute' // export * from '../../build/three.js'; diff --git a/src/geom/buffer/point/sdfCommonWords.js b/src/geom/buffer/point/sdfCommonWords.js deleted file mode 100644 index 3567025d13..0000000000 --- a/src/geom/buffer/point/sdfCommonWords.js +++ /dev/null @@ -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; diff --git a/src/geom/buffer/point/text.js b/src/geom/buffer/point/text.js new file mode 100644 index 0000000000..2552bdf894 --- /dev/null +++ b/src/geom/buffer/point/text.js @@ -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; +} diff --git a/src/geom/buffer/point/text/font-manager.js b/src/geom/buffer/point/text/font-manager.js new file mode 100644 index 0000000000..0fdb8bb53f --- /dev/null +++ b/src/geom/buffer/point/text/font-manager.js @@ -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}`; + } +} diff --git a/src/geom/buffer/point/text/lru-cache.js b/src/geom/buffer/point/text/lru-cache.js new file mode 100644 index 0000000000..8b417e53e2 --- /dev/null +++ b/src/geom/buffer/point/text/lru-cache.js @@ -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); + } +} diff --git a/src/geom/buffer/point/textBuffer.js b/src/geom/buffer/point/textBuffer.js deleted file mode 100644 index 211dc00ff6..0000000000 --- a/src/geom/buffer/point/textBuffer.js +++ /dev/null @@ -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; -// } diff --git a/src/geom/buffer/text.js b/src/geom/buffer/text.js deleted file mode 100644 index 4680ffeafe..0000000000 --- a/src/geom/buffer/text.js +++ /dev/null @@ -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; - } -} diff --git a/src/geom/material/rainPass.js b/src/geom/material/rainPass.js deleted file mode 100644 index d80b0b0fc5..0000000000 --- a/src/geom/material/rainPass.js +++ /dev/null @@ -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'); - } - - } - -} diff --git a/src/geom/material/textMaterial.js b/src/geom/material/textMaterial.js index b0c51bc4b4..7e9d52ca1b 100644 --- a/src/geom/material/textMaterial.js +++ b/src/geom/material/textMaterial.js @@ -9,12 +9,13 @@ export default function TextMaterial(options) { u_texture: { value: options.u_texture }, u_strokeWidth: { value: options.u_strokeWidth }, 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_gamma: { value: options.u_gamma }, 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, diff --git a/src/geom/shader/polygon_frag1.glsl b/src/geom/shader/polygon_frag1.glsl deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/geom/shader/polygon_vert1.glsl b/src/geom/shader/polygon_vert1.glsl deleted file mode 100644 index 9c8508c674..0000000000 --- a/src/geom/shader/polygon_vert1.glsl +++ /dev/null @@ -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); -} \ No newline at end of file diff --git a/src/geom/shader/rainPass_frag.glsl b/src/geom/shader/rainPass_frag.glsl deleted file mode 100644 index 555ee65f21..0000000000 --- a/src/geom/shader/rainPass_frag.glsl +++ /dev/null @@ -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); - -} \ No newline at end of file diff --git a/src/geom/shader/rainPass_vert.glsl b/src/geom/shader/rainPass_vert.glsl deleted file mode 100644 index 531a4b1557..0000000000 --- a/src/geom/shader/rainPass_vert.glsl +++ /dev/null @@ -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; -} \ No newline at end of file diff --git a/src/geom/shader/rain_frag.glsl b/src/geom/shader/rain_frag.glsl deleted file mode 100644 index 350368fdab..0000000000 --- a/src/geom/shader/rain_frag.glsl +++ /dev/null @@ -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); - -} \ No newline at end of file diff --git a/src/geom/shader/rain_vert.glsl b/src/geom/shader/rain_vert.glsl deleted file mode 100644 index 69b4129d2d..0000000000 --- a/src/geom/shader/rain_vert.glsl +++ /dev/null @@ -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; -} \ No newline at end of file diff --git a/src/geom/shader/text_frag.glsl b/src/geom/shader/text_frag.glsl index 886403db72..f29d97a435 100644 --- a/src/geom/shader/text_frag.glsl +++ b/src/geom/shader/text_frag.glsl @@ -1,32 +1,28 @@ precision mediump float; uniform sampler2D u_texture; -varying vec4 v_color; +varying vec4 v_color; uniform vec4 u_stroke; uniform float u_strokeWidth; uniform float u_buffer; uniform float u_gamma; - +uniform float u_opacity; varying vec2 v_texcoord; - -void main() { - - float dist =texture2D(u_texture, vec2(v_texcoord.x,1.0-v_texcoord.y)).r; +void main(){ + float dist=texture2D(u_texture,vec2(v_texcoord.x,v_texcoord.y)).a; float alpha; - if(u_strokeWidth == 0.0){ - alpha = smoothstep(u_buffer - u_gamma, u_buffer + u_gamma, dist); - gl_FragColor = vec4(v_color.rgb, alpha * v_color.a); + if(u_strokeWidth==0.){ + 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+ 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+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); + 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); + }else if(dist 0 && this.animateDuration < this.scene._engine.clock.getElapsedTime()) { this.layerMesh.material.setDefinesvalue('ANIMATE', false); this.emit('animateEnd'); + this.scene.stopAnimate(); this.animateDuration = Infinity; } } diff --git a/src/layer/pointLayer.js b/src/layer/pointLayer.js index b9a5817c12..de3cbf3a57 100644 --- a/src/layer/pointLayer.js +++ b/src/layer/pointLayer.js @@ -1,10 +1,9 @@ import Layer from '../core/layer'; -import * as THREE from '../core/three'; 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 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'; const { pointShape } = Global; /** @@ -15,26 +14,22 @@ const { pointShape } = Global; */ export default class PointLayer extends Layer { - render() { this.type = 'point'; - this.init(); if (!this._hasRender) { this._prepareRender(this.shapeType); this._hasRender = true; } else { this._initAttrs(); - (this._needUpdateFilter || this._needUpdateColor) ? this._updateFilter() : null; + this._needUpdateFilter || this._needUpdateColor + ? this._updateFilter() + : null; } return this; } _prepareRender() { const { stroke, fill } = this.get('styleOptions'); - if (this.shapeType === 'text') { // 绘制文本图层 - this._textPoint(); - return; - } const style = this.get('styleOptions'); const activeOption = this.get('activedOptions'); const config = { @@ -42,36 +37,55 @@ export default class PointLayer extends Layer { activeColor: activeOption.fill }; const pointShapeType = this._getShape(); - switch (pointShapeType) { - case 'fill' :// 填充图形 - { - if (fill !== 'none') { // 是否填充 - const attributes = PointBuffer.FillBuffer(this.layerData, style); - const meshfill = drawPoint.DrawFill(attributes, config); - 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 'fill': { // 填充图形 + if (fill !== 'none') { + // 是否填充 + const attributes = PointBuffer.FillBuffer(this.layerData, style); + const meshfill = drawPoint.DrawFill(attributes, config); + this.add(meshfill); } - 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; + if (stroke !== 'none') { + // 是否绘制边界 + const lineAttribute = PointBuffer.StrokeBuffer(this.layerData, style); + const meshStroke = drawPoint.DrawStroke(lineAttribute, config); + this.add(meshStroke, 'line'); } + 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: return null; } @@ -88,51 +102,14 @@ export default class PointLayer extends Layer { 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'; - } else if (shape === 'text') { - return 'text'; } else if (this.scene.image.imagesIds.indexOf(shape) !== -1) { 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); - - }); - - } - } - diff --git a/src/layer/polygonLayer.js b/src/layer/polygonLayer.js index bea74f5975..17c95244ef 100644 --- a/src/layer/polygonLayer.js +++ b/src/layer/polygonLayer.js @@ -44,6 +44,7 @@ export default class PolygonLayer extends Layer { return drawPolygon.DrawLine(attributes, style); } else if (animateOptions.enable) { const { near, far } = this.map.getCameraState(); + this.scene.startAnimate(); return drawPolygon.DrawAnimate(attributes, { ...style, near, far }); } return drawPolygon.DrawFill(attributes, config); diff --git a/src/layer/render/point/drawText.js b/src/layer/render/point/drawText.js index 0be1854239..0604938e61 100644 --- a/src/layer/render/point/drawText.js +++ b/src/layer/render/point/drawText.js @@ -1,23 +1,48 @@ -// import * as THREE from '../../../core/three'; -// import TextMaterial from '../../../geom/material/textMaterial'; -// export default function DawText(attributes, texture, 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: texture, -// 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); -// } +import * as THREE from '../../../core/three'; +import TextMaterial from '../../../geom/material/textMaterial'; + +export default function DrawText(attributes, style) { + const geometry = new THREE.BufferGeometry(); + 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) + ); + 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; +} diff --git a/src/util/font-util.js b/src/util/font-util.js new file mode 100644 index 0000000000..1e0b96a98d --- /dev/null +++ b/src/util/font-util.js @@ -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) + }; +}