From 420459ce5aee91dc8d6f770a2a2078c7e5bca4bf Mon Sep 17 00:00:00 2001 From: thinkinggis Date: Wed, 30 Oct 2019 16:38:59 +0800 Subject: [PATCH] feat(layer): add arc2d layer --- packages/layers/src/core/triangulation.ts | 6 +- packages/layers/src/index.ts | 4 +- packages/layers/src/line/arc2d.ts | 108 ++++++++++++++++++ .../src/line/shaders/line_arc2d_vert.glsl | 89 +++++++++++++++ .../layers/src/line/shaders/line_vert.glsl | 1 + stories/Layers/Layers.stories.tsx | 4 +- stories/Layers/components/Arc2DLine.tsx | 56 +++++++++ stories/Layers/components/Arcline.tsx | 2 +- 8 files changed, 266 insertions(+), 4 deletions(-) create mode 100644 packages/layers/src/line/arc2d.ts create mode 100644 packages/layers/src/line/shaders/line_arc2d_vert.glsl create mode 100644 stories/Layers/components/Arc2DLine.tsx diff --git a/packages/layers/src/core/triangulation.ts b/packages/layers/src/core/triangulation.ts index dc52a9d5db..84cf8e6b47 100644 --- a/packages/layers/src/core/triangulation.ts +++ b/packages/layers/src/core/triangulation.ts @@ -136,7 +136,11 @@ export function RasterImageTriangulation(feature: IEncodeFeature) { size: 5, }; } - +/** + * 计算3D弧线顶点 + * @param feature 映射数据 + * @param segNum 弧线线段数 + */ export function LineArcTriangulation(feature: IEncodeFeature, segNum = 30) { const coordinates = feature.coordinates as IPosition[]; const positions = []; diff --git a/packages/layers/src/index.ts b/packages/layers/src/index.ts index 79bfc5e97a..2c83d5b7ff 100644 --- a/packages/layers/src/index.ts +++ b/packages/layers/src/index.ts @@ -1,8 +1,9 @@ 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 Arc2DLineLayer from './line/arc2d'; +import LineLayer from './line/index'; import Point3dLayer from './point/extrude'; import PointImageLayer from './point/image'; import PointLayer from './point/index'; @@ -74,6 +75,7 @@ export { ImageLayer, HeatMapGridLayer, ArcLineLayer, + Arc2DLineLayer, // Line, // ImageLayer, // HeatMapLayer, diff --git a/packages/layers/src/line/arc2d.ts b/packages/layers/src/line/arc2d.ts new file mode 100644 index 0000000000..18e3c7a16d --- /dev/null +++ b/packages/layers/src/line/arc2d.ts @@ -0,0 +1,108 @@ +import { AttributeType, gl, IEncodeFeature, ILayer } from '@l7/core'; +import BaseLayer from '../core/BaseLayer'; +import { LineArcTriangulation } from '../core/triangulation'; +import line_arc2d_vert from './shaders/line_arc2d_vert.glsl'; +import line_arc_frag from './shaders/line_arc_frag.glsl'; +interface IArcLayerStyleOptions { + opacity: number; + segmentNumber: number; +} +export default class Arc2DLineLayer 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: 'arc2dline', + vertexShader: line_arc2d_vert, + fragmentShader: line_arc_frag, + triangulation: LineArcTriangulation, + depth: { enable: false }, + 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_arc2d_vert.glsl b/packages/layers/src/line/shaders/line_arc2d_vert.glsl new file mode 100644 index 0000000000..360aed1ee5 --- /dev/null +++ b/packages/layers/src/line/shaders/line_arc2d_vert.glsl @@ -0,0 +1,89 @@ +precision mediump float; +attribute vec4 a_Color; +attribute vec3 a_Position; +attribute vec4 a_Instance; +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.)); +} + +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; +} +float getAngularDist (vec2 source, vec2 target) { + vec2 delta = source - target; + vec2 sin_half_delta = sin(delta / 2.0); + float a = + sin_half_delta.y * sin_half_delta.y + + cos(source.y) * cos(target.y) * + sin_half_delta.x * sin_half_delta.x; + return 2.0 * atan(sqrt(a), sqrt(1.0 - a)); +} +vec2 interpolate (vec2 source, vec2 target, float angularDist, float t) { + // if the angularDist is PI, linear interpolation is applied. otherwise, use spherical interpolation + if(abs(angularDist - PI) < 0.001) { + return (1.0 - t) * source + t * target; + } + float a = sin((1.0 - t) * angularDist) / sin(angularDist); + float b = sin(t * angularDist) / sin(angularDist); + vec2 sin_source = sin(source); + vec2 cos_source = cos(source); + vec2 sin_target = sin(target); + vec2 cos_target = cos(target); + float x = a * cos_source.y * cos_source.x + b * cos_target.y * cos_target.x; + float y = a * cos_source.y * sin_source.x + b * cos_target.y * sin_target.x; + float z = a * sin_source.y + b * sin_target.y; + return vec2(atan(y, x), atan(z, sqrt(x * x + y * y))); +} + + +void main() { + v_color = a_Color; + vec2 source = radians(a_Instance.rg); + vec2 target = radians(a_Instance.ba); + float angularDist = getAngularDist(source, target); + 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); + + vec4 curr = project_position(vec4(degrees(interpolate(source, target, angularDist, segmentRatio)), 0.0, 1.0)); + vec4 next = project_position(vec4(degrees(interpolate(source, target, angularDist, nextSegmentRatio)), 0.0, 1.0)); + + vec2 offset = getExtrusionOffset((next.xy - curr.xy) * indexDir, a_Position.y); + + // vec4 project_pos = project_position(vec4(curr.xy, 0, 1.0)); + gl_Position = project_common_position_to_clipspace(vec4(curr.xy + offset, 0, 1.0)); +} + diff --git a/packages/layers/src/line/shaders/line_vert.glsl b/packages/layers/src/line/shaders/line_vert.glsl index 3cc4620ea4..75f3992525 100644 --- a/packages/layers/src/line/shaders/line_vert.glsl +++ b/packages/layers/src/line/shaders/line_vert.glsl @@ -12,6 +12,7 @@ uniform mat4 u_ModelMatrix; varying vec4 v_color; varying float v_dash_array; varying vec3 v_normal; + #pragma include "projection" void main() { v_normal = a_Normal; diff --git a/stories/Layers/Layers.stories.tsx b/stories/Layers/Layers.stories.tsx index 6946fffa22..4a105d43ee 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 Arc2DLineDemo from './components/Arc2DLine'; import ArcLineDemo from './components/Arcline'; import GridHeatMap from './components/heatMapgrid'; import LineLayer from './components/Line'; @@ -16,6 +17,7 @@ storiesOf('图层', module) .add('图片标注', () => ) .add('面3d图层', () => ) .add('线图层', () => ) - .add('弧线', () => ) + .add('3D弧线', () => ) + .add('2D弧线', () => ) .add('网格热力图', () => ) .add('图片', () => ); diff --git a/stories/Layers/components/Arc2DLine.tsx b/stories/Layers/components/Arc2DLine.tsx new file mode 100644 index 0000000000..8629a3f8e8 --- /dev/null +++ b/stories/Layers/components/Arc2DLine.tsx @@ -0,0 +1,56 @@ +import { Arc2DLineLayer } from '@l7/layers'; +import { Scene } from '@l7/scene'; +import * as React from 'react'; + +export default class Arc2DLineDemo 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 Arc2DLineLayer({}) + .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/Arcline.tsx b/stories/Layers/components/Arcline.tsx index 42333b66bb..0ffe2e4439 100644 --- a/stories/Layers/components/Arcline.tsx +++ b/stories/Layers/components/Arcline.tsx @@ -2,7 +2,7 @@ import { ArcLineLayer } from '@l7/layers'; import { Scene } from '@l7/scene'; import * as React from 'react'; -export default class ArxLineDemo extends React.Component { +export default class ArcLineDemo extends React.Component { private scene: Scene; public componentWillUnmount() {