ソースを参照

Merge branch 'webgl'

ncannasse 12 年 前
コミット
c16c37e00a
100 ファイル変更8946 行追加1007 行削除
  1. 3 0
      .gitignore
  2. 12 5
      engine.hxml
  3. 6 9
      engine.hxproj
  4. 39 0
      h2d/Anim.hx
  5. 1 1
      h2d/Bitmap.hx
  6. 68 9
      h2d/Drawable.hx
  7. 9 2
      h2d/Font.hx
  8. 265 43
      h2d/Graphics.hx
  9. 19 19
      h2d/Interactive.hx
  10. 33 104
      h2d/Scene.hx
  11. 3 3
      h2d/Sprite.hx
  12. 82 28
      h2d/SpriteBatch.hx
  13. 33 17
      h2d/Tile.hx
  14. 74 76
      h2d/TileColorGroup.hx
  15. 18 20
      h2d/TileGroup.hx
  16. 5 5
      h2d/Tools.hx
  17. 0 0
      h2d/col/Delaunay.hx
  18. 10 0
      h2d/col/Poly.hx
  19. 41 29
      h2d/col/Voronoi.hx
  20. 26 25
      h2d/comp/ColorPicker.hx
  21. 2 2
      h2d/comp/Component.hx
  22. 1 1
      h2d/comp/Context.hx
  23. 10 8
      h2d/comp/GradientEditor.hx
  24. 3 2
      h2d/comp/Input.hx
  25. 1 1
      h2d/comp/Slider.hx
  26. 1 1
      h3d/Drawable.hx
  27. 54 290
      h3d/Engine.hx
  28. 1 1
      h3d/anim/Animation.hx
  29. 2 2
      h3d/anim/FrameAnimation.hx
  30. 2 2
      h3d/anim/LinearAnimation.hx
  31. 15 0
      h3d/col/Bounds.hx
  32. 13 4
      h3d/fbx/Library.hx
  33. 1 1
      h3d/fbx/Parser.hx
  34. 4 4
      h3d/impl/Buffer.hx
  35. 118 0
      h3d/impl/Driver.hx
  36. 6 1
      h3d/impl/Indexes.hx
  37. 48 79
      h3d/impl/MemoryManager.hx
  38. 115 0
      h3d/impl/Shader.hx
  39. 43 2
      h3d/impl/Shaders.hx
  40. 422 0
      h3d/impl/Stage3dDriver.hx
  41. 474 0
      h3d/impl/WebglDriver.hx
  42. 6 0
      h3d/mat/Data.hx
  43. 4 1
      h3d/mat/Material.hx
  44. 275 7
      h3d/mat/MeshMaterial.hx
  45. 5 2
      h3d/mat/PartMaterial.hx
  46. 0 18
      h3d/mat/PngBytes.hx
  47. 12 32
      h3d/mat/Texture.hx
  48. 13 14
      h3d/prim/Cube.hx
  49. 18 21
      h3d/prim/FBXModel.hx
  50. 22 23
      h3d/prim/GeoSphere.hx
  51. 2 2
      h3d/prim/MeshPrimitive.hx
  52. 1 1
      h3d/prim/Plan2D.hx
  53. 1 1
      h3d/prim/Plan3D.hx
  54. 23 26
      h3d/prim/Polygon.hx
  55. 9 10
      h3d/prim/Quads.hx
  56. 1 1
      h3d/prim/RawPrimitive.hx
  57. 1 1
      h3d/prim/Sphere.hx
  58. 1 2
      h3d/scene/CustomObject.hx
  59. 23 19
      h3d/scene/Image.hx
  60. 2 1
      h3d/scene/Mesh.hx
  61. 2 1
      h3d/scene/MultiMaterial.hx
  62. 4 4
      h3d/scene/Object.hx
  63. 10 10
      h3d/scene/Particles.hx
  64. 2 1
      h3d/scene/RenderContext.hx
  65. 93 0
      hxd/BitmapData.hx
  66. 47 0
      hxd/BytesBuffer.hx
  67. 1 1
      hxd/Event.hx
  68. 80 0
      hxd/FloatBuffer.hx
  69. 62 0
      hxd/IndexBuffer.hx
  70. 14 2
      hxd/Key.hx
  71. 365 0
      hxd/Perlin.hx
  72. 53 0
      hxd/Rand.hx
  73. 10 0
      hxd/Res.hx
  74. 209 0
      hxd/Stage.hx
  75. 59 10
      hxd/System.hx
  76. 34 0
      hxd/Timer.hx
  77. 8 0
      hxd/clipper/ClipType.hx
  78. 3602 0
      hxd/clipper/Clipper.hx
  79. 8 0
      hxd/clipper/EndType.hx
  80. 7 0
      hxd/clipper/JoinType.hx
  81. 16 0
      hxd/clipper/Point.hx
  82. 8 0
      hxd/clipper/PolyFillType.hx
  83. 6 0
      hxd/clipper/PolyType.hx
  84. 86 0
      hxd/clipper/Polygon.hx
  85. 3 0
      hxd/clipper/Polygons.hx
  86. 13 0
      hxd/clipper/Rect.hx
  87. 18 0
      hxd/impl/ArrayIterator.hx
  88. 41 0
      hxd/impl/Memory.hx
  89. 33 0
      hxd/impl/Tmp.hx
  90. 86 0
      hxd/poly2tri/AdvancingFront.hx
  91. 26 0
      hxd/poly2tri/Basin.hx
  92. 22 0
      hxd/poly2tri/Constants.hx
  93. 47 0
      hxd/poly2tri/Edge.hx
  94. 12 0
      hxd/poly2tri/EdgeEvent.hx
  95. 57 0
      hxd/poly2tri/Node.hx
  96. 23 0
      hxd/poly2tri/Orientation.hx
  97. 75 0
      hxd/poly2tri/Point.hx
  98. 647 0
      hxd/poly2tri/Sweep.hx
  99. 168 0
      hxd/poly2tri/SweepContext.hx
  100. 387 0
      hxd/poly2tri/Triangle.hx

+ 3 - 0
.gitignore

@@ -6,3 +6,6 @@
 *.n
 
 /samples/comps/arial.ttf
+/engine.js
+/engine.js.map
+/samples/2d/demo.js

+ 12 - 5
engine.hxml

@@ -1,11 +1,18 @@
 -cp samples/basic
--swf engine.swf
--swf-header 800:600:60:FFFFFF
---flash-strict
--swf-version 11
+-js engine.js
 -main Test
+--macro include('h3d')
+--macro include('h2d')
+--macro include('hxd')
+-lib format
+--next
+-swf engine.swf
+-swf-header 800:600:60:000000
+-swf-version 11.6
 -lib hxsl
 -D h3d
--swf-version 11.6
+-cp samples/basic
+-main Test
 --macro include('h3d')
 --macro include('h2d')
+--macro include('hxd')

+ 6 - 9
engine.hxproj

@@ -4,13 +4,13 @@
   <output>
     <movie outputType="Application" />
     <movie input="" />
-    <movie path="engine.swf" />
+    <movie path="engine.js" />
     <movie fps="60" />
     <movie width="800" />
     <movie height="600" />
-    <movie version="11" />
+    <movie version="1" />
     <movie minorVersion="0" />
-    <movie platform="Flash Player" />
+    <movie platform="JavaScript" />
     <movie background="#FFFFFF" />
   </output>
   <!-- Other classes to be compiled into your SWF -->
@@ -23,7 +23,7 @@
     <option flashStrict="True" />
     <option mainClass="Test" />
     <option enabledebug="False" />
-    <option additional="-lib hxsl&#xA;-D h3d&#xA;-swf-version 11.6&#xA;--macro include('h3d')&#xA;--macro include('h2d')" />
+    <option additional="--macro include('h3d')&#xA;--macro include('h2d')&#xA;--macro include('hxd')&#xA;-lib format&#xA;&#xA;--next&#xA;&#xA;-swf engine.swf&#xA;-swf-header 800:600:60:000000&#xA;-swf-version 11.6&#xA;-lib hxsl&#xA;-D h3d&#xA;-cp samples/basic&#xA;-main Test&#xA;--macro include('h3d')&#xA;--macro include('h2d')&#xA;--macro include('hxd')" />
   </build>
   <!-- haxelib libraries -->
   <haxelib>
@@ -33,10 +33,6 @@
   <compileTargets>
     <!-- example: <compile path="..." /> -->
   </compileTargets>
-  <!-- Assets to embed into the output SWF -->
-  <library>
-    <!-- example: <asset path="..." id="..." update="..." glyphs="..." mode="..." place="..." sharepoint="..." /> -->
-  </library>
   <!-- Paths to exclude from the Project Explorer tree -->
   <hiddenPaths>
     <hidden path="engine.hxml" />
@@ -48,7 +44,8 @@
   <!-- Other project options -->
   <options>
     <option showHiddenPaths="False" />
-    <option testMovie="Default" />
+    <option testMovie="OpenDocument" />
+    <option testMovieCommand="index.html" />
   </options>
   <!-- Plugin storage -->
   <storage />

+ 39 - 0
h2d/Anim.hx

@@ -0,0 +1,39 @@
+package h2d;
+
+class Anim extends Drawable {
+
+	public var frames : Array<Tile>;
+	public var currentFrame : Float;
+	public var speed : Float;
+	public var loop : Bool = true;
+	
+	public function new( ?parent ) {
+		super(parent);
+		this.frames = [];
+		this.currentFrame = 0;
+		this.speed = 1.;
+	}
+	
+	public function play( frames ) {
+		this.frames = frames;
+		this.currentFrame = 0;
+	}
+	
+	override function sync( ctx : RenderContext ) {
+		currentFrame += speed * ctx.elapsedTime;
+		if( loop )
+			currentFrame %= frames.length;
+		else if( currentFrame >= frames.length )
+			currentFrame = frames.length - 0.00001;
+	}
+	
+	public function getFrame() {
+		return frames[Std.int(currentFrame)];
+	}
+	
+	override function draw( ctx : RenderContext ) {
+		var t = getFrame();
+		if( t != null ) drawTile(ctx.engine,t);
+	}
+	
+}

+ 1 - 1
h2d/Bitmap.hx

@@ -13,7 +13,7 @@ class Bitmap extends Drawable {
 		drawTile(ctx.engine,tile);
 	}
 			
-	public static function create( bmp : flash.display.BitmapData, ?allocPos : h3d.impl.AllocPos ) {
+	public static function create( bmp : hxd.BitmapData, ?allocPos : h3d.impl.AllocPos ) {
 		return new Bitmap(Tile.fromBitmap(bmp,allocPos));
 	}
 	

+ 68 - 9
h2d/Drawable.hx

@@ -1,6 +1,7 @@
 package h2d;
 
-private class DrawableShader extends hxsl.Shader {
+private class DrawableShader extends h3d.impl.Shader {
+	#if flash
 	static var SRC = {
 		var input : {
 			pos : Float2,
@@ -19,12 +20,12 @@ private class DrawableShader extends hxsl.Shader {
 		var skew : Float;
 		var zValue : Float;
 
-		function vertex( size : Float3, mat1 : Float3, mat2 : Float3 ) {
+		function vertex( size : Float3, matA : Float3, matB : Float3 ) {
 			var tmp : Float4;
 			var spos = input.pos.xyw;
 			if( size != null ) spos *= size;
-			tmp.x = spos.dp3(mat1);
-			tmp.y = spos.dp3(mat2);
+			tmp.x = spos.dp3(matA);
+			tmp.y = spos.dp3(matB);
 			tmp.z = zValue;
 			tmp.w = skew != null ? 1 - skew * input.pos.y : 1;
 			out = tmp;
@@ -56,9 +57,12 @@ private class DrawableShader extends hxsl.Shader {
 		var multMapFactor : Float;
 		var multMap : Texture;
 		var multUV : Float4;
+		var hasColorKey : Bool;
+		var colorKey : Int;
 
 		function fragment( tex : Texture ) {
-			var col = tex.get(sinusDeform != null ? [tuv.x + sin(tuv.y*sinusDeform.y + sinusDeform.x) * sinusDeform.z, tuv.y] : tuv, filter = ! !filter, wrap=tileWrap);
+			var col = tex.get(sinusDeform != null ? [tuv.x + sin(tuv.y * sinusDeform.y + sinusDeform.x) * sinusDeform.z, tuv.y] : tuv, filter = ! !filter, wrap = tileWrap);
+			if( hasColorKey ) kill(abs(col.rgb - colorKey.rgb).length() - 0.001);
 			if( killAlpha ) kill(col.a - 0.001);
 			if( hasVertexAlpha ) col.a *= talpha;
 			if( hasVertexColor ) col *= tcolor;
@@ -74,6 +78,50 @@ private class DrawableShader extends hxsl.Shader {
 
 	}
 	
+	#elseif js
+	
+	static var VERTEX = "
+	
+		attribute vec2 pos;
+		attribute vec2 uv;
+
+		uniform vec3 size;
+		uniform vec3 matA;
+		uniform vec3 matB;
+		uniform lowp float zValue;
+		
+		varying lowp vec2 tuv;
+
+		void main(void) {
+			vec3 spos = vec3(pos.xy,1.0) * size;
+			vec4 tmp;
+			tmp.x = dot(spos,matA);
+			tmp.y = dot(spos,matB);
+			tmp.z = zValue;
+			tmp.w = 1.;
+			gl_Position = tmp;
+			tuv = uv;
+		}
+
+	";
+	
+	static var FRAGMENT = "
+	
+		varying lowp vec2 tuv;
+		uniform sampler2D tex;
+		
+		const bool hasAlpha = true;
+		uniform lowp float alpha;
+	
+		void main(void) {
+			lowp vec4 col = texture2D(tex, tuv);
+			if( hasAlpha ) col.w *= alpha;
+			gl_FragColor = col;
+		}
+			
+	";
+	
+	#end
 }
 
 class Drawable extends Sprite {
@@ -103,6 +151,8 @@ class Drawable extends Sprite {
 	public var multiplyMap(default, set) : h2d.Tile;
 	public var multiplyFactor(get, set) : Float;
 	
+	public var colorKey(get, set) : Int;
+	
 	public var writeAlpha : Bool;
 	
 	function new(parent) {
@@ -212,6 +262,15 @@ class Drawable extends Sprite {
 	inline function set_killAlpha(v) {
 		return shader.killAlpha = v;
 	}
+
+	inline function get_colorKey() {
+		return shader.colorKey;
+	}
+	
+	inline function set_colorKey(v) {
+		shader.hasColorKey = true;
+		return shader.colorKey = v;
+	}
 	
 	function drawTile( engine, tile ) {
 		setupShader(engine, tile, HAS_SIZE | HAS_UV_POS | HAS_UV_SCALE);
@@ -273,16 +332,16 @@ class Drawable extends Sprite {
 		var cm = writeAlpha ? 15 : 7;
 		if( mat.colorMask != cm ) mat.colorMask = cm;
 		
-		var tmp = core.tmpMat1;
+		var tmp = core.tmpMatA;
 		tmp.x = matA;
 		tmp.y = matC;
 		tmp.z = absX + tile.dx * matA + tile.dy * matC;
-		shader.mat1 = tmp;
-		var tmp = core.tmpMat2;
+		shader.matA = tmp;
+		var tmp = core.tmpMatB;
 		tmp.x = matB;
 		tmp.y = matD;
 		tmp.z = absY + tile.dx * matB + tile.dy * matD;
-		shader.mat2 = tmp;
+		shader.matB = tmp;
 		shader.tex = tile.getTexture();
 		mat.shader = shader;
 		engine.selectMaterial(mat);

+ 9 - 2
h2d/Font.hx

@@ -69,6 +69,7 @@ class Font #if !macro extends Tile #end {
 	}
 		
 	function init() {
+		#if flash
 		lineHeight = 0;
 		var tf = new flash.text.TextField();
 		var fmt = tf.defaultTextFormat;
@@ -158,9 +159,10 @@ class Font #if !macro extends Tile #end {
 			}
 		}
 		var bytes = haxe.io.Bytes.ofData(bytes);
+		var width = bmp.width, height = bmp.height;
 		
 		if( innerTex == null ) {
-			var t = h3d.Engine.getCurrent().mem.allocTexture(bmp.width, bmp.height);
+			var t = h3d.Engine.getCurrent().mem.allocTexture(width, height);
 			t.uploadBytes(bytes);
 			setTexture(t);
 			for( t in all )
@@ -170,6 +172,11 @@ class Font #if !macro extends Tile #end {
 			innerTex.uploadBytes(bytes);
 		
 		bmp.dispose();
+		#else
+		
+		throw "TODO";
+		
+		#end
 		
 		inline function map(code, to) {
 			if( glyphs[code] == null ) glyphs[code] = glyphs[to] else if( glyphs[to] == null ) glyphs[to] = glyphs[code];
@@ -222,7 +229,7 @@ class Font #if !macro extends Tile #end {
 			if( !skipErrors ) throw "Font file not found " + file;
 			return macro false;
 		}
-		if( chars == null ) chars = DEFAULT_CHARS;
+		if( chars == null ) chars = DEFAULT_CHARSET;
 		var pos = haxe.macro.Context.currentPos();
 		var safeName = "F_"+~/[^A-Za-z_]+/g.replace(name, "_");
 		haxe.macro.Context.defineType({

+ 265 - 43
h2d/Graphics.hx

@@ -1,74 +1,296 @@
 package h2d;
 
-@:allow(h2d.Graphics)
-class GraphicsContext {
+private typedef GraphicsPoint = hxd.poly2tri.Point;
+
+private class LinePoint {
+	public var x : Float;
+	public var y : Float;
+	public var r : Float;
+	public var g : Float;
+	public var b : Float;
+	public var a : Float;
+	public function new(x, y, r, g, b, a) {
+		this.x = x;
+		this.y = y;
+		this.r = r;
+		this.g = g;
+		this.b = b;
+		this.a = a;
+	}
+}
+
+private class GraphicsContent extends h3d.prim.Primitive {
+
+	var tmp : hxd.FloatBuffer;
+	var index : hxd.IndexBuffer;
 	
-	var g : Graphics;
-	var mc : flash.display.Sprite;
-	var mcg : flash.display.Graphics;
+	var buffers : Array<{ buf : hxd.FloatBuffer, vbuf : h3d.impl.Buffer, idx : hxd.IndexBuffer, ibuf : h3d.impl.Indexes }>;
 	
-	function new(g) {
-		this.g = g;
-		this.mc = new flash.display.Sprite();
-		mcg = mc.graphics;
+	public function new() {
+		buffers = [];
+	}
+
+	public inline function addIndex(i) {
+		index.push(i);
+	}
+
+	public inline function add( x : Float, y : Float, u : Float, v : Float, r : Float, g : Float, b : Float, a : Float ) {
+		tmp.push(x);
+		tmp.push(y);
+		tmp.push(u);
+		tmp.push(v);
+		tmp.push(r);
+		tmp.push(g);
+		tmp.push(b);
+		tmp.push(a);
 	}
 	
-	public inline function beginFill( color : Int, alpha = 1. ) {
-		mcg.beginFill(color, alpha);
+	public function next() {
+		var nvect = tmp.length >> 3;
+		if( nvect < 1 << 15 )
+			return false;
+		buffers.push( { buf : tmp, idx : index, vbuf : null, ibuf : null } );
+		tmp = new hxd.FloatBuffer();
+		index = new hxd.IndexBuffer();
+		super.dispose();
+		return true;
 	}
 	
-	public inline function drawCircle(cx, cy, radius) {
-		mcg.drawCircle(cx, cy, radius);
+	override function alloc( engine : h3d.Engine ) {
+		buffer = engine.mem.allocVector(tmp, 8, 0);
+		indexes = engine.mem.allocIndex(index);
+		for( b in buffers ) {
+			if( b.vbuf == null || b.vbuf.isDisposed() ) b.vbuf = engine.mem.allocVector(b.buf, 8, 0);
+			if( b.ibuf == null || b.ibuf.isDisposed() ) b.ibuf = engine.mem.allocIndex(b.idx);
+		}
 	}
 	
-	public inline function drawRect(x,y,width,height) {
-		mcg.drawRect(x, y, width, height);
+	override function render( engine : h3d.Engine ) {
+		if( buffer == null || buffer.isDisposed() ) alloc(engine);
+		for( b in buffers )
+			engine.renderIndexed(b.vbuf, b.ibuf);
+		super.render(engine);
 	}
 	
-	public inline function endFill() {
-		mcg.endFill();
+	override function dispose() {
+		for( b in buffers ) {
+			if( b.vbuf != null ) b.vbuf.dispose();
+			if( b.ibuf != null ) b.ibuf.dispose();
+			b.vbuf = null;
+			b.ibuf = null;
+		}
+		super.dispose();
+	}
+	
+	
+	public function reset() {
+		dispose();
+		tmp = new hxd.FloatBuffer();
+		index = new hxd.IndexBuffer();
+		buffers = [];
 	}
 	
 }
 
 class Graphics extends Drawable {
 
-	var tile : Tile;
-	var ctx : GraphicsContext;
+	var content : GraphicsContent;
+	var pts : Array<GraphicsPoint>;
+	var linePts : Array<LinePoint>;
+	var pindex : Int;
+	var prev : Array<Array<GraphicsPoint>>;
+	var curR : Float;
+	var curG : Float;
+	var curB : Float;
+	var curA : Float;
+	var lineSize : Float;
+	var lineR : Float;
+	var lineG : Float;
+	var lineB : Float;
+	var lineA : Float;
+	var doFill : Bool;
+	
+	public var tile : h2d.Tile;
 	
 	public function new(?parent) {
 		super(parent);
+		content = new GraphicsContent();
+		shader.hasVertexColor = true;
+		tile = h2d.Tile.fromColor(0xFFFFFFFF);
+		clear();
+	}
+	
+	public function clear() {
+		content.reset();
+		pts = [];
+		prev = [];
+		linePts = [];
+		pindex = 0;
+		lineSize = 0;
 	}
-		
-	public function beginDraw() {
-		return (ctx = new GraphicsContext(this));
+
+	function isConvex( points : Array<GraphicsPoint> ) {
+		for( i in 0...points.length ) {
+			var p1 = points[i];
+			var p2 = points[(i + 1) % points.length];
+			var p3 = points[(i + 2) % points.length];
+			if( (p2.x - p1.x) * (p3.y - p1.y) - (p2.y - p1.y) * (p3.x - p1.x) > 0 )
+				return false;
+		}
+		return true;
+	}
+	
+	function flushLine() {
+		if( linePts.length == 0 )
+			return;
+		var last = linePts.length - 1;
+		var prev = linePts[last];
+		var p = linePts[0];
+		var start = pindex;
+		for( i in 0...linePts.length ) {
+			var next = linePts[(i + 1) % linePts.length];
+			var nx1 = prev.y - p.y;
+			var ny1 = p.x - prev.x;
+			var ns1 = h3d.FMath.isqrt(nx1 * nx1 + ny1 * ny1);
+			var nx2 = p.y - next.y;
+			var ny2 = next.x - p.x;
+			var ns2 = h3d.FMath.isqrt(nx2 * nx2 + ny2 * ny2);
+			
+			var nx = (nx1 * ns1 + nx2 * ns2) * lineSize * 0.5;
+			var ny = (ny1 * ns1 + ny2 * ns2) * lineSize * 0.5;
+			
+			content.add(p.x + nx, p.y + ny, 0, 0, p.r, p.g, p.b, p.a);
+			content.add(p.x - nx, p.y - ny, 0, 0, p.r, p.g, p.b, p.a);
+			
+			var pnext = i == last ? start : pindex + 2;
+			
+			content.addIndex(pindex);
+			content.addIndex(pindex + 1);
+			content.addIndex(pnext);
+
+			content.addIndex(pindex + 1);
+			content.addIndex(pnext);
+			content.addIndex(pnext + 1);
+			
+			pindex += 2;
+			
+			prev = p;
+			p = next;
+		}
+		linePts = [];
+		if( content.next() )
+			pindex = 0;
 	}
 	
-	override function onDelete() {
-		if( tile != null ) {
-			tile.dispose();
-			tile = null;
+	function flushFill() {
+		if( pts.length > 0 ) {
+			prev.push(pts);
+			pts = [];
 		}
-		super.onDelete();
-	}
-	
-	public function endDraw( restoreOnContextLost = false, ?allocPos : h3d.impl.AllocPos ) {
-		if( ctx == null ) return;
-		if( tile != null ) tile.dispose();
-		tile = Tile.fromSprites([ctx.mc], allocPos)[0];
-		if( restoreOnContextLost ) {
-			var ctx = ctx;
-			tile.getTexture().onContextLost = function() {
-				this.ctx = ctx;
-				endDraw();
-			};
+		if( prev.length == 0 )
+			return;
+			
+		if( prev.length == 1 && isConvex(prev[0]) ) {
+			var p0 = prev[0][0].id;
+			for( i in 1...prev[0].length - 1 ) {
+				content.addIndex(p0);
+				content.addIndex(p0 + i);
+				content.addIndex(p0 + i + 1);
+			}
+		} else {
+			var ctx = new hxd.poly2tri.SweepContext();
+			for( p in prev )
+				ctx.addPolyline(p);
+				
+			var p = new hxd.poly2tri.Sweep(ctx);
+			p.triangulate();
+			
+			for( t in ctx.triangles )
+				for( p in t.points )
+					content.addIndex(p.id);
 		}
+				
+		prev = [];
+		if( content.next() )
+			pindex = 0;
+	}
+	
+	function flush() {
+		flushFill();
+		flushLine();
+	}
+	
+	public function beginFill( color : Int = 0, alpha = 1.  ) {
+		flush();
+		setColor(color,alpha);
+		doFill = true;
+	}
+	
+	public function lineStyle( size : Float = 0, color = 0, alpha = 1. ) {
+		flush();
+		this.lineSize = size;
+		lineA = alpha;
+		lineR = ((color >> 16) & 0xFF) / 255.;
+		lineG = ((color >> 8) & 0xFF) / 255.;
+		lineB = (color & 0xFF) / 255.;
+	}
+	
+	public function endFill() {
+		flush();
+		doFill = false;
+	}
+	
+	public inline function setColor( color : Int, alpha : Float = 1. ) {
+		curA = alpha;
+		curR = ((color >> 16) & 0xFF) / 255.;
+		curG = ((color >> 8) & 0xFF) / 255.;
+		curB = (color & 0xFF) / 255.;
+	}
+	
+	public function drawRect( x : Float, y : Float, w : Float, h : Float ) {
+		addPoint(x, y);
+		addPoint(x + w, y);
+		addPoint(x + w, y + h);
+		addPoint(x, y + h);
+	}
+	
+	public function drawCircle( cx : Float, cy : Float, ray : Float, nsegments = 0 ) {
+		if( nsegments == 0 )
+			nsegments = Math.ceil(ray * 3.14 * 2 / 4);
+		if( nsegments < 3 ) nsegments = 3;
+		var angle = Math.PI * 2 / (nsegments + 1);
+		for( i in 0...nsegments ) {
+			var a = i * angle;
+			addPoint(cx + Math.cos(a) * ray, cy + Math.sin(a) * ray);
+		}
+	}
+	
+	public function addHole() {
+		if( pts.length > 0 ) {
+			prev.push(pts);
+			pts = [];
+		}
+	}
+	
+	public inline function addPoint( x : Float, y : Float ) {
+		addPointFull(x, y, curR, curG, curB, curA);
+	}
+
+	public function addPointFull( x : Float, y : Float, r : Float, g : Float, b : Float, a : Float, u : Float = 0., v : Float = 0. ) {
+		if( doFill ) {
+			var p = new GraphicsPoint(x, y);
+			p.id = pindex++;
+			pts.push(p);
+			content.add(x, y, u, v, r, g, b, a);
+		}
+		if( lineSize > 0 )
+			linePts.push(new LinePoint(x, y, lineR, lineG, lineB, lineA));
 	}
 	
 	override function draw(ctx:RenderContext) {
-		if( tile == null ) endDraw();
-		if( tile == null ) return;
-		drawTile(ctx.engine, tile);
+		flush();
+		setupShader(ctx.engine, tile, 0);
+		content.render(ctx.engine);
 	}
 
-}
+}

+ 19 - 19
h2d/Interactive.hx

@@ -4,7 +4,7 @@ class Interactive extends Drawable {
 
 	public var width : Float;
 	public var height : Float;
-	public var cursor(default,set) : h3d.System.Cursor;
+	public var cursor(default,set) : hxd.System.Cursor;
 	public var isEllipse : Bool;
 	public var blockEvents : Bool = true;
 	public var propagateEvents : Bool = false;
@@ -45,13 +45,13 @@ class Interactive extends Drawable {
 			scene.removeEventTarget(this);
 			if( scene.currentOver == this ) {
 				scene.currentOver = null;
-				h3d.System.setCursor(Default);
+				hxd.System.setCursor(Default);
 			}
 		}
 		super.onDelete();
 	}
 
-	function checkBounds( e : Event ) {
+	function checkBounds( e : hxd.Event ) {
 		return switch( e.kind ) {
 		case EOut, ERelease, EFocus, EFocusLost: false;
 		default: true;
@@ -59,7 +59,7 @@ class Interactive extends Drawable {
 	}
 	
 	@:allow(h2d.Scene)
-	function handleEvent( e : Event ) {
+	function handleEvent( e : hxd.Event ) {
 		if( isEllipse && checkBounds(e) ) {
 			var cx = width * 0.5, cy = height * 0.5;
 			var dx = (e.relX - cx) / cx;
@@ -79,10 +79,10 @@ class Interactive extends Drawable {
 		case ERelease:
 			onRelease(e);
 		case EOver:
-			h3d.System.setCursor(cursor);
+			hxd.System.setCursor(cursor);
 			onOver(e);
 		case EOut:
-			h3d.System.setCursor(Default);
+			hxd.System.setCursor(Default);
 			onOut(e);
 		case EWheel:
 			onWheel(e);
@@ -102,11 +102,11 @@ class Interactive extends Drawable {
 	function set_cursor(c) {
 		this.cursor = c;
 		if( scene != null && scene.currentOver == this )
-			h3d.System.setCursor(cursor);
+			hxd.System.setCursor(cursor);
 		return c;
 	}
 	
-	function globalToLocal( e : Event ) {
+	function globalToLocal( e : hxd.Event ) {
 		// convert global event to our local space
 		var x = e.relX, y = e.relY;
 		var rx = x * scene.matA + y * scene.matB + scene.absX;
@@ -149,7 +149,7 @@ class Interactive extends Drawable {
 	public function focus() {
 		if( scene == null )
 			return;
-		var ev = new h2d.Event(null);
+		var ev = new hxd.Event(null);
 		if( scene.currentFocus != null ) {
 			if( scene.currentFocus == this )
 				return;
@@ -165,34 +165,34 @@ class Interactive extends Drawable {
 		return scene != null && scene.currentFocus == this;
 	}
 	
-	public dynamic function onOver( e : Event ) {
+	public dynamic function onOver( e : hxd.Event ) {
 	}
 
-	public dynamic function onOut( e : Event ) {
+	public dynamic function onOut( e : hxd.Event ) {
 	}
 	
-	public dynamic function onPush( e : Event ) {
+	public dynamic function onPush( e : hxd.Event ) {
 	}
 
-	public dynamic function onRelease( e : Event ) {
+	public dynamic function onRelease( e : hxd.Event ) {
 	}
 	
-	public dynamic function onMove( e : Event ) {
+	public dynamic function onMove( e : hxd.Event ) {
 	}
 
-	public dynamic function onWheel( e : Event ) {
+	public dynamic function onWheel( e : hxd.Event ) {
 	}
 
-	public dynamic function onFocus( e : Event ) {
+	public dynamic function onFocus( e : hxd.Event ) {
 	}
 	
-	public dynamic function onFocusLost( e : Event ) {
+	public dynamic function onFocusLost( e : hxd.Event ) {
 	}
 
-	public dynamic function onKeyUp( e : Event ) {
+	public dynamic function onKeyUp( e : hxd.Event ) {
 	}
 
-	public dynamic function onKeyDown( e : Event ) {
+	public dynamic function onKeyDown( e : hxd.Event ) {
 	}
 	
 }

+ 33 - 104
h2d/Scene.hx

@@ -10,9 +10,9 @@ class Scene extends Layers implements h3d.IDrawable {
 	
 	var fixedSize : Bool;
 	var interactive : Array<Interactive>;
-	var pendingEvents : flash.Vector<Event>;
-	var stage : flash.display.Stage;
+	var pendingEvents : Array<hxd.Event>;
 	var ctx : RenderContext;
+	var stage : hxd.Stage;
 	
 	@:allow(h2d.Interactive)
 	var currentOver : Interactive;
@@ -20,7 +20,7 @@ class Scene extends Layers implements h3d.IDrawable {
 	var currentFocus : Interactive;
 		
 	var pushList : Array<Interactive>;
-	var currentDrag : { f : Event -> Void, ref : Null<Int> };
+	var currentDrag : { f : hxd.Event -> Void, ref : Null<Int> };
 	
 	public function new() {
 		super(null);
@@ -30,7 +30,7 @@ class Scene extends Layers implements h3d.IDrawable {
 		height = e.height;
 		interactive = new Array();
 		pushList = new Array();
-		stage = flash.Lib.current.stage;
+		stage = hxd.Stage.getInstance();
 		posChanged = true;
 	}
 	
@@ -41,47 +41,26 @@ class Scene extends Layers implements h3d.IDrawable {
 		posChanged = true;
 	}
 
+	override function onAlloc() {
+		stage.addEventTarget(onEvent);
+		super.onAlloc();
+	}
+	
 	override function onDelete() {
-		if( h3d.System.isTouch ) {
-			stage.removeEventListener(flash.events.TouchEvent.TOUCH_BEGIN, onTouchDown);
-			stage.removeEventListener(flash.events.TouchEvent.TOUCH_MOVE, onTouchMove);
-			stage.removeEventListener(flash.events.TouchEvent.TOUCH_END, onTouchUp);
-		} else {
-			stage.removeEventListener(flash.events.MouseEvent.MOUSE_DOWN, onMouseDown);
-			stage.removeEventListener(flash.events.MouseEvent.MOUSE_MOVE, onMouseMove);
-			stage.removeEventListener(flash.events.MouseEvent.MOUSE_UP, onMouseUp);
-			stage.removeEventListener(flash.events.MouseEvent.MOUSE_WHEEL, onMouseWheel);
-			stage.removeEventListener(flash.events.MouseEvent.MOUSE_UP, onMouseUp);
-			stage.removeEventListener(flash.events.KeyboardEvent.KEY_DOWN, onKeyDown);
-			stage.removeEventListener(flash.events.KeyboardEvent.KEY_UP, onKeyUp);
-			h3d.System.setCursor(Default);
-		}
+		stage.removeEventTarget(onEvent);
 		super.onDelete();
 	}
 	
-	override function onAlloc() {
-		if( h3d.System.isTouch ) {
-			flash.ui.Multitouch.inputMode = flash.ui.MultitouchInputMode.TOUCH_POINT;
-			stage.addEventListener(flash.events.TouchEvent.TOUCH_BEGIN, onTouchDown);
-			stage.addEventListener(flash.events.TouchEvent.TOUCH_MOVE, onTouchMove);
-			stage.addEventListener(flash.events.TouchEvent.TOUCH_END, onTouchUp);
-		} else {
-			stage.addEventListener(flash.events.MouseEvent.MOUSE_DOWN, onMouseDown);
-			stage.addEventListener(flash.events.MouseEvent.MOUSE_MOVE, onMouseMove);
-			stage.addEventListener(flash.events.MouseEvent.MOUSE_UP, onMouseUp);
-			stage.addEventListener(flash.events.MouseEvent.MOUSE_WHEEL, onMouseWheel);
-			stage.addEventListener(flash.events.KeyboardEvent.KEY_DOWN, onKeyDown);
-			stage.addEventListener(flash.events.KeyboardEvent.KEY_UP, onKeyUp);
-		}
-		super.onAlloc();
+	function onEvent( e : hxd.Event ) {
+		if( pendingEvents != null ) pendingEvents.push(e);
 	}
 	
 	function screenXToLocal(mx:Float) {
-		return (mx - x) * width / (stage.stageWidth * scaleX);
+		return (mx - x) * width / (stage.width * scaleX);
 	}
 
 	function screenYToLocal(my:Float) {
-		return (my - y) * height / (stage.stageHeight * scaleY);
+		return (my - y) * height / (stage.height * scaleY);
 	}
 	
 	function get_mouseX() {
@@ -91,73 +70,8 @@ class Scene extends Layers implements h3d.IDrawable {
 	function get_mouseY() {
 		return screenYToLocal(stage.mouseY);
 	}
-			
-	function onMouseDown(e:Dynamic) {
-		if( pendingEvents != null )
-			pendingEvents.push(new Event(EPush, mouseX, mouseY));
-	}
-
-	function onMouseUp(e:Dynamic) {
-		if( pendingEvents != null )
-			pendingEvents.push(new Event(ERelease, mouseX, mouseY));
-	}
-	
-	function onMouseMove(e:Dynamic) {
-		if( pendingEvents != null )
-			pendingEvents.push(new Event(EMove, mouseX, mouseY));
-	}
-	
-	function onMouseWheel(e:flash.events.MouseEvent) {
-		if( pendingEvents != null ) {
-			var ev = new Event(EWheel, mouseX, mouseY);
-			ev.wheelDelta = -e.delta / 3.0;
-			pendingEvents.push(ev);
-		}
-	}
-	
-	function onKeyUp(e:flash.events.KeyboardEvent) {
-		if( pendingEvents != null ) {
-			var ev = new Event(EKeyUp);
-			ev.keyCode = e.keyCode;
-			ev.charCode = e.charCode;
-			pendingEvents.push(ev);
-		}
-	}
-
-	function onKeyDown(e:flash.events.KeyboardEvent) {
-		if( pendingEvents != null ) {
-			var ev = new Event(EKeyDown);
-			ev.keyCode = e.keyCode;
-			ev.charCode = e.charCode;
-			pendingEvents.push(ev);
-		}
-	}
-	
-	function onTouchDown(e:flash.events.TouchEvent) {
-		if( pendingEvents != null ) {
-			var ev = new Event(EPush, screenXToLocal(e.localX), screenYToLocal(e.localY));
-			ev.touchId = e.touchPointID;
-			pendingEvents.push(ev);
-		}
-	}
 
-	function onTouchUp(e:flash.events.TouchEvent) {
-		if( pendingEvents != null ) {
-			var ev = new Event(ERelease, screenXToLocal(e.localX), screenYToLocal(e.localY));
-			ev.touchId = e.touchPointID;
-			pendingEvents.push(ev);
-		}
-	}
-	
-	function onTouchMove(e:flash.events.TouchEvent) {
-		if( pendingEvents != null ) {
-			var ev = new Event(EMove, screenXToLocal(e.localX), screenYToLocal(e.localY));
-			ev.touchId = e.touchPointID;
-			pendingEvents.push(ev);
-		}
-	}
-	
-	function emitEvent( event : Event ) {
+	function emitEvent( event : hxd.Event ) {
 		var x = event.relX, y = event.relY;
 		var rx = x * matA + y * matB + absX;
 		var ry = x * matC + y * matD + absY;
@@ -279,13 +193,24 @@ class Scene extends Layers implements h3d.IDrawable {
 		if( pendingEvents == null ) {
 			if( interactive.length == 0 )
 				return;
-			pendingEvents = new flash.Vector();
+			pendingEvents = new Array();
 		}
 		var old = pendingEvents;
 		if( old.length == 0 )
 			return;
 		pendingEvents = null;
+		var ox = 0., oy = 0.;
 		for( e in old ) {
+			var hasPos = switch( e.kind ) {
+			case EKeyUp, EKeyDown: false;
+			default: true;
+			}
+			
+			if( hasPos ) {
+				ox = e.relX;
+				oy = e.relY;
+			}
+			
 			if( currentDrag != null && (currentDrag.ref == null || currentDrag.ref == e.touchId) ) {
 				currentDrag.f(e);
 				if( e.cancel )
@@ -301,10 +226,10 @@ class Scene extends Layers implements h3d.IDrawable {
 			}
 		}
 		if( interactive.length > 0 )
-			pendingEvents = new flash.Vector();
+			pendingEvents = new Array();
 	}
 	
-	public function startDrag( f : Event -> Void, ?refEvent : Event ) {
+	public function startDrag( f : hxd.Event -> Void, ?refEvent : hxd.Event ) {
 		currentDrag = { f : f, ref : refEvent == null ? null : refEvent.touchId };
 	}
 	
@@ -416,6 +341,10 @@ class Scene extends Layers implements h3d.IDrawable {
 			onDelete();
 	}
 	
+	public function setElapsedTime( v : Float ) {
+		ctx.elapsedTime = v;
+	}
+	
 	public function render( engine : h3d.Engine ) {
 		ctx.engine = engine;
 		ctx.frame++;

+ 3 - 3
h2d/Sprite.hx

@@ -274,16 +274,16 @@ class Sprite {
 		scaleY = v;
 	}
 
-	public function getChildAt( n ) {
+	public inline function getChildAt( n ) {
 		return childs[n];
 	}
 	
-	function get_numChildren() {
+	inline function get_numChildren() {
 		return childs.length;
 	}
 
 	public inline function iterator() {
-		return childs.iterator();
+		return new hxd.impl.ArrayIterator(childs);
 	}
 
 }

+ 82 - 28
h2d/SpriteBatch.hx

@@ -4,6 +4,8 @@ package h2d;
 class BatchElement {
 	public var x : Float;
 	public var y : Float;
+	public var scale : Float;
+	public var rotation : Float;
 	public var alpha : Float;
 	public var t : Tile;
 	public var batch(default, null) : SpriteBatch;
@@ -11,12 +13,16 @@ class BatchElement {
 	var prev : BatchElement;
 	var next : BatchElement;
 	
-	function new(b,t) {
+	function new(t) {
 		x = 0; y = 0; alpha = 1;
-		this.batch = b;
+		rotation = 0; scale = 1;
 		this.t = t;
 	}
 	
+	function update(et:Float) {
+		return true;
+	}
+	
 	public inline function remove() {
 		batch.delete(this);
 	}
@@ -26,9 +32,11 @@ class BatchElement {
 class SpriteBatch extends Drawable {
 
 	public var tile : Tile;
+	public var hasRotationScale : Bool;
+	public var hasUpdate : Bool;
 	var first : BatchElement;
 	var last : BatchElement;
-	var tmpBuf : flash.Vector<Float>;
+	var tmpBuf : hxd.FloatBuffer;
 		
 	public function new(t,?parent) {
 		super(parent);
@@ -36,8 +44,8 @@ class SpriteBatch extends Drawable {
 		shader.hasVertexAlpha = true;
 	}
 	
-	public function alloc(t) {
-		var e = new BatchElement(this, t);
+	public function add(e:BatchElement) {
+		e.batch = this;
 		if( first == null )
 			first = last = e;
 		else {
@@ -48,6 +56,10 @@ class SpriteBatch extends Drawable {
 		return e;
 	}
 	
+	public function alloc(t) {
+		return add(new BatchElement(t));
+	}
+	
 	@:allow(h2d.BatchElement)
 	function delete(e : BatchElement) {
 		if( e.prev == null ) {
@@ -62,37 +74,79 @@ class SpriteBatch extends Drawable {
 			e.next.prev = e.prev;
 	}
 	
+	override function sync(ctx) {
+		super.sync(ctx);
+		if( hasUpdate ) {
+			var e = first;
+			while( e != null ) {
+				if( !e.update(ctx.elapsedTime) )
+					e.remove();
+				e = e.next;
+			}
+		}
+	}
+	
+	
 	override function draw( ctx : RenderContext ) {
 		if( first == null )
 			return;
-		if( tmpBuf == null ) tmpBuf = new flash.Vector();
+		if( tmpBuf == null ) tmpBuf = new hxd.FloatBuffer();
 		var pos = 0;
 		var e = first;
 		var tmp = tmpBuf;
 		while( e != null ) {
 			var t = e.t;
-			var sx = e.x + t.dx;
-			var sy = e.y + t.dy;
-			tmp[pos++] = sx;
-			tmp[pos++] = sy;
-			tmp[pos++] = t.u;
-			tmp[pos++] = t.v;
-			tmp[pos++] = e.alpha;
-			tmp[pos++] = sx + t.width + 0.1;
-			tmp[pos++] = sy;
-			tmp[pos++] = t.u2;
-			tmp[pos++] = t.v;
-			tmp[pos++] = e.alpha;
-			tmp[pos++] = sx;
-			tmp[pos++] = sy + t.height + 0.1;
-			tmp[pos++] = t.u;
-			tmp[pos++] = t.v2;
-			tmp[pos++] = e.alpha;
-			tmp[pos++] = sx + t.width + 0.1;
-			tmp[pos++] = sy + t.height + 0.1;
-			tmp[pos++] = t.u2;
-			tmp[pos++] = t.v2;
-			tmp[pos++] = e.alpha;
+			if( hasRotationScale ) {
+				var ca = Math.cos(e.rotation), sa = Math.sin(e.rotation);
+				var hx = t.width, hy = t.height;
+				var px = t.dx, py = t.dy;
+				tmp[pos++] = (px * ca + py * sa) * e.scale + e.x;
+				tmp[pos++] = (py * ca - px * sa) * e.scale + e.y;
+				tmp[pos++] = t.u;
+				tmp[pos++] = t.v;
+				tmp[pos++] = e.alpha;
+				var px = t.dx + hx, py = t.dy;
+				tmp[pos++] = (px * ca + py * sa) * e.scale + e.x;
+				tmp[pos++] = (py * ca - px * sa) * e.scale + e.y;
+				tmp[pos++] = t.u2;
+				tmp[pos++] = t.v;
+				tmp[pos++] = e.alpha;
+				var px = t.dx, py = t.dy + hy;
+				tmp[pos++] = (px * ca + py * sa) * e.scale + e.x;
+				tmp[pos++] = (py * ca - px * sa) * e.scale + e.y;
+				tmp[pos++] = t.u;
+				tmp[pos++] = t.v2;
+				tmp[pos++] = e.alpha;
+				var px = t.dx + hx, py = t.dy + hy;
+				tmp[pos++] = (px * ca + py * sa) * e.scale + e.x;
+				tmp[pos++] = (py * ca - px * sa) * e.scale + e.y;
+				tmp[pos++] = t.u2;
+				tmp[pos++] = t.v2;
+				tmp[pos++] = e.alpha;
+			} else {
+				var sx = e.x + t.dx;
+				var sy = e.y + t.dy;
+				tmp[pos++] = sx;
+				tmp[pos++] = sy;
+				tmp[pos++] = t.u;
+				tmp[pos++] = t.v;
+				tmp[pos++] = e.alpha;
+				tmp[pos++] = sx + t.width + 0.1;
+				tmp[pos++] = sy;
+				tmp[pos++] = t.u2;
+				tmp[pos++] = t.v;
+				tmp[pos++] = e.alpha;
+				tmp[pos++] = sx;
+				tmp[pos++] = sy + t.height + 0.1;
+				tmp[pos++] = t.u;
+				tmp[pos++] = t.v2;
+				tmp[pos++] = e.alpha;
+				tmp[pos++] = sx + t.width + 0.1;
+				tmp[pos++] = sy + t.height + 0.1;
+				tmp[pos++] = t.u2;
+				tmp[pos++] = t.v2;
+				tmp[pos++] = e.alpha;
+			}
 			e = e.next;
 		}
 		var stride = 5;

+ 33 - 17
h2d/Tile.hx

@@ -58,6 +58,10 @@ class Tile {
 		return new Tile(innerTex, this.x + x, this.y + y, w, h, dx, dy);
 	}
 	
+	public function center(dx, dy) {
+		return sub(0, 0, width, height, -dx, -dy);
+	}
+	
 	public function setPos(x, y) {
 		this.x = x;
 		this.y = y;
@@ -109,11 +113,17 @@ class Tile {
 	}
 	
 	
-	public function split( frames : Int ) {
+	public function split( frames : Int, vertical = false ) {
 		var tl = [];
-		var stride = Std.int(width / frames);
-		for( i in 0...frames )
-			tl.push(sub(i * stride, 0, stride, height));
+		if( vertical ) {
+			var stride = Std.int(height / frames);
+			for( i in 0...frames )
+				tl.push(sub(0, i * stride, width, stride));
+		} else {
+			var stride = Std.int(width / frames);
+			for( i in 0...frames )
+				tl.push(sub(i * stride, 0, stride, height));
+		}
 		return tl;
 	}
 	
@@ -121,17 +131,20 @@ class Tile {
 		return "Tile(" + x + "," + y + "," + width + "x" + height + (dx != 0 || dy != 0 ? "," + dx + ":" + dy:"") + ")";
 	}
 
-	function upload(bmp:flash.display.BitmapData) {
+	function upload( bmp:hxd.BitmapData ) {
 		var w = innerTex.width;
 		var h = innerTex.height;
+		#if flash
 		if( w != bmp.width || h != bmp.height ) {
 			var bmp2 = new flash.display.BitmapData(w, h, true, 0);
 			var p0 = new flash.geom.Point(0, 0);
+			var bmp = bmp.toNative();
 			bmp2.copyPixels(bmp, bmp.rect, p0, bmp, p0, true);
-			innerTex.upload(bmp2);
+			innerTex.uploadBitmap(hxd.BitmapData.fromNative(bmp2));
 			bmp2.dispose();
 		} else
-			innerTex.upload(bmp);
+		#end
+			innerTex.uploadBitmap(bmp);
 	}
 	
 
@@ -139,7 +152,7 @@ class Tile {
 	public static function fromColor( color : Int, ?width = 1, ?height = 1, ?allocPos : h3d.impl.AllocPos ) {
 		var t = COLOR_CACHE.get(color);
 		if( t == null || t.isDisposed() ) {
-			t = h3d.Engine.getCurrent().mem.allocTexture(1, 1, allocPos);
+			t = h3d.Engine.getCurrent().mem.allocTexture(1, 1, false, allocPos);
 			var bmp = haxe.io.Bytes.alloc(4);
 			bmp.set(0, color & 0xFF);
 			bmp.set(1, (color >> 8) & 0xFF);
@@ -158,28 +171,28 @@ class Tile {
 		return t;
 	}
 	
-	public static function fromBitmap( bmp : flash.display.BitmapData, ?allocPos : h3d.impl.AllocPos ) {
+	public static function fromBitmap( bmp : hxd.BitmapData, ?allocPos : h3d.impl.AllocPos ) {
 		var w = 1, h = 1;
 		while( w < bmp.width )
 			w <<= 1;
 		while( h < bmp.height )
 			h <<= 1;
-		var tex = h3d.Engine.getCurrent().mem.allocTexture(w, h, allocPos);
+		var tex = h3d.Engine.getCurrent().mem.allocTexture(w, h, false, allocPos);
 		var t = new Tile(tex, 0, 0, bmp.width, bmp.height);
 		t.upload(bmp);
 		return t;
 	}
 
-	public static function autoCut( bmp : flash.display.BitmapData, width : Int, ?height : Int, ?allocPos : h3d.impl.AllocPos ) {
+	public static function autoCut( bmp : hxd.BitmapData, width : Int, ?height : Int, ?allocPos : h3d.impl.AllocPos ) {
 		if( height == null ) height = width;
-		var colorBG = bmp.getPixel32(bmp.width - 1, bmp.height - 1);
+		var colorBG = bmp.getPixel(bmp.width - 1, bmp.height - 1);
 		var tl = new Array();
 		var w = 1, h = 1;
 		while( w < bmp.width )
 			w <<= 1;
 		while( h < bmp.height )
 			h <<= 1;
-		var tex = h3d.Engine.getCurrent().mem.allocTexture(w, h, allocPos);
+		var tex = h3d.Engine.getCurrent().mem.allocTexture(w, h, false, allocPos);
 		for( y in 0...Std.int(bmp.height / height) ) {
 			var a = [];
 			tl[y] = a;
@@ -199,6 +212,7 @@ class Tile {
 		return new Tile(t, 0, 0, t.width, t.height);
 	}
 	
+	#if flash
 	public static function fromSprites( sprites : Array<flash.display.Sprite>, ?allocPos : h3d.impl.AllocPos ) {
 		var tmp = [];
 		var width = 0;
@@ -225,19 +239,21 @@ class Tile {
 			m.ty = -t.dy;
 			bmp.draw(t.s, m);
 		}
-		var main = fromBitmap(bmp,allocPos);
+		var main = fromBitmap(hxd.BitmapData.fromNative(bmp), allocPos);
+		bmp.dispose();
 		var tiles = [];
 		for( t in tmp )
 			tiles.push(main.sub(t.x, 0, t.w, t.h, t.dx, t.dy));
 		return tiles;
 	}
+	#end
 	
-	static function isEmpty( b : flash.display.BitmapData, px, py, width, height, bg : UInt ) {
+	static function isEmpty( b : hxd.BitmapData, px, py, width, height, bg : Int ) {
 		var empty = true;
 		var xmin = width, ymin = height, xmax = 0, ymax = 0;
 		for( x in 0...width )
 			for( y in 0...height ) {
-				var color = b.getPixel32(x+px, y+py);
+				var color : Int = b.getPixel(x+px, y+py);
 				if( color != bg ) {
 					empty = false;
 					if( x < xmin ) xmin = x;
@@ -246,7 +262,7 @@ class Tile {
 					if( y > ymax ) ymax = y;
 				}
 				if( color == bg )
-					b.setPixel32(x+px, y+py, 0);
+					b.setPixel(x+px, y+py, 0);
 			}
 		return empty ? null : { dx : xmin, dy : ymin, w : xmax - xmin + 1, h : ymax - ymin + 1 };
 	}

+ 74 - 76
h2d/TileColorGroup.hx

@@ -2,16 +2,14 @@ package h2d;
 
 private class TileLayerContent extends h3d.prim.Primitive {
 
-	var tmp : flash.Vector<Float>;
-	var pos : Int;
+	var tmp : hxd.FloatBuffer;
 
 	public function new() {
 		reset();
 	}
 
 	public function reset() {
-		tmp = new flash.Vector();
-		pos = 0;
+		tmp = new hxd.FloatBuffer();
 		if( buffer != null ) buffer.dispose();
 		buffer = null;
 	}
@@ -31,98 +29,98 @@ private class TileLayerContent extends h3d.prim.Primitive {
 	public function add( x : Int, y : Int, r : Float, g : Float, b : Float, a : Float, t : Tile ) {
 		var sx = x + t.dx;
 		var sy = y + t.dy;
-		tmp[pos++] = sx;
-		tmp[pos++] = sy;
-		tmp[pos++] = t.u;
-		tmp[pos++] = t.v;
-		tmp[pos++] = r;
-		tmp[pos++] = g;
-		tmp[pos++] = b;
-		tmp[pos++] = a;
-		tmp[pos++] = sx + t.width;
-		tmp[pos++] = sy;
-		tmp[pos++] = t.u2;
-		tmp[pos++] = t.v;
-		tmp[pos++] = r;
-		tmp[pos++] = g;
-		tmp[pos++] = b;
-		tmp[pos++] = a;
-		tmp[pos++] = sx;
-		tmp[pos++] = sy + t.height;
-		tmp[pos++] = t.u;
-		tmp[pos++] = t.v2;
-		tmp[pos++] = r;
-		tmp[pos++] = g;
-		tmp[pos++] = b;
-		tmp[pos++] = a;
-		tmp[pos++] = sx + t.width;
-		tmp[pos++] = sy + t.height;
-		tmp[pos++] = t.u2;
-		tmp[pos++] = t.v2;
-		tmp[pos++] = r;
-		tmp[pos++] = g;
-		tmp[pos++] = b;
-		tmp[pos++] = a;
+		tmp.push(sx);
+		tmp.push(sy);
+		tmp.push(t.u);
+		tmp.push(t.v);
+		tmp.push(r);
+		tmp.push(g);
+		tmp.push(b);
+		tmp.push(a);
+		tmp.push(sx + t.width);
+		tmp.push(sy);
+		tmp.push(t.u2);
+		tmp.push(t.v);
+		tmp.push(r);
+		tmp.push(g);
+		tmp.push(b);
+		tmp.push(a);
+		tmp.push(sx);
+		tmp.push(sy + t.height);
+		tmp.push(t.u);
+		tmp.push(t.v2);
+		tmp.push(r);
+		tmp.push(g);
+		tmp.push(b);
+		tmp.push(a);
+		tmp.push(sx + t.width);
+		tmp.push(sy + t.height);
+		tmp.push(t.u2);
+		tmp.push(t.v2);
+		tmp.push(r);
+		tmp.push(g);
+		tmp.push(b);
+		tmp.push(a);
 	}
 	
 	public function addPoint( x : Float, y : Float, color : Int ) {
-		tmp[pos++] = x;
-		tmp[pos++] = y;
-		tmp[pos++] = 0;
-		tmp[pos++] = 0;
+		tmp.push(x);
+		tmp.push(y);
+		tmp.push(0);
+		tmp.push(0);
 		insertColor(color);
 	}
 
 	inline function insertColor( c : Int ) {
-		tmp[pos++] = ((c >> 16) & 0xFF) / 255.;
-		tmp[pos++] = ((c >> 8) & 0xFF) / 255.;
-		tmp[pos++] = (c & 0xFF) / 255.;
-		tmp[pos++] = (c >>> 24) / 255.;
+		tmp.push(((c >> 16) & 0xFF) / 255.);
+		tmp.push(((c >> 8) & 0xFF) / 255.);
+		tmp.push((c & 0xFF) / 255.);
+		tmp.push((c >>> 24) / 255.);
 	}
 
 	public inline function rectColor( x : Float, y : Float, w : Float, h : Float, color : Int ) {
-		tmp[pos++] = x;
-		tmp[pos++] = y;
-		tmp[pos++] = 0;
-		tmp[pos++] = 0;
+		tmp.push(x);
+		tmp.push(y);
+		tmp.push(0);
+		tmp.push(0);
 		insertColor(color);
-		tmp[pos++] = x + w;
-		tmp[pos++] = y;
-		tmp[pos++] = 1;
-		tmp[pos++] = 0;
+		tmp.push(x + w);
+		tmp.push(y);
+		tmp.push(1);
+		tmp.push(0);
 		insertColor(color);
-		tmp[pos++] = x;
-		tmp[pos++] = y + h;
-		tmp[pos++] = 0;
-		tmp[pos++] = 1;
+		tmp.push(x);
+		tmp.push(y + h);
+		tmp.push(0);
+		tmp.push(1);
 		insertColor(color);
-		tmp[pos++] = x + w;
-		tmp[pos++] = y + h;
-		tmp[pos++] = 1;
-		tmp[pos++] = 1;
+		tmp.push(x + w);
+		tmp.push(y + h);
+		tmp.push(1);
+		tmp.push(1);
 		insertColor(color);
 	}
 
 	public inline function rectGradient( x : Float, y : Float, w : Float, h : Float, ctl : Int, ctr : Int, cbl : Int, cbr : Int ) {
-		tmp[pos++] = x;
-		tmp[pos++] = y;
-		tmp[pos++] = 0;
-		tmp[pos++] = 0;
+		tmp.push(x);
+		tmp.push(y);
+		tmp.push(0);
+		tmp.push(0);
 		insertColor(ctl);
-		tmp[pos++] = x + w;
-		tmp[pos++] = y;
-		tmp[pos++] = 1;
-		tmp[pos++] = 0;
+		tmp.push(x + w);
+		tmp.push(y);
+		tmp.push(1);
+		tmp.push(0);
 		insertColor(ctr);
-		tmp[pos++] = x;
-		tmp[pos++] = y + h;
-		tmp[pos++] = 0;
-		tmp[pos++] = 1;
+		tmp.push(x);
+		tmp.push(y + h);
+		tmp.push(0);
+		tmp.push(1);
 		insertColor(cbl);
-		tmp[pos++] = x + w;
-		tmp[pos++] = y + h;
-		tmp[pos++] = 1;
-		tmp[pos++] = 0;
+		tmp.push(x + w);
+		tmp.push(y + h);
+		tmp.push(1);
+		tmp.push(0);
 		insertColor(cbr);
 	}
 

+ 18 - 20
h2d/TileGroup.hx

@@ -2,8 +2,7 @@ package h2d;
 
 private class TileLayerContent extends h3d.prim.Primitive {
 
-	var tmp : flash.Vector<Float>;
-	var pos : Int;
+	var tmp : hxd.FloatBuffer;
 	
 	public function new() {
 		reset();
@@ -14,8 +13,7 @@ private class TileLayerContent extends h3d.prim.Primitive {
 	}
 	
 	public function reset() {
-		tmp = new flash.Vector();
-		pos = 0;
+		tmp = new hxd.FloatBuffer();
 		if( buffer != null ) buffer.dispose();
 		buffer = null;
 	}
@@ -25,22 +23,22 @@ private class TileLayerContent extends h3d.prim.Primitive {
 		var sy = y + t.dy;
 		var sx2 = sx + t.width;
 		var sy2 = sy + t.height;
-		tmp[pos++] = sx;
-		tmp[pos++] = sy;
-		tmp[pos++] = t.u;
-		tmp[pos++] = t.v;
-		tmp[pos++] = sx2;
-		tmp[pos++] = sy;
-		tmp[pos++] = t.u2;
-		tmp[pos++] = t.v;
-		tmp[pos++] = sx;
-		tmp[pos++] = sy2;
-		tmp[pos++] = t.u;
-		tmp[pos++] = t.v2;
-		tmp[pos++] = sx2;
-		tmp[pos++] = sy2;
-		tmp[pos++] = t.u2;
-		tmp[pos++] = t.v2;
+		tmp.push(sx);
+		tmp.push(sy);
+		tmp.push(t.u);
+		tmp.push(t.v);
+		tmp.push(sx2);
+		tmp.push(sy);
+		tmp.push(t.u2);
+		tmp.push(t.v);
+		tmp.push(sx);
+		tmp.push(sy2);
+		tmp.push(t.u);
+		tmp.push(t.v2);
+		tmp.push(sx2);
+		tmp.push(sy2);
+		tmp.push(t.u2);
+		tmp.push(t.v2);
 	}
 	
 	override public function triCount() {

+ 5 - 5
h2d/Tools.hx

@@ -2,8 +2,8 @@ package h2d;
 
 private class CoreObjects  {
 	
-	public var tmpMat1 : h3d.Vector;
-	public var tmpMat2 : h3d.Vector;
+	public var tmpMatA : h3d.Vector;
+	public var tmpMatB : h3d.Vector;
 	public var tmpSize : h3d.Vector;
 	public var tmpUVPos : h3d.Vector;
 	public var tmpUVScale : h3d.Vector;
@@ -15,8 +15,8 @@ private class CoreObjects  {
 	var emptyTexture : h3d.mat.Texture;
 	
 	public function new() {
-		tmpMat1 = new h3d.Vector();
-		tmpMat2 = new h3d.Vector();
+		tmpMatA = new h3d.Vector();
+		tmpMatB = new h3d.Vector();
 		tmpColor = new h3d.Vector();
 		tmpSize = new h3d.Vector();
 		tmpUVPos = new h3d.Vector();
@@ -26,7 +26,7 @@ private class CoreObjects  {
 		tmpMaterial.culling = None;
 		tmpMaterial.depth(false, Always);
 		
-		var vector = new flash.Vector<Float>();
+		var vector = new hxd.FloatBuffer();
 		for( pt in [[0, 0], [1, 0], [0, 1], [1, 1]] ) {
 			vector.push(pt[0]);
 			vector.push(pt[1]);

ファイルの差分が大きいため隠しています
+ 0 - 0
h2d/col/Delaunay.hx


+ 10 - 0
h2d/col/Poly.hx

@@ -19,6 +19,16 @@ class Poly {
 		}
 		return true;
 	}
+	
+	public function calculateArea() {
+		var s = 0.;
+		for( i in 0...points.length ) {
+			var p = points[i];
+			var n = points[(i + 1) % points.length];
+			s += p.y * n.x - p.x * n.y;
+		}
+		return s * 0.5;
+	}
 
 	public function getSegments() {
 		if( segments != null )

+ 41 - 29
h2d/col/Voronoi.hx

@@ -331,12 +331,23 @@ class Cell {
     }
 	
 	public function getCircle() {
+		// still not the best enclosing circle
+		// would require implementing http://www.personal.kent.edu/~rmuhamma/Compgeometry/MyCG/CG-Applets/Center/centercli.htm for complete solution
+		var p = new Point(), ec = 0;
+		for( e in halfedges ) {
+			var ep = e.getStartpoint();
+			p.x += ep.x;
+			p.y += ep.y;
+			ec++;
+		}
+		p.x /= ec;
+		p.y /= ec;
 		var r = 0.;
 		for( e in halfedges ) {
-			var d = point.distanceSq(e.getStartpoint());
+			var d = p.distanceSq(e.getStartpoint());
 			if( d > r ) r = d;
 		}
-		return new Circle(point.x, point.y, h3d.FMath.sqrt(r));
+		return new Circle(p.x, p.y, h3d.FMath.sqrt(r));
 	}
 
 	public function prepare() {
@@ -536,6 +547,7 @@ private class CircleEvent extends RBNode<CircleEvent> {
 
 class Voronoi {
 	
+	var epsilon : Float;
 	var beachline : RBTree<Beachsection>;
 	var vertices : Array<Point>;
 	var edges : Array<Edge>;
@@ -546,7 +558,8 @@ class Voronoi {
 	var firstCircleEvent : CircleEvent;
 	var pointCell : Map<Point,Cell>;
 
-	public function new() {
+	public function new( epsilon = 1e-9 ) {
+		this.epsilon = epsilon;
 		this.vertices = null;
 		this.edges = null;
 		this.cells = null;
@@ -576,13 +589,12 @@ class Voronoi {
 		this.cells = [];
 	}
 
-	static inline var EPSILON = 1e-9;
 	static inline function abs(x:Float) return x < 0 ? -x : x;
-	inline function equalWithEpsilon(a:Float,b:Float) return abs(a-b)<EPSILON;
-	inline function greaterThanWithEpsilon(a:Float,b:Float) return a-b>EPSILON;
-	inline function greaterThanOrEqualWithEpsilon(a:Float,b:Float) return b-a<EPSILON;
-	inline function lessThanWithEpsilon(a:Float,b:Float) return b-a>EPSILON;
-	inline function lessThanOrEqualWithEpsilon(a:Float,b:Float) return a-b<EPSILON;
+	inline function equalWithepsilon(a:Float,b:Float) return abs(a-b)<epsilon;
+	inline function greaterThanWithepsilon(a:Float,b:Float) return a-b>epsilon;
+	inline function greaterThanOrEqualWithepsilon(a:Float,b:Float) return b-a<epsilon;
+	inline function lessThanWithepsilon(a:Float,b:Float) return b-a>epsilon;
+	inline function lessThanOrEqualWithepsilon(a:Float,b:Float) return a-b<epsilon;
 
 	function createVertex(x, y) {
 		var v = new Point(x, y);
@@ -759,7 +771,7 @@ class Voronoi {
 
 		// look left
 		var lArc = previous;
-		while (lArc.circleEvent != null && abs(x-lArc.circleEvent.x)<1e-9 && abs(y-lArc.circleEvent.ycenter)<1e-9) {
+		while (lArc.circleEvent != null && abs(x-lArc.circleEvent.x)<epsilon && abs(y-lArc.circleEvent.ycenter)<epsilon) {
 			previous = lArc.rbPrevious;
 			disappearingTransitions.unshift(lArc);
 			this.detachBeachsection(lArc); // mark for reuse
@@ -774,7 +786,7 @@ class Voronoi {
 
 		// look right
 		var rArc = next;
-		while (rArc.circleEvent != null && abs(x-rArc.circleEvent.x)<1e-9 && abs(y-rArc.circleEvent.ycenter)<1e-9) {
+		while (rArc.circleEvent != null && abs(x-rArc.circleEvent.x)<epsilon && abs(y-rArc.circleEvent.ycenter)<epsilon) {
 			next = rArc.rbNext;
 			disappearingTransitions.push(rArc);
 			this.detachBeachsection(rArc); // mark for reuse
@@ -825,8 +837,8 @@ class Voronoi {
 
 		while (node != null) {
 			dxl = this.leftBreakPoint(node,directrix)-x;
-			// x lessThanWithEpsilon xl => falls somewhere before the left edge of the beachsection
-			if (dxl > 1e-9) {
+			// x lessThanWithepsilon xl => falls somewhere before the left edge of the beachsection
+			if (dxl > epsilon) {
 				// this case should never happen
 				// if (!node.rbLeft) {
 				//    rArc = node.rbLeft;
@@ -836,8 +848,8 @@ class Voronoi {
 				}
 			else {
 				dxr = x-this.rightBreakPoint(node,directrix);
-				// x greaterThanWithEpsilon xr => falls somewhere after the right edge of the beachsection
-				if (dxr > 1e-9) {
+				// x greaterThanWithepsilon xr => falls somewhere after the right edge of the beachsection
+				if (dxr > epsilon) {
 					if (node.rbRight == null) {
 						lArc = node;
 						break;
@@ -845,13 +857,13 @@ class Voronoi {
 					node = node.rbRight;
 					}
 				else {
-					// x equalWithEpsilon xl => falls exactly on the left edge of the beachsection
-					if (dxl > -1e-9) {
+					// x equalWithepsilon xl => falls exactly on the left edge of the beachsection
+					if (dxl > -epsilon) {
 						lArc = node.rbPrevious;
 						rArc = node;
 						}
-					// x equalWithEpsilon xr => falls exactly on the right edge of the beachsection
-					else if (dxr > -1e-9) {
+					// x equalWithepsilon xr => falls exactly on the right edge of the beachsection
+					else if (dxr > -epsilon) {
 						lArc = node;
 						rArc = node.rbNext;
 						}
@@ -1308,7 +1320,7 @@ class Voronoi {
 			// edge is removed if:
 			//   it is wholly outside the bounding box
 			//   it is actually a point rather than a line
-			if (!this.connectEdge(edge, bbox) || !this.clipEdge(edge, bbox) || (abs(edge.va.x-edge.vb.x)<1e-9 && abs(edge.va.y-edge.vb.y)<1e-9)) {
+			if (!this.connectEdge(edge, bbox) || !this.clipEdge(edge, bbox) || (abs(edge.va.x-edge.vb.x)<epsilon && abs(edge.va.y-edge.vb.y)<epsilon)) {
 				edge.va = edge.vb = null;
 				edges.splice(iEdge,1);
 			}
@@ -1356,26 +1368,26 @@ class Voronoi {
 				startpoint = halfedges[iRight].getStartpoint();
 				// if end point is not equal to start point, we need to add the missing
 				// halfedge(s) to close the cell
-				if (abs(endpoint.x-startpoint.x)>=1e-9 || abs(endpoint.y-startpoint.y)>=1e-9) {
+				if (abs(endpoint.x-startpoint.x)>=epsilon || abs(endpoint.y-startpoint.y)>=epsilon) {
 					// if we reach this point, cell needs to be closed by walking
 					// counterclockwise along the bounding box until it connects
 					// to next halfedge in the list
 					va = endpoint;
 					// walk downward along left side
-					if (this.equalWithEpsilon(endpoint.x,xl) && this.lessThanWithEpsilon(endpoint.y,yb)) {
-						vb = this.createVertex(xl, this.equalWithEpsilon(startpoint.x,xl) ? startpoint.y : yb);
+					if (this.equalWithepsilon(endpoint.x,xl) && this.lessThanWithepsilon(endpoint.y,yb)) {
+						vb = this.createVertex(xl, this.equalWithepsilon(startpoint.x,xl) ? startpoint.y : yb);
 					}
 					// walk rightward along bottom side
-					else if (this.equalWithEpsilon(endpoint.y,yb) && this.lessThanWithEpsilon(endpoint.x,xr)) {
-						vb = this.createVertex(this.equalWithEpsilon(startpoint.y,yb) ? startpoint.x : xr, yb);
+					else if (this.equalWithepsilon(endpoint.y,yb) && this.lessThanWithepsilon(endpoint.x,xr)) {
+						vb = this.createVertex(this.equalWithepsilon(startpoint.y,yb) ? startpoint.x : xr, yb);
 					}
 					// walk upward along right side
-					else if (this.equalWithEpsilon(endpoint.x,xr) && this.greaterThanWithEpsilon(endpoint.y,yt)) {
-						vb = this.createVertex(xr, this.equalWithEpsilon(startpoint.x,xr) ? startpoint.y : yt);
+					else if (this.equalWithepsilon(endpoint.x,xr) && this.greaterThanWithepsilon(endpoint.y,yt)) {
+						vb = this.createVertex(xr, this.equalWithepsilon(startpoint.x,xr) ? startpoint.y : yt);
 					}
 					// walk leftward along top side
-					else if (this.equalWithEpsilon(endpoint.y,yt) && this.greaterThanWithEpsilon(endpoint.x,xl)) {
-						vb = this.createVertex(this.equalWithEpsilon(startpoint.y,yt) ? startpoint.x : xl, yt);
+					else if (this.equalWithepsilon(endpoint.y,yt) && this.greaterThanWithepsilon(endpoint.x,xl)) {
+						vb = this.createVertex(this.equalWithepsilon(startpoint.y,yt) ? startpoint.x : xl, yt);
 					}
 					edge = this.createBorderEdge(cell.point, va, vb);
 					halfedges.insert(iLeft+1, new Halfedge(edge, cell.point, null));

+ 26 - 25
h2d/comp/ColorPicker.hx

@@ -69,7 +69,7 @@ private class Cross extends h2d.css.Fill {
 		lineRect(FillStyle.Color(color), 0, 0, size, size, 1);
 	}
 	
-	public function setColor(color:UInt) {
+	public function setColor(color:Int) {
 		reset();
 		lineRect(FillStyle.Color(color), 0, 0, size, size, 1);
 	}
@@ -78,8 +78,8 @@ private class Cross extends h2d.css.Fill {
 private class Color extends h2d.Sprite{
 	public var width :Float;
 	public var height :Float;
-	public var color(default, set):UInt = 0xFFFFFFFF;
-	public var preview(default, set):UInt = 0xFFFFFFFF;
+	public var color(default, set):Int = 0xFFFFFFFF;
+	public var preview(default, set):Int = 0xFFFFFFFF;
 	public var alpha(default, set):Float = 1.;
 	
 	var canvas:h2d.css.Fill;
@@ -96,7 +96,7 @@ private class Color extends h2d.Sprite{
 		init();
 	}
 	
-	function set_color(v:UInt) {
+	function set_color(v:Int) {
 		if(v != color) {
 			color = v;
 			drawAll();
@@ -104,7 +104,7 @@ private class Color extends h2d.Sprite{
 		return color;
 	}
 	
-	function set_preview(v:UInt) {
+	function set_preview(v:Int) {
 		if(v != preview) {
 			preview = v;
 			drawAll();
@@ -118,7 +118,7 @@ private class Color extends h2d.Sprite{
 		return color;
 	}
 	
-	public function updateColor(v:UInt) {
+	public function updateColor(v:Int) {
 		color = v;
 		input.value = StringTools.hex(preview, 6).substr(2);
 	}
@@ -161,7 +161,7 @@ private class Color extends h2d.Sprite{
 private class Palette extends h2d.Sprite {
 	public var width :Float;
 	public var height :Float;
-	public var color(default, set):UInt;
+	public var color(default, set):Int;
 	
 	var canvas:h2d.css.Fill;
 	var interact:h2d.Interactive;
@@ -199,7 +199,7 @@ private class Palette extends h2d.Sprite {
 		drawAll();
 	}
 	
-	function set_color(v:UInt) {
+	function set_color(v:Int) {
 		color = v;
 		if(!ColorPicker.change.equals(SPalette))
 			updateCursor();
@@ -237,7 +237,7 @@ private class Palette extends h2d.Sprite {
 		return(ColorPicker.HSLtoINT(h, s, l));
 	}
 	
-	public function setColorFrom(newColor:UInt) {
+	public function setColorFrom(newColor:Int) {
 		var rgb = ColorPicker.INTtoRGB(newColor);
 		var hsl = ColorPicker.RGBtoHLS(rgb[0], rgb[1], rgb[2]);
 		hsl[1] = 1; hsl[2] = 0.5;
@@ -249,8 +249,8 @@ private class Palette extends h2d.Sprite {
 private class Chart extends h2d.Sprite{
 	public var width :Int;
 	public var height :Int;
-	public var refColor(default, set):UInt = 0xffffffff;
-	public var color:UInt = 0xffffffff;
+	public var refColor(default, set):Int = 0xffffffff;
+	public var color:Int = 0xffffffff;
 	
 	var ray :Float;
 	var canvas:h2d.css.Fill;
@@ -299,7 +299,7 @@ private class Chart extends h2d.Sprite{
 		cross.setColor(ColorPicker.complementaryColor(color));
 	}
 	
-	function set_refColor(v:UInt) {
+	function set_refColor(v:Int) {
 		refColor = v;
 		color = getColor(lastPos.x, lastPos.y);
 		cross.setColor(ColorPicker.complementaryColor(color));
@@ -348,7 +348,7 @@ private class Chart extends h2d.Sprite{
 		return ColorPicker.RGBtoINT(r, g, b);
 	}
 	
-	public function setColorFrom(newColor:UInt) {
+	public function setColorFrom(newColor:Int) {
 		var rgb = ColorPicker.INTtoRGB(newColor);
 		var hsl = ColorPicker.RGBtoHLS(rgb[0], rgb[1], rgb[2]);
 		hsl[1] = 1; hsl[2] = 0.5;
@@ -367,7 +367,7 @@ private class Chart extends h2d.Sprite{
 private class ColorGauge extends h2d.Sprite{
 	public var width :Int;
 	public var height :Int;
-	public var color(default, set):UInt = 0xffffffff;
+	public var color(default, set):Int = 0xffffffff;
 	public var ratio(get, null):Float;
 	
 	var canvas:h2d.css.Fill;
@@ -422,7 +422,7 @@ private class ColorGauge extends h2d.Sprite{
 		drawAll();
 	}
 	
-	function set_color(v:UInt) {
+	function set_color(v:Int) {
 		color = v;
 		if(!bindTo.equals(RGBA.A))
 			updateCursor();
@@ -475,8 +475,8 @@ private class ColorGauge extends h2d.Sprite{
 		var r = (color >> 16) & 0xFF;
 		var g =	(color >> 8) & 0xFF;
 		var b = color & 0xFF;
-		var left:UInt;
-		var right:UInt;
+		var left:Int;
+		var right:Int;
 		switch(bindTo) {
 			case RGBA.R: left = ColorPicker.RGBtoINT(0, g, b);	right = ColorPicker.RGBtoINT(255, g, b);
 			case RGBA.G: left = ColorPicker.RGBtoINT(r, 0, b);	right = ColorPicker.RGBtoINT(r, 255, b);
@@ -522,10 +522,12 @@ class ColorPicker extends h2d.comp.Component {
 		chart.refColor = palette.color;
 		change = SNone;
 		
+		#if flash
 		flash.Lib.current.stage.addEventListener(flash.events.Event.ENTER_FRAME, doUpdate);
+		#end
 	}
-	
-	function doUpdate(e:flash.events.Event) {
+
+	function doUpdate(_) {
 		finalColor.preview = chart.color;
 		if(change.equals(SNone)) {
 			if(finalColor.color != chart.color) {
@@ -535,7 +537,6 @@ class ColorPicker extends h2d.comp.Component {
 			return;
 		}
 			
-		var p = new flash.geom.Point(flash.Lib.current.mouseX, flash.Lib.current.mouseY);
 		switch(change) {
 			case SColor:	palette.setColorFrom(finalColor.color);
 							chart.setColorFrom(finalColor.color);
@@ -555,7 +556,7 @@ class ColorPicker extends h2d.comp.Component {
 		gaugeBlue.color = chart.color;
 	}
 	
-	public function show (color:UInt) {
+	public function show(color:Int) {
 		finalColor.color = color;
 		palette.setColorFrom(finalColor.color);
 		chart.setColorFrom(finalColor.color);
@@ -565,15 +566,15 @@ class ColorPicker extends h2d.comp.Component {
 		visible = false;
 	}
 	
-	public dynamic function onChange( value : UInt ) {
+	public dynamic function onChange( value : Int ) {
 	}
 	
 //////////////////
-	inline public static function INTtoRGB(color:UInt) {
+	inline public static function INTtoRGB(color:Int) {
 		return [(color >> 16) & 0xFF, (color >> 8) & 0xFF,  color & 0xFF];
 	}
 	
-	inline public static function INTtoHSL(color:UInt) {
+	inline public static function INTtoHSL(color:Int) {
 		var rgb = INTtoRGB(color);
 		return RGBtoHLS(rgb[0], rgb[1], rgb[2]);
 	}
@@ -632,7 +633,7 @@ class ColorPicker extends h2d.comp.Component {
 		return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
 	}
 	
-	inline public static function complementaryColor (color:UInt) {
+	inline public static function complementaryColor (color:Int) {
 		var rgb = INTtoRGB(color);
 		var r = rgb[0] ^ 0xFF;
 		var g = rgb[1] ^ 0xFF;

+ 2 - 2
h2d/comp/Component.hx

@@ -43,7 +43,7 @@ class Component extends Sprite {
 	}
 	
 	function getElementByIdRec( s : h2d.Sprite, id : String ) : Component {
-		var c = flash.Lib.as(s, Component);
+		var c = Std.instance(s, Component);
 		if( c != null && c.id == id )
 			return c;
 		for( s in s.childs ) {
@@ -65,7 +65,7 @@ class Component extends Sprite {
 		var old = parentComponent;
 		var p = parent;
 		while( p != null ) {
-			var c = flash.Lib.as(p, Component);
+			var c = Std.instance(p, Component);
 			if( c != null ) {
 				parentComponent = c;
 				if( old != c ) {

+ 1 - 1
h2d/comp/Context.hx

@@ -36,7 +36,7 @@ class Context {
 
 	static var FONTS = new Map<String,h2d.Font>();
 	
-	public static var DEFAULT_CSS = h3d.System.getFileContent("h2d/css/default.css");
+	public static var DEFAULT_CSS = hxd.Resource.getFileContent("h2d/css/default.css");
 	
 	static var DEF = null;
 	public static function getDefaultCss() {

+ 10 - 8
h2d/comp/GradientEditor.hx

@@ -2,7 +2,7 @@ package h2d.comp;
 import h2d.css.Defs;
 import h2d.css.Fill;
 
-private typedef Key = { x:Float, value:UInt };
+private typedef Key = { x:Float, value:Int };
 
 private enum KCursor {
 	KAlpha;
@@ -71,10 +71,10 @@ private class CFlag extends h2d.css.Fill {
 }
 
 private class Cursor extends h2d.Sprite {
-	public var value(default, set):UInt;
+	public var value(default, set):Int;
 	public var coeff(get, null):Float;
-	public var color:UInt = 0xFFFFFFFF;
-	public var bgcolor:UInt = 0xFFFF00FF;
+	public var color:Int = 0xFFFFFFFF;
+	public var bgcolor:Int = 0xFFFF00FF;
 	public var cursor:h2d.Sprite;
 	public var kind:KCursor;
 	var ang:Float;
@@ -251,7 +251,7 @@ private class ColorSelector extends h2d.Sprite {
 	var locInput:h2d.comp.Input;
 	var colorInput:h2d.comp.Input;
 	var canvas:h2d.css.Fill;
-	var color:UInt = 0xFFFFFFFF;
+	var color:Int = 0xFFFFFFFF;
 	var interact : h2d.Interactive;
 	
 	public function new (ix, iy, parent) {
@@ -418,11 +418,13 @@ class GradientEditor extends h2d.comp.Component {
 		updateTarget = colorsKeys[0];
 		updateKeys();
 		drawGradient();
-		
+	
+		#if flash
 		flash.Lib.current.stage.addEventListener(flash.events.Event.ENTER_FRAME, doUpdate);
+		#end
 	}
 	
-	function doUpdate(e:flash.events.Event) {
+	function doUpdate(_) {
 		if(dragTarget != null) {
 			if(dragOut) {
 				switch(dragTarget.kind) {
@@ -612,7 +614,7 @@ class GradientEditor extends h2d.comp.Component {
 	}
 	
 	
-	inline public static function INTtoRGB(color:UInt) {
+	inline public static function INTtoRGB(color:Int) {
 		return [(color >> 16) & 0xFF, (color >> 8) & 0xFF,  color & 0xFF, color >>> 24];
 	}
 	

+ 3 - 2
h2d/comp/Input.hx

@@ -1,4 +1,5 @@
 package h2d.comp;
+import hxd.Key;
 
 @:access(h2d.comp.Input.scene)
 class Input extends Component {
@@ -36,7 +37,7 @@ class Input extends Component {
 			removeClass(":focus");
 			cursor.visible = false;
 		};
-		input.onKeyDown = function(e:Event) {
+		input.onKeyDown = function(e:hxd.Event) {
 			if( input.hasFocus() ) {
 				// BACK
 				switch( e.keyCode ) {
@@ -54,7 +55,7 @@ class Input extends Component {
 					value = value.substr(0, cursorPos) + value.substr(cursorPos + 1);
 					onChange(value);
 					return;
-				case Key.BACK:
+				case Key.BACKSPACE:
 					value = value.substr(0, cursorPos - 1) + value.substr(cursorPos);
 					cursorPos--;
 					onChange(value);

+ 1 - 1
h2d/comp/Slider.hx

@@ -28,7 +28,7 @@ class Slider extends Component {
 		value = 0.;
 	}
 	
-	function pixelToVal( e : h2d.Event ) {
+	function pixelToVal( e : hxd.Event ) {
 		return Std.int(e.relX - (style.borderSize + cursor.width * 0.5) ) / (input.width - (style.borderSize * 2 + cursor.width));
 	}
 	

+ 1 - 1
h3d/Drawable.hx

@@ -4,7 +4,7 @@ package h3d;
 	A core object is a rendering context but completely outside of the 3d scene.
 	It is meant to be able to share a rendering context between several similar physical objects.
  **/
-class Drawable<S:hxsl.Shader> implements IDrawable {
+class Drawable<S:h3d.impl.Shader> implements IDrawable {
 
 	public var shader : S;
 	public var primitive : h3d.prim.Primitive;

+ 54 - 290
h3d/Engine.hx

@@ -3,9 +3,8 @@ import h3d.mat.Data;
 
 class Engine {
 
-	var s3d : flash.display.Stage3D;
-	var ctx : flash.display3D.Context3D;
-
+	var driver : h3d.impl.Driver;
+	
 	public var mem(default,null) : h3d.impl.MemoryManager;
 
 	public var hardware(default, null) : Bool;
@@ -28,18 +27,9 @@ class Engine {
 	public var forcedMatMask : Int = 0xFFFFFF;
 	
 	var realFps : Float;
-	var lastTime : Int;
-	
-	var curMatBits : Int;
-	var curShader : hxsl.Shader.ShaderInstance;
-	var curBuffer : h3d.impl.MemoryManager.BigBuffer;
-	var curMultiBuffer : Array<h3d.impl.Buffer.BufferOffset>;
-	var curAttributes : Int;
-	var curTextures : Array<h3d.mat.Texture>;
-	var curSamplerBits : Array<Int>;
+	var lastTime : Float;
 	var antiAlias : Int;
-	var inTarget : Bool;
-
+	
 	var debugPoint : h3d.Drawable<h3d.impl.Shaders.PointShader>;
 	var debugLine : h3d.Drawable<h3d.impl.Shaders.LineShader>;
 	
@@ -50,13 +40,18 @@ class Engine {
 		this.hardware = hardware;
 		this.antiAlias = aa;
 		this.autoResize = true;
-		fullScreen = !System.isWindowed;
-		var stage = flash.Lib.current.stage;
-		realFps = stage.frameRate;
-		lastTime = flash.Lib.getTimer();
-		stage.scaleMode = flash.display.StageScaleMode.NO_SCALE;
-		stage.addEventListener(flash.events.Event.RESIZE, onStageResize);
-		s3d = stage.stage3Ds[0];
+		fullScreen = !hxd.System.isWindowed;
+		var stage = hxd.Stage.getInstance();
+		realFps = stage.getFrameRate();
+		lastTime = haxe.Timer.stamp();
+		stage.addResizeEvent(onStageResize);
+		#if flash
+		driver = new h3d.impl.Stage3dDriver();
+		#elseif js
+		driver = new h3d.impl.WebglDriver();
+		#else
+		throw "No driver";
+		#end
 		if( CURRENT == null )
 			CURRENT = this;
 	}
@@ -71,120 +66,30 @@ class Engine {
 		CURRENT = this;
 	}
 
-	public function show( b ) {
-		s3d.visible = b;
-	}
-
 	public function init() {
-		s3d.addEventListener(flash.events.Event.CONTEXT3D_CREATE, onCreate);
-		s3d.requestContext3D( hardware ? "auto" : "software" );
-	}
-
-	public function saveTo( bmp : flash.display.BitmapData ) {
-		ctx.drawToBitmapData(bmp);
+		driver.init(onCreate, !hardware);
 	}
 
 	public function driverName(details=false) {
-		return ctx == null ? "None" : (details ? ctx.driverInfo : ctx.driverInfo.split(" ")[0]);
-	}
-
-	public function isReady() {
-		return ctx != null;
+		return driver.getDriverName(details);
 	}
 
-	public function selectShader( shader : hxsl.Shader ) {
-		var s = shader.getInstance();
-		if( s.program == null ) {
-			s.program = ctx.createProgram();
-			var vdata = s.vertexBytes.getData();
-			var fdata = s.fragmentBytes.getData();
-			vdata.endian = flash.utils.Endian.LITTLE_ENDIAN;
-			fdata.endian = flash.utils.Endian.LITTLE_ENDIAN;
-			s.program.upload(vdata, fdata);
-			curShader = null; // in case we had the same shader and it was disposed
-		}
-		if( s != curShader ) {
-			ctx.setProgram(s.program);
+	public function selectShader( shader : h3d.impl.Shader ) {
+		if( driver.selectShader(shader) )
 			shaderSwitches++;
-			s.varsChanged = true;
-			// unbind extra textures
-			var tcount : Int = s.textures.length;
-			while( curTextures.length > tcount ) {
-				curTextures.pop();
-				ctx.setTextureAt(curTextures.length, null);
-			}
-			// force remapping of vertex buffer
-			curBuffer = null;
-			curMultiBuffer = null;
-			curShader = s;
-		}
-		if( s.varsChanged ) {
-			s.varsChanged = false;
-			ctx.setProgramConstantsFromVector(flash.display3D.Context3DProgramType.VERTEX, 0, s.vertexVars.toData());
-			ctx.setProgramConstantsFromVector(flash.display3D.Context3DProgramType.FRAGMENT, 0, s.fragmentVars.toData());
-			for( i in 0...s.textures.length ) {
-				var t = s.textures[i];
-				if( t == null || t.isDisposed() )
-					t = h2d.Tile.fromColor(0xFFFF00FF).getTexture();
-				var cur = curTextures[i];
-				if( t != cur ) {
-					ctx.setTextureAt(i, t.t);
-					curTextures[i] = t;
-				}
-				// if we have set one of the texture flag manually or if the shader does not configure the texture flags
-				if( !t.hasDefaultFlags() || !s.texHasConfig[s.textureMap[i]] ) {
-					if( cur == null || t.bits != curSamplerBits[i] ) {
-						ctx.setSamplerStateAt(i, WRAP[t.wrap.getIndex()], FILTER[t.filter.getIndex()], MIP[t.mipMap.getIndex()]);
-						curSamplerBits[i] = t.bits;
-					}
-				} else {
-					// the texture flags has been set by the shader, so we are in an unkown state
-					curSamplerBits[i] = -1;
-				}
-			}
-		}
 	}
 
 	@:access(h3d.mat.Material.bits)
 	public function selectMaterial( m : h3d.mat.Material ) {
 		var mbits = (m.bits & forcedMatMask) | forcedMatBits;
-		var diff = curMatBits ^ mbits;
-		if( diff != 0 ) {
-			if( curMatBits < 0 || diff&3 != 0 )
-				ctx.setCulling(FACE[mbits&3]);
-			if( curMatBits < 0 || diff & (0xFF << 6) != 0 )
-				ctx.setBlendFactors(BLEND[(mbits>>6)&15], BLEND[(mbits>>10)&15]);
-			if( curMatBits < 0 || diff & (15 << 2) != 0 )
-				ctx.setDepthTest((mbits >> 2) & 1 == 1, COMPARE[(mbits>>3)&7]);
-			if( curMatBits < 0 || diff & (15 << 14) != 0 )
-				ctx.setColorMask((mbits >> 14) & 1 != 0, (mbits >> 14) & 2 != 0, (mbits >> 14) & 4 != 0, (mbits >> 14) & 8 != 0);
-			curMatBits = mbits;
-		}
+		driver.selectMaterial(mbits);
 		selectShader(m.shader);
 	}
 
 	function selectBuffer( buf : h3d.impl.MemoryManager.BigBuffer ) {
 		if( buf.isDisposed() )
 			return false;
-		if( buf == curBuffer )
-			return true;
-		curBuffer = buf;
-		curMultiBuffer = null;
-		if( buf.stride < curShader.stride )
-			throw "Buffer stride (" + buf.stride + ") and shader stride (" + curShader.stride + ") mismatch";
-		if( !buf.written )
-			mem.finalize(buf);
-		var pos = 0, offset = 0;
-		var bits = curShader.bufferFormat;
-		while( offset < curShader.stride ) {
-			var size = bits & 7;
-			ctx.setVertexBufferAt(pos++, buf.vbuf, offset, FORMAT[size]);
-			offset += size == 0 ? 1 : size;
-			bits >>= 3;
-		}
-		for( i in pos...curAttributes )
-			ctx.setVertexBufferAt(i, null);
-		curAttributes = pos;
+		driver.selectBuffer(buf.vbuf);
 		return true;
 	}
 
@@ -223,7 +128,7 @@ class Engine {
 			}
 			if( ntri > 0 && selectBuffer(b.b) ) {
 				// *3 because it's the position in indexes which are always by 3
-				ctx.drawTriangles(indexes.ibuf, pos * 3, ntri);
+				driver.draw(indexes.ibuf, pos * 3, ntri);
 				drawTriangles += ntri;
 				drawCalls++;
 			}
@@ -241,7 +146,7 @@ class Engine {
 		if( drawTri < 0 ) drawTri = maxTri - startTri;
 		if( drawTri > 0 && selectBuffer(b.b) ) {
 			// *3 because it's the position in indexes which are always by 3
-			ctx.drawTriangles(indexes.ibuf, startTri * 3, drawTri);
+			driver.draw(indexes.ibuf, startTri * 3, drawTri);
 			drawTriangles += drawTri;
 			drawCalls++;
 		}
@@ -250,44 +155,13 @@ class Engine {
 	public function renderMultiBuffers( buffers : Array<h3d.impl.Buffer.BufferOffset>, indexes : h3d.impl.Indexes, startTri = 0, drawTri = -1 ) {
 		var maxTri = Std.int(indexes.count / 3);
 		if( maxTri <= 0 ) return;
-		
-		// select the multiple buffers elements
-		var changed = curMultiBuffer == null || curMultiBuffer.length != buffers.length;
-		if( !changed )
-			for( i in 0...curMultiBuffer.length )
-				if( buffers[i] != curMultiBuffer[i] ) {
-					changed = true;
-					break;
-				}
-		if( changed ) {
-			var pos = 0, offset = 0;
-			var bits = curShader.bufferFormat;
-			while( offset < curShader.stride ) {
-				var size = bits & 7;
-				var b = buffers[pos];
-				if( b.b.next != null )
-					throw "Buffer is split";
-				if( !b.b.b.written )
-					mem.finalize(b.b.b);
-				ctx.setVertexBufferAt(pos, b.b.b.vbuf, b.offset, FORMAT[size]);
-				offset += size == 0 ? 1 : size;
-				bits >>= 3;
-				pos++;
-			}
-			for( i in pos...curAttributes )
-				ctx.setVertexBufferAt(i, null);
-			curAttributes = pos;
-			curBuffer = null;
-			curMultiBuffer = buffers;
-		}
-		
+		driver.selectMultiBuffers(buffers);
 		if( indexes.isDisposed() )
 			return;
-		
 		if( drawTri < 0 ) drawTri = maxTri - startTri;
 		if( drawTri > 0 ) {
 			// render
-			ctx.drawTriangles(indexes.ibuf, startTri * 3, drawTri);
+			driver.draw(indexes.ibuf, startTri * 3, drawTri);
 			drawTriangles += drawTri;
 			drawCalls++;
 		}
@@ -295,31 +169,24 @@ class Engine {
 
 	function set_debug(d) {
 		debug = d;
-		if( ctx != null ) ctx.enableErrorChecking = d && hardware;
+		driver.setDebug(debug);
 		return d;
 	}
 
-	function onCreate(_) {
-		var old = ctx;
+	function onCreate( disposed ) {
 		if( autoResize ) {
-			width = System.width;
-			height = System.height;
+			width = hxd.System.width;
+			height = hxd.System.height;
 		}
-		if( old != null ) {
-			if( old.driverInfo != "Disposed" ) throw "Duplicate onCreate()";
-			old.dispose();
-			hxsl.Shader.ShaderGlobals.disposeAll();
-			ctx = s3d.context3D;
-			mem.onContextLost(ctx);
-		} else {
-			ctx = s3d.context3D;
-			mem = new h3d.impl.MemoryManager(ctx, 65400);
-		}
-		hardware = ctx.driverInfo.toLowerCase().indexOf("software") == -1;
+		if( disposed )
+			mem.onContextLost();
+		else
+			mem = new h3d.impl.MemoryManager(driver, 65400);
+		hardware = driver.isHardware();
 		set_debug(debug);
 		set_fullScreen(fullScreen);
 		resize(width, height, antiAlias);
-		if( old != null )
+		if( disposed )
 			onContextLost();
 		else
 			onReady();
@@ -331,33 +198,19 @@ class Engine {
 	public dynamic function onReady() {
 	}
 	
-	function onStageResize(_) {
-		if( ctx != null && autoResize && ctx.driverInfo != "Disposed" ) {
-			var w = System.width, h = System.height;
+	function onStageResize() {
+		if( autoResize && !driver.isDisposed() ) {
+			var w = hxd.System.width, h = hxd.System.height;
 			if( w != width || h != height )
 				resize(w, h, antiAlias);
 			onResized();
 		}
 	}
 	
-	var fsDelayed : Bool;
 	function set_fullScreen(v) {
 		fullScreen = v;
-		if( ctx != null && System.isWindowed ) {
-			var stage = flash.Lib.current.stage;
-			var isAir = flash.system.Capabilities.playerType == "Desktop";
-			var state = v ? (isAir ? flash.display.StageDisplayState.FULL_SCREEN_INTERACTIVE : flash.display.StageDisplayState.FULL_SCREEN) : flash.display.StageDisplayState.NORMAL;
-			if( stage.displayState != state ) {
-				var t = flash.Lib.getTimer();
-				// delay first fullsrceen toggle on OSX/Air to prevent the command window to spawn over
-				if( v && isAir && t < 5000 && !fsDelayed && flash.system.Capabilities.os.indexOf("Mac") != -1 ) {
-					fsDelayed = true;
-					haxe.Timer.delay(function() this.fullScreen = fullScreen, 1000);
-					return v;
-				}
-				stage.displayState = state;
-			}
-		}
+		if( mem != null && hxd.System.isWindowed )
+			hxd.Stage.getInstance().setFullScreen(v);
 		return v;
 	}
 	
@@ -371,72 +224,39 @@ class Engine {
 		this.width = width;
 		this.height = height;
 		this.antiAlias = aa;
-		if( ctx.driverInfo != "Disposed" ) {
-			try {
-				ctx.configureBackBuffer(width, height, aa);
-			} catch( e : Dynamic ) {
-				throw Std.string(e) + " " + width + "x" + height;
-			}
-		}
+		if( !driver.isDisposed() ) driver.resize(width, height, aa);
 	}
 
 	public function begin() {
-		if( ctx == null || ctx.driverInfo == "Disposed" )
+		if( driver.isDisposed() )
 			return false;
-		ctx.clear( ((backgroundColor>>16)&0xFF)/255 , ((backgroundColor>>8)&0xFF)/255, (backgroundColor&0xFF)/255, ((backgroundColor>>>24)&0xFF)/255);
+		driver.clear( ((backgroundColor>>16)&0xFF)/255 , ((backgroundColor>>8)&0xFF)/255, (backgroundColor&0xFF)/255, ((backgroundColor>>>24)&0xFF)/255);
 		// init
 		frameCount++;
 		drawTriangles = 0;
 		shaderSwitches = 0;
 		drawCalls = 0;
-		curMatBits = -1;
-		curShader = null;
-		curBuffer = null;
-		curMultiBuffer = null;
 		curProjMatrix = null;
-		curTextures = [];
-		curSamplerBits = [];
+		driver.reset();
 		return true;
 	}
 
 	function reset() {
-		curMatBits = -1;
-		curShader = null;
-		curBuffer = null;
-		curMultiBuffer = null;
-		for( i in 0...curAttributes )
-			ctx.setVertexBufferAt(i, null);
-		curAttributes = 0;
-		for( i in 0...curTextures.length )
-			ctx.setTextureAt(i, null);
-		curTextures = [];
+		driver.reset();
 	}
 
 	public function end() {
-		ctx.present();
+		driver.present();
 		reset();
 		curProjMatrix = null;
 	}
 
 	public function setTarget( tex : h3d.mat.Texture, useDepth = false, clearColor = 0 ) {
-		if( tex == null ) {
-			ctx.setRenderToBackBuffer();
-			inTarget = false;
-		} else {
-			if( inTarget )
-				throw "Calling setTarget() while already set";
-			ctx.setRenderToTexture(tex.t, useDepth);
-			inTarget = true;
-			reset();
-			ctx.clear( ((clearColor>>16)&0xFF)/255 , ((clearColor>>8)&0xFF)/255, (clearColor&0xFF)/255, ((clearColor>>>24)&0xFF)/255);
-		}
+		driver.setRenderTarget(tex == null ? null : tex.t, useDepth, clearColor);
 	}
 
 	public function setRenderZone( x = 0, y = 0, width = -1, height = -1 ) {
-		if( x == 0 && y == 0 && width < 0 && height < 0 )
-			ctx.setScissorRectangle(null);
-		else
-			ctx.setScissorRectangle(new flash.geom.Rectangle(x, y, width < 0 ? this.width : width, height < 0 ? this.height : height));
+		driver.setRenderZone(x, y, width, height);
 	}
 
 	public function render( obj : { function render( engine : Engine ) : Void; } ) {
@@ -444,12 +264,12 @@ class Engine {
 		obj.render(this);
 		end();
 				
-		var delta = flash.Lib.getTimer() - lastTime;
+		var delta = haxe.Timer.stamp() - lastTime;
 		lastTime += delta;
 		if( delta > 0 ) {
-			var curFps = 1000 / delta;
+			var curFps = 1. / delta;
 			if( curFps > realFps * 2 ) curFps = realFps * 2 else if( curFps < realFps * 0.5 ) curFps = realFps * 0.5;
-			var f = delta / 500;
+			var f = delta / .5;
 			if( f > 0.3 ) f = 0.3;
 			realFps = realFps * (1 - f) + curFps * f; // smooth a bit the fps
 		}
@@ -496,68 +316,12 @@ class Engine {
 	}
 
 	public function dispose() {
-		s3d.removeEventListener(flash.events.Event.CONTEXT3D_CREATE, onCreate);
-		ctx.dispose();
-		ctx = null;
+		driver.dispose();
+		hxd.Stage.getInstance().removeResizeEvent(onStageResize);
 	}
 	
 	function get_fps() {
 		return Math.ceil(realFps * 100) / 100;
 	}
-
-	static var BLEND = [
-		flash.display3D.Context3DBlendFactor.ONE,
-		flash.display3D.Context3DBlendFactor.ZERO,
-		flash.display3D.Context3DBlendFactor.SOURCE_ALPHA,
-		flash.display3D.Context3DBlendFactor.SOURCE_COLOR,
-		flash.display3D.Context3DBlendFactor.DESTINATION_ALPHA,
-		flash.display3D.Context3DBlendFactor.DESTINATION_COLOR,
-		flash.display3D.Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA,
-		flash.display3D.Context3DBlendFactor.ONE_MINUS_SOURCE_COLOR,
-		flash.display3D.Context3DBlendFactor.ONE_MINUS_DESTINATION_ALPHA,
-		flash.display3D.Context3DBlendFactor.ONE_MINUS_DESTINATION_COLOR
-	];
-
-	static var FACE = [
-		flash.display3D.Context3DTriangleFace.NONE,
-		flash.display3D.Context3DTriangleFace.BACK,
-		flash.display3D.Context3DTriangleFace.FRONT,
-		flash.display3D.Context3DTriangleFace.FRONT_AND_BACK,
-	];
-
-	static var COMPARE = [
-		flash.display3D.Context3DCompareMode.ALWAYS,
-		flash.display3D.Context3DCompareMode.NEVER,
-		flash.display3D.Context3DCompareMode.EQUAL,
-		flash.display3D.Context3DCompareMode.NOT_EQUAL,
-		flash.display3D.Context3DCompareMode.GREATER,
-		flash.display3D.Context3DCompareMode.GREATER_EQUAL,
-		flash.display3D.Context3DCompareMode.LESS,
-		flash.display3D.Context3DCompareMode.LESS_EQUAL,
-	];
-
-	static var FORMAT = [
-		flash.display3D.Context3DVertexBufferFormat.BYTES_4,
-		flash.display3D.Context3DVertexBufferFormat.FLOAT_1,
-		flash.display3D.Context3DVertexBufferFormat.FLOAT_2,
-		flash.display3D.Context3DVertexBufferFormat.FLOAT_3,
-		flash.display3D.Context3DVertexBufferFormat.FLOAT_4,
-	];
-	
-	static var WRAP = [
-		flash.display3D.Context3DWrapMode.CLAMP,
-		flash.display3D.Context3DWrapMode.REPEAT,
-	];
-	
-	static var FILTER = [
-		flash.display3D.Context3DTextureFilter.NEAREST,
-		flash.display3D.Context3DTextureFilter.LINEAR,
-	];
-	
-	static var MIP = [
-		flash.display3D.Context3DMipFilter.MIPNONE,
-		flash.display3D.Context3DMipFilter.MIPNEAREST,
-		flash.display3D.Context3DMipFilter.MIPLINEAR,
-	];
 	
 }

+ 1 - 1
h3d/anim/Animation.hx

@@ -123,7 +123,7 @@ class Animation {
 			var obj = base.getObjectByName(a.objectName);
 			if( obj == null )
 				throw a.objectName + " was not found";
-			var joint = flash.Lib.as(obj, h3d.scene.Skin.Joint);
+			var joint = Std.instance(obj, h3d.scene.Skin.Joint);
 			if( joint != null ) {
 				currentSkin = cast joint.parent;
 				a2.targetSkin = currentSkin;

+ 2 - 2
h3d/anim/FrameAnimation.hx

@@ -2,8 +2,8 @@ package h3d.anim;
 import h3d.anim.Animation;
 
 class FrameObject extends AnimatedObject {
-	public var frames : flash.Vector<h3d.Matrix>;
-	public var alphas : flash.Vector<Float>;
+	public var frames : haxe.ds.Vector<h3d.Matrix>;
+	public var alphas : haxe.ds.Vector<Float>;
 	
 	override function clone() : AnimatedObject {
 		var o = new FrameObject(objectName);

+ 2 - 2
h3d/anim/LinearAnimation.hx

@@ -19,8 +19,8 @@ class LinearFrame {
 class LinearObject extends AnimatedObject {
 	public var hasRotation : Bool;
 	public var hasScale : Bool;
-	public var frames : flash.Vector<LinearFrame>;
-	public var alphas : flash.Vector<Float>;
+	public var frames : haxe.ds.Vector<LinearFrame>;
+	public var alphas : haxe.ds.Vector<Float>;
 	public var matrix : h3d.Matrix;
 	override function clone() : AnimatedObject {
 		var o = new LinearObject(objectName);

+ 15 - 0
h3d/col/Bounds.hx

@@ -135,6 +135,21 @@ class Bounds {
 		if( p.z > zMax ) zMax = p.z;
 	}
 	
+	public function intersection( a : Bounds, b : Bounds ) {
+		var xMin = FMath.max(a.xMin, b.xMin);
+		var yMin = FMath.max(a.yMin, b.yMin);
+		var zMin = FMath.max(a.zMin, b.zMin);
+		var xMax = FMath.max(a.xMax, b.xMax);
+		var yMax = FMath.max(a.yMax, b.yMax);
+		var zMax = FMath.max(a.zMax, b.zMax);
+		this.xMin = xMin;
+		this.yMin = yMin;
+		this.zMin = zMin;
+		this.xMax = xMax;
+		this.yMax = yMax;
+		this.zMax = zMax;
+	}
+	
 	public inline function setMin( p : Vector ) {
 		xMin = p.x;
 		yMin = p.y;

+ 13 - 4
h3d/fbx/Library.hx

@@ -195,6 +195,15 @@ class Library {
 		return root;
 	}
 	
+	public function ignoreMissingObject( name : String ) {
+		var def = defaultModelMatrixes.get(name);
+		if( def == null ) {
+			def = new DefaultMatrixes();
+			def.wasRemoved = -1;
+			defaultModelMatrixes.set(name, def);
+		}
+	}
+
 	public function loadAnimation( mode : AnimationMode, ?animName : String, ?root : FbxNode, ?lib : Library ) : h3d.anim.Animation {
 		if( lib != null ) {
 			lib.defaultModelMatrixes = defaultModelMatrixes;
@@ -353,8 +362,8 @@ class Library {
 			var anim = new h3d.anim.FrameAnimation(animName, numFrames, sampling);
 		
 			for( c in curves ) {
-				var frames = c.t == null && c.r == null && c.s == null ? null : new flash.Vector(numFrames);
-				var alpha = c.a == null ? null : new flash.Vector(numFrames);
+				var frames = c.t == null && c.r == null && c.s == null ? null : new haxe.ds.Vector(numFrames);
+				var alpha = c.a == null ? null : new haxe.ds.Vector(numFrames);
 				// skip empty curves
 				if( frames == null && alpha == null )
 					continue;
@@ -440,8 +449,8 @@ class Library {
 			var q = new h3d.Quat(), q2 = new h3d.Quat();
 
 			for( c in curves ) {
-				var frames = c.t == null && c.r == null && c.s == null ? null : new flash.Vector(numFrames);
-				var alpha = c.a == null ? null : new flash.Vector(numFrames);
+				var frames = c.t == null && c.r == null && c.s == null ? null : new haxe.ds.Vector(numFrames);
+				var alpha = c.a == null ? null : new haxe.ds.Vector(numFrames);
 				// skip empty curves
 				if( frames == null && alpha == null )
 					continue;

+ 1 - 1
h3d/fbx/Parser.hx

@@ -237,7 +237,7 @@ class Parser {
 					do {
 						c = nextChar();
 					} while( c >= '0'.code && c <= '9'.code );
-					if( c != '.'.code && pos - start < 10 ) {
+					if( c != '.'.code && c != 'E'.code && c != 'e'.code && pos - start < 10 ) {
 						pos--;
 						return TInt(getBuf(start, pos - start));
 					}

+ 4 - 4
h3d/impl/Buffer.hx

@@ -38,24 +38,24 @@ class Buffer {
 		}
 	}
 	
-	public function uploadVector( data : flash.Vector<Float>, dataPos : Int, nverts : Int ) {
+	public function uploadVector( data : hxd.FloatBuffer, dataPos : Int, nverts : Int ) {
 		var cur = this;
 		while( nverts > 0 ) {
 			if( cur == null ) throw "Too many vertexes";
 			var count = nverts > cur.nvert ? cur.nvert : nverts;
-			cur.b.vbuf.uploadFromVector( dataPos == 0 ? data : data.slice(dataPos,count*b.stride+dataPos), cur.pos, count );
+			cur.b.mem.driver.uploadVertexBuffer(cur.b.vbuf, cur.pos, count, data, dataPos);
 			dataPos += count * b.stride;
 			nverts -= count;
 			cur = cur.next;
 		}
 	}
 	
-	public function upload( data : flash.utils.ByteArray, dataPos : Int, nverts : Int ) {
+	public function uploadBytes( data : haxe.io.Bytes, dataPos : Int, nverts : Int ) {
 		var cur = this;
 		while( nverts > 0 ) {
 			if( cur == null ) throw "Too many vertexes";
 			var count = nverts > cur.nvert ? cur.nvert : nverts;
-			cur.b.vbuf.uploadFromByteArray(data, dataPos, cur.pos, count);
+			cur.b.mem.driver.uploadVertexBytes(cur.b.vbuf, cur.pos, count, data, dataPos);
 			dataPos += count * b.stride * 4;
 			nverts -= count;
 			cur = cur.next;

+ 118 - 0
h3d/impl/Driver.hx

@@ -0,0 +1,118 @@
+package h3d.impl;
+
+#if flash
+typedef IndexBuffer = flash.display3D.IndexBuffer3D;
+typedef VertexBuffer = Stage3dDriver.VertexWrapper;
+typedef Texture = flash.display3D.textures.TextureBase;
+#elseif js
+typedef IndexBuffer = js.html.webgl.Buffer;
+typedef VertexBuffer = js.html.webgl.Buffer;
+typedef Texture = js.html.webgl.Texture;
+#else
+typedef IndexBuffer = Int;
+typedef VertexBuffer = Int;
+typedef Texture = Int;
+#end
+
+class Driver {
+	
+	public function isDisposed() {
+		return true;
+	}
+	
+	public function dispose() {
+	}
+	
+	public function clear( r : Float, g : Float, b : Float, a : Float ) {
+	}
+	
+	public function reset() {
+	}
+	
+	public function getDriverName( details : Bool ) {
+		return "Not available";
+	}
+	
+	public function init( onCreate : Bool -> Void, forceSoftware = false ) {
+	}
+	
+	public function resize( width : Int, height : Int, aa : Int ) {
+	}
+	
+	public function selectMaterial( mbits : Int ) {
+	}
+	
+	/** return value tells if we have shader shader **/
+	public function selectShader( shader : Shader ) : Bool {
+		return false;
+	}
+	
+	public function selectBuffer( buffer : VertexBuffer ) {
+	}
+	
+	public function getShaderInputNames() : Array<String> {
+		return null;
+	}
+	
+	public function selectMultiBuffers( buffers : Array<Buffer.BufferOffset> ) {
+	}
+	
+	public function draw( ibuf : IndexBuffer, startIndex : Int, ntriangles : Int ) {
+	}
+	
+	public function setRenderZone( x : Int, y : Int, width : Int, height : Int ) {
+	}
+	
+	public function setRenderTarget( tex : Null<Texture>, useDepth : Bool, clearColor : Int ) {
+	}
+	
+	public function present() {
+	}
+	
+	public function isHardware() {
+		return true;
+	}
+	
+	public function setDebug( b : Bool ) {
+	}
+	
+	public function allocTexture( t : h3d.mat.Texture ) : Texture {
+		return null;
+	}
+
+	public function allocIndexes( count : Int ) : IndexBuffer {
+		return null;
+	}
+
+	public function allocVertex( count : Int, stride : Int ) : VertexBuffer {
+		return null;
+	}
+	
+	public function disposeTexture( t : Texture ) {
+	}
+	
+	public function disposeIndexes( i : IndexBuffer ) {
+	}
+	
+	public function disposeVertex( v : VertexBuffer ) {
+	}
+	
+	public function uploadIndexesBuffer( i : IndexBuffer, startIndice : Int, indiceCount : Int, buf : hxd.IndexBuffer, bufPos : Int ) {
+	}
+
+	public function uploadIndexesBytes( i : IndexBuffer, startIndice : Int, indiceCount : Int, buf : haxe.io.Bytes , bufPos : Int ) {
+	}
+	
+	public function uploadVertexBuffer( v : VertexBuffer, startVertex : Int, vertexCount : Int, buf : hxd.FloatBuffer, bufPos : Int ) {
+	}
+
+	public function uploadVertexBytes( v : VertexBuffer, startVertex : Int, vertexCount : Int, buf : haxe.io.Bytes, bufPos : Int ) {
+	}
+	
+	public function uploadTextureBitmap( t : h3d.mat.Texture, bmp : hxd.BitmapData, mipLevel : Int, side : Int ) {
+	}
+
+	public function uploadTextureBytes( t : h3d.mat.Texture, bytes : haxe.io.Bytes, mipLevel : Int, side : Int ) {
+	}
+	
+}

+ 6 - 1
h3d/impl/Indexes.hx

@@ -1,10 +1,11 @@
 package h3d.impl;
 
 @:allow(h3d.impl.MemoryManager)
+@:allow(h3d.Engine)
 class Indexes {
 
 	var mem : MemoryManager;
-	public var ibuf : flash.display3D.IndexBuffer3D;
+	var ibuf : Driver.IndexBuffer;
 	public var count(default,null) : Int;
 	
 	function new(mem, ibuf, count) {
@@ -17,6 +18,10 @@ class Indexes {
 		return ibuf == null;
 	}
 	
+	public function upload( indexes : hxd.IndexBuffer, pos : Int, count : Int, bufferPos = 0 ) {
+		mem.driver.uploadIndexesBuffer(this.ibuf, pos, count, indexes, bufferPos);
+	}
+	
 	public function dispose() {
 		if( ibuf != null )
 			mem.deleteIndexes(this);

+ 48 - 79
h3d/impl/MemoryManager.hx

@@ -1,5 +1,11 @@
 package h3d.impl;
 
+#if flash
+private typedef WeakMap<K,T> = haxe.ds.WeakMap<K,T>;
+#else
+private typedef WeakMap<K,T> = haxe.ds.ObjectMap<K,T>;
+#end
+
 @:allow(h3d)
 class FreeCell {
 	var pos : Int;
@@ -15,18 +21,19 @@ class FreeCell {
 @:allow(h3d)
 class BigBuffer {
 
+	var mem : MemoryManager;
 	var stride : Int;
 	var size : Int;
-	var written : Bool;
-	var vbuf : flash.display3D.VertexBuffer3D;
+	
+	var vbuf : Driver.VertexBuffer;
 	var free : FreeCell;
 	var next : BigBuffer;
 	#if debug
 	public var allocHead : Buffer;
 	#end
 	
-	function new(v, stride, size) {
-		written = false;
+	function new(mem, v, stride, size) {
+		this.mem = mem;
 		this.size = size;
 		this.stride = stride;
 		this.vbuf = v;
@@ -64,7 +71,7 @@ class BigBuffer {
 	}
 
 	function dispose() {
-		vbuf.dispose();
+		mem.driver.disposeVertex(vbuf);
 		vbuf = null;
 	}
 	
@@ -79,13 +86,13 @@ class MemoryManager {
 	static inline var MAX_MEMORY = 250 << 20; // MB
 	static inline var MAX_BUFFERS = 4096;
 
-	var ctx : flash.display3D.Context3D;
-	var empty : flash.utils.ByteArray;
+	@:allow(h3d)
+	var driver : Driver;
 	var buffers : Array<BigBuffer>;
 	var idict : Map<Indexes,Bool>;
 	
-	var tdict : haxe.ds.WeakMap<h3d.mat.Texture,flash.display3D.textures.TextureBase>;
-	var textures : Array<flash.display3D.textures.TextureBase>;
+	var tdict : WeakMap<h3d.mat.Texture,Driver.Texture>;
+	var textures : Array<Driver.Texture>;
 	
 	public var indexes(default,null) : Indexes;
 	public var quadIndexes(default,null) : Indexes;
@@ -93,34 +100,33 @@ class MemoryManager {
 	public var bufferCount(default,null) : Int;
 	public var allocSize(default,null) : Int;
 
-	public function new(ctx,allocSize) {
-		this.ctx = ctx;
+	public function new(driver,allocSize) {
+		this.driver = driver;
 		this.allocSize = allocSize;
 
 		idict = new Map();
-		tdict = new haxe.ds.WeakMap();
+		tdict = new WeakMap();
 		textures = new Array();
-		empty = new flash.utils.ByteArray();
 		buffers = new Array();
 		
 		initIndexes();
 	}
 	
 	function initIndexes() {
-		var indices = new flash.Vector<UInt>();
-		for( i in 0...allocSize ) indices[i] = i;
+		var indices = new hxd.IndexBuffer();
+		for( i in 0...allocSize ) indices.push(i);
 		indexes = allocIndex(indices);
 
-		var indices = new flash.Vector<UInt>();
+		var indices = new hxd.IndexBuffer();
 		var p = 0;
 		for( i in 0...allocSize >> 2 ) {
 			var k = i << 2;
-			indices[p++] = k;
-			indices[p++] = k + 1;
-			indices[p++] = k + 2;
-			indices[p++] = k + 2;
-			indices[p++] = k + 1;
-			indices[p++] = k + 3;
+			indices.push(k);
+			indices.push(k + 1);
+			indices.push(k + 2);
+			indices.push(k + 2);
+			indices.push(k + 1);
+			indices.push(k + 3);
 		}
 		quadIndexes = allocIndex(indices);
 	}
@@ -234,16 +240,7 @@ class MemoryManager {
 	}
 	
 	function initTexture( t : h3d.mat.Texture ) {
-		var fmt = switch( t.format ) {
-		case Rgba, Atf:
-			flash.display3D.Context3DTextureFormat.BGRA;
-		case AtfCompressed(alpha):
-			alpha ? flash.display3D.Context3DTextureFormat.COMPRESSED_ALPHA : flash.display3D.Context3DTextureFormat.COMPRESSED;
-		}
-		if( t.isCubic )
-			t.t = ctx.createCubeTexture(t.width, fmt, t.isTarget, t.mipLevels);
-		else
-			t.t = ctx.createTexture(t.width, t.height, fmt, t.isTarget, t.mipLevels);
+		t.t = driver.allocTexture(t);
 		tdict.set(t, t.t);
 		textures.push(t.t);
 	}
@@ -251,7 +248,7 @@ class MemoryManager {
 	@:allow(h3d.impl.Indexes.dispose)
 	function deleteIndexes( i : Indexes ) {
 		idict.remove(i);
-		i.ibuf.dispose();
+		driver.disposeIndexes(i.ibuf);
 		i.ibuf = null;
 		usedMemory -= i.count * 2;
 	}
@@ -260,7 +257,7 @@ class MemoryManager {
 	function deleteTexture( t : h3d.mat.Texture ) {
 		textures.remove(t.t);
 		tdict.remove(t);
-		t.t.dispose();
+		driver.disposeTexture(t.t);
 		t.t = null;
 	}
 
@@ -295,10 +292,9 @@ class MemoryManager {
 		};
 	}
 
-	public function allocAtfTexture( width : Int, height : Int, mipLevels : Int = 0, alpha : Bool = false, compress : Bool = false, cubic : Bool = false, ?allocPos : AllocPos ) {
+	public function allocCustomTexture( fmt : h3d.mat.Data.TextureFormat, width : Int, height : Int, mipLevels : Int = 0, cubic : Bool = false, target : Bool = false, ?allocPos : AllocPos ) {
 		freeTextures();
-		var fmt = compress ? (alpha ? flash.display3D.Context3DTextureFormat.COMPRESSED_ALPHA : flash.display3D.Context3DTextureFormat.COMPRESSED) : flash.display3D.Context3DTextureFormat.BGRA;
-		return newTexture(compress ? AtfCompressed(alpha) : Atf, width, height, cubic, false, mipLevels, allocPos);
+		return newTexture(fmt, width, height, cubic, target, mipLevels, allocPos);
 	}
 	
 	public function allocTexture( width : Int, height : Int, ?mipMap = false, ?allocPos : AllocPos ) {
@@ -316,19 +312,6 @@ class MemoryManager {
 		return newTexture(Rgba, width, height, false, true, 0, allocPos);
 	}
 
-	public function makeTexture( ?bmp : flash.display.BitmapData, ?mbmp : h3d.mat.Bitmap, ?hasMipMap = false, ?allocPos : AllocPos ) {
-		var t;
-		if( bmp != null ) {
-			t = allocTexture(bmp.width, bmp.height, hasMipMap, allocPos);
-			if( hasMipMap ) t.uploadMipMap(bmp) else t.upload(bmp);
-		} else {
-			if( hasMipMap ) throw "No support for mipmap + bytes";
-			t = allocTexture(mbmp.width, mbmp.height, hasMipMap, allocPos);
-			t.uploadBytes(mbmp.bytes);
-		}
-		return t;
-	}
-
 	public function allocCubeTexture( size : Int, ?mipMap = false, ?allocPos : AllocPos ) {
 		freeTextures();
 		var levels = 0;
@@ -339,23 +322,24 @@ class MemoryManager {
 		return newTexture(Rgba, size, size, true, false, levels, allocPos);
 	}
 
-	public function allocIndex( indices : flash.Vector<UInt> ) {
-		var ibuf = ctx.createIndexBuffer(indices.length);
-		ibuf.uploadFromVector(indices, 0, indices.length);
-		var idx = new Indexes(this, ibuf, indices.length);
+	public function allocIndex( indices : hxd.IndexBuffer, pos = 0, count = -1 ) {
+		if( count < 0 ) count = indices.length;
+		var ibuf = driver.allocIndexes(count);
+		var idx = new Indexes(this, ibuf, count);
+		idx.upload(indices, 0, count);
 		idict.set(idx, true);
 		usedMemory += idx.count * 2;
 		return idx;
 	}
 
-	public function allocBytes( bytes : flash.utils.ByteArray, stride : Int, align, ?allocPos : AllocPos ) {
+	public function allocBytes( bytes : haxe.io.Bytes, stride : Int, align, ?allocPos : AllocPos ) {
 		var count = Std.int(bytes.length / (stride * 4));
 		var b = alloc(count, stride, align, allocPos);
-		b.upload(bytes, 0, count);
+		b.uploadBytes(bytes, 0, count);
 		return b;
 	}
 
-	public function allocVector( v : flash.Vector<Float>, stride, align, ?allocPos : AllocPos ) {
+	public function allocVector( v : hxd.FloatBuffer, stride, align, ?allocPos : AllocPos ) {
 		var nvert = Std.int(v.length / stride);
 		var b = alloc(nvert, stride, align, allocPos);
 		b.uploadVector(v, 0, nvert);
@@ -375,7 +359,7 @@ class MemoryManager {
 			tall.remove(t);
 		var count = 0;
 		for( t in tall.keys() ) {
-			t.dispose();
+			driver.disposeTexture(t);
 			textures.remove(t);
 			count++;
 		}
@@ -498,10 +482,13 @@ class MemoryManager {
 				}
 				return alloc(nvect, stride, align, allocPos);
 			}
-			var v = ctx.createVertexBuffer(size, stride);
+			var v = driver.allocVertex(size, stride);
 			usedMemory += mem;
 			bufferCount++;
-			b = new BigBuffer(v, stride, size);
+			b = new BigBuffer(this, v, stride, size);
+			#if flash
+			untyped v.b = b;
+			#end
 			b.next = buffers[stride];
 			buffers[stride] = b;
 			free = b.free;
@@ -525,25 +512,7 @@ class MemoryManager {
 		return b;
 	}
 
-	@:allow(h3d)
-	function finalize( b : BigBuffer ) {
-		if( !b.written ) {
-			b.written = true;
-			// fill all the free positions that were unwritten with zeroes (necessary for flash)
-			var f = b.free;
-			while( f != null ) {
-				if( f.count > 0 ) {
-					var mem : UInt = f.count * b.stride * 4;
-					if( empty.length < mem ) empty.length = mem;
-					b.vbuf.uploadFromByteArray(empty, 0, f.pos, f.count);
-				}
-				f = f.next;
-			}
-		}
-	}
-	
-	public function onContextLost( newContext ) {
-		ctx = newContext;
+	public function onContextLost() {
 		indexes.dispose();
 		quadIndexes.dispose();
 		var tkeys = Lambda.array({ iterator : tdict.keys });

+ 115 - 0
h3d/impl/Shader.hx

@@ -0,0 +1,115 @@
+package h3d.impl;
+#if macro
+import haxe.macro.Context;
+#end
+
+#if flash
+typedef Shader = hxsl.Shader;
+#elseif js
+
+enum ShaderType {
+	Float;
+	Vec2;
+	Vec3;
+	Vec4;
+	Mat2;
+	Mat3;
+	Mat4;
+	Tex2d;
+	TexCube;
+	Byte4;
+	Struct( field : String, t : ShaderType );
+	Index( index : Int, t : ShaderType );
+}
+
+typedef Uniform = { name : String, loc : js.html.webgl.UniformLocation, type : ShaderType, index : Int }
+
+class ShaderInstance {
+
+	public var program : js.html.webgl.Program;
+	public var attribs : Array<{ name : String, type : ShaderType, etype : Int, offset : Int, index : Int, size : Int }>;
+	public var uniforms : Array<Uniform>;
+	public var stride : Int;
+	public function new() {
+	}
+
+}
+
+@:autoBuild(h3d.impl.Shader.ShaderMacros.buildGLShader())
+class Shader {
+	
+	var instance : ShaderInstance;
+	
+	public function new() {
+	}
+	
+	function getConstants( vertex : Bool ) {
+		return "";
+	}
+
+}
+
+#else
+
+class Shader implements Dynamic {
+	public function new() {
+	}
+}
+
+#end
+
+#if macro
+class ShaderMacros {
+	
+	public static function buildGLShader() {
+		var pos = Context.getLocalClass().get().pos;
+		var fields = Context.getBuildFields();
+		var hasVertex = false, hasFragment = false;
+		var r_uni = ~/uniform[ \t]+((lowp|mediump|highp)[ \t]+)?([A-Za-z0-9_]+)[ \t]+([A-Za-z0-9_]+)[ \t]*(\/\*([A-Za-z0-9_]+)\*\/)?/;
+		function addUniforms( code : String ) {
+			while( r_uni.match(code) ) {
+				var name = r_uni.matched(4);
+				var type = r_uni.matched(3);
+				var hint = r_uni.matched(6);
+				code = r_uni.matchedRight();
+				var t = switch( type ) {
+				case "float": macro : Float;
+				case "vec4" if( hint == "byte4" ): macro : Int;
+				case "vec2", "vec3", "vec4": macro : h3d.Vector;
+				case "mat3", "mat4": macro : h3d.Matrix;
+				case "sampler2D", "samplerCube": macro : h3d.mat.Texture;
+				default:
+					// most likely a struct, handle it manually
+					if( type.charCodeAt(0) >= 'A'.code && type.charCodeAt(0) <= 'Z'.code )
+						continue;
+					throw "Unsupported type " + type;
+				}
+				if( code.charCodeAt(0) == '['.code )
+					t = macro : Array<$t>;
+				fields.push( {
+					name : name,
+					kind : FVar(t),
+					pos : pos,
+					access : [APublic],
+				});
+			}
+		}
+		for( f in fields )
+			switch( [f.name, f.kind] ) {
+			case ["VERTEX", FVar(_,{ expr : EConst(CString(code)) }) ]:
+				hasVertex = true;
+				addUniforms(code);
+			case ["FRAGMENT", FVar(_,{ expr : EConst(CString(code)) })]:
+				hasFragment = true;
+				addUniforms(code);
+			default:
+			}
+		if( !hasVertex )
+			haxe.macro.Context.error("Missing VERTEX shader", pos);
+		if( !hasFragment )
+			haxe.macro.Context.error("Missing FRAGMENT shader", pos);
+		return fields;
+	}
+	
+}
+#end

+ 43 - 2
h3d/impl/Shaders.hx

@@ -1,7 +1,8 @@
 package h3d.impl;
 
-class PointShader extends hxsl.Shader {
+class PointShader extends h3d.impl.Shader {
 
+#if flash
 	static var SRC = {
 		var input : {
 			pos : Float2,
@@ -18,11 +19,39 @@ class PointShader extends hxsl.Shader {
 			out = color;
 		}
 	}
+#elseif js
+
+	static var VERTEX = "
+		attribute vec2 pos;
+		varying mediump tuv;
+		uniform mat4 mproj;
+		uniform vec4 delta;
+		uniform vec2 size;
+		
+		void main(void) {
+			vec4 p = mproj * delta;
+			p.xy += pos.xy * size * p.z;
+			gl_Position = p;
+			tuv = pos;
+		}
+	";
+	static var FRAGMENT = "
+		varying mediump tuv;
+		uniform vec4 color /*byte4*/;
+		
+		void main(void) {
+			if( 1 - dot(tuv, tuv) < 0 ) discard;
+			gl_FragColor = color;
+		}
+	";
+
+#end
 	
 }
 
-class LineShader extends hxsl.Shader {
+class LineShader extends h3d.impl.Shader {
 
+#if flash
 	static var SRC = {
 		var input : {
 			pos : Float2,
@@ -44,4 +73,16 @@ class LineShader extends hxsl.Shader {
 		}
 	}
 	
+#elseif js
+
+	public var mproj : Matrix;
+	public var start : Vector;
+	public var end : Vector;
+	public var color : Int;
+	
+	static var VERTEX = "TODO";
+	static var FRAGMENT = "TODO";
+	
+#end
+
 }

+ 422 - 0
h3d/impl/Stage3dDriver.hx

@@ -0,0 +1,422 @@
+package h3d.impl;
+import h3d.impl.Driver;
+
+#if flash
+
+@:allow(h3d.impl.Stage3dDriver)
+class VertexWrapper {
+	var vbuf : flash.display3D.VertexBuffer3D;
+	var stride : Int;
+	var written : Bool;
+	var b : MemoryManager.BigBuffer;
+	
+	function new(vbuf, stride) {
+		this.vbuf = vbuf;
+		this.stride = stride;
+	}
+	
+	function finalize( driver : Stage3dDriver ) {
+		if( written ) return;
+		written = true;
+		// fill all the free positions that were unwritten with zeroes (necessary for flash)
+		var f = b.free;
+		while( f != null ) {
+			if( f.count > 0 ) {
+				var mem : UInt = f.count * b.stride * 4;
+				if( driver.empty.length < mem ) driver.empty.length = mem;
+				driver.uploadVertexBytes(b.vbuf, f.pos, f.count, haxe.io.Bytes.ofData(driver.empty), 0);
+			}
+			f = f.next;
+		}
+	}
+
+}
+
+class Stage3dDriver extends Driver {
+	
+	var s3d : flash.display.Stage3D;
+	var ctx : flash.display3D.Context3D;
+	var onCreateCallback : Bool -> Void;
+	
+	var curMatBits : Int;
+	var curShader : hxsl.Shader.ShaderInstance;
+	var curBuffer : VertexBuffer;
+	var curMultiBuffer : Array<h3d.impl.Buffer.BufferOffset>;
+	var curAttributes : Int;
+	var curTextures : Array<h3d.mat.Texture>;
+	var curSamplerBits : Array<Int>;
+	var inTarget : Bool;
+	
+	@:allow(h3d.impl.VertexWrapper)
+	var empty : flash.utils.ByteArray;
+	
+	public function new() {
+		empty = new flash.utils.ByteArray();
+		s3d = flash.Lib.current.stage.stage3Ds[0];
+		curTextures = [];
+	}
+	
+	override function getDriverName(details:Bool) {
+		return ctx == null ? "None" : (details ? ctx.driverInfo : ctx.driverInfo.split(" ")[0]);
+	}
+	
+	override function reset() {
+		curMatBits = -1;
+		curShader = null;
+		curBuffer = null;
+		curMultiBuffer = null;
+		for( i in 0...curAttributes )
+			ctx.setVertexBufferAt(i, null);
+		curAttributes = 0;
+		for( i in 0...curTextures.length )
+			ctx.setTextureAt(i, null);
+		curTextures = [];
+		curSamplerBits = [];
+	}
+	
+	override function init( onCreate, forceSoftware = false ) {
+		this.onCreateCallback = onCreate;
+		s3d.addEventListener(flash.events.Event.CONTEXT3D_CREATE, this.onCreate);
+		s3d.requestContext3D( forceSoftware ? "software" : "auto" );
+	}
+	
+	function onCreate(_) {
+		var old = ctx;
+		if( old != null ) {
+			if( old.driverInfo != "Disposed" ) throw "Duplicate onCreate()";
+			old.dispose();
+			hxsl.Shader.ShaderGlobals.disposeAll();
+			ctx = s3d.context3D;
+			onCreateCallback(true);
+		} else {
+			ctx = s3d.context3D;
+			onCreateCallback(false);
+		}
+	}
+	
+	override function isHardware() {
+		return ctx != null && ctx.driverInfo.toLowerCase().indexOf("software") == -1;
+	}
+	
+	override function resize(width, height, aa) {
+		ctx.configureBackBuffer(width, height, aa);
+	}
+	
+	override function clear(r, g, b, a) {
+		ctx.clear(r, g, b, a);
+	}
+	
+	override function dispose() {
+		s3d.removeEventListener(flash.events.Event.CONTEXT3D_CREATE, onCreate);
+		if( ctx != null ) ctx.dispose();
+		ctx = null;
+	}
+	
+	override function isDisposed() {
+		return ctx == null || ctx.driverInfo == "Disposed";
+	}
+	
+	override function present() {
+		ctx.present();
+	}
+	
+	override function disposeTexture( t : Texture ) {
+		t.dispose();
+	}
+	
+	override function allocVertex( count : Int, stride : Int ) : VertexBuffer {
+		return new VertexWrapper(ctx.createVertexBuffer(count, stride), stride);
+	}
+
+	override function allocIndexes( count : Int ) : IndexBuffer {
+		return ctx.createIndexBuffer(count);
+	}
+	
+	override function allocTexture( t : h3d.mat.Texture ) : Texture {
+		var fmt = switch( t.format ) {
+		case Rgba, Atf:
+			flash.display3D.Context3DTextureFormat.BGRA;
+		case AtfCompressed(alpha):
+			alpha ? flash.display3D.Context3DTextureFormat.COMPRESSED_ALPHA : flash.display3D.Context3DTextureFormat.COMPRESSED;
+		}
+		return if( t.isCubic )
+			ctx.createCubeTexture(t.width, fmt, t.isTarget, t.mipLevels);
+		else
+			ctx.createTexture(t.width, t.height, fmt, t.isTarget, t.mipLevels);
+	}
+
+	override function uploadTextureBitmap( t : h3d.mat.Texture, bmp : hxd.BitmapData, mipLevel : Int, side : Int ) {
+		if( t.isCubic ) {
+			var t = flash.Lib.as(t.t, flash.display3D.textures.CubeTexture);
+			t.uploadFromBitmapData(bmp.toNative(), side, mipLevel);
+		}
+		else {
+			var t = flash.Lib.as(t.t, flash.display3D.textures.Texture);
+			t.uploadFromBitmapData(bmp.toNative(), mipLevel);
+		}
+	}
+
+	override function uploadTextureBytes( t : h3d.mat.Texture, bytes : haxe.io.Bytes, mipLevel : Int, side : Int ) {
+		switch( t.format ) {
+		case Atf, AtfCompressed(_):
+			if( t.isCubic ) {
+				var t = flash.Lib.as(t.t, flash.display3D.textures.CubeTexture);
+				t.uploadCompressedTextureFromByteArray(bytes.getData(), 0);
+			}
+			else {
+				var t = flash.Lib.as(t.t,  flash.display3D.textures.Texture);
+				t.uploadCompressedTextureFromByteArray(bytes.getData(), 0);
+			}
+		default:
+			if( t.isCubic ) {
+				var t = flash.Lib.as(t.t, flash.display3D.textures.CubeTexture);
+				t.uploadFromByteArray(bytes.getData(), 0, side, mipLevel);
+			}
+			else {
+				var t = flash.Lib.as(t.t,  flash.display3D.textures.Texture);
+				t.uploadFromByteArray(bytes.getData(), 0, mipLevel);
+			}
+		}
+	}
+	
+	override function disposeVertex( v : VertexBuffer ) {
+		v.vbuf.dispose();
+		v.b = null;
+	}
+	
+	override function disposeIndexes( i : IndexBuffer ) {
+		i.dispose();
+	}
+	
+	override function setDebug( d : Bool ) {
+		if( ctx != null ) ctx.enableErrorChecking = d && isHardware();
+	}
+	
+	override function uploadVertexBuffer( v : VertexBuffer, startVertex : Int, vertexCount : Int, buf : hxd.FloatBuffer, bufPos : Int ) {
+		var data = buf.getNative();
+		v.vbuf.uploadFromVector( bufPos == 0 ? data : data.slice(bufPos, vertexCount * v.stride + bufPos), startVertex, vertexCount );
+	}
+
+	override function uploadVertexBytes( v : VertexBuffer, startVertex : Int, vertexCount : Int, bytes : haxe.io.Bytes, bufPos : Int ) {
+		v.vbuf.uploadFromByteArray( bytes.getData(), bufPos, startVertex, vertexCount );
+	}
+
+	override function uploadIndexesBuffer( i : IndexBuffer, startIndice : Int, indiceCount : Int, buf : hxd.IndexBuffer, bufPos : Int ) {
+		var data = buf.getNative();
+		i.uploadFromVector( bufPos == 0 ? data : data.slice(bufPos, indiceCount + bufPos), startIndice, indiceCount );
+	}
+
+	override function uploadIndexesBytes( i : IndexBuffer, startIndice : Int, indiceCount : Int, buf : haxe.io.Bytes, bufPos : Int ) {
+		i.uploadFromByteArray(buf.getData(), bufPos, startIndice, indiceCount );
+	}
+	
+	override function selectMaterial( mbits : Int ) {
+		var diff = curMatBits ^ mbits;
+		if( diff != 0 ) {
+			if( curMatBits < 0 || diff&3 != 0 )
+				ctx.setCulling(FACE[mbits&3]);
+			if( curMatBits < 0 || diff & (0xFF << 6) != 0 )
+				ctx.setBlendFactors(BLEND[(mbits>>6)&15], BLEND[(mbits>>10)&15]);
+			if( curMatBits < 0 || diff & (15 << 2) != 0 )
+				ctx.setDepthTest((mbits >> 2) & 1 == 1, COMPARE[(mbits>>3)&7]);
+			if( curMatBits < 0 || diff & (15 << 14) != 0 )
+				ctx.setColorMask((mbits >> 14) & 1 != 0, (mbits >> 14) & 2 != 0, (mbits >> 14) & 4 != 0, (mbits >> 14) & 8 != 0);
+			curMatBits = mbits;
+		}
+	}
+
+	override function selectShader( shader : Shader ) {
+		var shaderChanged = false;
+		var s = shader.getInstance();
+		if( s.program == null ) {
+			s.program = ctx.createProgram();
+			var vdata = s.vertexBytes.getData();
+			var fdata = s.fragmentBytes.getData();
+			vdata.endian = flash.utils.Endian.LITTLE_ENDIAN;
+			fdata.endian = flash.utils.Endian.LITTLE_ENDIAN;
+			s.program.upload(vdata, fdata);
+			curShader = null; // in case we had the same shader and it was disposed
+		}
+		if( s != curShader ) {
+			ctx.setProgram(s.program);
+			shaderChanged = true;
+			s.varsChanged = true;
+			// unbind extra textures
+			var tcount : Int = s.textures.length;
+			while( curTextures.length > tcount ) {
+				curTextures.pop();
+				ctx.setTextureAt(curTextures.length, null);
+			}
+			// force remapping of vertex buffer
+			curBuffer = null;
+			curMultiBuffer = null;
+			curShader = s;
+		}
+		if( s.varsChanged ) {
+			s.varsChanged = false;
+			ctx.setProgramConstantsFromVector(flash.display3D.Context3DProgramType.VERTEX, 0, s.vertexVars.toData());
+			ctx.setProgramConstantsFromVector(flash.display3D.Context3DProgramType.FRAGMENT, 0, s.fragmentVars.toData());
+			for( i in 0...s.textures.length ) {
+				var t = s.textures[i];
+				if( t == null || t.isDisposed() )
+					t = h2d.Tile.fromColor(0xFFFF00FF).getTexture();
+				var cur = curTextures[i];
+				if( t != cur ) {
+					ctx.setTextureAt(i, t.t);
+					curTextures[i] = t;
+				}
+				// if we have set one of the texture flag manually or if the shader does not configure the texture flags
+				if( !t.hasDefaultFlags() || !s.texHasConfig[s.textureMap[i]] ) {
+					if( cur == null || t.bits != curSamplerBits[i] ) {
+						ctx.setSamplerStateAt(i, WRAP[t.wrap.getIndex()], FILTER[t.filter.getIndex()], MIP[t.mipMap.getIndex()]);
+						curSamplerBits[i] = t.bits;
+					}
+				} else {
+					// the texture flags has been set by the shader, so we are in an unkown state
+					curSamplerBits[i] = -1;
+				}
+			}
+		}
+		return shaderChanged;
+	}
+	
+	override function selectBuffer( v : VertexBuffer ) {
+		if( v == curBuffer )
+			return;
+		curBuffer = v;
+		curMultiBuffer = null;
+		if( v.stride < curShader.stride )
+			throw "Buffer stride (" + v.stride + ") and shader stride (" + curShader.stride + ") mismatch";
+		if( !v.written )
+			v.finalize(this);
+		var pos = 0, offset = 0;
+		var bits = curShader.bufferFormat;
+		while( offset < curShader.stride ) {
+			var size = bits & 7;
+			ctx.setVertexBufferAt(pos++, v.vbuf, offset, FORMAT[size]);
+			offset += size == 0 ? 1 : size;
+			bits >>= 3;
+		}
+		for( i in pos...curAttributes )
+			ctx.setVertexBufferAt(i, null);
+		curAttributes = pos;
+	}
+	
+	override function getShaderInputNames() {
+		return curShader.bufferNames;
+	}
+	
+	override function selectMultiBuffers( buffers : Array<Buffer.BufferOffset> ) {
+		// select the multiple buffers elements
+		var changed = curMultiBuffer == null || curMultiBuffer.length != buffers.length;
+		if( !changed )
+			for( i in 0...curMultiBuffer.length )
+				if( buffers[i] != curMultiBuffer[i] ) {
+					changed = true;
+					break;
+				}
+		if( changed ) {
+			var pos = 0, offset = 0;
+			var bits = curShader.bufferFormat;
+			while( offset < curShader.stride ) {
+				var size = bits & 7;
+				var b = buffers[pos];
+				if( b.b.next != null )
+					throw "Buffer is split";
+				if( !b.b.b.vbuf.written )
+					b.b.b.vbuf.finalize(this);
+				ctx.setVertexBufferAt(pos, b.b.b.vbuf.vbuf, b.offset, FORMAT[size]);
+				offset += size == 0 ? 1 : size;
+				bits >>= 3;
+				pos++;
+			}
+			for( i in pos...curAttributes )
+				ctx.setVertexBufferAt(i, null);
+			curAttributes = pos;
+			curBuffer = null;
+			curMultiBuffer = buffers;
+		}
+	}
+	
+	override function draw( ibuf : IndexBuffer, startIndex : Int, ntriangles : Int ) {
+		ctx.drawTriangles(ibuf, startIndex, ntriangles);
+	}
+
+	override function setRenderZone( x : Int, y : Int, width : Int, height : Int ) {
+		if( x == 0 && y == 0 && width < 0 && height < 0 )
+			ctx.setScissorRectangle(null);
+		else
+			ctx.setScissorRectangle(new flash.geom.Rectangle(x, y, width, height));
+	}
+
+	override function setRenderTarget( tex : Null<Texture>, useDepth : Bool, clearColor : Int ) {
+		if( tex == null ) {
+			ctx.setRenderToBackBuffer();
+			inTarget = false;
+		} else {
+			if( inTarget )
+				throw "Calling setTarget() while already set";
+			ctx.setRenderToTexture(tex, useDepth);
+			inTarget = true;
+			reset();
+			ctx.clear( ((clearColor>>16)&0xFF)/255 , ((clearColor>>8)&0xFF)/255, (clearColor&0xFF)/255, ((clearColor>>>24)&0xFF)/255);
+		}
+	}
+	
+	static var BLEND = [
+		flash.display3D.Context3DBlendFactor.ONE,
+		flash.display3D.Context3DBlendFactor.ZERO,
+		flash.display3D.Context3DBlendFactor.SOURCE_ALPHA,
+		flash.display3D.Context3DBlendFactor.SOURCE_COLOR,
+		flash.display3D.Context3DBlendFactor.DESTINATION_ALPHA,
+		flash.display3D.Context3DBlendFactor.DESTINATION_COLOR,
+		flash.display3D.Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA,
+		flash.display3D.Context3DBlendFactor.ONE_MINUS_SOURCE_COLOR,
+		flash.display3D.Context3DBlendFactor.ONE_MINUS_DESTINATION_ALPHA,
+		flash.display3D.Context3DBlendFactor.ONE_MINUS_DESTINATION_COLOR
+	];
+
+	static var FACE = [
+		flash.display3D.Context3DTriangleFace.NONE,
+		flash.display3D.Context3DTriangleFace.BACK,
+		flash.display3D.Context3DTriangleFace.FRONT,
+		flash.display3D.Context3DTriangleFace.FRONT_AND_BACK,
+	];
+
+	static var COMPARE = [
+		flash.display3D.Context3DCompareMode.ALWAYS,
+		flash.display3D.Context3DCompareMode.NEVER,
+		flash.display3D.Context3DCompareMode.EQUAL,
+		flash.display3D.Context3DCompareMode.NOT_EQUAL,
+		flash.display3D.Context3DCompareMode.GREATER,
+		flash.display3D.Context3DCompareMode.GREATER_EQUAL,
+		flash.display3D.Context3DCompareMode.LESS,
+		flash.display3D.Context3DCompareMode.LESS_EQUAL,
+	];
+
+	static var FORMAT = [
+		flash.display3D.Context3DVertexBufferFormat.BYTES_4,
+		flash.display3D.Context3DVertexBufferFormat.FLOAT_1,
+		flash.display3D.Context3DVertexBufferFormat.FLOAT_2,
+		flash.display3D.Context3DVertexBufferFormat.FLOAT_3,
+		flash.display3D.Context3DVertexBufferFormat.FLOAT_4,
+	];
+	
+	static var WRAP = [
+		flash.display3D.Context3DWrapMode.CLAMP,
+		flash.display3D.Context3DWrapMode.REPEAT,
+	];
+	
+	static var FILTER = [
+		flash.display3D.Context3DTextureFilter.NEAREST,
+		flash.display3D.Context3DTextureFilter.LINEAR,
+	];
+	
+	static var MIP = [
+		flash.display3D.Context3DMipFilter.MIPNONE,
+		flash.display3D.Context3DMipFilter.MIPNEAREST,
+		flash.display3D.Context3DMipFilter.MIPLINEAR,
+	];
+	
+}
+#end

+ 474 - 0
h3d/impl/WebglDriver.hx

@@ -0,0 +1,474 @@
+package h3d.impl;
+import h3d.impl.Driver;
+
+#if js
+
+private typedef GL = js.html.webgl.GL;
+
+@:access(h3d.impl.Shader)
+class WebglDriver extends Driver {
+
+	var canvas : js.html.CanvasElement;
+	var gl : js.html.webgl.RenderingContext;
+	
+	var curAttribs : Int;
+	var curShader : Shader.ShaderInstance;
+	var curMatBits : Int;
+	
+	public function new() {
+		curAttribs = 0;
+		canvas = cast js.Browser.document.getElementById("webgl");
+		if( canvas == null ) throw "Canvas #webgl not found";
+		gl = canvas.getContextWebGL();
+		if( gl == null ) throw "Could not acquire GL context";
+		// debug if webgl_debug.js is included
+		untyped if( __js__('typeof')(WebGLDebugUtils) != "undefined" ) gl = untyped WebGLDebugUtils.makeDebugContext(gl);
+		curMatBits = -1;
+		selectMaterial(0);
+	}
+	
+	override function reset() {
+		curShader = null;
+		gl.useProgram(null);
+	}
+	
+	override function selectMaterial( mbits : Int ) {
+		var diff = curMatBits ^ mbits;
+		if( diff == 0 )
+			return;
+		if( diff & 3 != 0 ) {
+			if( mbits & 3 == 0 )
+				gl.disable(GL.CULL_FACE);
+			else {
+				if( curMatBits & 3 == 0 ) gl.enable(GL.CULL_FACE);
+				gl.cullFace(FACES[mbits&3]);
+			}
+		}
+		if( diff & (0xFF << 6) != 0 ) {
+			var src = (mbits >> 6) & 15;
+			var dst = (mbits >> 10) & 15;
+			if( src == 0 && dst == 1 )
+				gl.disable(GL.BLEND);
+			else {
+				if( curMatBits < 0 || (curMatBits >> 6) & 0xFF == 0x10 ) gl.enable(GL.BLEND);
+				gl.blendFunc(BLEND[src], BLEND[dst]);
+			}
+		}
+	
+		if( diff & (15 << 2) != 0 ) {
+			var write = (mbits >> 2) & 1 == 1;
+			if( curMatBits < 0 || diff & 4 != 0 ) gl.depthMask(write);
+			var cmp = (mbits >> 3) & 7;
+			if( cmp == 0 )
+				gl.disable(GL.DEPTH_TEST);
+			else {
+				if( curMatBits < 0 || (curMatBits >> 3) & 7 == 0 ) gl.enable(GL.DEPTH_TEST);
+				gl.depthFunc(COMPARE[cmp]);
+			}
+		}
+			
+		if( diff & (15 << 14) != 0 )
+			gl.colorMask((mbits >> 14) & 1 != 0, (mbits >> 14) & 2 != 0, (mbits >> 14) & 4 != 0, (mbits >> 14) & 8 != 0);
+			
+		curMatBits = mbits;
+	}
+	
+	override function clear( r : Float, g : Float, b : Float, a : Float ) {
+		gl.clearColor(r, g, b, a);
+		gl.clearDepth(1);
+		gl.clear(GL.COLOR_BUFFER_BIT|GL.DEPTH_BUFFER_BIT);
+	}
+	
+	override function resize(width, height, aa:Int) {
+		canvas.width = width;
+		canvas.height = height;
+		gl.viewport(0, 0, width, height);
+	}
+	
+	override function allocTexture( t : h3d.mat.Texture ) : Texture {
+		var tt = gl.createTexture();
+		gl.bindTexture(GL.TEXTURE_2D, tt);
+		gl.texImage2D(GL.TEXTURE_2D, 0, GL.RGBA, t.width, t.height, 0, GL.RGBA, GL.UNSIGNED_BYTE, null);
+		gl.bindTexture(GL.TEXTURE_2D, null);
+		return tt;
+	}
+	
+	override function allocVertex( count : Int, stride : Int ) : VertexBuffer {
+		var b = gl.createBuffer();
+		gl.bindBuffer(GL.ARRAY_BUFFER, b);
+		gl.bufferData(GL.ARRAY_BUFFER, count * stride * 4, GL.STATIC_DRAW);
+		gl.bindBuffer(GL.ARRAY_BUFFER, null);
+		untyped b.stride = stride;
+		return b;
+	}
+	
+	override function allocIndexes( count : Int ) : IndexBuffer {
+		var b = gl.createBuffer();
+		gl.bindBuffer(GL.ELEMENT_ARRAY_BUFFER, b);
+		gl.bufferData(GL.ELEMENT_ARRAY_BUFFER, count * 2, GL.STATIC_DRAW);
+		gl.bindBuffer(GL.ELEMENT_ARRAY_BUFFER, null);
+		return b;
+	}
+
+	override function disposeTexture( t : Texture ) {
+		gl.deleteTexture(t);
+	}
+
+	override function disposeIndexes( i : IndexBuffer ) {
+		gl.deleteBuffer(i);
+	}
+	
+	override function disposeVertex( v : VertexBuffer ) {
+		gl.deleteBuffer(v);
+	}
+	
+	override function uploadTextureBytes( t : h3d.mat.Texture, bytes : haxe.io.Bytes, mipLevel : Int, side : Int ) {
+		gl.bindTexture(GL.TEXTURE_2D, t.t);
+		var pixels = new js.html.Uint8Array(bytes.getData());
+		gl.texImage2D(GL.TEXTURE_2D, mipLevel, GL.RGBA, t.width, t.height, 0, GL.RGBA, GL.UNSIGNED_BYTE, pixels);
+		gl.bindTexture(GL.TEXTURE_2D, null);
+	}
+	
+	override function uploadVertexBuffer( v : VertexBuffer, startVertex : Int, vertexCount : Int, buf : hxd.FloatBuffer, bufPos : Int ) {
+		var stride : Int = untyped v.stride;
+		var buf = new js.html.Float32Array(buf.getNative());
+		gl.bindBuffer(GL.ARRAY_BUFFER, v);
+		gl.bufferSubData(GL.ARRAY_BUFFER, startVertex * stride * 4, new js.html.Float32Array(buf.buffer, bufPos, vertexCount * stride));
+		gl.bindBuffer(GL.ARRAY_BUFFER, null);
+	}
+
+	override function uploadVertexBytes( v : VertexBuffer, startVertex : Int, vertexCount : Int, buf : haxe.io.Bytes, bufPos : Int ) {
+		var stride : Int = untyped v.stride;
+		var buf = new js.html.Uint8Array(buf.getData());
+		gl.bindBuffer(GL.ARRAY_BUFFER, v);
+		gl.bufferSubData(GL.ARRAY_BUFFER, startVertex * stride * 4, new js.html.Uint8Array(buf.buffer, bufPos, vertexCount * stride * 4));
+		gl.bindBuffer(GL.ARRAY_BUFFER, null);
+	}
+
+	override function uploadIndexesBuffer( i : IndexBuffer, startIndice : Int, indiceCount : Int, buf : hxd.IndexBuffer, bufPos : Int ) {
+		var buf = new js.html.Uint16Array(buf.getNative());
+		gl.bindBuffer(GL.ELEMENT_ARRAY_BUFFER, i);
+		gl.bufferSubData(GL.ELEMENT_ARRAY_BUFFER, startIndice * 2, new js.html.Uint16Array(buf.buffer, bufPos, indiceCount));
+		gl.bindBuffer(GL.ELEMENT_ARRAY_BUFFER, null);
+	}
+
+	override function uploadIndexesBytes( i : IndexBuffer, startIndice : Int, indiceCount : Int, buf : haxe.io.Bytes , bufPos : Int ) {
+		var buf = new js.html.Uint8Array(buf.getData());
+		gl.bindBuffer(GL.ELEMENT_ARRAY_BUFFER, i);
+		gl.bufferSubData(GL.ELEMENT_ARRAY_BUFFER, startIndice * 2, new js.html.Uint8Array(buf.buffer, bufPos, indiceCount * 2));
+		gl.bindBuffer(GL.ELEMENT_ARRAY_BUFFER, null);
+	}
+	
+	function decodeType( t : String ) : Shader.ShaderType {
+		return switch( t ) {
+		case "float": Float;
+		case "vec2": Vec2;
+		case "vec3": Vec3;
+		case "vec4": Vec4;
+		case "mat4": Mat4;
+		default: throw "Unknown type " + t;
+		}
+	}
+	
+	function decodeTypeInt( t : Int ) : Shader.ShaderType {
+		return switch( t ) {
+		case GL.SAMPLER_2D:	Tex2d;
+		case GL.SAMPLER_CUBE: TexCube;
+		case GL.FLOAT: Float;
+		case GL.FLOAT_VEC2: Vec2;
+		case GL.FLOAT_VEC3: Vec3;
+		case GL.FLOAT_VEC4: Vec4;
+		case GL.FLOAT_MAT2: Mat2;
+		case GL.FLOAT_MAT3: Mat3;
+		case GL.FLOAT_MAT4: Mat4;
+		default:
+			gl.pixelStorei(t, 0); // get DEBUG value
+			throw "Unknown type " + t;
+		}
+	}
+	
+	function typeSize( t : Shader.ShaderType ) {
+		return switch( t ) {
+		case Float, Byte4: 1;
+		case Vec2: 2;
+		case Vec3: 3;
+		case Vec4: 4;
+		case Mat2: 4;
+		case Mat3: 9;
+		case Mat4: 16;
+		case Tex2d, TexCube, Struct(_), Index(_): throw "Unexpected " + t;
+		}
+	}
+	
+	function buildShaderInstance( shader : Shader ) {
+		var cl = Type.getClass(shader);
+		function compileShader(type) {
+			var vertex = type == GL.VERTEX_SHADER;
+			var name = vertex ? "VERTEX" : "FRAGMENT";
+			var code = Reflect.field(cl, name);
+			if( code == null ) throw "Missing " + Type.getClassName(cl) + "." + name + " shader source";
+			var cst = shader.getConstants(vertex);
+			code = StringTools.trim(cst + code);
+			// replace haxe-like #if/#else/#end by GLSL ones
+			code = ~/#if ([A-Za-z0-9_]+)/g.replace(code, "#if defined($1)");
+			code = ~/#elseif ([A-Za-z0-9_]+)/g.replace(code, "#elif defined($1)");
+			code = code.split("#end").join("#endif");
+			var s = gl.createShader(type);
+			gl.shaderSource(s, code);
+			gl.compileShader(s);
+			if( !gl.getShaderParameter(s, GL.COMPILE_STATUS) ) {
+				var log = gl.getShaderInfoLog(s);
+				var line = code.split("\n")[Std.parseInt(log.substr(9)) - 1];
+				if( line == null ) line = "" else line = "(" + StringTools.trim(line) + ")";
+				throw "An error occurred compiling the shaders: " + log + line;
+			}
+			return s;
+		}
+		var vs = compileShader(GL.VERTEX_SHADER);
+		var fs = compileShader(GL.FRAGMENT_SHADER);
+		
+		var p = gl.createProgram();
+		gl.attachShader(p, vs);
+		gl.attachShader(p, fs);
+		gl.linkProgram(p);
+		if( !gl.getProgramParameter(p, GL.LINK_STATUS) ) {
+			var log = gl.getProgramInfoLog(p);
+			throw "Program linkage failure: "+log;
+		}
+	
+		var inst = new Shader.ShaderInstance();
+			
+		var nattr = gl.getProgramParameter(p, GL.ACTIVE_ATTRIBUTES);
+		inst.attribs = [];
+		
+		var amap = new Map();
+		for( k in 0...nattr ) {
+			var inf = gl.getActiveAttrib(p, k);
+			amap.set(inf.name, { index : k, inf : inf });
+		}
+		
+		
+		var code = gl.getShaderSource(vs);
+
+		// remove (and save) all #define's
+		var rdef = ~/#define ([A-Za-z0-9_]+)/;
+		var defs = new Map();
+		while( rdef.match(code) ) {
+			defs.set(rdef.matched(1), true);
+			code = rdef.matchedLeft() + rdef.matchedRight();
+		}
+		
+		// remove parts of the codes that are undefined
+		var rif = ~/#if defined\(([A-Za-z0-9_]+)\)([^#]+)#endif/;
+		while( rif.match(code) ) {
+			if( defs.get(rif.matched(1)) )
+				code = rif.matchedLeft() + rif.matched(2) + rif.matchedRight();
+			else
+				code = rif.matchedLeft() + rif.matchedRight();
+		}
+		
+		// extract attributes from code (so we know the offset and stride)
+		var r = ~/attribute[ \t\r\n]+([A-Za-z0-9_]+)[ \t\r\n]+([A-Za-z0-9_]+)/;
+		var offset = 0;
+		var ccode = code;
+		while( r.match(ccode) ) {
+			var aname = r.matched(2);
+			var atype = decodeType(r.matched(1));
+			var a = amap.get(aname);
+			var size = typeSize(atype);
+			if( a != null )
+				inst.attribs.push( { name : aname, type : atype, etype : GL.FLOAT, size : size, index : a.index, offset : offset } );
+			offset += size;
+			ccode = r.matchedRight();
+		}
+		inst.stride = offset;
+		
+		// list uniforms needed by shader
+		var allCode = code + gl.getShaderSource(fs);
+		var nuni = gl.getProgramParameter(p, GL.ACTIVE_UNIFORMS);
+		inst.uniforms = [];
+		var texIndex = -1;
+		var r_array = ~/\[([0-9]+)\]$/;
+		for( k in 0...nuni ) {
+			var inf = gl.getActiveUniform(p, k);
+			if( inf.name.substr(0, 6) == "webgl_" )
+				continue; // skip native uniforms
+			var t = decodeTypeInt(inf.type);
+			switch( t ) {
+			case Tex2d, TexCube:
+				texIndex++;
+			case Vec4:
+				var r = new EReg(inf.name + "[ \\t]*\\/\\*([A-Za-z0-9_]+)\\*\\/", "");
+				if( r.match(allCode) )
+					switch( r.matched(1) ) {
+					case "byte4":
+						t = Byte4;
+					default:
+					}
+			default:
+			}
+			var name = inf.name;
+			while( true ) {
+				if( r_array.match(name) ) {
+					name = r_array.matchedLeft();
+					t = Index(Std.parseInt(r_array.matched(1)), t);
+					continue;
+				}
+				var c = name.lastIndexOf(".");
+				if( c > 0 ) {
+					var field = name.substr(c + 1);
+					name = name.substr(0, c);
+					t = Struct(field, t);
+				}
+				break;
+			}
+			inst.uniforms.push( {
+				name : name,
+				type : t,
+				loc : gl.getUniformLocation(p, inf.name),
+				index : texIndex,
+			});
+		}
+		inst.program = p;
+		return inst;
+	}
+
+	override function selectShader( shader : Shader ) : Bool {
+		var change = false;
+		if( shader.instance == null )
+			shader.instance = buildShaderInstance(shader);
+		if( shader.instance != curShader ) {
+			curShader = shader.instance;
+			gl.useProgram(curShader.program);
+			for( i in curAttribs...curShader.attribs.length ) {
+				gl.enableVertexAttribArray(i);
+				curAttribs++;
+			}
+			while( curAttribs > curShader.attribs.length )
+				gl.disableVertexAttribArray(--curAttribs);
+			change = true;
+		}
+			
+		
+		for( u in curShader.uniforms ) {
+			var val : Dynamic = Reflect.field(shader, u.name);
+			if( val == null ) throw "Missing shader value " + u.name;
+			setUniform(val, u, u.type);
+		}
+		
+		return change;
+	}
+	
+	function setUniform( val : Dynamic, u : Shader.Uniform, t : Shader.ShaderType ) {
+		switch( t ) {
+		case Mat4:
+			var m : Matrix = val;
+			gl.uniformMatrix4fv(u.loc, false, new js.html.Float32Array(m.getFloats()));
+		case Tex2d:
+			var t : h3d.mat.Texture = val;
+			gl.activeTexture(GL.TEXTURE0 + u.index);
+			gl.bindTexture(GL.TEXTURE_2D, t.t);
+			var flags = TFILTERS[Type.enumIndex(t.mipMap)][Type.enumIndex(t.filter)];
+			gl.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_MAG_FILTER, flags[0]);
+			gl.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_MIN_FILTER, flags[1]);
+			gl.uniform1i(u.loc, u.index);
+		case Float:
+			gl.uniform1f(u.loc, val);
+		case Vec2:
+			var v : h3d.Vector = val;
+			gl.uniform2f(u.loc, v.x, v.y);
+		case Vec3:
+			var v : h3d.Vector = val;
+			gl.uniform3f(u.loc, v.x, v.y, v.z);
+		case Vec4:
+			var v : h3d.Vector = val;
+			gl.uniform4f(u.loc, v.x, v.y, v.z, v.w);
+		case Struct(field, t):
+			var v = Reflect.field(val, field);
+			if( v == null ) throw "Missing shader field " + field;
+			setUniform(v, u, t);
+		case Index(index, t):
+			var v = val[index];
+			if( v == null ) throw "Missing shader index " + index;
+			setUniform(v, u, t);
+		case Byte4:
+			var v : Int = val;
+			gl.uniform4f(u.loc, ((v >> 16) & 0xFF) / 255, ((v >> 8) & 0xFF) / 255, (v & 0xFF) / 255, (v >>> 24) / 255);
+		default:
+			throw "Unsupported uniform " + u.type;
+		}
+		
+	}
+	
+	override function selectBuffer( v : VertexBuffer ) {
+		var stride : Int = untyped v.stride;
+		if( stride < curShader.stride )
+			throw "Buffer stride (" + stride + ") and shader stride (" + curShader.stride + ") mismatch";
+		gl.bindBuffer(GL.ARRAY_BUFFER, v);
+		for( a in curShader.attribs )
+			gl.vertexAttribPointer(a.index, a.size, a.etype, false, stride * 4, a.offset * 4);
+	}
+	
+	override function draw( ibuf : IndexBuffer, startIndex : Int, ntriangles : Int ) {
+		gl.bindBuffer(GL.ELEMENT_ARRAY_BUFFER, ibuf);
+		gl.drawElements(GL.TRIANGLES, ntriangles * 3, GL.UNSIGNED_SHORT, startIndex * 2);
+		gl.bindBuffer(GL.ELEMENT_ARRAY_BUFFER, null);
+	}
+	
+	override function present() {
+		gl.finish();
+	}
+
+	override function isDisposed() {
+		return false;
+	}
+
+	override function init( onCreate : Bool -> Void, forceSoftware = false ) {
+		haxe.Timer.delay(onCreate.bind(false), 1);
+	}
+	
+	static var TFILTERS = [
+		[[GL.NEAREST,GL.NEAREST],[GL.LINEAR,GL.LINEAR]],
+		[[GL.NEAREST,GL.NEAREST_MIPMAP_NEAREST],[GL.LINEAR,GL.LINEAR_MIPMAP_NEAREST]],
+		[[GL.NEAREST,GL.NEAREST_MIPMAP_LINEAR],[GL.LINEAR,GL.LINEAR_MIPMAP_LINEAR]],
+	];
+	
+	static var FACES = [
+		0,
+		GL.FRONT, // front/back reversed wrt stage3d
+		GL.BACK,
+		GL.FRONT_AND_BACK,
+	];
+	
+	static var BLEND = [
+		GL.ZERO,
+		GL.ONE,
+		GL.SRC_ALPHA,
+		GL.SRC_COLOR,
+		GL.DST_ALPHA,
+		GL.DST_COLOR,
+		GL.ONE_MINUS_SRC_ALPHA,
+		GL.ONE_MINUS_SRC_COLOR,
+		GL.ONE_MINUS_DST_ALPHA,
+		GL.ONE_MINUS_DST_COLOR,
+		GL.CONSTANT_COLOR,
+		GL.CONSTANT_ALPHA,
+		GL.ONE_MINUS_CONSTANT_COLOR,
+		GL.ONE_MINUS_CONSTANT_ALPHA,
+		GL.SRC_ALPHA_SATURATE,
+	];
+	
+	static var COMPARE = [
+		GL.ALWAYS,
+		GL.NEVER,
+		GL.EQUAL,
+		GL.NOTEQUAL,
+		GL.GREATER,
+		GL.GEQUAL,
+		GL.LESS,
+		GL.LEQUAL,
+	];
+
+}
+
+#end

+ 6 - 0
h3d/mat/Data.hx

@@ -18,6 +18,12 @@ enum Blend {
 	OneMinusSrcColor;
 	OneMinusDstAlpha;
 	OneMinusDstColor;
+	// only supported on WebGL
+	ConstantColor;
+	ConstantAlpha;
+	OneMinusConstantColor;
+	OneMinusConstantAlpha;
+	SrcAlphaSaturate;
 }
 
 enum Compare {

+ 4 - 1
h3d/mat/Material.hx

@@ -10,7 +10,7 @@ class Material {
 	public var blendSrc(default,set) : Blend;
 	public var blendDst(default,set) : Blend;
 	public var colorMask(default,set) : Int;
-	public var shader : hxsl.Shader;
+	public var shader : h3d.impl.Shader;
 	public var renderPass : Int;
 	
 	public function new(shader) {
@@ -25,6 +25,9 @@ class Material {
 		this.colorMask = 15;
 	}
 	
+	public function setup( ctx : h3d.scene.RenderContext ) {
+	}
+	
 	public function blend(src, dst) {
 		blendSrc = src;
 		blendDst = dst;

+ 275 - 7
h3d/mat/MeshMaterial.hx

@@ -13,8 +13,9 @@ typedef ShadowMap = {
 	var texture : Texture;
 }
 
-private class MeshShader extends hxsl.Shader {
+private class MeshShader extends h3d.impl.Shader {
 	
+#if flash
 	static var SRC = {
 
 		var input : {
@@ -88,10 +89,10 @@ private class MeshShader extends hxsl.Shader {
 			if( lightSystem != null ) {
 				// calculate normal
 				var n = input.normal;
-				if( mpos != null ) n *= mpos;
+				if( mpos != null ) n *= mpos; // WRONG : we should use m33 only
 				if( hasSkin ) {
 					n = n * input.weights.x * skinMatrixes[input.indexes.x * (255 * 3)] + n * input.weights.y * skinMatrixes[input.indexes.y * (255 * 3)] + n * input.weights.z * skinMatrixes[input.indexes.z * (255 * 3)];
-					if( mpos != null ) n = n * mposInv;
+					if( mpos != null ) n = n * mposInv; // should be the 3x3 part only
 				}
 				var col = lightSystem.ambient;
 				n = n.normalize();
@@ -148,6 +149,269 @@ private class MeshShader extends hxsl.Shader {
 		}
 		
 	}
+#else
+
+	public var maxSkinMatrixes : Int = 34;
+	public var hasVertexColor : Bool;
+	public var hasVertexColorAdd : Bool;
+	public var lightSystem(default, set) : LightSystem;
+	public var hasSkin : Bool;
+	public var hasZBias : Bool;
+	public var hasShadowMap : Bool;
+	public var killAlpha : Bool;
+	public var hasAlphaMap : Bool;
+	public var hasBlend : Bool;
+	public var hasGlow : Bool;
+	
+	var lights : {
+		ambient : h3d.Vector,
+		dirsDir : Array<h3d.Vector>,
+		dirsColor : Array<h3d.Vector>,
+		pointsPos : Array<h3d.Vector>,
+		pointsColor : Array<h3d.Vector>,
+		pointsAtt : Array<h3d.Vector>,
+	};
+	
+	function set_lightSystem(l) {
+		this.lightSystem = l;
+		lights = {
+			ambient : l.ambient,
+			dirsDir : [for( l in l.dirs ) l.dir],
+			dirsColor : [for( l in l.dirs ) l.color],
+			pointsPos : [for( p in l.points ) p.pos],
+			pointsColor : [for( p in l.points ) p.color],
+			pointsAtt : [for( p in l.points ) p.att],
+		};
+		return l;
+	}
+	
+	override function getConstants(vertex) {
+		var cst = [];
+		if( hasVertexColor ) cst.push("#define hasVertexColor");
+		if( hasVertexColorAdd ) cst.push("#define hasVertexColorAdd");
+		if( fog != null ) cst.push("#define hasFog");
+		if( hasBlend ) cst.push("#define hasBlend");
+		if( hasShadowMap ) cst.push("#define hasShadowMap");
+		if( lightSystem != null ) {
+			cst.push("#define hasLightSystem");
+			cst.push("const int numDirLights = " + lightSystem.dirs.length+";");
+			cst.push("const int numPointLights = " + lightSystem.points.length+";");
+		}
+		if( vertex ) {
+			if( mpos != null ) cst.push("#define hasPos");
+			if( hasSkin ) {
+				cst.push("#define hasSkin");
+				cst.push("const int maxSkinMatrixes = " + maxSkinMatrixes+";");
+			}
+			if( uvScale != null ) cst.push("#define hasUVScale");
+			if( uvDelta != null ) cst.push("#define hasUVDelta");
+			if( hasZBias ) cst.push("#define hasZBias");
+		} else {
+			if( killAlpha ) cst.push("#define killAlpha");
+			if( colorAdd != null ) cst.push("#define hasColorAdd");
+			if( colorMul != null ) cst.push("#define hasColorMul");
+			if( colorMatrix != null ) cst.push("#define hasColorMatrix");
+			if( hasAlphaMap ) cst.push("#define hasAlphaMap");
+			if( hasGlow ) cst.push("#define hasGlow");
+			if( hasVertexColorAdd || lightSystem != null ) cst.push("#define hasFragColor");
+		}
+		return cst.join("\n");
+	}
+
+	static var VERTEX = "
+	
+		attribute vec3 pos;
+		attribute vec2 uv;
+		#if hasLightSystem
+		attribute vec3 normal;
+		#end
+		#if hasVertexColor
+		attribute vec3 color;
+		#end
+		#if hasVertexColorAdd
+		attribute vec3 colorAdd;
+		#end
+		#if hasBlend
+		attribute float blending;
+		#end
+		#if hasSkin
+		uniform mat4 skinMatrixes[maxSkinMatrixes];
+		#end
+
+		uniform mat4 mpos;
+		uniform mat4 mproj;
+		uniform float zBias;
+		uniform vec2 uvScale;
+		uniform vec2 uvDelta;
+		
+		// we can't use Array of structures in GLSL
+		struct LightSystem {
+			vec3 ambient;
+			vec3 dirsDir[numDirLights];
+			vec3 dirsColor[numDirLights];
+			vec3 pointsPos[numPointLights];
+			vec3 pointsColor[numPointLights];
+			vec3 pointsAtt[numPointLights];
+		};
+		uniform LightSystem lights;
+			
+		uniform mat4 shadowLightProj;
+		uniform mat4 shadowLightCenter;
+
+		uniform vec4 fog;
+		
+		varying lowp vec2 tuv;
+		varying lowp vec3 tcolor;
+		varying lowp vec3 acolor;
+		varying mediump float talpha;
+		varying mediump float tblend;
+		varying mediump vec4 tshadowPos;
+		
+		uniform mat3 mposInv;
+
+		void main(void) {
+			vec4 tpos = vec4(pos, 1.0);
+			#if hasSkin
+//				tpos.xyz = tpos * input.weights.x * skinMatrixes[input.indexes.x * (255 * 3)] + tpos * input.weights.y * skinMatrixes[input.indexes.y * (255 * 3)] + tpos * input.weights.z * skinMatrixes[input.indexes.z * (255 * 3)];
+			#elseif hasPos
+				tpos = mpos * tpos;
+			#end
+			vec4 ppos = mproj * tpos;
+			#if hasZBias
+				ppos.z += zBias;
+			#end
+			gl_Position = ppos;
+			vec2 t = uv;
+			#if hasUVScale
+				t *= uvScale;
+			#end
+			#if hasUVDelta
+				t += uvDelta;
+			#end
+			tuv = t;
+			#if hasLightSystem
+				vec3 n = normal;
+				#if hasPos
+					n = mat3(mpos) * n;
+				#elseif hasSkin
+					//n = n * input.weights.x * skinMatrixes[input.indexes.x * (255 * 3)] + n * input.weights.y * skinMatrixes[input.indexes.y * (255 * 3)] + n * input.weights.z * skinMatrixes[input.indexes.z * (255 * 3)];
+					#if hasPos
+						n = mposInv * n;
+					#end
+				#end
+				n = normalize(n);
+				vec3 col = lights.ambient;
+				for(int i = 0; i < numDirLights; i++ )
+					col += lights.dirsColor[i] * max(dot(n,-lights.dirsDir[i]),0.);
+				for(int i = 0; i < numPointLights; i++ ) {
+					vec3 d = tpos.xyz - lights.pointsPos[i];
+					float dist2 = dot(d,d);
+					float dist = sqrt(dist2);
+					col += lights.pointsColor[i] * (max(dot(n,d),0.) / dot(lights.pointsAtt[i],vec3(dist,dist2,dist2*dist)));
+				}
+				#if hasVertexColor
+					tcolor = col * color;
+				#else
+					tcolor = col;
+				#end
+			#elseif hasVertexColor
+				tcolor = color;
+			#end
+			#if hasVertexColorAdd
+				acolor = colorAdd;
+			#end
+			#if hasFog
+				vec3 dist = tpos.xyz - fog.xyz;
+				talpha = (fog.w * dist.dot(dist).rsqrt()).min(1);
+			#end
+			#if hasBlend
+				tblend = blending;
+			#end
+			#if hasShadowMap
+				tshadowPos = shadowLightCenter * shadowLightProj * tpos;
+			#end
+		}
+
+	";
+	
+	static var FRAGMENT = "
+	
+		varying lowp vec2 tuv;
+		varying lowp vec3 tcolor;
+		varying lowp vec3 acolor;
+		varying mediump float talpha;
+		varying mediump float tblend;
+		varying mediump vec4 tshadowPos;
+
+		uniform sampler2D tex;
+		uniform lowp vec4 colorAdd;
+		uniform lowp vec4 colorMul;
+		uniform mediump mat4 colorMatrix;
+		
+		uniform lowp float killAlphaThreshold;
+
+		#if hasAlphaMap
+		uniform sampler2D alphaMap;
+		#end
+		
+		#if hasBlend
+		uniform sampler2D blendTexture;
+		#end
+
+		#if hasGlow
+		uniform sampler2D glowTexture;
+		uniform float glowAmount;
+		#end
+
+		#if hasShadowMap
+		uniform sampler2D shadowTexture;
+		uniform vec4 shadowColor;
+		#end
+
+		void main(void) {
+			lowp vec4 c = texture2D(tex, tuv);
+			#if hasFog
+				c.a *= talpha;
+			#end
+			#if hasAlphaMap
+				c.a *= texture2D(alphaMap, tuv).b;
+			#end
+			#if killAlpha
+				if( c.a - killAlphaThreshold ) discard;
+			#end
+			#if hasBlend
+				c.rgb = c.rgb * (1 - tblend) + tblend * texture2D(blendTexture, tuv).rgb;
+			#end
+			#if hasColorAdd
+				c += colorAdd;
+			#end
+			#if hasColorMul
+				c *= colorMul;
+			#end
+			#if hasColorMatrix
+				c = colorMatrix * c;
+			#end
+			#if hasVertexColorAdd
+				c.rgb += acolor;
+			#end
+			#if hasFragColor
+				c.rgb *= tcolor;
+			#end
+			#if hasShadowMap
+				// ESM filtering
+				mediump float shadow = exp( shadowColor.w * (tshadowPos.z - shadowTexture.get(tshadowPos.xy).dot([1, 1 / 255, 1 / (255 * 255), 1 / (255 * 255 * 255)]))).sat();
+				c.rgb *= (1 - shadow) * shadowColor.rgb + shadow.xxx;
+			#end
+			#if hasGlow
+				c.rgb += texture2D(glowTexture,tuv).rgb * glowAmount;
+			#end
+			gl_FragColor = c;
+		}
+
+	";
+
+
+#end
 	
 }
 
@@ -220,12 +484,12 @@ class MeshMaterial extends Material {
 		return m;
 	}
 	
-	function setup( camera : h3d.Camera, mpos ) {
-		mshader.mpos = useMatrixPos ? mpos : null;
-		mshader.mproj = camera.m;
+	override function setup( ctx : h3d.scene.RenderContext ) {
+		mshader.mpos = useMatrixPos ? ctx.localPos : null;
+		mshader.mproj = ctx.camera.m;
 		if( mshader.hasSkin && useMatrixPos && mshader.lightSystem != null ) {
 			var tmp = new h3d.Matrix();
-			tmp.inverse(mpos);
+			tmp.inverse(ctx.localPos);
 			mshader.mposInv = tmp;
 		}
 		mshader.tex = texture;
@@ -235,6 +499,7 @@ class MeshMaterial extends Material {
 		Set the DXT compression access mode for all textures of this material.
 	**/
 	public function setDXTSupport( enable : Bool, alpha = false ) {
+		#if flash
 		if( !enable ) {
 			mshader.isDXT1 = false;
 			mshader.isDXT5 = false;
@@ -242,6 +507,9 @@ class MeshMaterial extends Material {
 			mshader.isDXT1 = !alpha;
 			mshader.isDXT5 = alpha;
 		}
+		#else
+		throw "Not implemented";
+		#end
 	}
 	
 	inline function get_uvScale() {

+ 5 - 2
h3d/mat/PartMaterial.hx

@@ -1,7 +1,8 @@
 package h3d.mat;
 
-private class PartShader extends hxsl.Shader {
+private class PartShader extends h3d.impl.Shader {
 
+#if flash
 	static var SRC = {
 
 		var input : {
@@ -53,7 +54,9 @@ private class PartShader extends hxsl.Shader {
 		}
 	
 	}
-	
+
+#end
+
 }
 
 class PartMaterial extends Material {

+ 0 - 18
h3d/mat/PngBytes.hx

@@ -1,18 +0,0 @@
-package h3d.mat;
-
-class PngBytes extends flash.utils.ByteArray {
-
-	public function getBitmapBytes() {
-		return decode(haxe.io.Bytes.ofData(this));
-	}
-	
-	public static function decode( b : haxe.io.Bytes ) {
-		var mem = new haxe.io.BytesInput(b);
-		var png = new format.png.Reader(mem);
-		var data = png.read();
-		var h = format.png.Tools.getHeader(data);
-		var data = format.png.Tools.extract32(data);
-		return new Bitmap(h.width, h.height, data);
-	}
-	
-}

+ 12 - 32
h3d/mat/Texture.hx

@@ -7,7 +7,7 @@ class Texture {
 
 	static var UID = 0;
 	
-	var t : flash.display3D.textures.TextureBase;
+	var t : h3d.impl.Driver.Texture;
 	var mem : h3d.impl.MemoryManager;
 	#if debug
 	var allocPos : h3d.impl.AllocPos;
@@ -76,7 +76,8 @@ class Texture {
 		mem.resizeTexture(this, width, height);
 	}
 
-	public function uploadMipMap( bmp : flash.display.BitmapData, smoothing = false, side = 0 ) {
+	/*
+	public function uploadMipMap( bmp : hxd.BitmapData, smoothing = false, side = 0 ) {
 		upload(bmp, 0, side);
 		var w = bmp.width >> 1, h = bmp.height >> 1, mip = 1;
 		var m = new flash.geom.Matrix();
@@ -94,38 +95,14 @@ class Texture {
 			h >>= 1;
 		}
 	}
+	*/
 	
-	public function uploadAtfData( bytes : haxe.io.Bytes ) {
-		if( isCubic ) {
-			var t = flash.Lib.as(t, flash.display3D.textures.CubeTexture);
-			t.uploadCompressedTextureFromByteArray(bytes.getData(), 0);
-		}
-		else {
-			var t = flash.Lib.as(t,  flash.display3D.textures.Texture);
-			t.uploadCompressedTextureFromByteArray(bytes.getData(), 0);
-		}
-	}
-
-	public function upload( bmp : flash.display.BitmapData, mipLevel = 0, side = 0 ) {
-		if( isCubic ) {
-			var t = flash.Lib.as(t, flash.display3D.textures.CubeTexture);
-			t.uploadFromBitmapData(bmp, side, mipLevel);
-		}
-		else {
-			var t = flash.Lib.as(t,  flash.display3D.textures.Texture);
-			t.uploadFromBitmapData(bmp, mipLevel);
-		}
+	public function uploadBitmap( bmp : hxd.BitmapData, mipLevel = 0, side = 0 ) {
+		mem.driver.uploadTextureBitmap(this, bmp, mipLevel, side);
 	}
 
 	public function uploadBytes( bmp : haxe.io.Bytes, mipLevel = 0, side = 0 ) {
-		if( isCubic ) {
-			var t = flash.Lib.as(t, flash.display3D.textures.CubeTexture);
-			t.uploadFromByteArray(bmp.getData(), 0, side, mipLevel);
-		}
-		else {
-			var t = flash.Lib.as(t,  flash.display3D.textures.Texture);
-			t.uploadFromByteArray(bmp.getData(), 0, mipLevel);
-		}
+		mem.driver.uploadTextureBytes(this, bmp, mipLevel, side);
 	}
 
 	public function dispose() {
@@ -133,8 +110,11 @@ class Texture {
 			mem.deleteTexture(this);
 	}
 	
-	public static function fromBitmap( bmp : flash.display.BitmapData, hasMipMap = false, ?allocPos : h3d.impl.AllocPos ) {
-		return h3d.Engine.getCurrent().mem.makeTexture(bmp,hasMipMap,allocPos);
+	public static function fromBitmap( bmp : hxd.BitmapData, ?allocPos : h3d.impl.AllocPos ) {
+		var mem = h3d.Engine.getCurrent().mem;
+		var t = mem.allocTexture(bmp.width, bmp.height, false, allocPos);
+		t.uploadBitmap(bmp);
+		return t;
 	}
 
 }

+ 13 - 14
h3d/prim/Cube.hx

@@ -14,20 +14,19 @@ class Cube extends Polygon {
 			new Point(0, y, z),
 			new Point(x, y, z),
 		];
-		var idx = [
-			0, 1, 5,
-			0, 5, 3,
-			1, 4, 7,
-			1, 7, 5,
-			3, 5, 7,
-			3, 7, 6,
-			0, 6, 2,
-			0, 3, 6,
-			2, 7, 4,
-			2, 6, 7,
-			0, 4, 1,
-			0, 2, 4,
-		];
+		var idx = new hxd.IndexBuffer();
+		idx.push(0); idx.push(1); idx.push(5);
+		idx.push(0); idx.push(5); idx.push(3);
+		idx.push(1); idx.push(4); idx.push(7);
+		idx.push(1); idx.push(7); idx.push(5);
+		idx.push(3); idx.push(5); idx.push(7);
+		idx.push(3); idx.push(7); idx.push(6);
+		idx.push(0); idx.push(6); idx.push(2);
+		idx.push(0); idx.push(3); idx.push(6);
+		idx.push(2); idx.push(7); idx.push(4);
+		idx.push(2); idx.push(6); idx.push(7);
+		idx.push(0); idx.push(4); idx.push(1);
+		idx.push(0); idx.push(2); idx.push(4);
 		super(p, idx);
 	}
 	

+ 18 - 21
h3d/prim/FBXModel.hx

@@ -87,13 +87,10 @@ class FBXModel extends MeshPrimitive {
 		var gt = geom.getGeomTranslate();
 		if( gt == null ) gt = new h3d.prim.Point();
 		
-		var idx = new flash.Vector<UInt>();
-		var midx = new Array<flash.Vector<UInt>>();
-		var pbuf = new flash.Vector<Float>(), nbuf = (norms == null ? null : new flash.Vector<Float>()), sbuf = (skin == null ? null : new flash.utils.ByteArray()), tbuf = (tuvs == null ? null : new flash.Vector<Float>());
-		var cbuf = (colors == null ? null : new flash.Vector<Float>());
-		var pout = 0, nout = 0, sout = 0, tout = 0, cout = 0;
-		
-		if( sbuf != null ) sbuf.endian = flash.utils.Endian.LITTLE_ENDIAN;
+		var idx = new hxd.IndexBuffer();
+		var midx = new Array<hxd.IndexBuffer>();
+		var pbuf = new hxd.FloatBuffer(), nbuf = (norms == null ? null : new hxd.FloatBuffer()), sbuf = (skin == null ? null : new hxd.BytesBuffer()), tbuf = (tuvs == null ? null : new hxd.FloatBuffer());
+		var cbuf = (colors == null ? null : new hxd.FloatBuffer());
 		
 		// triangulize indexes : format is  A,B,...,-X : negative values mark the end of the polygon
 		var count = 0, pos = 0, matPos = 0;
@@ -110,20 +107,20 @@ class FBXModel extends MeshPrimitive {
 					var x = verts[vidx * 3] + gt.x;
 					var y = verts[vidx * 3 + 1] + gt.y;
 					var z = verts[vidx * 3 + 2] + gt.z;
-					pbuf[pout++] = x;
-					pbuf[pout++] = y;
-					pbuf[pout++] = z;
+					pbuf.push(x);
+					pbuf.push(y);
+					pbuf.push(z);
 
 					if( nbuf != null ) {
-						nbuf[nout++] = norms[k*3];
-						nbuf[nout++] = norms[k*3 + 1];
-						nbuf[nout++] = norms[k*3 + 2];
+						nbuf.push(norms[k*3]);
+						nbuf.push(norms[k*3 + 1]);
+						nbuf.push(norms[k*3 + 2]);
 					}
 
 					if( tbuf != null ) {
 						var iuv = tuvs.index[k];
-						tbuf[tout++] = tuvs.values[iuv*2];
-						tbuf[tout++] = 1 - tuvs.values[iuv * 2 + 1];
+						tbuf.push(tuvs.values[iuv*2]);
+						tbuf.push(1 - tuvs.values[iuv * 2 + 1]);
 					}
 					
 					if( sbuf != null ) {
@@ -133,14 +130,14 @@ class FBXModel extends MeshPrimitive {
 							sbuf.writeFloat(skin.vertexWeights[p + i]);
 							idx = (skin.vertexJoints[p + i] << (8*i)) | idx;
 						}
-						sbuf.writeUnsignedInt(idx);
+						sbuf.writeInt32(idx);
 					}
 					
 					if( cbuf != null ) {
 						var icol = colors.index[k];
-						cbuf[cout++] = colors.values[icol * 4];
-						cbuf[cout++] = colors.values[icol * 4 + 1];
-						cbuf[cout++] = colors.values[icol * 4 + 2];
+						cbuf.push(colors.values[icol * 4]);
+						cbuf.push(colors.values[icol * 4 + 1]);
+						cbuf.push(colors.values[icol * 4 + 2]);
 					}
 				}
 				// polygons are actually triangle fans
@@ -154,7 +151,7 @@ class FBXModel extends MeshPrimitive {
 					var mid = mats[matPos++];
 					var idx = midx[mid];
 					if( idx == null ) {
-						idx = new flash.Vector<UInt>();
+						idx = new hxd.IndexBuffer();
 						midx[mid] = idx;
 					}
 					for( n in 0...count - 2 ) {
@@ -175,7 +172,7 @@ class FBXModel extends MeshPrimitive {
 		if( sbuf != null ) {
 			var nverts = Std.int(sbuf.length / ((skin.bonesPerVertex + 1) * 4));
 			var skinBuf = engine.mem.alloc(nverts, skin.bonesPerVertex + 1, 0);
-			skinBuf.upload(sbuf, 0, nverts);
+			skinBuf.uploadBytes(sbuf.getBytes(), 0, nverts);
 			addBuffer("weights", skinBuf, 0);
 			addBuffer("indexes", skinBuf, skin.bonesPerVertex);
 		}

+ 22 - 23
h3d/prim/GeoSphere.hx

@@ -5,20 +5,19 @@ class GeoSphere extends Polygon {
 	public function new( subdiv = 2 ) {
 		var a = 1 / Math.sqrt(2);
 		var p = [new Point(0, 0, 1), new Point(0, 0, -1), new Point(-a, -a, 0), new Point(a, -a, 0), new Point(a, a, 0), new Point( -a, a, 0)];
-		var idx = [
-			0, 3, 4,
-			0, 4, 5,
-			0, 5, 2,
-			0, 2, 3,
-			1, 4, 3,
-			1, 5, 4,
-			1, 2, 5,
-			1, 3, 2,
-		];
+		var idx = new hxd.IndexBuffer();
+		idx.push(0); idx.push(3); idx.push(4);
+		idx.push(0); idx.push(4); idx.push(5);
+		idx.push(0); idx.push(5); idx.push(2);
+		idx.push(0); idx.push(2); idx.push(3);
+		idx.push(1); idx.push(4); idx.push(3);
+		idx.push(1); idx.push(5); idx.push(4);
+		idx.push(1); idx.push(2); idx.push(5);
+		idx.push(1); idx.push(3); idx.push(2);
 		for( k in 0...subdiv ) {
 			var i = 0;
 			var count = idx.length, np = p.length;
-			var nidx = [], k = 0;
+			var nidx = new hxd.IndexBuffer();
 			while( i < count ) {
 				var p1 = p[idx[i]], p2 = p[idx[i + 1]], p3 = p[idx[i + 2]];
 				var pa = p1.add(p2);
@@ -28,21 +27,21 @@ class GeoSphere extends Polygon {
 				pb.normalize();
 				pc.normalize();
 				
-				nidx[k++] = np;
-				nidx[k++] = np + 1;
-				nidx[k++] = np + 2;
+				nidx.push(np);
+				nidx.push(np + 1);
+				nidx.push(np + 2);
 				
-				nidx[k++] = idx[i];
-				nidx[k++] = np;
-				nidx[k++] = np + 2;
+				nidx.push(idx[i]);
+				nidx.push(np);
+				nidx.push(np + 2);
 
-				nidx[k++] = np;
-				nidx[k++] = idx[i + 1];
-				nidx[k++] = np + 1;
+				nidx.push(np);
+				nidx.push(idx[i + 1]);
+				nidx.push(np + 1);
 				
-				nidx[k++] = np + 1;
-				nidx[k++] = idx[i + 2];
-				nidx[k++] = np + 2;
+				nidx.push(np + 1);
+				nidx.push(idx[i + 2]);
+				nidx.push(np + 2);
 				
 				p[np++] = pa;
 				p[np++] = pb;

+ 2 - 2
h3d/prim/MeshPrimitive.hx

@@ -24,12 +24,12 @@ class MeshPrimitive extends Primitive {
 		bufferCache = null;
 	}
 
-	@:access(h3d.Engine.curShader)
+	@:access(h3d.Engine.driver)
 	function getBuffers( engine : h3d.Engine ) {
 		if( bufferCache == null )
 			bufferCache = new Map();
 		var buffers = [];
-		for( name in engine.curShader.bufferNames ) {
+		for( name in engine.driver.getShaderInputNames() ) {
 			var b = bufferCache.get(name);
 			if( b == null ) {
 				b = allocBuffer(engine, name);

+ 1 - 1
h3d/prim/Plan2D.hx

@@ -6,7 +6,7 @@ class Plan2D extends Primitive {
 	}
 	
 	override function alloc( engine : h3d.Engine ) {
-		var v = new flash.Vector<Float>();
+		var v = new hxd.FloatBuffer();
 		v.push( -1);
 		v.push( -1);
 		v.push( 0);

+ 1 - 1
h3d/prim/Plan3D.hx

@@ -33,7 +33,7 @@ class Plan3D extends Primitive {
 	}
 
 	override function alloc( engine : h3d.Engine ) {
-		var v = new flash.Vector<Float>();
+		var v = new hxd.FloatBuffer();
 		var hw = width * 0.5, hh = height * 0.5;
 		v.push( -hw);
 		v.push( -hh);

+ 23 - 26
h3d/prim/Polygon.hx

@@ -5,7 +5,7 @@ class Polygon extends Primitive {
 	public var points : Array<Point>;
 	public var normals : Array<Point>;
 	public var tcoords : Array<UV>;
-	public var idx : Array<Int>;
+	public var idx : hxd.IndexBuffer;
 	public var colors : Array<Point>;
 		
 	public function new( points, ?idx ) {
@@ -24,37 +24,34 @@ class Polygon extends Primitive {
 		if( colors != null )
 			size += 3;
 			
-		var buf = new flash.Vector<Float>();
-		var i = 0;
+		var buf = new hxd.FloatBuffer();
 		for( k in 0...points.length ) {
 			var p = points[k];
-			buf[i++] = p.x;
-			buf[i++] = p.y;
-			buf[i++] = p.z;
+			buf.push(p.x);
+			buf.push(p.y);
+			buf.push(p.z);
 			if( tcoords != null ) {
 				var t = tcoords[k];
-				buf[i++] = t.u;
-				buf[i++] = t.v;
+				buf.push(t.u);
+				buf.push(t.v);
 			}
 			if( normals != null ) {
 				var n = normals[k];
-				buf[i++] = n.x;
-				buf[i++] = n.y;
-				buf[i++] = n.z;
+				buf.push(n.x);
+				buf.push(n.y);
+				buf.push(n.z);
 			}
 			if( colors != null ) {
 				var c = colors[k];
-				buf[i++] = c.x;
-				buf[i++] = c.y;
-				buf[i++] = c.z;
+				buf.push(c.x);
+				buf.push(c.y);
+				buf.push(c.z);
 			}
 		}
 		buffer = engine.mem.allocVector(buf, size, idx == null ? 3 : 0);
 		
-		if( idx != null ) {
-			var idx : Array<UInt> = cast idx;
-			indexes = engine.mem.allocIndex(flash.Vector.ofArray(idx));
-		}
+		if( idx != null )
+			indexes = engine.mem.allocIndex(idx);
 	}
 
 
@@ -62,24 +59,24 @@ class Polygon extends Primitive {
 		if( idx != null && points.length != idx.length ) {
 			var p = [];
 			var used = [];
-			for( i in idx )
-				p.push(points[i].clone());
+			for( i in 0...idx.length )
+				p.push(points[idx[i]].clone());
 			if( normals != null ) {
 				var n = [];
-				for( i in idx )
-					n.push(normals[i].clone());
+				for( i in 0...idx.length )
+					n.push(normals[idx[i]].clone());
 				normals = n;
 			}
 			if( colors != null ) {
 				var n = [];
-				for( i in idx )
-					n.push(colors[i].clone());
+				for( i in 0...idx.length )
+					n.push(colors[idx[i]].clone());
 				colors = n;
 			}
 			if( tcoords != null ) {
 				var t = [];
-				for( i in idx )
-					t.push(tcoords[i].clone());
+				for( i in 0...idx.length )
+					t.push(tcoords[idx[i]].clone());
 				tcoords = t;
 			}
 			points = p;

+ 9 - 10
h3d/prim/Quads.hx

@@ -40,23 +40,22 @@ class Quads extends Primitive {
 	
 	override function alloc( engine : Engine ) {
 		dispose();
-		var v = new flash.Vector();
-		var p = 0;
+		var v = new hxd.FloatBuffer();
 		for( i in 0...pts.length ) {
 			var pt = pts[i];
-			v[p++] = pt.x;
-			v[p++] = pt.y;
-			v[p++] = pt.z;
+			v.push(pt.x);
+			v.push(pt.y);
+			v.push(pt.z);
 			if( uvs != null ) {
 				var t = uvs[i];
-				v[p++] = t.u;
-				v[p++] = t.v;
+				v.push(t.u);
+				v.push(t.v);
 			}
 			if( normals != null ) {
 				var n = normals[i];
-				v[p++] = n.x;
-				v[p++] = n.y;
-				v[p++] = n.z;
+				v.push(n.x);
+				v.push(n.y);
+				v.push(n.z);
 			}
 		}
 		var size = 3;

+ 1 - 1
h3d/prim/RawPrimitive.hx

@@ -2,7 +2,7 @@ package h3d.prim;
 
 class RawPrimitive extends Primitive {
 
-	public function new( engine : h3d.Engine, vbuf : flash.Vector<Float>, stride : Int, ?ibuf : flash.Vector<UInt> ) {
+	public function new( engine : h3d.Engine, vbuf : hxd.FloatBuffer, stride : Int, ?ibuf : hxd.IndexBuffer ) {
 		buffer = engine.mem.allocVector(vbuf, stride, ibuf == null ? 3 : 0);
 		if( ibuf != null )
 			indexes = engine.mem.allocIndex(ibuf);

+ 1 - 1
h3d/prim/Sphere.hx

@@ -9,7 +9,7 @@ class Sphere extends Polygon {
 		this.segsH = segsH;
 		this.segsW = segsW;
 		var t = 0., dt = Math.PI / segsH, dp = Math.PI * 2 / segsW;
-		var pts = [], idx = [];
+		var pts = [], idx = new hxd.IndexBuffer();
 		var dx = 1, dy = segsW + 1;
 		for( y in 0...segsH ) {
 			var p = 0.;

+ 1 - 2
h3d/scene/CustomObject.hx

@@ -31,8 +31,7 @@ class CustomObject extends Object {
 			ctx.addPass(draw);
 			return;
 		}
-		try Reflect.setProperty(material.shader,__unprotect__("mproj"),ctx.camera.m) catch( e : Dynamic ) {};
-		try Reflect.setProperty(material.shader,__unprotect__("mpos"),absPos) catch( e : Dynamic ) {};
+		material.setup(ctx);
 		ctx.engine.selectMaterial(material);
 		primitive.render(ctx.engine);
 	}

+ 23 - 19
h3d/scene/Image.hx

@@ -1,6 +1,7 @@
 package h3d.scene;
 
-private class ImageShader extends hxsl.Shader {
+private class ImageShader extends h3d.impl.Shader {
+	#if flash
 	static var SRC = {
 		var input : {
 			pos : Float2,
@@ -15,6 +16,10 @@ private class ImageShader extends hxsl.Shader {
 			out = tex.get(tuv);
 		}
 	}
+	#elseif js
+	static var VERTEX = "TODO";
+	static var FRAGMENT = "TODO";
+	#end
 }
 
 class Image extends Object {
@@ -32,31 +37,30 @@ class Image extends Object {
 	
 	@:access(h2d.Tile)
 	override function draw( ctx : RenderContext ) {
-		var tmp = new flash.Vector();
-		var pos = 0;
+		var tmp = new hxd.FloatBuffer();
 		var dx = absPos._41 * 2 / ctx.engine.width;
 		var dy = -absPos._42 * 2 / ctx.engine.height;
 		if( tile == null )
 			tile = new h2d.Tile(null, 0, 0, 0, 0);
-		tmp[pos++] = -1 * absPos._11 - 1 * absPos._21 + dx;
-		tmp[pos++] = 1 * absPos._12 + 1 * absPos._22 + dy;
-		tmp[pos++] = tile.u;
-		tmp[pos++] = tile.v;
+		tmp.push(-1 * absPos._11 - 1 * absPos._21 + dx);
+		tmp.push(1 * absPos._12 + 1 * absPos._22 + dy);
+		tmp.push(tile.u);
+		tmp.push(tile.v);
 
-		tmp[pos++] = 1 * absPos._11 + 1 * absPos._21 + dx;
-		tmp[pos++] = 1 * absPos._12 + 1 * absPos._22 + dy;
-		tmp[pos++] = tile.u2;
-		tmp[pos++] = tile.v;
+		tmp.push(1 * absPos._11 + 1 * absPos._21 + dx);
+		tmp.push(1 * absPos._12 + 1 * absPos._22 + dy);
+		tmp.push(tile.u2);
+		tmp.push(tile.v);
 
-		tmp[pos++] = -1 * absPos._11 - 1 * absPos._21 + dx;
-		tmp[pos++] = -1 * absPos._12 - 1 * absPos._22 + dy;
-		tmp[pos++] = tile.u;
-		tmp[pos++] = tile.v2;
+		tmp.push(-1 * absPos._11 - 1 * absPos._21 + dx);
+		tmp.push(-1 * absPos._12 - 1 * absPos._22 + dy);
+		tmp.push(tile.u);
+		tmp.push(tile.v2);
 
-		tmp[pos++] = 1 * absPos._11 - 1 * absPos._21 + dx;
-		tmp[pos++] = 1 * absPos._12 - 1 * absPos._22 + dy;
-		tmp[pos++] = tile.u2;
-		tmp[pos++] = tile.v2;
+		tmp.push(1 * absPos._11 - 1 * absPos._21 + dx);
+		tmp.push(1 * absPos._12 - 1 * absPos._22 + dy);
+		tmp.push(tile.u2);
+		tmp.push(tile.v2);
 		
 		cast(material.shader,ImageShader).tex = tile.getTexture();
 		var b = ctx.engine.mem.allocVector(tmp, 4, 4);

+ 2 - 1
h3d/scene/Mesh.hx

@@ -32,7 +32,8 @@ class Mesh extends Object {
 			ctx.addPass(draw);
 			return;
 		}
-		material.setup(ctx.camera, this.absPos);
+		ctx.localPos = absPos;
+		material.setup(ctx);
 		ctx.engine.selectMaterial(material);
 		primitive.render(ctx.engine);
 	}

+ 2 - 1
h3d/scene/MultiMaterial.hx

@@ -26,7 +26,8 @@ class MultiMaterial extends Mesh {
 			ctx.addPass(drawMaterial.bind(_,mid));
 			return;
 		}
-		m.setup(ctx.camera, this.absPos);
+		ctx.localPos = this.absPos;
+		m.setup(ctx);
 		ctx.engine.selectMaterial(m);
 		primitive.selectMaterial(mid);
 		primitive.render(ctx.engine);

+ 4 - 4
h3d/scene/Object.hx

@@ -378,16 +378,16 @@ class Object {
 		return Type.getClassName(Type.getClass(this)).split(".").pop() + (name == null ? "" : "(" + name + ")");
 	}
 	
-	public function getChildAt( n ) {
+	public inline function getChildAt( n ) {
 		return childs[n];
 	}
 	
-	function get_numChildren() {
+	inline function get_numChildren() {
 		return childs.length;
 	}
 	
-	public inline function iterator() {
-		return childs.iterator();
+	public inline function iterator() : hxd.impl.ArrayIterator<Object> {
+		return new hxd.impl.ArrayIterator(childs);
 	}
 	
 	public function dispose() {

+ 10 - 10
h3d/scene/Particles.hx

@@ -36,7 +36,7 @@ class Particles extends Object {
 
 	var first : Particle;
 	var last : Particle;
-	var tmpBuf : flash.Vector<Float>;
+	var tmpBuf : hxd.FloatBuffer;
 
 	public function new( ?mat, ?parent ) {
 		super(parent);
@@ -90,7 +90,7 @@ class Particles extends Object {
 	override function draw( ctx : RenderContext ) {
 		if( first == null )
 			return;
-		if( tmpBuf == null ) tmpBuf = new flash.Vector();
+		if( tmpBuf == null ) tmpBuf = new hxd.FloatBuffer();
 		var pos = 0;
 		var p = first;
 		var tmp = tmpBuf;
@@ -100,8 +100,8 @@ class Particles extends Object {
 			tmp[pos++] = p.x;
 			tmp[pos++] = p.y;
 			tmp[pos++] = p.z;
-			tmp[pos++] = 0;
-			tmp[pos++] = 0;
+			tmp[pos++] = 0.;
+			tmp[pos++] = 0.;
 			tmp[pos++] = p.alpha;
 			if( hasFrame ) {
 				curFrame = p.frame / frameCount;
@@ -112,8 +112,8 @@ class Particles extends Object {
 			tmp[pos++] = p.x;
 			tmp[pos++] = p.y;
 			tmp[pos++] = p.z;
-			tmp[pos++] = 0;
-			tmp[pos++] = 1;
+			tmp[pos++] = 0.;
+			tmp[pos++] = 1.;
 			tmp[pos++] = p.alpha;
 			if( hasFrame ) tmp[pos++] = curFrame;
 			if( hasRotation ) tmp[pos++] = p.rotation;
@@ -121,8 +121,8 @@ class Particles extends Object {
 			tmp[pos++] = p.x;
 			tmp[pos++] = p.y;
 			tmp[pos++] = p.z;
-			tmp[pos++] = 1;
-			tmp[pos++] = 0;
+			tmp[pos++] = 1.;
+			tmp[pos++] = 0.;
 			tmp[pos++] = p.alpha;
 			if( hasFrame ) tmp[pos++] = curFrame;
 			if( hasRotation ) tmp[pos++] = p.rotation;
@@ -130,8 +130,8 @@ class Particles extends Object {
 			tmp[pos++] = p.x;
 			tmp[pos++] = p.y;
 			tmp[pos++] = p.z;
-			tmp[pos++] = 1;
-			tmp[pos++] = 1;
+			tmp[pos++] = 1.;
+			tmp[pos++] = 1.;
 			tmp[pos++] = p.alpha;
 			if( hasFrame ) tmp[pos++] = curFrame;
 			if( hasRotation ) tmp[pos++] = p.rotation;

+ 2 - 1
h3d/scene/RenderContext.hx

@@ -7,11 +7,12 @@ class RenderContext {
 	public var elapsedTime : Float;
 	public var currentPass : Int;
 	public var frame : Int;
+	public var localPos : h3d.Matrix;
 	var passes : Array<RenderContext -> Void>;
 	
 	public function new() {
 		time = 0.;
-		elapsedTime = 1. / flash.Lib.current.stage.frameRate;
+		elapsedTime = 1. / hxd.Stage.getInstance().getFrameRate();
 	}
 	
 	public function addPass(p) {

+ 93 - 0
hxd/BitmapData.hx

@@ -0,0 +1,93 @@
+package hxd;
+
+private typedef InnerData = #if flash flash.display.BitmapData #elseif js js.html.ImageData #else Int #end;
+
+abstract BitmapData(InnerData) {
+
+	public var width(get, never) : Int;
+	public var height(get, never) : Int;
+	
+	public inline function new(width:Int, height:Int) {
+		#if flash
+		this = new flash.display.BitmapData(width, height, true, 0);
+		#else
+		throw "TODO";
+		#end
+	}
+	
+	public inline function clear( color : Int ) {
+		#if flash
+		this.fillRect(this.rect, color);
+		#else
+		throw "TODO";
+		#end
+	}
+	
+	public inline function dispose() {
+		#if flash
+		this.dispose();
+		#end
+	}
+	
+	public inline function getPixel( x : Int, y : Int ) {
+		#if flash
+		return this.getPixel32(x, y);
+		#else
+		throw "TODO";
+		return 0;
+		#end
+	}
+
+	public inline function setPixel( x : Int, y : Int, c : Int ) {
+		#if flash
+		this.setPixel32(x, y, c);
+		#else
+		throw "TODO";
+		#end
+	}
+	
+	inline function get_width() {
+		return this.width;
+	}
+
+	inline function get_height() {
+		return this.height;
+	}
+	
+	public inline function getBytes() {
+		return nativeGetBytes(this);
+	}
+
+	public inline function toNative() : InnerData {
+		return this;
+	}
+	
+	public static inline function fromNative( bmp : InnerData ) : BitmapData {
+		return cast bmp;
+	}
+	
+	static function nativeGetBytes( b : InnerData ) {
+		#if flash
+		var bytes = haxe.io.Bytes.ofData(b.getPixels(b.rect));
+		// it is necessary to swap the bytes from BE to LE
+		var mem = hxd.impl.Memory.select(bytes);
+		for( i in 0...b.width*b.height ) {
+			var p = i << 2;
+			var a = mem.b(p);
+			var r = mem.b(p+1);
+			var g = mem.b(p+2);
+			var b = mem.b(p+3);
+			mem.wb(p, b);
+			mem.wb(p+1, g);
+			mem.wb(p+2, r);
+			mem.wb(p+3, a);
+		}
+		mem.end();
+		return bytes;
+		#else
+		throw "TODO";
+		return null;
+		#end
+	}
+	
+}

+ 47 - 0
hxd/BytesBuffer.hx

@@ -0,0 +1,47 @@
+package hxd;
+
+private typedef InnerData = #if flash flash.utils.ByteArray #else haxe.io.BytesOutput #end
+
+abstract BytesBuffer(InnerData) {
+	
+	public var length(get, never) : Int;
+	
+	public inline function new() {
+		#if flash
+		this = new flash.utils.ByteArray();
+		this.endian = flash.utils.Endian.LITTLE_ENDIAN;
+		#else
+		this = new haxe.io.BytesOutput();
+		#end
+	}
+	
+	public inline function writeFloat( v : Float ) {
+		this.writeFloat(v);
+	}
+	
+	public inline function writeInt32( v : Int ) {
+		#if flash
+		this.writeUnsignedInt(v);
+		#else
+		this.writeInt32(v);
+		#end
+	}
+	
+	public inline function getBytes() : haxe.io.Bytes {
+		#if flash
+		return haxe.io.Bytes.ofData(this);
+		#else
+		return this.getBytes();
+		#end
+	}
+	
+	inline function get_length() {
+		#if flash
+		return this.length;
+		#else
+		return this.length;
+		#end
+	}
+
+}
+		

+ 1 - 1
h2d/Event.hx → hxd/Event.hx

@@ -1,4 +1,4 @@
-package h2d;
+package hxd;
 
 enum EventKind {
 	EPush;

+ 80 - 0
hxd/FloatBuffer.hx

@@ -0,0 +1,80 @@
+package hxd;
+
+private typedef InnerData = #if flash flash.Vector<Float> #else Array<Float> #end
+
+private class InnerIterator {
+	var b : InnerData;
+	var len : Int;
+	var pos : Int;
+	public inline function new( b : InnerData )  {
+		this.b = b;
+		this.len = this.b.length;
+		this.pos = 0;
+	}
+	public inline function hasNext() {
+		return pos < len;
+	}
+	public inline function next() {
+		return b[pos++];
+	}
+}
+
+abstract FloatBuffer(InnerData) {
+
+	public var length(get, never) : Int;
+		
+	public inline function new(length = 0) {
+		#if js
+		this = untyped __new__(Array, length);
+		#else
+		this = new InnerData(length);
+		#end
+	}
+	
+	public inline function push( v : Float ) {
+		#if flash
+		this[this.length] = v;
+		#else
+		this.push(v);
+		#end
+	}
+	
+	public inline function grow( v : Int ) {
+		#if flash
+		if( v > this.length ) this.length = v;
+		#else
+		while( this.length < v ) this.push(0.);
+		#end
+	}
+
+	public inline function resize( v : Int ) {
+		#if flash
+		this.length = v;
+		#else
+		while( this.length < v ) this.push(0.);
+		if( this.length > v ) this.splice(v, this.length - v);
+		#end
+	}
+	
+	
+	@:arrayAccess inline function arrayRead(key:Int) : Float {
+		return this[key];
+	}
+
+	@:arrayAccess inline function arrayWrite(key:Int, value : Float) : Float {
+		return this[key] = value;
+	}
+	
+	public inline function getNative() : InnerData {
+		return this;
+	}
+	
+	public inline function iterator() {
+		return new InnerIterator(this);
+	}
+	
+	inline function get_length() : Int {
+		return this.length;
+	}
+	
+}

+ 62 - 0
hxd/IndexBuffer.hx

@@ -0,0 +1,62 @@
+package hxd;
+
+private typedef InnerData = #if flash flash.Vector<UInt> #else Array<Int> #end
+
+private class InnerIterator {
+	var b : InnerData;
+	var len : Int;
+	var pos : Int;
+	public inline function new( b : InnerData )  {
+		this.b = b;
+		this.len = this.b.length;
+		this.pos = 0;
+	}
+	public inline function hasNext() {
+		return pos < len;
+	}
+	public inline function next() : Int {
+		return b[pos++];
+	}
+}
+
+abstract IndexBuffer(InnerData) {
+	
+	public var length(get, never) : Int;
+	
+	public inline function new(length = 0) {
+		#if js
+		this = untyped __new__(Array, length);
+		#else
+		this = new InnerData(length);
+		#end
+	}
+	
+	public inline function push( v : Int ) {
+		#if flash
+		this[this.length] = v;
+		#else
+		this.push(v);
+		#end
+	}
+
+	@:arrayAccess inline function arrayRead(key:Int) : Int {
+		return this[key];
+	}
+
+	@:arrayAccess inline function arrayWrite(key:Int, value : Int) : Int {
+		return this[key] = value;
+	}
+	
+	public inline function getNative() : InnerData {
+		return this;
+	}
+
+	public inline function iterator() {
+		return new InnerIterator(this);
+	}
+
+	inline function get_length() : Int {
+		return this.length;
+	}
+	
+}

+ 14 - 2
h2d/Key.hx → hxd/Key.hx

@@ -1,7 +1,8 @@
-package h2d;
+package hxd;
 
 class Key {
-	public static inline var BACK 		= 8;
+	
+	public static inline var BACKSPACE	= 8;
 	public static inline var TAB		= 9;
 	public static inline var ENTER		= 13;
 	public static inline var SHIFT		= 16;
@@ -24,6 +25,17 @@ class Key {
 	public static inline var NUMPAD_0	= 96;
 	public static inline var A			= 65;
 	public static inline var F1			= 112;
+	public static inline var F2			= 113;
+	public static inline var F3			= 114;
+	public static inline var F4			= 115;
+	public static inline var F5			= 116;
+	public static inline var F6			= 117;
+	public static inline var F7			= 118;
+	public static inline var F8			= 119;
+	public static inline var F9			= 120;
+	public static inline var F10		= 121;
+	public static inline var F11		= 122;
+	public static inline var F12		= 123;
 	
 	public static inline var NUMPAD_MULT = 106;
 	public static inline var NUMPAD_ADD	= 107;

+ 365 - 0
hxd/Perlin.hx

@@ -0,0 +1,365 @@
+package hxd;
+
+class Perlin {
+
+	public var repeat : Int;
+	public var seed : Int;
+	var buf : flash.utils.ByteArray;
+	
+	public function new(seed) {
+		this.seed = seed;
+		repeat = 0x7FFFFFFF;
+		// space for gradients
+		buf = new flash.utils.ByteArray();
+		buf.length += NGRADS * 4 * 8;
+		// init gradients and alpha channel
+		flash.Memory.select(buf);
+		for( i in 0...NGRADS ) {
+			var p = i << 5;
+			setDouble(p, GRADIENTS[i * 3] * 2.12);
+			setDouble(p + 8, GRADIENTS[i * 3 + 1] * 2.12);
+			setDouble(p + 16, GRADIENTS[i * 3 + 2] * 2.12);
+			setDouble(p + 24, 0); // padding
+		}
+	}
+
+	public inline function select() {
+		flash.Memory.select(buf);
+	}
+
+	inline function setDouble( index : Int, v : Float )  {
+		flash.Memory.setDouble(index, v);
+	}
+	
+	inline function double( index : Int ) : Float {
+		return flash.Memory.getDouble(index);
+	}
+	
+	inline function scurve( a : Float ) {
+		var a2 = a * a;
+		return a2 * a * (6.0 * a2 - 15.0 * a + 10.0);
+	}
+	
+	inline function linear( a : Float, b : Float, k : Float ) : Float {
+		return a * (1.0 - k) + b * k;
+	}
+	
+	public inline function gradient3D( x : Float, y : Float, z : Float, ix : Int, iy : Int, iz : Int, seed : Int ) {
+		var index = seed * 1013 + (ix % repeat) * 1619 + (iy % repeat) * 31337 + iz * 6971;
+		index = ((index ^ (index >>> 8)) & 0xFF) << 5;
+		var gx = double(index);
+		var gy = double(index + 8);
+		var gz = double(index + 16);
+		return gx * (x - ix) + gy * (y - iy) + gz * (z - iz);
+	}
+
+	inline function gradient( x : Float, y : Float, ix : Int, iy : Int, seed : Int ) {
+		var index = seed * 1013 + (ix%repeat) * 1619 + (iy%repeat) * 31337;
+		index = ((index ^ (index >>> 8)) & 0xFF) << 5;
+		var gx = double(index);
+		var gy = double(index + 8);
+		return gx * (x - ix) + gy * (y - iy);
+	}
+	
+	public function init( seed ) {
+		this.seed = seed;
+	}
+	
+	public function adjustScale( size : Int, scale : Float ) {
+		repeat = Std.int(size * scale);
+		return repeat / size;
+	}
+	
+	public inline function gradient3DAt( x : Float, y : Float, z : Float ) {
+		var ix = Std.int(x), xs = scurve(x - ix);
+		var iy = Std.int(y), ys = scurve(y - iy);
+		var iz = Std.int(z), zs = scurve(z - iz);
+		
+		var ga = gradient3D(x, y, z, ix, iy, iz, seed);
+		var gb = gradient3D(x, y, z, ix + 1, iy, iz, seed);
+		var gc = gradient3D(x, y, z, ix, iy + 1, iz, seed);
+		var gd = gradient3D(x, y, z, ix + 1, iy + 1, iz, seed);
+		var v1 = linear(linear(ga, gb, xs), linear(gc, gd, xs), ys);
+		
+		var ga = gradient3D(x, y, z, ix, iy, iz + 1, seed);
+		var gb = gradient3D(x, y, z, ix + 1, iy, iz + 1, seed);
+		var gc = gradient3D(x, y, z, ix, iy + 1, iz + 1, seed);
+		var gd = gradient3D(x, y, z, ix + 1, iy + 1, iz + 1, seed);
+		var v2 = linear(linear(ga, gb, xs), linear(gc, gd, xs), ys);
+		
+		return linear(v1, v2, zs);
+	}
+
+	public inline function gradient2DAt( x : Float, y : Float ) {
+		var ix = Std.int(x), xs = scurve(x - ix);
+		var iy = Std.int(y), ys = scurve(y - iy);
+		var ga = gradient(x, y, ix, iy, seed);
+		var gb = gradient(x, y, ix + 1, iy, seed);
+		var gc = gradient(x, y, ix, iy + 1, seed);
+		var gd = gradient(x, y, ix + 1, iy + 1, seed);
+		return linear(linear(ga, gb, xs), linear(gc, gd, xs), ys);
+	}
+
+	// 256 randomized 3D gradients
+	static inline var NGRADS = 256;
+	static inline var GPREC = 65536;
+	static var GRADIENTS = [
+		-0.763874, -0.596439, -0.246489,
+		0.396055, 0.904518, -0.158073,
+		-0.499004, -0.8665, -0.0131631,
+		0.468724, -0.824756, 0.316346,
+		0.829598, 0.43195, 0.353816,
+		-0.454473, 0.629497, -0.630228,
+		-0.162349, -0.869962, -0.465628,
+		0.932805, 0.253451, 0.256198,
+		-0.345419, 0.927299, -0.144227,
+		-0.715026, -0.293698, -0.634413,
+		-0.245997, 0.717467, -0.651711,
+		-0.967409, -0.250435, -0.037451,
+		0.901729, 0.397108, -0.170852,
+		0.892657, -0.0720622, -0.444938,
+		0.0260084, -0.0361701, 0.999007,
+		0.949107, -0.19486, 0.247439,
+		0.471803, -0.807064, -0.355036,
+		0.879737, 0.141845, 0.453809,
+		0.570747, 0.696415, 0.435033,
+		-0.141751, -0.988233, -0.0574584,
+		-0.58219, -0.0303005, 0.812488,
+		-0.60922, 0.239482, -0.755975,
+		0.299394, -0.197066, -0.933557,
+		-0.851615, -0.220702, -0.47544,
+		0.848886, 0.341829, -0.403169,
+		-0.156129, -0.687241, 0.709453,
+		-0.665651, 0.626724, 0.405124,
+		0.595914, -0.674582, 0.43569,
+		0.171025, -0.509292, 0.843428,
+		0.78605, 0.536414, -0.307222,
+		0.18905, -0.791613, 0.581042,
+		-0.294916, 0.844994, 0.446105,
+		0.342031, -0.58736, -0.7335,
+		0.57155, 0.7869, 0.232635,
+		0.885026, -0.408223, 0.223791,
+		-0.789518, 0.571645, 0.223347,
+		0.774571, 0.31566, 0.548087,
+		-0.79695, -0.0433603, -0.602487,
+		-0.142425, -0.473249, -0.869339,
+		-0.0698838, 0.170442, 0.982886,
+		0.687815, -0.484748, 0.540306,
+		0.543703, -0.534446, -0.647112,
+		0.97186, 0.184391, -0.146588,
+		0.707084, 0.485713, -0.513921,
+		0.942302, 0.331945, 0.043348,
+		0.499084, 0.599922, 0.625307,
+		-0.289203, 0.211107, 0.9337,
+		0.412433, -0.71667, -0.56239,
+		0.87721, -0.082816, 0.47291,
+		-0.420685, -0.214278, 0.881538,
+		0.752558, -0.0391579, 0.657361,
+		0.0765725, -0.996789, 0.0234082,
+		-0.544312, -0.309435, -0.779727,
+		-0.455358, -0.415572, 0.787368,
+		-0.874586, 0.483746, 0.0330131,
+		0.245172, -0.0838623, 0.965846,
+		0.382293, -0.432813, 0.81641,
+		-0.287735, -0.905514, 0.311853,
+		-0.667704, 0.704955, -0.239186,
+		0.717885, -0.464002, -0.518983,
+		0.976342, -0.214895, 0.0240053,
+		-0.0733096, -0.921136, 0.382276,
+		-0.986284, 0.151224, -0.0661379,
+		-0.899319, -0.429671, 0.0812908,
+		0.652102, -0.724625, 0.222893,
+		0.203761, 0.458023, -0.865272,
+		-0.030396, 0.698724, -0.714745,
+		-0.460232, 0.839138, 0.289887,
+		-0.0898602, 0.837894, 0.538386,
+		-0.731595, 0.0793784, 0.677102,
+		-0.447236, -0.788397, 0.422386,
+		0.186481, 0.645855, -0.740335,
+		-0.259006, 0.935463, 0.240467,
+		0.445839, 0.819655, -0.359712,
+		0.349962, 0.755022, -0.554499,
+		-0.997078, -0.0359577, 0.0673977,
+		-0.431163, -0.147516, -0.890133,
+		0.299648, -0.63914, 0.708316,
+		0.397043, 0.566526, -0.722084,
+		-0.502489, 0.438308, -0.745246,
+		0.0687235, 0.354097, 0.93268,
+		-0.0476651, -0.462597, 0.885286,
+		-0.221934, 0.900739, -0.373383,
+		-0.956107, -0.225676, 0.186893,
+		-0.187627, 0.391487, -0.900852,
+		-0.224209, -0.315405, 0.92209,
+		-0.730807, -0.537068, 0.421283,
+		-0.0353135, -0.816748, 0.575913,
+		-0.941391, 0.176991, -0.287153,
+		-0.154174, 0.390458, 0.90762,
+		-0.283847, 0.533842, 0.796519,
+		-0.482737, -0.850448, 0.209052,
+		-0.649175, 0.477748, 0.591886,
+		0.885373, -0.405387, -0.227543,
+		-0.147261, 0.181623, -0.972279,
+		0.0959236, -0.115847, -0.988624,
+		-0.89724, -0.191348, 0.397928,
+		0.903553, -0.428461, -0.00350461,
+		0.849072, -0.295807, -0.437693,
+		0.65551, 0.741754, -0.141804,
+		0.61598, -0.178669, 0.767232,
+		0.0112967, 0.932256, -0.361623,
+		-0.793031, 0.258012, 0.551845,
+		0.421933, 0.454311, 0.784585,
+		-0.319993, 0.0401618, -0.946568,
+		-0.81571, 0.551307, -0.175151,
+		-0.377644, 0.00322313, 0.925945,
+		0.129759, -0.666581, -0.734052,
+		0.601901, -0.654237, -0.457919,
+		-0.927463, -0.0343576, -0.372334,
+		-0.438663, -0.868301, -0.231578,
+		-0.648845, -0.749138, -0.133387,
+		0.507393, -0.588294, 0.629653,
+		0.726958, 0.623665, 0.287358,
+		0.411159, 0.367614, -0.834151,
+		0.806333, 0.585117, -0.0864016,
+		0.263935, -0.880876, 0.392932,
+		0.421546, -0.201336, 0.884174,
+		-0.683198, -0.569557, -0.456996,
+		-0.117116, -0.0406654, -0.992285,
+		-0.643679, -0.109196, -0.757465,
+		-0.561559, -0.62989, 0.536554,
+		0.0628422, 0.104677, -0.992519,
+		0.480759, -0.2867, -0.828658,
+		-0.228559, -0.228965, -0.946222,
+		-0.10194, -0.65706, -0.746914,
+		0.0689193, -0.678236, 0.731605,
+		0.401019, -0.754026, 0.52022,
+		-0.742141, 0.547083, -0.387203,
+		-0.00210603, -0.796417, -0.604745,
+		0.296725, -0.409909, -0.862513,
+		-0.260932, -0.798201, 0.542945,
+		-0.641628, 0.742379, 0.192838,
+		-0.186009, -0.101514, 0.97729,
+		0.106711, -0.962067, 0.251079,
+		-0.743499, 0.30988, -0.592607,
+		-0.795853, -0.605066, -0.0226607,
+		-0.828661, -0.419471, -0.370628,
+		0.0847218, -0.489815, -0.8677,
+		-0.381405, 0.788019, -0.483276,
+		0.282042, -0.953394, 0.107205,
+		0.530774, 0.847413, 0.0130696,
+		0.0515397, 0.922524, 0.382484,
+		-0.631467, -0.709046, 0.313852,
+		0.688248, 0.517273, 0.508668,
+		0.646689, -0.333782, -0.685845,
+		-0.932528, -0.247532, -0.262906,
+		0.630609, 0.68757, -0.359973,
+		0.577805, -0.394189, 0.714673,
+		-0.887833, -0.437301, -0.14325,
+		0.690982, 0.174003, 0.701617,
+		-0.866701, 0.0118182, 0.498689,
+		-0.482876, 0.727143, 0.487949,
+		-0.577567, 0.682593, -0.447752,
+		0.373768, 0.0982991, 0.922299,
+		0.170744, 0.964243, -0.202687,
+		0.993654, -0.035791, -0.106632,
+		0.587065, 0.4143, -0.695493,
+		-0.396509, 0.26509, -0.878924,
+		-0.0866853, 0.83553, -0.542563,
+		0.923193, 0.133398, -0.360443,
+		0.00379108, -0.258618, 0.965972,
+		0.239144, 0.245154, -0.939526,
+		0.758731, -0.555871, 0.33961,
+		0.295355, 0.309513, 0.903862,
+		0.0531222, -0.91003, -0.411124,
+		0.270452, 0.0229439, -0.96246,
+		0.563634, 0.0324352, 0.825387,
+		0.156326, 0.147392, 0.976646,
+		-0.0410141, 0.981824, 0.185309,
+		-0.385562, -0.576343, -0.720535,
+		0.388281, 0.904441, 0.176702,
+		0.945561, -0.192859, -0.262146,
+		0.844504, 0.520193, 0.127325,
+		0.0330893, 0.999121, -0.0257505,
+		-0.592616, -0.482475, -0.644999,
+		0.539471, 0.631024, -0.557476,
+		0.655851, -0.027319, -0.754396,
+		0.274465, 0.887659, 0.369772,
+		-0.123419, 0.975177, -0.183842,
+		-0.223429, 0.708045, 0.66989,
+		-0.908654, 0.196302, 0.368528,
+		-0.95759, -0.00863708, 0.288005,
+		0.960535, 0.030592, 0.276472,
+		-0.413146, 0.907537, 0.0754161,
+		-0.847992, 0.350849, -0.397259,
+		0.614736, 0.395841, 0.68221,
+		-0.503504, -0.666128, -0.550234,
+		-0.268833, -0.738524, -0.618314,
+		0.792737, -0.60001, -0.107502,
+		-0.637582, 0.508144, -0.579032,
+		0.750105, 0.282165, -0.598101,
+		-0.351199, -0.392294, -0.850155,
+		0.250126, -0.960993, -0.118025,
+		-0.732341, 0.680909, -0.0063274,
+		-0.760674, -0.141009, 0.633634,
+		0.222823, -0.304012, 0.926243,
+		0.209178, 0.505671, 0.836984,
+		0.757914, -0.56629, -0.323857,
+		-0.782926, -0.339196, 0.52151,
+		-0.462952, 0.585565, 0.665424,
+		0.61879, 0.194119, -0.761194,
+		0.741388, -0.276743, 0.611357,
+		0.707571, 0.702621, 0.0752872,
+		0.156562, 0.819977, 0.550569,
+		-0.793606, 0.440216, 0.42,
+		0.234547, 0.885309, -0.401517,
+		0.132598, 0.80115, -0.58359,
+		-0.377899, -0.639179, 0.669808,
+		-0.865993, -0.396465, 0.304748,
+		-0.624815, -0.44283, 0.643046,
+		-0.485705, 0.825614, -0.287146,
+		-0.971788, 0.175535, 0.157529,
+		-0.456027, 0.392629, 0.798675,
+		-0.0104443, 0.521623, -0.853112,
+		-0.660575, -0.74519, 0.091282,
+		-0.0157698, -0.307475, -0.951425,
+		-0.603467, -0.250192, 0.757121,
+		0.506876, 0.25006, 0.824952,
+		0.255404, 0.966794, 0.00884498,
+		0.466764, -0.874228, -0.133625,
+		0.475077, -0.0682351, -0.877295,
+		-0.224967, -0.938972, -0.260233,
+		-0.377929, -0.814757, -0.439705,
+		-0.305847, 0.542333, -0.782517,
+		0.26658, -0.902905, -0.337191,
+		0.0275773, 0.322158, -0.946284,
+		0.0185422, 0.716349, 0.697496,
+		-0.20483, 0.978416, 0.0273371,
+		-0.898276, 0.373969, 0.230752,
+		-0.00909378, 0.546594, 0.837349,
+		0.6602, -0.751089, 0.000959236,
+		0.855301, -0.303056, 0.420259,
+		0.797138, 0.0623013, -0.600574,
+		0.48947, -0.866813, 0.0951509,
+		0.251142, 0.674531, 0.694216,
+		-0.578422, -0.737373, -0.348867,
+		-0.254689, -0.514807, 0.818601,
+		0.374972, 0.761612, 0.528529,
+		0.640303, -0.734271, -0.225517,
+		-0.638076, 0.285527, 0.715075,
+		0.772956, -0.15984, -0.613995,
+		0.798217, -0.590628, 0.118356,
+		-0.986276, -0.0578337, -0.154644,
+		-0.312988, -0.94549, 0.0899272,
+		-0.497338, 0.178325, 0.849032,
+		-0.101136, -0.981014, 0.165477,
+		-0.521688, 0.0553434, -0.851339,
+		-0.786182, -0.583814, 0.202678,
+		-0.565191, 0.821858, -0.0714658,
+		0.437895, 0.152598, -0.885981,
+		-0.92394, 0.353436, -0.14635,
+		0.212189, -0.815162, -0.538969,
+		-0.859262, 0.143405, -0.491024,
+		0.991353, 0.112814, 0.0670273,
+		0.0337884, -0.979891, -0.196654,
+	];
+	
+}

+ 53 - 0
hxd/Rand.hx

@@ -0,0 +1,53 @@
+package hxd;
+
+class Rand {
+
+	var seed : Int;
+	var seed2 : Int;
+
+	public function new( seed : Int ) {
+		this.seed = seed;
+		this.seed2 = hash(seed);
+		if( this.seed == 0 ) this.seed = 1;
+		if( this.seed2 == 0 ) this.seed2 = 1;
+	}
+	
+	// this is the Murmur3 hashing function which has both excellent distribution and good randomness
+	public static function hash(n, seed = 5381) {
+		n *= 0xcc9e2d51;
+		n = (n << 15) | (n >>> 17);
+		n *= 0x1b873593;
+		var h = seed;
+		h ^= n;
+		h = (h << 13) | (h >>> 19);
+		h = h*5 + 0xe6546b64;
+		h ^= h >> 16;
+		h *= 0x85ebca6b;
+		h ^= h >> 13;
+		h *= 0xc2b2ae35;
+		h ^= h >> 16;
+		return h;
+	}
+
+	public inline function random( n ) {
+		return uint() % n;
+	}
+
+	public inline function rand() {
+		// we can't use a divider > 16807 or else two consecutive seeds
+		// might generate a similar float
+		return (uint() % 10007) / 10007.0;
+	}
+
+	// this is two Marsaglia Multiple-with-Carry (MWC) generators combined
+	inline function int() : Int {
+		seed = 36969 * (seed & 0xFFFF) + (seed >> 16);
+		seed2 = 18000 * (seed2 & 0xFFFF) + (seed2 >> 16);
+		return (seed<<16) + seed2;
+	}
+	
+	inline function uint() {
+		return int() & 0x3FFFFFFF;
+	}
+
+}

+ 10 - 0
hxd/Res.hx

@@ -0,0 +1,10 @@
+package hxd;
+
+@:build(hxd.res.FileTree.build())
+class Res {
+	
+	public static function load(name:String) {
+		return loader.load(name);
+	}
+	
+}

+ 209 - 0
hxd/Stage.hx

@@ -0,0 +1,209 @@
+package hxd;
+
+class Stage {
+	
+	#if flash
+	var stage : flash.display.Stage;
+	var fsDelayed : Bool;
+	#end
+	var resizeEvents : List < Void -> Void > ;
+	var eventTargets : List<Event -> Void>;
+	
+	public var width(get, null) : Float;
+	public var height(get, null) : Float;
+	public var mouseX(get, null) : Float;
+	public var mouseY(get, null) : Float;
+	
+	function new() {
+		eventTargets = new List();
+		resizeEvents = new List();
+		#if flash
+		stage = flash.Lib.current.stage;
+		stage.scaleMode = flash.display.StageScaleMode.NO_SCALE;
+		stage.addEventListener(flash.events.Event.RESIZE, onResize);
+		if( hxd.System.isTouch ) {
+			flash.ui.Multitouch.inputMode = flash.ui.MultitouchInputMode.TOUCH_POINT;
+			stage.addEventListener(flash.events.TouchEvent.TOUCH_BEGIN, onTouchDown);
+			stage.addEventListener(flash.events.TouchEvent.TOUCH_MOVE, onTouchMove);
+			stage.addEventListener(flash.events.TouchEvent.TOUCH_END, onTouchUp);
+		} else {
+			stage.addEventListener(flash.events.MouseEvent.MOUSE_DOWN, onMouseDown);
+			stage.addEventListener(flash.events.MouseEvent.MOUSE_MOVE, onMouseMove);
+			stage.addEventListener(flash.events.MouseEvent.MOUSE_UP, onMouseUp);
+			stage.addEventListener(flash.events.MouseEvent.MOUSE_WHEEL, onMouseWheel);
+			stage.addEventListener(flash.events.KeyboardEvent.KEY_DOWN, onKeyDown);
+			stage.addEventListener(flash.events.KeyboardEvent.KEY_UP, onKeyUp);
+		}
+		#elseif js
+		js.Browser.window.addEventListener("mousedown", onMouseDown);
+		js.Browser.window.addEventListener("mouseup", onMouseUp);
+		js.Browser.window.addEventListener("keydown", onKeyDown);
+		js.Browser.window.addEventListener("keyup", onKeyUp);
+		js.Browser.window.addEventListener("resize", onResize);
+		#end
+	}
+	
+	public function event( e : hxd.Event ) {
+		for( et in eventTargets )
+			et(e);
+	}
+	
+	public function addEventTarget(et) {
+		eventTargets.add(et);
+	}
+
+	public function removeEventTarget(et) {
+		eventTargets.remove(et);
+	}
+	
+	public function addResizeEvent( f : Void -> Void ) {
+		resizeEvents.push(f);
+	}
+
+	public function removeResizeEvent( f : Void -> Void ) {
+		resizeEvents.remove(f);
+	}
+	
+	public function getFrameRate() : Float {
+		#if flash
+		return stage.frameRate;
+		#else
+		return 60.;
+		#end
+	}
+
+	public function setFullScreen( v : Bool ) {
+		#if flash
+		var isAir = flash.system.Capabilities.playerType == "Desktop";
+		var state = v ? (isAir ? flash.display.StageDisplayState.FULL_SCREEN_INTERACTIVE : flash.display.StageDisplayState.FULL_SCREEN) : flash.display.StageDisplayState.NORMAL;
+		if( stage.displayState != state ) {
+			var t = flash.Lib.getTimer();
+			// delay first fullsrceen toggle on OSX/Air to prevent the command window to spawn over
+			if( v && isAir && t < 5000 && !fsDelayed && flash.system.Capabilities.os.indexOf("Mac") != -1 ) {
+				fsDelayed = true;
+				haxe.Timer.delay(function() this.setFullScreen(v), 1000);
+				return;
+			}
+			stage.displayState = state;
+		}
+		#else
+		#end
+	}
+	
+	static var inst = null;
+	public static function getInstance() {
+		if( inst == null ) inst = new Stage();
+		return inst;
+	}
+	
+#if flash
+
+	inline function get_mouseX() {
+		return stage.mouseX;
+	}
+
+	inline function get_mouseY() {
+		return stage.mouseY;
+	}
+
+	inline function get_width() {
+		return stage.stageWidth;
+	}
+
+	inline function get_height() {
+		return stage.stageHeight;
+	}
+	
+	function onResize(_) {
+		for( e in resizeEvents )
+			e();
+	}
+
+	function onMouseDown(e:Dynamic) {
+		event(new Event(EPush, mouseX, mouseY));
+	}
+
+	function onMouseUp(e:Dynamic) {
+		event(new Event(ERelease, mouseX, mouseY));
+	}
+	
+	function onMouseMove(e:Dynamic) {
+		event(new Event(EMove, mouseX, mouseY));
+	}
+	
+	function onMouseWheel(e:flash.events.MouseEvent) {
+		var ev = new Event(EWheel, mouseX, mouseY);
+		ev.wheelDelta = -e.delta / 3.0;
+		event(ev);
+	}
+	
+	function onKeyUp(e:flash.events.KeyboardEvent) {
+		var ev = new Event(EKeyUp);
+		ev.keyCode = e.keyCode;
+		ev.charCode = e.charCode;
+		event(ev);
+	}
+
+	function onKeyDown(e:flash.events.KeyboardEvent) {
+		var ev = new Event(EKeyDown);
+		ev.keyCode = e.keyCode;
+		ev.charCode = e.charCode;
+		event(ev);
+	}
+	
+	function onTouchDown(e:flash.events.TouchEvent) {
+		var ev = new Event(EPush, e.localX, e.localY);
+		ev.touchId = e.touchPointID;
+		event(ev);
+	}
+
+	function onTouchUp(e:flash.events.TouchEvent) {
+		var ev = new Event(ERelease, e.localX, e.localY);
+		ev.touchId = e.touchPointID;
+		event(ev);
+	}
+	
+	function onTouchMove(e:flash.events.TouchEvent) {
+		var ev = new Event(EMove, e.localX, e.localY);
+		ev.touchId = e.touchPointID;
+		event(ev);
+	}
+	
+#elseif js
+
+	function get_width() {
+		return js.Browser.document.width;
+	}
+
+	function get_height() {
+		return js.Browser.document.height;
+	}
+
+	function get_mouseX() {
+		throw "TODO";
+		return 0.;
+	}
+
+	function get_mouseY() {
+		throw "TODO";
+		return 0.;
+	}
+	
+	function onMouseDown(e) {
+	}
+
+	function onMouseUp(e) {
+	}
+
+	function onKeyDown(e) {
+	}
+
+	function onKeyUp(e) {
+	}
+
+	function onResize(e) {
+	}
+
+#end
+
+}

+ 59 - 10
h3d/System.hx → hxd/System.hx

@@ -1,4 +1,4 @@
-package h3d;
+package hxd;
 
 enum Cursor {
 	Default;
@@ -9,17 +9,17 @@ enum Cursor {
 
 class System {
 	
-	#if !macro
-
 	public static var width(get,never) : Int;
 	public static var height(get,never) : Int;
 	public static var isTouch(get,never) : Bool;
 	public static var isWindowed(get,never) : Bool;
-	
+	public static var lang(get,never) : String;
 	public static var isAndroid(get, never) : Bool;
 	
 	public static var screenDPI(get,never) : Float;
 
+	#if flash
+	
 	static function get_isWindowed() {
 		var p = flash.system.Capabilities.playerType;
 		return p == "ActiveX" || p == "PlugIn" || p == "StandAlone" || p == "Desktop";
@@ -109,14 +109,63 @@ class System {
 		return name;
 	}
 	
+	
+	static function get_lang() {
+		return flash.system.Capabilities.language;
+	}
+	
+	#elseif js
 
-	#end
+	static var LOOP = null;
+	static var LOOP_INIT = false;
+	
+	static function loopFunc() {
+		var window : Dynamic = js.Browser.window;
+		var rqf : Dynamic = window.requestAnimationFrame ||
+			window.webkitRequestAnimationFrame ||
+			window.mozRequestAnimationFrame;
+		rqf(loopFunc);
+		if( LOOP != null ) LOOP();
+	}
 	
-	public static macro function getFileContent( file : String ) {
-		var file = haxe.macro.Context.resolvePath(file);
-		var m = haxe.macro.Context.getLocalClass().get().module;
-		haxe.macro.Context.registerModuleDependency(m, file);
-		return macro $v{sys.io.File.getContent(file)};
+	public static function setLoop( f : Void -> Void ) {
+		if( !LOOP_INIT ) {
+			LOOP_INIT = true;
+			loopFunc();
+		}
+		LOOP = f;
+	}
+
+	public static function setCursor( c : Cursor ) {
+		throw "TODO";
 	}
 	
+	static function get_screenDPI() {
+		return 72.;
+	}
+	
+	static function get_isAndroid() {
+		return false;
+	}
+	
+	static function get_isWindowed() {
+		return true;
+	}
+	
+	static function get_isTouch() {
+		return false;
+	}
+	
+	static function get_width() {
+		return js.Browser.document.width;
+	}
+	
+	static function get_height() {
+		return js.Browser.document.height;
+	}
+	
+	#else
+
+	#end
+	
 }

+ 34 - 0
hxd/Timer.hx

@@ -0,0 +1,34 @@
+package hxd;
+
+class Timer {
+
+	public static var wantedFPS = 60;
+	public static var maxDeltaTime = 0.5;
+	public static var oldTime = haxe.Timer.stamp();
+	public static var tmod_factor = 0.95;
+	public static var calc_tmod : Float = 1;
+	public static var tmod : Float = 1;
+	public static var deltaT : Float = 1;
+	static var frameCount = 0;
+
+	public inline static function update() {
+		frameCount++;
+		var newTime = haxe.Timer.stamp();
+		deltaT = newTime - oldTime;
+		oldTime = newTime;
+		if( deltaT < maxDeltaTime )
+			calc_tmod = calc_tmod * tmod_factor + (1 - tmod_factor) * deltaT * wantedFPS;
+		else
+			deltaT = 1 / wantedFPS;
+		tmod = calc_tmod;
+	}
+
+	public inline static function fps() : Float {
+		return wantedFPS/tmod ;
+	}
+	
+	public static function skip() {
+		oldTime = haxe.Timer.stamp();
+	}
+
+}

+ 8 - 0
hxd/clipper/ClipType.hx

@@ -0,0 +1,8 @@
+package hxd.clipper;
+
+enum ClipType {
+	Intersection;
+	Union;
+	Difference;
+	Xor;
+}

+ 3602 - 0
hxd/clipper/Clipper.hx

@@ -0,0 +1,3602 @@
+/*******************************************************************************
+*																				*
+* Author	:	Angus Johnson													*
+* Version	 :	5.1.6															*
+* Date		:	23 May 2013													 	*
+* Website	 :	http://www.angusj.com											*
+* Copyright :	Angus Johnson 2010-2013										 	*
+*																				*
+* License:																	 	*
+* Use, modification & distribution is subject to Boost Software License Ver 1. 	*
+* http://www.boost.org/LICENSE_1_0.txt										 	*
+*																				*
+* Attributions:																	*
+* The code in this library is an extension of Bala Vatti's clipping algorithm: 	*
+* "A generic solution to polygon clipping"									 	*
+* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63.			 	*
+* http://portal.acm.org/citation.cfm?id=129906								 	*
+*																				*
+* Computer graphics and geometric modeling: implementation and algorithms		*
+* By Max K. Agoston																*
+* Springer; 1 edition (January 4, 2005)											*
+* http://books.google.com/books?q=vatti+clipping+agoston						*
+*																				*
+* See also:																		*
+* "Polygon Offsetting by Computing Winding Numbers"								*
+* Paper no. DETC2005-85513 pp. 565-575										 	*
+* ASME 2005 International Design Engineering Technical Conferences			 	*
+* and Computers and Information in Engineering Conference (IDETC/CIE2005)		*
+* September 24-28, 2005 , Long Beach, California, USA							*
+* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf				*
+*																				*
+*******************************************************************************/
+// Ported from C# by @ncannasse
+//		original version uses 64 bits integer and 128 bit "bigint", we use 32 bit integer instead
+// 		so valid range should be limited to [-32768,32767] in order to prevent overflow while multiplying
+
+package hxd.clipper;
+
+@:allow(hxd.clipper)
+private class PolyNode {
+
+	public var parent(default,null) : PolyNode;
+	public var childs(default,null) : Array<PolyNode>;
+	var polygon : Polygon;
+	var index : Int;
+
+	public function new() {
+		polygon = new Polygon([]);
+		childs = [];
+	}
+	
+	private function isHoleNode() {
+		var result = true;
+		var node = parent;
+		while( node != null ) {
+			result = !result;
+			node = node.parent;
+		}
+		return result;
+	}
+
+	public var childCount(get, never) : Int;
+	
+	inline function get_childCount() {
+		return childs.length;
+	}
+
+	public var contour(get, never) : Polygon;
+	
+	inline function get_contour() {
+		return polygon;
+	}
+
+	function addChild(child:PolyNode) {
+		var cnt = childs.length;
+		childs.push(child);
+		child.parent = this;
+		child.index = cnt;
+	}
+
+	public function getNext()
+	{
+		if (childs.length > 0)
+			return childs[0];
+		else
+			return getNextSiblingUp();
+	}
+
+	function getNextSiblingUp()
+	{
+		if (parent == null)
+			return null;
+		else if (index == parent.childs.length - 1)
+			return parent.getNextSiblingUp();
+		else
+			return parent.childs[index + 1];
+	}
+
+	public inline function isHole() {
+		return isHoleNode();
+	}
+
+}
+
+
+private class PolyTree extends PolyNode {
+
+	public var allPolys : Array<PolyNode>;
+
+	public function new() {
+		super();
+		allPolys = [];
+	}
+
+	public function toPolygons(polygons:Polygons) {
+		polygons.splice(0,polygons.length);
+		addRec(this, polygons);
+	}
+
+	static function addRec(polynode:PolyNode,polygons:Polygons) {
+		if (polynode.contour.length > 0)
+			polygons.push(polynode.contour);
+		for(pn in polynode.childs)
+			addRec(pn, polygons);
+	}
+	
+	public function clear() {
+		allPolys = [];
+		childs = [];
+	}
+	
+	public function getFirst() {
+		if( childs.length > 0)
+			return childs[0];
+		return null;
+    }
+
+	public var total(get, never) : Int;
+	
+	inline function get_total() {
+		return allPolys.length;
+    }
+	
+}
+
+@:generic
+private class Ref<T> {
+	public var val : T;
+	public inline function new(?v:T) {
+		val = v;
+	}
+}
+
+private class EdgeSide {
+	public static inline var Left = 1;
+	public static inline var Right = 2;
+}
+
+private class Protects {
+	public static inline var None = 0;
+	public static inline var Left = 1;
+	public static inline var Right = 1;
+	public static inline var Both = 3;
+}
+
+private class Direction {
+	public static inline var RightToLeft = 0;
+	public static inline var LeftToRight = 1;
+}
+	
+private class TEdge {
+	public var xbot : Int;
+	public var ybot : Int;
+	public var xcurr : Int;
+	public var ycurr : Int;
+	public var xtop : Int;
+	public var ytop : Int;
+	public var dx : Float;
+	public var deltaX : Int;
+	public var deltaY : Int;
+	public var polyType : PolyType;
+	public var side : Int;
+	public var windDelta : Int; //1 or -1 depending on winding direction
+	public var windCnt : Int;
+	public var windCnt2 : Int; //winding count of the opposite polytype
+	public var outIdx : Int;
+	public var next : TEdge;
+	public var prev : TEdge;
+	public var nextInLML : TEdge;
+	public var nextInAEL : TEdge;
+	public var prevInAEL : TEdge;
+	public var nextInSEL : TEdge;
+	public var prevInSEL : TEdge;
+	public function new() {
+	}
+}
+
+private class IntersectNode {
+	public var edge1 : TEdge;
+	public var edge2 : TEdge;
+	public var pt : Point;
+	public var next : IntersectNode;
+	public function new() {
+	}
+}
+
+private class LocalMinima {
+	public var y : Int;
+	public var leftBound : TEdge;
+	public var rightBound : TEdge;
+	public var next : LocalMinima;
+	public function new() {
+	}
+}
+
+private class Scanbeam {
+	public var y : Int;
+	public var next : Scanbeam;
+	public function new() {
+	}
+}
+
+private class OutRec {
+	public var idx : Int;
+	public var isHole : Bool;
+	public var firstLeft : OutRec;
+	public var pts : OutPt;
+	public var bottomPt : OutPt;
+	public var polyNode : PolyNode;
+	public function new() {
+	}
+}
+
+private class OutPt {
+	public var idx : Int;
+	public var pt : Point;
+	public var next : OutPt;
+	public var prev : OutPt;
+	public function new() {
+	}
+}
+
+private class JoinRec {
+	public var pt1a : Point;
+	public var pt1b : Point;
+	public var poly1Idx : Int;
+	public var pt2a : Point;
+	public var pt2b : Point;
+	public var poly2Idx : Int;
+	public function new() {
+	}
+}
+
+private class HorzJoinRec {
+	public var edge : TEdge;
+	public var savedIdx : Int;
+	public function new() {
+	}
+}
+
+private typedef DoublePoint = h2d.col.Point;
+
+//------------------------------------------------------------------------------
+
+private class PolyOffsetBuilder
+{
+	private var m_p : Polygons;
+	private var currentPoly : Polygon;
+	private var normals : Array<DoublePoint>;
+	private var m_delta : Float;
+	private var m_r : Float;
+	private var m_rmin : Float;
+	private var m_i : Int;
+	private var m_j : Int;
+	private var m_k : Int;
+	private static inline var m_buffLength = 128;
+
+	function OffsetPoint(jointype:JoinType,limit:Float)
+	{
+		switch (jointype)
+		{
+			case JoinType.Miter:
+				{
+					m_r = 1 + (normals[m_j].x * normals[m_k].x +
+						normals[m_j].y * normals[m_k].y);
+					if (m_r >= m_rmin) DoMiter(); else DoSquare();
+				}
+			case JoinType.Square: DoSquare();
+			case JoinType.Round: DoRound(limit);
+		}
+		m_k = m_j;
+	}
+	//------------------------------------------------------------------------------
+
+	public function new(pts:Polygons, solution:Polygons, isPolygon:Bool, delta:Float,jointype:JoinType, endtype:EndType, limit:Float = 0)
+	{
+		//precondition: solution != pts
+		normals = [];
+		
+		if (delta == 0) {solution = pts; return; }
+		m_p = pts;
+		m_delta = delta;
+		m_rmin = 0.5;
+
+		if (jointype == JoinType.Miter)
+		{
+			if (limit > 2) m_rmin = 2.0 / (limit * limit);
+			limit = 0.25; //just in case endtype == etRound
+		}
+		else
+		{
+			if (limit <= 0) limit = 0.25;
+			else if (limit > Math.abs(delta)) limit = Math.abs(delta);
+		}
+
+		var deltaSq = delta * delta;
+		solution.splice(0,solution.length);
+		var m_i = -1;
+		while( ++m_i < pts.length )
+		{
+			var len:Int = pts[m_i].length;
+			if (len == 0 || (len < 3 && delta <= 0))
+				continue;
+			else if (len == 1)
+			{
+				currentPoly = new Polygon();
+				currentPoly = Clipper.BuildArc(pts[m_i][0], 0, 2*Math.PI, delta, limit);
+				solution.push(currentPoly);
+				continue;
+			}
+
+			var forceClose = ClipperBase.PointsEqual(pts[m_i][0], pts[m_i][len - 1]);
+			if (forceClose) len--;
+
+			//build normals
+			normals = [];
+			
+			for( j in 0...len-1 )
+				normals.push(Clipper.GetUnitNormal(pts[m_i][j], pts[m_i][j+1]));
+			if (isPolygon || forceClose)
+				normals.push(Clipper.GetUnitNormal(pts[m_i][len - 1], pts[m_i][0]));
+			else {
+				var n = normals[len - 2];
+				normals.push(new DoublePoint(n.x,n.y));
+			}
+
+			currentPoly = new Polygon();
+			if (isPolygon || forceClose)
+			{
+				m_k = len - 1;
+				m_j = 0;
+				while( m_j < len ) {
+					OffsetPoint(jointype, limit);
+					m_j++;
+				}
+				solution.push(currentPoly);
+				if (!isPolygon)
+				{
+					currentPoly = new Polygon();
+					m_delta = -m_delta;
+					m_k = len - 1;
+					m_j = 0;
+					while( m_j < len ) {
+						OffsetPoint(jointype, limit);
+						m_j++;
+					}
+					m_delta = -m_delta;
+					currentPoly.reverse();
+					solution.push(currentPoly);
+				}
+			}
+			else
+			{
+				m_k = 0;
+				m_j = 1;
+				while( m_j < len - 1 ) {
+					OffsetPoint(jointype, limit);
+					m_j++;
+				}
+
+				var pt1;
+				if (endtype == EndType.Butt)
+				{
+					m_j = len - 1;
+					pt1 = new Point(Round(pts[m_i][m_j].x + normals[m_j].x *
+						delta), Round(pts[m_i][m_j].y + normals[m_j].y * delta));
+					AddPoint(pt1);
+					pt1 = new Point(Round(pts[m_i][m_j].x - normals[m_j].x *
+						delta), Round(pts[m_i][m_j].y - normals[m_j].y * delta));
+					AddPoint(pt1);
+				}
+				else
+				{
+					m_j = len - 1;
+					m_k = len - 2;
+					normals[m_j].x = -normals[m_j].x;
+					normals[m_j].y = -normals[m_j].y;
+					if (endtype == EndType.Square) DoSquare();
+					else DoRound(limit);
+				}
+
+				//re-build Normals
+				var j = len - 1;
+				while( j > 0 ) {
+					normals[j].x = -normals[j - 1].x;
+					normals[j].y = -normals[j - 1].y;
+					j--;
+				}
+				normals[0].x = -normals[1].x;
+				normals[0].y = -normals[1].y;
+
+				m_k = len - 1;
+				m_j = m_k - 1;
+				while( m_j > 0 ) {
+					OffsetPoint(jointype, limit);
+					m_j--;
+				}
+
+				if (endtype == EndType.Butt)
+				{
+					pt1 = new Point(Round(pts[m_i][0].x - normals[0].x * delta),
+						Round(pts[m_i][0].y - normals[0].y * delta));
+					AddPoint(pt1);
+					pt1 = new Point(Round(pts[m_i][0].x + normals[0].x * delta),
+						Round(pts[m_i][0].y + normals[0].y * delta));
+					AddPoint(pt1);
+				}
+				else
+				{
+					m_k = 1;
+					if (endtype == EndType.Square) DoSquare();
+					else DoRound(limit);
+				}
+				solution.push(currentPoly);
+			}
+		}
+
+		//finally, clean up untidy corners
+		var clpr:Clipper = new Clipper();
+		clpr.addPolygons(solution, PolyType.Subject);
+		if (delta > 0)
+		{
+			clpr.execute(ClipType.Union, solution, PolyFillType.Positive, PolyFillType.Positive);
+		}
+		else
+		{
+			var r:Rect = clpr.GetBounds();
+			var outer:Polygon = new Polygon();
+
+			outer.add(r.left - 10, r.bottom + 10);
+			outer.add(r.right + 10, r.bottom + 10);
+			outer.add(r.right + 10, r.top - 10);
+			outer.add(r.left - 10, r.top - 10);
+
+			clpr.addPolygon(outer, PolyType.Subject);
+			clpr.reverseSolution = true;
+			clpr.execute(ClipType.Union, solution, PolyFillType.Negative, PolyFillType.Negative);
+			if (solution.length > 0) solution.shift();
+		}
+	}
+	//------------------------------------------------------------------------------
+
+	inline function AddPoint(pt:Point)
+	{
+		currentPoly.addPoint(pt);
+	}
+	//------------------------------------------------------------------------------
+
+	function DoSquare()
+	{
+		var pt1 = new Point(Round(m_p[m_i][m_j].x + normals[m_k].x * m_delta),
+			Round(m_p[m_i][m_j].y + normals[m_k].y * m_delta));
+		var pt2 = new Point(Round(m_p[m_i][m_j].x + normals[m_j].x * m_delta),
+			Round(m_p[m_i][m_j].y + normals[m_j].y * m_delta));
+		if ((normals[m_k].x * normals[m_j].y - normals[m_j].x * normals[m_k].y) * m_delta >= 0)
+		{
+			var a1 = Math.atan2(normals[m_k].y, normals[m_k].x);
+			var a2 = Math.atan2(-normals[m_j].y, -normals[m_j].x);
+			a1 = Math.abs(a2 - a1);
+			if (a1 > Math.PI) a1 = Math.PI * 2 - a1;
+			var dx:Float = Math.tan((Math.PI - a1) / 4) * Math.abs(m_delta);
+			pt1 = new Point(Std.int(pt1.x - normals[m_k].y * dx),
+				Std.int(pt1.y + normals[m_k].x * dx));
+			AddPoint(pt1);
+			pt2 = new Point(Std.int(pt2.x + normals[m_j].y * dx),
+				Std.int(pt2.y - normals[m_j].x * dx));
+			AddPoint(pt2);
+		}
+		else
+		{
+			AddPoint(pt1);
+			AddPoint(m_p[m_i][m_j]);
+			AddPoint(pt2);
+		}
+	}
+	//------------------------------------------------------------------------------
+
+
+	inline function Round(v:Float) {
+		return Math.round(v);
+	}
+	
+	function DoMiter()
+	{
+		if ((normals[m_k].x * normals[m_j].y - normals[m_j].x * normals[m_k].y) * m_delta >= 0)
+		{
+			var q:Float = m_delta / m_r;
+			AddPoint(new Point(Round(m_p[m_i][m_j].x +
+				(normals[m_k].x + normals[m_j].x) * q),
+				Round(m_p[m_i][m_j].y + (normals[m_k].y + normals[m_j].y) * q)));
+		}
+		else
+		{
+			var pt1 = new Point(Round(m_p[m_i][m_j].x + normals[m_k].x * m_delta),
+				Round(m_p[m_i][m_j].y + normals[m_k].y * m_delta));
+			var pt2 = new Point(Round(m_p[m_i][m_j].x + normals[m_j].x * m_delta),
+				Round(m_p[m_i][m_j].y + normals[m_j].y * m_delta));
+			AddPoint(pt1);
+			AddPoint(m_p[m_i][m_j]);
+			AddPoint(pt2);
+		}
+	}
+	//------------------------------------------------------------------------------
+
+	function DoRound(Limit:Float)
+	{
+		var pt1 = new Point(Round(m_p[m_i][m_j].x + normals[m_k].x * m_delta),
+			Round(m_p[m_i][m_j].y + normals[m_k].y * m_delta));
+		var pt2 = new Point(Round(m_p[m_i][m_j].x + normals[m_j].x * m_delta),
+			Round(m_p[m_i][m_j].y + normals[m_j].y * m_delta));
+		AddPoint(pt1);
+		//round off reflex angles (ie > 180 deg) unless almost flat (ie < 10deg).
+		//cross product normals < 0 . angle > 180 deg.
+		//dot product normals == 1 . no angle
+		if ((normals[m_k].x * normals[m_j].y - normals[m_j].x * normals[m_k].y) * m_delta >= 0)
+		{
+			if ((normals[m_j].x * normals[m_k].x + normals[m_j].y * normals[m_k].y) < 0.985)
+			{
+				var a1 = Math.atan2(normals[m_k].y, normals[m_k].x);
+				var a2 = Math.atan2(normals[m_j].y, normals[m_j].x);
+				if (m_delta > 0 && a2 < a1) a2 += Math.PI * 2;
+				else if (m_delta < 0 && a2 > a1) a2 -= Math.PI * 2;
+				var arc:Polygon = Clipper.BuildArc(m_p[m_i][m_j], a1, a2, m_delta, Limit);
+				for ( pt in arc.points )
+					AddPoint(pt);
+			}
+		}
+		else
+			AddPoint(m_p[m_i][m_j]);
+		AddPoint(pt2);
+	}
+	//------------------------------------------------------------------------------
+
+}
+
+@:allow(hxd.clipper)
+private class ClipperBase
+{
+	static inline var HORIZONTAL = -9007199254740992.; // -2^53, big enough for JS
+
+	var m_MinimaList : LocalMinima;
+	var m_CurrentLM : LocalMinima;
+	var m_edges : Array<Array<TEdge>>;
+
+	//------------------------------------------------------------------------------
+
+	inline static function PointsEqual(pt1:Point, pt2:Point) {
+		return pt1.x == pt2.x && pt1.y == pt2.y;
+	}
+	
+	inline function abs(i:Int):Int {
+		return i < 0 ? -i : i;
+	}
+	
+	//------------------------------------------------------------------------------
+
+	function PointIsVertex(pt:Point, pp:OutPt) {
+		var pp2 = pp;
+		do {
+			if( ClipperBase.PointsEqual(pp2.pt, pt) ) return true;
+			pp2 = pp2.next;
+		} while (pp2 != pp);
+		return false;
+	}
+	//------------------------------------------------------------------------------
+
+	function PointOnLineSegment(pt:Point, linePt1:Point, linePt2:Point) {
+		return ((pt.x == linePt1.x) && (pt.y == linePt1.y)) ||
+			((pt.x == linePt2.x) && (pt.y == linePt2.y)) ||
+			(((pt.x > linePt1.x) == (pt.x < linePt2.x)) &&
+			((pt.y > linePt1.y) == (pt.y < linePt2.y)) &&
+			((pt.x - linePt1.x) * (linePt2.y - linePt1.y) ==
+			(linePt2.x - linePt1.x) * (pt.y - linePt1.y)));
+	}
+	//------------------------------------------------------------------------------
+
+	function PointOnPolygon(pt:Point, pp:OutPt)
+	{
+		var pp2 = pp;
+		while (true) {
+			if (PointOnLineSegment(pt, pp2.pt, pp2.next.pt))
+				return true;
+			pp2 = pp2.next;
+			if (pp2 == pp) break;
+		}
+		return false;
+	}
+	
+	//------------------------------------------------------------------------------
+
+	function PointInPolygon(pt:Point, pp:OutPt)
+	{
+		var pp2 = pp;
+		var result = false;
+		/*
+		if (useFulllongRange)
+		{
+			do
+			{
+				if ((((pp2.pt.y <= pt.y) && (pt.y < pp2.prev.pt.y)) ||
+					((pp2.prev.pt.y <= pt.y) && (pt.y < pp2.pt.y))) &&
+					new Int128(pt.x - pp2.pt.x) <
+					Int128.Int128Mul(pp2.prev.pt.x - pp2.pt.x,	pt.y - pp2.pt.y) /
+					new Int128(pp2.prev.pt.y - pp2.pt.y))
+					result = !result;
+				pp2 = pp2.next;
+			}
+			while (pp2 != pp);
+		}
+		else
+		*/
+		{
+			do
+			{
+				if ((((pp2.pt.y <= pt.y) && (pt.y < pp2.prev.pt.y)) ||
+				((pp2.prev.pt.y <= pt.y) && (pt.y < pp2.pt.y))) &&
+				(pt.x - pp2.pt.x < (pp2.prev.pt.x - pp2.pt.x) * (pt.y - pp2.pt.y) /
+				(pp2.prev.pt.y - pp2.pt.y))) result = !result;
+				pp2 = pp2.next;
+			}
+			while (pp2 != pp);
+		}
+		return result;
+	}
+	//------------------------------------------------------------------------------
+
+	static inline function SlopesEqual(e1:TEdge, e2:TEdge) {
+		/*
+		if (useFullRange)
+			return Int128.Int128Mul(e1.deltaY, e2.deltaX) ==
+				Int128.Int128Mul(e1.deltaX, e2.deltaY);
+		else
+		*/
+		return e1.deltaY * e2.deltaX == e1.deltaX * e2.deltaY;
+	}
+	//------------------------------------------------------------------------------
+
+	static inline function SlopesEqual3(pt1:Point, pt2:Point,pt3:Point) {
+		/*if (useFullRange)
+			return Int128.Int128Mul(pt1.y - pt2.y, pt2.x - pt3.x) ==
+				Int128.Int128Mul(pt1.x - pt2.x, pt2.y - pt3.y);
+		else*/ return
+			(pt1.y - pt2.y) * (pt2.x - pt3.x) - (pt1.x - pt2.x) * (pt2.y - pt3.y) == 0;
+	}
+	//------------------------------------------------------------------------------
+
+	static inline function SlopesEqual4(pt1:Point, pt2:Point, pt3:Point, pt4:Point) {
+		/*if (useFullRange)
+			return Int128.Int128Mul(pt1.y - pt2.y, pt3.x - pt4.x) ==
+				Int128.Int128Mul(pt1.x - pt2.x, pt3.y - pt4.y);
+		else*/ return
+			(pt1.y - pt2.y) * (pt3.x - pt4.x) - (pt1.x - pt2.x) * (pt3.y - pt4.y) == 0;
+	}
+	
+	//------------------------------------------------------------------------------
+
+	function new()
+	{
+		m_edges = [];
+		m_MinimaList = null;
+		m_CurrentLM = null;
+	}
+	//------------------------------------------------------------------------------
+
+	//destructor - commented out since I gather this impedes the GC
+	//~ClipperBase()
+	//{
+	//	Clear();
+	//}
+	//------------------------------------------------------------------------------
+
+	public function clear()
+	{
+		disposeLocalMinimaList();
+		/*
+		for (var i:Int = 0; i < m_edges.length; ++i)
+		{
+			for (var j:Int = 0; j < m_edges[i].Count; ++j) m_edges[i][j] = null;
+			m_edges[i] = [];
+		}
+		*/
+		m_edges = [];
+	}
+	//------------------------------------------------------------------------------
+
+	function disposeLocalMinimaList()
+	{
+		while( m_MinimaList != null )
+		{
+			var tmpLm = m_MinimaList.next;
+			m_MinimaList = null;
+			m_MinimaList = tmpLm;
+		}
+		m_CurrentLM = null;
+	}
+	//------------------------------------------------------------------------------
+
+	public function addPolygons(ppg:Polygons, polyType:PolyType)
+	{
+		var result = false;
+		for ( p in ppg)
+			if (addPolygon(p, polyType)) result = true;
+		return result;
+	}
+	//------------------------------------------------------------------------------
+
+	public function addPolygon(pg:Polygon, polyType:PolyType)
+	{
+		var len = pg.length;
+		if (len < 3) return false;
+
+		var p = new Polygon();
+		p.add(pg[0].x, pg[0].y);
+		var j = 0;
+		for( i in 1...len )
+		{
+			if (ClipperBase.PointsEqual(p[j], pg[i])) continue;
+			else if (j > 0 && SlopesEqual3(p[j-1], p[j], pg[i]))
+			{
+				if (ClipperBase.PointsEqual(p[j-1], pg[i])) j--;
+			} else j++;
+			if (j < p.length)
+				p[j] = pg[i]; else
+				p.add(pg[i].x, pg[i].y);
+		}
+		if (j < 2) return false;
+
+		len = j+1;
+		while (len > 2)
+		{
+			//nb: test for point equality before testing slopes
+			if (ClipperBase.PointsEqual(p[j], p[0])) j--;
+			else if (ClipperBase.PointsEqual(p[0], p[1]) || SlopesEqual3(p[j], p[0], p[1]))
+				p[0] = p[j--];
+			else if (SlopesEqual3(p[j - 1], p[j], p[0])) j--;
+			else if (SlopesEqual3(p[0], p[1], p[2]))
+			{
+				for ( i in 2...j+1 ) p[i - 1] = p[i];
+				j--;
+			}
+			else break;
+			len--;
+		}
+		if (len < 3) return false;
+
+		//create a new edge array
+		var edges = [];
+		for ( i in 0...len ) edges.push(new TEdge());
+		m_edges.push(edges);
+
+		//convert vertices to a Float-linked-list of edges and initialize
+		edges[0].xcurr = p[0].x;
+		edges[0].ycurr = p[0].y;
+		InitEdge(edges[len - 1], edges[0], edges[len - 2], p[len - 1], polyType);
+		var i = len - 2;
+		while( i > 0 ) {
+			InitEdge(edges[i], edges[i + 1], edges[i - 1], p[i], polyType);
+			i--;
+		}
+		InitEdge(edges[0], edges[1], edges[len-1], p[0], polyType);
+		
+		//reset xcurr & ycurr and find 'eHighest' (given the y axis coordinates
+		//increase downward so the 'highest' edge will have the smallest ytop)
+		var e = edges[0];
+		var eHighest = e;
+		do
+		{
+			e.xcurr = e.xbot;
+			e.ycurr = e.ybot;
+			if (e.ytop < eHighest.ytop) eHighest = e;
+			e = e.next;
+		} while ( e != edges[0]);
+
+		//make sure eHighest is positioned so the following loop works safely
+		if (eHighest.windDelta > 0) eHighest = eHighest.next;
+		if (eHighest.dx == ClipperBase.HORIZONTAL) eHighest = eHighest.next;
+
+		//finally insert each local minima
+		e = eHighest;
+		do {
+		e = AddBoundsToLML(e);
+		}
+		while( e != eHighest );
+		
+		
+		return true;
+	}
+	//------------------------------------------------------------------------------
+
+	private function InitEdge(e:TEdge, eNext:TEdge,ePrev:TEdge, pt:Point, polyType:PolyType) {
+		e.next = eNext;
+		e.prev = ePrev;
+		e.xcurr = pt.x;
+		e.ycurr = pt.y;
+		if (e.ycurr >= e.next.ycurr)
+		{
+			e.xbot = e.xcurr;
+			e.ybot = e.ycurr;
+			e.xtop = e.next.xcurr;
+			e.ytop = e.next.ycurr;
+			e.windDelta = 1;
+		} else {
+			e.xtop = e.xcurr;
+			e.ytop = e.ycurr;
+			e.xbot = e.next.xcurr;
+			e.ybot = e.next.ycurr;
+			e.windDelta = -1;
+		}
+		SetDx(e);
+		e.polyType = polyType;
+		e.outIdx = -1;
+	}
+	//------------------------------------------------------------------------------
+
+	private function SetDx(e:TEdge)
+	{
+		e.deltaX = (e.xtop - e.xbot);
+		e.deltaY = (e.ytop - e.ybot);
+		if (e.deltaY == 0) e.dx = ClipperBase.HORIZONTAL;
+		else e.dx = e.deltaX / e.deltaY;
+	}
+	//---------------------------------------------------------------------------
+
+	function AddBoundsToLML(e:TEdge)
+	{
+		//Starting at the top of one bound we progress to the bottom where there's
+		//a local minima. We then go to the top of the next bound. These two bounds
+		//form the left and right (or right and left) bounds of the local minima.
+		e.nextInLML = null;
+		e = e.next;
+		while( true )
+		{
+		if ( e.dx == ClipperBase.HORIZONTAL )
+		{
+			//nb: proceed through horizontals when approaching from their right,
+			//	but break on ClipperBase.HORIZONTAL minima if approaching from their left.
+			//	This ensures 'local minima' are always on the left of horizontals.
+			if (e.next.ytop < e.ytop && e.next.xbot > e.prev.xbot) break;
+			if (e.xtop != e.prev.xbot) SwapX(e);
+			e.nextInLML = e.prev;
+		}
+		else if (e.ycurr == e.prev.ycurr) break;
+		else e.nextInLML = e.prev;
+		e = e.next;
+		}
+
+		//e and e.prev are now at a local minima
+		var newLm = new LocalMinima();
+		newLm.next = null;
+		newLm.y = e.prev.ybot;
+
+		if ( e.dx == ClipperBase.HORIZONTAL ) //ClipperBase.HORIZONTAL edges never start a left bound
+		{
+		if (e.xbot != e.prev.xbot) SwapX(e);
+		newLm.leftBound = e.prev;
+		newLm.rightBound = e;
+		} else if (e.dx < e.prev.dx)
+		{
+		newLm.leftBound = e.prev;
+		newLm.rightBound = e;
+		} else
+		{
+		newLm.leftBound = e;
+		newLm.rightBound = e.prev;
+		}
+		newLm.leftBound.side = EdgeSide.Left;
+		newLm.rightBound.side = EdgeSide.Right;
+		InsertLocalMinima( newLm );
+
+		while( true )
+		{
+		if ( e.next.ytop == e.ytop && e.next.dx != ClipperBase.HORIZONTAL ) break;
+		e.nextInLML = e.next;
+		e = e.next;
+		if ( e.dx == ClipperBase.HORIZONTAL && e.xbot != e.prev.xtop) SwapX(e);
+		}
+		return e.next;
+	}
+	//------------------------------------------------------------------------------
+
+	private function InsertLocalMinima(newLm:LocalMinima)
+	{
+		if( m_MinimaList == null )
+		{
+		m_MinimaList = newLm;
+		}
+		else if( newLm.y >= m_MinimaList.y )
+		{
+		newLm.next = m_MinimaList;
+		m_MinimaList = newLm;
+		} else
+		{
+		var tmpLm = m_MinimaList;
+		while( tmpLm.next != null	&& ( newLm.y < tmpLm.next.y ) )
+			tmpLm = tmpLm.next;
+		newLm.next = tmpLm.next;
+		tmpLm.next = newLm;
+		}
+	}
+	//------------------------------------------------------------------------------
+
+	function PopLocalMinima() {
+		if (m_CurrentLM == null) return;
+		m_CurrentLM = m_CurrentLM.next;
+	}
+	//------------------------------------------------------------------------------
+
+	private function SwapX(e:TEdge)
+	{
+		//swap ClipperBase.HORIZONTAL edges' top and bottom x's so they follow the natural
+		//progression of the bounds - ie so their xbots will align with the
+		//adjoining lower edge. [Helpful in the ProcessHorizontal() method.]
+		e.xcurr = e.xtop;
+		e.xtop = e.xbot;
+		e.xbot = e.xcurr;
+	}
+	//------------------------------------------------------------------------------
+
+	function Reset()
+	{
+		m_CurrentLM = m_MinimaList;
+
+		//reset all edges
+		var lm = m_MinimaList;
+		while (lm != null)
+		{
+			var e = lm.leftBound;
+			while (e != null)
+			{
+				e.xcurr = e.xbot;
+				e.ycurr = e.ybot;
+				e.side = EdgeSide.Left;
+				e.outIdx = -1;
+				e = e.nextInLML;
+			}
+			e = lm.rightBound;
+			while (e != null)
+			{
+				e.xcurr = e.xbot;
+				e.ycurr = e.ybot;
+				e.side = EdgeSide.Right;
+				e.outIdx = -1;
+				e = e.nextInLML;
+			}
+			lm = lm.next;
+		}
+		return;
+	}
+	//------------------------------------------------------------------------------
+
+	public function GetBounds()
+	{
+		var result = new Rect();
+		var lm = m_MinimaList;
+		if (lm == null) return result;
+		result.left = lm.leftBound.xbot;
+		result.top = lm.leftBound.ybot;
+		result.right = lm.leftBound.xbot;
+		result.bottom = lm.leftBound.ybot;
+		while (lm != null)
+		{
+			if (lm.leftBound.ybot > result.bottom)
+				result.bottom = lm.leftBound.ybot;
+			var e = lm.leftBound;
+			while( true )
+			{
+				var bottomE = e;
+				while (e.nextInLML != null)
+				{
+					if (e.xbot < result.left) result.left = e.xbot;
+					if (e.xbot > result.right) result.right = e.xbot;
+					e = e.nextInLML;
+				}
+				if (e.xbot < result.left) result.left = e.xbot;
+				if (e.xbot > result.right) result.right = e.xbot;
+				if (e.xtop < result.left) result.left = e.xtop;
+				if (e.xtop > result.right) result.right = e.xtop;
+				if (e.ytop < result.top) result.top = e.ytop;
+
+				if (bottomE == lm.leftBound) e = lm.rightBound;
+				else break;
+			}
+			lm = lm.next;
+		}
+		return result;
+	}
+
+} //ClipperBase
+
+@:allow(hxd.clipper)
+class Clipper extends ClipperBase {
+	
+	var m_PolyOuts : Array<OutRec>;
+	var m_ClipType : ClipType;
+	var m_Scanbeam : Scanbeam;
+	var m_ActiveEdges : TEdge;
+	var m_SortedEdges : TEdge;
+	var m_IntersectNodes : IntersectNode;
+	var m_ExecuteLocked : Bool;
+	var m_ClipFillType : PolyFillType;
+	var m_SubjFillType : PolyFillType;
+	var m_Joins : Array<JoinRec>;
+	var m_HorizJoins : Array<HorzJoinRec>;
+	var m_ReverseOutput : Bool;
+	var m_UsingPolyTree : Bool;
+	public var forceSimple: Bool;
+
+	public function new()
+	{
+		super();
+		m_Scanbeam = null;
+		m_ActiveEdges = null;
+		m_SortedEdges = null;
+		m_IntersectNodes = null;
+		m_ExecuteLocked = false;
+		m_UsingPolyTree = false;
+		m_PolyOuts = new Array();
+		m_Joins = new Array();
+		m_HorizJoins = new Array();
+		m_ReverseOutput = false;
+		forceSimple = false;
+	}
+
+	inline function xor(a, b) {
+		return if( a ) !b else b;
+	}
+	
+	//------------------------------------------------------------------------------
+	
+	public override function clear()
+	{
+		if (m_edges.length == 0) return; //avoids problems with ClipperBase destructor
+		DisposeAllPolyPts();
+		super.clear();
+	}
+	//------------------------------------------------------------------------------
+
+	function DisposeScanbeamList()
+	{
+		while ( m_Scanbeam != null ) {
+			var sb2 = m_Scanbeam.next;
+			m_Scanbeam = null;
+			m_Scanbeam = sb2;
+		}
+	}
+	//------------------------------------------------------------------------------
+
+	override function Reset()
+	{
+		super.Reset();
+		m_Scanbeam = null;
+		m_ActiveEdges = null;
+		m_SortedEdges = null;
+		DisposeAllPolyPts();
+		var lm = m_MinimaList;
+		while (lm != null)
+		{
+			InsertScanbeam(lm.y);
+			lm = lm.next;
+		}
+	}
+	
+	//------------------------------------------------------------------------------
+
+	public var reverseSolution(get, set) : Bool;
+	
+	function get_reverseSolution() {
+		return m_ReverseOutput;
+	}
+	function set_reverseSolution(v) {
+		return m_ReverseOutput = v;
+	}
+
+	//------------------------------------------------------------------------------
+
+	private function InsertScanbeam(y:Int)
+	{
+		if( m_Scanbeam == null )
+		{
+		m_Scanbeam = new Scanbeam();
+		m_Scanbeam.next = null;
+		m_Scanbeam.y = y;
+		}
+		else if(	y > m_Scanbeam.y )
+		{
+		var newSb = new Scanbeam();
+		newSb.y = y;
+		newSb.next = m_Scanbeam;
+		m_Scanbeam = newSb;
+		} else
+		{
+		var sb2 = m_Scanbeam;
+		while( sb2.next != null	&& ( y <= sb2.next.y ) ) sb2 = sb2.next;
+		if(	y == sb2.y ) return; //ie ignores duplicates
+		var newSb = new Scanbeam();
+		newSb.y = y;
+		newSb.next = sb2.next;
+		sb2.next = newSb;
+		}
+	}
+	//------------------------------------------------------------------------------
+
+	public function execute(clipType:ClipType,solution:Polygons,?subjFillType,?clipFillType) : Bool
+	{
+		if( subjFillType == null ) subjFillType = PolyFillType.EvenOdd;
+		if( clipFillType == null ) clipFillType = PolyFillType.EvenOdd;
+		if (m_ExecuteLocked) return false;
+		m_ExecuteLocked = true;
+		solution.splice(0,solution.length);
+		m_SubjFillType = subjFillType;
+		m_ClipFillType = clipFillType;
+		m_ClipType = clipType;
+		m_UsingPolyTree = false;
+		var succeeded = ExecuteInternal();
+		//build the return polygons
+		if (succeeded) BuildResult(solution);
+		m_ExecuteLocked = false;
+		return succeeded;
+	}
+	//------------------------------------------------------------------------------
+
+	public function ExecuteTree(clipType:ClipType, polytree:PolyTree, ?subjFillType, ?clipFillType ) {
+		if( subjFillType == null ) subjFillType = PolyFillType.EvenOdd;
+		if( clipFillType == null ) clipFillType = PolyFillType.EvenOdd;
+		if (m_ExecuteLocked) return false;
+		m_ExecuteLocked = true;
+		m_SubjFillType = subjFillType;
+		m_ClipFillType = clipFillType;
+		m_ClipType = clipType;
+		m_UsingPolyTree = true;
+		var succeeded = ExecuteInternal();
+		//build the return polygons
+		if (succeeded) BuildResult2(polytree);
+		m_ExecuteLocked = false;
+		return succeeded;
+	}
+
+	//------------------------------------------------------------------------------
+
+	function FixHoleLinkage(outRec:OutRec) {
+		//skip if an outermost polygon or
+		//already already points to the correct firstLeft
+		if (outRec.firstLeft == null ||
+				(outRec.isHole != outRec.firstLeft.isHole &&
+				outRec.firstLeft.pts != null)) return;
+
+		var orfl:OutRec = outRec.firstLeft;
+		while (orfl != null && ((orfl.isHole == outRec.isHole) || orfl.pts == null))
+			orfl = orfl.firstLeft;
+		outRec.firstLeft = orfl;
+	}
+	//------------------------------------------------------------------------------
+
+	private function ExecuteInternal() : Bool
+	{
+		var succeeded;
+//		try
+		{
+			Reset();
+			if (m_CurrentLM == null) return true;
+			var botY = PopScanbeam();
+			do
+			{
+				InsertLocalMinimaIntoAEL(botY);
+				m_HorizJoins = [];
+				ProcessHorizontals();
+				var topY = PopScanbeam();
+				succeeded = ProcessIntersections(botY, topY);
+				if (!succeeded) break;
+				ProcessEdgesAtTopOfScanbeam(topY);
+				botY = topY;
+			} while (m_Scanbeam != null || m_CurrentLM != null);
+		}
+//		catch { succeeded = false; }
+
+		if (succeeded)
+		{
+			//tidy up output polygons and fix orientations where necessary
+			for ( outRec in m_PolyOuts)
+			{
+				if (outRec.pts == null) continue;
+				FixupOutPolygon(outRec);
+				if (outRec.pts == null) continue;
+				if (xor(outRec.isHole,m_ReverseOutput) == (Area(outRec) > 0))
+					ReversePolyPtLinks(outRec.pts);
+			}
+			JoinCommonEdges();
+			if (forceSimple) DoSimplePolygons();
+		}
+		m_Joins = [] /*clear*/;
+		m_HorizJoins = [] /*clear*/;
+		return succeeded;
+	}
+	//------------------------------------------------------------------------------
+
+	private function PopScanbeam() : Int
+	{
+		var y = m_Scanbeam.y;
+		var sb2 = m_Scanbeam;
+		m_Scanbeam = m_Scanbeam.next;
+		sb2 = null;
+		return y;
+	}
+	//------------------------------------------------------------------------------
+
+	private function DisposeAllPolyPts(){
+		for ( i in 0...m_PolyOuts.length ) DisposeOutRec(i);
+		m_PolyOuts = [];
+	}
+	//------------------------------------------------------------------------------
+
+	function DisposeOutRec(index:Int)
+	{
+		var outRec = m_PolyOuts[index];
+		if (outRec.pts != null) DisposeOutPts(outRec.pts);
+		outRec = null;
+		m_PolyOuts[index] = null;
+	}
+	//------------------------------------------------------------------------------
+
+	private function DisposeOutPts(pp:OutPt)
+	{
+		if (pp == null) return;
+		var tmpPp = null;
+		pp.prev.next = null;
+		while (pp != null)
+		{
+			tmpPp = pp;
+			pp = pp.next;
+			tmpPp = null;
+		}
+	}
+	//------------------------------------------------------------------------------
+
+	private function AddJoin(e1:TEdge,e2:TEdge,e1OutIdx:Int,e2OutIdx:Int)
+	{
+		var jr:JoinRec = new JoinRec();
+		if (e1OutIdx >= 0)
+			jr.poly1Idx = e1OutIdx; else
+		jr.poly1Idx = e1.outIdx;
+		jr.pt1a = new Point(e1.xcurr, e1.ycurr);
+		jr.pt1b = new Point(e1.xtop, e1.ytop);
+		if (e2OutIdx >= 0)
+			jr.poly2Idx = e2OutIdx; else
+			jr.poly2Idx = e2.outIdx;
+		jr.pt2a = new Point(e2.xcurr, e2.ycurr);
+		jr.pt2b = new Point(e2.xtop, e2.ytop);
+		m_Joins.push(jr);
+	}
+	//------------------------------------------------------------------------------
+
+	private function AddHorzJoin(e:TEdge,idx:Int)
+	{
+		var hj:HorzJoinRec = new HorzJoinRec();
+		hj.edge = e;
+		hj.savedIdx = idx;
+		m_HorizJoins.push(hj);
+	}
+	//------------------------------------------------------------------------------
+
+	private function InsertLocalMinimaIntoAEL(botY:Int)
+	{
+		while(	m_CurrentLM != null	&& ( m_CurrentLM.y == botY ) )
+		{
+		var lb:TEdge = m_CurrentLM.leftBound;
+		var rb:TEdge = m_CurrentLM.rightBound;
+
+		InsertEdgeIntoAEL( lb );
+		InsertScanbeam( lb.ytop );
+		InsertEdgeIntoAEL( rb );
+
+		if (IsEvenOddFillType(lb))
+		{
+			lb.windDelta = 1;
+			rb.windDelta = 1;
+		}
+		else
+		{
+			rb.windDelta = -lb.windDelta;
+		}
+		SetWindingCount(lb);
+		rb.windCnt = lb.windCnt;
+		rb.windCnt2 = lb.windCnt2;
+
+		if(	rb.dx == ClipperBase.HORIZONTAL )
+		{
+			//nb: only rightbounds can have a ClipperBase.HORIZONTAL bottom edge
+			AddEdgeToSEL( rb );
+			InsertScanbeam( rb.nextInLML.ytop );
+		}
+		else
+			InsertScanbeam( rb.ytop );
+
+		if( IsContributing(lb) )
+			AddLocalMinPoly(lb, rb, new Point(lb.xcurr, m_CurrentLM.y));
+
+		//if any output polygons share an edge, they'll need joining later
+		if (rb.outIdx >= 0 && rb.dx == ClipperBase.HORIZONTAL)
+		{
+			var i = -1;
+			while( ++i < m_HorizJoins.length )
+			{
+				var hj:HorzJoinRec = m_HorizJoins[i];
+				//if horizontals rb and hj.edge overlap, flag for joining later
+				if (HasOverlapSegment(new Point(hj.edge.xbot, hj.edge.ybot),
+					new Point(hj.edge.xtop, hj.edge.ytop),
+					new Point(rb.xbot, rb.ybot),
+					new Point(rb.xtop, rb.ytop)))
+					AddJoin(hj.edge, rb, hj.savedIdx, -1);
+			}
+		}
+
+
+		if( lb.nextInAEL != rb )
+		{
+			if (rb.outIdx >= 0 && rb.prevInAEL.outIdx >= 0 &&
+				ClipperBase.SlopesEqual(rb.prevInAEL, rb))
+				AddJoin(rb, rb.prevInAEL, -1, -1);
+
+			var e:TEdge = lb.nextInAEL;
+			var pt:Point = new Point(lb.xcurr, lb.ycurr);
+			while( e != rb )
+			{
+			if(e == null)
+				throw "InsertLocalMinimaIntoAEL: missing rightbound!";
+			//nb: For calculating winding counts etc, IntersectEdges() assumes
+			//that param1 will be to the right of param2 ABOVE the intersection
+			IntersectEdges( rb , e , pt , Protects.None); //order important here
+			e = e.nextInAEL;
+			}
+		}
+		PopLocalMinima();
+		}
+	}
+	//------------------------------------------------------------------------------
+
+	private function InsertEdgeIntoAEL(edge:TEdge)
+	{
+		edge.prevInAEL = null;
+		edge.nextInAEL = null;
+		if (m_ActiveEdges == null)
+		{
+		m_ActiveEdges = edge;
+		}
+		else if( E2InsertsBeforeE1(m_ActiveEdges, edge) )
+		{
+		edge.nextInAEL = m_ActiveEdges;
+		m_ActiveEdges.prevInAEL = edge;
+		m_ActiveEdges = edge;
+		} else
+		{
+		var e:TEdge = m_ActiveEdges;
+		while (e.nextInAEL != null && !E2InsertsBeforeE1(e.nextInAEL, edge))
+			e = e.nextInAEL;
+		edge.nextInAEL = e.nextInAEL;
+		if (e.nextInAEL != null) e.nextInAEL.prevInAEL = edge;
+		edge.prevInAEL = e;
+		e.nextInAEL = edge;
+		}
+	}
+	//----------------------------------------------------------------------
+
+	private function E2InsertsBeforeE1(e1:TEdge,e2:TEdge) : Bool
+	{
+		if (e2.xcurr == e1.xcurr)
+		{
+			if (e2.ytop > e1.ytop)
+				return e2.xtop < TopX(e1, e2.ytop);
+			else return e1.xtop > TopX(e2, e1.ytop);
+		}
+		else return e2.xcurr < e1.xcurr;
+	}
+	//------------------------------------------------------------------------------
+
+	private function IsEvenOddFillType(edge:TEdge) : Bool
+	{
+		if (edge.polyType == PolyType.Subject)
+			return m_SubjFillType == PolyFillType.EvenOdd;
+		else
+			return m_ClipFillType == PolyFillType.EvenOdd;
+	}
+	//------------------------------------------------------------------------------
+
+	private function IsEvenOddAltFillType(edge:TEdge) : Bool
+	{
+		if (edge.polyType == PolyType.Subject)
+			return m_ClipFillType == PolyFillType.EvenOdd;
+		else
+			return m_SubjFillType == PolyFillType.EvenOdd;
+	}
+	//------------------------------------------------------------------------------
+
+	private function IsContributing(edge:TEdge) : Bool
+	{
+		var pft, pft2;
+		if (edge.polyType == PolyType.Subject)
+		{
+			pft = m_SubjFillType;
+			pft2 = m_ClipFillType;
+		}
+		else
+		{
+			pft = m_ClipFillType;
+			pft2 = m_SubjFillType;
+		}
+
+		switch (pft)
+		{
+			case PolyFillType.EvenOdd:
+			case PolyFillType.NonZero:
+				if (Math.abs(edge.windCnt) != 1) return false;
+			case PolyFillType.Positive:
+				if (edge.windCnt != 1) return false;
+			default: //PolyFillType.Negative
+				if (edge.windCnt != -1) return false;
+		}
+
+		switch (m_ClipType)
+		{
+			case ClipType.Intersection:
+				switch (pft2)
+				{
+					case PolyFillType.EvenOdd:
+					case PolyFillType.NonZero:
+						return (edge.windCnt2 != 0);
+					case PolyFillType.Positive:
+						return (edge.windCnt2 > 0);
+					default:
+						return (edge.windCnt2 < 0);
+				}
+			case ClipType.Union:
+				switch (pft2)
+				{
+					case PolyFillType.EvenOdd:
+					case PolyFillType.NonZero:
+						return (edge.windCnt2 == 0);
+					case PolyFillType.Positive:
+						return (edge.windCnt2 <= 0);
+					default:
+						return (edge.windCnt2 >= 0);
+				}
+			case ClipType.Difference:
+				if (edge.polyType == PolyType.Subject)
+					switch (pft2)
+					{
+						case PolyFillType.EvenOdd:
+						case PolyFillType.NonZero:
+							return (edge.windCnt2 == 0);
+						case PolyFillType.Positive:
+							return (edge.windCnt2 <= 0);
+						default:
+							return (edge.windCnt2 >= 0);
+					}
+				else
+					switch (pft2)
+					{
+						case PolyFillType.EvenOdd:
+						case PolyFillType.NonZero:
+							return (edge.windCnt2 != 0);
+						case PolyFillType.Positive:
+							return (edge.windCnt2 > 0);
+						default:
+							return (edge.windCnt2 < 0);
+					}
+			case ClipType.Xor:
+				// nothing
+		}
+		return true;
+	}
+	//------------------------------------------------------------------------------
+
+	private function SetWindingCount(edge:TEdge)
+	{
+		var e:TEdge = edge.prevInAEL;
+		//find the edge of the same polytype that immediately preceeds 'edge' in AEL
+		while (e != null && e.polyType != edge.polyType)
+			e = e.prevInAEL;
+		if (e == null)
+		{
+			edge.windCnt = edge.windDelta;
+			edge.windCnt2 = 0;
+			e = m_ActiveEdges; //ie get ready to calc windCnt2
+		}
+		else if (IsEvenOddFillType(edge))
+		{
+			//even-odd filling
+			edge.windCnt = 1;
+			edge.windCnt2 = e.windCnt2;
+			e = e.nextInAEL; //ie get ready to calc windCnt2
+		}
+		else
+		{
+			//nonZero filling
+			if (e.windCnt * e.windDelta < 0)
+			{
+				if (Math.abs(e.windCnt) > 1)
+				{
+					if (e.windDelta * edge.windDelta < 0)
+						edge.windCnt = e.windCnt;
+					else
+						edge.windCnt = e.windCnt + edge.windDelta;
+				}
+				else
+					edge.windCnt = e.windCnt + e.windDelta + edge.windDelta;
+			}
+			else
+			{
+				if (Math.abs(e.windCnt) > 1 && e.windDelta * edge.windDelta < 0)
+					edge.windCnt = e.windCnt;
+				else if (e.windCnt + edge.windDelta == 0)
+					edge.windCnt = e.windCnt;
+				else
+					edge.windCnt = e.windCnt + edge.windDelta;
+			}
+			edge.windCnt2 = e.windCnt2;
+			e = e.nextInAEL; //ie get ready to calc windCnt2
+		}
+
+		//update windCnt2
+		if (IsEvenOddAltFillType(edge))
+		{
+			//even-odd filling
+			while (e != edge)
+			{
+				edge.windCnt2 = (edge.windCnt2 == 0) ? 1 : 0;
+				e = e.nextInAEL;
+			}
+		}
+		else
+		{
+			//nonZero filling
+			while (e != edge)
+			{
+				edge.windCnt2 += e.windDelta;
+				e = e.nextInAEL;
+			}
+		}
+	}
+	//------------------------------------------------------------------------------
+
+	private function AddEdgeToSEL(edge:TEdge)
+	{
+		//SEL pointers in PEdge are reused to build a list of ClipperBase.HORIZONTAL edges.
+		//However, we don't need to worry about order with ClipperBase.HORIZONTAL edge processing.
+		if (m_SortedEdges == null)
+		{
+			m_SortedEdges = edge;
+			edge.prevInSEL = null;
+			edge.nextInSEL = null;
+		}
+		else
+		{
+			edge.nextInSEL = m_SortedEdges;
+			edge.prevInSEL = null;
+			m_SortedEdges.prevInSEL = edge;
+			m_SortedEdges = edge;
+		}
+	}
+	//------------------------------------------------------------------------------
+
+	private function CopyAELToSEL()
+	{
+		var e:TEdge = m_ActiveEdges;
+		m_SortedEdges = e;
+		while (e != null)
+		{
+			e.prevInSEL = e.prevInAEL;
+			e.nextInSEL = e.nextInAEL;
+			e = e.nextInAEL;
+		}
+	}
+	//------------------------------------------------------------------------------
+
+	private function SwapPositionsInAEL(edge1:TEdge,edge2:TEdge)
+	{
+		if (edge1.nextInAEL == edge2)
+		{
+			var next:TEdge = edge2.nextInAEL;
+			if (next != null)
+				next.prevInAEL = edge1;
+			var prev:TEdge = edge1.prevInAEL;
+			if (prev != null)
+				prev.nextInAEL = edge2;
+			edge2.prevInAEL = prev;
+			edge2.nextInAEL = edge1;
+			edge1.prevInAEL = edge2;
+			edge1.nextInAEL = next;
+		}
+		else if (edge2.nextInAEL == edge1)
+		{
+			var next:TEdge = edge1.nextInAEL;
+			if (next != null)
+				next.prevInAEL = edge2;
+			var prev:TEdge = edge2.prevInAEL;
+			if (prev != null)
+				prev.nextInAEL = edge1;
+			edge1.prevInAEL = prev;
+			edge1.nextInAEL = edge2;
+			edge2.prevInAEL = edge1;
+			edge2.nextInAEL = next;
+		}
+		else
+		{
+			var next:TEdge = edge1.nextInAEL;
+			var prev:TEdge = edge1.prevInAEL;
+			edge1.nextInAEL = edge2.nextInAEL;
+			if (edge1.nextInAEL != null)
+				edge1.nextInAEL.prevInAEL = edge1;
+			edge1.prevInAEL = edge2.prevInAEL;
+			if (edge1.prevInAEL != null)
+				edge1.prevInAEL.nextInAEL = edge1;
+			edge2.nextInAEL = next;
+			if (edge2.nextInAEL != null)
+				edge2.nextInAEL.prevInAEL = edge2;
+			edge2.prevInAEL = prev;
+			if (edge2.prevInAEL != null)
+				edge2.prevInAEL.nextInAEL = edge2;
+		}
+
+		if (edge1.prevInAEL == null)
+			m_ActiveEdges = edge1;
+		else if (edge2.prevInAEL == null)
+			m_ActiveEdges = edge2;
+	}
+	//------------------------------------------------------------------------------
+
+	private function SwapPositionsInSEL(edge1:TEdge,edge2:TEdge)
+	{
+		if (edge1.nextInSEL == null && edge1.prevInSEL == null)
+			return;
+		if (edge2.nextInSEL == null && edge2.prevInSEL == null)
+			return;
+
+		if (edge1.nextInSEL == edge2)
+		{
+			var next:TEdge = edge2.nextInSEL;
+			if (next != null)
+				next.prevInSEL = edge1;
+			var prev:TEdge = edge1.prevInSEL;
+			if (prev != null)
+				prev.nextInSEL = edge2;
+			edge2.prevInSEL = prev;
+			edge2.nextInSEL = edge1;
+			edge1.prevInSEL = edge2;
+			edge1.nextInSEL = next;
+		}
+		else if (edge2.nextInSEL == edge1)
+		{
+			var next:TEdge = edge1.nextInSEL;
+			if (next != null)
+				next.prevInSEL = edge2;
+			var prev:TEdge = edge2.prevInSEL;
+			if (prev != null)
+				prev.nextInSEL = edge1;
+			edge1.prevInSEL = prev;
+			edge1.nextInSEL = edge2;
+			edge2.prevInSEL = edge1;
+			edge2.nextInSEL = next;
+		}
+		else
+		{
+			var next:TEdge = edge1.nextInSEL;
+			var prev:TEdge = edge1.prevInSEL;
+			edge1.nextInSEL = edge2.nextInSEL;
+			if (edge1.nextInSEL != null)
+				edge1.nextInSEL.prevInSEL = edge1;
+			edge1.prevInSEL = edge2.prevInSEL;
+			if (edge1.prevInSEL != null)
+				edge1.prevInSEL.nextInSEL = edge1;
+			edge2.nextInSEL = next;
+			if (edge2.nextInSEL != null)
+				edge2.nextInSEL.prevInSEL = edge2;
+			edge2.prevInSEL = prev;
+			if (edge2.prevInSEL != null)
+				edge2.prevInSEL.nextInSEL = edge2;
+		}
+
+		if (edge1.prevInSEL == null)
+			m_SortedEdges = edge1;
+		else if (edge2.prevInSEL == null)
+			m_SortedEdges = edge2;
+	}
+	//------------------------------------------------------------------------------
+
+
+	private function AddLocalMaxPoly(e1:TEdge,e2:TEdge,pt:Point)
+	{
+		AddOutPt(e1, pt);
+		if (e1.outIdx == e2.outIdx)
+		{
+			e1.outIdx = -1;
+			e2.outIdx = -1;
+		}
+		else if (e1.outIdx < e2.outIdx)
+			AppendPolygon(e1, e2);
+		else
+			AppendPolygon(e2, e1);
+	}
+	//------------------------------------------------------------------------------
+
+	private function AddLocalMinPoly(e1:TEdge,e2:TEdge,pt:Point)
+	{
+		var e, prevE;
+		if (e2.dx == ClipperBase.HORIZONTAL || (e1.dx > e2.dx))
+		{
+			AddOutPt(e1, pt);
+			e2.outIdx = e1.outIdx;
+			e1.side = EdgeSide.Left;
+			e2.side = EdgeSide.Right;
+			e = e1;
+			if (e.prevInAEL == e2)
+				prevE = e2.prevInAEL;
+			else
+				prevE = e.prevInAEL;
+		}
+		else
+		{
+			AddOutPt(e2, pt);
+			e1.outIdx = e2.outIdx;
+			e1.side = EdgeSide.Right;
+			e2.side = EdgeSide.Left;
+			e = e2;
+			if (e.prevInAEL == e1)
+				prevE = e1.prevInAEL;
+			else
+				prevE = e.prevInAEL;
+		}
+
+		if (prevE != null && prevE.outIdx >= 0 &&
+			(TopX(prevE, pt.y) == TopX(e, pt.y)) &&
+			 ClipperBase.SlopesEqual(e, prevE))
+				 AddJoin(e, prevE, -1, -1);
+
+	}
+	//------------------------------------------------------------------------------
+
+	private function CreateOutRec() : OutRec
+	{
+		var result:OutRec = new OutRec();
+		result.idx = -1;
+		result.isHole = false;
+		result.firstLeft = null;
+		result.pts = null;
+		result.bottomPt = null;
+		result.polyNode = null;
+		m_PolyOuts.push(result);
+		result.idx = m_PolyOuts.length - 1;
+		return result;
+	}
+	//------------------------------------------------------------------------------
+
+	private function AddOutPt(e:TEdge,pt:Point)
+	{
+		var ToFront = (e.side == EdgeSide.Left);
+		if(	e.outIdx < 0 )
+		{
+			var outRec = CreateOutRec();
+			e.outIdx = outRec.idx;
+			var op:OutPt = new OutPt();
+			outRec.pts = op;
+			op.pt = pt;
+			op.idx = outRec.idx;
+			op.next = op;
+			op.prev = op;
+			SetHoleState(e, outRec);
+		} else
+		{
+			var outRec = m_PolyOuts[e.outIdx];
+			var op:OutPt = outRec.pts, op2;
+			if (ToFront && ClipperBase.PointsEqual(pt, op.pt) ||
+				(!ToFront && ClipperBase.PointsEqual(pt, op.prev.pt))) return;
+
+			op2 = new OutPt();
+			op2.pt = pt;
+			op2.idx = outRec.idx;
+			op2.next = op;
+			op2.prev = op.prev;
+			op2.prev.next = op2;
+			op.prev = op2;
+			if (ToFront) outRec.pts = op2;
+		}
+	}
+	//------------------------------------------------------------------------------
+	/*
+	function SwapPoints(ref Point pt1, ref Point pt2)
+	{
+		var tmp:Point = pt1;
+		pt1 = pt2;
+		pt2 = tmp;
+	}
+	*/
+	//------------------------------------------------------------------------------
+
+	private inline function HasOverlapSegment(pt1a:Point, pt1b:Point, pt2a:Point,pt2b:Point) : Bool {
+		//precondition: segments are colinear.
+		if (abs(pt1a.x - pt1b.x) > abs(pt1a.y - pt1b.y))
+		{
+			if (pt1a.x > pt1b.x) {
+				var tmp = pt1a;
+				pt1a = pt1b;
+				pt1b = tmp;
+			}
+			if (pt2a.x > pt2b.x) {
+				var tmp = pt2a;
+				pt2a = pt2b;
+				pt2b = tmp;
+			}
+			var pt1 = if (pt1a.x > pt2a.x) pt1a else pt2a;
+			var pt2 = if (pt1b.x < pt2b.x) pt1b else pt2b;
+			return pt1.x < pt2.x;
+		} else {
+			if (pt1a.y < pt1b.y) {
+				var tmp = pt1a;
+				pt1a = pt1b;
+				pt1b = tmp;
+			}
+			if (pt2a.y < pt2b.y) {
+				var tmp = pt2a;
+				pt2a = pt2b;
+				pt2b = tmp;
+			}
+			var pt1 = if (pt1a.y < pt2a.y) pt1a else pt2a;
+			var pt2 = if (pt1b.y > pt2b.y) pt1b else pt2b;
+			return pt1.y > pt2.y;
+		}
+	}
+	
+	
+	private function GetOverlapSegment(pt1a:Point, pt1b:Point, pt2a:Point,pt2b:Point,pt1:Ref<Point>,pt2:Ref<Point>) : Bool {
+		//precondition: segments are colinear.
+		if (abs(pt1a.x - pt1b.x) > abs(pt1a.y - pt1b.y))
+		{
+			if (pt1a.x > pt1b.x) {
+				var tmp = pt1a;
+				pt1a = pt1b;
+				pt1b = tmp;
+			}
+			if (pt2a.x > pt2b.x) {
+				var tmp = pt2a;
+				pt2a = pt2b;
+				pt2b = tmp;
+			}
+			pt1.val = if (pt1a.x > pt2a.x) pt1a else pt2a;
+			pt2.val = if (pt1b.x < pt2b.x) pt1b else pt2b;
+			return pt1.val.x < pt2.val.x;
+		} else {
+			if (pt1a.y < pt1b.y) {
+				var tmp = pt1a;
+				pt1a = pt1b;
+				pt1b = tmp;
+			}
+			if (pt2a.y < pt2b.y) {
+				var tmp = pt2a;
+				pt2a = pt2b;
+				pt2b = tmp;
+			}
+			pt1.val = if (pt1a.y < pt2a.y) pt1a else pt2a;
+			pt2.val = if (pt1b.y > pt2b.y) pt1b else pt2b;
+			return pt1.val.y > pt2.val.y;
+		}
+	}
+	
+	//------------------------------------------------------------------------------
+
+	private function FindSegment(pp : OutPt,  pt1 : Ref<Point>, pt2 : Ref<Point>) {
+		if (pp == null) return null;
+		var pp2 : OutPt = pp;
+		var pt1a = pt1.val.clone();
+		var pt2a = pt2.val.clone();
+		do
+		{
+			if (ClipperBase.SlopesEqual4(pt1a, pt2a, pp.pt, pp.prev.pt) &&
+				ClipperBase.SlopesEqual3(pt1a, pt2a, pp.pt) &&
+				GetOverlapSegment(pt1a, pt2a, pp.pt, pp.prev.pt, pt1, pt2))
+					return pp;
+			pp = pp.next;
+		}
+		while (pp != pp2);
+		return null;
+	}
+	//------------------------------------------------------------------------------
+
+	function Pt3IsBetweenPt1AndPt2(pt1:Point, pt2:Point, pt3:Point)
+	{
+		if (ClipperBase.PointsEqual(pt1, pt3) || ClipperBase.PointsEqual(pt2, pt3)) return true;
+		else if (pt1.x != pt2.x) return (pt1.x < pt3.x) == (pt3.x < pt2.x);
+		else return (pt1.y < pt3.y) == (pt3.y < pt2.y);
+	}
+	//------------------------------------------------------------------------------
+
+	private function InsertPolyPtBetween(p1:OutPt,p2:OutPt,pt:Point) : OutPt
+	{
+		var result:OutPt = new OutPt();
+		result.pt = pt;
+		if (p2 == p1.next)
+		{
+			p1.next = result;
+			p2.prev = result;
+			result.next = p2;
+			result.prev = p1;
+		} else
+		{
+			p2.next = result;
+			p1.prev = result;
+			result.next = p1;
+			result.prev = p2;
+		}
+		return result;
+	}
+	//------------------------------------------------------------------------------
+
+	private function SetHoleState(e:TEdge,outRec:OutRec)
+	{
+		var isHole = false;
+		var e2 = e.prevInAEL;
+		while (e2 != null)
+		{
+			if (e2.outIdx >= 0)
+			{
+				isHole = !isHole;
+				if (outRec.firstLeft == null)
+					outRec.firstLeft = m_PolyOuts[e2.outIdx];
+			}
+			e2 = e2.prevInAEL;
+		}
+		if (isHole) outRec.isHole = true;
+	}
+	//------------------------------------------------------------------------------
+
+	private function GetDx(pt1:Point,pt2:Point) : Float
+	{
+		if (pt1.y == pt2.y) return ClipperBase.HORIZONTAL;
+		else return (pt2.x - pt1.x) / (pt2.y - pt1.y);
+	}
+	//---------------------------------------------------------------------------
+
+	private function FirstIsBottomPt(btmPt1:OutPt,btmPt2:OutPt) : Bool
+	{
+		var p:OutPt = btmPt1.prev;
+		while (ClipperBase.PointsEqual(p.pt, btmPt1.pt) && (p != btmPt1)) p = p.prev;
+		var dx1p = Math.abs(GetDx(btmPt1.pt, p.pt));
+		p = btmPt1.next;
+		while (ClipperBase.PointsEqual(p.pt, btmPt1.pt) && (p != btmPt1)) p = p.next;
+		var dx1n = Math.abs(GetDx(btmPt1.pt, p.pt));
+
+		p = btmPt2.prev;
+		while (ClipperBase.PointsEqual(p.pt, btmPt2.pt) && (p != btmPt2)) p = p.prev;
+		var dx2p = Math.abs(GetDx(btmPt2.pt, p.pt));
+		p = btmPt2.next;
+		while (ClipperBase.PointsEqual(p.pt, btmPt2.pt) && (p != btmPt2)) p = p.next;
+		var dx2n = Math.abs(GetDx(btmPt2.pt, p.pt));
+		return (dx1p >= dx2p && dx1p >= dx2n) || (dx1n >= dx2p && dx1n >= dx2n);
+	}
+	//------------------------------------------------------------------------------
+
+	private function GetBottomPt(pp:OutPt) : OutPt
+	{
+		var dups:OutPt = null;
+		var p:OutPt = pp.next;
+		while (p != pp)
+		{
+		if (p.pt.y > pp.pt.y)
+		{
+			pp = p;
+			dups = null;
+		}
+		else if (p.pt.y == pp.pt.y && p.pt.x <= pp.pt.x)
+		{
+			if (p.pt.x < pp.pt.x)
+			{
+				dups = null;
+				pp = p;
+			} else
+			{
+			if (p.next != pp && p.prev != pp) dups = p;
+			}
+		}
+		p = p.next;
+		}
+		if (dups != null)
+		{
+		//there appears to be at least 2 vertices at bottomPt so
+		while (dups != p)
+		{
+			if (!FirstIsBottomPt(p, dups)) pp = dups;
+			dups = dups.next;
+			while (!ClipperBase.PointsEqual(dups.pt, pp.pt)) dups = dups.next;
+		}
+		}
+		return pp;
+	}
+	//------------------------------------------------------------------------------
+
+	private function GetLowermostRec(outRec1:OutRec,outRec2:OutRec) : OutRec
+	{
+		//work out which polygon fragment has the correct hole state
+		if (outRec1.bottomPt == null)
+			outRec1.bottomPt = GetBottomPt(outRec1.pts);
+		if (outRec2.bottomPt == null)
+			outRec2.bottomPt = GetBottomPt(outRec2.pts);
+		var bPt1 = outRec1.bottomPt;
+		var bPt2 = outRec2.bottomPt;
+		if (bPt1.pt.y > bPt2.pt.y) return outRec1;
+		else if (bPt1.pt.y < bPt2.pt.y) return outRec2;
+		else if (bPt1.pt.x < bPt2.pt.x) return outRec1;
+		else if (bPt1.pt.x > bPt2.pt.x) return outRec2;
+		else if (bPt1.next == bPt1) return outRec2;
+		else if (bPt2.next == bPt2) return outRec1;
+		else if (FirstIsBottomPt(bPt1, bPt2)) return outRec1;
+		else return outRec2;
+	}
+	//------------------------------------------------------------------------------
+
+	function Param1RightOfParam2(outRec1:OutRec, outRec2:OutRec)
+	{
+		do
+		{
+			outRec1 = outRec1.firstLeft;
+			if (outRec1 == outRec2) return true;
+		} while (outRec1 != null);
+		return false;
+	}
+	//------------------------------------------------------------------------------
+
+	private function GetOutRec(idx:Int) : OutRec
+	{
+		var outrec:OutRec = m_PolyOuts[idx];
+		while (outrec != m_PolyOuts[outrec.idx])
+		outrec = m_PolyOuts[outrec.idx];
+		return outrec;
+	}
+	//------------------------------------------------------------------------------
+
+	private function AppendPolygon(e1:TEdge,e2:TEdge)
+	{
+		//get the start and ends of both output polygons
+		var outRec1 = m_PolyOuts[e1.outIdx];
+		var outRec2 = m_PolyOuts[e2.outIdx];
+
+		var holeStateRec;
+		if (Param1RightOfParam2(outRec1, outRec2))
+			holeStateRec = outRec2;
+		else if (Param1RightOfParam2(outRec2, outRec1))
+			holeStateRec = outRec1;
+		else
+			holeStateRec = GetLowermostRec(outRec1, outRec2);
+
+		var p1_lft = outRec1.pts;
+		var p1_rt = p1_lft.prev;
+		var p2_lft = outRec2.pts;
+		var p2_rt = p2_lft.prev;
+
+		var side : Int;
+		//join e2 poly onto e1 poly and delete pointers to e2
+		if(	e1.side == EdgeSide.Left )
+		{
+		if (e2.side == EdgeSide.Left)
+		{
+			//z y x a b c
+			ReversePolyPtLinks(p2_lft);
+			p2_lft.next = p1_lft;
+			p1_lft.prev = p2_lft;
+			p1_rt.next = p2_rt;
+			p2_rt.prev = p1_rt;
+			outRec1.pts = p2_rt;
+		} else
+		{
+			//x y z a b c
+			p2_rt.next = p1_lft;
+			p1_lft.prev = p2_rt;
+			p2_lft.prev = p1_rt;
+			p1_rt.next = p2_lft;
+			outRec1.pts = p2_lft;
+		}
+		side = EdgeSide.Left;
+		} else
+		{
+		if (e2.side == EdgeSide.Right)
+		{
+			//a b c z y x
+			ReversePolyPtLinks( p2_lft );
+			p1_rt.next = p2_rt;
+			p2_rt.prev = p1_rt;
+			p2_lft.next = p1_lft;
+			p1_lft.prev = p2_lft;
+		} else
+		{
+			//a b c x y z
+			p1_rt.next = p2_lft;
+			p2_lft.prev = p1_rt;
+			p1_lft.prev = p2_rt;
+			p2_rt.next = p1_lft;
+		}
+		side = EdgeSide.Right;
+		}
+
+		outRec1.bottomPt = null;
+		if (holeStateRec == outRec2)
+		{
+			if (outRec2.firstLeft != outRec1)
+				outRec1.firstLeft = outRec2.firstLeft;
+			outRec1.isHole = outRec2.isHole;
+		}
+		outRec2.pts = null;
+		outRec2.bottomPt = null;
+
+		outRec2.firstLeft = outRec1;
+
+		var OKIdx = e1.outIdx;
+		var ObsoleteIdx = e2.outIdx;
+
+		e1.outIdx = -1; //nb: safe because we only get here via AddLocalMaxPoly
+		e2.outIdx = -1;
+
+		var e:TEdge = m_ActiveEdges;
+		while( e != null )
+		{
+		if( e.outIdx == ObsoleteIdx )
+		{
+			e.outIdx = OKIdx;
+			e.side = side;
+			break;
+		}
+		e = e.nextInAEL;
+		}
+		outRec2.idx = outRec1.idx;
+	}
+	//------------------------------------------------------------------------------
+
+	private function ReversePolyPtLinks(pp:OutPt)
+	{
+		if (pp == null) return;
+		var pp1;
+		var pp2;
+		pp1 = pp;
+		do
+		{
+			pp2 = pp1.next;
+			pp1.next = pp1.prev;
+			pp1.prev = pp2;
+			pp1 = pp2;
+		} while (pp1 != pp);
+	}
+	//------------------------------------------------------------------------------
+
+	private static function SwapSides(edge1:TEdge,edge2:TEdge)
+	{
+		var side = edge1.side;
+		edge1.side = edge2.side;
+		edge2.side = side;
+	}
+	//------------------------------------------------------------------------------
+
+	private static function SwapPolyIndexes(edge1:TEdge,edge2:TEdge)
+	{
+		var outIdx = edge1.outIdx;
+		edge1.outIdx = edge2.outIdx;
+		edge2.outIdx = outIdx;
+	}
+	//------------------------------------------------------------------------------
+
+	private function IntersectEdges(e1:TEdge,e2:TEdge,pt:Point,protects:Int)
+	{
+		//e1 will be to the left of e2 BELOW the intersection. Therefore e1 is before
+		//e2 in AEL except when e1 is being inserted at the intersection point
+
+		var e1stops = (Protects.Left & protects) == 0 && e1.nextInLML == null &&
+			e1.xtop == pt.x && e1.ytop == pt.y;
+		var e2stops = (Protects.Right & protects) == 0 && e2.nextInLML == null &&
+			e2.xtop == pt.x && e2.ytop == pt.y;
+		var e1Contributing = (e1.outIdx >= 0);
+		var e2contributing = (e2.outIdx >= 0);
+
+		//update winding counts
+		//assumes that e1 will be to the right of e2 ABOVE the intersection
+		if (e1.polyType == e2.polyType)
+		{
+			if (IsEvenOddFillType(e1))
+			{
+				var oldE1WindCnt = e1.windCnt;
+				e1.windCnt = e2.windCnt;
+				e2.windCnt = oldE1WindCnt;
+			}
+			else
+			{
+				if (e1.windCnt + e2.windDelta == 0) e1.windCnt = -e1.windCnt;
+				else e1.windCnt += e2.windDelta;
+				if (e2.windCnt - e1.windDelta == 0) e2.windCnt = -e2.windCnt;
+				else e2.windCnt -= e1.windDelta;
+			}
+		}
+		else
+		{
+			if (!IsEvenOddFillType(e2)) e1.windCnt2 += e2.windDelta;
+			else e1.windCnt2 = (e1.windCnt2 == 0) ? 1 : 0;
+			if (!IsEvenOddFillType(e1)) e2.windCnt2 -= e1.windDelta;
+			else e2.windCnt2 = (e2.windCnt2 == 0) ? 1 : 0;
+		}
+
+		var e1FillType, e2FillType, e1FillType2, e2FillType2;
+		if (e1.polyType == PolyType.Subject)
+		{
+			e1FillType = m_SubjFillType;
+			e1FillType2 = m_ClipFillType;
+		}
+		else
+		{
+			e1FillType = m_ClipFillType;
+			e1FillType2 = m_SubjFillType;
+		}
+		if (e2.polyType == PolyType.Subject)
+		{
+			e2FillType = m_SubjFillType;
+			e2FillType2 = m_ClipFillType;
+		}
+		else
+		{
+			e2FillType = m_ClipFillType;
+			e2FillType2 = m_SubjFillType;
+		}
+
+		var e1Wc, e2Wc;
+		switch (e1FillType)
+		{
+			case PolyFillType.Positive: e1Wc = e1.windCnt;
+			case PolyFillType.Negative: e1Wc = -e1.windCnt;
+			default: e1Wc = abs(e1.windCnt);
+		}
+		switch (e2FillType)
+		{
+			case PolyFillType.Positive: e2Wc = e2.windCnt;
+			case PolyFillType.Negative: e2Wc = -e2.windCnt;
+			default: e2Wc = abs(e2.windCnt);
+		}
+
+		if (e1Contributing && e2contributing)
+		{
+			if ( e1stops || e2stops ||
+				(e1Wc != 0 && e1Wc != 1) || (e2Wc != 0 && e2Wc != 1) ||
+				(e1.polyType != e2.polyType && m_ClipType != ClipType.Xor))
+				AddLocalMaxPoly(e1, e2, pt);
+			else
+			{
+				AddOutPt(e1, pt);
+				AddOutPt(e2, pt);
+				SwapSides(e1, e2);
+				SwapPolyIndexes(e1, e2);
+			}
+		}
+		else if (e1Contributing)
+		{
+			if (e2Wc == 0 || e2Wc == 1)
+			{
+				AddOutPt(e1, pt);
+				SwapSides(e1, e2);
+				SwapPolyIndexes(e1, e2);
+			}
+
+		}
+		else if (e2contributing)
+		{
+			if (e1Wc == 0 || e1Wc == 1)
+			{
+				AddOutPt(e2, pt);
+				SwapSides(e1, e2);
+				SwapPolyIndexes(e1, e2);
+			}
+		}
+		else if ( (e1Wc == 0 || e1Wc == 1) &&
+			(e2Wc == 0 || e2Wc == 1) && !e1stops && !e2stops )
+		{
+			//neither edge is currently contributing
+			var e1Wc2, e2Wc2;
+			switch (e1FillType2)
+			{
+				case PolyFillType.Positive: e1Wc2 = e1.windCnt2;
+				case PolyFillType.Negative: e1Wc2 = -e1.windCnt2;
+				default: e1Wc2 = abs(e1.windCnt2);
+			}
+			switch (e2FillType2)
+			{
+				case PolyFillType.Positive: e2Wc2 = e2.windCnt2;
+				case PolyFillType.Negative: e2Wc2 = -e2.windCnt2;
+				default: e2Wc2 = abs(e2.windCnt2);
+			}
+
+			if (e1.polyType != e2.polyType)
+				AddLocalMinPoly(e1, e2, pt);
+			else if (e1Wc == 1 && e2Wc == 1)
+				switch (m_ClipType)
+				{
+					case ClipType.Intersection:
+						if (e1Wc2 > 0 && e2Wc2 > 0)
+							AddLocalMinPoly(e1, e2, pt);
+					case ClipType.Union:
+						if (e1Wc2 <= 0 && e2Wc2 <= 0)
+							AddLocalMinPoly(e1, e2, pt);
+					case ClipType.Difference:
+						if (((e1.polyType == PolyType.Clip) && (e1Wc2 > 0) && (e2Wc2 > 0)) ||
+							((e1.polyType == PolyType.Subject) && (e1Wc2 <= 0) && (e2Wc2 <= 0)))
+								AddLocalMinPoly(e1, e2, pt);
+					case ClipType.Xor:
+						AddLocalMinPoly(e1, e2, pt);
+				}
+			else
+				SwapSides(e1, e2);
+		}
+
+		if ((e1stops != e2stops) &&
+			((e1stops && (e1.outIdx >= 0)) || (e2stops && (e2.outIdx >= 0))))
+		{
+			SwapSides(e1, e2);
+			SwapPolyIndexes(e1, e2);
+		}
+
+		//finally, delete any non-contributing maxima edges
+		if (e1stops) DeleteFromAEL(e1);
+		if (e2stops) DeleteFromAEL(e2);
+	}
+	//------------------------------------------------------------------------------
+
+	private function DeleteFromAEL(e:TEdge)
+	{
+		var AelPrev = e.prevInAEL;
+		var AelNext = e.nextInAEL;
+		if (AelPrev == null && AelNext == null && (e != m_ActiveEdges))
+			return; //already deleted
+		if (AelPrev != null)
+			AelPrev.nextInAEL = AelNext;
+		else m_ActiveEdges = AelNext;
+		if (AelNext != null)
+			AelNext.prevInAEL = AelPrev;
+		e.nextInAEL = null;
+		e.prevInAEL = null;
+	}
+	//------------------------------------------------------------------------------
+
+	private function DeleteFromSEL(e:TEdge)
+	{
+		var SelPrev = e.prevInSEL;
+		var SelNext = e.nextInSEL;
+		if (SelPrev == null && SelNext == null && (e != m_SortedEdges))
+			return; //already deleted
+		if (SelPrev != null)
+			SelPrev.nextInSEL = SelNext;
+		else m_SortedEdges = SelNext;
+		if (SelNext != null)
+			SelNext.prevInSEL = SelPrev;
+		e.nextInSEL = null;
+		e.prevInSEL = null;
+	}
+	//------------------------------------------------------------------------------
+
+	private function UpdateEdgeIntoAEL(e : TEdge)
+	{
+		if (e.nextInLML == null)
+			throw "UpdateEdgeIntoAEL: invalid call";
+		var AelPrev = e.prevInAEL;
+		var AelNext = e.nextInAEL;
+		e.nextInLML.outIdx = e.outIdx;
+		if (AelPrev != null)
+			AelPrev.nextInAEL = e.nextInLML;
+		else m_ActiveEdges = e.nextInLML;
+		if (AelNext != null)
+			AelNext.prevInAEL = e.nextInLML;
+		e.nextInLML.side = e.side;
+		e.nextInLML.windDelta = e.windDelta;
+		e.nextInLML.windCnt = e.windCnt;
+		e.nextInLML.windCnt2 = e.windCnt2;
+		e = e.nextInLML;
+		e.prevInAEL = AelPrev;
+		e.nextInAEL = AelNext;
+		if (e.dx != ClipperBase.HORIZONTAL) InsertScanbeam(e.ytop);
+		return e;
+	}
+	//------------------------------------------------------------------------------
+
+	private function ProcessHorizontals()
+	{
+		var horzEdge = m_SortedEdges;
+		while (horzEdge != null)
+		{
+			DeleteFromSEL(horzEdge);
+			ProcessHorizontal(horzEdge);
+			horzEdge = m_SortedEdges;
+		}
+	}
+	//------------------------------------------------------------------------------
+
+	private function ProcessHorizontal(horzEdge:TEdge)
+	{
+		var dir;
+		var horzLeft, horzRight;
+
+		if (horzEdge.xcurr < horzEdge.xtop)
+		{
+			horzLeft = horzEdge.xcurr;
+			horzRight = horzEdge.xtop;
+			dir = Direction.LeftToRight;
+		}
+		else
+		{
+			horzLeft = horzEdge.xtop;
+			horzRight = horzEdge.xcurr;
+			dir = Direction.RightToLeft;
+		}
+
+		var eMaxPair;
+		if (horzEdge.nextInLML != null)
+			eMaxPair = null;
+		else
+			eMaxPair = GetMaximaPair(horzEdge);
+
+		var e:TEdge = GetNextInAEL(horzEdge, dir);
+		while (e != null)
+		{
+			if (e.xcurr == horzEdge.xtop && eMaxPair == null)
+			{
+				if (ClipperBase.SlopesEqual(e, horzEdge.nextInLML))
+				{
+					//if output polygons share an edge, they'll need joining later
+					if (horzEdge.outIdx >= 0 && e.outIdx >= 0)
+						AddJoin(horzEdge.nextInLML, e, horzEdge.outIdx, -1);
+					break; //we've reached the end of the ClipperBase.HORIZONTAL line
+				}
+				else if (e.dx < horzEdge.nextInLML.dx)
+					//we really have got to the end of the intermediate horz edge so quit.
+					//nb: More -ve slopes follow more +ve slopes ABOVE the ClipperBase.HORIZONTAL.
+					break;
+			}
+
+			var eNext = GetNextInAEL(e, dir);
+			if (eMaxPair != null ||
+				((dir == Direction.LeftToRight) && (e.xcurr < horzRight)) ||
+				((dir == Direction.RightToLeft) && (e.xcurr > horzLeft)))
+			{
+				//so far we're still in range of the ClipperBase.HORIZONTAL edge
+
+				if (e == eMaxPair)
+				{
+					//horzEdge is evidently a maxima ClipperBase.HORIZONTAL and we've arrived at its end.
+					if (dir == Direction.LeftToRight)
+						IntersectEdges(horzEdge, e, new Point(e.xcurr, horzEdge.ycurr), 0);
+					else
+						IntersectEdges(e, horzEdge, new Point(e.xcurr, horzEdge.ycurr), 0);
+					if (eMaxPair.outIdx >= 0) throw "ProcessHorizontal error";
+					return;
+				}
+				else if (e.dx == ClipperBase.HORIZONTAL && !IsMinima(e) && !(e.xcurr > e.xtop))
+				{
+					if (dir == Direction.LeftToRight)
+						IntersectEdges(horzEdge, e, new Point(e.xcurr, horzEdge.ycurr),
+							(IsTopHorz(horzEdge, e.xcurr)) ? Protects.Left : Protects.Both);
+					else
+						IntersectEdges(e, horzEdge, new Point(e.xcurr, horzEdge.ycurr),
+							(IsTopHorz(horzEdge, e.xcurr)) ? Protects.Right : Protects.Both);
+				}
+				else if (dir == Direction.LeftToRight)
+				{
+					IntersectEdges(horzEdge, e, new Point(e.xcurr, horzEdge.ycurr),
+						(IsTopHorz(horzEdge, e.xcurr)) ? Protects.Left : Protects.Both);
+				}
+				else
+				{
+					IntersectEdges(e, horzEdge, new Point(e.xcurr, horzEdge.ycurr),
+						(IsTopHorz(horzEdge, e.xcurr)) ? Protects.Right : Protects.Both);
+				}
+				SwapPositionsInAEL(horzEdge, e);
+			}
+			else if ( (dir == Direction.LeftToRight && e.xcurr >= horzRight) ||
+				(dir == Direction.RightToLeft && e.xcurr <= horzLeft) ) break;
+			e = eNext;
+		} //end while ( e )
+
+		if (horzEdge.nextInLML != null)
+		{
+			if (horzEdge.outIdx >= 0)
+				AddOutPt(horzEdge, new Point(horzEdge.xtop, horzEdge.ytop));
+			horzEdge = UpdateEdgeIntoAEL(horzEdge);
+		}
+		else
+		{
+			if (horzEdge.outIdx >= 0)
+				IntersectEdges(horzEdge, eMaxPair,
+					new Point(horzEdge.xtop, horzEdge.ycurr), Protects.Both);
+			DeleteFromAEL(eMaxPair);
+			DeleteFromAEL(horzEdge);
+		}
+	}
+	//------------------------------------------------------------------------------
+
+	private function IsTopHorz(horzEdge:TEdge,XPos:Float) : Bool
+	{
+		var e:TEdge = m_SortedEdges;
+		while (e != null)
+		{
+			if ((XPos >= Math.min(e.xcurr, e.xtop)) && (XPos <= Math.max(e.xcurr, e.xtop)))
+				return false;
+			e = e.nextInSEL;
+		}
+		return true;
+	}
+	//------------------------------------------------------------------------------
+
+	private function GetNextInAEL(e:TEdge,dir:Int) : TEdge
+	{
+		return dir == Direction.LeftToRight ? e.nextInAEL: e.prevInAEL;
+	}
+	//------------------------------------------------------------------------------
+
+	private function IsMinima(e:TEdge) : Bool
+	{
+		return e != null && (e.prev.nextInLML != e) && (e.next.nextInLML != e);
+	}
+	//------------------------------------------------------------------------------
+
+	private function IsMaxima(e:TEdge,y:Float) : Bool
+	{
+		return (e != null && e.ytop == y && e.nextInLML == null);
+	}
+	//------------------------------------------------------------------------------
+
+	private function IsIntermediate(e:TEdge,y:Float) : Bool
+	{
+		return (e.ytop == y && e.nextInLML != null);
+	}
+	//------------------------------------------------------------------------------
+
+	private function GetMaximaPair(e:TEdge) : TEdge
+	{
+		if (!IsMaxima(e.next, e.ytop) || (e.next.xtop != e.xtop))
+			return e.prev; else
+			return e.next;
+	}
+	//------------------------------------------------------------------------------
+
+	private function ProcessIntersections(botY:Int,topY:Int) : Bool
+	{
+		if( m_ActiveEdges == null ) return true;
+		try {
+			BuildIntersectList(botY, topY);
+			if ( m_IntersectNodes == null) return true;
+			if (m_IntersectNodes.next == null || FixupIntersectionOrder())
+				ProcessIntersectList();
+			else
+				return false;
+		} catch( e : Dynamic ) {
+			m_SortedEdges = null;
+			DisposeIntersectNodes();
+			throw "ProcessIntersections error";
+		}
+		m_SortedEdges = null;
+		return true;
+	}
+	//------------------------------------------------------------------------------
+
+	private function BuildIntersectList(botY:Int,topY:Int)
+	{
+		if ( m_ActiveEdges == null ) return;
+
+		//prepare for sorting
+		var e:TEdge = m_ActiveEdges;
+		m_SortedEdges = e;
+		while( e != null )
+		{
+		e.prevInSEL = e.prevInAEL;
+		e.nextInSEL = e.nextInAEL;
+		e.xcurr = TopX( e, topY );
+		e = e.nextInAEL;
+		}
+
+		//bubblesort
+		var isModified = true;
+		while( isModified && m_SortedEdges != null )
+		{
+		isModified = false;
+		e = m_SortedEdges;
+		while( e.nextInSEL != null )
+		{
+			var eNext = e.nextInSEL;
+			var pt:Point = new Point();
+			if (e.xcurr > eNext.xcurr)
+			{
+				if (!IntersectPoint(e, eNext, pt) && e.xcurr > eNext.xcurr +1)
+					throw "Intersection error";
+				if (pt.y > botY)
+				{
+					pt.y = botY;
+					pt.x = TopX(e, pt.y);
+				}
+				InsertIntersectNode(e, eNext, pt);
+				SwapPositionsInSEL(e, eNext);
+				isModified = true;
+			}
+			else
+			e = eNext;
+		}
+		if( e.prevInSEL != null ) e.prevInSEL.nextInSEL = null;
+		else break;
+		}
+		m_SortedEdges = null;
+	}
+	//------------------------------------------------------------------------------
+
+	private function EdgesAdjacent(inode:IntersectNode) : Bool
+	{
+		return (inode.edge1.nextInSEL == inode.edge2) ||
+		(inode.edge1.prevInSEL == inode.edge2);
+	}
+	//------------------------------------------------------------------------------
+
+	private function FixupIntersectionOrder() : Bool
+	{
+		//pre-condition: intersections are sorted bottom-most (then left-most) first.
+		//Now it's crucial that intersections are made only between adjacent edges,
+		//so to ensure this the order of intersections may need adjusting
+		var inode:IntersectNode = m_IntersectNodes;
+		CopyAELToSEL();
+		while (inode != null)
+		{
+			if (!EdgesAdjacent(inode))
+			{
+				var nextNode = inode.next;
+				while (nextNode != null && !EdgesAdjacent(nextNode))
+					nextNode = nextNode.next;
+				if (nextNode == null)
+					return false;
+				SwapIntersectNodes(inode, nextNode);
+			}
+			SwapPositionsInSEL(inode.edge1, inode.edge2);
+			inode = inode.next;
+		}
+		return true;
+	}
+	//------------------------------------------------------------------------------
+
+	private function ProcessIntersectList()
+	{
+		while (m_IntersectNodes != null)
+		{
+		var iNode = m_IntersectNodes.next;
+		{
+			IntersectEdges( m_IntersectNodes.edge1 ,
+			m_IntersectNodes.edge2 , m_IntersectNodes.pt, Protects.Both );
+			SwapPositionsInAEL( m_IntersectNodes.edge1 , m_IntersectNodes.edge2 );
+		}
+		m_IntersectNodes = null;
+		m_IntersectNodes = iNode;
+		}
+	}
+	//------------------------------------------------------------------------------
+
+	private static function Round(value:Float) : Int
+	{
+		return value < 0 ? Std.int(value - 0.5) : Std.int(value + 0.5);
+	}
+	//------------------------------------------------------------------------------
+
+	private static function TopX(edge:TEdge,currentY:Int) : Int
+	{
+		if (currentY == edge.ytop)
+			return edge.xtop;
+		return edge.xbot + Round(edge.dx *(currentY - edge.ybot));
+	}
+	//------------------------------------------------------------------------------
+
+	private function InsertIntersectNode(e1:TEdge,e2:TEdge,pt:Point)
+	{
+		var newNode = new IntersectNode();
+		newNode.edge1 = e1;
+		newNode.edge2 = e2;
+		newNode.pt = pt;
+		newNode.next = null;
+		if (m_IntersectNodes == null) m_IntersectNodes = newNode;
+		else if (newNode.pt.y > m_IntersectNodes.pt.y)
+		{
+		newNode.next = m_IntersectNodes;
+		m_IntersectNodes = newNode;
+		}
+		else
+		{
+		var iNode = m_IntersectNodes;
+		while (iNode.next != null && newNode.pt.y < iNode.next.pt.y)
+			iNode = iNode.next;
+		newNode.next = iNode.next;
+		iNode.next = newNode;
+		}
+	}
+	//------------------------------------------------------------------------------
+
+	private function SwapIntersectNodes(int1:IntersectNode,int2:IntersectNode)
+	{
+		var e1 = int1.edge1;
+		var e2 = int1.edge2;
+		var p:Point = int1.pt;
+		int1.edge1 = int2.edge1;
+		int1.edge2 = int2.edge2;
+		int1.pt = int2.pt;
+		int2.edge1 = e1;
+		int2.edge2 = e2;
+		int2.pt = p;
+	}
+	//------------------------------------------------------------------------------
+
+	private function IntersectPoint(edge1:TEdge,edge2:TEdge,ip:Point)
+	{
+		var b1, b2;
+		if (ClipperBase.SlopesEqual(edge1, edge2))
+		{
+			if (edge2.ybot > edge1.ybot)
+			ip.y = edge2.ybot;
+			else
+			ip.y = edge1.ybot;
+			return false;
+		}
+		else if (edge1.dx == 0)
+		{
+			ip.x = edge1.xbot;
+			if (edge2.dx == ClipperBase.HORIZONTAL)
+			{
+				ip.y = edge2.ybot;
+			}
+			else
+			{
+				b2 = edge2.ybot - (edge2.xbot / edge2.dx);
+				ip.y = Round(ip.x / edge2.dx + b2);
+			}
+		}
+		else if (edge2.dx == 0)
+		{
+			ip.x = edge2.xbot;
+			if (edge1.dx == ClipperBase.HORIZONTAL)
+			{
+				ip.y = edge1.ybot;
+			}
+			else
+			{
+				b1 = edge1.ybot - (edge1.xbot / edge1.dx);
+				ip.y = Round(ip.x / edge1.dx + b1);
+			}
+		}
+		else
+		{
+			b1 = edge1.xbot - edge1.ybot * edge1.dx;
+			b2 = edge2.xbot - edge2.ybot * edge2.dx;
+			var q:Float = (b2 - b1) / (edge1.dx - edge2.dx);
+			ip.y = Round(q);
+			if (Math.abs(edge1.dx) < Math.abs(edge2.dx))
+				ip.x = Round(edge1.dx * q + b1);
+			else
+				ip.x = Round(edge2.dx * q + b2);
+		}
+
+		if (ip.y < edge1.ytop || ip.y < edge2.ytop)
+		{
+			if (edge1.ytop > edge2.ytop)
+			{
+				ip.x = edge1.xtop;
+				ip.y = edge1.ytop;
+				return TopX(edge2, edge1.ytop) < edge1.xtop;
+			}
+			else
+			{
+				ip.x = edge2.xtop;
+				ip.y = edge2.ytop;
+				return TopX(edge1, edge2.ytop) > edge2.xtop;
+			}
+		}
+		else
+			return true;
+	}
+	//------------------------------------------------------------------------------
+
+	private function DisposeIntersectNodes()
+	{
+		while ( m_IntersectNodes != null )
+		{
+		var iNode = m_IntersectNodes.next;
+		m_IntersectNodes = null;
+		m_IntersectNodes = iNode;
+		}
+	}
+	//------------------------------------------------------------------------------
+
+	private function ProcessEdgesAtTopOfScanbeam(topY:Int)
+	{
+		var e:TEdge = m_ActiveEdges;
+		while( e != null )
+		{
+		//1. process maxima, treating them as if they're 'bent' ClipperBase.HORIZONTAL edges,
+		//	 but exclude maxima with ClipperBase.HORIZONTAL edges. nb: e can't be a ClipperBase.HORIZONTAL.
+		if( IsMaxima(e, topY) && GetMaximaPair(e).dx != ClipperBase.HORIZONTAL )
+		{
+			//'e' might be removed from AEL, as may any following edges so
+			var ePrev = e.prevInAEL;
+			DoMaxima(e, topY);
+			if( ePrev == null ) e = m_ActiveEdges;
+			else e = ePrev.nextInAEL;
+		}
+		else
+		{
+			var intermediateVert = IsIntermediate(e, topY);
+			//2. promote ClipperBase.HORIZONTAL edges, otherwise update xcurr and ycurr
+			if (intermediateVert && e.nextInLML.dx == ClipperBase.HORIZONTAL)
+			{
+			if (e.outIdx >= 0)
+			{
+				AddOutPt(e, new Point(e.xtop, e.ytop));
+
+				var i = -1;
+				while( ++i < m_HorizJoins.length ) {
+					var hj:HorzJoinRec = m_HorizJoins[i];
+					if (HasOverlapSegment(new Point(hj.edge.xbot, hj.edge.ybot),
+						new Point(hj.edge.xtop, hj.edge.ytop),
+						new Point(e.nextInLML.xbot, e.nextInLML.ybot),
+						new Point(e.nextInLML.xtop, e.nextInLML.ytop)))
+							AddJoin(hj.edge, e.nextInLML, hj.savedIdx, e.outIdx);
+				}
+
+				AddHorzJoin(e.nextInLML, e.outIdx);
+			}
+			e = UpdateEdgeIntoAEL(e);
+			AddEdgeToSEL(e);
+			}
+			else
+			{
+			e.xcurr = TopX( e, topY );
+			e.ycurr = topY;
+			if (forceSimple && e.prevInAEL != null &&
+				e.prevInAEL.xcurr == e.xcurr &&
+				e.outIdx >= 0 && e.prevInAEL.outIdx >= 0)
+			{
+				if (intermediateVert)
+					AddOutPt(e.prevInAEL, new Point(e.xcurr, topY));
+				else
+					AddOutPt(e, new Point(e.xcurr, topY));
+			}
+			}
+			e = e.nextInAEL;
+		}
+		}
+
+		//3. Process horizontals at the top of the scanbeam
+		ProcessHorizontals();
+
+		//4. Promote intermediate vertices
+		e = m_ActiveEdges;
+		while( e != null )
+		{
+		if( IsIntermediate( e, topY ) )
+		{
+			if (e.outIdx >= 0) AddOutPt(e, new Point(e.xtop, e.ytop));
+			e = UpdateEdgeIntoAEL(e);
+
+			//if output polygons share an edge, they'll need joining later
+			var ePrev = e.prevInAEL;
+			var eNext = e.nextInAEL;
+			if (ePrev != null && ePrev.xcurr == e.xbot &&
+			ePrev.ycurr == e.ybot && e.outIdx >= 0 &&
+			ePrev.outIdx >= 0 && ePrev.ycurr > ePrev.ytop &&
+			ClipperBase.SlopesEqual(e, ePrev))
+			{
+				AddOutPt(ePrev, new Point(e.xbot, e.ybot));
+				AddJoin(e, ePrev, -1, -1);
+			}
+			else if (eNext != null && eNext.xcurr == e.xbot &&
+			eNext.ycurr == e.ybot && e.outIdx >= 0 &&
+			eNext.outIdx >= 0 && eNext.ycurr > eNext.ytop &&
+			ClipperBase.SlopesEqual(e, eNext))
+			{
+				AddOutPt(eNext, new Point(e.xbot, e.ybot));
+				AddJoin(e, eNext, -1, -1);
+			}
+		}
+		e = e.nextInAEL;
+		}
+	}
+	//------------------------------------------------------------------------------
+
+	private function DoMaxima(e:TEdge,topY:Int)
+	{
+		var eMaxPair = GetMaximaPair(e);
+		var x:Int = e.xtop;
+		var eNext = e.nextInAEL;
+		while( eNext != eMaxPair )
+		{
+		if (eNext == null) throw "DoMaxima error";
+		IntersectEdges( e, eNext, new Point(x, topY), Protects.Both );
+		SwapPositionsInAEL(e, eNext);
+		eNext = e.nextInAEL;
+		}
+		if( e.outIdx < 0 && eMaxPair.outIdx < 0 )
+		{
+		DeleteFromAEL( e );
+		DeleteFromAEL( eMaxPair );
+		}
+		else if( e.outIdx >= 0 && eMaxPair.outIdx >= 0 )
+		{
+			IntersectEdges(e, eMaxPair, new Point(x, topY), Protects.None);
+		}
+		else throw "DoMaxima error";
+	}
+	//------------------------------------------------------------------------------
+
+	static function reversePolygons(polys:Polygons)
+	{
+		for( p in polys ) p.reverse();
+	}
+
+	//------------------------------------------------------------------------------
+
+	private function PointCount(pts:OutPt) : Int
+	{
+		if (pts == null) return 0;
+		var result:Int = 0;
+		var p:OutPt = pts;
+		do
+		{
+			result++;
+			p = p.next;
+		}
+		while (p != pts);
+		return result;
+	}
+	//------------------------------------------------------------------------------
+
+	private function BuildResult(polyg:Polygons)
+	{
+		polyg.splice(0,polyg.length);
+		for( outRec in m_PolyOuts )
+		{
+			if (outRec.pts == null) continue;
+			var p:OutPt = outRec.pts;
+			var cnt:Int = PointCount(p);
+			if (cnt < 3) continue;
+			var pg:Polygon = new Polygon();
+			for( j in 0...cnt )
+			{
+				pg.addPoint(p.pt);
+				p = p.prev;
+			}
+			polyg.push(pg);
+		}
+	}
+	//------------------------------------------------------------------------------
+
+	private function BuildResult2(polytree:PolyTree)
+	{
+		polytree.clear();
+
+		//add each output polygon/contour to polytree
+		for ( outRec in m_PolyOuts )
+		{
+			var cnt:Int = PointCount(outRec.pts);
+			if (cnt < 3) continue;
+			FixHoleLinkage(outRec);
+			var pn:PolyNode = new PolyNode();
+			polytree.allPolys.push(pn);
+			outRec.polyNode = pn;
+			var op:OutPt = outRec.pts;
+			for( j in 0...cnt )
+			{
+				pn.polygon.addPoint(op.pt);
+				op = op.prev;
+			}
+		}
+
+		//fixup PolyNode links etc
+		for( outRec in m_PolyOuts )
+		{
+			if (outRec.polyNode == null) continue;
+			if (outRec.firstLeft == null)
+				polytree.addChild(outRec.polyNode);
+			else
+				outRec.firstLeft.polyNode.addChild(outRec.polyNode);
+		}
+	}
+	//------------------------------------------------------------------------------
+
+	private function FixupOutPolygon(outRec:OutRec)
+	{
+		//FixupOutPolygon() - removes duplicate points and simplifies consecutive
+		//parallel edges by removing the middle vertex.
+		var lastOK = null;
+		outRec.bottomPt = null;
+		var pp:OutPt = outRec.pts;
+		while( true )
+		{
+			if (pp.prev == pp || pp.prev == pp.next)
+			{
+				DisposeOutPts(pp);
+				outRec.pts = null;
+				return;
+			}
+			//test for duplicate points and for same slope (cross-product)
+			if (ClipperBase.PointsEqual(pp.pt, pp.next.pt) ||
+				ClipperBase.SlopesEqual3(pp.prev.pt, pp.pt, pp.next.pt))
+			{
+				lastOK = null;
+				var tmp:OutPt = pp;
+				pp.prev.next = pp.next;
+				pp.next.prev = pp.prev;
+				pp = pp.prev;
+				tmp = null;
+			}
+			else if (pp == lastOK) break;
+			else
+			{
+				if (lastOK == null) lastOK = pp;
+				pp = pp.next;
+			}
+		}
+		outRec.pts = pp;
+	}
+	//------------------------------------------------------------------------------
+
+	private function JoinPoints(j:JoinRec) : Null<{ p1 : OutPt, p2 : OutPt }>
+	{
+		var p1 = null, p2 = null;
+		var outRec1 = m_PolyOuts[j.poly1Idx];
+		var outRec2 = m_PolyOuts[j.poly2Idx];
+		if (outRec1	== null || outRec2 == null)	return null;
+		var pp1a = outRec1.pts;
+		var pp2a = outRec2.pts;
+		var pt1 = new Ref<Point>(j.pt2a), pt2 = new Ref<Point>(j.pt2b);
+		var pt3 = new Ref<Point>(j.pt1a), pt4 = new Ref<Point>(j.pt1b);
+		pp1a = FindSegment(pp1a, pt1, pt2);
+		if( pp1a == null ) return null;
+		if (outRec1 == outRec2)
+		{
+			//we're searching the same polygon for overlapping segments so
+			//segment 2 mustn't be the same as segment 1
+			pp2a = pp1a.next;
+			pp2a = FindSegment(pp2a, pt3, pt4);
+			if( pp2a == null || pp2a == pp1a )
+				return null;
+		}
+		else {
+			pp2a = FindSegment(pp2a, pt3, pt4);
+			if( pp2a == null )
+				return null;
+		}
+
+		if (!GetOverlapSegment(pt1.val, pt2.val, pt3.val, pt4.val, pt1, pt2)) return null;
+		
+		// unref
+		var pt1 = pt1.val;
+		var pt2 = pt2.val;
+		var pt3 = pt3.val;
+		var pt4 = pt4.val;
+
+		var p3, p4, prev = pp1a.prev;
+		//get p1 & p2 polypts - the overlap start & endpoints on poly1
+		if (ClipperBase.PointsEqual(pp1a.pt, pt1)) p1 = pp1a;
+		else if (ClipperBase.PointsEqual(prev.pt, pt1)) p1 = prev;
+		else p1 = InsertPolyPtBetween(pp1a, prev, pt1);
+
+		if (ClipperBase.PointsEqual(pp1a.pt, pt2)) p2 = pp1a;
+		else if (ClipperBase.PointsEqual(prev.pt, pt2)) p2 = prev;
+		else if ((p1 == pp1a) || (p1 == prev))
+			p2 = InsertPolyPtBetween(pp1a, prev, pt2);
+		else if (Pt3IsBetweenPt1AndPt2(pp1a.pt, p1.pt, pt2))
+			p2 = InsertPolyPtBetween(pp1a, p1, pt2); else
+			p2 = InsertPolyPtBetween(p1, prev, pt2);
+
+		//get p3 & p4 polypts - the overlap start & endpoints on poly2
+		prev = pp2a.prev;
+		if (ClipperBase.PointsEqual(pp2a.pt, pt1)) p3 = pp2a;
+		else if (ClipperBase.PointsEqual(prev.pt, pt1)) p3 = prev;
+		else p3 = InsertPolyPtBetween(pp2a, prev, pt1);
+
+		if (ClipperBase.PointsEqual(pp2a.pt, pt2)) p4 = pp2a;
+		else if (ClipperBase.PointsEqual(prev.pt, pt2)) p4 = prev;
+		else if ((p3 == pp2a) || (p3 == prev))
+			p4 = InsertPolyPtBetween(pp2a, prev, pt2);
+		else if (Pt3IsBetweenPt1AndPt2(pp2a.pt, p3.pt, pt2))
+			p4 = InsertPolyPtBetween(pp2a, p3, pt2); else
+			p4 = InsertPolyPtBetween(p3, prev, pt2);
+
+		//p1.pt == p3.pt and p2.pt == p4.pt so join p1 to p3 and p2 to p4
+		if (p1.next == p2 && p3.prev == p4)
+		{
+			p1.next = p3;
+			p3.prev = p1;
+			p2.prev = p4;
+			p4.next = p2;
+			return {p1:p1,p2:p2};
+		}
+		else if (p1.prev == p2 && p3.next == p4)
+		{
+			p1.prev = p3;
+			p3.next = p1;
+			p2.next = p4;
+			p4.prev = p2;
+			return {p1:p1,p2:p2};
+		}
+		else
+			return null; //an orientation is probably wrong
+	}
+	//----------------------------------------------------------------------
+
+	private function FixupJoinRecs(j:JoinRec,pt:OutPt,startIdx:Int)
+	{
+		for( j2 in m_Joins )
+		{
+			if (j2.poly1Idx == j.poly1Idx && PointIsVertex(j2.pt1a, pt))
+			j2.poly1Idx = j.poly2Idx;
+			if (j2.poly2Idx == j.poly1Idx && PointIsVertex(j2.pt2a, pt))
+			j2.poly2Idx = j.poly2Idx;
+		}
+	}
+	//----------------------------------------------------------------------
+
+	private function Poly2ContainsPoly1(outPt1:OutPt,outPt2:OutPt) : Bool
+	{
+		var pt:OutPt = outPt1;
+		//Because the polygons may be touching, we need to find a vertex that
+		//isn't touching the other polygon
+		if (PointOnPolygon(pt.pt, outPt2))
+		{
+			pt = pt.next;
+			while (pt != outPt1 && PointOnPolygon(pt.pt, outPt2))
+				pt = pt.next;
+			if (pt == outPt1) return true;
+		}
+		return PointInPolygon(pt.pt, outPt2);
+	}
+	//----------------------------------------------------------------------
+
+	private function FixupFirstLefts1(OldOutRec:OutRec,NewOutRec:OutRec)
+	{
+		for ( outRec in m_PolyOuts )
+		{
+			if (outRec.pts != null && outRec.firstLeft == OldOutRec)
+			{
+				if (Poly2ContainsPoly1(outRec.pts, NewOutRec.pts))
+					outRec.firstLeft = NewOutRec;
+			}
+		}
+	}
+	//----------------------------------------------------------------------
+
+	private function FixupFirstLefts2(OldOutRec:OutRec,NewOutRec:OutRec)
+	{
+		for( outRec in m_PolyOuts )
+			if (outRec.firstLeft == OldOutRec) outRec.firstLeft = NewOutRec;
+	}
+	//----------------------------------------------------------------------
+
+	private function JoinCommonEdges()
+	{
+		var i = -1;
+		while( ++i < m_Joins.length )
+		{
+			var j = m_Joins[i];
+
+		var outRec1 = GetOutRec(j.poly1Idx);
+		var outRec2 = GetOutRec(j.poly2Idx);
+
+		if (outRec1.pts == null || outRec2.pts == null) continue;
+
+		//get the polygon fragment with the correct hole state (firstLeft)
+		//before calling JoinPoints()
+		var holeStateRec;
+		if (outRec1 == outRec2) holeStateRec = outRec1;
+		else if (Param1RightOfParam2(outRec1, outRec2)) holeStateRec = outRec2;
+		else if (Param1RightOfParam2(outRec2, outRec1)) holeStateRec = outRec1;
+		else holeStateRec = GetLowermostRec(outRec1, outRec2);
+
+		var r = JoinPoints(j);
+		if( r == null ) continue;
+		var p1 = r.p1, p2 = r.p2;
+
+		if (outRec1 == outRec2)
+		{
+			//instead of joining two polygons, we've just created a new one by
+			//splitting one polygon into two.
+			outRec1.pts = p1;
+			outRec1.bottomPt = null;
+			outRec2 = CreateOutRec();
+			outRec2.pts = p2;
+
+			if (Poly2ContainsPoly1(outRec2.pts, outRec1.pts))
+			{
+				//outRec2 is contained by outRec1
+				outRec2.isHole = !outRec1.isHole;
+				outRec2.firstLeft = outRec1;
+
+				FixupJoinRecs(j, p2, i + 1);
+
+				//fixup firstLeft pointers that may need reassigning to OutRec1
+				if (m_UsingPolyTree) FixupFirstLefts2(outRec2, outRec1);
+
+				FixupOutPolygon(outRec1); //nb: do this BEFORE testing orientation
+				FixupOutPolygon(outRec2); //	but AFTER calling FixupJoinRecs()
+
+				if (xor(outRec2.isHole,m_ReverseOutput) == (Area(outRec2) > 0))
+					ReversePolyPtLinks(outRec2.pts);
+
+			}
+			else if (Poly2ContainsPoly1(outRec1.pts, outRec2.pts))
+			{
+				//outRec1 is contained by outRec2
+				outRec2.isHole = outRec1.isHole;
+				outRec1.isHole = !outRec2.isHole;
+				outRec2.firstLeft = outRec1.firstLeft;
+				outRec1.firstLeft = outRec2;
+
+				FixupJoinRecs(j, p2, i + 1);
+
+				//fixup firstLeft pointers that may need reassigning to OutRec1
+				if (m_UsingPolyTree) FixupFirstLefts2(outRec1, outRec2);
+
+				FixupOutPolygon(outRec1); //nb: do this BEFORE testing orientation
+				FixupOutPolygon(outRec2); //	but AFTER calling FixupJoinRecs()
+
+				if (xor(outRec1.isHole,m_ReverseOutput) == (Area(outRec1) > 0))
+					ReversePolyPtLinks(outRec1.pts);
+			}
+			else
+			{
+				//the 2 polygons are completely separate
+				outRec2.isHole = outRec1.isHole;
+				outRec2.firstLeft = outRec1.firstLeft;
+
+				FixupJoinRecs(j, p2, i + 1);
+
+				//fixup firstLeft pointers that may need reassigning to OutRec2
+				if (m_UsingPolyTree) FixupFirstLefts1(outRec1, outRec2);
+
+				FixupOutPolygon(outRec1); //nb: do this BEFORE testing orientation
+				FixupOutPolygon(outRec2); //	but AFTER calling FixupJoinRecs()
+			}
+		}
+		else
+		{
+			//joined 2 polygons together
+
+			//cleanup redundant edges
+			FixupOutPolygon(outRec1);
+
+			outRec2.pts = null;
+			outRec2.bottomPt = null;
+			outRec2.idx = outRec1.idx;
+
+			outRec1.isHole = holeStateRec.isHole;
+			if (holeStateRec == outRec2)
+				outRec1.firstLeft = outRec2.firstLeft;
+			outRec2.firstLeft = outRec1;
+
+			//fixup firstLeft pointers that may need reassigning to OutRec1
+			if (m_UsingPolyTree) FixupFirstLefts2(outRec2, outRec1);
+		}
+		}
+	}
+	//------------------------------------------------------------------------------
+
+	private function UpdateOutPtIdxs(outrec:OutRec)
+	{
+		var op:OutPt = outrec.pts;
+		do
+		{
+		op.idx = outrec.idx;
+		op = op.prev;
+		}
+		while(op != outrec.pts);
+	}
+	//------------------------------------------------------------------------------
+
+	private function DoSimplePolygons()
+	{
+		var i:Int = 0;
+		while (i < m_PolyOuts.length)
+		{
+		var outrec:OutRec = m_PolyOuts[i++];
+		var op:OutPt = outrec.pts;
+		if (op == null) continue;
+		do //for each Pt in Polygon until duplicate found do
+		{
+			var op2 = op.next;
+			while (op2 != outrec.pts)
+			{
+			if (ClipperBase.PointsEqual(op.pt, op2.pt) && op2.next != op && op2.prev != op)
+			{
+				//split the polygon into two
+				var op3 = op.prev;
+				var op4 = op2.prev;
+				op.prev = op4;
+				op4.next = op;
+				op2.prev = op3;
+				op3.next = op2;
+
+				outrec.pts = op;
+				var outrec2 = CreateOutRec();
+				outrec2.pts = op2;
+				UpdateOutPtIdxs(outrec2);
+				if (Poly2ContainsPoly1(outrec2.pts, outrec.pts))
+				{
+				//OutRec2 is contained by OutRec1
+				outrec2.isHole = !outrec.isHole;
+				outrec2.firstLeft = outrec;
+				}
+				else
+				if (Poly2ContainsPoly1(outrec.pts, outrec2.pts))
+				{
+				//OutRec1 is contained by OutRec2
+				outrec2.isHole = outrec.isHole;
+				outrec.isHole = !outrec2.isHole;
+				outrec2.firstLeft = outrec.firstLeft;
+				outrec.firstLeft = outrec2;
+				} else
+				{
+				//the 2 polygons are separate
+				outrec2.isHole = outrec.isHole;
+				outrec2.firstLeft = outrec.firstLeft;
+				}
+				op2 = op; //ie get ready for the next iteration
+			}
+			op2 = op2.next;
+			}
+			op = op.next;
+		}
+		while (op != outrec.pts);
+		}
+	}
+
+	//------------------------------------------------------------------------------
+
+	static function Area(outRec:OutRec) {
+		var op:OutPt = outRec.pts;
+		if (op == null) return 0.;
+		var a:Float = 0;
+		do {
+			a = a + (op.pt.x + op.prev.pt.x) * (op.prev.pt.y - op.pt.y);
+			op = op.next;
+		} while (op != outRec.pts);
+		return a/2;
+	}
+
+	//------------------------------------------------------------------------------
+	// OffsetPolygon functions
+	//------------------------------------------------------------------------------
+
+	static function BuildArc(pt:Point , a1:Float , a2:Float , r:Float , limit:Float) : Polygon
+	{
+		//see notes in clipper.pas regarding steps
+		var arcFrac = Math.abs(a2 - a1) / (2 * Math.PI);
+		var steps:Int = Std.int(arcFrac * Math.PI / Math.acos(1 - limit / Math.abs(r)));
+		if (steps < 2)
+			steps = 2;
+		else if (steps > Std.int(222.0 * arcFrac))
+			steps = Std.int(222.0 * arcFrac);
+
+		var x:Float = Math.cos(a1);
+		var y:Float = Math.sin(a1);
+		var c:Float = Math.cos((a2 - a1) / steps);
+		var s:Float = Math.sin((a2 - a1) / steps);
+		var result:Polygon = new Polygon();
+		for ( i in 0...steps+1 )
+		{
+			result.add(pt.x + Round(x * r), pt.y + Round(y * r));
+			var x2 = x;
+			x = x * c - s * y;	//cross product
+			y = x2 * s + y * c; //dot product
+		}
+		return result;
+	}
+	//------------------------------------------------------------------------------
+
+	static function GetUnitNormal(pt1:Point, pt2:Point)
+	{
+		var dx:Float = (pt2.x - pt1.x);
+		var dy:Float = (pt2.y - pt1.y);
+		if ((dx == 0) && (dy == 0)) return new DoublePoint();
+
+		var f:Float = 1 * 1.0 / Math.sqrt(dx * dx + dy * dy);
+		dx *= f;
+		dy *= f;
+
+		return new DoublePoint(dy, -dx);
+	}
+	//------------------------------------------------------------------------------
+
+	static inline function UpdateBotPt(pt:Point, botPt:Point)
+	{
+		return if (pt.y > botPt.y || (pt.y == botPt.y && pt.x < botPt.x)) pt else null;
+	}
+	//------------------------------------------------------------------------------
+
+	public static function offsetPolygons(poly:Polygons, delta:Float, ?jointype:JoinType, MiterLimit:Float=0, AutoFix:Bool=true) : Polygons
+	{
+		if( jointype == null )
+			jointype = JoinType.Square;
+		var result:Polygons = new Polygons();
+
+		//AutoFix - fixes polygon orientation if necessary and removes
+		//duplicate vertices. Can be set false when you're sure that polygon
+		//orientation is correct and that there are no duplicate vertices.
+		if (AutoFix)
+		{
+			var Len = poly.length, botI = 0;
+			while (botI < Len && poly[botI].length == 0) botI++;
+			if (botI == Len) return result;
+
+			//botPt: used to find the lowermost (in inverted y-axis) & leftmost point
+			//This point (on pts[botI]) must be on an outer polygon ring and if
+			//its orientation is false (counterclockwise) then assume all polygons
+			//need reversing
+			var botPt = poly[botI][0];
+			for ( i in botI...Len )
+			{
+				if (poly[i].length == 0) continue;
+				var tmp = UpdateBotPt(poly[i][0], botPt);
+				if( tmp != null ) {
+					botPt = tmp;
+					botI = i;
+				}
+				var j = poly[i].length - 1;
+				while( j > 0 )
+				{
+					if (ClipperBase.PointsEqual(poly[i][j], poly[i][j - 1]))
+						poly[i].removeAt(j);
+					else {
+						var tmp = UpdateBotPt(poly[i][j], botPt);
+						if (tmp != null) {
+							botPt = tmp;
+							botI = i;
+						}
+					}
+					j--;
+				}
+			}
+			if (!poly[botI].getOrientation())
+				reversePolygons(poly);
+		}
+
+		new PolyOffsetBuilder(poly, result, true, delta, jointype, EndType.Closed, MiterLimit);
+		return result;
+	}
+
+	//------------------------------------------------------------------------------
+
+	public static function offsetPolyLines(lines:Polygons, delta:Float, jointype:JoinType, endtype:EndType, limit:Float) {
+		var result:Polygons = new Polygons();
+
+		//automatically strip duplicate points because it gets complicated with
+		//open and closed lines and when to strip duplicates across begin-end
+		var pts:Polygons = lines.copy();
+		for( poly in pts ) {
+			var j = poly.length - 1;
+			while( j > 0 ) {
+				if (ClipperBase.PointsEqual(poly[j], poly[j - 1]))
+					poly.removeAt(j);
+				j--;
+			}
+		}
+
+		if (endtype == EndType.Closed)
+		{
+			var sz:Int = pts.length;
+			for ( i in 0...sz )
+			{
+				var line:Polygon = new Polygon(pts[i].points.copy());
+				line.reverse();
+				pts.push(line);
+			}
+			new PolyOffsetBuilder(pts, result, true, delta, jointype, endtype, limit);
+		}
+		else
+			new PolyOffsetBuilder(pts, result, false, delta, jointype, endtype, limit);
+
+		return result;
+	}
+	//------------------------------------------------------------------------------
+
+	//------------------------------------------------------------------------------
+	// SimplifyPolygon functions
+	// Convert self-intersecting polygons into simple polygons
+	//------------------------------------------------------------------------------
+
+	public static function simplifyPolygon(poly:Polygon, ?fillType ) : Polygons {
+		if( fillType == null ) fillType = PolyFillType.EvenOdd;
+		var result:Polygons = new Polygons();
+		var c:Clipper = new Clipper();
+		c.forceSimple = true;
+		c.addPolygon(poly, PolyType.Subject);
+		c.execute(ClipType.Union, result, fillType, fillType);
+		return result;
+	}
+	//------------------------------------------------------------------------------
+
+	public static function simplifyPolygons(polys:Polygons, ?fillType) : Polygons {
+		if( fillType == null ) fillType = PolyFillType.EvenOdd;
+		var result:Polygons = new Polygons();
+		var c:Clipper = new Clipper();
+		c.forceSimple = true;
+		c.addPolygons(polys, PolyType.Subject);
+		c.execute(ClipType.Union, result, fillType, fillType);
+		return result;
+	}
+	//------------------------------------------------------------------------------
+
+	private static function DistanceSqrd(pt1:Point,pt2:Point) : Float
+	{
+		var dx:Float = (pt1.x*1.0 - pt2.x);
+		var dy:Float = (pt1.y*1.0 - pt2.y);
+		return (dx*dx + dy*dy);
+	}
+	//------------------------------------------------------------------------------
+
+	private static function ClosestPointOnLine(pt:Point,linePt1:Point,linePt2:Point) : DoublePoint
+	{
+		var dx:Float = (linePt2.x*1.0 - linePt1.x);
+		var dy:Float = (linePt2.y*1.0 - linePt1.y);
+		if (dx == 0 && dy == 0)
+			return new DoublePoint(linePt1.x, linePt1.y);
+		var q:Float = ((pt.x-linePt1.x)*dx + (pt.y-linePt1.y)*dy) / (dx*dx + dy*dy);
+		return new DoublePoint(
+			(1-q)*linePt1.x + q*linePt2.x,
+			(1-q)*linePt1.y + q*linePt2.y);
+	}
+	//------------------------------------------------------------------------------
+
+	private static function SlopesNearColinear(pt1:Point,pt2:Point,pt3:Point,distSqrd:Float) : Bool {
+		if (DistanceSqrd(pt1, pt2) > DistanceSqrd(pt1, pt3)) return false;
+		var cpol:DoublePoint = ClosestPointOnLine(pt2, pt1, pt3);
+		var dx:Float = pt2.x - cpol.x;
+		var dy:Float = pt2.y - cpol.y;
+		return (dx*dx + dy*dy) < distSqrd;
+	}
+	//------------------------------------------------------------------------------
+
+	private static function PointsAreClose(pt1:Point,pt2:Point,distSqrd:Float) : Bool
+	{
+		var dx:Float = pt1.x*1.0 - pt2.x;
+		var dy:Float = pt1.y*1.0 - pt2.y;
+		return ((dx * dx) + (dy * dy) <= distSqrd);
+	}
+	//------------------------------------------------------------------------------
+
+	public static function cleanPolygon(poly:Polygon, distance:Float = 1.415) {
+		//distance = proximity in units/pixels below which vertices
+		//will be stripped. Default ~= sqrt(2) so when adjacent
+		//vertices have both x & y coords within 1 unit, then
+		//the second vertex will be stripped.
+		var distSqrd = (distance * distance);
+		var highI = poly.length -1;
+		var result:Polygon = new Polygon();
+		while (highI > 0 && PointsAreClose(poly[highI], poly[0], distSqrd)) highI--;
+		if (highI < 2) return result;
+		var pt:Point = poly[highI];
+		var i:Int = 0;
+		while( true )
+		{
+			while (i < highI && PointsAreClose(pt, poly[i], distSqrd)) i+=2;
+			var i2 = i;
+			while (i < highI && (PointsAreClose(poly[i], poly[i + 1], distSqrd) ||
+				SlopesNearColinear(pt, poly[i], poly[i + 1], distSqrd))) i++;
+			if (i >= highI) break;
+			else if (i != i2) continue;
+			pt = poly[i++];
+			result.addPoint(pt);
+		}
+		if (i <= highI) result.addPoint(poly[i]);
+		i = result.length;
+		if (i > 2 && SlopesNearColinear(result[i - 2], result[i - 1], result[0], distSqrd))
+			result.removeAt(i -  1);
+		if (result.length < 3) result = new Polygon([]) /*clear*/;
+		return result;
+	}
+	//------------------------------------------------------------------------------
+
+	public static function cleanPolygons(polys:Polygons, ?distance:Float = 1.415)
+	{
+		return [for( p in polys ) cleanPolygon(p,distance)];
+	}
+
+}
+
+//------------------------------------------------------------------------------
+
+

+ 8 - 0
hxd/clipper/EndType.hx

@@ -0,0 +1,8 @@
+package hxd.clipper;
+
+enum EndType {
+	Closed;
+	Butt;
+	Square;
+	Round;
+}

+ 7 - 0
hxd/clipper/JoinType.hx

@@ -0,0 +1,7 @@
+package hxd.clipper;
+
+enum JoinType {
+	Square;
+	Round;
+	Miter;
+}

+ 16 - 0
hxd/clipper/Point.hx

@@ -0,0 +1,16 @@
+package hxd.clipper;
+
+class Point {
+	public var x : Int;
+	public var y : Int;
+	public inline function new(x=0, y=0) {
+		this.x = x;
+		this.y = y;
+	}
+	public inline function clone() {
+		return new Point(x, y);
+	}
+	function toString() {
+		return "{" + x + "," + y + "}";
+	}
+}

+ 8 - 0
hxd/clipper/PolyFillType.hx

@@ -0,0 +1,8 @@
+package hxd.clipper;
+
+enum PolyFillType {
+	EvenOdd;
+	NonZero;
+	Positive;
+	Negative;
+}

+ 6 - 0
hxd/clipper/PolyType.hx

@@ -0,0 +1,6 @@
+package hxd.clipper;
+
+enum PolyType {
+	Subject;
+	Clip;
+}

+ 86 - 0
hxd/clipper/Polygon.hx

@@ -0,0 +1,86 @@
+package hxd.clipper;
+
+abstract Polygon(Array<Point>) {
+
+	public var length(get, never): Int;
+	public var points(get, never) : Array<Point>;
+	
+	public inline function new(?pts) {
+		this = pts == null ? [] : pts;
+	}
+	
+	public inline function add(x, y) {
+		this.push(new Point(x, y));
+	}
+	
+	public inline function addPoint(p) {
+		this.push(p);
+	}
+	
+	inline function get_points() : Array<Point> {
+		return this;
+	}
+	
+	inline function get_length() {
+		return this.length;
+	}
+	
+	public inline function reverse() {
+		this.reverse();
+	}
+	
+	public function getArea() {
+		var highI = this.length - 1;
+		if (highI < 2) return 0.;
+		/*if (FullRangeNeeded(poly))
+		{
+			var a:Int128 = new Int128(0);
+			a = Int128.Int128Mul(poly[highI].x + poly[0].x, poly[0].y - poly[highI].y);
+			for ( i in 1...highI+1 )
+				a += Int128.Int128Mul(poly[i - 1].x + poly[i].x, poly[i].y - poly[i - 1].y);
+			return a.ToDouble() / 2;
+		}
+		else
+		*/
+		{
+			var area:Float = (this[highI].x + this[0].x) * 1.0 * (this[0].y * 1.0 - this[highI].y);
+			for ( i in 1...highI+1 )
+				area += (this[i - 1].x + this[i].x) * 1.0 * (this[i].y - this[i -1].y);
+			return area * 0.5;
+		}
+	}
+
+	public inline function clean(?distance) {
+		return Clipper.cleanPolygon(this, distance);
+	}
+
+	public inline function simplify(?fillType) {
+		return Clipper.simplifyPolygon(this, fillType);
+	}
+	
+	public inline function getOrientation() : Bool {
+		return getArea() >= 0;
+	}
+	
+	public inline function removeAt(i:Int) {
+		this.splice(i, 1);
+	}
+		
+	@:arrayAccess function arrayGet(i:Int) : Point {
+		return this[i];
+	}
+
+	@:arrayAccess function arraySet(i:Int, v : Point) : Point {
+		return this[i] = v;
+	}
+
+	@:to public function toArray() : Array<Point> {
+		return this;
+	}
+	
+	@:from public static function fromArray( pts : Array<Point> ) {
+		return new Polygon(pts);
+	}
+	
+}
+

+ 3 - 0
hxd/clipper/Polygons.hx

@@ -0,0 +1,3 @@
+package hxd.clipper;
+
+typedef Polygons = Array<Polygon>;

+ 13 - 0
hxd/clipper/Rect.hx

@@ -0,0 +1,13 @@
+package hxd.clipper;
+
+class Rect {
+	public var left : Int;
+	public var top : Int;
+	public var right : Int;
+	public var bottom : Int;
+
+	public function new(l=0,t=0,r=0,b=0) {
+		this.left = l; this.top = t;
+		this.right = r; this.bottom = b;
+	}
+}

+ 18 - 0
hxd/impl/ArrayIterator.hx

@@ -0,0 +1,18 @@
+package hxd.impl;
+
+class ArrayIterator<T> {
+	var i : Int;
+	var l : Int;
+	var a : Array<T>;
+	public inline function new(a) {
+		this.i = 0;
+		this.a = a;
+		this.l = this.a.length;
+	}
+	public inline function hasNext() {
+		return i < l;
+	}
+	public inline function next() {
+		return a[i++];
+	}
+}

+ 41 - 0
hxd/impl/Memory.hx

@@ -0,0 +1,41 @@
+package hxd.impl;
+
+class MemoryReader {
+
+	public function new() {
+	}
+	
+	public inline function b( addr : Int ) {
+		return flash.Memory.getByte(addr);
+	}
+	
+	public inline function wb( addr : Int, v : Int ) {
+		flash.Memory.setByte(addr, v);
+	}
+	
+	public inline function end() {
+		@:privateAccess Memory.end();
+	}
+
+}
+
+class Memory {
+
+	static var stack = new Array<haxe.io.Bytes>();
+	static var current : haxe.io.Bytes = null;
+	static var inst = new MemoryReader();
+	
+	public static function select( b : haxe.io.Bytes ) {
+		flash.Memory.select(b.getData());
+		if( current != null ) stack.push(current);
+		current = b;
+		return inst;
+	}
+	
+	static function end() {
+		current = stack.pop();
+		if( current != null )
+			flash.Memory.select(current.getData());
+	}
+
+}

+ 33 - 0
hxd/impl/Tmp.hx

@@ -0,0 +1,33 @@
+package hxd.impl;
+
+class Tmp {
+
+	static var bytes = new Array<haxe.io.Bytes>();
+	
+	public static function getBytes( size : Int ) {
+		for( i in 0...bytes.length ) {
+			var b = bytes[i];
+			if( b.length >= size ) {
+				bytes.splice(i, 1);
+				return b;
+			}
+		}
+		var sz = 1024;
+		while( sz < size )
+			sz = (sz * 3) >> 1;
+		return haxe.io.Bytes.alloc(sz);
+	}
+	
+	public static function saveBytes( b : haxe.io.Bytes ) {
+		for( i in 0...bytes.length ) {
+			if( bytes[i].length <= b.length ) {
+				bytes.insert(i, b);
+				if( bytes.length > 8 )
+					bytes.pop();
+				return;
+			}
+		}
+		bytes.push(b);
+	}
+	
+}

+ 86 - 0
hxd/poly2tri/AdvancingFront.hx

@@ -0,0 +1,86 @@
+package hxd.poly2tri;
+
+class AdvancingFront
+{
+	public var head:Node;
+	public var tail:Node;
+	public var search_node:Node;
+
+	public function new(head:Node, tail:Node)
+	{
+		this.search_node = this.head = head;
+		this.tail = tail;
+	}
+
+	public function locateNode(x:Constants.Unit):Node
+	{
+		var node:Node = this.search_node;
+
+		if (x < node.value)
+		{
+			while ((node = node.prev) != null)
+			{
+				if (x >= node.value)
+				{
+					this.search_node = node;
+					return node;
+				}
+			}
+		}
+		else
+		{
+			while ((node = node.next) != null)
+			{
+				if (x < node.value)
+				{
+					this.search_node = node.prev;
+					return node.prev;
+				}
+			}
+		}
+		
+		return null;
+	}
+
+	public function locatePoint(point:Point):Node
+	{
+		var px = point.x;
+		//var node:* = this.FindSearchNode(px);
+		var node:Node = this.search_node;
+		var nx = node.point.x;
+
+		if (px == nx)
+		{
+			if (!point.equals(node.point))
+			{
+				// We might have two nodes with same x value for a short time
+				if (point.equals(node.prev.point))
+				{
+					node = node.prev;
+				}
+				else if (point.equals(node.next.point))
+				{
+					node = node.next;
+				}
+				else
+				{
+					throw 'Invalid AdvancingFront.locatePoint call!';
+				}
+			}
+		}
+		else if (px < nx)
+		{
+			while ((node = node.prev) != null)
+				if (point.equals(node.point)) break;
+		}
+		else
+		{
+			while ((node = node.next) != null)
+				if (point.equals(node.point)) break;
+		}
+
+		if (node != null) this.search_node = node;
+		return node;
+
+	}
+}

+ 26 - 0
hxd/poly2tri/Basin.hx

@@ -0,0 +1,26 @@
+package hxd.poly2tri;
+
+class Basin
+{
+	public var left_node:Node;
+	public var bottom_node:Node;
+	public var right_node:Node;
+	public var width:Float;
+	public var left_highest:Bool;
+	
+	public function new()
+	{
+		width = 0;
+	}
+
+	public function clear()
+	{
+
+		this.left_node    = null ;
+		this.bottom_node  = null ;
+		this.right_node   = null ;
+		this.width        = 0.0  ;
+		this.left_highest = false;
+	}
+
+}

+ 22 - 0
hxd/poly2tri/Constants.hx

@@ -0,0 +1,22 @@
+package hxd.poly2tri;
+
+#if fastPoly2tri
+typedef Unit = Int;
+#else
+typedef Unit = Float;
+#end
+
+class Constants {
+	/*
+	 * Inital triangle factor, seed triangle will extend 30% of
+	 * PointSet width to both left and right.
+	 */
+	static public var kAlpha:Float   = 0.3;
+	#if fastPoly2tri
+	static public var EPSILON = 0;
+	#else
+	static public var EPSILON:Float  = 1e-12;
+	#end
+	static public var PI_2:Float     = Math.PI / 2;
+	static public var PI_3div4:Float = 3 * Math.PI / 4;
+}

+ 47 - 0
hxd/poly2tri/Edge.hx

@@ -0,0 +1,47 @@
+package hxd.poly2tri;
+
+class Edge
+{
+	public var p:Point;
+	public var q:Point;
+
+	public function new(p1:Point, p2:Point)
+	{
+		if (p1==null || p2==null) throw "Edge::new p1 or p2 is null";
+
+		var swap = false;
+
+		if (p1.y > p2.y)
+		{
+			swap = true;
+		}
+		else if (p1.y == p2.y)
+		{
+			if (p1.x == p2.x) throw "Edge::repeat points " + p1;
+
+			swap = (p1.x > p2.x);
+		}
+
+
+		if (swap)
+		{
+			q = p1;
+			p = p2;
+		}
+		else
+		{
+			p = p1;
+			q = p2;
+		}
+
+		q.edge_list.push( this );
+	}
+
+
+
+	public function toString()
+	{
+		return "Edge(" + this.p + ", " + this.q + ")";
+	}
+
+}

+ 12 - 0
hxd/poly2tri/EdgeEvent.hx

@@ -0,0 +1,12 @@
+package hxd.poly2tri;
+
+class EdgeEvent
+{
+	public var constrained_edge:Edge;
+	public var right:Bool;
+
+	public function new()
+	{
+
+	}
+}

+ 57 - 0
hxd/poly2tri/Node.hx

@@ -0,0 +1,57 @@
+package hxd.poly2tri;
+
+class Node
+{
+
+
+	public var point:Point;
+	public var triangle:Triangle;
+	public var prev:Node;
+	public var next:Node;
+	public var value:Float;
+
+	public function new(point:Point = null, triangle:Triangle = null)
+	{
+
+		this.point = point;
+		this.triangle = triangle;
+		this.value = this.point.x;
+	}
+
+	/**
+	 *
+	 * @param node - middle node
+	 * @return the angle between 3 front nodes
+	 */
+	public function getHoleAngle():Float
+	{
+		/* Complex plane
+		 * ab = cosA +i*sinA
+		 * ab = (ax + ay*i)(bx + by*i) = (ax*bx + ay*by) + i(ax*by-ay*bx)
+		 * atan2(y,x) computes the principal value of the argument function
+		 * applied to the complex number x+iy
+		 * Where x = ax*bx + ay*by
+		 *       y = ax*by - ay*bx
+		 */
+		var ax = this.next.point.x - this.point.x;
+		var ay = this.next.point.y - this.point.y;
+		var bx = this.prev.point.x - this.point.x;
+		var by = this.prev.point.y - this.point.y;
+		return Math.atan2(
+			ax * by - ay * bx,
+			ax * bx + ay * by
+		);
+	}
+	
+	public function getBasinAngle():Float
+	{
+		return Math.atan2(
+			this.point.y - this.next.next.point.y, // ay
+			this.point.x - this.next.next.point.x  // ax
+		);
+	}
+
+
+
+
+}

+ 23 - 0
hxd/poly2tri/Orientation.hx

@@ -0,0 +1,23 @@
+package hxd.poly2tri;
+
+class Orientation
+{
+	inline public static var CW = 1;
+	inline public static var CCW = -1;
+	inline public static var COLLINEAR = 0;
+
+	public static function orient2d(pa:Point, pb:Point, pc:Point):Int
+	{
+		var detleft  = (pa.x - pc.x) * (pb.y - pc.y);
+		var detright = (pa.y - pc.y) * (pb.x - pc.x);
+		var val = detleft - detright;
+
+		#if fastPoly2tri
+		if( val == 0 ) return COLLINEAR;
+		#else
+		if ((val > -Constants.EPSILON) && (val < Constants.EPSILON)) return Orientation.COLLINEAR;
+		#end
+		if (val > 0) return Orientation.CCW;
+		return Orientation.CW;
+	}
+}

+ 75 - 0
hxd/poly2tri/Point.hx

@@ -0,0 +1,75 @@
+package hxd.poly2tri;
+
+class Point
+{
+	public var id:Int;
+
+	#if fastPoly2tri
+	public var x:Int;
+	public var y:Int;
+	#else
+	public var x:Float;
+	public var y:Float;
+	#end
+
+	/// The edges this point constitutes an upper ending point
+	#if haxe3
+	public var edge_list(get, null):Array<Edge>;
+	#else
+	public var edge_list(get_edge_list, null):Array<Edge>;
+	#end
+
+	public function new(x,y)
+	{
+		this.x = x;
+		this.y = y;
+
+		id = C_ID;
+		C_ID++;
+
+	}
+
+
+	function get_edge_list()
+	{
+		if (edge_list==null) edge_list = new Array();
+		return edge_list;
+	}
+
+
+
+	public inline function equals(that:Point):Bool
+	{
+		#if fastPoly2Tri
+		return this == that;
+		#else
+		return (this.x == that.x) && (this.y == that.y);
+		#end
+	}
+
+	public static function sortPoints(points:Array<Point>)
+	{
+		points.sort( cmpPoints );
+	}
+
+	public static function cmpPoints(l:Point,r:Point)
+	{
+		var ret = l.y - r.y;
+		if (ret == 0) ret = l.x - r.x;
+		if (ret <  0) return -1;
+		if (ret >  0) return 1;
+		return 0;
+	}
+
+	public function toString()
+	{
+		return "Point(" + x + ", " + y + ")";
+	}
+
+	public static var C_ID = 0;
+
+
+
+
+}
+

+ 647 - 0
hxd/poly2tri/Sweep.hx

@@ -0,0 +1,647 @@
+package hxd.poly2tri;
+
+class Sweep
+{
+	var context:SweepContext;
+
+	public function new(context)
+	{
+		this.context = context;
+	}
+
+	public function triangulate()
+	{
+		context.initTriangulation();
+		context.createAdvancingFront();
+		sweepPoints();// Sweep points; build mesh
+		finalizationPolygon();            // Clean up
+	}
+
+	public function sweepPoints()
+	{
+		for (i in 1...context.points.length)
+		{
+			// trace("i"+i);
+			var point = this.context.points[i];
+			var node  = this.pointEvent(point);
+			for (j in 0...point.edge_list.length)
+			{
+				// trace("j"+j);
+				this.edgeEventByEdge(point.edge_list[j], node);
+			}
+		}
+	}
+
+	public function finalizationPolygon()
+	{
+		// Get an Internal triangle to start with
+		var t:Triangle = this.context.front.head.next.triangle;
+		var p:Point    = this.context.front.head.next.point;
+		while (!t.getConstrainedEdgeCW(p)) t = t.neighborCCW(p);
+		
+		// Collect interior triangles constrained by edges
+		this.context.meshClean(t);
+	}
+
+	/**
+	 * Find closes node to the left of the new point and
+	 * create a new triangle. If needed new holes and basins
+	 * will be filled to.
+	 */
+	public function pointEvent(point:Point) :Node
+	{
+		// trace(point);
+		var node = this.context.locateNode(point);
+		var new_node = newFrontTriangle(point, node);
+
+		// Only need to check +epsilon since point never have smaller
+		// x value than node due to how we fetch nodes from the front
+		if (point.x <= (node.point.x + Constants.EPSILON)) fill(node);
+		
+		fillAdvancingFront(new_node);
+		return new_node;
+	}
+
+
+	public function edgeEventByEdge(edge:Edge, node:Node)
+	{
+		this.context.edge_event.constrained_edge = edge;
+		this.context.edge_event.right = (edge.p.x > edge.q.x);
+
+		if (node.triangle.isEdgeSide(edge.p, edge.q)) return;
+
+		// For now we will do all needed filling
+		// TODO: integrate with flip process might give some better performance
+		//       but for now this avoid the issue with cases that needs both flips and fills
+		this.fillEdgeEvent(edge, node);
+		
+		this.edgeEventByPoints(edge.p, edge.q, node.triangle, edge.q);
+	}
+	
+	public function edgeEventByPoints(ep:Point, eq:Point, triangle:Triangle, point:Point)
+	{
+		if (triangle.isEdgeSide(ep, eq)) return;
+
+		var p1:Point = triangle.pointCCW(point);
+		var o1:Int   = Orientation.orient2d(eq, p1, ep);
+		if (o1 == Orientation.COLLINEAR) throw 'Sweep.edgeEvent: Collinear not supported!';
+
+		var p2:Point = triangle.pointCW(point);
+		var o2:Int   = Orientation.orient2d(eq, p2, ep);
+		if (o2 == Orientation.COLLINEAR) throw'Sweep.edgeEvent: Collinear not supported!';
+
+		if (o1 == o2)
+		{
+			// Need to decide if we are rotating CW or CCW to get to a triangle
+			// that will cross edge
+			triangle = ((o1 == Orientation.CW)
+				? triangle.neighborCCW(point)
+				: triangle.neighborCW(point)
+			);
+			edgeEventByPoints(ep, eq, triangle, point);
+		}
+		else
+		{
+			// This triangle crosses constraint so lets flippin start!
+			flipEdgeEvent(ep, eq, triangle, point);
+		}
+	}
+
+
+
+	public function newFrontTriangle(point:Point, node:Node) :Node
+	{
+		var triangle = new Triangle(point, node.point, node.next.point);
+
+		// trace(node.id);
+		// trace(node.triangle);
+		triangle.markNeighborTriangle(node.triangle);
+		this.context.addToMap(triangle);
+
+		var new_node = new Node(point);
+		new_node.next  = node.next;
+		new_node.prev  = node;
+		node.next.prev = new_node;
+		node.next      = new_node;
+
+		if (!legalize(triangle)) this.context.mapTriangleToNodes(triangle);
+
+		return new_node;
+
+	}
+
+
+	/**
+	 * Adds a triangle to the advancing front to fill a hole.
+	 * @param tcx
+	 * @param node - middle node, that is the bottom of the hole
+	 */
+	public function fill(node:Node)
+	{
+		var triangle = new Triangle(node.prev.point, node.point, node.next.point);
+
+		// TODO: should copy the constrained_edge value from neighbor triangles
+		//       for now constrained_edge values are copied during the legalize
+		triangle.markNeighborTriangle(node.prev.triangle);
+		triangle.markNeighborTriangle(node.triangle);
+			
+		this.context.addToMap(triangle);
+
+		// Update the advancing front
+		node.prev.next = node.next;
+		node.next.prev = node.prev;
+
+		// If it was legalized the triangle has already been mapped
+		if (!legalize(triangle))
+		{
+			this.context.mapTriangleToNodes(triangle);
+		}
+
+		this.context.removeNode(node);
+	}
+
+	/**
+	 * Fills holes in the Advancing Front
+	 */
+	public function fillAdvancingFront(n:Node)
+	{
+		var node:Node;
+		var angle:Float;
+		
+		// Fill right holes
+		node = n.next;
+		while (node.next != null)
+		{
+			angle = node.getHoleAngle();
+			if ((angle > Constants.PI_2) || (angle < -Constants.PI_2)) break;
+			this.fill(node);
+			node = node.next;
+		}
+
+		// Fill left holes
+		node = n.prev;
+		while (node.prev != null)
+		{
+			angle = node.getHoleAngle();
+			if ((angle > Constants.PI_2) || (angle < -Constants.PI_2)) break;
+			this.fill(node);
+			node = node.prev;
+		}
+
+		// Fill right basins
+		if ((n.next != null) && (n.next.next != null))
+		{
+			angle = n.getBasinAngle();
+			if (angle < Constants.PI_3div4) this.fillBasin(n);
+		}
+	}
+
+
+	/**
+	 * Returns true if triangle was legalized
+	 */
+	public function legalize(t:Triangle):Bool
+	{
+		for (i in 0...3)
+		{
+			if (t.delaunay_edge[i]) continue;
+
+			var ot:Triangle = t.neighbors[i];
+			if (ot != null)
+			{
+				var p:Point  = t.points[i];
+				var op:Point = ot.oppositePoint(t, p);
+				var oi:Int = ot.index(op);
+
+				// If this is a Constrained Edge or a Delaunay Edge(only during recursive legalization)
+				// then we should not try to legalize
+				if (ot.constrained_edge[oi] || ot.delaunay_edge[oi])
+				{
+					t.constrained_edge[i] = ot.constrained_edge[oi];
+					continue;
+				}
+
+
+				if (Utils.insideIncircle(p, t.pointCCW(p), t.pointCW(p), op))
+				{
+					// Lets mark this shared edge as Delaunay
+					t.delaunay_edge[i]   = true;
+					ot.delaunay_edge[oi] = true;
+
+					// Lets rotate shared edge one vertex CW to legalize it
+					Triangle.rotateTrianglePair(t, p, ot, op);
+
+					// We now got one valid Delaunay Edge shared by two triangles
+					// This gives us 4 new edges to check for Delaunay
+						
+					var not_legalized:Bool;
+
+					// Make sure that triangle to node mapping is done only one time for a specific triangle
+					not_legalized = !this.legalize(t);
+					if (not_legalized) this.context.mapTriangleToNodes(t);
+
+					not_legalized = !this.legalize(ot);
+					if (not_legalized) this.context.mapTriangleToNodes(ot);
+
+					// Reset the Delaunay edges, since they only are valid Delaunay edges
+					// until we add a new triangle or point.
+					// XXX: need to think about this. Can these edges be tried after we
+					//      return to previous recursive level?
+					t.delaunay_edge[i]   = false;
+					ot.delaunay_edge[oi] = false;
+
+					// If triangle have been legalized no need to check the other edges since
+					// the recursive legalization will handles those so we can end here.
+					return true;
+				}
+
+			}
+		}
+
+		return false;
+	}
+
+	/**
+	 * Fills a basin that has formed on the Advancing Front to the right
+	 * of given node.<br>
+	 * First we decide a left,bottom and right node that forms the
+	 * boundaries of the basin. Then we do a reqursive fill.
+	 *
+	 * @param tcx
+	 * @param node - starting node, this or next node will be left node
+	 */
+	public function fillBasin(node:Node)
+	{
+		this.context.basin.left_node = ((Orientation.orient2d(node.point, node.next.point, node.next.next.point) == Orientation.CCW)
+			? node.next.next
+			: node.next
+		);
+
+		// Find the bottom and right node
+		this.context.basin.bottom_node = this.context.basin.left_node;
+		while ((this.context.basin.bottom_node.next != null) && (this.context.basin.bottom_node.point.y >= this.context.basin.bottom_node.next.point.y))
+		{
+			this.context.basin.bottom_node = this.context.basin.bottom_node.next;
+		}
+
+		// No valid basin
+		if (this.context.basin.bottom_node == this.context.basin.left_node) return;
+
+		this.context.basin.right_node = this.context.basin.bottom_node;
+		while ((this.context.basin.right_node.next != null) && (this.context.basin.right_node.point.y < this.context.basin.right_node.next.point.y))
+		{
+			this.context.basin.right_node = this.context.basin.right_node.next;
+		}
+
+		// No valid basins
+		if (this.context.basin.right_node == this.context.basin.bottom_node) return;
+
+		this.context.basin.width        = (this.context.basin.right_node.point.x - this.context.basin.left_node.point.x);
+		this.context.basin.left_highest = (this.context.basin.left_node.point.y > this.context.basin.right_node.point.y);
+
+		this.fillBasinReq(this.context.basin.bottom_node);
+	}
+
+	/**
+	 * Recursive algorithm to fill a Basin with triangles
+	 *
+	 * @param tcx
+	 * @param node - bottom_node
+	 */
+	public function fillBasinReq(node:Node)
+	{
+		// if shallow stop filling
+		if (this.isShallow(node)) return;
+
+		this.fill(node);
+
+		if (node.prev == this.context.basin.left_node && node.next == this.context.basin.right_node)
+		{
+			return;
+		}
+		else if (node.prev == this.context.basin.left_node)
+		{
+			if (Orientation.orient2d(node.point, node.next.point, node.next.next.point) == Orientation.CW) return;
+			node = node.next;
+		}
+		else if (node.next == this.context.basin.right_node)
+		{
+			if (Orientation.orient2d(node.point, node.prev.point, node.prev.prev.point) == Orientation.CCW) return;
+			node = node.prev;
+		}
+		else
+		{
+			// Continue with the neighbor node with lowest Y value
+			node = ((node.prev.point.y < node.next.point.y)
+				? node.prev
+				: node.next
+			);
+		}
+
+		this.fillBasinReq(node);
+	}
+
+
+	public inline function isShallow(node:Node):Bool
+	{
+		var height = ( (this.context.basin.left_highest)
+			? this.context.basin.left_node.point.y - node.point.y
+			: this.context.basin.right_node.point.y - node.point.y
+		);
+
+		// if shallow stop filling
+		return (this.context.basin.width > height);
+	}
+
+	public function fillEdgeEvent(edge:Edge, node:Node)
+	{
+		if (this.context.edge_event.right)
+		{
+			this.fillRightAboveEdgeEvent(edge, node);
+		}
+		else
+		{
+			this.fillLeftAboveEdgeEvent(edge, node);
+		}
+	}
+
+	public function fillRightAboveEdgeEvent(edge:Edge, node:Node)
+	{
+		while (node.next.point.x < edge.p.x)
+		{
+			// Check if next node is below the edge
+			if (Orientation.orient2d(edge.q, node.next.point, edge.p) == Orientation.CCW)
+			{
+				this.fillRightBelowEdgeEvent(edge, node);
+			}
+			else
+			{
+				node = node.next;
+			}
+		}
+	}
+
+	public function fillRightBelowEdgeEvent(edge:Edge, node:Node)
+	{
+		if (node.point.x >= edge.p.x) return;
+		if (Orientation.orient2d(node.point, node.next.point, node.next.next.point) == Orientation.CCW)
+		{
+			// Concave
+			this.fillRightConcaveEdgeEvent(edge, node);
+		}
+		else
+		{
+			this.fillRightConvexEdgeEvent(edge, node); // Convex
+			this.fillRightBelowEdgeEvent(edge, node); // Retry this one
+		}
+	}
+	
+	public function fillRightConcaveEdgeEvent(edge:Edge, node:Node)
+	{
+		this.fill(node.next);
+		if (node.next.point != edge.p)
+		{
+			// Next above or below edge?
+			if (Orientation.orient2d(edge.q, node.next.point, edge.p) == Orientation.CCW)
+			{
+				// Below
+				if (Orientation.orient2d(node.point, node.next.point, node.next.next.point) == Orientation.CCW)
+				{
+					// Next is concave
+					this.fillRightConcaveEdgeEvent(edge, node);
+				}
+				else
+				{
+					// Next is convex
+				}
+			}
+		}
+	}
+
+	public function fillRightConvexEdgeEvent(edge:Edge, node:Node)
+	{
+		// Next concave or convex?
+		if (Orientation.orient2d(node.next.point, node.next.next.point, node.next.next.next.point) == Orientation.CCW)
+		{
+			// Concave
+			this.fillRightConcaveEdgeEvent(edge, node.next);
+		}
+		else
+		{
+			// Convex
+			// Next above or below edge?
+			if (Orientation.orient2d(edge.q, node.next.next.point, edge.p) == Orientation.CCW)
+			{
+				// Below
+				this.fillRightConvexEdgeEvent(edge, node.next);
+			}
+			else
+			{
+				// Above
+			}
+		}
+	}
+
+	public function fillLeftAboveEdgeEvent(edge:Edge, node:Node)
+	{
+		while (node.prev.point.x > edge.p.x)
+		{
+			// Check if next node is below the edge
+			if (Orientation.orient2d(edge.q, node.prev.point, edge.p) == Orientation.CW)
+			{
+				this.fillLeftBelowEdgeEvent(edge, node);
+			}
+			else
+			{
+				node = node.prev;
+			}
+		}
+	}
+
+	public function fillLeftBelowEdgeEvent(edge:Edge, node:Node)
+	{
+		if (node.point.x > edge.p.x)
+		{
+			if (Orientation.orient2d(node.point, node.prev.point, node.prev.prev.point) == Orientation.CW)
+			{
+				// Concave
+				this.fillLeftConcaveEdgeEvent(edge, node);
+			}
+			else
+			{
+				// Convex
+				this.fillLeftConvexEdgeEvent(edge, node);
+				// Retry this one
+				this.fillLeftBelowEdgeEvent(edge, node);
+			}
+		}
+	}
+
+	public function fillLeftConvexEdgeEvent(edge:Edge, node:Node)
+	{
+		// Next concave or convex?
+		if (Orientation.orient2d(node.prev.point, node.prev.prev.point, node.prev.prev.prev.point) == Orientation.CW)
+		{
+			// Concave
+			this.fillLeftConcaveEdgeEvent(edge, node.prev);
+		}
+		else
+		{
+			// Convex
+			// Next above or below edge?
+			if (Orientation.orient2d(edge.q, node.prev.prev.point, edge.p) == Orientation.CW)
+			{
+				// Below
+				this.fillLeftConvexEdgeEvent(edge, node.prev);
+			}
+			else
+			{
+				// Above
+			}
+		}
+	}
+
+	public function fillLeftConcaveEdgeEvent(edge:Edge, node:Node)
+	{
+		this.fill(node.prev);
+		if (node.prev.point != edge.p)
+		{
+			// Next above or below edge?
+			if (Orientation.orient2d(edge.q, node.prev.point, edge.p) == Orientation.CW)
+			{
+				// Below
+				if (Orientation.orient2d(node.point, node.prev.point, node.prev.prev.point) == Orientation.CW)
+				{
+					// Next is concave
+					this.fillLeftConcaveEdgeEvent(edge, node);
+				}
+				else
+				{
+					// Next is convex
+				}
+			}
+		}
+	}
+
+
+
+	public function flipEdgeEvent(ep:Point, eq:Point, t:Triangle, p:Point)
+	{
+		var ot:Triangle = t.neighborAcross(p);
+		if (ot == null)
+		{
+			// If we want to integrate the fillEdgeEvent do it here
+			// With current implementation we should never get here
+			throw 'Sweep::[BUG:FIXME] FLIP failed due to missing triangle!';
+		}
+		var op:Point = ot.oppositePoint(t, p);
+
+		if (Utils.inScanArea(p, t.pointCCW(p), t.pointCW(p), op))
+		{
+			// Lets rotate shared edge one vertex CW
+			Triangle.rotateTrianglePair(t, p, ot, op);
+			this.context.mapTriangleToNodes(t);
+			this.context.mapTriangleToNodes(ot);
+
+			// @TODO: equals?
+			if ((p == eq) && (op == ep))
+			{
+				if ((eq == this.context.edge_event.constrained_edge.q) && (ep == this.context.edge_event.constrained_edge.p)) {
+					 t.markConstrainedEdgeByPoints(ep, eq);
+					ot.markConstrainedEdgeByPoints(ep, eq);
+					this.legalize( t);
+					this.legalize(ot);
+				}
+				else
+				{
+					// XXX: I think one of the triangles should be legalized here?
+				}
+			}
+			else
+			{
+				var o:Int = Orientation.orient2d(eq, op, ep);
+				t = this.nextFlipTriangle(o, t, ot, p, op);
+				this.flipEdgeEvent(ep, eq, t, p);
+			}
+		}
+		else
+		{
+			var newP:Point = Sweep.nextFlipPoint(ep, eq, ot, op);
+			this.flipScanEdgeEvent(ep, eq, t, ot, newP);
+			this.edgeEventByPoints(ep, eq, t, p);
+		}
+	}
+
+
+
+	public function nextFlipTriangle(o:Int, t:Triangle, ot:Triangle, p:Point, op:Point):Triangle {
+		var edge_index:Int;
+		if (o == Orientation.CCW)
+		{
+			// ot is not crossing edge after flip
+			edge_index = ot.edgeIndex(p, op);
+			ot.delaunay_edge[edge_index] = true;
+			this.legalize(ot);
+			ot.clearDelunayEdges();
+			return t;
+		}
+
+		// t is not crossing edge after flip
+		edge_index = t.edgeIndex(p, op);
+
+		t.delaunay_edge[edge_index] = true;
+		this.legalize(t);
+		t.clearDelunayEdges();
+		return ot;
+	}
+
+	static public function nextFlipPoint(ep:Point, eq:Point, ot:Triangle, op:Point):Point
+	{
+		var o2d:Int = Orientation.orient2d(eq, op, ep);
+		if (o2d == Orientation.CW)
+		{
+			// Right
+			return ot.pointCCW(op);
+		}
+		else if (o2d == Orientation.CCW)
+		{
+			// Left
+			return ot.pointCW(op);
+		}
+		else
+		{
+			throw "Sweep:: [Unsupported] Sweep.NextFlipPoint: opposing point on constrained edge!";
+		}
+	}
+
+	public function flipScanEdgeEvent(ep:Point, eq:Point, flip_triangle:Triangle, t:Triangle, p:Point)
+	{
+		var ot:Triangle = t.neighborAcross(p);
+
+		// If we want to integrate the fillEdgeEvent do it here
+		// With current implementation we should never get here
+		if (ot == null) throw 'Sweep::[BUG:FIXME] FLIP failed due to missing triangle';
+		var op:Point = ot.oppositePoint(t, p);
+
+		if (Utils.inScanArea(eq, flip_triangle.pointCCW(eq), flip_triangle.pointCW(eq), op))
+		{
+			// flip with new edge op.eq
+			this.flipEdgeEvent(eq, op, ot, op);
+			// TODO: Actually I just figured out that it should be possible to
+			//       improve this by getting the next ot and op before the the above
+			//       flip and continue the flipScanEdgeEvent here
+			// set new ot and op here and loop back to inScanArea test
+			// also need to set a new flip_triangle first
+			// Turns out at first glance that this is somewhat complicated
+			// so it will have to wait.
+		}
+		else
+		{
+			var newP:Point = nextFlipPoint(ep, eq, ot, op);
+			this.flipScanEdgeEvent(ep, eq, flip_triangle, ot, newP);
+		}
+	}
+	
+
+
+}

+ 168 - 0
hxd/poly2tri/SweepContext.hx

@@ -0,0 +1,168 @@
+package hxd.poly2tri;
+
+class SweepContext
+{
+	public var triangles:Array<Triangle>;
+	public var points:Array<Point>;
+	public var edge_list:Array<Edge>;
+
+	#if haxe3
+	public var map:Map<String,Triangle>;
+	#else
+	public var map:Hash<Triangle>;
+	#end
+
+	public var front:AdvancingFront;
+	public var head:Point;
+	public var tail:Point;
+
+	public var basin:Basin;
+	public var edge_event:EdgeEvent;
+
+
+
+	public function new()
+	{
+		triangles = new Array();
+		points = new Array();
+		edge_list = new Array();
+		#if haxe3
+		map = new Map();
+		#else
+		map = new Hash();
+		#end
+
+
+		basin = new Basin();
+		edge_event = new EdgeEvent();
+
+	}
+
+
+	function addPoints(points:Array<Point>)
+	{
+		for (point in points)
+		{
+			//OPT use concat instead
+			this.points.push( point );
+		}
+	}
+
+
+	public function addPolyline(polyline:Array<Point>)
+	{
+		initEdges(polyline);
+		addPoints(polyline);
+	}
+
+
+	function initEdges(polyline:Array<Point>)
+	{
+
+		for (n in 0...polyline.length)
+		{
+
+			var nx = polyline[(n + 1) % polyline.length];
+			edge_list.push ( new Edge(polyline[n], nx) );
+									
+		}
+	}
+
+
+	public function addToMap(triangle:Triangle)
+	{
+		map.set( triangle.toString(), triangle );
+	}
+
+
+	public function initTriangulation()
+	{
+		//OPT
+		var xmin = points[0].x;
+		var xmax = points[0].x;
+		var ymin = points[0].y;
+		var ymax = points[0].y;
+
+		// Calculate bounds
+		for (p in points)
+		{
+			if (p.x > xmax) xmax = p.x;
+			if (p.x < xmin) xmin = p.x;
+			if (p.y > ymax) ymax = p.y;
+			if (p.y < ymin) ymin = p.y;
+		}
+
+		var dx = Constants.kAlpha * (xmax - xmin);
+		var dy = Constants.kAlpha * (ymax - ymin);
+		
+		#if fastPoly2tri
+		var dx = Math.ceil(dx);
+		var dy = Math.ceil(dy);
+		#end
+
+		this.head = new Point(xmax + dx, ymin - dy);
+		this.tail = new Point(xmin - dy, ymin - dy);
+
+
+		// Sort points along y-axis
+		Point.sortPoints(points);
+
+	}
+
+	public function locateNode(point:Point):Node
+	{
+		return this.front.locateNode(point.x);
+	}
+
+	public function createAdvancingFront()
+	{
+		// Initial triangle
+		var triangle = new Triangle(points[0], this.tail, this.head);
+
+		addToMap(triangle);
+
+		var head = new Node( triangle.points[1], triangle );
+		var middle = new Node( triangle.points[0], triangle );
+		var tail = new Node( triangle.points[2] );
+
+		this.front = new AdvancingFront(head, tail);
+
+		head.next   = middle;
+		middle.next = tail;
+		middle.prev = head;
+		tail.prev   = middle;
+
+	}
+
+	public function removeNode(node:Node)
+	{
+		// do nothing
+	}
+
+	public function mapTriangleToNodes(triangle:Triangle)
+	{
+		for (n in 0...3)
+		{
+			if (triangle.neighbors[n] == null)
+			{
+				var neighbor = this.front.locatePoint(triangle.pointCW(triangle.points[n]));
+				if (neighbor != null) neighbor.triangle = triangle;
+			}
+		}
+	}
+
+	public function meshClean(t:Triangle)
+	{
+		var tmp = [t];
+		while( true ) {
+			var t = tmp.pop();
+			if( t == null ) break;
+			if( t.id >= 0 ) continue;
+			t.id = this.triangles.length;
+			this.triangles.push(t);
+			for (n in 0...3)
+				if (!t.constrained_edge[n])
+					tmp.push(t.neighbors[n]);
+		}
+	}
+}

+ 387 - 0
hxd/poly2tri/Triangle.hx

@@ -0,0 +1,387 @@
+package hxd.poly2tri;
+
+class Triangle
+{
+	public var points:Array<Point>;
+
+	// Neighbor list
+	public var neighbors:Array<Triangle>;
+
+	// Has this triangle been marked as an interior triangle?
+	public var id:Int = -1;
+
+	// Flags to determine if an edge is a Constrained edge
+	public var constrained_edge:Array<Bool>;
+
+	// Flags to determine if an edge is a Delauney edge
+	public var delaunay_edge:Array<Bool>;
+
+
+
+	public function new(p1:Point, p2:Point, p3:Point, fixOrientation = false, checkOrientation = true)
+	{
+		if (fixOrientation)
+		{
+			if (Orientation.orient2d(p1,p2,p3) == Orientation.CW)
+			{
+				var pt = p3;
+				p3 = p2;
+				p2 = pt;
+			}
+		}
+
+		if (checkOrientation && Orientation.orient2d(p3,p2,p1) != Orientation.CW)
+			throw "Triangle::Triangle must defined with Orientation.CW";
+
+		points = [p1,p2,p3];
+
+
+
+		neighbors = [null, null, null];
+		constrained_edge = [false, false, false];
+		delaunay_edge = [false, false, false];
+		
+	}
+
+	/**
+	 * Test if this Triangle contains the Point object given as parameter as its vertices.
+	 *
+	 * @return <code>True</code> if the Point objects are of the Triangle's vertices,
+	 *         <code>false</code> otherwise.
+	 */
+	public function containsPoint(point:Point):Bool
+	{
+		return point.equals(points[0]) || point.equals(points[1]) || point.equals(points[2]);
+	}
+
+	public function containsEdgePoints(p1:Point, p2:Point):Bool
+	{
+		// In a triangle to check if contains and edge is enough to check if it contains the two vertices.
+		return containsPoint(p1) && containsPoint(p2);
+	}
+
+
+	/**
+	 * Update neighbor pointers.<br>
+	 * This method takes either 3 parameters (<code>p1</code>, <code>p2</code> and
+	 * <code>t</code>) or 1 parameter (<code>t</code>).
+	 * @param   t   Triangle object.
+	 * @param   p1  Point object.
+	 * @param   p2  Point object.
+	 */
+	public function markNeighbor(t:Triangle, p1:Point, p2:Point)
+	{
+		if ((p1.equals(this.points[2]) && p2.equals(this.points[1])) || (p1.equals(this.points[1]) && p2.equals(this.points[2])))
+		{
+			this.neighbors[0] = t; return;
+		}
+		if ((p1.equals(this.points[0]) && p2.equals(this.points[2])) || (p1.equals(this.points[2]) && p2.equals(this.points[0])))
+		{
+			this.neighbors[1] = t; return;
+		}
+		if ((p1.equals(this.points[0]) && p2.equals(this.points[1])) || (p1.equals(this.points[1]) && p2.equals(this.points[0])))
+		{
+			this.neighbors[2] = t; return;
+		}
+		throw 'Invalid markNeighbor call (1)!';
+	}
+
+
+	public function markNeighborTriangle(that:Triangle)
+	{
+		// exhaustive search to update neighbor pointers
+		if (that.containsEdgePoints( this.points[1], this.points[2]))
+		{
+			this.neighbors[0] = that;
+			that.markNeighbor(this, this.points[1], this.points[2]);
+			return;
+		}
+		
+		if (that.containsEdgePoints(this.points[0], this.points[2])) {
+			this.neighbors[1] = that;
+			that.markNeighbor(this, this.points[0], this.points[2]);
+			return;
+		}
+		
+		if (that.containsEdgePoints(this.points[0], this.points[1])) {
+			this.neighbors[2] = that;
+			that.markNeighbor(this, this.points[0], this.points[1]);
+			return;
+		}
+	}
+
+
+
+	// Optimized?
+	public function getPointIndexOffset(p:Point, offset:Int = 0):Int
+	{
+		var no:Int = offset;
+		for (n in 0...3)
+		{
+			while (no < 0) no += 3;
+			while (no > 2) no -= 3;
+			if (p.equals(this.points[n])) return no;
+			no++;
+		}
+
+		throw "Triangle::Point not in triangle";
+		// for (var n:uint = 0; n < 3; n++, no++) {
+		// 	while (no < 0) no += 3;
+		// 	while (no > 2) no -= 3;
+		// 	if (p.equals(this.points[n])) return no;
+		// }
+		// throw(new Error("Point not in triangle"));
+	}
+
+
+
+
+	/**
+	 * Return the point clockwise to the given point.
+	 * Return the point counter-clockwise to the given point.
+	 *
+	 * Return the neighbor clockwise to given point.
+	 * Return the neighbor counter-clockwise to given point.
+	 */
+	
+	inline static private var CW_OFFSET = 1;
+	inline static private var CCW_OFFSET = -1;
+
+	public inline function pointCW (p:Point):Point
+	{
+		return this.points[getPointIndexOffset(p, CCW_OFFSET)];
+	}
+
+	public inline function pointCCW(p:Point):Point
+	{
+		return this.points[getPointIndexOffset(p, CW_OFFSET)];
+	}
+
+	public inline function neighborCW(p:Point):Triangle
+	{
+	 	return this.neighbors[getPointIndexOffset(p, CW_OFFSET)];
+	}
+	
+	public inline function neighborCCW(p:Point):Triangle
+	{
+		return this.neighbors[getPointIndexOffset(p, CCW_OFFSET)];
+	}
+
+	public inline function getConstrainedEdgeCW(p:Point):Bool              { return this.constrained_edge[getPointIndexOffset(p, CW_OFFSET)]; }
+	public inline function setConstrainedEdgeCW(p:Point, ce:Bool):Bool  { return this.constrained_edge[getPointIndexOffset(p, CW_OFFSET)] = ce; }
+
+	public inline function getConstrainedEdgeCCW(p:Point):Bool             { return this.constrained_edge[getPointIndexOffset(p, CCW_OFFSET)]; }
+	public inline function setConstrainedEdgeCCW(p:Point, ce:Bool):Bool { return this.constrained_edge[getPointIndexOffset(p, CCW_OFFSET)] = ce; }
+
+	public inline function getDelaunayEdgeCW(p:Point):Bool                 { return this.delaunay_edge[getPointIndexOffset(p, CW_OFFSET)]; }
+	public inline function setDelaunayEdgeCW(p:Point, e:Bool):Bool      { return this.delaunay_edge[getPointIndexOffset(p, CW_OFFSET)] = e; }
+	
+	public inline function getDelaunayEdgeCCW(p:Point):Bool                { return this.delaunay_edge[getPointIndexOffset(p, CCW_OFFSET)]; }
+	public inline function setDelaunayEdgeCCW(p:Point, e:Bool):Bool     { return this.delaunay_edge[getPointIndexOffset(p, CCW_OFFSET)] = e; }
+
+
+	/**
+	 * The neighbor across to given point.
+	 */
+	public inline function neighborAcross(p:Point):Triangle { return this.neighbors[getPointIndexOffset(p, 0)]; }
+
+	public inline function oppositePoint(t:Triangle, p:Point):Point
+	{
+		return this.pointCW(t.pointCW(p));
+	}
+
+	/**
+	 * Legalize triangle by rotating clockwise.<br>
+	 * This method takes either 1 parameter (then the triangle is rotated around
+	 * points(0)) or 2 parameters (then the triangle is rotated around the first
+	 * parameter).
+	 */
+	public function legalize(opoint:Point, npoint:Point = null)
+	{
+		if (npoint == null)
+		{
+			this.legalize(this.points[0], opoint);
+			return;
+		}
+
+		if (opoint.equals(this.points[0])) {
+			this.points[1] = this.points[0];
+			this.points[0] = this.points[2];
+			this.points[2] = npoint;
+		} else if (opoint.equals(this.points[1])) {
+			this.points[2] = this.points[1];
+			this.points[1] = this.points[0];
+			this.points[0] = npoint;
+		} else if (opoint.equals(this.points[2])) {
+			this.points[0] = this.points[2];
+			this.points[2] = this.points[1];
+			this.points[1] = npoint;
+		} else {
+			throw 'Invalid js.poly2tri.Triangle.Legalize call!';
+		}
+	}
+	
+
+	/**
+	 * Alias for getPointIndexOffset
+	 *
+	 * @param	p
+	 */
+	public inline function index(p:Point):Int
+	{
+		return this.getPointIndexOffset(p, 0);
+	}
+
+
+	public function edgeIndex(p1:Point, p2:Point):Int
+	{
+		if (p1.equals(this.points[0]))
+		{
+			if (p2.equals(this.points[1])) return 2;
+			if (p2.equals(this.points[2])) return 1;
+		}
+		else if (p1.equals(this.points[1]))
+		{
+			if (p2.equals(this.points[2])) return 0;
+			if (p2.equals(this.points[0])) return 2;
+		}
+		else if (p1.equals(this.points[2]))
+		{
+			if (p2.equals(this.points[0])) return 1;
+			if (p2.equals(this.points[1])) return 0;
+		}
+		return -1;
+	}
+
+
+	public inline function markConstrainedEdgeByEdge(edge:Edge)
+	{
+		this.markConstrainedEdgeByPoints(edge.p, edge.q);
+	}
+
+	public function markConstrainedEdgeByPoints(p:Point, q:Point)
+	{
+		if ((q.equals(this.points[0]) && p.equals(this.points[1])) || (q.equals(this.points[1]) && p.equals(this.points[0]))) {
+			this.constrained_edge[2] = true;
+			return;
+		}
+
+		if ((q.equals(this.points[0]) && p.equals(this.points[2])) || (q.equals(this.points[2]) && p.equals(this.points[0]))) {
+			this.constrained_edge[1] = true;
+			return;
+		}
+		
+		if ((q.equals(this.points[1]) && p.equals(this.points[2])) || (q.equals(this.points[2]) && p.equals(this.points[1]))) {
+			this.constrained_edge[0] = true;
+			return;
+		}
+	}
+	
+
+
+	/**
+	 * Checks if a side from this triangle is an edge side.
+	 * If sides are not marked they will be marked.
+	 *
+	 * @param	ep
+	 * @param	eq
+	 * @return
+	 */
+	public function isEdgeSide(ep:Point, eq:Point):Bool
+	{
+		var index:Int = this.edgeIndex(ep, eq);
+		if (index == -1) return false;
+		
+		/**
+		 * Mark an edge of this triangle as constrained.<br>
+		 * This method takes either 1 parameter (an edge index or an Edge instance) or
+		 * 2 parameters (two Point instances defining the edge of the triangle).
+		 */
+		this.constrained_edge[index] = true;
+		
+		var that:Triangle = this.neighbors[index];
+		if (that != null) that.markConstrainedEdgeByPoints(ep, eq);
+		return true;
+	}
+
+
+	/**
+	 * Rotates a triangle pair one vertex CW
+	 *<pre>
+	 *       n2                    n2
+	 *  P +-----+             P +-----+
+	 *    | t  /|               |\  t |
+	 *    |   / |               | \   |
+	 *  n1|  /  |n3           n1|  \  |n3
+	 *    | /   |    after CW   |   \ |
+	 *    |/ oT |               | oT \|
+	 *    +-----+ oP            +-----+
+	 *       n4                    n4
+	 * </pre>
+	 */
+	static public function rotateTrianglePair(t:Triangle, p:Point, ot:Triangle, op:Point)
+	{
+		var n1:Triangle =  t.neighborCCW( p);
+		var n2:Triangle =  t.neighborCW ( p);
+		var n3:Triangle = ot.neighborCCW(op);
+		var n4:Triangle = ot.neighborCW (op);
+
+		var ce1:Bool =  t.getConstrainedEdgeCCW( p);
+		var ce2:Bool =  t.getConstrainedEdgeCW ( p);
+		var ce3:Bool = ot.getConstrainedEdgeCCW(op);
+		var ce4:Bool = ot.getConstrainedEdgeCW (op);
+
+		var de1:Bool =  t.getDelaunayEdgeCCW( p);
+		var de2:Bool =  t.getDelaunayEdgeCW ( p);
+		var de3:Bool = ot.getDelaunayEdgeCCW(op);
+		var de4:Bool = ot.getDelaunayEdgeCW (op);
+
+		t.legalize( p, op);
+		ot.legalize(op,  p);
+
+		// Remap delaunay_edge
+		ot.setDelaunayEdgeCCW( p, de1);
+		 t.setDelaunayEdgeCW ( p, de2);
+		 t.setDelaunayEdgeCCW(op, de3);
+		ot.setDelaunayEdgeCW (op, de4);
+
+		// Remap constrained_edge
+		ot.setConstrainedEdgeCCW( p, ce1);
+		 t.setConstrainedEdgeCW ( p, ce2);
+		 t.setConstrainedEdgeCCW(op, ce3);
+		ot.setConstrainedEdgeCW (op, ce4);
+
+		// Remap neighbors
+		// XXX: might optimize the markNeighbor by keeping track of
+		//      what side should be assigned to what neighbor after the
+		//      rotation. Now mark neighbor does lots of testing to find
+		//      the right side.
+		 t.clearNeigbors();
+		ot.clearNeigbors();
+		if (n1 != null) ot.markNeighborTriangle(n1);
+		if (n2 != null)  t.markNeighborTriangle(n2);
+		if (n3 != null)  t.markNeighborTriangle(n3);
+		if (n4 != null) ot.markNeighborTriangle(n4);
+		t.markNeighborTriangle(ot);
+	}
+
+	public function clearNeigbors()
+	{
+		this.neighbors[0] = null;
+		this.neighbors[1] = null;
+		this.neighbors[2] = null;
+	}
+
+	public function clearDelunayEdges()
+	{
+		this.delaunay_edge[0] = false;
+		this.delaunay_edge[1] = false;
+		this.delaunay_edge[2] = false;
+	}
+
+
+	public function toString():String
+	{
+		return "Triangle(" + this.points[0] + ", " + this.points[1] + ", " + this.points[2] + ")";
+	}
+}

この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません