判断一个点是否在指定三角形内(1)
判断一个点是否在在三角形内,最常用的两种方法:面积法、向量同向法。算法虽然很简单,但要做到高效却不容易,要考虑到二维、三维的区别,还要考虑到坐标是用浮点数还是用整数来表示。
在二维平面上,问题相对简单,一般只需6次乘法计算。但在三维平面时问题要复杂很多,在网上看到的算法,一般都需要30次乘法计算(如果已知点P在平面ABC上,则需21次)。实际上,在三维坐标系下,可以做到增加1次比较,将乘法计算降到13次(如果点P在平面ABC上,则最多只要8次乘法计算)。
最常用的两种方法:面积法和向量同向法本质上是等价的。
向量同向法:若点P在三角形内,则三个向量:ab × ap、ap × ac、pb × pc平行同向(它们也与向量ab × ac平行同向),由于这三个向量均有可能为0,直接判断它们平行同向相当麻烦,但考虑到ab × ac不可能为0,直接判断“向量:ab × ap、ap × ac、pb × pc均与ab × ac平行同向”反而更简单。
面积法:当点p在三角形abc内时,4个三角形的面积满足: abc = abp + apc + pbc
对面积的计算,可以通过向量的向量积计算得到: 面积 abc = |ab × ac| / 2
表面上,要计算4个三角形的面积,但根据下面的公式:
ap × ap = 0, pb × pc = (ab - ap) × (ac - ap) = ab × ac - ab × ap - ap × ac
可以少算一次矢量积。
公式: |ab × ac| = |ab × ap| + |ap × ac| + |(ab × ac - ab × ap - ap × ac)|
对任意向量a、b、c: |a + b + c| = |a| + |b| + |c| <==> 向量a、b、c 平行同向
因而,面积法和向量同向法本质上是等价的。
下面先讨论二维坐标系(每个点X,都看作是原点O到该点X的二维向量OX)。
先定义一个二维向量模板:
template<typename T> class Vec2 {
T x, y;
public:
typedef T value_type;
Vec2(T xx = 0, T yy = 0) : x(xx), y(yy) {};
T cross(const Vec2& v) const { return x * v.y - y * v.x;} // 矢量积
Vec2 operator-(const Vec2& v) const { return Vec2(x - v.x, y - v.y); }
};
如果坐标采用浮点数,考虑到浮点数取绝对值方便(有专门的浮点指令),但彼此间比较大小存在误差,采用面积法比较方便:
typedef Vec2<double> Vd2;
bool is_in_triangle(const Vd2& a, const Vd2& b, const Vd2& c, const Vd2& p)
{
Vd2 ab(b -a), ac(c - a), ap(p - a);
//用矢量积计算面积,下面4个值的绝对值,是对应的三角形的面积的两倍,
double abc = ab.cross(ac);
double abp = ab.cross(ap);
double apc = ap.cross(ac);
double pbc = abc - abp - apc; //等于pb.cross(pc)
//面积法:4个三角形的面积差 等于 0
double delta = fabs(abc) - fabs(abp) - fabs(apc) - fabs(pbc);
return fabs(delta) < DBL_EPSILON;
}
如果坐标采用整数表示,代码相对麻烦点:
typedef Vec2<int> Vi2;
bool is_in_triangle(const Vi2& a, const Vi2& b, const Vi2& c, const Vi2& p)
{
Vi2 ab(b -a), ac(c - a), ap(p - a);
//用矢量积计算面积,下面4个值的绝对值,是对应的三角形的面积的两倍,
int abc = ab.cross(ac);
int abp = ab.cross(ap);
int apc = ap.cross(ac);
int pbc = abc - abp - apc; //等于pb.cross(pc)
//方法1: 面积法:4个三角形的面积差 等于 0
return abs(abc) == abs(abp) + abs(apc) + abs(pbc)
//方法2: 矢量同向法: abp apc pbc 均与 abc 同向:
if (abc < 0) { abp = -abp; apc = -apc; pbc = -pbc; }
return (abp >= 0) & (apc >= 0) & (pbc >= 0);
}
方法1:要计算4次绝对值,看似需要4次条件跳转,但主流的编译器,都能采用位运算直接计算绝对值(注意:GCC需要加额外的参数),不需要任何条件跳转。
方法2:比方法1指令少,但多1次条件跳转。
哪种方法效率较高,与编译器生成的具体代码有关。
上面代码中,可采用的两种优化方法:
① 对整数x取绝对值,可以利用位运算:
设 y = 0 (当x >= 0)
= -1 (当x < 0)
(编译器可以利用cdq或sar等指令直接由x计算出y值)
则 abs(x) = (x xor y) – y
或: = (x + y) xor y
或: = x – (2 * x & y)
② 对整数a、b、c, a >= 0 && b >= 0 && c >= 0 等价于
(a >= 0) & (b >= 0) & (c >= 0) 等价于:
(a | b | c) >= 0
为避免编译器没有进行相关优化,直接手动优化,可得:
inline int chg_sign(int x, int sign) //sign只能取0或-1,函数分别返回x、-x
{
return (x + sign) ^ sign;
//return (x ^ sign) - sign;
}
bool is_in_triangle(const Vi2& a, const Vi2& b, const Vi2& c, const Vi2& p)
{
Vi2 ab(b -a), ac(c - a), ap(p - a);
//用矢量积计算面积,下面4个值的绝对值,是对应的三角形的面积的两倍,
int abc = ab.cross(ac);
int abp = ab.cross(ap);
int apc = ap.cross(ac);
int pbc = abc - abp - apc; //等于pb.cross(pc)
//方法3: 矢量同向法(优化版)
const int sign = (abc >= 0) - 1;
//const int sign = abc >> (sizeof(abc) * CHAR_BIT - 1);
return (chg_sign(abp, sign) | chg_sign(apc, sign) | chg_sign(pbc, sign)) >= 0;
}
转载于:https://www.cnblogs.com/flyinghearts/archive/2011/07/07/2100549.html
判断一个点是否在指定三角形内(1)相关推荐
- Qt 判断一个点是否落在三角形内(算法)
利用重心法判断一个点是否落在三角形面积内,三角形的三个点在同一个平面上,如果选中其中一个点,其他两个点不过是相对该点的位移而已,比如选择点A作为起点,那么点B相当于在AB方向移动一段距离得到,而点C相 ...
- Java黑皮书课后题第3章:**3.27(几何:点是否在三角形内)假设一个平面上有一个直角三角形。编写程序,提示用户输入一个点的x坐标和y坐标,然后判断这个点是否在该三角形内
**3.27(几何:点是否在三角形内)假设一个平面上有一个直角三角形.编写程序,提示用户输入一个点的x坐标和y坐标,然后判断这个点是否在该三角形内 题目 题目描述 破题 运行示例 代码 题目 题目描述 ...
- 判断一个点是否在某个区域内(多边形)
判断一个点是否在某个区域内(多边形) 背景: 比如滴滴会根据乘客所在的不同区域,给出不同的价格.市区堵一点,那么价格也高点.获取服务范围只规定在某个范围内 原理: 求解从该点向右发出的水平线射线与多边 ...
- JAVA判断一个地理坐标是否在一个多边形区域内和是否在一个圆形区域内(经纬度)
怎么样判断一个坐标点在一个多边形区域内?包括规则多边形,不规则多边形,还有圆... 1 判断一个坐标是否在圆形区域内? 多边形和圆分开写,首先简单的就是判断是否在圆里面,如何判断一个坐标是否在圆形区域 ...
- C#判断一个类中有无指定名称的方法
C#中可以通过反射分析元数据来解决这个问题,示例代码如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 2 ...
- java 判断范围_java判断一个点是否在一个围栏范围内
应项目需求,需要判断一个点所属哪个区域范围内管辖,突然想起来三年前做了个外卖的项目里面有个功能,判断用户是否在商家自己划的配送范围内,又找回来以前的代码来看了下,所以在此处记录一下 @Data @No ...
- 地图处理方法-判断一个点是否在某个区域内
判断一个点是否在某个区域内(多边形) 背景: 比如滴滴会根据乘客所在的不同区域,给出不同的价格.市区堵一点,那么价格也高点.获取服务范围只规定在某个范围内 原理: 求解从该点向右发出的水平线射线与多边 ...
- Unity判断一个物体是否在相机范围内
Unity判断一个物体是否在相机范围内 思路 代码 注意点 思路 先说下思路,先把物体从世界坐标转到该相机的视口坐标viewPos, 如果满足这两个条件: 0<viewPos.x < 1 ...
- 怎么用C语言搜索有根区间,C语言 判断一个数字是否在一个指定的区间范围内?求解答。。。。...
#include #include usingnamespacestd; intmain() { inta,b,c,d; intn; printf("请输入第一个区间:"); sc ...
最新文章
- JavaScript 方法传参
- 阿里产品专家杨文韬:你想了解的1688都在这里
- Oracle TNSListener服务启动后自动停止问题
- 针对CMS中的tag标签理解
- AndroidStudio创建jinLibs文件夹
- 求1+2+3+……+100的累加和。
- (树莓派、Arduino、物联网、智能家居、机器人)传感器、机械装置、电子元件
- strtus2 与springmvc
- linux 查看端口
- vmplayer7安装OSX10.10
- matlab bp结果,BP-networkmatlab BP神经网络实现手写数字识别,使用 。内有测试数据及实验结果,非常适合入门 276万源代码下载- www.pudn.com...
- 程序小白天天打卡(函数模板)
- 激光共聚焦显微镜原理
- 1000以内的素数(质数)
- JWT 避坑指南:nbf 验签失效问题的解决
- Icarus iverilog中PLI使用范例
- CSS 权威指南 读书笔记(二)
- mysql中selec sum返回null的解决方法
- S5PV210-裸机中断
- torch.nn 和 torch.functional 的区别
热门文章
- cmd 找不到java power 可以_高三总是找不到学习状态?这几个方法一定可以帮到你!...
- 用户研究,你还在“凭感觉”吗?
- 2018创投圈风云再起,企服征途百家争鸣,寻找中国创业最强音!
- react - antd (Table 与 Cascader 平级数据转树形实操)
- 简明writeStream实现
- Ubuntu下打开rar乱码问题的解决方法
- Ubuntu 修改mysql存放路径后无法启动解决办法
- jboss启动之奇葩问题
- Apache https服务器配置笔记
- Revit API取得变量的内参名称