当前位置: 首页 > news >正文

【recast-navigation/源码解析】findStraightPath详解以及寻路结果贴边优化

说在前面

  • recast-navigation版本:1.6.0

叉积cross product

  • 正常来讲,叉乘为:
    ∣ A ⃗ × B ⃗ ∣ = ∣ x A y A x B y B ∣ = x A ⋅ y B − x B ⋅ y A |\vec{A} \times \vec{B}|=\begin{vmatrix} x_A & y_A \\ x_B & y_B \end{vmatrix}=x_A \cdot y_B - x_B \cdot y_A A ×B = xAxByAyB =xAyBxByA
  • 例如:
    在这里插入图片描述
  • 但是在recast-navigation库中:
    /// Derives the signed xz-plane area of the triangle ABC, 
    /// or the relationship of line AB to point C.
    ///  @param[in]		a		Vertex A. [(x, y, z)]
    ///  @param[in]		b		Vertex B. [(x, y, z)]
    ///  @param[in]		c		Vertex C. [(x, y, z)]
    /// @return The signed xz-plane area of the triangle.
    inline float dtTriArea2D(const float* a, const float* b, const float* c)
    {const float abx = b[0] - a[0];const float abz = b[2] - a[2];const float acx = c[0] - a[0];const float acz = c[2] - a[2];return acx*abz - abx*acz;
    }
    
    这应该是因为x轴与正常坐标系相反

findStraightPath

  • 在使用findStraightPath前,我们通常会先使用findPath进行A*搜索,得到一个基于多边形的路径,假设findPath的结果如下图:
    在这里插入图片描述

  • findStraightPath的目的则是从这几个多边形中寻找一条路径出来,接下来我们一步步的分解这个过程

    1. 首先我们对一些计算过程中使用到的临时变量进行初始化, 主要为portalApexportalLeftportalRight

      float portalApex[3], portalLeft[3], portalRight[3];
      dtVcopy(portalApex, closestStartPos);
      dtVcopy(portalLeft, portalApex);
      dtVcopy(portalRight, portalApex);
      

      可以看到这三个变量初始化为start点的值
      在这里插入图片描述

    2. 我们从起始点(start)所在的多边形开始遍历,遍历次数(至少)为所有多边形的数量,在我们的例子中,数量为3;

      for (int i = 0; i < pathSize; ++i)
      {
      
    3. 在每次循环中,我们首先需要计算得到当前多边形与下一个多边形的公共边,得到公共边的两个点;假设当前多边形为绿色,下一个多边形为紫色,计算得到的两个点分别为leftright,如下图
      在这里插入图片描述

      if (i+1 < pathSize)
      {unsigned char fromType; // fromType is ignored.// Next portal.if (dtStatusFailed(getPortalPoints(path[i], path[i+1], left, right, fromType, toType))){
      
    4. 然后我们使用三角形的有向面积(signed area of triangle叉乘)对几个点的位置进行判定

      /// Derives the signed xz-plane area of the triangle ABC, or the relationship of line AB to point C.
      ///  @param[in]		a		Vertex A. [(x, y, z)]
      ///  @param[in]		b		Vertex B. [(x, y, z)]
      ///  @param[in]		c		Vertex C. [(x, y, z)]
      /// @return The signed xz-plane area of the triangle.
      inline float dtTriArea2D(const float* a, const float* b, const float* c)
      {const float abx = b[0] - a[0];const float abz = b[2] - a[2];const float acx = c[0] - a[0];const float acz = c[2] - a[2];return acx*abz - abx*acz;
      }
      

      首先是点portalApexportalRightright,由于此时portalApexportalRight相同,所以结果为0;
      在这里插入图片描述

      这个时候我们将点portalRight置为点right;同理,对于点portalApexportalLeftleft,将点portalLeft置为点left
      在这里插入图片描述

      // Right vertex.
      if (dtTriArea2D(portalApex, portalRight, right) <= 0.0f)
      {if (dtVequal(portalApex, portalRight) || dtTriArea2D(portalApex, portalLeft, right) > 0.0f){dtVcopy(portalRight, right);rightPolyRef = (i+1 < pathSize) ? path[i+1] : 0;rightPolyType = toType;rightIndex = i;}else{
      // ......
      // Left vertex.
      if (dtTriArea2D(portalApex, portalLeft, left) >= 0.0f)
      {if (dtVequal(portalApex, portalLeft) || dtTriArea2D(portalApex, portalRight, left) < 0.0f){dtVcopy(portalLeft, left);leftPolyRef = (i+1 < pathSize) ? path[i+1] : 0;leftPolyType = toType;leftIndex = i;}else{
      

      至此,第一个循环结束

    5. 第二个循环,我们挪动第二个多边形,即当前多边形为第二个,计算第二、第三多边形的公共边,得到新的leftright;如下图,当前多边形为绿色,下一个多边形为紫色,红色为公共边
      在这里插入图片描述

    6. 同样,先对点portalApexportalRightright以及点portalApexportalLeftleft进行计算,这一步实际上是在判定边(left-right)是否在夹角(portalLeft-portalApex-portalRight)之内
      在这里插入图片描述
      首先是点right,由于点right在向量portalApex-portalRight左侧,不再进行下一步判定;即以下条件不满足:

      // Right vertex.
      if (dtTriArea2D(portalApex, portalRight, right) <= 0.0f)
      {
      

      接着是点left,由于点left在向量portalApex-portalLeft左侧,满足条件,继续下一步判定;

      // Left vertex.
      if (dtTriArea2D(portalApex, portalLeft, left) >= 0.0f)
      {
      

      继续判定点left是否在向量portalApex-portalRight左侧,结果满足条件,所以点portalRight是拐点

      if (dtVequal(portalApex, portalLeft) || dtTriArea2D(portalApex, portalRight, left) < 0.0f)
      {//......
      }
      else
      {//....dtVcopy(portalApex, portalRight);
      

      得到拐点之后,将拐点置为新的portalApexportalRightportalLeft,同时,下一次循环将继续从绿色多边形开始
      在这里插入图片描述
      此时,我们就得到了部分路径(上图中绿色点)

    7. 继续下一次循环,同理可得到下述结果:
      在这里插入图片描述

    8. 下一次循环,由于此时已经没有下一个多边形了,我们将leftright置为点end
      在这里插入图片描述
      同样,先对点portalApexportalRightright以及点portalApexportalLeftleft进行计算,
      在这里插入图片描述
      最终得到拐点portalRight
      在这里插入图片描述

    9. 下一次循环,同样,由于此时已经没有下一个多边形了,我们将leftright置为点end
      在这里插入图片描述
      继续对点portalApexportalRightright以及点portalApexportalLeftleft进行计算,
      在这里插入图片描述
      此时循环结束,我们将点end加入路径

      }// Ignore status return value as we're just about to return anyway.
      appendVertex(closestEndPos, DT_STRAIGHTPATH_END, 0,straightPath, straightPathFlags, straightPathRefs,straightPathCount, maxStraightPath);
      
    10. 最终结果,如下图绿色路径
      在这里插入图片描述

寻路结果贴边优化的一种方式

  • 通过上面的解释,我们知道寻路结果上的拐点都是多边形的边上的顶点,也就是leftright点,所以想要让结果不那么贴边,可以将两点向内偏移,即:
    // Next portal.
    if (dtStatusFailed(getPortalPoints(path[i], path[i+1], left, right, fromType, toType)))
    {//......
    }if (!dtVequal(left, right)) {float _left[3], _right[3];dtVcopy(_left, left);dtVcopy(_right, right);dtVlerp(left, _left, _right, 0.1);dtVlerp(right, _right, _left, 0.1);
    }				
    
  • 优化后
    在这里插入图片描述
    在这里插入图片描述

相关文章:

【recast-navigation/源码解析】findStraightPath详解以及寻路结果贴边优化

说在前面 recast-navigation版本&#xff1a;1.6.0 叉积cross product 正常来讲&#xff0c;叉乘为&#xff1a; ∣ A ⃗ B ⃗ ∣ ∣ x A y A x B y B ∣ x A ⋅ y B − x B ⋅ y A |\vec{A} \times \vec{B}|\begin{vmatrix} x_A & y_A \\ x_B & y_B \end{vmatrix…...

‌移动管家手机智能控制汽车系统

‌ 手机可以通过下载特定的应用程序来控制汽车系统&#xff0c;实现远程启动、锁/解锁车门、调节车内温度等功能。‌ ‌ 手机智能控制汽车系统主要通过下载并安装特定的APP来实现。‌ 首先&#xff0c;用户需要确定自己的手机系统是安卓还是苹果版&#xff0c;然后前往应用…...

828华为云征文|华为云Flexus X实例Redis性能加速评测及对比

目录 前言 一、华为云Flexus X加速Redis购买 1.1 Flexus X实例购买 1.2 Redis加速镜像选择 1.3 重置密码 1.4 登录Flexus X实例 1.5 Flexus X实例Redis验证 二、Redis测评工具介绍 三、华为云Flexus X实例加速Redis测评 3.1 string类型 3.2 hash类型 3.3 list类型 3.4 set类型 …...

【OpenCV3】图像的翻转、图像的旋转、仿射变换之图像平移、仿射变换之获取变换矩阵、透视变换

1 图像的放大与缩小 2 图像的翻转 3 图像的旋转 4 仿射变换之图像平移 5 仿射变换之获取变换矩阵 6 透视变换 1 图像的放大与缩小 resize(src, dsize[, dst[, fx[, fy[, interpolation]]]]) src: 要缩放的图片dsize: 缩放之后的图片大小, 元组和列表表示均可.dst: 可选参数, 缩…...

不要认为996是开玩笑

996 预防针 随着秋招进程的不断推进&#xff0c;有部分同学已经 OC&#xff0c;有部分同学还在苦苦挣扎&#xff0c;并不断降低自己的预期&#xff0c;包括在和 HR 沟通过程中&#xff0c;主动说出自己愿意接受加班&#xff0c;愿意接受 996&#xff0c;以此来博得企业方面的加…...

精益工程师资格证书:2024年CLMP报名指南

随着全球对精益管理的需求日益增长&#xff0c;精益管理专业人士资格认证&#xff08;CLMP&#xff09;正成为越来越多精益工程师和精益管理人员提升职业竞争力的首选。作为一种注重管理而非生产的认证&#xff0c;CLMP不仅适用于制造业的专业人士&#xff0c;也吸引了各行业的…...

【Unity基础】如何选择脚本编译方式Mono和IL2CPP?

Edit -> Project Settings -> Player 在 Unity 中&#xff0c;Scripting Backend 决定了项目的脚本编译方式&#xff0c;即如何将 C# 代码转换为可执行代码。Unity 提供了两种主要的 Scripting Backend 选项&#xff1a;Mono 和 IL2CPP。它们之间的区别影响了项目的性能、…...

写在OceanBase开源三周年

我收获的深刻感触get 感触1&#xff1a;解决问题才有生存价值 [产品力] 感触2&#xff1a;永无止境的“易用性” [易用性] 感触3&#xff1a;立下“双赢”的flag 感触4&#xff1a;社区建设离不开用户和开发者参与 感触5&#xff1a;从易用到用户自助 [自助能力] 当时想法很简…...

【笔记】408刷题笔记

文章目录 三对角三叉树求最小带权路径UDP报文首部和TCP报文首部IP报文首部TCP报文首部UDP报文首部 刷新和再生的区别地址译码 为了区分队空队满&#xff0c;可以使用三种处理方式 1&#xff09;牺牲一个单元 队头指针在队尾指针的下一位置作为队满的标志 队满条件&#xff1a;(…...

GitHub Star 数量前 13 的自托管项目清单

一个多月前&#xff0c;我们撰写并发布了这篇文章《终极自托管解决方案指南》。在那篇文章里我们深入探讨了云端服务与自托管方案的对比、自托管的潜在挑战、如何选择适合自托管解决方案&#xff0c;并深入介绍了五款涵盖不同场景的优秀自托管产品。 关于自托管的优势&#xf…...

js实现生成随机数值的数组

生成随机数值的数组 方法一&#xff1a;使用while循环和Set // min 开始数值&#xff0c; max 结束数值&#xff0c; count 数组内填充几个数值 function generateUniqueRandomNumbers(min, max, count) { let result new Set(); while (result.size < count) { let n…...

视频怎么转换成mp3格式?分享5种便捷的转换方法

在日常生活中&#xff0c;我们经常会遇到需要将视频文件中的音频提取出来&#xff0c;转换成MP3格式的情况&#xff0c;以便在手机、MP3播放器或其他设备上播放。今天&#xff0c;我将为大家介绍5种视频转MP3的方法&#xff0c;非常简单便捷&#xff0c;一起来学习下吧。 方法一…...

Reflection 70B如何革新语言模型的准确性与推理能力

在开源人工智能模型领域&#xff0c;HyperWrite 公司开发的 Reflection 70B 模型以其创新的“反射”机制成为新的重量级竞争者。这一模型旨在解决大型语言模型常见的“幻觉”问题&#xff0c;即生成不准确或虚构的信息。Reflection 70B 通过在提供最终响应之前评估和纠正自己的…...

覆盖索引是什么意思?

文章目录 Q1&#xff1a;覆盖索引是什么意思&#xff1f;覆盖索引的工作原理覆盖索引的优势覆盖索引的示例覆盖索引的使用场景覆盖索引的限制总结 Q2&#xff1a;为什么查询所涉及的所有字段都在索引中存在&#xff0c;那么数据库就无需回表&#xff1f;1. **索引本身存储了字段…...

最大间距问题

LeetCode164 最大间距 基数排序 #include <iostream> #include <vector> using namespace std;class Solution { public:int maximumGap(vector<int>& nums) {int nnums.size();if(n<2) return 0;int exp1;int Maxnums[0];vector<int> buf(n)…...

【Hadoop|MapReduce篇】Hadoop序列化概述

1. 什么是序列化 序列化就是把内存中的对象&#xff0c;转换成字节序列&#xff08;或其他数据传输协议&#xff09;以便于存储到磁盘&#xff08;持久化&#xff09;和网络传输。 反序列化就是将收到的字节序列&#xff08;或其他数据传输协议&#xff09;或者磁盘的持久化数…...

【Elasticsearch系列】Elasticsearch中的分页

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…...

NLTK:一个强大的自然语言处理处理Python库

我是东哥&#xff0c;一名热爱技术的自媒体创作者。今天&#xff0c;我将为大家介绍一个非常有趣且强大的Python库——NLTK。无论你是刚刚接触Python的小白&#xff0c;还是对自然语言处理&#xff08;NLP&#xff09;有些许了解的朋友&#xff0c;NLTK都是一个值得学习的工具。…...

NUUO网络视频录像机 css_parser.php 任意文件读取漏洞复现

0x01 产品简介 NUUO网络视频录像机(Network Video Recorder,简称NVR)是NUUO Inc.生产的一种专业视频监控设备,它广泛应用于零售、交通、教育、政府和银行等多个领域。能够同时管理多个IP摄像头,实现视频录制、存储、回放及远程监控等功能。它采用先进的视频处理技术,提供…...

【支付】Stripe支付通道Java对接(产品 价格 支付 查询 退款 回调)

Stripe是一家美国科技公司&#xff0c;成立于2010年&#xff0c;由爱尔兰兄弟Patrick Collison和John Collison共同创立。该公司致力于提供高效、简洁的互联网支付收款服务&#xff0c;为开发者或商家提供支付API接口或代码&#xff0c;使商家的网站、移动APP支持信用卡付款。S…...

【物联网专业】案例9_2:控制数码管(定时器中断)

文章目录0 文章介绍1 仿真图2 效果图3 不完整代码4 思考题0 文章介绍 对应定时器/计数器案例目标的实现 用计数器中断0&#xff08;P3^4&#xff09;控制数码管段选 P1^6&#xff09;控制数码位选 1 仿真图 2 效果图 3 不完整代码 复制该代码&#xff0c;其中有7个补充点&#…...

哪家网卡公司靠谱必看5大关键清单 企业采购专属版

选网卡公司的3个常见决策误区很多企业采购网卡时踩坑&#xff0c;往往是陷入了三个常见的决策误区。第一个误区是唯价格论&#xff0c;过度压低采购预算&#xff0c;优先选择报价较低的供应商&#xff0c;忽略了产品的授权资质和正品保障&#xff0c;后续容易买到翻新、贴牌的不…...

别再死磕原生OpenStack了!华为云Stack HCS 8.0的极简部署与高可用设计,真香!

华为云Stack HCS 8.0&#xff1a;企业私有云部署的革命性突破 当企业IT架构师面对私有云平台选型时&#xff0c;部署复杂性和系统可靠性往往成为最令人头疼的两大难题。原生OpenStack以其高度灵活性和开源特性吸引了大量技术团队&#xff0c;但随之而来的却是漫长的部署周期、繁…...

OpenAI Codex 安装部署指南:从零到跑通,2026最新版

⏱️ 阅读时间&#xff1a;8分钟 | &#x1f4cc; 难度&#xff1a;入门级 | &#x1f527; 适用系统&#xff1a;macOS / Linux / Windows(WSL2) 前言 距离上次写 Codex 测评已经有一段时间了&#xff0c;这期间 Codex 又经历了好几轮大更新&#xff1a;Computer Use 能力、内…...

新手别怕!用51单片机+74HC138/573点亮静态数码管,保姆级代码+仿真(Keil C51)

从零玩转51单片机&#xff1a;静态数码管驱动全攻略&#xff08;74HC13874HC573实战&#xff09; 第一次拿到51单片机开发板时&#xff0c;看到原理图上密密麻麻的74HC138、74HC573芯片标识&#xff0c;很多初学者都会感到无从下手。这些看似复杂的数字芯片&#xff0c;实际上是…...

终极指南:如何解锁光猫全部性能?RTL960x开源方案深度解析

终极指南&#xff1a;如何解锁光猫全部性能&#xff1f;RTL960x开源方案深度解析 【免费下载链接】RTL960x Hacking & Reverse Engineering RTL960x-based xPON ONTs to suit your OLT 项目地址: https://gitcode.com/gh_mirrors/rt/RTL960x RTL960x开源光猫固件是基…...

从74LS00与非门到74LS86异或门:手把手教你用面包板搭建数字电路基础实验(附波形分析)

从74LS00与非门到74LS86异或门&#xff1a;面包板上的数字电路实战指南 在电子技术的浩瀚海洋中&#xff0c;数字电路犹如一座连接现实与虚拟的桥梁。对于初学者而言&#xff0c;从理论到实践的跨越往往充满挑战——实验室里昂贵的设备、复杂的接线、固定的实验流程&#xff0c…...

Taotoken API Key管理功能实现团队权限与访问控制

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 Taotoken API Key管理功能实现团队权限与访问控制 在团队协作开发或项目管理中&#xff0c;如何安全、可控地分发大模型调用资源是…...

ControlPlane开发者指南:如何创建自定义证据源和动作插件

ControlPlane开发者指南&#xff1a;如何创建自定义证据源和动作插件 【免费下载链接】ControlPlane ControlPlane - context-sensitive computing for OS X 项目地址: https://gitcode.com/gh_mirrors/co/ControlPlane ControlPlane是一款功能强大的macOS上下文感知计算…...

别再死磕开发了!网络安全职业前景全面解析:薪资水平、就业方向与学习路线图

别&#xff0c;你可千万别后悔&#xff01;&#xff01;&#xff01; 首先&#xff0c;你学网安这个选择没有一点毛病&#xff0c;作为一个前辈&#xff0c;我可以明明白白的告诉你&#xff0c;近年程序员就业情况当中&#xff0c;网安是最舒服的一批&#xff0c;所以我看到你说…...