window.rect = (function(){ var FRONT = 0x1, BACK = 0x2, LEFT = 0x4, RIGHT = 0x8 var rect = function (x0,y0,x1,y1){ if (x0 instanceof vec2) { this.x = x0 this.y = y0 } else if (x1 === undefined) { this.x = new vec2(x0,x0) this.y = new vec2(y0,y0) } else { this.x = new vec2(x0,x1) this.y = new vec2(y0,y1) } this.translation = new vec2(0,0) this.sides = FRONT | BACK | LEFT | RIGHT this.focused = false this.id = 0 } rect.prototype.clone = function(){ return new rect( this.x.clone(), this.y.clone() ) } rect.prototype.center = function(){ return new vec2(this.x.midpoint(), this.y.midpoint()) } rect.prototype.area = function(){ return this.x.length() * this.y.length() } rect.prototype.normalize = function(){ this.x.abs().add(this.translation.a) this.y.abs().add(this.translation.b) this.translation.a = this.translation.b = 0 return this } rect.prototype.contains = function(x,y){ return this.x.contains(x) && this.y.contains(y) } rect.prototype.intersects = function(r){ return this.x.intersects(r.x) && this.y.intersects(r.y) } rect.prototype.width = function(){ return this.x.length() } rect.prototype.height = function(){ return this.y.length() } rect.prototype.fill = function(){ // if (! this.sides) return this ctx.fillRect(this.x.a + this.translation.a, this.y.a + this.translation.b, this.x.length(), this.y.length()) return this } rect.prototype.stroke_sides = function(){ if (this.sides & FRONT) line(this.x.a, this.y.a, this.x.b, this.y.a) if (this.sides & BACK) line(this.x.a, this.y.b, this.x.b, this.y.b) if (this.sides & LEFT) line(this.x.a, this.y.a, this.x.a, this.y.b) if (this.sides & RIGHT) line(this.x.b, this.y.a, this.x.b, this.y.b) function line(a,b,c,d){ ctx.beginPath() ctx.moveTo(a,b) ctx.lineTo(c,d) ctx.stroke() } return this } rect.prototype.stroke = function(){ ctx.beginPath() ctx.moveTo(this.x.a, this.y.a) ctx.lineTo(this.x.b, this.y.b) ctx.stroke() return this } rect.prototype.perimeter = function(){ line( this.x.a, this.y.a, this.x.b, this.y.a, this.translation ) line( this.x.a, this.y.b, this.x.b, this.y.b, this.translation ) line( this.x.a, this.y.a, this.x.a, this.y.b, this.translation ) line( this.x.b, this.y.a, this.x.b, this.y.b, this.translation ) } rect.prototype.toString = function(){ var sides = "" if (this.sides & FRONT) sides += "front " if (this.sides & BACK) sides += "back " if (this.sides & LEFT) sides += "left " if (this.sides & RIGHT) sides += "right " var s = this.id + " " + "[" + this.x.toString() + " " + this.y.toString() + "] " + sides if (this.focused) return "" + s + "" return s } rect.prototype.quantize = function(n){ this.x.quantize(n) this.y.quantize(n) } rect.prototype.reset = function(){ var copy = this.clone() copy.sides = FRONT | BACK | LEFT | RIGHT copy.focused = this.focused copy.id = this.id this.regions = [ copy ] } rect.prototype.clipTo = function(r){ // for each of this rect's regions split the region if necessary var regions = this.regions var splits for (var i = 0, len = regions.length; i < len; i++) { if (regions[i] && regions[i].intersects(r)) { splits = regions[i].split(r) regions = regions.concat(splits) regions[i] = null } } this.regions = regions } rect.prototype.split = function(r){ var rz = this var splits = [] var split_contains = 0 var x_intervals = [], y_intervals = [] var sides = this.sides // Split vertically if (this.x.contains(r.x.a) && r.x.contains(this.x.b)) { x_intervals.push([ new vec2( this.x.a, r.x.a ), LEFT ]) x_intervals.push([ new vec2( r.x.a, this.x.b ), RIGHT ]) split_contains |= RIGHT } else if (r.x.contains(this.x.a) && this.x.contains(r.x.b)) { x_intervals.push([ new vec2( this.x.a, r.x.b ), LEFT ]) x_intervals.push([ new vec2( r.x.b, this.x.b ), RIGHT ]) split_contains |= LEFT } else if (this.x.contains(r.x.a) && this.x.contains(r.x.b)) { x_intervals.push([ new vec2( this.x.a, r.x.a ), LEFT ]) x_intervals.push([ new vec2( r.x.a, r.x.b ), 0 ]) x_intervals.push([ new vec2( r.x.b, this.x.b ), RIGHT ]) split_contains |= LEFT | RIGHT } else { // if (r.x.contains(this.x.a) && r.x.contains(r.x.b)) { x_intervals.push([ new vec2( this.x.a, this.x.b ), LEFT | RIGHT ]) split_contains |= LEFT | RIGHT } // Split horizontally if (this.y.contains(r.y.a) && r.y.contains(this.y.b)) { y_intervals.push([ new vec2( this.y.a, r.y.a ), FRONT ]) y_intervals.push([ new vec2( r.y.a, this.y.b ), BACK ]) split_contains |= BACK } else if (r.y.contains(this.y.a) && this.y.contains(r.y.b)) { y_intervals.push([ new vec2( this.y.a, r.y.b ), FRONT ]) y_intervals.push([ new vec2( r.y.b, this.y.b ), BACK ]) split_contains |= FRONT } else if (this.y.contains(r.y.a) && this.y.contains(r.y.b)) { y_intervals.push([ new vec2( this.y.a, r.y.a ), FRONT ]) y_intervals.push([ new vec2( r.y.a, r.y.b ), 0 ]) y_intervals.push([ new vec2( r.y.b, this.y.b ), BACK ]) split_contains |= FRONT | BACK } else { // if (r.y.contains(this.y.a) && this.y.contains(r.y.b)) { y_intervals.push([ new vec2( this.y.a, this.y.b ), FRONT | BACK ]) split_contains |= FRONT | BACK } x_intervals.forEach(function(x){ y_intervals.forEach(function(y){ var rn = new rect(x[0], y[0]) rn.id = rz.id rn.sides = ((x[1] | y[1]) & sides) if (r.intersects(rn)) { rn.sides = 0 } rn.focused = rz.focused splits.push(rn) }) }) return splits } return rect })()