ios 的坐标系和我们几何课本中的二维坐标系并不一样!
# bezierpath绘制圆弧
使用 uibezierpath 进行绘制圆弧的方法,通常会直接使用 addarc :
1
|
addarc(withcenter:, radius:, startangle:, endangle:, clockwise:) |
或者使用 addcurve 进行拟圆弧:
1
|
addcurve(to:, controlpoint1:, controlpoint2:) |
其实我们可以通过,两个坐标点(startpoint & endpoint),和两点间的线段对应的圆弧的弧度(angle/radian)就能确定这个圆的信息(半径radius, center), 所以我们是不是可以封装出只提供 start, end 和 angle 就能绘制 arc 的函数?
1
|
addarc(startpoint: , endpoint: , angle: , clockwise:) |
# 计算两点间的距离
这里逻辑很简单不做赘述。
1
2
3
4
5
|
func calculatelinelength(_ point1: cgpoint, _ point2: cgpoint) -> cgfloat { let w = point1.x - point2.x let h = point1.y - point2.y return sqrt (w * w + h * h) } |
# 计算两点间的夹角
计算 point 和 origin 连线在 ios 坐标系的角度
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
func calculateangle(point: cgpoint, origin: cgpoint) -> double { if point.y == origin.y { return point.x > origin.x ? 0.0 : - double .pi } if point.x == origin.x { return point.y > origin.y ? double .pi * 0.5 : double .pi * -0.5 } // note: 修正标准坐标系角度到 ios 坐标系 let rotationadjustment = double .pi * 0.5 let offsetx = point.x - origin.x let offsety = point.y - origin.y // note: 使用 -offsety 是因为 ios 坐标系与标准坐标系的区别 if offsety > 0 { return double ( atan (offsetx / -offsety)) + rotationadjustment } else { return double ( atan (offsetx / -offsety)) - rotationadjustment } } |
# 计算圆心的坐标
如果你已经将几何知识丢的差不多了的话,我在这里画了个大概的草图,如下( angle 比较小时):
angle 比较大时:
所以我么可以写出如下计算中心点的代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
|
// woring: 只计算从start到end **顺时针** 计算对应的 **小于π** 圆弧对应的圆心 // note: 计算逆时针(end到start)可以看做将传入的start和end对调后计算顺时针时的圆心位置 // note: 计算大于π的叫相当于将end和start对换后计算2π-angle的顺时针圆心位置 // note: 综上传入start,end,angle 右外部自行处理逻辑 func calculatecenterfor(startpoint start: cgpoint, endpoint end: cgpoint, radian: double ) -> cgpoint { guard radian <= double .pi else { fatalerror( "does not support radian calculations greater than π!" ) } guard start != end else { fatalerror( "start position and end position cannot be equal!" ) } if radian == double .pi { let centerx = (end.x - start.x) * 0.5 + start.x let centery = (end.y - start.y) * 0.5 + start.y return cgpoint(x: centerx, y: centery) } let lineab = calculatelinelength(start, end) // 平行 y 轴 if start.x == end.x { let centery = (end.y - start.y) * 0.5 + start.y let tanresult = cgfloat( tan (radian * 0.5)) let offsetx = lineab * 0.5 / tanresult let centerx = start.x + offsetx * (start.y > end.y ? 1.0 : -1.0) return cgpoint(x: centerx, y: centery) } // 平行 x 轴 if start.y == end.y { let centerx = (end.x - start.x) * 0.5 + start.x let tanresult = cgfloat( tan (radian * 0.5)) let offsety = lineab * 0.5 / tanresult let centery = start.y + offsety * (start.x < end.x ? 1.0 : -1.0) return cgpoint(x: centerx, y: centery) } // 普通情况 // 计算半径 let radius = lineab * 0.5 / cgfloat( sin (radian * 0.5)) // 计算与 y 轴的夹角 let angletoyaxis = atan ( abs (start.x - end.x) / abs (start.y - end.y)) let cacluteangle = cgfloat( double .pi - radian) * 0.5 - angletoyaxis // 偏移量 let offsetx = radius * sin (cacluteangle) let offsety = radius * cos (cacluteangle) var centetx = end.x var centery = end.y // 以 start 为原点判断象限区间(ios坐标系) if end.x > start.x && end.y < start.y { // 第一象限 centetx = end.x + offsetx centery = end.y + offsety } else if end.x > start.x && end.y > start.y { // 第二象限 centetx = start.x - offsetx centery = start.y + offsety } else if end.x < start.x && end.y > start.y { // 第三象限 centetx = end.x - offsetx centery = end.y - offsety } else if end.x < start.x && end.y < start.y { // 第四象限 centetx = start.x + offsetx centery = start.y - offsety } return cgpoint(x: centetx, y: centery) } |
这里附上一个逆时针绘制第一张图中圆心位置的草图,图中已将 start 和 end 对换
如果你对其中计算时到底该使用 + 还是 - 有困惑的话也可以自己多画些草图大概验证下,总之有疑惑多动手