├── .gitignore ├── .npmignore ├── LICENSE.txt ├── README.md ├── bench.js ├── demo ├── 2d.js └── raytracer.js ├── package.json ├── ray-aabb.js └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | v8.log 3 | s.js 4 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | v8.log 2 | s.js 3 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright © 2015 Elijah Insua 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the “Software”), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ray-aabb 2 | 3 | test if a ray intersects an aabb in 2d/3d space 4 | 5 | Implemented via the techniques described in [Fast Ray/Axis-Aligned Bounding Box Overlap Tests using Ray Slopes](http://www.cg.cs.tu-bs.de/publications/Eisemann07FRA/) 6 | 7 | ## install 8 | 9 | `npm install ray-aabb` 10 | 11 | ## use 12 | 13 | ```javascript 14 | var createRay = require('ray-aabb'); 15 | 16 | /* 17 | +------+ 18 | / /| 19 | (-1, 1, 0) ----> +------+ | 20 | | | + 21 | | |/ 22 | +------+ 23 | */ 24 | 25 | var ray_origin = [-1, 1, 0]; 26 | var ray_direction = [1, 0, 0]; 27 | var ray = createRay(ray_origin, ray_direction); 28 | 29 | var box = [ 30 | [0, 0, 0], 31 | [2, 2, 2] 32 | ]; 33 | 34 | console.log(ray.intersects(box)); 35 | // outputs: true 36 | 37 | // avoid allocating new memory by reusing rays 38 | ray.update(ray_origin, [-1, 0, 0]); 39 | 40 | console.log(ray.intersects(box)); 41 | // outputs: false 42 | 43 | var normal = [0, 0, 0]; 44 | var d = ray.intersects(box, normal); 45 | console.log(d); 46 | // outputs: 1 47 | 48 | console.log(normal); 49 | // outputs: [ -1, 0, 0 ] 50 | ``` 51 | 52 | ## api surface 53 | 54 | all vectors specified are arrays in the format: `[x, y, z]` with `z` being optional for 2d vectors 55 | 56 | __createRay__(`ray_origin`, `ray_direction`) 57 | 58 | _parameters_: 59 | 60 | * `ray_origin` - a vector defining the ray origin 61 | * `ray_direction` - a _normalized_ vector defining the ray direction 62 | _returns_: a `Ray` instance 63 | 64 | --- 65 | 66 | __Ray#update__(`ray_origin`, `ray_direction`) 67 | 68 | 69 | Allows `Ray` instances to be reused by precomputing ray classification. The intention here is that you will be casting a ray against __many__ aabbs 70 | 71 | _parameters_: same as __createRay__ 72 | 73 | _returns_: `this` (e.g. `ray.update(ro, rd).intersects(box)` 74 | 75 | --- 76 | 77 | __Ray#intersects__(`aabb`[, `normal`]) 78 | where `aabb` specifies the corners of the bounding box: 79 | 80 | ```javascript 81 | [[x1, y1, z1], [x2, y2, z2]] 82 | ``` 83 | 84 | and the optional `normal` argument is a 2d/3d vector (e.g., `[0, 0]`) that will be populated with the _non-normalized_ normal of the corner/edge/face that the ray intersected with. 85 | 86 | _returns_ 87 | 88 | if `normal` is not passed 89 | 90 | * `true` if intersection detected 91 | * `false` if no intersection 92 | 93 | if `normal` is passed: 94 | 95 | * `false` if no intersection or a number denoting how far along the ray the collision occurred. You can use this number to compute the point of intersection. See the demos for example usage. 96 | 97 | ## platforms 98 | 99 | node and evergreen browsers using [browserify](browserify.io) 100 | 101 | ## Demos 102 | 103 | ### 2d 104 | 105 | ![random-ray](http://i.imgur.com/oo3nYo2.png) 106 | 107 | `npm run demo-2d` 108 | 109 | ### 3d (software raytracer) 110 | 111 | ![random-ray](http://imgur.com/6tjoDEa.png) 112 | 113 | `npm run demo-raytracer` 114 | 115 | 116 | ## license 117 | 118 | [MIT](LICENSE.txt) 119 | -------------------------------------------------------------------------------- /bench.js: -------------------------------------------------------------------------------- 1 | var createRay = require('./ray-aabb'); 2 | var Suite = require('benchmark').Suite; 3 | var suite = new Suite(); 4 | 5 | var boxa = [ 6 | [-1, -1, -1], 7 | [ 1, 1, 1] 8 | ]; 9 | 10 | var origin = [-2, 0, 0]; 11 | var dir = [1, 0, 0]; 12 | 13 | var ray = createRay(origin, dir); 14 | 15 | suite.add('raycast along x axis', function() { 16 | for (var x=-1; x<=1; x++) { 17 | origin[0] = -x * 2; 18 | dir[0] = x; 19 | 20 | for (var y=-1; y<=1; y++) { 21 | origin[1] = -y * 2; 22 | dir[1] = y; 23 | 24 | for (var z=-1; z<=1; z++) { 25 | origin[2] = -z * 2; 26 | dir[2] = z; 27 | 28 | ray.update(origin, dir); 29 | ray.intersects(boxa) 30 | } 31 | } 32 | } 33 | }) 34 | 35 | suite.on('cycle', function(event) { 36 | console.log(String(event.target)); 37 | }); 38 | 39 | suite.on('complete', function() { 40 | console.log('Fastest is ' + this.filter('fastest').pluck('name')); 41 | }); 42 | 43 | suite.run(); 44 | -------------------------------------------------------------------------------- /demo/2d.js: -------------------------------------------------------------------------------- 1 | var fc = require('fc'); 2 | var center = require('ctx-translate-center'); 3 | var createRay = require('../ray-aabb'); 4 | 5 | var v2normalize = require('gl-vec2/normalize'); 6 | var v2lerp = require('gl-vec2/lerp'); 7 | var v2copy = require('gl-vec2/copy'); 8 | var v2dist = require('gl-vec2/distance'); 9 | 10 | var drawCircle = require('ctx-circle'); 11 | 12 | var boxColor = 'rgba(214, 235, 173, .5)' 13 | var boxes = [ 14 | [[-10, -50], [100, 10], boxColor], 15 | [[-10, 40], [10, 100], boxColor], 16 | [[-105, -100], [-100, 100], boxColor], 17 | [[-105, 100], [105, 105], boxColor], 18 | [[100, -100], [105, 100], boxColor], 19 | [[-105, -105], [105, -100], boxColor], 20 | ]; 21 | 22 | function drawBox(ctx, bounds) { 23 | var lb = bounds[0]; 24 | var ub = bounds[1]; 25 | 26 | ctx.beginPath(); 27 | ctx.moveTo(lb[0], lb[1]) 28 | ctx.lineTo(ub[0], lb[1]) 29 | ctx.lineTo(ub[0], ub[1]) 30 | ctx.lineTo(lb[0], ub[1]) 31 | ctx.closePath(); 32 | ctx.fillStyle = bounds[2] || "#A7B787" 33 | ctx.fill(); 34 | } 35 | 36 | function drawLine(ctx, s, e, color) { 37 | ctx.beginPath() 38 | ctx.moveTo(s[0]|0, s[1]|0); 39 | ctx.lineTo(e[0]|0, e[1]|0); 40 | ctx.strokeStyle = color; 41 | ctx.stroke(); 42 | } 43 | 44 | var lines = []; 45 | var v2scratch = [0, 0]; 46 | var ro = [-50, 3]; 47 | var rd = [1, .1]; 48 | v2normalize(rd, rd); 49 | var ray = createRay(ro, rd); 50 | var ctx = fc(function() { 51 | 52 | ctx.clear(); 53 | center(ctx); 54 | ctx.scale(1, -1); 55 | 56 | var active = false; 57 | 58 | for (var j=0; j 0 ? -Math.random() : Math.random();//-rd[1]/10; 99 | rd[1] = t > 0 ? Math.random() : -Math.random(); 100 | v2normalize(rd, rd); 101 | ray.update(ro, rd); 102 | } 103 | 104 | }, true) 105 | -------------------------------------------------------------------------------- /demo/raytracer.js: -------------------------------------------------------------------------------- 1 | var fc = require('fc'); 2 | var v3normalize = require('gl-vec3/normalize'); 3 | var v3sub = require('gl-vec3/subtract'); 4 | var v3mul = require('gl-vec3/multiply'); 5 | var v3scale = require('gl-vec3/scale'); 6 | var v3add = require('gl-vec3/add'); 7 | var v3dist = require('gl-vec3/distance'); 8 | var v3distSquared = require('gl-vec3/squaredDistance'); 9 | var v3tm4 = require('gl-vec3/transformMat4'); 10 | var m4create = require('gl-mat4/create'); 11 | var m4perspective = require('gl-mat4/perspective'); 12 | var m4invert = require('gl-mat4/invert'); 13 | var m4mul = require('gl-mat4/multiply'); 14 | var createRay = require('../ray-aabb'); 15 | var ndarray = require('ndarray'); 16 | var fill = require('ndarray-fill'); 17 | var unproject = require('camera-unproject'); 18 | 19 | var createOrbitCamera = require("orbit-camera") 20 | 21 | var camera = createOrbitCamera([5, 5, -10], 22 | [0, 0, 0], 23 | [0, 1, 0]) 24 | var projection = m4create(); 25 | var view = m4create(); 26 | var m4mvp = m4create(); 27 | var m4inverted = m4create(); 28 | var m4scratch = m4create(); 29 | var box = [[0, 0], [0, 0]]; 30 | var mouse = [0, 0]; 31 | var mouseDown = false; 32 | 33 | var rayOrigin = [0, 0, 0]; 34 | var rayDirection = [0, 0, 0]; 35 | var normal = [0, 0, 0]; 36 | var tnormal = [0, 0, 0]; 37 | var isect = [0, 0, 0]; 38 | 39 | var ray = createRay(rayOrigin, rayDirection); 40 | var modelWidth = 8 41 | var modelHalfWidth = modelWidth/2; 42 | var modelBounds = [ 43 | [-modelHalfWidth, -modelHalfWidth, -modelHalfWidth], 44 | [modelHalfWidth, modelHalfWidth, modelHalfWidth] 45 | ]; 46 | var model = ndarray(new Uint8Array(modelWidth*modelWidth*modelWidth), [modelWidth, modelWidth, modelWidth]); 47 | var depthSorted = new Array(modelWidth*modelWidth*modelWidth); 48 | var depthPos = 0; 49 | fill(model, function(x, y, z) { 50 | 51 | if (x%2 && y%2 && z%2) { 52 | 53 | // poor mans compression 54 | x = -modelHalfWidth + x; 55 | y = -modelHalfWidth + y; 56 | z = -modelHalfWidth + z; 57 | 58 | var bounds = [ 59 | [x - 0.5, y - 0.5, z - 0.5], 60 | [x + 0.5, y + 0.5, z + 0.5] 61 | ]; 62 | 63 | var center = [ 64 | (bounds[0][0] + bounds[1][0]) / 2, 65 | (bounds[0][1] + bounds[1][1]) / 2, 66 | (bounds[0][2] + bounds[1][2]) / 2 67 | ]; 68 | depthSorted[depthPos] = [bounds, center, depthPos]; 69 | return 255; 70 | } 71 | 72 | depthPos++; 73 | return 0; 74 | }) 75 | 76 | function getEye(out, view) { 77 | m4invert(m4scratch, view); 78 | out[0] = m4scratch[12]; 79 | out[1] = m4scratch[13]; 80 | out[2] = m4scratch[14] 81 | return out; 82 | } 83 | 84 | window.addEventListener('mousedown', function() { 85 | mouseDown = true; 86 | }); 87 | 88 | window.addEventListener('mouseup', function() { 89 | mouseDown = false; 90 | }); 91 | 92 | window.addEventListener('mousemove', function(ev) { 93 | var x = ev.clientX; 94 | var y = ev.clientY; 95 | 96 | if (mouseDown) { 97 | var w = ctx.canvas.width; 98 | var h = ctx.canvas.height; 99 | camera.rotate( 100 | [x/w-0.5, y/h-0.5], 101 | [mouse[0]/w-0.5, mouse[1]/h-0.5] 102 | ); 103 | ctx.dirty(); 104 | } 105 | mouse[0] = x; 106 | mouse[1] = y; 107 | }); 108 | 109 | window.addEventListener('mousewheel', function(ev) { 110 | camera.zoom(ev.wheelDeltaY * -.001); 111 | ctx.dirty(); 112 | ev.preventDefault(); 113 | }); 114 | 115 | var viewport = [0, 0, 0, 0]; 116 | var near = [0, 0, 0]; 117 | 118 | var ctx = fc(function render() { 119 | ctx.clear(); 120 | var w = viewport[2] = ctx.canvas.width; 121 | var h = viewport[3] = ctx.canvas.height; 122 | var imageData = ctx.createImageData(w, h); 123 | var buffer = imageData.data; 124 | 125 | var aspect = w/h 126 | m4perspective( 127 | projection, 128 | Math.PI/4.0, 129 | aspect, 130 | 0.1, 131 | 1000.0 132 | ) 133 | 134 | camera.view(view) 135 | 136 | m4invert( 137 | m4inverted, 138 | m4mul(m4mvp, projection, view) 139 | ); 140 | 141 | getEye(rayOrigin, view); 142 | 143 | depthSorted.sort(function depthSort(a, b) { 144 | var da = v3distSquared(a[1], rayOrigin); 145 | var db = v3distSquared(b[1], rayOrigin); 146 | return da-db; 147 | }) 148 | 149 | for (var y=0; y", 32 | "license": "MIT", 33 | "bugs": { 34 | "url": "https://github.com/tmpvar/ray-aabb/issues" 35 | }, 36 | "homepage": "https://github.com/tmpvar/ray-aabb", 37 | "devDependencies": { 38 | "benchmark": "^1.0.0", 39 | "budo": "^5.1.4", 40 | "camera-unproject": "^1.0.1", 41 | "ctx-circle": "^1.0.0", 42 | "ctx-translate-center": "^1.0.0", 43 | "fc": "^1.4.3", 44 | "gl-mat4": "^1.1.4", 45 | "gl-vec2": "^1.0.0", 46 | "gl-vec3": "^1.0.2", 47 | "microtime": "^1.2.0", 48 | "ndarray": "^1.0.18", 49 | "ndarray-fill": "^1.0.1", 50 | "orbit-camera": "^1.0.0", 51 | "tape": "^3.4.0" 52 | }, 53 | "dependencies": { 54 | "ray-direction-classify": "^1.0.3" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /ray-aabb.js: -------------------------------------------------------------------------------- 1 | var classify = require('ray-direction-classify'); 2 | 3 | module.exports = createRay; 4 | 5 | var tests = {}; 6 | var max = Math.max; 7 | var abs = Math.abs; 8 | 9 | tests[classify.MMM] = function testMMM(ray, lb, ub) { 10 | var ro = ray.ro; 11 | 12 | return !(ro[0] < lb[0] || 13 | ro[1] < lb[1] || 14 | ro[2] < lb[2] || 15 | ray.jbyi * lb[0] - ub[1] + ray.cxy > 0 || 16 | ray.ibyj * lb[1] - ub[0] + ray.cyx > 0 || 17 | ray.jbyk * lb[2] - ub[1] + ray.czy > 0 || 18 | ray.kbyj * lb[1] - ub[2] + ray.cyz > 0 || 19 | ray.kbyi * lb[0] - ub[2] + ray.cxz > 0 || 20 | ray.ibyk * lb[2] - ub[0] + ray.czx > 0); 21 | }; 22 | tests[classify.MMP] = function testMMP(ray, lb, ub) { 23 | var ro = ray.ro; 24 | 25 | return !(ro[0] < lb[0] || 26 | ro[1] < lb[1] || 27 | ro[2] > ub[2] || 28 | ray.jbyi * lb[0] - ub[1] + ray.cxy > 0 || 29 | ray.ibyj * lb[1] - ub[0] + ray.cyx > 0 || 30 | ray.jbyk * ub[2] - ub[1] + ray.czy > 0 || 31 | ray.kbyj * lb[1] - lb[2] + ray.cyz < 0 || 32 | ray.kbyi * lb[0] - lb[2] + ray.cxz < 0 || 33 | ray.ibyk * ub[2] - ub[0] + ray.czx > 0); 34 | }; 35 | tests[classify.MPM] = function testMPM(ray, lb, ub) { 36 | var ro = ray.ro; 37 | 38 | return !(ro[0] < lb[0] || 39 | ro[1] > ub[1] || 40 | ro[2] < lb[2] || 41 | ray.jbyi * lb[0] - lb[1] + ray.cxy < 0 || 42 | ray.ibyj * ub[1] - ub[0] + ray.cyx > 0 || 43 | ray.jbyk * lb[2] - lb[1] + ray.czy < 0 || 44 | ray.kbyj * ub[1] - ub[2] + ray.cyz > 0 || 45 | ray.kbyi * lb[0] - ub[2] + ray.cxz > 0 || 46 | ray.ibyk * lb[2] - ub[0] + ray.czx > 0); 47 | }; 48 | tests[classify.MPP] = function testMPP(ray, lb, ub) { 49 | var ro = ray.ro; 50 | 51 | return !(ro[0] < lb[0] || 52 | ro[1] > ub[1] || 53 | ro[2] > ub[2] || 54 | ray.jbyi * lb[0] - lb[1] + ray.cxy < 0 || 55 | ray.ibyj * ub[1] - ub[0] + ray.cyx > 0 || 56 | ray.jbyk * ub[2] - lb[1] + ray.czy < 0 || 57 | ray.kbyj * ub[1] - lb[2] + ray.cyz < 0 || 58 | ray.kbyi * lb[0] - lb[2] + ray.cxz < 0 || 59 | ray.ibyk * ub[2] - ub[0] + ray.czx > 0); 60 | }; 61 | tests[classify.PMM] = function testPMM(ray, lb, ub) { 62 | var ro = ray.ro; 63 | 64 | return !(ro[0] > ub[0] || 65 | ro[1] < lb[1] || 66 | ro[2] < lb[2] || 67 | ray.jbyi * ub[0] - ub[1] + ray.cxy > 0 || 68 | ray.ibyj * lb[1] - lb[0] + ray.cyx < 0 || 69 | ray.jbyk * lb[2] - ub[1] + ray.czy > 0 || 70 | ray.kbyj * lb[1] - ub[2] + ray.cyz > 0 || 71 | ray.kbyi * ub[0] - ub[2] + ray.cxz > 0 || 72 | ray.ibyk * lb[2] - lb[0] + ray.czx < 0); 73 | }; 74 | tests[classify.PMP] = function testPMP(ray, lb, ub) { 75 | var ro = ray.ro; 76 | 77 | return !(ro[0] > ub[0] || 78 | ro[1] < lb[1] || 79 | ro[2] > ub[2] || 80 | ray.jbyi * ub[0] - ub[1] + ray.cxy > 0 || 81 | ray.ibyj * lb[1] - lb[0] + ray.cyx < 0 || 82 | ray.jbyk * ub[2] - ub[1] + ray.czy > 0 || 83 | ray.kbyj * lb[1] - lb[2] + ray.cyz < 0 || 84 | ray.kbyi * ub[0] - lb[2] + ray.cxz < 0 || 85 | ray.ibyk * ub[2] - lb[0] + ray.czx < 0); 86 | }; 87 | tests[classify.PPM] = function testPPM(ray, lb, ub) { 88 | var ro = ray.ro; 89 | 90 | return !(ro[0] > ub[0] || 91 | ro[1] > ub[1] || 92 | ro[2] < lb[2] || 93 | ray.jbyi * ub[0] - lb[1] + ray.cxy < 0 || 94 | ray.ibyj * ub[1] - lb[0] + ray.cyx < 0 || 95 | ray.jbyk * lb[2] - lb[1] + ray.czy < 0 || 96 | ray.kbyj * ub[1] - ub[2] + ray.cyz > 0 || 97 | ray.kbyi * ub[0] - ub[2] + ray.cxz > 0 || 98 | ray.ibyk * lb[2] - lb[0] + ray.czx < 0); 99 | }; 100 | tests[classify.PPP] = function testPPP(ray, lb, ub) { 101 | var ro = ray.ro; 102 | 103 | return !(ro[0] > ub[0] || 104 | ro[1] > ub[1] || 105 | ro[2] > ub[2] || 106 | ray.jbyi * ub[0] - lb[1] + ray.cxy < 0 || 107 | ray.ibyj * ub[1] - lb[0] + ray.cyx < 0 || 108 | ray.jbyk * ub[2] - lb[1] + ray.czy < 0 || 109 | ray.kbyj * ub[1] - lb[2] + ray.cyz < 0 || 110 | ray.kbyi * ub[0] - lb[2] + ray.cxz < 0 || 111 | ray.ibyk * ub[2] - lb[0] + ray.czx < 0); 112 | }; 113 | tests[classify.POO] = function testPOO(ray, lb, ub) { 114 | var ro = ray.ro; 115 | var ro1 = ro[1]; 116 | var ro2 = ro[2]; 117 | 118 | return !(ro[0] > ub[0] || 119 | ro1 < lb[1] || 120 | ro1 > ub[1] || 121 | ro2 < lb[2] || 122 | ro2 > ub[2]); 123 | }; 124 | tests[classify.MOO] = function testMOO(ray, lb, ub) { 125 | var ro = ray.ro; 126 | 127 | return !(ro[0] < lb[0] || 128 | ro[1] < lb[1] || 129 | ro[1] > ub[1] || 130 | ro[2] < lb[2] || 131 | ro[2] > ub[2]); 132 | }; 133 | tests[classify.OPO] = function testOPO(ray, lb, ub) { 134 | var ro = ray.ro; 135 | 136 | return !(ro[1] > ub[1] || 137 | ro[0] < lb[0] || 138 | ro[0] > ub[0] || 139 | ro[2] < lb[2] || 140 | ro[2] > ub[2]); 141 | }; 142 | tests[classify.OMO] = function testOMO(ray, lb, ub) { 143 | var ro = ray.ro; 144 | 145 | return !(ro[1] < lb[1] || 146 | ro[0] < lb[0] || 147 | ro[0] > ub[0] || 148 | ro[2] < lb[2] || 149 | ro[2] > ub[2]); 150 | }; 151 | tests[classify.OOP] = function testOOP(ray, lb, ub) { 152 | var ro = ray.ro; 153 | 154 | return !(ro[2] > ub[2] || 155 | ro[0] < lb[0] || 156 | ro[0] > ub[0] || 157 | ro[1] < lb[1] || 158 | ro[1] > ub[1]); 159 | }; 160 | tests[classify.OOM] = function testOOM(ray, lb, ub) { 161 | var ro = ray.ro; 162 | 163 | return !(ro[2] < lb[2] || 164 | ro[0] < lb[0] || 165 | ro[0] > ub[0] || 166 | ro[1] < lb[1] || 167 | ro[1] > ub[1]); 168 | }; 169 | tests[classify.OMM] = function testOMM(ray, lb, ub) { 170 | var ro = ray.ro; 171 | 172 | return !(ro[0] < lb[0] || 173 | ro[0] > ub[0] || 174 | ro[1] < lb[1] || 175 | ro[2] < lb[2] || 176 | ray.jbyk * lb[2] - ub[1] + ray.czy > 0 || 177 | ray.kbyj * lb[1] - ub[2] + ray.cyz > 0); 178 | }; 179 | tests[classify.OMP] = function testOMP(ray, lb, ub) { 180 | var ro = ray.ro; 181 | 182 | return !(ro[0] < lb[0] || 183 | ro[0] > ub[0] || 184 | ro[1] < lb[1] || 185 | ro[2] > ub[2] || 186 | ray.jbyk * ub[2] - ub[1] + ray.czy > 0 || 187 | ray.kbyj * lb[1] - lb[2] + ray.cyz < 0); 188 | }; 189 | tests[classify.OPM] = function testOPM(ray, lb, ub) { 190 | var ro = ray.ro; 191 | 192 | return !(ro[0] < lb[0] || 193 | ro[0] > ub[0] || 194 | ro[1] > ub[1] || 195 | ro[2] < lb[2] || 196 | ray.jbyk * lb[2] - lb[1] + ray.czy < 0 || 197 | ray.kbyj * ub[1] - ub[2] + ray.cyz > 0); 198 | }; 199 | tests[classify.OPP] = function testOPP(ray, lb, ub) { 200 | var ro = ray.ro; 201 | 202 | return !(ro[0] < lb[0] || 203 | ro[0] > ub[0] || 204 | ro[1] > ub[1] || 205 | ro[2] > ub[2] || 206 | ray.jbyk * ub[2] - lb[1] + ray.czy < 0 || 207 | ray.kbyj * ub[1] - lb[2] + ray.cyz < 0); 208 | }; 209 | tests[classify.MOM] = function testMOM(ray, lb, ub) { 210 | var ro = ray.ro; 211 | 212 | return !(ro[1] < lb[1] || 213 | ro[1] > ub[1] || 214 | ro[0] < lb[0] || 215 | ro[2] < lb[2] || 216 | ray.kbyi * lb[0] - ub[2] + ray.cxz > 0 || 217 | ray.ibyk * lb[2] - ub[0] + ray.czx > 0); 218 | }; 219 | tests[classify.MOP] = function testMOP(ray, lb, ub) { 220 | var ro = ray.ro; 221 | 222 | return !(ro[1] < lb[1] || 223 | ro[1] > ub[1] || 224 | ro[0] < lb[0] || 225 | ro[2] > ub[2] || 226 | ray.kbyi * lb[0] - lb[2] + ray.cxz < 0 || 227 | ray.ibyk * ub[2] - ub[0] + ray.czx > 0); 228 | }; 229 | tests[classify.POM] = function testPOM(ray, lb, ub) { 230 | var ro = ray.ro; 231 | 232 | return !(ro[1] < lb[1] || 233 | ro[1] > ub[1] || 234 | ro[0] > ub[0] || 235 | ro[2] < lb[2] || 236 | ray.kbyi * ub[0] - ub[2] + ray.cxz > 0 || 237 | ray.ibyk * lb[2] - lb[0] + ray.czx < 0); 238 | }; 239 | tests[classify.POP] = function testPOP(ray, lb, ub) { 240 | var ro = ray.ro; 241 | 242 | return !(ro[1] < lb[1] || 243 | ro[1] > ub[1] || 244 | ro[0] > ub[0] || 245 | ro[2] > ub[2] || 246 | ray.kbyi * ub[0] - lb[2] + ray.cxz < 0 || 247 | ray.ibyk * ub[2] - lb[0] + ray.czx < 0); 248 | }; 249 | tests[classify.MMO] = function testMMO(ray, lb, ub) { 250 | var ro = ray.ro; 251 | 252 | return !(ro[2] < lb[2] || 253 | ro[2] > ub[2] || 254 | ro[0] < lb[0] || 255 | ro[1] < lb[1] || 256 | ray.jbyi * lb[0] - ub[1] + ray.cxy > 0 || 257 | ray.ibyj * lb[1] - ub[0] + ray.cyx > 0); 258 | }; 259 | tests[classify.MPO] = function testMPO(ray, lb, ub) { 260 | var ro = ray.ro; 261 | 262 | return !(ro[2] < lb[2] || 263 | ro[2] > ub[2] || 264 | ro[0] < lb[0] || 265 | ro[1] > ub[1] || 266 | ray.jbyi * lb[0] - lb[1] + ray.cxy < 0 || 267 | ray.ibyj * ub[1] - ub[0] + ray.cyx > 0); 268 | }; 269 | tests[classify.PMO] = function testPMO(ray, lb, ub) { 270 | var ro = ray.ro; 271 | 272 | return !(ro[2] < lb[2] || 273 | ro[2] > ub[2] || 274 | ro[0] > ub[0] || 275 | ro[1] < lb[1] || 276 | ray.jbyi * ub[0] - ub[1] + ray.cxy > 0 || 277 | ray.ibyj * lb[1] - lb[0] + ray.cyx < 0); 278 | }; 279 | tests[classify.PPO] = function testPPO(ray, lb, ub) { 280 | var ro = ray.ro; 281 | 282 | return !(ro[2] < lb[2] || 283 | ro[2] > ub[2] || 284 | ro[0] > ub[0] || 285 | ro[1] > ub[1] || 286 | ray.jbyi * ub[0] - lb[1] + ray.cxy < 0 || 287 | ray.ibyj * ub[1] - lb[0] + ray.cyx < 0); 288 | }; 289 | 290 | var lerps = {}; 291 | 292 | lerps[classify.MMM] = function lerpMMM(ray, aabb, norm) { 293 | var ro = ray.ro; 294 | var ub = aabb[1]; 295 | var a = (ub[0] - ro[0]) * ray.ii; 296 | var b = (ub[1] - ro[1]) * ray.ij; 297 | var c = (ub[2] - ro[2]) * ray.ik; 298 | 299 | norm[0] = (a >= b && a >= c) ? 1 : 0; 300 | norm[1] = (b >= c && b >= a) ? 1 : 0; 301 | norm[2] = (c >= a && c >= b) ? 1 : 0; 302 | 303 | return max(a, b, c); 304 | }; 305 | 306 | lerps[classify.MMP] = function lerpMMP(ray, aabb, norm) { 307 | var ro = ray.ro; 308 | var ub = aabb[1]; 309 | var lb = aabb[0]; 310 | 311 | var a = (ub[0] - ro[0]) * ray.ii; 312 | var b = (ub[1] - ro[1]) * ray.ij; 313 | var c = (lb[2] - ro[2]) * ray.ik; 314 | 315 | norm[0] = (a >= b && a >= c) ? 1 : 0; 316 | norm[1] = (b >= c && b >= a) ? 1 : 0; 317 | norm[2] = (c >= a && c >= b) ? -1 : 0; 318 | 319 | return max(a, b, c); 320 | }; 321 | 322 | lerps[classify.MPM] = function lerpMPM(ray, aabb, norm) { 323 | var ro = ray.ro; 324 | var ub = aabb[1]; 325 | var lb = aabb[0]; 326 | 327 | var a = (ub[0] - ro[0]) * ray.ii; 328 | var b = (lb[1] - ro[1]) * ray.ij; 329 | var c = (ub[2] - ro[2]) * ray.ik; 330 | 331 | norm[0] = (a >= b && a >= c) ? 1 : 0; 332 | norm[1] = (b >= c && b >= a) ? -1 : 0; 333 | norm[2] = (c >= a && c >= b) ? 1 : 0; 334 | 335 | return max(a, b, c); 336 | }; 337 | 338 | lerps[classify.MPP] = function lerpMPP(ray, aabb, norm) { 339 | var ro = ray.ro; 340 | var ub = aabb[1]; 341 | var lb = aabb[0]; 342 | 343 | var a = (ub[0] - ro[0]) * ray.ii; 344 | var b = (lb[1] - ro[1]) * ray.ij; 345 | var c = (lb[2] - ro[2]) * ray.ik; 346 | 347 | norm[0] = (a >= b && a >= c) ? 1 : 0; 348 | norm[1] = (b >= c && b >= a) ? -1 : 0; 349 | norm[2] = (c >= a && c >= b) ? -1 : 0; 350 | 351 | return max(a, b, c); 352 | }; 353 | 354 | lerps[classify.PMM] = function lerpPMM(ray, aabb, norm) { 355 | var ro = ray.ro; 356 | var ub = aabb[1]; 357 | var lb = aabb[0]; 358 | 359 | var a = (lb[0] - ro[0]) * ray.ii; 360 | var b = (ub[1] - ro[1]) * ray.ij; 361 | var c = (ub[2] - ro[2]) * ray.ik; 362 | 363 | norm[0] = (a >= b && a >= c) ? -1 : 0; 364 | norm[1] = (b >= c && b >= a) ? 1 : 0; 365 | norm[2] = (c >= a && c >= b) ? 1 : 0; 366 | 367 | return max(a, b, c); 368 | }; 369 | 370 | lerps[classify.PMP] = function lerpPMP(ray, aabb, norm) { 371 | var ro = ray.ro; 372 | var ub = aabb[1]; 373 | var lb = aabb[0]; 374 | 375 | var a = (lb[0] - ro[0]) * ray.ii; 376 | var b = (ub[1] - ro[1]) * ray.ij; 377 | var c = (lb[2] - ro[2]) * ray.ik; 378 | 379 | norm[0] = (a >= b && a >= c) ? -1 : 0; 380 | norm[1] = (b >= c && b >= a) ? 1 : 0; 381 | norm[2] = (c >= a && c >= b) ? -1 : 0; 382 | 383 | return max(a, b, c); 384 | }; 385 | 386 | lerps[classify.PPM] = function lerpPPM(ray, aabb, norm) { 387 | var ro = ray.ro; 388 | var lb = aabb[0]; 389 | var ub = aabb[1]; 390 | 391 | var a = (lb[0] - ro[0]) * ray.ii; 392 | var b = (lb[1] - ro[1]) * ray.ij; 393 | var c = (ub[2] - ro[2]) * ray.ik; 394 | 395 | norm[0] = (a >= b && a >= c) ? -1 : 0; 396 | norm[1] = (b >= c && b >= a) ? -1 : 0; 397 | norm[2] = (c >= a && c >= b) ? 1 : 0; 398 | 399 | return max(a, b, c); 400 | }; 401 | 402 | lerps[classify.PPP] = function lerpPPP(ray, aabb, norm) { 403 | var ro = ray.ro; 404 | var lb = aabb[0]; 405 | 406 | var a = (lb[0] - ro[0]) * ray.ii; 407 | var b = (lb[1] - ro[1]) * ray.ij; 408 | var c = (lb[2] - ro[2]) * ray.ik; 409 | 410 | norm[0] = (a >= b && a >= c) ? -1 : 0; 411 | norm[1] = (b >= c && b >= a) ? -1 : 0; 412 | norm[2] = (c >= a && c >= b) ? -1 : 0; 413 | 414 | return max(a, b, c); 415 | }; 416 | 417 | lerps[classify.OMM] = function lerpOMM(ray, aabb, norm) { 418 | var ro = ray.ro; 419 | var ub = aabb[1]; 420 | 421 | var a = (ub[1] - ro[1]) * ray.ij; 422 | var b = (ub[2] - ro[2]) * ray.ik; 423 | 424 | norm[0] = 0 425 | norm[1] = (a >= b) ? 1 : 0; 426 | norm[2] = (b >= a) ? 1 : 0; 427 | 428 | return max(a, b); 429 | }; 430 | 431 | lerps[classify.OMP] = function lerpOMP(ray, aabb, norm) { 432 | var ro = ray.ro; 433 | 434 | var a = (aabb[1][1] - ro[1]) * ray.ij; 435 | var b = (aabb[0][2] - ro[2]) * ray.ik; 436 | 437 | norm[0] = 0 438 | norm[1] = (a >= b) ? 1 : 0; 439 | norm[2] = (b >= a) ? -1 : 0; 440 | 441 | return max(a, b); 442 | }; 443 | 444 | lerps[classify.OPM] = function lerpOPM(ray, aabb, norm) { 445 | var ro = ray.ro; 446 | 447 | var a = (aabb[0][1] - ro[1]) * ray.ij; 448 | var b = (aabb[1][2] - ro[2]) * ray.ik; 449 | 450 | norm[0] = 0 451 | norm[1] = (a >= b) ? -1 : 0; 452 | norm[2] = (b >= a) ? 1 : 0; 453 | 454 | return max(a, b); 455 | }; 456 | 457 | lerps[classify.OPP] = function lerpOPP(ray, aabb, norm) { 458 | var ro = ray.ro; 459 | var lb = aabb[0]; 460 | 461 | var a = (lb[1] - ro[1]) * ray.ij; 462 | var b = (lb[2] - ro[2]) * ray.ik; 463 | 464 | norm[0] = 0 465 | norm[1] = (a >= b) ? -1 : 0; 466 | norm[2] = (b >= a) ? -1 : 0; 467 | 468 | return max(a, b); 469 | } 470 | 471 | lerps[classify.MOM] = function lerpMOM(ray, aabb, norm) { 472 | var ro = ray.ro; 473 | var ub = aabb[1]; 474 | 475 | var a = (ub[0] - ro[0]) * ray.ii; 476 | var b = (ub[2] - ro[2]) * ray.ik; 477 | 478 | norm[0] = (a >= b) ? 1 : 0; 479 | norm[1] = 0 480 | norm[2] = (b >= a) ? 1 : 0; 481 | 482 | return max(a, b); 483 | }; 484 | 485 | lerps[classify.MOP] = function lerpMOP(ray, aabb, norm) { 486 | var ro = ray.ro; 487 | 488 | var a = (aabb[1][0] - ro[0]) * ray.ii; 489 | var b = (aabb[0][2] - ro[2]) * ray.ik; 490 | 491 | norm[0] = (a >= b) ? 1 : 0; 492 | norm[1] = 0 493 | norm[2] = (b >= a) ? -1 : 0; 494 | 495 | return max(a, b); 496 | }; 497 | 498 | lerps[classify.POM] = function lerpPOM(ray, aabb, norm) { 499 | var ro = ray.ro; 500 | 501 | var a = (aabb[0][0] - ray.ro[0]) * ray.ii; 502 | var b = (aabb[1][2] - ray.ro[2]) * ray.ik; 503 | 504 | norm[0] = (a >= b) ? -1 : 0; 505 | norm[1] = 0; 506 | norm[2] = (b >= a) ? 1 : 0; 507 | 508 | return max(a, b); 509 | }; 510 | 511 | lerps[classify.POP] = function lerpPOP(ray, aabb, norm) { 512 | var ro = ray.ro; 513 | var lb = aabb[0]; 514 | 515 | var a = (lb[0] - ro[0]) * ray.ii; 516 | var b = (lb[2] - ro[2]) * ray.ik; 517 | 518 | norm[0] = (a >= b) ? -1 : 0; 519 | norm[1] = 0 520 | norm[2] = (b >= a) ? -1 : 0; 521 | 522 | return max(a, b); 523 | } 524 | 525 | lerps[classify.MMO] = function lerpMMO(ray, aabb, norm) { 526 | var ro = ray.ro; 527 | var ub = aabb[1]; 528 | 529 | var a = (ub[0] - ro[0]) * ray.ii; 530 | var b = (ub[1] - ro[1]) * ray.ij; 531 | 532 | norm[0] = (a >= b) ? 1 : 0; 533 | norm[1] = (b >= a) ? 1 : 0; 534 | norm[2] = 0 535 | 536 | return max(a, b); 537 | } 538 | 539 | lerps[classify.MPO] = function lerpMPO(ray, aabb, norm) { 540 | var ro = ray.ro; 541 | 542 | var a = (aabb[1][0] - ro[0]) * ray.ii; 543 | var b = (aabb[0][1] - ro[1]) * ray.ij; 544 | 545 | norm[0] = (a >= b) ? 1 : 0; 546 | norm[1] = (b >= a) ? -1 : 0; 547 | norm[2] = 0 548 | 549 | return max(a, b); 550 | }; 551 | 552 | lerps[classify.PMO] = function lerpPMO(ray, aabb, norm) { 553 | var ro = ray.ro; 554 | 555 | var a = (aabb[0][0] - ro[0]) * ray.ii; 556 | var b = (aabb[1][1] - ro[1]) * ray.ij; 557 | 558 | norm[0] = (a >= b) ? -1 : 0; 559 | norm[1] = (b >= a) ? 1 : 0; 560 | norm[2] = 0 561 | 562 | return max(a, b); 563 | }; 564 | 565 | lerps[classify.PPO] = function lerpPPO(ray, aabb, norm) { 566 | var ro = ray.ro; 567 | var lb = aabb[0]; 568 | 569 | var a = (lb[0] - ro[0]) * ray.ii; 570 | var b = (lb[1] - ro[1]) * ray.ij; 571 | 572 | norm[0] = (a >= b) ? -1 : 0; 573 | norm[1] = (b >= a) ? -1 : 0; 574 | norm[2] = 0; 575 | 576 | return max(a, b); 577 | }; 578 | 579 | lerps[classify.MOO] = function lerpMOO(ray, aabb, norm) { 580 | norm[0] = 1; 581 | norm[1] = norm[2] = 0; 582 | return (aabb[1][0] - ray.ro[0]) * ray.ii; 583 | }; 584 | 585 | lerps[classify.POO] = function lerpPOO(ray, aabb, norm) { 586 | norm[0] = -1; 587 | norm[1] = norm[2] = 0; 588 | return (aabb[0][0] - ray.ro[0]) * ray.ii; 589 | }; 590 | 591 | lerps[classify.OMO] = function lerpOMO(ray, aabb, norm) { 592 | norm[0] = 0; 593 | norm[1] = 1; 594 | norm[2] = 0; 595 | return (aabb[1][1] - ray.ro[1]) * ray.ij; 596 | }; 597 | 598 | lerps[classify.OPO] = function lerpOPO(ray, aabb, norm) { 599 | norm[0] = 0; 600 | norm[1] = -1; 601 | norm[2] = 0; 602 | return (aabb[0][1] - ray.ro[1]) * ray.ij; 603 | }; 604 | 605 | lerps[classify.OOM] = function lerpOOM(ray, aabb, norm) { 606 | norm[0] = norm[1] = 0; 607 | norm[2] = 1; 608 | return (aabb[1][2] - ray.ro[2]) * ray.ik; 609 | }; 610 | 611 | lerps[classify.OOP] = function lerpOOP(ray, aabb, norm) { 612 | norm[0] = norm[1] = 0; 613 | norm[2] = -1; 614 | return (aabb[0][2] - ray.ro[2]) * ray.ik; 615 | } 616 | 617 | function Ray(ro, rd) { 618 | this.ro = [0, 0, 0]; 619 | this.rd = [0, 0, 0]; 620 | this.update(ro, rd); 621 | } 622 | 623 | Ray.prototype.ii = 0.0; 624 | Ray.prototype.ij = 0.0; 625 | Ray.prototype.ik = 0.0; 626 | Ray.prototype.ibyj = 0.0; 627 | Ray.prototype.jbyi = 0.0; 628 | Ray.prototype.jbyk = 0.0; 629 | Ray.prototype.kbyj = 0.0; 630 | Ray.prototype.ibyk = 0.0; 631 | Ray.prototype.kbyi = 0.0; 632 | Ray.prototype.cxy = 0.0; 633 | Ray.prototype.cxz = 0.0; 634 | Ray.prototype.cyx = 0.0; 635 | Ray.prototype.cyz = 0.0; 636 | Ray.prototype.czx = 0.0; 637 | Ray.prototype.czy = 0.0; 638 | Ray.prototype.classification = 0.0; 639 | Ray.prototype.result = null; 640 | 641 | var scratchNormal = [0, 0, 0]; 642 | 643 | Ray.prototype.intersects = function rayIntersectsAABB(aabb, computeDistance) { 644 | var classification = this.classification; 645 | var t = tests[classification]; 646 | if (t && t(this, aabb[0], aabb[1])) { 647 | if (!computeDistance) { 648 | return true; 649 | } 650 | 651 | var lerp = lerps[classification] 652 | var normal = Array.isArray(computeDistance) ? computeDistance : scratchNormal; 653 | return lerp && lerp(this, aabb, normal); 654 | } 655 | return false; 656 | }; 657 | 658 | Ray.prototype.update = function updateRay(ro, rd) { 659 | var r = this; 660 | var i = rd[0], j = rd[1], k = rd[2]; 661 | var x = ro[0], y = ro[1], z = ro[2]; 662 | 663 | var tro = r.ro; 664 | var trd = r.rd; 665 | 666 | if (i === trd[0] && j === trd[1] && k === trd[2] && 667 | x === tro[0] && y === tro[1] && z === tro[2]) 668 | { 669 | return r; 670 | } 671 | 672 | r.ro[0] = x; 673 | r.ro[1] = y; 674 | r.ro[2] = z; 675 | r.rd[0] = i; 676 | r.rd[1] = j; 677 | r.rd[2] = k; 678 | 679 | var ii = r.ii = (i === 0)?0:1.0/i; 680 | var ij = r.ij = (j === 0)?0:1.0/j; 681 | var ik = r.ik = (k === 0)?0:1.0/k; 682 | //ray slope 683 | var ibyj = r.ibyj = i * ij; 684 | var jbyi = r.jbyi = j * ii; 685 | var jbyk = r.jbyk = j * ik; 686 | var kbyj = r.kbyj = k * ij; 687 | var ibyk = r.ibyk = i * ik; 688 | var kbyi = r.kbyi = k * ii; 689 | r.cxy = y - jbyi * x; 690 | r.cxz = z - kbyi * x; 691 | r.cyx = x - ibyj * y; 692 | r.cyz = z - kbyj * y; 693 | r.czx = x - ibyk * z; 694 | r.czy = y - jbyk * z; 695 | 696 | r.classification = classify(i, j, k); 697 | return r; 698 | }; 699 | 700 | function createRay(rayOrigin, rayDirection) { 701 | return new Ray(rayOrigin, rayDirection) 702 | } 703 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var createRay = require('./ray-aabb'); 3 | var vec3 = require('gl-vec3'); 4 | var classify = require('ray-direction-classify'); 5 | 6 | var obox = [[-1, -1, -1], [1, 1, 1]]; 7 | 8 | function intersect(origin, direction, box) { 9 | var normal = [0, 0, 0] 10 | vec3.normalize(direction, direction) 11 | var ray = createRay(origin, direction); 12 | if (ray.intersects(box, normal)) { 13 | return normal 14 | } 15 | return false; 16 | } 17 | 18 | test('MMM', function(t) { 19 | var box = [ 20 | [2, 2, 2], 21 | [4, 4, 4] 22 | ]; 23 | 24 | var ray = createRay( 25 | [5, 5, 5], 26 | [-1, -1, -1] 27 | ) 28 | 29 | t.ok(ray.intersects(box)); 30 | t.end(); 31 | }) 32 | 33 | test('MMM - miss', function(t) { 34 | var box = [ 35 | [2, 2, 2], 36 | [4, 4, 4] 37 | ]; 38 | 39 | var ray = createRay( 40 | [0, 0, 0], 41 | [-1, -1, -1] 42 | ) 43 | 44 | t.ok(!ray.intersects(box)); 45 | t.end(); 46 | }) 47 | 48 | test('MMP', function(t) { 49 | var box = [ 50 | [2, 2, 2], 51 | [4, 4, 4] 52 | ]; 53 | 54 | var ray = createRay( 55 | [5, 5, 2], 56 | [-1, -1, 1] 57 | ) 58 | 59 | t.ok(ray.intersects(box)); 60 | t.end(); 61 | }) 62 | 63 | test('MMP - miss', function(t) { 64 | var box = [ 65 | [2, 2, 2], 66 | [4, 4, 4] 67 | ]; 68 | 69 | var ray = createRay( 70 | [5, 1, 2], 71 | [-1, -1, 1] 72 | ) 73 | 74 | t.ok(!ray.intersects(box)); 75 | t.end(); 76 | }) 77 | 78 | test('MPM', function(t) { 79 | var box = [ 80 | [2, 2, 2], 81 | [4, 4, 4] 82 | ]; 83 | 84 | var ray = createRay( 85 | [5, 2, 5], 86 | [-1, 1, -1] 87 | ) 88 | 89 | t.ok(ray.intersects(box)); 90 | t.end(); 91 | }) 92 | 93 | test('MPM - miss', function(t) { 94 | var box = [ 95 | [2, 2, 2], 96 | [4, 4, 4] 97 | ]; 98 | 99 | var ray = createRay( 100 | [5, 2, 1], 101 | [-1, 1, -1] 102 | ) 103 | 104 | t.ok(!ray.intersects(box)); 105 | t.end(); 106 | }) 107 | 108 | test('MPP', function(t) { 109 | var box = [ 110 | [2, 2, 2], 111 | [4, 4, 4] 112 | ]; 113 | 114 | var ray = createRay( 115 | [5, 2, 2], 116 | [-1, 1, 1] 117 | ) 118 | 119 | t.ok(ray.intersects(box)); 120 | t.end(); 121 | }) 122 | 123 | test('MPP', function(t) { 124 | var box = [ 125 | [2, 2, 2], 126 | [4, 4, 4] 127 | ]; 128 | 129 | var ray = createRay( 130 | [5, 2, 2], 131 | [-1, 1, 1] 132 | ) 133 | 134 | t.ok(ray.intersects(box)); 135 | t.end(); 136 | }) 137 | 138 | test('PMM', function(t) { 139 | var origin = [0, 6, 6]; 140 | var box = [ 141 | [2, 2, 2], 142 | [4, 4, 4] 143 | ]; 144 | var direction = [1, -1, -1]; 145 | 146 | t.ok(createRay(origin, direction).intersects(box)); 147 | 148 | t.end(); 149 | }); 150 | 151 | test('PMM - miss', function(t) { 152 | var origin = [0, 0, 0]; 153 | var box = [ 154 | [2, 2, 2], 155 | [4, 4, 4] 156 | ]; 157 | var direction = [1, -1, -1]; 158 | 159 | t.ok(!createRay(origin, direction).intersects(box)); 160 | 161 | t.end(); 162 | }); 163 | 164 | test('PMP', function(t) { 165 | 166 | var box = [ 167 | [2, 2, 2], 168 | [4, 4, 4] 169 | ]; 170 | 171 | var ray = createRay( 172 | [1, 5, 1], 173 | [1, -1, 1] 174 | ) 175 | 176 | t.ok(ray.intersects(box)); 177 | 178 | t.end(); 179 | }) 180 | 181 | test('PMP - miss', function(t) { 182 | 183 | var box = [ 184 | [2, 2, 2], 185 | [4, 4, 4] 186 | ]; 187 | 188 | var ray = createRay( 189 | [1, 5, 5], 190 | [1, -1, 1] 191 | ) 192 | 193 | t.ok(!ray.intersects(box)); 194 | 195 | t.end(); 196 | }) 197 | 198 | test('PPM', function(t) { 199 | var box = [ 200 | [2, 2, 2], 201 | [4, 4, 4] 202 | ]; 203 | 204 | var ray = createRay( 205 | [2, 2, 5], 206 | [1, 1, -1] 207 | ) 208 | 209 | t.ok(ray.intersects(box)); 210 | t.end(); 211 | }) 212 | 213 | test('PPM - miss', function(t) { 214 | var box = [ 215 | [2, 2, 2], 216 | [4, 4, 4] 217 | ]; 218 | 219 | var ray = createRay( 220 | [2, 2, 0], 221 | [1, 1, -1] 222 | ) 223 | 224 | t.ok(!ray.intersects(box)); 225 | t.end(); 226 | }) 227 | 228 | test('PPP', function(t) { 229 | var origin = [0, 0, 0]; 230 | var box = [ 231 | [2, 2, 2], 232 | [4, 4, 4] 233 | ]; 234 | var direction = [1, 1, 1]; 235 | 236 | t.ok(createRay(origin, direction).intersects(box)); 237 | 238 | t.end(); 239 | }) 240 | 241 | test('PPP - miss', function(t) { 242 | var origin = [5, 0, 0]; 243 | var box = [ 244 | [2, 2, 2], 245 | [4, 4, 4] 246 | ]; 247 | var direction = [1, 1, 1]; 248 | 249 | t.ok(!createRay(origin, direction).intersects(box)); 250 | 251 | t.end(); 252 | }) 253 | 254 | test('POO', function(t) { 255 | 256 | var box = [ 257 | [2, 2, 2], 258 | [4, 4, 4] 259 | ]; 260 | 261 | var ray = createRay( 262 | [0, 3, 3], 263 | [1, 0, 0] 264 | ) 265 | 266 | t.ok(ray.intersects(box)); 267 | 268 | t.end(); 269 | }) 270 | 271 | test('POO - miss', function(t) { 272 | 273 | var box = [ 274 | [2, 2, 2], 275 | [4, 4, 4] 276 | ]; 277 | 278 | var ray = createRay( 279 | [1, 1, 1], 280 | [1, 0, 0] 281 | ) 282 | 283 | t.ok(!ray.intersects(box)); 284 | 285 | t.end(); 286 | }) 287 | 288 | test('MOO', function(t) { 289 | var box = [ 290 | [2, 2, 2], 291 | [4, 4, 4] 292 | ]; 293 | 294 | var ray = createRay( 295 | [5, 3, 3], 296 | [-1, 0, 0] 297 | ) 298 | 299 | t.ok(ray.intersects(box)); 300 | t.end(); 301 | }) 302 | 303 | test('MOO - miss', function(t) { 304 | var box = [ 305 | [2, 2, 2], 306 | [4, 4, 4] 307 | ]; 308 | 309 | var ray = createRay( 310 | [2, 3, 3], 311 | [-1, 0, 0] 312 | ) 313 | 314 | t.ok(ray.intersects(box)); 315 | t.end(); 316 | }) 317 | 318 | test('OPO', function(t) { 319 | var box = [ 320 | [2, 2, 2], 321 | [4, 4, 4] 322 | ]; 323 | 324 | var ray = createRay( 325 | [3, 0, 3], 326 | [0, 1, 0] 327 | ) 328 | 329 | t.ok(ray.intersects(box)); 330 | t.end(); 331 | }); 332 | 333 | test('OPO - miss', function(t) { 334 | var box = [ 335 | [2, 2, 2], 336 | [4, 4, 4] 337 | ]; 338 | 339 | var ray = createRay( 340 | [1, 0, 3], 341 | [0, 1, 0] 342 | ) 343 | 344 | t.ok(!ray.intersects(box)); 345 | t.end(); 346 | }); 347 | 348 | test('OMO', function(t) { 349 | var box = [ 350 | [2, 2, 2], 351 | [4, 4, 4] 352 | ]; 353 | 354 | var ray = createRay( 355 | [3, 5, 3], 356 | [0, -1, 0] 357 | ) 358 | 359 | t.ok(ray.intersects(box)); 360 | 361 | t.end(); 362 | }); 363 | 364 | test('OMO - miss', function(t) { 365 | var box = [ 366 | [2, 2, 2], 367 | [4, 4, 4] 368 | ]; 369 | 370 | var ray = createRay( 371 | [3, 1, 3], 372 | [0, -1, 0] 373 | ) 374 | 375 | t.ok(!ray.intersects(box)); 376 | 377 | t.end(); 378 | }); 379 | 380 | test('OOP', function(t) { 381 | var box = [ 382 | [2, 2, 2], 383 | [4, 4, 4] 384 | ]; 385 | 386 | var ray = createRay( 387 | [3, 3, 1], 388 | [0, 0, 1] 389 | ) 390 | 391 | t.ok(ray.intersects(box)); 392 | t.end(); 393 | }); 394 | 395 | test('OOP', function(t) { 396 | var box = [ 397 | [2, 2, 2], 398 | [4, 4, 4] 399 | ]; 400 | 401 | var ray = createRay( 402 | [3, 1, 1], 403 | [0, 0, 1] 404 | ) 405 | 406 | t.ok(!ray.intersects(box)); 407 | t.end(); 408 | }); 409 | 410 | test('OOM', function(t) { 411 | var box = [ 412 | [2, 2, 2], 413 | [4, 4, 4] 414 | ]; 415 | 416 | var ray = createRay( 417 | [3, 3, 5], 418 | [0, 0, -1] 419 | ) 420 | 421 | t.ok(ray.intersects(box)); 422 | 423 | t.end(); 424 | }); 425 | 426 | test('OOM - miss', function(t) { 427 | var box = [ 428 | [2, 2, 2], 429 | [4, 4, 4] 430 | ]; 431 | 432 | var ray = createRay( 433 | [3, 3, 1], 434 | [0, 0, -1] 435 | ) 436 | 437 | t.ok(!ray.intersects(box)); 438 | 439 | t.end(); 440 | }); 441 | 442 | test('OMM', function(t) { 443 | var box = [ 444 | [2, 2, 2], 445 | [4, 4, 4] 446 | ]; 447 | 448 | var ray = createRay( 449 | [2, 5, 5], 450 | [0, -1, -1] 451 | ) 452 | 453 | t.ok(ray.intersects(box)); 454 | 455 | t.end(); 456 | }); 457 | 458 | test('OMM - miss', function(t) { 459 | var box = [ 460 | [2, 2, 2], 461 | [4, 4, 4] 462 | ]; 463 | 464 | var ray = createRay( 465 | [0, 5, 5], 466 | [0, -1, -1] 467 | ) 468 | 469 | t.ok(!ray.intersects(box)); 470 | 471 | t.end(); 472 | }); 473 | 474 | test('OMP', function(t) { 475 | var box = [ 476 | [2, 2, 2], 477 | [4, 4, 4] 478 | ]; 479 | 480 | var ray = createRay( 481 | [2, 5, 0], 482 | [0, -1, 1] 483 | ) 484 | 485 | t.ok(ray.intersects(box)); 486 | t.end(); 487 | }); 488 | 489 | test('OMP - miss', function(t) { 490 | var box = [ 491 | [2, 2, 2], 492 | [4, 4, 4] 493 | ]; 494 | 495 | var ray = createRay( 496 | [2, 0, 0], 497 | [0, -1, 1] 498 | ) 499 | 500 | t.ok(!ray.intersects(box)); 501 | t.end(); 502 | }); 503 | 504 | test('OPM', function(t) { 505 | var box = [ 506 | [2, 2, 2], 507 | [4, 4, 4] 508 | ]; 509 | 510 | var ray = createRay( 511 | [2, 0, 5], 512 | [0, 1, -1] 513 | ) 514 | 515 | t.ok(ray.intersects(box)); 516 | t.end(); 517 | }); 518 | 519 | test('OPM - miss', function(t) { 520 | var box = [ 521 | [2, 2, 2], 522 | [4, 4, 4] 523 | ]; 524 | 525 | var ray = createRay( 526 | [2, 0, 0], 527 | [0, 1, -1] 528 | ) 529 | 530 | t.ok(!ray.intersects(box)); 531 | t.end(); 532 | }); 533 | 534 | test('OPP', function(t) { 535 | var box = [ 536 | [2, 2, 2], 537 | [4, 4, 4] 538 | ]; 539 | 540 | var ray = createRay( 541 | [2, 0, 0], 542 | [0, 1, 1] 543 | ) 544 | 545 | t.ok(ray.intersects(box)); 546 | t.end(); 547 | }); 548 | 549 | test('OPP - miss', function(t) { 550 | var box = [ 551 | [2, 2, 2], 552 | [4, 4, 4] 553 | ]; 554 | 555 | var ray = createRay( 556 | [2, 5, 5], 557 | [0, 1, 1] 558 | ) 559 | 560 | t.ok(!ray.intersects(box)); 561 | t.end(); 562 | }); 563 | 564 | test('MOM', function(t) { 565 | var box = [ 566 | [2, 2, 2], 567 | [4, 4, 4] 568 | ]; 569 | 570 | var ray = createRay( 571 | [5, 2, 5], 572 | [-1, 0, -1] 573 | ) 574 | 575 | t.ok(ray.intersects(box)); 576 | t.end(); 577 | }); 578 | 579 | test('MOM - miss', function(t) { 580 | var box = [ 581 | [2, 2, 2], 582 | [4, 4, 4] 583 | ]; 584 | 585 | var ray = createRay( 586 | [5, 1, 5], 587 | [-1, 0, -1] 588 | ) 589 | 590 | t.ok(!ray.intersects(box)); 591 | t.end(); 592 | }); 593 | 594 | test('MOP', function(t) { 595 | var box = [ 596 | [2, 2, 2], 597 | [4, 4, 4] 598 | ]; 599 | 600 | var ray = createRay( 601 | [5, 2, 0], 602 | [-1, 0, 1] 603 | ) 604 | 605 | t.ok(ray.intersects(box)); 606 | t.end(); 607 | }); 608 | 609 | test('MOP - miss', function(t) { 610 | var box = [ 611 | [2, 2, 2], 612 | [4, 4, 4] 613 | ]; 614 | 615 | var ray = createRay( 616 | [0, 2, 0], 617 | [-1, 0, 1] 618 | ) 619 | 620 | t.ok(!ray.intersects(box)); 621 | t.end(); 622 | }); 623 | 624 | test('POM', function(t) { 625 | var box = [ 626 | [2, 2, 2], 627 | [4, 4, 4] 628 | ]; 629 | 630 | var ray = createRay( 631 | [0, 2, 5], 632 | [1, 0, -1] 633 | ) 634 | 635 | t.ok(ray.intersects(box)); 636 | t.end(); 637 | }); 638 | 639 | test('POM - miss', function(t) { 640 | var box = [ 641 | [2, 2, 2], 642 | [4, 4, 4] 643 | ]; 644 | 645 | var ray = createRay( 646 | [0, 1, 5], 647 | [1, 0, -1] 648 | ) 649 | 650 | t.ok(!ray.intersects(box)); 651 | t.end(); 652 | }); 653 | 654 | test('POP', function(t) { 655 | var box = [ 656 | [2, 2, 2], 657 | [4, 4, 4] 658 | ]; 659 | 660 | var ray = createRay( 661 | [0, 2, 0], 662 | [1, 0, 1] 663 | ) 664 | 665 | t.ok(ray.intersects(box)); 666 | t.end(); 667 | }); 668 | 669 | test('POP - miss', function(t) { 670 | var box = [ 671 | [2, 2, 2], 672 | [4, 4, 4] 673 | ]; 674 | 675 | var ray = createRay( 676 | [0, 5, 0], 677 | [1, 0, 1] 678 | ) 679 | 680 | t.ok(!ray.intersects(box)); 681 | t.end(); 682 | }); 683 | 684 | test('MMO', function(t) { 685 | var box = [ 686 | [2, 2, 2], 687 | [4, 4, 4] 688 | ]; 689 | 690 | var ray = createRay( 691 | [5, 5, 2], 692 | [-1, -1, 0] 693 | ) 694 | 695 | t.ok(ray.intersects(box)); 696 | t.end(); 697 | }); 698 | 699 | test('MMO - miss', function(t) { 700 | var box = [ 701 | [2, 2, 2], 702 | [4, 4, 4] 703 | ]; 704 | 705 | var ray = createRay( 706 | [5, 5, 0], 707 | [-1, -1, 0] 708 | ) 709 | 710 | t.ok(!ray.intersects(box)); 711 | t.end(); 712 | }); 713 | 714 | test('MPO', function(t) { 715 | var box = [ 716 | [2, 2, 2], 717 | [4, 4, 4] 718 | ]; 719 | 720 | var ray = createRay( 721 | [5, 1, 2], 722 | [-1, 1, 0] 723 | ) 724 | 725 | t.ok(ray.intersects(box)); 726 | t.end(); 727 | }); 728 | 729 | test('MPO - miss', function(t) { 730 | var box = [ 731 | [2, 2, 2], 732 | [4, 4, 4] 733 | ]; 734 | 735 | var ray = createRay( 736 | [5, 1, 0], 737 | [-1, 1, 0] 738 | ) 739 | 740 | t.ok(!ray.intersects(box)); 741 | t.end(); 742 | }); 743 | 744 | test('PMO', function(t) { 745 | var box = [ 746 | [2, 2, 2], 747 | [4, 4, 4] 748 | ]; 749 | 750 | var ray = createRay( 751 | [1, 5, 2], 752 | [1, -1, 0] 753 | ) 754 | 755 | t.ok(ray.intersects(box)); 756 | t.end(); 757 | }); 758 | 759 | test('PMO - miss', function(t) { 760 | var box = [ 761 | [2, 2, 2], 762 | [4, 4, 4] 763 | ]; 764 | 765 | var ray = createRay( 766 | [5, 5, 2], 767 | [1, -1, 0] 768 | ) 769 | 770 | t.ok(!ray.intersects(box)); 771 | t.end(); 772 | }); 773 | 774 | test('PPO', function(t) { 775 | var box = [ 776 | [2, 2, 2], 777 | [4, 4, 4] 778 | ]; 779 | 780 | var ray = createRay( 781 | [1, 1, 2], 782 | [1, 1, 0] 783 | ) 784 | 785 | t.ok(ray.intersects(box)); 786 | t.end(); 787 | }); 788 | 789 | test('PPO - miss', function(t) { 790 | var box = [ 791 | [2, 2, 2], 792 | [4, 4, 4] 793 | ]; 794 | 795 | var ray = createRay( 796 | [5, 1, 2], 797 | [1, 1, 0] 798 | ) 799 | 800 | t.ok(!ray.intersects(box)); 801 | t.end(); 802 | }); 803 | 804 | test('simple case - intersects (0, 0, 1)', function(t) { 805 | var origin = [0, 1, 0]; 806 | var box = [ 807 | [0, 0, 2], 808 | [2, 2, 4] 809 | ]; 810 | var direction = [0, 0, 1]; 811 | 812 | t.ok(createRay(origin, direction).intersects(box)); 813 | 814 | t.end(); 815 | }); 816 | 817 | 818 | test('simple case - no isect (0, 0, -1)', function(t) { 819 | var origin = [0, 1, 0]; 820 | var box = [ 821 | [0, 0, 2], 822 | [2, 2, 4] 823 | ]; 824 | var direction = [0, 0, -1]; 825 | 826 | t.ok(!createRay(origin, direction).intersects(box)); 827 | 828 | t.end(); 829 | }); 830 | 831 | test('simple case - isect (0, 0, -1)', function(t) { 832 | var origin = [0, 1, 5]; 833 | var box = [ 834 | [0, 0, 2], 835 | [2, 2, 4] 836 | ]; 837 | var direction = [0, 0, -1]; 838 | 839 | t.ok(createRay(origin, direction).intersects(box)); 840 | 841 | t.end(); 842 | }); 843 | 844 | test('simple case - isect (-1, 0, 0)', function(t) { 845 | var origin = [3, 1, 3]; 846 | var box = [ 847 | [0, 0, 2], 848 | [2, 2, 4] 849 | ]; 850 | var direction = [-1, 0, 0]; 851 | 852 | t.ok(createRay(origin, direction).intersects(box)); 853 | 854 | t.end(); 855 | }); 856 | 857 | test('simple case - no isect (1, 0, 0)', function(t) { 858 | var origin = [3, 1, 3]; 859 | var box = [ 860 | [0, 0, 2], 861 | [2, 2, 4] 862 | ]; 863 | var direction = [1, 0, 0]; 864 | 865 | t.ok(!createRay(origin, direction).intersects(box)); 866 | 867 | t.end(); 868 | }); 869 | 870 | test('simple case - isect (1, 0, 0)', function(t) { 871 | var origin = [-3, 1, 3]; 872 | var box = [ 873 | [0, 0, 2], 874 | [2, 2, 4] 875 | ]; 876 | var direction = [1, 0, 0]; 877 | 878 | t.ok(createRay(origin, direction).intersects(box)); 879 | 880 | t.end(); 881 | }); 882 | 883 | test('rotated on the z - isect', function(t) { 884 | var r = 10; 885 | var l = 100; 886 | 887 | var box = [ 888 | [-1, -1, -1], 889 | [ 1, 1, 1] 890 | ]; 891 | 892 | for (var i=0; i 1) { 997 | if (x && y) { 998 | var xyedge = intersect([-x*2, -y*2, -z*2], [x, y, z*1.2], obox); 999 | var xyedge2 = intersect([-x*2, -y*2, -z], [x, y, z], obox); 1000 | t.deepEqual(xyedge, [-sx, -sy, 0], 'x/y edge direction change'); 1001 | t.deepEqual(xyedge, xyedge2, 'x/y edge origin change'); 1002 | } 1003 | 1004 | if (y && z) { 1005 | var yzedge = intersect([-x*2, -y*2, -z*2], [x*1.2, y, z], obox); 1006 | var yzedge2 = intersect([-x, -y*2, -z*2], [x, y, z], obox); 1007 | t.deepEqual(yzedge, [0, -sy, -sz], 'y/z edge direction change'); 1008 | t.deepEqual(yzedge, yzedge2, 'y/z edge origin change'); 1009 | } 1010 | 1011 | if (x && z) { 1012 | var xzedge = intersect([-x*2, -y*2, -z*2], [x, y*1.2, z], obox); 1013 | var xzedge2 = intersect([-x*2, -y, -z*2], [x, y, z], obox); 1014 | t.deepEqual(xzedge, [-sx, 0, -sz], 'x/z edge direction change'); 1015 | t.deepEqual(xzedge, xzedge2, 'x/z edge origin change'); 1016 | } 1017 | } 1018 | 1019 | // test corner hits 1020 | if (c > 2) { 1021 | var corner = intersect([-x*2, -y*2, -z*2], [x, y, z], obox); 1022 | t.deepEqual(corner, [-sx, -sy, -sz], 'corner'); 1023 | } 1024 | 1025 | t.end() 1026 | }) 1027 | } 1028 | 1029 | for (var x=-1; x<=1; x++) { 1030 | for (var y=-1; y<=1; y++) { 1031 | for (var z=-1; z<=1; z++) { 1032 | buildNormalTest(x, y, z) 1033 | } 1034 | } 1035 | } 1036 | --------------------------------------------------------------------------------