Algorithm: 如何判断一个点是否在一个三角形内

昨日因为机缘巧合,做了一道阿里的实习生编程题。题目很有趣,其中涉及到了如何判断一个点是否在一个三角形内。


其中,判断这个问题最简单的方法是面积法。(图片来源:http://www.cnblogs.com/TenosDoIt/p/4024413.html)




如果一个点在三角形内,其与三角形的三个点构成的三个子三角形的面积等于大三角形的面积。否则,大于大三角形的面积。


所以,这个问题就转化成如何在知道三角形的三个点的情况下,求这个三角形的面积的问题了。


这个问题,比较好理解的方法是使用向量法:先求出这个三角形的对应的平行四边形的面积。然后这个面积的1/2就是三角形的面积了。


先随意选择两个点,如B、C通过其坐标相减得向量(B,C)。记得谁减另一个就是指向谁。然后求出其中一个点和剩下一个点的向量。这两个向量的叉乘的便是平行四边形的面积。除以2就是三角形的面积。(注意这里是叉乘 (cross product),而非内积(dot product))


代码如下:


private static final double ABS_DOUBLE_0 = 0.0001;
	
private static double getTriangleArea(Point p0, Point p1, Point p2) {
	Point ab, bc;
	ab = new Point(p1.x - p0.x, p1.y - p0.y);
	bc = new Point(p2.x - p1.x, p2.y - p1.y);
	return Math.abs((ab.x * bc.y - ab.y * bc.x) / 2.0);
}
	
private static boolean isInTriangle(Point a, Point b, Point c, Point d) {
	double sabc, sadb, sbdc, sadc;
	sabc = getTriangleArea(a, b, c);
	sadb = getTriangleArea(a, d, b);
	sbdc = getTriangleArea(b, d, c);
	sadc = getTriangleArea(a, d, c);
		
	double sumSuqar = sadb + sbdc + sadc;
		
	if (-ABS_DOUBLE_0 < (sabc - sumSuqar) && (sabc - sumSuqar) < ABS_DOUBLE_0) {
		return true;
	} else {
		return false;
	}
}

向量之间的积分为两种:叉乘和点乘。叉乘求面积,点乘求投影。这是两者的意义。而且,叉乘理论得到的是一个向量,而点乘得到的是一个标量。


公式如下(参考链接:https://zh.wikipedia.org/wiki/%E5%90%91%E9%87%8F%E7%A7%AF; https://zh.wikipedia.org/wiki/%E6%95%B0%E9%87%8F%E7%A7%AF):




所以,当它是平行四边形的两边的时候,它的模就是平行四边形的面积。


接下来看向量的点乘,就是求一个向量在另一个向量上的投影:







  • 3
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
一种高效的算法是扫描线算法。具体步骤如下: 1. 找出三角形的最小包围矩形,确定扫描线的范围。 2. 从最小包围矩形的上边界开始,向下沿着扫描线扫描。 3. 对于每条扫描线,计算出与三角形的交,并将这些交按照x坐标排序。 4. 根据相邻两个交之间的距离和扫描线间距,计算出每个像素的y坐标,并将其加入到像素集合中。 下面是C++代码实现: ```c++ #include <iostream> #include <vector> #include <algorithm> using namespace std; struct Point { int x; int y; }; void drawLine(Point p1, Point p2, vector<Point>& points) { int x1 = p1.x; int y1 = p1.y; int x2 = p2.x; int y2 = p2.y; int dx = abs(x2 - x1); int dy = abs(y2 - y1); int sx = x1 < x2 ? 1 : -1; int sy = y1 < y2 ? 1 : -1; int err = dx - dy; while (true) { points.push_back({x1, y1}); if (x1 == x2 && y1 == y2) break; int e2 = 2 * err; if (e2 > -dy) { err -= dy; x1 += sx; } if (e2 < dx) { err += dx; y1 += sy; } } } void drawTriangle(Point p1, Point p2, Point p3, vector<Point>& points) { drawLine(p1, p2, points); drawLine(p2, p3, points); drawLine(p3, p1, points); } void getBoundingBox(Point p1, Point p2, Point p3, Point& minP, Point& maxP) { minP = {min({p1.x, p2.x, p3.x}), min({p1.y, p2.y, p3.y})}; maxP = {max({p1.x, p2.x, p3.x}), max({p1.y, p2.y, p3.y})}; } void scanTriangle(Point p1, Point p2, Point p3, vector<Point>& points) { Point minP, maxP; getBoundingBox(p1, p2, p3, minP, maxP); for (int y = minP.y; y <= maxP.y; y++) { vector<double> intersections; if (y >= p1.y && y < p2.y) { intersections.push_back(p1.x + (double)(p2.x - p1.x) * (double)(y - p1.y) / (double)(p2.y - p1.y)); } if (y >= p1.y && y < p3.y) { intersections.push_back(p1.x + (double)(p3.x - p1.x) * (double)(y - p1.y) / (double)(p3.y - p1.y)); } if (y >= p2.y && y < p3.y) { intersections.push_back(p2.x + (double)(p3.x - p2.x) * (double)(y - p2.y) / (double)(p3.y - p2.y)); } sort(intersections.begin(), intersections.end()); for (int i = 0; i < (int)intersections.size() - 1; i++) { int x1 = (int)intersections[i]; int x2 = (int)intersections[i + 1]; for (int x = x1; x <= x2; x++) { points.push_back({x, y}); } } } } int main() { Point p1 = {0, 0}; Point p2 = {5, 10}; Point p3 = {10, 5}; vector<Point> points; scanTriangle(p1, p2, p3, points); for (auto p : points) { cout << "(" << p.x << ", " << p.y << ")" << endl; } return 0; } ``` 在这个例子中,我们定义了一个getBoundingBox函数来获取三角形的最小包围矩形,定义了一个scanTriangle函数来实现扫描线算法,将计算出来的像素加入到points向量中。在scanTriangle函数中,对于每条扫描线,我们计算出与三角形的交,并将这些交按照x坐标排序,然后根据相邻两个交之间的距离和扫描线间距,计算出每个像素的y坐标,并将其加入到像素集合中。最后,在main函数中,我们使用scanTriangle函数来画出三角形,并输出所有的像素坐标。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值