浏览代码

Improve h2d.Interactive collision with shape. (#469)

Pavel Alexandrov 6 年之前
父节点
当前提交
f3014ca943
共有 2 个文件被更改,包括 61 次插入40 次删除
  1. 26 18
      h2d/Interactive.hx
  2. 35 22
      h2d/Scene.hx

+ 26 - 18
h2d/Interactive.hx

@@ -1,10 +1,23 @@
 package h2d;
 
+@:allow(h2d.Scene)
 class Interactive extends Drawable implements hxd.SceneEvents.Interactive {
 
+	/**
+		Width of the Interactive. Ignored if `shape` is set.
+	**/
 	public var width : Float;
+	/**
+		Height of the Interactive. Ignored if `shape` is set.
+	**/
 	public var height : Float;
+	/**
+		Cursor used when Interactive is under mouse cursor ( default : Button )
+	**/
 	public var cursor(default,set) : hxd.Cursor;
+	/**
+		Should object collision be in rectangle or ellipse form? Ignored if `shape` is set.
+	**/
 	public var isEllipse : Bool;
 	/**
 		Set the default `cancel` mode (see `hxd.Event`), default to false.
@@ -19,10 +32,11 @@ class Interactive extends Drawable implements hxd.SceneEvents.Interactive {
 	var scene : Scene;
 	var mouseDownButton : Int = -1;
 	var parentMask : Mask;
+	var invDet : Float;
 
 	/**
 		Detailed shape collider for Interactive.
-		Keep in mind that shape parts that are out of [0, 0, width, height] bounds will never interact with the mouse.
+		If set, `width` and `height` properties are ignored for collision checks.
 	**/
 	public var shape : h2d.col.Collider;
 	/**
@@ -123,7 +137,7 @@ class Interactive extends Drawable implements hxd.SceneEvents.Interactive {
 				p = @:privateAccess p.parentMask;
 			}
 		}
-		if( isEllipse && checkBounds(e) ) {
+		if(shape == null && isEllipse && checkBounds(e) ) {
 			var cx = width * 0.5, cy = height * 0.5;
 			var dx = (e.relX - cx) / cx;
 			var dy = (e.relY - cy) / cy;
@@ -182,6 +196,12 @@ class Interactive extends Drawable implements hxd.SceneEvents.Interactive {
 		}
 	}
 
+	override private function calcAbsPos()
+	{
+		super.calcAbsPos();
+		invDet = 1 / (matA * matD - matB * matC);
+	}
+
 	function set_cursor(c) {
 		this.cursor = c;
 		if( isOver() && cursor != null )
@@ -191,25 +211,13 @@ class Interactive extends Drawable implements hxd.SceneEvents.Interactive {
 
 	function eventToLocal( e : hxd.Event ) {
 		// convert scene event to our local space
-		var rx = e.relX, ry = e.relY;
-
 		var i = this;
 
-		var dx = rx - i.absX;
-		var dy = ry - i.absY;
-
-		var w1 = i.width * i.matA;
-		var h1 = i.width * i.matC;
-		var ky = h1 * dx - w1 * dy;
-
-		var w2 = i.height * i.matB;
-		var h2 = i.height * i.matD;
-		var kx = w2 * dy - h2 * dx;
-
-		var max = h1 * w2 - w1 * h2;
+		var dx = e.relX - i.absX;
+		var dy = e.relY - i.absY;
 
-		e.relX = (kx / max) * i.width;
-		e.relY = (ky / max) * i.height;
+		e.relX = ( dx * i.matD - dy * i.matC) * i.invDet;
+		e.relY = (-dx * i.matB + dy * i.matA) * i.invDet;
 	}
 
 	public function startDrag(callb,?onCancel) {

+ 35 - 22
h2d/Scene.hx

@@ -255,27 +255,44 @@ class Scene extends Layers implements h3d.IDrawable implements hxd.SceneEvents.I
 			var dx = rx - i.absX;
 			var dy = ry - i.absY;
 
-			var w1 = i.width * i.matA;
-			var h1 = i.width * i.matC;
-			var ky = h1 * dx + w1 * dy;
+			if ( i.shape != null ) {
+				// Check collision for Shape Interactive.
 
-			// up line
-			if( ky < 0 )
-				continue;
+				pt.set(( dx * i.matD - dy * i.matC) * i.invDet + i.shapeX,
+				       (-dx * i.matB + dy * i.matA) * i.invDet + i.shapeY);
+				if ( !i.shape.contains(pt) ) continue;
 
-			var w2 = i.height * i.matB;
-			var h2 = i.height * i.matD;
-			var kx = w2 * dy + h2 * dx;
+				dx = pt.x - i.shapeX;
+				dy = pt.y - i.shapeY;
 
-			// left line
-			if( kx < 0 )
-				continue;
+			} else {
+				// Check AABB for width/height Interactive.
 
-			var max = w1 * h2 - h1 * w2;
+				var w1 = i.width * i.matA;
+				var h1 = i.width * i.matC;
+				var ky = h1 * dx + w1 * dy;
 
-			// bottom/right
-			if( ky >= max || kx >= max )
-				continue;
+				// up line
+				if( ky < 0 )
+					continue;
+
+				var w2 = i.height * i.matB;
+				var h2 = i.height * i.matD;
+				var kx = w2 * dy + h2 * dx;
+
+				// left line
+				if( kx < 0 )
+					continue;
+
+				var max = w1 * h2 - h1 * w2;
+
+				// bottom/right
+				if( ky >= max || kx >= max )
+					continue;
+
+				dx = (kx / max) * i.width;
+				dy = (ky / max) * i.height;
+			}
 
 			// check visibility
 			var visible = true;
@@ -289,13 +306,9 @@ class Scene extends Layers implements h3d.IDrawable implements hxd.SceneEvents.I
 			}
 			if( !visible ) continue;
 
-			if (i.shape != null) {
-				pt.set((kx / max) * i.width + i.shapeX, (ky / max) * i.height + i.shapeY);
-				if ( !i.shape.contains(pt) ) continue;
-			}
+			event.relX = dx;
+			event.relY = dy;
 
-			event.relX = (kx / max) * i.width;
-			event.relY = (ky / max) * i.height;
 			i.handleEvent(event);
 
 			if( event.cancel ) {