電球から出てくる光線を、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 = (320 – 60) / 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