How to draw a freehand-looking ellipse or circle?
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)
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:
x=(width*cos(t)/2)+centerx;
y=(height*cos(t)/2)+centery;
then make a function that generates small random numbers
make a function that finds the normal vector to the curve (parametrically)
x=width*cos(t);
y=height*sin(t);
normal=UnitVector(x,y);
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.
精彩评论