如何曲线CGMutablePath? [英] How to curve CGMutablePath?

查看:382
本文介绍了如何曲线CGMutablePath?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用以下形状:





我想知道如何让它像这样弯曲:





同样:



>



假设所有的圆/线被包装到一个 CGMutablePath 中,然后应用某种曲线,圆弧或四边形曲线到它,虽然我有麻烦来,甚至接近复制它。

解决方案

在你的第一个例子中,你从一个路径开始有几个闭合的子路径。显然,你想扭曲的子路径的中心,但留下相对于他们的(新的)中心的单独的子路径untarped。我将忽略这一点,因为即使没有这个解决方案已经非常复杂了。



因此,让我们考虑如何定义warp字段。我们将使用三个控制点:





warp不变。



它通过旋转缩放将startPoint移动到endPoint。

此外,它根据与fixedPoint的距离应用旋转和缩放。而不是仅仅基于简单的欧几里德距离。请注意,我们不希望对图中V形的顶端点应用任何旋转或缩放,即使这些端点是距fixedPoint的可测量欧氏距离。我们要测量沿着fixedPoint-> startPoint向量的距离,并且随着距离的增加应用更多的旋转/缩放。



这需要一些非常重的三角学。我不打算尝试解释细节。我只是把你的代码转储为 UIBezierPath 上的一个类别:



UIBezierPath + Rob_warp.h



  #import< UIKit / UIKit.h> 

@interface UIBezierPath(Rob_warp)

- (UIBezierPath *)Rob_warpedWithFixedPoint:(CGPoint)fixedPoint startPoint:(CGPoint)startPoint endPoint:

@end



UIBezierPath + Rob_warp.m



请注意,您需要


With the following shape:

I was wondering how do you get it to curve like this:

Also similarly:

I'm assuming that the all of the circles / lines are packed into one CGMutablePath, and then some kind of curve, arc, or quad curve, is applied to it, though I'm having trouble coming even close to replicating it. Does anyone know how to do this?

解决方案

In your first example, you start with a path that has several closed subpaths. Apparently you want to warp the centers of the subpaths, but leave the individual subpaths unwarped relative to their (new) centers. I'm going to ignore that, because the solution even without that is already terribly complex.

So, let's consider how to define the "warp field". We'll use three control points:

The warp leaves fixedPoint unchanged. It moves startPoint to endPoint by rotation and scaling, not by simply interpolating the coordinates.

Furthermore, it applies the rotation and scaling based on distance from fixedPoint. And not just based on the simple Euclidean distance. Notice that we don't want apply any rotation or scaling to the top endpoints of the "V" shape in the picture, even though those endpoints are a measurable Euclidean distance from fixedPoint. We want to measure distance along the fixedPoint->startPoint vector, and apply more rotation/scaling as that distance increases.

This all requires some pretty heavy trigonometry. I'm not going to try to explain the details. I'm just going to dump code on you, as a category on UIBezierPath:

UIBezierPath+Rob_warp.h

#import <UIKit/UIKit.h>

@interface UIBezierPath (Rob_warp)

- (UIBezierPath *)Rob_warpedWithFixedPoint:(CGPoint)fixedPoint startPoint:(CGPoint)startPoint endPoint:(CGPoint)endPoint;

@end

UIBezierPath+Rob_warp.m

Note that you'll need the Rob_forEach category from this answer.

#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

Ok, so how do you use this crazy thing? In the case of a path like the "V" in the example, you could do it like this:

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];

I'm computing fixedPoint as the center of the top edge of the path's bounding box, and startPoint as the center of the bottom edge. The endAnchor is under user control in my test program. It looks like this in the simulator:

A bubble-type path looks like this:

You can find my test project here: https://github.com/mayoff/path-warp

这篇关于如何曲线CGMutablePath?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆