iPhoneアプリ ピタゴラスのリュート

正五角形の対角線をつかって書いた星を、星形五角形というらしい。(英語だと、pentagonとpentagram) 星形五角形の線を使って、正五角形を付け足して、また星形五角形を書いて… と繰り返していくと、ピタゴラスのリュートと呼ばれる形の出来上がりです。 というのをiPhoneアプリで作ってみます。


動作イメージ
XcodeからiOS6 iPhone Simulatorで動かすとこんな感じになります。

ポイント
正五角形を作るメソッドと、星形五角形を作るメソッドを用意しておいて、画面の下の方に音符に見立てたボタンのタッチで五角形を表示するようにしました。

サンプルコード

#import “ViewController.h”

#import <QuartzCore/QuartzCore.h>

@interface ViewController () {

    int colorCount;

}

@end

@implementation ViewController

– (void)viewDidLoad

{

    [super viewDidLoad];

    self.view.backgroundColor = [self color:1];

    [self createStaffNotation];

}

– (void)createStaffNotation

{

    UIView *staffNotation = [[UIView alloc] initWithFrame:CGRectMake(-50, 450, 350, 100)];

    

    float y = 0;

    float dh = 20.0;

    UIBezierPath *path = [UIBezierPath bezierPath];

    for (int i=0; i<5; i++) {

        [path moveToPoint:CGPointMake(0, y + dh * i)];

        [path addLineToPoint:CGPointMake(400, y + dh * i)];

    }

    

    CAShapeLayer *sl = [[CAShapeLayer alloc] init];

    sl.fillColor = [UIColor clearColor].CGColor;

    sl.strokeColor = [self color:4].CGColor;

    sl.lineWidth = 1;

    sl.path = path.CGPath;

    [staffNotation.layer addSublayer:sl];

    

    [self.view addSubview:staffNotation];

    

    staffNotation.transform = CGAffineTransformMakeRotation(-M_PI / 10.0);

    

    for (int i=0; i<5; i++) {

        float x = 60 * i + 80;

        float y = 3010 * i;

        

        UIView *bar = [[UIView alloc] initWithFrame:CGRectMake(x+35, y-30, 5, 50)];

        bar.backgroundColor = [self color:4];

        [staffNotation addSubview:bar];

        

        UILabel *btn = [[UILabel alloc] initWithFrame:CGRectMake(x, y, 40, 40)];

        btn.backgroundColor = [self color:4];

        btn.layer.cornerRadius = 20;

        btn.text = [NSString stringWithFormat:@”%d”, i + 1];

        btn.font = [UIFont boldSystemFontOfSize:30];

        btn.textColor = [self color:0];

        btn.textAlignment = NSTextAlignmentCenter;

        [staffNotation addSubview:btn];

        

        btn.userInteractionEnabled = YES;

        UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapNote:)];

        [btn addGestureRecognizer:tap];

        

    }

}

– (void)tapNote:(UITapGestureRecognizer*)gr

{

    UILabel *l = (UILabel*)gr.view;

    [self createLute:[l.text intValue] – 1];

}

– (void)createLute:(int)index

{

    float rad = 120;

    CGPoint o = CGPointMake(160, 130);

    switch (index) {

        case 0:

            break;

        case 1:

            o = CGPointMake(o.x, o.y + rad); // use old radius

            rad = rad / 1.618;

            break;

        case 2:

            o = CGPointMake(o.x, o.y + rad * (1.0 + 1.0/1.618));

            rad = rad / pow(1.618, 2);

            break;

        case 3:

            o = CGPointMake(o.x, o.y + rad * (1.0 + 1.0/1.618 + 1.0/pow(1.618, 2)));

            rad = rad / pow(1.618, 3);

            break;

        case 4:

            o = CGPointMake(o.x, o.y + rad * (1.0 + 1.0/1.618 + 1.0/pow(1.618, 2) + 1.0/pow(1.618, 3)));

            rad = rad / pow(1.618, 4);

            break;

        default:

            break;

    }

    

    [self createPentagon:rad at:o];

    [self createPentagram:rad – 2 at:o];

    

}

– (void)createPentagon:(float)radius at:(CGPoint)origin

{

    

    UIBezierPath *path = [UIBezierPath bezierPath];

    

    float dAngle = 2.0 * M_PI / 5.0;

    for (int i=0; i<5; i++) {

        float x = radius * cos(dAngle * i – M_PI / 2.0) + origin.x;

        float y = radius * sin(dAngle * i – M_PI / 2.0) + origin.y;

        

        if (i == 0) {

            [path moveToPoint:CGPointMake(x, y)];

        } else {

            [path addLineToPoint:CGPointMake(x, y)];

        }

    }

    [path closePath];

    

    CAShapeLayer *sl = [[CAShapeLayer alloc] init];

    // color index:2, 3, 2, 3 …

    sl.fillColor = [self color:++colorCount % 2 + 2].CGColor;

    sl.path = path.CGPath;

    

    [self.view.layer addSublayer:sl];

}

– (void)createPentagram:(float)radius at:(CGPoint)origin

{

    CGPoint p[5];

    float dAngle = 2.0 * M_PI / 5.0;

    for (int i=0; i<5; i++) {

        float x = radius * cos(dAngle * i – M_PI / 2.0) + origin.x;

        float y = radius * sin(dAngle * i – M_PI / 2.0) + origin.y;

        p[i] = CGPointMake(x, y);

    }

    

    int index[] = {0,2,4,1,3,0};

    UIBezierPath *path = [UIBezierPath bezierPath];

    [path moveToPoint:p[index[0]]];

    for (int i=1; i<6; i++) {

        [path addLineToPoint:p[index[i]]];

    }

    

    CAShapeLayer *sl = [[CAShapeLayer alloc] init];

    sl.fillColor = [UIColor clearColor].CGColor;

    sl.strokeColor = [self color:0].CGColor;

    sl.lineWidth = 2;

    sl.lineJoin = kCALineJoinRound;

    sl.path = path.CGPath;

    

    [self.view.layer addSublayer:sl];

    

    [self startDraw:sl duration:0.5];

}

#pragma mark – utility

– (void)startDraw:(CALayer*)l duration:(float)duration

{

    CABasicAnimation *a = [CABasicAnimation animationWithKeyPath:@”strokeEnd”];

    a.duration = duration;

    a.fromValue = @0;

    a.toValue = @1;

    [l addAnimation:a forKey:@”strokeEnd”];

}

#define UIColorHex(rgbValue) [UIColor colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 green:((float)((rgbValue & 0xFF00) >> 8))/255.0 blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0]

– (UIColor*)color:(int)i

{

    switch (i) {

        case 0:

            return UIColorHex(0xEB6E44);

        case 1:

            return UIColorHex(0xFFE69E);

        case 2:

            return UIColorHex(0xCFEE7F);

        case 3:

            return UIColorHex(0x8DCDC1);

        case 4:

            return UIColorHex(0x4F4A47);

        default:

            break;

    }

    return nil;

}

– (void)didReceiveMemoryWarning

{

    [super didReceiveMemoryWarning];

    // Dispose of any resources that can be recreated.

}

@end