如何徒手绘制椭圆或圆形?
我的问题涉及各种徒手绘制线条的技术:
具体来说,Steve Hanov 发布了这篇优秀的 博客条目。
由此,我能够使用贝塞尔曲线实现一种漂亮的手绘线条算法。然而,我一直困惑于如何实现徒手绘制的椭圆。理想情况下,我想给它一个矩形作为边界,类似于其他椭圆绘图调用。但是,我希望它看起来非常写意。
到目前为止,我想出的最好的
- (UIBezierPath*) freehandEllipseFromRect:(CGRect) rect {
// freehand ellipses need a lil more height
rect = CGRectMake(rect.origin.x, rect.origin.y-5, rect.size.width, rect.size.height+10);
UIBezierPath* path = [UIBezierPath bezierPath];
CGPoint topMidPoint = CGPointMake(rect.origin.x + (rect.size.width/2), rect.origin.y);
CGPoint bottomMidPoint = CGPointMake(rect.origin.x + (rect.size.width/2), rect.origin.y+rect.size.height);
// random point along bottom quarter of height, cause makes it look better
CGFloat randomY = (((CGFloat) (arc4random() % RAND_MAX) / RAND_MAX)) * (rect.size.height/4);
CGPoint leftControlPoint = CGPointMake(rect.origin.x-(rect.size.width), rect.origin.y+(rect.size.height-randomY));
// another random y;
randomY = (((CGFloat) (arc4random() % RAND_MAX) / RAND_MAX)) * (rect.size.height/4);
CGPoint rightControlPoint = CGPointMake(rect.origin.x+(rect.size.width*2), rect.origin.y+(rect.size.height-randomY));
CGFloat overshootValueX = (((CGFloat) (arc4random() % RAND_MAX) / RAND_MAX)) * 4;
CGFloat overshootValueY = (((CGFloat) (arc4random() % RAND_MAX) / RAND_MAX)) * 6;
[path moveToPoint:CGPointMake(topMidPoint.x+overshootValueX, topMidPoint.y)];
[path addQuadCurveToPoint:bottomMidPoint controlPoint:leftControlPoint];
// random value to overshoot
overshootValueX = (((CGFloat) (arc4random() % RAND_MAX) / RAND_MAX)) * 20;
overshootValueY = (((CGFloat) (arc4random() % RAND_MAX) / RAND_MAX)) * 4;
[path addQuadCurveToPoint:CGPointMake(topMidPoint.x-overshootValueX, topMidPoint.y-overshootValueY) controlPoint:rightControlPoint];
return path;
}
结果是这样的:
我不知道不喜欢它在顶部的尖角,尽管我尽了一切努力,但还是无法让它变得更好。另外,我喜欢曲线看起来不那么完美,并且不依赖悬垂作为唯一的“徒手”外观部分。我认为 2 个四边形曲线是错误的方法......
也许 4 条弧线?
有人有其他解决方案或一些示例代码吗? (任何语言都可以)
My question involves various techniques for drawing lines that appear to be freehand:
How do you draw like a Crayon?
Specifically Steve Hanov posted this excellent blog entry.
From that I was able to implement a nice looking algorithm for freehand lines using bezier curves. However, I am stuck on how to implement a freehand looking ellipse. Ideally, I'd like to give it a rect to use as a boundary, similar to other ellipse drawing calls. But, I want it to look very freehand.
So far, the best I have come up with this:
- (UIBezierPath*) freehandEllipseFromRect:(CGRect) rect {
// freehand ellipses need a lil more height
rect = CGRectMake(rect.origin.x, rect.origin.y-5, rect.size.width, rect.size.height+10);
UIBezierPath* path = [UIBezierPath bezierPath];
CGPoint topMidPoint = CGPointMake(rect.origin.x + (rect.size.width/2), rect.origin.y);
CGPoint bottomMidPoint = CGPointMake(rect.origin.x + (rect.size.width/2), rect.origin.y+rect.size.height);
// random point along bottom quarter of height, cause makes it look better
CGFloat randomY = (((CGFloat) (arc4random() % RAND_MAX) / RAND_MAX)) * (rect.size.height/4);
CGPoint leftControlPoint = CGPointMake(rect.origin.x-(rect.size.width), rect.origin.y+(rect.size.height-randomY));
// another random y;
randomY = (((CGFloat) (arc4random() % RAND_MAX) / RAND_MAX)) * (rect.size.height/4);
CGPoint rightControlPoint = CGPointMake(rect.origin.x+(rect.size.width*2), rect.origin.y+(rect.size.height-randomY));
CGFloat overshootValueX = (((CGFloat) (arc4random() % RAND_MAX) / RAND_MAX)) * 4;
CGFloat overshootValueY = (((CGFloat) (arc4random() % RAND_MAX) / RAND_MAX)) * 6;
[path moveToPoint:CGPointMake(topMidPoint.x+overshootValueX, topMidPoint.y)];
[path addQuadCurveToPoint:bottomMidPoint controlPoint:leftControlPoint];
// random value to overshoot
overshootValueX = (((CGFloat) (arc4random() % RAND_MAX) / RAND_MAX)) * 20;
overshootValueY = (((CGFloat) (arc4random() % RAND_MAX) / RAND_MAX)) * 4;
[path addQuadCurveToPoint:CGPointMake(topMidPoint.x-overshootValueX, topMidPoint.y-overshootValueY) controlPoint:rightControlPoint];
return path;
}
The result looks like this:
I don't like how pointed it is on top, and despite all my trying I just can't get it much better. Plus, I like the curves to look less perfect, and not rely on the overhang as the only "freehand" looking part. I think 2 quad curves are just the wrong way to go.....
Maybe 4 arcs?
Anyone have another solution or some sample code for me? (any language is fine)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
所以这个问题已经开放了很长时间了,让我尝试一下。有两个部分:(1)使路径看起来不完美。 (2)像手绘一样抚摸路径。
对于(1),将事物细分出来。使用 100 个左右的控制点来制作它,并使用缓慢变化的包裹函数来扭曲它们。对于(2),在路径上分配缓慢变化的连续厚度和角度,也许还会添加一些噪音。对于一个看起来很好的人类噪音来说,读一下柏林噪音,它太棒了。另外,看看其他人是如何做的,在 Photoshop 中创建路径并描边它们总是一个好主意,它有一个选项可以让它看起来很自然。
So this question has been open for a long time, let me try to give it a shot. There are two parts: (1) Making the path look not perfect. (2) Stroking the path like drawn by hand.
For (1), subdivide the shit out of the thing. Make it out of 100 or so control points and distort them with a wrapping function that varies slowly. For (2) assign a slowly varying, continuous thickness and angle over the path, maybe also adding some noise. For a good human looking noise read up on Perlin noise, it's awesome. Also it's always a good idea to look at how other people do it, create paths in Photoshop and stroke them, it has an option to do it naturally looking.
就我个人而言,我会参数化地定义你的椭圆,如下所示:
然后创建一个生成小随机数的函数,
创建一个函数,为椭圆上的每个点找到曲线的法线向量(参数化)
,通过缩放该点的法线来偏移一个小的随机数。
使用三次插值通过这些点绘制一条曲线。
Personally I would define your ellipse parametrically, like this:
then make a function that generates small random numbers
make a function that finds the normal vector to the curve (parametrically)
for every point on your ellipse, offset by scaling the normal at that point by a small random number.
Draw a curve through the points using cubic interpolation.