2014软专高级程序语言T2(用向量叉乘判断点与三角形的位置关系)

编写程序,输入A,B,C,D四个点的坐标,假设A,B,C三点可以构成一个三角形,判断D点是否落在三角形内。

解题思路:

假设三角形的三个点按照顺时针(或者逆时针)顺序是A,B,C。对于某一点P,求出三个向量PA,PB,PC, 然后计算以下三个叉乘(^表示叉乘符号):

t1 = PA^PB,

t2 = PB^PC,

t3 = PC^PA,

如果t1,t2,t3同号(同正或同负),那么P在三角形内部,否则在外部。

(更多在代码后)

本题代码如下:

#include<stdio.h>
#include<math.h>

typedef struct Point
{
	double x,y;
}Point;

Point decPoint(Point p1,Point p2)//计算p1-p2向量
{
	Point ret;
	ret.x=p1.x-p2.x;
	ret.y=p1.y-p2.y;
	return ret;
}

double multiPoint(Point p1,Point p2)//计算向量叉乘 
{
	return(p1.x*p2.y-p1.y*p2.x);
}

double crossByThreePoint(Point A,Point B,Point C)//叉乘(A
{
	return multiPoint(decPoint(B,A),decPoint(C,A));
}

int sign(double x)//对x进行正负判断返回标志符
{
	double eps=1e-8;
	if(fabs(x)<eps) return 0;
	if(x>0) return 1;
	return -1;
}

int main()
{
	int i,j,k;
	Point pts[4];
	double x,y;
	int sgn[3];
	for(int i=0;i<4;i++)//将A,B,C,D四个点存放在数组中 
	{
		scanf("%lf%lf",&pts[i].x,&pts[i].y);
	}
	sgn[0]=sign(crossByThreePoint(pts[0],pts[1],pts[2]));//D点都参与形成向量
	sgn[1]=sign(crossByThreePoint(pts[1],pts[2],pts[3]));
	sgn[2]=sign(crossByThreePoint(pts[2],pts[0],pts[3]));
	if(sgn[0]==0||sgn[1]==0||sgn[2]==0)//若D在三条边上 
	{
		printf("Point D is on the triangle!\n");
	}
	else if(sgn[0]==sgn[1]&&sgn[1]==sgn[2])//若D在三角形内部
	{
		printf("Point D is on the triangle!\n");
	}
	else
	{
		printf("Point D is out of the triangle!\n");
	}
	return 0;
}

https://blog.csdn.net/zxj1988/article/details/6260576

数学知识:

向量(Vector
在几乎所有的几何问题中,向量(有时也称矢量)是一个基本点。向量的定义包含方向和一个数(长度)。在二维空间中,一个向量可以用一对xy来表示。例如由点(1,3)到(5,1的向量可以用(4,-2)来表示。这里大家要特别注意,我这样说并不代表向量定义了起点和终点。向量仅仅定义方向和长度。

向量加法
向量也支持各种数学运算。最简单的就是加法。我们可以对两个向量相加,得到的仍然是一个向量。我们有:
    V1x1, y1+V2x2, y2=V3(x1+x2, y1+y2)
下图表示了四个向量相加。注意就像普通的加法一样,相加的次序对结果没有影响(满足交换律),减法也是一样的。
 

点乘(Dot Product
如果说加法是凭直觉就可以知道的,另外还有一些运算就不是那么明显的,比如点乘和叉乘。
点乘比较简单,是相应元素的乘积的和:
    V1( x1, y1)   V2(x2, y2) = x1*x2 + y1*y2
注意结果不是一个向量,而是一个标量(Scalar)。点乘有什么用呢,我们有:
    A   B = |A||B|Cos(θ)
θ
是向量A和向量B见的
夹角。这里|A|我们称为向量A的模(norm),也就是A的长度, 在二维空间中就是|A| = sqrt(x2+y2)。这样我们就和容易计算两条线的夹角
    Cos(θ) = A   B /(|A||B|)

当然你知道要用一下反余弦函数acos()啦。(回忆一下cos(90)=0 cos(0) = 1还是有好处的,希望你没有忘记。)这可以告诉我们如果点乘的结果,简称点积,为0的话就表示这两个向量垂直。当两向量平行时,点积有最大值
另外,点乘运算不仅限于2维空间,他可以推广到任意维空间。(译注:不少人对量子力学中的高维空间无法理解,其实如果你不要试图在视觉上想象高维空间,而仅仅把它看成三维空间在数学上的推广,那么就好理解了)


叉乘(cross product
相对于点乘,叉乘可能更有用吧。2维空间中的叉乘是:
    V1(x1, y1) X V2(x2, y2) = x1y2 – y1x2
看起来像个标量,事实上叉乘的结果是个向量,方向在z轴上。上述结果是它的模。在二维空间里,让我们暂时忽略它的方向,将结果看成一个向量,那么这个结果类似于上述的点积,我们有:
    A x B = |A||B|Sin(θ)
然而角度 θ和上面点乘的角度有一点点不同,他是有正负的,是指从AB的角度。下图中 θ为负。
另外还有一个有用的特征那就是叉积的绝对值就是AB为两边说形成的平行四边形的面积。也就是AB所包围三角形面积的两倍。在计算面积时,我们要经常用到叉积。
(译注:三维及以上的叉乘参看维基:http://en.wikipedia.org/wiki/Cross_product


-线距离
找出一个点和一条线间的距离是经常遇见的几何问题之一。假设给出三个点,ABC,你想找出点C到点AB定出的直线间距离。第一步是找出AB的向量ABAC的向量AC,现在我们用该两向量的叉积除以|AB|,这就是我们要找的的距离了(下图中的红线)。
    d = (AB x AC)/|AB|


如果你有基础的高中几何知识,你就知道原因了。上一节我们知道(AB X AC)/2是三角形ABC的面积,这个三角形的底是|AB|,高就是CAB的距离。有时叉积得到的是一个负值,这种情况下距离就是上述结果的绝对值。
当我们要找点到线段的距离时,情况变得稍稍复杂一些。这时线段与点的最短距离可能是点到线段的某一端点,而不是点到直线的垂线。例如上图中点C到线段AB的最短距离应该是线段BC。我们有集中不同的方法来判断这种特殊情况。第一种情况是计算点积AB Bc来判定两线段间夹角。如果点积大于等于零,那么表示ABBC是在-9090度间,也就是说CAB的垂线在AB外,那么AB上到C距离最近的点就是B。同样,如果BAAC大于等于零,那么点A就是距离C最近的点。如果两者均小于零,那么距离最近的点就在线段AB中的莫一点。

https://www.cnblogs.com/TenosDoIt/p/4024413.html

二维平面上判断点是否在三角形内

最近在项目中碰到的这个问题,在此记录一下。已知三角形的三个顶点坐标,判断某个点是否在三角形中(在三角形的边上,我们也视作在三角形中),本文给出了三种方法。

2014软专高级程序语言T2(用向量叉乘判断点与三角形的位置关系)

算法1

利用面积法,如上图所示,如果点P在三角形ABC的内部,则三个小三角形PAB, PBC, PAC的面积之和 = ABC的面积,反之则不相等。

已知三角形的三个顶点坐标求其面积,可以根据向量的叉乘,参考here

该算法详见后面代码中的函数:IsPointInTriangle1

算法2

首先看一下这个问题,如何判断某两个点在某条直线的同一侧(代码中函数:IsPointAtSameSideOfLine)?

2014软专高级程序语言T2(用向量叉乘判断点与三角形的位置关系)

根据向量的叉乘以及右手螺旋定则,AB^AM (^表示叉乘,这里向量省略了字母上面的箭头符号)的方向为向外指出屏幕,AB^AN也是向外指出屏幕,但AB^AO的方向是向内指向屏幕,因此M,N在直线AB的同侧,M ,O在直线AB的两侧。实际计算时,只需要考虑叉积的数值正负

假设以上各点坐标为A(0,0), B(4,0), M(1,2), N(3,4), O(3,-4), 则:

AB^AM = (4,0)^(1,2) = 4*2 - 0*1 = 8

AB^AN = (4,0)^(3,4) = 4*4 – 0*3 = 16

AB^AO = (4,0)^(3,-4) = 4*-4 – 0*3 = –16

由上面的数值可知,可以根据数值的正负判断叉乘后向量的方向。即,如果叉积AB^AM 和 AB^AN的结果同号,那么M,N两点就在直线的同侧,否则不在同一侧。特殊地,如果点M在直线AB上,则AB^AM的值为0。(如果是在三维坐标系中,求出的叉积是一个向量,可以根据两个向量的点积结果正负来判断两个向量的是否指向同一侧)                                                         本文地址

以上的问题解决了,就很容易的用来判断某个点是否在三角形内,如果P在三角形ABC内部,则满足以下三个条件:P,A在BC的同侧、P,B在AC的同侧、PC在AB的同侧。某一个不满足则表示P不在三角形内部。

该算法详见后面代码中的函数:IsPointInTriangle2

算法3

该方法也用到了向量。对于三角形ABC和一点P,可以有如下的向量表示:

2014软专高级程序语言T2(用向量叉乘判断点与三角形的位置关系)

p点在三角形内部的充分必要条件是:1 >= u >= 0,   1 >= v >= 0,   u+v <= 1。

已知A,B,C,P四个点的坐标,可以求出u,v,把上面的式子分别点乘向量AC和向量AB

2014软专高级程序语言T2(用向量叉乘判断点与三角形的位置关系)

解方程得到:

2014软专高级程序语言T2(用向量叉乘判断点与三角形的位置关系)

解出u,v后只需要看他们是否满足“1 >= u >= 0, 1 >= v >= 0, u+v <= 1”,如满足,则,p 在三角形内。

(u = 0时,p在AB上, v = 0时,p在AC上,两者均为0时,p和A重合)

该算法详见后面代码中的函数:IsPointInTriangle3

算法4

该算法和算法2类似,可以看作是对算法2的简化,也是用到向量的叉乘。假设三角形的三个点按照顺时针(或者逆时针)顺序是A,B,C。对于某一点P,求出三个向量PA,PB,PC, 然后计算以下三个叉乘(^表示叉乘符号):

t1 = PA^PB,

t2 = PB^PC,

t3 = PC^PA,

如果t1,t2,t3同号(同正或同负),那么P在三角形内部,否则在外部。

该算法详见后面代码中的函数:IsPointInTriangle4


经过测试,算法4最快,算法3次之,接着算法2,算法1最慢。直观的从计算量上来看,也是算法4的计算量最少。

以下是代码,定义了两个类:二维向量类 和 三角形类

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

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

#include<string>

 #include<sstream>

 #include<fstream>

 #include<cstdio>

 #include<cstring>

 #include<iostream>

 #include<vector>

 #include<stack>

 #include<queue>

 #include<list>

 #include<algorithm>

 #include<ctime>

 #include<unordered_map>

 #include<map>

 #include<typeinfo>

 #include<cmath>

 #include<ctime>

 #include<climits>

 using namespace std;

//类定义:二维向量

class Vector2d

{

public:

    double x_;

    double y_;

public:

    Vector2d(double x, double y):x_(x), y_(y){}

    Vector2d():x_(0), y_(0){}

    //二维向量叉乘, 叉乘的结果其实是向量,方向垂直于两个向量组成的平面,这里我们只需要其大小和方向

    double CrossProduct(const Vector2d vec)

    {

        return x_*vec.y_ - y_*vec.x_;

    }

    //二维向量点积

    double DotProduct(const Vector2d vec)

    {

        return x_ * vec.x_ + y_ * vec.y_;

    }

    //二维向量减法

    Vector2d Minus(const Vector2d vec) const

    {

        return Vector2d(x_ - vec.x_, y_ - vec.y_);

    }

    //判断点M,N是否在直线AB的同一侧

    static bool IsPointAtSameSideOfLine(const Vector2d &pointM, const Vector2d &pointN,

                                        const Vector2d &pointA, const Vector2d &pointB)

    {

        Vector2d AB = pointB.Minus(pointA);

        Vector2d AM = pointM.Minus(pointA);

        Vector2d AN = pointN.Minus(pointA);

        //等于0时表示某个点在直线上

        return AB.CrossProduct(AM) * AB.CrossProduct(AN) >= 0;

    }

};

//三角形类

class Triangle

{

private:

    Vector2d pointA_, pointB_, pointC_;

public:

    Triangle(Vector2d point1, Vector2d point2, Vector2d point3)

        :pointA_(point1), pointB_(point2), pointC_(point3)

    {

        //todo 判断三点是否共线

    }

    //计算三角形面积

    double ComputeTriangleArea()

    {

        //依据两个向量的叉乘来计算,可参考向量的点乘与叉乘_zxj1988的专栏-CSDN博客_向量叉乘和点乘

        Vector2d AB = pointB_.Minus(pointA_);

        Vector2d BC = pointC_.Minus(pointB_);

        return fabs(AB.CrossProduct(BC) / 2.0);

    }

    bool IsPointInTriangle1(const Vector2d pointP)

    {

        double area_ABC = ComputeTriangleArea();

        double area_PAB = Triangle(pointP, pointA_, pointB_).ComputeTriangleArea();

        double area_PAC = Triangle(pointP, pointA_, pointC_).ComputeTriangleArea();

        double area_PBC = Triangle(pointP, pointB_, pointC_).ComputeTriangleArea();

        if(fabs(area_PAB + area_PBC + area_PAC - area_ABC) < 0.000001)

            return true;

        else return false;

    }

    bool IsPointInTriangle2(const Vector2d pointP)

    {

        return Vector2d::IsPointAtSameSideOfLine(pointP, pointA_, pointB_, pointC_) &&

            Vector2d::IsPointAtSameSideOfLine(pointP, pointB_, pointA_, pointC_) &&

            Vector2d::IsPointAtSameSideOfLine(pointP, pointC_, pointA_, pointB_);

    }

    bool IsPointInTriangle3(const Vector2d pointP)

    {

        Vector2d AB = pointB_.Minus(pointA_);

        Vector2d AC = pointC_.Minus(pointA_);

        Vector2d AP = pointP.Minus(pointA_);

        double dot_ac_ac = AC.DotProduct(AC);

        double dot_ac_ab = AC.DotProduct(AB);

        double dot_ac_ap = AC.DotProduct(AP);

        double dot_ab_ab = AB.DotProduct(AB);

        double dot_ab_ap = AB.DotProduct(AP);

        double tmp = 1.0 / (dot_ac_ac * dot_ab_ab - dot_ac_ab * dot_ac_ab);

         

        double u = (dot_ab_ab * dot_ac_ap - dot_ac_ab * dot_ab_ap) * tmp;

        if(u < 0 || u > 1)

            return false;

        double v = (dot_ac_ac * dot_ab_ap - dot_ac_ab * dot_ac_ap) * tmp;

        if(v < 0 || v > 1)

            return false;

        return u + v <= 1;

    }

    bool IsPointInTriangle4(const Vector2d pointP)

    {

        Vector2d PA = pointA_.Minus(pointP);

        Vector2d PB = pointB_.Minus(pointP);

        Vector2d PC = pointC_.Minus(pointP);

        double t1 = PA.CrossProduct(PB);

        double t2 = PB.CrossProduct(PC);

        double t3 = PC.CrossProduct(PA);

        return t1*t2 >= 0 && t1*t3 >= 0;

    }

};

int main()

{

    Triangle tri(Vector2d(0,0), Vector2d(6,6), Vector2d(12,0));

    srand(time(0));

    for(int i = 0; i < 20; ++i)

    {

         Vector2d point((rand()*1.0 / RAND_MAX) * 12, (rand()*1.0 / RAND_MAX) * 6);

         cout<<point.x_<<" "<<point.y_<<":     ";

         cout<<tri.IsPointInTriangle1(point)<<" ";

         cout<<tri.IsPointInTriangle2(point)<<" ";

         cout<<tri.IsPointInTriangle3(point)<<" ";

         cout<<tri.IsPointInTriangle4(point)<<endl;

    }

}

上一篇:HTML5期末大作业:服装购物网站设计——粉色服装购物商城(4页) 服装购物商城网页设计作品 大学生购物专题网页设计作业模板 商店静态HTML网页模板下载


下一篇:西南交通大学840数据结构编程大题-2014年