iPhone iOS Generate star, sunburst or polygon UIBe

2019-02-10 13:07发布

I'm looking for a way to programmatically create stars, sunburst, and other "spiky" effects using UIBezierPath.

Starburst image

UIBezierPath *sunbeamsPath = [UIBezierPath bezierPath];
[sunbeamsPath moveToPoint: CGPointMake(x, y)];

Are there any algorithms that can generate points for sunburst like shapes programmatically, without paths overlapping?

I'm also interested in an irregular shape sunburst like the one below:

irregular sunburst

I would imagine that such algorithm would take a certain number of rays, then roughly divide the circle in a number of segments and generate points for such segment in a clockwise direction. Does an algorithm like the one I'm describing already exists or will I have to write one by myself?

Thank you!

3条回答
祖国的老花朵
2楼-- · 2019-02-10 13:30

Swift version for this

import UIKit

extension Int {
    var degreesToRadians: Double { return Double(self) * .pi / 180 }
    var radiansToDegrees: Double { return Double(self) * 180 / .pi }
}
extension FloatingPoint {
    var degreesToRadians: Self { return self * .pi / 180 }
    var radiansToDegrees: Self { return self * 180 / .pi }
}

class SunBurstView: UIView {

    override func draw(_ rect: CGRect) {
        let radius: CGFloat = rect.size.width / 2.0
        UIColor.red.setFill()
        UIColor.blue.setStroke()
        let bezierPath = UIBezierPath()
        let centerPoint = CGPoint(x: rect.origin.x + radius, y: rect.origin.y + radius)
        var thisPoint = CGPoint(x: centerPoint.x + radius, y: centerPoint.y)
        bezierPath.move(to: centerPoint)
        var thisAngle: CGFloat = 0.0
        let sliceDegrees: CGFloat = 360.0 / self.beams / 2.0

        for _ in 0..<self.beams {
            let x = radius * CGFloat(cosf(Float((thisAngle + sliceDegrees).degreesToRadians))) + centerPoint.x
            let y = radius * CGFloat(sinf(Float((thisAngle + sliceDegrees).degreesToRadians))) + centerPoint.y
            thisPoint = CGPoint(x: x, y: y)
            bezierPath.addLine(to: thisPoint)
            thisAngle += sliceDegrees
            let x2 = radius * CGFloat(cosf(Float((thisAngle + sliceDegrees).degreesToRadians))) + centerPoint.x
            let y2 = radius * CGFloat(sinf(Float((thisAngle + sliceDegrees).degreesToRadians))) + centerPoint.y
            thisPoint = CGPoint(x: x2, y: y2)
            bezierPath.addLine(to: thisPoint)
            bezierPath.addLine(to: centerPoint)
            thisAngle += sliceDegrees
        }
        bezierPath.close()
        bezierPath.lineWidth = 1
        bezierPath.fill()
        bezierPath.stroke()
    }

}
查看更多
Fickle 薄情
3楼-- · 2019-02-10 13:44

I know this old, but I was curious about the first part of this question myself, and going off jrturton's post, I created a custom UIView that generates a UIBezierPath from center of the view. Even animated it spinning for bonus points. Here is the result:

enter image description here

The code I used is here:

- (void)drawRect:(CGRect)rect {
CGFloat radius = rect.size.width/2.0f;

[self.fillColor setFill];
[self.strokeColor setStroke];

UIBezierPath *bezierPath = [UIBezierPath bezierPath];
CGPoint centerPoint = CGPointMake(rect.origin.x + radius, rect.origin.y + radius);

CGPoint thisPoint = CGPointMake(centerPoint.x + radius, centerPoint.y);
[bezierPath moveToPoint:centerPoint];

CGFloat thisAngle = 0.0f;
CGFloat sliceDegrees = 360.0f / self.beams / 2.0f;

for (int i = 0; i < self.beams; i++) {

    CGFloat x = radius * cosf(DEGREES_TO_RADIANS(thisAngle + sliceDegrees)) + centerPoint.x;
    CGFloat y = radius * sinf(DEGREES_TO_RADIANS(thisAngle + sliceDegrees)) + centerPoint.y;
    thisPoint = CGPointMake(x, y);
    [bezierPath addLineToPoint:thisPoint];
    thisAngle += sliceDegrees;

    CGFloat x2 = radius * cosf(DEGREES_TO_RADIANS(thisAngle + sliceDegrees)) + centerPoint.x;
    CGFloat y2 = radius * sinf(DEGREES_TO_RADIANS(thisAngle + sliceDegrees)) + centerPoint.y;
    thisPoint = CGPointMake(x2, y2);
    [bezierPath addLineToPoint:thisPoint];
    [bezierPath addLineToPoint:centerPoint];
    thisAngle += sliceDegrees;

}

[bezierPath closePath];

bezierPath.lineWidth = 1;
[bezierPath fill];
[bezierPath stroke];

}

And you can download a sample project here:

https://github.com/meekapps/Sunburst

查看更多
一纸荒年 Trace。
4楼-- · 2019-02-10 13:49

I'm not aware of an algorithm to create these but I do have some advice - create your bezier path such that (0,0) is the centre of the sunburst, then define however many points you need to draw one "beam" of your sunburst going upwards, returning to (0,0)

Then, for as many beams as you want, perform a loop: apply a rotation transform (2 pi / number of beams) to your sunbeam points (CGPointApplyTransform), and add them to the path.

Once you are finished, you can translate and scale the path for drawing.

I used a similar process to draw star polygons recently and it was very simple. Credit to Rob Napier's book for the idea.

查看更多
登录 后发表回答