Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to draw stars using Quartz Core?

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);
    }
}
like image 266
daveoncode Avatar asked Dec 09 '11 12:12

daveoncode


2 Answers

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

like image 78
zaph Avatar answered Nov 01 '22 21:11

zaph


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!

like image 32
mbm29414 Avatar answered Nov 01 '22 21:11

mbm29414