首页 > 其他分享 >Threejs路径规划案例V1

Threejs路径规划案例V1

时间:2024-08-30 21:25:04浏览次数:15  
标签:Threejs 案例 THREE V1 let new box1 tempPoint box3

   最近在做一个路径规划的例子,大概场景是在一个xy坐标系中,有几个障碍物,一个车要绕过这些障碍物到达目的地,本来用java来实现,但是java调试太不直观了,我就想起用threejs把场景简单搭建出来,规划好的路线也直接展示出来,就可以实时查看路径规划的怎么样了。

先来一张效果图:

Threejs路径规划案例V1_Math

首先是添加threejs场景,包括灯光,相机,渲染器,也包括地板,然后得到一个空的场景,为了方便观看,我们把相机调整到地图的正上方,且对准地图的中心点。

 initScene(){
      this.scene = new THREE.Scene();
      const axesHelper = new THREE.AxesHelper( 100 );
      axesHelper.position.set(0,0,10)
      this.scene.add( axesHelper );
    },
    initCamera(){
      this.camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 10000);
      this.camera.position.set(200,150,400);
      this.camera.lookAt(200,150,0);
      // this.camera.up.set(0, 0, 1);   // <=== spin // around Z-axis
    },
    initLight(){
      //添加两个平行光
      const directionalLight1 = new THREE.DirectionalLight(0xffffff, 1.5);
      directionalLight1.position.set(300,300,600)
      this.scene.add(directionalLight1);
      const directionalLight2 = new THREE.DirectionalLight(0xffffff, 1.5);
      directionalLight2.position.set(600,200,600)
      this.scene.add(directionalLight2);
    },
    initFloor(){
      let floorGeometry = new THREE.BoxGeometry( this.floor.floorWidth,this.floor.floorLength,this.floor.depth);
      let floorMaterial = new THREE.MeshPhysicalMaterial({color:'#FFFFFF'});
      let textureFloor = new THREE.TextureLoader().load('/static/images/floor.jpg', function (texture) {
        texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
      })
      floorMaterial.map = textureFloor
      let floor = new THREE.Mesh( floorGeometry, floorMaterial );
      floor.name = '地板';
      floor.position.set(this.floor.floorWidth/2,this.floor.floorLength/2,0)
      this.scene.add(floor)
    },
 initRenderer(){
      this.renderer = new THREE.WebGLRenderer({ antialias: true });
      this.container = document.getElementById("container")
      this.renderer.setSize(this.container.clientWidth, this.container.clientHeight);
      this.renderer.setClearColor('#AAAAAA', 1.0);
      this.container.appendChild(this.renderer.domElement);
    },
    initControl(){
      this.controls = new OrbitControls(this.camera, this.renderer.domElement);
      this.controls.enableDamping = true;
      this.controls.dampingFactor = 0.5;
      // 视角最小距离
      this.controls.minDistance = 100;
      // 视角最远距离
      this.controls.maxDistance = 1000;
      // 最大角度
      this.controls.target = new THREE.Vector3(200, 150, 0);
      this.camera.position.set(200, 150, 400);
      this.camera.lookAt(200, 150, 0);
    },
    initAnimate() {
      requestAnimationFrame(this.initAnimate);
      this.renderer.render(this.scene, this.camera);
    },

Threejs路径规划案例V1_路径规划_02

为了方面后期调试,这里给页面添加上几个输入框,分别输入起始点的xy和目标点的xy,再加一个路径规划的按钮,输入起始点和目的地后点击路径规划,会擦除上一次的路径,再将本次的路径显示在地图上。这里可以引入element框架,简单且漂亮。

<div style="position:absolute;width:360px; right:30px;top:60px;">
      <el-form :inline="true"  class="demo-form-inline">
        <el-form-item label="起始点X" label-width="100px">
          <el-input style="width:80px;" type="number" v-model="beginPoint.x" placeholder="请输入Y"></el-input>
        </el-form-item>
        <el-form-item label="Y">
          <el-input style="width:80px;" type="number"  v-model="beginPoint.y" placeholder="请输入Y"></el-input>
        </el-form-item>

        <el-form-item label="目标点X" label-width="100px">
          <el-input style="width:80px;" type="number"  v-model="endPoint.x" placeholder="请输入Y"></el-input>
        </el-form-item>
        <el-form-item label="Y">
          <el-input style="width:80px;" type="number"  v-model="endPoint.y" placeholder="请输入Y"></el-input>
        </el-form-item>
      </el-form>
      <el-button type="primary" @click="buildPath">绘制路径</el-button>
    </div>

Threejs路径规划案例V1_路径规划_03

然后开始添加障碍物,这里先添加三个障碍物,随机放在地图上,障碍物用红色表示

initBox(){
      const particlesGeometry1 = new THREE.BoxGeometry((this.box1.endX-this.box1.beginX), (this.box1.endY-this.box1.beginY), 10);
      let material = new THREE.MeshPhongMaterial({
        color: '#FF0000', // 设置颜色
        shininess: 20 // 设置高光大小,范围为0到128,默认值为30
      });
      let box1 = new THREE.Mesh( particlesGeometry1, material, 0 );
      box1.position.set((this.box1.endX+this.box1.beginX)/2,(this.box1.endY+this.box1.beginY)/2,5)
      this.scene.add(box1);

      const particlesGeometry2 = new THREE.BoxGeometry((this.box2.endX-this.box2.beginX), (this.box2.endY-this.box2.beginY),10);
      let box2 = new THREE.Mesh( particlesGeometry2, material, 0 );
      box2.position.set((this.box2.endX+this.box2.beginX)/2,(this.box2.endY+this.box2.beginY)/2,5)
      this.scene.add(box2);

      const particlesGeometry3 = new THREE.BoxGeometry((this.box3.endX-this.box3.beginX), (this.box3.endY-this.box3.beginY), 10);
      let box3 = new THREE.Mesh( particlesGeometry3, material, 0 );
      box3.position.set((this.box3.endX+this.box3.beginX)/2,(this.box3.endY+this.box3.beginY)/2,5)
      this.scene.add(box3);
    },

Threejs路径规划案例V1_Threejs_04

然后添加绘制线的方法,将路径上多个点用线连起来,我们再给每个点添加一个小圆球,我这里是要求线不能太长所以会有多个小圆球,但并非每个小圆球都是拐弯点,顺便再添加一个擦除的方法,在绘制的时候把线和点的name设置为相同的,后期擦除的时候就只需要比较同一个name了

drawPoint(point){
      const particlesGeometry = new THREE.SphereGeometry(2, 8, 8);
      let material = new THREE.MeshPhongMaterial({
        color: '#00FF00', // 设置颜色
        shininess: 20 // 设置高光大小,范围为0到128,默认值为30
      });
      let pointMesh = new THREE.Mesh( particlesGeometry, material, 0 );
      pointMesh.position.set(point.x,point.y,point.z)
      pointMesh.name ='once'
      this.scene.add(pointMesh);
    },
 drawPath(pointList){
      let drawPointList = [];
      for (let i = 0; i < pointList.length; i++) {
        let vector = new THREE.Vector3(pointList[i].x, pointList[i].y, pointList[i].z)
        drawPointList.push(vector)
        this.drawPoint(pointList[i])
      }
      if(pointList.length>1){
        let curve = new THREE.CatmullRomCurve3(drawPointList);
        curve.curveType = 'chordal'; // 曲线类型
        curve.closed = false; // 曲线是否自动闭环
        let ponits = curve.getPoints(100); // 分段值,数值越大,曲线越圆滑
        let line = new THREE.Line(new THREE.BufferGeometry().setFromPoints(ponits), new THREE.LineBasicMaterial({ color: 0x000000 })); // 构建三维曲线
        line.name ='once'
        this.scene.add(line); // 加入场景
      }
    },
 removeOnce(){
      for (let i = 0; i < this.scene.children.length; i++) {
        if(this.scene.children[i].name === 'once'){
          this.scene.remove(this.scene.children[i]);
          i--;
        }
      }
    },

然后是添加路径规划,暂时是临时写的规划方法,逻辑是沿着x和y向目的地方向移动,当发现x或者y与障碍物重合时,就沿着另一个方向移动,直到没有障碍物遮挡。再继续沿着x和y向目的地移动,最终到达目标。

judgeObstacle(x,y){ //判断障碍物
      let result = {
        xy:"",
        distance:0,
        box:null
      };
      if(x>=this.box1.beginX && x<=this.box1.endX){
        result.xy = "x";result.distance = Math.abs(this.box1.beginX-x);result.box = this.box1;
      }else if (x>=this.box2.beginX && x<=this.box2.endX){
        result.xy = "x";result.distance = Math.abs(this.box2.beginX-x);result.box = this.box2;
      }else if (x>=this.box3.beginX && x<=this.box3.endX){
        result.xy = "x";result.distance = Math.abs(this.box3.beginX-x);result.box = this.box2;
      }

      if(y>=this.box1.beginY && y<=this.box1.endY){
        result.xy = "y";result.distance = Math.abs(this.box1.beginX-x);result.box = this.box1;
      }else if(y>=this.box2.beginY && y<=this.box2.endY){
        result.xy = "y";result.distance = Math.abs(this.box2.beginX-x);result.box = this.box2;
      }else if(y>=this.box3.beginY && y<=this.box3.endY){
        result.xy = "y";result.distance = Math.abs(this.box3.beginX-x);result.box = this.box3;
      }
      if(x>=this.box1.beginX && x<=this.box1.endX && y>=this.box1.beginY && y<=this.box1.endY){
        result.xy = "xy";
      }else if (x>=this.box2.beginX && x<=this.box2.endX && y>=this.box2.beginY && y<=this.box2.endY){
        result.xy = "xy";
      }else if (x>=this.box3.beginX && x<=this.box3.endX && y>=this.box3.beginY && y<=this.box3.endY){
        result.xy = "xy";
      }
      return result;
    },
    buildPath(){
      let judgeBegin = this.judgeObstacle(this.beginPoint.x,this.beginPoint.y)
      let judgeEnd = this.judgeObstacle(this.endPoint.x,this.endPoint.y)
      if("xy" === judgeBegin.xy || "xy" === judgeEnd.xy){
        console.log("出发和结束点不能再障碍物里")
        return "出发和结束点不能再障碍物里"
      }
      this.removeOnce();

      let pointList = [];
      let distanceX = this.endPoint.x - this.beginPoint.x;
      let abxX = distanceX/Math.abs(distanceX)
      let distanceY = this.endPoint.y - this.beginPoint.y;
      let abxY = distanceY/Math.abs(distanceY)
      let countX = Math.abs(distanceX) / 5;
      let countY = Math.abs(distanceY) / 5;
      let tempPoint = {
        x:this.beginPoint.x,
        y:this.beginPoint.y
      }
      for (let i = 0; i < countX; i++) {
        tempPoint.x = parseInt(tempPoint.x) + 5 * abxX
        if(countY>0){
          countY --;
          tempPoint.y = parseInt(tempPoint.y) + 5 * abxY
        }
        let judgeResult = this.judgeObstacle(tempPoint.x, tempPoint.y);
        if("x" === judgeResult.xy){
          if(i === countX){
            tempPoint.x = parseInt(tempPoint.x) + 5 * abxX
          }else{
            if(countY>0){
              countY ++;
              tempPoint.y = tempPoint.y - 5 * abxY
            }
          }
          console.log("在x里")
        }
        else if("y" === judgeResult.xy){
          if(countY === 0){
            tempPoint.y = parseInt(tempPoint.y) + 5 * abxX
          }else{
            i --;
            tempPoint.x = tempPoint.x - 5 * abxX
          }
          console.log("在y里,")
        }
        console.log(tempPoint.x + " " + tempPoint.y)
        let point = {x:tempPoint.x,y:tempPoint.y,z:2}
        pointList.push(point)
      }
      let point = {x:this.endPoint.x,y:this.endPoint.y,z:2}
      pointList.push(point)
      this.drawPath(pointList);
    },

Threejs路径规划案例V1_Math_05

不过目前路径规划还有很多问题,后面会继续修改,但是已经可以实现直观的帮助我调整规划逻辑了

标签:Threejs,案例,THREE,V1,let,new,box1,tempPoint,box3
From: https://blog.51cto.com/BakerZhuang/10894150

相关文章

  • JS小案例:单个按钮绑定时钟开启和暂停以及随机点名
     单个按钮开启暂停时钟案例单个按钮绑定时钟案例中,核心在于进行value值的判断,每次点击按钮都会进行一次value值判断以及value值更改HTML源码:<!DOCTYPEhtml><html> <head> <metacharset="utf-8"> <title>一个按钮绑定时钟开始和暂停</title> </head> <bodyonlo......
  • 发红包案例(java)
    User类创建publicclassUser{privateStringname;privateintmoney;publicUser(){}publicUser(Stringname,intmoney){this.name=name;this.money=money;}publicvoidshow(){System.out.println(&qu......
  • JS逆向入门案例-xx志愿服务网encData-05
    文章目录概要整体架构流程技术细节小结概要提示:仅供学习,不得用做商业交易,如有侵权请及时联系!逆向:xx志愿服务网URL:aHR0cDovL2NoaW5hdm9sdW50ZWVyLm1jYS5nb3YuY24vc2l0ZS9wcm9qZWN0目标:表单数据中的encData参数整体架构流程提示:分析-调试-猜想-实现-执......
  • 服务器数据恢复—异常断电导致ESXI主机共享存储中raid6阵列崩溃的数据恢复案例
    服务器存储数据恢复环境:一台存储中有一组由12块SAS硬盘组建的raid6磁盘阵列,划分了1个卷,由数台VmwareESXI主机共享存储。卷中存放了大量的Windows系统虚拟机。这些虚拟机系统盘大小一致,数据盘大小不确定,数据盘都是精简模式。服务器存储故障:机房异常断电导致存储瘫痪,加电后存储依......
  • 【案例分享】如何利用京东云建设高可用业务架构
    本文以2022年一个实际项目为基础,来演示在京东云上构建高可用业务的整个过程。公有云及私有云客户可通过使用京东云的弹性IAAS、PAAS服务,创建高可用、高弹性、高可扩展、高安全的云上业务环境,提升业务SLA,提升运维自动化水平,降低资源成本及运维成本。有业务迁移上云需求,希望构建云......
  • Java Script网页设计案例04A6PZbSPCHdMhmS
    1.JavaScript网页设计案例下面我将提供一个简单的JavaScript网页设计案例,该案例将实现一个动态的待办事项列表(TodoList)。用户可以在页面上添加新的待办事项,标记它们为已完成,以及删除它们。这个案例将使用HTML来构建页面结构,CSS来美化页面,以及JavaScript来添加动态功能。1.1HT......
  • 【案例分享】如何利用京东云建设高可用业务架构
    本文以2022年一个实际项目为基础,来演示在京东云上构建高可用业务的整个过程。公有云及私有云客户可通过使用京东云的弹性IAAS、PAAS服务,创建高可用、高弹性、高可扩展、高安全的云上业务环境,提升业务SLA,提升运维自动化水平,降低资源成本及运维成本。有业务迁移上云需求,希望构建云上......
  • opc da 服务器数据 转IEC61850项目案例
    目录1 案例说明 12 VFBOX网关工作原理 13 应用条件 24 查看OPCDA服务器的相关参数 25 配置网关采集opcda数据 46 用IEC61850协议转发数据 67 网关使用多个逻辑设备和逻辑节点的方法 98 在服务器上运行仰科OPCDA采集软件 109 案例总结 121 案例说明在OPCDA服务器上......
  • Java Script网页设计案例
    1.JavaScript网页设计案例下面我将提供一个简单的JavaScript网页设计案例,该案例将实现一个动态的待办事项列表(TodoList)。用户可以在页面上添加新的待办事项,标记它们为已完成,以及删除它们。这个案例将使用HTML来构建页面结构,CSS来美化页面,以及JavaScript来添加动态功能。1.1HT......
  • 爬虫案例2-爬取视频的三种方式之一:requests篇(1)
    (目录)前言本文写了一个爬取视频的案例,使用requests库爬取了好看视频的视频,并进行保存到本地。后续也会更新selenium篇和DrissionPage篇。当然,爬取图片肯定不止这三种方法,还有基于python的scrapy框架,基于node.js的express框架以及基于Java的webmagic框架等等。爬虫步骤确定网址......