iPhone гладкий алгоритм рисования эскизов - PullRequest
60 голосов
/ 22 февраля 2011

Я работаю над приложением для рисования на iPhone.Я получил это работает, но не так хорошо, как видно здесь enter image description here

И я ищу любое предложение, чтобы сгладить рисунок В основном, я сделал, когда пользователь кладет палец на экран, который я назвал

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 

затем я собираю одно касание в массиве с

- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event

, и когда пользователь убирает палец с экрана, я звоню

- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event

, затем я рисую всеточки в массиве, используя

NSMutableArray *points = [collectedArray points];   

CGPoint firstPoint;
[[points objectAtIndex:0] getValue:&firstPoint];

CGContextMoveToPoint(context, firstPoint.x, firstPoint.y);
CGContextSetLineCap(context, kCGLineCapRound);
CGContextSetLineJoin(context, kCGLineJoinRound);

for (int i=1; i < [points count]; i++) {
    NSValue *value = [points objectAtIndex:i];
    CGPoint point;
    [value getValue:&point];    
    CGContextAddLineToPoint(context, point.x, point.y);

} 

CGContextStrokePath(context);
UIGraphicsPushContext(context);

А теперь я хочу улучшить рисунок так, чтобы он больше походил на приложение "Sketch Book" enter image description here

Я думаю, что-то связано с обработкой сигналовАлгоритм переставить все точки в массиве, но я не уверен.Любая помощь приветствуется.

Заранее благодарен:)

Ответы [ 6 ]

56 голосов
/ 31 марта 2011
CGPoint midPoint(CGPoint p1, CGPoint p2)
{

    return CGPointMake((p1.x + p2.x) * 0.5, (p1.y + p2.y) * 0.5);

}

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{

    UITouch *touch = [touches anyObject];

    previousPoint1 = [touch previousLocationInView:self];
    previousPoint2 = [touch previousLocationInView:self];
    currentPoint = [touch locationInView:self];

}

-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{

    UITouch *touch = [touches anyObject];

    previousPoint2 = previousPoint1;
    previousPoint1 = [touch previousLocationInView:self];
    currentPoint = [touch locationInView:self];


    // calculate mid point
    CGPoint mid1 = midPoint(previousPoint1, previousPoint2); 
    CGPoint mid2 = midPoint(currentPoint, previousPoint1);

    UIGraphicsBeginImageContext(self.imageView.frame.size);
    CGContextRef context = UIGraphicsGetCurrentContext();
    [self.imageView.image drawInRect:CGRectMake(0, 0, self.imageView.frame.size.width, self.imageView.frame.size.height)];

    CGContextMoveToPoint(context, mid1.x, mid1.y);
    // Use QuadCurve is the key
    CGContextAddQuadCurveToPoint(context, previousPoint1.x, previousPoint1.y, mid2.x, mid2.y); 

    CGContextSetLineCap(context, kCGLineCapRound);
    CGContextSetLineWidth(context, 2.0);
    CGContextSetRGBStrokeColor(context, 1.0, 0.0, 0.0, 1.0);
    CGContextStrokePath(context);

    self.imageView.image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

}
22 голосов
/ 23 февраля 2011

Самый простой способ сгладить кривую, подобную этой, - это использовать кривую Безье вместо отрезков прямых линий. Математика, стоящая за этим, приведена в этой статье (на которую указывает этот ответ ), в которой описано, как рассчитать кривые, необходимые для сглаживания кривой, проходящей через несколько точек.

Я полагаю, что Framework Core Plot теперь имеет возможность сглаживать кривые графиков, так что вы можете взглянуть на код, используемый там для реализации такого типа сглаживания.

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

15 голосов
/ 25 октября 2012

Мне очень нравится эта тема. Спасибо за все реализации, особенно Кшиштоф Заблоцкий и Ю-Сен Хан. Я изменил версию Yu-Sen Han, чтобы изменить толщину линии в зависимости от скорости панорамирования (фактически расстояние между последними касаниями). Также я реализовал точечное рисование (для точек touchBegan и touchEnded, расположенных близко друг к другу) Вот результат: enter image description here

Чтобы определить толщину линии, я выбрал такую ​​функцию расстояния:

(Не спрашивайте меня, почему ... Я хотя это хорошо подходит, но я уверен, что вы можете найти лучший)

enter image description here

CGFloat dist = distance(previousPoint1, currentPoint);
CGFloat newWidth = 4*(atan(-dist/15+1) + M_PI/2)+2;

Еще один намек. Чтобы толщина менялась плавно, я ограничил ее в зависимости от толщины предыдущего сегмента и пользовательского коэффициента:

self.lineWidth = MAX(MIN(newWidth,lastWidth*WIDTH_RANGE_COEF),lastWidth/WIDTH_RANGE_COEF);
5 голосов
/ 23 сентября 2017

Я перевел ответ Кёдзи на Swift, как подкласс многократного использования UIImageView.Подкласс TouchDrawImageView позволяет пользователю рисовать на изображении пальцем.

После добавления этого класса TouchDrawImageView в свой проект обязательно откройте раскадровку и

* 1009.* выберите TouchDrawImageView в качестве «Пользовательского класса» для представления вашего изображения отметьте свойство «Взаимодействие с пользователем включено» для вашего представления изображения

Вот код TouchDrawImageView.swift:

import UIKit

class TouchDrawImageView: UIImageView {

    var previousPoint1 = CGPoint()

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard let touch = touches.first else { return }
        previousPoint1 = touch.previousLocation(in: self)
    }

    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard let touch = touches.first else { return }

        let previousPoint2 = previousPoint1
        previousPoint1 = touch.previousLocation(in: self)
        let currentPoint = touch.location(in: self)


        // calculate mid point
        let mid1 = midPoint(p1: previousPoint1, p2: previousPoint2)
        let mid2 = midPoint(p1: currentPoint, p2: previousPoint1)

        UIGraphicsBeginImageContext(self.frame.size)
        guard let context = UIGraphicsGetCurrentContext() else { return }
        if let image = self.image {
            image.draw(in: CGRect(x: 0, y: 0, width: frame.size.width, height: frame.size.height))
        }

        context.move(to: mid1)
        context.addQuadCurve(to: mid2, control: previousPoint1)

        context.setLineCap(.round)
        context.setLineWidth(2.0)
        context.setStrokeColor(red: 1.0, green: 0, blue: 0, alpha: 1.0)
        context.strokePath()

        self.image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
    }

    func midPoint(p1: CGPoint, p2: CGPoint) -> CGPoint {
        return CGPoint(x: (p1.x + p2.x) / 2.0, y: (p1.y + p2.y) / 2.0)
    }
}
3 голосов
/ 24 февраля 2011

Спасибо за ввод. Я обновляю свой квест здесь, потому что мне нужно место для него.

Я ищу решения кривой CorePlot и Bezier, которые вы предложили, с небольшим успехом.

Для corePlot я могу получить график графика из массива int, но не могу найти ничего, связанного со сглаживанием кривой. Кстати, здесь я использую CPScatterPlot с некоторым случайным числом.

enter image description here

Что касается кривой Безье, Мой квест привел меня к здесь Это как-то связано с реализацией Spline в iOS

  CatmullRomSpline *myC = [[CatmullRomSpline alloc] initAtPoint:CGPointMake(1.0, 1.0)];
  [myC addPoint:CGPointMake(1.0, 1.5)];
  [myC addPoint:CGPointMake(1.0, 1.15)];
  [myC addPoint:CGPointMake(1.0, 1.25)];
  [myC addPoint:CGPointMake(1.0, 1.23)];
  [myC addPoint:CGPointMake(1.0, 1.24)];
  [myC addPoint:CGPointMake(1.0, 1.26)];
  NSLog(@"xxppxx %@",[myC asPointArray]);
  NSLog(@"xxppxx2 %@",myC.curves);

и результат, который я получаю:

  2011-02-24 14:45:53.915 DVA[10041:40b] xxppxx (
  "NSPoint: {1, 1}",
  "NSPoint: {1, 1.26}"
   )

  2011-02-24 14:45:53.942 DVA[10041:40b] xxppxx2 (
  "QuadraticBezierCurve: 0x59eea70"
  )

Я не совсем уверен, как идти оттуда.Так что я застрял на этом фронте: (

Я действительно искал GLPaint, как последний ресурс. Он использует OpenGLES и использует спрайт «мягкой точки» для построения точек в массиве. Я знаю, что этобольше похоже на то, чтобы избежать проблемы, а не исправить ее. Но я думаю, что в любом случае я поделюсь своими выводами.

Черное - это GLPaint, а белое - старый метод. И последний - рисунок "Sketch Book "просто для сравнения

enter image description here enter image description here enter image description here

Я все еще пытаюсь сделать это правильно, любые дальнейшие предложения приветствуются.

1 голос
/ 24 февраля 2011

Чтобы избавиться от глупой точки в коде GLPaint.

Изменение

-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event

этой функции

//Ändrat av OLLE
/*
// Convert touch point from UIView referential to OpenGL one (upside-down flip)
if (firstTouch) {
    firstTouch = NO;
    previousLocation = [touch previousLocationInView:self];
    previousLocation.y = bounds.size.height - previousLocation.y;
} else {
    location = [touch locationInView:self];
    location.y = bounds.size.height - location.y;
    previousLocation = [touch previousLocationInView:self];
    previousLocation.y = bounds.size.height - previousLocation.y;
}
 */
location = [touch locationInView:self];
location.y = bounds.size.height - location.y;
previousLocation = [touch previousLocationInView:self];
previousLocation.y = bounds.size.height - previousLocation.y;

//Ändrat av OLLE//

Я знаю, что это не такрешение для нашей проблемы, но это что-то.

...