计算几何学 | 几何对象的基本元素与表现 | C/C++实现

点与向量

用程序求解几何问题,先要想办法用编程中的数据结构来表示几何对象。这里,使用向量就是解决办法之一。

我们将既有大小又有方向的量称为向量。相对地,只有大小没有方向的量称为标量。为了用数据结构表示向量,将向量考虑成从原点O(0, 0)指向对象点P(x, y)的有向线段。

向量落实在纸面上容易给人带来一种误解,觉得向量可以表示平面上的线段。但实际上,向量只具有大小和方向,而线段却要由两个端点确定,因此用向量表示线段还需要另外规定一个起点。

平面几何中最简单的元素是点(x, y),我们可以用如下所示结构体或类来实现:

表示点的结构体:

struct Point { double x, y; };

由于向量也可以仅用一个点来定义,所以我们用和点完全相同的数据结构来表示向量。这里的typedef用来给已有数据类型创建新的名称,这样Point和Vector虽然表示同一个数据结构,却可以视情况分开使用(用于不同意义的函数、变量等)。

表示向量的结构体:

typedef Point Vector;

线段与直线

我们可以用包含两个点(起点p1和终点p2)的结构体或类来表示线段。

表示线段的结构体:

struct Segment {
	Point p1, p2;
};

要注意区分线段与直线。线段是由两个端点及其间距离定义的具有一定长度的线,而直线是通过两个点且长度无限的线。也就是说,直线由两个不同的点定义,并不具有端点。

直线和线段可以用相同的方法实现。我们用与线段相同的数据结构来表示直线。

表示直线的结构体:

typedef Segment Line;

圆可以用包含圆心c和半径r的结构体或类来表示。

表示圆的类:

class Circle {
	public:
		Point c;
		double r;
		Circle(Point c = Point(), double r = 0.0): c(c), r(r) {}
};

多边形

多边形可以用点的序列来表示。

多边形的表示:

typedef vector<Point> Polygon;

向量的基本运算

向量的几种基本运算:
两个向量的和(sum)a + b
两个向量的差(difference)a - b
一个向量的标量倍(scalar multiplication)ka

向量运算可以定义为函数。我们为了更直观地对点对象进行操作,选择将点的结构体或类之间的运算定义为运算符。C++允许我们对运算符进行定义。

定义点/向量间的运算符:

double x, y;
Point operator + (Point &p) {
	return Point(x + p.x, y + p.y);
}

Point operator - (Point &p) {
	return Point(x - p.x, y - p.y);
}

Point operator * (double k) {
	return Point(x * k, y * k);
}

x、y表示相应类中的点,p表示对象点。
定义运算符之后,向量间的运算便可以用下面的方法描述了。

向量间的运算示例:

Vector a, b, c, d;
c = a - b;//向量的差
d = a * 2.0;//向量的标量倍

向量的大小

向量 a = ( a x , a y ) a=(a_x,a_y) a=(ax,ay)的大小|a|(absolute,a的绝对值)就是原点到表示向量的点的距离。除此之外,还有表示向量大小的平方的概念,称为范数(norm)。

我们可以这样实现一个以向量为参数,返回该向量大小及范数的函数。

向量的范数及大小:

double norm(Vector a) {
	return a.x * a.x + a.y * a.y;
}

double abs(Vector a) {
	return sqrt(norm(a));
}

请注意,此abs与C/C++中的abs函数(用于返回给定数值的绝对值)不同。二者由引用的参数类型区分。

另外,我们还可以视情况将abs、norm等向量间的基本运算以类的成员函数形式来实现。

Point/Vector类

下述程序是实现Point类(Vector类)的例子,其中包含了我们前面讲到的运算符等内容。

Point类:

#define EPS (1e - 10)
#define equals(a, b) (fabs((a) - (b)) < EPS)

class Point {
	public:
		double x, y;
		
		Point(double x = 0, double y = 0): x(x), y(y) {}

		Point operator + (Point p) { return Point(x + p.x, y + p.y); }
		Point operator - (Point p) { return Point(x - p.x, y - p.y); }
		Point operator * (double a) { return Point(a * x, a * y); }
		Point operator / (double a) { return Point(x / a, y / a); }

		double abs() { return sqrt(norm()); }
		double norm() { return x * x + y * y; }
		
		bool operator < (const Point &p) const {
			return x != p.x ? x < p.x : y < p.y;
		}

		bool operator == (const Point &p) const {
			return fabs(x - p.x) < EPS && fabs(y - p.y) < EPS;
		}
};

typedef Point Vector;

向量的内积

利用向量内积/外积的几何性质,我们可以创建许多“零件”(程序)来求解计算几何学的相关问题。

设向量a、b的夹角为θ(0 ≤ θ ≤ 180),那么a、b的内积(dot product)a · b = |a||b|cosθ。

a = ( a x , a y ) a=(a_x,a_y) a=(ax,ay) b = ( b x , b y ) b=(b_x,b_y) b=(bx,by)的形式表示两个向量,根据余弦定理可推导出二维平面上2个向量a、b的内积可以表示为
a · b = |a||b|cosθ = a x ∗ b x + a y ∗ b y a_x * b_x + a_y * b_y axbx+ayby
下面是一个求向量a、b内积的程序。内积dot()的返回值为实数。

向量a和b的内积:

double dot(Vector a, Vector b) {
	return a.x * b.x + a.y * b.y;
}

向量的外积

设向量a、b的夹角为θ,那么a、b的外积(cross product)|a × b| = |a||b|sinθ

两个向量a、b的外积是一个具有大小和方向的向量。外积的方向与a、b所在的平面垂直,且满足右手螺旋定则。以a为起始边向b旋转角度θ与b重合,此时伸出右手,四指握拳指向a的旋转方向,大拇指所指方向即为外积的方向。另外,外积的大小等于两个向量构成的平行四边形的面积。

a = ( a x , a y , a z ) , b = ( b x , b y , b z ) a=(a_x,a_y,a_z),b=(b_x,b_y,b_z) a=(ax,ay,az),b=(bx,by,bz)的形式表示两个向量,则a与b的外积为
∣ a × b ∣ = ( a y × b z − a z × b y , a z × b x − a x × b z , a x × b y − a y × b x ) |a×b|=(a_y×b_z-a_z×b_y,a_z×b_x-a_x×b_z,a_x×b_y-a_y×b_x) a×b=(ay×bzaz×by,az×bxax×bz,ax×byay×bx)。(可通过矩阵导出)

将0代入z轴的值,则二维平面上两个向量a、b的外积大小为
∣ a × b ∣ = ∣ a ∣ ∣ b ∣ s i n θ = a x × b y − a y × b x |a×b|=|a||b|sinθ=a_x×b_y-a_y×b_x a×b=absinθ=ax×byay×bx
下面是一个求向量a、b外积的程序。外积cross()的返回值为表示向量大小的实数。

向量a与b的外积:

double cross(Vector a, Vector b) {
	return a.x*b.y - a.y*b.x;
}
  • 9
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值