diff --git a/packages/core/src/services/layer/StyleAttributeService.ts b/packages/core/src/services/layer/StyleAttributeService.ts index cb0106c1fd..e4b0293013 100644 --- a/packages/core/src/services/layer/StyleAttributeService.ts +++ b/packages/core/src/services/layer/StyleAttributeService.ts @@ -246,7 +246,6 @@ export default class StyleAttributeService implements IStyleAttributeService { const attributes: { [attributeName: string]: IAttribute; } = {}; - descriptors.forEach((descriptor, attributeIdx) => { if (descriptor) { // IAttribute 参数透传 diff --git a/packages/layers/package.json b/packages/layers/package.json index 07ba65fc5a..6a1fa83048 100644 --- a/packages/layers/package.json +++ b/packages/layers/package.json @@ -24,6 +24,7 @@ "@l7/utils": "^0.0.1", "@turf/meta": "^6.0.2", "@types/d3-color": "^1.2.2", + "inversify": "^5.0.1", "d3-array": "^2.3.1", "d3-color": "^1.4.0", "d3-scale": "^3.1.0", diff --git a/packages/layers/src/core/BaseLayer.ts b/packages/layers/src/core/BaseLayer.ts index 91faa909aa..42d9229866 100644 --- a/packages/layers/src/core/BaseLayer.ts +++ b/packages/layers/src/core/BaseLayer.ts @@ -103,6 +103,9 @@ export default class BaseLayer implements ILayer { protected layerSource: Source; + @lazyInject(TYPES.IRendererService) + protected readonly rendererService: IRendererService; + private encodedData: IEncodeFeature[]; private configSchema: object; @@ -117,9 +120,6 @@ export default class BaseLayer implements ILayer { @lazyInject(TYPES.IShaderModuleService) private readonly shaderModuleService: IShaderModuleService; - @lazyInject(TYPES.IRendererService) - private readonly rendererService: IRendererService; - @lazyInject(TYPES.IMapService) private readonly map: IMapService; diff --git a/packages/layers/src/core/shape/Path.ts b/packages/layers/src/core/shape/Path.ts new file mode 100644 index 0000000000..365fd01bba --- /dev/null +++ b/packages/layers/src/core/shape/Path.ts @@ -0,0 +1,65 @@ +export type IPosition = [number, number, number] | [number, number]; +export type IPath = IPosition[]; +export enum ShapeType3D { + CYLINDER = 'cylinder', + SQUARECOLUMN = 'squareColumn', + TRIANGLECOLUMN = 'triangleColumn', + HEXAGONCOLUMN = 'hexagonColumn', + PENTAGONCOLUMN = 'pentagonColumn', +} +export enum ShapeType2D { + CIRCLE = 'circle', + SQUARE = 'square', + TRIANGLE = 'triangle', + HEXAGON = 'hexagon', + PENTAGON = 'pentagon', +} + +/** + * 生成规则多边形顶点个数 + * @param pointCount 顶点个数 3 => 三角形 + * @param start 顶点起始角度 调整图形的方向 + */ +export function polygonPath(pointCount: number, start: number = 0): IPath { + const step = (Math.PI * 2) / pointCount; + const line = []; + for (let i = 0; i < pointCount; i++) { + line.push(step * i + (start * Math.PI) / 12); + } + const path: IPath = line.map((t) => { + const x = Math.sin(t + Math.PI / 4); + const y = Math.cos(t + Math.PI / 4); + return [x, y, 0]; + }); + // path.push(path[0]); + return path; +} + +export function circle(): IPath { + return polygonPath(30); +} +export function square(): IPath { + return polygonPath(4); +} +export function triangle(): IPath { + return polygonPath(3); +} +export function hexagon(): IPath { + return polygonPath(6); +} +export function pentagon(): IPath { + return polygonPath(5); +} + +export const geometryShape = { + [ShapeType2D.CIRCLE]: circle, + [ShapeType2D.HEXAGON]: hexagon, + [ShapeType2D.TRIANGLE]: triangle, + [ShapeType2D.SQUARE]: square, + [ShapeType2D.PENTAGON]: pentagon, + [ShapeType3D.CYLINDER]: circle, + [ShapeType3D.HEXAGONCOLUMN]: hexagon, + [ShapeType3D.TRIANGLECOLUMN]: triangle, + [ShapeType3D.SQUARECOLUMN]: square, + [ShapeType3D.PENTAGONCOLUMN]: pentagon, +}; diff --git a/packages/layers/src/core/shape/extrude.ts b/packages/layers/src/core/shape/extrude.ts new file mode 100644 index 0000000000..6fa7ae3f48 --- /dev/null +++ b/packages/layers/src/core/shape/extrude.ts @@ -0,0 +1,83 @@ +import earcut from 'earcut'; +import { IPath } from './Path'; +export interface IExtrudeGeomety { + positions: number[]; + index: number[]; +} +/** + * 拉伸多边形顶点,返回拉伸后的顶点信息 + * @param paths 路径数据组 + * @param extrude 是否拉伸 + */ +export default function extrudePolygon(path: IPath[]): IExtrudeGeomety { + const p1 = path[0][0]; + const p2 = path[0][path[0].length - 1]; + if (p1[0] === p2[0] && p1[1] === p2[1]) { + path[0] = path[0].slice(0, path[0].length - 1); + } + + const n = path[0].length; + const flattengeo = earcut.flatten(path); + const { vertices, dimensions } = flattengeo; + const positions = []; + const indexArray = []; + // 设置顶部z值 + for (let j = 0; j < vertices.length / dimensions; j++) { + if (dimensions === 2) { + positions.push(vertices[j * 2], vertices[j * 2 + 1], 1); + } else { + positions.push(vertices[j * 3], vertices[j * 3 + 1], 1); + } + } + const triangles = earcut( + flattengeo.vertices, + flattengeo.holes, + flattengeo.dimensions, + ); + indexArray.push(...triangles); + for (let i = 0; i < n; i++) { + const prePoint = flattengeo.vertices.slice( + i * dimensions, + (i + 1) * dimensions, + ); + let nextPoint = flattengeo.vertices.slice( + (i + 2) * dimensions, + (i + 3) * dimensions, + ); + if (nextPoint.length === 0) { + nextPoint = flattengeo.vertices.slice(0, dimensions); + } + const indexOffset = positions.length / 3; + positions.push( + prePoint[0], + prePoint[1], + 1, + nextPoint[0], + nextPoint[1], + 1, + prePoint[0], + prePoint[1], + 0, + nextPoint[0], + nextPoint[1], + 0, + ); + indexArray.push(...[1, 2, 0, 3, 2, 1].map((v) => v + indexOffset)); + } + return { + positions, + index: indexArray, + }; +} +export function fillPolygon(points: IPath[]) { + const flattengeo = earcut.flatten(points); + const triangles = earcut( + flattengeo.vertices, + flattengeo.holes, + flattengeo.dimensions, + ); + return { + positions: flattengeo.vertices, + index: triangles, + }; +} diff --git a/packages/layers/src/point/triangulation.ts b/packages/layers/src/core/triangulation.ts similarity index 65% rename from packages/layers/src/point/triangulation.ts rename to packages/layers/src/core/triangulation.ts index 09e3188ec6..ca390c3359 100644 --- a/packages/layers/src/point/triangulation.ts +++ b/packages/layers/src/core/triangulation.ts @@ -1,11 +1,21 @@ import { IEncodeFeature } from '@l7/core'; import { vec3 } from 'gl-matrix'; +import getNormals from '../utils/polylineNormal'; import extrudePolygon, { IExtrudeGeomety } from './shape/extrude'; -import { geometryShape, ShapeType2D, ShapeType3D } from './shape/Path'; +import { + geometryShape, + IPosition, + ShapeType2D, + ShapeType3D, +} from './shape/Path'; interface IGeometryCache { [key: string]: IExtrudeGeomety; } const GeometryCache: IGeometryCache = {}; +/** + * 计算2D 填充点图顶点 + * @param feature 映射feature + */ export function PointFillTriangulation(feature: IEncodeFeature) { const coordinates = feature.coordinates as number[]; return { @@ -15,6 +25,10 @@ export function PointFillTriangulation(feature: IEncodeFeature) { }; } +/** + * 计算3D 拉伸点图 + * @param feature 映射feature + */ export function PointExtrudeTriangulation(feature: IEncodeFeature) { const { shape } = feature; const { positions, index } = getGeometry(shape as ShapeType3D); @@ -26,6 +40,46 @@ export function PointExtrudeTriangulation(feature: IEncodeFeature) { }; } +/** + * 计算图片标注 + * @param feature 映射feature + */ +export function PointImageTriangulation(feature: IEncodeFeature) { + const coordinates = feature.coordinates as number[]; + return { + vertices: [...coordinates], + indices: [0], + size: coordinates.length, + }; +} + +/** + * 线三角化 + * @param feature 映射feature + */ +export function LineTriangulation(feature: IEncodeFeature) { + const { coordinates } = feature; + const line = getNormals(coordinates as number[][], false, 0); + return { + vertices: line.attrPos, // [ x,y,z, distance, miter ] + indices: line.attrIndex, + normals: line.normals, + size: 5, + }; +} + +export function PolygonExtrudeTriangulation(feature: IEncodeFeature) { + const coordinates = feature.coordinates as IPosition[][]; + const { positions, index } = extrudePolygon(coordinates); + + return { + vertices: positions, // [ x, y, z ] + indices: index, + normals: Array.from(computeVertexNormals(positions, index)), + size: 3, + }; +} + function getGeometry(shape: ShapeType3D): IExtrudeGeomety { if (GeometryCache && GeometryCache[shape]) { return GeometryCache[shape]; @@ -37,6 +91,7 @@ function getGeometry(shape: ShapeType3D): IExtrudeGeomety { GeometryCache[shape] = geometry; return geometry; } + function computeVertexNormals( positions: number[], indexArray: number[], @@ -75,6 +130,7 @@ function computeVertexNormals( normalizeNormals(normals); return normals; } + function normalizeNormals(normals: Float32Array) { for (let i = 0, li = normals.length; i < li; i += 3) { const normal = vec3.fromValues(normals[i], normals[i + 1], normals[i + 2]); @@ -83,3 +139,9 @@ function normalizeNormals(normals: Float32Array) { normals.set(newNormal, i); } } + +function checkIsClosed(points: number[][][]) { + const p1 = points[0][0]; + const p2 = points[0][points[0].length - 1]; + return p1[0] === p2[0] && p1[1] === p2[1]; +} diff --git a/packages/layers/src/index.ts b/packages/layers/src/index.ts index aa5ecc6e54..a373b611f2 100644 --- a/packages/layers/src/index.ts +++ b/packages/layers/src/index.ts @@ -1,11 +1,13 @@ import { container, ILayerPlugin, TYPES } from '@l7/core'; import BaseLayer from './core/BaseLayer'; -import Point3dLayer from './point/extrude'; // import HeatMapLayer from './heatmap'; -// import Line from './line'; +import LineLayer from './line/index'; +import Point3dLayer from './point/extrude'; +import PointImageLayer from './point/image'; import PointLayer from './point/index'; // import Point from './point/point'; import PolygonLayer from './polygon'; +import Polygon3DLayer from './polygon/polygon3D'; // import ImageLayer from './raster'; import ConfigSchemaValidationPlugin from './plugins/ConfigSchemaValidationPlugin'; @@ -65,7 +67,9 @@ export { PointLayer, PolygonLayer, Point3dLayer, - // Point, + PointImageLayer, + LineLayer, + Polygon3DLayer, // Line, // ImageLayer, // HeatMapLayer, diff --git a/packages/layers/src/line/index.ts b/packages/layers/src/line/index.ts index 59d2fe5818..127d3847c1 100644 --- a/packages/layers/src/line/index.ts +++ b/packages/layers/src/line/index.ts @@ -1,102 +1,154 @@ -// import { -// gl, -// IRendererService, -// IShaderModuleService, -// lazyInject, -// TYPES, -// } from '@l7/core'; -// import BaseLayer from '../core/BaseLayer'; -// import LineBuffer from './buffers/line'; -// import line_frag from './shaders/line_frag.glsl'; -// import line_vert from './shaders/line_vert.glsl'; -// export default class LineLayer extends BaseLayer { -// public name: string = 'LineLayer'; -// @lazyInject(TYPES.IShaderModuleService) -// private readonly shaderModule: IShaderModuleService; +import { AttributeType, gl, IEncodeFeature, ILayer } from '@l7/core'; +import BaseLayer from '../core/BaseLayer'; +import { LineTriangulation } from '../core/triangulation'; +import line_frag from './shaders/line_frag.glsl'; +import line_vert from './shaders/line_vert.glsl'; +interface IPointLayerStyleOptions { + opacity: number; +} +export default class LineLayer extends BaseLayer { + public name: string = 'LineLayer'; -// @lazyInject(TYPES.IRendererService) -// private readonly renderer: IRendererService; + protected getConfigSchema() { + return { + properties: { + opacity: { + type: 'number', + minimum: 0, + maximum: 1, + }, + }, + }; + } -// protected renderModels() { -// this.models.forEach((model) => -// model.draw({ -// uniforms: { -// u_ModelMatrix: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], -// }, -// }), -// ); -// return this; -// } -// protected buildModels(): void { -// this.shaderModule.registerModule('line', { -// vs: line_vert, -// fs: line_frag, -// }); + protected renderModels() { + const { opacity } = this.getStyleOptions(); + this.models.forEach((model) => + model.draw({ + uniforms: { + u_Opacity: opacity || 0, + }, + }), + ); + return this; + } -// this.models = []; -// const { vs, fs, uniforms } = this.shaderModule.getModule('line'); -// const buffer = new LineBuffer({ -// data: this.getEncodedData(), -// style: this.styleOption, -// }); -// const { -// createAttribute, -// createBuffer, -// createElements, -// createModel, -// } = this.renderer; + protected buildModels() { + this.registerBuiltinAttributes(this); + this.models = [ + this.buildLayerModel({ + moduleName: 'line', + vertexShader: line_vert, + fragmentShader: line_frag, + triangulation: LineTriangulation, + blend: { + enable: true, + func: { + srcRGB: gl.SRC_ALPHA, + srcAlpha: 1, + dstRGB: gl.ONE_MINUS_SRC_ALPHA, + dstAlpha: 1, + }, + }, + }), + ]; + } -// this.models.push( -// createModel({ -// attributes: { -// a_Position: createAttribute({ -// buffer: createBuffer({ -// data: buffer.attributes.positions, -// type: gl.FLOAT, -// }), -// size: 3, -// }), -// a_normal: createAttribute({ -// buffer: createBuffer({ -// data: buffer.attributes.normals, -// type: gl.FLOAT, -// }), -// size: 3, -// }), -// a_color: createAttribute({ -// buffer: createBuffer({ -// data: buffer.attributes.colors, -// type: gl.FLOAT, -// }), -// size: 4, -// }), -// a_size: createAttribute({ -// buffer: createBuffer({ -// data: buffer.attributes.sizes, -// type: gl.FLOAT, -// }), -// size: 1, -// }), -// a_miter: createAttribute({ -// buffer: createBuffer({ -// data: buffer.attributes.miters, -// type: gl.FLOAT, -// }), -// size: 1, -// }), -// }, -// uniforms: { -// ...uniforms, -// u_opacity: this.styleOption.opacity as number, -// }, -// fs, -// vs, -// count: buffer.indexArray.length, -// elements: createElements({ -// data: buffer.indexArray, -// type: gl.UNSIGNED_INT, -// }), -// }), -// ); -// } -// } + private registerBuiltinAttributes(layer: ILayer) { + // point layer size; + layer.styleAttributeService.registerStyleAttribute({ + name: 'size', + type: AttributeType.Attribute, + descriptor: { + name: 'a_Size', + buffer: { + // give the WebGL driver a hint that this buffer may change + usage: gl.DYNAMIC_DRAW, + data: [], + type: gl.FLOAT, + }, + size: 1, + update: ( + feature: IEncodeFeature, + featureIdx: number, + vertex: number[], + attributeIdx: number, + ) => { + const { size } = feature; + return Array.isArray(size) ? [size[0]] : [size as number]; + }, + }, + }); + + // point layer size; + layer.styleAttributeService.registerStyleAttribute({ + name: 'normal', + type: AttributeType.Attribute, + descriptor: { + name: 'a_Normal', + buffer: { + // give the WebGL driver a hint that this buffer may change + usage: gl.STATIC_DRAW, + data: [], + type: gl.FLOAT, + }, + size: 3, + update: ( + feature: IEncodeFeature, + featureIdx: number, + vertex: number[], + attributeIdx: number, + normal: number[], + ) => { + return normal; + }, + }, + }); + + layer.styleAttributeService.registerStyleAttribute({ + name: 'miter', + type: AttributeType.Attribute, + descriptor: { + name: 'a_Miter', + buffer: { + // give the WebGL driver a hint that this buffer may change + usage: gl.DYNAMIC_DRAW, + data: [], + type: gl.FLOAT, + }, + size: 1, + update: ( + feature: IEncodeFeature, + featureIdx: number, + vertex: number[], + attributeIdx: number, + ) => { + return [vertex[4]]; + }, + }, + }); + + layer.styleAttributeService.registerStyleAttribute({ + name: 'distance', + type: AttributeType.Attribute, + descriptor: { + name: 'a_Distance', + buffer: { + // give the WebGL driver a hint that this buffer may change + usage: gl.DYNAMIC_DRAW, + data: [], + type: gl.FLOAT, + }, + size: 1, + update: ( + feature: IEncodeFeature, + featureIdx: number, + vertex: number[], + attributeIdx: number, + ) => { + return [vertex[3]]; + }, + }, + }); + } +} diff --git a/packages/layers/src/line/shaders/line_vert.glsl b/packages/layers/src/line/shaders/line_vert.glsl index fd3bc6d43e..3cc4620ea4 100644 --- a/packages/layers/src/line/shaders/line_vert.glsl +++ b/packages/layers/src/line/shaders/line_vert.glsl @@ -1,11 +1,11 @@ -attribute float a_miter; -attribute vec4 a_color; -attribute float a_size; -attribute float a_distance; +attribute float a_Miter; +attribute vec4 a_Color; +attribute float a_Size; +attribute float a_Distance; attribute float a_dash_array; attribute float a_total_distance; -attribute vec3 a_normal; +attribute vec3 a_Normal; attribute vec3 a_Position; uniform mat4 u_ModelMatrix; @@ -14,9 +14,9 @@ varying float v_dash_array; varying vec3 v_normal; #pragma include "projection" void main() { - v_normal = a_normal; - v_color = a_color; - vec3 size = a_miter * a_size * v_normal; + v_normal = a_Normal; + v_color = a_Color; + vec3 size = a_Miter * a_Size * v_normal; vec2 offset = project_pixel(size.xy); vec4 project_pos = project_position(vec4(a_Position.xy, 0, 1.0)); gl_Position = project_common_position_to_clipspace(vec4(project_pos.xy + offset, 0, 1.0)); diff --git a/packages/layers/src/plugins/RegisterStyleAttributePlugin.ts b/packages/layers/src/plugins/RegisterStyleAttributePlugin.ts index 0c75fd9229..ea709b6952 100644 --- a/packages/layers/src/plugins/RegisterStyleAttributePlugin.ts +++ b/packages/layers/src/plugins/RegisterStyleAttributePlugin.ts @@ -40,7 +40,9 @@ export default class RegisterStyleAttributePlugin implements ILayerPlugin { featureIdx: number, vertex: number[], ) => { - return vertex.length === 3 ? vertex : [vertex[0], vertex[1], 0]; + return vertex.length === 2 + ? [vertex[0], vertex[1], 0] + : [vertex[0], vertex[1], vertex[2]]; }, }, }); diff --git a/packages/layers/src/point/extrude.ts b/packages/layers/src/point/extrude.ts index df79456556..a4080cc247 100644 --- a/packages/layers/src/point/extrude.ts +++ b/packages/layers/src/point/extrude.ts @@ -1,8 +1,8 @@ import { AttributeType, gl, IEncodeFeature, ILayer } from '@l7/core'; import BaseLayer from '../core/BaseLayer'; +import { PointExtrudeTriangulation } from '../core/triangulation'; import pointExtrudeFrag from './shaders/extrude_frag.glsl'; import pointExtrudeVert from './shaders/extrude_vert.glsl'; -import { PointExtrudeTriangulation } from './triangulation'; interface IPointLayerStyleOptions { opacity: number; } diff --git a/packages/layers/src/point/image.ts b/packages/layers/src/point/image.ts new file mode 100644 index 0000000000..4463c664a7 --- /dev/null +++ b/packages/layers/src/point/image.ts @@ -0,0 +1,141 @@ +import { + AttributeType, + gl, + IEncodeFeature, + ILayer, + ILayerPlugin, + ILogService, + IStyleAttributeService, + lazyInject, + TYPES, +} from '@l7/core'; +import BaseLayer from '../core/BaseLayer'; +import { PointImageTriangulation } from '../core/triangulation'; +import pointImageFrag from './shaders/image_frag.glsl'; +import pointImageVert from './shaders/image_vert.glsl'; +interface IPointLayerStyleOptions { + opacity: number; +} +export function PointTriangulation(feature: IEncodeFeature) { + const coordinates = feature.coordinates as number[]; + return { + vertices: [...coordinates, ...coordinates, ...coordinates, ...coordinates], + extrude: [-1, -1, 1, -1, 1, 1, -1, 1], + indices: [0, 1, 2, 2, 3, 0], + size: coordinates.length, + }; +} +export default class PointLayer extends BaseLayer { + public name: string = 'PointLayer'; + + protected getConfigSchema() { + return { + properties: { + opacity: { + type: 'number', + minimum: 0, + maximum: 1, + }, + }, + }; + } + + protected renderModels() { + const { opacity } = this.getStyleOptions(); + const { createTexture2D } = this.rendererService; + this.models.forEach((model) => + model.draw({ + uniforms: { + u_Opacity: opacity || 0, + u_texture: createTexture2D({ + data: this.iconService.getCanvas(), + width: 1024, + height: this.iconService.canvasHeight || 64, + }), + }, + }), + ); + + return this; + } + + protected buildModels() { + this.registerBuiltinAttributes(this); + this.iconService.on('imageUpdate', () => { + this.renderModels(); + }); + this.models = [ + this.buildLayerModel({ + moduleName: 'pointImage', + vertexShader: pointImageVert, + fragmentShader: pointImageFrag, + triangulation: PointTriangulation, + primitive: gl.POINTS, + depth: { enable: false }, + blend: { + enable: true, + func: { + srcRGB: gl.SRC_ALPHA, + srcAlpha: 1, + dstRGB: gl.ONE_MINUS_SRC_ALPHA, + dstAlpha: 1, + }, + }, + }), + ]; + } + + private registerBuiltinAttributes(layer: ILayer) { + // point layer size; + layer.styleAttributeService.registerStyleAttribute({ + name: 'size', + type: AttributeType.Attribute, + descriptor: { + name: 'a_Size', + buffer: { + // give the WebGL driver a hint that this buffer may change + usage: gl.DYNAMIC_DRAW, + data: [], + type: gl.FLOAT, + }, + size: 1, + update: ( + feature: IEncodeFeature, + featureIdx: number, + vertex: number[], + attributeIdx: number, + ) => { + const { size } = feature; + return Array.isArray(size) ? [size[0]] : [size as number]; + }, + }, + }); + + // point layer size; + layer.styleAttributeService.registerStyleAttribute({ + name: 'uv', + type: AttributeType.Attribute, + descriptor: { + name: 'a_Uv', + buffer: { + // give the WebGL driver a hint that this buffer may change + usage: gl.DYNAMIC_DRAW, + data: [], + type: gl.FLOAT, + }, + size: 2, + update: ( + feature: IEncodeFeature, + featureIdx: number, + vertex: number[], + attributeIdx: number, + ) => { + const iconMap = this.iconService.getIconMap(); + const { shape } = feature; + const { x, y } = iconMap[shape as string] || { x: 0, y: 0 }; + return [x, y]; + }, + }, + }); + } +} diff --git a/packages/layers/src/point/shaders/image_frag.glsl b/packages/layers/src/point/shaders/image_frag.glsl index e93d519f3b..a7a2e01450 100644 --- a/packages/layers/src/point/shaders/image_frag.glsl +++ b/packages/layers/src/point/shaders/image_frag.glsl @@ -6,9 +6,9 @@ void main(){ // pos.y= 1.- pos.y; vec4 textureColor=texture2D(u_texture,pos); if(v_color == vec4(0.)){ - gl_FragColor= textureColor; + gl_FragColor= textureColor; }else { gl_FragColor= step(0.01, textureColor.x) * v_color; } return; -} \ No newline at end of file +} diff --git a/packages/layers/src/point/shaders/image_vert.glsl b/packages/layers/src/point/shaders/image_vert.glsl index bb0d424b45..5b86d4df16 100644 --- a/packages/layers/src/point/shaders/image_vert.glsl +++ b/packages/layers/src/point/shaders/image_vert.glsl @@ -1,18 +1,17 @@ precision highp float; attribute vec3 a_Position; -attribute vec4 a_color; -attribute vec2 a_uv; -attribute float a_size; -attribute float a_shape; +attribute vec4 a_Color; +attribute vec2 a_Uv; +attribute float a_Size; varying vec4 v_color; varying vec2 v_uv; uniform mat4 u_ModelMatrix; #pragma include "projection" void main() { - v_color = a_color; - v_uv = a_uv; + v_color = a_Color; + v_uv = a_Uv; vec4 project_pos = project_position(vec4(a_Position, 1.0)); gl_Position = project_common_position_to_clipspace(vec4(project_pos.xyz, 1.0)); - gl_PointSize = a_size; - -} \ No newline at end of file + gl_PointSize = a_Size; + +} diff --git a/packages/layers/src/polygon/polygon3D.ts b/packages/layers/src/polygon/polygon3D.ts new file mode 100644 index 0000000000..79457439ad --- /dev/null +++ b/packages/layers/src/polygon/polygon3D.ts @@ -0,0 +1,107 @@ +import { AttributeType, gl, IEncodeFeature, ILayer } from '@l7/core'; +import BaseLayer from '../core/BaseLayer'; +import { PolygonExtrudeTriangulation } from '../core/triangulation'; +import pointExtrudeFrag from './shaders/polygon_frag.glsl'; +import pointExtrudeVert from './shaders/polygon_vert.glsl'; +interface IPointLayerStyleOptions { + opacity: number; +} +export default class PointLayer extends BaseLayer { + public name: string = 'PolygonLayer'; + + protected getConfigSchema() { + return { + properties: { + opacity: { + type: 'number', + minimum: 0, + maximum: 1, + }, + }, + }; + } + + protected renderModels() { + const { opacity } = this.getStyleOptions(); + this.models.forEach((model) => + model.draw({ + uniforms: { + u_Opacity: opacity || 0, + }, + }), + ); + return this; + } + + protected buildModels() { + this.registerBuiltinAttributes(this); + this.models = [ + this.buildLayerModel({ + moduleName: 'polygonExtrude', + vertexShader: pointExtrudeVert, + fragmentShader: pointExtrudeFrag, + triangulation: PolygonExtrudeTriangulation, + blend: { + enable: true, + func: { + srcRGB: gl.SRC_ALPHA, + srcAlpha: 1, + dstRGB: gl.ONE_MINUS_SRC_ALPHA, + dstAlpha: 1, + }, + }, + }), + ]; + } + + private registerBuiltinAttributes(layer: ILayer) { + // point layer size; + layer.styleAttributeService.registerStyleAttribute({ + name: 'normal', + type: AttributeType.Attribute, + descriptor: { + name: 'a_Normal', + buffer: { + // give the WebGL driver a hint that this buffer may change + usage: gl.STATIC_DRAW, + data: [], + type: gl.FLOAT, + }, + size: 3, + update: ( + feature: IEncodeFeature, + featureIdx: number, + vertex: number[], + attributeIdx: number, + normal: number[], + ) => { + return normal; + }, + }, + }); + + layer.styleAttributeService.registerStyleAttribute({ + name: 'size', + type: AttributeType.Attribute, + descriptor: { + name: 'a_Size', + buffer: { + // give the WebGL driver a hint that this buffer may change + usage: gl.DYNAMIC_DRAW, + data: [], + type: gl.FLOAT, + }, + size: 1, + update: ( + feature: IEncodeFeature, + featureIdx: number, + vertex: number[], + attributeIdx: number, + ) => { + const { size } = feature; + return Array.isArray(size) ? [size[0]] : [size as number]; + }, + }, + }); + } +} diff --git a/packages/layers/src/polygon/shaders/polygon_vert.glsl b/packages/layers/src/polygon/shaders/polygon_vert.glsl index e83cbd85bb..4eeca1eace 100644 --- a/packages/layers/src/polygon/shaders/polygon_vert.glsl +++ b/packages/layers/src/polygon/shaders/polygon_vert.glsl @@ -1,6 +1,7 @@ -attribute vec4 a_Color; +attribute vec4 a_Color; attribute vec3 a_Position; -// attribute vec3 a_normal; +attribute vec3 a_Normal; +attribute float a_Size; uniform mat4 u_ModelMatrix; varying vec4 v_Color; @@ -9,8 +10,8 @@ varying vec4 v_Color; #pragma include "picking" void main() { - v_Color = a_Color; - vec4 project_pos = project_position(vec4(a_Position, 1.0)); + v_Color = vec4(a_Normal,1.0); + vec4 project_pos = project_position(vec4(a_Position.xy,a_Position.z * a_Size, 1.0)); gl_Position = project_common_position_to_clipspace(vec4(project_pos.xyz, 1.0)); setPickingColor(a_PickingColor); diff --git a/packages/layers/src/utils/polylineNormal.ts b/packages/layers/src/utils/polylineNormal.ts index c49892a52a..ddfed304a3 100644 --- a/packages/layers/src/utils/polylineNormal.ts +++ b/packages/layers/src/utils/polylineNormal.ts @@ -210,12 +210,20 @@ export default function( } } } - + const pickData = []; + for (let i = 0; i < miters.length; i++) { + pickData.push( + attrPos[i * 3], + attrPos[i * 3 + 1], + attrPos[i * 3 + 1], + attrDistance[i], + miters[i], + ); + } return { normals: out, attrIndex, - attrPos, - attrDistance, - miters, + attrPos: pickData, // [x,y,z, distance, miter ] }; } +// [x,y,z, distance, miter ] diff --git a/stories/Layers/Layers.stories.tsx b/stories/Layers/Layers.stories.tsx index ca0928dd04..77e98759b2 100644 --- a/stories/Layers/Layers.stories.tsx +++ b/stories/Layers/Layers.stories.tsx @@ -1,8 +1,15 @@ import { storiesOf } from '@storybook/react'; import * as React from 'react'; +import LineLayer from './components/Line'; import PointDemo from './components/Point'; import Point3D from './components/Point3D'; +import PointImage from './components/pointImage'; +import Polygon3D from './components/polygon3D'; + // @ts-ignore storiesOf('图层', module) .add('点图层', () => ) - .add('3D点', () => ); + .add('3D点', () => ) + .add('图片标注', () => ) + .add('面3d图层', () => ) + .add('线图层', () => ); diff --git a/stories/Layers/components/Line.tsx b/stories/Layers/components/Line.tsx new file mode 100644 index 0000000000..ad2e84487f --- /dev/null +++ b/stories/Layers/components/Line.tsx @@ -0,0 +1,80 @@ +import { LineLayer } from '@l7/layers'; +import { Scene } from '@l7/scene'; +import * as React from 'react'; + +export default class Point3D extends React.Component { + private scene: Scene; + + public componentWillUnmount() { + this.scene.destroy(); + } + + public async componentDidMount() { + const response = await fetch( + 'https://gw.alipayobjects.com/os/rmsportal/ZVfOvhVCzwBkISNsuKCc.json', + ); + const testdata = { + type: 'FeatureCollection', + features: [ + { + type: 'Feature', + properties: {}, + geometry: { + type: 'LineString', + coordinates: [ + [91.58203125, 34.95799531086792], + [96.767578125, 34.379712580462204], + [99.228515625, 33.7243396617476], + ], + }, + }, + ], + }; + const scene = new Scene({ + center: [102.602992, 23.107329], + id: 'map', + pitch: 0, + type: 'mapbox', + style: 'mapbox://styles/mapbox/dark-v9', + zoom: 13, + }); + const lineLayer = new LineLayer({}) + .source(await response.json()) + .size(1) + .shape('line') + .color( + 'ELEV', + [ + '#E8FCFF', + '#CFF6FF', + '#A1E9ff', + '#65CEF7', + '#3CB1F0', + '#2894E0', + '#1772c2', + '#105CB3', + '#0D408C', + '#002466', + ].reverse(), + ) + .render(); + scene.addLayer(lineLayer); + scene.render(); + this.scene = scene; + } + + public render() { + return ( +
+ ); + } +} diff --git a/stories/Layers/components/pointImage.tsx b/stories/Layers/components/pointImage.tsx new file mode 100644 index 0000000000..8ed25ed2db --- /dev/null +++ b/stories/Layers/components/pointImage.tsx @@ -0,0 +1,61 @@ +import { PointImageLayer } from '@l7/layers'; +import { Scene } from '@l7/scene'; +import * as React from 'react'; +import data from '../data/data.json'; +export default class PointImage extends React.Component { + private scene: Scene; + + public componentWillUnmount() { + this.scene.destroy(); + } + + public componentDidMount() { + const scene = new Scene({ + center: [120.19382669582967, 30.258134], + id: 'map', + pitch: 0, + type: 'mapbox', + style: 'mapbox://styles/mapbox/streets-v9', + zoom: 1, + }); + const pointLayer = new PointImageLayer({}); + const p1 = { + type: 'FeatureCollection', + features: [ + { + type: 'Feature', + properties: {}, + geometry: { + type: 'Point', + coordinates: [83.671875, 44.84029065139799], + }, + }, + ], + }; + scene.addImage( + '00', + 'https://gw.alipayobjects.com/mdn/antv_site/afts/img/A*kzTMQqS2QdUAAAAAAAAAAABkARQnAQ', + ); + pointLayer + .source(data) + .shape('00') + .size(30); + scene.addLayer(pointLayer); + scene.render(); + } + + public render() { + return ( +
+ ); + } +} diff --git a/stories/Layers/components/polygon3D.tsx b/stories/Layers/components/polygon3D.tsx new file mode 100644 index 0000000000..d1ffed903a --- /dev/null +++ b/stories/Layers/components/polygon3D.tsx @@ -0,0 +1,78 @@ +// @ts-ignore +import { Polygon3DLayer } from '@l7/layers'; +// @ts-ignore +import { Scene } from '@l7/scene'; +import * as dat from 'dat.gui'; +import * as React from 'react'; + +function convertRGB2Hex(rgb: number[]) { + return ( + '#' + rgb.map((r) => ('0' + Math.floor(r).toString(16)).slice(-2)).join('') + ); +} + +export default class Polygon3D extends React.Component { + private gui: dat.GUI; + private $stats: Node; + private scene: Scene; + + public componentWillUnmount() { + if (this.gui) { + this.gui.destroy(); + } + if (this.$stats) { + document.body.removeChild(this.$stats); + } + this.scene.destroy(); + } + + public async componentDidMount() { + const response = await fetch( + 'https://gw.alipayobjects.com/os/basement_prod/d2e0e930-fd44-4fca-8872-c1037b0fee7b.json', + ); + const scene = new Scene({ + id: 'map', + type: 'mapbox', + style: 'mapbox://styles/mapbox/streets-v9', + center: [110.19382669582967, 50.258134], + pitch: 0, + zoom: 3, + }); + this.scene = scene; + const layer = new Polygon3DLayer({}); + + // TODO: new GeoJSONSource() + layer + .source(await response.json()) + .size('name', [0, 10000, 50000, 30000, 100000]) + .color('name', [ + '#2E8AE6', + '#69D1AB', + '#DAF291', + '#FFD591', + '#FF7A45', + '#CF1D49', + ]) + .shape('fill') + .style({ + opacity: 1.0, + }); + scene.addLayer(layer); + scene.render(); + } + + public render() { + return ( +
+ ); + } +}