NSBezierPath containsPoint: is too sensitive when path not closed

552 views Asked by At

I have a problem with the containsPoint method. I draw some boxes and connectors between the boxes. The connectors are basicly a single curve, based on a single curveToPoint:controlPoint1:controlPoint2 call. When I now try to select this curve/path with the mouse then this is tricky to do. The containsPoint: method seems to be very sensitive. I tried to draw the line bigger (setLineWidth:), but that doesn't seem to help.

Any ideas what I need to do differently?

2

There are 2 answers

1
David Rönnqvist On BEST ANSWER

For a CGPath, you can always create a closed path which is the contour of the stroked path using:

CGPathRef strokedPath = CGPathCreateCopyByStrokingPath(
    path,             // your original CGPathRef
    NULL,             // don't transform
    10.0,             // lineWidth
    kCGLineCapButt,   // lineCap    (default value)
    kCGLineJoinMiter, // lineJoin   (default value)
    0.0               // miterLimit
);

You can read more about path hit testing here (by Ole Begemann) and here (by Rob Napier).

0
Wizard of Kneup On

Thanks to David's answer, I can now provide a full answer. What I needed were three parts.

  1. Convert the NSBezierPath into a CGPath. This can be done as provided in the Apple Documentation. Or you can use the https://github.com/iccir/XUIKit‎ library, which adds the iPhone framework capabilities to the MacOS frameworks.
  2. Use the CGPathCreateCopyByStrokingPath function as suggested by David.
  3. Convert the new CGPath into a NSBezierPath. David's link to Ole Begemann's block was quite helpful to show how to do it. However, XUIKit is once again a step ahead and provides a +(NSBezierPath) bezierPathWithCGPath: function

The result looks like this.

//con as Connector was the starting point
CGPathRef tapTargetPath = CGPathCreateCopyByStrokingPath(con.CGPath, NULL, 4, kCGLineCapButt, kCGLineJoinBevel, kCGLineJoinMiter );
NSBezierPath * hitPath = [NSBezierPath bezierPathWithCGPath:tapTargetPath];