二维图形学是计算机图形学中的重要分支,主要研究平面内的点、线、形状等基本元素及其相互关系与变换。在本文中,将会详细介绍二维图形学中的点、线和形状,并提供相应的代码实现。
点(Point)
点的定义
点(Point)是平面上没有大小的几何图形,只有位置信息,通常用坐标表示。在二维坐标系中,一个点可以用一对有序数表示,比如 (�,�)(x,y),其中 �x 表示横坐标,�y 表示纵坐标。
点的表示
在程序中,我们通常用结构体来表示一个点,例如下面这段 C++ 代码:
c++Copy Code
struct Point {
double x;
double y;
};
在该代码中,我们定义了一个名为 Point
的结构体,它包含两个成员变量 x
和 y
,分别表示点的横坐标和纵坐标。
点的操作
- 构造函数
构造函数用于创建一个点对象。在 C++ 中,可以通过函数重载的方式实现多种构造函数,例如:
c++Copy Code
struct Point {
double x;
double y;
// 默认构造函数
Point() : x(0), y(0) {}
// 初始化构造函数
Point(double x, double y) : x(x), y(y) {}
};
上述代码中,我们定义了两个构造函数:默认构造函数和初始化构造函数。默认构造函数用于创建一个值全部为 0 的点对象,初始化构造函数则可以根据传入的参数创建对应的点对象。
- 距离计算
距离计算是指计算两个点之间的距离。在二维坐标系中,可以使用勾股定理求解两点间的距离,如下所示:
对于上述公式,可以通过下面这段 C++ 代码来实现:
c++Copy Code
#include<cmath>
double Distance(Point p1, Point p2) {
double dx = p2.x - p1.x;
double dy = p2.y - p1.y;
return std::sqrt(dx*dx + dy*dy);
}
在该代码中,我们定义了一个 Distance()
函数,该函数接受两个点作为参数,并返回这两个点之间的距离。
- 平移操作
平移是指将一个点沿着一个向量进行移动。在二维坐标系中,可以根据向量的加减运算来实现平移操作。例如,假设有一个点 (x,y) 和一个向量 (dx,dy),则可以使用下面的公式实现平移操作:
对于上述公式,可以通过下面这段 C++ 代码来实现:
c++Copy Code
void Translate(Point &p, double dx, double dy) {
p.x += dx;
p.y += dy;
}
在该代码中,我们定义了一个 Translate()
函数,该函数接受一个点和两个实数作为参数,将该点沿着向量 (dx,dy) 进行平移。
线(Line)
线的定义
线是一个由多个点组成的几何体,它可以看作是连接两个点的无限长直线,或者连接两个有限长度的线段等。
线的表示
在程序中,我们通常用结构体来表示一条线,例如下面这段 C++ 代码:
c++Copy Code
struct Line {
Point start;
Point end;
};
在该代码中,我们定义了一个名为 Line
的结构体,它包含两个成员变量 start
和 end
,分别表示直线的起点和终点。
线的操作
- 构造函数
构造函数用于创建一条线对象。在 C++ 中,可以通过函数重载的方式实现多种构造函数,例如:
c++Copy Code
struct Line {
Point start;
Point end;
// 默认构造函数
Line() {}
// 初始化构造函数
Line(Point start, Point end) : start(start), end(end) {}
};
上述代码中,我们定义了两个构造函数:默认构造函数和初始化构造函数。默认构造函数用于创建一条没有起点和终点的线对象,初始化构造函数则可以根据传入的参数创建对应的线对象。
- 长度计算
长度计算是指计算一条线段的长度。在二维坐标系中,可以使用勾股定理求解线段的长度,如下所示:
对于上述公式,可以通过下面这段 C++ 代码来实现:
c++Copy Code
double Length(Line l) {
return Distance(l.start, l.end);
}
在该代码中,我们定义了一个 Length()
函数,该函数接受一条线作为参数,并返回这条线的长度。
- 平移操作
平移是指将一条直线沿着一个向量进行移动。在二维坐标系中,可以根据向量的加减运算来实现平移操作。例如,假设有一条直线 AB 和一个向量 (dx,dy),则可以将直线的起点和终点分别平移 (dx,dy),从而得到新的直线 A′B′。
对于上述操作,可以通过下面这段 C++ 代码来实现:
c++Copy Code
void TranslateLine(Line &l, double dx, double dy) {
Translate(l.start, dx, dy);
Translate(l.end, dx, dy);
}
在该代码中,我们定义了一个 TranslateLine()
函数,该函数接受一条线和两个实数作为参数,将该线沿着向量 (dx,dy) 进行平移。
- 二分法查找
二分法查找是指在一条直线上查找离某个点最近的点。假设有一条直线 AB 和一个点 P,则可以通过下面的方式将点 P 投影到直线 AB 上,从而得到它在直线上离它最近的点 C:
对于上述操作,可以通过下面这段 C++ 代码来实现:
c++Copy Code
Point NearestPointOnLine(Line l, Point p) {
double L = Length(l);
double dx = l.end.x - l.start.x;
double dy = l.end.y - l.start.y;
double h = std::abs(dy*p.x - dx*p.y + l.end.x*l.start.y - l.end.y*l.start.x) / L;
double xc = l.start.x + h*dx / L;
double yc = l.start.y + h*dy / L;
return { xc, yc };
}
在该代码中,我们定义了一个 NearestPointOnLine()
函数,该函数接受一条线和一个点作为参数,返回该点在直线上离它最近的点。
形状(Shape)
形状的定义
形状是指由多个线段组成的封闭图形,例如矩形、三角形等。在二维图形学中,我们通常通过对形状进行组合、旋转、缩放等变换操作来创建不同的图形。
形状的表示
在程序中,我们通常使用数组或链表等数据结构来存储一组线段,从而表示一个形状。例如下面这段 C++ 代码:
c++Copy Code
struct Shape {
std::vector<Line> lines;
};
在该代码中,我们定义了一个名为 Shape
的结构体,它包含一个 std::vector<Line>
类型的成员变量 lines
,该变量用于存储一组线段。
形状的操作
- 构造函数
构造函数用于创建一个形状对象。在 C++ 中,可以通过函数重载的方式实现多种构造函数,例如:
c++Copy Code
struct Shape {
std::vector<Line> lines;
// 默认构造函数
Shape() {}
// 从点集构造
Shape(std::vector<Point> points) {
for (int i = 0; i < points.size() - 1; i++) {
lines.push_back({ points[i], points[i+1] });
}
lines.push_back({ points.back(), points.front() });
}
};
上述代码中,我们定义了两个构造函数:默认构造函数和从点集构造函数。默认构造函数用于创建一个没有任何线段的形状对象,从点集构造函数则可以根据传入的点集创建对应的形状对象。
- 面积计算
面积计算是指计算一个形状的面积。在二维坐标系中,可以使用叉积求解一个三角形的面积,进而将形状分解为若干个三角形,从而计算出整个形状的面积,如下所示:
对于上述公式,可以通过下面这段 C++ 代码来实现:
c++Copy Code
double Area(Shape s) {
double area = 0;
for (int i = 0; i < s.lines.size() - 1; i++) {
area += s.lines[i].start.x * s.lines[i+1].start.y - s.lines[i+1].start.x * s.lines[i].start.y;
}
return std::abs(area / 2);
}
在该代码中,我们定义了一个 Area()
函数,该函数接受一个形状作为参数,并返回该形状的面积。
- 平移操作
平移是指将一个形状沿着一个向量进行移动。在二维坐标系中,可以对形状中的每条线段都进行平移操作,从而实现整个形状的平移。
对于上述操作,可以通过下面这段 C++ 代码来实现:
c++Copy Code
void TranslateShape(Shape &s, double dx, double dy) {
for (auto &line : s.lines) {
Translate(line.start, dx, dy);
Translate(line.end, dx, dy);
}
}
在该代码中,我们定义了一个 TranslateShape()
函数,该函数接受一个形状和两个实数作为参数,将该形状沿着向量 (dx,dy) 进行平移。
- 旋转操作
旋转是指将一个形状绕某个点进行旋转。在二维坐标系中,可以使用旋转矩阵来实现形状的旋转。例如,假设有一个形状 S 和一个点 P,要将形状 S 绕点 P 逆时针旋转 θ 角度,则可以通过下面的旋转矩阵计算出旋转后的点 ′P′ 的坐标:
其中 (x,y) 表示原始点的坐标,(x′,y′) 表示旋转后的点的坐标,(xp,yp) 表示旋转中心的坐标,θ 表示旋转角度。
对于上述操作,可以通过下面这段 C++ 代码来实现:
c++Copy Code
void RotateShape(Shape &s, Point center, double angle) {
double c = std::cos(angle);
double s = std::sin(angle);
for (auto &line : s.lines) {
line.start = { c * (line.start.x - center.x) - s * (line.start.y - center.y) + center.x,
s * (line.start.x - center.x) + c * (line.start.y - center.y) + center.y };
line.end = { c * (line.end.x - center.x) - s * (line.end.y - center.y) + center.x,
s * (line.end.x - center.x) + c * (line.end.y - center.y) + center.y };
}
}
在该代码中,我们定义了一个 RotateShape()
函数,该函数接受一个形状、一个旋转中心和一个旋转角度作为参数,将该形状绕旋转中心顺时针旋转给定角度。
- 缩放操作
缩放是指将一个形状沿着某个方向进行缩放。在二维坐标系中,可以根据缩放比例对形状中的每个点进行缩放操作,从而实现整个形状的缩放。
对于上述操作,可以通过下面这段 C++ 代码来实现:
c++Copy Code
void ScaleShape(Shape &s, double factor) {
for (auto &line : s.lines) {
line.start.x *= factor;
line.start.y *= factor;
line.end.x *= factor;
line.end.y *= factor;
}
}
在该代码中,我们定义了一个 ScaleShape()
函数,该函数接受一个形状和一个缩放比例作为参数,将该形状沿着横纵坐标方向按照给定比例进行缩放。
总之,二维图形学中的点、线和形状是计算机图形学中的基本元素,它们可以用于创建各种各样的图形,并通过不同的操作进行变换和组合,使得我们能够实现更加丰富和复杂的图形应用。
6. 剪切操作
剪切是指将一个形状沿着某个方向进行剪切。在二维坐标系中,可以使用剪切矩阵来实现形状的剪切。例如,假设有一个形状 S,要将其在 x 方向上剪切 tx,在 y 方向上剪切 ty,则可以通过下面的剪切矩阵计算出剪切后的点的坐标:
(
其中 kx=tankx=tantx,kx=tanky=tanty。
对于上述操作,可以通过下面这段 C++ 代码来实现:
c++Copy Code
void ShearShape(Shape &s, double tx, double ty) {
double kx = std::tan(tx);
double ky = std::tan(ty);
for (auto &line : s.lines) {
line.start = { line.start.x + kx * line.start.y, line.start.y + ky * line.start.x };
line.end = { line.end.x + kx * line.end.y, line.end.y + ky * line.end.x };
}
}
在该代码中,我们定义了一个 ShearShape()
函数,该函数接受一个形状、在 x 方向上的剪切因子和在 y 方向上的剪切因子作为参数,将该形状沿着指定方向进行剪切。
- 组合操作
组合操作是指将多个形状按照一定规则组合成一个新的形状。在二维坐标系中,常见的组合方式包括并集、交集和差集等。
对于上述操作,可以通过下面这段 C++ 代码来实现:
c++Copy Code
void UnionShapes(Shape &s1, Shape &s2) {
s1.lines.insert(s1.lines.end(), s2.lines.begin(), s2.lines.end());
}
void IntersectShapes(Shape &s1, Shape &s2) {
std::vector<Line> new_lines;
for (auto &line1 : s1.lines) {
for (auto &line2 : s2.lines) {
Point p;
if (Intersect(line1, line2, p)) {
new_lines.push_back({ line1.start, p });
new_lines.push_back({ p, line1.end });
}
}
}
s1.lines = new_lines;
}
void SubtractShapes(Shape &s1, Shape &s2) {
std::vector<Line> new_lines;
for (auto &line1 : s1.lines) {
bool cut = false;
for (auto &line2 : s2.lines) {
Point p;
if (Intersect(line1, line2, p)) {
if (p == line2.start || p == line2.end) {
cut = true;
break;
}
}
}
if (!cut) {
new_lines.push_back({ line1.start, line1.end });
}
}
s1.lines = new_lines;
}
在该代码中,我们定义了三个函数,分别为 UnionShapes()
、IntersectShapes()
和 SubtractShapes()
,分别表示形状的并集、交集和差集。其中,UnionShapes()
函数将两个形状合并成一个形状;IntersectShapes()
函数将两个形状的交集生成一个新的形状;SubtractShapes()
函数将第一个形状中不在第二个形状中的部分保留下来生成一个新的形状。
- 矩形裁剪操作
矩形裁剪是指将一个形状按照给定的矩形进行裁剪。在二维坐标系中,可以使用 Cohen-Sutherland 或 Liang-Barsky 算法来实现矩形裁剪。
对于上述操作,可以通过下面这段 C++ 代码来实现:
c++Copy Code
void ClipShape(Shape &s, Point clip_min, Point clip_max) {
std::vector<Line> new_lines;
for (auto &line : s.lines) {
double t0 = 0, t1 = 1;
if (Clip(line.start.x - clip_min.x, clip_max.x - line.start.x, t0, t1) &&
Clip(line.start.y - clip_min.y, clip_max.y - line.start.y, t0, t1) &&
Clip(line.end.x - clip_min.x, clip_max.x - line.end.x, t0, t1) &&
Clip(line.end.y - clip_min.y, clip_max.y - line.end.y, t0, t1)) {
new_lines.push_back({ {line.start.x + (line.end.x - line.start.x) * t0,
line.start.y + (line.end.y - line.start.y) * t0 },
{line.start.x + (line.end.x - line.start.x) * t1,
line.start.y + (line.end.y - line.start.y) * t1 } });
}
}
s.lines = new_lines;
}
在该代码中,我们定义了一个 ClipShape()
函数,该函数接受一个形状和一个矩形作为参数,将该形状沿着矩形进行裁剪。
总之,二维图形学中的基本变换和组合操作可以使得我们能够实现各种各样的图形操作,例如平移、旋转、缩放、剪切、组合和裁剪等,同时也是计算机图形学中的基础知识。通过对这些操作的深入理解和娴熟掌握,我们可以更加高效地创建和编辑图形,并实现更加复杂和实用的图形应用。