diff --git a/js/__tests__/snapUtils-test.js b/js/__tests__/snapUtils-test.js index 65484db2..ef3be524 100644 --- a/js/__tests__/snapUtils-test.js +++ b/js/__tests__/snapUtils-test.js @@ -6,7 +6,9 @@ import { left, right, near, - snap + snap, + overlapY, + overlapX } from '../snapUtils'; describe('side functions', () => { @@ -46,6 +48,51 @@ describe('near function', () => { }); }); +describe('overlap functions', () => { + it('overlapY detects when the boxes overlap in the Y axis', () => { + const a = {y: 10, height: 50}; + const b = {y: 40, height: 50}; + const actual = overlapY(a, b); + const expected = true; + expect(actual).toEqual(expected); + }); + it('overlapY detects when the boxes are within SNAP_DISTANCE on the Y axis', () => { + const a = {y: 10, height: 50}; + const b = {y: 70, height: 50}; + const actual = overlapY(a, b); + const expected = true; + expect(actual).toEqual(expected); + }); + it('overlapY detects when the boxes do not overlap in the Y axis', () => { + const a = {y: 10, height: 50}; + const b = {y: 90, height: 50}; + const actual = overlapY(a, b); + const expected = false; + expect(actual).toEqual(expected); + }); + it('overlapX detects when the boxes overlap in the X axis', () => { + const a = {x: 10, width: 50}; + const b = {x: 40, width: 50}; + const actual = overlapX(a, b); + const expected = true; + expect(actual).toEqual(expected); + }); + it('overlapX detects when the boxes are within SNAP_DISTANCE on the X axis', () => { + const a = {x: 10, width: 50}; + const b = {x: 70, width: 50}; + const actual = overlapX(a, b); + const expected = true; + expect(actual).toEqual(expected); + }); + it('overlapX detects when the boxes do not overlap in the X axis', () => { + const a = {x: 10, width: 50}; + const b = {x: 90, width: 50}; + const actual = overlapX(a, b); + const expected = false; + expect(actual).toEqual(expected); + }); +}); + describe('snap function', () => { it('does not snap if A and B are obviously far apart', () => { const a = {x: 10, y: 10, width: 100, height: 100}; @@ -82,4 +129,18 @@ describe('snap function', () => { const expected = {y: 110}; expect(actual).toEqual(expected); }); + it('does not snap to the X axis if A is below B', () => { + const a = {x: 10, y: 10, width: 100, height: 100}; + const b = {x: 110, y: 150, width: 100, height: 100}; + const actual = snap(a, b); + const expected = {}; + expect(actual).toEqual(expected); + }); + it('snaps in both axis if the corners are within SNAP_DISTANCE', () => { + const a = {x: 10, y: 10, width: 100, height: 100}; + const b = {x: 120, y: 120, width: 100, height: 100}; + const actual = snap(a, b); + const expected = {x: 20, y: 20}; + expect(actual).toEqual(expected); + }); }); diff --git a/js/snapUtils.js b/js/snapUtils.js index 5815d43e..376a6b68 100644 --- a/js/snapUtils.js +++ b/js/snapUtils.js @@ -9,23 +9,31 @@ export const right = (box) => box.x + box.width; export const near = (a, b) => Math.abs(a - b) < SNAP_DISTANCE; +// http://stackoverflow.com/a/3269471/1263117 +export const overlapX = (a, b) => left(a) <= right(b) + SNAP_DISTANCE && left(b) <= right(a) + SNAP_DISTANCE; +export const overlapY = (a, b) => top(a) <= bottom(b) + SNAP_DISTANCE && top(b) <= bottom(a) + SNAP_DISTANCE; + export const snap = (boxA, boxB) => { let x, y; - if (near(left(boxA), right(boxB))) { - x = right(boxB); + if (overlapY(boxA, boxB)) { + if (near(left(boxA), right(boxB))) { + x = right(boxB); + } + + if (near(right(boxA), left(boxB))) { + x = left(boxB) - boxA.width; + } } - if (near(right(boxA), left(boxB))) { - x = left(boxB) - boxA.width; - } + if (overlapX(boxA, boxB)) { + if (near(top(boxA), bottom(boxB))) { + y = bottom(boxB); + } - if (near(top(boxA), bottom(boxB))) { - y = bottom(boxB); - } - - if (near(bottom(boxA), top(boxB))) { - y = top(boxB) - boxA.height; + if (near(bottom(boxA), top(boxB))) { + y = top(boxB) - boxA.height; + } } return {x, y}; };