iPhone プリズムアプリ

電球から出てくる光線を、16個のプリズムを使って直角に反射させる簡単なシミュレーションをiPhoneアプリで作ってみます。三角プリズムの一番長い辺光が45度で当たった場合に、光を直角に曲げるようにします。


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

ポイント
プリズムに当たる際の光の向き(上、右、下、左)とプリズムの角度(5パターン)について、次はどっちの方向に光が進むかを分岐で判定しています。格子状にならべたプリズムの開始位置をNo.13として、そこから順番に光を進めていき、UIBezierPathを使って、光線を表示してみました。

サンプルコード

#import “ViewController.h”

#import <QuartzCore/QuartzCore.h>

#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]

@interface ViewController () {

    NSMutableArray *reflectors;

    CGPoint gridPoints[16];

    NSMutableArray *prismArr;

    UIView *goal;

    UIView *lightingView;

}

@end

@implementation ViewController

– (void)viewDidLoad

{

    [super viewDidLoad];

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

    [self createGridPoints];

    [self drawGrid];

    

    [self createLight];

    [self createPrism];

    

    [self lighting];

}

– (void)createGridPoints

{

    float w = (32060) / 3.0;

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

        float x = (i % 4) * w + 30;

        float y = (i / 4) * w + 100;

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

    }

}

– (void)drawGrid

{

    UIBezierPath *path = [[UIBezierPath alloc] init];

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

        // horizontal

        [path moveToPoint:gridPoints[i * 4]];

        [path addLineToPoint:gridPoints[i * 4 + 3]];

        

        // vertical

        [path moveToPoint:gridPoints[i]];

        [path addLineToPoint:gridPoints[3 * 4 + i]];

    }

    

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

    sl.fillColor = [UIColor clearColor].CGColor;

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

    sl.lineWidth = 1.0;

    sl.path = path.CGPath;

    

    [self.view.layer addSublayer:sl];

}

– (void)createLight

{

    float w = 30;

    float h = w * 1.618;

    float r = 30 * 0.4;

    UIView *light = [[UIView alloc] initWithFrame:CGRectMake(0, 0, w, h)];

    light.center = CGPointMake(gridPoints[1].x, 420);

    light.backgroundColor = [UIColor clearColor];

    [self.view addSubview:light];

    

    UIView *bulb = [[UIView alloc] initWithFrame:CGRectMake(w/2.0 – r, 0, 2*r, 2*r)];

    bulb.backgroundColor = [self color:4];

    bulb.layer.cornerRadius = r;

    [light addSubview:bulb];

    

    UIView *body = [[UIView alloc] initWithFrame:CGRectMake(0, r, w, h-r)];

    body.backgroundColor = [self color:3];

    [light addSubview:body];

}

– (void)createPrism

{

    prismArr = [[NSMutableArray alloc] init];

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

        CGPoint p = gridPoints[i];

        float size = 20;

        UIBezierPath *path = [[UIBezierPath alloc] init];

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

        [path addLineToPoint:CGPointMake(2*size, size)];

        [path addLineToPoint:CGPointMake(size, 0)];

        

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

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

        sl.path = path.CGPath;

        

        UIView *prism = [[UIView alloc] initWithFrame:CGRectMake(0, 0, size*2, size)];

        prism.center = p;

        [prism.layer addSublayer:sl];

        [self.view addSubview:prism];

        

        [prismArr addObject:prism];

        

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

        [prism addGestureRecognizer:tap];

    }

}

– (void)tap:(UITapGestureRecognizer*)gr

{

    float angle = 0;

    

    if (gr.view.tag == 0) {

        angle = 45.0 * M_PI/180.0;

        gr.view.tag = 1;

    } else if (gr.view.tag == 1) {

        angle = 135.0 * M_PI/180.0;

        gr.view.tag = 2;

    } else if (gr.view.tag == 2) {

        angle = 225.0 * M_PI/180.0;

        gr.view.tag = 3;

    } else if (gr.view.tag == 3) {

        angle = 315.0 * M_PI/180.0;

        gr.view.tag = 4;

    } else {

        gr.view.tag = 0;

    }

    

    [UIView animateWithDuration:0.3 animations:^{

        gr.view.transform = CGAffineTransformMakeRotation(angle);

    }];

    

    [self lighting];

}

– (void)lighting

{

    [lightingView removeFromSuperview];

    lightingView = [[UIView alloc] initWithFrame:self.view.bounds];

    lightingView.userInteractionEnabled = NO;

    [self.view addSubview:lightingView];

    

    UIBezierPath *path = [UIBezierPath bezierPath];

    [path moveToPoint:CGPointMake(gridPoints[1].x, 400)];

    

    int start = 13;

    int index = start;

    int direction = 0;

    

    // 無限はあれなので、30を指定

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

        

        // draw laser beam

        [path addLineToPoint:gridPoints[index]];

        

        

        // direction

        direction = [self checkDirection:direction AtIndex:index];

        // next point

        int next;

        // direction: up:0 right:1 down:2 left:3

        switch (direction) {

            case 0:

                next = index – 4;

                break;

            case 1:

                next = index + 1;

                if (index / 4 != next / 4) {

                    next = –1;

                }

                break;

            case 2:

                next = index + 4;

                break;

                

            case 3:

                next = index – 1;

                if (index / 4 != next / 4) {

                    next = –1;

                }

                break;

        }

        

        if (next < 0 || next >= 16) {

            break;

        }

        index = next;

    }

    

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

    sl.fillColor = [UIColor clearColor].CGColor;

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

    sl.lineWidth = 5;

    sl.path = path.CGPath;

    

    [lightingView.layer addSublayer:sl];

}

– (int)checkDirection:(int)direction AtIndex:(int)index

{

    // direction: up:0 right:1 down:2 left:3

    

    UIView *prism = [prismArr objectAtIndex:index];

    int nextDirection = 0;

    if (prism.tag == 0) {

        nextDirection = direction;

    }

    

    if (prism.tag == 1) {

        switch (direction) {

            case 0: nextDirection = 0; break;

            case 1: nextDirection = 1; break;

            case 2: nextDirection = 1; break;

            case 3: nextDirection = 0; break;

        }

    }

    

    if (prism.tag == 2) {

        switch (direction) {

            case 0: nextDirection = 1; break;

            case 1: nextDirection = 1; break;

            case 2: nextDirection = 2; break;

            case 3: nextDirection = 2; break;

        }

    }

    

    if (prism.tag == 3) {

        switch (direction) {

            case 0: nextDirection = 3; break;

            case 1: nextDirection = 2; break;

            case 2: nextDirection = 2; break;

            case 3: nextDirection = 3; break;

        }

    }

    

    if (prism.tag == 4) {

        switch (direction) {

            case 0: nextDirection = 0; break;

            case 1: nextDirection = 0; break;

            case 2: nextDirection = 3; break;

            case 3: nextDirection = 3; break;

        }

    }

    

    return nextDirection;

}

– (UIColor*)color:(int)i

{

    switch (i) {

        case 0:

            return UIColorHex(0x44CB6B);

        case 1:

            return UIColorHex(0x91FCE0);

        case 2:

            return UIColorHex(0xE8F9E4);

        case 3:

            return UIColorHex(0xC3E26E);

        case 4:

            return UIColorHex(0xFCF498);

        default:

            break;

    }

    return nil;

}

– (void)didReceiveMemoryWarning

{

    [super didReceiveMemoryWarning];

    // Dispose of any resources that can be recreated.

}

@end