iPhone 黄金三角形から黄金螺旋

黄金三角形は、底角から36°(元は72°)の線を引くことで、どんどん新しい黄金三角形に分割していくことができます。この三角形のへんを半径とする弧をかいていくと螺旋の出来上がり。この作図過程をアニメーションで表示する iPhoneアプリを書いてみます。


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

ポイント
底角を半分にしていく角度は、弧の角度で元の三角形から回転した角度と一致するので、分割する線の角度は、一つ前の分割線から108° (弧の角度)回転させたものを計算上は利用しました。三角形を描いて、弧を描いてというのを三角形を分割するたびに繰り返しています。

サンプルコード

#import “ViewController.h”

#import <QuartzCore/QuartzCore.h>

@interface ViewController () {

    CGPoint p[3];

    CGPoint center;

    float angle;

    float rad;

    int count;

}

@end

@implementation ViewController

– (void)viewDidLoad

{

    [super viewDidLoad];

    

    [self createGrid];

        

    [self createButton];

}

– (void)createGrid

{

    UIBezierPath *path = [UIBezierPath bezierPathWithRect:self.view.bounds];

    

    // horizontal line

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

        float h = 20 * i;

        [path moveToPoint:CGPointMake(0, h)];

        [path addLineToPoint:CGPointMake(320, h)];

    }

    // vertical line

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

        float w = 20 * i;

        [path moveToPoint:CGPointMake(w, 0)];

        [path addLineToPoint:CGPointMake(w, 568)];

    }

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

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

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

    sl.lineWidth = 2;

    sl.path = path.CGPath;

    

    [self.view.layer addSublayer:sl];

}

– (void)createButton

{

    UILabel *btn = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 160, 160)];

    btn.layer.cornerRadius = 80;

    btn.backgroundColor = [self color:3];

    btn.center = CGPointMake(160, 380);

    btn.text = @”push”;

    btn.textColor = [self color:0];

    btn.textAlignment = NSTextAlignmentCenter;

    btn.font = [UIFont boldSystemFontOfSize:40];

    [self.view addSubview:btn];

    

    btn.userInteractionEnabled = YES;

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

    [btn addGestureRecognizer:tap];

}

– (void)next

{

    switch (count) {

        case 0: [self drawGoldTriangleA];break;

        case 1: [self drawGoldTriangleB]; break;

        case 2: [self drawArcA]; break;

        case 3: [self drawGoldTriangleC]; break;

        case 4: [self drawArcB]; break;

        case 5: [self drawGoldTriangleD]; break;

        case 6: [self drawArcC]; break;

        case 7: [self drawGoldTriangleE]; break;

        case 8: [self drawArcD]; break;

        default:

            break;

    }

    count++;

    

    if (count > 9) {

        //reset

        self.view.layer.sublayers = nil;

        [self createGrid];

        [self createButton];

        count = 0;

    }

}

#define Deg72 2.0 * M_PI / 5.0

#define degree36 (36.0 * M_PI / 180.0)

– (void)drawGoldTriangleA

{

    CGPoint o = CGPointMake(160, 160);

    

    // trianle

    p[0] = CGPointMake(120.0 * cos(0) + o.x, 120.0 * sin(0) + o.y);

    p[1] = CGPointMake(120.0 * cos(2*Deg72) + o.x, 120.0 * sin(2*Deg72) + o.y);

    p[2] = CGPointMake(120.0 * cos(3*Deg72) + o.x, 120.0 * sin(3*Deg72) + o.y);

    

    UIBezierPath *path = [UIBezierPath bezierPath];

    [path moveToPoint:p[0]];

    [path addLineToPoint:p[1]];

    [path addLineToPoint:p[2]];

    [path closePath];

    

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

    sl.fillColor = [UIColor clearColor].CGColor;

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

    sl.lineWidth = 2;

    sl.path = path.CGPath;

    

    [self.view.layer addSublayer:sl];

    

    // init angle

    angle = M_PI18.0 * M_PI / 180.0;

    

    [self startDraw:sl duration:0.3];

}

– (void)drawArcA

{    

    float startAngle = – 18.0 * M_PI / 180.0;

    float endAngle = startAngle – (M_PI2 * degree36);

    UIBezierPath *path = [UIBezierPath bezierPath];

    [path addArcWithCenter:p[0] radius:rad startAngle:startAngle endAngle:endAngle clockwise:NO];

    

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

    sl.fillColor = [UIColor clearColor].CGColor;

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

    sl.lineWidth = 3;

    sl.path = path.CGPath;

    

    [self.view.layer addSublayer:sl];

    

    [self startDraw:sl duration:0.3];

}

– (void)drawGoldTriangleB

{

    rad = hypot(p[1].xp[2].x, p[1].yp[2].y);

    // update trianle vertex

    angle = angle – (M_PI2 * degree36);

    p[0] = CGPointMake(rad * cos(angle) + p[2].x, rad * sin(angle) + p[2].y);

    

    UIBezierPath *path = [UIBezierPath bezierPath];

    [path moveToPoint:p[0]];

    [path addLineToPoint:p[1]];

    [path addLineToPoint:p[2]];

    [path closePath];

    

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

    sl.fillColor = [UIColor clearColor].CGColor;

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

    sl.lineWidth = 2;

    sl.path = path.CGPath;

    

    [self.view.layer addSublayer:sl];

    [self startDraw:sl duration:0.3];

}

– (void)drawArcB

{    

    float startAngle = – 18.0 * M_PI / 180.0 – (M_PI2 * degree36);

    float endAngle = startAngle – (M_PI2 * degree36);

    UIBezierPath *path = [UIBezierPath bezierPath];

    [path addArcWithCenter:p[2] radius:rad startAngle:startAngle endAngle:endAngle clockwise:NO];

    

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

    sl.fillColor = [UIColor clearColor].CGColor;

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

    sl.lineWidth = 3;

    sl.path = path.CGPath;

    

    [self.view.layer addSublayer:sl];

    

    [self startDraw:sl duration:0.3];

}

– (void)drawGoldTriangleC

{

    // update trianle vertex

    rad = hypot(p[0].xp[1].x, p[0].yp[1].y);

    angle = angle – (M_PI2 * degree36);

    p[2] = CGPointMake(rad * cos(angle) + p[1].x, rad * sin(angle) + p[1].y);

    

    UIBezierPath *path = [UIBezierPath bezierPath];

    [path moveToPoint:p[0]];

    [path addLineToPoint:p[1]];

    [path addLineToPoint:p[2]];

    [path closePath];

    

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

    sl.fillColor = [UIColor clearColor].CGColor;

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

    sl.lineWidth = 2;

    sl.path = path.CGPath;

    

    [self.view.layer addSublayer:sl];

    

    [self startDraw:sl duration:0.3];

}

– (void)drawArcC

{    

    float startAngle = – 18.0 * M_PI / 180.0 – (M_PI2 * degree36) * 2;

    float endAngle = startAngle – (M_PI2 * degree36);

    UIBezierPath *path = [UIBezierPath bezierPath];

    [path addArcWithCenter:p[1] radius:rad startAngle:startAngle endAngle:endAngle clockwise:NO];

    

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

    sl.fillColor = [UIColor clearColor].CGColor;

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

    sl.lineWidth = 3;

    sl.path = path.CGPath;

    

    [self.view.layer addSublayer:sl];

    

    [self startDraw:sl duration:0.3];

}

– (void)drawGoldTriangleD

{

    // update trianle vertex

    rad = hypot(p[2].xp[0].x, p[2].yp[0].y);

    angle = angle – (M_PI2 * degree36);

    p[1] = CGPointMake(rad * cos(angle) + p[0].x, rad * sin(angle) + p[0].y);

    

    UIBezierPath *path = [UIBezierPath bezierPath];

    [path moveToPoint:p[0]];

    [path addLineToPoint:p[1]];

    [path addLineToPoint:p[2]];

    [path closePath];

    

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

    sl.fillColor = [UIColor clearColor].CGColor;

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

    sl.lineWidth = 2;

    sl.path = path.CGPath;

    

    [self.view.layer addSublayer:sl];

    

    [self startDraw:sl duration:0.3];

}

– (void)drawArcD

{    

    float startAngle = – 18.0 * M_PI / 180.0 – (M_PI2 * degree36) * 3;

    float endAngle = startAngle – (M_PI2 * degree36);

    UIBezierPath *path = [UIBezierPath bezierPath];

    [path addArcWithCenter:p[0] radius:rad startAngle:startAngle endAngle:endAngle clockwise:NO];

    

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

    sl.fillColor = [UIColor clearColor].CGColor;

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

    sl.lineWidth = 3;

    sl.path = path.CGPath;

    

    [self.view.layer addSublayer:sl];

    

    [self startDraw:sl duration:0.3];

}

– (void)drawGoldTriangleE

{

    // update trianle vertex

    rad = hypot(p[2].xp[1].x, p[2].yp[1].y);

    angle = angle – (M_PI2 * degree36);

    p[0] = CGPointMake(rad * cos(angle) + p[2].x, rad * sin(angle) + p[2].y);

    

    UIBezierPath *path = [UIBezierPath bezierPath];

    [path moveToPoint:p[0]];

    [path addLineToPoint:p[1]];

    [path addLineToPoint:p[2]];

    [path closePath];

    

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

    sl.fillColor = [UIColor clearColor].CGColor;

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

    sl.lineWidth = 2;

    sl.path = path.CGPath;

    

    [self.view.layer addSublayer:sl];

    

    [self startDraw:sl duration:0.3];

}

– (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(0xFFFFFF);

        case 1:

            return UIColorHex(0x05C7F2);

        case 2:

            return UIColorHex(0x80DDF2);

        case 3:

            return UIColorHex(0xF29966);

        case 4:

            return UIColorHex(0xF27B35);

        default:

            break;

    }

    return nil;

}

– (void)didReceiveMemoryWarning

{

    [super didReceiveMemoryWarning];

    // Dispose of any resources that can be recreated.

}

@end