Re: How to get the stroke geometry of a NSBezierPath?
Re: How to get the stroke geometry of a NSBezierPath?
- Subject: Re: How to get the stroke geometry of a NSBezierPath?
- From: Jesse Grosjean <email@hidden>
- Date: Tue, 13 Feb 2007 20:45:30 -0500
Mark,
Thanks so much for your detailed response, and I'm sorry for taking
so long to respond. I think you approach makes lots of sense for the
standard cases, but it breaks down with non standard strokes. For
example consider a dashed stroke, or a the intersection of two lines
with a small angle using the NSMiterLineJoinStyle. In those cases the
stroke isn't always half stroke width away from the "idea" shape
border and so it won't work.
Of course I'm sure there is a way to solve those problems too, but it
shouldn't be that hard I think. The system should already be
calculating the strokes path somewhere, all I want to do is access
it. For example in Java (generally I'm not a fan of the Java apis)
they make this easy with their BasicStroke.createStrokedShape() method.
Anyway I've got it mostly working now in Quartz. These methods are in
a catagory in NSBezierPath. So you can use [myBezierPath
quartzStrokePath] to get a path representing the shapes stroke path.
The key line is:
strokePath = (CGPathRef) CGContextCopyPath(context);
That allows you to copy the stroke path back out of the context. But
it also seems to be a private api call (at least I didn't find it
documented anywhere) so it's a bit bridle.
Jesse
- (void)loadQuartzPathOf:(NSBezierPath *)bezierPath intoContext:
(CGContextRef)context {
CGPathRef path = [bezierPath quartzPath];
CGContextAddPath(context, path);
CGContextSetLineWidth(context, [self lineWidth]);
CGContextSetLineJoin(context, [self lineJoinStyle]);
CGContextSetLineCap(context, [self lineCapStyle]);
CGContextSetMiterLimit(context, [self miterLimit]);
float *myPattern = nil;
float myPhase = nil;
int myCount = -1;
[self getLineDash:nil count:&myCount phase:&myPhase];
if (myCount > 0) {
myPattern = malloc(myCount * sizeof(float)) ;
[self getLineDash:myPattern count:&myCount phase:&myPhase];
CGContextSetLineDash(context,myPhase,myPattern,myCount);
free(myPattern);
}
CFRelease(path);
}
- (CGPathRef)quartzStrokePath {
CGContextRef context = (CGContextRef)[[NSGraphicsContext
currentContext] graphicsPort];
CGPathRef immutablePath = NULL;
CGPathRef strokePath = NULL;
CGContextSaveGState(context);
[self loadQuartzPathOf:self intoContext:context];
CGContextReplacePathWithStrokedPath(context);
strokePath = (CGPathRef) CGContextCopyPath(context);
immutablePath = CGPathCreateCopy(strokePath);
CFRelease(strokePath);
CGContextRestoreGState(context);
return immutablePath;
}
Jesse
On Feb 5, 2007, at 6:50 PM, Mark Onyschuk wrote:
(copied, minus graphic, plus a few more words, for benefit of the
list)
On 4-Feb-07, at 5:56 PM, Jesse Grosjean wrote:
I'm trying to find the point where a given line intersects a
NSBezierPath, and I'd like to also include the paths current
stroke in this calculation. I know how to do it without taking the
stroke into consideration by using [NSBezierPath-OAExtensions
intersectionWithLine:lineStart:lineEnd:] from the Omni frameworks,
but that method ignores the paths current stroke.
I think the easiest way to solve this is to find a way to turn the
path's stroke into a path itself, and then just use the same test
on that path. (if there's a better way please let me know) And so
that lead to my question, how can I get and access the stroke
geometry from a NSBezierPath.
You might try a geometric approach to solving the problem:
1. find the calculated intersection as you describe above.
2. calculate a normal for the line at that point. I'm not sure
whether Omni's library provides normal calculation along a bezier,
but it's pretty straightforward and can be gleaned using the same
technique as is used to calculate an intercept: progressively split
the bezier into "flatter and flatter" line segment approximations,
then locate the intercept by doing a straight-line intercept
calculation with the appropriate split segment.
3. given the line width (actually, given 1/2 the line width, since
a stroke is applied equidistantly to either side of the conceptual
line), step back to the edge from the standard intersection point.
To do this: conceptualize a right triangle whose hypotenuse is your
own line pointing in towards the calculated intersection point. The
adjacent side is the normal from the calculated intercept and has a
length of 1/2 line width. SOHCAHTOA (man, that brings back memories
of HS math) gives us: cos theta = adjacent / hypotenuse.
so your hypotenuse length is (1/2 line width) / (cos theta)
-Mark
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden