diff options
Diffstat (limited to 'assets/javascripts/rectangles')
| -rw-r--r-- | assets/javascripts/rectangles/_env.js | 23 | ||||
| -rw-r--r-- | assets/javascripts/rectangles/builder.js | 32 | ||||
| -rw-r--r-- | assets/javascripts/rectangles/clipper.js | 173 | ||||
| -rw-r--r-- | assets/javascripts/rectangles/colors.js | 36 | ||||
| -rw-r--r-- | assets/javascripts/rectangles/debug.js | 2 | ||||
| -rw-r--r-- | assets/javascripts/rectangles/draw.js | 48 | ||||
| -rw-r--r-- | assets/javascripts/rectangles/rect.js | 242 | ||||
| -rw-r--r-- | assets/javascripts/rectangles/sort.js | 29 | ||||
| -rw-r--r-- | assets/javascripts/rectangles/tree.js | 30 | ||||
| -rw-r--r-- | assets/javascripts/rectangles/vec2.js | 81 |
10 files changed, 696 insertions, 0 deletions
diff --git a/assets/javascripts/rectangles/_env.js b/assets/javascripts/rectangles/_env.js new file mode 100644 index 0000000..e2e8e31 --- /dev/null +++ b/assets/javascripts/rectangles/_env.js @@ -0,0 +1,23 @@ + +var environment = new function(){} +environment.init = function(){ + scene.camera.move({ + "x": 240, + "y": -1000, + "z": 240, + "rotationX": -PI/2, + "rotationY": 0 // PI + }) + map && map.zoom(3.00) && map.recenter() + + clipper.rects.push( new rect(100,100, 200,300) ) + clipper.rects.push( new rect(200,300, 300,500) ) + + clipper.rects.push( new rect(300,100, 600,300) ) + clipper.rects.push( new rect(400,200, 700,400) ) + + clipper.init() + scene.update() +} +environment.update = function(t){ +} diff --git a/assets/javascripts/rectangles/builder.js b/assets/javascripts/rectangles/builder.js new file mode 100644 index 0000000..ef0a28c --- /dev/null +++ b/assets/javascripts/rectangles/builder.js @@ -0,0 +1,32 @@ +var builder = new function(){ + var base = this + base.tube = new Tube () + + var els = [] + + base.tube.on("clipper:update", rebuild) + + function rebuild(){ + clear() + build() + } + function build (){ + clipper.regions.forEach(function(r){ + var walls = r.walls() + walls.forEach(function(el){ + els.push(el) + scene.add(el) + }) + }) + } + function clear (){ + console.log(els.length) + els.forEach(function(el){ + scene.remove(el) + }) + els = [] + } + +} + + diff --git a/assets/javascripts/rectangles/clipper.js b/assets/javascripts/rectangles/clipper.js new file mode 100644 index 0000000..bd0c521 --- /dev/null +++ b/assets/javascripts/rectangles/clipper.js @@ -0,0 +1,173 @@ +window.ctx = window.w = window.h = null; + +var clipper = new function(){ + var base = this + var canvas = document.createElement("canvas") + var ctx = window.ctx = canvas.getContext("2d") + var w = window.w = canvas.width = 500 + var h = window.h = canvas.height = 500 + var regions = [] + document.querySelector("#map").appendChild(canvas) + + base.init = function (){ + bind() + animate() + } + function animate(){ + requestAnimationFrame(animate) + clear_canvas() + + if (modified) { + solve_rects() + builder.tube("clipper:update") + } + draw_ruler() + draw_regions(base.regions) + draw_mouse(mouse) + z = false + } + + var rects = base.rects = [] + + this.creating = false + this.dragging = false + + var modified = true + var mouse = new rect(0,0,0,0) + + function bind(){ + canvas.addEventListener("mousedown", function(e){ + var x = e.pageX, y = e.pageY + mouse = new rect (x,y) + if (e.shiftKey) { + mouse.quantize(10) + } + + var intersects = rects.filter(function(r){ return r.focused = r.contains(x,y) }) + // console.log(intersects) + + if (intersects.length){ + clipper.dragging = intersects[0] + } + else { + clipper.creating = true + } + if (e.shiftKey && clipper.dragging) { + clipper.dragging.quantize(10) + } + }) + canvas.addEventListener("mousemove", function(e){ + var x, y + if (e.shiftKey) { + x = quantize( e.pageX, 10 ) + y = quantize( e.pageY, 10 ) + } + else { + x = e.pageX + y = e.pageY + } + + mouse.x.b = x + mouse.y.b = y + + if (clipper.dragging) { + clipper.dragging.translation.a = mouse.x.magnitude() + clipper.dragging.translation.b = mouse.y.magnitude() + } + else if (clipper.creating) { + mouse.x.b = x + mouse.y.b = y + } + else { + mouse.x.a = mouse.x.b + mouse.y.a = mouse.y.b + } + }) + document.addEventListener("mouseup", function(e){ + if (clipper.creating) { + if (mouse.height() != 0 && mouse.width() != 0) { + rects.push(mouse.translate()) + } + } + if (clipper.dragging) { + clipper.dragging.translate() + } + mouse = new rect(e.pageX, e.pageY) + clipper.creating = clipper.dragging = false + modified = true + }) + } + + function solve_rects(){ + var rects = sort_rects_by_position(base.rects) + + for (var i = 0; i < rects.length; i++) { + rects[i].id = i + rects[i].reset() + } + var regions = [] + + var left, right + for (var i = 0; i < rects.length; i++) { + left = rects[i] + for (var j = i+1; j < rects.length; j++) { + right = rects[j] + if (left.intersects(right)) { + left.clipTo(right) + right.clipTo(left) + } + if (left.x.b < right.x.a) { + break + } + } + } + for (var i = 0; i < rects.length; i++) { + regions = regions.concat(rects[i].regions) + } + + regions = sort_rects_by_area( regions.filter(function(r){ return !!r }) ) + + var ty = new tree (regions[0].y.a, [regions[0]]) + var tx = new tree (regions[0].x.a, ty) + var ttx, tty + + for (var i = 1; i < regions.length; i++) { + ttx = tx.add (regions[i].x.a, null) + if (ttx.data) { + tty = ttx.data.add (regions[i].y.a, null) + // duplicate polygon? + if (tty.data) { + tty.data.forEach(function(yy, ii){ + if (yy.intersects(regions[i])) { + if (yy.area() > regions[i].area()) { + regions[i].dupe = true + } + else { + yy.dupe = true + tty.data[ii] = regions[i] + } + } + }) + } + else { + tty.data = [regions[i]] + } + } + else { + ttx.data = new tree (regions[i].y.a, [regions[i]]) + } + } + + base.regions = sort_rects_by_position(regions) + + modified = false + // document.getElementById("intersects").innerHTML = sort_rects_by_position(regions).join("<br>") + } + + // generate floor and ceiling for some regions + // generate walls from surviving regions + // generate ceiling-walls where ceiling has discontinuity + + return base +} + diff --git a/assets/javascripts/rectangles/colors.js b/assets/javascripts/rectangles/colors.js new file mode 100644 index 0000000..3bdebbc --- /dev/null +++ b/assets/javascripts/rectangles/colors.js @@ -0,0 +1,36 @@ +(function(){ + var color_palettes = { + alpha: [ + "rgba(0,0,0,0.1)", + ], + redblue: [ + "rgba(0,0,0,0.2)", + "rgba(255,0,0,0.2)", + "rgba(0,0,255,0.2)", + "rgba(0,255,0,0.2)", + ], + gray: [ + "rgba(0,0,0,0.1)", + "rgba(0,0,0,0.2)", + "rgba(0,0,0,0.3)", + "rgba(0,0,0,0.4)", + ], + colors: [ + "rgba(255,0,0,0.5)", + "rgba(255,128,0,0.5)", + "rgba(128,255,0,0.5)", + "rgba(0,255,0,0.5)", + "rgba(0,255,128,0.5)", + "rgba(0,128,255,0.5)", + "rgba(0,0,255,0.5)", + "rgba(128,0,255,0.5)", + "rgba(255,0,255,0.5)", + "rgba(255,0,128,0.5)", + ] + } + + var select = document.querySelector("#palette") + select.addEventListener("change", function(){ colors = color_palettes[select.value] }) + + window.colors = color_palettes[select.value] +})() diff --git a/assets/javascripts/rectangles/debug.js b/assets/javascripts/rectangles/debug.js new file mode 100644 index 0000000..437abb8 --- /dev/null +++ b/assets/javascripts/rectangles/debug.js @@ -0,0 +1,2 @@ +window.z = true; +document.body.addEventListener("mousedown", function(){ z = true }) diff --git a/assets/javascripts/rectangles/draw.js b/assets/javascripts/rectangles/draw.js new file mode 100644 index 0000000..560c281 --- /dev/null +++ b/assets/javascripts/rectangles/draw.js @@ -0,0 +1,48 @@ +function clear_canvas(){ + ctx.fillStyle = "#fff" + ctx.fillRect(0,0,w,h) +} +function draw_ruler(){ + ctx.strokeStyle = "rgba(80,80,80,0.5)" + ctx.lineWidth = 1 + var len = 5 + for (var i = 0.5; i < w; i += 10) { + line(i, 0, i, len) + line(0, i, len, i) + } +} +function line (x,y,a,b,translation){ + if (translation) { + x += translation.a + a += translation.a + y += translation.b + b += translation.b + } + ctx.beginPath() + ctx.moveTo(x,y) + ctx.lineTo(a,b) + ctx.stroke() +} +function draw_regions(regions){ + for (var i = 0; i < regions.length; i++) { + if (regions[i].dupe) continue + ctx.fillStyle = colors[i % colors.length] + regions[i].fill().stroke_sides() + } +} +function draw_mouse(mouse){ + ctx.fillStyle = "rgba(255,0,0,0.4)"; + ctx.beginPath(); + ctx.arc(mouse.x.b, mouse.y.b, 5, 0, 2*Math.PI, false); + ctx.fill(); + + if (mouse.width() != 0 && mouse.height() != 0) { + if (clipper.dragging) { + mouse.stroke() + } + else { + ctx.fillStyle = "rgba(255,255,0,0.5)" + mouse.clone().translate().fill() + } + } +} diff --git a/assets/javascripts/rectangles/rect.js b/assets/javascripts/rectangles/rect.js new file mode 100644 index 0000000..a539b74 --- /dev/null +++ b/assets/javascripts/rectangles/rect.js @@ -0,0 +1,242 @@ +window.rect = (function(){ + var FRONT = 0x1, BACK = 0x2, LEFT = 0x4, RIGHT = 0x8 + + var rect = function (x0,y0,x1,y1){ + if (x0 instanceof vec2) { + this.x = x0 + this.y = y0 + } + else if (x1 === undefined) { + this.x = new vec2(x0,x0) + this.y = new vec2(y0,y0) + } + else { + this.x = new vec2(x0,x1) + this.y = new vec2(y0,y1) + } + this.translation = new vec2(0,0) + this.sides = FRONT | BACK | LEFT | RIGHT + this.focused = false + this.id = 0 + } + rect.prototype.clone = function(){ + return new rect( this.x.clone(), this.y.clone() ) + } + rect.prototype.center = function(){ + return new vec2(this.x.midpoint(), this.y.midpoint()) + } + rect.prototype.area = function(){ + return this.x.length() * this.y.length() + } + rect.prototype.translate = function(){ + this.x.abs().add(this.translation.a) + this.y.abs().add(this.translation.b) + this.translation.a = this.translation.b = 0 + return this + } + rect.prototype.contains = function(x,y){ + return this.x.contains(x) && this.y.contains(y) + } + rect.prototype.intersects = function(r){ + return this.x.intersects(r.x) && this.y.intersects(r.y) + } + rect.prototype.width = function(){ return this.x.length() } + rect.prototype.height = function(){ return this.y.length() } + rect.prototype.fill = function(){ + // if (! this.sides) return this + ctx.fillRect(this.x.a + this.translation.a, this.y.a + this.translation.b, this.x.length(), this.y.length()) + return this + } + rect.prototype.stroke_sides = function(){ + if (this.sides & FRONT) line(this.x.a, this.y.a, this.x.b, this.y.a) + if (this.sides & BACK) line(this.x.a, this.y.b, this.x.b, this.y.b) + if (this.sides & LEFT) line(this.x.a, this.y.a, this.x.a, this.y.b) + if (this.sides & RIGHT) line(this.x.b, this.y.a, this.x.b, this.y.b) + + function line(a,b,c,d){ + ctx.beginPath() + ctx.moveTo(a,b) + ctx.lineTo(c,d) + ctx.stroke() + } + return this + } + rect.prototype.stroke = function(){ + ctx.beginPath() + ctx.moveTo(this.x.a, this.y.a) + ctx.lineTo(this.x.b, this.y.b) + ctx.stroke() + return this + } + rect.prototype.perimeter = function(){ + line( this.x.a, this.y.a, this.x.b, this.y.a, this.translation ) + line( this.x.a, this.y.b, this.x.b, this.y.b, this.translation ) + line( this.x.a, this.y.a, this.x.a, this.y.b, this.translation ) + line( this.x.b, this.y.a, this.x.b, this.y.b, this.translation ) + } + rect.prototype.toString = function(){ + var sides = "" + if (this.sides & FRONT) sides += "front " + if (this.sides & BACK) sides += "back " + if (this.sides & LEFT) sides += "left " + if (this.sides & RIGHT) sides += "right " + var s = this.id + " " + "[" + this.x.toString() + " " + this.y.toString() + "] " + sides + if (this.focused) return "<b>" + s + "</b>" + return s + } + rect.prototype.quantize = function(n){ + this.x.quantize(n) + this.y.quantize(n) + } + rect.prototype.reset = function(){ + var copy = this.clone() + copy.sides = FRONT | BACK | LEFT | RIGHT + copy.focused = this.focused + copy.id = this.id + this.regions = [ copy ] + } + rect.prototype.clipTo = function(r){ + // for each of this rect's regions split the region if necessary + var regions = this.regions + var splits + + for (var i = 0, len = regions.length; i < len; i++) { + if (regions[i] && regions[i].intersects(r)) { + splits = regions[i].split(r) + regions = regions.concat(splits) + regions[i] = null + } + } + this.regions = regions + } + rect.prototype.split = function(r){ + var rz = this + var splits = [] + var split_contains = 0 + var x_intervals = [], y_intervals = [] + var sides = this.sides + + // Split vertically + if (this.x.contains(r.x.a) && r.x.contains(this.x.b)) { + x_intervals.push([ new vec2( this.x.a, r.x.a ), LEFT ]) + x_intervals.push([ new vec2( r.x.a, this.x.b ), RIGHT ]) + split_contains |= RIGHT + } + + else if (r.x.contains(this.x.a) && this.x.contains(r.x.b)) { + x_intervals.push([ new vec2( this.x.a, r.x.b ), LEFT ]) + x_intervals.push([ new vec2( r.x.b, this.x.b ), RIGHT ]) + split_contains |= LEFT + } + + else if (this.x.contains(r.x.a) && this.x.contains(r.x.b)) { + x_intervals.push([ new vec2( this.x.a, r.x.a ), LEFT ]) + x_intervals.push([ new vec2( r.x.a, r.x.b ), 0 ]) + x_intervals.push([ new vec2( r.x.b, this.x.b ), RIGHT ]) + split_contains |= LEFT | RIGHT + } + + else { // if (r.x.contains(this.x.a) && r.x.contains(r.x.b)) { + x_intervals.push([ new vec2( this.x.a, this.x.b ), LEFT | RIGHT ]) + split_contains |= LEFT | RIGHT + } + + // Split horizontally + if (this.y.contains(r.y.a) && r.y.contains(this.y.b)) { + y_intervals.push([ new vec2( this.y.a, r.y.a ), FRONT ]) + y_intervals.push([ new vec2( r.y.a, this.y.b ), BACK ]) + split_contains |= BACK + } + + else if (r.y.contains(this.y.a) && this.y.contains(r.y.b)) { + y_intervals.push([ new vec2( this.y.a, r.y.b ), FRONT ]) + y_intervals.push([ new vec2( r.y.b, this.y.b ), BACK ]) + split_contains |= FRONT + } + + else if (this.y.contains(r.y.a) && this.y.contains(r.y.b)) { + y_intervals.push([ new vec2( this.y.a, r.y.a ), FRONT ]) + y_intervals.push([ new vec2( r.y.a, r.y.b ), 0 ]) + y_intervals.push([ new vec2( r.y.b, this.y.b ), BACK ]) + split_contains |= FRONT | BACK + } + + else { // if (r.y.contains(this.y.a) && this.y.contains(r.y.b)) { + y_intervals.push([ new vec2( this.y.a, this.y.b ), FRONT | BACK ]) + split_contains |= FRONT | BACK + } + + x_intervals.forEach(function(x){ + y_intervals.forEach(function(y){ + var rn = new rect(x[0], y[0]) + rn.id = rz.id + rn.sides = ((x[1] | y[1]) & sides) + if (r.intersects(rn)) { + rn.sides = 0 + } + rn.focused = rz.focused + splits.push(rn) + }) + }) + return splits + } + + rect.prototype.walls = function(){ + var list = [], el = null + + var width = this.x.length() + var depth = this.y.length() + var height = 500 + + if (this.sides & FRONT) { + el = wall('.face.front') + el.width = width + el.height = height + el.x = this.x.a + el.z = this.y.a + list.push(el) + } + if (this.sides & BACK) { + var el = wall('.face.back') + el.width = width + el.height = height + el.rotationY = PI + el.x = this.x.a + el.z = this.y.a + depth + list.push(el) + } + +// if (this.sides & LEFT) { +// el = wall('.face.left') +// el.rotationY = -HALF_PI +// el.height = height +// el.width = depth +// el.z = this.y.a - depth/2 +// el.x = this.x.a +// list.push(el) +// } +// if (this.sides & RIGHT) { +// el = wall('.face.right') +// el.rotationY = HALF_PI +// el.height = height +// el.width = depth +// el.z = this.y.a - depth/2 +// el.x = this.x.b +// list.push(el) +// } + + function wall(klass){ + var el = new MX.Object3D(klass || ".face") + el.width = el.height = el.scaleX = el.scaleY = el.scaleZ = 1 + el.z = el.y = el.x = 0 + el.y = height/2 + el.type = "Face" + return el + } + + return list + } + + return rect + +})() diff --git a/assets/javascripts/rectangles/sort.js b/assets/javascripts/rectangles/sort.js new file mode 100644 index 0000000..0d79af8 --- /dev/null +++ b/assets/javascripts/rectangles/sort.js @@ -0,0 +1,29 @@ +function sort_rects_by_position(list){ + return list.sort(function(a,b){ + if (a.x.a < b.x.a) { + return -1 + } + if (a.x.a > b.x.a) { + return 1 + } + if (a.y.a < b.y.a) { + return -1 + } + if (a.y.a > b.y.a) { + return 1 + } + return 0 + }) +} + +function sort_rects_by_area(list){ + return list.map(function(r){ return [r.area(), r] }).sort(function(a,b){ + if (a[0] < b[0]) { + return 1 + } + if (a[0] > b[0]) { + return -1 + } + return 0 + }).map(function(r){ return r[1] }) +} diff --git a/assets/javascripts/rectangles/tree.js b/assets/javascripts/rectangles/tree.js new file mode 100644 index 0000000..f5eb117 --- /dev/null +++ b/assets/javascripts/rectangles/tree.js @@ -0,0 +1,30 @@ +var tree = function(n, data){ + this.lo = null + this.hi = null + this.value = n + this.data = data +} +tree.prototype.find = function(n){ + if (n == this.value) return this + if (n < this.value) return this.lo ? this.lo.find(n) : this + if (n > this.value) return this.hi ? this.hi.find(n) : this +} +tree.prototype.add = function(n, data){ + var closest = this.find(n) + if (n == closest.value) return closest + if (n < closest.value) return closest.lo = new tree(n, data) + if (n > closest.value) return closest.hi = new tree(n, data) +} +tree.prototype.toString = function(){ + var s = ""; + if (this.lo) s += this.lo.toString() + s += this.value + "," + if (this.hi) s += this.hi.toString() + return s +} +tree.prototype.depth = function(){ + if (this.lo && this.hi) return 1 + max(this.lo.depth(), this.hi.depth()) + else if (this.lo) return 1 + this.lo.depth() + else if (this.hi) return 1 + this.hi.depth() + else return 0 +} diff --git a/assets/javascripts/rectangles/vec2.js b/assets/javascripts/rectangles/vec2.js new file mode 100644 index 0000000..4e2ad36 --- /dev/null +++ b/assets/javascripts/rectangles/vec2.js @@ -0,0 +1,81 @@ +function vec2(a,b){ + this.a = a + this.b = b +} +vec2.prototype.magnitude = function(){ + return this.b-this.a +} +vec2.prototype.length = function(){ + return abs(this.b-this.a) +} +vec2.prototype.clone = function(){ + return new vec2(this.a, this.b) +} +vec2.prototype.abs = function(){ + if (this.b < this.a) { + this.a = this.a ^ this.b + this.b = this.a ^ this.b + this.a = this.a ^ this.b + } + return this +} +vec2.prototype.midpoint = function(){ + return lerp(0.5, this.a, this.b) +} +vec2.prototype.eq = function(v){ + return this.a == v.a && this.b == v.b +} +vec2.prototype.add = function(n){ + this.a += n + this.b += n +} +vec2.prototype.sub = function(n){ + this.a -= n + this.b -= n +} +vec2.prototype.mul = function(n){ + this.a *= n + this.b *= n +} +vec2.prototype.div = function(n){ + this.a /= n + this.b /= n +} +vec2.normalize = function(){ + var dim = max(this.a, this.b) + this.a = this.a/dim + this.b = this.b/dim +} +vec2.prototype.contains = function(n){ + return this.a <= n && n <= this.b +} +vec2.prototype.intersects = function(v){ + if (this.a < v.a) { + return (v.a < this.b && this.b <= v.b) || (this.a < v.b && v.b <= this.b) + } + else if (this.a == v.a) { + return true + } + else if (this.a > v.a) { + return (this.a < v.b && v.b <= this.b) || (v.a < this.b && this.b <= v.b) + } +} +vec2.prototype.union = function(v){ + if (this.intersects(v)) { + return new vec2( min(this.a,v.a), max(this.b, v.b) ) + } +} +vec2.prototype.intersection = function(v){ + if (this.intersects(v)) { + return new vec2( max(this.a,v.a), min(this.b, v.b) ) + } +} +vec2.prototype.toString = function(){ + return "[" + this.a + " " + this.b + "]" +} +vec2.prototype.quantize = function(n){ + n = n || 10 + this.a = quantize(this.a, n) + this.b = quantize(this.b, n) +} + |
