如何创建一个随机封闭平滑CGPath?
Posted
技术标签:
【中文标题】如何创建一个随机封闭平滑CGPath?【英文标题】:How to create a random closed smooth CGPath? 【发布时间】:2014-03-03 19:32:55 【问题描述】:我正在尝试找到一种方法来创建随机闭合平滑路径(CGPath
或 UIBezierPath
)。我已经阅读了 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?的主要内容,如果未能解决你的问题,请参考以下文章