2017-11-26 3 views
0

Mein Ziel ist es, Adobe Illustrator wie Linien zu erzielen, wenn mein Benutzer über den Bildschirm streicht. Im Moment ist es sehr abgehackt - hier ist ein Screenshot von der aktuellen Situation:UIBezierPath - Glättungsdiagramm für abgehackte/verrauschte Daten

Noisy lines

Ich denke, es ist offensichtlich, was ich will. Ich möchte, dass diese geraden Linien geglättet werden. Wird das Anti-Aliasing genannt?

- (void)touchesBegan: (CGPoint)point 
{ 
    currentStroke = [[Stroke alloc] initWithStartPoint:point]; 
    [strokes addObject:currentStroke]; 
} 

- (void)touchesMoved: (CGPoint)point 
{ 
    if (!currentStroke) { 
     [self touchesBegan:point]; 
    } 
    [currentStroke.points addObject:[NSValue valueWithCGPoint:point]] 
    pathTwo = [self createPath]; 
    [self scaleToFit]; 
    [self setNeedsDisplay]; 
} 

- (UIBezierPath *)createPath { 
    UIBezierPath * bezierPath = [UIBezierPath bezierPath]; 
    for (Stroke * stroke in strokes) { 
     [bezierPath moveToPoint:stroke.startPoint]; 
     for (NSValue * value in stroke.points) { 
      [bezierPath addLineToPoint:[value CGPointValue]]; 
     } 
    } 
    return bezierPath; 
} 

- (void)touchesEnded: (CGPoint)point 
{ 
    [points removeAllObjects]; 
    [self setNeedsDisplay]; 
} 

drawRect hat dies darin:

[[UIColor whiteColor] setStroke]; 
[pathTwo setLineWidth: _strokeWidth]; 
[pathTwo stroke]; 

EDIT

Ich glaube, ich habe meine Antwort gefunden ... https://github.com/jnfisher/ios-curve-interpolation

Ich weiß nur nicht, wie Um dies zu verwenden, gab meine createPath Formel.

Vielleicht kann jemand mir helfen, meine Arrays einstecken?

+6

Nein, dies ist kein Antialiasing-Problem. Sie müssen Zugriff auf die einzelnen Punkte haben, die die gesamte Linie bilden, und eine Glättungsoperation anwenden, die etwa so aussieht: 1) Startpunkt und Endpunkt nehmen und nur durch die gerade Linie dazwischen ersetzen oder 2) Mittelung des Neigung mehrerer Segmente usw. Viele Möglichkeiten, Kurven zu glätten. –

+0

Kannst du mir Code für Option # 2 zeigen? – noodlez

+0

Wie werden Ihre Daten gespeichert? Haben Sie Zugang zu einem Array von Punkten (x, y)? Ich kann helfen, aber es ist wichtig, mit was Sie arbeiten, um anzufangen. Mit anderen Worten, hat jeder Punkt auch die "Zeit" oder eine Angabe, die ihn in die richtige Reihenfolge relativ zur Linie bringt? UND wie planen Sie, es neu zu zeichnen? Sie müssen sicher sein, dass das zuerst funktioniert. –

Antwort

0

Vielen Dank für Ihre Unterstützung. Ich war eine Weile weg. Aber ich fand eine Lösung für meine Situation, die phänomenal ist. Es war eine Klasse, die interpoliert.

https://github.com/jnfisher/ios-curve-interpolation

Alles, was ich tun musste, war meine Reihe von Punkten übergeben und es glättet sich aus!

5

Das Problem ist, dass Sie einen Pfad zwischen einer Reihe von Punkten in einer Geste streichen, und diese Punkte sind nicht alle auf der gleichen Linie, weil sie natürliche Abweichungen enthalten, die stattfinden, wenn der Benutzer den Finger über die zieht Bildschirm.

Dies steht in keinem Zusammenhang mit "Anti-Aliasing", einer Technik, die eine gerade Linie glatter erscheinen lässt, ohne visuell "jaggies" zu stören. Aber das Problem hier ist nicht, dass Sie versuchen, das Rendern einer einzelnen geraden Linie zu verbessern, sondern dass Sie nicht gerade eine gerade Linie zeichnen, sondern stattdessen eine Reihe von Liniensegmenten zeichnen, die das nicht tun passieren zu richten.

Mein Ziel ist es Adobe Illustrator wie Linien zu erreichen, wenn mein Benutzer über den Bildschirm streicht

In diesem Fall Sie nicht wollen, „Anti-Aliasing“. Alles, was Sie brauchen, ist ein createPath, dass

  • moveToPoint zum ersten Punkt; und
  • addLineToPoint bis zum letzten Punkt.

Machen Sie sich keine Sorgen über irgendwelche Punkte dazwischen. Diese einfache Lösung ergibt eine einzelne gerade Linie, die überall dort beginnt, wo Ihre Berührungen begonnen haben und sich dorthin erstrecken, wo Ihre Berührungen enden, und wird dies weiterhin tun, bis Sie Ihren Finger heben.

denke ich, dass etwas übersetzen wird wie:

- (UIBezierPath *)createPath { 
    UIBezierPath * bezierPath = [UIBezierPath bezierPath]; 
    [bezierPath moveToPoint:stroke.startPoint]; 
    [bezierPath addLineToPoint:[[stroke.points lastObject] CGPointValue]]; 
    return bezierPath; 
} 

Wenn Sie nicht über eine tatsächliche Linie wollen, sondern wollen nur diese glätten, ich würde vorschlagen, (a) Sie rollen Durchschnitt, um Spitzen und Täler in Ihren hüpfenden Datenpunkten zu eliminieren; und (b) Hermite- oder Catmull-Rom-Spline verwenden (wie in https://spin.atomicobject.com/2014/05/28/ios-interpolating-points/ umrissen), so dass der resultierende Pfad eher glatt ist als eine Reihe von Liniensegmenten.

So

- (UIBezierPath * _Nullable)createActualPathFor:(Stroke *)stroke { 
    if (stroke.points.count < 2) return nil; 

    UIBezierPath * path = [UIBezierPath bezierPath]; 

    [path moveToPoint:[stroke.points.firstObject CGPointValue]]; 
    for (NSInteger i = 1; i < stroke.points.count; i++) { 
     [path addLineToPoint:[stroke.points[i] CGPointValue]]; 
    } 

    return path; 
} 

- (UIBezierPath * _Nullable)createSmoothedPathFor:(Stroke *)stroke interval:(NSInteger)interval { 
    if (stroke.points.count < 2) return nil; 

    NSMutableArray <NSValue *> *averagedPoints = [NSMutableArray arrayWithObject:stroke.points[0]]; 

    NSInteger current = 1; 
    NSInteger count = 0; 
    do { 
     CGFloat sumX = 0; 
     CGFloat sumY = 0; 
     do { 
      CGPoint point = [stroke.points[current] CGPointValue]; 
      sumX += point.x; 
      sumY += point.y; 
      current++; 
      count++; 
     } while (count < interval && current < stroke.points.count); 

     if (count >= interval) { 
      CGPoint average = CGPointMake(sumX/count, sumY/count); 
      [averagedPoints addObject:[NSValue valueWithCGPoint:average]]; 
      count = 0; 
     } 
    } while (current < stroke.points.count); 

    if (count != 0) { 
     [averagedPoints addObject:stroke.points.lastObject]; 
    } 

    return [UIBezierPath interpolateCGPointsWithHermite:averagedPoints closed:false]; 
} 

Werden ergeben die folgende (wobei die tatsächlichen Datenpunkte in blau sind und die geglättete Wiedergabe ist in rot):

enter image description here

Hinweis, ich gehe davon aus, dass Ihr Array von points von Stroke enthält den Startpunkt, also müssen Sie das entsprechend anpassen.

+0

Frage: Ich meinte nicht nur eine gerade Linie, ich meine, ich möchte meine Scribbles "glatt" sein – noodlez

+1

Wenn Sie Wenn Sie nur Ihre Scribbles glätten möchten, können Sie Hermite oder Catmull-Rom Splines wie https://stackoverflow.com/a/34583708/1271826 oder https://spin.atomicobject.com/2014/05/28/ios- Interpolationspunkte /. Aber diese werden immer noch versuchen, alle deine Punkte zu durchlaufen, nur um sie zu glätten. Wenn Sie das nicht möchten, müssen Sie sich vielleicht eine "beste Anpassung" oder eine Regression höherer Ordnung ansehen. – Rob

+0

https://github.com/jnfisher/ios-curve-interpolation/blob/master/Curve%20Interpolation/UIBezierPath%2BInterpolation.h Das sieht gut aus. Ich kann einfach nicht herausfinden, wie ich meine Punkte einbinden soll !? Kannst du mir ein Beispiel mit der createPath-Funktion zeigen, die ich gepostet habe ?, = stroke = erster Stroke (touchBegan) und die Punkte innerhalb des Strokes sind diejenigen, die seiner Verfolgungszeit entsprechen touchend – noodlez