package {
import flash.display.*;
import flash.events.Event;
import flash.filters.*;
[SWF(width="400", height="400", backgroundColor="0x000000")]
public class Reflection extends Sprite {
private var _sprite:Sprite = new Sprite();
private var _lines:Array = new Array();
public function Reflection() {
addEventListener(Event.ENTER_FRAME, onEnterFrame);
var g:Graphics;
var i:int;
var list:Array = [
[new Vec2(0, 0), new Vec2(0, 400)],
[new Vec2(0, 400), new Vec2(400, 400)],
[new Vec2(400, 400), new Vec2(400, 0)],
[new Vec2(400, 0), new Vec2(0, 0)],
[new Vec2(80, 80), new Vec2(200, 80)],
[new Vec2(200, 80), new Vec2(50, 200)],
[new Vec2(50, 200), new Vec2(80, 80)],
[new Vec2(300, 100), new Vec2(350, 300)],
[new Vec2(350, 300), new Vec2(250, 300)],
[new Vec2(250, 300), new Vec2(300, 100)],
[new Vec2(100, 250), new Vec2(200, 250)],
[new Vec2(200, 250), new Vec2(200, 350)],
[new Vec2(200, 350), new Vec2(100, 250)]
]
// create lines
var s:Sprite = new Sprite();
g = s.graphics;
g.lineStyle(2, 0xaaddff);
for (i = 0; i < list.length; i++) {
var li:Line = new Line();
li.a = new Vec2(list[i][0].x, list[i][0].y);
li.b = new Vec2(list[i][1].x, list[i][1].y);
li.n = Vec2.sub(li.a, li.b);
li.n.normalize();
var x:Number = li.n.x;
var y:Number = li.n.y;
li.n.x = -y;
li.n.y = x;
_lines[i] = li;
g.moveTo(li.a.x, li.a.y);
g.lineTo(li.b.x, li.b.y);
}
this.addChild(s);
this.filters = [new GlowFilter(0xffffff, 1.0, 8, 8)];
addChild(_sprite);
}
public function onEnterFrame(ev:Event) : void {
frame();
}
public function raytrace(r:Ray, g:Graphics, depth:int):void {
var i:int;
if (depth >= 6) return;
var k:Number = 999999;
var hit:Line = null;
for (i = 0; i < _lines.length; i++) {
var kk:Number = _lines[i].intersect(r);
if (kk > 0 && kk < k) {
k = kk;
hit = _lines[i];
}
}
if (hit == null)
return;
if (0 < k) {
var x:Number = r.pos.x + k * r.dir.x;
var y:Number = r.pos.y + k * r.dir.y;
var z:Number = 255 * Math.pow(0.7, depth);
g.lineStyle(2, (int(z) << 16) + (int(255 - z)));
g.moveTo(r.pos.x, r.pos.y);
g.lineTo(x, y);
var ss:Number = 0.6;
var cosi:Number = -Vec2.dot(r.dir, hit.n);
var vn = hit.n.clone();
if (cosi < 0) {
ss = 1 / ss;
cosi = -cosi;
vn = Vec2.mul(-1, vn);
}
var ray:Ray = new Ray();
ray.pos = Vec2.add(
Vec2.add(r.pos, Vec2.mul(k, r.dir)),
Vec2.mul(0.1, vn));
ray.dir = Vec2.add(r.dir, Vec2.mul(2 * cosi, vn));
ray.dir.normalize();
raytrace(ray, g, depth + 1);
const zz:Number = 1 - (ss * ss) * (1 - cosi * cosi);
if (zz >= 0) {
ray.pos = Vec2.add(
Vec2.add(r.pos, Vec2.mul(k + 0.01, r.dir)),
Vec2.mul(0, vn));
ray.dir.x = ss * (cosi * vn.x + r.dir.x) - Math.sqrt(zz) * vn.x;
ray.dir.y = ss * (cosi * vn.y + r.dir.y) - Math.sqrt(zz) * vn.y;
ray.dir.normalize();
raytrace(ray, g, depth + 1);
}
}
}
private var tt:Number = 0;
public function frame() : void {
var i:int;
var g:Graphics = _sprite.graphics;
g.clear();
tt += 0.01;
var ray:Ray = new Ray();
ray.pos = new Vec2(200, 200);
ray.dir = new Vec2(Math.cos(tt), Math.sin(tt));
ray.dir.normalize();
raytrace(ray, g, 0);
}
}
}
class Ray {
public var pos:Vec2 = new Vec2(0, 0);
public var dir:Vec2 = new Vec2(0, 0);
}
class Line {
public var a:Vec2 = new Vec2(0, 0);
public var b:Vec2 = new Vec2(0, 0);
public var n:Vec2 = new Vec2(0, 0);
public function intersect(r:Ray) : Number {
var det:Number = r.dir.x * (a.y - b.y) - r.dir.y * (a.x - b.x);
if (Math.abs(det) < 1e-5)
return -1;
var t:Number = (r.dir.x * (a.y - r.pos.y) - r.dir.y * (a.x - r.pos.x)) / det;
if (t < 0 || t > 1)
return -1;
var k:Number = ((a.y - b.y) * (a.x - r.pos.x) - (a.x - b.x) * (a.y - r.pos.y)) / det;
return k;
}
}
class Vec2 {
public var x:Number, y:Number;
public function Vec2(x:Number = 0, y:Number = 0) {
this.x = x; this.y = y;
}
public function clone():Vec2 {
return new Vec2(this.x, this.y);
}
public function copyTo(v:Vec2):void {
v.x = x; v.y = y;
}
public function zero():void {
x = y = 0.0;
}
public static function add(a:Vec2, b:Vec2):Vec2 {
return new Vec2(a.x + b.x, a.y + b.y);
}
public static function sub(a:Vec2, b:Vec2):Vec2 {
return new Vec2(a.x - b.x, a.y - b.y);
}
public static function mul(a:Number, v:Vec2):Vec2 {
return new Vec2(a * v.x, a * v.y);
}
public static function dot(a:Vec2, b:Vec2):Number {
return a.x * b.x + a.y * b.y;
}
public function length():Number {
return Math.sqrt(x * x + y * y);
}
public function lengthSq():Number {
return x * x + y * y;
}
public function normalize():void {
var len:Number = length();
x /= len; y /= len;
}
}