Add snapWithin() and add snapping to like sides

This commit is contained in:
Jordan Eldredge 2016-09-26 18:26:50 -07:00
parent 59727d78ed
commit 785af97700
2 changed files with 148 additions and 11 deletions

View file

@ -8,7 +8,9 @@ import {
near,
snap,
overlapY,
overlapX
overlapX,
snapWithin,
applySnap
} from '../snapUtils';
describe('side functions', () => {
@ -102,28 +104,28 @@ describe('snap function', () => {
expect(actual).toEqual(expected);
});
it('snaps the left of A to the right of B', () => {
const a = {x: 120, y: 10, width: 100, height: 100};
const a = {x: 120, y: 30, width: 100, height: 100};
const b = {x: 10, y: 10, width: 100, height: 100};
const actual = snap(a, b);
const expected = {x: 110};
expect(actual).toEqual(expected);
});
it('snaps the right of A to the left of B', () => {
const a = {x: 10, y: 10, width: 100, height: 100};
const a = {x: 10, y: 30, width: 100, height: 100};
const b = {x: 120, y: 10, width: 100, height: 100};
const actual = snap(a, b);
const expected = {x: 20};
expect(actual).toEqual(expected);
});
it('snaps the top of A to the bottom of B', () => {
const a = {x: 10, y: 10, width: 100, height: 100};
const a = {x: 30, y: 10, width: 100, height: 100};
const b = {x: 10, y: 120, width: 100, height: 100};
const actual = snap(a, b);
const expected = {y: 20};
expect(actual).toEqual(expected);
});
it('snaps the bottom of A to the top of B', () => {
const a = {x: 10, y: 120, width: 100, height: 100};
const a = {x: 30, y: 120, width: 100, height: 100};
const b = {x: 10, y: 10, width: 100, height: 100};
const actual = snap(a, b);
const expected = {y: 110};
@ -143,4 +145,108 @@ describe('snap function', () => {
const expected = {x: 20, y: 20};
expect(actual).toEqual(expected);
});
it('snaps the left of A to the left of B', () => {
const a = {x: 10, y: 10, width: 100, height: 100};
const b = {x: 15, y: 110, width: 100, height: 100};
const actual = snap(a, b);
const expected = {x: 15, y: 10};
expect(actual).toEqual(expected);
});
it('snaps the top of A to the bottom of B', () => {
const a = {x: 10, y: 10, width: 100, height: 100};
const b = {x: 110, y: 15, width: 100, height: 100};
const actual = snap(a, b);
const expected = {x: 10, y: 15};
expect(actual).toEqual(expected);
});
it('snaps the top left corner of A to the bottom right corner of B', () => {
const a = {x: 110, y: 110, width: 100, height: 100};
const b = {x: 0, y: 0, width: 100, height: 100};
const actual = snap(a, b);
const expected = {x: 100, y: 100};
expect(actual).toEqual(expected);
});
});
describe('snapWithin function', () => {
it('snaps the inner box to the top of the outer box', () => {
const inner = {x: 120, y: 10, width: 100, height: 100};
const outer = {width: 1000, height: 1000};
const actual = snapWithin(inner, outer);
const expected = {y: 0};
expect(actual).toEqual(expected);
});
it('snaps the inner box to the bottom of the outer box', () => {
const inner = {x: 120, y: 910, width: 100, height: 100};
const outer = {width: 1000, height: 1000};
const actual = snapWithin(inner, outer);
const expected = {y: 900};
expect(actual).toEqual(expected);
});
it('snaps the inner box to the right of the outer box', () => {
const inner = {x: 910, y: 120, width: 100, height: 100};
const outer = {width: 1000, height: 1000};
const actual = snapWithin(inner, outer);
const expected = {x: 900};
expect(actual).toEqual(expected);
});
it('snaps the inner box to the left of the outer box', () => {
const inner = {x: 10, y: 120, width: 100, height: 100};
const outer = {width: 1000, height: 1000};
const actual = snapWithin(inner, outer);
const expected = {x: 0};
expect(actual).toEqual(expected);
});
it('snaps the inner box to the top-left of the outer box', () => {
const inner = {x: 10, y: 10, width: 100, height: 100};
const outer = {width: 1000, height: 1000};
const actual = snapWithin(inner, outer);
const expected = {x: 0, y: 0};
expect(actual).toEqual(expected);
});
});
describe('applySnap function', () => {
it('does not apply undefined values', () => {
const original = {x: 10, y: 10};
const snapped = {x: undefined, y: undefined};
const actual = applySnap(original, snapped);
const expected = original;
expect(actual).toEqual(expected);
});
it('preserves other values on original', () => {
const original = {x: 10, y: 10, foo: 'bar'};
const snapped = {x: 50, y: 50};
const actual = applySnap(original, snapped);
const expected = {x: 50, y: 50, foo: 'bar'};
expect(actual).toEqual(expected);
});
it('will clobber original with falsy "0" values', () => {
const original = {x: 10, y: 10};
const snapped = {x: 0, y: 0};
const actual = applySnap(original, snapped);
expect(actual).toEqual(snapped);
});
it('will clobber original with falsy "0" values', () => {
const original = {x: 10, y: 10};
const snapped = {x: 0, y: 0};
const actual = applySnap(original, snapped);
expect(actual).toEqual(snapped);
});
it('can apply multiple snaps', () => {
const original = {x: 10, y: 10};
const snapped1 = {x: 0};
const snapped2 = {y: 0};
const actual = applySnap(original, snapped1, snapped2);
const expected = {x: 0, y: 0};
expect(actual).toEqual(expected);
});
it('the last snap wins', () => {
const original = {x: 10, y: 10};
const snapped1 = {x: 50, y: 20};
const snapped2 = {x: 60, y: 60};
const actual = applySnap(original, snapped1, snapped2);
const expected = {x: 60, y: 60};
expect(actual).toEqual(expected);
});
});

View file

@ -22,23 +22,28 @@ export const overlapY = (a, b) => {
export const snap = (boxA, boxB) => {
let x, y;
// TODO: Refactor/simplify this code
if (overlapY(boxA, boxB)) {
if (near(left(boxA), right(boxB))) {
x = right(boxB);
}
if (near(right(boxA), left(boxB))) {
} else if (near(right(boxA), left(boxB))) {
x = left(boxB) - boxA.width;
} else if (near(left(boxA), left(boxB))) {
x = left(boxB);
} else if (near(right(boxA), right(boxB))) {
x = right(boxB) - boxA.width;
}
}
if (overlapX(boxA, boxB)) {
if (near(top(boxA), bottom(boxB))) {
y = bottom(boxB);
}
if (near(bottom(boxA), top(boxB))) {
} else if (near(bottom(boxA), top(boxB))) {
y = top(boxB) - boxA.height;
} else if (near(top(boxA), top(boxB))) {
y = top(boxB);
} else if (near(bottom(boxA), bottom(boxB))) {
y = bottom(boxB) - boxA.height;
}
}
return {x, y};
@ -55,3 +60,29 @@ export const snapToMany = (boxA, otherBoxes) => {
return {x, y};
};
export const snapWithin = (boxA, boundingBox) => {
let x, y;
if (boxA.x - SNAP_DISTANCE < 0) {
x = 0;
} else if ((boxA.x + boxA.width + SNAP_DISTANCE) > boundingBox.width) {
x = boundingBox.width - boxA.width;
}
if (boxA.y - SNAP_DISTANCE < 0) {
y = 0;
} else if ((boxA.y + boxA.height + SNAP_DISTANCE) > boundingBox.height) {
y = boundingBox.height - boxA.height;
}
return {x, y};
};
export const applySnap = (original, ...snaps) => (
snaps.reduce((previous, snapped) => ({
...previous,
x: typeof snapped.x !== 'undefined' ? snapped.x : previous.x,
y: typeof snapped.y !== 'undefined' ? snapped.y : previous.y
}), original)
);