如何创建一个随机闭合的平滑CGPath?

时间:2014-03-03 19:32:55

标签: ios objective-c cocoa-touch uibezierpath cgpath

我正在尝试找到一种创建随机闭合平滑路径(CGPathUIBezierPath)的方法。我已经阅读了关于贝塞尔路径的De Casteljau's algorithm和其他文章,但它似乎不符合我的目标。

我考虑过创建一个CGPath圆圈。然后我会通过一个函数来增加路径,这个函数会扭曲点的位置,比如正弦或余弦。但是我不知道这是否是正确的方向,因为路径不会有随机的形状。

CGMutablePathRef circle = CGPathCreateMutable();
CGPathAddArc(circle, nil, 0.0f, 0.0f, 100.0f, 2 * M_PI, 0.0f, true);
...
CGPathRelease(circle);

如果有人能指出我正确的方向如何开始实施它会很棒。我试图生成的路径示例:

enter image description here

2 个答案:

答案 0 :(得分:6)

你所绘制的内容看起来像是一个扭曲的圆圈。

假设这就是你所追求的,这就是我要做的事情:

编写以0到2pi的角度步长固定步数的代码。 (尝试8)角度变化小于±pi /步的小随机量。

选择一个基本半径,该半径略小于封闭正方形边长的1/2,因此有足够的空间使您的点在基本半径的内部或外部,而不会超出边界方格。尝试3/8的边界框长度。

对于沿圆的每个稍微随机化的角度值,计算一个半径值,它是基础半径±从0到基本半径/ 2的随机值。

使用正弦和余弦将角度和半径值转换为点的x和y坐标。

将每个点添加到数组中。如果你使用这些点创建一个封闭的路径,它会给你一个8边不规则的非自相关多边形,这是一个扭曲的圆。

现在使用这些点作为Catmull-Rom样条曲线的控制点,将其转换为平滑曲线。

编辑:我在github上创建了一个名为RandomBlobs的项目,它完成了我上面描述的内容,以及另一种方法:

将正方形区域划分为3x3的较小正方形网格。忽略中心广场。 顺时针绕着剩下的8个方块走。对于每个方格,在方形内部选择一个随机的x / y coorindate(但防止它太靠近边缘。) 创建关闭的UIBezierPath按顺序连接8个点。 使用Catmull-Rom平滑将不规则八边形转变为平滑曲线。

然而第三种方法可能更简单:

使用与上述第一种方法类似的圆形布局。选择随机控制点。但是,不是使用Catmull-Rom样条曲线,而是将扭曲圆上每对端点之间的角度平分,并为二次贝塞尔曲线添加控制点,同时使用随机半径值。因此,当您绕着圆圈走动时,您将拥有交替的端点和控制点。您可能需要向贝塞尔控制点添加一些约束,这样您的曲线形状就不会出现“扭结”(为了避免扭结,相邻贝塞尔曲线的控制点需要遵循共享终点的一条直线这两条曲线。)


以下是来自RandomBlobs项目的几个示例图像。我上传的图片按比例缩小。该程序可选择显示它用于生成每个图像的控制点,但您无法真正看到缩小图像中的控制点。

首先,基于圆圈的blob(使用Josh Caswell和我建议的第一种方法): Circle-based blob 在该图片中,起始圆形以浅灰色显示:

第二,基于我描述的第二种基于方形的技术的blob:

Square-based blob

在该图中,显示了正方形网格以供参考。形状基于网格中每个点的随机点(不包括中心方块)。

答案 1 :(得分:1)

我试图建立你的道路,但它并不完美......无论如何,我会分享我的测试;-跳这可以帮助。

//
//  DrawView.h
//  test
//
//  Created by Armand DOHM on 03/03/2014.
//
//

#import <UIKit/UIKit.h>

@interface DrawView : UIView

@end


//
//  DrawView.m
//  test
//
//  Created by Armand DOHM on 03/03/2014.
//
//

#import "DrawView.h"
#import <math.h>

@implementation DrawView

- (void)drawRect:(CGRect)rect
{
    float r; //radius
    float rmax = MIN(rect.size.height,rect.size.width) * .5; //max radius
    float rmin = rmax * .1; //min radius

    NSMutableArray *points = [[NSMutableArray alloc] init];

    /*cut a circle into x pies. for each of this pie take a point at a random radius
     link all of this point with quadcurve*/

    for (double a=0;a < 2 * M_PI;a += M_PI / 10) {
        r = rmin +  ((arc4random_uniform((int)(rmax - rmin) * 100)) / 100.0f);
        CGPoint p = CGPointMake((rect.size.width / 2) + (r * cos (a)) , (rect.size.height / 2) + (r * sin (a)));
        [points addObject:[NSValue valueWithCGPoint:p]];
    }


    UIBezierPath *myPath=[[UIBezierPath alloc]init];
    myPath.lineWidth=2;
    [myPath strokeWithBlendMode:kCGBlendModeNormal alpha:1.0];

    r = rmin +  ((arc4random_uniform((int)(rmax - rmin) * 100)) / 100.0f);
    [myPath moveToPoint:CGPointMake((rect.size.width / 2) + (r * cos (0)) , (rect.size.height / 2) + (r * sin (0)))];


    for (int i = 0; i < points.count; i+=2) {
        NSValue *value = [points objectAtIndex:i];
        CGPoint p1 = [value CGPointValue];
        value = [points objectAtIndex:(i+1)];
        CGPoint p2 = [value CGPointValue];
        [myPath addQuadCurveToPoint:p2 controlPoint:p1];
    }


    [myPath closePath];

    [myPath stroke];
}

@end