Как нарисовать нарисованный от руки эллипс или круг? - PullRequest
7 голосов
/ 04 августа 2011

Мой вопрос касается различных техник рисования линий, которые кажутся от руки:

Как вы рисуете, как карандаш?

В частности, Стив Ханов написал это превосходно запись в блоге .

После этого я смог реализовать красивый алгоритм для линий от руки, используя кривые Безье.Тем не менее, я застрял на том, как реализовать эллипс, выглядящий от руки.В идеале я хотел бы дать ему прямоугольник для использования в качестве границы, аналогично другим вызовам рисования эллипса.Но я хочу, чтобы это выглядело очень от руки.

Пока лучшее, что я придумал, это:

- (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;
}

Результат выглядит так:

freehand ellipse

НадеюсьМне не нравится, как он указан сверху, и, несмотря на все мои попытки, я просто не могу получить его намного лучше.Кроме того, мне нравится, что кривые выглядят менее совершенными, и не полагаюсь на выступ, как единственную деталь, выглядящую «от руки».Я думаю, что 2 четырехугольных кривых - неправильный путь .....

Может быть, 4 дуги?

У кого-нибудь есть другое решение или пример кода для меня?(любой язык в порядке)

Ответы [ 2 ]

1 голос
/ 05 февраля 2012

Лично я бы определил ваш эллипс параметрически, например:

x=(width*cos(t)/2)+centerx;
y=(height*cos(t)/2)+centery;

, затем сделал бы функцию, которая генерирует маленькие случайные числа

, сделал бы функцию, которая находит вектор нормали к кривой (параметрически)

x=width*cos(t);
y=height*sin(t);
normal=UnitVector(x,y);

для каждой точки вашего эллипса, смещение путем масштабирования нормали в этой точке на небольшое случайное число.

Нарисуйте кривую через точки, используя кубическую интерполяцию.

1 голос
/ 03 февраля 2012

Так что этот вопрос давно открыт, позвольте мне попробовать. Есть две части: (1) Создание пути не идеальным. (2) Поглаживание пути, как нарисовано от руки. Для (1) подразделите дерьмо из вещи. Сделайте из примерно 100 контрольных точек и исказите их с помощью функции обтекания, которая изменяется медленно. Для (2) назначьте медленно изменяющуюся, непрерывную толщину и угол наклона пути, возможно также добавив некоторый шум. Для хорошего человеческого взгляда, читающего шум Perlin, это круто. Также всегда полезно посмотреть, как это делают другие люди, создать пути в Photoshop и погладить их, у него есть возможность сделать это естественным образом.

...