(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.bounds = new Rect (new vec2(0, 0), new vec2(0, 0)) this.faces = [] if (face) { this.add(face) } } Surface.prototype.add = function(rect){ if (this.faces.length == 0) { this.bounds.x.a = rect.x.a this.bounds.x.b = rect.x.a this.bounds.y.a = rect.y.a this.bounds.y.b = rect.y.a } this.bounds.x.b += rect.width() this.bounds.y.b = Math.max(this.bounds.y.b, rect.height()) this.faces.push(rect) } 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.bounds.x.b < v.a || this.bounds.y.b < 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 } function center_for (dimension, position, delta) { var center = new vec2( position.a + dimension.a/2, position.b + dimension.b/2 ) if (delta) { center.a += delta.a center.b += delta.b } return center } Surface.prototype.clamp_delta = function (bounds, dimension, position, delta) { var left_edge = position.a + delta.a - bounds.x.a if (left_edge < 0) { delta.a -= left_edge } var right_edge = position.a + dimension.a + delta.a - bounds.x.b if (right_edge > 0) { delta.a -= right_edge } var bottom_edge = position.b + delta.b + bounds.y.a if (bottom_edge < 0) { delta.b -= bottom_edge } var top_edge = position.b + dimension.b + delta.b - bounds.y.b if (top_edge > 0) { delta.b -= top_edge } } Surface.prototype.translate = function (old_bounds, dimension, position, delta) { this.clamp_delta( this.bounds, dimension, position, delta ) var left_side = this.index_for_x( position.a + delta.a, 0 ) var right_side = this.index_for_x( position.a + dimension.a + delta.a, left_side ) var bounds = this.sides[left_side].clone() for (var i = left_side+1; i <= right_side; i++) { if (this.faces[i].y.a > bounds.y.a) { bounds.y.a = this.faces[i].y.a } if (this.faces[i].y.b < bounds.y.b) { bounds.y.b = this.faces[i].y.b } bounds.x.b = this.faces[i].x.b } if (bounds.width() > dimension.a || bounds.height() > dimension.b) { bounds = old_bounds } this.clamp_delta(bounds, dimension, position, delta) return bounds } Surface.prototype.index_for_x = function(x, min_i){ min_i = min_i || 0 if (x < 0 || x > width) { return -1 } for (var i = min_i; i < this.faces.length; i++) { if (this.faces[i].x.contains(x)) { return i } } return -1 } Surface.prototype.bounds_at_index_with_dimensions = function(index, dimensions){ var faces = this.faces if (index == -1) index = this.faces.length-1 var bounds, intersection var width = dimensions.a var height = dimensions.b for (var i = index; i >= 0; i--) { var face = faces[i] if (face.y.length() < height) { if (bounds) break else continue } if (! bounds) { bounds = face.clone() bounds.last = i } else if (bounds.width() < width) { intersection = bounds.y.intersection(face.y) if (intersection.length() < height) { break } else { bounds.y.a = intersection.a bounds.y.b = intersection.b bounds.x.a = face.x.a bounds.first = i continue } } else { if (face.y.a > bounds.y.a) break if (face.y.b < bounds.y.b) break } bounds.x.a = face.x.a bounds.first = i } for (var i = bounds ? bounds.last+1 : index+1; i < faces.length; i++) { var face = faces[i] if (face.y.length() < height) { if (bounds) break else continue } if (! bounds) { bounds = face.clone() bounds.first = i } else if (bounds.width() < width) { intersection = bounds.y.intersection(face.y) if (intersection.length() < height) { break } else { bounds.y.a = intersection.a bounds.y.b = intersection.b bounds.x.b = face.x.b bounds.last = i continue } } if (face.y.a > bounds.y.a) break if (face.y.b < bounds.y.b) break bounds.x.b = face.x.b bounds.last = i } if (! bounds) { // console.log('no bounds') return false } else if (bounds.width() < width) { // console.log('too narrow') return false } return bounds } Surface.prototype.bounds_at_index = function(index){ var faces = this.faces if (index == -1) index = this.faces.length-1 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) break if (face.y.a > bounds.y.a) break if (face.y.b < bounds.y.b) break bounds.x.a = face.x.a bounds.first = i } for (var i = index+1; i < faces.length; i++) { var face = faces[i] if (face.y.length() < height) break if (face.y.a > bounds.y.a) break if (face.y.b < bounds.y.b) break bounds.x.b = face.x.b bounds.last = i } return bounds } if ('window' in this) { window.Surface = Surface } else { module.exports = Surface } })()