Files
td_official/src/views/equipment/index.js

393 lines
13 KiB
JavaScript
Raw Normal View History

2025-09-06 18:51:05 +08:00
export class ModelPathMover {
/**
* 构造函数
* @param {Cesium.Viewer} viewer - Cesium viewer实例
* @param {Object} options - 配置选项
* @param {Number} [options.baseHeight=0] - 外部传入的统一高度将叠加到所有位置点上
*/
constructor(viewer, options) {
this.viewer = viewer;
this.modelUrl = options.modelUrl;
this.positions = options.positions || [];
this.speed = options.speed || 10; // 米/秒
this.loop = options.loop !== undefined ? options.loop : true;
this.pathStyle = options.pathStyle || {
color: Cesium.Color.YELLOW,
width: 3
};
this.markerStyle = options.markerStyle || {
color: Cesium.Color.RED,
pixelSize: 10,
outlineColor: Cesium.Color.WHITE,
outlineWidth: 2
};
this.iconUrl = options.iconUrl;
this.iconSize = options.iconSize || 32;
this.iconOffset = options.iconOffset || 0;
this.headingOffset = options.headingOffset || 0;
// 新增接收外部传入的统一高度默认0米
this.baseHeight = options.baseHeight || 0;
// 内部状态(保持不变)
this.modelEntity = null;
this.pathEntity = null;
this.markerEntities = [];
this.iconEntity = null;
this.currentIndex = 0;
this.isMoving = false;
this.lastTime = 0;
this.animationFrameId = null;
this.currentPosition = null;
this.progress = 0;
this.modelHeight = 0;
// 初始化
this.init();
// 调试信息增加baseHeight打印
console.log('ModelPathMover初始化完成', {
positionCount: this.positions.length,
speed: this.speed,
loop: this.loop,
baseHeight: this.baseHeight // 打印传入的统一高度
});
}
/**
* 初始化保持不变
*/
init() {
if (this.positions.length < 2) {
console.warn('至少需要2个位置点才能进行移动');
}
this.createPath();
this.createMarkers();
this.createModel();
if (this.iconUrl) {
this.createIcon();
}
}
/**
* 创建模型修改叠加baseHeight
*/
createModel() {
if (this.positions.length === 0) return;
const firstPos = this.positions[0];
// 关键修改在原始高度基础上叠加baseHeight
const finalHeight = (firstPos.height || 0) + this.baseHeight;
this.currentPosition = Cesium.Cartesian3.fromDegrees(
firstPos.lon,
firstPos.lat,
finalHeight // 使用叠加后的高度
);
// 初始朝向计算(保持不变)
let initialOrientation = Cesium.Transforms.headingPitchRollQuaternion(this.currentPosition, new Cesium.HeadingPitchRoll(0, 0, 0));
if (this.positions.length > 1) {
const nextPos = this.positions[1];
// 计算下一个点时同样叠加baseHeight
const nextFinalHeight = (nextPos.height || 0) + this.baseHeight;
const nextCartesian = Cesium.Cartesian3.fromDegrees(nextPos.lon, nextPos.lat, nextFinalHeight);
const heading = this.calculateHeading(this.currentPosition, nextCartesian);
initialOrientation = Cesium.Transforms.headingPitchRollQuaternion(this.currentPosition, new Cesium.HeadingPitchRoll(heading, 0, 0));
}
this.modelEntity = this.viewer.entities.add({
name: 'Moving Model',
position: this.currentPosition,
orientation: initialOrientation,
model: {
uri: this.modelUrl,
minimumPixelSize: 64,
readyPromise: (model) => {
const boundingSphere = model.boundingSphere;
if (boundingSphere) {
this.modelHeight = boundingSphere.radius * 2;
console.log('模型高度检测完成:', this.modelHeight, '米');
}
}
}
});
}
/**
* 创建路径修改叠加baseHeight
*/
createPath() {
if (this.positions.length < 2) return;
// 关键修改遍历所有点时给每个点的高度叠加baseHeight
const pathPositions = this.positions.map((pos) => {
const finalHeight = (pos.height || 0) + this.baseHeight;
return Cesium.Cartesian3.fromDegrees(pos.lon, pos.lat, finalHeight);
});
this.pathEntity = this.viewer.entities.add({
name: 'Model Path',
polyline: {
positions: pathPositions,
width: this.pathStyle.width,
material: this.pathStyle.color,
clampToGround: false
}
});
}
/**
* 创建位置标记修改叠加baseHeight
*/
createMarkers() {
this.markerEntities = this.positions.map((pos, index) => {
// 关键修改标记点高度同样叠加baseHeight
const finalHeight = (pos.height || 0) + this.baseHeight;
const position = Cesium.Cartesian3.fromDegrees(pos.lon, pos.lat, finalHeight);
const marker = this.viewer.entities.add({
name: `Position Marker ${index}`,
position: position,
point: {
color: this.markerStyle.color,
pixelSize: this.markerStyle.pixelSize,
outlineColor: this.markerStyle.outlineColor,
outlineWidth: this.markerStyle.outlineWidth
},
label: {
text: pos.name || `Point ${index + 1}`,
font: '14px sans-serif',
pixelOffset: new Cesium.Cartesian2(0, -20),
fillColor: Cesium.Color.WHITE
}
});
return marker;
});
}
/**
* 创建模型上方的图标保持不变
*/
createIcon() {
this.iconEntity = this.viewer.entities.add({
name: 'Model Icon',
position: new Cesium.CallbackProperty(() => {
return this.adjustCartesianHeight(this.currentPosition, 0.3);
}, false),
billboard: {
image: this.iconUrl,
width: this.iconSize,
height: this.iconSize,
verticalOrigin: Cesium.VerticalOrigin.BOTTOM
},
label: {
text: 'Model Icon',
font: '14px sans-serif',
pixelOffset: new Cesium.Cartesian2(0, -40),
fillColor: Cesium.Color.WHITE
}
});
}
/**
* 调整笛卡尔坐标的高度在原有高度基础上叠加指定值
* @param {Cesium.Cartesian3} cartesian - 原始笛卡尔坐标
* @param {Number} heightOffset - 要增加的高度值可为负数
* @param {Cesium.Ellipsoid} [ellipsoid=Cesium.Ellipsoid.WGS84] - 参考椭球体
* @returns {Cesium.Cartesian3|null} 调整高度后的笛卡尔坐标失败返回null
*/
adjustCartesianHeight(cartesian, heightOffset, ellipsoid = Cesium.Ellipsoid.WGS84) {
// 1. 校验输入的笛卡尔坐标是否有效
// if (!Cesium.Cartesian3.isValid(cartesian)) {
// console.error("无效的笛卡尔坐标,无法调整高度");
// return null;
// }
// 2. 笛卡尔坐标 → Cartographic地理坐标含弧度经纬度和高度
const cartographic = Cesium.Cartographic.fromCartesian(cartesian, ellipsoid);
if (!cartographic) {
console.error('笛卡尔坐标转换为Cartographic失败');
return null;
}
// 3. 调整高度在原有高度上叠加offset可为负数
cartographic.height += heightOffset;
// 可选确保高度不低于0根据需求决定是否保留
// cartographic.height = Math.max(cartographic.height, 0);
// 4. Cartographic → 笛卡尔坐标(使用弧度经纬度转换)
const adjustedCartesian = Cesium.Cartesian3.fromRadians(
cartographic.longitude, // 经度(弧度)
cartographic.latitude, // 纬度(弧度)
cartographic.height, // 调整后的高度(米)
ellipsoid
);
return adjustedCartesian;
}
/**
* 开始移动保持不变
*/
start() {
if (this.isMoving) {
console.log('模型已经在移动中');
return;
}
if (this.positions.length < 2) {
console.error('无法移动位置点数量不足至少需要2个');
return;
}
this.isMoving = true;
this.lastTime = performance.now();
this.progress = 0;
console.log('开始移动,速度:', this.speed, '米/秒');
this.animate();
}
/**
* 动画循环函数保持不变
*/
animate() {
if (!this.isMoving) return;
const currentTime = performance.now();
const timeDeltaMs = currentTime - this.lastTime;
this.lastTime = currentTime;
const timeDelta = timeDeltaMs / 1000;
this.updatePosition(timeDelta);
this.animationFrameId = requestAnimationFrame(() => this.animate());
}
/**
* 停止移动保持不变
*/
stop() {
if (!this.isMoving) return;
this.isMoving = false;
console.log('停止移动');
if (this.animationFrameId) {
cancelAnimationFrame(this.animationFrameId);
this.animationFrameId = null;
}
}
/**
* 更新模型位置修改叠加baseHeight
*/
updatePosition(timeDelta) {
if (!this.isMoving) return;
const distanceToMove = this.speed * timeDelta;
const currentPos = this.positions[this.currentIndex];
const nextIndex = (this.currentIndex + 1) % this.positions.length;
const nextPos = this.positions[nextIndex];
// 关键修改计算当前点和下一点时均叠加baseHeight
const currentFinalHeight = (currentPos.height || 0) + this.baseHeight;
const nextFinalHeight = (nextPos.height || 0) + this.baseHeight;
const currentCartesian = Cesium.Cartesian3.fromDegrees(currentPos.lon, currentPos.lat, currentFinalHeight);
const nextCartesian = Cesium.Cartesian3.fromDegrees(nextPos.lon, nextPos.lat, nextFinalHeight);
// 后续逻辑保持不变
const totalDistance = Cesium.Cartesian3.distance(currentCartesian, nextCartesian);
if (totalDistance < 0.001) {
this.currentIndex = nextIndex;
this.progress = 0;
this.setModelPosition(nextCartesian, this.getNextPositionCartesian(nextIndex));
return;
}
this.progress += distanceToMove / totalDistance;
if (this.progress >= 1.0) {
const remainingDistance = (this.progress - 1.0) * totalDistance;
this.currentIndex = nextIndex;
if (!this.loop && this.currentIndex === this.positions.length - 1) {
this.setModelPosition(nextCartesian, null);
this.stop();
console.log('已到达终点,停止移动');
return;
}
const newTotalDistance = Cesium.Cartesian3.distance(nextCartesian, this.getNextPositionCartesian(this.currentIndex));
this.progress = newTotalDistance > 0.001 ? remainingDistance / newTotalDistance : 0;
this.setModelPosition(nextCartesian, this.getNextPositionCartesian(this.currentIndex));
} else {
const newPosition = Cesium.Cartesian3.lerp(currentCartesian, nextCartesian, this.progress, new Cesium.Cartesian3());
this.setModelPosition(newPosition, nextCartesian);
}
this.viewer.scene.requestRender();
}
/**
* 获取下一个位置的笛卡尔坐标修改叠加baseHeight
*/
getNextPositionCartesian(currentIndex) {
const nextIndex = (currentIndex + 1) % this.positions.length;
const nextPos = this.positions[nextIndex];
// 关键修改返回下一点时叠加baseHeight
const finalHeight = (nextPos.height || 0) + this.baseHeight;
return Cesium.Cartesian3.fromDegrees(nextPos.lon, nextPos.lat, finalHeight);
}
/**
* 设置模型位置和方向保持不变
*/
setModelPosition(position, targetPosition) {
if (!this.modelEntity) return;
this.currentPosition = position;
this.modelEntity.position.setValue(position);
if (targetPosition) {
const heading = this.calculateHeading(position, targetPosition);
this.modelEntity.orientation.setValue(Cesium.Transforms.headingPitchRollQuaternion(position, new Cesium.HeadingPitchRoll(heading, 0, 0)));
}
}
/**
* 计算两点之间的朝向保持不变
*/
calculateHeading(from, to) {
const fromCartographic = Cesium.Cartographic.fromCartesian(from);
const toCartographic = Cesium.Cartographic.fromCartesian(to);
const startLat = fromCartographic.latitude;
const startLon = fromCartographic.longitude;
const endLat = toCartographic.latitude;
const endLon = toCartographic.longitude;
const dLon = endLon - startLon;
const y = Math.sin(dLon) * Math.cos(endLat);
const x = Math.cos(startLat) * Math.sin(endLat) - Math.sin(startLat) * Math.cos(endLat) * Math.cos(dLon);
let heading = Math.atan2(y, x);
heading = (heading + Cesium.Math.TWO_PI) % Cesium.Math.TWO_PI;
heading = (heading + this.headingOffset) % Cesium.Math.TWO_PI;
return heading;
}
/**
* 手动设置模型高度保持不变
*/
setModelHeight(height) {
this.modelHeight = height;
console.log('手动设置模型高度:', height, '米');
}
/**
* 设置航向角偏移保持不变
*/
setHeadingOffset(offsetDegrees) {
this.headingOffset = Cesium.Math.toRadians(offsetDegrees);
console.log('设置航向角偏移:', offsetDegrees, '度');
}
/**
* 销毁所有资源保持不变
*/
destroy() {
this.stop();
if (this.modelEntity) this.viewer.entities.remove(this.modelEntity);
if (this.pathEntity) this.viewer.entities.remove(this.pathEntity);
this.markerEntities.forEach((marker) => this.viewer.entities.remove(marker));
if (this.iconEntity) this.viewer.entities.remove(this.iconEntity);
this.modelEntity = null;
this.pathEntity = null;
this.markerEntities = [];
this.iconEntity = null;
}
}