📅  最后修改于: 2023-12-03 15:10:55.294000             🧑  作者: Mango
在计算机图形学中,常常需要判断一个点是在一个矢量的左侧还是右侧。这在绘制多边形时常常用到,例如判断多边形是顺时针方向还是逆时针方向,或者判断一个点是否在多边形内部。本文介绍两种常用算法来完成这个任务。
叉积法基于向量叉积的定义,即两个向量的叉积等于它们构成的平行四边形的面积。对于向量 $\vec{a}$ 和 $\vec{b}$,它们的叉积可以表示为:
$$ \vec{a} \times \vec{b} = |\vec{a}| |\vec{b}| \sin \theta \cdot \vec{n} $$
其中 $|\vec{a}|$ 和 $|\vec{b}|$ 分别表示向量的长度,$\theta$ 表示向量夹角,$\vec{n}$ 是垂直于两个向量所在平面的单位向量。
我们可以利用叉积的性质来判断点 $p$ 是否在向量 $\vec{ab}$ 的左侧或右侧。设向量 $\vec{ap}$ 和 $\vec{ab}$ 分别为 $\vec{a}$ 和 $\vec{b}$,则它们的叉积为:
$$ \vec{a} \times \vec{p} = |\vec{a}| |\vec{p}| \sin \theta \cdot \vec{n} $$
如果 $\vec{ab}$ 是从 $\vec{a}$ 指向 $\vec{b}$,则 $\vec{a} \times \vec{p}$ 的方向会和 $\vec{n}$ 方向一致或者相反。我们可以根据 $\vec{a} \times \vec{p}$ 的符号来判断 $p$ 在向量左侧还是右侧。如果 $\vec{a} \times \vec{p}$ 的符号为正,则 $p$ 在向量的左侧;如果为负,则在向量的右侧;如果为零,则 $p$ 在向量上。
具体的实现可以参考以下代码:
#include <iostream>
#include <cmath>
using namespace std;
struct Point {
double x, y;
};
struct Vector {
double x, y;
};
double cross(Vector a, Vector b) {
return a.x * b.y - a.y * b.x;
}
int position(Point a, Point b, Point p) {
Vector ap = {p.x - a.x, p.y - a.y};
Vector ab = {b.x - a.x, b.y - a.y};
double product = cross(ap, ab);
if (product > 0) {
return 1; // left
} else if (product < 0) {
return -1; // right
} else {
return 0; // on the line
}
}
int main() {
Point a = {0, 0};
Point b = {0, 1};
Point p = {1, 1};
cout << position(a, b, p) << endl; // should be 1 (left)
p = {-1, 1};
cout << position(a, b, p) << endl; // should be -1 (right)
p = {0, 1};
cout << position(a, b, p) << endl; // should be 0 (on the line)
return 0;
}
函数 position
判断点 $p$ 在向量 $\vec{ab}$ 的位置。如果返回值为 1,则 $p$ 在左侧;如果为 -1,则在右侧;如果为 0,则在直线上。
三角形法的基本思想是构造一个以点 $p$ 和向量 $\vec{ab}$ 为边的三角形,然后计算三角形面积。如果点 $p$ 在向量的左侧,则三角形面积为正;如果在向量的右侧,则为负;如果在向量上,则为零。
具体的实现可以参考以下代码:
#include <iostream>
#include <cmath>
using namespace std;
struct Point {
double x, y;
};
double cross(Point a, Point b, Point c) {
double ax = b.x - a.x;
double ay = b.y - a.y;
double bx = c.x - a.x;
double by = c.y - a.y;
return ax * by - ay * bx;
}
int position(Point a, Point b, Point p) {
double area = cross(a, b, p) / 2;
if (area > 0) {
return 1; // left
} else if (area < 0) {
return -1; // right
} else {
return 0; // on the line
}
}
int main() {
Point a = {0, 0};
Point b = {0, 1};
Point p = {1, 1};
cout << position(a, b, p) << endl; // should be 1 (left)
p = {-1, 1};
cout << position(a, b, p) << endl; // should be -1 (right)
p = {0, 1};
cout << position(a, b, p) << endl; // should be 0 (on the line)
return 0;
}
函数 cross
计算三角形面积。函数 position
判断点 $p$ 在向量 $\vec{ab}$ 的位置。如果返回值为 1,则 $p$ 在左侧;如果为 -1,则在右侧;如果为 0,则在直线上。
叉积法和三角形法都是比较简单有效的算法,可以用于判断点和线/矢量的位置关系。两种算法的时间复杂度都为 $O(1)$,没有额外的空间复杂度。在实际应用中,需要根据具体问题来选用合适的算法。