Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CGGradient: Drawing a linear gradient on an angle

I am trying to draw a linear CGGradient on an angle. Because "CGContextDrawLinearGradientWithAngle()" does not exist, I am trying to use CGContextDrawLinearGradient(CGContextRef, CGGradientRef, CGPoint startPoint, CGPoint endPoint, CGGradientDrawingOptions).

With that in mind, I need to convert an angle (degrees) into a starting point and an ending point. I would like to mimic NSGradient's drawInBezierPath:angle. (As a part of AppKit, NSGradient is sadly not available to iOS developers.) Fortunately, the documentation tells us how to get the starting gradient:

- (CGPoint)startingPointForAngle:(CGFloat)angle rect:(CGRect)rect { 
    CGPoint point = CGPointZero;
    if (angle < 90.0f)
        point = CGPointMake(CGRectGetMinX(rect), CGRectGetMaxY(rect));
    else if (angle < 180.0f)
        point = CGPointMake(CGRectGetMaxX(rect), CGRectGetMaxY(rect));
    else if (angle < 270.0f)
        point = CGPointMake(CGRectGetMaxX(rect), CGRectGetMinY(rect));
    else
        point = CGPointMake(CGRectGetMinX(rect), CGRectGetMinY(rect));

    return point;
}

Unfortunately, the documentation does not tell us how to get the ending point. (Using either the height or the width of the rect as the distance is only sufficient for certain angles.) Several sites out there tells us how we can find the ending point. Unfortunately, the distance needs to be known before I can compute the ending point. Yet the ending point needs to be computed to get the distance. There is clearly more to it, as NSGradient seems to have it figured out.

- (CGPoint)endingPointForAngle:(CGFloat)angle rect:(CGRect)rect startingPoint:(CGPoint)startingPoint {
    //http://www.zahniser.net/~russell/computer/index.php?title=Angle%20and%20Coordinates
    //(x + distance * cos(a), y + distance * sin(a))
    CGFloat angleInRadians = (CGFloat)M_PI/180.0f * angle;
    CGFloat distance = ????????;
    CGPoint point = CGPointMake(startingPoint.x + distance * cosf(angleInRadians), startingPoint.y + distance * sinf(angleInRadians));
    return point;
}

CGPoint startingGradientPoint = [self startingPointForAngle:self.fillGradientAngle rect:rect];
CGPoint endingGradientPoint = [self endingPointForAngle:self.fillGradientAngle rect:rect startingPoint:startingGradientPoint];
CGContextDrawLinearGradient(graphicsContext, self.fillGradient, startingGradientPoint, endingGradientPoint, 0);

Any ideas.

like image 269
Bay Avatar asked Nov 14 '22 09:11

Bay


1 Answers

I'm dealing with the same problem, with a little different way, I use the center point and and angle, and extend the side left and right to find it's points on the edge, my problem was there will be white space if the angel is not pointing any axis, and the drawing option the functions provide a "kCGGradientDrawsBeforeStartLocation or kCGGradientDrawsAfterEndLocation" so it looks like one side will be empty.

But it turns out I can combine the two options with '|', so problem solved, here's my code:

CGGradientRef grRef = CGGradientCreateWithColors(colorSpace,  (__bridge CFArrayRef)gradient.colorArray, NULL);
CGFloat degree = angle * M_PI / 180;
CGPoint center = CGPointMake(width/2, height/2);
CGPoint startPoint = CGPointMake(center.x - cos (degree) * width/2, center.y - sin(degree) * height/2);
CGPoint endPoint = CGPointMake(center.x + cos (degree) * width/2, center.y + sin(degree) * height/2);

NSLog(@"start point:%@ \n, end point: %@",NSStringFromCGPoint(startPoint),NSStringFromCGPoint(endPoint));

CGContextDrawLinearGradient(gradientContext, grRef, startPoint, endPoint, kCGGradientDrawsBeforeStartLocation|kCGGradientDrawsAfterEndLocation);
like image 155
ian Avatar answered Nov 16 '22 04:11

ian