|
@@ -1,5 +1,7 @@
|
|
|
import * as THREE from 'three';
|
|
|
import {GLTFLoader} from "three/addons/loaders/GLTFLoader.js";
|
|
|
+import List from '../../config/routeList.json'
|
|
|
+import texture1 from "../../assets/lineTexture.png";
|
|
|
export const FlyGLTFClass = {
|
|
|
constructor(options){
|
|
|
this.webgl = options.webgl;
|
|
@@ -111,7 +113,7 @@ export const FlyGLTFClass = {
|
|
|
{ text: '无人机信息: ', color: '#fff', font: '15px Arial', offsetX:40, offsetY: 25 },
|
|
|
{ text: '无人机名称:'+this.panelInfo.data.name, color: '#fff', font: '15px Arial', offsetX:40, offsetY: 50 },
|
|
|
{ text: '所属企业: 美团: ', color: '#fff', font: '15px Arial', offsetX:40, offsetY: 70 },
|
|
|
- { text: '速度: '+this.panelInfo.data.speed * 3.6+'km/h',color: '#fff', font: '15px Arial', offsetX:40, offsetY: 90 },
|
|
|
+ { text: '速度: '+Math.floor(this.panelInfo.data.speed * 3.6)+'km/h',color: '#fff', font: '15px Arial', offsetX:40, offsetY: 90 },
|
|
|
{ text: '高度: '+ this.panelInfo.data.altitude+'m', color: '#fff', font: '15px Arial', offsetX:40, offsetY: 110 },
|
|
|
{ text: '状态: 正常',color: '#fff',font: '15px Arial', offsetX:40, offsetY: 130 }
|
|
|
];
|
|
@@ -132,7 +134,7 @@ export const FlyGLTFClass = {
|
|
|
panelGeometry.scale(-1,1,1);
|
|
|
const panelMesh = new THREE.Mesh(panelGeometry, panelMaterial);
|
|
|
// 将面板附加到无人机模型
|
|
|
- panelMesh.position.set(0, 2, 0); // 调整面板位置到无人机上方
|
|
|
+ panelMesh.position.set(0, 4, 0); // 调整面板位置到无人机上方
|
|
|
gltf.scene.add(panelMesh);
|
|
|
|
|
|
// 保存信息面板,以便更新
|
|
@@ -190,160 +192,83 @@ export const FlyGLTFClass = {
|
|
|
let typeInfo = "";
|
|
|
switch (this.panelInfo.type){
|
|
|
case "conflict":
|
|
|
- backgroundImage.src = 'public/imgs/warningBackground_conflict.png'; // 替换为你的图片路径
|
|
|
+ backgroundImage.src = 'public/imgs/基本信息框-红.png'; // 替换为你的图片路径
|
|
|
topImage.src = 'public/imgs/warningBackground_time.png'; // 替换为你的图片路径
|
|
|
bottomImage.src = 'public/imgs/warningBackground_conflict.png'; // 替换为你的图片路径
|
|
|
- typeInfo = "碰撞检测"
|
|
|
+ typeInfo = "异常"
|
|
|
break;
|
|
|
case "electricityWarning":
|
|
|
- backgroundImage.src = 'public/imgs/warningBackground_lowbattery.png'; // 替换为你的图片路径
|
|
|
+ backgroundImage.src = 'public/imgs/基本信息框-红.png'; // 替换为你的图片路径
|
|
|
typeInfo = "电池电量低"
|
|
|
break;
|
|
|
case "routeDeviate":
|
|
|
- backgroundImage.src = 'public/imgs/warningBackground_outofrange.png'; // 替换为你的图片路径
|
|
|
- typeInfo = "偏移航线"
|
|
|
+ backgroundImage.src = 'public/imgs/基本信息框-红.png'; // 替换为你的图片路径
|
|
|
+ typeInfo = "异常"
|
|
|
break;
|
|
|
case "noFlyZone":
|
|
|
- backgroundImage.src = 'public/imgs/warningBackground_noFlyZone.png'; // 替换为你的图片路径
|
|
|
- typeInfo = "禁飞区"
|
|
|
+ backgroundImage.src = 'public/imgs/基本信息框-红.png'; // 替换为你的图片路径
|
|
|
+ typeInfo = "异常"
|
|
|
break;
|
|
|
case "collision":
|
|
|
- backgroundImage.src = 'public/imgs/warningBackground_noFlyZone.png'; // 替换为你的图片路径
|
|
|
+ backgroundImage.src = 'public/imgs/基本信息框-红.png'; // 替换为你的图片路径
|
|
|
typeInfo = "建筑物过近"
|
|
|
break;
|
|
|
case "clearZone":
|
|
|
- backgroundImage.src = 'public/imgs/warningBackground_noFlyZone.png'; // 替换为你的图片路径
|
|
|
+ backgroundImage.src = 'public/imgs/基本信息框-红.png'; // 替换为你的图片路径
|
|
|
typeInfo = "净空区"
|
|
|
break;
|
|
|
default:
|
|
|
- backgroundImage.src = 'public/imgs/serviceBackground.png'; // 替换为你的图片路径
|
|
|
+ backgroundImage.src = 'public/imgs/基本信息框.png'; // 替换为你的图片路径
|
|
|
typeInfo = "正常"
|
|
|
break;
|
|
|
}
|
|
|
// backgroundImage.src = 'public/imgs/serviceBackground.png'; // 替换为你的图片路径
|
|
|
- if(this.panelInfo.type == "conflict"){
|
|
|
- topImage.onload = () => {
|
|
|
- bottomImage.onload = () => {
|
|
|
- // 确保背景透明
|
|
|
- context.clearRect(0, 0, canvas.width, canvas.height); // 清除所有内容,包含黑色
|
|
|
- context.fillStyle = 'rgba(0, 0, 0, 0)'; // 设置透明背景
|
|
|
- context.fillRect(0, 0, canvas.width, canvas.height); // 填充透明背景
|
|
|
-
|
|
|
- // 绘制顶部图片
|
|
|
- const topImageHeight = canvas.height * 0.2; // 设定顶部图片高度为 canvas 高度的 20%
|
|
|
- context.drawImage(topImage, 0, 0, canvas.width, topImageHeight);
|
|
|
-
|
|
|
- // 绘制底部图片
|
|
|
- const bottomImageHeight = canvas.height * 0.7; // 设定底部图片高度为 canvas 高度的 20%
|
|
|
- context.drawImage(bottomImage, 0, canvas.height - bottomImageHeight, canvas.width, bottomImageHeight);
|
|
|
-
|
|
|
- const textInfo = [
|
|
|
- { text: '暂停飞行原地等待:' + this.panelInfo.data.waitTime +'秒', color: '#fff', font: 'bold 16px Arial', offsetX:50, offsetY: 20 },
|
|
|
- { text: '距预测撞点前方: ', color: '#fff', font: '20px Verdana', offsetX:70, offsetY: 90 },
|
|
|
- { text: Math.floor(this.panelInfo.data.danger_path_distance * 1) + '米', color: '#ff0000', font: 'bold 22px Courier New', offsetX:120, offsetY: 120 }
|
|
|
- ];
|
|
|
- // 绘制文字
|
|
|
- textInfo.forEach((line) => {
|
|
|
- context.fillStyle = line.color;
|
|
|
- context.font = line.font;
|
|
|
- context.fillText(line.text, line.offsetX, line.offsetY);
|
|
|
- });
|
|
|
- // 标记纹理需要更新
|
|
|
- texture.needsUpdate = true;
|
|
|
+ if(this.panelInfo.type == "conflict" && this.panelInfo.action == "wait"){
|
|
|
+ topImage.onload = () => {
|
|
|
+ bottomImage.onload = () => {
|
|
|
+ // 确保背景透明
|
|
|
+ context.clearRect(0, 0, canvas.width, canvas.height); // 清除所有内容,包含黑色
|
|
|
+ context.fillStyle = 'rgba(0, 0, 0, 0)'; // 设置透明背景
|
|
|
+ context.fillRect(0, 0, canvas.width, canvas.height); // 填充透明背景
|
|
|
+
|
|
|
+ // 绘制顶部图片
|
|
|
+ const topImageHeight = canvas.height * 0.2; // 设定顶部图片高度为 canvas 高度的 20%
|
|
|
+ context.drawImage(topImage, 0, 0, canvas.width, topImageHeight);
|
|
|
+
|
|
|
+ // 绘制底部图片
|
|
|
+ const bottomImageHeight = canvas.height * 0.7; // 设定底部图片高度为 canvas 高度的 20%
|
|
|
+ context.drawImage(bottomImage, 0, canvas.height - bottomImageHeight, canvas.width, bottomImageHeight);
|
|
|
+
|
|
|
+ const textInfo = [
|
|
|
+ { text: '暂停飞行原地等待:' + this.panelInfo.data.waitTime +'秒', color: '#fff', font: 'bold 16px Arial', offsetX:50, offsetY: 20 },
|
|
|
+ { text: '距预测撞点前方: ', color: '#fff', font: '20px Verdana', offsetX:70, offsetY: 90 },
|
|
|
+ { text: Math.floor(this.panelInfo.data.danger_path_distance * 1) + '米', color: '#ff0000', font: 'bold 22px Courier New', offsetX:120, offsetY: 120 }
|
|
|
+ ];
|
|
|
+ // 绘制文字
|
|
|
+ textInfo.forEach((line) => {
|
|
|
+ context.fillStyle = line.color;
|
|
|
+ context.font = line.font;
|
|
|
+ context.fillText(line.text, line.offsetX, line.offsetY);
|
|
|
+ });
|
|
|
+ // 标记纹理需要更新
|
|
|
+ texture.needsUpdate = true;
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
- }else if(this.panelInfo.type == "electricityWarning"){
|
|
|
- backgroundImage.onload = () => {
|
|
|
- // 确保背景透明
|
|
|
- context.clearRect(0, 0, canvas.width, canvas.height); // 清除所有内容,包含黑色
|
|
|
- context.fillStyle = 'rgba(0, 0, 0, 0)'; // 设置透明背景
|
|
|
- context.fillRect(0, 0, canvas.width, canvas.height); // 填充透明背景
|
|
|
- // 绘制背景图片,填满整个 canvas
|
|
|
- context.drawImage(backgroundImage, 0, 0, canvas.width, canvas.height);
|
|
|
- const textInfo = [
|
|
|
- { text: '电量预警: ', color: '#fff', font: '15px Arial', offsetX:40, offsetY: 25 },
|
|
|
- { text: '低电量警告: ', color: '#fff', font: '15px Arial', offsetX:40, offsetY: 70 },
|
|
|
- { text: this.panelInfo.data.speed, color: '#fff', font: '15px Arial', offsetX:40, offsetY: 90 },
|
|
|
- { text: this.panelInfo.data.altitude, color: '#fff', font: '15px Arial', offsetX:40, offsetY: 110 },
|
|
|
- { text: this.panelInfo.data.altitude, color: '#fff', font: '15px Arial', offsetX:80, offsetY: 110 }
|
|
|
- ];
|
|
|
- // 绘制文字
|
|
|
- textInfo.forEach((line) => {
|
|
|
- context.fillStyle = line.color;
|
|
|
- context.font = line.font;
|
|
|
- context.fillText(line.text, line.offsetX, line.offsetY);
|
|
|
- });
|
|
|
- // 标记纹理需要更新
|
|
|
- texture.needsUpdate = true;
|
|
|
- };
|
|
|
- }else if(this.panelInfo.type == "routeDeviate"){
|
|
|
- backgroundImage.onload = () => {
|
|
|
- // 确保背景透明
|
|
|
- context.clearRect(0, 0, canvas.width, canvas.height); // 清除所有内容,包含黑色
|
|
|
- context.fillStyle = 'rgba(0, 0, 0, 0)'; // 设置透明背景
|
|
|
- context.fillRect(0, 0, canvas.width, canvas.height); // 填充透明背景
|
|
|
- // 绘制背景图片,填满整个 canvas
|
|
|
- context.drawImage(backgroundImage, 0, 0, canvas.width, canvas.height);
|
|
|
- const textInfo = [
|
|
|
- { text: '计划偏离: ', color: '#fff', font: '15px Arial', offsetX:40, offsetY: 25 },
|
|
|
- { text: '偏离距离: '+this.panelInfo.data.speed * 3.6+'米', color: '#fff', font: '15px Arial', offsetX:40, offsetY: 90 },
|
|
|
- { text: '飞行航线偏离: '+ this.panelInfo.data.altitude, color: '#fff', font: '15px Arial', offsetX:40, offsetY: 110 },
|
|
|
- { text: '无人机正在偏离计划航线飞行,预警提示请关注', color: '#fff', font: '15px Arial', offsetX:40, offsetY: 130 }
|
|
|
- ];
|
|
|
- // 绘制文字
|
|
|
- textInfo.forEach((line) => {
|
|
|
- context.fillStyle = line.color;
|
|
|
- context.font = line.font;
|
|
|
- context.fillText(line.text, line.offsetX, line.offsetY);
|
|
|
- });
|
|
|
- // 标记纹理需要更新
|
|
|
- texture.needsUpdate = true;
|
|
|
- };
|
|
|
- }else if(this.panelInfo.type == "noFlyZone"){
|
|
|
- backgroundImage.onload = () => {
|
|
|
- // 确保背景透明
|
|
|
- context.clearRect(0, 0, canvas.width, canvas.height); // 清除所有内容,包含黑色
|
|
|
- context.fillStyle = 'rgba(0, 0, 0, 0)'; // 设置透明背景
|
|
|
- context.fillRect(0, 0, canvas.width, canvas.height); // 填充透明背景
|
|
|
+ }else if(this.panelInfo.type == "routeDeviate"){
|
|
|
+ //获取当前下标index
|
|
|
+ let index = this.panelInfo.index * 1 +Math.floor(this.panelInfo.data.distance * 1 / 10) * 5 + 1;
|
|
|
+ let targetPoint = [];
|
|
|
+ this.webgl.toRenderCoordinates(
|
|
|
+ this.view,
|
|
|
+ [List.originList[index].mapX, List.originList[index].mapY, List.originList[index].mapZ],
|
|
|
+ 0,
|
|
|
+ this.view.spatialReference,
|
|
|
+ targetPoint,
|
|
|
+ 0,
|
|
|
+ 1
|
|
|
+ );
|
|
|
|
|
|
- // 绘制背景图片,填满整个 canvas
|
|
|
- context.drawImage(backgroundImage, 0, 0, canvas.width, canvas.height);
|
|
|
- const textInfo = [
|
|
|
- { text: '空域闯入-禁飞区', color: '#fff', font: '15px Arial', offsetX:40, offsetY: 25 },
|
|
|
- { text: '已闯入禁飞区,请及时校正航线', color: '#fff', font: '15px Arial', offsetX:40, offsetY: 90 }
|
|
|
- ];
|
|
|
- // 绘制文字
|
|
|
- textInfo.forEach((line) => {
|
|
|
- context.fillStyle = line.color;
|
|
|
- context.font = line.font;
|
|
|
- context.fillText(line.text, line.offsetX, line.offsetY);
|
|
|
- });
|
|
|
- // 标记纹理需要更新
|
|
|
- texture.needsUpdate = true;
|
|
|
- };
|
|
|
- }else if(this.panelInfo.type == "collision"){ //需要米数,已返回
|
|
|
- backgroundImage.onload = () => {
|
|
|
- // 确保背景透明
|
|
|
- context.clearRect(0, 0, canvas.width, canvas.height); // 清除所有内容,包含黑色
|
|
|
- context.fillStyle = 'rgba(0, 0, 0, 0)'; // 设置透明背景
|
|
|
- context.fillRect(0, 0, canvas.width, canvas.height); // 填充透明背景
|
|
|
- // 绘制背景图片,填满整个 canvas
|
|
|
- context.drawImage(backgroundImage, 0, 0, canvas.width, canvas.height);
|
|
|
- const textInfo = [
|
|
|
- { text: '建筑避障: ', color: '#fff', font: '15px Arial', offsetX:40, offsetY: 25 },
|
|
|
- { text: '当前飞行轨迹距离前方建筑物实时距离: '+this.panelInfo.data.distance+'米', color: '#fff', font: '15px Arial', offsetX:40, offsetY: 90 },
|
|
|
- { text: '请注意飞行安全', color: '#fff', font: '15px Arial', offsetX:40, offsetY: 110 },
|
|
|
- ];
|
|
|
- // 绘制文字
|
|
|
- textInfo.forEach((line) => {
|
|
|
- context.fillStyle = line.color;
|
|
|
- context.font = line.font;
|
|
|
- context.fillText(line.text, line.offsetX, line.offsetY);
|
|
|
- });
|
|
|
- // 标记纹理需要更新
|
|
|
- texture.needsUpdate = true;
|
|
|
- };
|
|
|
- }else if(this.panelInfo.type == "clearZone"){
|
|
|
+ this.targetPosition = new THREE.Vector3(targetPoint[0], targetPoint[1], targetPoint[2]);
|
|
|
backgroundImage.onload = () => {
|
|
|
// 确保背景透明
|
|
|
context.clearRect(0, 0, canvas.width, canvas.height); // 清除所有内容,包含黑色
|
|
@@ -352,9 +277,10 @@ export const FlyGLTFClass = {
|
|
|
// 绘制背景图片,填满整个 canvas
|
|
|
context.drawImage(backgroundImage, 0, 0, canvas.width, canvas.height);
|
|
|
const textInfo = [
|
|
|
- { text: '空域闯入-净空区', color: '#fff', font: '15px Arial', offsetX:40, offsetY: 25 },
|
|
|
- { text: '当前飞行轨迹距离前方净空区实时距离: '+this.panelInfo.data.distance+'米', color: '#fff', font: '15px Arial', offsetX:40, offsetY: 90 },
|
|
|
- { text: '请注意飞行安全', color: '#fff', font: '15px Arial', offsetX:40, offsetY: 110 },
|
|
|
+ { text: '计划偏离: ', color: '#fff', font: '15px Arial', offsetX:40, offsetY: 25 },
|
|
|
+ { text: '偏离距离: '+Math.floor(this.panelInfo.data.distance* 1) +'米', color: '#ff0000', font: 'bold 22px Courier New', offsetX:70, offsetY: 60 },
|
|
|
+ { text: '无人机正在偏离计划航线飞行', color: '#fff', font: '18px Arial', offsetX:40, offsetY: 90 },
|
|
|
+ { text: '请及时调整飞行路线', color: '#fff', font: '18px Arial', offsetX:40, offsetY: 120 }
|
|
|
];
|
|
|
// 绘制文字
|
|
|
textInfo.forEach((line) => {
|
|
@@ -371,17 +297,13 @@ export const FlyGLTFClass = {
|
|
|
context.clearRect(0, 0, canvas.width, canvas.height); // 清除所有内容,包含黑色
|
|
|
context.fillStyle = 'rgba(0, 0, 0, 0)'; // 设置透明背景
|
|
|
context.fillRect(0, 0, canvas.width, canvas.height); // 填充透明背景
|
|
|
-
|
|
|
// 绘制背景图片,填满整个 canvas
|
|
|
context.drawImage(backgroundImage, 0, 0, canvas.width, canvas.height);
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
const textInfo = [
|
|
|
{ text: '无人机信息: ', color: '#fff', font: '15px Arial', offsetX:40, offsetY: 25 },
|
|
|
{ text: '无人机名称:'+this.panelInfo.data.name, color: '#fff', font: '15px Arial', offsetX:40, offsetY: 50 },
|
|
|
{ text: '所属企业: 美团: ', color: '#fff', font: '15px Arial', offsetX:40, offsetY: 70 },
|
|
|
- { text: '速度: '+this.panelInfo.data.speed * 3.6+'km/h', color: '#fff', font: '15px Arial', offsetX:40, offsetY: 90 },
|
|
|
+ { text: '速度: '+Math.floor(this.panelInfo.data.speed * 3.6)+'km/h', color: '#fff', font: '15px Arial', offsetX:40, offsetY: 90 },
|
|
|
{ text: '高度: '+ this.panelInfo.data.altitude+'m', color: '#fff', font: '15px Arial', offsetX:40, offsetY: 110 },
|
|
|
{ text: `状态: ${typeInfo}`, color: '#fff', font: '15px Arial', offsetX:40, offsetY: 130 }
|
|
|
];
|
|
@@ -401,6 +323,21 @@ export const FlyGLTFClass = {
|
|
|
},
|
|
|
|
|
|
|
|
|
+ getClosestPointOnCurve(position, curve) {
|
|
|
+ let closestPoint = null;
|
|
|
+ let minDistance = Infinity;
|
|
|
+ const points = curve.getPoints(100);
|
|
|
+ points.forEach(point => {
|
|
|
+ const distance = position.distanceTo(point);
|
|
|
+ if (distance < minDistance) {
|
|
|
+ minDistance = distance;
|
|
|
+ closestPoint = point;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ return closestPoint;
|
|
|
+ },
|
|
|
+
|
|
|
+
|
|
|
setPathCurve(path,duration){
|
|
|
this.path = path;
|
|
|
this.pathProgress = 0; // 重置路径进度
|
|
@@ -532,6 +469,74 @@ export const FlyGLTFClass = {
|
|
|
//this.targetObject.rotation.x = -Math.PI/2;
|
|
|
this.targetObject.rotation.y = 0;
|
|
|
this.targetObject.rotation.z = -Math.PI/2;
|
|
|
+
|
|
|
+ // if (this.recommandPathLine) {
|
|
|
+ // this.scene.remove(this.recommandPathLine);
|
|
|
+ // }
|
|
|
+ // if(this.panelInfo.type == "routeDeviate"){
|
|
|
+ // this.updateRecommandPath();
|
|
|
+ // }
|
|
|
+
|
|
|
+ },
|
|
|
+
|
|
|
+ updateRecommandPath() {
|
|
|
+ debugger;
|
|
|
+ // 如果已存在曲线或管道,将其从场景中移除
|
|
|
+
|
|
|
+ let startPoint = this.pathCurve.getPointAt(this.pathProgress);
|
|
|
+ let endPoint = this.targetPosition;
|
|
|
+
|
|
|
+ // 创建两个点之间的中间控制点
|
|
|
+ const midPoint1 = new THREE.Vector3(
|
|
|
+ (2 * startPoint.x + endPoint.x) / 3,
|
|
|
+ (2 * startPoint.y + endPoint.y) / 3 + 5, // 可调整中间点的高度
|
|
|
+ (2 * startPoint.z + endPoint.z) / 3
|
|
|
+ );
|
|
|
+ const midPoint2 = new THREE.Vector3(
|
|
|
+ (startPoint.x + 2 * endPoint.x) / 3,
|
|
|
+ (startPoint.y + 2 * endPoint.y) / 3 + 5, // 可调整中间点的高度
|
|
|
+ (startPoint.z + 2 * endPoint.z) / 3
|
|
|
+ );
|
|
|
+
|
|
|
+ // 使用 CatmullRomCurve3 创建一条平滑曲线
|
|
|
+ const curve = new THREE.CatmullRomCurve3([
|
|
|
+ startPoint,
|
|
|
+ midPoint1,
|
|
|
+ midPoint2,
|
|
|
+ endPoint
|
|
|
+ ]);
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ let tubeGeometry = new THREE.TubeGeometry(curve, 20, 100, 24, false);
|
|
|
+ var textureLoader = new THREE.TextureLoader();
|
|
|
+ //设置纹理贴图
|
|
|
+ this.map = textureLoader.load(texture1);
|
|
|
+
|
|
|
+ this.map.wrapS = THREE.RepeatWrapping;
|
|
|
+ this.map.wrapT = THREE.RepeatWrapping;
|
|
|
+ this.map.repeat.set(20, 10);
|
|
|
+
|
|
|
+ const material = new THREE.MeshBasicMaterial({
|
|
|
+ color: 0xaaaaaa,
|
|
|
+ // map: this.map,
|
|
|
+ transparent: true,
|
|
|
+ depthWrite: false,
|
|
|
+ // side: 200
|
|
|
+ side: THREE.DoubleSide,
|
|
|
+ wireframeLinewidth: 20,
|
|
|
+ });
|
|
|
+
|
|
|
+
|
|
|
+ const tubeMesh = new THREE.Mesh(tubeGeometry, material);
|
|
|
+
|
|
|
+ // 将新的管道添加到场景
|
|
|
+ this.scene.add(tubeMesh);
|
|
|
+
|
|
|
+ // 保存当前管道对象,以便下次更新时移除
|
|
|
+ this.recommandPathLine = tubeMesh;
|
|
|
+
|
|
|
+ return curve; // 返回生成的曲线对象
|
|
|
},
|
|
|
|
|
|
|