├── .npmignore ├── .travis.yml ├── DOC-API.md ├── Gruntfile.js ├── LICENSE ├── README.md ├── browser.js ├── dist ├── aframe-border-component.js └── aframe-border-component.min.js ├── examples ├── cdn-example │ ├── img │ │ ├── arrow-left-blue.png │ │ ├── arrow-left-red.png │ │ └── arrow-left.png │ └── index.html ├── client-example │ ├── img │ │ ├── arrow-left-blue.png │ │ ├── arrow-left-red.png │ │ └── arrow-left.png │ └── index.html ├── gif │ └── border-demo.png └── simple │ ├── img │ ├── arrow-left-blue.png │ ├── arrow-left-red.png │ └── arrow-left.png │ └── index.html ├── modules └── index.js ├── package.json ├── test └── module │ └── module-smoke-test.js └── upcoming-info.json /.npmignore: -------------------------------------------------------------------------------- 1 | Gruntfile.js 2 | test 3 | coverage 4 | example 5 | examples 6 | support 7 | .gitignore 8 | .travis.yml -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "stable" 4 | script: 5 | - npm run test-coverage 6 | after_success: 7 | - bash <(curl -s https://codecov.io/bash) -------------------------------------------------------------------------------- /DOC-API.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## aframe-border-component 4 | Module 5 | 6 | 7 | * [aframe-border-component](#module_aframe-border-component) 8 | * [.schema](#module_aframe-border-component.schema) 9 | * [.Component()](#module_aframe-border-component.Component) 10 | * [.init()](#module_aframe-border-component.init) 11 | 12 | 13 | 14 | ### aframe-border-component.schema 15 | defines the attribute properties 16 | 17 | **Kind**: static property of [aframe-border-component](#module_aframe-border-component) 18 | **Object**: 19 | 20 | 21 | ### aframe-border-component.Component() 22 | Specification for registering a border component with Aframe 23 | 24 | **Kind**: static method of [aframe-border-component](#module_aframe-border-component) 25 | **Example** *(browserify example)* 26 | ```js 27 | var border = require('aframe-border-component'); 28 | if (AFRAME.aframeCore) { 29 | AFRAME.aframeCore.registerComponent("border", border.Component); 30 | } else { 31 | AFRAME.registerComponent("border", border.Component); 32 | } 33 | ``` 34 | 35 | 36 | ### aframe-border-component.init() 37 | Called once when component is attached. Generally for initial setup. 38 | 39 | **Kind**: static method of [aframe-border-component](#module_aframe-border-component) 40 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function (grunt) { 2 | 3 | grunt.loadNpmTasks('grunt-contrib-jshint'); 4 | grunt.loadNpmTasks('grunt-bump'); 5 | grunt.loadNpmTasks('grunt-shell'); 6 | grunt.loadNpmTasks("grunt-browserify"); 7 | grunt.loadNpmTasks('grunt-contrib-uglify'); 8 | grunt.loadNpmTasks("grunt-contrib-watch"); 9 | grunt.loadNpmTasks('grunt-jsdoc-to-markdown'); 10 | 11 | grunt.initConfig({ 12 | 13 | // used by the changelog task 14 | pkg: grunt.file.readJSON('package.json'), 15 | 16 | jshint: { 17 | options: { 18 | node: true 19 | }, 20 | all: ['*.js','./modules/*.js'] 21 | }, 22 | 23 | shell: { 24 | publish: { 25 | command: 'npm publish' 26 | }, 27 | pubinit: { 28 | // command: 'npm publish --access public' 29 | command: [ 30 | 'npm publish --access public', 31 | 'git tag v0.1.0', 32 | 'git push origin --tags', 33 | ].join('&&') 34 | }, 35 | }, 36 | 37 | // To test: grunt bump --dry-run 38 | 39 | bump: { 40 | options: { 41 | 42 | commit: true, 43 | createTag: true, 44 | push: true, 45 | pushTo: 'origin', 46 | 47 | updateConfigs: ['pkg'], 48 | commitFiles: ['package.json'] 49 | } 50 | }, 51 | 52 | 53 | browserify: { 54 | dist: { 55 | options: { 56 | browserifyOptions: { 57 | // dashes will be converted to caps in actual name 58 | // i.e.: -test-zeta becomes TestZeta 59 | // standalone: 'MitchAllen.-aframe-border-component' 60 | // standalone: 'mitchallen.-aframe-border-component' 61 | }, 62 | transform: [['babelify', {presets: ['es2015']}]], 63 | plugin: [[ "browserify-derequire" ]] 64 | }, 65 | files: { 66 | // if the source file has an extension of es6 then 67 | // we change the name of the source file accordingly. 68 | // The result file's extension is always .js 69 | "./dist/aframe-border-component.js": ["./browser.js"] 70 | // For non-standalone, use ./browser.js instead. 71 | // "./dist/aframe-border-component.js": ["./browser.js"] 72 | } 73 | } 74 | }, 75 | 76 | uglify: { 77 | my_target: { 78 | files: { 79 | './dist/aframe-border-component.min.js': ['./dist/aframe-border-component.js'] 80 | } 81 | } 82 | }, 83 | 84 | watch: { 85 | scripts: { 86 | files: ["./modules/*.js","./*.js"], 87 | tasks: ['jshint','browserify','uglify'] 88 | } 89 | }, 90 | 91 | jsdoc2md: { 92 | oneOutputFile: { 93 | src: 'modules/*.js', 94 | dest: 'DOC-API.md' 95 | } 96 | }, 97 | 98 | upcoming: { 99 | default: { 100 | files: [ 101 | { src: 'package.json', dest: ['upcoming-info.json'] } 102 | ] 103 | } 104 | } 105 | 106 | }); 107 | 108 | grunt.loadNpmTasks('grunt-upcoming'); 109 | 110 | grunt.registerTask('default', ['upcoming:patch','build']); 111 | grunt.registerTask("build", ['jshint','browserify','uglify']); 112 | grunt.registerTask('pubinit', ['build','shell:pubinit']); 113 | grunt.registerTask('publish', ['upcoming:patch','build','bump','shell:publish']); 114 | }; 115 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright 2017 Mitch Allen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | aframe-border-component 2 | == 3 | A component for creating borders in VR 4 | -- 5 | 6 |

7 | 8 |

9 | 10 |

11 | 12 | CI 13 | 14 | 15 | Coverage 16 | 17 | 18 | Downloads 19 | 20 | 21 | Version 22 | 23 | 24 | License 25 | 26 |

27 | 28 | ## Browser Usage 29 | 30 | ### Live Example 31 | 32 | Here are some live examples of the component. The simpler demo is ideal for mobile. 33 | 34 | * https://mitchallen.bitbucket.io/border/index.html - best viewed in Chrome 35 | * https://mitchallen.bitbucket.io/border/simple.html - simpler demo for mobile 36 | 37 | Demo notes: 38 | 39 | * Works fine in Chrome on a Mac 40 | * Having issues with player falling through floor on iOS for complex demos (keep it simple on mobile) 41 | * On Windows 10 machine (Lenovo Yoga 710 laptop) can't seem to walk and turn at the same time 42 | 43 | * * * 44 | 45 | ### HTML Example 46 | 47 | Run this example in a browser (you'll need to add your own image files or get them from my repo). Step off the birds-eye view platform and wander around. 48 | 49 | 50 | 51 | 52 | 53 | simple aframe-border-component example 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 70 | 73 | 77 | 80 | 83 | 86 | 87 | 88 | 92 | 97 | 98 | 99 | 103 | 104 | 105 | 109 | 110 | 111 | 115 | 119 | 120 | 121 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | ### Include Script 134 | 135 | Include the latest script. Update the @version in the URL as needed: 136 | 137 | 138 | 139 | ### Define Assets 140 | 141 | Create an __a-assets__ section and add the following: 142 | 143 | * an __img__ to act as a material for the border 144 | * an entity, like __a-box__, __a-cylinder__ or __a-sphere__, to act as a border 145 | 146 | Be sure to give each entity unique __id__ attributes. They will be needed to define the border. 147 | 148 | The example below uses the __static-body__ component from __[aframe-extras](https://github.com/donmccurdy/aframe-extras)__ so the player can not go through the borders. 149 | 150 | 151 | 152 | 155 | 156 | 157 | You can also set the __rotation__ for an asset. It will rotate relative to it's position and rotation along the border. 158 | 159 | ### Create an Entity with a Border Component 160 | 161 | A __border__ component can consist of the following: 162 | 163 | * __sides__ - the number of sides to create for the border (__sides: 6;__) 164 | * __radius__ - the distance from the parent entity to create each border wall (__radius: 10;__) 165 | * __wall__ - the __id__ of the entity asset that will be used to create walls of the border (__wall: #wall-one;__) 166 | * __open__ - a zero-based list of border walls that can be opened (not drawn) to allow entering and exiting (__open: 0 2;__) 167 | 168 | Example border with no open walls: 169 | 170 | 173 | 174 | A border with open walls: 175 | 176 | 179 | 180 | ### Adding / Removing Through JavaScript 181 | 182 | An example of how to remove and add border attributes though JavaScript. The target element should be an __a-entity__. 183 | 184 | 190 | 191 | * * * 192 | 193 | ## NPM Installation 194 | 195 | $ npm init 196 | $ npm install aframe-border-component --save 197 | 198 | ### Browserify Example 199 | 200 | Below is an example of requiring the module within a file to be passed to browserify. 201 | 202 | The modules named export, __Component__, should be passed to __AFRAME.aframeCore.registerComponent__: 203 | 204 | // Browser distribution of the A-Frame component. 205 | (function () { 206 | if (typeof AFRAME === 'undefined') { 207 | console.error('Component attempted to register before AFRAME was available.'); 208 | return; 209 | } 210 | 211 | var border = require('aframe-border-component'); 212 | 213 | // Register all components here. 214 | var components = { 215 | "border": border.Component 216 | }; 217 | 218 | var primitives = { 219 | }; 220 | 221 | Object.keys(components).forEach(function (name) { 222 | if (AFRAME.aframeCore) { 223 | AFRAME.aframeCore.registerComponent(name, components[name]); 224 | } else { 225 | AFRAME.registerComponent(name, components[name]); 226 | } 227 | }); 228 | 229 | Object.keys(primitives).forEach(function (name) { 230 | if (AFRAME.aframeCore) { 231 | AFRAME.aframeCore.registerPrimitive(name, primitives[name]); 232 | } else { 233 | AFRAME.registerPrimitive(name, primitives[name]); 234 | } 235 | }); 236 | 237 | })(); 238 | 239 | ### Build with grunt 240 | 241 | Use a [grunt](http://gruntjs.com/) task to build the distribution file: 242 | 243 | browserify: { 244 | dist: { 245 | options: { 246 | browserifyOptions: { 247 | // ... 248 | }, 249 | transform: [['babelify', {presets: ['es2015']}]], 250 | plugin: [[ "browserify-derequire" ]] 251 | }, 252 | files: { 253 | // substitute your component name for the distribution file 254 | "./dist/YOUR-COMPONENT.js": ["./browser.js"] 255 | } 256 | } 257 | }, 258 | 259 | For more information, review the __Gruntfile.js__ and __package.json__ files in the root of this projects source code. 260 | 261 | * * * 262 | 263 | ## Testing 264 | 265 | To test, go to the root folder and type (sans __$__): 266 | 267 | $ npm test 268 | 269 | * * * 270 | 271 | ## Repo(s) 272 | 273 | * [bitbucket.org/mitchallen/aframe-border-component.git](https://bitbucket.org/mitchallen/aframe-border-component.git) 274 | * [github.com/mitchallen/aframe-border-component.git](https://github.com/mitchallen/aframe-border-component.git) 275 | 276 | * * * 277 | 278 | ## Contributing 279 | 280 | In lieu of a formal style guide, take care to maintain the existing coding style. 281 | Add unit tests for any new or changed functionality. Lint and test your code. 282 | 283 | * * * 284 | 285 | ## Version History 286 | 287 | #### Version 0.1.6 288 | 289 | * updated doc to point to demos in their new location on bitbucket 290 | 291 | #### Version 0.1.5 292 | 293 | * brought code coverage up to 100% 294 | 295 | #### Version 0.1.4 296 | 297 | * added logging of name and version to console 298 | * integrated travis-ci and codecov 299 | 300 | #### Version 0.1.3 301 | 302 | * code cleanup 303 | * added MIT license 304 | * updated examples to latest CDN 305 | 306 | #### Version 0.1.2 307 | 308 | * fixed / updated documentation 309 | 310 | #### Version 0.1.1 311 | 312 | * added cdn example 313 | 314 | #### Version 0.1.0 315 | 316 | * initial release 317 | 318 | * * * 319 | -------------------------------------------------------------------------------- /browser.js: -------------------------------------------------------------------------------- 1 | // Browser distribution of the A-Frame component. 2 | (function () { 3 | if (typeof AFRAME === 'undefined') { 4 | console.error('Component attempted to register before AFRAME was available.'); 5 | return; 6 | } 7 | 8 | var border = require('./modules/index'); 9 | 10 | console.log( border.Component.name() + ": " + border.Component.version() ); 11 | 12 | // Register all components here. 13 | var components = { 14 | "border": border.Component 15 | }; 16 | 17 | var primitives = { 18 | }; 19 | 20 | Object.keys(components).forEach(function (name) { 21 | if (AFRAME.aframeCore) { 22 | AFRAME.aframeCore.registerComponent(name, components[name]); 23 | } else { 24 | AFRAME.registerComponent(name, components[name]); 25 | } 26 | }); 27 | 28 | Object.keys(primitives).forEach(function (name) { 29 | if (AFRAME.aframeCore) { 30 | AFRAME.aframeCore.registerPrimitive(name, primitives[name]); 31 | } else { 32 | AFRAME.registerPrimitive(name, primitives[name]); 33 | } 34 | }); 35 | 36 | })(); -------------------------------------------------------------------------------- /dist/aframe-border-component.js: -------------------------------------------------------------------------------- 1 | (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;obrowserify example 62 | * var border = require('aframe-border-component'); 63 | * if (AFRAME.aframeCore) { 64 | * AFRAME.aframeCore.registerComponent("border", border.Component); 65 | * } else { 66 | * AFRAME.registerComponent("border", border.Component); 67 | * } 68 | */ 69 | module.exports.Component = { 70 | 71 | version: function version() { 72 | return packageVersion; 73 | }, 74 | name: function name() { 75 | return packageName; 76 | }, 77 | 78 | dependencies: ['position', 'rotation'], 79 | 80 | /** defines the attribute properties 81 | * @Object 82 | * @memberof module:aframe-border-component 83 | */ 84 | 85 | schema: { 86 | enabled: { default: true }, 87 | sides: { 88 | type: 'int', 89 | default: 6 90 | }, 91 | radius: { 92 | type: 'int', 93 | default: 10 94 | }, 95 | open: { 96 | default: "" 97 | }, 98 | wall: { default: "" } 99 | }, 100 | 101 | /** Called once when component is attached. Generally for initial setup. 102 | * @function 103 | * @memberof module:aframe-border-component 104 | */ 105 | init: function init() { 106 | 107 | this.borderData = {}; 108 | 109 | var wallId = this.data.wall; 110 | 111 | wallId = wallId[0] == '#' ? wallId.substring(1) : wallId; 112 | 113 | var p = document.getElementById(wallId); 114 | if (p) { 115 | this.borderData.wallWidth = p.getAttribute("width"); 116 | this.borderData.wallDepth = p.getAttribute("depth"); 117 | this.borderData.wallHeight = p.getAttribute("height"); 118 | this.borderData.wallRotation = p.getAttribute("rotation"); 119 | } else { 120 | this.borderData.wallWidth = 4; 121 | this.borderData.wallDepth = 1; 122 | this.borderData.wallHeight = 1; 123 | this.borderData.wallRotation = { x: 0, y: 0, z: 0 }; 124 | } 125 | 126 | this.borderData.openList = []; 127 | 128 | this.buildOpenList(); 129 | }, 130 | 131 | buildOpenList: function buildOpenList() { 132 | // build open border list 133 | 134 | var tokens = this.data.open.split(' '); 135 | for (var key in tokens) { 136 | var token = tokens[key]; 137 | var i = parseInt(token, 10); 138 | if (!isNaN(i)) { 139 | if (i >= 0) { 140 | this.borderData.openList[i] = true; 141 | } 142 | } 143 | } 144 | }, 145 | 146 | drawBorderWall: function drawBorderWall(spec) { 147 | 148 | spec = spec || {}; 149 | var position = spec.position || { x: 0, y: 0, z: 0 }, 150 | rotation = spec.rotation || { x: 0, y: 0, z: 0 }, 151 | wallId = this.data.wall; 152 | 153 | wallId = wallId[0] == '#' ? wallId.substring(1) : wallId; 154 | 155 | var w = null; 156 | var p = document.getElementById(wallId); 157 | if (!p) { 158 | w = document.createElement('a-box'); 159 | this.el.appendChild(w); 160 | w.setAttribute('color', 'tomato'); 161 | w.setAttribute('width', this.borderData.wallWidth); 162 | w.setAttribute('depth', this.borderData.wallDepth); 163 | w.setAttribute('height', this.borderData.wallHeight); 164 | w.setAttribute('static-body', ''); 165 | } else { 166 | w = p.cloneNode(true); 167 | this.el.appendChild(w); 168 | } 169 | w.setAttribute('rotation', rotation); 170 | w.setAttribute('position', position); 171 | }, 172 | 173 | update: function update() { 174 | if (!this.data.enabled) { 175 | return; 176 | } 177 | var sides = this.data.sides, 178 | radius = this.data.radius; 179 | 180 | var WALL_WIDTH = this.borderData.wallWidth, 181 | WALL_DEPTH = this.borderData.wallDepth, 182 | WALL_HEIGHT = this.borderData.wallHeight, 183 | yPos = 0; 184 | 185 | var wallRotation = this.borderData.wallRotation; 186 | 187 | var step = 2 * Math.PI / sides, 188 | turn = 360 / sides; 189 | for (var i = 0, angle = 0; i < sides; i++, angle += step) { 190 | 191 | var xPos = radius * Math.cos(angle); 192 | var zPos = radius * Math.sin(angle); 193 | 194 | if (!this.borderData.openList[i]) { 195 | 196 | this.drawBorderWall({ 197 | position: { 198 | x: xPos, 199 | y: 0, 200 | z: zPos 201 | }, 202 | rotation: { 203 | x: wallRotation.x, 204 | y: wallRotation.y + 90 - i * turn, 205 | z: wallRotation.z 206 | } 207 | }); 208 | } 209 | } 210 | }, 211 | 212 | remove: function remove() {} 213 | }; 214 | 215 | },{"../upcoming-info":3}],3:[function(_dereq_,module,exports){ 216 | module.exports={"name":"aframe-border-component","version":"0.1.4","upcoming":{"release":"patch","version":"0.1.5"}} 217 | },{}]},{},[1]); 218 | -------------------------------------------------------------------------------- /dist/aframe-border-component.min.js: -------------------------------------------------------------------------------- 1 | !function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);var j=new Error("Cannot find module '"+g+"'");throw j.code="MODULE_NOT_FOUND",j}var k=c[g]={exports:{}};b[g][0].call(k.exports,function(a){var c=b[g][1][a];return e(c?c:a)},k,k.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;g=0&&(this.borderData.openList[d]=!0)}},drawBorderWall:function(a){a=a||{};var b=a.position||{x:0,y:0,z:0},c=a.rotation||{x:0,y:0,z:0},d=this.data.wall;d="#"==d[0]?d.substring(1):d;var e=null,f=document.getElementById(d);f?(e=f.cloneNode(!0),this.el.appendChild(e)):(e=document.createElement("a-box"),this.el.appendChild(e),e.setAttribute("color","tomato"),e.setAttribute("width",this.borderData.wallWidth),e.setAttribute("depth",this.borderData.wallDepth),e.setAttribute("height",this.borderData.wallHeight),e.setAttribute("static-body","")),e.setAttribute("rotation",c),e.setAttribute("position",b)},update:function(){if(this.data.enabled)for(var a=this.data.sides,b=this.data.radius,c=(this.borderData.wallWidth,this.borderData.wallDepth,this.borderData.wallHeight,this.borderData.wallRotation),d=2*Math.PI/a,e=360/a,f=0,g=0;f 2 | 3 | 4 | 5 | simple aframe-border-component example 6 | 7 | 8 | 9 | 10 | 11 | 12 | --> 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 23 | 26 | 30 | 33 | 36 | 39 | 40 | 41 | 45 | 50 | 51 | 52 | 56 | 57 | 58 | 62 | 63 | 64 | 68 | 72 | 73 | 74 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /examples/client-example/img/arrow-left-blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchallen/aframe-border-component/b582e83730b8c00ef85eede849be59442d7f3168/examples/client-example/img/arrow-left-blue.png -------------------------------------------------------------------------------- /examples/client-example/img/arrow-left-red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchallen/aframe-border-component/b582e83730b8c00ef85eede849be59442d7f3168/examples/client-example/img/arrow-left-red.png -------------------------------------------------------------------------------- /examples/client-example/img/arrow-left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchallen/aframe-border-component/b582e83730b8c00ef85eede849be59442d7f3168/examples/client-example/img/arrow-left.png -------------------------------------------------------------------------------- /examples/client-example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | aframe-border-component example 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 17 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 32 | 36 | 39 | 42 | 45 | 49 | 52 | 55 | 58 | 62 | 66 | 71 | 76 | 80 | 81 | 82 | 86 | 91 | 92 | 93 | 97 | 98 | 99 | 103 | 104 | 105 | 109 | 113 | 114 | 115 | 119 | 120 | 121 | 125 | 126 | 127 | 128 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | -------------------------------------------------------------------------------- /examples/gif/border-demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchallen/aframe-border-component/b582e83730b8c00ef85eede849be59442d7f3168/examples/gif/border-demo.png -------------------------------------------------------------------------------- /examples/simple/img/arrow-left-blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchallen/aframe-border-component/b582e83730b8c00ef85eede849be59442d7f3168/examples/simple/img/arrow-left-blue.png -------------------------------------------------------------------------------- /examples/simple/img/arrow-left-red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchallen/aframe-border-component/b582e83730b8c00ef85eede849be59442d7f3168/examples/simple/img/arrow-left-red.png -------------------------------------------------------------------------------- /examples/simple/img/arrow-left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchallen/aframe-border-component/b582e83730b8c00ef85eede849be59442d7f3168/examples/simple/img/arrow-left.png -------------------------------------------------------------------------------- /examples/simple/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | simple aframe-border-component example 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 17 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 31 | 34 | 38 | 41 | 44 | 47 | 48 | 49 | 53 | 58 | 59 | 60 | 64 | 65 | 66 | 70 | 71 | 72 | 76 | 80 | 81 | 82 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /modules/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | Module: @mitchallen/aframe-border-component 3 | Author: Mitch Allen 4 | */ 5 | /*jshint browser: true */ 6 | /*jshint node: true */ 7 | /*jshint esversion: 6 */ 8 | 9 | 10 | "use strict"; 11 | 12 | /** 13 | * Module 14 | * @module aframe-border-component 15 | */ 16 | 17 | var packageName = require("../upcoming-info").name, 18 | packageVersion = require("../upcoming-info").upcoming.version; 19 | 20 | /** 21 | * Specification for registering a border component with Aframe 22 | * @function 23 | * @example browserify example 24 | * var border = require('aframe-border-component'); 25 | * if (AFRAME.aframeCore) { 26 | * AFRAME.aframeCore.registerComponent("border", border.Component); 27 | * } else { 28 | * AFRAME.registerComponent("border", border.Component); 29 | * } 30 | */ 31 | module.exports.Component = { 32 | 33 | version: function() { return packageVersion; }, 34 | name: function() { return packageName; }, 35 | 36 | dependencies: ['position', 'rotation'], 37 | 38 | /** defines the attribute properties 39 | * @Object 40 | * @memberof module:aframe-border-component 41 | */ 42 | 43 | schema: { 44 | enabled: { default: true }, 45 | sides: { 46 | type: 'int', 47 | default: 6 48 | }, 49 | radius: { 50 | type: 'int', 51 | default: 10 52 | }, 53 | open: { 54 | default: "" 55 | }, 56 | wall: { default: "" } 57 | }, 58 | 59 | /** Called once when component is attached. Generally for initial setup. 60 | * @function 61 | * @memberof module:aframe-border-component 62 | */ 63 | init: function () { 64 | 65 | this.borderData = {}; 66 | 67 | var wallId = this.data.wall; 68 | 69 | wallId = wallId[0] == '#' ? wallId.substring(1) : wallId; 70 | 71 | var p = document.getElementById(wallId); 72 | if(p) { 73 | this.borderData.wallWidth = p.getAttribute("width"); 74 | this.borderData.wallDepth = p.getAttribute("depth"); 75 | this.borderData.wallHeight = p.getAttribute("height"); 76 | this.borderData.wallRotation = p.getAttribute("rotation"); 77 | } else { 78 | this.borderData.wallWidth = 4; 79 | this.borderData.wallDepth = 1; 80 | this.borderData.wallHeight = 1; 81 | this.borderData.wallRotation = { x: 0, y: 0, z: 0 }; 82 | } 83 | 84 | this.borderData.openList = []; 85 | 86 | this.buildOpenList(); 87 | }, 88 | 89 | buildOpenList: function() { 90 | // build open border list 91 | 92 | var tokens = this.data.open.split(' '); 93 | for( var key in tokens ) { 94 | var token = tokens[key]; 95 | var i = parseInt(token,10); 96 | if(!isNaN(i)) { 97 | if(i >= 0) { 98 | this.borderData.openList[i] = true; 99 | } 100 | } 101 | } 102 | }, 103 | 104 | drawBorderWall: function(spec) { 105 | 106 | spec = spec || {}; 107 | var position = spec.position || { x: 0, y: 0, z: 0 }, 108 | rotation = spec.rotation || { x: 0, y: 0, z: 0 }, 109 | wallId = this.data.wall; 110 | 111 | wallId = wallId[0] == '#' ? wallId.substring(1) : wallId; 112 | 113 | var w = null; 114 | var p = document.getElementById(wallId); 115 | if(!p) { 116 | w = document.createElement('a-box'); 117 | this.el.appendChild(w); 118 | w.setAttribute('color', 'tomato' ); 119 | w.setAttribute('width', this.borderData.wallWidth ); 120 | w.setAttribute('depth', this.borderData.wallDepth ); 121 | w.setAttribute('height', this.borderData.wallHeight ); 122 | w.setAttribute('static-body', ''); 123 | } else { 124 | w = p.cloneNode(true); 125 | this.el.appendChild(w); 126 | } 127 | w.setAttribute('rotation', rotation); 128 | w.setAttribute('position', position); 129 | }, 130 | 131 | update: function () { 132 | if (!this.data.enabled) { return; } 133 | var sides = this.data.sides, 134 | radius = this.data.radius; 135 | 136 | var WALL_WIDTH = this.borderData.wallWidth, 137 | WALL_DEPTH = this.borderData.wallDepth, 138 | WALL_HEIGHT = this.borderData.wallHeight, 139 | yPos = 0; 140 | 141 | var wallRotation = this.borderData.wallRotation; 142 | 143 | var step = (2*Math.PI) / sides, 144 | turn = 360 / sides; 145 | for(var i = 0, angle = 0; i < sides; i++, angle += step) { 146 | 147 | var xPos = radius * Math.cos(angle); 148 | var zPos = radius * Math.sin(angle); 149 | 150 | if( !this.borderData.openList[i] ) { 151 | 152 | this.drawBorderWall({ 153 | position: { 154 | x: xPos, 155 | y: 0, 156 | z: zPos 157 | }, 158 | rotation: { 159 | x: wallRotation.x, 160 | y: wallRotation.y + 90 - i * turn, 161 | z: wallRotation.z 162 | } 163 | }); 164 | } 165 | } 166 | }, 167 | 168 | remove: function () { 169 | } 170 | }; 171 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aframe-border-component", 3 | "version": "0.1.6", 4 | "description": "A component for creating borders in VR", 5 | "main": "modules/index.js", 6 | "scripts": { 7 | "//": "To use: npm run test-nodemon (assumes global location of mocha)", 8 | "start": "node dist/aframe-border-component.js", 9 | "test": "grunt && /usr/local/bin/mocha --recursive --timeout 20000", 10 | "test-coverage": "./node_modules/.bin/istanbul cover _mocha --recursive test/**/*", 11 | "test-nodemon": "nodemon /usr/local/bin/mocha --timeout 5000", 12 | "test-debug": "nodemon /usr/local/bin/mocha -g '@DEBUG' --recursive --timeout 20000", 13 | "test-jenkins": "grunt && /usr/local/bin/mocha --recursive --timeout 20000 -R xunit test/ > test-reports.xml", 14 | "web-server": "python -m SimpleHTTPServer $PORT || 8000" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/mitchallen/aframe-border-component.git" 19 | }, 20 | "author": "Mitch Allen (http://mitchallen.com)", 21 | "license": "MIT", 22 | "dependencies": {}, 23 | "devDependencies": { 24 | "babel-preset-es2015": "^6.18.0", 25 | "babelify": "^7.3.0", 26 | "browserify-derequire": "^0.9.4", 27 | "grunt": "^1.0.1", 28 | "grunt-browserify": "^5.0.0", 29 | "grunt-bump": "^0.8.0", 30 | "grunt-contrib-jshint": "^1.0.0", 31 | "grunt-contrib-uglify": "^2.0.0", 32 | "grunt-contrib-watch": "^1.0.0", 33 | "grunt-jsdoc-to-markdown": "^2.0.0", 34 | "grunt-shell": "^1.3.0", 35 | "grunt-upcoming": "^0.2.0", 36 | "istanbul": "^0.4.5", 37 | "mocha": "^3.2.0", 38 | "mockbot-document": "^0.1.14", 39 | "should": "^10.0.0", 40 | "supertest": "^2.0.0" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /test/module/module-smoke-test.js: -------------------------------------------------------------------------------- 1 | /** 2 | Module: @mitchallen/aframe-border-component 3 | Test: smoke-test-module 4 | Author: Mitch Allen 5 | */ 6 | 7 | "use strict"; 8 | 9 | var request = require('supertest'), 10 | should = require('should'), 11 | documentFactory = require("mockbot-document"), 12 | modulePath = "../../modules/index"; 13 | 14 | describe('module smoke test', () => { 15 | 16 | var module = null; 17 | 18 | var component = {}; 19 | 20 | before( done => { 21 | // Call before all tests 22 | 23 | // mock browser document 24 | 25 | global.document = documentFactory.create(); 26 | 27 | document.mockElement( { tagName: "a-box", id: "wall-one" } ); 28 | 29 | done(); 30 | }); 31 | 32 | after( done => { 33 | // Call after all tests 34 | 35 | delete global.document; 36 | 37 | done(); 38 | }); 39 | 40 | beforeEach( done => { 41 | // Call before each test 42 | delete require.cache[require.resolve(modulePath)]; 43 | module = require(modulePath); 44 | 45 | component = module.Component; 46 | component.el = { 47 | appendChild: function(el) {}, 48 | } 49 | component.data = {}; 50 | // copy defaults from schema 51 | for(var k in component.schema) { 52 | var cType = component.schema[k].type; 53 | switch(cType) { 54 | default: 55 | component.data[k] = component.schema[k].default; 56 | break; 57 | } 58 | } 59 | done(); 60 | }); 61 | 62 | afterEach( done => { 63 | // Call after eeach test 64 | done(); 65 | }); 66 | 67 | it('module should exist', done => { 68 | should.exist(module); 69 | done(); 70 | }) 71 | 72 | 73 | it('Component should return component object', done => { 74 | should.exist(module); 75 | var component = module.Component; 76 | should.exist(component); 77 | done(); 78 | }) 79 | 80 | it('component.version should match upcoming version', done => { 81 | var component = module.Component; 82 | should.exist(component.version); 83 | console.log(component.version()); 84 | component.version().should.eql(require("../../upcoming-info").upcoming.version); 85 | done(); 86 | }) 87 | 88 | it('component.name should match package name', done => { 89 | var component = module.Component; 90 | should.exist(component.name); 91 | console.log(component.name()); 92 | component.name().should.eql(require("../../upcoming-info").name); 93 | done(); 94 | }) 95 | 96 | it('component init should succeed', done => { 97 | component.init(); 98 | done(); 99 | }) 100 | 101 | it('component init should succeed for found wall element', done => { 102 | var wallId = "wall-one"; 103 | var el = document.getElementById(wallId); 104 | should.exist(el); 105 | var w = el.getAttribute("width"); 106 | component.data.wall = wallId; 107 | component.init(); 108 | done(); 109 | }) 110 | 111 | it('component init should succeed for found wall element with pound sign starting name', done => { 112 | component.data.wall = "#wall-one"; 113 | component.init(); 114 | done(); 115 | }) 116 | 117 | 118 | it('component init should succeed for open data', done => { 119 | component.data.open = "N 0"; 120 | component.init(); 121 | done(); 122 | }) 123 | 124 | it('component init should succeed for open data has negative value', done => { 125 | component.data.open = "N -1"; 126 | component.init(); 127 | done(); 128 | }) 129 | 130 | it('component update should succeed', done => { 131 | component.init(); 132 | component.update(); 133 | done(); 134 | }) 135 | 136 | it('component update should succeed if enabled = false', done => { 137 | component.init(); 138 | component.data.enabled = false; 139 | component.update(); 140 | done(); 141 | }) 142 | 143 | it('component update with valid wall id should succeed', done => { 144 | component.init(); 145 | component.data.wall = "#wall-one"; 146 | component.update(); 147 | done(); 148 | }) 149 | 150 | it('component update should succeed for found wall element', done => { 151 | component.data.wall = "wall-one"; 152 | component.init(); 153 | component.update(); 154 | done(); 155 | }) 156 | 157 | it('component update should succeed for found wall element with pound sign starting name', done => { 158 | component.data.wall = "#wall-one"; 159 | component.init(); 160 | component.update(); 161 | done(); 162 | }) 163 | 164 | it('component update should succeed for open data', done => { 165 | component.data.open = "N 0"; 166 | component.init(); 167 | component.update(); 168 | done(); 169 | }) 170 | 171 | it('drawBorderWall with no spec should succeed', done => { 172 | component.init(); 173 | component.drawBorderWall(); 174 | done(); 175 | }) 176 | 177 | it('remove should succeed', done => { 178 | component.remove(); 179 | done(); 180 | }) 181 | 182 | it('remove after init should succeed', done => { 183 | component.init(); 184 | component.remove(); 185 | done(); 186 | }) 187 | 188 | it('remove after update should succeed', done => { 189 | component.init(); 190 | component.update(); 191 | component.remove(); 192 | done(); 193 | }) 194 | }); 195 | -------------------------------------------------------------------------------- /upcoming-info.json: -------------------------------------------------------------------------------- 1 | {"name":"aframe-border-component","version":"0.1.4","upcoming":{"release":"patch","version":"0.1.5"}} --------------------------------------------------------------------------------