summaryrefslogtreecommitdiff
path: root/public/assets/javascripts/rectangles
diff options
context:
space:
mode:
Diffstat (limited to 'public/assets/javascripts/rectangles')
-rw-r--r--public/assets/javascripts/rectangles/_env.js35
-rw-r--r--public/assets/javascripts/rectangles/engine/map/_map.js21
-rw-r--r--public/assets/javascripts/rectangles/engine/map/draw.js104
-rw-r--r--public/assets/javascripts/rectangles/engine/map/tools/_base.js11
-rw-r--r--public/assets/javascripts/rectangles/engine/map/tools/arrow.js83
-rw-r--r--public/assets/javascripts/rectangles/engine/map/tools/eraser.js29
-rw-r--r--public/assets/javascripts/rectangles/engine/map/tools/line.js63
-rw-r--r--public/assets/javascripts/rectangles/engine/map/tools/ortho.js118
-rw-r--r--public/assets/javascripts/rectangles/engine/map/tools/polyline.js73
-rw-r--r--public/assets/javascripts/rectangles/engine/map/tools/position.js19
-rw-r--r--public/assets/javascripts/rectangles/engine/map/tools/start.js29
-rw-r--r--public/assets/javascripts/rectangles/engine/map/ui/editor.js (renamed from public/assets/javascripts/rectangles/engine/map/ui_editor.js)8
-rw-r--r--public/assets/javascripts/rectangles/engine/map/ui/minimap.js (renamed from public/assets/javascripts/rectangles/engine/map/ui_minimap.js)0
-rw-r--r--public/assets/javascripts/rectangles/engine/map/ui/ortho.js107
-rw-r--r--public/assets/javascripts/rectangles/engine/rooms/_rooms.js41
-rw-r--r--public/assets/javascripts/rectangles/engine/scenery/_scenery.js30
-rw-r--r--public/assets/javascripts/rectangles/engine/scenery/move.js8
-rw-r--r--public/assets/javascripts/rectangles/engine/scenery/resize.js5
-rw-r--r--public/assets/javascripts/rectangles/engine/scenery/sound.js16
-rw-r--r--public/assets/javascripts/rectangles/engine/scenery/types/_object.js4
-rw-r--r--public/assets/javascripts/rectangles/engine/scenery/types/audio.js4
-rw-r--r--public/assets/javascripts/rectangles/engine/scenery/types/video.js11
-rw-r--r--public/assets/javascripts/rectangles/engine/scenery/undo.js65
-rw-r--r--public/assets/javascripts/rectangles/engine/sculpture/_sculpture.js105
-rw-r--r--public/assets/javascripts/rectangles/engine/sculpture/move.js111
-rw-r--r--public/assets/javascripts/rectangles/engine/sculpture/resize.js208
-rw-r--r--public/assets/javascripts/rectangles/engine/sculpture/types/_object.js176
-rw-r--r--public/assets/javascripts/rectangles/engine/sculpture/types/image.js38
-rw-r--r--public/assets/javascripts/rectangles/engine/shapes/ortho.js66
-rw-r--r--public/assets/javascripts/rectangles/engine/shapes/polyline.js212
-rw-r--r--public/assets/javascripts/rectangles/engine/shapes/regionlist.js240
-rw-r--r--public/assets/javascripts/rectangles/engine/shapes/shapelist.js124
-rw-r--r--public/assets/javascripts/rectangles/models/floor.js41
-rw-r--r--public/assets/javascripts/rectangles/models/rect.js12
-rw-r--r--public/assets/javascripts/rectangles/models/room.js7
-rw-r--r--public/assets/javascripts/rectangles/models/vec2.js18
-rw-r--r--public/assets/javascripts/rectangles/models/vec3.js25
-rw-r--r--public/assets/javascripts/rectangles/models/wall.js14
-rw-r--r--public/assets/javascripts/rectangles/util/constants.js5
-rw-r--r--public/assets/javascripts/rectangles/util/coords.js2
-rw-r--r--public/assets/javascripts/rectangles/util/measurement.js4
-rw-r--r--public/assets/javascripts/rectangles/util/mouse.js10
-rw-r--r--public/assets/javascripts/rectangles/util/wheel.js10
43 files changed, 2244 insertions, 68 deletions
diff --git a/public/assets/javascripts/rectangles/_env.js b/public/assets/javascripts/rectangles/_env.js
index 3221bac..b3c7d66 100644
--- a/public/assets/javascripts/rectangles/_env.js
+++ b/public/assets/javascripts/rectangles/_env.js
@@ -1,6 +1,22 @@
var environment = new function(){}
environment.init = function(){
+ scene = new MX.Scene().addTo('#scene')
+ scene.width = window.innerWidth
+ scene.height = window.innerHeight
+ scene.perspective = window.innerHeight
+
+ cam = scene.camera
+ cam.y = viewHeight
+
+ if (is_mobile) {
+ app.movements = new MX.MobileMovements(cam, viewHeight)
+ }
+ else {
+ app.movements = new MX.Movements(cam, viewHeight)
+ }
+ app.movements.init()
+
map = new Map ()
if (window.scene) {
@@ -16,10 +32,18 @@ environment.init = function(){
scene.camera.radius = 20
}
-
+
+ window.onresize = function () {
+ scene.width = window.innerWidth
+ scene.height = window.innerHeight
+ scene.perspective = window.innerHeight
+ scene.update()
+ }
+
Rooms.init()
Walls.init()
Scenery.init()
+ Sculpture.init()
scene.update()
environment.update()
@@ -51,8 +75,13 @@ environment.init = function(){
}
})
}
-environment.update = function(t){
+environment.minimal = function(){
+ environment.update = function(t){}
+}
+environment.update = function(t, dt){
+ app.movements.update(dt || 0)
+ scene.update()
map.update()
- window.minimap && window.minimap.update && minimap.update()
+ window.minimap && minimap.update && minimap.update()
z = false
}
diff --git a/public/assets/javascripts/rectangles/engine/map/_map.js b/public/assets/javascripts/rectangles/engine/map/_map.js
index 202803a..6492db6 100644
--- a/public/assets/javascripts/rectangles/engine/map/_map.js
+++ b/public/assets/javascripts/rectangles/engine/map/_map.js
@@ -16,6 +16,7 @@ var Map = function(opt){
var base = this
base.el = opt.el
base.$el = $(base.el)
+ base.opt = opt
if (! base.el) return
@@ -33,7 +34,6 @@ var Map = function(opt){
scene.camera.x + sides.a, scene.camera.z + sides.b )
}
-
base.set_zoom = function (n) {
n = clamp(n, opt.zoom_min, opt.zoom_max)
base.zoom_exponent = n
@@ -44,12 +44,21 @@ var Map = function(opt){
var canvas = base.canvas = document.createElement("canvas")
canvas.width = base.dimensions.a
canvas.height = base.dimensions.b
-
+
base.el.appendChild(canvas)
switch (opt.type) {
+ case "ortho":
+ base.draw = new Map.Draw (base, { ortho: true })
+ base.draw.grid_size = MAP_GRID_SIZE
+ base.ui = new Map.UI.Ortho (base)
+ base.sides = base.sides_for_center
+ $(window).resize(base.resize)
+ break
+
case "editor":
base.draw = new Map.Draw (base)
+ base.draw.grid_size = MAP_GRID_SIZE
base.ui = new Map.UI.Editor (base)
base.sides = base.sides_for_center
$(window).resize(base.resize)
@@ -57,14 +66,16 @@ var Map = function(opt){
case "minimap":
base.draw = new Map.Draw (base, { center: scene.camera, minimap: true })
+ base.draw.grid_size = MAP_GRID_SIZE * 10
base.ui = new Map.UI.Minimap (base)
base.sides = base.sides_for_camera
break
}
- base.resize = function(){
- canvas.width = base.dimensions.a = window.innerWidth
- canvas.height = base.dimensions.b = window.innerHeight
+ base.resize = function(w, h){
+ canvas.width = base.dimensions.a = w || window.innerWidth
+ canvas.height = base.dimensions.b = h || window.innerHeight
+ // resize here - esp if 2d-hires
}
base.toggle = function(state){
diff --git a/public/assets/javascripts/rectangles/engine/map/draw.js b/public/assets/javascripts/rectangles/engine/map/draw.js
index eceda3c..4f4759f 100644
--- a/public/assets/javascripts/rectangles/engine/map/draw.js
+++ b/public/assets/javascripts/rectangles/engine/map/draw.js
@@ -5,24 +5,32 @@ Map.Draw = function(map, opt){
var draw = this
- var ctx = map.canvas.getContext("2d")
+ var ctx = draw.ctx = map.canvas.getContext("2d")
draw.animate = function(){
ctx.save()
draw.clear()
draw.fill("rgba(255,255,255,0.98)")
- if (opt.minimap) {
+ if (opt.ortho) {
+ }
+ else if (opt.minimap) {
ctx.translate( map.dimensions.a * 1/2, map.dimensions.b * 1/2)
ctx.scale( map.zoom, map.zoom )
+ if (map.opt.pivot) { ctx.rotate(scene.camera.rotationY + PI) }
ctx.translate( opt.center.x, - opt.center.z )
ctx.scale( -1, 1 )
draw.coords()
- draw.regions(Rooms.regions, [ "#fff" ], "#000")
+ if (Rooms.shapesMode) {
+ shapes.draw(draw.ctx, "#fff", null)
+ shapes.draw(draw.ctx, null, "#000")
+ }
+ else {
+ draw.regions(Rooms.regions, [ "#fff" ], "#000")
+ }
draw.camera(scene.camera)
}
-
else {
ctx.translate( map.dimensions.a * 1/2, map.dimensions.b * 1/2)
ctx.scale( map.zoom, map.zoom )
@@ -31,12 +39,19 @@ Map.Draw = function(map, opt){
draw.regions(Rooms.regions, [ "#f8f8f8" ], "#000")
draw.mouse(map.ui.mouse.cursor)
+ draw.mouse_dimensions(map.ui.mouse.cursor)
draw.coords()
scene && draw.camera(scene.camera)
}
ctx.restore()
}
+ draw.translate = function(){
+ ctx.translate( map.dimensions.a * 1/2, map.dimensions.b * 1/2)
+ ctx.scale( map.zoom, map.zoom )
+ ctx.translate( map.center.a, map.center.b )
+ ctx.scale( -1, 1 )
+ }
// changes the ctx temporarily
draw.render = function(){
@@ -56,9 +71,9 @@ Map.Draw = function(map, opt){
}
var canvas = document.createElement("canvas")
- ctx = canvas.getContext('2d')
canvas.width = thumbnail_width
canvas.height = thumbnail_height
+ ctx = canvas.getContext('2d')
draw.clear()
@@ -87,7 +102,7 @@ Map.Draw = function(map, opt){
ctx.putImageData(pixelData, 0, 0)
// reset the ctx
- ctx = map.canvas.getContext("2d")
+ ctx = draw.ctx
return canvas
}
@@ -123,7 +138,8 @@ Map.Draw = function(map, opt){
ctx.beginPath();
ctx.arc(mouse.x.b, mouse.y.b, radius, 0, 2*Math.PI, false);
ctx.fill();
-
+ }
+ draw.mouse_dimensions = function(mouse){
if (mouse.width() != 0 && mouse.height() != 0) {
if (map.ui.dragging) {
stroke_rect(mouse)
@@ -159,36 +175,39 @@ Map.Draw = function(map, opt){
}
draw.coords = function(){
- /*
- ctx.fillStyle = "#888";
- dot_at(0,0)
- ctx.fillStyle = "#bbb";
- dot_at(100,0)
- dot_at(0,100)
- ctx.fillStyle = "#ddd";
- dot_at(200,0)
- dot_at(0,200)
- ctx.fillStyle = "#eee";
- dot_at(300,0)
- dot_at(0,300)
- */
-
ctx.strokeStyle = "rgba(0,0,0,0.1)"
ctx.lineWidth = 1/map.zoom
var sides = map.sides()
- var quant = sides.clone().quantize(MAP_GRID_SIZE)
- for (var x = quant.x.a - MAP_GRID_SIZE; x <= quant.x.b; x += MAP_GRID_SIZE) {
- line(x, sides.y.a, x, sides.y.b)
- }
- for (var y = quant.y.a - MAP_GRID_SIZE; y <= quant.y.b; y += MAP_GRID_SIZE) {
- line(sides.x.a, y, sides.x.b, y)
+ var quant = sides.clone().quantize(draw.grid_size)
+ for (var x = quant.x.a - draw.grid_size; x <= quant.x.b; x += draw.grid_size) {
+ if (Math.round(x) % 360 == 0) {
+ ctx.strokeStyle = "rgba(0,0,0,0.3)"
+ ctx.lineWidth = 1/map.zoom
+ }
+ else {
+ ctx.strokeStyle = "rgba(0,0,0,0.05)"
+ ctx.lineWidth = 1/map.zoom
+ }
+ line(x, sides.y.a, x, sides.y.b)
}
+
+ for (var y = quant.y.a - draw.grid_size; y <= quant.y.b; y += draw.grid_size) {
+ if (Math.round(y) % 360 == 0) {
+ ctx.strokeStyle = "rgba(0,0,0,0.3)"
+ ctx.lineWidth = 1/map.zoom
+ }
+ else {
+ ctx.strokeStyle = "rgba(0,0,0,0.05)"
+ ctx.lineWidth = 1/map.zoom
+ }
+ line(sides.x.a, y, sides.x.b, y)
+ }
}
//
- function line (x,y,a,b,translation){
+ var line = draw.line = function (x,y,a,b,translation){
if (translation) {
x += translation.a
a += translation.a
@@ -219,11 +238,11 @@ Map.Draw = function(map, opt){
line(r.x.a, r.y.a, r.x.b, r.y.b, r.translation)
}
- function dot_at (x,z){
+ draw.dot_at = function dot_at (x, z, radius){
ctx.save()
ctx.translate(x,z)
- var radius = 2 / map.zoom
+ radius = (radius || 2) / map.zoom
ctx.beginPath();
ctx.arc(0, 0, radius, 0, 2*Math.PI, false);
@@ -231,4 +250,29 @@ Map.Draw = function(map, opt){
ctx.restore()
}
+
+ draw.x_at = function x_at (x, z, length){
+ ctx.save()
+ if (x && 'x' in x) {
+ length = z
+ ctx.translate(x.x,x.z)
+ }
+ else {
+ ctx.translate(x,z)
+ }
+
+ var len = (length/2 || 4) / map.zoom
+
+ ctx.lineCap = "square"
+ ctx.lineWidth = 2/map.zoom
+ ctx.beginPath()
+ ctx.moveTo( -len, -len);
+ ctx.lineTo( len, len);
+ ctx.moveTo( -len, len);
+ ctx.lineTo( len, -len);
+ ctx.stroke();
+
+ ctx.restore()
+ }
+
} \ No newline at end of file
diff --git a/public/assets/javascripts/rectangles/engine/map/tools/_base.js b/public/assets/javascripts/rectangles/engine/map/tools/_base.js
new file mode 100644
index 0000000..17b247d
--- /dev/null
+++ b/public/assets/javascripts/rectangles/engine/map/tools/_base.js
@@ -0,0 +1,11 @@
+var MapTool = Fiber.extend(function(base){
+ var exports = {
+ recenterCursor: true,
+ down: function(e, cursor){},
+ move: function(e, cursor){},
+ drag: function(e, cursor){},
+ up: function(e, cursor, new_cursor){},
+ cancel: function(){},
+ }
+ return exports
+}) \ No newline at end of file
diff --git a/public/assets/javascripts/rectangles/engine/map/tools/arrow.js b/public/assets/javascripts/rectangles/engine/map/tools/arrow.js
new file mode 100644
index 0000000..0b0557e
--- /dev/null
+++ b/public/assets/javascripts/rectangles/engine/map/tools/arrow.js
@@ -0,0 +1,83 @@
+// Tool used to move corners of polylines
+
+var ArrowTool = MapTool.extend(function(base){
+ var exports = {}
+
+ var selected_point = null, selected_segment = null, original_point = null, selected_shape = null
+ var src_points, dest_points
+
+ exports.down = function(e, cursor){
+ last_point.a = cursor.x.a
+ last_point.b = cursor.y.a
+ var p = shapes.findClosestPoint(last_point)
+ if (p && p.shape.type() !== "ortho") {
+ selected_shape = p.shape
+ selected_point = p.point
+ original_point = selected_point.clone()
+ return
+ }
+ var segment = shapes.findClosestSegment(last_point)
+ if (segment) {
+ document.body.style.cursor = "pointer"
+
+ selected_segment = segment
+ console.log(segment.head, segment.tail)
+ selected_shape = segment.shape
+ src_points = segment.shape.cloneSegment( segment )
+ dest_points = segment.shape.getSegment( segment )
+
+ last_point.a = segment.x
+ last_point.b = segment.y
+ cursor.x.a = cursor.x.b = last_point.a
+ cursor.y.a = cursor.y.b = last_point.b
+ }
+ else {
+ map.ui.set_drag_tool("position")
+ }
+ }
+
+ exports.move = function(e, cursor){
+ last_point.a = cursor.x.a
+ last_point.b = cursor.y.a
+ var p = shapes.findClosestPoint(last_point)
+ if (p) {
+ document.body.style.cursor = "pointer"
+ last_point.assign(p.point)
+ cursor.x.a = cursor.x.b = last_point.a
+ cursor.y.a = cursor.y.b = last_point.b
+ return
+ }
+ var segment = shapes.findClosestSegment(last_point)
+ if (segment) {
+ document.body.style.cursor = "pointer"
+ last_point.a = segment.x
+ last_point.b = segment.y
+ cursor.x.a = cursor.x.b = last_point.a
+ cursor.y.a = cursor.y.b = last_point.b
+ }
+ else {
+ document.body.style.cursor = "crosshair"
+ }
+ }
+
+ exports.drag = function(e, cursor){
+ if (selected_point) {
+ selected_point.a = original_point.a + cursor.x.magnitude()
+ selected_point.b = original_point.b + cursor.y.magnitude()
+ selected_shape.rebuild()
+ }
+ else if (selected_segment) {
+ selected_shape.translateSegment(
+ src_points, dest_points,
+ cursor.x.magnitude(), cursor.y.magnitude()
+ )
+ selected_shape.rebuild()
+ }
+ }
+
+ exports.up = function(e, cursor){
+ selected_point = selected_shape = selected_segment = original_point = null
+ }
+
+ return exports
+}) \ No newline at end of file
diff --git a/public/assets/javascripts/rectangles/engine/map/tools/eraser.js b/public/assets/javascripts/rectangles/engine/map/tools/eraser.js
new file mode 100644
index 0000000..8fc3687
--- /dev/null
+++ b/public/assets/javascripts/rectangles/engine/map/tools/eraser.js
@@ -0,0 +1,29 @@
+// Tool used to delete lines
+
+var EraserTool = MapTool.extend(function(base){
+ var exports = {}
+ exports.down = function(e, cursor){
+ last_point.a = cursor.x.a
+ last_point.b = cursor.y.a
+ var segment = shapes.findClosestSegment(last_point)
+ if (segment) {
+ shapes.removeSegment(segment)
+ }
+ }
+ exports.move = function(e, cursor){
+ last_point.a = cursor.x.a
+ last_point.b = cursor.y.a
+ var segment = shapes.findClosestSegment(last_point)
+ if (segment) {
+ document.body.style.cursor = "pointer"
+ last_point.a = segment.x
+ last_point.b = segment.y
+ cursor.x.a = cursor.x.b = last_point.a
+ cursor.y.a = cursor.y.b = last_point.b
+ }
+ else {
+ document.body.style.cursor = "crosshair"
+ }
+ }
+ return exports
+})
diff --git a/public/assets/javascripts/rectangles/engine/map/tools/line.js b/public/assets/javascripts/rectangles/engine/map/tools/line.js
new file mode 100644
index 0000000..8175d66
--- /dev/null
+++ b/public/assets/javascripts/rectangles/engine/map/tools/line.js
@@ -0,0 +1,63 @@
+// Tool is used to define a very simple line between two points.
+// It is used by the BlueprintScaler to specify a sample distance to scale.
+
+var LineTool = MapTool.extend(function(base){
+ var exports = {}
+
+ var selected_point = null
+
+ var line = exports.line = []
+
+ var can_drag, dragging
+
+ exports.down = function(e, cursor){
+
+ // rightclick?
+ if (e.ctrlKey || e.which === 3) {
+ cursor.quantize(1/map.zoom)
+ app.router.blueprintView.map.center.a = cursor.x.a
+ app.router.blueprintView.map.center.b = -cursor.y.a
+ cursor.x.b = cursor.x.a
+ cursor.y.b = cursor.y.a
+ return
+ }
+
+ this.cursor = cursor
+ switch (line.length) {
+ case 0:
+ line[0] = cursor.x_component()
+ can_drag = true
+ break
+ case 1:
+ line[1] = cursor.x_component()
+ can_drag = false
+ break
+ case 2:
+ line[0] = cursor.x_component()
+ line.pop()
+ can_drag = true
+ break
+ }
+ }
+
+ exports.move = function(e, cursor){
+ this.cursor = cursor
+ }
+
+ exports.drag = function(e, cursor){
+ if (dragging) {
+ line[1].a = cursor.x.b
+ line[1].b = cursor.y.b
+ }
+ else if (can_drag && cursor.magnitude() > 10/map.zoom) {
+ line[1] = cursor.y_component()
+ dragging = true
+ }
+ }
+
+ exports.up = function(e, cursor){
+ can_drag = dragging = false
+ }
+
+ return exports
+}) \ No newline at end of file
diff --git a/public/assets/javascripts/rectangles/engine/map/tools/ortho.js b/public/assets/javascripts/rectangles/engine/map/tools/ortho.js
new file mode 100644
index 0000000..374c822
--- /dev/null
+++ b/public/assets/javascripts/rectangles/engine/map/tools/ortho.js
@@ -0,0 +1,118 @@
+// Tool to make a polyline where all walls are orthogonal
+
+var OrthoPolylineTool = MapTool.extend(function (base) {
+
+ var prev_point, horizontal = false, first_edge_is_horizontal = false
+
+ var exports = {}
+ exports.down = function(e, cursor){
+ // rightclick?
+ if (e.ctrlKey || e.which === 3) {
+ e.preventDefault()
+ e.stopPropagation()
+ if (map.ui.placing) {
+ // close polyline or cancel
+ map.ui.placing = false
+ if (shapes.workline.points.length > 2) {
+ shapes.workline.build()
+ shapes.add(shapes.workline)
+ }
+ else {
+ shapes.workline.reset()
+ }
+ return
+ }
+ else {
+ map.ui.tools.position.rightclick(e, cursor)
+ }
+ return
+ }
+
+ // compare to initial point
+ var p = last_point.clone()
+ if (map.ui.placing) {
+ if (shapes.workline.lastPoint().eq(p)) {
+ return
+ }
+ else if (shapes.workline.canCloseWith(p)) {
+ shapes.workline.close()
+ shapes.workline.build()
+ shapes.add(shapes.workline)
+ map.ui.placing = false
+ }
+ else {
+ shapes.workline.add(p)
+ prev_point = p
+ horizontal = ! horizontal
+ }
+ }
+ else {
+ map.ui.placing = true
+ shapes.workline = new OrthoPolyline ()
+ shapes.workline.add(p)
+ first_point = prev_point = p
+ horizontal = false
+ }
+ }
+ exports.move = function(e, cursor){
+ last_point.a = cursor.x.a
+ last_point.b = cursor.y.a
+ if (map.ui.placing) {
+ if (shapes.workline.points.length == 1) {
+ var x = abs(prev_point.a - last_point.a)
+ var y = abs(prev_point.b - last_point.b)
+ if (x > y) {
+ last_point.b = prev_point.b
+ first_edge_is_horizontal = horizontal = true
+ }
+ else {
+ last_point.a = prev_point.a
+ first_edge_is_horizontal = horizontal = false
+ }
+ }
+ else {
+ if (horizontal) {
+ last_point.b = prev_point.b
+ }
+ else {
+ last_point.a = prev_point.a
+ }
+ if (horizontal == first_edge_is_horizontal) {
+ // check if this point is within N pixels of the normal
+ // and lock it into place if so
+ if (horizontal && abs( first_point.a - last_point.a ) < 10/map.zoom) {
+ last_point.a = first_point.a
+ }
+ else if (! horizontal && abs( first_point.b - last_point.b ) < 10/map.zoom) {
+ last_point.b = first_point.b
+ }
+ }
+ }
+
+ if (shapes.workline.canCloseWith(last_point)) {
+ document.body.style.cursor = "pointer"
+ last_point.assign(first_point)
+ cursor.x.a = cursor.x.b = last_point.a
+ cursor.y.a = cursor.y.b = last_point.b
+ }
+ return
+ }
+ var end_point = shapes.findClosestEndPoint(last_point)
+ if (end_point) {
+ document.body.style.cursor = "pointer"
+ last_point.assign(end_point.point)
+ cursor.x.a = cursor.x.b = last_point.a
+ cursor.y.a = cursor.y.b = last_point.b
+ return
+ }
+ else {
+ document.body.style.cursor = "crosshair"
+ }
+ }
+ exports.cancel = function(){
+ if (map.ui.placing) { shapes.workline.reset() }
+ first_point = null
+ map.ui.placing = false
+ }
+ return exports
+}) \ No newline at end of file
diff --git a/public/assets/javascripts/rectangles/engine/map/tools/polyline.js b/public/assets/javascripts/rectangles/engine/map/tools/polyline.js
new file mode 100644
index 0000000..445ae26
--- /dev/null
+++ b/public/assets/javascripts/rectangles/engine/map/tools/polyline.js
@@ -0,0 +1,73 @@
+// Tool used to draw polylines with arbitrary angles
+
+var PolylineTool = MapTool.extend(function (base) {
+ var exports = {}
+ exports.down = function(e, cursor){
+
+ // rightclick?
+ if (e.ctrlKey || e.which === 3) {
+ e.preventDefault()
+ e.stopPropagation()
+ if (map.ui.placing) {
+ // close polyline or cancel
+ map.ui.placing = false
+ if (shapes.workline.points.length > 2) {
+ shapes.workline.build()
+ shapes.add(shapes.workline)
+ }
+ else {
+ shapes.workline.reset()
+ }
+ return
+ }
+ map.ui.tools.position.rightclick(e, cursor)
+ return
+ }
+
+ // compare to initial point
+ var p = last_point.clone()
+ if (map.ui.placing) {
+ if (shapes.workline.canCloseWith(p)) {
+ shapes.workline.close()
+ shapes.workline.build()
+ shapes.add(shapes.workline)
+ map.ui.placing = false
+ }
+ else {
+ shapes.workline.add(p)
+ }
+ }
+ else {
+ map.ui.placing = true
+ shapes.workline = new Polyline ()
+ shapes.workline.add(p)
+ }
+ }
+ exports.move = function(e, cursor){
+ last_point.a = cursor.x.a
+ last_point.b = cursor.y.a
+ if (map.ui.placing && shapes.workline.canCloseWith(last_point)) {
+ document.body.style.cursor = "pointer"
+ last_point.assign(shapes.workline.points[0])
+ cursor.x.a = cursor.x.b = last_point.a
+ cursor.y.a = cursor.y.b = last_point.b
+ return
+ }
+ var end_point = shapes.findClosestEndPoint(last_point)
+ if (end_point) {
+ document.body.style.cursor = "pointer"
+ last_point.assign(end_point.point)
+ cursor.x.a = cursor.x.b = last_point.a
+ cursor.y.a = cursor.y.b = last_point.b
+ return
+ }
+ else {
+ document.body.style.cursor = "crosshair"
+ }
+ }
+ exports.cancel = function(){
+ if (map.ui.placing) { shapes.workline.reset() }
+ map.ui.placing = false
+ }
+ return exports
+}) \ No newline at end of file
diff --git a/public/assets/javascripts/rectangles/engine/map/tools/position.js b/public/assets/javascripts/rectangles/engine/map/tools/position.js
new file mode 100644
index 0000000..f8365bc
--- /dev/null
+++ b/public/assets/javascripts/rectangles/engine/map/tools/position.js
@@ -0,0 +1,19 @@
+// Tool used to set position on the map and let you change the view by dragging.
+
+var PositionTool = MapTool.extend(function(base){
+ var exports = {
+ recenterCursor: false,
+ drag: function(e, cursor){
+ map.center.a = -cursor.x.magnitude()
+ map.center.b = cursor.y.magnitude()
+ },
+ rightclick: function(e, cursor){
+ cursor.quantize(1/map.zoom)
+ map.center.a = cursor.x.a
+ map.center.b = -cursor.y.a
+ cursor.x.b = cursor.x.a
+ cursor.y.b = cursor.y.a
+ }
+ }
+ return exports
+}) \ No newline at end of file
diff --git a/public/assets/javascripts/rectangles/engine/map/tools/start.js b/public/assets/javascripts/rectangles/engine/map/tools/start.js
new file mode 100644
index 0000000..203a85f
--- /dev/null
+++ b/public/assets/javascripts/rectangles/engine/map/tools/start.js
@@ -0,0 +1,29 @@
+// Tool is used to set the start position on the map.
+
+var StartPositionTool = MapTool.extend(function(base){
+ var exports = {}
+
+ var selected_point = null
+
+ var line = exports.line = []
+
+ var can_drag, dragging
+
+ exports.down = function(e, cursor){
+ // rightclick?
+ if (e.ctrlKey || e.which === 3) {
+ cursor.quantize(1/map.zoom)
+ app.router.blueprintView.map.center.a = cursor.x.a
+ app.router.blueprintView.map.center.b = -cursor.y.a
+ cursor.x.b = cursor.x.a
+ cursor.y.b = cursor.y.a
+ return
+ }
+
+ cam.x = app.controller.startPosition.x = cursor.x.a
+ cam.z = app.controller.startPosition.z = cursor.y.a
+ }
+
+ return exports
+
+})
diff --git a/public/assets/javascripts/rectangles/engine/map/ui_editor.js b/public/assets/javascripts/rectangles/engine/map/ui/editor.js
index 7308344..3e1d720 100644
--- a/public/assets/javascripts/rectangles/engine/map/ui_editor.js
+++ b/public/assets/javascripts/rectangles/engine/map/ui/editor.js
@@ -73,7 +73,7 @@ Map.UI.Editor = function(map){
app.tube("builder-destroy-room", room)
// TODO: watch individual scenery object here
- Minotaur.watch( app.router.editorView.settings )
+ Minotaur.watch( app.controller.settings )
return
}
else if (intersects.length) {
@@ -205,7 +205,7 @@ Map.UI.Editor = function(map){
Rooms.rebuild()
// TODO: watch individual scenery object here
- Minotaur.watch( app.router.editorView.settings )
+ Minotaur.watch( app.controller.settings )
}
var intersects = Rooms.filter(function(r){
@@ -247,7 +247,7 @@ Map.UI.Editor = function(map){
Rooms.rebuild()
// TODO: watch individual scenery object here
- Minotaur.watch( app.router.editorView.settings )
+ Minotaur.watch( app.controller.settings )
wheelState = null
}, 250)
@@ -268,7 +268,7 @@ Map.UI.Editor = function(map){
Rooms.rebuild()
// TODO: watch individual scenery object here
- Minotaur.watch( app.router.editorView.settings )
+ Minotaur.watch( app.controller.settings )
wheelState = null
}, 250)
diff --git a/public/assets/javascripts/rectangles/engine/map/ui_minimap.js b/public/assets/javascripts/rectangles/engine/map/ui/minimap.js
index 0fdd336..0fdd336 100644
--- a/public/assets/javascripts/rectangles/engine/map/ui_minimap.js
+++ b/public/assets/javascripts/rectangles/engine/map/ui/minimap.js
diff --git a/public/assets/javascripts/rectangles/engine/map/ui/ortho.js b/public/assets/javascripts/rectangles/engine/map/ui/ortho.js
new file mode 100644
index 0000000..5be7446
--- /dev/null
+++ b/public/assets/javascripts/rectangles/engine/map/ui/ortho.js
@@ -0,0 +1,107 @@
+Map.UI = Map.UI || {}
+Map.UI.Ortho = function(map){
+
+ var base = this
+ var last_event = null
+
+ base.creating = base.dragging = base.resizing = false
+
+ base.mouse = new mouse({
+ el: map.el,
+ down: function(e, cursor){
+ last_event = e
+ cursor.x.div(map.dimensions.a).add(0.5).mul(map.dimensions.a / map.zoom)
+ cursor.y.div(map.dimensions.b).sub(0.5).mul(map.dimensions.b / map.zoom)
+ if (tool.recenterCursor) {
+ cursor.x.add(map.center.a)
+ cursor.y.sub(map.center.b)
+ base.tools[currentTool].down(e, cursor)
+ }
+ else {
+ base.tools[currentTool].down(e, cursor)
+ cursor.x.add(map.center.a)
+ cursor.y.sub(map.center.b)
+ }
+ },
+ move: function(e, cursor){
+ last_event = e
+ cursor.x.div(map.dimensions.a).add(0.5).mul(map.dimensions.a / map.zoom)
+ cursor.y.div(map.dimensions.b).sub(0.5).mul(map.dimensions.b / map.zoom)
+ if (tool.recenterCursor) {
+ cursor.x.add(map.center.a)
+ cursor.y.sub(map.center.b)
+ base.tools[currentTool].move(e, cursor)
+ }
+ else {
+ base.tools[currentTool].move(e, cursor)
+ cursor.x.add(map.center.a)
+ cursor.y.sub(map.center.b)
+ }
+ },
+ drag: function(e, cursor){
+ last_event = e
+ cursor.x.b = ((cursor.x.b/map.dimensions.a)+0.5) * map.dimensions.a / map.zoom
+ cursor.y.b = ((cursor.y.b/map.dimensions.b)-0.5) * map.dimensions.b / map.zoom
+ if (tool.recenterCursor) {
+ cursor.x.b += map.center.a
+ cursor.y.b -= map.center.b
+ base.tools[currentTool].drag(e, cursor)
+ }
+ else {
+ base.tools[currentTool].drag(e, cursor)
+ cursor.x.b += map.center.a
+ cursor.y.b -= map.center.b
+ }
+ },
+ up: function(e, cursor, new_cursor){
+ last_event = e
+ new_cursor.x.div(map.dimensions.a).add(0.5).mul(map.dimensions.a / map.zoom)
+ new_cursor.y.div(map.dimensions.b).sub(0.5).mul(map.dimensions.b / map.zoom)
+ if (tool.recenterCursor) {
+ new_cursor.x.add(map.center.a)
+ new_cursor.y.sub(map.center.b)
+ base.tools[currentTool].up(e, cursor, new_cursor)
+ }
+ else {
+ base.tools[currentTool].up(e, cursor, new_cursor)
+ new_cursor.x.add(map.center.a)
+ new_cursor.y.sub(map.center.b)
+ }
+ if (nextTool) {
+ console.log('found nextTool')
+ base.set_tool(nextTool)
+ nextTool = null
+ }
+ }
+ })
+
+ var currentTool = "polyline", nextTool, tool
+ base.add_tool = function(name, tool){
+ base.tools[name] = tool
+ }
+ base.set_tool = function(s){
+ console.log("set tool to", s)
+ if (base.tools[currentTool]) {
+ base.tools[currentTool].cancel()
+ }
+ currentTool = s
+ tool = base.tools[currentTool]
+ }
+ base.set_drag_tool = function(s){
+ console.log('set drag tool to', s)
+ nextTool = currentTool
+ currentTool = s
+ tool = base.tools[currentTool]
+ base.tools[currentTool].down(last_event, base.mouse.cursor)
+ }
+ base.tools = {}
+
+ base.wheel = new wheel({
+ el: map.el,
+ update: mousewheel,
+ })
+
+ function mousewheel (e, deltaY, deltaX){
+ map.set_zoom(map.zoom_exponent - deltaY/20)
+ }
+}
diff --git a/public/assets/javascripts/rectangles/engine/rooms/_rooms.js b/public/assets/javascripts/rectangles/engine/rooms/_rooms.js
index 46c1d7f..9aff33f 100644
--- a/public/assets/javascripts/rectangles/engine/rooms/_rooms.js
+++ b/public/assets/javascripts/rectangles/engine/rooms/_rooms.js
@@ -37,6 +37,7 @@
base.list = {}
base.regions = []
+ base.shapesMode = false
base.uid = new UidGenerator(base.list)
@@ -95,6 +96,7 @@
}
base.rebuild = function(walls_data){
+ if (base.shapesMode) return
walls_data = walls_data || Walls.serialize()
Rooms.clipper.update()
Rooms.builder.rebuild()
@@ -124,6 +126,45 @@
})
Rooms.rebuild(walls_data)
}
+
+ base.deserializeFromShapes = function(data, walls_data) {
+ walls_data = walls_data || Walls.serialize()
+ base.shapesMode = true
+ window.viewHeight = data.viewHeight || app.defaults.viewHeight
+ window.wallHeight = data.wallHeight || app.defaults.wallHeight
+ $(".units").val( data.units )
+
+ Rooms.builder.clear()
+
+ shapes.deserialize( data.shapes )
+ // shapes.build()
+ var regions = RegionList.build()
+
+ regions.forEach(function(region){
+ var room = new Room({
+ rect: region,
+ regions: [region],
+ height: wallHeight,
+ })
+
+ room.sides = region.sides
+ region.id = Rooms.uid("room_")
+ Rooms.list[ region.id ] = room
+ var mx_walls = Rooms.builder.build_walls(region)
+ room.mx_floor = Rooms.builder.make_floor(room, region)
+ room.mx_ceiling = Rooms.builder.make_ceiling(room, region)
+
+ mx_walls.forEach(function(mx){ scene.add(mx) })
+ scene.add(room.mx_floor)
+ scene.add(room.mx_ceiling)
+ })
+
+ Rooms.grouper.build()
+
+ Walls.paint()
+ Walls.deserialize(walls_data)
+ app.tube("rooms-built")
+ }
base.report = function(){
var data = []
diff --git a/public/assets/javascripts/rectangles/engine/scenery/_scenery.js b/public/assets/javascripts/rectangles/engine/scenery/_scenery.js
index d52fe21..6203c20 100644
--- a/public/assets/javascripts/rectangles/engine/scenery/_scenery.js
+++ b/public/assets/javascripts/rectangles/engine/scenery/_scenery.js
@@ -11,6 +11,7 @@ var Scenery = new function(){
base.init = function(){
base.resize.init()
+ base.sound.init()
}
base.add = function(opt){
@@ -113,6 +114,35 @@ var Scenery = new function(){
return added
}
+ base.rewindAll = function(){
+ base.forEach(function(scenery){
+ if (scenery.type == "video") scenery.seek(0)
+ })
+ }
+ base.playAll = function(){
+ base.forEach(function(scenery){
+ if (scenery.type == "video") {
+ scenery.unmute()
+ scenery.play()
+ }
+ })
+ }
+ base.pauseAll = function(){
+ base.forEach(function(scenery){
+ if (scenery.type == "video") scenery.pause()
+ })
+ }
+ base.muteAll = function(){
+ base.forEach(function(scenery){
+ if (scenery.type == "video") scenery.mx.mute()
+ })
+ }
+ base.unmuteAll = function(){
+ base.forEach(function(scenery){
+ if (scenery.type == "video") scenery.mx.unmute()
+ })
+ }
+
return base
}
diff --git a/public/assets/javascripts/rectangles/engine/scenery/move.js b/public/assets/javascripts/rectangles/engine/scenery/move.js
index f57ddba..94c6281 100644
--- a/public/assets/javascripts/rectangles/engine/scenery/move.js
+++ b/public/assets/javascripts/rectangles/engine/scenery/move.js
@@ -28,7 +28,7 @@ Scenery.move = function(base){
base.remove()
return
}
-
+
// load the modal
app.controller.pick(base)
@@ -75,13 +75,14 @@ Scenery.move = function(base){
switch (base.wall.side) {
case FRONT:
case BACK:
- base.mx.x = position.a + delta.a * cos(wall_rotation[base.wall.side]) + dimension.a / 2
+ base.mx.x = position.a + delta.a * cos(base.wall.rotationY) + dimension.a / 2
break
case LEFT:
case RIGHT:
- base.mx.z = position.a + delta.a * sin(wall_rotation[base.wall.side]) + dimension.a / 2
+ base.mx.z = position.a + delta.a * sin(base.wall.rotationY) + dimension.a / 2
break
}
+
if (editor.permissions.resize) {
Scenery.resize.move_dots()
}
@@ -104,7 +105,6 @@ Scenery.move = function(base){
dragging = moved = false
oldState = null
document.body.classList.remove("dragging")
-
}
function switch_wall (e, target, cursor){
diff --git a/public/assets/javascripts/rectangles/engine/scenery/resize.js b/public/assets/javascripts/rectangles/engine/scenery/resize.js
index 5af7f3f..252af74 100644
--- a/public/assets/javascripts/rectangles/engine/scenery/resize.js
+++ b/public/assets/javascripts/rectangles/engine/scenery/resize.js
@@ -8,6 +8,7 @@ Scenery.resize = new function(){
var dragging = false
var naturalDimension, naturalDimensionCopy, dimension, position, scale
var oldState
+ var rotationY
var dots = [], dot, selected_dot
@@ -49,7 +50,8 @@ Scenery.resize = new function(){
// rotate the dots as appropriate
base.rotate_dots = function(){
- rotationY = wall_rotation[obj.wall.side]
+ // console.trace()
+ rotationY = obj.wall.rotationY
dots.forEach(function(dot){
dot.rotationY = rotationY
})
@@ -93,6 +95,7 @@ Scenery.resize = new function(){
base.add_dots()
base.rotate_dots()
base.move_dots()
+ Sculpture.resize.hide()
}
// dismiss the dots on blur
diff --git a/public/assets/javascripts/rectangles/engine/scenery/sound.js b/public/assets/javascripts/rectangles/engine/scenery/sound.js
new file mode 100644
index 0000000..5a783d6
--- /dev/null
+++ b/public/assets/javascripts/rectangles/engine/scenery/sound.js
@@ -0,0 +1,16 @@
+
+Scenery.sound = {}
+Scenery.sound.max_distance = 1500
+Scenery.sound.init = function(){
+ app.tube.on("move", Scenery.sound.move)
+}
+Scenery.sound.move = function(){
+ Scenery.forEach(function(scenery){
+ if ((scenery.type == "video" || scenery.type == "audio")) { // && ! scenery.muted()) {
+ var distance = dist(cam.x, cam.z,
+ scenery.mx.x, scenery.mx.z)
+ var volume = 1 - (clamp( distance, 0, Scenery.sound.max_distance ) / Scenery.sound.max_distance)
+ scenery.setVolume(volume)
+ }
+ })
+}
diff --git a/public/assets/javascripts/rectangles/engine/scenery/types/_object.js b/public/assets/javascripts/rectangles/engine/scenery/types/_object.js
index cd3f981..e3b9b4d 100644
--- a/public/assets/javascripts/rectangles/engine/scenery/types/_object.js
+++ b/public/assets/javascripts/rectangles/engine/scenery/types/_object.js
@@ -90,11 +90,11 @@ Scenery.types.base = Fiber.extend(function(base){
switch (this.wall.side) {
case FRONT:
case BACK:
- this.position.a += delta.a * cos(wall_rotation[this.wall.side])
+ this.position.a += delta.a * cos(this.wall.rotationY)
break
case LEFT:
case RIGHT:
- this.position.a += delta.a * sin(wall_rotation[this.wall.side])
+ this.position.a += delta.a * sin(this.wall.rotationY)
break
}
},
diff --git a/public/assets/javascripts/rectangles/engine/scenery/types/audio.js b/public/assets/javascripts/rectangles/engine/scenery/types/audio.js
index 82f984e..fdd221d 100644
--- a/public/assets/javascripts/rectangles/engine/scenery/types/audio.js
+++ b/public/assets/javascripts/rectangles/engine/scenery/types/audio.js
@@ -39,6 +39,10 @@ Scenery.types.audio = Scenery.types.base.extend(function(base){
this.dimensions.deserialize(data.dimensions)
},
+ setVolume: function(n){
+ this.mx.setVolume(n)
+ },
+
play: function(){
this.mx.play()
},
diff --git a/public/assets/javascripts/rectangles/engine/scenery/types/video.js b/public/assets/javascripts/rectangles/engine/scenery/types/video.js
index d1b1763..163e19e 100644
--- a/public/assets/javascripts/rectangles/engine/scenery/types/video.js
+++ b/public/assets/javascripts/rectangles/engine/scenery/types/video.js
@@ -38,7 +38,8 @@ Scenery.types.video = Scenery.types.base.extend(function(base){
backface: false,
})
scene.add(this.mx)
- this.mx.load()
+
+ this.mx.load()
},
play: function(){
@@ -90,6 +91,14 @@ Scenery.types.video = Scenery.types.base.extend(function(base){
}
},
+ unmute: function(){
+ this.mx.unmute()
+ },
+
+ setVolume: function(n){
+ this.mx.setVolume(n)
+ },
+
serialize: function(){
var data = base.serialize.call(this)
return data
diff --git a/public/assets/javascripts/rectangles/engine/scenery/undo.js b/public/assets/javascripts/rectangles/engine/scenery/undo.js
index 1232780..b976ea2 100644
--- a/public/assets/javascripts/rectangles/engine/scenery/undo.js
+++ b/public/assets/javascripts/rectangles/engine/scenery/undo.js
@@ -195,5 +195,70 @@
},
},
+ //
+ {
+ type: "create-sculpture",
+ undo: function(state){
+ Sculpture.remove(state.id)
+ Sculpture.resize.hide()
+
+ // TODO: watch individual scenery object here
+ Minotaur.watch( app.router.editorView.settings )
+ },
+ redo: function(state){
+ var scenery = Sculpture.deserialize([ state ])
+ Sculpture.resize.show( sculpture )
+
+ // TODO: watch individual scenery object here
+ Minotaur.watch( app.router.editorView.settings )
+ },
+ },
+ {
+ type: "update-sculpture",
+ undo: function(state){
+ var sculpture = Sculpture.find(state.id)
+
+ sculpture.deserialize(state)
+
+ if (editor.permissions.resize) {
+ Sculpture.resize.show(sculpture)
+ }
+
+ // TODO: watch individual scenery object here
+ Minotaur.watch( app.router.editorView.settings )
+ },
+ redo: function(state){
+ var sculpture = Sculpture.find(state.id)
+
+ sculpture.deserialize(state)
+
+ if (editor.permissions.resize) {
+ Sculpture.resize.show(sculpture)
+ Sculpture.resize.move_dots()
+ Sculpture.resize.rotate_dots()
+ }
+
+ // TODO: watch individual scenery object here
+ Minotaur.watch( app.router.editorView.settings )
+ },
+ },
+ {
+ type: "destroy-sculpture",
+ undo: function(state){
+ var sculpture = Sculpture.deserialize([ state ])
+ Sculpture.resize.show( sculpture )
+
+ // TODO: watch individual scenery object here
+ Minotaur.watch( app.router.editorView.settings )
+ },
+ redo: function(state){
+ Sculpture.resize.hide()
+ Sculpture.remove(state.id)
+
+ // TODO: watch individual scenery object here
+ Minotaur.watch( app.router.editorView.settings )
+ },
+ },
+
])
})()
diff --git a/public/assets/javascripts/rectangles/engine/sculpture/_sculpture.js b/public/assets/javascripts/rectangles/engine/sculpture/_sculpture.js
new file mode 100644
index 0000000..888b925
--- /dev/null
+++ b/public/assets/javascripts/rectangles/engine/sculpture/_sculpture.js
@@ -0,0 +1,105 @@
+
+var Sculpture = new function(){
+
+ var base = this;
+
+ base.list = {}
+
+ base.mouse = new mouse ({ use_offset: false, mousedownUsesCapture: true })
+
+ base.init = function(){
+ app.on("move", base.updateBillboards)
+ base.resize.init()
+ }
+
+ base.updateBillboards = function(){
+ base.forEach(function(sculpture){
+ if (sculpture.billboard) {
+ sculpture.mx.rotationY = cam.rotationY
+ }
+ })
+ if (Sculpture.resize.obj && Sculpture.resize.obj.billboard) {
+ Sculpture.resize.move_dots()
+ }
+ }
+
+ base.add = function(opt){
+ var sculpture
+ switch (opt.media.type) {
+ case 'image':
+ sculpture = new Sculpture.types.image (opt)
+ break
+ }
+ base.list[sculpture.id] = sculpture
+ return sculpture
+ }
+
+ base.addNext = function(opt){
+ opt.newMedia = true
+ opt.media = Scenery.nextMedia
+ var sculpture = base.add(opt)
+
+ // test if sculpture was placed here
+ if (! sculpture) {
+ return null
+ }
+ else {
+ Scenery.nextMedia = null
+ return sculpture
+ }
+ }
+
+ base.find = function(id){
+ return base.list[id] || null
+ }
+
+ base.remove = function(id){
+ var scene_media = base.list[id]
+ delete base.list[id]
+ scene_media && scene_media.destroy()
+ }
+
+ base.removeAll = function(){
+ base.forEach(function(scene_media){
+ base.remove(scene_media.id)
+ })
+ }
+
+ base.uid = new UidGenerator(base.list)
+
+ base.forEach = function(f){
+ return base.values().forEach(f)
+ }
+
+ base.map = function(f){
+ return base.values().map(f)
+ }
+
+ base.values = function(){
+ return _.values(base.list)
+ }
+
+ base.serialize = function(){
+ var sculptures = base.map(function(sculpture){
+ return sculpture.serialize()
+ })
+ return sculptures
+ }
+
+ base.deserialize = function(sculpture_data){
+ var added = []
+ sculpture_data.forEach(function(data){
+ var scene_media = base.add({
+ id: data.id,
+ data: data,
+ media: data.media,
+ })
+ added.push(scene_media)
+ })
+ return added
+ }
+
+ return base
+}
+
+Sculpture.types = {}
diff --git a/public/assets/javascripts/rectangles/engine/sculpture/move.js b/public/assets/javascripts/rectangles/engine/sculpture/move.js
new file mode 100644
index 0000000..0cbeccd
--- /dev/null
+++ b/public/assets/javascripts/rectangles/engine/sculpture/move.js
@@ -0,0 +1,111 @@
+
+Sculpture.move = function(base){
+
+ var x, y, z, position, dimension, bounds
+ var rotationY
+ var dragging = false, altPressed = false, shiftPressed = false, moved = false
+ var oldState
+ var height, width
+
+ this.bind = function(){
+ Sculpture.mouse.bind_el(base.mx.el)
+ Sculpture.mouse.on("down", down)
+ Sculpture.mouse.on("drag", drag)
+ Sculpture.mouse.on("up", up)
+ }
+
+ this.unbind = function(){
+ base.focused = false
+ Sculpture.mouse.unbind_el(base.mx.el)
+ Sculpture.mouse.off("down", down)
+ Sculpture.mouse.off("drag", drag)
+ Sculpture.mouse.off("up", up)
+ }
+
+ function down (e, cursor){
+ if (e.target != base.mx.el && (e.target != base.mx.overlay)) return;
+ if (editor.permissions.destroy) {
+ base.remove()
+ return
+ }
+
+ // load the modal
+ app.controller.pick(base)
+
+ if (! base.focused) {
+ e.clickAccepted = false
+ base.focused = true
+ return
+ }
+ if (! (editor.permissions.move || editor.permissions.resize) ) {
+ e.clickAccepted = false
+ return
+ }
+ altPressed = (e.altKey && ! base.billboard)
+ shiftPressed = e.shiftKey
+ dragging = true
+ moved = false
+ x = base.mx.x
+ y = base.mx.y
+ z = base.mx.z
+ rotationY = base.mx.rotationY
+ height = base.mx.height * base.mx.scale
+
+ bounds = base.bounds
+ dimension = base.dimensions
+
+ oldState = base.serialize()
+ document.body.classList.add("dragging")
+ }
+
+ function drag (e, cursor){
+ if (! dragging) return
+
+ moved = true
+
+ var delta = cursor.delta()
+ delta.mul( cursor_amp ) // TODO: this should be proportional to your distance from the wall
+ delta.b *= -1
+
+ // here we need to move the element based on the XY coords, as
+ // base.mx.y = position.b + delta.b + dimension.b / 2
+ if (shiftPressed) {
+ base.mx.y = clamp( y + delta.b, height/2 + sculpture_distance_from_floor, Infinity )
+ }
+ else if (altPressed) {
+ base.mx.rotationY = mod( rotationY + delta.a / (window.innerWidth / 2) , TWO_PI )
+ Sculpture.resize.move_dots()
+ }
+ else {
+ base.mx.x = x + delta.a * cos(cam.rotationY) - delta.b * sin(cam.rotationY)
+ base.mx.z = z + delta.a * sin(cam.rotationY) + delta.b * cos(cam.rotationY)
+ }
+
+ if (editor.permissions.resize) {
+ Sculpture.resize.move_dots()
+ base.updateOutline()
+ }
+ }
+
+ function up (e, cursor){
+ if (! dragging || ! oldState) return
+
+ if (moved) {
+ UndoStack.push({
+ type: 'update-sculpture',
+ undo: oldState,
+ redo: base.serialize(),
+ })
+
+ // TODO: watch individual sculpture object here
+ Minotaur.watch( app.router.editorView.settings )
+ }
+
+ dragging = moved = false
+ oldState = null
+ document.body.classList.remove("dragging")
+ }
+
+ return this
+
+} \ No newline at end of file
diff --git a/public/assets/javascripts/rectangles/engine/sculpture/resize.js b/public/assets/javascripts/rectangles/engine/sculpture/resize.js
new file mode 100644
index 0000000..5f21d66
--- /dev/null
+++ b/public/assets/javascripts/rectangles/engine/sculpture/resize.js
@@ -0,0 +1,208 @@
+Sculpture.resize = new function(){
+
+ var base = this
+
+ var obj
+ var x, y, z, bounds
+ var dragging = false
+ var naturalDimension, naturalDimensionCopy, dimension, position, scale
+ var oldState
+ var rotationY
+
+ var dots = [], dot, selected_dot
+
+ base.init = function(){
+ base.build()
+ base.bind()
+ }
+
+ // create 9 dots at the corners of the div
+ base.build = function(){
+ [ TOP,
+ TOP_RIGHT,
+ RIGHT,
+ BOTTOM_RIGHT,
+ BOTTOM,
+ BOTTOM_LEFT,
+ LEFT,
+ TOP_LEFT ].forEach(base.build_dot)
+ }
+
+ // generate a dot element
+ base.build_dot = function(side) {
+ var dot = new MX.Object3D('.dot')
+ dot.width = dot.height = dot_side * 2
+ dot.scale = 0.5
+ dot.side = side
+ $(dot.el).on({
+ mouseenter: function(){ base.hovering = true },
+ mouseleave: function(){ base.hovering = false },
+ })
+ dots.push(dot)
+ }
+
+ base.add_dots = function(){
+ dots.forEach(function(dot){
+ scene.add(dot)
+ })
+ }
+
+ // move all the dots to the object's current position
+ base.move_dots = function(){
+ rotationY = obj.mx.rotationY
+
+ var x = obj.mx.x + sin(rotationY) * dot_distance_from_picture
+ var y = obj.mx.y
+ var z = obj.mx.z - cos(rotationY) * dot_distance_from_picture
+
+ dots.forEach(function(dot){
+ base.move_dot(dot, { x: x, y: y, z: z, rotationY: rotationY })
+ })
+ }
+
+ // move a dot .. to the initial position of the image
+ base.move_dot = function(dot, pos){
+ if (dot.side & TOP) {
+ pos.y += obj.dimensions.b / 2
+ }
+ if (dot.side & BOTTOM) {
+ pos.y -= obj.dimensions.b / 2
+ }
+ if (dot.side & LEFT) {
+ pos.x -= cos(rotationY) * (obj.dimensions.a) / 2
+ pos.z -= sin(rotationY) * (obj.dimensions.a) / 2
+ }
+ if (dot.side & RIGHT) {
+ pos.x += cos(rotationY) * (obj.dimensions.a) / 2
+ pos.z += sin(rotationY) * (obj.dimensions.a) / 2
+ }
+ dot.move(pos)
+ }
+
+ // pick a new object to focus on and show the dots
+ base.show = function(new_object) {
+ // if (obj === new_object) return
+ if (! new_object) return
+ base.obj = obj = new_object
+ base.add_dots()
+ base.move_dots()
+ }
+
+ // dismiss the dots on blur
+ var dotsHideTimeout;
+ base.defer_hide = function(){
+ clearTimeout(dotsHideTimeout)
+
+ dotsHideTimeout = setTimeout(function(){
+ if (Scenery.hovering || Scenery.resize.hovering || Scenery.mouse.down) return
+ Scenery.resize.hide()
+ }, dot_hide_delay)
+ }
+
+ base.hide = function () {
+ if (! obj) return
+ base.obj = obj = null
+ dots.forEach(function(dot){
+ scene.remove(dot)
+ })
+ }
+
+ base.bind = function(){
+ dots.forEach(function(dot){
+ Sculpture.mouse.bind_el(dot.el)
+ })
+ Sculpture.mouse.on("down", down)
+ Sculpture.mouse.on("drag", drag)
+ Sculpture.mouse.on("up", up)
+ }
+
+ base.unbind = function(){
+ dots.forEach(function(dot){
+ Sculpture.mouse.unbind_el(dot.el)
+ })
+ Sculpture.mouse.off("down", down)
+ Sculpture.mouse.off("drag", drag)
+ Sculpture.mouse.off("up", up)
+ }
+
+ function down (e, cursor){
+ var selection = dots.filter(function(dot){return e.target == dot.el})
+ if (! selection.length) return
+
+ selected_dot = selection[0]
+ dragging = true
+
+ naturalDimension = obj.naturalDimensions
+ dimension = obj.dimensions
+ position = new vec3(obj.mx.x, obj.mx.y, obj.mx.z)
+ oldState = obj.serialize()
+
+ if (obj.type == "text") {
+ naturalDimensionCopy = naturalDimension.clone()
+ positionCopy = position.clone()
+ }
+
+ document.body.classList.add("dragging")
+ }
+
+ function drag (e, cursor){
+ if (! dragging) return
+
+ var x_sign = selected_dot.side & LEFT ? -1 : selected_dot.side & RIGHT ? 1 : 0
+ var y_sign = selected_dot.side & TOP ? -1 : selected_dot.side & BOTTOM ? 1 : 0
+ var width = cursor.x.magnitude()
+ var height = cursor.y.magnitude()
+ var mag = cursor.magnitude()
+
+ if (abs(width) > abs(height)) {
+ mag = x_sign * mag * sign(width)
+ }
+ else {
+ mag = y_sign * mag * sign(height)
+ }
+
+ if (obj.type == "text") {
+ obj.mx.width = obj.media.width = naturalDimension.a = naturalDimensionCopy.a + (mag * 2)
+ obj.mx.height = obj.media.height = naturalDimension.b = naturalDimensionCopy.b + (mag * 2)
+ dimension.a = naturalDimension.a * obj.scale
+ dimension.b = naturalDimension.b * obj.scale
+ }
+ else {
+ obj.set_scale( ( dimension.a + mag ) / naturalDimension.a )
+ }
+
+ if (selected_dot.side & LEFT_RIGHT) {
+ obj.mx.x = position.a + cos(rotationY) * mag/2 * (x_sign)
+ obj.mx.z = position.c + sin(rotationY) * mag/2 * (x_sign)
+ }
+ if (selected_dot.side & TOP_BOTTOM) {
+ obj.mx.y = position.b - mag/2 * y_sign
+ }
+
+ base.move_dots()
+ obj.updateOutline()
+
+ app.controller.sculptureEditor.setDimensions()
+ }
+
+ function up (e, cursor){
+ if (! dragging) return
+ dragging = false
+ if (! editor.permissions.resize) { return }
+
+ obj.scale = obj.mx.ops.scale = obj.mx.scale
+ obj.dimensions.assign(obj.naturalDimensions).mul(obj.scale)
+
+ UndoStack.push({
+ type: 'update-sculpture',
+ undo: oldState,
+ redo: obj.serialize(),
+ })
+
+ // TODO: watch individual scenery object here
+ Minotaur.watch( app.router.editorView.settings )
+
+ document.body.classList.remove("dragging")
+ selected_dot = null
+ }
+} \ No newline at end of file
diff --git a/public/assets/javascripts/rectangles/engine/sculpture/types/_object.js b/public/assets/javascripts/rectangles/engine/sculpture/types/_object.js
new file mode 100644
index 0000000..2f593e8
--- /dev/null
+++ b/public/assets/javascripts/rectangles/engine/sculpture/types/_object.js
@@ -0,0 +1,176 @@
+Sculpture.types.base = Fiber.extend(function(base){
+
+ var exports = {
+
+ init: function(opt){
+ this.id = opt.id || Sculpture.uid("sculpture")
+ // this.move = new Sculpture.move (this)
+ this.media = opt.media
+ this.naturalDimensions = new vec3(this.media.width, this.media.height, this.media.width)
+ this.dimensions = new vec3(this.media.width, this.media.height, this.media.width)
+
+ this.move = new Sculpture.move (this)
+ this.billboard = true
+ this.backface = true
+
+ this.set_scale( opt.scale || this.media.scale || 1.0 )
+ this.position = new vec2(0,0)
+
+ this.isSculpture = true
+ },
+
+ set_scale: function(scale){
+ this.scale = scale || 1.0
+ if (this.mx) {
+ this.mx.scale = this.mx.ops.scale = this.scale
+ }
+ this.dimensions = this.naturalDimensions.clone().mul(this.scale)
+ },
+ set_depth: function(depth){
+ console.log(this.dimensions.c, this.naturalDimensions.c, depth)
+ this.dimensions.c = depth
+ this.naturalDimensions.c = depth / this.scale
+ },
+
+ place: function(opt){
+ if (opt.data) {
+ this.deserialize(opt.data)
+ }
+ else {
+ this.mx.move(opt.position)
+ }
+ },
+
+ bind: function(){
+ this.move.bind()
+ },
+
+ unbind: function(){
+ this.move.unbind()
+ },
+
+ setOutline: function(val){
+ this.outline = val
+ if (val && ! this.outlineEl) {
+ this.buildOutline()
+ }
+ this.outlineEl.el.style.display = val ? "block" : "none"
+ },
+ setOutlineColor: function(val){
+ this.outlineColor = val
+ if (this.outlineEl) {
+ this.outlineColor = this.outlineEl.el.style.borderColor = val
+ }
+ },
+ setBillboard: function(val){
+ this.billboard = val
+ if (this.billboard) {
+ this.mx.el.classList.add("backface-hidden")
+ this.mx.el.classList.remove("backface-visible")
+ this.mx.rotationY = cam.rotationY
+ }
+ else {
+ this.mx.el.classList.add("backface-visible")
+ this.mx.el.classList.remove("backface-hidden")
+ this.mx.rotationY = quantize(cam.rotationY, PI/2)
+ }
+ Sculpture.resize.move_dots()
+ },
+
+ buildOutline: function(){
+ this.outlineEl = new MX.Object3D(".mx-outline")
+ this.outlineEl.width = this.naturalDimensions.a
+ this.outlineEl.height = this.naturalDimensions.c
+ this.outlineEl.scale = this.mx.scale
+ this.outlineEl.rotationX = -PI/2
+ this.outlineEl.x = this.mx.x
+ this.outlineEl.y = sculpture_distance_from_floor
+ this.outlineEl.z = this.mx.z
+ this.outlineEl.el.style.borderColor = this.outlineColor || "#000000"
+ scene.add(this.outlineEl)
+ },
+ updateOutline: function(){
+ if (! this.outline) { return }
+ if (! this.outlineEl) {
+ this.buildOutline()
+ }
+ this.outlineEl.x = this.mx.x
+ this.outlineEl.y = sculpture_distance_from_floor
+ this.outlineEl.z = this.mx.z
+ this.outlineEl.width = this.naturalDimensions.a
+ this.outlineEl.height = this.naturalDimensions.c
+
+ this.outlineEl.scale = this.mx.scale
+ },
+
+ remove: function(){
+ if (this.removed) return
+ this.removed = true
+
+ UndoStack.push({
+ type: 'destroy-sculpture',
+ undo: this.serialize(),
+ redo: { id: this.id },
+ })
+
+ // TODO: watch individual scenery object here
+ Minotaur.watch( app.router.editorView.settings )
+
+ Sculpture.remove(this.id)
+
+ Sculpture.resize.hide()
+ if (app.controller.sculptureEditor) {
+ app.controller.sculptureEditor.tainted = false
+ app.controller.sculptureEditor.hide()
+ }
+ },
+
+ destroy: function(){
+ this.unbind()
+ scene.remove(this.mx)
+ this.mx.media = null
+ this.mx.ops = null
+ this.mx = null
+ this.move = null
+ this.media = null
+ this.dimensions = null
+ this.naturalDimensions = null
+ this.wall = null
+ this.bounds = null
+ this.center = null
+ },
+
+ serialize: function(){
+ var data = {
+ id: this.id,
+ dimensions: this.dimensions.serialize(),
+ position: app.position(this.mx),
+ scale: this.scale,
+ media: this.media,
+ billboard: this.billboard,
+ outline: this.outline,
+ outlineColor: this.outlineColor || "#000000",
+ backface: this.backface,
+ }
+ return data
+ },
+
+ deserialize: function(data){
+ this.mx.move(data.position)
+ this.mx.ops.width = data.dimensions.a
+ this.mx.ops.height = data.dimensions.b
+ this.billboard = data.billboard
+ this.outline = data.outline
+ this.outlineColor = data.outlineColor || "#000000"
+ this.backface = data.backface
+ if (! this.backface) this.mx.el.classList.remove("backface-visible")
+ this.dimensions.deserialize(data.dimensions)
+ if (this.outline) {
+ this.buildOutline()
+ }
+ },
+ }
+
+ return exports
+
+})
diff --git a/public/assets/javascripts/rectangles/engine/sculpture/types/image.js b/public/assets/javascripts/rectangles/engine/sculpture/types/image.js
new file mode 100644
index 0000000..1a53f5b
--- /dev/null
+++ b/public/assets/javascripts/rectangles/engine/sculpture/types/image.js
@@ -0,0 +1,38 @@
+
+Sculpture.types.image = Sculpture.types.base.extend(function(base){
+
+ var exports = {
+
+ type: 'image',
+
+ init: function(opt){
+
+ opt.scale = opt.scale || (opt.data && opt.data.scale) || DEFAULT_PICTURE_WIDTH / max(DEFAULT_PICTURE_WIDTH, opt.media.width)
+
+ base.init.call(this, opt)
+
+ this.build(opt)
+ this.bind()
+ this.place(opt)
+ },
+
+ build: function(opt){
+ this.mx = new MX.Image({
+ src: this.media.url,
+ scale: this.scale,
+ media: this.media,
+ backface: true,
+ })
+
+ if (opt.position) {
+ opt.position.y = opt.position.y || this.scale * this.media.height/2 + sculpture_distance_from_floor
+ opt.position.rotationY = opt.position.rotationY || scene.camera.rotationY
+ }
+
+ scene.add( this.mx )
+ },
+
+ }
+
+ return exports
+})
diff --git a/public/assets/javascripts/rectangles/engine/shapes/ortho.js b/public/assets/javascripts/rectangles/engine/shapes/ortho.js
new file mode 100644
index 0000000..163f646
--- /dev/null
+++ b/public/assets/javascripts/rectangles/engine/shapes/ortho.js
@@ -0,0 +1,66 @@
+// An OrthoPolyline is a Polyline where all angles are 90 degrees.
+
+if (! ('window' in this) ) {
+ var Polyline = require("./polyline.js")
+}
+
+var OrthoPolyline = Polyline.extend(function(base){
+ var exports = {}
+ exports.type = function(){
+ return "ortho"
+ }
+ exports.instantiate = function(){
+ return new OrthoPolyline
+ }
+ exports.canCloseWith = function(p){
+ return (this.points.length > 2 && this.points[0].distanceTo( p ) < 10/map.zoom)
+ }
+ exports.draw = function(ctx, fillStyle, strokeStyle){
+ var points = this.points
+ if (! points.length) return
+ if (points.length == 1) {
+ ctx.fillStyle = "#f80"
+ map.draw.dot_at(this.points[0].a, points[0].b, 5)
+ }
+ if (points.length > 1) {
+ ctx.fillStyle = fillStyle
+ ctx.strokeStyle = strokeStyle
+ ctx.lineWidth = 2 / map.zoom
+ ctx.beginPath()
+ ctx.moveTo(points[0].a, points[0].b)
+ points.forEach(function(point, i){
+ i && ctx.lineTo(point.a, point.b)
+ })
+ strokeStyle && ctx.stroke()
+ if (! map.ui.placing || this.closed) {
+ fillStyle && ctx.fill()
+ }
+ }
+ }
+ exports.translateSegment = function(src, dest, dx, dy) {
+ if (src[0].a == src[1].a) {
+ dest[0].a = src[0].a + dx
+ dest[1].a = src[1].a + dx
+ if (src.length == 3) {
+ dest[2].a = src[2].a + dx
+ }
+ }
+ else {
+ dest[0].b = src[0].b + dy
+ dest[1].b = src[1].b + dy
+ if (src.length == 3) {
+ dest[2].b = src[2].b + dy
+ }
+ }
+ }
+ exports.close = function(){
+ this.points[this.points.length] = this.points[0]
+ this.closed = true
+ }
+ return exports
+})
+
+
+if (! ('window' in this) ) {
+ module.exports = OrthoPolyline
+}
diff --git a/public/assets/javascripts/rectangles/engine/shapes/polyline.js b/public/assets/javascripts/rectangles/engine/shapes/polyline.js
new file mode 100644
index 0000000..b2cd92f
--- /dev/null
+++ b/public/assets/javascripts/rectangles/engine/shapes/polyline.js
@@ -0,0 +1,212 @@
+// A Polyline is a set of points inputted by the user using the V2 editor to trace a blueprint.
+// Additionally, it manages a set of MX objects which correspond to the walls in 3D.
+// In this way, it attempts to bridge the 2D (canvas, imperative) and 3D (css, declarative) views.
+
+if (! ('window' in this) ) {
+ var Fiber = require("../../../vendor/bower_components/fiber/src/fiber.js")
+ var vec2 = require("../../models/vec2")
+}
+
+var Polyline = Fiber.extend(function(base){
+ var exports = {}
+ exports.init = function(){
+ this.points = []
+ this.mx_points = []
+ this.closed = false
+ }
+ exports.type = function(){
+ return "polyline"
+ }
+ exports.instantiate = function(){
+ return new Polyline
+ }
+ exports.add = function(p){
+ this.points.push( p )
+ this.mx_points.push( new MX.Point(p) )
+ }
+ exports.firstPoint = function(){
+ return this.points[0]
+ }
+ exports.lastPoint = function(){
+ return this.points[this.points.length-1]
+ }
+ exports.canCloseWith = function(p){
+ return (this.points.length > 2 && this.points[0].distanceTo( p ) < 10/map.zoom)
+ }
+ exports.getHeadAtIndex = function(index){
+ if (index == 0) { return null }
+ if (index == this.points.length-1) { return this.clone() }
+ var head = this.instantiate()
+ head.points = this.points.slice(0, index+1)
+ return head
+ }
+ exports.getTailAtIndex = function(index){
+ if (index == this.points.length-1) { return null }
+ if (index == 0) { return this.clone() }
+ var tail = this.instantiate()
+ tail.points = this.points.slice(index, this.points.length)
+ return tail
+ }
+ exports.clone = function(){
+ var clone = this.instantiate()
+ clone.points = this.points.concat()
+ }
+ exports.getSegment = function(segment){
+ var seg = [
+ this.points[segment.head],
+ this.points[segment.tail],
+ ]
+ if (segment.head == 0) {
+ seg.push( this.lastPoint() )
+ }
+ else if (segment.tail == this.points.length-1) {
+ seg.push( this.firstPoint() )
+ }
+ return seg
+ }
+ exports.cloneSegment = function(segment){
+ return this.getSegment(segment).map(function(point){ return point.clone() })
+ }
+ exports.translateSegment = function(src, dest, dx, dy){
+ dest[0].a = src[0].a + dx
+ dest[0].b = src[0].b + dy
+ dest[1].a = src[1].a + dx
+ dest[1].b = src[1].b + dy
+ if (src.length == 3) {
+ dest[2].a = src[2].a + dx
+ dest[2].b = src[2].b + dy
+ }
+ }
+ exports.hasPointNear = function(p){
+ var point
+ for (var i = 0; i < this.points.length; i++){
+ point = this.points[i]
+ if (point.distanceTo( p ) < 10/map.zoom) {
+ return point
+ }
+ }
+ return null
+ }
+ exports.hasEndPointNear = function(p){
+ if (this.closed || ! this.points.length) return null
+ if (this.firstPoint().distanceTo( p ) < 10/map.zoom) {
+ return this.firstPoint()
+ }
+ if (this.lastPoint().distanceTo( p ) < 10/map.zoom) {
+ return this.lastPoint()
+ }
+ return null
+ }
+ exports.hasSegmentNear = function(p, min_dist){
+ var p1, p2, d1, d2, sum, rat
+ var dx, dy, new_x, new_y, x, y, closest_distance = min_dist || Infinity
+ var closest_i = -1
+ var points = this.points
+ var p1, p2 = points[0]
+ for (var i = 1; i < points.length; i++) {
+ p1 = p2
+ p2 = points[i]
+ d1 = p2.a - p1.a
+ d2 = p2.b - p1.b
+ sum = d1*d1 + d2*d2
+ rat = ((p.a - p1.a) * d1 + (p.b - p1.b) * d2) / sum
+ rat = rat < 0 ? 0 : rat < 1 ? rat : 1
+ new_x = p1.a + rat * d1
+ new_y = p1.b + rat * d2
+ dx = new_x - p.a
+ dy = new_y - p.b
+ sum2 = sqrt(dx*dx+dy*dy)
+ if (sum2 < closest_distance) {
+ x = new_x
+ y = new_y
+ closest_distance = sum2
+ closest_i = i
+ }
+ }
+ if (closest_i == -1) return null
+ return {
+ x: x,
+ y: y,
+ distance: closest_distance,
+ head: closest_i-1,
+ tail: closest_i,
+ }
+ }
+ exports.draw = function(ctx, fillStyle, strokeStyle){
+ var points = this.points
+ if (! points.length) return
+ if (points.length == 1) {
+ ctx.fillStyle = "#f80"
+ map.draw.dot_at(this.points[0].a, points[0].b, 5)
+ }
+ if (points.length > 1) {
+ ctx.fillStyle = fillStyle
+ ctx.strokeStyle = strokeStyle
+ ctx.lineWidth = 2 / map.zoom
+ ctx.beginPath()
+ ctx.moveTo(points[0].a, points[0].b)
+ points.forEach(function(point, i){
+ i && ctx.lineTo(point.a, point.b)
+ })
+ strokeStyle && ctx.stroke()
+ if (! map.ui.placing || this.closed) {
+ fillStyle && ctx.fill()
+ }
+ }
+ }
+ exports.draw_line = function (ctx, p){
+ var last = this.points[this.points.length-1]
+ ctx.strokeStyle = "#f80"
+ ctx.lineWidth = 2 / map.zoom
+ ctx.beginPath()
+ ctx.moveTo(last.a, last.b)
+ ctx.lineTo(p.a, p.b)
+ ctx.stroke()
+ }
+ exports.close = function(){
+ this.points[this.points.length] = this.points[0]
+ this.closed = true
+ }
+ exports.build = function(){
+ this.mx_points && this.mx_points.forEach(function(mx){ scene.remove(mx) })
+ this.mx = new MX.Polyline(this)
+ }
+ exports.rebuild = function(){
+ this.mx.rebuild()
+ }
+ exports.getSegments = function(){
+ if (this.points.length == 1) {
+ return []
+ }
+ var segments = []
+ for (var i = 1; i < this.points.length; i++) {
+ segments.push( [ this.points[i-1], this.points[i] ] )
+ }
+ return segments
+ }
+ exports.serialize = function(){
+ return {
+ type: this.type(),
+ closed: this.closed,
+ points: this.points.map(function(point){ return [point.a, point.b] }),
+ }
+ }
+ exports.deserialize = function(data){
+ this.closed = data.closed || false
+ this.points = (data.points || data).map(function(point){ return new vec2(point[0], point[1]) })
+ }
+ exports.reset = function(){
+ this.mx_points.forEach(function(mx){ scene.remove(mx) })
+ this.mx_points.length = 0
+ this.points.length = 0
+ }
+ exports.destroy = function(){
+ this.reset()
+ this.mx && this.mx.destroy()
+ }
+ return exports
+})
+
+if (! ('window' in this) ) {
+ module.exports = Polyline
+}
diff --git a/public/assets/javascripts/rectangles/engine/shapes/regionlist.js b/public/assets/javascripts/rectangles/engine/shapes/regionlist.js
new file mode 100644
index 0000000..8c9e732
--- /dev/null
+++ b/public/assets/javascripts/rectangles/engine/shapes/regionlist.js
@@ -0,0 +1,240 @@
+// This algorithm takes the polylines from ShapeList as input and produces
+// a set of Rooms which can be used by the existing V1 mover and editor.
+
+// The algorithm assumes that
+// 1) all angles are orthogonal
+// 2) all polylines are closed
+
+if (! ('window' in this) ) {
+ var Fiber = require("../../../vendor/bower_components/fiber/src/fiber.js")
+ var vec2 = require("../../models/vec2")
+ var Rect = require("../../models/rect")
+ var sort = require("../../util/sort")
+}
+
+var RegionList = (function(){
+
+ var RegionList = {}
+ var regions = RegionList.regions
+
+ // Build a list of regions from the existing shapes.
+ // Operates on all the shapes at once.
+ RegionList.build = function(){
+ var segments = RegionList.getSortedSegments()
+ return RegionList.buildRoomsFromSegments(segments)
+ }
+
+ // Build a list of regions from the individual shapes.
+ // Same, but operates on the shapes individually
+ RegionList.buildByShape = function(){
+ var shapes = RegionList.getSortedSegmentsByShape()
+ var region_lists = shapes.map(function(shape){
+ return RegionList.buildRoomsFromSegments(shape.segments)
+ })
+ var regions = []
+ return regions.concat.apply(regions, region_lists);
+ }
+
+ RegionList.buildRoomsFromSegments = function(segments){
+
+ var rooms = []
+ var seen_rooms = {}
+ var open_segments = []
+
+ var segment, open_segment, x_segment, y_segments, overlapped, seen_segments
+
+ // first pass: generate rooms from the vertical segments only
+ for (var i = 0; i < segments.length; i++) {
+ segment = segments[i]
+ if (! segment.isVertical()) {
+ continue
+ }
+
+ // check all the "open segments" we know about, i.e. rooms where we've only found
+ // the right wall.
+ overlapped = false
+ for (var j = 0; j < open_segments.length; j++) {
+ open_segment = open_segments[j]
+
+ // if these two segments overlap each other, then there is a room between them.
+ if (segment.y.overlaps(open_segment.y)) {
+ overlapped = true
+ open_segments.splice(j--, 1)
+
+ // the X part of the room will be the span between these two vertical segments'
+ // X components. the Y part of the room is the "split" or subdivision between
+ // the two horizontal vectors.
+
+ // the split function is non-commutative,
+ // so we need to call A split B and B split A,
+ // then dedupe the segments we got back..
+ x_segment = new vec2( open_segment.x.a, segment.x.b )
+ y_segments = open_segment.y.split(segment.y, 0, 0)
+
+ seen_segments = {}
+
+ // check each of the splits.. if the two segments overlap, then we definitely
+ // have a room here.
+ y_segments.forEach(function(seg){
+ seen_segments[ seg[0]+"" ] = true
+ var room = new Rect( x_segment, seg[0] )
+
+ if (seen_rooms[ room+"" ]) return
+ seen_rooms[ room+"" ] = true
+
+ room.sides = 0
+
+ rooms.push(room)
+ var new_seg = new Rect( segment.x, seg[0] )
+ open_segments.unshift(new_seg)
+ j++
+ })
+
+ // splitting the other way..
+ y_segments = segment.y.split(open_segment.y, 0, 0)
+ y_segments.forEach(function(seg){
+ if (seen_segments[ seg[0]+"" ]) return;
+ var new_seg = new Rect( segment.x, seg[0] )
+ open_segments.unshift(new_seg)
+ j++
+ })
+ }
+ }
+
+ // if we have overlap, then re-sort the open segments Y-wise
+ // and (again) dedupe..
+ if (overlapped) {
+ open_segments = open_segments.sort(function(a,b){
+ if (a.y.a < b.y.a) { return -1 }
+ if (a.y.a == b.y.a) { return 0 }
+ if (a.y.a > b.y.a) { return 1 }
+ })
+
+ if (open_segments.length < 2) { continue }
+
+ for (var k = 1; k < open_segments.length; k++) {
+ if (open_segments[k-1].y.containsVec(open_segments[k].y)) {
+ open_segments.splice(k--, 1)
+ }
+ else if (open_segments[k-1].y.overlaps(open_segments[k].y)) {
+ open_segments[k].y = open_segments[k].y.clone()
+ open_segments[k].y.a = open_segments[k-1].y.b
+ }
+ }
+ }
+ // if we don't have any overlap, then this is a new open segment.
+ else {
+ open_segments.push(segment)
+ }
+ }
+
+ var splits, splitter
+
+ // second pass: now that we have a bunch of rooms, assign sides to all of them.
+ // sides are used in the "mover" script to do bounds checking.
+ for (var i = 0; i < segments.length; i++) {
+ segment = segments[i]
+ var horizontal = segment.isHorizontal(), vertical = segment.isVertical()
+ for (var r = 0; r < rooms.length; r++){
+ room = rooms[r]
+
+ // vertical segments determine the left and right edges of the room, fairly simply.
+ if (vertical) {
+ if (segment.y.containsVec(room.y)) {
+ if (segment.x.a == room.x.a) {
+ room.sides |= LEFT
+ }
+ if (segment.x.a == room.x.b) {
+ room.sides |= RIGHT
+ }
+ }
+ }
+
+ // horizontal segments determine the front and back edges of the room.
+ if (horizontal) {
+ if (segment.x.containsVec(room.x)) {
+ if (segment.y.a == room.y.a) {
+ room.sides |= FRONT
+ }
+ if (segment.y.a == room.y.b) {
+ room.sides |= BACK
+ }
+ }
+
+ // however, since we did not split on horizontal segments, our rooms may
+ // only have partial overlap with these segments, in which case we need to
+ // split the rooms apart.
+ else if ((segment.y.a == room.y.a || segment.y.a == room.y.b) && room.x.overlaps(segment.x)) {
+
+ // split the room across the segment. preserve whether or not we know the
+ // room borders a segment on the left or right.
+ splits = room.x.split(segment.x, room.sides & LEFT, room.sides & RIGHT)
+ rooms.splice(r--, 1)
+ for (var k = 0; k < splits.length; k++) {
+ splitter = splits[k]
+ var new_room = new Rect( splitter[0], room.y )
+ new_room.sides = splitter[1] | ( room.sides & FRONT_BACK )
+ if (segment.x.overlaps( splitter[0] )) {
+ if (segment.y.a == new_room.y.a) {
+ new_room.sides |= FRONT
+ }
+ if (segment.y.a == new_room.y.b) {
+ new_room.sides |= BACK
+ }
+ }
+ rooms.unshift(new_room)
+ r++
+ }
+ }
+
+ }
+ }
+ }
+
+ return rooms
+ }
+
+ // Gets a list of polylines from the ShapeList and sorts the segments.
+ RegionList.getSortedSegments = function(){
+ // get a list of all segments from these polylines
+ var segments = shapes.getAllSegments()
+
+ segments = segments.map(RegionList.segmentsToRects)
+
+ return sort.rects_by_position(segments)
+ }
+
+ RegionList.getSortedSegmentsByShape = function(){
+ return shapes.getAllShapeSegments().map(function(shape){
+ var segments = shape.map(RegionList.segmentsToRects)
+ return {
+ shape: shape,
+ segments: sort.rects_by_position(segments)
+ }
+ })
+ }
+
+ // re-orient a segment so it's either facing up or right and make it into a rect
+ RegionList.segmentsToRects = function(segment){
+ // vertical
+ if (segment[0].a == segment[1].a) {
+ if (segment[0].b > segment[1].b) {
+ segment.push(segment.shift())
+ }
+ }
+ // horizontal
+ else if (segment[0].b == segment[1].b) {
+ if (segment[0].a > segment[1].a) {
+ segment.push(segment.shift())
+ }
+ }
+ return new Rect( segment[0].a, segment[0].b, segment[1].a, segment[1].b )
+ }
+
+ if (! ('window' in this) ) {
+ module.exports = RegionList
+ }
+
+ return RegionList
+
+})() \ No newline at end of file
diff --git a/public/assets/javascripts/rectangles/engine/shapes/shapelist.js b/public/assets/javascripts/rectangles/engine/shapes/shapelist.js
new file mode 100644
index 0000000..21beb76
--- /dev/null
+++ b/public/assets/javascripts/rectangles/engine/shapes/shapelist.js
@@ -0,0 +1,124 @@
+// The ShapeList manages the list of polylines which form a V2 layout.
+
+if (! ('window' in this) ) {
+ var Fiber = require("../../../vendor/bower_components/fiber/src/fiber.js")
+ var Polyline = require("./polyline.js")
+ var OrthoPolyline = require("./ortho.js")
+}
+
+var ShapeList = Fiber.extend(function(base){
+ var exports = {}
+ exports.init = function(){
+ this.shapes = []
+ this.workline = null
+ }
+ exports.add = function(shape){
+ this.shapes.push(shape)
+ }
+ exports.remove = function(shape){
+ var index = this.shapes.indexOf(shape)
+ if (index !== -1) {
+ this.shapes.splice(index, 1)
+ }
+ }
+ exports.destroy = function(){
+ this.shapes.forEach(function(shape){
+ shape.destroy()
+ })
+ this.shapes = []
+ }
+ exports.count = function(){
+ return this.shapes.length
+ }
+ exports.removeSegment = function (segment){
+ var shape = segment.shape
+ var head = shape.getHeadAtIndex(segment.head)
+ var tail = shape.getTailAtIndex(segment.tail)
+ this.remove(shape)
+ shape.destroy()
+ if (head) {
+ this.add(head)
+ head.build()
+ }
+ if (tail) {
+ this.add(tail)
+ tail.build()
+ }
+ }
+ exports.findClosestPoint = function (p){
+ var point
+ for (var i = 0; i < this.shapes.length; i++) {
+ point = this.shapes[i].hasPointNear(p)
+ if (point) return { point: point, shape: this.shapes[i] }
+ }
+ return null
+ }
+ exports.findClosestEndPoint = function (p){
+ var point
+ for (var i = 0; i < this.shapes.length; i++) {
+ point = this.shapes[i].hasEndPointNear(p)
+ if (point) return { point: point, shape: this.shapes[i] }
+ }
+ return null
+ }
+ exports.findClosestSegment = function (p){
+ var segment = null, closest_segment = null
+ for (var i = 0; i < this.shapes.length; i++) {
+ segment = this.shapes[i].hasSegmentNear(p, 10)
+ if (segment && (! closest_segment || segment.distance < closest_segment.distance)) {
+ closest_segment = segment
+ closest_segment.shape = this.shapes[i]
+ }
+ }
+ return closest_segment
+ }
+ exports.forEach = function(fn){
+ this.shapes.forEach(fn)
+ }
+ exports.getAllShapeSegments = function(){
+ return this.shapes.map(function(shape){ return shape.getSegments() })
+ }
+ exports.getAllSegments = function(){
+ var segments = []
+ this.shapes.forEach(function(shape){
+ segments = segments.concat( shape.getSegments() )
+ })
+ return segments
+ }
+ exports.draw = function(ctx, fillStyle, strokeStyle) {
+ this.shapes.forEach(function(shape){
+ shape.draw(ctx, fillStyle, strokeStyle)
+ })
+ }
+ exports.serialize = function(){
+ return this.shapes.map(function(shape){
+ return shape.serialize()
+ })
+ }
+ exports.deserialize = function(data){
+ data && data.forEach(function(shape_data){
+ var line
+ switch (shape_data.type) {
+ case 'ortho':
+ line = new OrthoPolyline()
+ break
+ default:
+ line = new Polyline()
+ break
+ }
+ line.deserialize(shape_data)
+ shapes.add(line)
+ }.bind(this))
+ }
+ exports.build = function(){
+ this.shapes.forEach(function(shape){
+ shape.build()
+ })
+ }
+ return exports
+})
+
+if (! ('window' in this) ) {
+ shapes = new ShapeList
+ module.exports = shapes
+}
diff --git a/public/assets/javascripts/rectangles/models/floor.js b/public/assets/javascripts/rectangles/models/floor.js
index 51537f3..63eebcc 100644
--- a/public/assets/javascripts/rectangles/models/floor.js
+++ b/public/assets/javascripts/rectangles/models/floor.js
@@ -68,8 +68,43 @@
}
return
}
+
- if (Scenery.nextWallpaper) {
+ var offset = offsetFromPoint(e, mx.el)
+ if (! offset) { return }
+
+ var x = mx.x + mx.width * (offset.left-0.5)
+ var z = mx.z + mx.height * (0.5-offset.top)
+
+ if (Scenery.nextMedia) {
+ e.preventDefault()
+
+ var sculpture = Sculpture.addNext({
+ position: { x: x, y: 0, z: z },
+ })
+
+ // scenery was not placed
+ if (! sculpture) {
+ e.stopPropagation()
+ return
+ }
+
+ app.controller.toolbar.resetPermissions()
+ Sculpture.resize.show(sculpture)
+ Sculpture.hovering = true
+
+ // app.controller.pick(sculpture)
+
+ UndoStack.push({
+ type: 'create-sculpture',
+ undo: { id: sculpture.id },
+ redo: sculpture.serialize(),
+ })
+
+ // TODO: watch individual sculpture object here
+ Minotaur.watch( app.router.editorView.settings )
+ }
+ else if (Scenery.nextWallpaper) {
var oldState = base.serialize()
base.wallpaper(Scenery.nextWallpaper)
// Scenery.nextWallpaper = null
@@ -98,7 +133,7 @@
this.mx.reverse()
}
}
-
+
Floor.prototype.color = function(color){
this.$els.css("background-color", color)
}
@@ -118,7 +153,7 @@
background.scale = background.scale || 1
this.background = background
- this.background.src = this.background.src.replace(/url\(\"?\'?/,"").replace(/\"?\'?\)/,"")
+ this.background.src = this.background.src.replace(/url\(\"?\'?/,"").replace(/\"?\'?\)/,"")
if (this.background.src == "none") {
this.wallpaperLoad(this.background.src)
diff --git a/public/assets/javascripts/rectangles/models/rect.js b/public/assets/javascripts/rectangles/models/rect.js
index c667cf5..a4756ed 100644
--- a/public/assets/javascripts/rectangles/models/rect.js
+++ b/public/assets/javascripts/rectangles/models/rect.js
@@ -39,6 +39,12 @@
Rect.prototype.clone = function(){
return new Rect( this.x.clone(), this.y.clone() )
}
+ Rect.prototype.x_component = function(){
+ return new vec2( this.x.a, this.y.a )
+ }
+ Rect.prototype.y_component = function(){
+ return new vec2( this.x.b, this.y.b )
+ }
Rect.prototype.assign = function(r) {
this.x.assign(r.x)
this.y.assign(r.y)
@@ -56,6 +62,12 @@
Rect.prototype.maxDimension = function(){
return abs(this.width) > abs(this.height) ? this.width : this.height
}
+ Rect.prototype.isVertical = function(){
+ return this.x.isPoint()
+ }
+ Rect.prototype.isHorizontal = function(){
+ return this.y.isPoint()
+ }
Rect.prototype.mul = function(n){
this.x.mul(n)
diff --git a/public/assets/javascripts/rectangles/models/room.js b/public/assets/javascripts/rectangles/models/room.js
index 26bf055..1abe2ba 100644
--- a/public/assets/javascripts/rectangles/models/room.js
+++ b/public/assets/javascripts/rectangles/models/room.js
@@ -31,10 +31,14 @@
var Room = function(opt){
this.id = opt.id || Rooms.uid("room_")
this.rect = opt.rect
- this.regions = []
+ this.regions = opt.regions || []
this.height = opt.height || 200
this.focused = false
+
+ this.mx_walls = []
+ this.mx_floor = []
+ this.mx_ceiling = []
}
Room.prototype.copy = function(){
@@ -125,6 +129,7 @@
Room.prototype.collidesDisc = function(src, dest, radius){
var x = dest.x, y = dest.z
var collision = 0, wall_collision, contains_x, contains_y
+
this.regions.forEach(function(r){
if (! r.sides) return
diff --git a/public/assets/javascripts/rectangles/models/vec2.js b/public/assets/javascripts/rectangles/models/vec2.js
index 14d0e6b..8942d92 100644
--- a/public/assets/javascripts/rectangles/models/vec2.js
+++ b/public/assets/javascripts/rectangles/models/vec2.js
@@ -43,6 +43,9 @@
vec2.prototype.eq = function(v){
return this.a == v.a && this.b == v.b
}
+ vec2.prototype.isPoint = function(){
+ return this.a == this.b
+ }
vec2.prototype.add = function(n){
this.a += n
this.b += n
@@ -80,6 +83,11 @@
this.a = Math.round(this.a)
this.b = Math.round(this.b)
}
+ vec2.prototype.distanceTo = function(v){
+ var va = (this.a - v.a)
+ var vb = (this.b - v.b)
+ return Math.sqrt( va*va + vb*vb )
+ }
vec2.prototype.setPosition = function(n){
var len = this.length()
this.a = n
@@ -103,6 +111,12 @@
vec2.prototype.containsDisc = function(n,r){
return this.a <= n-r && n+r <= this.b
}
+ vec2.prototype.containsVec = function(v){
+ return this.a <= v.a && v.b <= this.b
+ }
+ vec2.prototype.containsCenterVec = function(v){
+ return this.a < v.a && v.b < this.b
+ }
vec2.prototype.clamp = function(n){
return clamp(n, this.a, this.b)
}
@@ -200,13 +214,13 @@
}
vec2.prototype.toString = function(){
- return "[" + round(this.a) + " " + round(this.b) + "]"
+ return "[" + Math.round(this.a) + " " + Math.round(this.b) + "]"
}
vec2.prototype.exactString = function(){
return "[" + this.a + " " + this.b + "]"
}
vec2.prototype.serialize = function(){
- return [ round(this.a), round(this.b) ]
+ return [ Math.round(this.a), Math.round(this.b) ]
}
vec2.prototype.deserialize = function(data){
this.a = data[0]
diff --git a/public/assets/javascripts/rectangles/models/vec3.js b/public/assets/javascripts/rectangles/models/vec3.js
index c44dfe6..b3825a9 100644
--- a/public/assets/javascripts/rectangles/models/vec3.js
+++ b/public/assets/javascripts/rectangles/models/vec3.js
@@ -32,3 +32,28 @@ vec3.prototype.apply_projection = function (m) {
return this;
}
+
+vec3.prototype.serialize = function(){
+ return [ round(this.a), round(this.b), round(this.c) ]
+}
+vec3.prototype.deserialize = function(data){
+ this.a = data[0]
+ this.b = data[1]
+ this.c = data[2] || data[0]
+ return this
+}
+vec3.prototype.clone = function(){
+ return new vec3(this.a, this.b, this.c)
+}
+vec3.prototype.assign = function(v){
+ this.a = v.a
+ this.b = v.b
+ this.c = v.c
+ return this
+}
+vec3.prototype.mul = function(n) {
+ this.a *= n
+ this.b *= n
+ this.c *= n
+ return this
+} \ No newline at end of file
diff --git a/public/assets/javascripts/rectangles/models/wall.js b/public/assets/javascripts/rectangles/models/wall.js
index fc1ccbe..cf3cea8 100644
--- a/public/assets/javascripts/rectangles/models/wall.js
+++ b/public/assets/javascripts/rectangles/models/wall.js
@@ -10,6 +10,11 @@
vec2 = require('./vec2')
Rect = require('./rect')
UidGenerator = require('../util/uid')
+ wall_rotation = {}
+ wall_rotation[FRONT] = PI
+ wall_rotation[BACK] = 0
+ wall_rotation[LEFT] = HALF_PI
+ wall_rotation[RIGHT] = -HALF_PI
}
var Wall = function(opt){
@@ -18,6 +23,7 @@
this.edge = opt.edge
this.side = opt.side
this.surface = opt.surface
+ this.rotationY = ('rotationY' in opt) ? opt.rotationY : wall_rotation[opt.side]
this.mx = opt.mx
this.background = { src: "none" }
}
@@ -89,7 +95,7 @@
mx_dot.move(mx_pos)
mx_dot.width = 5
mx_dot.height = 5
- mx_dot.rotationY = wall_rotation[base.side]
+ mx_dot.rotationY = base.rotationY
mx_dot.el.style.backgroundColor = "red"
scene.add(mx_dot)
}
@@ -192,7 +198,7 @@
}
Wall.prototype.serialize = function(){
- return {
+ return {
id: this.id,
background: this.background,
}
@@ -236,7 +242,7 @@
x: x,
y: position.b + dimension.b / 2,
z: z,
- rotationY: wall_rotation[ this.side ],
+ rotationY: this.rotationY,
}
}
Wall.prototype.mxToPosition = function(mx, dimension) {
@@ -288,7 +294,7 @@
background.scale = background.scale || 1
this.background = background
- this.background.src = this.background.src.replace(/url\(\"?\'?/,"").replace(/\"?\'?\)/,"")
+ this.background.src = this.background.src.replace(/url\(\"?\'?/,"").replace(/\"?\'?\)/,"")
if (this.background.src == "none") {
this.wallpaperLoad(this.background.src)
diff --git a/public/assets/javascripts/rectangles/util/constants.js b/public/assets/javascripts/rectangles/util/constants.js
index 3bc314c..522689b 100644
--- a/public/assets/javascripts/rectangles/util/constants.js
+++ b/public/assets/javascripts/rectangles/util/constants.js
@@ -22,10 +22,11 @@ var height_min = 200,
resize_margin = 8,
cursor_amp = 1.5,
DEFAULT_PICTURE_WIDTH = 350,
- MAP_GRID_SIZE = 360 // 10 feet
+ MAP_GRID_SIZE = 36 // 10 feet
var painting_distance_from_wall = 10,
- dot_distance_from_picture = 3
+ dot_distance_from_picture = 3,
+ sculpture_distance_from_floor = 3
var dot_hide_delay = 50, // ms
dot_side = 20
diff --git a/public/assets/javascripts/rectangles/util/coords.js b/public/assets/javascripts/rectangles/util/coords.js
index 74b7fda..ff56199 100644
--- a/public/assets/javascripts/rectangles/util/coords.js
+++ b/public/assets/javascripts/rectangles/util/coords.js
@@ -30,4 +30,4 @@ function offsetFromPoint(event, element) {
return 'left: ' + l + '%, top: ' + t + '%';
}
} : null;
-}
+} \ No newline at end of file
diff --git a/public/assets/javascripts/rectangles/util/measurement.js b/public/assets/javascripts/rectangles/util/measurement.js
index d6a0b35..6346eac 100644
--- a/public/assets/javascripts/rectangles/util/measurement.js
+++ b/public/assets/javascripts/rectangles/util/measurement.js
@@ -28,6 +28,10 @@ function measurementToString( n ) {
case 'ft':
ft = floor(n / 36)
inch = abs(round((n % 36) / 3))
+ if (inch == 12) {
+ inch = 0
+ ft += 1
+ }
s = ft + "'"
if (inch > 0) {
s += " " + inch + '"'
diff --git a/public/assets/javascripts/rectangles/util/mouse.js b/public/assets/javascripts/rectangles/util/mouse.js
index cb36038..2b98cee 100644
--- a/public/assets/javascripts/rectangles/util/mouse.js
+++ b/public/assets/javascripts/rectangles/util/mouse.js
@@ -76,6 +76,14 @@ function mouse (opt) {
window.addEventListener("mousemove", base.mousemove)
window.addEventListener("mouseup", base.mouseup)
}
+ base.unbind = function(){
+ if (opt.el) {
+ opt.el.removeEventListener("mousedown", base.mousedown)
+ opt.el.removeEventListener("contextmenu", base.contextmenu)
+ }
+ window.removeEventListener("mousemove", base.mousemove)
+ window.removeEventListener("mouseup", base.mouseup)
+ }
base.bind_el = function(el){
el.addEventListener("mousedown", base.mousedown)
@@ -126,7 +134,7 @@ function mouse (opt) {
}
var x = pos.a, y = pos.b
-
+
if (base.down) {
base.cursor.x.b = x
base.cursor.y.b = y
diff --git a/public/assets/javascripts/rectangles/util/wheel.js b/public/assets/javascripts/rectangles/util/wheel.js
index 712d470..4155a70 100644
--- a/public/assets/javascripts/rectangles/util/wheel.js
+++ b/public/assets/javascripts/rectangles/util/wheel.js
@@ -3,8 +3,8 @@
base.wheel = new wheel({
el: document.querySelector("#map"),
- update: function(e, val, delta){
- // do something with val
+ update: function(e, delta){
+ // do something with delta
},
})
@@ -13,7 +13,7 @@
function wheel (opt) {
opt = defaults(opt, {
el: document,
- fn: function(e, val, delta){},
+ update: function(e, delta){},
propagate: false,
locked: false,
reversible: true,
@@ -22,7 +22,7 @@ function wheel (opt) {
})
opt.el.addEventListener('wheel', onMouseWheel, false);
-// opt.el.addEventListener('mousewheel', onMouseWheel, false);
+ // opt.el.addEventListener('mousewheel', onMouseWheel, false);
opt.el.addEventListener('DOMMouseScroll', onMouseWheel, false);
function onMouseWheel (e) {
@@ -58,6 +58,8 @@ function wheel (opt) {
// opt.val = clamp(opt.val + delta, opt.min, opt.max)
+ // deltaX is also passed, but these values tend to be unusable
+ // try http://vvalls.com/assets/test/wheel.html with a trackpad
opt.update(e, deltaY, deltaX)
}