Draw ellipse with start and end angle in Objective

2019-03-20 06:07发布

问题:

I am writing an iPad app in which I am rendering XML objects that represent shapes into graphics on the screen. One of the objects I am trying to render is arcs. Essentially these arcs provide me with a bounding rectangle as well as a start and end angle.

Given attributes:

  • x
  • y
  • width
  • height
  • startAngle
  • endAngle

With these values I need to draw the arc (which is essentially part of an ellipse). I can not use the following:

    UIBezierPath *arc = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(x, y, width, height)];
    [UIColor blackColor] setStroke];
    [arc stroke];

because it draws a full ellipse. Basically I need the above but it needs to take into account the start and end angles so only part of the ellipse is displayed. I am thinking this will involve either drawing a cubic Bezier curve or a quadratic Bezier curve. The problem is I have no clue how to calculate the start point, end point, or control points with the information I am given.

回答1:

You can probably achieve what you want by setting up a clip path around the drawing of the ellipse.

CGContextSaveGState(theCGContext);
CGPoint center = CGPointMake(x + width / 2.0, y + height / 2.0);
UIBezierPath* clip = [UIBezierPath bezierPathWithArcCenter:center
                                                    radius:max(width, height)
                                                startAngle:startAngle
                                                  endAngle:endAngle
                                                 clockwise:YES];
[clip addLineToPoint:center];
[clip closePath];
[clip addClip];

UIBezierPath *arc = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(x, y, width, height)];
[[UIColor blackColor] setStroke];
[arc stroke];

CGContextRestoreGState(theCGContext);

The exact radius for the clipping isn't important. It needs to be big enough so it only clips the ellipse at the ends, not through the desired arc.



回答2:

This category will help.

#import <Foundation/Foundation.h>

@interface UIBezierPath (OvalSegment)

+ (UIBezierPath *)bezierPathWithOvalInRect:(CGRect)rect startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle;
+ (UIBezierPath *)bezierPathWithOvalInRect:(CGRect)rect startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle angleStep:(CGFloat)angleStep;

@end

#import "UIBezierPath+OvalSegment.h"

@implementation UIBezierPath (OvalSegment)

+ (UIBezierPath *)bezierPathWithOvalInRect:(CGRect)rect startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle angleStep:(CGFloat)angleStep {
    CGPoint center = CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect));
    CGFloat xRadius = CGRectGetWidth(rect)/2.0f;
    CGFloat yRadius = CGRectGetHeight(rect)/2.0f;

    UIBezierPath *ellipseSegment = [UIBezierPath new];

    CGPoint firstEllipsePoint = [self ellipsePointForAngle:startAngle withCenter:center xRadius:xRadius yRadius:yRadius];
    [ellipseSegment moveToPoint:firstEllipsePoint];

    for (CGFloat angle = startAngle + angleStep; angle < endAngle; angle += angleStep) {
        CGPoint ellipsePoint = [self ellipsePointForAngle:angle withCenter:center xRadius:xRadius yRadius:yRadius];
        [ellipseSegment addLineToPoint:ellipsePoint];
    }

    CGPoint lastEllipsePoint = [self ellipsePointForAngle:endAngle withCenter:center xRadius:xRadius yRadius:yRadius];
    [ellipseSegment addLineToPoint:lastEllipsePoint];

    return ellipseSegment;
}

+ (UIBezierPath *)bezierPathWithOvalInRect:(CGRect)rect startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle {
    return [UIBezierPath bezierPathWithOvalInRect:rect startAngle:startAngle endAngle:endAngle angleStep:M_PI/20.0f];
}

+ (CGPoint)ellipsePointForAngle:(CGFloat)angle withCenter:(CGPoint)center xRadius:(CGFloat)xRadius yRadius:(CGFloat)yRadius {
    CGFloat x = center.x + xRadius * cosf(angle);
    CGFloat y = center.y - yRadius * sinf(angle);
    return CGPointMake(x, y);
}

@end


回答3:

You will have to use addQuadCurveToPoint:controlPoint:instead of bezierPathWithOvalInRect.

The method definition is as:-

- (void)addQuadCurveToPoint:(CGPoint)endPoint controlPoint:(CGPoint)controlPoint

that is CGPoint are the arguments(input) for both the parameters.

You will also have to set the start point using moveToPoint:before calling addQuadCurveToPoint which will act as current point(As you can see their's no start point as parameter in the method).

In your case you will have

1>x,y as your starting point

2>x+width and y+height as endpoint

you don't need angles here or you can implement your logic to use the angles.Am uploading an image to make thing clear.