summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile9
-rw-r--r--package.json3
-rw-r--r--public/assets/img/playbutton.pngbin0 -> 3616 bytes
-rw-r--r--public/assets/javascripts/mx/mx.js2
-rw-r--r--public/assets/javascripts/rectangles/engine/rooms/_rooms.js196
-rw-r--r--public/assets/javascripts/rectangles/engine/rooms/builder.js526
-rw-r--r--public/assets/javascripts/rectangles/engine/rooms/clipper.js211
-rw-r--r--public/assets/javascripts/rectangles/engine/rooms/mover.js11
-rw-r--r--public/assets/javascripts/rectangles/engine/scenery/_scenery.js2
-rw-r--r--public/assets/javascripts/rectangles/models/rect.js118
-rw-r--r--public/assets/javascripts/rectangles/models/room.js49
-rw-r--r--public/assets/javascripts/rectangles/models/tree.js85
-rw-r--r--public/assets/javascripts/rectangles/models/vec2.js89
-rw-r--r--public/assets/javascripts/rectangles/util/colors.js10
-rw-r--r--public/assets/javascripts/rectangles/util/sort.js161
-rw-r--r--public/assets/javascripts/rectangles/util/uid.js35
-rw-r--r--public/assets/javascripts/ui/editor/MediaViewer.js4
-rwxr-xr-xpublic/assets/stylesheets/app.css2
-rw-r--r--test/00-setup.js2
-rw-r--r--test/01-test-vec2.js102
-rw-r--r--test/02-test-rect.js211
-rw-r--r--test/03-test-clipper.js129
-rw-r--r--test/04-test-builder.js260
-rw-r--r--test/05-test-mover.js74
-rw-r--r--test/mocks/mx.js22
-rw-r--r--views/partials/scripts.ejs24
26 files changed, 1706 insertions, 631 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..877cd69
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,9 @@
+
+test:
+ ./node_modules/.bin/mocha -R nyan
+
+spec:
+ ./node_modules/.bin/mocha -R spec
+
+.PHONY: test
+
diff --git a/package.json b/package.json
index ca45bff..b4ac400 100644
--- a/package.json
+++ b/package.json
@@ -39,6 +39,7 @@
"grunt-contrib-watch": "~0.5.3",
"grunt-contrib-clean": "~0.5.0",
"grunt-contrib-copy": "~0.5.0",
- "grunt-dentist": "~0.3.4"
+ "grunt-dentist": "~0.3.4",
+ "mocha": "~1.20.1"
}
}
diff --git a/public/assets/img/playbutton.png b/public/assets/img/playbutton.png
new file mode 100644
index 0000000..51ad4a4
--- /dev/null
+++ b/public/assets/img/playbutton.png
Binary files differ
diff --git a/public/assets/javascripts/mx/mx.js b/public/assets/javascripts/mx/mx.js
index 6b36392..40a5f2e 100644
--- a/public/assets/javascripts/mx/mx.js
+++ b/public/assets/javascripts/mx/mx.js
@@ -587,7 +587,7 @@ var MX = MX || (function (undefined) {
}
}
})
-
+
return MX
})() \ No newline at end of file
diff --git a/public/assets/javascripts/rectangles/engine/rooms/_rooms.js b/public/assets/javascripts/rectangles/engine/rooms/_rooms.js
index e0033e3..29dee41 100644
--- a/public/assets/javascripts/rectangles/engine/rooms/_rooms.js
+++ b/public/assets/javascripts/rectangles/engine/rooms/_rooms.js
@@ -1,99 +1,139 @@
-var Rooms = new function(){
-
- var base = this
-
- base.list = {}
- base.walls = {}
- base.regions = []
+(function(){
- base.init = function(){
- Rooms.builder.init()
- Rooms.clipper.init()
- Rooms.mover.init()
+ var vec2, Rect, Room, sort, UidGenerator, _
+ if ('window' in this) {
+ vec2 = window.vec2
+ Rect = window.Rect
+ Room = window.Room
+ sort = window.sort
+ UidGenerator = window.UidGenerator
+ _ = window._
}
-
- base.filter = function(f){
- return _.values(base.list).filter(f)
+ else {
+ vec2 = require('../../models/vec2')
+ Rect = require('../../models/rect')
+ Room = require('../../models/room')
+ UidGenerator = require('../../util/uid')
+ sort = require('../../util/sort')
+ _ = require('lodash')
+ FRONT = 0x1, BACK = 0x2, LEFT = 0x4, RIGHT = 0x8, FLOOR = 0x10, CEILING = 0x20
+ TOP = CEILING, BOTTOM = FLOOR
+ function sidesToString(sides){
+ var s = ""
+ if (sides & FRONT) s += "front "
+ if (sides & BACK) s += "back "
+ if (sides & LEFT) s += "left "
+ if (sides & RIGHT) s += "right "
+ if (sides & TOP) s += "top "
+ if (sides & BOTTOM) s += "bottom "
+ return s
+ }
}
+
+ var Rooms = new function(){
+
+ var base = this
- base.add = function(room){
- base.list[room.id] = room
- }
+ base.list = {}
+ base.walls = {}
+ base.regions = []
+
+ base.uid = new UidGenerator(base.list)
+
+ base.init = function(){
+ Rooms.builder.init()
+ Rooms.clipper.init()
+ Rooms.mover.init()
+ }
- base.add_with_rect = function(rect){
- var room = new Room({
- rect: rect,
- height: 500
- })
- base.add(room)
- return room
- }
+ base.filter = function(f){
+ return _.values(base.list).filter(f)
+ }
- base.remove = function(room){
- delete base.list[room.id]
- Rooms.clipper.update()
- }
+ base.add = function(room){
+ base.list[room.id] = room
+ }
- base.removeAll = function(){
- base.list = {}
- base.regions = []
- Rooms.clipper.update()
- }
+ base.add_with_rect = function(rect){
+ var room = new Room({
+ rect: rect,
+ height: 500
+ })
+ base.add(room)
+ return room
+ }
- base.count = function(){
- return this.values().length
- }
+ base.remove = function(room){
+ delete base.list[room.id]
+ Rooms.clipper.update()
+ }
- base.forEach = function(f){
- return base.values().forEach(f)
- }
+ base.removeAll = function(){
+ base.list = {}
+ base.regions = []
+ Rooms.clipper.update()
+ }
- base.map = function(f){
- return base.values().map(f)
- }
+ base.count = function(){
+ return this.values().length
+ }
- base.values = function(){
- return _.values(base.list)
- }
+ base.forEach = function(f){
+ return base.values().forEach(f)
+ }
- base.serialize = function(){
- var rooms = base.map(function(room){
- return room.serialize()
- })
- return rooms
- }
+ base.map = function(f){
+ return base.values().map(f)
+ }
- base.deserialize = function(rooms_data){
- rooms_data.forEach(function(data){
- var rect = new Rect(data.rect.x[0], data.rect.y[0], data.rect.x[1], data.rect.y[1])
- var room = new Room({
- id: data.id,
- rect: rect,
- height: data.height
+ base.values = function(){
+ return _.values(base.list)
+ }
+
+ base.serialize = function(){
+ var rooms = base.map(function(room){
+ return room.serialize()
})
- base.add(room)
- })
- Rooms.clipper.update()
- }
+ return rooms
+ }
+
+ base.deserialize = function(rooms_data){
+ rooms_data.forEach(function(data){
+ var rect = new Rect(data.rect.x[0], data.rect.y[0], data.rect.x[1], data.rect.y[1])
+ var room = new Room({
+ id: data.id,
+ rect: rect,
+ height: data.height
+ })
+ base.add(room)
+ })
+ Rooms.clipper.update()
+ }
- base.serializeWalls = function(){
- return []
- }
+ base.serializeWalls = function(){
+ return []
+ }
- base.deserializeWalls = function(walls_data){
- return []
- }
+ base.deserializeWalls = function(walls_data){
+ return []
+ }
- base.uid = UidGenerator(base.list)
+ base.sorted_by_position = function(){
+ return sort.rooms_by_position( base.values() )
+ }
+ base.sorted_by_height = function(){
+ return sort.rooms_by_height( base.values() )
+ }
+ base.sorted_by_area = function(){
+ return sort.rooms_by_area( base.values() )
+ }
- base.sorted_by_position = function(){
- return sort_rooms_by_position( base.values() )
}
- base.sorted_by_height = function(){
- return sort_rooms_by_height( base.values() )
+
+ if ('window' in this) {
+ window.Rooms = Rooms
}
- base.sorted_by_area = function(){
- return sort_rooms_by_area( base.values() )
+ else {
+ module.exports = Rooms
}
-
-}
+})()
diff --git a/public/assets/javascripts/rectangles/engine/rooms/builder.js b/public/assets/javascripts/rectangles/engine/rooms/builder.js
index 49e55dc..dfabc86 100644
--- a/public/assets/javascripts/rectangles/engine/rooms/builder.js
+++ b/public/assets/javascripts/rectangles/engine/rooms/builder.js
@@ -1,295 +1,319 @@
+(function(){
-Rooms.builder = new function(){
- var base = this
+ var Rooms, sort
+ if ('window' in this) {
+ Rooms = window.Rooms
+ sort = window.sort
+ }
+ else {
+ MX = require('../../../../../../test/mocks/mx.js')
+ scene = MX.scene
+ Rooms = require('./_rooms')
+ sort = require('../../util/sort')
+ FRONT = 0x1, BACK = 0x2, LEFT = 0x4, RIGHT = 0x8, FLOOR = 0x10, CEILING = 0x20
+ PI = Math.PI
+ HALF_PI = PI/2
+ TOP = CEILING, BOTTOM = FLOOR
+ function sidesToString(sides){
+ var s = ""
+ if (sides & FRONT) s += "front "
+ if (sides & BACK) s += "back "
+ if (sides & LEFT) s += "left "
+ if (sides & RIGHT) s += "right "
+ if (sides & TOP) s += "top "
+ if (sides & BOTTOM) s += "bottom "
+ return s
+ }
+ }
+
+ Rooms.builder = new function(){
+ var base = this
- var els = []
+ var els = []
- base.init = function(){
- base.bind()
- }
+ base.init = function(){
+ base.bind()
+ }
- base.bind = function(){
- app.on("clip", rebuild)
- }
+ base.bind = function(){
+ app.on("clip", base.rebuild.bind(base))
+ }
- function rebuild(){
- if (window.scene) {
- clear()
- build()
- bind()
+ base.rebuild = function(){
+ if (window.scene) {
+ base.clear()
+ base.build()
+ base.bind_walls()
+ }
}
- }
- function build (){
- Rooms.regions.forEach(function(region){
- build_walls(region).forEach(function(el){
- els.push(el)
- scene.add(el)
- })
- })
+
+ base.build = function (){
+ Rooms.regions.forEach(function(region){
+ this.build_walls(region).forEach(function(el){
+ els.push(el)
+ scene.add(el)
+ })
+ }.bind(this))
- Rooms.sorted_by_height().forEach(function(room){
- build_floors(room).forEach(function(el){
- els.push(el)
- scene.add(el)
- })
- })
- }
+ Rooms.sorted_by_height().forEach(function(room){
+ this.build_floors(room).forEach(function(el){
+ els.push(el)
+ scene.add(el)
+ })
+ }.bind(this))
+ }
- function bind (){
- Rooms.forEach(function(room){
- room.walls = room.group_mx_walls()
- room.walls.forEach(function(wall){
- Rooms.walls[ wall.id ] = wall
- wall.bind()
- wall.randomize_colors()
+ base.bind_walls = function (){
+ Rooms.forEach(function(room){
+ room.walls = room.group_mx_walls()
+ room.walls.forEach(function(wall){
+ Rooms.walls[ wall.id ] = wall
+ wall.bind()
+ wall.randomize_colors()
+ })
})
- })
- }
+ }
- function clear (){
- els.forEach(function(el){
- scene.remove(el)
- el.destroy && el.destroy()
- })
- els = []
- }
+ base.clear = function (){
+ els.forEach(function(el){
+ scene.remove(el)
+ el.destroy && el.destroy()
+ })
+ els = []
+ }
- function build_walls (region){
- var room = Rooms.list[ region.id ]
+ this.build_walls = function (region) {
+ var room = Rooms.list[ region.id ]
- var list = [], el = null
+ var list = [], el = null
- var width = region.x.length()
- var depth = region.y.length()
- var height = room.height
+ var width = region.x.length()
+ var depth = region.y.length()
+ var height = room.height
- if (region.sides & FRONT) {
- el = make_wall('.front')
- el.width = width
- el.height = height
- el.rotationY = PI
- el.x = region.x.a + width/2
- el.y = height/2
- el.z = region.y.a
- el.rect = region
- el.side = FRONT
- room.mx_walls.push(el)
- list.push(el)
- }
- if (region.sides & BACK) {
- var el = make_wall('.back')
- el.width = width
- el.height = height
- el.rotationY = 0
- el.x = region.x.b - width/2
- el.y = height/2
- el.z = region.y.b
- el.rect = region
- el.side = BACK
- room.mx_walls.push(el)
- list.push(el)
- }
- if (region.sides & LEFT) {
- el = make_wall('.left')
- el.rotationY = HALF_PI
- el.height = height
- el.width = depth
- el.x = region.x.a
- el.y = height/2
- el.z = region.y.a + depth/2
- el.rect = region
- el.side = LEFT
- room.mx_walls.push(el)
- list.push(el)
- }
- if (region.sides & RIGHT) {
- el = make_wall('.right')
- el.rotationY = -HALF_PI
- el.height = height
- el.width = depth
- el.x = region.x.b
- el.y = height/2
- el.z = region.y.b - depth/2
- el.rect = region
- el.side = RIGHT
- room.mx_walls.push(el)
- list.push(el)
+ if (region.sides & FRONT) {
+ el = this.make_wall('.front')
+ el.width = width
+ el.height = height
+ el.rotationY = PI
+ el.x = region.x.a + width/2
+ el.y = height/2
+ el.z = region.y.a
+ el.rect = region
+ el.side = FRONT
+ room.mx_walls.push(el)
+ list.push(el)
+ }
+ if (region.sides & BACK) {
+ var el = this.make_wall('.back')
+ el.width = width
+ el.height = height
+ el.rotationY = 0
+ el.x = region.x.b - width/2
+ el.y = height/2
+ el.z = region.y.b
+ el.rect = region
+ el.side = BACK
+ room.mx_walls.push(el)
+ list.push(el)
+ }
+ if (region.sides & LEFT) {
+ el = this.make_wall('.left')
+ el.rotationY = HALF_PI
+ el.height = height
+ el.width = depth
+ el.x = region.x.a
+ el.y = height/2
+ el.z = region.y.a + depth/2
+ el.rect = region
+ el.side = LEFT
+ room.mx_walls.push(el)
+ list.push(el)
+ }
+ if (region.sides & RIGHT) {
+ el = this.make_wall('.right')
+ el.rotationY = -HALF_PI
+ el.height = height
+ el.width = depth
+ el.x = region.x.b
+ el.y = height/2
+ el.z = region.y.b - depth/2
+ el.rect = region
+ el.side = RIGHT
+ room.mx_walls.push(el)
+ list.push(el)
+ }
+
+ return list
}
- return list
- }
+ this.build_floors = function (room){
+ var list = [], el = null
- function build_floors(room){
- var list = [], el = null
-
- var constructed = room.intersects.filter(function(room){ return room.constructed })
- sort_rooms_by_height(constructed)
-
- if (constructed.length > 0) {
- // render the regions that don't intersect with anything we've already rendered
- // if the height is different, calculate the overlapping sides and render half-walls
- room.regions.forEach(function(region){
- var intersected = false
- for (var i = 0; i < constructed.length; i++) {
- if (constructed[i].rect.contains(region)) {
- intersected = true
- // r.sides = 0xf
- // half_sides
- }
- else if (constructed[i].rect.intersects(region)) {
- intersected = true
- if (room.height < constructed[i].height) {
- var ceiling_walls = make_ceiling_walls( room, constructed[i], region )
- list = list.concat(ceiling_walls)
+ var constructed = room.intersects.filter(function(room){ return room.constructed })
+ sort.rooms_by_height(constructed)
+
+ if (constructed.length > 0) {
+ // render the regions that don't intersect with anything we've already rendered
+ // if the height is different, calculate the overlapping sides and render half-walls
+ room.regions.forEach(function(region){
+ var intersected = false
+ for (var i = 0; i < constructed.length; i++) {
+ if (constructed[i].rect.overlaps(region)) {
+ intersected = true
+ if (room.height < constructed[i].height) {
+ var ceiling_walls = this.make_ceiling_walls( room, constructed[i], region )
+ list = list.concat(ceiling_walls)
+ }
}
}
- }
- if (! intersected) {
- el = make_floor(room, region)
- list.push( el )
- room.mx_floor.push(el)
+ if (! intersected) {
+ el = this.make_floor(room, region)
+ list.push( el )
+ room.mx_floor.push(el)
- el = make_ceiling(room, region)
- list.push( el )
- room.mx_ceiling.push(el)
- }
- })
+ el = this.make_ceiling(room, region)
+ list.push( el )
+ room.mx_ceiling.push(el)
+ }
+ }.bind(this))
- }
- else {
- // render floor and ceiling for the entire rectangle
- el = make_floor(room, room.rect)
- list.push( el )
- room.mx_floor.push(el)
+ }
+ else {
+ // render floor and ceiling for the entire rectangle
+ el = this.make_floor(room, room.rect)
+ list.push( el )
+ room.mx_floor.push(el)
- el = make_ceiling(room, room.rect)
- list.push( el )
- room.mx_ceiling.push(el)
- }
+ el = this.make_ceiling(room, room.rect)
+ list.push( el )
+ room.mx_ceiling.push(el)
+ }
- room.constructed = true
- return list
- }
+ room.constructed = true
+ return list
+ }
- function make_ceiling_walls( lo, hi, region ){
- var list = []
+ this.make_ceiling_walls = function ( lo, hi, region ){
+ var list = []
- var width = region.x.length()
- var depth = region.y.length()
- var height = hi.height - lo.height
+ var width = region.x.length()
+ var depth = region.y.length()
+ var height = hi.height - lo.height
- if (! (region.half_sides & LEFT) && region.x.a == hi.rect.x.a) {
- el = make_wall('.left')
- el.rotationY = HALF_PI
- el.height = height
- el.width = depth
- el.x = region.x.a
- el.y = lo.height + height/2
- el.z = region.y.a + depth/2
- el.rect = region
- list.push(el)
- hi.mx_walls.push(el)
- region.half_sides |= LEFT
- el.half_side = LEFT
- }
+ if (! (region.half_sides & LEFT) && region.x.a == hi.rect.x.a) {
+ el = this.make_wall('.left')
+ el.rotationY = HALF_PI
+ el.height = height
+ el.width = depth
+ el.x = region.x.a
+ el.y = lo.height + height/2
+ el.z = region.y.a + depth/2
+ el.rect = region
+ list.push(el)
+ hi.mx_walls.push(el)
+ region.half_sides |= LEFT
+ el.half_side = LEFT
+ }
- if (! (region.half_sides & RIGHT) && region.x.b == hi.rect.x.b) {
- el = make_wall('.right')
- el.rotationY = -HALF_PI
- el.height = height
- el.width = depth
- el.x = region.x.b
- el.y = lo.height + height/2
- el.z = region.y.b - depth/2
- el.rect = region
- list.push(el)
- hi.mx_walls.push(el)
- region.half_sides |= RIGHT
- el.half_side = RIGHT
+ if (! (region.half_sides & RIGHT) && region.x.b == hi.rect.x.b) {
+ el = this.make_wall('.right')
+ el.rotationY = -HALF_PI
+ el.height = height
+ el.width = depth
+ el.x = region.x.b
+ el.y = lo.height + height/2
+ el.z = region.y.b - depth/2
+ el.rect = region
+ list.push(el)
+ hi.mx_walls.push(el)
+ region.half_sides |= RIGHT
+ el.half_side = RIGHT
+ }
+
+ if (! (region.half_sides & FRONT) && region.y.a == hi.rect.y.a) {
+ el = this.make_wall('.front')
+ el.width = width
+ el.height = height
+ el.rotationY = PI
+ el.x = region.x.a + width/2
+ el.y = lo.height + height/2
+ el.z = region.y.a
+ el.rect = region
+ list.push(el)
+ hi.mx_walls.push(el)
+ region.half_sides |= FRONT
+ el.half_side = FRONT
+ }
+
+ if (! (region.half_sides & BACK) && region.y.b == hi.rect.y.b) {
+ el = this.make_wall('.back')
+ el.width = width
+ el.height = height
+ el.rotationY = 0
+ el.x = region.x.b - width/2
+ el.y = lo.height + height/2
+ el.z = region.y.b
+ el.rect = region
+ list.push(el)
+ hi.mx_walls.push(el)
+ region.half_sides |= BACK
+ el.half_side = BACK
+ }
+ return list
}
- if (! (region.half_sides & FRONT) && region.y.a == hi.rect.y.a) {
- el = make_wall('.front')
+ this.make_floor = function (room, region) {
+ var width = region.x.length()
+ var depth = region.y.length()
+
+ var el = this.make_wall('.floor')
+ el.height = depth
el.width = width
- el.height = height
- el.rotationY = PI
el.x = region.x.a + width/2
- el.y = lo.height + height/2
- el.z = region.y.a
+ el.y = 0
+ el.z = region.y.a + depth/2
+ el.rotationX = PI/2
el.rect = region
- list.push(el)
- hi.mx_walls.push(el)
- region.half_sides |= FRONT
- el.half_side = FRONT
+ el.side = FLOOR
+ return el
}
+ this.make_ceiling = function (room, region) {
+ var width = region.x.length()
+ var depth = region.y.length()
+ var height = room.height
- if (! (region.half_sides & BACK) && region.y.b == hi.rect.y.b) {
- el = make_wall('.back')
+ var el = this.make_wall('.ceiling')
+ el.height = depth
el.width = width
- el.height = height
- el.rotationY = 0
- el.x = region.x.b - width/2
- el.y = lo.height + height/2
- el.z = region.y.b
+ el.x = region.x.a + width/2
+ el.y = height
+ el.z = region.y.a + depth/2
+ el.rotationX = -PI/2
el.rect = region
- list.push(el)
- hi.mx_walls.push(el)
- region.half_sides |= BACK
- el.half_side = BACK
+ el.side = CEILING
+ return el
}
- return list
- }
- function make_floor(room, region){
- var width = region.x.length()
- var depth = region.y.length()
-
- var el = make_wall('.floor')
- el.height = depth
- el.width = width
- el.x = region.x.a + width/2
- el.y = 0
- el.z = region.y.a + depth/2
- el.rotationX = PI/2
- el.rect = region
- el.side = FLOOR
- return el
- }
- function make_ceiling(room, region){
- var width = region.x.length()
- var depth = region.y.length()
- var height = room.height
+ this.make_wall = function (klass) {
+ var el = new MX.Object3D(".face" + (klass || ""))
+ el.width = el.height = el.scaleX = el.scaleY = el.scaleZ = 1
+ el.z = el.y = el.x = 0
+ el.side = 0
+ el.type = "Face"
+ el.el.style.opacity = 1.0
+ el.side = 0
+ el.rect = null
+ el.destroy = function(){
+ this.el = this.rect = null
+ }
- var el = make_wall('.ceiling')
- el.height = depth
- el.width = width
- el.x = region.x.a + width/2
- el.y = height
- el.z = region.y.a + depth/2
- el.rotationX = -PI/2
- el.rect = region
- el.side = CEILING
- return el
- }
+ // possible if walls are opaque
+ // el.el.classList.add("backface-hidden")
- function make_wall(klass){
- var el = new MX.Object3D(".face" + (klass || ""))
- el.width = el.height = el.scaleX = el.scaleY = el.scaleZ = 1
- el.z = el.y = el.x = 0
- el.side = 0
- el.type = "Face"
- el.el.style.opacity = 1.0
- el.side = 0
- el.rect = null
- el.destroy = function(){
- this.el = this.rect = null
+ return el
}
- // possible if walls are opaque
- // el.el.classList.add("backface-hidden")
-
- return el
}
-}
-
+})()
diff --git a/public/assets/javascripts/rectangles/engine/rooms/clipper.js b/public/assets/javascripts/rectangles/engine/rooms/clipper.js
index e2bb894..33e3a84 100644
--- a/public/assets/javascripts/rectangles/engine/rooms/clipper.js
+++ b/public/assets/javascripts/rectangles/engine/rooms/clipper.js
@@ -1,107 +1,158 @@
+(function(){
-Rooms.clipper = new function(){
- var base = this
-
- base.init = function(){
- base.bind()
- base.update()
+ var Rooms, Tree, sort
+ if ('window' in this) {
+ Rooms = window.Rooms
+ Tree = window.Tree
+ sort = window.sort
}
-
- base.bind = function(){
- map.ui && map.ui.mouse.tube.on("up", function(){ base.update() })
+ else {
+ Rooms = require('./_rooms')
+ Tree = require('../../models/tree')
+ sort = require('../../util/sort')
+ FRONT = 0x1, BACK = 0x2, LEFT = 0x4, RIGHT = 0x8, FLOOR = 0x10, CEILING = 0x20
+ TOP = CEILING, BOTTOM = FLOOR
+ function sidesToString(sides){
+ var s = ""
+ if (sides & FRONT) s += "front "
+ if (sides & BACK) s += "back "
+ if (sides & LEFT) s += "left "
+ if (sides & RIGHT) s += "right "
+ if (sides & TOP) s += "top "
+ if (sides & BOTTOM) s += "bottom "
+ return s
+ }
}
- base.update = function(){
- base.solve_rects()
- app.tube("clip")
- }
-
- var rooms, regions
+ Rooms.clipper = new function(){
+ var base = this
+
+ base.init = function(){
+ base.bind()
+ base.update()
+ }
- // Given a set of overlapping rooms, clip any intersections, then cull any duplicate polygons
- base.solve_rects = function(){
- if (Rooms.count() == 0) {
- Rooms.regions = regions = []
- return
+ base.bind = function(){
+ map.ui && map.ui.mouse.tube.on("up", function(){ base.update() })
}
+
+ base.update = function(){
+ base.solve_rects()
+ app.tube("clip")
+ }
+
+ var regions
+
+ // Given a set of overlapping rooms, clip any intersections, then cull any duplicate polygons
+ base.solve_rects = function(){
+ if (Rooms.count() == 0) {
+ Rooms.regions = regions = []
+ return
+ }
- base.reset_rects()
- base.clip_rects()
- base.cull_rects()
+ base.reset_rects()
+ base.clip_rects()
+ var culled = base.cull_rects_iterative()
- Rooms.regions = sort_rects_by_position(regions)
- }
+ Rooms.regions = sort.rects_by_position(culled)
+ }
- // Reset the clipping/culling states of each of the rooms
- base.reset_rects = function(){
- Rooms.forEach(function(room){
- room.reset()
- })
- }
+ // Reset the clipping/culling states of each of the rooms
+ base.reset_rects = function(){
+ regions = []
+ Rooms.forEach(function(room){
+ room.reset()
+ })
+ }
- // Compare each room to the rooms it overlaps, and subdivide
- base.clip_rects = function(){
- var rooms = Rooms.sorted_by_position()
- regions = []
+ // Compare each room to the rooms it overlaps, and subdivide
+ base.clip_rects = function(){
+ var rooms = Rooms.sorted_by_position()
- var left, right
- for (var i = 0; i < rooms.length; i++) {
- left = rooms[i]
- for (var j = i+1; j < rooms.length; j++) {
- right = rooms[j]
- if (left.rect.intersects(right.rect)) {
- left.clipTo(right.rect)
- right.clipTo(left.rect)
- left.intersects.push(right)
- right.intersects.push(left)
- }
- if (left.rect.x.b < right.rect.x.a) {
- break
+ var left, right
+ for (var i = 0; i < rooms.length; i++) {
+ left = rooms[i]
+ for (var j = i+1; j < rooms.length; j++) {
+ right = rooms[j]
+ if (left.rect.intersects(right.rect)) {
+ left.clipTo(right.rect)
+ right.clipTo(left.rect)
+ left.intersects.push(right)
+ right.intersects.push(left)
+ }
+ if (left.rect.x.b < right.rect.x.a) {
+ break
+ }
}
}
+ for (var i = 0; i < rooms.length; i++) {
+ rooms[i].regions = rooms[i].regions.filter(function(r){ return !!r })
+ regions = regions.concat(rooms[i].regions)
+ }
+
+ return regions
}
- for (var i = 0; i < rooms.length; i++) {
- rooms[i].regions = rooms[i].regions.filter(function(r){ return !!r })
- regions = regions.concat(rooms[i].regions)
- }
- }
- // Find overlapping regions (of the same size) and dedupe
- base.cull_rects = function(){
- regions = sort_rects_by_area( regions )
+ // Find overlapping regions (of the same size) and dedupe
+ base.cull_rects = function(){
+ regions = sort.rects_by_area( regions )
- var ty = new Tree (regions[0].y.a, [regions[0]])
- var tx = new Tree (regions[0].x.a, ty)
- var ttx, tty
+ var ty = new Tree (regions[0].y.a, [regions[0]])
+ var tx = new Tree (regions[0].x.a, ty)
+ var ttx, tty
- for (var i = 1; i < regions.length; i++) {
- ttx = tx.add (regions[i].x.a, null)
- if (ttx.data) {
- tty = ttx.data.add (regions[i].y.a, null)
- // duplicate polygon?
- if (tty.data) {
- tty.data.forEach(function(yy, ii){
- if (yy.intersects(regions[i])) {
- if (yy.area() > regions[i].area()) {
- regions[i].dupe = true
+ for (var i = 1; i < regions.length; i++) {
+ ttx = tx.add (regions[i].x.a, null)
+ if (ttx.data) {
+ tty = ttx.data.add (regions[i].y.a, null)
+ // duplicate polygon?
+ if (tty.data) {
+ tty.data.forEach(function(yy, ii){
+ if (yy.intersects(regions[i])) {
+ if (yy.area() > regions[i].area()) {
+ regions[i].dupe = true
+ }
+ else {
+ yy.dupe = true
+ tty.data[ii] = regions[i]
+ }
}
- else {
- yy.dupe = true
- tty.data[ii] = regions[i]
- }
- }
- })
+ })
+ }
+ else {
+ tty.data = [regions[i]]
+ }
}
else {
- tty.data = [regions[i]]
+ ttx.data = new Tree (regions[i].y.a, [regions[i]])
}
}
- else {
- ttx.data = new Tree (regions[i].y.a, [regions[i]])
+
+ return regions
+ }
+
+ // Find overlapping regions and dedupe the smaller ones
+ base.cull_rects_iterative = function(){
+ regions = sort.rects_by_area( regions )
+
+ var region_i, region_j, i, j, _len
+
+ for (i = 0, _len = regions.length; i < _len-1; i++) {
+ region_i = regions[i]
+ for (j = i+1; j < _len; j++) {
+ region_j = regions[j]
+ if (region_j.dupe) continue;
+ if (region_i.overlaps(region_j)) {
+ region_i.dupe = true
+ }
+ }
}
+
+ return regions.filter(function(r){ return ! r.dupe })
}
+
+ return base
}
- return base
-}
+})()
diff --git a/public/assets/javascripts/rectangles/engine/rooms/mover.js b/public/assets/javascripts/rectangles/engine/rooms/mover.js
index e67d9bc..7195fcc 100644
--- a/public/assets/javascripts/rectangles/engine/rooms/mover.js
+++ b/public/assets/javascripts/rectangles/engine/rooms/mover.js
@@ -43,15 +43,8 @@ Rooms.mover = new function(){
var collision = base.room.collidesDisc(pos.x, pos.z, radius)
if (collision) {
- if (! (collision & LEFT_RIGHT)) {
- cam.x = base.room.rect.x.clampDisc(pos.x, radius)
- }
- else {
- // cam.x = base.room.rect.x.clampDisc(pos.x, radius)
- }
- if (! (collision & FRONT_BACK)) {
- cam.z = base.room.rect.y.clampDisc(pos.z, radius)
- }
+ cam.x = (collision & LEFT_RIGHT) ? base.room.rect.x.clampDisc(pos.x, radius) : pos.x
+ cam.z = (collision & FRONT_BACK) ? base.room.rect.y.clampDisc(pos.z, radius) : pos.z
return
}
diff --git a/public/assets/javascripts/rectangles/engine/scenery/_scenery.js b/public/assets/javascripts/rectangles/engine/scenery/_scenery.js
index abd14ba..fe5f037 100644
--- a/public/assets/javascripts/rectangles/engine/scenery/_scenery.js
+++ b/public/assets/javascripts/rectangles/engine/scenery/_scenery.js
@@ -40,7 +40,7 @@ var Scenery = new function(){
media && media.destroy()
}
- base.uid = UidGenerator(base.list)
+ base.uid = new UidGenerator(base.list)
base.forEach = function(f){
return base.values().forEach(f)
diff --git a/public/assets/javascripts/rectangles/models/rect.js b/public/assets/javascripts/rectangles/models/rect.js
index 315adef..590440a 100644
--- a/public/assets/javascripts/rectangles/models/rect.js
+++ b/public/assets/javascripts/rectangles/models/rect.js
@@ -1,5 +1,25 @@
(function(){
+ var vec2
+ if ('window' in this) {
+ vec2 = window.vec2
+ }
+ else {
+ vec2 = require('./vec2')
+ FRONT = 0x1, BACK = 0x2, LEFT = 0x4, RIGHT = 0x8, FLOOR = 0x10, CEILING = 0x20
+ TOP = CEILING, BOTTOM = FLOOR
+ function sidesToString(sides){
+ var s = ""
+ if (sides & FRONT) s += "front "
+ if (sides & BACK) s += "back "
+ if (sides & LEFT) s += "left "
+ if (sides & RIGHT) s += "right "
+ if (sides & TOP) s += "top "
+ if (sides & BOTTOM) s += "bottom "
+ return s
+ }
+ }
+
var Rect = function (x0,y0,x1,y1){
if (x0 instanceof vec2) {
this.x = x0
@@ -74,8 +94,18 @@
Rect.prototype.containsDisc = function(x,y,r){
return this.x.containsDisc(x,r) && this.y.containsDisc(y,r)
}
+ Rect.prototype.overlaps = function(rect){
+ return this.x.overlaps(rect.x) && this.y.overlaps(rect.y)
+ }
Rect.prototype.intersects = function(r){
- return this.x.intersects(r.x) && this.y.intersects(r.y)
+ var corner_intersect = (this.x.b === r.x.a && this.y.b === r.y.a)
+ return this.x.intersects(r.x) && this.y.intersects(r.y) && ! corner_intersect
+ }
+ Rect.prototype.adjacent = function(r){
+ return this.x.adjacent(r.x) && this.y.adjacent(r.y)
+ }
+ Rect.prototype.eq = function(r){
+ return this.x.eq(r.x) && this.y.eq(r.y)
}
Rect.prototype.nearEdge = function (x, y, r) {
var edges = 0
@@ -111,79 +141,51 @@
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
- }
+ // bisect (or trisect) two overlapping rectangles
+ var x_intervals = this.x.split( r.x, LEFT, RIGHT )
+ var y_intervals = this.y.split( r.y, FRONT, BACK )
- x_intervals.forEach(function(x){
- y_intervals.forEach(function(y){
+ // generate rectangular regions by crossing the two sets of vectors
+ x_intervals.forEach(function(x, i){
+ y_intervals.forEach(function(y, i){
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)
+
+ // cull extra walls from overlapping regions
+ if (r.x.contains(rn.x.a) && r.x.contains(rn.x.b)) {
+ if (rz.y.a == rn.y.a && r.y.containsCenter(rn.y.a)) { // top edges
+ rn.sides &= ~ FRONT
+ }
+ if (rz.y.b == rn.y.b && r.y.containsCenter(rn.y.b)) { // bottom edges
+ rn.sides &= ~ BACK
+ }
+ }
+
+ if (r.y.contains(rn.y.a) && r.y.contains(rn.y.b)) {
+ if (rz.x.a == rn.x.a && r.x.containsCenter(rn.x.a)) { // left edges
+ rn.sides &= ~ LEFT
+ }
+
+ if (rz.x.b == rn.x.b && r.x.containsCenter(rn.x.b) ) { // right edges
+ rn.sides &= ~ RIGHT
+ }
+ }
+
})
})
+
return splits
}
if ('window' in this) {
window.Rect = Rect
}
- else if ('module' in this) {
+ else {
module.exports = Rect
}
diff --git a/public/assets/javascripts/rectangles/models/room.js b/public/assets/javascripts/rectangles/models/room.js
index 61a7447..d19ca2f 100644
--- a/public/assets/javascripts/rectangles/models/room.js
+++ b/public/assets/javascripts/rectangles/models/room.js
@@ -1,4 +1,32 @@
-window.Room = (function(){
+
+(function(){
+ var vec2, Rect, sort
+ if ('window' in this) {
+ vec2 = window.vec2
+ Rect = window.Rect
+ sort = window.sort
+ }
+ else {
+ vec2 = require('./vec2')
+ Rect = require('./rect')
+ UidGenerator = require('../util/uid')
+ Rooms = { uid: new UidGenerator({}) }
+ sort = require('../util/sort')
+ FRONT = 0x1, BACK = 0x2, LEFT = 0x4, RIGHT = 0x8, FLOOR = 0x10, CEILING = 0x20
+ TOP = CEILING, BOTTOM = FLOOR
+ FRONT_BACK = FRONT | BACK
+ LEFT_RIGHT = LEFT | RIGHT
+ function sidesToString(sides){
+ var s = ""
+ if (sides & FRONT) s += "front "
+ if (sides & BACK) s += "back "
+ if (sides & LEFT) s += "left "
+ if (sides & RIGHT) s += "right "
+ if (sides & TOP) s += "top "
+ if (sides & BOTTOM) s += "bottom "
+ return s
+ }
+ }
var Room = function(opt){
this.id = opt.id || Rooms.uid("room_")
@@ -81,10 +109,10 @@ window.Room = (function(){
var side = pair[0], els = pair[1]
if (side & LEFT_RIGHT) {
- els.sort(compare_x)
+ els.sort(sort.compare_x)
}
else if (side & FRONT_BACK) {
- els.sort(compare_z)
+ els.sort(sort.compare_z)
}
// wall holds state for the last wall we created/saw..
@@ -212,17 +240,22 @@ window.Room = (function(){
if (contains_x) {
collision |= wall_collision & FRONT_BACK
}
- else if (contains_y) {
+ if (contains_y) {
collision |= wall_collision & LEFT_RIGHT
}
- else if (bitcount(wall_collision) > 1) {
- collision |= wall_collision
- }
+// if (bitcount(wall_collision) > 1) {
+// collision |= wall_collision
+// }
})
return collision
}
- return Room
+ if ('window' in this) {
+ window.Room = Room
+ }
+ else {
+ module.exports = Room
+ }
})()
diff --git a/public/assets/javascripts/rectangles/models/tree.js b/public/assets/javascripts/rectangles/models/tree.js
index 8193988..7c698fe 100644
--- a/public/assets/javascripts/rectangles/models/tree.js
+++ b/public/assets/javascripts/rectangles/models/tree.js
@@ -1,37 +1,48 @@
-var Tree = function(n, data){
- this.lo = null
- this.hi = null
- this.value = n
- this.data = data
-}
-Tree.prototype.find = function(n){
- if (n == this.value) return this
- if (n < this.value) return this.lo ? this.lo.find(n) : this
- if (n > this.value) return this.hi ? this.hi.find(n) : this
-}
-Tree.prototype.add = function(n, data){
- var closest = this.find(n)
- if (n == closest.value) return closest
- if (n < closest.value) return closest.lo = new Tree(n, data)
- if (n > closest.value) return closest.hi = new Tree(n, data)
-}
-Tree.prototype.toArray = function(){
- var a = []
- if (this.lo) a = a.concat(this.lo.toArray())
- a.push(this.data)
- if (this.hi) a = a.concat(this.hi.toArray())
- return a
-}
-Tree.prototype.toString = function(){
- var s = "";
- if (this.lo) s += this.lo.toString()
- s += this.value + ","
- if (this.hi) s += this.hi.toString()
- return s
-}
-Tree.prototype.depth = function(){
- if (this.lo && this.hi) return 1 + max(this.lo.depth(), this.hi.depth())
- else if (this.lo) return 1 + this.lo.depth()
- else if (this.hi) return 1 + this.hi.depth()
- else return 0
-}
+(function(){
+
+ var Tree = function(n, data){
+ this.lo = null
+ this.hi = null
+ this.value = n
+ this.data = data
+ }
+ Tree.prototype.find = function(n){
+ if (n == this.value) return this
+ if (n < this.value) return this.lo ? this.lo.find(n) : this
+ if (n > this.value) return this.hi ? this.hi.find(n) : this
+ }
+ Tree.prototype.add = function(n, data){
+ var closest = this.find(n)
+ if (n == closest.value) return closest
+ if (n < closest.value) return closest.lo = new Tree(n, data)
+ if (n > closest.value) return closest.hi = new Tree(n, data)
+ }
+ Tree.prototype.toArray = function(){
+ var a = []
+ if (this.lo) a = a.concat(this.lo.toArray())
+ a.push(this.data)
+ if (this.hi) a = a.concat(this.hi.toArray())
+ return a
+ }
+ Tree.prototype.toString = function(){
+ var s = "";
+ if (this.lo) s += this.lo.toString()
+ s += this.value + ","
+ if (this.hi) s += this.hi.toString()
+ return s
+ }
+ Tree.prototype.depth = function(){
+ if (this.lo && this.hi) return 1 + max(this.lo.depth(), this.hi.depth())
+ else if (this.lo) return 1 + this.lo.depth()
+ else if (this.hi) return 1 + this.hi.depth()
+ else return 0
+ }
+
+ if ('window' in this) {
+ window.Tree = Tree
+ }
+ else {
+ module.exports = Tree
+ }
+
+})()
diff --git a/public/assets/javascripts/rectangles/models/vec2.js b/public/assets/javascripts/rectangles/models/vec2.js
index 9233aec..2bf286b 100644
--- a/public/assets/javascripts/rectangles/models/vec2.js
+++ b/public/assets/javascripts/rectangles/models/vec2.js
@@ -1,4 +1,6 @@
(function(){
+ function clamp(n,a,b){ return n<a?a:n<b?n:b }
+
var vec2 = function (a,b){
this.a = a
this.b = b
@@ -7,7 +9,7 @@
return this.b-this.a
}
vec2.prototype.length = function(){
- return abs(this.b-this.a)
+ return Math.abs(this.b-this.a)
}
vec2.prototype.dist = function(){
return dist(0,this.a,0,this.b)
@@ -70,6 +72,9 @@
vec2.prototype.contains = function(n){
return this.a <= n && n <= this.b
}
+ vec2.prototype.containsCenter = function(n){
+ return this.a < n && n < this.b
+ }
vec2.prototype.containsDisc = function(n,r){
return this.a <= n-r && n+r <= this.b
}
@@ -80,16 +85,35 @@
return clamp(n, this.a+r, this.b-r)
}
vec2.prototype.intersects = function(v){
- if (this.a < v.a) {
- return (v.a < this.b && this.b <= v.b) || (this.a < v.b && v.b <= this.b)
- }
- else if (this.a == v.a) {
+ if (this.a == v.a || this.b == v.b || this.a == v.b || this.b == v.a) {
return true
}
+ else if (this.a < v.a) {
+ return (v.a < this.b && this.b <= v.b) || (this.a < v.b && v.b <= this.b)
+ }
else if (this.a > v.a) {
return (this.a < v.b && v.b <= this.b) || (v.a < this.b && this.b <= v.b)
}
}
+ vec2.prototype.overlaps = function(v){
+ if (this.a == v.a || this.b == v.b) {
+ return true
+ }
+ if (this.a < v.a) {
+ return (v.a < this.b && this.b < v.b) || (this.a < v.b && v.b < this.b)
+ }
+ else if (v.a < this.a) {
+ return (this.a < v.b && v.b < this.b) || (v.a < this.b && this.b < v.b)
+ }
+ }
+
+
+ vec2.prototype.adjacent = function(v){
+ if (this.a == v.a || this.b == v.b || this.a == v.b || this.b == v.a) {
+ return true
+ }
+ return false
+ }
vec2.prototype.union = function(v){
if (this.intersects(v)) {
return new vec2( min(this.a,v.a), max(this.b, v.b) )
@@ -100,6 +124,56 @@
return new vec2( max(this.a,v.a), min(this.b, v.b) )
}
}
+
+ // given two vectors, test how they overlap
+ // return the set of overlapping segments in the initial vector, labelled with sides
+ vec2.prototype.split = function(v, left, right){
+ var intervals = [], _len
+
+ if (this.eq(v)) {
+ intervals.push([ new vec2( this.a, this.b ), left | right ])
+ }
+
+ // a---A===b---B (rightways overlap)
+ else if (this.contains(v.a) && v.contains(this.b)) {
+ intervals.push([ new vec2( this.a, v.a ), left ])
+ intervals.push([ new vec2( v.a, this.b ), right ])
+ }
+
+ // A---a===B---b (leftways overlap)
+ else if (v.contains(this.a) && this.contains(v.b)) {
+ intervals.push([ new vec2( this.a, v.b ), left ])
+ intervals.push([ new vec2( v.b, this.b ), right ])
+ }
+
+ // a---A===B---b (contains v)
+ else if (this.contains(v.a) && this.contains(v.b)) {
+ intervals.push([ new vec2( this.a, v.a ), left ])
+ intervals.push([ new vec2( v.a, v.b ), 0 ])
+ intervals.push([ new vec2( v.b, this.b ), right ])
+ }
+
+ // A---a===b---B (contained in v)
+ else { // if (v.contains(this.a) && v.contains(v.b)) {
+ intervals.push([ new vec2( this.a, this.b ), left | right ])
+ }
+
+ // cull empty vectors
+ _len = intervals.length
+ if (_len > 1) {
+ if (intervals[0][0].magnitude() == 0) {
+ intervals[1][1] |= intervals[0][1]
+ intervals.shift()
+ }
+ else if (intervals[_len-1][0].magnitude() == 0) {
+ intervals[_len-2][1] |= intervals[_len-1][1]
+ intervals.pop()
+ }
+ }
+
+ return intervals
+ }
+
vec2.prototype.toString = function(){
return "[" + ~~this.a + " " + ~~this.b + "]"
}
@@ -115,8 +189,7 @@
if ('window' in this) {
window.vec2 = vec2
}
- else if ('module' in this) {
+ else {
module.exports = vec2
}
-
-})() \ No newline at end of file
+})()
diff --git a/public/assets/javascripts/rectangles/util/colors.js b/public/assets/javascripts/rectangles/util/colors.js
index c590072..95827cc 100644
--- a/public/assets/javascripts/rectangles/util/colors.js
+++ b/public/assets/javascripts/rectangles/util/colors.js
@@ -3,6 +3,12 @@
alpha: [
"rgba(0,0,0,0.1)",
],
+ alphaQuad: [
+ "rgba(0,0,0,0.1)",
+ "rgba(0,0,0,0.1)",
+ "rgba(0,0,0,0.1)",
+ "rgba(0,0,0,0.1)",
+ ],
redblue: [
"rgba(0,0,0,0.2)",
"rgba(255,0,0,0.2)",
@@ -52,9 +58,9 @@
select.blur()
})
- window.colors = color_palettes[select ? select.value : 'bone']
+ window.colors = color_palettes[select ? select.value : 'alphaQuad']
window.grayColors = {}
- _.zip([FRONT, LEFT, BACK, RIGHT], color_palettes.bone).map(function(pair){
+ _.zip([FRONT, LEFT, BACK, RIGHT], color_palettes.alphaQuad).map(function(pair){
window.grayColors[pair[0]] = pair[1]
})
window.palettes = color_palettes
diff --git a/public/assets/javascripts/rectangles/util/sort.js b/public/assets/javascripts/rectangles/util/sort.js
index a0665ae..3b4771c 100644
--- a/public/assets/javascripts/rectangles/util/sort.js
+++ b/public/assets/javascripts/rectangles/util/sort.js
@@ -1,86 +1,101 @@
+(function(){
+ function compare_rect_position(a,b){
+ if (a[0].x.a < b[0].x.a) {
+ return -1
+ }
+ if (a[0].x.a > b[0].x.a) {
+ return 1
+ }
+ if (a[0].y.a < b[0].y.a) {
+ return -1
+ }
+ if (a[0].y.a > b[0].y.a) {
+ return 1
+ }
+ return 0
+ }
-function compare_rect_position(a,b){
- if (a[0].x.a < b[0].x.a) {
- return -1
+ function compare_car_reversed (a,b){
+ if (a[0] < b[0]) {
+ return 1
+ }
+ if (a[0] > b[0]) {
+ return -1
+ }
+ return 0
}
- if (a[0].x.a > b[0].x.a) {
- return 1
+ function compare_car (a,b){
+ if (a[0] < b[0]) {
+ return -1
+ }
+ if (a[0] > b[0]) {
+ return 1
+ }
+ return 0
}
- if (a[0].y.a < b[0].y.a) {
- return -1
+
+ function room_id_tuple (r){ return [r.id, r] }
+ function room_height_tuple (r){ return [r.height, r] }
+ function room_area_tuple (r){ return [r.rect.area(), r] }
+ function rect_area_tuple (r){ return [r.area(), r] }
+ function rect_area_tuple_larger (r){ return [-r.area(), r] }
+
+ function room_rect_tuple (r){ return [r.rect, r] }
+ function identity_tuple (r){ return [r, r] }
+ function car (r){ return r[0] }
+ function cdr (r){ return r[1] }
+
+ var sort = {}
+
+ sort.rooms_by_id = function (list){
+ return list.map(room_id_tuple)
+ .sort(compare_car)
+ .map(cdr)
}
- if (a[0].y.a > b[0].y.a) {
- return 1
+ sort.rooms_by_height = function (list){
+ return list.map(room_height_tuple)
+ .sort(compare_car_reversed)
+ .map(cdr)
+ }
+ sort.rooms_by_position = function (list){
+ return list.map(room_rect_tuple)
+ .sort(compare_rect_position)
+ .map(cdr)
+ }
+ sort.rooms_by_area = function (list){
+ return list.map(room_area_tuple)
+ .sort(compare_car)
+ .map(cdr)
}
- return 0
-}
-function compare_car_reversed (a,b){
- if (a[0] < b[0]) {
- return 1
+ sort.rects_by_position = function (list){
+ return list.map(identity_tuple)
+ .sort(compare_rect_position)
+ .map(cdr)
}
- if (a[0] > b[0]) {
- return -1
+ sort.rects_by_area = function (list){
+ return list.map(rect_area_tuple)
+ .sort(compare_car)
+ .map(cdr)
}
- return 0
-}
-function compare_car (a,b){
- if (a[0] < b[0]) {
- return -1
+ sort.rects_by_larger_area = function (list){
+ return list.map(rect_area_tuple_larger)
+ .sort(compare_car)
+ .map(cdr)
}
- if (a[0] > b[0]) {
- return 1
+ sort.compare_z = function (a,b){
+ return a.rect.y.a < b.rect.y.a ? -1 : a.rect.y.a == b.rect.y.a ? 0 : 1
+ }
+ sort.compare_x = function (a,b){
+ return a.rect.x.a > b.rect.x.a ? -1 : a.rect.x.a == b.rect.x.a ? 0 : 1
}
- return 0
-}
-
-function room_id_tuple (r){ return [r.id, r] }
-function room_height_tuple (r){ return [r.height, r] }
-function room_area_tuple (r){ return [r.rect.area(), r] }
-function rect_area_tuple (r){ return [r.area(), r] }
-
-function room_rect_tuple (r){ return [r.rect, r] }
-function identity_tuple (r){ return [r, r] }
-function car (r){ return r[0] }
-function cdr (r){ return r[1] }
-
-
-function sort_rooms_by_id(list){
- return list.map(room_id_tuple)
- .sort(compare_car)
- .map(cdr)
-}
-function sort_rooms_by_height(list){
- return list.map(room_height_tuple)
- .sort(compare_car_reversed)
- .map(cdr)
-}
-function sort_rooms_by_position(list){
- return list.map(room_rect_tuple)
- .sort(compare_rect_position)
- .map(cdr)
-}
-function sort_rooms_by_area(list){
- return list.map(room_area_tuple)
- .sort(compare_car)
- .map(cdr)
-}
-function sort_rects_by_position(list){
- return list.map(identity_tuple)
- .sort(compare_rect_position)
- .map(cdr)
-}
-function sort_rects_by_area(list){
- return list.map(rect_area_tuple)
- .sort(compare_car)
- .map(cdr)
-}
+ if ("window" in this) {
+ window.sort = sort
+ }
+ else {
+ module.exports = sort
+ }
-function compare_z(a,b){
- return a.rect.y.a < b.rect.y.a ? -1 : a.rect.y.a == b.rect.y.a ? 0 : 1
-}
-function compare_x(a,b){
- return a.rect.x.a > b.rect.x.a ? -1 : a.rect.x.a == b.rect.x.a ? 0 : 1
-}
+})()
diff --git a/public/assets/javascripts/rectangles/util/uid.js b/public/assets/javascripts/rectangles/util/uid.js
index ca22fb3..0c0b176 100644
--- a/public/assets/javascripts/rectangles/util/uid.js
+++ b/public/assets/javascripts/rectangles/util/uid.js
@@ -1,14 +1,29 @@
+(function(){
-var UidGenerator = function(list){
- var id = 0
- return function(s){
- s = s || ""
- var ss
- while (1) {
- ss = s + (id++)
- if (! (ss in list)) {
- return ss
+ var UidGenerator = function(list){
+ var id = 0
+ var generator = function(s){
+ s = s || ""
+ var ss
+ while (1) {
+ ss = s + (id++)
+ if (! (ss in list)) {
+ return ss
+ }
}
}
+ generator.setList = function(newList){
+ list = newList
+ }
+ return generator
+ }
+
+ if ('window' in this) {
+ window.UidGenerator = UidGenerator
}
-} \ No newline at end of file
+ else {
+ module.exports = UidGenerator
+ }
+
+})()
+
diff --git a/public/assets/javascripts/ui/editor/MediaViewer.js b/public/assets/javascripts/ui/editor/MediaViewer.js
index 264ed7c..5540023 100644
--- a/public/assets/javascripts/ui/editor/MediaViewer.js
+++ b/public/assets/javascripts/ui/editor/MediaViewer.js
@@ -133,10 +133,10 @@ var MediaViewer = ModalView.extend({
var $floatingImg = $('.floatingImg');
Scenery.nextMedia = media
-
+ console.log(media.type)
switch (media.type) {
case "video":
- $floatingImg.attr('src', 'http://www.rawrcast.com/wp-content/uploads/2010/02/BluePlayButton.png')
+ $floatingImg.attr('src', '/assets/img/playbutton.png')
break
default:
diff --git a/public/assets/stylesheets/app.css b/public/assets/stylesheets/app.css
index 9072542..a5b1733 100755
--- a/public/assets/stylesheets/app.css
+++ b/public/assets/stylesheets/app.css
@@ -468,6 +468,8 @@ iframe.embed {
.profilepage .bio span:last-of-type:after { display: none; }
.templates {
+ overflow: auto;
+ max-height: 80%;
}
.no-templates {
display: none;
diff --git a/test/00-setup.js b/test/00-setup.js
new file mode 100644
index 0000000..78ad2c4
--- /dev/null
+++ b/test/00-setup.js
@@ -0,0 +1,2 @@
+Error.stackTraceLimit = 5
+
diff --git a/test/01-test-vec2.js b/test/01-test-vec2.js
new file mode 100644
index 0000000..6104f92
--- /dev/null
+++ b/test/01-test-vec2.js
@@ -0,0 +1,102 @@
+var assert = require("assert")
+var vec2 = require("../public/assets/javascripts/rectangles/models/vec2.js")
+
+describe('vec2', function(){
+ var vec = new vec2(0, 10)
+ describe('#intersects()', function(){
+
+ it('intersects itself', function(){
+ assert.equal(true, vec.intersects( new vec2(0, 10) ));
+ })
+ it('intersects w/ same startpoint (shorter)', function(){
+ assert.equal(true, vec.intersects( new vec2(0, 5) ));
+ })
+ it('intersects w/ same startpoint (longer)', function(){
+ assert.equal(true, vec.intersects( new vec2(0, 15) ));
+ })
+ it('intersects w/ same endpoint (shorter)', function(){
+ assert.equal(true, vec.intersects( new vec2(5, 10) ));
+ })
+ it('intersects w/ same endpoint (longer)', function(){
+ assert.equal(true, vec.intersects( new vec2(-5, 10) ));
+ })
+ it('intersects when contained', function(){
+ assert.equal(true, vec.intersects( new vec2(-5, 15) ));
+ })
+ it('does not intersect when before', function(){
+ assert.equal(false, vec.intersects( new vec2(-10, -5) ));
+ })
+ it('does not intersect when after', function(){
+ assert.equal(false, vec.intersects( new vec2(15, 20) ));
+ })
+ it('intersects when only startpoint matches', function(){
+ assert.equal(true, vec.intersects( new vec2(-5, 0) ));
+ })
+ it('intersects when only endpoint matches', function(){
+ assert.equal(true, vec.intersects( new vec2(10, 15) ));
+ })
+ })
+
+ describe('#contains()', function(){
+ it('contains itself', function(){
+ assert.equal(true, vec.contains( 0 ));
+ assert.equal(true, vec.contains( 5 ));
+ assert.equal(true, vec.contains( 10 ));
+ })
+ it('does not contain before or after', function(){
+ assert.equal(false, vec.contains( -5 ));
+ assert.equal(false, vec.contains( 15 ));
+ })
+ })
+
+ describe('#containsCenter()', function(){
+ it('containsCenter itself', function(){
+ assert.equal(true, vec.containsCenter( 5 ));
+ })
+ it('does not containsCenter its endpoints', function(){
+ assert.equal(false, vec.containsCenter( 0 ));
+ assert.equal(false, vec.containsCenter( 10 ));
+ })
+ })
+
+ describe('#containsDisc()', function(){
+ it('containsDisc within a radius', function(){
+ assert.equal(true, vec.containsDisc( 2, 2 ));
+ assert.equal(true, vec.containsDisc( 5, 2 ));
+ assert.equal(true, vec.containsDisc( 7, 2 ));
+ })
+ it('does not containsDisc its endpoints', function(){
+ assert.equal(false, vec.containsDisc( 0, 2 ));
+ assert.equal(false, vec.containsDisc( 1, 2 ));
+ assert.equal(false, vec.containsDisc( 9, 2 ));
+ assert.equal(false, vec.containsDisc( 10, 2 ));
+ })
+ it('does not containsDisc outside points', function(){
+ assert.equal(false, vec.containsDisc( -5, 2 ));
+ assert.equal(false, vec.containsDisc( -2, 2 ));
+ assert.equal(false, vec.containsDisc( 12, 2 ));
+ assert.equal(false, vec.containsDisc( 15, 2 ));
+ })
+ })
+
+ describe('#clampDisc()', function(){
+ it('clampDisc clamps on the left', function(){
+ assert.equal(2, vec.clampDisc( -1, 2 ));
+ assert.equal(2, vec.clampDisc( 0, 2 ));
+ assert.equal(2, vec.clampDisc( 1, 2 ));
+ assert.equal(2, vec.clampDisc( 2, 2 ));
+ })
+ it('clampDisc clamps on the right', function(){
+ assert.equal(8, vec.clampDisc( 8, 2 ));
+ assert.equal(8, vec.clampDisc( 9, 2 ));
+ assert.equal(8, vec.clampDisc( 10, 2 ));
+ assert.equal(8, vec.clampDisc( 11, 2 ));
+ })
+ it('clampDisc doesnt clamp in the middle', function(){
+ assert.equal(3, vec.clampDisc( 3, 2 ));
+ assert.equal(4, vec.clampDisc( 4, 2 ));
+ assert.equal(5, vec.clampDisc( 5, 2 ));
+ })
+ })
+})
+
diff --git a/test/02-test-rect.js b/test/02-test-rect.js
new file mode 100644
index 0000000..29998da
--- /dev/null
+++ b/test/02-test-rect.js
@@ -0,0 +1,211 @@
+var assert = require("assert")
+var vec = require("../public/assets/javascripts/rectangles/models/vec2.js")
+var Rect = require("../public/assets/javascripts/rectangles/models/rect.js")
+var FRONT = 0x1, BACK = 0x2, LEFT = 0x4, RIGHT = 0x8, FLOOR = 0x10, CEILING = 0x20
+var ALL = FRONT | BACK | LEFT | RIGHT
+
+describe('rect', function(){
+ describe('#intersects()', function(){
+ var rect = new Rect(0, 0, 10, 10)
+
+ it('intersects itself', function(){
+ assert.equal(true, rect.intersects( new Rect(0, 0, 10, 10) ));
+ })
+ it('intersects more wide', function(){
+ assert.equal(true, rect.intersects( new Rect(0, 0, 5, 10) ));
+ })
+ it('intersects less wide', function(){
+ assert.equal(true, rect.intersects( new Rect(0, 0, 15, 10) ));
+ })
+ it('intersects more tall', function(){
+ assert.equal(true, rect.intersects( new Rect(0, 0, 10, 5) ));
+ })
+ it('intersects less tall', function(){
+ assert.equal(true, rect.intersects( new Rect(0, 0, 10, 15) ));
+ })
+ it('intersects if right-adjacent', function(){
+ assert.equal(true, rect.intersects( new Rect(10, 0, 20, 10) ));
+ })
+ it('intersects if bottom-adjacent', function(){
+ assert.equal(true, rect.intersects( new Rect(0, 10, 10, 20) ));
+ })
+ it('does not intersect if to the right', function(){
+ assert.equal(false, rect.intersects( new Rect(20, 0, 40, 10) ));
+ })
+ it('does not intersect if beneath', function(){
+ assert.equal(false, rect.intersects( new Rect(0, 20, 10, 40) ));
+ })
+ /*
+ it('does not intersect if corners intersect', function(){
+ assert.equal(false, rect.intersects( new Rect(10, 10, 20, 20) ));
+ })
+ */
+
+ })
+
+ var rect = new Rect( new vec(1,4), new vec(1,4) )
+
+ var east_in = new Rect( new vec(2,3), new vec(1,4) )
+ var east_edge = new Rect( new vec(2,4), new vec(1,4) )
+ var east = new Rect( new vec(2,5), new vec(1,4) )
+
+ var south_in = new Rect( new vec(1,4), new vec(2,3) )
+ var south_edge = new Rect( new vec(1,4), new vec(2,4) )
+ var south = new Rect( new vec(1,4), new vec(2,5) )
+
+ var corner = new Rect( new vec(3,5), new vec(3,5) )
+
+ function sides (s) {
+ return s.reduce(function(prev, curr){
+ return prev | curr.sides
+ }, 0)
+ }
+ /*
+ console.log(s0.map(function(r){ return r.toString() }))
+ console.log(s1.map(function(r){ return r.toString() }))
+ */
+
+ describe('#split(rect, east)', function(){
+ var s0 = rect.split(east)
+ var s1 = east.split(rect)
+
+ it('splits on all 4 sides', function(){
+ assert.equal(ALL, sides(s0) | sides(s1))
+ })
+ it('rect is front/back/left', function(){
+ assert.equal(FRONT | BACK | LEFT, sides(s0))
+ })
+ it('east is front/back/right', function(){
+ assert.equal(FRONT | BACK | RIGHT, sides(s1))
+ })
+ })
+
+ describe('#split(rect, east_in)', function(){
+ var s0 = rect.split(east_in)
+ var s1 = east_in.split(rect)
+ it('splits on all 4 sides', function(){
+ assert.equal(ALL, sides(s0) | sides(s1))
+ })
+ it('rect is has all sides', function(){
+ assert.equal(ALL, sides(s0))
+ })
+ it('east_in only has front/back', function(){
+ assert.equal(FRONT | BACK, sides(s1))
+ })
+ })
+
+ describe('#split(rect, east_edge)', function(){
+ var s0 = rect.split(east_edge)
+ var s1 = east_edge.split(rect)
+
+ it('has no degenerate vectors', function(){
+ s0.forEach(function(r){
+ assert.notEqual(0, r.x.magnitude())
+ assert.notEqual(0, r.y.magnitude())
+ })
+ s1.forEach(function(r){
+ assert.notEqual(0, r.x.magnitude())
+ assert.notEqual(0, r.y.magnitude())
+ })
+ })
+ it('splits on all 4 sides', function(){
+ assert.equal(ALL, sides(s0) | sides(s1))
+ })
+ it('rect is front/back/left', function(){
+ assert.equal(ALL, sides(s0))
+ })
+ it('east is front/back/right', function(){
+ assert.equal(FRONT | BACK | RIGHT, sides(s1))
+ })
+ })
+
+ describe('#split(rect, south)', function(){
+ var s0 = rect.split(south)
+ var s1 = south.split(rect)
+
+ it('splits on all 4 sides', function(){
+ assert.equal(ALL, sides(s0) | sides(s1))
+ })
+ it('rect is front/left/right', function(){
+ assert.equal(FRONT | LEFT | RIGHT, sides(s0))
+ })
+ it('south is back/left/right', function(){
+ assert.equal(BACK | LEFT | RIGHT, sides(s1))
+ })
+ })
+
+ describe('#split(rect, south_in)', function(){
+ var s0 = rect.split(south_in)
+ var s1 = south_in.split(rect)
+ it('splits on all 4 sides', function(){
+ assert.equal(ALL, sides(s0) | sides(s1))
+ })
+ it('rect is has all sides', function(){
+ assert.equal(ALL, sides(s0))
+ })
+ it('south_in only has left/right', function(){
+ assert.equal(LEFT | RIGHT, sides(s1))
+ })
+ })
+
+ describe('#split(rect, south_edge)', function(){
+ var s0 = rect.split(south_edge)
+ var s1 = south_edge.split(rect)
+
+ it('has no degenerate vectors', function(){
+ s0.forEach(function(r){
+ assert.notEqual(0, r.x.magnitude())
+ assert.notEqual(0, r.y.magnitude())
+ })
+ s1.forEach(function(r){
+ assert.notEqual(0, r.x.magnitude())
+ assert.notEqual(0, r.y.magnitude())
+ })
+ })
+ it('splits on all 4 sides', function(){
+ assert.equal(ALL, sides(s0) | sides(s1))
+ })
+ it('rect has all sides', function(){
+ assert.equal(ALL, sides(s0))
+ })
+ it('south is back/left/right', function(){
+ assert.equal(BACK | LEFT | RIGHT, sides(s1))
+ })
+ })
+
+ describe('#split(rect, corner)', function(){
+ var s0 = rect.split(corner)
+ var s1 = corner.split(rect)
+
+ it('rect splits on all 4 sides', function(){
+ assert.equal(ALL, sides(s0))
+ })
+ it('corner splits on all 4 sides', function(){
+ assert.equal(ALL, sides(s1))
+ })
+
+ var rect_map = {}
+ var corner_map = {}
+ var rect_state = s0.forEach(function(r){
+ rect_map[r.sides] = rect_map[r.sides] || []
+ rect_map[r.sides].push(r)
+ })
+ var corner_state = s1.forEach(function(r){
+ corner_map[r.sides] = corner_map[r.sides] || []
+ corner_map[r.sides].push(r)
+ })
+
+ it('rect contains a rect with no sides', function(){
+ assert.equal(1, rect_map[0].length)
+ })
+ it('corner contains a rect with no sides', function(){
+ assert.equal(1, corner_map[0].length)
+ })
+ it('rect and corner overlap', function(){
+ assert.equal(String(rect_map[0][0]), String(corner_map[0][0]))
+ })
+
+ })
+
+})
+
diff --git a/test/03-test-clipper.js b/test/03-test-clipper.js
new file mode 100644
index 0000000..923a8a5
--- /dev/null
+++ b/test/03-test-clipper.js
@@ -0,0 +1,129 @@
+var assert = require("assert")
+var vec = require("../public/assets/javascripts/rectangles/models/vec2.js")
+var Rect = require("../public/assets/javascripts/rectangles/models/rect.js")
+var Room = require("../public/assets/javascripts/rectangles/models/room.js")
+var Rooms = require("../public/assets/javascripts/rectangles/engine/rooms/_rooms.js")
+var Clipper = require("../public/assets/javascripts/rectangles/engine/rooms/clipper.js")
+var FRONT = 0x1, BACK = 0x2, LEFT = 0x4, RIGHT = 0x8, FLOOR = 0x10, CEILING = 0x20
+var ALL = FRONT | BACK | LEFT | RIGHT
+
+var rect = new Rect( new vec(1,5), new vec(1,5) )
+var east = new Rect( new vec(2,6), new vec(1,5) )
+var corner = new Rect( new vec(3,7), new vec(3,7) )
+var peninsula = new Rect( new vec(4,6), new vec(6,8) )
+
+function report(a) {
+ console.log( a.join("\n") )
+}
+
+describe('clipper', function(){
+ Rooms.list = {}
+ Rooms.regions = []
+ Rooms.add_with_rect( rect )
+ Rooms.add_with_rect( east )
+
+ describe('#clip_rects(rect, east)', function(){
+ Rooms.clipper.reset_rects()
+ var regions = Rooms.clipper.clip_rects()
+
+ it('contains duplicates', function(){
+ var map = {}
+ var state = regions.some(function(a){
+ var s = a.toString()
+ if (s in map) return true
+ map[s] = s
+ return false
+ })
+ assert.equal(true, state)
+ })
+ })
+
+ describe('#cull_rects(rect, east)', function(){
+ Rooms.clipper.reset_rects()
+ var regions = Rooms.clipper.clip_rects()
+ var culled = Rooms.clipper.cull_rects_iterative()
+ var culled_dupes = regions.filter(function(r){ return r.dupe })
+
+ it('clipper returns 4 rects', function(){
+ assert.equal(4, regions.length)
+ })
+ it('culling marks 1 duplicate', function(){
+ assert.equal(1, culled_dupes.length)
+ })
+ it('culling returns 3 non-duplicate', function(){
+ assert.equal(3, culled.length)
+ })
+ })
+
+ //
+
+ Rooms.list = {}
+ Rooms.regions = []
+ Rooms.add_with_rect( rect )
+ Rooms.add_with_rect( corner )
+
+ describe('#cull_rects(rect, corner)', function(){
+ Rooms.clipper.reset_rects()
+ var regions = Rooms.clipper.clip_rects()
+ var culled = Rooms.clipper.cull_rects_iterative()
+ var culled_dupes = regions.filter(function(r){ return r.dupe })
+
+ it('clipper returns 8 rects', function(){
+ assert.equal(8, regions.length)
+ })
+ it('culling marks 1 duplicate', function(){
+ assert.equal(1, culled_dupes.length)
+ })
+ it('culling returns 7 non-duplicate', function(){
+ assert.equal(7, culled.length)
+ })
+ })
+
+ //
+
+ Rooms.list = {}
+ Rooms.regions = []
+ Rooms.add_with_rect( rect )
+ Rooms.add_with_rect( corner )
+ Rooms.add_with_rect( peninsula )
+
+/*
+ // this method uses a tree to match areas, which tends to leave extra overlapping regions
+ describe('#cull_rects(rect, corner, peninsula)', function(){
+ Rooms.clipper.reset_rects()
+ var regions = Rooms.clipper.clip_rects()
+ var culled = Rooms.clipper.cull_rects()
+ var culled_dupes = regions.filter(function(r){ return r.dupe })
+
+ it('clipper returns 16 rects', function(){
+ assert.equal(16, regions.length)
+ })
+ it('culling marks 3 duplicate', function(){
+ assert.equal(3, culled_dupes.length)
+ })
+ it('culling returns 13 non-duplicate', function(){
+ assert.equal(13, regions.length)
+ })
+ })
+*/
+
+ // this method iterates to match areas, which omits regions in some cases
+ describe('#cull_rects_iterative(rect, corner, peninsula)', function(){
+ Rooms.clipper.reset_rects()
+ var regions = Rooms.clipper.clip_rects()
+ var culled = Rooms.clipper.cull_rects_iterative()
+ var culled_dupes = regions.filter(function(r){ return r.dupe })
+
+ it('clipper returns 16 rects', function(){
+ assert.equal(16, regions.length)
+ })
+ it('culling marks 3 duplicate', function(){
+ assert.equal(3, culled_dupes.length)
+ })
+ it('culling returns 14 non-duplicate', function(){
+ assert.equal(13, culled.length)
+ })
+ })
+
+})
+
diff --git a/test/04-test-builder.js b/test/04-test-builder.js
new file mode 100644
index 0000000..fa624f8
--- /dev/null
+++ b/test/04-test-builder.js
@@ -0,0 +1,260 @@
+var assert = require("assert")
+var vec = require("../public/assets/javascripts/rectangles/models/vec2.js")
+var Rect = require("../public/assets/javascripts/rectangles/models/rect.js")
+var Room = require("../public/assets/javascripts/rectangles/models/room.js")
+var Rooms = require("../public/assets/javascripts/rectangles/engine/rooms/_rooms.js")
+var Clipper = require("../public/assets/javascripts/rectangles/engine/rooms/clipper.js")
+var Builder = require("../public/assets/javascripts/rectangles/engine/rooms/builder.js")
+var FRONT = 0x1, BACK = 0x2, LEFT = 0x4, RIGHT = 0x8, FLOOR = 0x10, CEILING = 0x20
+var ALL = FRONT | BACK | LEFT | RIGHT
+function sidesToString(sides){
+ var s = ""
+ if (sides & FRONT) s += "front "
+ if (sides & BACK) s += "back "
+ if (sides & LEFT) s += "left "
+ if (sides & RIGHT) s += "right "
+ if (sides & TOP) s += "top "
+ if (sides & BOTTOM) s += "bottom "
+ return s
+}
+function bitcount(v) {
+ v = v - ((v >>> 1) & 0x55555555);
+ v = (v & 0x33333333) + ((v >>> 2) & 0x33333333);
+ return ((v + (v >>> 4) & 0xF0F0F0F) * 0x1010101) >>> 24;
+}
+
+var rect = new Rect( new vec(1,5), new vec(1,5) )
+var east = new Rect( new vec(2,6), new vec(1,5) )
+var corner = new Rect( new vec(3,7), new vec(3,7) )
+var peninsula = new Rect( new vec(4,6), new vec(6,8) )
+
+var rect_room = new Room({ id: "rect", rect: rect, height: 2 })
+var east_room = new Room({ id: "east", rect: east, height: 2 })
+var corner_room = new Room({ id: "corner", rect: corner, height: 2 })
+var peninsula_room = new Room({ id: "peninsula", rect: peninsula, height: 2 })
+
+var taller_room = new Room({ id: "taller", rect: rect, height: 3 })
+
+function report(a) {
+ console.log( a.join("\n") )
+}
+function reportSides(walls) {
+ console.log(walls.map(function(w){ return sidesToString(w.side) }).join("\n"))
+}
+function count_wall_sides (wall_groups) {
+ var wall_sides = {}
+ wall_sides[LEFT] = 0
+ wall_sides[RIGHT] = 0
+ wall_sides[FRONT] = 0
+ wall_sides[BACK] = 0
+ wall_sides[TOP] = 0
+ wall_sides[BOTTOM] = 0
+ wall_sides.total = 0
+ wall_groups.map(function(walls){
+ walls.forEach(function(wall){
+ wall_sides[wall.side] += 1
+ wall_sides.total += 1
+ })
+ })
+ return wall_sides
+}
+function reset(){
+ Rooms.forEach(function(room){
+ room.reset()
+ })
+ Rooms.list = {}
+ Rooms.regions = []
+}
+function rebuild(){
+ Rooms.clipper.reset_rects()
+ var regions = Rooms.clipper.clip_rects()
+ var culled = Rooms.clipper.cull_rects_iterative()
+ return culled
+}
+
+describe('builder', function(){
+
+ reset()
+ Rooms.add( rect_room )
+ rebuild()
+
+ describe('#build_walls(rect)', function(){
+ var walls = Rooms.builder.build_walls(rect_room.regions[0])
+
+ it("should return 4 walls", function(){
+ assert.equal(4, walls.length)
+ })
+ it("should have one side per wall", function(){
+ assert.equal(1, bitcount(walls[0].side))
+ assert.equal(1, bitcount(walls[1].side))
+ assert.equal(1, bitcount(walls[2].side))
+ assert.equal(1, bitcount(walls[3].side))
+ })
+ })
+
+ describe('#build_floors(rect)', function(){
+ var floors = Rooms.builder.build_floors(rect_room)
+ it("should make 2 floors", function(){
+ assert.equal(2, floors.length)
+ })
+ })
+
+ // rect vs east
+
+ reset()
+ Rooms.add( rect_room )
+ Rooms.add( east_room )
+ var regions = rebuild()
+
+ describe('#build_walls(rect, east)', function(){
+
+ var wall_groups = regions.map(Rooms.builder.build_walls.bind(Rooms.builder))
+ var wall_sides = count_wall_sides(wall_groups)
+
+ // reportSides(w); console.log("--")
+
+ it("should return 8 walls", function(){
+ assert.equal(8, wall_sides.total)
+ })
+ it("should have 3 front walls", function(){
+ assert.equal(3, wall_sides[FRONT])
+ })
+ it("should have 3 back walls", function(){
+ assert.equal(3, wall_sides[BACK])
+ })
+ it("should have 1 left wall", function(){
+ assert.equal(1, wall_sides[LEFT])
+ })
+ it("should have 1 right wall", function(){
+ assert.equal(1, wall_sides[RIGHT])
+ })
+ })
+
+ describe('#build_floors(rect, east)', function(){
+ var fg = Rooms.builder.build_floors(rect_room)
+ var fg2 = Rooms.builder.build_floors(east_room)
+ var fg_floors = fg.filter(function(r){ return ! r.half_side })
+ var fg_halves = fg.filter(function(r){ return r.half_side })
+ var fg2_floors = fg2.filter(function(r){ return ! r.half_side })
+ var fg2_halves = fg2.filter(function(r){ return r.half_side })
+
+ it("should make 4 floors", function(){
+ assert.equal(2, fg_floors.length)
+ assert.equal(2, fg2_floors.length)
+ })
+ it("should make 0 half-walls", function(){
+ assert.equal(0, fg_halves.length)
+ assert.equal(0, fg2_halves.length)
+ })
+ })
+
+ // rect vs corner
+
+ reset()
+ Rooms.add( rect_room )
+ Rooms.add( corner_room )
+ var regions = rebuild()
+
+ describe('#build_walls(rect, corner)', function(){
+
+ var wall_groups = regions.map(Rooms.builder.build_walls.bind(Rooms.builder))
+ var wall_sides = count_wall_sides(wall_groups)
+
+ // reportSides(w); console.log("--")
+
+ it("should return 12 walls", function(){
+ assert.equal(12, wall_sides.total)
+ })
+ it("should have 3 front walls", function(){
+ assert.equal(3, wall_sides[FRONT])
+ })
+ it("should have 3 back walls", function(){
+ assert.equal(3, wall_sides[BACK])
+ })
+ it("should have 3 left walls", function(){
+ assert.equal(3, wall_sides[LEFT])
+ })
+ it("should have 3 right walls", function(){
+ assert.equal(3, wall_sides[RIGHT])
+ })
+ })
+
+ describe('#build_floors(rect, corner)', function(){
+ var fg = Rooms.builder.build_floors(rect_room)
+ var fg2 = Rooms.builder.build_floors(corner_room)
+ var fg_floors = fg.filter(function(r){ return ! r.half_side })
+ var fg_halves = fg.filter(function(r){ return r.half_side })
+ var fg2_floors = fg2.filter(function(r){ return ! r.half_side })
+ var fg2_halves = fg2.filter(function(r){ return r.half_side })
+
+ it("should make 8 floors", function(){
+ assert.equal(2, fg_floors.length)
+ assert.equal(6, fg2_floors.length)
+ })
+ it("should make 0 half-walls", function(){
+ assert.equal(0, fg_halves.length)
+ assert.equal(0, fg2_halves.length)
+ })
+ })
+
+ // taller (rect) vs east
+
+ reset()
+ Rooms.add( taller_room )
+ Rooms.add( east_room )
+ var regions = rebuild()
+
+ describe('#build_floors(taller, east)', function(){
+ var fg = Rooms.builder.build_floors(taller_room)
+ var fg2 = Rooms.builder.build_floors(east_room)
+ var fg_floors = fg.filter(function(r){ return ! r.half_side })
+ var fg_halves = fg.filter(function(r){ return r.half_side })
+ var fg2_floors = fg2.filter(function(r){ return ! r.half_side })
+ var fg2_halves = fg2.filter(function(r){ return r.half_side })
+
+ it("should make 4 floors", function(){
+ assert.equal(2, fg_floors.length)
+ assert.equal(2, fg2_floors.length)
+ })
+ it("should make 3 half-walls", function(){
+ assert.equal(0, fg_halves.length)
+ assert.equal(3, fg2_halves.length)
+ })
+ })
+
+ // taller vs corner
+
+ reset()
+ Rooms.add( taller_room )
+ Rooms.add( corner_room )
+ var regions = rebuild()
+
+ describe('#build_floors(taller, corner)', function(){
+ var fg = Rooms.builder.build_floors(taller_room)
+ var fg2 = Rooms.builder.build_floors(corner_room)
+ var fg_floors = fg.filter(function(r){ return ! r.half_side })
+ var fg_halves = fg.filter(function(r){ return r.half_side })
+ var fg2_floors = fg2.filter(function(r){ return ! r.half_side })
+ var fg2_halves = fg2.filter(function(r){ return r.half_side })
+
+ it("should make 8 floors", function(){
+ assert.equal(2, fg_floors.length)
+ assert.equal(6, fg2_floors.length)
+ })
+ it("should make 2 half-walls", function(){
+ assert.equal(0, fg_halves.length)
+ assert.equal(2, fg2_halves.length)
+ })
+ })
+
+ // TODO ... test half-wall generation on rooms intersecting each other crossways:
+ // right now this generates extra half-walls from cross on the box side of bulge
+ // this could be fixed by cropping regions during the cull stage?
+/*
+ var box = new Rect( new vec(1,6), new vec(1,5) )
+ var bulge = new Rect( new vec(2,5), new vec(4,6) )
+ var cross = new Rect( new vec(3,4), new vec(3,7) )
+*/
+
+})
+
diff --git a/test/05-test-mover.js b/test/05-test-mover.js
new file mode 100644
index 0000000..42c8653
--- /dev/null
+++ b/test/05-test-mover.js
@@ -0,0 +1,74 @@
+var assert = require("assert")
+var vec = require("../public/assets/javascripts/rectangles/models/vec2.js")
+var Rect = require("../public/assets/javascripts/rectangles/models/rect.js")
+var Room = require("../public/assets/javascripts/rectangles/models/room.js")
+var Rooms = require("../public/assets/javascripts/rectangles/engine/rooms/_rooms.js")
+var Clipper = require("../public/assets/javascripts/rectangles/engine/rooms/clipper.js")
+var Builder = require("../public/assets/javascripts/rectangles/engine/rooms/builder.js")
+var FRONT = 0x1, BACK = 0x2, LEFT = 0x4, RIGHT = 0x8, FLOOR = 0x10, CEILING = 0x20
+var ALL = FRONT | BACK | LEFT | RIGHT
+var NONE = 0x0
+function sidesToString(sides){
+ var s = ""
+ if (sides & FRONT) s += "front "
+ if (sides & BACK) s += "back "
+ if (sides & LEFT) s += "left "
+ if (sides & RIGHT) s += "right "
+ if (sides & TOP) s += "top "
+ if (sides & BOTTOM) s += "bottom "
+ return s
+}
+
+var radius = 1
+
+var rect = new Rect( new vec(1,5), new vec(1,5) )
+var east = new Rect( new vec(2,6), new vec(1,5) )
+var corner = new Rect( new vec(3,7), new vec(3,7) )
+var peninsula = new Rect( new vec(4,6), new vec(6,8) )
+
+var rect_room = new Room({ id: "rect", rect: rect, height: 2 })
+var east_room = new Room({ id: "east", rect: east, height: 2 })
+var corner_room = new Room({ id: "corner", rect: corner, height: 2 })
+var peninsula_room = new Room({ id: "peninsula", rect: peninsula, height: 2 })
+
+function reset(){
+ Rooms.forEach(function(room){
+ room.reset()
+ })
+ Rooms.list = {}
+ Rooms.regions = []
+}
+function rebuild(){
+ Rooms.clipper.reset_rects()
+ var regions = Rooms.clipper.clip_rects()
+ var culled = Rooms.clipper.cull_rects_iterative()
+ return culled
+}
+
+describe('mover', function(){
+ reset()
+ Rooms.add( rect_room )
+ rebuild()
+
+ describe('room#collidesDisc()', function(){
+ var room = rect_room
+ it("should not collide in the center", function(){
+ assert.equal(NONE, room.collidesDisc(3, 3, radius))
+ assert.equal(NONE, room.collidesDisc(2, 2, radius))
+ assert.equal(NONE, room.collidesDisc(4, 4, radius))
+ })
+ it("should collide on the sides", function(){
+ assert.equal(FRONT, room.collidesDisc(3, 1, radius))
+ assert.equal(BACK, room.collidesDisc(3, 5, radius))
+ assert.equal(LEFT, room.collidesDisc(1, 3, radius))
+ assert.equal(RIGHT, room.collidesDisc(5, 3, radius))
+ })
+ it("should collide in the corners", function(){
+ assert.equal(LEFT | FRONT, room.collidesDisc(1, 1, radius))
+ assert.equal(RIGHT | FRONT, room.collidesDisc(5, 1, radius))
+ assert.equal(LEFT | BACK, room.collidesDisc(1, 5, radius))
+ assert.equal(RIGHT | BACK, room.collidesDisc(5, 5, radius))
+ })
+ })
+})
+
diff --git a/test/mocks/mx.js b/test/mocks/mx.js
new file mode 100644
index 0000000..2a76ffc
--- /dev/null
+++ b/test/mocks/mx.js
@@ -0,0 +1,22 @@
+// Non-DOM-dependent stub MX library
+// Used for testing code that builds MX elements, without a DOM dependency
+
+var MX = module.exports = {}
+
+MX.Object3D = function (klass) {
+ this.klass = klass
+ this.width = this.height = this.scaleX = this.scaleY = this.scaleZ = 1
+ this.z = this.y = this.x = 0
+ this.side = 0
+ this.type = "Face"
+ this.el = { style: {} }
+ this.rect = null
+}
+
+MX.Scene = {
+ els: [],
+ add: function (el) {
+ els.push(el)
+ }
+}
+
diff --git a/views/partials/scripts.ejs b/views/partials/scripts.ejs
index 0dea452..0133ad0 100644
--- a/views/partials/scripts.ejs
+++ b/views/partials/scripts.ejs
@@ -9,6 +9,14 @@
<script type="text/javascript" src="/assets/javascripts/vendor/dataUriToBlob.js"></script>
<script type="text/javascript" src="/assets/javascripts/util.js"></script>
+<script type="text/javascript" src="/assets/javascripts/mx/mx.js"></script>
+<script type="text/javascript" src="/assets/javascripts/mx/extensions/mx.scene.js"></script>
+<script type="text/javascript" src="/assets/javascripts/mx/extensions/mx.movements.js"></script>
+<script type="text/javascript" src="/assets/javascripts/mx/primitives/mx.image.js"></script>
+<script type="text/javascript" src="/assets/javascripts/mx/primitives/mx.video.js"></script>
+<script type="text/javascript" src="/assets/javascripts/mx/primitives/mx.youtube.js"></script>
+<script type="text/javascript" src="/assets/javascripts/mx/primitives/mx.vimeo.js"></script>
+
<script type="text/javascript" src="/assets/javascripts/rectangles/_env.js"></script>
<script type="text/javascript" src="/assets/javascripts/rectangles/util/constants.js"></script>
<script type="text/javascript" src="/assets/javascripts/rectangles/util/colors.js"></script>
@@ -19,13 +27,13 @@
<script type="text/javascript" src="/assets/javascripts/rectangles/util/wheel.js"></script>
<script type="text/javascript" src="/assets/javascripts/rectangles/util/mouse.js"></script>
<script type="text/javascript" src="/assets/javascripts/rectangles/util/keys.js"></script>
-<script type="text/javascript" src="/assets/javascripts/rectangles/models/room.js"></script>
-<script type="text/javascript" src="/assets/javascripts/rectangles/models/wall.js"></script>
-<script type="text/javascript" src="/assets/javascripts/rectangles/models/tree.js"></script>
-<script type="text/javascript" src="/assets/javascripts/rectangles/models/rect.js"></script>
<script type="text/javascript" src="/assets/javascripts/rectangles/models/vec2.js"></script>
<script type="text/javascript" src="/assets/javascripts/rectangles/models/vec3.js"></script>
<script type="text/javascript" src="/assets/javascripts/rectangles/models/mat4.js"></script>
+<script type="text/javascript" src="/assets/javascripts/rectangles/models/rect.js"></script>
+<script type="text/javascript" src="/assets/javascripts/rectangles/models/tree.js"></script>
+<script type="text/javascript" src="/assets/javascripts/rectangles/models/room.js"></script>
+<script type="text/javascript" src="/assets/javascripts/rectangles/models/wall.js"></script>
<script type="text/javascript" src="/assets/javascripts/rectangles/engine/rooms/_rooms.js"></script>
<script type="text/javascript" src="/assets/javascripts/rectangles/engine/rooms/builder.js"></script>
@@ -44,14 +52,6 @@
<script type="text/javascript" src="/assets/javascripts/rectangles/engine/map/ui_minimap.js"></script>
<script type="text/javascript" src="/assets/javascripts/rectangles/engine/map/draw.js"></script>
-<script type="text/javascript" src="/assets/javascripts/mx/mx.js"></script>
-<script type="text/javascript" src="/assets/javascripts/mx/extensions/mx.scene.js"></script>
-<script type="text/javascript" src="/assets/javascripts/mx/extensions/mx.movements.js"></script>
-<script type="text/javascript" src="/assets/javascripts/mx/primitives/mx.image.js"></script>
-<script type="text/javascript" src="/assets/javascripts/mx/primitives/mx.video.js"></script>
-<script type="text/javascript" src="/assets/javascripts/mx/primitives/mx.youtube.js"></script>
-<script type="text/javascript" src="/assets/javascripts/mx/primitives/mx.vimeo.js"></script>
-
<script type="text/javascript" src="/assets/javascripts/ui/lib/View.js"></script>
<script type="text/javascript" src="/assets/javascripts/ui/lib/Router.js"></script>
<script type="text/javascript" src="/assets/javascripts/ui/lib/ModalView.js"></script>