diff options
| author | Jules Laplace <jules@okfoc.us> | 2014-10-30 17:59:44 -0400 |
|---|---|---|
| committer | Jules Laplace <jules@okfoc.us> | 2014-10-30 17:59:44 -0400 |
| commit | c0fe6750cc0462adbc2165ac7f8c9cf1e0aea925 (patch) | |
| tree | 38c5ab5e7ab3cc735090dfbfac2ef8d4a2f35ac2 | |
| parent | de6fa5bf0e7f0a55341d05f5a5b1dfb19330aeb0 (diff) | |
intersection test
| -rw-r--r-- | public/assets/javascripts/rectangles/engine/rooms/mover.js | 95 | ||||
| -rw-r--r-- | public/assets/javascripts/vendor/canvasutilities.js | 145 | ||||
| -rw-r--r-- | public/assets/test/intersect.html | 132 |
3 files changed, 348 insertions, 24 deletions
diff --git a/public/assets/javascripts/rectangles/engine/rooms/mover.js b/public/assets/javascripts/rectangles/engine/rooms/mover.js index 121ecec..8ce00fe 100644 --- a/public/assets/javascripts/rectangles/engine/rooms/mover.js +++ b/public/assets/javascripts/rectangles/engine/rooms/mover.js @@ -36,6 +36,18 @@ Rooms.mover = new function(){ // check if we've breached one of the walls.. clamp position if so var collision = base.room.collidesDisc(cam, pos, radius) + if (collision && ! base.noclip) { + cam.x = (collision & LEFT_RIGHT) ? base.room.rect.x.clampDisc(pos.x, radius) : pos.x + cam.z = (collision & FRONT_BACK) ? base.room.rect.y.clampDisc(pos.z, radius) : pos.z + return + } + + // in this case, we appear to have left the room.. + // $(".face.active").removeClass("active") + Walls.clearBodyColor() + base.room = null + } + /* var dz = new vec2( cam.z, pos.z ) dz.normalize() @@ -43,47 +55,82 @@ Rooms.mover = new function(){ var dx = new vec2( cam.x, pos.x ) dx.normalize() + // first check if the cam-pos movement intersects the wall. + // next check if it intersects any of the surface frames + // if we can pass through the wall at this point, we do not need to clamp. + // otherwise, get the distance along the cam-pos vector where we would hit the wall and clamp to it. Walls.list.forEach(function(wall){ + var t = -1 switch (wall.side) { case FRONT: + if (cam.z >= wall.edge + radius && wall.edge + radius >= pos.z && wall.vec.intersects(dx) ) { + t = check_intersection( front_back_intersection, cam, pos, dx, wall, radius ) + } break case BACK: + // console.log(cam.z|0, wall.edge-radius, pos.z|0, wall.vec.intersects(dx)) + if (cam.z <= wall.edge - radius && wall.edge - radius <= pos.z && wall.vec.intersects(dx) ) { + t = check_intersection( front_back_intersection, cam, pos, dx, wall, -radius ) + console.log(t) + } break case LEFT: - if (cam.x >= wall.edge + radius && wall.edge + radius >= pos.x && (wall.vec.intersects(dz) ) { - // intersects the wall.. next check if it intersects any of the surface frames - wall.surface.faces.some(function(face, i){ - // if we can pass through the wall at this point, we do not need to clamp - if (face.y.a === 0 && face.x.intersects(dz)) { - dz.a = pos.z = face.x.clamp(pos.z) - dz.b = cam.z - dz.normalize() - return true - } - }) + if (cam.x >= wall.edge + radius && wall.edge + radius >= pos.x && wall.vec.intersects(dz) ) { + t = check_intersection( left_right_intersection, cam, pos, dz, wall, radius ) } break case RIGHT: - if (cam.x <= wall.edge - radius && wall.edge - radius <= pos.x && (wall.vec.contains(cam.z) || wall.vec.contains(pos.z)) ) { - // intersects + if (cam.x <= wall.edge - radius && wall.edge - radius <= pos.x && wall.vec.intersects(dz) ) { + t = check_intersection( left_right_intersection, cam, pos, dz, wall, -radius ) } break } + if (0 <= t && t <= 1) { + pos.x = cam.x + (pos.x - cam.x) * t + pos.z = cam.z + (pos.z - cam.z) * t + + dz = new vec2( cam.z, pos.z ) + dz.normalize() + + dx = new vec2( cam.x, pos.x ) + dx.normalize() + } }) -*/ - if (collision && ! base.noclip) { - cam.x = (collision & LEFT_RIGHT) ? base.room.rect.x.clampDisc(pos.x, radius) : pos.x - cam.z = (collision & FRONT_BACK) ? base.room.rect.y.clampDisc(pos.z, radius) : pos.z - return - } + function check_intersection(intersection_function, cam, pos, cam_pos_vector, wall, radius) { + var t = -1 + wall.surface.faces.some(function(face, i){ + if (face.y.a == 0 && face.x.intersects(cam_pos_vector)) { + t = intersection_function( cam, pos, wall, face, radius ) + console.log(">>", t) + if (0 <= t && t <= 1) { + return true + } + else { + t = -1 + } + } + return false + }) + return t + } + function left_right_intersection (cam, pos, wall, face, radius) { + var perp_n = (face.x.a - face.x.b) * (wall.edge - cam.z + radius) + var perp_d = (face.x.a - face.x.b) * (pos.z - cam.z) + if (perp_d == 0) return Infinity + return perp_n / perp_d + } + function front_back_intersection (cam, pos, wall, face, radius) { + // va.vx*vb.vy - va.vy*vb.vx + var perp_n = (face.x.b - face.x.a) * (wall.edge - cam.x + radius) + var perp_d = (face.x.b - face.x.a) * (pos.x - cam.x) - // in this case, we appear to have left the room.. - // $(".face.active").removeClass("active") - Walls.clearBodyColor() - base.room = null - } + console.log((pos.x - cam.x), wall.edge - cam.x, radius) + if (perp_d == 0) return Infinity + return perp_n / perp_d + } +*/ // collision test failed, so update position cam.x = pos.x cam.z = pos.z diff --git a/public/assets/javascripts/vendor/canvasutilities.js b/public/assets/javascripts/vendor/canvasutilities.js new file mode 100644 index 0000000..011ebb0 --- /dev/null +++ b/public/assets/javascripts/vendor/canvasutilities.js @@ -0,0 +1,145 @@ +var drawArrow = function(ctx, x1, y1, x2, y2, style, which, angle, d) { + 'use strict'; + // Ceason pointed to a problem when x1 or y1 were a string, and concatenation + // would happen instead of addition + if (typeof(x1) == 'string') x1 = parseInt(x1); + if (typeof(y1) == 'string') y1 = parseInt(y1); + if (typeof(x2) == 'string') x2 = parseInt(x2); + if (typeof(y2) == 'string') y2 = parseInt(y2); + style = typeof(style) != 'undefined' ? style : 3; + which = typeof(which) != 'undefined' ? which : 1; // end point gets arrow + angle = typeof(angle) != 'undefined' ? angle : Math.PI / 8; + d = typeof(d) != 'undefined' ? d : 10; + // default to using drawHead to draw the head, but if the style + // argument is a function, use it instead + var toDrawHead = typeof(style) != 'function' ? drawHead : style; + + // For ends with arrow we actually want to stop before we get to the arrow + // so that wide lines won't put a flat end on the arrow. + // + var dist = Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)); + var ratio = (dist - d / 3) / dist; + var tox, toy, fromx, fromy; + if (which & 1) { + tox = Math.round(x1 + (x2 - x1) * ratio); + toy = Math.round(y1 + (y2 - y1) * ratio); + } else { + tox = x2; + toy = y2; + } + if (which & 2) { + fromx = x1 + (x2 - x1) * (1 - ratio); + fromy = y1 + (y2 - y1) * (1 - ratio); + } else { + fromx = x1; + fromy = y1; + } + + // Draw the shaft of the arrow + ctx.beginPath(); + ctx.moveTo(fromx, fromy); + ctx.lineTo(tox, toy); + ctx.stroke(); + + // calculate the angle of the line + var lineangle = Math.atan2(y2 - y1, x2 - x1); + // h is the line length of a side of the arrow head + var h = Math.abs(d / Math.cos(angle)); + + if (which & 1) { // handle far end arrow head + var angle1 = lineangle + Math.PI + angle; + var topx = x2 + Math.cos(angle1) * h; + var topy = y2 + Math.sin(angle1) * h; + var angle2 = lineangle + Math.PI - angle; + var botx = x2 + Math.cos(angle2) * h; + var boty = y2 + Math.sin(angle2) * h; + toDrawHead(ctx, topx, topy, x2, y2, botx, boty, style); + } + if (which & 2) { // handle near end arrow head + var angle1 = lineangle + angle; + var topx = x1 + Math.cos(angle1) * h; + var topy = y1 + Math.sin(angle1) * h; + var angle2 = lineangle - angle; + var botx = x1 + Math.cos(angle2) * h; + var boty = y1 + Math.sin(angle2) * h; + toDrawHead(ctx, topx, topy, x1, y1, botx, boty, style); + } +} + +var drawHead = function(ctx, x0, y0, x1, y1, x2, y2, style) { + 'use strict'; + if (typeof(x0) == 'string') x0 = parseInt(x0); + if (typeof(y0) == 'string') y0 = parseInt(y0); + if (typeof(x1) == 'string') x1 = parseInt(x1); + if (typeof(y1) == 'string') y1 = parseInt(y1); + if (typeof(x2) == 'string') x2 = parseInt(x2); + if (typeof(y2) == 'string') y2 = parseInt(y2); + var radius = 3; + var twoPI = 2 * Math.PI; + + // all cases do this. + ctx.save(); + ctx.beginPath(); + ctx.moveTo(x0, y0); + ctx.lineTo(x1, y1); + ctx.lineTo(x2, y2); + switch (style) { + case 0: + // curved filled, add the bottom as an arcTo curve and fill + var backdist = Math.sqrt(((x2 - x0) * (x2 - x0)) + ((y2 - y0) * (y2 - y0))); + ctx.arcTo(x1, y1, x0, y0, .55 * backdist); + ctx.fill(); + break; + case 1: + // straight filled, add the bottom as a line and fill. + ctx.beginPath(); + ctx.moveTo(x0, y0); + ctx.lineTo(x1, y1); + ctx.lineTo(x2, y2); + ctx.lineTo(x0, y0); + ctx.fill(); + break; + case 2: + // unfilled head, just stroke. + ctx.stroke(); + break; + case 3: + //filled head, add the bottom as a quadraticCurveTo curve and fill + var cpx = (x0 + x1 + x2) / 3; + var cpy = (y0 + y1 + y2) / 3; + ctx.quadraticCurveTo(cpx, cpy, x0, y0); + ctx.fill(); + break; + case 4: + //filled head, add the bottom as a bezierCurveTo curve and fill + var cp1x, cp1y, cp2x, cp2y, backdist; + var shiftamt = 5; + if (x2 == x0) { + // Avoid a divide by zero if x2==x0 + backdist = y2 - y0; + cp1x = (x1 + x0) / 2; + cp2x = (x1 + x0) / 2; + cp1y = y1 + backdist / shiftamt; + cp2y = y1 - backdist / shiftamt; + } else { + backdist = Math.sqrt(((x2 - x0) * (x2 - x0)) + ((y2 - y0) * (y2 - y0))); + var xback = (x0 + x2) / 2; + var yback = (y0 + y2) / 2; + var xmid = (xback + x1) / 2; + var ymid = (yback + y1) / 2; + + var m = (y2 - y0) / (x2 - x0); + var dx = (backdist / (2 * Math.sqrt(m * m + 1))) / shiftamt; + var dy = m * dx; + cp1x = xmid - dx; + cp1y = ymid - dy; + cp2x = xmid + dx; + cp2y = ymid + dy; + } + + ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x0, y0); + ctx.fill(); + break; + } + ctx.restore(); +} diff --git a/public/assets/test/intersect.html b/public/assets/test/intersect.html new file mode 100644 index 0000000..3f6d110 --- /dev/null +++ b/public/assets/test/intersect.html @@ -0,0 +1,132 @@ +<canvas id="canvas"></canvas> + +<script src="/assets/javascripts/util.js"></script> +<script src="/assets/javascripts/vendor/tube.js"></script> +<script src="/assets/javascripts/rectangles/util/constants.js"></script> +<script src="/assets/javascripts/rectangles/util/mouse.js"></script> +<script src="/assets/javascripts/rectangles/models/vec2.js"></script> +<script src="/assets/javascripts/rectangles/models/rect.js"></script> +<script src="/assets/javascripts/vendor/canvasutilities.js"></script> +<script> + +var ctx = canvas.getContext('2d') + +var w = canvas.width = 600 +var h = canvas.height = 400 + +var vec_a = new Rect( 100, 120, 50, 100 ) +var vec_b = new Rect( 20, 200, 120, 120 ) +var vecs = [ vec_a, vec_b ] +var points = [ vec_a.x, vec_a.y, vec_b.x, vec_b.y ] +var r = 4 + +var vec_c = new Rect() +vec_c.x = vec_a.x +vec_c.y = vec_b.x + +var intersect = new vec2 () + +var dragging = false, dragging_a, dragging_b +var delta = new vec2( 0, 0 ) + +var mymouse = new mouse({ + el: canvas, + down: function(e, cursor){ + points.some(function(p){ + if (-cursor.x.a > p.a - r && -cursor.x.a < p.a + r && + cursor.y.a > p.b - r && cursor.y.a < p.b + r) { + dragging_a = p.a + dragging_b = p.b + dragging = p + return true + } + }) + }, + drag: function(e, cursor){ + if (! dragging) return + delta = cursor.delta() + delta.a = - delta.a + dragging.a = dragging_a + delta.a + dragging.b = dragging_b + delta.b + }, + up: function(e, cursor, new_cursor){ + delta.zero() + dragging.dragging = false + dragging = false + }, +}) + +function draw () { + ctx.fillStyle = "#fff" + ctx.fillRect(0,0,w,h) + + ctx.fillStyle = "#bbb" + points.forEach(drawPoint) + + drawLine(vec_a.x, vec_a.y, "#f00") + drawLine(vec_b.x, vec_b.y, "#00f") + drawLine(vec_a.x, vec_b.x, "#080") + + var t = perp(vec_c, vec_b) / ( perp(vec_a, vec_b) || 0.0000001 ) + intersect.a = vec_a.x.a + ( vec_a.y.a - vec_a.x.a ) * t + intersect.b = vec_a.x.b + ( vec_a.y.b - vec_a.x.b ) * t + + var collinear = is_collinear( intersect.b, vec_b ) + var long_enough_to_intersect = 0 <= t && t <= 1 + + if (long_enough_to_intersect && collinear) { + ctx.fillStyle = "#f00" + } + else if (is_on_line) { + ctx.fillStyle = "#0f0" + } + else { + ctx.fillStyle = "#000" + } + + drawPoint(intersect) +} +function drawLine (pa, pb, color) { + ctx.fillStyle = color + ctx.strokeStyle = color + var x1 = pa.a + var y1 = pa.b + var x2 = pb.a + var y2 = pb.b + drawArrow(ctx, x1, y1, x2, y2) +} +function drawPoint (p) { + var x = p.a - r + var y = p.b - r + ctx.fillRect(x, y, r*2, r*2) +} +function perp (va, vb) { + return (va.y.a - va.x.a) * (vb.y.b - vb.x.b) - (va.y.b - va.x.b) * (vb.y.a - vb.x.a) +} +function is_collinear (n, vec) { + var on_x, on_y + + if (vec.x.a < vec.y.a) { + on_x = vec.x.a <= n && n <= vec.y.a + } + else { + on_x = vec.x.a >= n && n >= vec.y.a + } + + if (vec.x.b < vec.y.b) { + on_y = vec.x.b <= n && n <= vec.y.b + } + else { + on_y = vec.x.b >= n && n >= vec.y.b + } + + return !! (on_x && on_y) +} + +function animate(){ + requestAnimationFrame(animate) + draw() +} +animate() + +</script> |
