packery
Version:
bin-packing layout library
163 lines (133 loc) • 3.43 kB
JavaScript
/**
* Rect
* low-level utility class for basic geometry
*/
( function( window ) {
"use strict";
// -------------------------- Packery -------------------------- //
// global namespace
var Packery = window.Packery = function() {};
function rectDefinition() {
// -------------------------- Rect -------------------------- //
function Rect( props ) {
// extend properties from defaults
for ( var prop in Rect.defaults ) {
this[ prop ] = Rect.defaults[ prop ];
}
for ( prop in props ) {
this[ prop ] = props[ prop ];
}
}
// make available
Packery.Rect = Rect;
Rect.defaults = {
x: 0,
y: 0,
width: 0,
height: 0
};
/**
* Determines whether or not this rectangle wholly encloses another rectangle or point.
* @param {Rect} rect
* @returns {Boolean}
**/
Rect.prototype.contains = function( rect ) {
// points don't have width or height
var otherWidth = rect.width || 0;
var otherHeight = rect.height || 0;
return this.x <= rect.x &&
this.y <= rect.y &&
this.x + this.width >= rect.x + otherWidth &&
this.y + this.height >= rect.y + otherHeight;
};
/**
* Determines whether or not the rectangle intersects with another.
* @param {Rect} rect
* @returns {Boolean}
**/
Rect.prototype.overlaps = function( rect ) {
var thisRight = this.x + this.width;
var thisBottom = this.y + this.height;
var rectRight = rect.x + rect.width;
var rectBottom = rect.y + rect.height;
// http://stackoverflow.com/a/306332
return this.x < rectRight &&
thisRight > rect.x &&
this.y < rectBottom &&
thisBottom > rect.y;
};
/**
* @param {Rect} rect - the overlapping rect
* @returns {Array} freeRects - rects representing the area around the rect
**/
Rect.prototype.getMaximalFreeRects = function( rect ) {
// if no intersection, return false
if ( !this.overlaps( rect ) ) {
return false;
}
var freeRects = [];
var freeRect;
var thisRight = this.x + this.width;
var thisBottom = this.y + this.height;
var rectRight = rect.x + rect.width;
var rectBottom = rect.y + rect.height;
// top
if ( this.y < rect.y ) {
freeRect = new Rect({
x: this.x,
y: this.y,
width: this.width,
height: rect.y - this.y
});
freeRects.push( freeRect );
}
// right
if ( thisRight > rectRight ) {
freeRect = new Rect({
x: rectRight,
y: this.y,
width: thisRight - rectRight,
height: this.height
});
freeRects.push( freeRect );
}
// bottom
if ( thisBottom > rectBottom ) {
freeRect = new Rect({
x: this.x,
y: rectBottom,
width: this.width,
height: thisBottom - rectBottom
});
freeRects.push( freeRect );
}
// left
if ( this.x < rect.x ) {
freeRect = new Rect({
x: this.x,
y: this.y,
width: rect.x - this.x,
height: this.height
});
freeRects.push( freeRect );
}
return freeRects;
};
Rect.prototype.canFit = function( rect ) {
return this.width >= rect.width && this.height >= rect.height;
};
return Rect;
}
// -------------------------- transport -------------------------- //
if ( typeof define === 'function' && define.amd ) {
// AMD
define( rectDefinition );
} else if ( typeof exports === 'object' ) {
// CommonJS
module.exports = rectDefinition();
} else {
// browser global
window.Packery = window.Packery || {};
window.Packery.Rect = rectDefinition();
}
})( window );