具有以下形状:
我想知道如何让它像这样弯曲:
同样地:
我假设所有的圆/线被打包成一个CGMutablePath
,然后应用某种曲线,弧线或四边形曲线,虽然我遇到了麻烦甚至接近复制它。有谁知道怎么做?
答案 0 :(得分:43)
在第一个示例中,您将从具有多个已关闭子路径的路径开始。显然,您想要扭曲子路径的中心,但是将各个子路径相对于它们的(新)中心保持未扭曲。我将忽略这一点,因为即使没有它,解决方案已经非常复杂了。
所以,让我们考虑如何定义“warp字段”。我们将使用三个控制点:
warp使fixedPoint保持不变。它通过旋转和缩放,不将startPoint移动到endPoint,只需插入坐标。
此外,它根据距固定点的距离应用旋转和缩放。而不只是基于简单的欧几里德距离。请注意,我们不希望对图片中“V”形的顶端点应用任何旋转或缩放,即使这些端点距离fixedPoint是可测量的欧几里德距离。我们想测量沿固定点 - > startPoint矢量的距离,并随着距离的增加应用更多的旋转/缩放。
这一切都需要一些相当重的三角学。我不会试图解释细节。我只是将代码转储给你,作为UIBezierPath
上的一个类别:
#import <UIKit/UIKit.h>
@interface UIBezierPath (Rob_warp)
- (UIBezierPath *)Rob_warpedWithFixedPoint:(CGPoint)fixedPoint startPoint:(CGPoint)startPoint endPoint:(CGPoint)endPoint;
@end
请注意,您需要this answer中的Rob_forEach
类别。
#import "UIBezierPath+Rob_warp.h"
#import "UIBezierPath+Rob_forEach.h"
#import <tgmath.h>
static CGPoint minus(CGPoint a, CGPoint b) {
return CGPointMake(a.x - b.x, a.y - b.y);
}
static CGFloat length(CGPoint vector) {
return hypot(vector.x, vector.y);
}
static CGFloat dotProduct(CGPoint a, CGPoint b) {
return a.x * b.x + a.y * b.y;
}
static CGFloat crossProductMagnitude(CGPoint a, CGPoint b) {
return a.x * b.y - a.y * b.x;
}
@implementation UIBezierPath (Rob_warp)
- (UIBezierPath *)Rob_warpedWithFixedPoint:(CGPoint)fixedPoint startPoint:(CGPoint)startPoint endPoint:(CGPoint)endPoint {
CGPoint startVector = minus(startPoint, fixedPoint);
CGFloat startLength = length(startVector);
CGPoint endVector = minus(endPoint, fixedPoint);
CGFloat endLength = length(minus(endPoint, fixedPoint));
CGFloat scale = endLength / startLength;
CGFloat dx = dotProduct(startVector, endVector);
CGFloat dy = crossProductMagnitude(startVector, endVector);
CGFloat radians = atan2(dy, dx);
CGPoint (^warp)(CGPoint) = ^(CGPoint input){
CGAffineTransform t = CGAffineTransformMakeTranslation(-fixedPoint.x, -fixedPoint.y);
CGPoint inputVector = minus(input, fixedPoint);
CGFloat factor = dotProduct(inputVector, startVector) / (startLength * startLength);
CGAffineTransform w = CGAffineTransformMakeRotation(radians * factor);
t = CGAffineTransformConcat(t, w);
CGFloat factoredScale = pow(scale, factor);
t = CGAffineTransformConcat(t, CGAffineTransformMakeScale(factoredScale, factoredScale));
// Note: next line is not the same as CGAffineTransformTranslate!
t = CGAffineTransformConcat(t, CGAffineTransformMakeTranslation(fixedPoint.x, fixedPoint.y));
return CGPointApplyAffineTransform(input, t);
};
UIBezierPath *copy = [self.class bezierPath];
[self Rob_forEachMove:^(CGPoint destination) {
[copy moveToPoint:warp(destination)];
} line:^(CGPoint destination) {
[copy addLineToPoint:warp(destination)];
} quad:^(CGPoint control, CGPoint destination) {
[copy addQuadCurveToPoint:warp(destination) controlPoint:warp(control)];
} cubic:^(CGPoint control0, CGPoint control1, CGPoint destination) {
[copy addCurveToPoint:warp(destination) controlPoint1:warp(control0) controlPoint2:warp(control1)];
} close:^{
[copy closePath];
}];
return copy;
}
@end
好的,你怎么用这个疯狂的东西?对于示例中类似“V”的路径,您可以这样做:
CGRect rect = path.bounds;
CGPoint fixedPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect));
CGPoint startPoint = CGPointMake(fixedPoint.x, CGRectGetMaxY(rect));
path = [path Rob_warpedWithFixedPoint:fixedPoint startPoint:startPoint endPoint:endAnchor];
我将fixedPoint计算为路径边界框顶边的中心,startPoint作为底边的中心。我的测试程序中endAnchor
受用户控制。它在模拟器中看起来像这样:
气泡型路径如下所示:
您可以在此处找到我的测试项目:https://github.com/mayoff/path-warp