Bhargav Kukadiya Bhargav Kukadiya - 6 months ago 47
iOS Question

How to draw arc through 3 points

How to draw an arc as shown in the image as suppose i have a UIView as the middle point of the line which is movable.

enter image description here

enter image description here

i had store line's start point in CGPoint name lineStartPoint and end point as lineEndPoint.

the moving object can be accessed through CGPoint named movingPoint.

Thanks in advance for help.

Answer

Here is an example how to calculate the required parameters for the bezierPathWithArcCenter:radius:startAngle:endAngle:clockwise: method.

- (void)viewDidLoad {
    [super viewDidLoad];

    // for example
    CGPoint lineStartPoint = {100,100};
    CGPoint lineEndPoint =   {100,200};
    CGPoint movingPoint =    {30,150};


    CGFloat eps = 1e-5;

    CGVector v1 = {movingPoint.x-lineEndPoint.x, movingPoint.y-lineEndPoint.y};
    CGFloat dist1 = sqrt(v1.dx*v1.dx + v1.dy*v1.dy);
    v1.dx = v1.dx/dist1;
    v1.dy = v1.dy/dist1;

    CGVector v2 = {movingPoint.x-lineStartPoint.x, movingPoint.y-lineStartPoint.y};
    CGFloat dist2 = sqrt(v2.dx*v2.dx + v2.dy*v2.dy);
    v2.dx = v2.dx/dist2;
    v2.dy = v2.dy/dist2;


    CGFloat det = v1.dx*v2.dy - v1.dy*v2.dx;
    if (fabs(det) < eps) {
        // the three points are collinear
        // TODO: draw a line from lineStartPoint to lineEndPoint
        return;
    }

    CGPoint mid1 = {(movingPoint.x+lineEndPoint.x)/2, (movingPoint.y+lineEndPoint.y)/2};
    CGPoint mid2 = {(movingPoint.x+lineStartPoint.x)/2, (movingPoint.y+lineStartPoint.y)/2};

    CGFloat b1 = v1.dx*mid1.x + v1.dy*mid1.y;
    CGFloat b2 = v2.dx*mid2.x + v2.dy*mid2.y;

    CGFloat centerX = v2.dy/det*b1 - v1.dy/det*b2;
    CGFloat centerY = -v2.dx/det*b1 + v1.dx/det*b2;

    CGPoint center = {centerX, centerY};
    CGFloat radius = sqrtf((movingPoint.x-center.x)*(movingPoint.x-center.x) + (movingPoint.y-center.y)*(movingPoint.y-center.y));
    CGFloat startAngle = atan2f(lineStartPoint.y-center.y, lineStartPoint.x-center.x);
    CGFloat movingAngle = atan2f(movingPoint.y-center.y, movingPoint.x-center.x);
    CGFloat endAngle = atan2f(lineEndPoint.y-center.y, lineEndPoint.x-center.x);

    BOOL isClockwise;
    if ((endAngle>startAngle && startAngle<movingAngle && movingAngle<endAngle) ||
        (endAngle<startAngle && !(endAngle<movingAngle && movingAngle<startAngle))) {
        isClockwise = YES;
    } else {
        isClockwise = NO;
    }


    //Show results

    CAShapeLayer* startPointLayer = [[CAShapeLayer alloc] init];
    startPointLayer.path = [UIBezierPath bezierPathWithArcCenter:lineStartPoint radius:2 startAngle:0 endAngle:2*M_PI clockwise:YES].CGPath;
    [self.view.layer addSublayer:startPointLayer];

    CAShapeLayer* endPointLayer = [[CAShapeLayer alloc] init];
    endPointLayer.path = [UIBezierPath bezierPathWithArcCenter:lineEndPoint radius:2 startAngle:0 endAngle:2*M_PI clockwise:YES].CGPath;
    [self.view.layer addSublayer:endPointLayer];

    CAShapeLayer* movingPointLayer = [[CAShapeLayer alloc] init];
    movingPointLayer.path = [UIBezierPath bezierPathWithArcCenter:movingPoint radius:2 startAngle:0 endAngle:2*M_PI clockwise:YES].CGPath;
    [self.view.layer addSublayer:movingPointLayer];

    CAShapeLayer* arcLayer = [[CAShapeLayer alloc] init];
    [arcLayer setFillColor:[UIColor clearColor].CGColor];
    [arcLayer setStrokeColor:[UIColor blueColor].CGColor];
    arcLayer.path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:isClockwise].CGPath;
    [self.view.layer addSublayer:arcLayer];

}
Comments