import { Rectangle } from '../math';
/**
*
* 检测一个显示对象是否在另一个显示对象内部
*
* ![rectContainsRect](http://tfs.alipayobjects.com/images/rmsweb/T1hoXgXXNfXXXXXXXX.png)
*
* 注意:边缘重叠始终返回fasle
*
* @example
* var rect1 = new Tiny.Rectangle(10, 10, 100, 100);
* var rect2 = new Tiny.Rectangle(30, 30, 50, 50);
* Tiny.rectContainsRect(rect1, rect2);
* //=> true
*
* @static
* @memberof Tiny
* @function rectContainsRect
* @param {Tiny.Rectangle} rect1
* @param {Tiny.Rectangle} rect2
* @return {boolean}
*/
export function rectContainsRect(rect1, rect2) {
if (!rect1 || !rect2) {
return false;
}
return !((rect1.x >= rect2.x) || (rect1.y >= rect2.y) ||
(rect1.x + rect1.width <= rect2.x + rect2.width) ||
(rect1.y + rect1.height <= rect2.y + rect2.height));
}
/**
* 获取显示对象的横向最大值
*
* @static
* @memberof Tiny
* @function rectGetMaxX
* @param {Tiny.Rectangle} rect
* @return {number}
*/
export function rectGetMaxX(rect) {
return (rect.x + rect.width);
}
/**
* 获取显示对象的横向中心值
*
* @static
* @memberof Tiny
* @function rectGetMidX
* @param {Tiny.Rectangle} rect
* @return {number}
*/
export function rectGetMidX(rect) {
return (rect.x + rect.width / 2.0);
}
/**
* 获取显示对象的横向最小值
*
* @static
* @memberof Tiny
* @function rectGetMinX
* @param {Tiny.Rectangle} rect
* @return {number}
*/
export function rectGetMinX(rect) {
return rect.x;
}
/**
* 获取显示对象的纵向最大值
*
* @static
* @memberof Tiny
* @function rectGetMaxY
* @param {Tiny.Rectangle} rect
* @return {number}
*/
export function rectGetMaxY(rect) {
return (rect.y + rect.height);
}
/**
* 获取显示对象的纵向中心值
*
* @static
* @memberof Tiny
* @function rectGetMidY
* @param {Tiny.Rectangle} rect
* @return {number}
*/
export function rectGetMidY(rect) {
return rect.y + rect.height / 2.0;
}
/**
* 获取显示对象的纵向最小值
*
* @static
* @memberof Tiny
* @function rectGetMinY
* @param {Tiny.Rectangle} rect
* @return {number}
*/
export function rectGetMinY(rect) {
return rect.y;
}
/**
* 检测一个点是否在一个显示对象内部
*
* ![rectContainsPoint](http://tfs.alipayobjects.com/images/rmsweb/T1GENgXkRaXXXXXXXX.png)
*
* 注意:边缘重叠始终返回fasle
*
* @example
* var rect = new Tiny.Rectangle(10, 10, 50, 50);
* var p = new Tiny.Point(49, 62);
* Tiny.rectContainsPoint(rect, p);
* //=> true
*
* @static
* @memberof Tiny
* @function rectContainsPoint
* @param {Tiny.Rectangle} rect
* @param {Tiny.Point} point
* @return {boolean}
*/
export function rectContainsPoint(rect, point) {
return (point.x >= rectGetMinX(rect) && point.x <= rectGetMaxX(rect) &&
point.y >= rectGetMinY(rect) && point.y <= rectGetMaxY(rect));
}
/**
* 检测两个显示对象是否相交,一般用于检测碰撞
*
* ![rectIntersectsRect](http://tfs.alipayobjects.com/images/rmsweb/T18.hgXoddXXXXXXXX.png)
*
* 注意:边缘重叠始终返回true
*
* @example
* var rect1 = new Tiny.Rectangle(10, 10, 50, 50);
* var rect2 = new Tiny.Rectangle(50, 30, 50, 50);
* Tiny.rectIntersectsRect(rect1, rect2);
* //=> true
*
* @static
* @memberof Tiny
* @function rectIntersectsRect
* @param {Tiny.Rectangle} rectA
* @param {Tiny.Rectangle} rectB
* @return {boolean}
*/
export function rectIntersectsRect(rectA, rectB) {
return !(rectGetMaxX(rectA) < rectGetMinX(rectB) ||
rectGetMaxX(rectB) < rectGetMinX(rectA) ||
rectGetMaxY(rectA) < rectGetMinY(rectB) ||
rectGetMaxY(rectB) < rectGetMinY(rectA));
}
/**
* 返回两个显示对象的总区域
*
* ![rectUnion](http://tfs.alipayobjects.com/images/rmsweb/T1JD4gXhlfXXXXXXXX.png)
*
* @example
* var rect1 = new Tiny.Rectangle(10, 10, 50, 50);
* var rect2 = new Tiny.Rectangle(33, 34, 50, 50);
* Tiny.rectUnion(rect1, rect2);
* //=> Tiny.Rectangle(10, 10, 73, 74)
*
* @static
* @memberof Tiny
* @function rectUnion
* @param {Tiny.Rectangle} rectA
* @param {Tiny.Rectangle} rectB
* @return {Tiny.Rectangle}
*/
export function rectUnion(rectA, rectB) {
const rect = Rectangle.EMPTY;
rect.x = Math.min(rectA.x, rectB.x);
rect.y = Math.min(rectA.y, rectB.y);
rect.width = Math.max(rectA.x + rectA.width, rectB.x + rectB.width) - rect.x;
rect.height = Math.max(rectA.y + rectA.height, rectB.y + rectB.height) - rect.y;
return rect;
}
/**
* 像素检测判断是否碰撞
*
* Tiny.js 的这个方法用于JavaScript HTML5 Canvas Image 纯像素级的碰撞检测。
* 作者是:JOE
*
* @see http://www.playmycode.com/blog/2011/08/javascript-per-pixel-html5-canvas-image-collision-detection/
*
* @static
* @memberof Tiny
* @function isPixelCollision
* @param {Tiny.DisplayObject} first
* @param {number} x
* @param {number} y
* @param {boolean} isFirstCentred
* @param {Tiny.DisplayObject} other
* @param {number} x2
* @param {number} y2
* @param {boolean} isOtherCentred
* @return {boolean}
*/
export function isPixelCollision(first, x, y, isFirstCentred, other, x2, y2, isOtherCentred) {
// we need to avoid using floats, as were doing array lookups
x = Math.round(x);
y = Math.round(y);
x2 = Math.round(x2);
y2 = Math.round(y2);
const w = first.collisionWidth || first.width;
const h = first.collisionHeight || first.height;
const w2 = other.collisionWidth || other.width;
const h2 = other.collisionHeight || other.height;
// deal with the image being centred
if (isFirstCentred) {
// fast rounding, but positive only
x -= (w / 2 + 0.5) << 0;
y -= (h / 2 + 0.5) << 0;
}
if (isOtherCentred) {
x2 -= (w2 / 2 + 0.5) << 0;
y2 -= (h2 / 2 + 0.5) << 0;
}
// find the top left and bottom right corners of overlapping area
const xMin = Math.max(x, x2);
const yMin = Math.max(y, y2);
const xMax = Math.min(x + w, x2 + w2);
const yMax = Math.min(y + h, y2 + h2);
// Sanity collision check, we ensure that the top-left corner is both
// above and to the left of the bottom-right corner.
if (xMin >= xMax || yMin >= yMax) {
return false;
}
const xDiff = xMax - xMin;
const yDiff = yMax - yMin;
// get the pixels out from the images
const pixels = first.data;
const pixels2 = other.data;
if (!pixels || !pixels2) {
throw new Error('The Sprit\'s data cannot be null' + (!pixels && ', first.data is ' + pixels) + (!pixels2 && ', other.data is ' + pixels2 + '.')); // eslint-disable-line
}
// if the area is really small,
// then just perform a normal image collision check
if (xDiff < 4 && yDiff < 4) {
for (let pixelX = xMin; pixelX < xMax; pixelX++) {
for (let pixelY = yMin; pixelY < yMax; pixelY++) {
if (
(pixels[((pixelX - x) + (pixelY - y) * w) * 4 + 3] !== 0) &&
(pixels2[((pixelX - x2) + (pixelY - y2) * w2) * 4 + 3] !== 0)
) {
return true;
}
}
}
} else {
/* What is this doing?
* It is iterating over the overlapping area,
* across the x then y the,
* checking if the pixels are on top of this.
*
* What is special is that it increments by incX or incY,
* allowing it to quickly jump across the image in large increments
* rather then slowly going pixel by pixel.
*
* This makes it more likely to find a colliding pixel early.
*/
// Work out the increments,
// it's a third, but ensure we don't get a tiny
// slither of an area for the last iteration (using fast ceil).
let incX = xDiff / 3.0;
let incY = yDiff / 3.0;
incX = (~~incX === incX) ? incX : (incX + 1 | 0);
incY = (~~incY === incY) ? incY : (incY + 1 | 0);
for (let offsetY = 0; offsetY < incY; offsetY++) {
for (let offsetX = 0; offsetX < incX; offsetX++) {
for (let pixelY = yMin + offsetY; pixelY < yMax; pixelY += incY) {
for (let pixelX = xMin + offsetX; pixelX < xMax; pixelX += incX) {
if (
(pixels[((pixelX - x) + (pixelY - y) * w) * 4 + 3] !== 0) &&
(pixels2[((pixelX - x2) + (pixelY - y2) * w2) * 4 + 3] !== 0)
) {
return true;
}
}
}
}
}
}
return false;
}