平面向量是在二维平面内既有方向(direction)又有大小(magnitude)的量,物理学中也称作矢量,与之相对的是只有大小、没有方向的数量(标量)。
大家可以忽略本章所有出现的代码,因为它们实际上就在这里。
那么向量和我们的绘图又有怎样的关系呢?
向量的数据结构
首先我们要搞清楚的是,如何来表示一个向量。
我在这里不会像数学课一样去详细的讲解向量,只会把向量中我们能应用于我们绘图的那些关键的知识点进行讲解。如果大家对向量完全不了解,那么推荐先了解一下平面向量再来阅读这篇文章。
在数学中,向量有多种表示方式,而我们如果想要把向量的数据用于绘图的话,通常会使用向量的坐标表示,即用一个坐标点来表示一个向量。比如我们可以声明一个向量 a⃗ = (2,3),那么实际上向量 a⃗ 就表示起点位于原点,终点位于坐标系中(2,3)的向量。
注意由于向量是由方向和大小来决定的,所以一旦两个向量的方向和大小相等,那么这两个向量就是相等的向量。比如起点位于(1,1)终点位于(3,4)的向量和我们上面的向量a就是方向相同,大小相等的向量,它们两个是相等的向量。
而由于我们在绘图中不能保证我们所需要的向量起点一定位于UIKit坐标系下的(0,0)点,所以在设计向量的数据结构的时候,我们必须至少要两个成员:起点和终点。
一旦我们确定了一个向量对象的起点和终点,那么这个向量就一定能确定下来了。
@interface DHVector2D : NSObject
@property (nonatomic) CGPoint startPoint;
@property (nonatomic) CGPoint endPoint;
@end
为了方便使用,我们可以声明一些initializer来灵活的创建向量。
/**
* 用两个点初始化一个向量
*
* @param start 起始点
* @param end 结束点
*
* @return 生成的向量
*/
- (instancetype)initWithStartPoint:(CGPoint)start endPoint:(CGPoint)end;
/**
* 指定一个角度生成一个单位向量
*
* @param radian 该向量在逆时针方向上到X轴正方向的角度
*
* @return 单位向量
*/
- (instancetype)initAsIdentityVectorWithAngleToXPositiveAxis:(CGFloat)radian;
/**
* 用一个CGPoint作为坐标表达式初始化一个向量,该向量起点在(0,0)点
*
* @param position 向量坐标表达式
*
* @return 生成的向量
*/
- (instancetype)initWithCoordinateExpression:(CGPoint)position;
/**
* 相当于复制一个向量
*
* @param vector 要复制的向量
*
* @return 生成的向量
*/
+ (instancetype)vectorWithVector:(DHVector2D *)vector;
那么这些方法的实现如下:
- (instancetype)initWithStartPoint:(CGPoint)start endPoint:(CGPoint)end
{
self = [super init];
_startPoint = start;
_endPoint = end;
return self;
}
- (instancetype)initWithCoordinateExpression:(CGPoint)position
{
self = [self initWithStartPoint:CGPointZero endPoint:position];
return self;
}
+ (instancetype)vectorWithVector:(DHVector2D *)vector
{
DHVector2D * aVector = [[DHVector2D alloc] initWithStartPoint:vector.startPoint endPoint:vector.endPoint];
return aVector;
}
- (NSString *)description
{
return [NSString stringWithFormat:@"start : %@, end: %@, coordinate: %@",NSStringFromCGPoint(self.startPoint),NSStringFromCGPoint(self.endPoint),NSStringFromCGPoint([self coordinateExpression])];
}
而initAsIdentityVectorWithAngleToXPositiveAxis方法初始化的向量涉及到了旋转操作,我们将在后面来实现。
除了我们制定一些参数初始化的向量以外,我们还可以直接生成一些特殊的向量:
@interface DHVector2D (SpecialVectors)
/**
* x轴正方向的单位向量
*
* @return x轴正方向的单位向量
*/
+ (DHVector2D *)xPositiveIdentityVector;
/**
* x轴负方向的单位向量
*
* @return x轴负方向的单位向量
*/
+ (DHVector2D *)xNegativeIdentityVector;
/**
* y轴正方向的单位向量
*
* @return y轴正方向的单位向量
*/
+ (DHVector2D *)yPositiveIdentityVector;
/**
* y轴负方向的单位向量
*
* @return y轴负方向的单位向量
*/
+ (DHVector2D *)yNegativeIdentityVector;
/**
* 零向量
*
* @return 零向量
*/
+ (DHVector2D *)zeroVector;
@end
实现如下:
@implementation DHVector2D (SpecialVectors)
#define IDENTITY_LENGTH 1
#pragma mark - 特殊向量
+ (DHVector2D *)xPositiveIdentityVector
{
DHVector2D * vector = [[DHVector2D alloc] initWithCoordinateExpression:CGPointMake(IDENTITY_LENGTH, 0)];
return vector;
}
+ (DHVector2D *)xNegativeIdentityVector
{
DHVector2D * vector = [[DHVector2D alloc] initWithCoordinateExpression:CGPointMake(-IDENTITY_LENGTH, 0)];
return vector;
}
+ (DHVector2D *)yPositiveIdentityVector
{
DHVector2D * vector = [[DHVector2D alloc] initWithCoordinateExpression:CGPointMake(0, IDENTITY_LENGTH)];
return vector;
}
+ (DHVector2D *)yNegativeIdentityVector
{
DHVector2D * vector = [[DHVector2D alloc] initWithCoordinateExpression:CGPointMake(0, -IDENTITY_LENGTH)];
return vector;
}
+ (DHVector2D *)zeroVector
{
DHVector2D * vector = [[DHVector2D alloc] initWithCoordinateExpression:CGPointZero];
return vector;
}
@end
向量的属性
向量拥有许多直接计算的属性,比如向量的长度,与另一个向量的夹角等,我们将这些属性定义为实例方法供外部直接调用。
向量的坐标表达式
即向量的标准表达式,也就是我们在上一部分讲到的使用一个坐标点来表示一个向量,该表达式是以起点为原点,取终点的值来表示一个向量。那么任意向量的坐标表达式应该如何来实现呢?实际上非常简单,就是找一个起始点在原点的向量 a⃗ ,使它等于原向量(方向和大小与原向量相同),那么向量 a⃗ 的终点就是原向量的坐标表达式。这一段描述是用来方便大家理解坐标表达式的,而我们通常在计算的时候直接可以套用公式:
对于一个向量 a⃗ ,起始点为 (x1,y1) ,终止点为 (x2,y2) ,那么它的坐标表达式就是:
向量相等
若两个向量的坐标表达式一样,那么它们就是相等的向量。
向量的长度
向量的长度又叫做向量的模,有时候我们需要直接计算一个向量的长度,而我们所知的信息只有向量的起始点和终止点的坐标,实际上在高中数学的解析几何中大量使用这个公式,向量长度的计算就是一个勾股定理的计算。
对于一个向量 a⃗ ,起始点为 (x1,y1) ,终止点为 (x2,y2) ,把向量的长度看做一个直角三角形的斜边,那么两条直角边的长度就分别是 |x2−x1| 和 |y2−y1| (绝对值符号应该认识吧),然后使用勾股定理计算出斜边长度也就是向量的模:
由于取了平方,所以能保证值为正数,可以去掉绝对值。
若该向量使用坐标表达式表示,即:
那么它的起始点为(0,0),则该向量的模为:
这就明显是一个勾股定理了。
向量的夹角
向量的夹角表示的是两个向量的起始点相同时所形成的弧度小于π的角的大小,通常我们会使用它们的坐标表达式来计算夹角(因为使用坐标表达式的时候这两个向量的起始点都是原点,满足计算夹角的条件)。
在这里我们直接套用向量的公式,实际上向量夹角的计算公式来源于向量点积(内积)的几何意义,关于点积,我们在下面会作讲解。
若有两个向量 a⃗ =(x1,y1) 和