// Moving box 2 box intercepts
var objBox = createBox(0, 0, 0, 0); // the moving box
var objLine = createLine(0, 0, 0, 0); // the line representing the box movement
var boxes = []; // array of boxes to check against
//Find closest intercept to start of line
function findIntercepts(B, L) {
lineAddSlopes(L); // get slopes and extras for line (one off calculation)
// for each obstacles check for intercept;
for (var i = 0; i < boxes.length; i++) {
intercept(B, L, boxes[i]);
}
// Line will hold the intercept pos as minX, minY, the normals of the side hit in nx,ny
// and the dist from the line start squared
}
function lineAddSlopes(l) { // adds the slopes of the lie for x,y and length as dist
var dx = l.x2 - l.x1; // vector from start to end of line
var dy = l.y2 - l.y1;
var dist = dx * dx + dy * dy;
l.dx = dx / dy; // slope of line in terms of y to find x
l.dy = dy / dx; // slope of line in terms of x to find y
l.dist = dist;
l.minX = dx; // the 2D intercept point.
l.minY = dy;
l.nx = 0; // the face normal of the intercept point
l.ny = 0;
}
function intercept(moveBox, moveLine, obstructionBox) { // find the closest intercept, if any
var check, iPosX, iPosY, distSqrX, distSqrY;
const b1 = moveBox, b2 = obstructionBox, l = moveLine;
distSqrX = distSqrY = l.dist;
const lr = l.x1 < l.x2; // lr for (l)eft to (r)ight is true is line moves from left to right.
const tb = l.y1 < l.y2; // tb for (t)op to (b)ottom is true is line moves from top to bottom
const w2 = b1.w / 2, h2 = b1.h / 2;
const right = b2.x + b2.w + w2;
const left = b2.x - w2;
const top = b2.y - h2;
const bottom = b2.y + b2.h + h2;
check = lr ? // quick check if collision is possible
l.x1 < right && l.x2 > left:
l.x2 < right && l.x1 > left;
check && (check = tb ?
l.y1 < bottom && l.y2 > top:
l.y2 < bottom && l.y1 > top);
if (check) {
const lrSide = lr ? left : right; // get closest left or right side
const tbSide = tb ? top : bottom; // get closest top or bottom side
const distX = lrSide - l.x1; // x Axis distance to closest side
const distY = tbSide - l.y1; // y Axis distance to closest side
iPosX = l.x1 + distY * l.dx; // X intercept of top or bottom
iPosY = l.y1 + distX * l.dy; // Y intercept of left or right
if (iPosX >= left && iPosX <= right) { // is there a x Axis intercept?
iPosX -= l.x1;
distSqrX = Math.min(distSqrX, distY * distY + iPosX * iPosX); // distance squared
}
if (iPosY >= top && iPosY <= bottom) { // is there a y Axis intercept?
iPosY -= l.y1;
distSqrY = Math.min(distSqrY, distX * distX + iPosY * iPosY);
}
if (distSqrX < l.dist || distSqrY < l.dist) {
if (distSqrX < distSqrY) {
l.dist = distSqrX;
l.minX = iPosX;
l.minY = distY;
l.nx = 0;
l.ny = tb ? -1 : 1;
} else {
l.dist = distSqrY;
l.minX = distX;
l.minY = iPosY;
l.nx = lr ? -1 : 1;
l.ny = 0;
}
l.x2 = l.x1 + l.minX; // Set new line end. This keeps the line
l.y2 = l.y1 + l.minY; // length as short as possible and avoid
// unnneeded intercept tests
}
}
}
//======================================================================================================================
// SUPPORT CODE FROM HERE DOWN
//======================================================================================================================
// The following code is support code that provides me with a standard interface to various forums.
// It provides a mouse interface, a full screen canvas, and some global often used variable
// like canvas, ctx, mouse, w, h (width and height), globalTime
// This code is not intended to be part of the answer unless specified and has been formated to reduce
// display size. It should not be used as an example of how to write a canvas interface.
// By Blindman67
const RESIZE_DEBOUNCE_TIME = 100;
var w, h, cw, ch, canvas, ctx, onResize, mouse, createCanvas, resizeCanvas, setGlobals, globalTime = 0, resizeCount = 0;
createCanvas = function () {
var c,
cs;
cs = (c = document.createElement("canvas")).style;
cs.position = "absolute";
cs.top = cs.left = "0px";
cs.zIndex = 1000;
document.body.appendChild(c);
return c;
}
resizeCanvas = function () {
if (canvas === undefined) {
canvas = createCanvas();
}
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
ctx = canvas.getContext("2d");
if (typeof setGlobals === "function") {
setGlobals();
}
if (typeof onResize === "function") {
resizeCount += 1;
setTimeout(debounceResize, RESIZE_DEBOUNCE_TIME);
}
}
function debounceResize() {
resizeCount -= 1;
if (resizeCount <= 0) {
onResize();
}
}
setGlobals = function () {
cw = (w = canvas.width) / 2;
ch = (h = canvas.height) / 2;
mouse.updateBounds();
}
mouse = (function () {
function preventDefault(e) {
e.preventDefault();
}
var mouse = {
x : 0,
y : 0,
w : 0,
alt : false,
shift : false,
ctrl : false,
buttonRaw : 0,
over : false,
bm : [1, 2, 4, 6, 5, 3],
active : false,
bounds : null,
crashRecover : null,
mouseEvents : "mousemove,mousedown,mouseup,mouseout,mouseover,mousewheel,DOMMouseScroll".split(",")
};
var m = mouse;
function mouseMove(e) {
var t = e.type;
m.x = e.clientX - m.bounds.left;
m.y = e.clientY - m.bounds.top;
m.alt = e.altKey;
m.shift = e.shiftKey;
m.ctrl = e.ctrlKey;
if (t === "mousedown") {
m.buttonRaw |= m.bm[e.which - 1];
} else if (t === "mouseup") {
m.buttonRaw &= m.bm[e.which + 2];
} else if (t === "mouseout") {
!m.buttonRaw && (m.over = false);
} else if (t === "mouseover") {
m.over = true;
} else if (t === "mousewheel") {
m.w = e.wheelDelta;
} else if (t === "DOMMouseScroll") {
m.w = -e.detail;
}
e.preventDefault();
}
m.updateBounds = function () {
if (m.active) {
m.bounds = m.element.getBoundingClientRect();
}
}
m.addCallback = function (callback) {
if (typeof callback === "function") {
if (m.callbacks === undefined) {
m.callbacks = [callback];
} else {
m.callbacks.push(callback);
}
} else {
throw new TypeError("mouse.addCallback argument must be a function");
}
}
m.start = function (element, blockContextMenu) {
if (m.element !== undefined) {
m.removeMouse();
}
m.element = element === undefined ? document : element;
m.blockContextMenu = blockContextMenu === undefined ? false : blockContextMenu;
m.mouseEvents.forEach(n => {
document.addEventListener(n, mouseMove);
});
if (m.blockContextMenu === true) {
m.element.addEventListener("contextmenu", preventDefault, false);
}
m.active = true;
m.updateBounds();
}
m.remove = function () {
if (m.element !== undefined) {
m.mouseEvents.forEach(n => {
m.element.removeEventListener(n, mouseMove);
});
if (m.contextMenuBlocked === true) {
m.element.removeEventListener("contextmenu", preventDefault);
}
m.element = m.callbacks = m.contextMenuBlocked = undefined;
m.active = false;
}