计算机图形学:多边形交点及实现
一、原理推导和公式说明
1.1 两条线段求交的数学原理
设有两条线段 A B AB AB 和 C D CD CD,它们的端点坐标分别为 A ( x a , y a ) A(x_a, y_a) A(xa,ya), B ( x b , y b ) B(x_b, y_b) B(xb,yb) 和 C ( x c , y c ) C(x_c, y_c) C(xc,yc), D ( x d , y d ) D(x_d, y_d) D(xd,yd)。要找出这两条线段是否相交,我们可以使用参数方程和行列式的概念。
首先,
-
线段 A B AB AB 的方程:
{ x = x a + t ( x b − x a ) y = y a + t ( y b − y a ) \begin{cases} x = x_a + t(x_b - x_a) \\ y = y_a + t(y_b - y_a) \end{cases} {x=xa+t(xb−xa)y=ya+t(yb−ya)
其中, t t t在 [ 0 , 1 ] [0, 1] [0,1] 范围内变化,表示从点 A A A 到点 B B B 的相对位置。 -
线段 C D CD CD 的方程:
{ x = x c + u ( x d − x c ) y = y c + u ( y d − y c ) \begin{cases} x = x_c + u(x_d - x_c) \\ y = y_c + u(y_d - y_c) \end{cases} {x=xc+u(xd−xc)y=yc+u(yd−yc)
其中, u u u 在 [ 0 , 1 ] [0, 1] [0,1] 范围内变化,表示从点 C C C 到点 D D D 的相对位置。
要使两个方程组表示同一个点,则交点的参数应满足:
{
x
a
+
t
(
x
b
−
x
a
)
=
x
c
+
u
(
x
d
−
x
c
)
y
a
+
t
(
y
b
−
y
a
)
=
y
c
+
u
(
y
d
−
y
c
)
\begin{cases} x_a + t(x_b - x_a) = x_c + u(x_d - x_c) \\ y_a + t(y_b - y_a) = y_c + u(y_d - y_c) \end{cases}
{xa+t(xb−xa)=xc+u(xd−xc)ya+t(yb−ya)=yc+u(yd−yc)
整理后得
{
t
(
x
b
−
x
a
)
−
u
(
x
d
−
x
c
)
=
x
c
−
x
a
t
(
y
b
−
y
a
)
−
u
(
y
d
−
y
c
)
=
y
c
−
y
a
\begin{cases} t(x_b-x_a)-u(x_d-x_c)=x_c-x_a \\ t(y_b-y_a)-u(y_d-y_c)=y_c-y_a \end{cases}
{t(xb−xa)−u(xd−xc)=xc−xat(yb−ya)−u(yd−yc)=yc−ya
因此,对于行列式
d
=
∣
x
b
−
x
a
x
d
−
x
c
y
b
−
y
a
y
d
−
y
c
∣
d = \begin{vmatrix} x_b - x_a & x_d - x_c \\ y_b - y_a & y_d - y_c \end{vmatrix}
d=
xb−xayb−yaxd−xcyd−yc
有以下结论:
-
若 d = 0 d=0 d=0,则 A B AB AB和 C D CD CD重合或者平行。
-
若 d ≠ 0 d \neq 0 d=0,则线段 A B AB AB和 C D CD CD不平行也不重合,有可能相交。求出两个线段所在的两条直线上交点对应的两个参数 t , u t,u t,u值:
t = ∣ x c − x a x d − x a y c − y a y d − y a ∣ d u = ∣ x b − x a x b − x c y b − y a y b − y c ∣ d t = \frac{\begin{vmatrix} x_c - x_a & x_d - x_a \\ y_c - y_a & y_d - y_a \end{vmatrix}}{\text{d}}\\ u = \frac{\begin{vmatrix} x_b - x_a & x_b - x_c \\ y_b - y_a & y_b - y_c \end{vmatrix}}{\text{d}} t=d xc−xayc−yaxd−xayd−ya u=d xb−xayb−yaxb−xcyb−yc
如果 t t t和 u u u都在 [ 0 , 1 ] [0, 1] [0,1]区间内,那么交点位于两条线段上,线段相交;否则,交点在两线段或其中某一条线段的延长线上,这时仍然认为是两线段不相交。
1.2 推导过程
- 将线段表示为参数方程。
- 构造参数 t t t和 u u u的方程组。
- 通过行列式方法消去参数 t t t和 u u u,得到交点的坐标。
- 检查 t t t和 u u u是否在 [ 0 , 1 ] [0, 1] [0,1] 区间内,以确定交点是否在两条线段上。
这就是两条线段求交的数学原理和公式推导。如果行列式 d d d为零,或者计算得到的 t t t和 u u u不在 [0, 1] 区间内,那么线段不相交。
二、代码设计与实现
2.1 点结构和多边形类定义
首先,定义了Point
结构体来表示二维空间中的点,包含x
和y
两个坐标成员。
struct Point {
double x, y;
};
Polygon
类用于表示多边形,它包含一个vertices
向量,存储多边形的顶点。类中还包含getEdge
方法,用于获取多边形的某条边,以及getNumberOfEdges
方法,返回多边形边的数量。
class Polygon {
public:
std::vector<Point> vertices;
Polygon(const std::vector<Point> &points) : vertices(points) {}
std::pair<Point, Point> getEdge(int i) const {
int nextIndex = (i + 1) % vertices.size();
return {vertices[i], vertices[nextIndex]};
}
int getNumberOfEdges() const { return vertices.size(); }
};
2.2 线段交点的计算
两条线段的交点可以通过解线性方程组得到。首先,通过计算行列式来判断线段是否平行或共线:
d
=
(
B
.
x
−
A
.
x
)
⋅
(
C
.
y
−
D
.
y
)
−
(
B
.
y
−
A
.
y
)
⋅
(
C
.
x
−
D
.
x
)
d = (B.x - A.x) \cdot (C.y - D.y) - (B.y - A.y) \cdot (C.x - D.x)
d=(B.x−A.x)⋅(C.y−D.y)−(B.y−A.y)⋅(C.x−D.x)
如果d
的绝对值小于一个很小的阈值(例如1e-6
),则认为线段平行或共线,没有交点。
如果线段不平行,可以通过求解以下方程得到交点参数t
和u
:
t
=
(
C
.
x
−
A
.
x
)
⋅
(
C
.
y
−
D
.
y
)
−
(
C
.
y
−
A
.
y
)
⋅
(
C
.
x
−
D
.
x
)
d
u
=
(
B
.
x
−
A
.
x
)
⋅
(
C
.
y
−
A
.
y
)
−
(
B
.
y
−
A
.
y
)
⋅
(
C
.
x
−
A
.
x
)
d
t = \frac{(C.x - A.x) \cdot (C.y - D.y) - (C.y - A.y) \cdot (C.x - D.x)}{d}\\ u = \frac{(B.x - A.x) \cdot (C.y - A.y) - (B.y - A.y) \cdot (C.x - A.x)}{d}
t=d(C.x−A.x)⋅(C.y−D.y)−(C.y−A.y)⋅(C.x−D.x)u=d(B.x−A.x)⋅(C.y−A.y)−(B.y−A.y)⋅(C.x−A.x)
如果t
和u
都在区间[0, 1]内,则交点在线段上:
intersection
.
x
=
A
.
x
+
t
⋅
(
B
.
x
−
A
.
x
)
intersection
.
y
=
A
.
y
+
t
⋅
(
B
.
y
−
A
.
y
)
\text{intersection}.x = A.x + t \cdot (B.x - A.x) \\ \text{intersection}.y = A.y + t \cdot (B.y - A.y)
intersection.x=A.x+t⋅(B.x−A.x)intersection.y=A.y+t⋅(B.y−A.y)
bool getLineSegmentIntersection(const Point &A, const Point &B, const Point &C,
const Point &D, Point &intersection) {
double d = (B.x - A.x) * (C.y - D.y) - (B.y - A.y) * (C.x - D.x);
if (std::abs(d) < 1e-6) {
return false;
}
double t = ((C.x - A.x) * (C.y - D.y) - (C.y - A.y) * (C.x - D.x)) / d;
double u = ((B.x - A.x) * (C.y - A.y) - (B.y - A.y) * (C.x - A.x)) / d;
if (t >= 0.0 && t <= 1.0 && u >= 0.0 && u <= 1.0) {
intersection.x = A.x + t * (B.x - A.x);
intersection.y = A.y + t * (B.y - A.y);
return true;
}
return false;
}
2.3 多边形交点的计算
对于两个多边形,程序遍历每条边的组合,使用getLineSegmentIntersection
函数检查是否有交点。
std::vector<Point> getPolygonIntersections(const Polygon &poly_a,
const Polygon &poly_b) {
std::vector<Point> intersections;
for (int i = 0; i < poly_a.getNumberOfEdges(); ++i) {
for (int j = 0; j < poly_b.getNumberOfEdges(); ++j) {
Point intersection;
if (getLineSegmentIntersection(
poly_a.getEdge(i).first, poly_a.getEdge(i).second,
poly_b.getEdge(j).first, poly_b.getEdge(j).second, intersection)) {
intersections.push_back(intersection);
}
}
}
return intersections;
}
三、完整代码示例
主函数创建了两个多边形poly_a
和poly_b
,并调用getPolygonIntersections
函数来计算它们的交点。然后,输出交点信息。
#include <algorithm>
#include <iostream>
#include <vector>
// 定义一个表示二维点的结构体
struct Point {
double x, y;
};
// 定义一个表示多边形的类
class Polygon {
public:
std::vector<Point> vertices; // 存储多边形顶点的向量
// 构造函数,接受一个点的向量来初始化多边形的顶点
Polygon(const std::vector<Point> &points) : vertices(points) {}
// 获取多边形的第i条边,返回一个包含两个端点的pair
std::pair<Point, Point> getEdge(int i) const {
int nextIndex = (i + 1) % vertices.size(); // 计算下一条边的索引,实现循环
return {vertices[i], vertices[nextIndex]}; // 返回当前边的两个端点
}
// 获取多边形的边数
int getNumberOfEdges() const { return vertices.size(); }
};
// 计算两条线段AB和CD的交点
bool getLineSegmentIntersection(const Point &A, const Point &B, const Point &C,
const Point &D, Point &intersection) {
double d = (B.x - A.x) * (C.y - D.y) - (B.y - A.y) * (C.x - D.x);
if (std::abs(d) < 1e-6) {
// 如果行列式的绝对值很小,则认为线段平行或共线
return false;
}
// 计算交点参数t和u
double t = ((C.x - A.x) * (C.y - D.y) - (C.y - A.y) * (C.x - D.x)) / d;
double u = ((B.x - A.x) * (C.y - A.y) - (B.y - A.y) * (C.x - A.x)) / d;
// 检查交点是否在两条线段上
if (t >= 0.0 && t <= 1.0 && u >= 0.0 && u <= 1.0) {
// 如果t和u都在[0, 1]区间内,则交点在线段上
intersection.x = A.x + t * (B.x - A.x);
intersection.y = A.y + t * (B.y - A.y);
return true;
}
// 如果交点不在线段上,则返回false
return false;
}
// 计算两个多边形的所有交点
std::vector<Point> getPolygonIntersections(const Polygon &poly_a,
const Polygon &poly_b) {
std::vector<Point> intersections;
for (int i = 0; i < poly_a.getNumberOfEdges(); ++i) {
for (int j = 0; j < poly_b.getNumberOfEdges(); ++j) {
Point intersection;
if (getLineSegmentIntersection(poly_a.getEdge(i).first,
poly_a.getEdge(i).second,
poly_b.getEdge(j).first,
poly_b.getEdge(j).second, intersection)) {
intersections.push_back(intersection); // 将交点添加到结果中
}
}
}
return intersections; // 返回所有交点
}
// 主函数
int main() {
// 定义两个多边形
Polygon poly_a({{0, 0}, {0, 4}, {-4, 4}, {-4, 0}});
Polygon poly_b({{-2, 2}, {-2, -2}, {2, -2}, {2, 2}});
// 计算两个多边形的交点
auto intersections = getPolygonIntersections(poly_a, poly_b);
// 输出交点信息
if (intersections.empty()) {
std::cout << "No intersection" << std::endl;
} else {
std::cout << "Intersections:" << std::endl;
for (const auto &pt : intersections) {
std::cout << "(" << pt.x << ", " << pt.y << ")" << std::endl;
}
}
return 0;
}
原文地址:https://blog.csdn.net/jianmo1993/article/details/144063056
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!