summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.bowerrc2
-rw-r--r--assets/javascripts/math/point.js9
-rw-r--r--assets/javascripts/math/vec2.js114
-rw-r--r--assets/javascripts/util/coords.js33
-rw-r--r--assets/javascripts/util/keys.js165
-rw-r--r--assets/javascripts/util/minotaur.js66
-rw-r--r--assets/javascripts/util/mouse.js171
-rw-r--r--assets/javascripts/util/permissions.js49
-rw-r--r--assets/javascripts/util/uid.js29
-rw-r--r--assets/javascripts/util/undostack.js54
-rw-r--r--assets/javascripts/util/wheel.js69
-rw-r--r--assets/test/lasso/index.html88
12 files changed, 753 insertions, 96 deletions
diff --git a/.bowerrc b/.bowerrc
index f21ed66..e35b1f5 100644
--- a/.bowerrc
+++ b/.bowerrc
@@ -1,3 +1,3 @@
{
- "directory": "public/assets/javascripts/vendor/bower_components"
+ "directory": "assets/javascripts/vendor/bower_components"
}
diff --git a/assets/javascripts/math/point.js b/assets/javascripts/math/point.js
index 354be00..5a5d0db 100644
--- a/assets/javascripts/math/point.js
+++ b/assets/javascripts/math/point.js
@@ -5,6 +5,15 @@
this.a = a
this.b = b
}
+
+ point.prototype.distanceTo = function(p){
+ return Math.sqrt(this.dot(p))
+ }
+ point.prototype.dot = function(p){
+ return Math.pow(this.a - p.a, 2) + Math.pow(this.b - p.b, 2)
+ }
+
+
point.prototype.magnitude = function(){
return this.b-this.a
}
diff --git a/assets/javascripts/math/vec2.js b/assets/javascripts/math/vec2.js
index 3e1f463..550d0db 100644
--- a/assets/javascripts/math/vec2.js
+++ b/assets/javascripts/math/vec2.js
@@ -6,18 +6,6 @@
}
else {
point = require('./point')
- 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 vec2 = function (x0,y0,x1,y1){
@@ -33,8 +21,6 @@
this.x = new point(x0,x1)
this.y = new point(y0,y1)
}
- this.translation = new point(0,0)
- this.sides = FRONT | BACK | LEFT | RIGHT
}
vec2.prototype.clone = function(){
return new vec2( this.x.clone(), this.y.clone() )
@@ -75,24 +61,7 @@
this.translation.a = this.translation.b = 0
return this
}
- vec2.prototype.resize = function(translation, sides){
- var translation = translation || this.translation
- sides = sides || translation.sides
-
- if (sides & LEFT) {
- this.x.a += translation.a
- }
- if (sides & RIGHT) {
- this.x.b += translation.a
- }
- if (sides & FRONT) {
- this.y.a += translation.b
- }
- if (sides & BACK) {
- this.y.b += translation.b
- }
- this.translation.a = this.translation.b = 0
- }
+
vec2.prototype.contains = function(x,y){
return this.x.contains(x) && this.y.contains(y)
}
@@ -122,22 +91,22 @@
this.a.zero()
this.b.zero()
}
- vec2.prototype.nearEdge = function (x, y, r) {
- var edges = 0
- if (x < this.x.a+r) {
- edges |= LEFT
- }
- else if (x > this.x.b-r) {
- edges |= RIGHT
- }
- if (y < this.y.a+r) {
- edges |= FRONT
- }
- else if (y > this.y.b-r) {
- edges |= BACK
- }
- return edges
- }
+// vec2.prototype.nearEdge = function (x, y, r) {
+// var edges = 0
+// if (x < this.x.a+r) {
+// edges |= LEFT
+// }
+// else if (x > this.x.b-r) {
+// edges |= RIGHT
+// }
+// if (y < this.y.a+r) {
+// edges |= FRONT
+// }
+// else if (y > this.y.b-r) {
+// edges |= BACK
+// }
+// return edges
+// }
vec2.prototype.width = function(){ return this.x.length() }
vec2.prototype.height = function(){ return this.y.length() }
vec2.prototype.delta = function(){ return new point( this.x.magnitude(), this.y.magnitude() ) }
@@ -165,13 +134,11 @@
return this
}
vec2.prototype.toString = function(){
- var sides = sidesToString(this.sides)
- var s = "[" + this.x.toString() + " " + this.y.toString() + "] " + sides
+ var s = "[" + this.x.toString() + " " + this.y.toString() + "] "
return s
}
vec2.prototype.exactString = function(){
- var sides = sidesToString(this.sides)
- var s = "[" + this.x.exactString() + " " + this.y.exactString() + "] " + sides
+ var s = "[" + this.x.exactString() + " " + this.y.exactString() + "] "
return s
}
@@ -183,49 +150,6 @@
this.y.quantize(n)
return this
}
- vec2.prototype.split = function(r){
- var rz = this
- var splits = []
- var sides = this.sides
-
- // 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 )
-
- // 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 vec2(x[0], y[0])
- rn.id = rz.id
- rn.sides = ((x[1] | y[1]) & sides)
- 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.vec2 = vec2
diff --git a/assets/javascripts/util/coords.js b/assets/javascripts/util/coords.js
new file mode 100644
index 0000000..74b7fda
--- /dev/null
+++ b/assets/javascripts/util/coords.js
@@ -0,0 +1,33 @@
+function offsetFromPoint(event, element) {
+ function a(width) {
+ var l = 0, r = 200;
+ while (r - l > 0.0001) {
+ var mid = (r + l) / 2;
+ var a = document.createElement('div');
+ a.style.cssText = 'position: absolute;left:0;top:0;background: red;z-index: 1000;';
+ a.style[width ? 'width' : 'height'] = mid.toFixed(3) + '%';
+ a.style[width ? 'height' : 'width'] = '100%';
+ element.appendChild(a);
+ var x = document.elementFromPoint(event.clientX, event.clientY);
+ element.removeChild(a);
+ if (x === a) {
+ r = mid;
+ } else {
+ if (r === 200) {
+ return null;
+ }
+ l = mid;
+ }
+ }
+ return mid;
+ }
+ var l = a(1),
+ t = a(0);
+ return l && t ? {
+ left: l / 100,
+ top: t / 100,
+ toString: function () {
+ return 'left: ' + l + '%, top: ' + t + '%';
+ }
+ } : null;
+}
diff --git a/assets/javascripts/util/keys.js b/assets/javascripts/util/keys.js
new file mode 100644
index 0000000..62d763f
--- /dev/null
+++ b/assets/javascripts/util/keys.js
@@ -0,0 +1,165 @@
+var keys = (function(){
+
+ var base = new function(){}
+ base.tube = new Tube ()
+ base.debug = false
+
+ base.on = function(){
+ base.tube.on.apply(base.tube, arguments)
+ }
+
+ base.off = function(){
+ base.tube.off.apply(base.tube, arguments)
+ }
+
+ $(window).keydown(function(e){
+ var key = KEY_NAMES[e.keyCode];
+ switch (key) {
+ case undefined:
+ break;
+ default:
+ if (keys.debug) console.log(key)
+ base.tube(key, e)
+ break;
+ }
+ })
+ var KEYMAP = {
+ STRG: 17,
+ CTRL: 17,
+ CTRLRIGHT: 18,
+ CTRLR: 18,
+ SHIFT: 16,
+ RETURN: 13,
+ ENTER: 13,
+ BACKSPACE: 8,
+ BCKSP:8,
+ ALT: 18,
+ ALTR: 17,
+ ALTRIGHT: 17,
+ SPACE: 32,
+ WIN: 91,
+ MAC: 91,
+ FN: null,
+ UP: 38,
+ DOWN: 40,
+ LEFT: 37,
+ RIGHT: 39,
+ ESC: 27,
+ DEL: 46,
+ F1: 112,
+ F2: 113,
+ F3: 114,
+ F4: 115,
+ F5: 116,
+ F6: 117,
+ F7: 118,
+ F8: 119,
+ F9: 120,
+ F10: 121,
+ F11: 122,
+ F12: 123
+ },
+ KEYCODES = {
+ 'backspace' : '8',
+ 'tab' : '9',
+ 'enter' : '13',
+ 'shift' : '16',
+ 'ctrl' : '17',
+ 'alt' : '18',
+ 'pause_break' : '19',
+ 'caps_lock' : '20',
+ 'escape' : '27',
+ 'page_up' : '33',
+ 'page down' : '34',
+ 'end' : '35',
+ 'home' : '36',
+ 'left_arrow' : '37',
+ 'up_arrow' : '38',
+ 'right_arrow' : '39',
+ 'down_arrow' : '40',
+ 'insert' : '45',
+ 'delete' : '46',
+ '0' : '48',
+ '1' : '49',
+ '2' : '50',
+ '3' : '51',
+ '4' : '52',
+ '5' : '53',
+ '6' : '54',
+ '7' : '55',
+ '8' : '56',
+ '9' : '57',
+ 'a' : '65',
+ 'b' : '66',
+ 'c' : '67',
+ 'd' : '68',
+ 'e' : '69',
+ 'f' : '70',
+ 'g' : '71',
+ 'h' : '72',
+ 'i' : '73',
+ 'j' : '74',
+ 'k' : '75',
+ 'l' : '76',
+ 'm' : '77',
+ 'n' : '78',
+ 'o' : '79',
+ 'p' : '80',
+ 'q' : '81',
+ 'r' : '82',
+ 's' : '83',
+ 't' : '84',
+ 'u' : '85',
+ 'v' : '86',
+ 'w' : '87',
+ 'x' : '88',
+ 'y' : '89',
+ 'z' : '90',
+ 'left_window key' : '91',
+ 'right_window key' : '92',
+ 'select_key' : '93',
+ 'numpad 0' : '96',
+ 'numpad 1' : '97',
+ 'numpad 2' : '98',
+ 'numpad 3' : '99',
+ 'numpad 4' : '100',
+ 'numpad 5' : '101',
+ 'numpad 6' : '102',
+ 'numpad 7' : '103',
+ 'numpad 8' : '104',
+ 'numpad 9' : '105',
+ 'multiply' : '106',
+ 'add' : '107',
+ 'subtract' : '109',
+ 'decimal point' : '110',
+ 'divide' : '111',
+ 'f1' : '112',
+ 'f2' : '113',
+ 'f3' : '114',
+ 'f4' : '115',
+ 'f5' : '116',
+ 'f6' : '117',
+ 'f7' : '118',
+ 'f8' : '119',
+ 'f9' : '120',
+ 'f10' : '121',
+ 'f11' : '122',
+ 'f12' : '123',
+ 'num_lock' : '144',
+ 'scroll_lock' : '145',
+ 'semi_colon' : '186',
+ 'equal_sign' : '187',
+ 'comma' : '188',
+ 'dash' : '189',
+ 'period' : '190',
+ 'forward_slash' : '191',
+ 'grave_accent' : '192',
+ 'open_bracket' : '219',
+ 'backslash' : '220',
+ 'closebracket' : '221',
+ 'single_quote' : '222'
+ },
+ KEY_NAMES = invert_hash(KEYCODES)
+
+ return base
+})() \ No newline at end of file
diff --git a/assets/javascripts/util/minotaur.js b/assets/javascripts/util/minotaur.js
new file mode 100644
index 0000000..d165ccc
--- /dev/null
+++ b/assets/javascripts/util/minotaur.js
@@ -0,0 +1,66 @@
+(function(){
+
+ var Monitor = function () {
+ var base = this
+ base.$el = $("#minotaur")
+ base.timeout = null
+ base.delay = 2500
+ base.objects = {}
+
+ base.init = function () {
+ base.$el.removeClass()
+ base.$el.click(base.save)
+ }
+
+ base.watch = function (object) {
+ base.objects[object.type] = base.objects[object.type] || {}
+ base.objects[object.type][object._id] = object
+ base.clear()
+ base.timeout = setTimeout(base.save, base.delay)
+ }
+
+ base.unwatch = function (object) {
+ if (base.objects[object.type] && base.objects[object.type][object._id]) {
+ delete base.objects[object.type][object._id]
+ }
+ }
+
+ base.clear = function () {
+ if (base.timeout) clearTimeout(base.timeout)
+ base.timeout = false
+ }
+
+ base.save = function () {
+ var saving = false
+ base.clear()
+
+ for (var type in base.objects) {
+ for (var id in base.objects[type]) {
+ var obj = base.objects[type][id]
+ if (obj) {
+ obj.save(null, function(){ base.hide() }, function(){})
+ }
+ delete base.objects[type][id]
+ saving = true
+ }
+ }
+
+ saving ? base.show() : base.hide()
+ }
+
+ base.show = function () {
+ base.$el.removeClass().addClass('saving')
+ }
+
+ base.hide = function () {
+ setTimeout(function(){
+ base.$el.removeClass()
+ }, 500)
+ }
+
+ base.init();
+ }
+
+ window.Minotaur = new Monitor ();
+
+})()
diff --git a/assets/javascripts/util/mouse.js b/assets/javascripts/util/mouse.js
new file mode 100644
index 0000000..d113d10
--- /dev/null
+++ b/assets/javascripts/util/mouse.js
@@ -0,0 +1,171 @@
+/*
+ usage:
+
+ base.mouse = new mouse({
+ el: document.querySelector("#map"),
+ down: function(e, cursor){
+ // do something with val
+ // cursor.x.a
+ // cursor.y.a
+ },
+ move: function(e, cursor){
+ // var delta = cursor.delta()
+ // delta.a (x)
+ // delta.b (y)
+ },
+ up: function(e, cursor, new_cursor){
+ // cursor.x.a
+ // cursor.y.a
+ },
+ })
+
+*/
+
+function mouse (opt) {
+ var base = this
+
+ opt = defaults(opt, {
+ el: null,
+ down: null,
+ move: null,
+ drag: null,
+ enter: null,
+ up: null,
+ rightclick: null,
+ propagate: false,
+ locked: false,
+ use_offset: true,
+ val: 0,
+ })
+
+ base.down = false
+
+ base.creating = false
+ base.dragging = false
+
+ base.cursor = new vec2(0,0,0,0)
+
+ base.tube = new Tube ()
+ opt.down && base.tube.on("down", opt.down)
+ opt.move && base.tube.on("move", opt.move)
+ opt.drag && base.tube.on("drag", opt.drag)
+ opt.enter && base.tube.on("enter", opt.enter)
+ opt.leave && base.tube.on("leave", opt.leave)
+ opt.up && base.tube.on("up", opt.up)
+ opt.rightclick && base.tube.on("rightclick", opt.rightclick)
+
+ var offset = (opt.use_offset && opt.el) ? opt.el.getBoundingClientRect() : null
+
+ base.init = function (){
+ base.bind()
+ }
+
+ base.on = function(){
+ base.tube.on.apply(base.tube, arguments)
+ }
+
+ base.off = function(){
+ base.tube.off.apply(base.tube, arguments)
+ }
+
+ base.bind = function(){
+ if (opt.el) {
+ opt.el.addEventListener("mousedown", base.mousedown)
+ opt.el.addEventListener("contextmenu", base.contextmenu)
+ }
+ window.addEventListener("mousemove", base.mousemove)
+ window.addEventListener("mouseup", base.mouseup)
+ }
+
+ base.bind_el = function(el){
+ el.addEventListener("mousedown", base.mousedown)
+ el.addEventListener("mousemove", base.mousemove)
+ }
+ base.unbind_el = function(el){
+ el.removeEventListener("mousedown", base.mousedown)
+ el.removeEventListener("mousemove", base.mousemove)
+ }
+
+ function positionFromMouse(e) {
+ if (offset) {
+ return new point(e.pageX - offset.left, e.pageY - offset.top)
+ }
+ else {
+ return new point(e.pageX, e.pageY)
+ }
+ }
+
+ base.mousedown = function(e){
+ if (opt.use_offset) {
+ offset = this.getBoundingClientRect()
+ }
+
+ var pos = positionFromMouse(e)
+
+ var x = pos.a, y = pos.b
+ base.cursor = new vec2 (x,y, x,y)
+ base.down = true
+ e.clickAccepted = true
+
+ base.tube("down", e, base.cursor)
+
+ if (e.clickAccepted) {
+ e.stopPropagation()
+ }
+ else {
+ base.down = false
+ }
+ }
+ base.mousemove = function(e){
+ if (opt.use_offset && ! offset) return
+
+ var pos = positionFromMouse(e)
+
+ if (e.shiftKey) {
+ pos.quantize(10)
+ }
+
+ var x = pos.a, y = pos.b
+
+ if (base.down) {
+ base.cursor.x.b = x
+ base.cursor.y.b = y
+ base.tube("drag", e, base.cursor)
+ e.stopPropagation()
+ }
+ else {
+ base.cursor.x.a = base.cursor.x.b = x
+ base.cursor.y.a = base.cursor.y.b = y
+ base.tube("move", e, base.cursor)
+ }
+ }
+ base.mouseenter = function(e, target, index){
+ if (! base.down) return
+ if (opt.use_offset && ! offset) return
+ base.tube("enter", e, target, base.cursor)
+ }
+ base.mouseleave = function(e, target){
+ if (! base.down) return
+ if (opt.use_offset && ! offset) return
+ base.tube("leave", e, target, base.cursor)
+ }
+ base.mouseup = function(e){
+ var pos, new_cursor
+
+ if (base.down) {
+ e.stopPropagation()
+ base.down = false
+ pos = positionFromMouse(e)
+ new_cursor = new vec2 (pos.a, pos.b)
+ base.tube("up", e, base.cursor, new_cursor)
+ base.cursor = new_cursor
+ }
+ }
+ base.contextmenu = function(e){
+ e.preventDefault()
+ base.tube("rightclick", e, base.cursor)
+ }
+
+ base.init()
+}
+
diff --git a/assets/javascripts/util/permissions.js b/assets/javascripts/util/permissions.js
new file mode 100644
index 0000000..9e3ef4d
--- /dev/null
+++ b/assets/javascripts/util/permissions.js
@@ -0,0 +1,49 @@
+
+var Permissions = function(ops){
+ var base = this
+ base.keys = _.keys(ops)
+ base.keys.forEach(function(op){
+ base[op] = ops[op]
+ })
+}
+
+Permissions.prototype.toggle = function (key) {
+ var base = this
+ var state = ! base[key]
+ base.keys.forEach(function(op){
+ base[op] = op == key ? state : false
+ })
+ return state
+}
+
+Permissions.prototype.assign = function (key, state) {
+ var base = this
+ base.keys.forEach(function(op){
+ base[op] = op == key ? state : false
+ })
+ return state
+}
+
+Permissions.prototype.add = function (key) {
+ var base = this
+ base[key] = true
+}
+
+Permissions.prototype.remove = function (key) {
+ var base = this
+ base[key] = false
+}
+
+Permissions.prototype.clear = function () {
+ var base = this
+ base.keys.forEach(function(op){
+ base[op] = false
+ })
+}
+
+Permissions.prototype.log = function () {
+ var base = this
+ base.keys.forEach(function(op){
+ console.log(op, base[op])
+ })
+} \ No newline at end of file
diff --git a/assets/javascripts/util/uid.js b/assets/javascripts/util/uid.js
new file mode 100644
index 0000000..0c0b176
--- /dev/null
+++ b/assets/javascripts/util/uid.js
@@ -0,0 +1,29 @@
+(function(){
+
+ 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
+ }
+ else {
+ module.exports = UidGenerator
+ }
+
+})()
+
diff --git a/assets/javascripts/util/undostack.js b/assets/javascripts/util/undostack.js
new file mode 100644
index 0000000..040a4eb
--- /dev/null
+++ b/assets/javascripts/util/undostack.js
@@ -0,0 +1,54 @@
+(function(){
+
+ var UndoStack = function(){
+ this.debug = true
+ this.stack = []
+ this.types = {}
+ this.pointer = -1
+ }
+ UndoStack.prototype.push = function(action){
+ this.pointer++
+ this.stack[this.pointer] = action
+ this.purge()
+ this.debug && console.log("push", action.type)
+ }
+ UndoStack.prototype.purge = function(){
+ if (this.stack.length-1 == this.pointer) return
+ this.stack.length = this.pointer+1
+ }
+ UndoStack.prototype.undo = function(){
+ if (this.pointer == -1) return false
+ var action = this.stack[this.pointer]
+ this.debug && console.log("undo", action.type)
+ this.types[ action.type ].undo(action.undo)
+ this.pointer--
+ return this.pointer > -1
+ }
+ UndoStack.prototype.redo = function(){
+ if (this.pointer == this.stack.length-1) return false
+ this.pointer++
+ var action = this.stack[this.pointer]
+ this.debug && console.log("redo", action.type)
+ this.types[ action.type ].redo(action.redo)
+ return this.pointer < this.stack.length-1
+ }
+ UndoStack.prototype.register = function(actions){
+ if (actions.length) {
+ actions.forEach(this.registerOne.bind(this))
+ }
+ else {
+ this.registerOne(actions)
+ }
+ }
+ UndoStack.prototype.registerOne = function(action){
+ if (! action.redo) { action.redo = action.undo }
+ this.types[ action.type ] = action
+ }
+ if ('window' in this) {
+ window.UndoStack = new UndoStack
+ }
+ else {
+ module.exports = new UndoStack
+ }
+
+})()
diff --git a/assets/javascripts/util/wheel.js b/assets/javascripts/util/wheel.js
new file mode 100644
index 0000000..712d470
--- /dev/null
+++ b/assets/javascripts/util/wheel.js
@@ -0,0 +1,69 @@
+/*
+ usage:
+
+ base.wheel = new wheel({
+ el: document.querySelector("#map"),
+ update: function(e, val, delta){
+ // do something with val
+ },
+ })
+
+*/
+
+function wheel (opt) {
+ opt = defaults(opt, {
+ el: document,
+ fn: function(e, val, delta){},
+ propagate: false,
+ locked: false,
+ reversible: true,
+ ratio: 0.02,
+ val: 0,
+ })
+
+ opt.el.addEventListener('wheel', onMouseWheel, false);
+// opt.el.addEventListener('mousewheel', onMouseWheel, false);
+ opt.el.addEventListener('DOMMouseScroll', onMouseWheel, false);
+
+ function onMouseWheel (e) {
+ if (opt.locked) {
+ return
+ }
+
+ if (! opt.propagate) {
+ e.stopPropagation()
+ e.preventDefault()
+ }
+
+ var deltaX = 0, deltaY = 0;
+
+ // WebKit
+ if ( event.deltaY ) {
+ deltaY -= event.deltaY * opt.ratio
+ deltaX -= event.deltaX * opt.ratio
+ }
+ else if ( event.wheelDeltaY ) {
+ deltaY -= event.wheelDeltaY * opt.ratio
+ deltaX -= event.wheelDeltaX * opt.ratio
+ }
+ // Opera / Explorer 9
+ else if ( event.wheelDelta ) {
+ deltaY -= event.wheelDelta * opt.ratio
+ }
+ // Firefox
+ else if ( event.detail ) {
+ deltaY += event.detail * 2
+ }
+ if (! opt.reversible && (deltaY < 0 && deltaX < 0)) return;
+
+ // opt.val = clamp(opt.val + delta, opt.min, opt.max)
+
+ opt.update(e, deltaY, deltaX)
+ }
+
+ opt.lock = function(){ opt.locked = true }
+ opt.unlock = function(){ opt.locked = false }
+
+ return opt
+}
+
diff --git a/assets/test/lasso/index.html b/assets/test/lasso/index.html
new file mode 100644
index 0000000..16cb27f
--- /dev/null
+++ b/assets/test/lasso/index.html
@@ -0,0 +1,88 @@
+<style>
+body,html{margin:0;padding:0;}
+#hud { position: absolute; top: 0; left: 0; pointer-events: none; }
+</style>
+<canvas id="canvas"></canvas>
+<div id="hud"></div>
+
+<script src="/assets/javascripts/vendor/bower_components/jquery/dist/jquery.min.js"></script>
+<script src="/assets/javascripts/vendor/bower_components/lodash/dist/lodash.min.js"></script>
+<script src="/assets/javascripts/vendor/canvasutilities.js"></script>
+<script src="/assets/javascripts/vendor/tube.js"></script>
+<script src="/assets/javascripts/math/util.js"></script>
+<script src="/assets/javascripts/math/point.js"></script>
+<script src="/assets/javascripts/math/vec2.js"></script>
+<script src="/assets/javascripts/util/mouse.js"></script>
+<script src="/assets/javascripts/util/uid.js"></script>
+<script>
+
+var ctx = canvas.getContext('2d')
+var w = canvas.width = window.innerWidth
+var h = canvas.height = window.innerHeight
+
+var placing = false
+var points = []
+
+var mymouse = new mouse({
+ el: canvas,
+ down: function(e, cursor){
+ // compare to initial point
+ if (placing) {
+ if (points.length > 3 && points[0].distanceTo({ a: cursor.x.a, b: cursor.y.a }) < 3) {
+ points.push(points[0].clone())
+ placing = false
+ }
+ else {
+ points.push( new point( cursor.x.a, cursor.y.a ) )
+ }
+ }
+ else {
+ placing = true
+ points.length = 0
+ points.push( new point( cursor.x.a, cursor.y.a ) )
+ }
+ },
+ move: function(e, cursor){
+ if (placing && points.length > 2 && points[0].distanceTo({ a: cursor.x.a, b: cursor.y.a }) < 3 ) {
+ document.body.style.cursor = "pointer"
+ }
+ else {
+ document.body.style.cursor = "crosshair"
+ }
+ },
+ drag: function(e, cursor){
+ },
+ up: function(e, cursor, new_cursor){
+ },
+})
+
+function draw (time) {
+ ctx.fillStyle = "#fff"
+ ctx.fillRect(0,0,w,h)
+
+ if (points.length == 1) {
+ ctx.fillStyle = "#000"
+ ctx.fillRect(points[0].a, points[0].b, 1, 1)
+ }
+ if (points.length > 1) {
+ ctx.fillStyle = "#000"
+ ctx.strokeStyle = "#000"
+ ctx.beginPath()
+ ctx.moveTo(points[0].a, points[0].b)
+ points.forEach(function(point, i){
+ i && ctx.lineTo(point.a, point.b)
+ })
+ if (placing) {
+ ctx.stroke()
+ }
+ else {
+ ctx.fill()
+ }
+ }
+}
+function animate(time){
+ requestAnimationFrame(animate)
+ draw(time)
+}
+animate()
+</script>