📜  检查点是矢量的左侧还是右侧 - C++ (1)

📅  最后修改于: 2023-12-03 15:10:55.294000             🧑  作者: Mango

检查点是矢量的左侧还是右侧 - C++

在计算机图形学中,常常需要判断一个点是在一个矢量的左侧还是右侧。这在绘制多边形时常常用到,例如判断多边形是顺时针方向还是逆时针方向,或者判断一个点是否在多边形内部。本文介绍两种常用算法来完成这个任务。

叉积法

叉积法基于向量叉积的定义,即两个向量的叉积等于它们构成的平行四边形的面积。对于向量 $\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)$,没有额外的空间复杂度。在实际应用中,需要根据具体问题来选用合适的算法。