Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to draw a triangle with rounded corners using quartz2d/coregraphics

Aight so I want to draw a triangle like so:

rounded triangle

Currently I'm using a combination of a CAShapeLayer and creating the path with a UIBezierPath (the code is below) and then applying that as a mask for another layer (self.layer as I'm in a UIView subclass and rather than setting layerclass I'm doing it this way so I can preserve the initial layer)

anyways the code:

_bezierPath = [[UIBezierPath bezierPath] retain];
#define COS30 0.86602540378
#define SIN30 0.5
[_bezierPath moveToPoint:(CGPoint){self.frame.size.width/2.f-r*SIN30,r*COS30}];
[_bezierPath addArcWithCenter:(CGPoint){self.frame.size.width/2.f,r*COS30*2.f} radius:r startAngle:2*M_PI/3.f endAngle:M_PI/3.f clockwise:YES];
[_bezierPath addLineToPoint:(CGPoint){self.frame.size.width-r*SIN30,self.frame.size.height-r*COS30}];
[_bezierPath addArcWithCenter:(CGPoint){self.frame.size.width-r*SIN30-r,self.frame.size.height-r*COS30} radius:r startAngle:0.f endAngle:-M_PI/3.f clockwise:YES];
[_bezierPath addLineToPoint:(CGPoint){r*SIN30,self.frame.size.height-r*COS30}];
[_bezierPath addArcWithCenter:(CGPoint){r*SIN30+r,self.frame.size.height-r*COS30} radius:r startAngle:4*M_PI/3.f endAngle:M_PI clockwise:YES];
[_bezierPath closePath];
CAShapeLayer *s = [CAShapeLayer layer];
s.frame = self.bounds;
s.path = _bezierPath.CGPath;
self.layer.mask = s;
self.layer.backgroundColor = [SLInsetButton backgroundColorForVariant:SLInsetButtonColorForSLGamePieceColor(_color)].CGColor;

and unfortunately the result is not what I was looking for, instead the corners become little knobs (as if its turning too much)

Thanks in advance

like image 476
DanZimm Avatar asked Nov 24 '12 04:11

DanZimm


1 Answers

A brilliant friend (John Heaton in case you run into him, he's pretty awesome) reminded me of the lineCap and lineJoin properties that core graphics deploys.

Somewhere:

#define border (10.f)

And in your interface:

@property (nonatomic, strong) UIBezierPath *bezierPath;

And in your init, create a short path like this:

CGFloat inset = border / 2.f;
UIBezierPath *bezierPath = [UIBezierPath bezierPath];
[bezierPath moveToPoint:(CGPoint){ self.frame.size.width/2.f, inset }];
[bezierPath addLineToPoint:(CGPoint){ self.frame.size.width - inset, self.frame.size.height - inset }];
[bezierPath addLineToPoint:(CGPoint){ a, self.frame.size.height - a }];
[bezierPath closePath];

self.bezierPath = bezierPath;

And then in your drawRect: method do something like the following:

CGContextRef c = UIGraphicsGetCurrentContext(), context = c;
CGColorRef col = [UIColor redColor].CGColor;
CGColorRef bcol = [UIColor redColor].CGColor;
CGContextSetFillColorWithColor(c, col);
CGContextSetStrokeColorWithColor(c, bcol);
CGContextSetLineWidth(c, border);
CGContextSetLineJoin(c, kCGLineJoinRound);
CGContextSetLineCap(c, kCGLineCapRound);
CGContextAddPath(c, self.bezierPath.CGPath);
CGContextStrokePath(c);
CGContextAddPath(c, self.bezierPath.CGPath);
CGContextFillPath(c);

And that will properly give you rounded corners on a triangle, also with the option to have a border or outline

like image 86
DanZimm Avatar answered Sep 26 '22 14:09

DanZimm