From 499684ee09c6a5c340ecb7af18a339a9a7276165 Mon Sep 17 00:00:00 2001 From: YiQianYao <42212176+2912401452@users.noreply.github.com> Date: Fri, 18 Mar 2022 11:00:42 +0800 Subject: [PATCH] Shihui (#1010) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 修改 CanvasLayer drawingOnCanvas 的 参数 * style: lint style * feat: 新增 geometryLayer plane --- .../core/src/services/asset/IconService.ts | 36 +-- packages/layers/src/Geometry/index.ts | 40 ++++ packages/layers/src/Geometry/models/index.ts | 7 + packages/layers/src/Geometry/models/plane.ts | 226 ++++++++++++++++++ .../src/Geometry/shaders/plane_frag.glsl | 21 ++ .../src/Geometry/shaders/plane_vert.glsl | 46 ++++ packages/layers/src/canvas/models/canvas.ts | 8 +- packages/layers/src/core/interface.ts | 29 ++- packages/layers/src/core/triangulation.ts | 1 + packages/layers/src/index.ts | 2 + stories/Object/components/canvas.tsx | 6 +- stories/Object/components/plane.tsx | 81 +++++++ stories/Object/map.stories.tsx | 4 +- 13 files changed, 477 insertions(+), 30 deletions(-) create mode 100644 packages/layers/src/Geometry/index.ts create mode 100644 packages/layers/src/Geometry/models/index.ts create mode 100644 packages/layers/src/Geometry/models/plane.ts create mode 100644 packages/layers/src/Geometry/shaders/plane_frag.glsl create mode 100644 packages/layers/src/Geometry/shaders/plane_vert.glsl create mode 100644 stories/Object/components/plane.tsx diff --git a/packages/core/src/services/asset/IconService.ts b/packages/core/src/services/asset/IconService.ts index 6dd9dc51fc..a112305633 100644 --- a/packages/core/src/services/asset/IconService.ts +++ b/packages/core/src/services/asset/IconService.ts @@ -126,6 +126,24 @@ export default class IconService extends EventEmitter implements IIconService { this.iconData = []; this.iconMap = {}; } + + public loadImage(url: IImage) { + return new Promise((resolve, reject) => { + if (url instanceof HTMLImageElement) { + resolve(url); + return; + } + const image = new Image(); + image.crossOrigin = 'anonymous'; + image.onload = () => { + resolve(image); + }; + image.onerror = () => { + reject(new Error('Could not load image at ' + url)); + }; + image.src = url instanceof File ? URL.createObjectURL(url) : url; + }); + } private update() { this.updateIconMap(); this.updateIconAtlas(); @@ -172,24 +190,6 @@ export default class IconService extends EventEmitter implements IIconService { this.canvasHeight = canvasHeight; } - private loadImage(url: IImage) { - return new Promise((resolve, reject) => { - if (url instanceof HTMLImageElement) { - resolve(url); - return; - } - const image = new Image(); - image.crossOrigin = 'anonymous'; - image.onload = () => { - resolve(image); - }; - image.onerror = () => { - reject(new Error('Could not load image at ' + url)); - }; - image.src = url instanceof File ? URL.createObjectURL(url) : url; - }); - } - /** * 适配小程序 * @param url diff --git a/packages/layers/src/Geometry/index.ts b/packages/layers/src/Geometry/index.ts new file mode 100644 index 0000000000..5a8d93e706 --- /dev/null +++ b/packages/layers/src/Geometry/index.ts @@ -0,0 +1,40 @@ +import BaseLayer from '../core/BaseLayer'; +import { IGeometryLayerStyleOptions } from '../core/interface'; +import GeometryModels, { GeometryModelType } from './models'; + +export default class GeometryLayer extends BaseLayer< + IGeometryLayerStyleOptions +> { + public type: string = 'GeometryLayer'; + public buildModels() { + const modelType = this.getModelType(); + this.layerModel = new GeometryModels[modelType](this); + this.models = this.layerModel.initModels(); + } + public rebuildModels() { + this.models = this.layerModel.buildModels(); + } + + protected getConfigSchema() { + return { + properties: { + opacity: { + type: 'number', + minimum: 0, + maximum: 1, + }, + }, + }; + } + protected getDefaultConfig() { + const type = this.getModelType(); + const defaultConfig = { + plane: {}, + }; + return defaultConfig[type]; + } + + protected getModelType(): GeometryModelType { + return 'plane'; + } +} diff --git a/packages/layers/src/Geometry/models/index.ts b/packages/layers/src/Geometry/models/index.ts new file mode 100644 index 0000000000..37a4540f90 --- /dev/null +++ b/packages/layers/src/Geometry/models/index.ts @@ -0,0 +1,7 @@ +import PlaneModel from './plane'; +export type GeometryModelType = 'plane'; + +const GeometryModels: { [key in GeometryModelType]: any } = { + plane: PlaneModel, +}; +export default GeometryModels; diff --git a/packages/layers/src/Geometry/models/plane.ts b/packages/layers/src/Geometry/models/plane.ts new file mode 100644 index 0000000000..88cd2f6846 --- /dev/null +++ b/packages/layers/src/Geometry/models/plane.ts @@ -0,0 +1,226 @@ +import { + AttributeType, + gl, + IEncodeFeature, + IModelUniform, + ITexture2D, +} from '@antv/l7-core'; +import { getMask, isMini } from '@antv/l7-utils'; +// import { mat4, vec3 } from 'gl-matrix'; +import BaseModel from '../../core/BaseModel'; +import { IGeometryLayerStyleOptions } from '../../core/interface'; +import planeFrag from '../shaders/plane_frag.glsl'; +import planeVert from '../shaders/plane_vert.glsl'; + +function initPlane( + width = 1, + height = 1, + widthSegments = 1, + heightSegments = 1, + lng = 120, + lat = 30, +) { + // https://github.com/mrdoob/three.js/blob/dev/src/geometries/PlaneGeometry.js + const widthHalf = width / 2; + const heightHalf = height / 2; + + const gridX = Math.floor(widthSegments); + const gridY = Math.floor(heightSegments); + + const gridX1 = gridX + 1; + const gridY1 = gridY + 1; + + const segmentWidth = width / gridX; + const segmentHeight = height / gridY; + + const indices = []; + const positions = []; + + for (let iy = 0; iy < gridY1; iy++) { + const y = iy * segmentHeight - heightHalf; + + for (let ix = 0; ix < gridX1; ix++) { + const x = ix * segmentWidth - widthHalf; + + positions.push(x + lng, -y + lat, 0); + + positions.push(ix / gridX); + positions.push(1 - iy / gridY); + } + } + + for (let iy = 0; iy < gridY; iy++) { + for (let ix = 0; ix < gridX; ix++) { + const a = ix + gridX1 * iy; + const b = ix + gridX1 * (iy + 1); + const c = ix + 1 + gridX1 * (iy + 1); + const d = ix + 1 + gridX1 * iy; + + indices.push(a, b, d); + indices.push(b, c, d); + } + } + + return { indices, positions }; +} + +export default class PlaneModel extends BaseModel { + protected texture: ITexture2D; + protected mapTexture: string | undefined; + public planeGeometryTriangulation = () => { + const { + width = 1, + height = 1, + widthSegments = 1, + heightSegments = 1, + center = [120, 30], + } = this.layer.getLayerConfig() as IGeometryLayerStyleOptions; + const { indices, positions } = initPlane( + width, + height, + widthSegments, + heightSegments, + ...center, + ); + return { + vertices: positions, + indices, + size: 5, + }; + }; + + public getUninforms(): IModelUniform { + const { + opacity, + mapTexture, + } = this.layer.getLayerConfig() as IGeometryLayerStyleOptions; + if (this.mapTexture !== mapTexture) { + this.mapTexture = mapTexture; + this.texture.destroy(); + this.updateTexture(mapTexture); + } + return { + u_opacity: opacity || 1, + u_mapFlag: mapTexture ? 1 : 0, + + u_texture: this.texture, + // u_ModelMatrix: mat4.translate(mat4.create(), mat4.create(), [1, 0, 0]) + // u_ModelMatrix: mat4.rotateZ(mat4.create(), mat4.create(), 10) + // u_ModelMatrix: mat4.rotateZ(mat4.create(), mat4.create(), 10) + // u_ModelMatrix: this.rotateZ() + }; + } + + // public rotateZ(): mat4 { + // const res = mat4.create() + // const roZero = mat4.translate(mat4.create(), mat4.create(), [-120, 0, -30]) + // const rotate = mat4.rotateZ(mat4.create(), mat4.create(), 10) + // const roOrigin = mat4.translate(mat4.create(), mat4.create(), [120, 0, 30]) + // mat4.multiply(res, res, roZero) + // mat4.multiply(res, res, rotate) + // mat4.multiply(res, res, roOrigin) + // return res + // } + + public clearModels(): void { + this.texture.destroy(); + } + + public initModels() { + const { + mask = false, + maskInside = true, + mapTexture, + } = this.layer.getLayerConfig() as IGeometryLayerStyleOptions; + this.mapTexture = mapTexture; + + const { createTexture2D } = this.rendererService; + this.texture = createTexture2D({ + height: 0, + width: 0, + }); + + this.updateTexture(mapTexture); + + return [ + this.layer.buildLayerModel({ + moduleName: 'geometry_plane', + vertexShader: planeVert, + fragmentShader: planeFrag, + triangulation: this.planeGeometryTriangulation, + primitive: gl.TRIANGLES, + // primitive: gl.POINTS, + depth: { enable: false }, + blend: this.getBlend(), + stencil: getMask(mask, maskInside), + }), + ]; + } + public buildModels() { + return this.initModels(); + } + + public updateTexture(mapTexture: string | undefined): void { + const { createTexture2D } = this.rendererService; + + if (mapTexture) { + const img = new Image(); + img.crossOrigin = 'anonymous'; + img.onload = () => { + this.texture = createTexture2D({ + data: img, + width: img.width, + height: img.height, + wrapS: gl.CLAMP_TO_EDGE, + wrapT: gl.CLAMP_TO_EDGE, + }); + this.layerService.updateLayerRenderList(); + this.layerService.renderLayers(); + }; + img.src = mapTexture; + } else { + this.texture = createTexture2D({ + width: 0, + height: 0, + }); + } + } + + protected getConfigSchema() { + return { + properties: { + opacity: { + type: 'number', + minimum: 0, + maximum: 1, + }, + }, + }; + } + + protected registerBuiltinAttributes() { + // point layer size; + this.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, + ) => { + return [vertex[3], vertex[4]]; + }, + }, + }); + } +} diff --git a/packages/layers/src/Geometry/shaders/plane_frag.glsl b/packages/layers/src/Geometry/shaders/plane_frag.glsl new file mode 100644 index 0000000000..4b89fa46bf --- /dev/null +++ b/packages/layers/src/Geometry/shaders/plane_frag.glsl @@ -0,0 +1,21 @@ + +uniform sampler2D u_texture; +uniform float u_mapFlag; +uniform float u_opacity; + +varying vec3 v_Color; +varying vec2 v_uv; + +#pragma include "picking" +void main() { + // gl_FragColor = vec4(v_Color, u_opacity); + if(u_mapFlag > 0.0) { + gl_FragColor = texture2D(u_texture, vec2(v_uv.x, 1.0 - v_uv.y)); + gl_FragColor.a *= u_opacity; + + } else { + // gl_FragColor = vec4(v_uv, 0.0, u_opacity); + gl_FragColor = vec4(v_Color, u_opacity); + } + gl_FragColor = filterColor(gl_FragColor); +} diff --git a/packages/layers/src/Geometry/shaders/plane_vert.glsl b/packages/layers/src/Geometry/shaders/plane_vert.glsl new file mode 100644 index 0000000000..9b94e8939b --- /dev/null +++ b/packages/layers/src/Geometry/shaders/plane_vert.glsl @@ -0,0 +1,46 @@ +precision highp float; +uniform mat4 u_ModelMatrix; + +uniform mat4 u_Mvp; + +attribute vec3 a_Position; +attribute vec2 a_Uv; +attribute vec3 a_Color; + +varying vec3 v_Color; +varying vec2 v_uv; + +#pragma include "projection" +#pragma include "picking" +void main() { + 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.xy,0., 1.0)); + + // float x = 1.0; + // float y = 0.0; + // float z = 0.0; + // mat3 translateMatrix = mat3( + // 1.0, 0.0, 0.0 + // 0.0, 1.0, 0.0 + // -project_pos.x, -project_pos.y, 1.0 + // ); + // mat4 translateMatrix = mat4( + // 1.0, 0.0, 0.0, 0.0 , + // 0.0, 1.0, 0.0, 0.0, + // 0.0, 0.0, 1.0, 0.0, + // 1.0, 0.0, 0.0, 1.0 + // ); + + if(u_CoordinateSystem == COORDINATE_SYSTEM_P20_2) { // gaode2.x + gl_Position = u_Mvp * (vec4(project_pos.xy, 0., 1.0)); + } else { + gl_Position = project_common_position_to_clipspace(vec4(project_pos.xy, 0., 1.0)); + } + + setPickingColor(a_PickingColor); + + gl_PointSize = 10.0; +} diff --git a/packages/layers/src/canvas/models/canvas.ts b/packages/layers/src/canvas/models/canvas.ts index 1b0739c0f0..955eeb1421 100644 --- a/packages/layers/src/canvas/models/canvas.ts +++ b/packages/layers/src/canvas/models/canvas.ts @@ -2,6 +2,7 @@ import BaseModel from '../../core/BaseModel'; import { CanvasUpdateType, ICanvasLayerStyleOptions, + IDrawingOnCanvas, } from '../../core/interface'; export default class CanvaModel extends BaseModel { @@ -121,7 +122,12 @@ export default class CanvaModel extends BaseModel { } = this.layer.getLayerConfig() as ICanvasLayerStyleOptions; if (this.ctx) { - drawingOnCanvas(this.ctx, this.mapService, [viewWidth, viewHeight]); + drawingOnCanvas({ + canvas: this.canvas, + ctx: this.ctx, + mapService: this.mapService, + size: [viewWidth, viewHeight], + }); } }; diff --git a/packages/layers/src/core/interface.ts b/packages/layers/src/core/interface.ts index 8b695634c1..f2bea205f4 100644 --- a/packages/layers/src/core/interface.ts +++ b/packages/layers/src/core/interface.ts @@ -106,18 +106,37 @@ export interface IImageLayerStyleOptions { maskInside?: boolean; } +export interface IGeometryLayerStyleOptions { + opacity: number; + mask?: boolean; + maskInside?: boolean; + + mapTexture?: string; + + // planeGeometry + center?: [number, number]; + width?: number; + height?: number; + + widthSegments?: number; + heightSegments?: number; +} + export enum CanvasUpdateType { 'AWAYS' = 'aways', 'DRAGEND' = 'dragend', } + +export interface IDrawingOnCanvas { + canvas: HTMLCanvasElement; + ctx: CanvasRenderingContext2D; + mapService: IMapService; + size: [number, number]; +} export interface ICanvasLayerStyleOptions { zIndex: number; update: CanvasUpdateType | string; - drawingOnCanvas: ( - ctx: CanvasRenderingContext2D, - mapService: IMapService, - size: [number, number], - ) => void; + drawingOnCanvas: (option: IDrawingOnCanvas) => void; } export interface IHeatMapLayerStyleOptions { diff --git a/packages/layers/src/core/triangulation.ts b/packages/layers/src/core/triangulation.ts index d8c93b211d..187f65baa5 100644 --- a/packages/layers/src/core/triangulation.ts +++ b/packages/layers/src/core/triangulation.ts @@ -289,6 +289,7 @@ export function RasterImageTriangulation(feature: IEncodeFeature) { size: 5, }; } + /** * 计算3D弧线顶点 * @param feature 映射数据 diff --git a/packages/layers/src/index.ts b/packages/layers/src/index.ts index bfbf6299fa..516e062fe7 100644 --- a/packages/layers/src/index.ts +++ b/packages/layers/src/index.ts @@ -2,6 +2,7 @@ import { container, ILayerPlugin, TYPES } from '@antv/l7-core'; import CanvasLayer from './canvas'; import CityBuildingLayer from './citybuliding/building'; import BaseLayer from './core/BaseLayer'; +import GeometryLayer from './Geometry'; import './glsl.d'; import HeatmapLayer from './heatmap'; import ImageLayer from './image'; @@ -142,6 +143,7 @@ export { PolygonLayer, LineLayer, CityBuildingLayer, + GeometryLayer, CanvasLayer, ImageLayer, ImageTileLayer, diff --git a/stories/Object/components/canvas.tsx b/stories/Object/components/canvas.tsx index 1f4ad4b98a..278958678c 100644 --- a/stories/Object/components/canvas.tsx +++ b/stories/Object/components/canvas.tsx @@ -33,11 +33,7 @@ export default class Demo extends React.Component { zIndex: 10, update: 'aways', // update: 'dragend', - drawingOnCanvas: ( - ctx: CanvasRenderingContext2D, - mapService: IMapService, - size: [number, number], - ) => { + drawingOnCanvas: ({ ctx, mapService, size }) => { const [width, height] = size; ctx.clearRect(0, 0, width, height); diff --git a/stories/Object/components/plane.tsx b/stories/Object/components/plane.tsx new file mode 100644 index 0000000000..267ed4de20 --- /dev/null +++ b/stories/Object/components/plane.tsx @@ -0,0 +1,81 @@ +import { GeometryLayer, Scene, IMapService } from '@antv/l7'; +import { GaodeMap, Mapbox } from '@antv/l7-maps'; +import * as React from 'react'; + +export default class Demo extends React.Component { + // @ts-ignore + private scene: Scene; + + public componentWillUnmount() { + this.scene.destroy(); + } + + public async componentDidMount() { + const scene = new Scene({ + id: 'map', + map: new GaodeMap({ + // map: new Mapbox({ + pitch: 0, + // style: 'dark', + center: [120, 30], + zoom: 5, + }), + }); + this.scene = scene; + + let layer = new GeometryLayer() + .source([{ lng: 120, lat: 30 }], { + parser: { + type: 'json', + x: 'lng', + y: 'lat', + }, + }) + .style({ + width: 2, + height: 2, + opacity: 0.8, + widthSegments: 3, + heightSegments: 3, + center: [120, 30], + // mapTexture: 'https://gw.alipayobjects.com/mdn/rms_816329/afts/img/A*FG4fT7h5AYMAAAAAAAAAAAAAARQnAQ' + }) + .active({ + color: '#00f', + mix: 0.5, + }) + .color('#ff0'); + + scene.on('loaded', () => { + scene.addLayer(layer); + + setTimeout(() => { + // layer.style({ + // mapTexture: "" + // }) + layer.style({ + mapTexture: + 'https://gw.alipayobjects.com/mdn/rms_816329/afts/img/A*GJhASbfQTK8AAAAAAAAAAAAAARQnAQ', + }); + scene.render(); + }, 2000); + }); + } + + public render() { + return ( + <> +
+ + ); + } +} diff --git a/stories/Object/map.stories.tsx b/stories/Object/map.stories.tsx index 58aa974fe5..68ad345a15 100644 --- a/stories/Object/map.stories.tsx +++ b/stories/Object/map.stories.tsx @@ -5,10 +5,12 @@ import Ocean from './components/ocean'; import Taifong from './components/taifeng' import Radar from './components/radar'; import CanvasDemo from './components/canvas'; +import Plane from './components/plane'; storiesOf('Object', module) .add('water', () => ) .add('Ocean', () => ) .add('Taifong', () => ) .add('Radar', () => ) - .add('CanvasDemo', () => ) \ No newline at end of file + .add('CanvasDemo', () => ) + .add('Plane', () => ) \ No newline at end of file