rotating NSBezierPath objects

  • Okay, I see in the doc set that you can perform an NSAffineTransform on an NSBezierPath, but how do I perform a rotation of the NSBezierPath about an arbitrary CGPoint and with a specified angle in radians? Is this something deep in CoreGraphics, or CoreAnimation? If not, I can do the trigonometry myself if I can get the list of points that make up the NSBezierPath, but it seems these aren't exposed, only NSBezierPathElement objects. Help!
      Also, what is the formula for calculating the area of an arbitrary triangle defined by three non-collinear points in 2D space? (i.e. I can't assume the triangle even has a 90 degree angle in it, or any other such shortcuts…)
      Finally, If an NSBezierPath consisting of a closed circuit of points:


    NSBezierPath *mySquarePath = [NSBezierPath bezierPath];

    [mySquarePath setLineWidth:1.0];
    [mySquarePath moveToPoint:CGPointMake(0.0, 0.0)];
    [mySquarePath lineToPoint:CGPointMake(10.0, 0.0)];
    [mySquarePath lineToPoint:CGPointMake(10.0, 10.0)];
    [mySquarePath lineToPoint:CGPointMake(0.0, 10.0)];
    [mySquarePath closePath];


    will [mySquarePath containsPoint:CGPointMake(5.0, 5.0)] return YES even before I fill or stroke the path, or do I have to fill the path first?
  • On 07/07/2012, at 10:10 AM, William Squires wrote:

    > Okay, I see in the doc set that you can perform an NSAffineTransform on an NSBezierPath, but how do I perform a rotation of the NSBezierPath about an arbitrary CGPoint and with a specified angle in radians?

    Exactly that - use [NSAffineTransform transformBezierPath:]

    You just have to set up the transform correctly (see below)

    > Is this something deep in CoreGraphics, or CoreAnimation? If not, I can do the trigonometry myself if I can get the list of points that make up the NSBezierPath, but it seems these aren't exposed, only NSBezierPathElement objects. Help!
    > Also, what is the formula for calculating the area of an arbitrary triangle defined by three non-collinear points in 2D space? (i.e. I can't assume the triangle even has a 90 degree angle in it, or any other such shortcuts…)

    Google it, there are loads of geometry answers out there. One I use which is overkill for this is to calculate the area of an arbitrary polygon:

    (This is a category on NSBezierPath, but the path must be flattened first - it doesn't work if there are curve segments)

    - (CGFloat)    signedAreaOfFlattenedPath
    {
    if([self isEmpty])
      return 0;

    NSInteger  m, i, j;
    CGFloat    a = 0;
    NSPoint    ap, np;

    m = [self elementCount];

    for( i = 0; i < m; ++i )
    {
      j = (i + 1) % m;

      [self elementAtIndex:i associatedPoints:&ap];
      [self elementAtIndex:j associatedPoints:&np];

      // because we are using a flattened path, we can be sure there are no curve elements

      a += ((ap.x * np.y) - (np.x * ap.y));
    }

    return a * 0.5;
    }

    > Finally, If an NSBezierPath consisting of a closed circuit of points:
    >
    > …
    > NSBezierPath *mySquarePath = [NSBezierPath bezierPath];
    >
    > [mySquarePath setLineWidth:1.0];
    > [mySquarePath moveToPoint:CGPointMake(0.0, 0.0)];
    > [mySquarePath lineToPoint:CGPointMake(10.0, 0.0)];
    > [mySquarePath lineToPoint:CGPointMake(10.0, 10.0)];
    > [mySquarePath lineToPoint:CGPointMake(0.0, 10.0)];
    > [mySquarePath closePath];
    > …
    >
    > will [mySquarePath containsPoint:CGPointMake(5.0, 5.0)] return YES even before I fill or stroke the path, or do I have to fill the path first?

    Yes. A path is a mathematical definition, it has nothing to do with graphics.

    Rotating a path: (again a category method on NSBezierPath)

    - (NSBezierPath*)        rotatedPath:(CGFloat) angle aboutPoint:(NSPoint) cp
    {
    // return a rotated copy of the receiver. The origin is taken as point <cp> relative to the original path.
    // angle is a value in radians

    if( angle == 0.0 )
      return self;
    else
    {
      NSBezierPath* copy = [self copy];

      NSAffineTransform* xfm = RotationTransform( angle, cp );
      [copy transformUsingAffineTransform:xfm];

      return [copy autorelease];
    }
    }

    which uses:

    NSAffineTransform*    RotationTransform( const CGFloat angle, const NSPoint cp )
    {
    // return a transform that will cause a rotation about the point given at the angle given

    NSAffineTransform* xfm = [NSAffineTransform transform];
    [xfm translateXBy:cp.x yBy:cp.y];
    [xfm rotateByRadians:angle];
    [xfm translateXBy:-cp.x yBy:-cp.y];

    return xfm;
    }

    --Graham
  • On 7 Jul, 2012, at 8:10 AM, William Squires <wsquires...> wrote:

    > Okay, I see in the doc set that you can perform an NSAffineTransform on an NSBezierPath, but how do I perform a rotation of the NSBezierPath about an arbitrary CGPoint and with a specified angle in radians? Is this something deep in CoreGraphics, or CoreAnimation? If not, I can do the trigonometry myself if I can get the list of points that make up the NSBezierPath, but it seems these aren't exposed, only NSBezierPathElement objects. Help!

    Make one affine transform which translates the point you want to rotate around to the origin, rotates, then translates it back. Just use the NSAffineTransrorm functions to concatenate the three operations and apply it.

    > Also, what is the formula for calculating the area of an arbitrary triangle defined by three non-collinear points in 2D space? (i.e. I can't assume the triangle even has a 90 degree angle in it, or any other such shortcuts…)

    Google it, I just did. There's 100s of answers. Pick the one which suits you best.

    > Finally, If an NSBezierPath consisting of a closed circuit of points:
    >
    > …
    > NSBezierPath *mySquarePath = [NSBezierPath bezierPath];
    >
    > [mySquarePath setLineWidth:1.0];
    > [mySquarePath moveToPoint:CGPointMake(0.0, 0.0)];
    > [mySquarePath lineToPoint:CGPointMake(10.0, 0.0)];
    > [mySquarePath lineToPoint:CGPointMake(10.0, 10.0)];
    > [mySquarePath lineToPoint:CGPointMake(0.0, 10.0)];
    > [mySquarePath closePath];
    > …
    >
    > will [mySquarePath containsPoint:CGPointMake(5.0, 5.0)] return YES even before I fill or stroke the path, or do I have to fill the path first?
    >
    >

    Yes it will return YES. Why not just write the code and confirm it for yourself.

    >
previous month july 2012 next month
MTWTFSS
            1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31          
Go to today