2016-08-18 25 views
7

Jestem w stanie wygenerować UIBezierPath postaci z dowolną czcionką i rozmiarem. Teraz chcę wytyczyć linię pomiędzy ścieżką Beziera. Czy mogę uzyskać środkowe punkty ścieżki Beziera? Lub w inny sposób, w jaki mogę uczynić środek linii przerywanej i podążać tą ścieżką?Zdobądź punkty centralne UIBezierpath

Oto kod jak zrobić tak

Reference Link. I want something like this.:

UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, 568, 568) cornerRadius:0]; 
UIBezierPath *circlePath = [self createArcPath]; 
[path appendPath:circlePath]; 
[path setUsesEvenOddFillRule:YES]; 





shapeView = [CAShapeLayer layer]; 
shapeView.geometryFlipped = false; 
shapeView.path = path.CGPath; 
shapeView.fillRule = kCAFillRuleEvenOdd; 
shapeView.fillColor = [UIColor grayColor].CGColor; 
shapeView.opacity = 1.0; 
shapeView.lineDashPattern = @[@2, @3]; 
[self.view.layer addSublayer:shapeView]; 

CGFloat dashes[] = {2, 3}; 
[path setLineDash:dashes count:2 phase:0]; 


- (UIBezierPath *)createArcPath 
{ 
// Create path from text 
// See: http://www.codeproject.com/KB/iPhone/Glyph.aspx 
// License: The Code Project Open License (CPOL) 1.02 http://www.codeproject.com/info/cpol10.aspx 
letters = CGPathCreateMutable(); 

CTFontRef font = CTFontCreateWithName(CFSTR("Helvetica-Bold"),80, NULL); 
NSDictionary *attrs = [NSDictionary dictionaryWithObjectsAndKeys: 
         (__bridge id)font, kCTFontAttributeName,//د 
         nil];//ج 
NSAttributedString *attrString = [[NSAttributedString alloc] initWithString:@"H" 
                   attributes:attrs]; 
CTLineRef line = CTLineCreateWithAttributedString((CFAttributedStringRef)attrString); 
CFArrayRef runArray = CTLineGetGlyphRuns(line); 

// for each RUN 
for (CFIndex runIndex = 0; runIndex < CFArrayGetCount(runArray); runIndex++) 
{ 
    // Get FONT for this run 
    CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runArray, runIndex); 
    CTFontRef runFont = CFDictionaryGetValue(CTRunGetAttributes(run), kCTFontAttributeName); 

    // for each GLYPH in run 
    for (CFIndex runGlyphIndex = 0; runGlyphIndex < CTRunGetGlyphCount(run); runGlyphIndex++) 
    { 
     // get Glyph & Glyph-data 
     CFRange thisGlyphRange = CFRangeMake(runGlyphIndex, 1); 
     CGGlyph glyph; 
     CGPoint position; 
     CTRunGetGlyphs(run, thisGlyphRange, &glyph); 
     CTRunGetPositions(run, thisGlyphRange, &position); 

     // Get PATH of outline 
     { 
      CGPathRef letter = CTFontCreatePathForGlyph(runFont, glyph, NULL); 
      CGAffineTransform t = CGAffineTransformMakeTranslation(position.x+200, position.y+80); 
      CGPathAddPath(letters, &t, letter); 
      CGPathRelease(letter); 
     } 
    } 
} 
CFRelease(line); 

self.path = [UIBezierPath bezierPath]; 
[self.path appendPath:[UIBezierPath bezierPathWithCGPath:letters]]; 

return self.path; 


} 

The center dots points I want to draw which is in the cenyter of the bezierPath

+0

http://stackoverflow.com/a/31698463/988769 – Kreiri

+0

jej nie działa. Mam zakrzywioną ścieżkę dla różnych alfabetów. Również chcę narysować wewnątrz ścieżki, a nie zarys. – NiKKi

+0

Czy próbowałeś tego https://stackoverflow.com/questions/34693651/how-to-draw-line-on-image-pattern-exactly/34723668#34723668? –

Odpowiedz

2

Podczas Beziera ścieżki są wygodnym rozwiązaniem dla prostego rysunku, są one skomplikowane obliczenia, więc to będzie zbyt drogie i skomplikowane w użyciu w tym celu. Dzieje się tak dlatego, że musisz umieć obliczać dowolne punkty wzdłuż ścieżki i znać poprzedni punkt, aby poznać aktualny kierunek.

Potrzebujesz innego opisu postaci, aby łatwiej poznać te szczegóły. Oznacza to użycie prostego wektora (lub obrysu) opisu czcionek (o czym wspomina w komentarzach rob mayoff). Ten opis dzieli znaki na kilka prostych odcinków, z którymi łatwo się pracuje. Wszystkie są zarządzane przez obliczenia y=mx+c i zawsze wiesz 2 punkty na każdym segmencie linii, więc łatwo jest interpolować między nimi i znać kierunek ruchu.

Jeśli opisy w linku podanym przez rob (here) nie są "dokładne" na tyle, na ile chcesz wyświetlać znaki, możesz utworzyć nowe wersje z większą liczbą punktów w celu uzyskania bliższego zbliżenia do Opcje krzywej Beziera.

Teraz, z tego opisu masz wiele opcji ...

Dla przeciągając palcem wzdłuż toru można interpolować między punktami, aby znaleźć najbliższy punkt na ścieżce do bieżącego punktu próbkowania i określić, kiedy punkt dotykowy zboczył zbyt daleko od ścieżki lub przeciął go. Przetwarzanie tego skrzyżowania pozwala także określić procent pokrycia. Trzeba tylko wybrać odległość przerwy interpolacji (wybraną dla odpowiedniej rozdzielczości bez tworzenia zbyt wielu punktów, które są naprawdę blisko siebie) i tolerancję na to, jak daleko mogą być punkty dotykowe od "następnego" punktu ścieżki.

Pozwala to również na inne rzeczy w łączonym wideo, np. Upuszczanie obrazów w każdym punkcie ścieżki (interpolowanej) i animowanie tych obrazów.

3

Czy mogę uzyskać środkowe punkty ścieżki Beziera? Lub w jakikolwiek inny sposób, że mogę zrobić środek linii przerywanej i podążać tą ścieżką?

Aby dowiedzieć palec tego autora jedzie wzdłuż ścieżki lub nie masz do HIT-Detection na ścieżce.

Aby ustalić, czy zdarzenie wystąpiło w dotyku napełnionego fragmencie ścieżce, można użyć containsPoint: metodę UIBezierPath. Ta metoda testuje określony punkt względem wszystkich zamkniętych ścieżek podrzędnych obiektu ścieżki i zwraca wartość TAK, jeśli znajduje się na jednej lub pod jedną z tych podrzędnych ścieżek .

Jeśli chcesz wykonać test trafień na obcinanej części ścieżki (zamiast obszaru wypełnienia), musisz użyć Core Graphics. Funkcja CGContextPathContainsPoint umożliwia testowanie punktów na części wypełnienia lub obrysu ścieżki aktualnie przypisanej do kontekstu grafiki.

Poniżej testów metod, aby sprawdzić, czy określony punkt przecina określoną ścieżkę. Parametr inFill pozwala dzwoniącemu określić, czy punkt powinien zostać przetestowany względem wypełnionej lub obrysowanej części ścieżki. Ścieżka przekazana przez wywołującego musi zawierać jedną lub więcej zamkniętych ścieżek podrzędnych, aby wykrywanie trafienia powiodło się.

Testowanie punktów względem obiektu ścieżki.

- (BOOL)containsPoint:(CGPoint)point onPath:(UIBezierPath *)path inFillArea:(BOOL)inFill 
{ 
    CGContextRef context = UIGraphicsGetCurrentContext(); 
    CGPathRef cgPath = path.CGPath; 
    BOOL isHit = NO; 

    // Determine the drawing mode to use. Default to 
    // detecting hits on the stroked portion of the path. 
    CGPathDrawingMode mode = kCGPathStroke; 
    if (inFill) 
    { 
     // Look for hits in the fill area of the path instead. 
     if (path.usesEvenOddFillRule) 
     mode = kCGPathEOFill; 
     else 
     mode = kCGPathFill; 
    } 

    // Save the graphics state so that the path can be removed later. 
    CGContextSaveGState(context); 
    CGContextAddPath(context, cgPath); 

    // Do the hit detection. 
    isHit = CGContextPathContainsPoint(context, point, mode); 

    CGContextRestoreGState(context); 

    return isHit; 
} 

Sprawdź ten link, aby dowiedzieć się więcej o Hit-Detection on a Path

Uzyskiwanie środek ścieżki

można uzyskać szerokość ścieżki jak myPath.bounds.size.width; i uzyskać środek ścieżki tylko podzielić szerokość przez 2.

I rysować sprawdzić linia przerywana to answer

Aby linia przerywana na dowolny UIBezierPath jak:

CGFloat dashes[] = {2, 3}; 
//passing an array with the values {2,3} sets a dash pattern that alternates between a 2 space-unit-long painted segment and a 3 space-unit-long unpainted segment. 
UIBezierPath *path = [UIBezierPath bezierPath]; 
[path setLineDash:dashes count:2 phase:0]; 

i pędzić na CAShapeLayer użyć właściwości lineDashPattern jak:

shapeView.lineDashPattern = @[@2, @3]; 
+0

Nie otrzymuję trafienia tą metodą, – NiKKi

+0

spróbuj użyć 'CGPathContainsPoint (ścieżka, NULL, punkt, NO);' zamiast 'CGContextPathContainsPoint (kontekst, punkt, tryb);' –

+0

Aby narysować przerywaną linię, łączysz losowania od początku do końca. Moje postacie są zakrzywione. – NiKKi

0

Spróbuj wpisać ten kod, aby narysować czerwoną linię:

CAShapeLayer* dotLayer = [CAShapeLayer layer]; 
dotLayer.path   = path.CGPath; 
dotLayer.strokeColor  = [UIColor redColor].CGColor; 
dotLayer.lineWidth  = 2;//or the diameter of the dots 
dotLayer.lineDashPattern = @[@0, @10];//change 10 to the distance between dots 
dotLayer.lineJoin  = kCALineJoinRound; 
dotLayer.lineCap   = kCALineCapRound; 
+0

Jeśli czytasz pytanie, problem polega na narysowaniu kropkowanej linii na środku UIBezierpath. –

2

Można obliczyć punkty wzdłuż UIBezierPath z kodem w tym open source repo:

https://github.com/ImJCabus/UIBezierPath-Length

jeśli masz 'a' Znak reprezentowany jako ścieżki Beziera, można obliczyć czerwone punkty na powyższym obrazku, pisząc coś takiego:

UIBezierPath *path = /* ... */; 
NSUInteger subdivisions = 100;//however precise you want to be 
for(NSUInteger i = 0; i < subdivisions; i++) { 
    CGFloat percent = (CGFloat)i/subdivisions; 
    CGPoint point = [path pointAtPercentOfLength:percent]; 
    //draw a dot at this point, or trace it, or whatever you want to do 
} 
+0

To wychodzi jako punkt konturu. Nie punkty centralne. – NiKKi

+0

Jeśli twoje wejście jest zarysem glifu z 'CTFontCreatePathForGlyph' i chcesz algorytmu, który wyprowadza ścieżkę, która reprezentuje jak narysować glif, będzie to bardzo trudne (jeśli to możliwe), aby działało w 100% prawidłowo. Jest mnóstwo zwariowanych glifów, które twój algorytm musiałby zinterpretować, więc nie ma prawie żadnych założeń, które możesz zrobić, analizując dane wejściowe. W filmie, do którego się połączyłeś, domyślam się, że zaczęło się od ścieżki środkowej i narysowałem postać używającą tej ścieżki na określonej szerokości zamiast używać konturu. – BradB

0

To, czego chcesz, jest dość skomplikowane, ale uważam, że można je osiągnąć z następujących etapów:

1) uzyskać ścieżkę list konspektu przy użyciu metody

2) stworzyć nową ścieżkę przy użyciu CGPathCreateCopyByStrokingPath z lineWidth równą 1

3) get all segments of the new path

4) find all intersection points of all segments

5) określ, które sąsiadujące ze sobą linie sąsiadują i wykorzystaj ich punkty przecięcia, aby utworzyć linię środkową

6) powtórz kroki 2-6 zwiększając wartość lineWidth w kroku 2