iPhone smooth sketch drawing algorithm
Pracuję nad aplikacją szkicującą na iPhone ' a. Mam go działa, ale nie ładny jak widać tutaj
I szukam jakiejkolwiek sugestii, aby wygładzić rysunek Zasadniczo, to, co zrobiłem, to kiedy użytkownik kładzie palec na ekranie, który wywołałem
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
Następnie zbieram Pojedynczy dotyk w tablicy z
- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
I kiedy użytkownik wyciągnie palec z ekranu, zadzwoniłem
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
Następnie rysuję wszystkie punkty w tablicy używając
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);
And now I want to popraw rysunek, aby bardziej przypominał aplikację" Sketch Book"
Myślę, że jest coś wspólnego z algorytmem przetwarzania sygnału, aby zmienić wszystkie punkty w tablicy, ale nie jestem pewien. Każda pomoc będzie mile widziana.
Z góry dziękuję:)
6 answers
Najprostszym sposobem wygładzenia krzywej takiej jak ta jest użycie krzywej Beziera zamiast odcinków prostych. Dla matematyki za tym, Zobacz Ten artykuł (wskazywany w ta odpowiedź), który opisuje, jak obliczyć krzywe wymagane do wygładzenia krzywej, która przechodzi przez wiele punktów.
Wierzę, że Core Plot framework ma teraz możliwość wygładzenia krzywych Wykresów, więc można spojrzeć na kod używany tam do implementacji tego rodzaju wygładzanie.
Nie ma w tym żadnej magii, ponieważ procedury wygładzania są szybkie i stosunkowo łatwe do wdrożenia.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:32:35
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();
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-31 14:20:33
Naprawdę podoba mi się ten temat. Dziękuję za wszystkie realizacje, szczególnie Krzysztofowi Zabłockiemu i Yu-Sen Han. Zmodyfikowałem wersję Yu-Sen Han, aby zmienić grubość linii w zależności od prędkości panoramowania (w rzeczywistości odległość między ostatnimi dotknięciami). Również zaimplementowałem rysowanie kropek (dla touchbegan i touchEnded lokalizacje są blisko siebie) Oto wynik:
Aby określić grubość linii Wybrałem taką funkcję odległości:
(nie zapytaj dlaczego... Po prostu uważam, że pasuje dobrze, ale jestem pewien, że znajdziesz lepszy)
CGFloat dist = distance(previousPoint1, currentPoint);
CGFloat newWidth = 4*(atan(-dist/15+1) + M_PI/2)+2;
Jeszcze jedna wskazówka. Aby mieć pewność, że grubość zmienia się płynnie, ograniczyłem ją w zależności od grubości poprzedniego segmentu i niestandardowego coef:
self.lineWidth = MAX(MIN(newWidth,lastWidth*WIDTH_RANGE_COEF),lastWidth/WIDTH_RANGE_COEF);
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-24 21:51:12
Dziękuję za wkład.Aktualizuję moje zadanie tutaj, ponieważ potrzebuję na to miejsca.
Sprawdzam zarówno rozwiązania corePlot, jak i Bezier curve, które zaproponowałeś z niewielkim sukcesem.
Dla corePlot jestem w stanie uzyskać Wykres z tablicy int, ale nie mogę znaleźć niczego związanego z wygładzaniem krzywych.BTW tutaj używam CPScatterPlot z jakimś losowym numerem.
Co do krzywej Beziera, to moje poszukiwania prowadzą mnie do tutaj jest to coś wspólnego ze Splinem implementacja w 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);
A wynik jaki otrzymuję to:
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"
)
Nie jestem pewien, jak stąd wyjść. Więc ja też utknąłem na tym froncie: (
Szukałem GLPaint, jako ostatniego zasobu. Używa Opengle i używa "miękkiej kropki" sprite do wykreślenia punktów w tablicy. Wiem, że to bardziej unikanie problemu, niż naprawianie go. Ale i tak podzielę się swoimi odkryciami.
Czarny to GLPaint, a biały to stara metoda. I ostatni czy rysunek z aplikacji "Sketch Book" jest tylko po to, aby porównaćNadal staram się to zrobić dobrze, wszelkie dalsze sugestie są mile widziane.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-13 10:57:24
Przetłumaczyłem odpowiedź kyoji na Swift, jako podklasę wielokrotnego użytku UIImageView
. Podklasa TouchDrawImageView
pozwala użytkownikowi rysować na widoku obrazu palcem.
Po dodaniu tej klasy TouchDrawImageView
do swojego projektu, upewnij się, że otworzysz storyboard i
- Wybierz
TouchDrawImageView
jako "klasę niestandardową" widoku obrazu - zaznacz właściwość "interakcja użytkownika włączona" w widoku obrazu
Oto kod 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)
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-03 21:55:44
Aby pozbyć się głupiej kropki w kodzie GLPaint.
Zmiana w
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
Ta funkcja
//Ä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//
Wiem, że to nie jest rozwiązanie naszego problemu, ale to coś.Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-13 10:53:28