morpheus.Positions = function() { this.spaces = undefined; this.defaultPositionFunction = function(index) { return (this.size * index); }; this.squishedPositionFunction = function(index) { return this.positions[index]; }; this.positionFunction = this.defaultPositionFunction; this.squishedIndices = {}; this.isSquished = false; }; morpheus.Positions.getBottom = function(rect, rowPositions) { var bottom = rowPositions.getLength(); if (rect != null) { bottom = 1 + rowPositions.getIndex(rect.y + rect.height, false); bottom = Math.max(0, bottom); bottom = Math.min(rowPositions.getLength(), bottom); } return bottom; }; morpheus.Positions.getTop = function(rect, rowPositions) { var top = 0; if (rect != null) { top = rowPositions.getIndex(rect.y, false) - 1; top = Math.max(0, top); top = Math.min(rowPositions.getLength(), top); } return top; }; morpheus.Positions.getLeft = function(rect, columnPositions) { var left = 0; if (rect != null) { left = columnPositions.getIndex(rect.x, false) - 1; left = Math.max(0, left); left = Math.min(columnPositions.getLength(), left); } return left; }; morpheus.Positions.getRight = function(rect, columnPositions) { var right = columnPositions.getLength(); if (rect != null) { right = 1 + columnPositions.getIndex(rect.x + rect.width, false); right = Math.min(columnPositions.getLength(), right); } return right; }; morpheus.Positions.prototype = { length : 0, size : 13, squishFactor : 0.1, compress : true, copy : function() { var copy = new morpheus.Positions(); if (this.spaces) { copy.spaces = this.spaces.slice(); } copy.compress = this.compress; copy.squishFactor = this.squishFactor; copy.size = this.size; copy.length = this.length; if (this.isSquished) { copy.positionFunction = copy.squishedPositionFunction; copy.squishedIndices = _.clone(this.squishedIndices); copy.isSquished = true; } return copy; }, getIndex : function(position, exact) { if (exact) { return this.binaryExactSearch(position); } else { return this.binaryInExactSearch(position); } }, getLength : function() { return this.length; }, getPosition : function(index) { return this.positionFunction(index) + (this.spaces !== undefined ? this.spaces[index] : 0); }, getItemSize : function(index) { return this.squishedIndices[index] === true ? this.size * this.squishFactor : this.size; }, getSize : function() { return this.size; }, setSpaces : function(spaces) { this.spaces = spaces; }, setLength : function(length) { this.length = length; }, setSize : function(size) { this.size = size; if (this.isSquished) { this.setSquishedIndices(this.squishedIndices); } }, setSquishedIndices : function(squishedIndices) { if (squishedIndices != null) { var compress = this.compress; this.squishedIndices = squishedIndices; var positions = []; var squishFactor = this.squishFactor; var size = this.size; var position = 0; for (var i = 0, length = this.getLength(); i < length; i++) { var itemSize; if (squishedIndices[i] === true) { positions.push(position); itemSize = size * squishFactor; position += itemSize; } else { if (!compress) { position = size * i; } positions.push(position); position += size; } } this.isSquished = true; this.positions = positions; this.positionFunction = this.squishedPositionFunction; } else { this.squishedIndices = {}; this.isSquished = false; this.positionFunction = this.defaultPositionFunction; } }, setSquishFactor : function(f) { if (this.squishFactor !== f) { this.squishFactor = f; if (this.isSquished) { this.setSquishedIndices(this.squishedIndices); } } }, getSquishFactor : function() { return this.squishFactor; }, binaryExactSearch : function(position) { var low = 0; var high = this.length - 1; while (low <= high) { var mid = (low + high) >> 1; var midVal = this.getPosition(mid); var size = this.getItemSize(mid); if (midVal <= position && position < (midVal + size)) { return mid; } if (midVal < position) { low = mid + 1; } else if (midVal > position) { high = mid - 1; } else { return mid; // key found } } return -1; // key not found }, binaryInExactSearch : function(position) { var low = 0; var high = this.getLength() - 1; var maxIndex = this.getLength() - 1; if (position <= this.getPosition(0)) { return 0; } while (low <= high) { var mid = (low + high) >> 1; var midVal = this.getPosition(mid); var size = this.getItemSize(mid); var nextStart = maxIndex === mid ? midVal + size : this .getPosition(mid + 1); if (midVal <= position && position < nextStart) { return mid; } if (midVal < position) { low = mid + 1; } else if (midVal > position) { high = mid - 1; } else { return mid; // key found } } return low - 1; // key not found } };