如何使用Quartz Core画星星?

发布于 2024-12-20 09:49:13 字数 2232 浏览 4 评论 0原文

我正在尝试改编Apple提供的示例,以便以编程方式在线绘制星星,代码如下:

CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, aSize);

    for (NSUInteger i=0; i<stars; i++) 
    {

       CGContextSetFillColorWithColor(context, aColor);
       CGContextSetStrokeColorWithColor(context, aColor);

       float w = item.size.width;
       double r = w / 2;
       double theta = 2 * M_PI * (2.0 / 5.0); // 144 degrees

       CGContextMoveToPoint(context, 0, r);

       for (NSUInteger k=1; k<5; k++) 
       {
          float x = r * sin(k * theta);
          float y = r * cos(k * theta);
          CGContextAddLineToPoint(context, x, y);
       }

       CGContextClosePath(context);
       CGContextFillPath(context);
    }

上面的代码绘制了一个完美的星星,但是1.显示颠倒了2.是黑色且没有边框。我想要实现的是在同一条线上并以给定的样式绘制许多星星。我知道我实际上在同一位置绘制了同一条路径 5 次,并且我必须以某种方式垂直翻转上下文,但经过几次测试后我放弃了! (我缺乏必要的数学和几何技能:P)...你能帮我吗?

更新:

好的,感谢CocoaFu,这是我重构和工作的绘图实用程序:

- (void)drawStars:(NSUInteger)count inContext:(CGContextRef)context;
{
    // constants
    const float w = self.itemSize.width;
    const float r = w/2;
    const double theta = 2 * M_PI * (2.0 / 5.0);
    const float flip = -1.0f; // flip vertically (default star representation)

    // drawing center for the star
    float xCenter = r;

    for (NSUInteger i=0; i<count; i++) 
    {
        // get star style based on the index
        CGContextSetFillColorWithColor(context, [self fillColorForItemAtIndex:i]);
        CGContextSetStrokeColorWithColor(context, [self strokeColorForItemAtIndex:i]);

        // update position
        CGContextMoveToPoint(context, xCenter, r * flip + r);

        // draw the necessary star lines
        for (NSUInteger k=1; k<5; k++) 
        {
            float x = r * sin(k * theta);
            float y = r * cos(k * theta);
            CGContextAddLineToPoint(context, x + xCenter, y * flip + r);
        }

        // update horizontal center for the next star
        xCenter += w + self.itemMargin; 

        // draw current star
        CGContextClosePath(context);
        CGContextFillPath(context);
        CGContextStrokePath(context);
    }
}

I'm trying to adapt an example provided by Apple in order to programmatically draw stars in line, the code is the following:

CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, aSize);

    for (NSUInteger i=0; i<stars; i++) 
    {

       CGContextSetFillColorWithColor(context, aColor);
       CGContextSetStrokeColorWithColor(context, aColor);

       float w = item.size.width;
       double r = w / 2;
       double theta = 2 * M_PI * (2.0 / 5.0); // 144 degrees

       CGContextMoveToPoint(context, 0, r);

       for (NSUInteger k=1; k<5; k++) 
       {
          float x = r * sin(k * theta);
          float y = r * cos(k * theta);
          CGContextAddLineToPoint(context, x, y);
       }

       CGContextClosePath(context);
       CGContextFillPath(context);
    }

The code above draws a perfect star, but is 1. displayed upside down 2. is black and without border. What I want to achive is to draw many stars on the same line and with the given style. I understand that I'm actually drawing the same path 5 times in the same position and that I have somehow to flip the context vertically, but after several tests I gave up! (I lack the necessary math and geometry skills :P)... could you please help me?

UPDATE:

Ok, thanks to CocoaFu, this is my refactored and working draw utility:

- (void)drawStars:(NSUInteger)count inContext:(CGContextRef)context;
{
    // constants
    const float w = self.itemSize.width;
    const float r = w/2;
    const double theta = 2 * M_PI * (2.0 / 5.0);
    const float flip = -1.0f; // flip vertically (default star representation)

    // drawing center for the star
    float xCenter = r;

    for (NSUInteger i=0; i<count; i++) 
    {
        // get star style based on the index
        CGContextSetFillColorWithColor(context, [self fillColorForItemAtIndex:i]);
        CGContextSetStrokeColorWithColor(context, [self strokeColorForItemAtIndex:i]);

        // update position
        CGContextMoveToPoint(context, xCenter, r * flip + r);

        // draw the necessary star lines
        for (NSUInteger k=1; k<5; k++) 
        {
            float x = r * sin(k * theta);
            float y = r * cos(k * theta);
            CGContextAddLineToPoint(context, x + xCenter, y * flip + r);
        }

        // update horizontal center for the next star
        xCenter += w + self.itemMargin; 

        // draw current star
        CGContextClosePath(context);
        CGContextFillPath(context);
        CGContextStrokePath(context);
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

不弃不离 2024-12-27 09:49:13

下面是在水平线上绘制 3 颗星的代码,它不太漂亮,但可能会有所帮助:

-(void)drawRect:(CGRect)rect
{
    int aSize = 100.0;
    const CGFloat color[4] = { 0.0, 0.0, 1.0, 1.0 }; // Blue
    CGColorRef aColor = CGColorCreate(CGColorSpaceCreateDeviceRGB(), color);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetLineWidth(context, aSize);
    CGFloat xCenter = 100.0;
    CGFloat yCenter = 100.0;

    float  w = 100.0;
    double r = w / 2.0;
    float flip = -1.0;

    for (NSUInteger i=0; i<3; i++) 
    {
        CGContextSetFillColorWithColor(context, aColor);
        CGContextSetStrokeColorWithColor(context, aColor);

        double theta = 2.0 * M_PI * (2.0 / 5.0); // 144 degrees

        CGContextMoveToPoint(context, xCenter, r*flip+yCenter);

        for (NSUInteger k=1; k<5; k++) 
        {
            float x = r * sin(k * theta);
            float y = r * cos(k * theta);
            CGContextAddLineToPoint(context, x+xCenter, y*flip+yCenter);
        }
        xCenter += 150.0;
    }
    CGContextClosePath(context);
    CGContextFillPath(context);
}

在此处输入图像描述

Here is code that will draw 3 stars in a horizontal line, it's is not pretty but it may help:

-(void)drawRect:(CGRect)rect
{
    int aSize = 100.0;
    const CGFloat color[4] = { 0.0, 0.0, 1.0, 1.0 }; // Blue
    CGColorRef aColor = CGColorCreate(CGColorSpaceCreateDeviceRGB(), color);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetLineWidth(context, aSize);
    CGFloat xCenter = 100.0;
    CGFloat yCenter = 100.0;

    float  w = 100.0;
    double r = w / 2.0;
    float flip = -1.0;

    for (NSUInteger i=0; i<3; i++) 
    {
        CGContextSetFillColorWithColor(context, aColor);
        CGContextSetStrokeColorWithColor(context, aColor);

        double theta = 2.0 * M_PI * (2.0 / 5.0); // 144 degrees

        CGContextMoveToPoint(context, xCenter, r*flip+yCenter);

        for (NSUInteger k=1; k<5; k++) 
        {
            float x = r * sin(k * theta);
            float y = r * cos(k * theta);
            CGContextAddLineToPoint(context, x+xCenter, y*flip+yCenter);
        }
        xCenter += 150.0;
    }
    CGContextClosePath(context);
    CGContextFillPath(context);
}

enter image description here

失而复得 2024-12-27 09:49:13

这是一个实现 buddhabrot 暗示的算法:

- (void)drawStarInContext:(CGContextRef)context withNumberOfPoints:(NSInteger)points center:(CGPoint)center innerRadius:(CGFloat)innerRadius outerRadius:(CGFloat)outerRadius fillColor:(UIColor *)fill strokeColor:(UIColor *)stroke strokeWidth:(CGFloat)strokeWidth {
    CGFloat arcPerPoint = 2.0f * M_PI / points;
    CGFloat theta = M_PI / 2.0f;

    // Move to starting point (tip at 90 degrees on outside of star)
    CGPoint pt = CGPointMake(center.x - (outerRadius * cosf(theta)), center.y - (outerRadius * sinf(theta)));
    CGContextMoveToPoint(context, pt.x, pt.y);

    for (int i = 0; i < points; i = i + 1) {
        // Calculate next inner point (moving clockwise), accounting for crossing of 0 degrees
        theta = theta - (arcPerPoint / 2.0f);
        if (theta < 0.0f) {
            theta = theta + (2 * M_PI);
        }
        pt = CGPointMake(center.x - (innerRadius * cosf(theta)), center.y - (innerRadius * sinf(theta)));
        CGContextAddLineToPoint(context, pt.x, pt.y);

        // Calculate next outer point (moving clockwise), accounting for crossing of 0 degrees
        theta = theta - (arcPerPoint / 2.0f);
        if (theta < 0.0f) {
            theta = theta + (2 * M_PI);
        }
        pt = CGPointMake(center.x - (outerRadius * cosf(theta)), center.y - (outerRadius * sinf(theta)));
        CGContextAddLineToPoint(context, pt.x, pt.y);
    }
    CGContextClosePath(context);
    CGContextSetLineWidth(context, strokeWidth);
    [fill setFill];
    [stroke setStroke];
    CGContextDrawPath(context, kCGPathFillStroke);
}

对我来说适用于大多数基本的恒星。测试从 2 分(是一颗好钻石!)到 9 星。

如果您想要点朝下的星星,请将减法更改为加法。

要绘制多个,请创建一个循环并多次调用此方法,每次传递一个新的中心。这应该很好地排列它们!

Here's an algorithm to implement what buddhabrot implied:

- (void)drawStarInContext:(CGContextRef)context withNumberOfPoints:(NSInteger)points center:(CGPoint)center innerRadius:(CGFloat)innerRadius outerRadius:(CGFloat)outerRadius fillColor:(UIColor *)fill strokeColor:(UIColor *)stroke strokeWidth:(CGFloat)strokeWidth {
    CGFloat arcPerPoint = 2.0f * M_PI / points;
    CGFloat theta = M_PI / 2.0f;

    // Move to starting point (tip at 90 degrees on outside of star)
    CGPoint pt = CGPointMake(center.x - (outerRadius * cosf(theta)), center.y - (outerRadius * sinf(theta)));
    CGContextMoveToPoint(context, pt.x, pt.y);

    for (int i = 0; i < points; i = i + 1) {
        // Calculate next inner point (moving clockwise), accounting for crossing of 0 degrees
        theta = theta - (arcPerPoint / 2.0f);
        if (theta < 0.0f) {
            theta = theta + (2 * M_PI);
        }
        pt = CGPointMake(center.x - (innerRadius * cosf(theta)), center.y - (innerRadius * sinf(theta)));
        CGContextAddLineToPoint(context, pt.x, pt.y);

        // Calculate next outer point (moving clockwise), accounting for crossing of 0 degrees
        theta = theta - (arcPerPoint / 2.0f);
        if (theta < 0.0f) {
            theta = theta + (2 * M_PI);
        }
        pt = CGPointMake(center.x - (outerRadius * cosf(theta)), center.y - (outerRadius * sinf(theta)));
        CGContextAddLineToPoint(context, pt.x, pt.y);
    }
    CGContextClosePath(context);
    CGContextSetLineWidth(context, strokeWidth);
    [fill setFill];
    [stroke setStroke];
    CGContextDrawPath(context, kCGPathFillStroke);
}

Works for me for most basic stars. Tested from 2 points (makes a good diamond!) up to 9 stars.

If you want a star with point down, change the subtraction to addition.

To draw multiples, create a loop and call this method multiple times, passing a new center each time. That should line them up nicely!

穿越时光隧道 2024-12-27 09:49:13

我更喜欢使用 CAShaperLayer 来实现 drawRect,因为它可以被动画化。
这是一个将创建 5 点星形状的路径的函数:

func createStarPath(size: CGSize) -> CGPath {
    let numberOfPoints: CGFloat = 5

    let starRatio: CGFloat = 0.5

    let steps: CGFloat = numberOfPoints * 2

    let outerRadius: CGFloat = min(size.height, size.width) / 2
    let innerRadius: CGFloat = outerRadius * starRatio

    let stepAngle = CGFloat(2) * CGFloat(M_PI) / CGFloat(steps)
    let center = CGPoint(x: size.width / 2, y: size.height / 2)

    let path = CGPathCreateMutable()

    for i in 0..<Int(steps) {
        let radius = i % 2 == 0 ? outerRadius : innerRadius

        let angle = CGFloat(i) * stepAngle - CGFloat(M_PI_2)

        let x = radius * cos(angle) + center.x
        let y = radius * sin(angle) + center.y

        if i == 0 {
            CGPathMoveToPoint(path, nil, x, y)
        }
        else {
            CGPathAddLineToPoint(path, nil, x, y)
        }
    }

    CGPathCloseSubpath(path)
    return path
}

然后它可以与 CAShapeLayer 一起使用,如下所示:

let layer = CAShapeLayer()
layer.path = createStarPath(CGSize(width: 100, height: 100))
layer.lineWidth = 1
layer.strokeColor = UIColor.blackColor().CGColor
layer.fillColor = UIColor.yellowColor().CGColor
layer.fillRule = kCAFillRuleNonZero

I prefer using a CAShaperLayer to implementing drawRect, as it can then be animated.
Here's a function that will create a path in the shape of a 5 point star:

func createStarPath(size: CGSize) -> CGPath {
    let numberOfPoints: CGFloat = 5

    let starRatio: CGFloat = 0.5

    let steps: CGFloat = numberOfPoints * 2

    let outerRadius: CGFloat = min(size.height, size.width) / 2
    let innerRadius: CGFloat = outerRadius * starRatio

    let stepAngle = CGFloat(2) * CGFloat(M_PI) / CGFloat(steps)
    let center = CGPoint(x: size.width / 2, y: size.height / 2)

    let path = CGPathCreateMutable()

    for i in 0..<Int(steps) {
        let radius = i % 2 == 0 ? outerRadius : innerRadius

        let angle = CGFloat(i) * stepAngle - CGFloat(M_PI_2)

        let x = radius * cos(angle) + center.x
        let y = radius * sin(angle) + center.y

        if i == 0 {
            CGPathMoveToPoint(path, nil, x, y)
        }
        else {
            CGPathAddLineToPoint(path, nil, x, y)
        }
    }

    CGPathCloseSubpath(path)
    return path
}

It can then be used with a CAShapeLayer like so:

let layer = CAShapeLayer()
layer.path = createStarPath(CGSize(width: 100, height: 100))
layer.lineWidth = 1
layer.strokeColor = UIColor.blackColor().CGColor
layer.fillColor = UIColor.yellowColor().CGColor
layer.fillRule = kCAFillRuleNonZero
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文