2

i have some uibezierpaths. as paths, they don't really have thickness. but, I am hoping to find a way to define an area around a path like the grayish areas around the lines in this pictureenter image description here

basically, i want to test whether drawn lines fall within the buffer zone around the lines. i thought this would be simple, but it's turning out to be much more complex than i thought. I can use the CGPathApply function to examine the points along my path, and then getting a range +or- each point, but it's more complicated than that with angles and curves. any ideas?

Lordof Theflies
  • 301
  • 1
  • 6
  • 16

2 Answers2

1

Expanding the width of a path is actually quite difficult. However, you could just stroke it with a thicker width and get pretty much the same effect. Something like...

CGContextSetRGBStrokeColor(context, 0.4, 0.4, 0.4, 1.0);
[path setLineWidth:15];
[path stroke];
CGContextSetRGBStrokeColor(context, 0.0, 0.0, 0.0, 1.0);
[path setLineWidth:3];
[path stroke];

...would produce a picture like the one in your question. But I doubt that's news to you.

The real trick is the test of "whether drawn lines fall within the buffer zone." That problem is very similar to one which I just answered for myself in another question. Take a look at the LineSample.zip code I shared there. This implements a bitmap/bitwise data comparison to detect hits on lines much like you need. You could just draw the thicker "buffer" paths into the bitmap for testing and show the thinner lines in your view.

Community
  • 1
  • 1
EFC
  • 1,890
  • 18
  • 39
  • thanks! I was wondering how to deal with line width like that. (the images i put up here were made by simply copy and paste + change line thickness. but i couldn't think of how to get the bordes of the changed thickness since the path itself was the same. I'll look at the cose you shared. much appreciated. – Lordof Theflies Jun 29 '11 at 23:39
  • that's a great app. I need some time to look at the code, but it should be quite helpful for my goals. Quick question, in your app, when i draw a new line crossing a ghost line it stays black as if it didn't cross anything. i didn't know if that is intentional or not. – Lordof Theflies Jun 29 '11 at 23:47
  • Yes, the ghost lines (the "failed" lines) are just left so you can really see what path they followed. The only lines that "block" other lines are the black and green ones. Note, in the code, that the ghost lines are drawn in the view (that's why they are visible) but not in the hit test context (which is part of what new line segments are tested against). However, beware: I took some shortcuts in that code that you'll want to reconsider if you use it. I'll probably update the sample after a bit more tweaking, but the basic principle is sound, I think. – EFC Jun 30 '11 at 01:38
  • ok. I actually saw the website you referenced before, but didn't quite understand it. with your code, now I might make some progress. I'm a bit of a beginner, so it might take some time to figure out. But, it seems the basic logic is to create a bitmap context layer that is the same as my path, but with a much thicker line. Then test whether the drawn line is within its bounds. is that right? that sounds perfect if I can do it. As some of my lines actually do cross themselves, it might end up being easier than what you did. at least if I can do it ... – Lordof Theflies Jun 30 '11 at 05:36
  • Good luck! FYI, the sample has been updated to get rid of the shortcuts I mentioned. If you don't have to worry about the line you are drawing crossing itself, you can just remove everything referring to the hitProgressContext. – EFC Jun 30 '11 at 05:44
  • thanks again. I wonder if you can answer another question that might be a bit obvious. In creating the buffer zone from my path, since I already have them all saved, is there a way to add it whole into the CGContextRef instead of going through each segment, moving to the next point and adding the next line? – Lordof Theflies Jun 30 '11 at 06:44
  • it seems like CGContextAddPath should work, but i'm sure. if i did CGContextAddPath(context, path); where context is the new bitmap context, would that make sense? – Lordof Theflies Jun 30 '11 at 06:52
  • OK! Thank you EFC! that ended up working beautifully and was much simpler than I expected. and detection is fast! i can easily vary the width of the buffer zone with linewidth, and even calculate what percent of the drawn line is inside the bounds. very very happy now. – Lordof Theflies Jun 30 '11 at 08:02
  • actually, i spoke too soon. my problem now is that the 2 contexts i'm comparing are not the same dimensions. the thinner line doesn't match up on top of the thicker one in terms of matching the bites because there are fewer of them. does that make sense? any ideas about how i can add empty space into the other context to make it the same size? – Lordof Theflies Jun 30 '11 at 09:48
  • Sorry, I didn't notice the hidden comments before now. The key to this approach would be to draw both lines into contexts of the same size. Is there a reason they couldn't be the same? I guess I'm not clear on what you are trying to accomplish... – EFC Jul 01 '11 at 03:53
  • @LordofTheflies let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/1038/discussion-between-efc-and-lordof-theflies) – EFC Jul 01 '11 at 03:54
0

Basically, you want to check if any point falls inside a region of specified size around your path.

It is actually very simple to do. First, you need a value which will define the amount of space around path you want to test. Let's say 20 points. So what you need to do is start a FOR loop, starting from -20 to 20, and at each iteration, create a copy of your path, translate the path's x and y co-odrinates, check each of them.

All of this is more clear in this code sample.

CGPoint touchPoint = /*get the point*/;

NSInteger space = 20;

for (NSInteger i = -space; i < space; i++) {

    UIBezierPath *pathX = [UIBezierPath bezierPathWithCGPath:originalPath.CGPath];
    [pathX applyTransform:CGAffineTransformMakeTranslation(i, 0)];

    if ([pathX containsPoint:touchPoint]) {
        /*YEAH!*/
    }

    else {
        UIBezierPath *pathY = [UIBezierPath bezierPathWithCGPath:originalPath.CGPath];
        [pathY applyTransform:CGAffineTransformMakeTranslation(0, i)];

        if ([pathY containsPoint:touchPoint]) {
            /*YEAH!*/
        }
    }

}