(function(){ var vec2, Rect if ('window' in this) { vec2 = window.vec2 Rect = window.Rect } else { vec2 = require('./vec2') Rect = require('./rect') } var Surface = function (face){ this.width = 0 this.height = 0 this.faces = [] if (face) { this.add(face) } } Surface.prototype.add = function(rect){ this.faces.push(rect) this.width += rect.width() this.height = Math.max(this.height, rect.height()) } Surface.prototype.fits_scale = function(v, scale){ v = v.clone().mul(scale) return this.fits(v) } Surface.prototype.fits = function(v){ var faces = this.faces var scratch if (this.width < v.a || this.height < v.b) { return null } for (var i = 0; i < faces.length; i++) { if (faces[i].fits(v)) { return faces[i] } } scratch = new Rect (0,0,0,0) for (var i = 0; i < faces.length; i++) { if (faces[i].y.length() < v.b) { continue } scratch.x.a = faces[i].x.a scratch.x.b = faces[i].x.b scratch.y.a = faces[i].y.a scratch.y.b = faces[i].y.b SEARCH: for (var j = i+1; j < faces.length; j++) { if (faces[j].y.a > scratch.y.a) { scratch.y.a = faces[j].y.a } if (faces[j].y.b < scratch.y.b) { scratch.y.b = faces[j].y.b } if (scratch.y.b <= scratch.y.a || scratch.y.length() < v.b) { break SEARCH } scratch.x.b = faces[j].x.b if (scratch.fits(v)) { return scratch } } } return null } Surface.prototype.place = function(v, index){ var face, faces = this.faces } Surface.prototype.bounds = function(index){ var bounds = faces[index].clone() var height = faces[index].height() bounds.first = bounds.last = index for (var i = index-1; i >= 0; i--) { var face = faces[i] if (face.y.length() < height) { continue } if (face.y.a > bounds.y.a) { continue } if (face.y.b < bounds.y.b) { continue } bounds.x.a = bounds.x.a bounds.first = i } for (var i = index+1; i < faces.length; i++) { var face = faces[i] if (face.y.length() < height) { continue } if (face.y.a > bounds.y.a) { continue } if (face.y.b < bounds.y.b) { continue } bounds.x.b = bounds.x.b bounds.last = i } return bounds } Surface.prototype.clamp = function (bounds, dimension, position, dx, dy) { // we start out with a set of boundaries where we know the object should fall // we then check if we've moved the box off any edge of the bounds // if so, check if the new position is valid with respect to the surface. // for horizontal movement, this means checking the height and boundaries of // faces with adjacent indexes var p = position.clone(), nextIndex, nextFace, newBounds p.a += dx p.b += dy // left edge if (p.a < bounds.x.a) { if (bounds.first == 0) { p.a = bounds.x.a } else { nextIndex = bounds.first - 1 nextFace = this.faces[nextIndex] if (nextFace.y.a <= p.b && p.b + dimension.b <= nextFace.y.b) { // it appears the div to the left will accomodate this element } else { p.a = bounds.x.a } } } // right edge else if (p.a + dimension.a > bounds.x.b) { if (bounds.last == this.faces.length-1) { p.a = bounds.x.b - dimension.a } else { nextIndex = bounds.last + 1 nextFace = this.faces[nextIndex] if (nextFace.y.a <= p.b && p.b + dimension.b <= nextFace.y.b) { // it appears the div to the right will accomodate this element } else { p.a = bounds.x.b - dimension.a } } } // for vertical movement, this means checking the height and boundaries of // elements in the set of bounds' indexes if (p.b < bounds.y.a) { for (var i = Math.min(nextIndex, bounds.first), last = Math.max(nextIndex, bounds.last); i < last; i++) { face = this.faces[i] // loop over each of the faces in the list // given a face, figure out if the new top-left is in its bounds // if so, find which one contains its right edge // get the lowest height of this set // then clamp to that lowest height // additionally, this is the new center-index // recalculate the bounds // if (face.x.b < p. // for (var j = bounds.first+1; j < bounds.last } } // bottom edge, so we can clamp here trivially else if (p.b + dimension.b > bounds.y.b) { p.b = bounds.y.b - dimension.b } // if we're able to move out of bounds in that direction, recalculate the bounds } if ('window' in this) { window.Surface = Surface } else { module.exports = Surface } })()