Saturday, 31 July 2010

Drawing text on a pie chart (Cocoa)

Drawing text along side a pie chart (see image below) is fairly simple :


You only need a bit of math.

Let's look a some code :

First you have your text :

NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:[NSFont fontWithName:@"Helvetica" size:12], NSFontAttributeName,[NSColor blackColor], NSForegroundColorAttributeName, nil];

NSAttributedString * currentText=[[NSAttributedString alloc] initWithString: @"some text" attributes: attributes];


Then you need to figure out where you will put the text :

dot = NSMakePoint( size_x/2 + cos (PI * mid_angle / 180 ) * 50 , size_y/2 + sin ( PI * mid_angle / 180 ) * 50 ) ;

[greenPath appendBezierPathWithArcWithCenter: dot radius: 2 startAngle: 0 endAngle: 360 ] ;


(This is the dot you see in the picture above)


The mid_angle is the angle of slice divided by 2.


Then you draw the text :

textStartPoint = makeTextStartingPoint( [currentText size], bounds , mid_angle, 50 ) ;

[currentText drawAtPoint:textStartPoint];


The function makeTextStartingPoint looks like follows :


NSPoint makeTextStartingPoint( NSSize textSize, NSRect bounds , float angle, int offset )

{

NSPoint textStartPoint ;

float size_x = bounds.size.width ;

float size_y = bounds.size.height ;

float angle_radian = PI * angle / 180 ;

if ( angle <= 90 )

{

textStartPoint = NSMakePoint( size_x/2 + cos (angle_radian) * offset + 5 , size_y/2 + sin (angle_radian ) * offset ) ;

}

if ( angle > 90 && angle <= 180)

{

textStartPoint = NSMakePoint( size_x/2 + cos ( angle_radian ) * offset - textSize.width - 5 , size_y/2 + sin (angle_radian ) * offset ) ;

}

if ( angle > 180 && angle <= 270 )

{

textStartPoint = NSMakePoint( size_x/2 + cos ( angle_radian ) * offset - textSize.width - 5, size_y/2 + sin ( angle_radian ) * offset - textSize.height ) ;

}

if ( angle > 270 )

{

textStartPoint = NSMakePoint( size_x/2 + cos ( angle_radian ) * offset + 8 , size_y/2 + sin (angle_radian ) * offset - textSize.height ) ;

}

return textStartPoint ;

}


The reason I use an attributed string is because now I get the length of the text in pixels. You also see that the starting point is different from quadrant to quadrant.