如何确定一个点是否在二维三角形中?

时间:2010-01-12 14:25:49

标签: algorithm math geometry

有没有一种简单的方法来确定一个点是否在三角形内?它是2D,而不是3D。

25 个答案:

答案 0 :(得分:239)

一般来说,最简单(也是最优的)算法是检查点边缘所创建的半平面的哪一侧。

以下是topic on GameDev中的一些高质量信息,包括性能问题。

这里有一些代码可以帮助你入门:

float sign (fPoint p1, fPoint p2, fPoint p3)
{
    return (p1.x - p3.x) * (p2.y - p3.y) - (p2.x - p3.x) * (p1.y - p3.y);
}

bool PointInTriangle (fPoint pt, fPoint v1, fPoint v2, fPoint v3)
{
    float d1, d2, d3;
    bool has_neg, has_pos;

    d1 = sign(pt, v1, v2);
    d2 = sign(pt, v2, v3);
    d3 = sign(pt, v3, v1);

    has_neg = (d1 < 0) || (d2 < 0) || (d3 < 0);
    has_pos = (d1 > 0) || (d2 > 0) || (d3 > 0);

    return !(has_neg && has_pos);
}

答案 1 :(得分:157)

解决以下等式系统:

p = p0 + (p1 - p0) * s + (p2 - p0) * t

如果p0 <= s <= 1以及0 <= t <= 1,则s + t <= 1点位于三角形内。

st1 - s - t被称为p点的barycentric coordinates

答案 2 :(得分:102)

我同意 Andreas Brinck ,重心坐标对此任务非常方便。请注意,每次都不需要求解方程系统:只需评估解析解。使用 Andreas '表示法,解决方案是:

s = 1/(2*Area)*(p0y*p2x - p0x*p2y + (p2y - p0y)*px + (p0x - p2x)*py);
t = 1/(2*Area)*(p0x*p1y - p0y*p1x + (p0y - p1y)*px + (p1x - p0x)*py);

其中Area是三角形的(带符号)区域:

Area = 0.5 *(-p1y*p2x + p0y*(-p1x + p2x) + p0x*(p1y - p2y) + p1x*p2y);

只需评估st1-s-t。点p位于三角形内部,当且仅当它们都是正数时。

编辑:请注意,该区域的上述表达式假定三角形节点编号是逆时针的。如果编号是顺时针方向,则此表达式将返回负区域(但具有正确的幅度)。但是,测试本身(s>0 && t>0 && 1-s-t>0)不依赖于编号的方向,因为如果三角形节点方向发生变化,上面的表达式乘以1/(2*Area)也会改变符号。

编辑2:为了获得更好的计算效率,请参阅下面的 coproc 注释(这表明如果事先知道三角形节点的方向(顺时针或逆时针),可以避免在2*Areas的表达式中按t划分。另请参阅 Andreas Brinck 的回答中的 Perro Azul 的jsfiddle代码。

答案 3 :(得分:39)

我在谷歌最后一次尝试之前编写了这段代码并找到了这个页面,所以我想我会分享它。它基本上是Kisielewicz答案的优化版本。我也研究了Barycentric方法,但从维基百科的文章来看,我很难看到它是如何更有效的(我猜有更深的等价)。无论如何,这种算法的优点是不使用除法;潜在的问题是边缘检测的行为取决于方向。

bool intpoint_inside_trigon(intPoint s, intPoint a, intPoint b, intPoint c)
{
    int as_x = s.x-a.x;
    int as_y = s.y-a.y;

    bool s_ab = (b.x-a.x)*as_y-(b.y-a.y)*as_x > 0;

    if((c.x-a.x)*as_y-(c.y-a.y)*as_x > 0 == s_ab) return false;

    if((c.x-b.x)*(s.y-b.y)-(c.y-b.y)*(s.x-b.x) > 0 != s_ab) return false;

    return true;
}

用语言来说,这个想法是这样的:点AB和AC的左边或右边是点吗?如果是真的,它不能在里面。如果为假,则至少在符合条件的“锥体”内。既然我们知道三角形(三角形)内的一个点必须与BC的同一侧(以及CA),我们检查它们是否不同。如果他们这样做,s不可能在里面,否则s必须在里面。

计算中的一些关键字是线半平面和行列式(2x2交叉乘积)。也许更教学的方式可能是将其视为内部的一个点,如果它与AB,BC和CA中的每条线的同一侧(左侧或右侧)。然而,上述方式似乎更适合某些优化。

答案 4 :(得分:25)

C#版本的重心方法由andreasdr和Perro Azul发布。请注意,如果st具有相反的符号,则可以避免区域计算。我通过非常彻底的单元测试验证了正确的行为。

public static bool PointInTriangle(Point p, Point p0, Point p1, Point p2)
{
    var s = p0.Y * p2.X - p0.X * p2.Y + (p2.Y - p0.Y) * p.X + (p0.X - p2.X) * p.Y;
    var t = p0.X * p1.Y - p0.Y * p1.X + (p0.Y - p1.Y) * p.X + (p1.X - p0.X) * p.Y;

    if ((s < 0) != (t < 0))
        return false;

    var A = -p1.Y * p2.X + p0.Y * (p2.X - p1.X) + p0.X * (p1.Y - p2.Y) + p1.X * p2.Y;

    return A < 0 ?
            (s <= 0 && s + t >= A) :
            (s >= 0 && s + t <= A);
}

[编辑]
接受@Pierre的建议修改;见评论

答案 5 :(得分:11)

重心方法的Java版本:

class Triangle {
    Triangle(double x1, double y1, double x2, double y2, double x3,
            double y3) {
        this.x3 = x3;
        this.y3 = y3;
        y23 = y2 - y3;
        x32 = x3 - x2;
        y31 = y3 - y1;
        x13 = x1 - x3;
        det = y23 * x13 - x32 * y31;
        minD = Math.min(det, 0);
        maxD = Math.max(det, 0);
    }

    boolean contains(double x, double y) {
        double dx = x - x3;
        double dy = y - y3;
        double a = y23 * dx + x32 * dy;
        if (a < minD || a > maxD)
            return false;
        double b = y31 * dx + x13 * dy;
        if (b < minD || b > maxD)
            return false;
        double c = det - a - b;
        if (c < minD || c > maxD)
            return false;
        return true;
    }

    private final double x3, y3;
    private final double y23, x32, y31, x13;
    private final double det, minD, maxD;
}

假设没有溢出,上面的代码将使用整数精确地工作。它也适用于顺时针和逆时针三角形。它不适用于共线三角形(但你可以通过测试det == 0来检查它。)

如果您要测试具有相同三角形的不同点,则重心版本是最快的。

重心版本在3个三角形点中不对称,因此由于浮点舍入误差,它可能不如Kornel Kisielewicz的边缘半平面版本一致。

信用:我从维基百科关于重心坐标的文章中提出了上述代码。

答案 6 :(得分:10)

一种简单的方法是:

  

找到连接的向量   指向三角形的三个   顶点和总和之间的角度   那些载体。如果总和了   角度是2 * pi然后点   在三角形里面。

两个解释替代品的好网站是:

blackpawnwolfram

答案 7 :(得分:7)

使用barycentric coordinates的分析解决方案(由 Andreas Brinck 指出)和:

  • 不在括号内分配乘法
  • 通过存储来避免多次计算相同的术语
  • 减少比较(正如 coproc Thomas Eding 所指出的那样)

可以最大限度地减少&#34; costy&#34;操作:

function ptInTriangle(p, p0, p1, p2) {
    var dX = p.x-p2.x;
    var dY = p.y-p2.y;
    var dX21 = p2.x-p1.x;
    var dY12 = p1.y-p2.y;
    var D = dY12*(p0.x-p2.x) + dX21*(p0.y-p2.y);
    var s = dY12*dX + dX21*dY;
    var t = (p2.y-p0.y)*dX + (p0.x-p2.x)*dY;
    if (D<0) return s<=0 && t<=0 && s+t>=D;
    return s>=0 && t>=0 && s+t<=D;
}

(代码可以粘贴在 Perro Azul jsfiddle

导致:

  • 变量&#34;召回&#34;:30
  • 变量存储:7
  • 补充:4
  • substractions:8
  • 乘法:6
  • divisions:none
  • 比较:4

这与 Kornel Kisielewicz 解决方案相比(25次召回,1次存储,15次减法,6次乘法,5次比较),如果需要顺时针/逆时针检测,可能会更好(如 rhgb 所指出的那样,使用解析解决方案决定因素,需要6次召回,1次加法,2次减法,2次乘法和1次比较。

答案 8 :(得分:5)

我所做的是预先计算三面法线,

  • 通过侧向量和面法向量的交叉乘积在3D中。

  • 在2D中通过简单地交换组件并否定一个,

然后任何一方的内/外是当侧法线和点到点矢量的点积时,改变符号。重复其他两个(或更多)方。

优点:

  • 对于同一个三角形上的多点测试,预计算得非常多。

  • 早期拒绝比内部更多的常见案例。 (如果点数分布加权到一边,可以先测试那一边。)

答案 9 :(得分:4)

这是一个高效的Python实施:

def PointInsideTriangle2(pt,tri):
    '''checks if point pt(2) is inside triangle tri(3x2). @Developer'''
    a = 1/(-tri[1,1]*tri[2,0]+tri[0,1]*(-tri[1,0]+tri[2,0])+ \
        tri[0,0]*(tri[1,1]-tri[2,1])+tri[1,0]*tri[2,1])
    s = a*(tri[2,0]*tri[0,1]-tri[0,0]*tri[2,1]+(tri[2,1]-tri[0,1])*pt[0]+ \
        (tri[0,0]-tri[2,0])*pt[1])
    if s<0: return False
    else: t = a*(tri[0,0]*tri[1,1]-tri[1,0]*tri[0,1]+(tri[0,1]-tri[1,1])*pt[0]+ \
              (tri[1,0]-tri[0,0])*pt[1])
    return ((t>0) and (1-s-t>0))

和示例输出:

enter image description here

答案 10 :(得分:3)

如果您正在寻找速度,这是一个可能对您有帮助的程序。

在纵坐标上对三角形顶点进行排序。这最糟糕的是三次比较。设Y0,Y1,Y2为三个排序值。通过绘制三个水平线,您可以将平面划分为两个半平面和两个平板。设Y是查询点的纵坐标。

if Y < Y1
    if Y <= Y0 -> the point lies in the upper half plane, outside the triangle; you are done
    else Y > Y0 -> the point lies in the upper slab
else
    if Y >= Y2 -> the point lies in the lower half plane, outside the triangle; you are done
    else Y < Y2 -> the point lies in the lower slab

再花费两次比较。如您所见,对于“边界板”之外的点,可以实现快速拒绝。

您可以选择在横坐标上进行测试,以便在左侧和右侧快速拒绝(X <= X0' or X >= X2')。这将同时实现快速边界框测试,但您也需要对横坐标进行排序。

最终,您需要根据划定相关板(上部或下部)的三角形的两边计算给定点的符号。测试的形式如下:

((X - Xi) * (Y - Yj) > (X - Xi) * (Y - Yj)) == ((X - Xi) * (Y - Yk) > (X - Xi) * (Y - Yk))

i, j, k组合的完整讨论(其中有六个,基于排序的结果)超出了本答案的范围,并“留给读者作为练习”;为了提高效率,它们应该是硬编码的。

如果您认为此解决方案很复杂,请注意它主要涉及简单的比较(其中一些可以预先计算),加上6次减法和4次乘法,以防边界框测试失败。后者的成本很难被击败,因为在最坏的情况下你无法避免将测试点与双方进行比较(其他答案中的方法没有成本较低,有些则更糟,如15次减法和6次乘法,有时是分裂)。 / p>

更新: 剪切变换更快

如上所述,您可以使用两个比较快速找到由三个顶点坐标分隔的四个水平带之一内的点。

您可以选择执行一个或两个额外的X测试来检查边界框(虚线)的内在性。

然后考虑X'= X - m Y, Y' = Y给出的“剪切”变换,其中m是最高边的斜率DX/DY。此变换将使三角形的这一边垂直。既然您知道中间水平面的哪一侧,那么就可以根据三角形的一侧测试符号。

enter image description here

假设您预先计算了斜率m,以及剪切三角形顶点的X'和边的方程系数X = m Y + p,您将需要最差的情况下

  • 垂直分类的两个纵坐标比较;
  • 可选择一个或两个横坐标比较用于边界框拒绝;
  • 计算X' = X - m Y;
  • 与剪切三角形的横坐标进行一两次比较;
  • 对剪切三角形的相关侧面进行一次符号测试X >< m' Y + p'

答案 11 :(得分:3)

如果您知道三个顶点的坐标和特定点的坐标,那么您可以得到完整三角形的面积。然后,计算三个三角形段的面积(一个点是给定的点,另外两个是三角形的任意两个顶点)。因此,您将获得三个三角形片段的面积。如果这些区域的总和等于总面积(您之前得到的),则该点应位于三角形内。否则,该点不在三角形内。这应该工作。如果有任何问题,请告诉我。谢谢。

答案 12 :(得分:3)

python 中的其他功能,比开发人员的方法(至少对我而言)更快,并受CédricDufour解决方案的启发:

def ptInTriang(p_test, p0, p1, p2):       
     dX = p_test[0] - p0[0]
     dY = p_test[1] - p0[1]
     dX20 = p2[0] - p0[0]
     dY20 = p2[1] - p0[1]
     dX10 = p1[0] - p0[0]
     dY10 = p1[1] - p0[1]

     s_p = (dY20*dX) - (dX20*dY)
     t_p = (dX10*dY) - (dY10*dX)
     D = (dX10*dY20) - (dY10*dX20)

     if D > 0:
         return (  (s_p >= 0) and (t_p >= 0) and (s_p + t_p) <= D  )
     else:
         return (  (s_p <= 0) and (t_p <= 0) and (s_p + t_p) >= D  )

您可以使用以下方法进行测试:

X_size = 64
Y_size = 64
ax_x = np.arange(X_size).astype(np.float32)
ax_y = np.arange(Y_size).astype(np.float32)
coords=np.meshgrid(ax_x,ax_y)
points_unif = (coords[0].reshape(X_size*Y_size,),coords[1].reshape(X_size*Y_size,))
p_test = np.array([0 , 0])
p0 = np.array([22 , 8]) 
p1 = np.array([12 , 55]) 
p2 = np.array([7 , 19]) 
fig = plt.figure(dpi=300)
for i in range(0,X_size*Y_size):
    p_test[0] = points_unif[0][i]
    p_test[1] = points_unif[1][i]
    if ptInTriang(p_test, p0, p1, p2):
        plt.plot(p_test[0], p_test[1], '.g')
    else:
        plt.plot(p_test[0], p_test[1], '.r')

绘制需要花费很多时间,但该网格在0.0195319652557秒内测试,而开发人员代码为0.0844349861145秒。

最后代码评论:

# Using barycentric coordintes, any point inside can be described as:
# X = p0.x * r + p1.x * s + p2.x * t
# Y = p0.y * r + p1.y * s + p2.y * t
# with:
# r + s + t = 1  and 0 < r,s,t < 1
# then: r = 1 - s - t
# and then:
# X = p0.x * (1 - s - t) + p1.x * s + p2.x * t
# Y = p0.y * (1 - s - t) + p1.y * s + p2.y * t
#
# X = p0.x + (p1.x-p0.x) * s + (p2.x-p0.x) * t
# Y = p0.y + (p1.y-p0.y) * s + (p2.y-p0.y) * t
#
# X - p0.x = (p1.x-p0.x) * s + (p2.x-p0.x) * t
# Y - p0.y = (p1.y-p0.y) * s + (p2.y-p0.y) * t
#
# we have to solve:
#
# [ X - p0.x ] = [(p1.x-p0.x)   (p2.x-p0.x)] * [ s ]
# [ Y - p0.Y ]   [(p1.y-p0.y)   (p2.y-p0.y)]   [ t ]
#
# ---> b = A*x ; ---> x = A^-1 * b
# 
# [ s ] =   A^-1  * [ X - p0.x ]
# [ t ]             [ Y - p0.Y ]
#
# A^-1 = 1/D * adj(A)
#
# The adjugate of A:
#
# adj(A)   =   [(p2.y-p0.y)   -(p2.x-p0.x)]
#              [-(p1.y-p0.y)   (p1.x-p0.x)]
#
# The determinant of A:
#
# D = (p1.x-p0.x)*(p2.y-p0.y) - (p1.y-p0.y)*(p2.x-p0.x)
#
# Then:
#
# s_p = { (p2.y-p0.y)*(X - p0.x) - (p2.x-p0.x)*(Y - p0.Y) }
# t_p = { (p1.x-p0.x)*(Y - p0.Y) - (p1.y-p0.y)*(X - p0.x) }
#
# s = s_p / D
# t = t_p / D
#
# Recovering r:
#
# r = 1 - (s_p + t_p)/D
#
# Since we only want to know if it is insidem not the barycentric coordinate:
#
# 0 < 1 - (s_p + t_p)/D < 1
# 0 < (s_p + t_p)/D < 1
# 0 < (s_p + t_p) < D
#
# The condition is:
# if D > 0:
#     s_p > 0 and t_p > 0 and (s_p + t_p) < D
# else:
#     s_p < 0 and t_p < 0 and (s_p + t_p) > D
#
# s_p = { dY20*dX - dX20*dY }
# t_p = { dX10*dY - dY10*dX }
# D = dX10*dY20 - dY10*dX20

答案 13 :(得分:1)

存在令人讨厌的边缘条件,其中一个点恰好位于两个相邻三角形的公共边缘上。该点不能同时存在于两者中,也不能同时存在于三角形中。您需要一种任意但一致的方式来分配点。例如,通过该点绘制一条水平线。如果该线与右侧三角形的另一侧相交,则该点被视为处于三角形内部。如果交叉点在左侧,则该点在外面。

如果点所在的线是水平的,请使用上方/下方。

如果该点位于多个三角形的公共顶点上,请使用三角形,其中心点形成最小角度。

更有趣:三个点可以是直线(零度),例如(0,0) - (0,10) - (0,5)。在三角测量算法中,“耳朵”(0,10)必须被剔除,生成的“三角形”是直线的退化情况。

答案 14 :(得分:1)

这是一个高效的python解决方案,已有文档证明,其中包含三个单元测试。它具有专业级的质量,可以按模块原样直接放入您的项目中。

import unittest

###############################################################################
def point_in_triangle(point, triangle):
    """Returns True if the point is inside the triangle
    and returns False if it falls outside.
    - The argument *point* is a tuple with two elements
    containing the X,Y coordinates respectively.
    - The argument *triangle* is a tuple with three elements each
    element consisting of a tuple of X,Y coordinates.

    It works like this:
    Walk clockwise or counterclockwise around the triangle
    and project the point onto the segment we are crossing
    by using the dot product.
    Finally, check that the vector created is on the same side
    for each of the triangle's segments.
    """
    # Unpack arguments
    x, y = point
    ax, ay = triangle[0]
    bx, by = triangle[1]
    cx, cy = triangle[2]
    # Segment A to B
    side_1 = (x - bx) * (ay - by) - (ax - bx) * (y - by)
    # Segment B to C
    side_2 = (x - cx) * (by - cy) - (bx - cx) * (y - cy)
    # Segment C to A
    side_3 = (x - ax) * (cy - ay) - (cx - ax) * (y - ay)
    # All the signs must be positive or all negative
    return (side_1 < 0.0) == (side_2 < 0.0) == (side_3 < 0.0)

###############################################################################
class TestPointInTriangle(unittest.TestCase):

    triangle = ((22 , 8),
                (12 , 55),
                (7 , 19))

    def test_inside(self):
        point = (15, 20)
        self.assertTrue(point_in_triangle(point, self.triangle))

    def test_outside(self):
        point = (1, 7)
        self.assertFalse(point_in_triangle(point, self.triangle))

    def test_border_case(self):
        """If the point is exactly on one of the triangle's edges,
        we consider it is inside."""
        point = (7, 19)
        self.assertTrue(point_in_triangle(point, self.triangle))

###############################################################################
if __name__ == "__main__":
    suite = unittest.defaultTestLoader.loadTestsFromTestCase(TestPointInTriangle)
    unittest.TextTestRunner().run(suite)

上述算法还有一个可选的图形测试,以确认其有效性:

import random
from matplotlib import pyplot
from triangle_test import point_in_triangle

###############################################################################
# The area #
size_x = 64
size_y = 64

# The triangle #
triangle = ((22 , 8),
            (12 , 55),
            (7 , 19))

# Number of random points #
count_points = 10000

# Prepare the figure #
figure = pyplot.figure()
axes = figure.add_subplot(111, aspect='equal')
axes.set_title("Test the 'point_in_triangle' function")
axes.set_xlim(0, size_x)
axes.set_ylim(0, size_y)

# Plot the triangle #
from matplotlib.patches import Polygon
axes.add_patch(Polygon(triangle, linewidth=1, edgecolor='k', facecolor='none'))

# Plot the points #
for i in range(count_points):
    x = random.uniform(0, size_x)
    y = random.uniform(0, size_y)
    if point_in_triangle((x,y), triangle): pyplot.plot(x, y, '.g')
    else:                                  pyplot.plot(x, y, '.b')

# Save it #
figure.savefig("point_in_triangle.pdf")

产生以下图形:

Test the point_in_triangle function

答案 15 :(得分:1)

我只是想用一些简单的矢量数学来解释安德烈斯给出的重心坐标解,这将更容易理解。

  1. 区域A定义为由s * v02 + t * v01给出的任何向量,条件s> = 0且t> = 0.如果三角形v0,v1,v2内的任何点,它必须在里面A区。
  2. enter image description here

    1. 如果进一步限制s,则t属于[0,1]。我们得到包含s * v02 + t * v01的所有向量的区域B,条件s,t属于[0,1]。值得注意的是,区域B的低部分是三角形v0,v1,v2的镜像。如果我们能给出s和t的某些条件,进一步排除B区的低部分,就会出现问题。
    2. enter image description here

      1. 假设我们给出一个值s,t在[0,1]中变化。在下面的图中,点p位于v1v2的边缘。所有s * v02 + t * v01的向量都是通过简单的向量和沿着虚线。在v1v2和虚线交叉点p,我们有:
      2. (1-S)的 | v0v2 | / | v0v2 | = tp | v0v1 | / | v0v1 |

        我们得到1 - s = tp,然后1 = s + tp。如果有任何t> tp,其中1 < s + t在双虚线上,矢量在三角形之外,任何t <= tp,其中1> = s + t,其中在单虚线上,矢量在三角形内。

        然后,如果我们在[0,1]中给出任何s,对应的t必须满足1> = s + t,对于三角形内的向量。

        enter image description here

        最后我们得到v = s * v02 + t * v01,v是条件s内的三角形,t,s + t属于[0,1]。然后转换为点,我们有

        p - p0 = s *(p1 - p0)+ t *(p2 - p0),[0,1]中的s,t,s + t

        这与安德烈斯解决方程式系统的解决方案相同 p = p0 + s *(p1-p0)+ t *(p2-p0),s,t,s + t属于[0,1]。

答案 16 :(得分:0)

最简单的方法是它适用于所有类型的三角形,只需确定P点A,B,C点角度的角度即可。如果任何角度大于180.0度那么它在外面,如果180.0然后它在圆周上并且如果acos欺骗你并且小于180.0然后它在里面。看看理解http://math-physics-psychology.blogspot.hu/2015/01/earlish-determination-that-point-is.html

答案 17 :(得分:0)

老实说它就像Simon P Steven's answer一样简单,但是通过这种方法,您无法确定是否要包含三角形边缘上的点。

我的方法有点不同但非常基本。考虑以下三角形;

enter image description here

为了得到三角形中的点,我们必须满足3个条件

  1. ACE角度(绿色)应小于ACB角度(红色)
  2. ECB角度(蓝色)应小于ACB角度(红色)
  3. 当它们的x和y值应用于| AB |的等式时,点E和点C应具有相同的符号。线。
  4. 在此方法中,您可以完全控制单独包含或排除边缘上的点。因此,您可以检查一个点是否在三角形中,仅包括| AC |例如,边缘。

    所以我的JavaScript解决方案如下:

    &#13;
    &#13;
    function isInTriangle(t,p){
    
      function isInBorder(a,b,c,p){
        var m = (a.y - b.y) / (a.x - b.x);                     // calculate the slope
        return Math.sign(p.y - m*p.x + m*a.x - a.y) === Math.sign(c.y - m*c.x + m*a.x - a.y);
      }
      
      function findAngle(a,b,c){                               // calculate the C angle from 3 points.
        var ca = Math.hypot(c.x-a.x, c.y-a.y),                 // ca edge length
            cb = Math.hypot(c.x-b.x, c.y-b.y),                 // cb edge length
            ab = Math.hypot(a.x-b.x, a.y-b.y);                 // ab edge length
        return Math.acos((ca*ca + cb*cb - ab*ab) / (2*ca*cb)); // return the C angle
      }
    
      var pas = t.slice(1)
                 .map(tp => findAngle(p,tp,t[0])),             // find the angle between (p,t[0]) with (t[1],t[0]) & (t[2],t[0])
           ta = findAngle(t[1],t[2],t[0]);
      return pas[0] < ta && pas[1] < ta && isInBorder(t[1],t[2],t[0],p);
    }
    
    var triangle = [{x:3, y:4},{x:10, y:8},{x:6, y:10}],
          point1 = {x:3, y:9},
          point2 = {x:7, y:9};
    
    console.log(isInTriangle(triangle,point1));
    console.log(isInTriangle(triangle,point2));
    &#13;
    &#13;
    &#13;

答案 18 :(得分:0)

bool isInside( float x, float y, float x1, float y1, float x2, float y2, float x3, float y3 ) {
  float l1 = (x-x1)*(y3-y1) - (x3-x1)*(y-y1), 
    l2 = (x-x2)*(y1-y2) - (x1-x2)*(y-y2), 
    l3 = (x-x3)*(y2-y3) - (x2-x3)*(y-y3);
  return (l1>0 && l2>0  && l3>0) || (l1<0 && l2<0 && l3<0);
}

它不能比这更有效!三角形的每一边可以具有独立的位置和方向,因此有三个计算:l1,l2和l3肯定需要每次涉及2次乘法。一旦知道了l1,l2和l3,结果只是一些基本的比较和布尔运算。

答案 19 :(得分:0)

据称我在JavaScript中修改了高性能代码(下面的文章):

function pointInTriangle (p, p0, p1, p2) {
  return (((p1.y - p0.y) * (p.x - p0.x) - (p1.x - p0.x) * (p.y - p0.y)) | ((p2.y - p1.y) * (p.x - p1.x) - (p2.x - p1.x) * (p.y - p1.y)) | ((p0.y - p2.y) * (p.x - p2.x) - (p0.x - p2.x) * (p.y - p2.y))) >= 0;
}

pointInTriangle(p,p0,p1,p2) - 对于逆时针三角形

pointInTriangle(p,p0,p1,p2) - 对于顺时针三角形

查看jsFiddle(包括性能测试),还可以在单​​独的函数中进行检查 http://jsfiddle.net/z7x0udf7/3/

受此启发: http://www.phatcode.net/articles.php?id=459

答案 20 :(得分:0)

由于没有JS回答,
顺时针&amp;逆时针解决方案:

function triangleContains(ax, ay, bx, by, cx, cy, x, y) {

    let det = (bx - ax) * (cy - ay) - (by - ay) * (cx - ax)

    return  det * ((bx - ax) * (y - ay) - (by - ay) * (x - ax)) > 0 &&
            det * ((cx - bx) * (y - by) - (cy - by) * (x - bx)) > 0 &&
            det * ((ax - cx) * (y - cy) - (ay - cy) * (x - cx)) > 0 

}

编辑:有一个输入错误的计算错误(cy - ay而不是cx - ax),这是固定的。

https://jsfiddle.net/jniac/rctb3gfL/ enter image description here

我在这里使用与上述相同的方法:如果他分别在&#34;同一&#34;每条线AB,BC,CA的一侧。 triangle inclusion example

答案 21 :(得分:0)

这是确定点是在三角形的内部还是外部或在三角形的手臂上的最简单概念。 Determination of a point is inside a tringle by determinants

最简单的工作代码: `

#-*- coding: utf-8 -*-

import numpy as np

tri_points = [(1,1),(2,3),(3,1)]

def pisinTri(point,tri_points):
    Dx , Dy = point

    A,B,C = tri_points
    Ax, Ay = A
    Bx, By = B
    Cx, Cy = C

    M1 = np.array([ [Dx - Bx, Dy - By, 0],
                    [Ax - Bx, Ay - By, 0],
                    [1      , 1      , 1]
                  ])

    M2 = np.array([ [Dx - Ax, Dy - Ay, 0],
                    [Cx - Ax, Cy - Ay, 0],
                    [1      , 1      , 1]
                  ])

    M3 = np.array([ [Dx - Cx, Dy - Cy, 0],
                    [Bx - Cx, By - Cy, 0],
                    [1      , 1      , 1]
                  ])

    M1 = np.linalg.det(M1)
    M2 = np.linalg.det(M2)
    M3 = np.linalg.det(M3)
    print(M1,M2,M3)

    if(M1 == 0 or M2 == 0 or M3 ==0):
            print("Point: ",point," lies on the arms of Triangle")
    elif((M1 > 0 and M2 > 0 and M3 > 0)or(M1 < 0 and M2 < 0 and M3 < 0)):
            #if products is non 0 check if all of their sign is same
            print("Point: ",point," lies inside the Triangle")
    else:
            print("Point: ",point," lies outside the Triangle")

print("Vertices of Triangle: ",tri_points)
points = [(0,0),(1,1),(2,3),(3,1),(2,2),(4,4),(1,0),(0,4)]
for c in points:
    pisinTri(c,tri_points)

`

答案 22 :(得分:-1)

bool point2Dtriangle(double e,double f, double a,double b,double c, double g,double h,double i, double v, double w){
    /* inputs: e=point.x, f=point.y
               a=triangle.Ax, b=triangle.Bx, c=triangle.Cx 
               g=triangle.Ay, h=triangle.By, i=triangle.Cy */
    v = 1 - (f * (b - c) + h * (c - e) + i * (e - b)) / (g * (b - c) + h * (c - a) + i * (a - b));
    w = (f * (a - b) + g * (b - e) + h * (e - a)) / (g * (b - c) + h * (c - a) + i * (a - b));
    if (*v > -0.0 && *v < 1.0000001 && *w > -0.0 && *w < *v) return true;//is inside
    else return false;//is outside
    return 0;
} 

几乎完美的笛卡尔坐标从重心转换而来 在* v(x)和* w(y)内输出双倍。 在每种情况下,两个导出双精度都应该有* char,可能是:* v和* w 代码也可以用于四边形的另一个三角形。 签约时,签名只写了顺时针abcd四边形的三角形abc。

A---B
|..\\.o|  
|....\\.| 
D---C 

o点在ABC三角形内 使用第二个三角形进行测试时调用此函数CDA方向,并且*v=1-*v;*w=1-*w;之后的结果应该是正确的四边形

答案 23 :(得分:-1)

我需要点三角形检查&#34;可控环境&#34;当你完全确定三角形是顺时针方向时。所以,我采用了 Perro Azul 的jsfiddle,并按照 coproc 的建议修改了这些案例;还删除了冗余的0.5和2乘法,因为它们只是相互抵消。

http://jsfiddle.net/dog_funtom/H7D7g/

以下是Unity的等效C#代码:

public static bool IsPointInClockwiseTriangle(Vector2 p, Vector2 p0, Vector2 p1, Vector2 p2)
{
    var s = (p0.y * p2.x - p0.x * p2.y + (p2.y - p0.y) * p.x + (p0.x - p2.x) * p.y);
    var t = (p0.x * p1.y - p0.y * p1.x + (p0.y - p1.y) * p.x + (p1.x - p0.x) * p.y);

    if (s <= 0 || t <= 0)
        return false;

    var A = (-p1.y * p2.x + p0.y * (-p1.x + p2.x) + p0.x * (p1.y - p2.y) + p1.x * p2.y);

    return (s + t) < A;
}

答案 24 :(得分:-1)

        one of the easiest ways to check if the area formed by the vertices of triangle 
        (x1,y1),(x2,y2),(x3,y3) is postive or not .
        area can by calculated by the formula.
        1/ 2 [x1(y2–y3) + x2 (y3–y1) + x3 (y1–y2)]
        or python code can be written as:-


        def triangleornot(p1,p2,p3):
            return (1/ 2) [p1[0](p2[1]–p3[1]) + p2[0] (p3[1]–p1[1]) + p3[0] (p1[0]–p2[0])]