CodeV

5.6-插值路径

代码清单5-7使您能够插入一个路径实例,以查找沿其具有一定百分比的点。代码必须是广泛适用的,因为两件事情。首先,必须考虑所有情况 - 直线,三次曲线和二次曲线。这涉及到大量的switch语句来考虑和计算每种可能性。

第二,此方法返回一个可选的斜率 - 这是作为最后一个参数传递的CGPoint地址。计算一个斜率需要的代码和计算实际的点一样多。你需要一个斜率值,因为它表示所讨论的点处路径的曲线的切线。这使得你可以让所有的形状朝向曲线的内部,如图5-3所示。

请注意,反转路径会使斜率反转。用于表示的每个点的项目曲线外侧的切线向内翻转,反之亦然。这是因为切线函数在Y轴上是对称的。从点p2移动到点p1而不是从p1移动到p2产生一个切线值,以及一个角度,它是原始值的负值。

使用这个百分比方法在计算上是昂贵的。在可能的情况下,您可能需要预先计算这些插值,并使用缓存的percentage-to-point数组来加快动画和布局。

清单5-7 路径插值

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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// Calculate a point that's a given percentage along a path
- (CGPoint) pointAtPercent: (CGFloat) percent withSlope: (CGPoint *) slope
{
// Retrieve path elements
NSArray *elements = self.elements;
if (percent == 0.0f)
{
BezierElement *first = [elements objectAtIndex:0];
return first.point;
}

// Retrieve the full path distance
float pathLength = self.pathLength; // see Listing 5-6
float totalDistance = 0.0f;

// Establish the current and firstPoint states
CGPoint current = NULLPOINT; CGPoint firstPoint = NULLPOINT;

// Iterate through elements until the percentage
// no longer overshoots
for (BezierElement *element in elements)
{
float distance = ElementDistanceFromPoint(
element, current, firstPoint);
CGFloat proposedTotalDistance =totalDistance + distance;
CGFloat proposedPercent = proposedTotalDistance / pathLength;

if (proposedPercent < percent)
{
// Consume and continue totalDistance = proposedTotalDistance;

if (element.elementType == kCGPathElementMoveToPoint)
firstPoint = element.point;

current = element.point;
continue;
}

// What percent between p1 and p2?
CGFloat currentPercent = totalDistance / pathLength;

CGFloat dPercent = percent - currentPercent;

CGFloat percentDistance = dPercent * pathLength;

CGFloat targetPercent = percentDistance / distance;

// Return result
CGPoint point = InterpolatePointFromElement(element, current,
firstPoint,targetPercent, slope);

return point;
}

return NULLPOINT;
}

// Interpolate individual distances along element
CGPoint InterpolatePointFromElement(
BezierElement *element, CGPoint point,
CGPoint startPoint, CGFloat percent,
CGPoint *slope)
{
switch (element.elementType)
{
case kCGPathElementMoveToPoint:
{
// No distance

if (slope)
*slope = CGPointMake(INFINITY, INFINITY);

return point;
}

case kCGPathElementCloseSubpath:
{
// from self.point to firstPoint
CGPoint p = InterpolateLineSegment(
point, startPoint, percent, slope);

return p;
}

case kCGPathElementAddLineToPoint:
{ // From point to self.point

CGPoint p = InterpolateLineSegment(
point, element.point, percent, slope);

return p; }

case kCGPathElementAddQuadCurveToPoint:
{ // From point to self.point
CGPoint p = QuadBezierPoint(percent, point,
element.controlPoint1, element.point);
// Calculate slope by moving back slightly

CGFloat dx = p.x - QuadBezier(percent * 0.9,
point.x, element.controlPoint1.x,
element.point.x);
CGFloat dy = p.y - QuadBezier(percent * 0.9,
point.y, element.controlPoint1.y,
element.point.y);

if (slope)
*slope = CGPointMake(dx, dy);

return p; }

case kCGPathElementAddCurveToPoint:
{ // From point to self.point
CGPoint p = CubicBezierPoint(percent, point,
element.controlPoint1, element.controlPoint2,
element.point);
// Calculate slope by moving back slightly

CGFloat dx = p.x - CubicBezier(percent * 0.9,
point.x, element.controlPoint1.x,
element.controlPoint2.x, element.point.x);

CGFloat dy = p.y - CubicBezier(percent * 0.9,
point.y, element.controlPoint1.y,
element.controlPoint2.y, element.point.y);

if (slope)
*slope = CGPointMake(dx, dy);

return p; }
}

// Element could not be interpolated
return NULLPOINT;
}

本文翻译自《iOS Drawing Practical UIKit Solutions》作者:Erica Sadun,翻译:Cheng Dong。如果觉得本书不错请购买支持正版:亚马逊购买传送门,本书所有源代码可在GitHub上下载。译者虽然力求做到信,达,雅,但是由于时间仓促加之译者水平十分有限,文中难免会出现不正确,不准确,词不达意,难于理解的地方,还望各位批评指正,共同进步,谢谢。转载请注明出处。