From 7e499fdc877d9715000c138a5d3505924ebd083e Mon Sep 17 00:00:00 2001 From: thinkinggis Date: Wed, 30 Oct 2019 14:53:18 +0800 Subject: [PATCH] feat(layers): add arclayer --- packages/layers/src/core/triangulation.ts | 38 +++++++ packages/layers/src/index.ts | 2 + packages/layers/src/line/arc.ts | 107 ++++++++++++++++++ .../src/line/shaders/line_arc_frag.glsl | 10 ++ .../src/line/shaders/line_arc_vert.glsl | 66 +++++++++++ .../src/polygon/shaders/polygon_vert.glsl | 2 +- stories/Layers/Layers.stories.tsx | 2 + stories/Layers/components/Arcline.tsx | 56 +++++++++ stories/Layers/components/Line.tsx | 2 +- 9 files changed, 283 insertions(+), 2 deletions(-) create mode 100644 packages/layers/src/line/arc.ts create mode 100644 packages/layers/src/line/shaders/line_arc_frag.glsl create mode 100644 packages/layers/src/line/shaders/line_arc_vert.glsl create mode 100644 stories/Layers/components/Arcline.tsx diff --git a/packages/layers/src/core/triangulation.ts b/packages/layers/src/core/triangulation.ts index e6d219000c..dc52a9d5db 100644 --- a/packages/layers/src/core/triangulation.ts +++ b/packages/layers/src/core/triangulation.ts @@ -137,6 +137,44 @@ export function RasterImageTriangulation(feature: IEncodeFeature) { }; } +export function LineArcTriangulation(feature: IEncodeFeature, segNum = 30) { + const coordinates = feature.coordinates as IPosition[]; + const positions = []; + const indexArray = []; + for (let i = 0; i < segNum; i++) { + // 上线两个顶点 + // [ x, y, z, sx,sy, tx,ty] + positions.push( + i, + 1, + i, + coordinates[0][0], + coordinates[0][1], + coordinates[1][0], + coordinates[1][1], + i, + -1, + i, + coordinates[0][0], + coordinates[0][1], + coordinates[1][0], + coordinates[1][1], + ); + if (i !== segNum - 1) { + indexArray.push( + ...[0, 1, 2, 1, 3, 2].map((v) => { + return i * 2 + v; + }), + ); + } + } + return { + vertices: positions, + indices: indexArray, + size: 7, + }; +} + /** * 点图层3d geomerty * @param shape 3D形状 diff --git a/packages/layers/src/index.ts b/packages/layers/src/index.ts index f1b752992c..79bfc5e97a 100644 --- a/packages/layers/src/index.ts +++ b/packages/layers/src/index.ts @@ -2,6 +2,7 @@ import { container, ILayerPlugin, TYPES } from '@l7/core'; import BaseLayer from './core/BaseLayer'; import HeatMapGridLayer from './heatmap/grid'; import LineLayer from './line/index'; +import ArcLineLayer from './line/arc'; import Point3dLayer from './point/extrude'; import PointImageLayer from './point/image'; import PointLayer from './point/index'; @@ -72,6 +73,7 @@ export { Polygon3DLayer, ImageLayer, HeatMapGridLayer, + ArcLineLayer, // Line, // ImageLayer, // HeatMapLayer, diff --git a/packages/layers/src/line/arc.ts b/packages/layers/src/line/arc.ts new file mode 100644 index 0000000000..5813156a68 --- /dev/null +++ b/packages/layers/src/line/arc.ts @@ -0,0 +1,107 @@ +import { AttributeType, gl, IEncodeFeature, ILayer } from '@l7/core'; +import BaseLayer from '../core/BaseLayer'; +import { LineArcTriangulation } from '../core/triangulation'; +import line_arc_frag from './shaders/line_arc_frag.glsl'; +import line_arc_vert from './shaders/line_arc_vert.glsl'; +interface IArcLayerStyleOptions { + opacity: number; + segmentNumber: number; +} +export default class ArcLineLayer extends BaseLayer { + public name: string = 'LineLayer'; + + 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 || 1, + segmentNumber: 30, + }, + }), + ); + return this; + } + + protected buildModels() { + this.registerBuiltinAttributes(this); + this.models = [ + this.buildLayerModel({ + moduleName: 'arcline', + vertexShader: line_arc_vert, + fragmentShader: line_arc_frag, + triangulation: LineArcTriangulation, + blend: { + enable: true, + func: { + srcRGB: gl.ONE, + srcAlpha: 1, + dstRGB: gl.ONE, + 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]; + }, + }, + }); + + layer.styleAttributeService.registerStyleAttribute({ + name: 'instance', // 弧线起始点信息 + type: AttributeType.Attribute, + descriptor: { + name: 'a_Instance', + buffer: { + usage: gl.STATIC_DRAW, + data: [], + type: gl.FLOAT, + }, + size: 4, + update: ( + feature: IEncodeFeature, + featureIdx: number, + vertex: number[], + attributeIdx: number, + ) => { + return [vertex[3], vertex[4], vertex[5], vertex[6]]; + }, + }, + }); + } +} diff --git a/packages/layers/src/line/shaders/line_arc_frag.glsl b/packages/layers/src/line/shaders/line_arc_frag.glsl new file mode 100644 index 0000000000..b8f0e5bbb4 --- /dev/null +++ b/packages/layers/src/line/shaders/line_arc_frag.glsl @@ -0,0 +1,10 @@ + precision mediump float; + uniform float u_Opacity; + varying vec4 v_color; + + void main() { + + gl_FragColor = v_color; + gl_FragColor.a = v_color.a * u_Opacity; + + } diff --git a/packages/layers/src/line/shaders/line_arc_vert.glsl b/packages/layers/src/line/shaders/line_arc_vert.glsl new file mode 100644 index 0000000000..abefcf54bd --- /dev/null +++ b/packages/layers/src/line/shaders/line_arc_vert.glsl @@ -0,0 +1,66 @@ +precision mediump float; +attribute vec3 a_Position; +attribute vec4 a_Instance; +attribute vec4 a_Color; +attribute float a_Size; + + +uniform mat4 u_ModelMatrix; +uniform float segmentNumber; +varying vec4 v_color; +#pragma include "projection" + +float maps (float value, float start1, float stop1, float start2, float stop2) { + return start2 + (stop2 - start2) * ((value - start1) / (stop1 - start1)); +} + +float getSegmentRatio(float index) { + return smoothstep(0.0, 1.0, index / (segmentNumber - 1.0)); +} + +float paraboloid(vec2 source, vec2 target, float ratio) { + vec2 x = mix(source, target, ratio); + vec2 center = mix(source, target, 0.5); + float dSourceCenter = distance(source, center); + float dXCenter = distance(x, center); + return (dSourceCenter + dXCenter) * (dSourceCenter - dXCenter); +} + +vec3 getPos(vec2 source, vec2 target, float segmentRatio) { + float vertex_height = paraboloid(source, target, segmentRatio); + + return vec3( + mix(source, target, segmentRatio), + sqrt(max(0.0, vertex_height)) + ); +} +vec2 getExtrusionOffset(vec2 line_clipspace, float offset_direction) { + // normalized direction of the line + vec2 dir_screenspace = normalize(line_clipspace); + // rotate by 90 degrees + dir_screenspace = vec2(-dir_screenspace.y, dir_screenspace.x); + + vec2 offset = dir_screenspace * offset_direction * a_Size / 2.0; + + return offset; +} + + +void main() { + v_color = a_Color; + vec2 source = project_position(vec4(a_Instance.rg, 0, 0)).xy; + vec2 target = project_position(vec4(a_Instance.ba, 0, 0)).xy; + float segmentIndex = a_Position.x; + float segmentRatio = getSegmentRatio(segmentIndex); + float indexDir = mix(-1.0, 1.0, step(segmentIndex, 0.0)); + + float nextSegmentRatio = getSegmentRatio(segmentIndex + indexDir); + vec3 curr = getPos(source, target, segmentRatio); + vec3 next = getPos(source, target, nextSegmentRatio); + vec2 offset = getExtrusionOffset((next.xy - curr.xy) * indexDir, a_Position.y); + + // vec4 project_pos = project_position(vec4(curr, 1.0)); + + gl_Position = project_common_position_to_clipspace(vec4(curr.xy + project_pixel(offset), curr.z, 1.0)); + +} diff --git a/packages/layers/src/polygon/shaders/polygon_vert.glsl b/packages/layers/src/polygon/shaders/polygon_vert.glsl index 710449f147..03094f786f 100644 --- a/packages/layers/src/polygon/shaders/polygon_vert.glsl +++ b/packages/layers/src/polygon/shaders/polygon_vert.glsl @@ -11,7 +11,7 @@ varying vec4 v_Color; void main() { v_Color =a_Color; - vec4 project_pos = project_position(vec4(a_Position 1.0)); + vec4 project_pos = project_position(vec4(a_Position,1.0)); gl_Position = project_common_position_to_clipspace(vec4(project_pos.xyz, 1.0)); setPickingColor(a_PickingColor); diff --git a/stories/Layers/Layers.stories.tsx b/stories/Layers/Layers.stories.tsx index a019f80220..6946fffa22 100644 --- a/stories/Layers/Layers.stories.tsx +++ b/stories/Layers/Layers.stories.tsx @@ -1,5 +1,6 @@ import { storiesOf } from '@storybook/react'; import * as React from 'react'; +import ArcLineDemo from './components/Arcline'; import GridHeatMap from './components/heatMapgrid'; import LineLayer from './components/Line'; import PointDemo from './components/Point'; @@ -15,5 +16,6 @@ storiesOf('图层', module) .add('图片标注', () => ) .add('面3d图层', () => ) .add('线图层', () => ) + .add('弧线', () => ) .add('网格热力图', () => ) .add('图片', () => ); diff --git a/stories/Layers/components/Arcline.tsx b/stories/Layers/components/Arcline.tsx new file mode 100644 index 0000000000..42333b66bb --- /dev/null +++ b/stories/Layers/components/Arcline.tsx @@ -0,0 +1,56 @@ +import { ArcLineLayer } from '@l7/layers'; +import { Scene } from '@l7/scene'; +import * as React from 'react'; + +export default class ArxLineDemo extends React.Component { + private scene: Scene; + + public componentWillUnmount() { + this.scene.destroy(); + } + + public async componentDidMount() { + const response = await fetch( + 'https://gw.alipayobjects.com/os/rmsportal/UEXQMifxtkQlYfChpPwT.txt', + ); + const scene = new Scene({ + center: [116.2825, 39.9], + id: 'map', + pitch: 0, + type: 'mapbox', + style: 'mapbox://styles/mapbox/dark-v9', + zoom: 2, + }); + const lineLayer = new ArcLineLayer({}) + .source(await response.text(), { + parser: { + type: 'csv', + x: 'lng1', + y: 'lat1', + x1: 'lng2', + y1: 'lat2', + }, + }) + .size(0.5) + .shape('arc') + .color('rgb(13,64,140)'); + scene.addLayer(lineLayer); + scene.render(); + this.scene = scene; + } + + public render() { + return ( +
+ ); + } +} diff --git a/stories/Layers/components/Line.tsx b/stories/Layers/components/Line.tsx index ad2e84487f..55dd4d5bcb 100644 --- a/stories/Layers/components/Line.tsx +++ b/stories/Layers/components/Line.tsx @@ -2,7 +2,7 @@ import { LineLayer } from '@l7/layers'; import { Scene } from '@l7/scene'; import * as React from 'react'; -export default class Point3D extends React.Component { +export default class LineDemo extends React.Component { private scene: Scene; public componentWillUnmount() {