Question

The short story is that I would like the bounds (I think I mean bounds instead of frame) of a NSBezierPath to fill a view. Something like this: map

To generate the above image I scaled/translated each point in my created path using the information from Covert latitude/longitude point to a pixels (x,y) on mercator projection. The problem is that this isn't scalable (I will be adding many more paths) and I want to easily add pan/zoom functionality to my view. Additionally, I want the stroke to remain the same regardless of scale (i.e. no fat boundaries when I zoom).

I think I want to generate a reusable path in some arbitrary reference frame (e.g. longitude and modified latitude) instead of generating a new path every time the window changes. Then I can translate/scale my view's coordinate system to fill the view with the path.

So I used Apple's geometry guide to to modify the view's frame. I got the translation right but scaling failed.

    [self setBoundsOrigin:self.path.bounds.origin];

map fail

    [self scaleUnitSquareToSize:NSMakeSize(1.5, 1.5)];

map fail

Then I tried a coordinate system transformation in my drawRect: method only to end up with a similar result.

NSAffineTransform* xform = [NSAffineTransform transform];
[xform translateXBy:(-self.path.bounds.origin.x) yBy:(-self.path.bounds.origin.y)];
[xform scaleXBy:1.5 yBy:1.5];
[xform concat];

map fail again

Finally I tried manually setting the view bounds in drawRect: but the result was ugly and very slow!

arg

I know I can also transform the NSBezierPath object and I think that would work, but I'd rather transform the view once instead of looping through and transforming each path every update. I think there's about three lines of code I'm missing that will do exactly what I'm looking for.

Edit: Here's the drawRect: method I'm using:

- (void)drawRect:(NSRect)dirtyRect
{
//    NSAffineTransform* xform = [NSAffineTransform transform];
//    [xform translateXBy:-self.path.bounds.origin.x yBy:-self.path.bounds.origin.y];
//    [xform scaleXBy:1.5 yBy:1.5];
//    [xform concat];

[self drawBoundaries];
NSRect bounds = [self bounds];
[[NSColor blackColor] set];
[NSBezierPath fillRect:bounds];

// Draw the path in white
[[NSColor whiteColor] set];
[self.path stroke];

[[NSColor redColor] set];
[NSBezierPath strokeRect:self.path.bounds];
NSLog(@"path origin %f x %f",self.path.bounds.origin.x, self.path.bounds.origin.y);
NSLog(@"path bounds %f x %f",self.path.bounds.size.width, self.path.bounds.size.height);
}
Was it helpful?

Solution

I was able to get it to work using two transformations. I was trying to avoid this to reduce complexity and computation when I have many paths to transform and a window that zooms/pans.

- (void)transformPath:(NSBezierPath *)path
{
NSAffineTransform *translateTransform = [NSAffineTransform transform];
NSAffineTransform *scaleTransform = [NSAffineTransform transform];

[translateTransform translateXBy:(-self.path.bounds.origin.x)
                             yBy:(-self.path.bounds.origin.y)];

float scale = MIN(self.bounds.size.width / self.path.bounds.size.width,
                  self.bounds.size.height / self.path.bounds.size.height);
[scaleTransform scaleBy:scale];

[path transformUsingAffineTransform: translateTransform];
[path transformUsingAffineTransform: scaleTransform];
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top