Parcourir la source

Merge pull request #220 from zishang520/feat/materials-curves

feat(vector): add new polyline material properties and examples
master
Caven Chen il y a 1 mois
Parent
révision
b5ec5704d3
Aucun compte lié à l'adresse e-mail de l'auteur
24 fichiers modifiés avec 2465 ajouts et 1 suppressions
  1. 20
    0
      examples/list.js
  2. 1
    0
      examples/tools/plot.html
  3. 48
    0
      examples/vector/polyline_custom_endpoint.html
  4. 53
    0
      examples/vector/polyline_dash_arrow.html
  5. 45
    0
      examples/vector/polyline_direction.html
  6. 49
    0
      examples/vector/polyline_fence.html
  7. 45
    0
      examples/vector/polyline_multi_arrow.html
  8. 6
    1
      src/modules/material/index.js
  9. 76
    0
      src/modules/material/property/polyline/PolylineCustomEndpointMaterialProperty.js
  10. 42
    0
      src/modules/material/property/polyline/PolylineDashArrowMaterialProperty.js
  11. 60
    0
      src/modules/material/property/polyline/PolylineDirectionMaterialProperty.js
  12. 58
    0
      src/modules/material/property/polyline/PolylineFenceMaterialProperty.js
  13. 60
    0
      src/modules/material/property/polyline/PolylineMultiArrowMaterialProperty.js
  14. 247
    0
      src/modules/material/shader/polyline/PolylineCustomEndpointMaterial.glsl
  15. 97
    0
      src/modules/material/shader/polyline/PolylineDashArrowMaterial.glsl
  16. 99
    0
      src/modules/material/shader/polyline/PolylineDirectionMaterial.glsl
  17. 70
    0
      src/modules/material/shader/polyline/PolylineFenceMaterial.glsl
  18. 92
    0
      src/modules/material/shader/polyline/PolylineMultiArrowMaterial.glsl
  19. 128
    0
      src/modules/material/type/polyline.js
  20. 1
    0
      src/modules/overlay/index.js
  21. 544
    0
      src/modules/overlay/vector/BezierCurve.js
  22. 8
    0
      src/modules/plot/Plot.js
  23. 201
    0
      src/modules/plot/draw/DrawBezierCurve.js
  24. 415
    0
      src/modules/plot/edit/EditBezierCurve.js

+ 20
- 0
examples/list.js Voir le fichier

@@ -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',

+ 1
- 0
examples/tools/plot.html Voir le fichier

@@ -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>

+ 48
- 0
examples/vector/polyline_custom_endpoint.html Voir le fichier

@@ -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>

+ 53
- 0
examples/vector/polyline_dash_arrow.html Voir le fichier

@@ -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>

+ 45
- 0
examples/vector/polyline_direction.html Voir le fichier

@@ -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>

+ 49
- 0
examples/vector/polyline_fence.html Voir le fichier

@@ -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>

+ 45
- 0
examples/vector/polyline_multi_arrow.html Voir le fichier

@@ -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>

+ 6
- 1
src/modules/material/index.js Voir le fichier

@@ -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'

/**

+ 76
- 0
src/modules/material/property/polyline/PolylineCustomEndpointMaterialProperty.js Voir le fichier

@@ -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

+ 42
- 0
src/modules/material/property/polyline/PolylineDashArrowMaterialProperty.js Voir le fichier

@@ -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

+ 60
- 0
src/modules/material/property/polyline/PolylineDirectionMaterialProperty.js Voir le fichier

@@ -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

+ 58
- 0
src/modules/material/property/polyline/PolylineFenceMaterialProperty.js Voir le fichier

@@ -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

+ 60
- 0
src/modules/material/property/polyline/PolylineMultiArrowMaterialProperty.js Voir le fichier

@@ -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

+ 247
- 0
src/modules/material/shader/polyline/PolylineCustomEndpointMaterial.glsl Voir le fichier

@@ -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;
}
}

+ 97
- 0
src/modules/material/shader/polyline/PolylineDashArrowMaterial.glsl Voir le fichier

@@ -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;
}

+ 99
- 0
src/modules/material/shader/polyline/PolylineDirectionMaterial.glsl Voir le fichier

@@ -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;
}

+ 70
- 0
src/modules/material/shader/polyline/PolylineFenceMaterial.glsl Voir le fichier

@@ -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;
}

+ 92
- 0
src/modules/material/shader/polyline/PolylineMultiArrowMaterial.glsl Voir le fichier

@@ -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;// 返回材质
}



+ 128
- 0
src/modules/material/type/polyline.js Voir le fichier

@@ -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
},
}
)

+ 1
- 0
src/modules/overlay/index.js Voir le fichier

@@ -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'

+ 544
- 0
src/modules/overlay/vector/BezierCurve.js Voir le fichier

@@ -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

+ 8
- 0
src/modules/plot/Plot.js Voir le fichier

@@ -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
}

+ 201
- 0
src/modules/plot/draw/DrawBezierCurve.js Voir le fichier

@@ -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

+ 415
- 0
src/modules/plot/edit/EditBezierCurve.js Voir le fichier

@@ -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

Chargement…
Annuler
Enregistrer