zoukankan      html  css  js  c++  java
  • 判断点在三角形内

    转载:自判断点是否在三角形内部

    给定三角形ABC和一点P(x,y,z),判断点P是否在ABC内。这是游戏设计中一个常见的问题。需要注意的是,这里假定点和三角形位于同一个平面内。

    内角和法

    连接点P和三角形的三个顶点得到三条线段PA,PB和PC,求出这三条线段与三角形各边的夹角,如果所有夹角之和为360度,那么点P在三角形内,否则不在,此法直观,但效率低下。

    同向法

    假设点P位于三角形内,会有这样一个规律,当我们沿着ABCA的方向在三条边上行走时,你会发现点P始终位于边AB,BC和CA的右侧。我们就利用这一点,但是如何判断一个点在线段的左侧还是右侧呢?我们可以从另一个角度来思考,当选定线段AB时,点C位于AB的右侧,同理选定BC时,点A位于BC的右侧,最后选定CA时,点B位于CA的右侧,所以当选择某一条边时,我们只需验证点P与该边所对的点在同一侧即可。问题又来了,如何判断两个点在某条线段的同一侧呢?可以通过叉积来实现,连接PA,将PA和AB做叉积,再将CA和AB做叉积,如果两个叉积的结果方向一致,那么两个点在同一测。判断两个向量的是否同向可以用点积实现,如果点积大于0,则两向量夹角是锐角,否则是钝角。

    代码如下,为了实现程序功能,添加了一个Vector3类,该类表示三维空间中的一个向量。

    // 3D vector
    class Vector3
    {
    public:
        Vector3(float fx, float fy, float fz)
            :x(fx), y(fy), z(fz)
        {
            
        }
        
        
        // Subtract
        Vector3 operator - (const Vector3& v) const
        {
            return Vector3(x - v.x, y - v.y, z - v.z) ;
        }
        
        
        // Dot product
        float Dot(const Vector3& v) const
        {
            return x * v.x + y * v.y + z * v.z ;
        }
        
        
        // Cross product
        Vector3 Cross(const Vector3& v) const
        {
            return Vector3(
                y * v.z - z * v.y,
                z * v.x - x * v.z,
                x * v.y - y * v.x ) ;
        }
        
        
    public:
        float x, y, z ;
    };
    
    
    // Determine whether two vectors v1 and v2 point to the same direction
    // v1 = Cross(AB, AC)
    // v2 = Cross(AB, AP)
    bool SameSide(Vector3 A, Vector3 B, Vector3 C, Vector3 P)
    {
        Vector3 AB = B - A ;
        Vector3 AC = C - A ;
        Vector3 AP = P - A ;
        
        
        Vector3 v1 = AB.Cross(AC) ;
        Vector3 v2 = AB.Cross(AP) ;
        
        
        
        // v1 and v2 should point to the same direction
        return v1.Dot(v2) >= 0 ;
    }
    
    
    // Same side method
    // Determine whether point P in triangle ABC
    bool PointinTriangle1(Vector3 A, Vector3 B, Vector3 C, Vector3 P)
    {
        return SameSide(A, B, C, P) && 
            SameSide(B, C, A, P) &&
            SameSide(C, A, B, P) ;
    }
    重心法

    上面这个方法简单易懂,速度也快,下面这个方法速度更快,只是稍微多了一点数学而已

    三角形的三个点在同一个平面上,如果选中其中一个点,其他两个点不过是相对该点的位移而已,比如选择点A作为起点,那么点B相当于在AB方向移动一段距离得到,而点C相当于在AC方向移动一段距离得到。

    所以对于平面内任意一点,都可以由如下方程来表示:

    P = A + u * (C – A) + v * (B - A) // 方程1

    如果系数u或v为负值,那么相当于朝相反的方向移动,即BA或CA方向。那么如果想让P位于三角形ABC内部,u和v必须满足什么条件呢?有如下三个条件:
    u >= 0
    v >= 0
    u + v <= 1

    几个边界情况,当u = 0且v = 0时,就是点A,当u = 0,v = 1时,就是点B,而当u = 1, v = 0时,就是点C。

    整理方程1得到P – A = u(C - A) + v(B - A)。

    令v0 = C – A, v1 = B – A, v2 = P – A,则v2 = u * v0 + v * v1,现在是一个方程,两个未知数,无法解出u和v,将等式两边分别点乘v0和v1的到两个等式。
    (v2) • v0 = (u * v0 + v * v1) • v0
    (v2) • v1 = (u * v0 + v * v1) • v1

    注意到这里u和v是数,而v0,v1和v2是向量,所以可以将点积展开得到下面的式子。
    v2 • v0 = u * (v0 • v0) + v * (v1 • v0)  // 式1
    v2 • v1 = u * (v0 • v1) + v * (v1• v1)   // 式2

    解这个方程得到:
    u = ((v1•v1)(v2•v0)-(v1•v0)(v2•v1)) / ((v0•v0)(v1•v1) - (v0•v1)(v1•v0))
    v = ((v0•v0)(v2•v1)-(v0•v1)(v2•v0)) / ((v0•v0)(v1•v1) - (v0•v1)(v1•v0))

    是时候上代码了,这段代码同样用到上面的Vector3类:

    view source

    print?

    // Determine whether point P in triangle ABC
    bool PointinTriangle(Vector3 A, Vector3 B, Vector3 C, Vector3 P)
    {
        Vector3 v0 = C - A ;
        Vector3 v1 = B - A ;
        Vector3 v2 = P - A ;
        
        
        float dot00 = v0.Dot(v0) ;
        float dot01 = v0.Dot(v1) ;
        float dot02 = v0.Dot(v2) ;
        float dot11 = v1.Dot(v1) ;
        float dot12 = v1.Dot(v2) ;
        
        
        float inverDeno = 1 / (dot00 * dot11 - dot01 * dot01) ;
        
        
        float u = (dot11 * dot02 - dot01 * dot12) * inverDeno ;
        if (u < 0 || u > 1) // if u out of range, return directly
        {
            return false ;
        }
        
        
        float v = (dot00 * dot12 - dot01 * dot02) * inverDeno ;
        if (v < 0 || v > 1) // if v out of range, return directly
        {
            return false ;
        }
        
        
        return u + v <= 1 ;
    }

    CSDN

    2:

    用三角形三个顶点按顺序生成三条线,
    判断该点是否同时满足在三条线的左边
    或右边,如果满足条件点在三角形内。

     

    3:

    正好大学时学过计算机图形学:
       当时老师介绍了两个方法,但楼上的老兄已经说出来了
       一种是用矢量叉乘法:由三个顶点向所求的点引出矢量(注意方向),然后任意用其中两个矢量形成平面,再用这个平面和剩下的矢量叉乘,得出一个新矢量,方向向里,则在三角形外,反之在里面。
        这种方法看似麻烦,但都有公式套入,并不是很复杂。
       一种是行扫描法:
         就是把三角形的三条边连起来,然后从要求的点从左向右引出射线,如果有奇数个交点,则在三角形内,否则在外面。但这种算法要注意的是:要考虑如果有三角形的一条边与水平线平行的问题,也就是边界问题。
        至于是不是最有效率的,就不知道了。

     

    shunan的博客

     

    之前有了解过这方面的东西,在图形编辑器中也用到了这个判断,当时总结出来的结果是要4次向量叉乘。同时在网上粗略的搜了一下,貌似也没找到有更好的方法了(也许是自己没找到,^-^).下面先说一下用4次向量叉乘的方法:

    设三角形三点A(x1,y1)B(x2,y2)C(x3,y3),已知点M(x,y),

    1,先求出三个向量MA,MB,MC. 

    2,计算MA*MB,MA*MC; MB*MC,MB*MA;

    3,如果此两组的向量叉乘的结果都是异号的,即方向相反的,则说明是在三角形内部,否则在三角形外部!

    当然还可以计算三角形面积的方法,其实面积就是向量叉乘的意思,本质上是一样的。最后说一下只用3次叉乘就ok的方法(昨天找下学期做毕业设计的老师,她让我做计算几何算法用并行计算实现方面的东东,因此突然间回忆了一下以前看的比较郁闷的计算几何方面的东东,嘿嘿),

    只需依次计算MA*MB,MB*MC,MC*MA,如果此3结果都是同号(或都正,或都负),则说明点M在三角形每条边的同侧,即内部。否则必在外部!

     

    一尾鱼的博客

     

    1. 题目:如何判断一个点在三角形内部?

    2. 解析:

      (1)方法1:面积法,如下面两幅图所示,图(a)点O在ABC外侧,面积S(ABO)+S(ACO)+S(BCO)>S(ABC);图(b)点O在ABC内部,S(ABO)+S(ACO)+S(BCO)=S(ABC)。由此可以使用面积法来判断点是否在三角形内侧。

      (2)方法2:三角形的边做逆时针标准,如下图。如果点在三角形内,则点必然在射线AB,BC,CA的左侧;如果点在三角形外,则点必在某条射线的右侧。

      (3)方法3:从点O水平向左作一条射线,并计算与三角形各条边是否相交。如果相交的边数为奇数,则点在三角形内部;如果相交的边数为偶数,则点在三角形外部。

    3. 相关知识

      (1)计算三角形面积的海伦公式:设三角形三条边长分别为a,b,c,p=(a+b+c)/2,三角形面积为sqrt(p*(p-a)*(p-b)*(p-c))

      (2)使用向量差乘判断一个点是否在一条射线的左侧:(未完待续)

    我的补充:

    0

    1

    2

  • 相关阅读:
    leetcode 48. Rotate Image
    leetcode 203. Remove Linked List Elements 、83. Remove Duplicates from Sorted List 、82. Remove Duplicates from Sorted List II(剑指offer57 删除链表中重复的结点) 、26/80. Remove Duplicates from Sorted ArrayI、II
    leetcode 263. Ugly Number 、264. Ugly Number II 、313. Super Ugly Number 、204. Count Primes
    leetcode 58. Length of Last Word
    安卓操作的一些问题解决
    leetcode 378. Kth Smallest Element in a Sorted Matrix
    android studio Gradle Build速度加快方法
    禁用gridview,listview回弹或下拉悬停
    Android Studio找不到FragmentActivity类
    安卓获取ListView、GridView等滚动的距离(高度)
  • 原文地址:https://www.cnblogs.com/sanghai/p/2771508.html
Copyright © 2011-2022 走看看