@stringsync/vexml
Version:
MusicXML to Vexflow
126 lines (125 loc) • 5.21 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Collision = void 0;
const circle_1 = require("./circle");
const rect_1 = require("./rect");
/** Represents a collision between two shapes. */
class Collision {
/** Creates a CollisionSubject for fluent APIs. */
static is(shape) {
return new CollisionSubject(shape);
}
}
exports.Collision = Collision;
/**
* A collision calculator that enables fluent APIs.
*
* This is done to reduce ambiguity of the semantics behind the collision checks.
*
* @example Collision.is(rect).collidingWith(circle); // boolean
* @example Collision.is(circle).surroundedBy(rect); // boolean
*/
class CollisionSubject {
shape;
constructor(shape) {
this.shape = shape;
}
collidingWith(shape) {
const shape1 = this.shape;
const shape2 = shape;
if (shape1 instanceof rect_1.Rect && shape2 instanceof rect_1.Rect) {
return this.isRectCollidingWithRect(shape1, shape2);
}
if (shape1 instanceof rect_1.Rect && shape2 instanceof circle_1.Circle) {
return this.isRectCollidingWithCircle(shape1, shape2);
}
if (shape1 instanceof circle_1.Circle && shape2 instanceof rect_1.Rect) {
return this.isRectCollidingWithCircle(shape2, shape1);
}
if (shape1 instanceof circle_1.Circle && shape2 instanceof circle_1.Circle) {
return this.isCircleCollidingWithCircle(shape1, shape2);
}
throw new Error(`unsupported collision between ${shape1.constructor.name} and ${shape2.constructor.name}`);
}
surrounding(shape) {
const shape1 = this.shape;
const shape2 = shape;
if (shape1 instanceof rect_1.Rect && shape2 instanceof rect_1.Rect) {
return this.isRectSurroundingRect(shape1, shape2);
}
if (shape1 instanceof rect_1.Rect && shape2 instanceof circle_1.Circle) {
return this.isRectSurroundingCircle(shape1, shape2);
}
if (shape1 instanceof circle_1.Circle && shape2 instanceof rect_1.Rect) {
return this.isCircleSurroundingRect(shape1, shape2);
}
if (shape1 instanceof circle_1.Circle && shape2 instanceof circle_1.Circle) {
return this.isCircleSurroundingCircle(shape1, shape2);
}
throw new Error(`unsupported collision between ${shape1.constructor.name} and ${shape2.constructor.name}`);
}
isRectCollidingWithRect(rect1, rect2) {
return !(rect2.x > rect1.x + rect1.w ||
rect2.x + rect2.w < rect1.x ||
rect2.y > rect1.y + rect1.h ||
rect2.y + rect2.h < rect1.y);
}
isCircleCollidingWithCircle(circle1, circle2) {
return circle1.center().distance(circle2.center()) <= circle1.r + circle2.r;
}
isRectCollidingWithCircle(rect, circle) {
if (rect.contains(circle.center())) {
return true;
}
if (circle.contains(rect.center())) {
return true;
}
const x = Math.max(rect.x, Math.min(circle.x, rect.x + rect.w));
const y = Math.max(rect.y, Math.min(circle.y, rect.y + rect.h));
const dx = circle.x - x;
const dy = circle.y - y;
return dx * dx + dy * dy <= circle.r * circle.r;
}
isRectSurroundingRect(rect1, rect2) {
return (rect1.x > rect2.x &&
rect1.y > rect2.y &&
rect1.x + rect1.w < rect2.x + rect2.w &&
rect1.y + rect1.h < rect2.y + rect2.h);
}
isCircleSurroundingCircle(circle1, circle2) {
const distance = circle1.center().distance(circle2.center());
return distance + circle1.r < circle2.r;
}
isRectSurroundingCircle(rect, circle) {
const circleCenter = circle.center();
const rectCenter = rect.center();
const distanceX = Math.abs(circleCenter.x - rectCenter.x);
const distanceY = Math.abs(circleCenter.y - rectCenter.y);
const halfRectWidth = rect.w / 2;
const halfRectHeight = rect.h / 2;
if (distanceX > halfRectWidth || distanceY > halfRectHeight) {
return false;
}
if (distanceX <= halfRectWidth - circle.r && distanceY <= halfRectHeight - circle.r) {
return true;
}
const cornerDistanceSq = (distanceX - halfRectWidth) ** 2 + (distanceY - halfRectHeight) ** 2;
return cornerDistanceSq <= circle.r ** 2;
}
isCircleSurroundingRect(circle, rect) {
const circleCenter = circle.center();
const rectCenter = rect.center();
const distanceX = Math.abs(circleCenter.x - rectCenter.x);
const distanceY = Math.abs(circleCenter.y - rectCenter.y);
const halfRectWidth = rect.w / 2;
const halfRectHeight = rect.h / 2;
if (distanceX > halfRectWidth + circle.r || distanceY > halfRectHeight + circle.r) {
return false;
}
if (distanceX <= halfRectWidth || distanceY <= halfRectHeight) {
return true;
}
const cornerDistanceSq = (distanceX - halfRectWidth) ** 2 + (distanceY - halfRectHeight) ** 2;
return cornerDistanceSq <= circle.r ** 2;
}
}