// * 创建视锥
|
import * as Cesium from 'cesium'
|
|
function computeFarCenter (position, hpr, far) {
|
const quaternion = Cesium.Transforms.headingPitchRollQuaternion(position, hpr)
|
const rotMat = Cesium.Matrix3.fromQuaternion(quaternion)
|
const dirLocal = new Cesium.Cartesian3(0, 0, far)
|
const dirWorld = Cesium.Matrix3.multiplyByVector(rotMat, dirLocal, new Cesium.Cartesian3())
|
return Cesium.Cartesian3.add(position, dirWorld, new Cesium.Cartesian3())
|
}
|
|
/* eslint-disable new-cap */
|
/* eslint-disable no-undef */
|
export default class CreateFrustum {
|
constructor(viewer, options) {
|
this._isShowVideoPlan = options.isShowVideoPlan || false
|
this.position = options.position
|
this._viewer = viewer
|
|
this.fov = options.fov || 0
|
this.near = options.near || 0.01
|
this.far = options.far || 0
|
|
this.heading = options.heading || 0
|
this.pitch = options.pitch || 0
|
this.roll = options.roll || 0
|
|
this.width = options.width
|
this.height = options.height
|
|
this.videoScreen = null
|
|
|
this.cameraPosition = Cesium.Cartesian3.fromDegrees(
|
this.position.longitude,
|
this.position.latitude,
|
this.position.altitude
|
)
|
this.hpr = Cesium.HeadingPitchRoll.fromDegrees(this.heading, this.pitch, this.roll)
|
this.orientation = Cesium.Transforms.headingPitchRollQuaternion(this.cameraPosition, this.hpr)
|
|
this._entityVideoDataSource = null
|
this._entityVideoDataSource = new Cesium.CustomDataSource('entityVideoDataSource')
|
this._viewer.dataSources.add(this._entityVideoDataSource)
|
|
this._primitiveCollection = null
|
this._primitiveCollection = new Cesium.PrimitiveCollection()
|
this._viewer.scene.primitives.add(this._primitiveCollection)
|
|
this.add()
|
}
|
|
// 创建视锥体和轮廓线
|
add () {
|
this.clear()
|
|
this._viewer.scene.globe.depthTestAgainstTerrain = true
|
|
this.addFrustum()
|
this.addOutline()
|
this.addCenterline()
|
}
|
|
// 创建视锥体
|
addFrustum () {
|
const frustum = new Cesium.PerspectiveFrustum({
|
// 查看的视场角,绕Z轴旋转,以弧度方式输入
|
fov: Cesium.Math.toRadians(this.fov),
|
// 视锥体的宽度/高度
|
aspectRatio: this.width / this.height,
|
// 近面距视点的距离
|
near: this.near,
|
// 远面距视点的距离
|
far: this.far,
|
})
|
|
const frustumGeometry = new Cesium.FrustumGeometry({
|
frustum: frustum,
|
origin: this.cameraPosition,
|
orientation: this.orientation,
|
vertexFormat: Cesium.VertexFormat.POSITION_ONLY,
|
})
|
|
const instance = new Cesium.GeometryInstance({
|
geometry: frustumGeometry,
|
attributes: {
|
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
|
Cesium.Color.fromBytes(0, 213, 144, 20)
|
),
|
},
|
})
|
|
const primitive = new Cesium.Primitive({
|
geometryInstances: instance,
|
releaseGeometryInstances: false,
|
appearance: new Cesium.PerInstanceColorAppearance({
|
closed: true,
|
flat: true,
|
translucent: true
|
}),
|
asynchronous: false,
|
})
|
|
this._primitiveCollection.add(primitive)
|
|
// if (this.roll > 135) {
|
// this.initVideoPolygon(frustumGeometry)
|
// }
|
}
|
|
// 创建轮廓线
|
addOutline () {
|
const frustum = new Cesium.PerspectiveFrustum({
|
// 查看的视场角度,绕Z轴旋转,以弧度方式输入
|
// The angle of the field of view (FOV), in radians.
|
// This angle will be used as the horizontal FOV if the width is greater than the height, otherwise it will be the vertical FOV.
|
fov: Cesium.Math.toRadians(this.fov),
|
// 视锥体的宽度/高度
|
aspectRatio: this.width / this.height,
|
// 近面距视点的距离
|
near: this.near,
|
// 远面距视点的距离
|
far: this.far,
|
})
|
|
const frustumGeometry = new Cesium.FrustumOutlineGeometry({
|
frustum: frustum,
|
origin: this.cameraPosition,
|
orientation: this.orientation,
|
vertexFormat: Cesium.VertexFormat.POSITION_ONLY,
|
})
|
|
const instance = new Cesium.GeometryInstance({
|
geometry: frustumGeometry,
|
attributes: {
|
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
|
Cesium.Color.fromBytes(0, 213, 144, 255)
|
),
|
},
|
})
|
|
const primitive = new Cesium.Primitive({
|
geometryInstances: instance,
|
appearance: new Cesium.PerInstanceColorAppearance({
|
closed: true,
|
flat: true,
|
translucent: true
|
}),
|
asynchronous: false,
|
})
|
|
this._primitiveCollection.add(primitive)
|
}
|
|
addCenterline () {
|
let matrix = Cesium.Matrix3.fromQuaternion(
|
this.orientation,
|
new Cesium.Matrix3()
|
)
|
|
let right1 = Cesium.Matrix3.getColumn(matrix, 0, new Cesium.Cartesian3())
|
let up1 = Cesium.Matrix3.getColumn(matrix, 1, new Cesium.Cartesian3())
|
let direction = Cesium.Matrix3.getColumn(matrix, 2, new Cesium.Cartesian3())
|
|
let frontDirect = Cesium.Cartesian3.multiplyByScalar(
|
direction,
|
this.far,
|
new Cesium.Cartesian3()
|
)
|
let bottomCenter = Cesium.Cartesian3.add(this.cameraPosition, frontDirect, new Cesium.Cartesian3())
|
|
// 创建Entity来显示中心线
|
this._entityVideoDataSource?.entities.add({
|
name: 'Center Line of Frustum',
|
polyline: {
|
positions: [this.cameraPosition, bottomCenter],
|
width: 1,
|
// material: Cesium.Color.RED.withAlpha(0.5)
|
material: new Cesium.PolylineDashMaterialProperty({
|
color: Cesium.Color.fromBytes(0, 213, 144, 255),
|
dashLength: 30.0,
|
gapColor: Cesium.Color.TRANSPARENT,
|
gapWidth: 1.0,
|
}),
|
},
|
})
|
|
const ray = new Cesium.Ray()
|
ray.origin = this.cameraPosition
|
// 计算方向向量(终点 - 起点)并归一化
|
ray.direction = Cesium.Cartesian3.normalize(
|
Cesium.Cartesian3.subtract(bottomCenter, this.cameraPosition, new Cesium.Cartesian3()),
|
new Cesium.Cartesian3()
|
)
|
|
const pickPosition = this._viewer.scene.globe.pick(ray, this._viewer.scene)
|
let distance = null
|
|
if (pickPosition) {
|
// 计算交点与A、B的距离
|
distance = Cesium.Cartesian3.distance(this.cameraPosition, pickPosition)
|
}
|
|
// 视椎体投放视频
|
// if (this.roll <= 135) {
|
|
// this._isShowVideoPlan && this.initVideoPlan(distance)
|
|
// }
|
}
|
|
initVideoPlan (distance = null) {
|
const element = document.querySelector('.video-player')
|
|
if (element === '') return
|
|
|
let curFar = distance != null && distance < this.far ? distance - 10 : this.far
|
|
let width = curFar * Math.tan(Cesium.Math.toRadians(this.fov) * 0.5) * 2
|
let height = width / (this.width / this.height)
|
|
this._entityVideoDataSource.entities.add({
|
position: new Cesium.CallbackProperty(() => {
|
return computeFarCenter(this.cameraPosition, this.hpr, curFar)
|
}, false),
|
orientation: new Cesium.CallbackProperty(() => {
|
return this.orientation
|
}, false),
|
plane: {
|
plane: new Cesium.Plane(new Cesium.Cartesian3(0, 0, -1), 0),
|
dimensions: new Cesium.Cartesian2(width, height),
|
material: element,
|
},
|
})
|
}
|
|
initVideoPolygon (frustumGeometry) {
|
const element = document.querySelector('.video-player')
|
|
if (element === '') return
|
|
// 取出视锥体的顶点坐标
|
const geometry = Cesium.FrustumOutlineGeometry.createGeometry(frustumGeometry)
|
|
const result = []
|
for (let i = 0; i < geometry.attributes.position.values.length; i += 3) {
|
result.push(Array.from(geometry.attributes.position.values).slice(i, i + 3))
|
}
|
|
// 将顶点分为近面(前4个)和远面(后4个)
|
const nearVertices = result.slice(0, 4)
|
const farVertices = result.slice(4, 8)
|
|
let videoPolygon = []
|
let videoIntersectionPolygon = []
|
|
// 处理四条棱线(近面顶点到远面对应顶点)
|
for (let i = 0; i < 4; i++) {
|
const nearVertex = new Cesium.Cartesian3(...nearVertices[i])
|
const farVertex = new Cesium.Cartesian3(...farVertices[i])
|
|
const ray = new Cesium.Ray()
|
ray.origin = nearVertex
|
// 计算方向向量(终点 - 起点)并归一化
|
ray.direction = Cesium.Cartesian3.normalize(
|
Cesium.Cartesian3.subtract(farVertex, nearVertex, new Cesium.Cartesian3()),
|
new Cesium.Cartesian3()
|
)
|
|
// 检测射线与地球表面交点
|
const pickPosition = this._viewer.scene.globe.pick(ray, this._viewer.scene)
|
|
// 有交点则使用交点,否则使用远面顶点
|
if (pickPosition) {
|
videoIntersectionPolygon.push(pickPosition)
|
}
|
|
videoPolygon.push(farVertex)
|
}
|
|
// 获得视锥体和地面的交点,构成平面,并使用video标签做为材质
|
if (videoPolygon.length === 4) {
|
if (videoIntersectionPolygon.length < 4) {
|
this._entityVideoDataSource.entities.add({
|
polygon: {
|
hierarchy: new Cesium.PolygonHierarchy(videoPolygon),
|
material: element,
|
stRotation: +Cesium.Math.toRadians(this.heading),
|
perPositionHeight: true,
|
heightReference: Cesium.HeightReference.NONE
|
},
|
|
orientation: new Cesium.CallbackProperty(() => {
|
return this.orientation
|
}, false),
|
})
|
} else {
|
this._entityVideoDataSource.entities.add({
|
polygon: {
|
hierarchy: new Cesium.PolygonHierarchy(videoIntersectionPolygon),
|
material: element,
|
stRotation: Cesium.Math.toRadians(135),
|
perPositionHeight: false,
|
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND
|
},
|
|
// orientation: new Cesium.CallbackProperty(() => {
|
// return this.orientation
|
// }, false),
|
})
|
}
|
}
|
|
}
|
|
// 清除视锥体和轮廓线
|
clear () {
|
if (this._viewer) {
|
this._viewer.scene.globe.depthTestAgainstTerrain = false
|
|
if (this._primitiveCollection) {
|
this._primitiveCollection.removeAll()
|
}
|
|
if (this._entityVideoDataSource) {
|
this._entityVideoDataSource?.entities.removeAll()
|
}
|
}
|
}
|
|
destroyed () {
|
if (this._viewer) {
|
this._viewer.scene.globe.depthTestAgainstTerrain = false
|
|
if (this._primitiveCollection) {
|
this._primitiveCollection.removeAll()
|
this._viewer.scene.primitives.remove(this._primitiveCollection)
|
this._primitiveCollection = null
|
}
|
|
if (this._entityVideoDataSource) {
|
this._entityVideoDataSource?.entities.removeAll()
|
this._viewer.dataSources.remove(this._entityVideoDataSource, true)
|
this._entityVideoDataSource = null
|
}
|
}
|
}
|
|
UpdateWidth (value) {
|
this.width = value
|
this.add()
|
}
|
UpdateHeight (value) {
|
this.height = value
|
this.add()
|
}
|
UpdateFov (value) {
|
this.fov = value
|
this.add()
|
}
|
UpdateNear (value) {
|
this.near = value
|
this.add()
|
}
|
UpdateFar (value) {
|
this.far = value
|
this.add()
|
}
|
UpdateHeading (value) {
|
this.heading = value
|
this.add()
|
}
|
UpdatePitch (value) {
|
this.pitch = value
|
this.add()
|
}
|
UpdateRoll (value) {
|
this.roll = value
|
this.add()
|
}
|
UpdataPosition (position) {
|
if (!position) return
|
this.position = position
|
this.add()
|
}
|
|
// 更新视锥体的姿态
|
Update (position, headingPitchRoll) {
|
this.position = Cesium.Cartesian3.fromDegrees(
|
position.longitude,
|
position.latitude,
|
position.altitude
|
)
|
this.orientation = Cesium.Transforms.headingPitchRollQuaternion(
|
this.position,
|
new Cesium.HeadingPitchRoll.fromDegrees(
|
headingPitchRoll.heading,
|
headingPitchRoll.pitch,
|
headingPitchRoll.roll
|
)
|
)
|
this.add()
|
}
|
}
|