2011-06-26 10 views
12

में रेखाएं खींचते समय पहचान को हिट करें, मैं उपयोगकर्ता को वक्र को इस तरह से आकर्षित करने की अनुमति देना चाहता हूं कि कोई भी रेखा किसी अन्य रेखा या यहां तक ​​कि स्वयं को पार न कर सके। वक्रों को चित्रित करना कोई समस्या नहीं है, और मैंने यह भी पाया कि मैं एक पथ बना सकता हूं जो बंद है और अभी भी लाइन के नोड्स को पीछे और पीछे और फिर पथ को बंद करके ट्रेस कर सकता है।आईओएस

दुर्भाग्यवश, आईओएस केवल एक परीक्षण प्रदान करता है कि एक बंद पथ में एक बिंदु निहित है (इसमें पॉइंट: और CGPathContainsPoint) है। दुर्भाग्यवश, उपयोगकर्ता आसानी से अपनी अंगुली को इतनी आसानी से ले जा सकता है कि स्पर्श पथ मौजूदा पथ के दोनों किनारों पर वास्तव में उस पथ से निहित किए बिना जमीन को इंगित करता है, इसलिए स्पर्श बिंदुओं का परीक्षण करना काफी व्यर्थ है।

मुझे पथ विधि का कोई "छेड़छाड़" नहीं मिल रहा है।

इस कार्य को पूरा करने के तरीके पर कोई अन्य विचार?

+0

यह प्रश्न किसी अन्य SO प्रश्न के समान है। http://stackoverflow.com/questions/1021801/cgpathref-intersection उन उत्तरों ने प्रत्येक व्यक्तिगत पिक्सेल को देखने का सुझाव दिया जो धीमा हो जाएगा। आप myBezierPath.CGPath – Andrew

+0

द्वारा अपने UIBezierPath ऑब्जेक्ट से एक CGPathRef प्राप्त कर सकते हैं, इसी तरह के प्रश्न पर अच्छी पकड़ लें। मैं ऐसे दृष्टिकोण पर काम कर रहा हूं जो लगातार बिटमैप्स की तुलना करता है। एक बार मेरे पास प्रदर्शन कोड हो जाने के बाद, मैं इसे यहां रखूंगा। इस बीच, मैं उस प्रश्न के उत्तर भी देखूंगा। – EFC

उत्तर

6

ठीक है, मैं ऐसा करने के लिए एक तरीका आया था। यह अपूर्ण है, लेकिन मैंने सोचा कि दूसरों को तकनीक देखना चाहेंगे क्योंकि इस सवाल को कुछ बार उखाड़ फेंक दिया गया था। मैं जिस तकनीक का उपयोग करता हूं वह सभी आइटम्स को बिटमैप संदर्भ में परीक्षण करने के लिए खींचता है और फिर प्रगतिशील रेखा के नए खंड को किसी अन्य बिटमैप संदर्भ में खींचता है। उन संदर्भों में डेटा की तुलना बिटटाइव ऑपरेटरों का उपयोग करके की जाती है और यदि कोई ओवरलैप पाया जाता है, तो हिट घोषित की जाती है।

इस तकनीक के पीछे विचार सभी पिछली खींची गई रेखाओं और यहां तक ​​कि उसी पंक्ति के पहले के टुकड़ों के खिलाफ एक नई खींची गई रेखा के प्रत्येक खंड का परीक्षण करना है। दूसरे शब्दों में, यह तकनीक तब पता लगाएगी जब एक रेखा दूसरी पंक्ति को पार करती है और जब यह स्वयं पर पार हो जाती है।

तकनीक का प्रदर्शन करने वाला एक नमूना ऐप उपलब्ध है: LineSample.zip

हिट परीक्षण का मूल मेरी लाइन व्यू ऑब्जेक्ट में किया जाता है। यहां दो मुख्य विधियां हैं:

- (CGContextRef)newBitmapContext { 

    // creating b&w bitmaps to do hit testing 
    // based on: http://robnapier.net/blog/clipping-cgrect-cgpath-531 
    // see "Supported Pixel Formats" in Quartz 2D Programming Guide 
    CGContextRef bitmapContext = 
    CGBitmapContextCreate(NULL, // data automatically allocated 
          self.bounds.size.width, 
          self.bounds.size.height, 
          8, 
          self.bounds.size.width, 
          NULL, 
          kCGImageAlphaOnly); 
    CGContextSetShouldAntialias(bitmapContext, NO); 
    // use CGBitmapContextGetData to get at this data 

    return bitmapContext; 
} 


- (BOOL)line:(Line *)line canExtendToPoint:(CGPoint) newPoint { 

    // Lines are made up of segments that go from node to node. If we want to test for self-crossing, then we can't just test the whole in progress line against the completed line, we actually have to test each segment since one segment of the in progress line may cross another segment of the same line (think of a loop in the line). We also have to avoid checking the first point of the new segment against the last point of the previous segment (which is the same point). Luckily, a line cannot curve back on itself in just one segment (think about it, it takes at least two segments to reach yourself again). This means that we can both test progressive segments and avoid false hits by NOT drawing the last segment of the line into the test! So we will put everything up to the last segment into the hitProgressLayer, we will put the new segment into the segmentLayer, and then we will test for overlap among those two and the hitTestLayer. Any point that is in all three layers will indicate a hit, otherwise we are OK. 

    if (line.failed) { 
     // shortcut in case a failed line is retested 
     return NO; 
    } 
    BOOL ok = YES; // thinking positively 

    // set up a context to hold the new segment and stroke it in 
    CGContextRef segmentContext = [self newBitmapContext]; 
    CGContextSetLineWidth(segmentContext, 2); // bit thicker to facilitate hits 
    CGPoint lastPoint = [[[line nodes] lastObject] point]; 
    CGContextMoveToPoint(segmentContext, lastPoint.x, lastPoint.y); 
    CGContextAddLineToPoint(segmentContext, newPoint.x, newPoint.y); 
    CGContextStrokePath(segmentContext); 

    // now we actually test 
    // based on code from benzado: http://stackoverflow.com/questions/6515885/how-to-do-comparisons-of-bitmaps-in-ios/6515999#6515999 
    unsigned char *completedData = CGBitmapContextGetData(hitCompletedContext); 
    unsigned char *progressData = CGBitmapContextGetData(hitProgressContext); 
    unsigned char *segmentData = CGBitmapContextGetData(segmentContext); 

    size_t bytesPerRow = CGBitmapContextGetBytesPerRow(segmentContext); 
    size_t height = CGBitmapContextGetHeight(segmentContext); 
    size_t len = bytesPerRow * height; 

    for (int i = 0; i < len; i++) { 
     if ((completedData[i] | progressData[i]) & segmentData[i]) { 
      ok = NO; 
      break; 
     } 
    } 

    CGContextRelease(segmentContext); 

    if (ok) { 
     // now that we know we are good to go, 
     // we will add the last segment onto the hitProgressLayer 
     int numberOfSegments = [[line nodes] count] - 1; 
     if (numberOfSegments > 0) { 
      // but only if there is a segment there! 
      CGPoint secondToLastPoint = [[[line nodes] objectAtIndex:numberOfSegments-1] point]; 
      CGContextSetLineWidth(hitProgressContext, 1); // but thinner 
      CGContextMoveToPoint(hitProgressContext, secondToLastPoint.x, secondToLastPoint.y); 
      CGContextAddLineToPoint(hitProgressContext, lastPoint.x, lastPoint.y); 
      CGContextStrokePath(hitProgressContext); 
     } 
    } else { 
     line.failed = YES; 
     [linesFailed addObject:line]; 
    } 
    return ok; 
} 

मुझे सुझाव सुनना या सुधार देखना अच्छा लगेगा। एक बात के लिए, पूरे दृश्य के बजाय नए सेगमेंट के बाउंडिंग रेक्ट को जांचना बहुत तेज़ होगा।

+2

उचित चेतावनी: मुझे नमूना ऐप में कुछ बग मिल चुकी हैं, इसलिए सुनिश्चित करें कि आप अपने कार्यान्वयन पर ध्यान दें। मूल तकनीक काम करने लगती है, केवल कुछ कार्यान्वयन के मुद्दों को बेहतर किया जा सकता है। मैं कुछ और नमूना के साथ झुकाऊंगा और इसे अद्यतन रखूंगा, लेकिन मेरा मुख्य फोकस कहीं और होगा। – EFC

+0

हाय @ ईएफसी, मैं समुदाय के लिए नया हूं और एक नौसिखिया आईओएस प्रोग्रामर हूं, क्या आप मुझे विशेष रूप से इंगित कर सकते हैं कि वह कोड कहां है जहां यह स्वयं को छेड़छाड़ करने से रोकता है? मुझे केवल उस हिस्से की जरूरत है। – EdSniper

+0

छेड़छाड़ को रोकने के लिए मैं बस यह देखने के लिए देख रहा हूं कि पुराने और नए सेगमेंट के बीच कोई बिट सामान्य है या नहीं। 'अगर ((पूरा डेटा [i] | progressData [i]) और सेगमेंटडाटा [i]) {' पंक्ति वह वास्तविक परीक्षण क्या करती है। यह परीक्षण http://stackoverflow.com/a/6515999/383737 से आया था। – EFC