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

Posted

技术标签:

【中文标题】如何创建一个随机封闭平滑CGPath?【英文标题】:How to create a random closed smooth CGPath? 【发布时间】:2014-03-03 19:32:55 【问题描述】:

我正在尝试找到一种方法来创建随机闭合平滑路径(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);

如果有人能指出我如何开始实施它的正确方向,那就太好了。我尝试生成的路径示例:

【问题讨论】:

即使是非自相交的封闭平滑路径也可能相当“复杂”。您能否对所需路径指定一些限制? 在我的脑海中,生成极地的航点可能会有所帮助,而不是笛卡尔坐标。绕圈走时随机改变半径;那么你的问题是“只是”插值。为此,请查看Catmull-Rom spline 过程,该过程将生成通过点的路径,而不是围绕它们。 @JoshCaswell,伟大的思想都一样。你的解释,使用极坐标,比我的更容易理解。看看我的答案链接的示例项目。 很好,感谢分享,@DuncanC。 【参考方案1】:

你画的看起来像一个扭曲的圆圈。

假设这就是你所追求的,我会这样做:

编写从 0 到 2pi 的角度按固定步数步进的代码。 (尝试 8)使角度变化小于 ± pi/steps 的一些小的随机量。

选择一个略小于包围正方形边长 1/2 的底边半径,这样就有空间让您的点在底边半径之内或之外,而不会超出您的边界正方形。尝试 3/8 的边界框长度。

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

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

将每个点添加到数组中。如果您使用这些点来创建闭合路径,它将为您提供一个 8 边不规则非自相交多边形,它是一个扭曲的圆。

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

编辑:我在 github 上创建了一个名为 RandomBlobs 的项目,它执行我上面描述的操作,以及另一种方法:

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

第三种方法可能更简单:

使用类似于上述第一种方法的圆形布局。选择随机控制点。但随后不再使用 Catmull-Rom 样条,而是将扭曲圆上每对端点之间的角度一分为二,并为二次贝塞尔曲线添加一个控制点,该控制点也具有随机半径值。所以当你绕着圆圈走时,你会有交替的端点和控制点。您可能需要向贝塞尔控制点添加一些约束,这样您的曲线形状中就不会出现“扭结”(为了避免扭结,相邻贝塞尔曲线的控制点需要跟随一条通过共享端点的线两条曲线。)


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

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

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

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

【讨论】:

谢谢邓肯。你的帖子很有帮助。我会执行的。 @reecon,我不知道为什么,但我决定将您的问题作为一个示例项目来解决。经过太多的工作,我在 github 上创建了一个项目 github.com/DuncanMC/RandomBlobs ,它可以按照您的要求使用 2 种不同的方式。第一种方法使用我上面回答中描述的圆形切片方法,第二种方法将视图分成9个正方形的网格,跳过中心正方形,然后在每个正方形中选择随机点,顺时针移动,形成一个封闭的连接 8 个点的路径。这两种形状给人的感觉是不同的。 一段很棒的代码!我想在周末解决这个问题,但看起来你让我很开心。这就是我如此喜欢 *** 的原因。您可以共享和合作,享受代码的乐趣!谢谢大哥。【参考方案2】:

我已经尝试构建你的路径,但它并不完美......无论如何,我会分享我的测试;-D Hop 这可以提供帮助。

//
//  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

【讨论】:

以上是关于如何创建一个随机封闭平滑CGPath?的主要内容,如果未能解决你的问题,请参考以下文章

如果我想要随机非结构化数据的 3D 样条/平滑插值怎么办?

用随机颜色填充封闭区域 - Haskell - 星期五

无法引用封闭范围中定义的非最终局部变量按钮,随机方法错误

图像处理与matlab实例之图像平滑

如何从文件创建 CGPath - 即 SVG

如何让 div 在页面中随机移动(使用 jQuery 或 CSS)