feat(vector): add new polyline material properties and examplesmaster
| @@ -327,6 +327,26 @@ const EXAMPLE_LIST = [ | |||
| name: '发光轨迹线', | |||
| page: 'polyline_lighting_trail.html', | |||
| }, | |||
| { | |||
| name: '围栏线', | |||
| page: 'polyline_fence.html', | |||
| }, | |||
| { | |||
| name: '多箭头线', | |||
| page: 'polyline_multi_arrow.html', | |||
| }, | |||
| { | |||
| name: '虚线箭头线', | |||
| page: 'polyline_dash_arrow.html', | |||
| }, | |||
| { | |||
| name: '方向线', | |||
| page: 'polyline_direction.html', | |||
| }, | |||
| { | |||
| name: '自定义端点线', | |||
| page: 'polyline_custom_endpoint.html', | |||
| }, | |||
| { | |||
| name: '面', | |||
| page: 'polygon_base.html', | |||
| @@ -24,6 +24,7 @@ | |||
| <li><button onclick="draw('fine_arrow')">直箭头</button></li> | |||
| <li><button onclick="draw('tailed_attack_arrow')">燕尾箭头</button></li> | |||
| <li><button onclick="draw('gathering_place')">聚集地</button></li> | |||
| <li><button onclick="draw('bezier_curve')">曲线</button></li> | |||
| <li><button onclick="removeAll()">清除</button></li> | |||
| </ul> | |||
| </div> | |||
| @@ -0,0 +1,48 @@ | |||
| <!DOCTYPE html> | |||
| <html lang="en"> | |||
| <head> | |||
| <meta charset="utf-8" /> | |||
| <meta name="viewport" content="width=device-width,initial-scale=1.0" /> | |||
| <title>dc-example</title> | |||
| <script src="/libs/dc-sdk/dc.min.js"></script> | |||
| <link href="/libs/dc-sdk/dc.min.css" type="text/css" rel="stylesheet" /> | |||
| <link href="../index.css" type="text/css" rel="stylesheet" /> | |||
| </head> | |||
| <body> | |||
| <div id="viewer-container" class="viewer-container"></div> | |||
| <script> | |||
| DC.config.baseUrl = '../libs/dc-sdk/resources/' | |||
| let viewer = new DC.Viewer('viewer-container') | |||
| let baseLayer = DC.ImageryLayerFactory.createImageryLayer( | |||
| DC.ImageryType.AMAP, | |||
| { | |||
| style: 'img', | |||
| crs: 'WGS84', | |||
| } | |||
| ) | |||
| viewer.addBaseLayer(baseLayer, { | |||
| brightness: 0.1, | |||
| }) | |||
| let layer = new DC.VectorLayer('layer') | |||
| viewer.addLayer(layer) | |||
| let polyline = new DC.Polyline('-75, 35; -125, 35; -125, 10') | |||
| polyline.setStyle({ | |||
| width: 20, | |||
| material: new DC.PolylineCustomEndpointMaterialProperty({ | |||
| color: DC.Color.BLUE, | |||
| // outlineColor: DC.Color.GREEN, | |||
| startType: 2, | |||
| endType: 1, | |||
| outlineShow: true, | |||
| lineWidth: 50, | |||
| }), | |||
| clampToGround: true, | |||
| }) | |||
| layer.addOverlay(polyline) | |||
| viewer.flyTo(layer) | |||
| </script> | |||
| </body> | |||
| </html> | |||
| @@ -0,0 +1,53 @@ | |||
| <!DOCTYPE html> | |||
| <html lang="en"> | |||
| <head> | |||
| <meta charset="utf-8" /> | |||
| <meta name="viewport" content="width=device-width,initial-scale=1.0" /> | |||
| <title>dc-example</title> | |||
| <script src="/libs/dc-sdk/dc.min.js"></script> | |||
| <link href="/libs/dc-sdk/dc.min.css" type="text/css" rel="stylesheet" /> | |||
| <link href="../index.css" type="text/css" rel="stylesheet" /> | |||
| </head> | |||
| <body> | |||
| <div id="viewer-container" class="viewer-container"></div> | |||
| <script> | |||
| DC.config.baseUrl = '../libs/dc-sdk/resources/' | |||
| let viewer = new DC.Viewer('viewer-container') | |||
| let baseLayer = DC.ImageryLayerFactory.createImageryLayer( | |||
| DC.ImageryType.AMAP, | |||
| { | |||
| style: 'img', | |||
| crs: 'WGS84', | |||
| } | |||
| ) | |||
| viewer.addBaseLayer(baseLayer, { | |||
| brightness: 0.1, | |||
| }) | |||
| let layer = new DC.VectorLayer('layer') | |||
| viewer.addLayer(layer) | |||
| let polyline = new DC.Polyline('-75, 35; -125, 35; -125, 10') | |||
| polyline.setStyle({ | |||
| width: 20, | |||
| material: new DC.PolylineDashArrowMaterialProperty({ | |||
| color: DC.Color.BLUE, | |||
| }), | |||
| clampToGround: true, | |||
| }) | |||
| layer.addOverlay(polyline) | |||
| let polyline2 = new DC.Polyline('-60, 20; -110, 20; -110, -80') | |||
| polyline2.setStyle({ | |||
| width: 20, | |||
| material: new DC.PolylineEmissionMaterialProperty({ | |||
| color: DC.Color.BLUE, | |||
| }), | |||
| clampToGround: true, | |||
| }) | |||
| layer.addOverlay(polyline2) | |||
| viewer.flyTo(layer) | |||
| </script> | |||
| </body> | |||
| </html> | |||
| @@ -0,0 +1,45 @@ | |||
| <!DOCTYPE html> | |||
| <html lang="en"> | |||
| <head> | |||
| <meta charset="utf-8" /> | |||
| <meta name="viewport" content="width=device-width,initial-scale=1.0" /> | |||
| <title>dc-example</title> | |||
| <script src="/libs/dc-sdk/dc.min.js"></script> | |||
| <link href="/libs/dc-sdk/dc.min.css" type="text/css" rel="stylesheet" /> | |||
| <link href="../index.css" type="text/css" rel="stylesheet" /> | |||
| </head> | |||
| <body> | |||
| <div id="viewer-container" class="viewer-container"></div> | |||
| <script> | |||
| DC.config.baseUrl = '../libs/dc-sdk/resources/' | |||
| let viewer = new DC.Viewer('viewer-container') | |||
| let baseLayer = DC.ImageryLayerFactory.createImageryLayer( | |||
| DC.ImageryType.AMAP, | |||
| { | |||
| style: 'img', | |||
| crs: 'WGS84', | |||
| } | |||
| ) | |||
| viewer.addBaseLayer(baseLayer, { | |||
| brightness: 0.1, | |||
| }) | |||
| let layer = new DC.VectorLayer('layer') | |||
| viewer.addLayer(layer) | |||
| let polyline = new DC.Polyline('-75, 35; -125, 35; -125, 10') | |||
| polyline.setStyle({ | |||
| width: 20, | |||
| material: new DC.PolylineDirectionMaterialProperty({ | |||
| color: DC.Color.BLUE, | |||
| outlineColor: DC.Color.GREEN, | |||
| outlineWidth: 5, | |||
| }), | |||
| clampToGround: true, | |||
| }) | |||
| layer.addOverlay(polyline) | |||
| viewer.flyTo(layer) | |||
| </script> | |||
| </body> | |||
| </html> | |||
| @@ -0,0 +1,49 @@ | |||
| <!DOCTYPE html> | |||
| <html lang="en"> | |||
| <head> | |||
| <meta charset="utf-8" /> | |||
| <meta name="viewport" content="width=device-width,initial-scale=1.0" /> | |||
| <title>dc-example</title> | |||
| <script src="/libs/dc-sdk/dc.min.js"></script> | |||
| <link href="/libs/dc-sdk/dc.min.css" type="text/css" rel="stylesheet" /> | |||
| <link href="../index.css" type="text/css" rel="stylesheet" /> | |||
| </head> | |||
| <body> | |||
| <div id="viewer-container" class="viewer-container"></div> | |||
| <script> | |||
| DC.config.baseUrl = '../libs/dc-sdk/resources/' | |||
| let viewer = new DC.Viewer('viewer-container') | |||
| let baseLayer = DC.ImageryLayerFactory.createImageryLayer( | |||
| DC.ImageryType.AMAP, | |||
| { | |||
| style: 'img', | |||
| crs: 'WGS84', | |||
| } | |||
| ) | |||
| viewer.addBaseLayer(baseLayer, { | |||
| brightness: 0.1, | |||
| }) | |||
| let layer = new DC.VectorLayer('layer') | |||
| viewer.addLayer(layer) | |||
| let polyline = new DC.Polyline('-75, 35; -125, 35; -125, 10') | |||
| polyline.setStyle({ | |||
| width: 20, | |||
| material: new DC.PolylineFenceMaterialProperty({ | |||
| color: DC.Color.WHITE, | |||
| outlineColor: new Cesium.Color(1, 1, 1, 0), | |||
| outlineWidth: new Cesium.CallbackProperty((time) => { | |||
| const seconds = Cesium.JulianDate.toDate(time).getSeconds() | |||
| return 5 + Math.abs(Math.sin(seconds)) * 15 | |||
| }, false), | |||
| maskLength: 20, | |||
| }), | |||
| clampToGround: true, | |||
| }) | |||
| layer.addOverlay(polyline) | |||
| viewer.flyTo(layer) | |||
| </script> | |||
| </body> | |||
| </html> | |||
| @@ -0,0 +1,45 @@ | |||
| <!DOCTYPE html> | |||
| <html lang="en"> | |||
| <head> | |||
| <meta charset="utf-8" /> | |||
| <meta name="viewport" content="width=device-width,initial-scale=1.0" /> | |||
| <title>dc-example</title> | |||
| <script src="/libs/dc-sdk/dc.min.js"></script> | |||
| <link href="/libs/dc-sdk/dc.min.css" type="text/css" rel="stylesheet" /> | |||
| <link href="../index.css" type="text/css" rel="stylesheet" /> | |||
| </head> | |||
| <body> | |||
| <div id="viewer-container" class="viewer-container"></div> | |||
| <script> | |||
| DC.config.baseUrl = '../libs/dc-sdk/resources/' | |||
| let viewer = new DC.Viewer('viewer-container') | |||
| let baseLayer = DC.ImageryLayerFactory.createImageryLayer( | |||
| DC.ImageryType.AMAP, | |||
| { | |||
| style: 'img', | |||
| crs: 'WGS84', | |||
| } | |||
| ) | |||
| viewer.addBaseLayer(baseLayer, { | |||
| brightness: 0.1, | |||
| }) | |||
| let layer = new DC.VectorLayer('layer') | |||
| viewer.addLayer(layer) | |||
| let polyline = new DC.Polyline('-75, 35; -125, 35; -125, 10') | |||
| polyline.setStyle({ | |||
| width: 20, | |||
| material: new DC.PolylineMultiArrowMaterialProperty({ | |||
| color: DC.Color.WHITE, | |||
| repeatFactor: 100.0, | |||
| antiClockWise: true, | |||
| }), | |||
| clampToGround: true, | |||
| }) | |||
| layer.addOverlay(polyline) | |||
| viewer.flyTo(layer) | |||
| </script> | |||
| </body> | |||
| </html> | |||
| @@ -34,12 +34,17 @@ export { default as EllipsoidTrailMaterialProperty } from './property/ellipsoid/ | |||
| /** | |||
| * polyline material property | |||
| */ | |||
| export { default as PolylineCustomEndpointMaterialProperty } from './property/polyline/PolylineCustomEndpointMaterialProperty' | |||
| export { default as PolylineDirectionMaterialProperty } from './property/polyline/PolylineDirectionMaterialProperty' | |||
| export { default as PolylineDashArrowMaterialProperty } from './property/polyline/PolylineDashArrowMaterialProperty' | |||
| export { default as PolylineEmissionMaterialProperty } from './property/polyline/PolylineEmissionMaterialProperty' | |||
| export { default as PolylineFenceMaterialProperty } from './property/polyline/PolylineFenceMaterialProperty' | |||
| export { default as PolylineFlickerMaterialProperty } from './property/polyline/PolylineFlickerMaterialProperty' | |||
| export { default as PolylineFlowMaterialProperty } from './property/polyline/PolylineFlowMaterialProperty' | |||
| export { default as PolylineImageTrailMaterialProperty } from './property/polyline/PolylineImageTrailMaterialProperty' | |||
| export { default as PolylineLightingMaterialProperty } from './property/polyline/PolylineLightingMaterialProperty' | |||
| export { default as PolylineLightingTrailMaterialProperty } from './property/polyline/PolylineLightingTrailMaterialProperty' | |||
| export { default as PolylineMultiArrowMaterialProperty } from './property/polyline/PolylineMultiArrowMaterialProperty' | |||
| export { default as PolylineTrailMaterialProperty } from './property/polyline/PolylineTrailMaterialProperty' | |||
| /** | |||
| @@ -0,0 +1,76 @@ | |||
| /** | |||
| * PolylineCustomEndpoint Material Property | |||
| * @Author : Converted from fh2 | |||
| */ | |||
| import { Cesium } from '../../../../libs' | |||
| import MaterialProperty from '../../MaterialProperty' | |||
| /** | |||
| * PolylineCustomEndpointMaterialProperty | |||
| */ | |||
| class PolylineCustomEndpointMaterialProperty extends MaterialProperty { | |||
| constructor(options = {}) { | |||
| super(options) | |||
| this.color = options.color || Cesium.Color.WHITE | |||
| this._startType = undefined | |||
| this._startTypeSubscription = undefined | |||
| this.startType = options.startType | |||
| this._endType = undefined | |||
| this._endTypeSubscription = undefined | |||
| this.endType = options.endType | |||
| this._outlineShow = undefined | |||
| this._outlineShowSubscription = undefined | |||
| this.outlineShow = options.outlineShow || false | |||
| this._lineWidth = undefined | |||
| this._lineWidthSubscription = undefined | |||
| this.lineWidth = options.lineWidth | |||
| this._outlineColor = undefined | |||
| this._outlineColorSubscription = undefined | |||
| this.outlineColor = | |||
| options.outlineColor || | |||
| (this.outlineShow ? Cesium.Color.WHITE : this.color) | |||
| } | |||
| getType(time) { | |||
| return Cesium.Material.PolylineCustomEndpointType | |||
| } | |||
| getValue(time, result) { | |||
| if (!result) { | |||
| result = {} | |||
| } | |||
| result.color = Cesium.Property.getValueOrUndefined(this._color, time) | |||
| result.startType = Cesium.Property.getValueOrUndefined(this._startType, time) | |||
| result.endType = Cesium.Property.getValueOrUndefined(this._endType, time) | |||
| result.outlineShow = Cesium.Property.getValueOrUndefined(this._outlineShow, time) | |||
| result.lineWidth = Cesium.Property.getValueOrUndefined(this._lineWidth, time) | |||
| result.outlineColor = Cesium.Property.getValueOrUndefined(this._outlineColor, time) | |||
| return result | |||
| } | |||
| equals(other) { | |||
| return ( | |||
| this === other || | |||
| (other instanceof PolylineCustomEndpointMaterialProperty && | |||
| Cesium.Property.equals(this._color, other._color) && | |||
| Cesium.Property.equals(this._startType, other._startType) && | |||
| Cesium.Property.equals(this._endType, other._endType) && | |||
| Cesium.Property.equals(this._outlineShow, other._outlineShow) && | |||
| Cesium.Property.equals(this._lineWidth, other._lineWidth) && | |||
| Cesium.Property.equals(this._outlineColor, other._outlineColor)) | |||
| ) | |||
| } | |||
| } | |||
| Object.defineProperties(PolylineCustomEndpointMaterialProperty.prototype, { | |||
| color: Cesium.createPropertyDescriptor('color'), | |||
| startType: Cesium.createPropertyDescriptor('startType'), | |||
| endType: Cesium.createPropertyDescriptor('endType'), | |||
| outlineShow: Cesium.createPropertyDescriptor('outlineShow'), | |||
| lineWidth: Cesium.createPropertyDescriptor('lineWidth'), | |||
| outlineColor: Cesium.createPropertyDescriptor('outlineColor'), | |||
| }) | |||
| export default PolylineCustomEndpointMaterialProperty | |||
| @@ -0,0 +1,42 @@ | |||
| /** | |||
| * PolylineDashArrow Material Property | |||
| * @Author : Converted from fh2 | |||
| */ | |||
| import { Cesium } from '../../../../libs' | |||
| import MaterialProperty from '../../MaterialProperty' | |||
| /** | |||
| * PolylineDashArrowMaterialProperty | |||
| */ | |||
| class PolylineDashArrowMaterialProperty extends MaterialProperty { | |||
| constructor(options = {}) { | |||
| super(options) | |||
| } | |||
| getType(time) { | |||
| return Cesium.Material.PolylineDashArrowType | |||
| } | |||
| getValue(time, result) { | |||
| if (!result) { | |||
| result = {} | |||
| } | |||
| result.color = Cesium.Property.getValueOrUndefined(this._color, time) | |||
| return result | |||
| } | |||
| equals(other) { | |||
| return ( | |||
| this === other || | |||
| (other instanceof PolylineDashArrowMaterialProperty && | |||
| Cesium.Property.equals(this._color, other._color)) | |||
| ) | |||
| } | |||
| } | |||
| Object.defineProperties(PolylineDashArrowMaterialProperty.prototype, { | |||
| color: Cesium.createPropertyDescriptor('color'), | |||
| }) | |||
| export default PolylineDashArrowMaterialProperty | |||
| @@ -0,0 +1,60 @@ | |||
| /** | |||
| * PolylineDirection Material Property | |||
| * @Author : Converted from fh2 | |||
| */ | |||
| import { Cesium } from '../../../../libs' | |||
| import MaterialProperty from '../../MaterialProperty' | |||
| /** | |||
| * PolylineDirectionMaterialProperty | |||
| */ | |||
| class PolylineDirectionMaterialProperty extends MaterialProperty { | |||
| constructor(options = {}) { | |||
| super(options) | |||
| this._outlineWidth = undefined | |||
| this._outlineWidthSubscription = undefined | |||
| this.outlineWidth = options.outlineWidth | |||
| this._outlineColor = undefined | |||
| this._outlineColorSubscription = undefined | |||
| this.outlineColor = options.outlineColor | |||
| } | |||
| getType(time) { | |||
| return Cesium.Material.PolylineDirectionType | |||
| } | |||
| getValue(time, result) { | |||
| if (!result) { | |||
| result = {} | |||
| } | |||
| result.color = Cesium.Property.getValueOrUndefined(this._color, time) | |||
| result.outlineWidth = Cesium.Property.getValueOrUndefined( | |||
| this._outlineWidth, | |||
| time | |||
| ) | |||
| result.outlineColor = Cesium.Property.getValueOrUndefined( | |||
| this._outlineColor, | |||
| time | |||
| ) | |||
| return result | |||
| } | |||
| equals(other) { | |||
| return ( | |||
| this === other || | |||
| (other instanceof PolylineDirectionMaterialProperty && | |||
| Cesium.Property.equals(this._color, other._color) && | |||
| Cesium.Property.equals(this._outlineWidth, other._outlineWidth) && | |||
| Cesium.Property.equals(this._outlineColor, other._outlineColor)) | |||
| ) | |||
| } | |||
| } | |||
| Object.defineProperties(PolylineDirectionMaterialProperty.prototype, { | |||
| color: Cesium.createPropertyDescriptor('color'), | |||
| outlineWidth: Cesium.createPropertyDescriptor('outlineWidth'), | |||
| outlineColor: Cesium.createPropertyDescriptor('outlineColor'), | |||
| }) | |||
| export default PolylineDirectionMaterialProperty | |||
| @@ -0,0 +1,58 @@ | |||
| /** | |||
| * PolylineFence Material Property | |||
| * @Author : Converted from fh2 | |||
| */ | |||
| import { Cesium } from '../../../../libs' | |||
| import MaterialProperty from '../../MaterialProperty' | |||
| class PolylineFenceMaterialProperty extends MaterialProperty { | |||
| constructor(options = {}) { | |||
| super(options) | |||
| this.color = options.color || Cesium.Color.WHITE | |||
| this._outlineColor = undefined | |||
| this._outlineColorSubscription = undefined | |||
| this.outlineColor = options.outlineColor || new Cesium.Color(1, 1, 1, 0) | |||
| this._outlineWidth = undefined | |||
| this._outlineWidthSubscription = undefined | |||
| this.outlineWidth = options.outlineWidth ?? 10 | |||
| this._maskLength = undefined | |||
| this._maskLengthSubscription = undefined | |||
| this.maskLength = options.maskLength ?? 20 | |||
| } | |||
| getType(time) { | |||
| return Cesium.Material.PolylineFenceType | |||
| } | |||
| getValue(time, result) { | |||
| if (!result) { | |||
| result = {} | |||
| } | |||
| result.color = Cesium.Property.getValueOrUndefined(this._color, time) | |||
| result.outlineColor = Cesium.Property.getValueOrUndefined(this._outlineColor, time) | |||
| result.outlineWidth = Cesium.Property.getValueOrUndefined(this._outlineWidth, time) | |||
| result.maskLength = Cesium.Property.getValueOrUndefined(this._maskLength, time) | |||
| return result | |||
| } | |||
| equals(other) { | |||
| return ( | |||
| this === other || | |||
| (other instanceof PolylineFenceMaterialProperty && | |||
| Cesium.Property.equals(this._color, other._color) && | |||
| Cesium.Property.equals(this._outlineColor, other._outlineColor) && | |||
| Cesium.Property.equals(this._outlineWidth, other._outlineWidth) && | |||
| Cesium.Property.equals(this._maskLength, other._maskLength)) | |||
| ) | |||
| } | |||
| } | |||
| Object.defineProperties(PolylineFenceMaterialProperty.prototype, { | |||
| color: Cesium.createPropertyDescriptor('color'), | |||
| outlineColor: Cesium.createPropertyDescriptor('outlineColor'), | |||
| outlineWidth: Cesium.createPropertyDescriptor('outlineWidth'), | |||
| maskLength: Cesium.createPropertyDescriptor('maskLength'), | |||
| }) | |||
| export default PolylineFenceMaterialProperty | |||
| @@ -0,0 +1,60 @@ | |||
| /** | |||
| * PolylineMultiArrow Material Property | |||
| * @Author : Converted from fh2 | |||
| */ | |||
| import { Cesium } from '../../../../libs' | |||
| import MaterialProperty from '../../MaterialProperty' | |||
| /** | |||
| * PolylineMultiArrowMaterialProperty | |||
| */ | |||
| class PolylineMultiArrowMaterialProperty extends MaterialProperty { | |||
| constructor(options = {}) { | |||
| super(options) | |||
| this._repeatFactor = undefined | |||
| this._repeatFactorSubscription = undefined | |||
| this.repeatFactor = options.repeatFactor | |||
| this._antiClockWise = undefined | |||
| this._antiClockWiseSubscription = undefined | |||
| this.antiClockWise = options.antiClockWise | |||
| } | |||
| getType(time) { | |||
| return Cesium.Material.PolylineMultiArrowType | |||
| } | |||
| getValue(time, result) { | |||
| if (!result) { | |||
| result = {} | |||
| } | |||
| result.color = Cesium.Property.getValueOrUndefined(this._color, time) | |||
| result.repeatFactor = Cesium.Property.getValueOrUndefined( | |||
| this._repeatFactor, | |||
| time | |||
| ) | |||
| result.antiClockWise = Cesium.Property.getValueOrUndefined( | |||
| this._antiClockWise, | |||
| time | |||
| ) | |||
| return result | |||
| } | |||
| equals(other) { | |||
| return ( | |||
| this === other || | |||
| (other instanceof PolylineMultiArrowMaterialProperty && | |||
| Cesium.Property.equals(this._color, other._color) && | |||
| Cesium.Property.equals(this._repeatFactor, other._repeatFactor) && | |||
| Cesium.Property.equals(this._antiClockWise, other._antiClockWise)) | |||
| ) | |||
| } | |||
| } | |||
| Object.defineProperties(PolylineMultiArrowMaterialProperty.prototype, { | |||
| color: Cesium.createPropertyDescriptor('color'), | |||
| repeatFactor: Cesium.createPropertyDescriptor('repeatFactor'), | |||
| antiClockWise: Cesium.createPropertyDescriptor('antiClockWise'), | |||
| }) | |||
| export default PolylineMultiArrowMaterialProperty | |||
| @@ -0,0 +1,247 @@ | |||
| uniform vec4 color; // 线主体颜色 | |||
| uniform float startType; // 起始点的类型 | |||
| uniform float endType; // 终点的类型 | |||
| uniform vec4 outlineColor; // 边界颜色 | |||
| uniform bool outlineShow; | |||
| uniform float lineWidth; | |||
| const float SHAPE_TYPE_NORMAL = 0.0; // 普通 | |||
| const float SHAPE_TYPE_ARROW= 1.0; // 箭头 | |||
| const float SHAPE_TYPE_CIRCLE = 2.0; // 圆 | |||
| const float SHAPE_TYPE_END = 3.0; // 终止竖线 | |||
| const float ratio = 2.5; // 线宽和base的比例,测试经验值 | |||
| float outlineWidth = 0.005; // 效果比较好的经验值,后面有需要再传入 | |||
| float getArrowPointOnLine(vec2 p0, vec2 p1, float x){ | |||
| float slope = (p0.y - p1.y) / (p0.x - p1.x); | |||
| return slope * (x - p0.x) + p0.y; | |||
| } | |||
| float getCirclePointOnLine(vec2 center, float radius, float x, float upper) { | |||
| // 计算 x 到圆心的水平距离的平方 | |||
| float dx = x - center.x; | |||
| // 在圆方程中求解出 y 变化部 | |||
| float dy = sqrt(radius * radius - dx * dx); | |||
| dy = dy * 0.5 / radius; | |||
| // 根据 upper 的值决定上边还是下边 | |||
| if (upper == 1.0) { | |||
| return center.y + dy; // 上半边 | |||
| } else { | |||
| return center.y - dy; // 下半边 | |||
| } | |||
| } | |||
| czm_material czm_getMaterial(czm_materialInput materialInput) | |||
| { | |||
| czm_material material = czm_getDefaultMaterial(materialInput); | |||
| vec2 st = materialInput.st; | |||
| #if (__VERSION__ == 300 || defined(GL_OES_standard_derivatives)) | |||
| float base = 1.0 - abs(fwidth(st.s)) * lineWidth * ratio * czm_pixelRatio; | |||
| #else | |||
| // If no derivatives available (IE 10?), 2.5% of the line will be the arrow head | |||
| float base = 0.975; | |||
| #endif | |||
| float reverseBase = 1.0 - base; | |||
| float halfB = reverseBase / 2.0; | |||
| float baseLeft = reverseBase; | |||
| if(startType == SHAPE_TYPE_END) { | |||
| baseLeft = halfB; | |||
| } | |||
| if(endType == SHAPE_TYPE_END) { | |||
| base += halfB; | |||
| } | |||
| // 这里用来解决圆形端点的裂缝问题 | |||
| float circleOffset = 0.01 * baseLeft; | |||
| float halfWidth = 0.08; | |||
| // 左侧 | |||
| if(st.s < baseLeft) { | |||
| if(outlineShow) { | |||
| halfWidth += outlineWidth; | |||
| } | |||
| float ptOnUpperLineLeft = 0.5 + halfWidth; | |||
| float ptOnLowerLineLeft = 0.5 - halfWidth; | |||
| if(startType == SHAPE_TYPE_CIRCLE && st.s < baseLeft - circleOffset) { | |||
| float r = baseLeft / 2.0; | |||
| vec2 leftCenter = vec2(r, 0.5); | |||
| ptOnUpperLineLeft = getCirclePointOnLine(leftCenter, r, st.s, 1.0); | |||
| ptOnLowerLineLeft = getCirclePointOnLine(leftCenter, r, st.s, 0.0); | |||
| } else if(startType == SHAPE_TYPE_END) { | |||
| ptOnUpperLineLeft = 1.0; | |||
| ptOnLowerLineLeft = 0.0; | |||
| } else if(startType == SHAPE_TYPE_ARROW) { | |||
| vec2 leftCenter = vec2(0.0, 0.5); | |||
| ptOnUpperLineLeft = getArrowPointOnLine(vec2(baseLeft, 1.0), leftCenter, st.s); | |||
| ptOnLowerLineLeft = getArrowPointOnLine(vec2(baseLeft, 0.0), leftCenter, st.s); | |||
| } | |||
| float t = 1.0 - step(ptOnUpperLineLeft, st.t); | |||
| t *= step(ptOnLowerLineLeft, st.t); | |||
| float d1 = czm_infinity; | |||
| if (st.t < 0.5 - halfWidth && st.t > 0.5 + halfWidth) | |||
| { | |||
| d1 = abs(st.s - baseLeft); | |||
| } | |||
| float d2 = abs(st.t - ptOnUpperLineLeft); | |||
| float d3 = abs(st.t - ptOnLowerLineLeft); | |||
| float dist = min(min(d1, d2), d3); | |||
| float dtUpper = abs(st.t - ptOnUpperLineLeft); | |||
| dtUpper = step(dtUpper, outlineWidth); | |||
| float dtLower = abs(st.t - ptOnLowerLineLeft); | |||
| dtLower = step(dtLower, outlineWidth); | |||
| vec4 contentColor; | |||
| if(outlineShow) { | |||
| contentColor = mix(color, outlineColor, clamp(dtUpper + dtLower, 0.0, 1.0)); | |||
| } else { | |||
| contentColor = color; | |||
| } | |||
| vec4 outsideColor = vec4(0.0); | |||
| vec4 currentColor = mix(outsideColor, contentColor, clamp(t, 0.0, 1.0)); | |||
| vec4 outColor = czm_antialias(outlineColor, color, currentColor, dist, 0.05); | |||
| outColor = czm_gammaCorrect(outColor); | |||
| material.diffuse = outColor.rgb; | |||
| material.alpha = outColor.a; | |||
| return material; | |||
| } else if(st.s <= base) { | |||
| float fuzzFactor = 0.1; // 效果比较好的经验值 | |||
| if(lineWidth > 10.0) { | |||
| fuzzFactor = 0.05; // 效果比较好的经验值 | |||
| } | |||
| if(outlineShow) { | |||
| halfWidth += outlineWidth; | |||
| } | |||
| float ptOnUpperLineRight = 0.5 + halfWidth; | |||
| float ptOnLowerLineRight = 0.5 - halfWidth; | |||
| float s = step(0.5 - halfWidth, st.t); | |||
| s *= 1.0 - step(0.5 + halfWidth, st.t); | |||
| s *= 1.0 - step(base, st.s); | |||
| float t = step(base, materialInput.st.s); | |||
| t *= 1.0 - step(ptOnUpperLineRight, st.t); | |||
| t *= step(ptOnLowerLineRight, st.t); | |||
| // Find the distance from the closest separator (region between two colors) | |||
| float d1 = abs(st.t - (0.5 - halfWidth)); | |||
| float d2 = abs(st.t - (0.5 + halfWidth)); | |||
| float dist = min(d1, d2); | |||
| float dtUpper = abs(st.t - (0.5 + halfWidth)); | |||
| dtUpper = step(dtUpper, outlineWidth); | |||
| float dtLower = abs(st.t - (0.5 - halfWidth)); | |||
| dtLower = step(dtLower, outlineWidth); | |||
| vec4 contentColor; | |||
| if(outlineShow) { | |||
| contentColor = mix(color, outlineColor, clamp(dtUpper + dtLower, 0.0, 1.0)); | |||
| } else { | |||
| contentColor = color; | |||
| } | |||
| vec4 outsideColor = vec4(contentColor.r, contentColor.g, contentColor.b, 0.0); | |||
| vec4 currentColor = mix(outsideColor, contentColor, clamp(s + t, 0.0, 1.0)); | |||
| vec4 outColor = czm_antialias(outlineColor, color, currentColor, dist, fuzzFactor); | |||
| float delta = outlineWidth * 10.0; | |||
| outColor = czm_gammaCorrect(outColor); | |||
| material.diffuse = outColor.rgb; | |||
| material.alpha = outColor.a; | |||
| return material; | |||
| } else { | |||
| if(outlineShow) { | |||
| halfWidth += outlineWidth; | |||
| } | |||
| float ptOnUpperLineRight = 0.5 + halfWidth; | |||
| float ptOnLowerLineRight = 0.5 - halfWidth; | |||
| if(endType == SHAPE_TYPE_CIRCLE && st.s > base + circleOffset) { | |||
| float r = reverseBase / 2.0; | |||
| vec2 rightCenter = vec2(1.0 - r, 0.5); | |||
| ptOnUpperLineRight = getCirclePointOnLine(rightCenter, r, st.s, 1.0); | |||
| ptOnLowerLineRight = getCirclePointOnLine(rightCenter, r, st.s, 0.0); | |||
| } else if(endType == SHAPE_TYPE_END) { | |||
| ptOnUpperLineRight = 1.0; | |||
| ptOnLowerLineRight = 0.0; | |||
| } else if(endType == SHAPE_TYPE_ARROW) { | |||
| vec2 rightCenter = vec2(1.0, 0.5); | |||
| ptOnUpperLineRight = getArrowPointOnLine(vec2(base, 1.0), rightCenter, st.s); | |||
| ptOnLowerLineRight = getArrowPointOnLine(vec2(base, 0.0), rightCenter, st.s); | |||
| } | |||
| float s = step(0.5 - halfWidth, st.t); | |||
| s *= 1.0 - step(0.5 + halfWidth, st.t); | |||
| s *= 1.0 - step(base, st.s); | |||
| float t = step(base, materialInput.st.s); | |||
| t *= 1.0 - step(ptOnUpperLineRight, st.t); | |||
| t *= step(ptOnLowerLineRight, st.t); | |||
| // Find the distance from the closest separator (region between two colors) | |||
| float d1 = czm_infinity; | |||
| if (st.t < 0.5 - halfWidth && st.t > 0.5 + halfWidth) | |||
| { | |||
| d1 = abs(st.s - base); | |||
| } | |||
| float d2 = abs(st.t - ptOnUpperLineRight); | |||
| float d3 = abs(st.t - ptOnLowerLineRight); | |||
| float dist = min(min(d1, d2), d3); | |||
| float dtUpper = abs(st.t - ptOnUpperLineRight); | |||
| dtUpper = step(dtUpper, outlineWidth); | |||
| float dtLower = abs(st.t - ptOnLowerLineRight); | |||
| dtLower = step(dtLower, outlineWidth); | |||
| vec4 contentColor; | |||
| if(outlineShow) { | |||
| contentColor = mix(color, outlineColor, clamp(dtUpper + dtLower, 0.0, 1.0)); | |||
| } else { | |||
| contentColor = color; | |||
| } | |||
| vec4 outsideColor = vec4(contentColor.r, contentColor.g, contentColor.b, 0.0); | |||
| vec4 currentColor = mix(outsideColor, contentColor, clamp(s + t, 0.0, 1.0)); | |||
| vec4 outColor = czm_antialias(outlineColor, color, currentColor, dist, 0.05); | |||
| outColor = czm_gammaCorrect(outColor); | |||
| material.diffuse = outColor.rgb; | |||
| material.alpha = outColor.a; | |||
| return material; | |||
| } | |||
| } | |||
| @@ -0,0 +1,97 @@ | |||
| #ifdef GL_OES_standard_derivatives | |||
| #extension GL_OES_standard_derivatives : enable | |||
| #endif | |||
| uniform vec4 color; | |||
| uniform vec4 gapColor; | |||
| uniform float dashLength; | |||
| uniform float dashPattern; | |||
| in float v_polylineAngle; | |||
| in float v_width; | |||
| const float maskLength = 16.0; | |||
| mat2 rotate(float rad) { | |||
| float c = cos(rad); | |||
| float s = sin(rad); | |||
| return mat2( | |||
| c, s, | |||
| -s, c | |||
| ); | |||
| } | |||
| float getPointOnLine(vec2 p0, vec2 p1, float x) | |||
| { | |||
| float slope = (p0.y - p1.y) / (p0.x - p1.x); | |||
| return slope * (x - p0.x) + p0.y; | |||
| } | |||
| czm_material czm_getMaterial(czm_materialInput materialInput) | |||
| { | |||
| czm_material material = czm_getDefaultMaterial(materialInput); | |||
| vec2 pos = rotate(v_polylineAngle) * gl_FragCoord.xy; | |||
| // Get the relative position within the dash from 0 to 1 | |||
| float dashPosition = fract(pos.x / (dashLength * czm_pixelRatio)); | |||
| // Figure out the mask index. | |||
| float maskIndex = floor(dashPosition * maskLength); | |||
| // Test the bit mask. | |||
| float maskTest = floor(dashPattern / pow(2.0, maskIndex)); | |||
| vec4 fragColor = (mod(maskTest, 2.0) < 1.0) ? gapColor : color; | |||
| vec2 st = materialInput.st; | |||
| #ifdef GL_OES_standard_derivatives | |||
| float base = 1.0 - abs(fwidth(st.s)) * 10.0 * czm_pixelRatio; | |||
| #else | |||
| float base = 0.975; // 2.5% of the line will be the arrow head | |||
| #endif | |||
| vec2 center = vec2(1.0, 0.5); | |||
| float ptOnUpperLine = getPointOnLine(vec2(base, 1.0), center, st.s); | |||
| float ptOnLowerLine = getPointOnLine(vec2(base, 0.0), center, st.s); | |||
| float halfWidth = 0.15; | |||
| float s = step(0.5 - halfWidth, st.t); | |||
| s *= 1.0 - step(0.5 + halfWidth, st.t); | |||
| s *= 1.0 - step(base, st.s); | |||
| float t = step(base, materialInput.st.s); | |||
| t *= 1.0 - step(ptOnUpperLine, st.t); | |||
| t *= step(ptOnLowerLine, st.t); | |||
| // Find the distance from the closest separator (region between two colors) | |||
| float dist; | |||
| if (st.s < base) | |||
| { | |||
| if (fragColor.a < 0.005) { // matches 0/255 and 1/255 | |||
| discard; | |||
| } | |||
| float d1 = abs(st.t - (0.5 - halfWidth)); | |||
| float d2 = abs(st.t - (0.5 + halfWidth)); | |||
| dist = min(d1, d2); | |||
| } | |||
| else | |||
| { | |||
| fragColor = color; | |||
| float d1 = czm_infinity; | |||
| if (st.t < 0.5 - halfWidth && st.t > 0.5 + halfWidth) | |||
| { | |||
| d1 = abs(st.s - base); | |||
| } | |||
| float d2 = abs(st.t - ptOnUpperLine); | |||
| float d3 = abs(st.t - ptOnLowerLine); | |||
| dist = min(min(d1, d2), d3); | |||
| } | |||
| vec4 outsideColor = vec4(0.0); | |||
| vec4 currentColor = mix(outsideColor, fragColor, clamp(s + t, 0.0, 1.0)); | |||
| vec4 outColor = czm_antialias(outsideColor, fragColor, currentColor, dist); | |||
| outColor = czm_gammaCorrect(outColor); | |||
| material.diffuse = outColor.rgb; | |||
| material.alpha = outColor.a; | |||
| return material; | |||
| } | |||
| @@ -0,0 +1,99 @@ | |||
| #ifdef GL_OES_standard_derivatives | |||
| #extension GL_OES_standard_derivatives : enable | |||
| #endif | |||
| uniform vec4 color; | |||
| uniform vec4 directionColor; // 箭头颜色 | |||
| uniform vec4 outlineColor; // 边界颜色 | |||
| uniform float outlineWidth; // 边界宽度 | |||
| in float v_width; // 线段宽度 | |||
| in float v_polylineAngle; // 线段角度 | |||
| const float fragLength = 100.0; // 每个箭头线段有多长 | |||
| const float startPosition = 0.45; // 开始的位置,从 0 ~ 1 | |||
| const float endPosition = 0.55; // 结束的位置,从 0 ~ 1 | |||
| mat2 rotate(float rad) { | |||
| float c = cos(rad); | |||
| float s = sin(rad); | |||
| return mat2( | |||
| c, s, | |||
| -s, c | |||
| ); | |||
| } | |||
| float getPointOnLine(vec2 p0, vec2 p1, float x) | |||
| { | |||
| float slope = (p0.y - p1.y) / (p0.x - p1.x); // 根据两个点获取斜率 | |||
| return slope * (x - p0.x) + p0.y; // 根据斜率和 x 值获取 y 值 | |||
| } | |||
| czm_material czm_getMaterial(czm_materialInput materialInput) | |||
| { | |||
| // 用 Dash 的方式渲染正常的线 | |||
| czm_material material = czm_getDefaultMaterial(materialInput); | |||
| vec2 st = materialInput.st; | |||
| // copy from polyline outline | |||
| float halfInteriorWidth = 0.5 * (v_width - outlineWidth) / v_width; | |||
| float b = step(0.5 - halfInteriorWidth, st.t); | |||
| b *= 1.0 - step(0.5 + halfInteriorWidth, st.t); | |||
| // Find the distance from the closest separator (region between two colors) | |||
| float d1 = abs(st.t - (0.5 - halfInteriorWidth)); | |||
| float d2 = abs(st.t - (0.5 + halfInteriorWidth)); | |||
| float dist = min(d1, d2); | |||
| vec4 currentColor = mix(outlineColor, color, b); | |||
| vec4 outColor = czm_antialias(outlineColor, color, currentColor, dist); | |||
| outColor = czm_gammaCorrect(outColor); | |||
| // 获取当前位置处于窗口的相对位置(像素值) | |||
| vec2 pos = rotate(v_polylineAngle) * gl_FragCoord.xy; | |||
| // 获取当前位置处于箭头线段的哪部分, 0 ~ 1 | |||
| float maskS = fract(pos.x / (fragLength * czm_pixelRatio)); | |||
| // float maskS = fract(st.s / (abs(fwidth(st.s)) * fragLength * czm_pixelRatio)); | |||
| float maskT = st.t; | |||
| // 判断是正常的线还是箭头 | |||
| bool isDirection = (maskS > startPosition) && (maskS <= endPosition); | |||
| vec4 fragColor; | |||
| if (isDirection) { | |||
| // 渲染箭头 | |||
| float arrowWidth = (endPosition - startPosition) / 2.0; | |||
| float midS = startPosition + arrowWidth; | |||
| float t = 1.0; | |||
| if (maskS < midS) { | |||
| // 左边的三角形 | |||
| vec2 center = vec2(midS, 0.5); | |||
| float ptOnUpperLine = getPointOnLine(vec2(startPosition, 1.0), center, maskS); // 三角形上边的线 | |||
| float ptOnLowerLine = getPointOnLine(vec2(startPosition, 0.0), center, maskS); // 三角形下边的线 | |||
| t *= 1.0 - step(ptOnUpperLine, maskT); // 低于上面的线 | |||
| t *= step(ptOnLowerLine, maskT); // 而且高于下面的线 | |||
| t = 1.0 - t; // 取反 | |||
| } else { | |||
| // 右边的三角形 | |||
| vec2 center = vec2(endPosition, 0.5); | |||
| float ptOnUpperLine = getPointOnLine(vec2(midS, 1.0), center, maskS); // 三角形上边的线 | |||
| float ptOnLowerLine = getPointOnLine(vec2(midS, 0.0), center, maskS); // 三角形下边的线 | |||
| t *= 1.0 - step(ptOnUpperLine, maskT); // 低于上面的线 | |||
| t *= step(ptOnLowerLine, maskT); // 而且高于下面的线 | |||
| } | |||
| vec4 outsideColor = outColor; | |||
| vec4 currentColor = mix(outsideColor, directionColor, clamp(t, 0.0, 1.0)); | |||
| fragColor = currentColor; | |||
| } else { | |||
| fragColor = outColor; | |||
| } | |||
| fragColor = czm_gammaCorrect(fragColor); | |||
| material.diffuse = fragColor.rgb; | |||
| material.alpha = fragColor.a; | |||
| return material; | |||
| } | |||
| @@ -0,0 +1,70 @@ | |||
| #ifdef GL_OES_standard_derivatives | |||
| #extension GL_OES_standard_derivatives : enable | |||
| #endif | |||
| uniform vec4 color; | |||
| uniform float dashLength; | |||
| uniform float dashPattern; | |||
| uniform float maskLength; | |||
| uniform float outlineWidth; | |||
| uniform vec4 outlineColor; | |||
| in float v_polylineAngle; | |||
| in float v_width; // 线段宽度 | |||
| mat2 rotate(float rad) { | |||
| float c = cos(rad); | |||
| float s = sin(rad); | |||
| return mat2( | |||
| c, s, | |||
| -s, c | |||
| ); | |||
| } | |||
| // 计算给定输入的材质 | |||
| // 栅栏形状的polyline是以虚线材质未基础,往虚线空隙里面填充外轮廓是透明的外轮廓线所绘制的 | |||
| czm_material czm_getMaterial(czm_materialInput materialInput) | |||
| { | |||
| // 获取默认的材质 | |||
| czm_material material = czm_getDefaultMaterial(materialInput); | |||
| // 外轮廓纹理部分 | |||
| // 获取标准化的纹理坐标 | |||
| vec2 st = materialInput.st; | |||
| //v_width是整个线宽,outlineWidth是轮廓线宽,两者差值的一半就是外轮廓线内部线条的宽度 | |||
| float halfInteriorWidth = 0.5 * (v_width - outlineWidth) / v_width; | |||
| // 判断当前的纹理坐标是否在内部线条范围内,如果在范围内,b值将为1,否则为0。step(edge, x)函数会返回一个值,如果x < edge,返回0.0,否则返回1.0 | |||
| float b = step(0.5 - halfInteriorWidth, st.t); | |||
| b *= 1.0 - step(0.5 + halfInteriorWidth, st.t); | |||
| //计算当前片元距离最近的颜色分隔线的距离,这个距离将被用于后面的抗锯齿处理。 | |||
| float d1 = abs(st.t - (0.5 - halfInteriorWidth)); | |||
| float d2 = abs(st.t - (0.5 + halfInteriorWidth)); | |||
| float dist = min(d1, d2); | |||
| // 根据b的值选择颜色,如果b是1,则选择color,否则选择outlineColor。这就确定了当前片元的基础颜色 | |||
| vec4 currentColor = mix(outlineColor, color, b); | |||
| // 使用czm_antialias()函数进行抗锯齿处理,输入参数包括轮廓色,内部色,当前色和距离 | |||
| vec4 outColor = czm_antialias(outlineColor, color, currentColor, dist); | |||
| // 对经过抗锯齿处理后的颜色进行伽马校正 | |||
| vec4 gapColor = czm_gammaCorrect(outColor); | |||
| // 虚线纹理部分 | |||
| // 计算旋转后的片元位置。 | |||
| vec2 pos = rotate(v_polylineAngle) * gl_FragCoord.xy; | |||
| // 计算当前片元在虚线模式中的位置。 | |||
| float dashPosition = fract(pos.x / (dashLength * czm_pixelRatio)); | |||
| // 计算对应虚线模式的索引位置。 | |||
| float maskIndex = floor(dashPosition * maskLength); | |||
| // 通过使用虚线模式和索引位置,计算当前位置是否应有线条。 | |||
| float maskTest = floor(dashPattern / pow(2.0, maskIndex)); | |||
| // 如果当前位置应为虚线的间隔部分,则颜色设为gapColor,否则设为color。 | |||
| vec4 fragColor = (mod(maskTest, 2.0) < 1.0) ? gapColor : color; | |||
| if (fragColor.a < 0.005) { | |||
| discard; | |||
| } | |||
| // 设置材质的颜色和透明度,后返回处理过后的材质 | |||
| fragColor = czm_gammaCorrect(fragColor); | |||
| material.emission = fragColor.rgb; | |||
| material.alpha = fragColor.a; | |||
| return material; | |||
| } | |||
| @@ -0,0 +1,92 @@ | |||
| #ifdef GL_OES_standard_derivatives | |||
| #extension GL_OES_standard_derivatives : enable | |||
| #endif | |||
| uniform vec4 color; | |||
| uniform float repeatFactor;// 重复箭头的次数 | |||
| uniform bool antiClockWise; // 确定箭头的方向 | |||
| // 一个辅助函数,给定两点(p0, p1)和一个x坐标,返回直线上对应的y坐标 | |||
| float getPointOnLine(vec2 p0, vec2 p1, float x) | |||
| { | |||
| float slope = (p0.y - p1.y) / (p0.x - p1.x);// 计算斜率 | |||
| return slope * (x - p0.x) + p0.y;// 使用斜率公式返回y坐标 | |||
| } | |||
| // 主材质函数,将被Cesium调用以获得每个片元的材质属性 | |||
| czm_material czm_getMaterial(czm_materialInput materialInput) | |||
| { | |||
| czm_material material = czm_getDefaultMaterial(materialInput);// 获取默认材质 | |||
| vec2 st = materialInput.st;// 获取当前片元的纹理坐标 | |||
| if (antiClockWise) { // 根据antiClockWise的值选择性地翻转s坐标 | |||
| st.s = 1.0 - st.s; | |||
| } | |||
| float arrowWidth = 1.0 / repeatFactor;// 每个箭头的宽度 | |||
| // 使用mod函数将片元的s坐标映射到[0, arrowWidth]的范围内,从而实现箭头的重复 | |||
| st.s = mod(st.s, arrowWidth) / arrowWidth; | |||
| // 如果可用,使用fwidth函数来获得箭头的宽度,否则使用固定值 | |||
| #if (__VERSION__ == 300 || defined(GL_OES_standard_derivatives)) | |||
| float base = 1.0 - abs(fwidth(st.s)) * 10.0 * czm_pixelRatio; | |||
| #else | |||
| float base = 0.995; | |||
| #endif | |||
| vec2 center = vec2(1.0, 0.5);// 箭头的顶点坐标 | |||
| // 增大箭头的尺寸来覆盖空隙 | |||
| center.s += 0.01; | |||
| // 计算箭头上部和下部直线上的点 | |||
| float ptOnUpperLine = getPointOnLine(vec2(base, 1.0), center, st.s); | |||
| float ptOnLowerLine = getPointOnLine(vec2(base, 0.0), center, st.s); | |||
| float halfWidth = 0.15;// 箭头的半宽 | |||
| // 计算箭头主体部分的纹理坐标 | |||
| float s = step(0.5 - halfWidth, st.t); | |||
| s *= 1.0 - step(0.5 + halfWidth, st.t); | |||
| s *= 1.0 - step(base, st.s); | |||
| // 计算箭头顶部部分的纹理坐标 | |||
| float t = step(base, st.s); | |||
| t *= 1.0 - step(ptOnUpperLine, st.t); | |||
| t *= step(ptOnLowerLine, st.t); | |||
| // 计算片元与箭头边界的距离,以便进行抗锯齿处理 | |||
| float dist; | |||
| if (st.s < base) | |||
| { | |||
| float d1 = abs(st.t - (0.5 - halfWidth)); | |||
| float d2 = abs(st.t - (0.5 + halfWidth)); | |||
| dist = min(d1, d2); | |||
| } | |||
| else | |||
| { | |||
| float d1 = czm_infinity; | |||
| if (st.t < 0.5 - halfWidth && st.t > 0.5 + halfWidth) | |||
| { | |||
| d1 = abs(st.s - base); | |||
| } | |||
| float d2 = abs(st.t - ptOnUpperLine); | |||
| float d3 = abs(st.t - ptOnLowerLine); | |||
| dist = min(min(d1, d2), d3); | |||
| } | |||
| vec4 outsideColor = vec4(0.0);// 外部颜色(非箭头部分) | |||
| // 根据s和t的值混合颜色,如果片元位于箭头上,则使用箭头颜色,否则使用外部颜色 | |||
| vec4 currentColor = mix(outsideColor, color, clamp(s + t, 0.0, 1.0)); | |||
| // 使用czm_antialias函数进行抗锯齿处理 | |||
| vec4 outColor = czm_antialias(outsideColor, color, currentColor, dist); | |||
| outColor = czm_gammaCorrect(outColor); | |||
| material.diffuse = outColor.rgb;// 设置材质的漫反射颜色 | |||
| material.alpha = outColor.a;// 设置材质的透明度 | |||
| return material;// 返回材质 | |||
| } | |||
| @@ -9,6 +9,11 @@ import LineImageTrailMaterial from '../shader/polyline/PolylineImageTrailMateria | |||
| import LineLightingMaterial from '../shader/polyline/PolylineLightingMaterial.glsl' | |||
| import LineLightingTrailMaterial from '../shader/polyline/PolylineLightingTrailMaterial.glsl' | |||
| import LineTrailMaterial from '../shader/polyline/PolylineTrailMaterial.glsl' | |||
| import LineFenceMaterial from '../shader/polyline/PolylineFenceMaterial.glsl' | |||
| import LineMultiArrowMaterial from '../shader/polyline/PolylineMultiArrowMaterial.glsl' | |||
| import LineDashArrowMaterial from '../shader/polyline/PolylineDashArrowMaterial.glsl' | |||
| import LineDirectionMaterial from '../shader/polyline/PolylineDirectionMaterial.glsl' | |||
| import LineCustomEndpointMaterial from '../shader/polyline/PolylineCustomEndpointMaterial.glsl' | |||
| /** | |||
| * PolylineFlicker | |||
| @@ -142,3 +147,126 @@ Cesium.Material._materialCache.addMaterial(Cesium.Material.PolylineTrailType, { | |||
| return true | |||
| }, | |||
| }) | |||
| /** | |||
| * PolylineFence | |||
| * @type {string} | |||
| */ | |||
| Cesium.Material.PolylineFenceType = 'PolylineFence' | |||
| Cesium.Material._materialCache.addMaterial(Cesium.Material.PolylineFenceType, { | |||
| fabric: { | |||
| type: Cesium.Material.PolylineFenceType, | |||
| uniforms: { | |||
| color: new Cesium.Color(1.0, 1.0, 1.0, 1.0), | |||
| outlineColor: new Cesium.Color(1.0, 1.0, 1.0, 1.0), | |||
| dashLength: 10, | |||
| dashPattern: 15, | |||
| outlineWidth: 16, | |||
| maskLength: 20, | |||
| }, | |||
| source: LineFenceMaterial, | |||
| }, | |||
| translucent: function (material) { | |||
| return true | |||
| }, | |||
| }) | |||
| /** | |||
| * PolylineMultiArrow | |||
| * @type {string} | |||
| */ | |||
| Cesium.Material.PolylineMultiArrowType = 'PolylineMultiArrow' | |||
| Cesium.Material._materialCache.addMaterial( | |||
| Cesium.Material.PolylineMultiArrowType, | |||
| { | |||
| strict: true, | |||
| fabric: { | |||
| type: Cesium.Material.PolylineMultiArrowType, | |||
| uniforms: { | |||
| color: Cesium.Color.WHITE, | |||
| repeatFactor: 1, | |||
| antiClockWise: true, | |||
| }, | |||
| source: LineMultiArrowMaterial, | |||
| }, | |||
| translucent: function (material) { | |||
| return true | |||
| }, | |||
| } | |||
| ) | |||
| /** | |||
| * PolylineDashArrow | |||
| * @type {string} | |||
| */ | |||
| Cesium.Material.PolylineDashArrowType = 'PolylineDashArrow' | |||
| Cesium.Material._materialCache.addMaterial( | |||
| Cesium.Material.PolylineDashArrowType, | |||
| { | |||
| strict: true, | |||
| fabric: { | |||
| type: Cesium.Material.PolylineDashArrowType, | |||
| uniforms: { | |||
| color: Cesium.Color.WHITE, | |||
| gapColor: Cesium.Color.TRANSPARENT, | |||
| dashLength: 16, | |||
| dashPattern: 255, | |||
| }, | |||
| source: LineDashArrowMaterial, | |||
| }, | |||
| translucent: function (material) { | |||
| return true | |||
| }, | |||
| } | |||
| ) | |||
| /** | |||
| * PolylineDirection | |||
| * @type {string} | |||
| */ | |||
| Cesium.Material.PolylineDirectionType = 'PolylineDirection' | |||
| Cesium.Material._materialCache.addMaterial( | |||
| Cesium.Material.PolylineDirectionType, | |||
| { | |||
| fabric: { | |||
| type: Cesium.Material.PolylineDirectionType, | |||
| uniforms: { | |||
| color: new Cesium.Color(0, 1, 1, 1), | |||
| directionColor: new Cesium.Color(1, 1, 1, 1), | |||
| outlineColor: new Cesium.Color(1, 1, 1, 1), | |||
| outlineWidth: 0, | |||
| }, | |||
| source: LineDirectionMaterial, | |||
| }, | |||
| translucent: function (material) { | |||
| return true | |||
| }, | |||
| } | |||
| ) | |||
| /** | |||
| * PolylineCustomEndpoint | |||
| * @type {string} | |||
| */ | |||
| Cesium.Material.PolylineCustomEndpointType = 'PolylineCustomEndpoint' | |||
| Cesium.Material._materialCache.addMaterial( | |||
| Cesium.Material.PolylineCustomEndpointType, | |||
| { | |||
| strict: true, | |||
| fabric: { | |||
| type: Cesium.Material.PolylineCustomEndpointType, | |||
| uniforms: { | |||
| color: Cesium.Color.WHITE, | |||
| startType: 0.0, // 普通 0.0; 箭头 1.0; 圆 2.0; 终止竖线 3.0 | |||
| endType: 0.0, // 普通 0.0; 箭头 1.0; 圆 2.0; 终止竖线 3.0 | |||
| outlineColor: Cesium.Color.WHITE, | |||
| outlineShow: !1, | |||
| lineWidth: 3, | |||
| }, | |||
| source: LineCustomEndpointMaterial, | |||
| }, | |||
| translucent: function (material) { | |||
| return true | |||
| }, | |||
| } | |||
| ) | |||
| @@ -76,3 +76,4 @@ export { default as Polyline } from './vector/Polyline' | |||
| export { default as PolylineVolume } from './vector/PolylineVolume' | |||
| export { default as Rect } from './vector/Rect' | |||
| export { default as Wall } from './vector/Wall' | |||
| export { default as BezierCurve } from './vector/BezierCurve' | |||
| @@ -0,0 +1,544 @@ | |||
| /** | |||
| * BezierCurve - 贝塞尔曲线覆盖物 | |||
| * @Author : zishang520 | |||
| */ | |||
| import { Cesium } from '../../../libs' | |||
| import Overlay from '../Overlay' | |||
| import Parse from '../../parse/Parse' | |||
| import State from '../../state/State' | |||
| import { Transform } from '../../transform' | |||
| import { Util } from '../../utils' | |||
| class BezierCurve extends Overlay { | |||
| constructor(positions, options = {}) { | |||
| super() | |||
| this._positions = Parse.parsePositions(positions) | |||
| this._delegate = new Cesium.Entity({ polyline: {} }) | |||
| // 贝塞尔曲线配置参数 | |||
| this.resolution = options.resolution || 100 // 曲线分段数 | |||
| this.curveType = options.curveType || 'spline' // 'quadratic' | 'cubic' | 'auto' | |||
| this.showControlPoints = options.showControlPoints || false | |||
| this.controlPointStyle = options.controlPointStyle || { | |||
| pixelSize: 8, | |||
| color: Cesium.Color.YELLOW, | |||
| outlineColor: Cesium.Color.BLACK, | |||
| outlineWidth: 2, | |||
| } | |||
| this._controlPointEntities = [] | |||
| this._state = State.INITIALIZED | |||
| } | |||
| get type() { | |||
| return Overlay.getOverlayType('bezier_curve') | |||
| } | |||
| set positions(positions) { | |||
| this._positions = Parse.parsePositions(positions) | |||
| this._updateCurve() | |||
| } | |||
| get positions() { | |||
| return this._positions | |||
| } | |||
| /** | |||
| * 计算贝塞尔曲线点 | |||
| * @private | |||
| */ | |||
| _calculateBezierPoints() { | |||
| if (this._positions.length < 2) { | |||
| return [] | |||
| } | |||
| const cartesianPositions = Transform.transformWGS84ArrayToCartesianArray( | |||
| this._positions | |||
| ) | |||
| // 根据控制点数量决定曲线类型 | |||
| let curvePoints = [] | |||
| const pointCount = cartesianPositions.length | |||
| if (this.curveType === 'auto') { | |||
| if (pointCount === 2) { | |||
| curvePoints = this._calculateLinear(cartesianPositions) | |||
| } else if (pointCount === 3) { | |||
| curvePoints = this._calculateQuadraticBezier(cartesianPositions) | |||
| } else if (pointCount === 4) { | |||
| curvePoints = this._calculateCubicBezier(cartesianPositions) | |||
| } else { | |||
| // 使用Cardinal样条处理多个点 | |||
| curvePoints = this._calculateCardinalSpline(cartesianPositions) | |||
| } | |||
| } else if (this.curveType === 'quadratic' && pointCount >= 3) { | |||
| curvePoints = this._calculateQuadraticBezier( | |||
| cartesianPositions.slice(0, 3) | |||
| ) | |||
| } else if (this.curveType === 'cubic' && pointCount >= 4) { | |||
| curvePoints = this._calculateCubicBezier(cartesianPositions.slice(0, 4)) | |||
| } else if (this.curveType === 'spline') { | |||
| // 新增样条类型 | |||
| curvePoints = this._calculateCardinalSpline(cartesianPositions) | |||
| } else { | |||
| curvePoints = this._calculateLinear(cartesianPositions) | |||
| } | |||
| return curvePoints | |||
| } | |||
| /** | |||
| * 计算线性插值 | |||
| * @param {Array} points | |||
| * @private | |||
| */ | |||
| _calculateLinear(points) { | |||
| if (points.length < 2) return points | |||
| const result = [] | |||
| const p0 = points[0] | |||
| const p1 = points[1] | |||
| for (let i = 0; i <= this.resolution; i++) { | |||
| const t = i / this.resolution | |||
| const point = new Cesium.Cartesian3( | |||
| p0.x + t * (p1.x - p0.x), | |||
| p0.y + t * (p1.y - p0.y), | |||
| p0.z + t * (p1.z - p0.z) | |||
| ) | |||
| result.push(point) | |||
| } | |||
| return result | |||
| } | |||
| /** | |||
| * 计算二次贝塞尔曲线 | |||
| * @param {Array} points [P0, P1, P2] | |||
| * @private | |||
| */ | |||
| _calculateQuadraticBezier(points) { | |||
| if (points.length < 3) return points | |||
| const result = [] | |||
| const [p0, p1, p2] = points | |||
| for (let i = 0; i <= this.resolution; i++) { | |||
| const t = i / this.resolution | |||
| const u = 1 - t | |||
| // B(t) = (1-t)²P0 + 2(1-t)tP1 + t²P2 | |||
| const point = new Cesium.Cartesian3( | |||
| u * u * p0.x + 2 * u * t * p1.x + t * t * p2.x, | |||
| u * u * p0.y + 2 * u * t * p1.y + t * t * p2.y, | |||
| u * u * p0.z + 2 * u * t * p1.z + t * t * p2.z | |||
| ) | |||
| result.push(point) | |||
| } | |||
| return result | |||
| } | |||
| /** | |||
| * 计算三次贝塞尔曲线 | |||
| * @param {Array} points [P0, P1, P2, P3] | |||
| * @private | |||
| */ | |||
| _calculateCubicBezier(points) { | |||
| if (points.length < 4) return points | |||
| const result = [] | |||
| const [p0, p1, p2, p3] = points | |||
| for (let i = 0; i <= this.resolution; i++) { | |||
| const t = i / this.resolution | |||
| const u = 1 - t | |||
| // B(t) = (1-t)³P0 + 3(1-t)²tP1 + 3(1-t)t²P2 + t³P3 | |||
| const point = new Cesium.Cartesian3( | |||
| u * u * u * p0.x + | |||
| 3 * u * u * t * p1.x + | |||
| 3 * u * t * t * p2.x + | |||
| t * t * t * p3.x, | |||
| u * u * u * p0.y + | |||
| 3 * u * u * t * p1.y + | |||
| 3 * u * t * t * p2.y + | |||
| t * t * t * p3.y, | |||
| u * u * u * p0.z + | |||
| 3 * u * u * t * p1.z + | |||
| 3 * u * t * t * p2.z + | |||
| t * t * t * p3.z | |||
| ) | |||
| result.push(point) | |||
| } | |||
| return result | |||
| } | |||
| /** | |||
| * 计算多段贝塞尔曲线(使用Catmull-Rom样条或连续三次贝塞尔) | |||
| * @param {Array} points | |||
| * @private | |||
| */ | |||
| _calculateMultiSegmentBezier(points) { | |||
| if (points.length < 4) { | |||
| return points.length === 3 | |||
| ? this._calculateQuadraticBezier(points) | |||
| : this._calculateLinear(points) | |||
| } | |||
| // 使用连续的三次贝塞尔曲线方法 | |||
| return this._calculateContinuousCubicBezier(points) | |||
| } | |||
| /** | |||
| * 计算连续三次贝塞尔曲线 | |||
| * 使用控制点生成平滑的连续曲线 | |||
| * @param {Array} points | |||
| * @private | |||
| */ | |||
| _calculateContinuousCubicBezier(points) { | |||
| if (points.length < 4) { | |||
| return this._calculateQuadraticBezier(points.slice(0, 3)) | |||
| } | |||
| const result = [] | |||
| // 方法1: 使用原始控制点序列作为贝塞尔控制点 | |||
| if (points.length === 4) { | |||
| return this._calculateCubicBezier(points) | |||
| } | |||
| // 方法2: 对于超过4个点,使用分段插值 | |||
| // 将控制点作为关键点,生成中间的控制点 | |||
| const keyPoints = points | |||
| const segments = keyPoints.length - 1 | |||
| for (let i = 0; i < segments; i++) { | |||
| const p0 = keyPoints[Math.max(0, i - 1)] | |||
| const p1 = keyPoints[i] | |||
| const p2 = keyPoints[i + 1] | |||
| const p3 = keyPoints[Math.min(keyPoints.length - 1, i + 2)] | |||
| // 使用Catmull-Rom样条的控制点计算方法 | |||
| const segmentPoints = this._generateBezierControlPoints(p0, p1, p2, p3) | |||
| const segmentCurve = this._calculateCubicBezier(segmentPoints) | |||
| // 避免重复点 | |||
| const startFrom = i === 0 ? 0 : 1 | |||
| result.push(...segmentCurve.slice(startFrom)) | |||
| } | |||
| return result | |||
| } | |||
| /** | |||
| * 根据四个关键点生成三次贝塞尔控制点 | |||
| * 使用Catmull-Rom样条的切线方法 | |||
| * @param {Cesium.Cartesian3} p0 | |||
| * @param {Cesium.Cartesian3} p1 | |||
| * @param {Cesium.Cartesian3} p2 | |||
| * @param {Cesium.Cartesian3} p3 | |||
| * @private | |||
| */ | |||
| _generateBezierControlPoints(p0, p1, p2, p3) { | |||
| // Catmull-Rom to Bezier conversion | |||
| const tension = 0.5 // 可调节张力参数 | |||
| // 计算切线 | |||
| const t1 = new Cesium.Cartesian3( | |||
| tension * (p2.x - p0.x), | |||
| tension * (p2.y - p0.y), | |||
| tension * (p2.z - p0.z) | |||
| ) | |||
| const t2 = new Cesium.Cartesian3( | |||
| tension * (p3.x - p1.x), | |||
| tension * (p3.y - p1.y), | |||
| tension * (p3.z - p1.z) | |||
| ) | |||
| // 生成贝塞尔控制点 | |||
| const cp1 = new Cesium.Cartesian3( | |||
| p1.x + t1.x / 3, | |||
| p1.y + t1.y / 3, | |||
| p1.z + t1.z / 3 | |||
| ) | |||
| const cp2 = new Cesium.Cartesian3( | |||
| p2.x - t2.x / 3, | |||
| p2.y - t2.y / 3, | |||
| p2.z - t2.z / 3 | |||
| ) | |||
| return [p1, cp1, cp2, p2] | |||
| } | |||
| /** | |||
| * 使用Cardinal样条方法(备选实现) | |||
| * @param {Array} points | |||
| * @private | |||
| */ | |||
| _calculateCardinalSpline(points) { | |||
| if (points.length < 3) { | |||
| return this._calculateLinear(points) | |||
| } | |||
| const result = [] | |||
| const tension = 0.5 | |||
| // 添加虚拟端点以确保曲线通过第一个和最后一个点 | |||
| const extendedPoints = [ | |||
| points[0], // 重复第一个点 | |||
| ...points, | |||
| points[points.length - 1], // 重复最后一个点 | |||
| ] | |||
| for (let i = 1; i < extendedPoints.length - 2; i++) { | |||
| const p0 = extendedPoints[i - 1] | |||
| const p1 = extendedPoints[i] | |||
| const p2 = extendedPoints[i + 1] | |||
| const p3 = extendedPoints[i + 2] | |||
| // Cardinal spline interpolation | |||
| for (let t = 0; t <= 1; t += 1 / this.resolution) { | |||
| if (i === 1 && t === 0) { | |||
| // 添加起始点 | |||
| result.push(p1) | |||
| continue | |||
| } | |||
| const t2 = t * t | |||
| const t3 = t2 * t | |||
| // Cardinal spline基函数 | |||
| const h1 = 2 * t3 - 3 * t2 + 1 | |||
| const h2 = -2 * t3 + 3 * t2 | |||
| const h3 = t3 - 2 * t2 + t | |||
| const h4 = t3 - t2 | |||
| // 切线向量 | |||
| const tan1 = new Cesium.Cartesian3( | |||
| tension * (p2.x - p0.x), | |||
| tension * (p2.y - p0.y), | |||
| tension * (p2.z - p0.z) | |||
| ) | |||
| const tan2 = new Cesium.Cartesian3( | |||
| tension * (p3.x - p1.x), | |||
| tension * (p3.y - p1.y), | |||
| tension * (p3.z - p1.z) | |||
| ) | |||
| const point = new Cesium.Cartesian3( | |||
| h1 * p1.x + h2 * p2.x + h3 * tan1.x + h4 * tan2.x, | |||
| h1 * p1.y + h2 * p2.y + h3 * tan1.y + h4 * tan2.y, | |||
| h1 * p1.z + h2 * p2.z + h3 * tan1.z + h4 * tan2.z | |||
| ) | |||
| result.push(point) | |||
| } | |||
| } | |||
| return result | |||
| } | |||
| /** | |||
| * 更新曲线 | |||
| * @private | |||
| */ | |||
| _updateCurve() { | |||
| const curvePoints = this._calculateBezierPoints() | |||
| this._delegate.polyline.positions = curvePoints | |||
| if (this.showControlPoints) { | |||
| this._updateControlPoints() | |||
| } | |||
| } | |||
| /** | |||
| * 更新控制点显示 | |||
| * @private | |||
| */ | |||
| _updateControlPoints() { | |||
| // 清除现有控制点 | |||
| this._clearControlPoints() | |||
| if (!this.showControlPoints) return | |||
| const cartesianPositions = Transform.transformWGS84ArrayToCartesianArray( | |||
| this._positions | |||
| ) | |||
| cartesianPositions.forEach((position, index) => { | |||
| const controlPoint = new Cesium.Entity({ | |||
| position: position, | |||
| point: { | |||
| ...this.controlPointStyle, | |||
| disableDepthTestDistance: Number.POSITIVE_INFINITY, | |||
| }, | |||
| label: { | |||
| text: `C${index}`, | |||
| font: '12pt sans-serif', | |||
| pixelOffset: new Cesium.Cartesian2(0, -25), | |||
| fillColor: Cesium.Color.WHITE, | |||
| outlineColor: Cesium.Color.BLACK, | |||
| outlineWidth: 1, | |||
| style: Cesium.LabelStyle.FILL_AND_OUTLINE, | |||
| disableDepthTestDistance: Number.POSITIVE_INFINITY, | |||
| }, | |||
| }) | |||
| this._controlPointEntities.push(controlPoint) | |||
| }) | |||
| } | |||
| /** | |||
| * 清除控制点 | |||
| * @private | |||
| */ | |||
| _clearControlPoints() { | |||
| this._controlPointEntities.forEach((entity) => { | |||
| if (entity && entity.parent) { | |||
| entity.parent.entities.remove(entity) | |||
| } | |||
| }) | |||
| this._controlPointEntities = [] | |||
| } | |||
| /** | |||
| * 挂载钩子 | |||
| * @private | |||
| */ | |||
| _mountedHook() { | |||
| this.positions = this._positions | |||
| } | |||
| /** | |||
| * 设置曲线类型 | |||
| * @param {string} type 'linear' | 'quadratic' | 'cubic' | 'auto' | 'spline' | |||
| * @returns {BezierCurve} | |||
| */ | |||
| setCurveType(type) { | |||
| this.curveType = type | |||
| this._updateCurve() | |||
| return this | |||
| } | |||
| /** | |||
| * 设置分辨率 | |||
| * @param {number} resolution | |||
| * @returns {BezierCurve} | |||
| */ | |||
| setResolution(resolution) { | |||
| this.resolution = Math.max(10, Math.min(1000, resolution)) | |||
| this._updateCurve() | |||
| return this | |||
| } | |||
| /** | |||
| * 显示/隐藏控制点 | |||
| * @param {boolean} show | |||
| * @returns {BezierCurve} | |||
| */ | |||
| setShowControlPoints(show) { | |||
| this.showControlPoints = show | |||
| if (show) { | |||
| this._updateControlPoints() | |||
| } else { | |||
| this._clearControlPoints() | |||
| } | |||
| return this | |||
| } | |||
| /** | |||
| * 设置标签(贝塞尔曲线通常不需要标签,但保持接口一致性) | |||
| * @param {string} text | |||
| * @param {Object} textStyle | |||
| * @returns {BezierCurve} | |||
| */ | |||
| setLabel(text, textStyle) { | |||
| // 可以在曲线中点添加标签 | |||
| if (text) { | |||
| const curvePoints = this._calculateBezierPoints() | |||
| if (curvePoints.length > 0) { | |||
| const midIndex = Math.floor(curvePoints.length / 2) | |||
| this._delegate.label = { | |||
| text: text, | |||
| position: curvePoints[midIndex], | |||
| ...textStyle, | |||
| } | |||
| } | |||
| } | |||
| return this | |||
| } | |||
| /** | |||
| * 设置样式 | |||
| * @param {Object} style | |||
| * @returns {BezierCurve} | |||
| */ | |||
| setStyle(style) { | |||
| if (Object.keys(style).length === 0) { | |||
| return this | |||
| } | |||
| delete style['positions'] | |||
| Util.merge(this._style, style) | |||
| Util.merge(this._delegate.polyline, style) | |||
| // 更新控制点样式 | |||
| if (style.controlPointStyle) { | |||
| this.controlPointStyle = { | |||
| ...this.controlPointStyle, | |||
| ...style.controlPointStyle, | |||
| } | |||
| if (this.showControlPoints) { | |||
| this._updateControlPoints() | |||
| } | |||
| } | |||
| return this | |||
| } | |||
| /** | |||
| * 获取曲线长度(近似) | |||
| * @returns {number} | |||
| */ | |||
| getCurveLength() { | |||
| const curvePoints = this._calculateBezierPoints() | |||
| let length = 0 | |||
| for (let i = 1; i < curvePoints.length; i++) { | |||
| length += Cesium.Cartesian3.distance(curvePoints[i - 1], curvePoints[i]) | |||
| } | |||
| return length | |||
| } | |||
| /** | |||
| * 在曲线上获取指定参数t处的点 | |||
| * @param {number} t 参数值 (0-1) | |||
| * @returns {Cesium.Cartesian3} | |||
| */ | |||
| getPointAtParameter(t) { | |||
| t = Math.max(0, Math.min(1, t)) | |||
| const curvePoints = this._calculateBezierPoints() | |||
| const index = Math.floor(t * (curvePoints.length - 1)) | |||
| return curvePoints[index] || curvePoints[curvePoints.length - 1] | |||
| } | |||
| /** | |||
| * 销毁 | |||
| */ | |||
| destroy() { | |||
| this._clearControlPoints() | |||
| super.destroy?.() | |||
| } | |||
| } | |||
| // 注册覆盖物类型 | |||
| Overlay.registerType('bezier_curve') | |||
| export default BezierCurve | |||
| @@ -16,6 +16,7 @@ import DrawDoubleArrow from './draw/DrawDoubleArrow' | |||
| import DrawFineArrow from './draw/DrawFineArrow' | |||
| import DrawGatheringPlace from './draw/DrawGatheringPlace' | |||
| import DrawTailedAttackArrow from './draw/DrawTailedAttackArrow' | |||
| import DrawBezierCurve from './draw/DrawBezierCurve' | |||
| import EditPoint from './edit/EditPoint' | |||
| import EditPolyline from './edit/EditPolyline' | |||
| @@ -28,6 +29,7 @@ import EditDoubleArrow from './edit/EditDoubleArrow' | |||
| import EditFineArrow from './edit/EditFineArrow' | |||
| import EditGatheringPlace from './edit/EditGatheringPlace' | |||
| import EditTailedAttackArrow from './edit/EditTailedAttackArrow' | |||
| import EditBezierCurve from './edit/EditBezierCurve' | |||
| class Plot { | |||
| constructor(viewer, options = {}) { | |||
| @@ -93,6 +95,9 @@ class Plot { | |||
| case OverlayType.GATHERING_PLACE: | |||
| drawWorker = new DrawGatheringPlace(style) | |||
| break | |||
| case OverlayType.BEZIER_CURVE: | |||
| drawWorker = new DrawBezierCurve(style) | |||
| break | |||
| default: | |||
| break | |||
| } | |||
| @@ -140,6 +145,9 @@ class Plot { | |||
| case OverlayType.GATHERING_PLACE: | |||
| editWorker = new EditGatheringPlace(overlay) | |||
| break | |||
| case OverlayType.BEZIER_CURVE: | |||
| editWorker = new EditBezierCurve(overlay) | |||
| break | |||
| default: | |||
| break | |||
| } | |||
| @@ -0,0 +1,201 @@ | |||
| /** | |||
| * DrawBezierCurve - 绘制贝塞尔曲线工具 | |||
| * @Author : zisahng520 | |||
| */ | |||
| import { Cesium } from '../../../libs' | |||
| import Draw from './Draw' | |||
| import { PlotEventType } from '../../event' | |||
| import { Transform } from '../../transform' | |||
| import { BezierCurve } from '../../overlay' | |||
| const DEF_STYLE = { | |||
| width: 3, | |||
| material: Cesium.Color.CYAN, | |||
| clampToGround: true, | |||
| } | |||
| class DrawBezierCurve extends Draw { | |||
| constructor(style, options = {}) { | |||
| super() | |||
| this._minAnchorSize = options.minPoints || 2 | |||
| this._curveType = options.curveType || 'spline' | |||
| this._resolution = options.resolution || 100 | |||
| this._showControlPoints = (options.showControlPoints || false) !== false | |||
| this._style = { | |||
| ...DEF_STYLE, | |||
| ...style, | |||
| } | |||
| this._previewCurve = new BezierCurve([], { | |||
| curveType: this._curveType, | |||
| resolution: this._resolution, | |||
| showControlPoints: this._showControlPoints, | |||
| }) | |||
| } | |||
| /** | |||
| * 挂载钩子 | |||
| * @private | |||
| */ | |||
| _mountedHook() { | |||
| this.drawTool.tooltipMess = '单击添加控制点,右击完成绘制' | |||
| this._delegate = new Cesium.Entity({ | |||
| polyline: { | |||
| ...this._style, | |||
| positions: new Cesium.CallbackProperty(() => { | |||
| if (this._positions.length >= this._minAnchorSize) { | |||
| const wgs84Positions = | |||
| Transform.transformCartesianArrayToWGS84Array(this._positions) | |||
| this._previewCurve.positions = wgs84Positions | |||
| return this._previewCurve._calculateBezierPoints() | |||
| } | |||
| return this._positions | |||
| }, false), | |||
| }, | |||
| }) | |||
| this._layer.entities.add(this._delegate) | |||
| this._createControlPointsPreview() | |||
| } | |||
| /** | |||
| * 创建控制点预览 | |||
| * @private | |||
| */ | |||
| _createControlPointsPreview() { | |||
| if (!this._showControlPoints) return | |||
| this._controlPointsEntity = new Cesium.Entity({ | |||
| point: { | |||
| pixelSize: 0, // 隐藏主点 | |||
| }, | |||
| }) | |||
| // 使用 CallbackProperty 动态显示控制点 | |||
| this._controlPointsEntity.position = new Cesium.CallbackProperty(() => { | |||
| return ( | |||
| this._positions[this._positions.length - 1] || new Cesium.Cartesian3() | |||
| ) | |||
| }, false) | |||
| this._layer.entities.add(this._controlPointsEntity) | |||
| } | |||
| /** | |||
| * 停止钩子 | |||
| * @private | |||
| */ | |||
| _stoppedHook() { | |||
| let bezierCurve = null | |||
| if (this._positions.length >= this._minAnchorSize) { | |||
| const wgs84Positions = Transform.transformCartesianArrayToWGS84Array( | |||
| this._positions | |||
| ) | |||
| bezierCurve = new BezierCurve(wgs84Positions, { | |||
| curveType: this._curveType, | |||
| resolution: this._resolution, | |||
| showControlPoints: this._showControlPoints, | |||
| }).setStyle(this._style) | |||
| } | |||
| this._options.onDrawStop && this._options.onDrawStop(bezierCurve) | |||
| } | |||
| /** | |||
| * 锚点绘制处理 | |||
| * @param {Cesium.Cartesian3} position | |||
| * @private | |||
| */ | |||
| _onDrawAnchor(position) { | |||
| this._positions.push(position) | |||
| this.drawTool.fire(PlotEventType.CREATE_ANCHOR, { position }) | |||
| // 创建控制点可视化 | |||
| if (this._showControlPoints) { | |||
| const controlPoint = this._layer.entities.add({ | |||
| position: position, | |||
| point: { | |||
| pixelSize: 8, | |||
| color: Cesium.Color.YELLOW, | |||
| outlineColor: Cesium.Color.BLACK, | |||
| outlineWidth: 2, | |||
| disableDepthTestDistance: Number.POSITIVE_INFINITY, | |||
| }, | |||
| label: { | |||
| text: `C${this._positions.length - 1}`, | |||
| font: '12pt sans-serif', | |||
| pixelOffset: new Cesium.Cartesian2(0, -25), | |||
| fillColor: Cesium.Color.WHITE, | |||
| outlineColor: Cesium.Color.BLACK, | |||
| outlineWidth: 1, | |||
| style: Cesium.LabelStyle.FILL_AND_OUTLINE, | |||
| }, | |||
| }) | |||
| if (!this._tempControlPoints) { | |||
| this._tempControlPoints = [] | |||
| } | |||
| this._tempControlPoints.push(controlPoint) | |||
| } | |||
| // 更新提示信息 | |||
| if (this._positions.length < this._minAnchorSize) { | |||
| this.drawTool.tooltipMess = `还需要 ${ | |||
| this._minAnchorSize - this._positions.length | |||
| } 个控制点` | |||
| } else { | |||
| this.drawTool.tooltipMess = '继续添加控制点或右击完成' | |||
| } | |||
| } | |||
| /** | |||
| * 右键完成绘制 | |||
| * @param {Cesium.Cartesian3} position | |||
| * @private | |||
| */ | |||
| _onDrawRight(position) { | |||
| if (this._positions.length >= this._minAnchorSize) { | |||
| this.drawTool.fire(PlotEventType.DRAW_STOP) | |||
| } | |||
| } | |||
| /** | |||
| * 设置曲线类型 | |||
| * @param {string} type | |||
| */ | |||
| setCurveType(type) { | |||
| this._curveType = type | |||
| if (this._previewCurve) { | |||
| this._previewCurve.setCurveType(type) | |||
| } | |||
| } | |||
| /** | |||
| * 设置分辨率 | |||
| * @param {number} resolution | |||
| */ | |||
| setResolution(resolution) { | |||
| this._resolution = resolution | |||
| if (this._previewCurve) { | |||
| this._previewCurve.setResolution(resolution) | |||
| } | |||
| } | |||
| /** | |||
| * 清理临时控制点 | |||
| * @private | |||
| */ | |||
| _cleanup() { | |||
| if (this._tempControlPoints) { | |||
| this._tempControlPoints.forEach((point) => { | |||
| this._layer.entities.remove(point) | |||
| }) | |||
| this._tempControlPoints = [] | |||
| } | |||
| super._cleanup?.() | |||
| } | |||
| } | |||
| export default DrawBezierCurve | |||
| @@ -0,0 +1,415 @@ | |||
| /** | |||
| * EditBezierCurve - 编辑贝塞尔曲线工具 | |||
| * @Author : zishang520 | |||
| */ | |||
| import { Cesium } from '../../../libs' | |||
| import Edit from './Edit' | |||
| import { PlotEventType } from '../../event' | |||
| import { Transform } from '../../transform' | |||
| class EditBezierCurve extends Edit { | |||
| constructor(overlay, options = {}) { | |||
| super(overlay) | |||
| this._options = { | |||
| showAnchors: true, | |||
| allowAddPoints: true, | |||
| allowRemovePoints: true, | |||
| minPoints: 2, | |||
| maxPoints: 20, | |||
| ...options, | |||
| } | |||
| this._previewCurve = overlay | |||
| this._anchors = [] | |||
| this._isEditing = false | |||
| } | |||
| /** | |||
| * 挂载钩子 | |||
| * @private | |||
| */ | |||
| _mountedHook() { | |||
| try { | |||
| // 设置动态更新 | |||
| this._delegate.polyline.positions = new Cesium.CallbackProperty(() => { | |||
| if (this._positions.length >= 2) { | |||
| const wgs84Positions = Transform.transformCartesianArrayToWGS84Array( | |||
| this._positions | |||
| ) | |||
| this._previewCurve.positions = wgs84Positions | |||
| return this._previewCurve._calculateBezierPoints() | |||
| } | |||
| return this._positions | |||
| }, false) | |||
| // 添加编辑模式样式 | |||
| this._delegate.polyline.width = (this._delegate.polyline.width || 3) + 1 | |||
| this._delegate.polyline.material = Cesium.Color.ORANGE.withAlpha(0.8) | |||
| this._layer.entities.add(this._delegate) | |||
| this._createAnchors() | |||
| this._setupDragHandlers() | |||
| this._isEditing = true | |||
| this.editTool?.fire(PlotEventType.EDIT_START, { | |||
| overlay: this._overlay, | |||
| positions: this._positions, | |||
| }) | |||
| } catch (error) { | |||
| console.error('Failed to mount EditBezierCurve:', error) | |||
| this._cleanup() | |||
| } | |||
| } | |||
| /** | |||
| * 创建锚点 | |||
| * @private | |||
| */ | |||
| _createAnchors() { | |||
| if (!this._options.showAnchors) return | |||
| this._cleanupAnchors() | |||
| this._anchors = [] | |||
| this._positions.forEach((position, index) => { | |||
| const anchor = this._layer.entities.add({ | |||
| position: position, | |||
| point: { | |||
| pixelSize: 10, | |||
| color: Cesium.Color.LIME, | |||
| outlineColor: Cesium.Color.BLACK, | |||
| outlineWidth: 2, | |||
| heightReference: Cesium.HeightReference.CLAMP_TO_GROUND, | |||
| disableDepthTestDistance: Number.POSITIVE_INFINITY, | |||
| }, | |||
| label: { | |||
| text: `C${index}`, | |||
| font: '12pt sans-serif', | |||
| pixelOffset: new Cesium.Cartesian2(0, -30), | |||
| fillColor: Cesium.Color.WHITE, | |||
| outlineColor: Cesium.Color.BLACK, | |||
| outlineWidth: 2, | |||
| style: Cesium.LabelStyle.FILL_AND_OUTLINE, | |||
| }, | |||
| }) | |||
| anchor._editIndex = index | |||
| this._anchors.push(anchor) | |||
| }) | |||
| } | |||
| /** | |||
| * 设置拖拽处理器 | |||
| * @private | |||
| */ | |||
| _setupDragHandlers() { | |||
| if (!this.editTool || !this.editTool.viewer) return | |||
| const viewer = this.editTool.viewer | |||
| const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas) | |||
| let isDragging = false | |||
| let draggedAnchor = null | |||
| let draggedIndex = -1 | |||
| // 左键按下事件 | |||
| handler.setInputAction((click) => { | |||
| const pickedObject = viewer.scene.pick(click.position) | |||
| if ( | |||
| pickedObject && | |||
| pickedObject.id && | |||
| this._anchors.includes(pickedObject.id) | |||
| ) { | |||
| isDragging = true | |||
| draggedAnchor = pickedObject.id | |||
| draggedIndex = draggedAnchor._editIndex | |||
| viewer.scene.screenSpaceCameraController.enableRotate = false | |||
| viewer.scene.screenSpaceCameraController.enableTranslate = false | |||
| viewer.scene.screenSpaceCameraController.enableZoom = false | |||
| } | |||
| }, Cesium.ScreenSpaceEventType.LEFT_DOWN) | |||
| // 鼠标移动事件 | |||
| handler.setInputAction((movement) => { | |||
| if (isDragging && draggedAnchor) { | |||
| const ray = viewer.camera.getPickRay(movement.endPosition) | |||
| const newPosition = viewer.scene.globe.pick(ray, viewer.scene) | |||
| if (newPosition) { | |||
| this.updateAnchorPosition(draggedIndex, newPosition) | |||
| } | |||
| } | |||
| }, Cesium.ScreenSpaceEventType.MOUSE_MOVE) | |||
| // 左键释放事件 | |||
| handler.setInputAction(() => { | |||
| if (isDragging) { | |||
| isDragging = false | |||
| draggedAnchor = null | |||
| draggedIndex = -1 | |||
| viewer.scene.screenSpaceCameraController.enableRotate = true | |||
| viewer.scene.screenSpaceCameraController.enableTranslate = true | |||
| viewer.scene.screenSpaceCameraController.enableZoom = true | |||
| } | |||
| }, Cesium.ScreenSpaceEventType.LEFT_UP) | |||
| // 右键菜单事件(可选功能:添加/删除控制点) | |||
| handler.setInputAction((click) => { | |||
| const pickedObject = viewer.scene.pick(click.position) | |||
| if ( | |||
| pickedObject && | |||
| pickedObject.id && | |||
| this._anchors.includes(pickedObject.id) | |||
| ) { | |||
| const anchorIndex = pickedObject.id._editIndex | |||
| this._showContextMenu(click.position, anchorIndex) | |||
| } | |||
| }, Cesium.ScreenSpaceEventType.RIGHT_CLICK) | |||
| // 存储handler以便清理 | |||
| this._dragHandler = handler | |||
| } | |||
| /** | |||
| * 显示右键菜单 | |||
| * @param {Cesium.Cartesian2} position | |||
| * @param {number} anchorIndex | |||
| * @private | |||
| */ | |||
| _showContextMenu(position, anchorIndex) { | |||
| // 这里可以集成一个简单的上下文菜单 | |||
| const actions = [] | |||
| if ( | |||
| this._options.allowAddPoints && | |||
| this._positions.length < this._options.maxPoints | |||
| ) { | |||
| actions.push({ | |||
| text: '在此处插入点', | |||
| action: () => { | |||
| const ray = this.editTool.viewer.camera.getPickRay(position) | |||
| const newPosition = this.editTool.viewer.scene.globe.pick( | |||
| ray, | |||
| this.editTool.viewer.scene | |||
| ) | |||
| if (newPosition) { | |||
| this.addControlPoint(newPosition, anchorIndex + 1) | |||
| } | |||
| }, | |||
| }) | |||
| } | |||
| if ( | |||
| this._options.allowRemovePoints && | |||
| this._positions.length > this._options.minPoints | |||
| ) { | |||
| actions.push({ | |||
| text: '删除此点', | |||
| action: () => { | |||
| this.removeControlPoint(anchorIndex) | |||
| }, | |||
| }) | |||
| } | |||
| // 触发菜单事件,让外部处理菜单显示 | |||
| this.editTool?.fire(PlotEventType.SHOW_CONTEXT_MENU, { | |||
| position, | |||
| anchorIndex, | |||
| actions, | |||
| overlay: this._overlay, | |||
| }) | |||
| } | |||
| /** | |||
| * @param {number} anchorIndex | |||
| * @param {Cesium.Cartesian3} newPosition | |||
| */ | |||
| updateAnchorPosition(anchorIndex, newPosition) { | |||
| if (anchorIndex >= 0 && anchorIndex < this._positions.length) { | |||
| this._positions[anchorIndex] = newPosition | |||
| // 更新锚点显示 | |||
| if (this._anchors[anchorIndex]) { | |||
| this._anchors[anchorIndex].position = newPosition | |||
| } | |||
| // 更新原始覆盖物 | |||
| const wgs84Positions = Transform.transformCartesianArrayToWGS84Array( | |||
| this._positions | |||
| ) | |||
| this._overlay.positions = wgs84Positions | |||
| this.editTool?.fire(PlotEventType.EDIT_ANCHOR_UPDATE, { | |||
| overlay: this._overlay, | |||
| anchorIndex, | |||
| position: newPosition, | |||
| positions: this._positions, | |||
| }) | |||
| } | |||
| } | |||
| /** | |||
| * 添加控制点 | |||
| * @param {Cesium.Cartesian3} position | |||
| * @param {number} insertIndex | |||
| */ | |||
| addControlPoint(position, insertIndex = -1) { | |||
| if (this._positions.length >= this._options.maxPoints) { | |||
| console.warn(`已达到最大控制点数量 (${this._options.maxPoints})`) | |||
| return false | |||
| } | |||
| if (insertIndex === -1) { | |||
| this._positions.push(position) | |||
| } else { | |||
| this._positions.splice(insertIndex, 0, position) | |||
| } | |||
| this._createAnchors() | |||
| const wgs84Positions = Transform.transformCartesianArrayToWGS84Array( | |||
| this._positions | |||
| ) | |||
| this._overlay.positions = wgs84Positions | |||
| this.editTool?.fire(PlotEventType.EDIT_ANCHOR_ADD, { | |||
| overlay: this._overlay, | |||
| position, | |||
| insertIndex, | |||
| positions: this._positions, | |||
| }) | |||
| return true | |||
| } | |||
| /** | |||
| * 删除控制点 | |||
| * @param {number} anchorIndex | |||
| */ | |||
| removeControlPoint(anchorIndex) { | |||
| if (this._positions.length <= this._options.minPoints) { | |||
| console.warn(`至少需要 ${this._options.minPoints} 个控制点`) | |||
| return false | |||
| } | |||
| if (anchorIndex >= 0 && anchorIndex < this._positions.length) { | |||
| const removedPosition = this._positions.splice(anchorIndex, 1)[0] | |||
| this._createAnchors() | |||
| const wgs84Positions = Transform.transformCartesianArrayToWGS84Array( | |||
| this._positions | |||
| ) | |||
| this._overlay.positions = wgs84Positions | |||
| this.editTool?.fire(PlotEventType.EDIT_ANCHOR_REMOVE, { | |||
| overlay: this._overlay, | |||
| anchorIndex, | |||
| removedPosition, | |||
| positions: this._positions, | |||
| }) | |||
| return true | |||
| } | |||
| return false | |||
| } | |||
| /** | |||
| * 清理锚点 | |||
| * @private | |||
| */ | |||
| _cleanupAnchors() { | |||
| if (this._anchors) { | |||
| this._anchors.forEach((anchor) => { | |||
| this._layer.entities.remove(anchor) | |||
| }) | |||
| this._anchors = [] | |||
| } | |||
| } | |||
| /** | |||
| * 完成编辑 | |||
| */ | |||
| finishEdit() { | |||
| if (this._isEditing) { | |||
| this._cleanup() | |||
| this._cleanupAnchors() | |||
| if (this._dragHandler) { | |||
| this._dragHandler.destroy() | |||
| this._dragHandler = null | |||
| } | |||
| this._isEditing = false | |||
| this.editTool?.fire(PlotEventType.EDIT_STOP, { | |||
| overlay: this._overlay, | |||
| positions: this._positions | |||
| }) | |||
| } | |||
| } | |||
| /** | |||
| * 取消编辑 | |||
| */ | |||
| cancelEdit() { | |||
| if (this._isEditing) { | |||
| // 恢复原始位置 | |||
| this._overlay.positions = this._originalPositions | |||
| this._cleanup() | |||
| this.editTool?.fire(PlotEventType.EDIT_CANCEL, { | |||
| overlay: this._overlay, | |||
| }) | |||
| this._isEditing = false | |||
| } | |||
| } | |||
| /** | |||
| * 获取编辑状态 | |||
| */ | |||
| getEditState() { | |||
| return { | |||
| isEditing: this._isEditing, | |||
| positions: this._positions, | |||
| controlPointCount: this._positions.length, | |||
| canAddPoints: this._positions.length < this._options.maxPoints, | |||
| canRemovePoints: this._positions.length > this._options.minPoints, | |||
| curveType: this._overlay.curveType, | |||
| curveLength: this._overlay.getCurveLength(), | |||
| } | |||
| } | |||
| /** | |||
| * 清理资源 | |||
| * @private | |||
| */ | |||
| _cleanup() { | |||
| this._cleanupAnchors() | |||
| // 清理拖拽处理器 | |||
| if (this._dragHandler) { | |||
| this._dragHandler.destroy() | |||
| this._dragHandler = null | |||
| } | |||
| // 恢复相机控制 | |||
| if (this.editTool && this.editTool.viewer) { | |||
| const viewer = this.editTool.viewer | |||
| viewer.scene.screenSpaceCameraController.enableRotate = true | |||
| viewer.scene.screenSpaceCameraController.enableTranslate = true | |||
| viewer.scene.screenSpaceCameraController.enableZoom = true | |||
| } | |||
| super._cleanup?.() | |||
| } | |||
| /** | |||
| * 销毁 | |||
| */ | |||
| destroy() { | |||
| this.cancelEdit() | |||
| this._previewCurve = null | |||
| super.destroy?.() | |||
| } | |||
| } | |||
| export default EditBezierCurve | |||