Source: tiny/core/utils/decide.js

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;
}
Documentation generated by JSDoc 3.4.3 on Fri Jul 09 2021 19:32:25 GMT+0800 (CST)