├── .github ├── CODE_OF_CONDUCT.md ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── custom.md │ └── feature_request.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .npmignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── config ├── build-typings.js ├── jsdoc.conf.json ├── webpack.config.dev.js └── webpack.config.js ├── dist ├── phaser-raycaster.js ├── phaser-raycaster.js.map ├── phaser-raycaster.min.js └── phaser-raycaster.min.js.map ├── docs ├── Raycaster.Map.html ├── Raycaster.Ray.html ├── Raycaster.html ├── classes.list.html ├── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.svg │ ├── glyphicons-halflings-regular.ttf │ ├── glyphicons-halflings-regular.woff │ └── glyphicons-halflings-regular.woff2 ├── img │ ├── glyphicons-halflings-white.png │ └── glyphicons-halflings.png ├── index.html ├── map_config.js.html ├── map_map-core.js.html ├── map_map-tilemap-methods.js.html ├── map_segmentsCount.js.html ├── quicksearch.html ├── ray_angle.js.html ├── ray_cast.js.html ├── ray_castCircle.js.html ├── ray_castCone.js.html ├── ray_cone.js.html ├── ray_config.js.html ├── ray_enablePhysics.js.html ├── ray_matter-physics-methods.js.html ├── ray_origin.js.html ├── ray_overlap.js.html ├── ray_range.js.html ├── ray_ray-core.js.html ├── ray_ray.js.html ├── raycaster-core.js.html ├── scripts │ ├── docstrap.lib.js │ ├── fulltext-search-ui.js │ ├── fulltext-search.js │ ├── lunr.min.js │ ├── prettify │ │ ├── Apache-License-2.0.txt │ │ ├── jquery.min.js │ │ ├── lang-css.js │ │ └── prettify.js │ ├── sunlight.js │ └── toc.js └── styles │ ├── darkstrap.css │ ├── prettify-tomorrow.css │ ├── site.cerulean.css │ ├── site.cosmo.css │ ├── site.cyborg.css │ ├── site.darkly.css │ ├── site.darkstrap.css │ ├── site.dibs-bootstrap.css │ ├── site.flatly.css │ ├── site.journal.css │ ├── site.lumen.css │ ├── site.paper.css │ ├── site.readable.css │ ├── site.sandstone.css │ ├── site.simplex.css │ ├── site.slate.css │ ├── site.spacelab.css │ ├── site.superhero.css │ ├── site.united.css │ ├── site.yeti.css │ ├── sunlight.dark.css │ └── sunlight.default.css ├── package.json ├── src ├── main.js ├── map │ ├── boundingBox.js │ ├── config.js │ ├── destroy.js │ ├── map-circle-methods.js │ ├── map-container-methods.js │ ├── map-core.js │ ├── map-line-methods.js │ ├── map-matterBody-methods.js │ ├── map-polygon-methods.js │ ├── map-rectangle-methods.js │ ├── map-tilemap-methods.js │ └── segmentsCount.js ├── ray │ ├── angle.js │ ├── cast.js │ ├── castCircle.js │ ├── castCone.js │ ├── cone.js │ ├── config.js │ ├── debug.js │ ├── destroy.js │ ├── enablePhysics.js │ ├── matter-physics-methods.js │ ├── origin.js │ ├── overlap.js │ ├── range.js │ ├── ray-core.js │ ├── ray.js │ ├── slice.js │ └── stats.js └── raycaster-core.js └── types └── types.d.ts /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at wiserim.msg.gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/custom.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Custom issue template 3 | about: Describe this issue template's purpose here. 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Please do not update the README or CHANGELOG, we will do this when we merge your PR. 2 | 3 | This PR (delete as applicable) 4 | 5 | * Updates the Documentation 6 | * Adds a new feature 7 | * Fixes a bug 8 | 9 | Describe the changes below: 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | 13 | # Runtime data 14 | pids 15 | *.pid 16 | *.seed 17 | *.pid.lock 18 | 19 | # Directory for instrumented libs generated by jscoverage/JSCover 20 | lib-cov 21 | 22 | # Coverage directory used by tools like istanbul 23 | coverage 24 | *.lcov 25 | 26 | # nyc test coverage 27 | .nyc_output 28 | 29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 30 | .grunt 31 | 32 | # Bower dependency directory (https://bower.io/) 33 | bower_components 34 | 35 | # node-waf configuration 36 | .lock-wscript 37 | 38 | # Compiled binary addons (https://nodejs.org/api/addons.html) 39 | build/Release 40 | 41 | # Dependency directories 42 | node_modules/ 43 | jspm_packages/ 44 | package-lock.json 45 | 46 | # docs 47 | docs/node_modules/ 48 | docs/jspm_packages/ 49 | docs/package-lock.json 50 | 51 | # TypeScript v1 declaration files 52 | typings/ 53 | 54 | # TypeScript cache 55 | *.tsbuildinfo 56 | 57 | # Optional npm cache directory 58 | .npm 59 | 60 | # Optional eslint cache 61 | .eslintcache 62 | 63 | # Microbundle cache 64 | .rpt2_cache/ 65 | .rts2_cache_cjs/ 66 | .rts2_cache_es/ 67 | .rts2_cache_umd/ 68 | 69 | # Optional REPL history 70 | .node_repl_history 71 | 72 | # Output of 'npm pack' 73 | *.tgz 74 | 75 | # Yarn Integrity file 76 | .yarn-integrity 77 | 78 | # dotenv environment variables file 79 | .env 80 | .env.test 81 | 82 | # parcel-bundler cache (https://parceljs.org/) 83 | .cache 84 | 85 | # Next.js build output 86 | .next 87 | 88 | # Nuxt.js build / generate output 89 | .nuxt 90 | 91 | # Gatsby files 92 | .cache/ 93 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 94 | # https://nextjs.org/blog/next-9-1#public-directory-support 95 | # public 96 | 97 | # vuepress build output 98 | .vuepress/dist 99 | 100 | # Serverless directories 101 | .serverless/ 102 | 103 | # FuseBox cache 104 | .fusebox/ 105 | 106 | # DynamoDB Local files 107 | .dynamodb/ 108 | 109 | # TernJS port file 110 | .tern-port 111 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /docs -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Marcin Walczak 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 all 13 | 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 THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /config/build-typings.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | const { exec } = require('child_process'); 4 | 5 | exec('node node_modules/jsdoc/jsdoc.js -t node_modules/tsd-jsdoc/dist -r src -d ./types', (err, stdout, stderr) => { 6 | if(err) { 7 | console.error(`exec error: ${err}`); 8 | return; 9 | } 10 | 11 | let file = fs.readFileSync(path.resolve(__dirname, '../types/types.d.ts'), 'utf8'); 12 | file += ` 13 | declare module 'phaser-raycaster' { 14 | export = PhaserRaycaster; 15 | }`; 16 | 17 | fs.writeFileSync(path.resolve(__dirname, '../types/types.d.ts'), file, 'utf8'); 18 | console.log('types.d.ts file build successfully.'); 19 | }); -------------------------------------------------------------------------------- /config/jsdoc.conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [], 3 | "recurseDepth": 10, 4 | "source": { 5 | "include": [ 6 | //raycaster 7 | "./src/raycaster-core.js", 8 | //ray 9 | "./src/ray/ray-core.js", 10 | "./src/ray/config.js", 11 | "./src/ray/cast.js", 12 | "./src/ray/castCircle.js", 13 | "./src/ray/castCone.js", 14 | "./src/ray/ray.js", 15 | "./src/ray/origin.js", 16 | "./src/ray/angle.js", 17 | "./src/ray/range.js", 18 | "./src/ray/cone.js", 19 | "./src/ray/overlap.js", 20 | "./src/ray/enablePhysics.js", 21 | "./src/ray/matter-physics-methods.js", 22 | //map 23 | "./src/map/map-core.js", 24 | "./src/map/config.js", 25 | "./src/map/segmentsCount.js", 26 | "./src/map/boundingBox.js", 27 | "./src/map/map-rectangle-methods.js", 28 | "./src/map/map-line-methods.js", 29 | "./src/map/map-circle-methods.js", 30 | "./src/map/map-polygon-methods.js", 31 | "./src/map/map-container-methods.js", 32 | "./src/map/map-tilemap-methods.js", 33 | "./src/map/map-matterBody-methods.js" 34 | ], 35 | "includePattern": ".+\\.js(doc|x)?$", 36 | "excludePattern": "(^|\\/|\\\\)_" 37 | }, 38 | "sourceType": "module", 39 | "tags": { 40 | "allowUnknownTags": true, 41 | "dictionaries": ["jsdoc","closure"] 42 | }, 43 | "opts": { 44 | "template": "./node_modules/ink-docstrap/template", 45 | "destination": "./docs" 46 | }, 47 | "templates": { 48 | "cleverLinks": false, 49 | "monospaceLinks": false, 50 | "systemName": "Phaser-raycaster", 51 | "footer": "

Phaser-raycaster

", 52 | "copyright": "© Marcin Walczak 2022", 53 | "includeDate": true, 54 | "navType": "vertical", 55 | "theme": "paper", 56 | "inverseNav": true, 57 | "linenums": true, 58 | "outputSourceFiles": true, 59 | "sort": false 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /config/webpack.config.dev.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const TerserPlugin = require('terser-webpack-plugin'); 3 | 4 | module.exports = { 5 | mode: "development", 6 | entry: { 7 | "phaser-raycaster": "./src/main.js", 8 | }, 9 | output: { 10 | path: path.resolve(__dirname, "../dist"), 11 | filename: "[name].js", 12 | library: 'PhaserRaycaster', 13 | libraryTarget: 'umd', 14 | umdNamedDefine: true 15 | }, 16 | watch: false, 17 | mode: "development", 18 | devtool: "source-map", 19 | optimization: { 20 | minimizer: [new TerserPlugin({ 21 | extractComments: false, 22 | })], 23 | }, 24 | module: { 25 | rules: [ 26 | { 27 | test: /\.m?js$/, 28 | exclude: /(node_modules|bower_components)/, 29 | use: { 30 | loader: "babel-loader", 31 | options: { 32 | presets: ["@babel/preset-env"] 33 | } 34 | } 35 | } 36 | ] 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /config/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const TerserPlugin = require('terser-webpack-plugin'); 3 | 4 | module.exports = { 5 | entry: { 6 | "phaser-raycaster": "./src/main.js", 7 | }, 8 | output: { 9 | path: path.resolve(__dirname, "../dist"), 10 | filename: "[name].min.js", 11 | library: 'PhaserRaycaster', 12 | libraryTarget: 'umd', 13 | umdNamedDefine: true 14 | }, 15 | watch: false, 16 | watchOptions: { 17 | ignored: '**/node_modules', 18 | }, 19 | mode: "development", 20 | devtool: "source-map", 21 | optimization: { 22 | minimizer: [new TerserPlugin({ 23 | extractComments: false, 24 | })], 25 | }, 26 | module: { 27 | rules: [ 28 | { 29 | test: /\.m?js$/, 30 | exclude: /(node_modules|bower_components)/, 31 | use: { 32 | loader: "babel-loader", 33 | options: { 34 | presets: ["@babel/preset-env"] 35 | } 36 | } 37 | } 38 | ] 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /docs/classes.list.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Phaser-raycaster Classes 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 57 | 58 | 59 |
60 |
61 | 62 | 63 |
64 | 65 |
66 | 67 | 68 |

Classes

69 |
70 | 71 |
72 | 73 |

74 | 75 |

76 | 77 | 78 |
79 | 80 | 81 |
82 |
83 | 84 | 85 | 86 | 87 |
88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 |
124 | 125 | 126 | 127 | 128 |
129 | 130 | 131 | 132 | 133 | 134 | 135 |

Classes

136 | 137 |
138 |
Raycaster
139 |
140 | 141 |
Ray
142 |
143 | 144 |
Map
145 |
146 |
147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 |
161 | 162 |
163 | 164 | 165 | 166 | 167 |
168 |
169 | 170 |
171 | 172 | 173 |
174 | 175 |
176 | 177 | 178 |
179 |
180 | 181 | 182 | 196 | 197 | 198 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 298 | 299 | 300 | 301 | -------------------------------------------------------------------------------- /docs/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiserim/phaser-raycaster/ee934eb2ec44c0213e68d52d0c89bcd3bd46e36c/docs/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /docs/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiserim/phaser-raycaster/ee934eb2ec44c0213e68d52d0c89bcd3bd46e36c/docs/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /docs/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiserim/phaser-raycaster/ee934eb2ec44c0213e68d52d0c89bcd3bd46e36c/docs/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /docs/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiserim/phaser-raycaster/ee934eb2ec44c0213e68d52d0c89bcd3bd46e36c/docs/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /docs/img/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiserim/phaser-raycaster/ee934eb2ec44c0213e68d52d0c89bcd3bd46e36c/docs/img/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /docs/img/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiserim/phaser-raycaster/ee934eb2ec44c0213e68d52d0c89bcd3bd46e36c/docs/img/glyphicons-halflings.png -------------------------------------------------------------------------------- /docs/map_segmentsCount.js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Phaser-raycaster Source: map/segmentsCount.js 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 57 | 58 | 59 |
60 |
61 | 62 | 63 |
64 | 65 |
66 | 67 | 68 |

Source: map/segmentsCount.js

69 | 70 |
71 |
72 |
/**
 74 |  * Set segment count for cirle's map.
 75 |  * If set to 0, map won't be generating segments and relay only on tangent points calculated for currently testing ray.
 76 |  *
 77 |  * @method Raycaster.Map#setSegmentCount
 78 |  * @memberof Raycaster.Map
 79 |  * @instance
 80 |  * @since 0.6.0
 81 |  *
 82 |  * @param {number} count - Circle map's segment count.
 83 |  *
 84 |  * @return {Raycaster.Map} {@link Raycaster.Map Raycaster.Map} instance
 85 |  */
 86 | export function setSegmentCount(count) {
 87 |     this.segmentCount = count;
 88 |     this.circle = count ? false : true;
 89 | 
 90 |     this.updateMap();
 91 |     return this;
 92 | }
 93 | 
94 |
95 |
96 | 97 | 98 | 99 | 100 | 101 |
102 |
103 | 104 |
105 | 106 | 107 | 108 |
109 |
110 | 111 | 112 | 126 | 127 | 128 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 228 | 229 | 230 | 231 | 232 | -------------------------------------------------------------------------------- /docs/ray_angle.js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Phaser-raycaster Source: ray/angle.js 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 57 | 58 | 59 |
60 |
61 | 62 | 63 |
64 | 65 |
66 | 67 | 68 |

Source: ray/angle.js

69 | 70 |
71 |
72 |
/**
 74 |  * Set ray's angle (direction) in radians.
 75 |  *
 76 |  * @method Raycaster.Ray#setAngle
 77 |  * @memberof Raycaster.Ray
 78 |  * @instance
 79 |  * @since 0.6.0
 80 |  *
 81 |  * @param {number} [angle = 0] - Ray's angle in radians.
 82 |  *
 83 |  * @return {Raycaster.Ray} {@link Raycaster.Ray Raycaster.Ray} instance
 84 |  */
 85 | export function setAngle(angle = 0) {
 86 |     this.angle = Phaser.Math.Angle.Normalize(angle);
 87 |     Phaser.Geom.Line.SetToAngle(this._ray, this.origin.x, this.origin.y, this.angle, this.rayRange);
 88 |     return this;
 89 | }
 90 | 
 91 | /**
 92 |  * Set ray's angle (direction) in degrees.
 93 |  *
 94 |  * @method Raycaster.Ray#setAngleDeg
 95 |  * @memberof Raycaster.Ray
 96 |  * @instance
 97 |  * @since 0.6.1
 98 |  *
 99 |  * @param {number} [angle = 0] - Ray's angle in degrees.
100 |  *
101 |  * @return {Raycaster.Ray} {@link Raycaster.Ray Raycaster.Ray} instance
102 |  */
103 | export function setAngleDeg(angle = 0) {
104 |     this.angle = Phaser.Math.Angle.Normalize(Phaser.Math.DegToRad(angle));
105 |     Phaser.Geom.Line.SetToAngle(this._ray, this.origin.x, this.origin.y, this.angle, this.rayRange);
106 |     return this;
107 | }
108 | 
109 |
110 |
111 | 112 | 113 | 114 | 115 | 116 |
117 |
118 | 119 |
120 | 121 | 122 | 123 |
124 |
125 | 126 | 127 | 141 | 142 | 143 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 243 | 244 | 245 | 246 | 247 | -------------------------------------------------------------------------------- /docs/ray_cone.js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Phaser-raycaster Source: ray/cone.js 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 57 | 58 | 59 |
60 |
61 | 62 | 63 |
64 | 65 |
66 | 67 | 68 |

Source: ray/cone.js

69 | 70 |
71 |
72 |
/**
 74 |  * Set ray's cone angle (width) in radians.
 75 |  *
 76 |  * @method Raycaster.Ray#setCone
 77 |  * @memberof Raycaster.Ray
 78 |  * @instance
 79 |  * @since 0.7.0
 80 |  *
 81 |  * @param {number} [cone = 0] - Ray's cone angle in radians.
 82 |  *
 83 |  * @return {Raycaster.Ray} {@link Raycaster.Ray Raycaster.Ray} instance
 84 |  */
 85 | export function setCone(cone = 0) {
 86 |     this.cone = cone;
 87 |     return this;
 88 | }
 89 | 
 90 | /**
 91 |  * Set ray's cone angle (width) in degrees.
 92 |  *
 93 |  * @method Raycaster.Ray#setConeDeg
 94 |  * @memberof Raycaster.Ray
 95 |  * @instance
 96 |  * @since 0.7.0
 97 |  *
 98 |  * @param {number} [cone = 0] - Ray's cone angle in degrees.
 99 |  *
100 |  * @return {Raycaster.Ray} {@link Raycaster.Ray Raycaster.Ray} instance
101 |  */
102 | export function setConeDeg(cone = 0) {
103 |     this.cone = Phaser.Math.DegToRad(cone);
104 |     return this;
105 | }
106 | 
107 |
108 |
109 | 110 | 111 | 112 | 113 | 114 |
115 |
116 | 117 |
118 | 119 | 120 | 121 |
122 |
123 | 124 | 125 | 139 | 140 | 141 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 241 | 242 | 243 | 244 | 245 | -------------------------------------------------------------------------------- /docs/ray_config.js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Phaser-raycaster Source: ray/config.js 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 57 | 58 | 59 |
60 |
61 | 62 | 63 |
64 | 65 |
66 | 67 | 68 |

Source: ray/config.js

69 | 70 |
71 |
72 |
/**
 74 |  * Configure ray.
 75 |  *
 76 |  * @method Raycaster.Ray#config
 77 |  * @memberof Raycaster.Ray
 78 |  * @instance
 79 |  * @since 0.6.0
 80 |  *
 81 |  * @param {object} [options] - Ray's congfiguration options. May include:
 82 |  * @param {Phaser.Geom.Point|Point} [options.origin = {x:0, y:0}] - Ray's position.
 83 |  * @param {number} [options.angle = 0] - Ray's angle in radians.
 84 |  * @param {number} [options.angleDeg = 0] - Ray's angle in degrees.
 85 |  * @param {number} [options.cone = 0] - Ray's cone angle in radians.
 86 |  * @param {number} [options.coneDeg = 0] - Ray's cone angle in degrees.
 87 |  * @param {number} [options.range = Phaser.Math.MAX_SAFE_INTEGER] - Ray's range.
 88 |  * @param {number} [options.collisionRange = Phaser.Math.MAX_SAFE_INTEGER] - Ray's maximum collision range of ray's field of view.
 89 |  * @param {number} [options.detectionRange = Phaser.Math.MAX_SAFE_INTEGER] - Maximum distance between ray's position and tested objects bounding boxes.
 90 |  * @param {boolean} [options.ignoreNotIntersectedRays = true] - If set true, ray returns false when it didn't hit anything. Otherwise returns ray's target position.
 91 |  * @param {boolean} [options.autoSlice = false] - If set true, ray will automatically slice intersections into array of triangles and store it in {@link Raycaster.Ray#slicedIntersections Ray.slicedIntersections}.
 92 |  * @param {boolean} [options.round = false] - If set true, point where ray hit will be rounded.
 93 |  * @param {(boolean|'arcade'|'matter')} [options.enablePhysics = false] - Add to ray physics body. Body will be a circle with radius equal to {@link Raycaster.Ray#collisionRange Ray.collisionRange}. If set true, arcade physics body will be added.
 94 |  *
 95 |  * @return {Raycaster.Ray} {@link Raycaster.Ray Raycaster.Ray} instance
 96 |  */
 97 | export function config(options) {
 98 |     this.object = options.object;
 99 |     //origin
100 |     if(options.origin !== undefined)
101 |         this.origin.setTo(options.origin.x, options.origin.y);
102 | 
103 |     //angle
104 |     if(options.angle !== undefined)
105 |         this.angle = Phaser.Math.Angle.Normalize(options.angle);
106 | 
107 |     //angle deg
108 |     if(options.angleDeg !== undefined)
109 |         this.angle = Phaser.Math.Angle.Normalize(Phaser.Math.DegToRad(options.angleDeg));
110 | 
111 |     //cone angle
112 |     if(options.cone !== undefined)
113 |         this.cone = options.cone;
114 | 
115 |     //cone angle deg
116 |     if(options.coneDeg !== undefined)
117 |         this.cone = Phaser.Math.DegToRad(options.coneDeg);
118 | 
119 |     //ray range (0 = max)
120 |     if(options.rayRange !== undefined)
121 |         this.rayRange = options.rayRange;
122 | 
123 |     //collision range (0 = max)
124 |     if(options.collisionRange !== undefined)
125 |         this.collisionRange = options.collisionRange;
126 | 
127 |     //detection range (0 = max)
128 |     if(options.detectionRange !== undefined)
129 |         this.detectionRange = options.detectionRange;
130 | 
131 |     //ignore not intersected rays
132 |     if(options.ignoreNotIntersectedRays !== undefined)
133 |         this.ignoreNotIntersectedRays = (options.ignoreNotIntersectedRays == true)
134 |     
135 |     //round
136 |     if(options.round !== undefined)
137 |         this.round = (options.round == true)
138 | 
139 |     //auto slice
140 |     if(options.autoSlice !== undefined)
141 |         this.autoSlice = (options.autoSlice == true)
142 | 
143 |     //enable physics
144 |     if(options.enablePhysics !== undefined && options.enablePhysics)
145 |         this.enablePhysics(options.enablePhysics);
146 |     
147 |     Phaser.Geom.Line.SetToAngle(this._ray, this.origin.x, this.origin.y, this.angle, this.rayRange);
148 |     this.detectionRangeCircle.setTo(this.origin.x, this.origin.y,this.detectionRange);
149 | 
150 |     if(this._raycaster.debugOptions.enabled && this._raycaster.scene !== undefined) {
151 |         this.graphics =  this._raycaster.scene.add.graphics({ lineStyle: { width: 1, color: 0x00ff00}, fillStyle: { color: 0xff00ff } });
152 |         this.graphics.setDepth(1000);
153 |     }
154 | 
155 |     return this;
156 | }
157 | 
158 |
159 |
160 | 161 | 162 | 163 | 164 | 165 |
166 |
167 | 168 |
169 | 170 | 171 | 172 |
173 |
174 | 175 | 176 | 190 | 191 | 192 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 292 | 293 | 294 | 295 | 296 | -------------------------------------------------------------------------------- /docs/ray_enablePhysics.js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Phaser-raycaster Source: ray/enablePhysics.js 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 57 | 58 | 59 |
60 |
61 | 62 | 63 |
64 | 65 |
66 | 67 | 68 |

Source: ray/enablePhysics.js

69 | 70 |
71 |
72 |
/**
 74 |  * Add to ray physics body. Body will be a circle with radius equal to {@link Raycaster.Ray#collisionRange Ray.collisionRange}. Physics body can be added only once.
 75 |  *
 76 |  * @method Raycaster.Ray#enablePhysics
 77 |  * @memberof Raycaster.Ray
 78 |  * @instance
 79 |  * @since 0.8.0
 80 |  *
 81 |  * @param {'arcade'|'matter'} [type = 'arcade'] - Physics type
 82 |  * 
 83 |  * @return {Raycaster.Ray} {@link Raycaster.Ray Raycaster.Ray} instance
 84 |  */
 85 | export function enablePhysics(type = 'arcade') {
 86 |     
 87 |     if(this.body !== undefined)
 88 |         return this;
 89 | 
 90 |     this.collisionCircle = this._raycaster.scene.add.circle(this.origin.x, this.origin.y, this.collisionRange);
 91 |     this.collisionCircle._ray = this;
 92 | 
 93 |     if(type === 'matter') {
 94 |         this.bodyType = 'matter';
 95 | 
 96 |         if(this.collisionRange == Phaser.Math.MAX_SAFE_INTEGER) {
 97 |             let bounds = this._raycaster.boundingBox;
 98 |             this._raycaster.scene.matter.add.gameObject(this.collisionCircle, { shape: { type: 'rectangle', x:bounds.rectangle.centerX, y:bounds.rectangle.centerY, width:bounds.rectangle.width, height:bounds.rectangle.height }, label: 'phaser-raycaster-ray-body', isSensor: true, ignoreGravity:true });
 99 |         }
100 |         else {
101 |             this._raycaster.scene.matter.add.gameObject(this.collisionCircle, { shape: { type: 'circle' }, label: 'phaser-raycaster-ray-body', isSensor: true, ignoreGravity:true });
102 |         }
103 | 
104 |         this.body = this.collisionCircle.body;
105 |         this.body._ray = this;
106 |         this.setOnCollideActive();
107 |     }
108 |     else {
109 |         this.bodyType = 'arcade';
110 |         this._raycaster.scene.physics.add.existing(this.collisionCircle);
111 | 
112 |         this.body = this.collisionCircle.body;
113 |         this.body
114 |             .setCircle(this.collisionRange)
115 |             .setAllowGravity(false)
116 |             .setImmovable(true);
117 |         this.body._ray = this;
118 |     }
119 | 
120 |     return this;
121 | }
122 | 
123 |
124 |
125 | 126 | 127 | 128 | 129 | 130 |
131 |
132 | 133 |
134 | 135 | 136 | 137 |
138 |
139 | 140 | 141 | 155 | 156 | 157 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 257 | 258 | 259 | 260 | 261 | -------------------------------------------------------------------------------- /docs/ray_origin.js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Phaser-raycaster Source: ray/origin.js 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 57 | 58 | 59 |
60 |
61 | 62 | 63 |
64 | 65 |
66 | 67 | 68 |

Source: ray/origin.js

69 | 70 |
71 |
72 |
/**
 74 |  * Set ray's source position.
 75 |  *
 76 |  * @method Raycaster.Ray#setOrigin
 77 |  * @memberof Raycaster.Ray
 78 |  * @instance
 79 |  * @since 0.6.0
 80 |  *
 81 |  * @param {number} x - X coordinate.
 82 |  * @param {number} y - Y coordinate.
 83 |  *
 84 |  * @return {Raycaster.Ray} {@link Raycaster.Ray Raycaster.Ray} instance
 85 |  */
 86 | export function setOrigin(x, y) {
 87 |     this.origin.setTo(x, y);
 88 |     Phaser.Geom.Line.SetToAngle(this._ray, this.origin.x, this.origin.y, this.angle, this.rayRange);
 89 |     this.detectionRangeCircle.setTo(this.origin.x, this.origin.y,this.detectionRange);
 90 | 
 91 |     if(this.bodyType === 'matter' && this.collisionRange !== Phaser.Math.MAX_SAFE_INTEGER) {
 92 |         this.collisionCircle.x = x;
 93 |         this.collisionCircle.y = y;
 94 |     }
 95 |     else if(this.bodyType === 'arcade') {
 96 |         this.collisionCircle.x = x;
 97 |         this.collisionCircle.y = y;
 98 |     }
 99 | 
100 |     return this;
101 | }
102 | 
103 |
104 |
105 | 106 | 107 | 108 | 109 | 110 |
111 |
112 | 113 |
114 | 115 | 116 | 117 |
118 |
119 | 120 | 121 | 135 | 136 | 137 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 237 | 238 | 239 | 240 | 241 | -------------------------------------------------------------------------------- /docs/ray_range.js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Phaser-raycaster Source: ray/range.js 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 57 | 58 | 59 |
60 |
61 | 62 | 63 |
64 | 65 |
66 | 67 | 68 |

Source: ray/range.js

69 | 70 |
71 |
72 |
/**
 74 |  * Set ray's range.
 75 |  *
 76 |  * @method Raycaster.Ray#setRayRange
 77 |  * @memberof Raycaster.Ray
 78 |  * @instance
 79 |  * @since 0.6.0
 80 |  *
 81 |  * @param {number} [rayRange = Phaser.Math.MAX_SAFE_INTEGER] - Ray's range.
 82 |  *
 83 |  * @return {Raycaster.Ray} {@link Raycaster.Ray Raycaster.Ray} instance
 84 |  */
 85 | export function setRayRange(rayRange = Phaser.Math.MAX_SAFE_INTEGER) {
 86 |     this.rayRange = rayRange;
 87 |     Phaser.Geom.Line.SetToAngle(this._ray, this.origin.x, this.origin.y, this.angle, this.rayRange);
 88 |     return this;
 89 | }
 90 | 
 91 | /**
 92 |  * Set ray's maximum detection range. Objects outside detection range won't be tested.
 93 |  * Ray tests all objects when set to 0.
 94 |  *
 95 |  * @method Raycaster.Ray#setDetectionRange
 96 |  * @memberof Raycaster.Ray
 97 |  * @instance
 98 |  * @since 0.6.0
 99 |  *
100 |  * @param {number} [detectionRange = 0] - Maximum distance between ray's position and tested objects bounding boxes.
101 |  *
102 |  * @return {Raycaster.Ray} {@link Raycaster.Ray Raycaster.Ray} instance
103 |  */
104 | export function setDetectionRange(detectionRange = 0) {
105 |     this.detectionRange = detectionRange;
106 |     this.detectionRangeCircle.setTo(this.origin.x, this.origin.y,this.detectionRange);
107 | 
108 |     return this;
109 | }
110 | 
111 | /**
112 |  * Set ray's field of view maximum collision range. Objects outside collision range won't be tested by {@link Raycaster.Ray#overlap Raycaster.Ray.overlap} method.
113 |  * Determines ray's physics body radius.
114 |  *
115 |  * @method Raycaster.Ray#setCollisionRange
116 |  * @memberof Raycaster.Ray
117 |  * @instance
118 |  * @since 0.8.0
119 |  *
120 |  * @param {number} [collisionRange = Phaser.Math.MAX_SAFE_INTEGER] - Ray's collision range and physics body radius.
121 |  *
122 |  * @return {Raycaster.Ray} {@link Raycaster.Ray Raycaster.Ray} instance
123 |  */
124 | export function setCollisionRange(collisionRange = Phaser.Math.MAX_SAFE_INTEGER) {
125 |     let oldRangeMax = this.collisionRange == Phaser.Math.MAX_SAFE_INTEGER;
126 |     this.collisionRange = collisionRange;
127 |     this.collisionCircle.setRadius(this.collisionRange);
128 | 
129 |     if(this.bodyType === 'matter') {
130 |         if(this.collisionRange == Phaser.Math.MAX_SAFE_INTEGER) {
131 |             let bounds = this._raycaster.boundingBox;
132 | 
133 |             this._raycaster.scene.matter.body.set(this.body, {
134 |                 shape: {
135 |                     type: 'rectangle',
136 |                     x: bounds.rectangle.centerX,
137 |                     y: bounds.rectangle.centerY,
138 |                     width: bounds.rectangle.width,
139 |                     height: bounds.rectangle.height,
140 |                     circleRadius:0
141 |                 }
142 |             });
143 |         }
144 |         else if(oldRangeMax) {
145 |             this._raycaster.scene.matter.body.set(this.body, {
146 |                 shape: {
147 |                     type: 'circle',
148 |                     x: this.collisionCircle.x,
149 |                     y: this.collisionCircle.y
150 |                 },
151 |                 circleRadius: this.collisionRange,
152 |                 isStatic: false
153 |             });
154 |         }
155 |         else {
156 |             this.collisionCircle.setRadius(this.collisionRange);
157 |         }
158 |         this._raycaster.scene.matter.body.set(this.body, 'circleRadius', this.collisionRange)
159 |     }
160 |     else if(this.bodyType === 'arcade') {
161 |         this.body.setCircle(this.collisionRange);
162 |     }
163 | 
164 |     return this;
165 | }
166 | 
167 | /**
168 |  * Test if object's bounding box is in ray's detection range.
169 |  *
170 |  * @method Raycaster.Ray#boundsInRange
171 |  * @memberof Raycaster.Ray
172 |  * @instance
173 |  * @since 0.6.0
174 |  *
175 |  * @param {object} object - Tested object
176 |  * @param {(Phaser.Geom.Rectangle|boolean)} [bounds = false] - Tested object's bounds. If not passed bounds will be generated automatically.
177 |  *
178 |  * @return {boolean} Information if object is in ray's detection range.
179 |  */
180 | export function boundsInRange(object, bounds = false) {
181 |     if(!this.detectionRange)
182 |         return true;
183 | 
184 |     let objectBounds;
185 |     if(bounds)
186 |         objectBounds = bounds;
187 |     else {
188 |         if(object.type === 'body' || object.type === 'composite')
189 |             objectBounds = object.raycasterMap.getBoundingBox();
190 |         else
191 |             objectBounds = object.data.get('raycasterMap').getBoundingBox();
192 |     }
193 | 
194 |     if(Phaser.Geom.Intersects.CircleToRectangle(this.detectionRangeCircle, objectBounds))
195 |         return true;
196 | 
197 |     return false;
198 | }
199 | 
200 |
201 |
202 | 203 | 204 | 205 | 206 | 207 |
208 |
209 | 210 |
211 | 212 | 213 | 214 |
215 |
216 | 217 | 218 | 232 | 233 | 234 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 334 | 335 | 336 | 337 | 338 | -------------------------------------------------------------------------------- /docs/ray_ray.js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Phaser-raycaster Source: ray/ray.js 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 57 | 58 | 59 |
60 |
61 | 62 | 63 |
64 | 65 |
66 | 67 | 68 |

Source: ray/ray.js

69 | 70 |
71 |
72 |
/**
 74 |  * Set ray's position, direction (angle) and range.
 75 |  *
 76 |  * @method Raycaster.Ray#setRay
 77 |  * @memberof Raycaster.Ray
 78 |  * @instance
 79 |  * @since 0.6.0
 80 |  *
 81 |  * @param {number} x - X coordinate.
 82 |  * @param {number} y - Y coordinate.
 83 |  * @param {number} [angle] - Ray's angle in radians.
 84 |  * @param {number} [range = Phaser.Math.MAX_SAFE_INTEGER] - Ray's range.
 85 |  *
 86 |  * @return {Raycaster.Ray} {@link Raycaster.Ray Raycaster.Ray} instance
 87 |  */
 88 | export function setRay(x, y, angle, rayRange = Phaser.Math.MAX_SAFE_INTEGER) {
 89 |     this.origin.setTo(x, y);
 90 |     this.angle = Phaser.Math.Angle.Normalize(angle);
 91 |     this.rayRange = rayRange;
 92 | 
 93 |     Phaser.Geom.Line.SetToAngle(this._ray, this.origin.x, this.origin.y, this.angle, this.rayRange);
 94 |     this.detectionRangeCircle.setTo(this.origin.x, this.origin.y, this.detectionRange);
 95 |     return this;
 96 | }
 97 | 
98 |
99 |
100 | 101 | 102 | 103 | 104 | 105 |
106 |
107 | 108 |
109 | 110 | 111 | 112 |
113 |
114 | 115 | 116 | 130 | 131 | 132 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 232 | 233 | 234 | 235 | 236 | -------------------------------------------------------------------------------- /docs/scripts/fulltext-search-ui.js: -------------------------------------------------------------------------------- 1 | window.SearcherDisplay = (function($) { 2 | /** 3 | * This class provides support for displaying quick search text results to users. 4 | */ 5 | function SearcherDisplay() { } 6 | 7 | SearcherDisplay.prototype.init = function() { 8 | this._displayQuickSearch(); 9 | }; 10 | 11 | /** 12 | * This method creates the quick text search entry in navigation menu and wires all required events. 13 | */ 14 | SearcherDisplay.prototype._displayQuickSearch = function() { 15 | var quickSearch = $(document.createElement("iframe")), 16 | body = $("body"), 17 | self = this; 18 | 19 | quickSearch.attr("src", "quicksearch.html"); 20 | quickSearch.css("width", "0px"); 21 | quickSearch.css("height", "0px"); 22 | 23 | body.append(quickSearch); 24 | 25 | $(window).on("message", function(msg) { 26 | var msgData = msg.originalEvent.data; 27 | 28 | if (msgData.msgid != "docstrap.quicksearch.done") { 29 | return; 30 | } 31 | 32 | var results = msgData.results || []; 33 | 34 | self._displaySearchResults(results); 35 | }); 36 | 37 | function startSearch() { 38 | var searchTerms = $('#search-input').prop("value"); 39 | if (searchTerms) { 40 | quickSearch[0].contentWindow.postMessage({ 41 | "searchTerms": searchTerms, 42 | "msgid": "docstrap.quicksearch.start" 43 | }, "*"); 44 | } 45 | } 46 | 47 | $('#search-input').on('keyup', function(evt) { 48 | if (evt.keyCode != 13) { 49 | return; 50 | } 51 | startSearch(); 52 | return false; 53 | }); 54 | $('#search-submit').on('click', function() { 55 | startSearch(); 56 | return false; 57 | }); 58 | }; 59 | 60 | /** 61 | * This method displays the quick text search results in a modal dialog. 62 | */ 63 | SearcherDisplay.prototype._displaySearchResults = function(results) { 64 | var resultsHolder = $($("#searchResults").find(".modal-body")), 65 | fragment = document.createDocumentFragment(), 66 | resultsList = document.createElement("ul"); 67 | 68 | resultsHolder.empty(); 69 | 70 | for (var idx = 0; idx < results.length; idx++) { 71 | var result = results[idx], 72 | item = document.createElement("li"), 73 | link = document.createElement("a"); 74 | 75 | link.href = result.id; 76 | link.innerHTML = result.title; 77 | 78 | item.appendChild(link) 79 | resultsList.appendChild(item); 80 | } 81 | 82 | fragment.appendChild(resultsList); 83 | resultsHolder.append(fragment); 84 | 85 | $("#searchResults").modal({"show": true}); 86 | }; 87 | 88 | return new SearcherDisplay(); 89 | })($); 90 | -------------------------------------------------------------------------------- /docs/scripts/fulltext-search.js: -------------------------------------------------------------------------------- 1 | window.Searcher = (function() { 2 | function Searcher() { 3 | this._index = lunr(function () { 4 | this.field('title', {boost: 10}) 5 | this.field('body') 6 | this.ref('id') 7 | }) ; 8 | 9 | this._indexContent = undefined; 10 | } 11 | 12 | Searcher.prototype.init = function() { 13 | var self = this; 14 | 15 | $("script[type='text/x-docstrap-searchdb']").each(function(idx, item) { 16 | self._indexContent = JSON.parse(item.innerHTML); 17 | 18 | for (var entryId in self._indexContent) { 19 | self._index.add(self._indexContent[entryId]); 20 | } 21 | }); 22 | }; 23 | 24 | Searcher.prototype.search = function(searchTerm) { 25 | var results = [], 26 | searchResults = this._index.search(searchTerm); 27 | 28 | for (var idx = 0; idx < searchResults.length; idx++) { 29 | results.push(this._indexContent[searchResults[idx].ref]) 30 | } 31 | 32 | return results; 33 | }; 34 | 35 | return new Searcher(); 36 | })(); -------------------------------------------------------------------------------- /docs/scripts/prettify/lang-css.js: -------------------------------------------------------------------------------- 1 | PR.registerLangHandler(PR.createSimpleLexer([ 2 | ["pln", /^[\t\n\f\r ]+/, null, " \t\r\n "] 3 | ], [ 4 | ["str", /^"(?:[^\n\f\r"\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*"/, null], 5 | ["str", /^'(?:[^\n\f\r'\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*'/, null], 6 | ["lang-css-str", /^url\(([^"')]*)\)/i], 7 | ["kwd", /^(?:url|rgb|!important|@import|@page|@media|@charset|inherit)(?=[^\w-]|$)/i, null], 8 | ["lang-css-kw", /^(-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*)\s*:/i], 9 | ["com", /^\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//], 10 | ["com", /^(?:<\!--|--\>)/], 11 | ["lit", /^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i], 12 | ["lit", /^#[\da-f]{3,6}/i], 13 | ["pln", /^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i], 14 | ["pun", /^[^\s\w"']+/] 15 | ]), ["css"]); 16 | PR.registerLangHandler(PR.createSimpleLexer([], [ 17 | ["kwd", /^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i] 18 | ]), ["css-kw"]); 19 | PR.registerLangHandler(PR.createSimpleLexer([], [ 20 | ["str", /^[^"')]+/] 21 | ]), ["css-str"]); -------------------------------------------------------------------------------- /docs/scripts/toc.js: -------------------------------------------------------------------------------- 1 | (function($) { 2 | var navbarHeight; 3 | var initialised = false; 4 | var navbarOffset; 5 | 6 | function elOffset($el) { 7 | return $el.offset().top - (navbarHeight + navbarOffset); 8 | } 9 | 10 | function scrollToHash(duringPageLoad) { 11 | var elScrollToId = location.hash.replace(/^#/, ''); 12 | var $el; 13 | 14 | function doScroll() { 15 | var offsetTop = elOffset($el); 16 | window.scrollTo(window.pageXOffset || window.scrollX, offsetTop); 17 | } 18 | 19 | if (elScrollToId) { 20 | $el = $(document.getElementById(elScrollToId)); 21 | 22 | if (!$el.length) { 23 | $el = $(document.getElementsByName(elScrollToId)); 24 | } 25 | 26 | if ($el.length) { 27 | if (duringPageLoad) { 28 | $(window).one('scroll', function() { 29 | setTimeout(doScroll, 100); 30 | }); 31 | } else { 32 | setTimeout(doScroll, 0); 33 | } 34 | } 35 | } 36 | } 37 | 38 | function init(opts) { 39 | if (initialised) { 40 | return; 41 | } 42 | initialised = true; 43 | navbarHeight = $('.navbar').height(); 44 | navbarOffset = opts.navbarOffset; 45 | 46 | // some browsers move the offset after changing location. 47 | // also catch external links coming in 48 | $(window).on("hashchange", scrollToHash.bind(null, false)); 49 | $(scrollToHash.bind(null, true)); 50 | } 51 | 52 | $.catchAnchorLinks = function(options) { 53 | var opts = $.extend({}, jQuery.fn.toc.defaults, options); 54 | init(opts); 55 | }; 56 | 57 | $.fn.toc = function(options) { 58 | var self = this; 59 | var opts = $.extend({}, jQuery.fn.toc.defaults, options); 60 | 61 | var container = $(opts.container); 62 | var tocs = []; 63 | var headings = $(opts.selectors, container); 64 | var headingOffsets = []; 65 | var activeClassName = 'active'; 66 | var ANCHOR_PREFIX = "__anchor"; 67 | var maxScrollTo; 68 | var visibleHeight; 69 | var headerHeight = 10; // so if the header is readable, its counted as shown 70 | init(); 71 | 72 | var scrollTo = function(e) { 73 | e.preventDefault(); 74 | var target = $(e.target); 75 | if (target.prop('tagName').toLowerCase() !== "a") { 76 | target = target.parent(); 77 | } 78 | var elScrollToId = target.attr('href').replace(/^#/, '') + ANCHOR_PREFIX; 79 | var $el = $(document.getElementById(elScrollToId)); 80 | 81 | var offsetTop = Math.min(maxScrollTo, elOffset($el)); 82 | 83 | $('body,html').animate({ scrollTop: offsetTop }, 400, 'swing', function() { 84 | location.hash = '#' + elScrollToId; 85 | }); 86 | 87 | $('a', self).removeClass(activeClassName); 88 | target.addClass(activeClassName); 89 | }; 90 | 91 | var calcHadingOffsets = function() { 92 | maxScrollTo = $("body").height() - $(window).height(); 93 | visibleHeight = $(window).height() - navbarHeight; 94 | headingOffsets = []; 95 | headings.each(function(i, heading) { 96 | var anchorSpan = $(heading).prev("span"); 97 | var top = 0; 98 | if (anchorSpan.length) { 99 | top = elOffset(anchorSpan); 100 | } 101 | headingOffsets.push(top > 0 ? top : 0); 102 | }); 103 | } 104 | 105 | //highlight on scroll 106 | var timeout; 107 | var highlightOnScroll = function(e) { 108 | if (!tocs.length) { 109 | return; 110 | } 111 | if (timeout) { 112 | clearTimeout(timeout); 113 | } 114 | timeout = setTimeout(function() { 115 | var top = $(window).scrollTop(), 116 | highlighted; 117 | for (var i = headingOffsets.length - 1; i >= 0; i--) { 118 | var isActive = tocs[i].hasClass(activeClassName); 119 | // at the end of the page, allow any shown header 120 | if (isActive && headingOffsets[i] >= maxScrollTo && top >= maxScrollTo) { 121 | return; 122 | } 123 | // if we have got to the first heading or the heading is the first one visible 124 | if (i === 0 || (headingOffsets[i] + headerHeight >= top && (headingOffsets[i - 1] + headerHeight <= top))) { 125 | // in the case that a heading takes up more than the visible height e.g. we are showing 126 | // only the one above, highlight the one above 127 | if (i > 0 && headingOffsets[i] - visibleHeight >= top) { 128 | i--; 129 | } 130 | $('a', self).removeClass(activeClassName); 131 | if (i >= 0) { 132 | highlighted = tocs[i].addClass(activeClassName); 133 | opts.onHighlight(highlighted); 134 | } 135 | break; 136 | } 137 | } 138 | }, 50); 139 | }; 140 | if (opts.highlightOnScroll) { 141 | $(window).bind('scroll', highlightOnScroll); 142 | $(window).bind('load resize', function() { 143 | calcHadingOffsets(); 144 | highlightOnScroll(); 145 | }); 146 | } 147 | 148 | return this.each(function() { 149 | //build TOC 150 | var el = $(this); 151 | var ul = $('
'); 152 | 153 | headings.each(function(i, heading) { 154 | var $h = $(heading); 155 | 156 | var anchor = $('').attr('id', opts.anchorName(i, heading, opts.prefix) + ANCHOR_PREFIX).insertBefore($h); 157 | 158 | var span = $('') 159 | .text(opts.headerText(i, heading, $h)); 160 | 161 | //build TOC item 162 | var a = $('') 163 | .append(span) 164 | .attr('href', '#' + opts.anchorName(i, heading, opts.prefix)) 165 | .bind('click', function(e) { 166 | scrollTo(e); 167 | el.trigger('selected', $(this).attr('href')); 168 | }); 169 | 170 | span.addClass(opts.itemClass(i, heading, $h, opts.prefix)); 171 | 172 | tocs.push(a); 173 | 174 | ul.append(a); 175 | }); 176 | el.html(ul); 177 | 178 | calcHadingOffsets(); 179 | }); 180 | }; 181 | 182 | 183 | jQuery.fn.toc.defaults = { 184 | container: 'body', 185 | selectors: 'h1,h2,h3', 186 | smoothScrolling: true, 187 | prefix: 'toc', 188 | onHighlight: function() {}, 189 | highlightOnScroll: true, 190 | navbarOffset: 0, 191 | anchorName: function(i, heading, prefix) { 192 | return prefix+i; 193 | }, 194 | headerText: function(i, heading, $heading) { 195 | return $heading.text(); 196 | }, 197 | itemClass: function(i, heading, $heading, prefix) { 198 | return prefix + '-' + $heading[0].tagName.toLowerCase(); 199 | } 200 | 201 | }; 202 | 203 | })(jQuery); 204 | -------------------------------------------------------------------------------- /docs/styles/prettify-tomorrow.css: -------------------------------------------------------------------------------- 1 | /* Tomorrow Theme */ 2 | /* Original theme - https://github.com/chriskempson/tomorrow-theme */ 3 | /* Pretty printing styles. Used with prettify.js. */ 4 | /* SPAN elements with the classes below are added by prettyprint. */ 5 | /* plain text */ 6 | .pln { 7 | color: #4d4d4c; } 8 | 9 | @media screen { 10 | /* string content */ 11 | .str { 12 | color: #718c00; } 13 | 14 | /* a keyword */ 15 | .kwd { 16 | color: #8959a8; } 17 | 18 | /* a comment */ 19 | .com { 20 | color: #8e908c; } 21 | 22 | /* a type name */ 23 | .typ { 24 | color: #4271ae; } 25 | 26 | /* a literal value */ 27 | .lit { 28 | color: #f5871f; } 29 | 30 | /* punctuation */ 31 | .pun { 32 | color: #4d4d4c; } 33 | 34 | /* lisp open bracket */ 35 | .opn { 36 | color: #4d4d4c; } 37 | 38 | /* lisp close bracket */ 39 | .clo { 40 | color: #4d4d4c; } 41 | 42 | /* a markup tag name */ 43 | .tag { 44 | color: #c82829; } 45 | 46 | /* a markup attribute name */ 47 | .atn { 48 | color: #f5871f; } 49 | 50 | /* a markup attribute value */ 51 | .atv { 52 | color: #3e999f; } 53 | 54 | /* a declaration */ 55 | .dec { 56 | color: #f5871f; } 57 | 58 | /* a variable name */ 59 | .var { 60 | color: #c82829; } 61 | 62 | /* a function name */ 63 | .fun { 64 | color: #4271ae; } } 65 | /* Use higher contrast and text-weight for printable form. */ 66 | @media print, projection { 67 | .str { 68 | color: #060; } 69 | 70 | .kwd { 71 | color: #006; 72 | font-weight: bold; } 73 | 74 | .com { 75 | color: #600; 76 | font-style: italic; } 77 | 78 | .typ { 79 | color: #404; 80 | font-weight: bold; } 81 | 82 | .lit { 83 | color: #044; } 84 | 85 | .pun, .opn, .clo { 86 | color: #440; } 87 | 88 | .tag { 89 | color: #006; 90 | font-weight: bold; } 91 | 92 | .atn { 93 | color: #404; } 94 | 95 | .atv { 96 | color: #060; } } 97 | /* Style */ 98 | /* 99 | pre.prettyprint { 100 | background: white; 101 | font-family: Menlo, Monaco, Consolas, monospace; 102 | font-size: 12px; 103 | line-height: 1.5; 104 | border: 1px solid #ccc; 105 | padding: 10px; } 106 | */ 107 | 108 | /* Specify class=linenums on a pre to get line numbering */ 109 | ol.linenums { 110 | margin-top: 0; 111 | margin-bottom: 0; } 112 | 113 | /* IE indents via margin-left */ 114 | li.L0, 115 | li.L1, 116 | li.L2, 117 | li.L3, 118 | li.L4, 119 | li.L5, 120 | li.L6, 121 | li.L7, 122 | li.L8, 123 | li.L9 { 124 | /* */ } 125 | 126 | /* Alternate shading for lines */ 127 | li.L1, 128 | li.L3, 129 | li.L5, 130 | li.L7, 131 | li.L9 { 132 | /* */ } 133 | -------------------------------------------------------------------------------- /docs/styles/sunlight.dark.css: -------------------------------------------------------------------------------- 1 | /* global styles */ 2 | .sunlight-container { 3 | clear: both !important; 4 | position: relative !important; 5 | margin: 10px 0 !important; 6 | } 7 | .sunlight-code-container { 8 | clear: both !important; 9 | position: relative !important; 10 | border: none; 11 | border-color: #626262 !important; 12 | background-color: #262626 !important; 13 | } 14 | .sunlight-highlighted, .sunlight-container, .sunlight-container textarea { 15 | font-family: Consolas, Inconsolata, Monaco, "Courier New" !important; 16 | font-size: 12px !important; 17 | line-height: 15px !important; 18 | } 19 | .sunlight-highlighted, .sunlight-container textarea { 20 | color: #FFFFFF !important; 21 | margin: 0 !important; 22 | } 23 | .sunlight-container textarea { 24 | padding-left: 0 !important; 25 | margin-left: 0 !important; 26 | margin-right: 0 !important; 27 | padding-right: 0 !important; 28 | } 29 | .sunlight-code-container > .sunlight-highlighted { 30 | white-space: pre; 31 | overflow-x: auto; 32 | overflow-y: hidden; /* ie requires this wtf? */ 33 | } 34 | .sunlight-highlighted { 35 | z-index: 1; 36 | position: relative; 37 | } 38 | .sunlight-highlighted * { 39 | background: transparent; 40 | } 41 | .sunlight-line-number-margin { 42 | float: left !important; 43 | margin-right: 5px !important; 44 | margin-top: 0 !important; 45 | margin-bottom: 0 !important; 46 | padding: 0 !important; 47 | padding-right: 4px !important; 48 | padding-left: 4px !important; 49 | border-right: 1px solid #9A9A9A !important; 50 | background-color: #3E3E3E !important; 51 | color: #9A9A9A !important; 52 | text-align: right !important; 53 | position: relative; 54 | z-index: 3; 55 | } 56 | .sunlight-highlighted a, .sunlight-line-number-margin a { 57 | border: none !important; 58 | text-decoration: none !important; 59 | font-style: normal !important; 60 | padding: 0 !important; 61 | } 62 | .sunlight-line-number-margin a { 63 | color: inherit !important; 64 | } 65 | .sunlight-line-highlight-overlay { 66 | position: absolute; 67 | top: 0; 68 | left: 0; 69 | width: 100%; 70 | z-index: 0; 71 | } 72 | .sunlight-line-highlight-overlay div { 73 | height: 15px; 74 | width: 100%; 75 | } 76 | .sunlight-line-highlight-overlay .sunlight-line-highlight-active { 77 | background-color: #4B4B4B; 78 | } 79 | 80 | /* menu */ 81 | .sunlight-menu { 82 | background-color: #FFFFCC; 83 | color: #000000; 84 | } 85 | .sunlight-menu ul { 86 | margin: 0 !important; 87 | padding: 0 !important; 88 | list-style-type: none !important; 89 | } 90 | .sunlight-menu li { 91 | float: right !important; 92 | margin-left: 5px !important; 93 | } 94 | .sunlight-menu a, .sunlight-menu img { 95 | color: #000099 !important; 96 | text-decoration: none !important; 97 | border: none !important; 98 | } 99 | 100 | 101 | 102 | 103 | .sunlight-string, 104 | .sunlight-char, 105 | .sunlight-heredoc, 106 | .sunlight-heredocDeclaration, 107 | .sunlight-nowdoc, 108 | .sunlight-longString, 109 | .sunlight-rawString, 110 | .sunlight-binaryString, 111 | .sunlight-verbatimString, 112 | .sunlight-rawLongString, 113 | .sunlight-binaryLongString, 114 | .sunlight-diff .sunlight-added { 115 | color: #55EB54 !important; 116 | } 117 | .sunlight-operator, 118 | .sunlight-punctuation, 119 | .sunlight-delimiter { 120 | color: #B1EDEC !important; 121 | } 122 | .sunlight-ident, 123 | .sunlight-diff .sunlight-unchanged { 124 | color: #E0E0E0 !important; 125 | font-weight: bold !important; 126 | } 127 | .sunlight-comment, 128 | .sunlight-xmlDocCommentContent, 129 | .sunlight-nginx .sunlight-ssiCommand, 130 | .sunlight-sln .sunlight-formatDeclaration, 131 | .sunlight-diff .sunlight-mergeHeader, 132 | .sunlight-diff .sunlight-noNewLine { 133 | color: #787D31 !important; 134 | } 135 | .sunlight-number, 136 | .sunlight-cdata, 137 | .sunlight-guid, 138 | .sunlight-diff .sunlight-modified { 139 | color: #F7BA7E !important; 140 | font-weight: bold !important; 141 | } 142 | .sunlight-named-ident, 143 | .sunlight-xml .sunlight-attribute, 144 | .sunlight-constant, 145 | .sunlight-javascript .sunlight-globalVariable, 146 | .sunlight-globalObject, 147 | .sunlight-css .sunlight-id, 148 | .sunlight-python .sunlight-attribute, 149 | .sunlight-nginx .sunlight-context, 150 | .sunlight-httpd .sunlight-context, 151 | .sunlight-lisp .sunlight-declarationSpecifier, 152 | .sunlight-erlang .sunlight-userDefinedFunction, 153 | .sunlight-diff .sunlight-removed { 154 | color: #FBBDEE !important; 155 | font-weight: bold !important; 156 | } 157 | .sunlight-keyword, 158 | .sunlight-languageConstruct, 159 | .sunlight-specialOperator, 160 | .sunlight-xml .sunlight-tagName, 161 | .sunlight-xml .sunlight-operator, 162 | .sunlight-bash .sunlight-command, 163 | .sunlight-erlang .sunlight-moduleAttribute { 164 | color: #A3CCF7 !important; 165 | font-weight: bold !important; 166 | } 167 | .sunlight-shortOpenTag, 168 | .sunlight-openTag, 169 | .sunlight-closeTag, 170 | .sunlight-xmlOpenTag, 171 | .sunlight-xmlCloseTag, 172 | .sunlight-aspOpenTag, 173 | .sunlight-aspCloseTag, 174 | .sunlight-label, 175 | .sunlight-css .sunlight-importantFlag { 176 | background-color: #7373C1 !important; 177 | } 178 | .sunlight-content { 179 | color: #FFFFFF !important; 180 | font-weight: bold !important; 181 | } 182 | .sunlight-function, 183 | .sunlight-globalFunction, 184 | .sunlight-objective-c .sunlight-messageDestination, 185 | .sunlight-ruby .sunlight-specialFunction, 186 | .sunlight-6502asm .sunlight-illegalOpcode, 187 | .sunlight-powershell .sunlight-switch, 188 | .sunlight-lisp .sunlight-macro, 189 | .sunlight-lisp .sunlight-specialForm, 190 | .sunlight-lisp .sunlight-type, 191 | .sunlight-sln .sunlight-sectionName, 192 | .sunlight-diff .sunlight-header { 193 | color: #C8BBF1 !important; 194 | font-weight: bold !important; 195 | } 196 | .sunlight-variable, 197 | .sunlight-environmentVariable, 198 | .sunlight-specialVariable, 199 | .sunlight-objective-c .sunlight-messageArgumentName, 200 | .sunlight-lisp .sunlight-globalVariable, 201 | .sunlight-ruby .sunlight-globalVariable, 202 | .sunlight-ruby .sunlight-instanceVariable { 203 | color: #F5E5B0 !important; 204 | font-weight: bold !important; 205 | } 206 | .sunlight-regexLiteral, 207 | .sunlight-lisp .sunlight-operator, 208 | .sunlight-6502asm .sunlight-pseudoOp, 209 | .sunlight-erlang .sunlight-macro, 210 | .sunlight-diff .sunlight-rangeInfo { 211 | color: #E0F16A !important; 212 | } 213 | .sunlight-specialVariable { 214 | font-style: italic !important; 215 | font-weight: bold !important; 216 | } 217 | .sunlight-csharp .sunlight-pragma, 218 | .sunlight-preprocessorDirective, 219 | .sunlight-vb .sunlight-compilerDirective { 220 | color: #666363 !important; 221 | font-style: italic !important; 222 | } 223 | .sunlight-xmlDocCommentMeta, 224 | .sunlight-java .sunlight-annotation, 225 | .sunlight-scala .sunlight-annotation, 226 | .sunlight-docComment { 227 | color: #666363 !important; 228 | } 229 | .sunlight-quotedIdent, 230 | .sunlight-ruby .sunlight-subshellCommand, 231 | .sunlight-lisp .sunlight-keywordArgument, 232 | .sunlight-haskell .sunlight-infixOperator, 233 | .sunlight-erlang .sunlight-quotedAtom { 234 | color: #F8CA16 !important; 235 | } 236 | 237 | 238 | 239 | 240 | /* html/xml */ 241 | .sunlight-xml .sunlight-tagName, 242 | .sunlight-xml .sunlight-operator, 243 | .sunlight-xml .sunlight-attribute { 244 | font-weight: normal !important; 245 | } 246 | .sunlight-doctype { 247 | color: #DEB9B2 !important; 248 | font-style: italic !important; 249 | } 250 | .sunlight-xml .sunlight-entity { 251 | background-color: #E6E585 !important; 252 | color: #000000 !important; 253 | } 254 | 255 | /* javascript */ 256 | .sunlight-javascript .sunlight-reservedWord { 257 | font-style: italic !important; 258 | } 259 | 260 | /* css */ 261 | .sunlight-css .sunlight-element { 262 | color: #E9EE97 !important; 263 | } 264 | .sunlight-css .sunlight-microsoftFilterPrefix { 265 | color: #C9FF9F !important; 266 | } 267 | .sunlight-css .sunlight-rule { 268 | color: #0099FF !important; 269 | } 270 | .sunlight-css .sunlight-class { 271 | color: #E78282 !important; 272 | } 273 | .sunlight-css .sunlight-pseudoClass, .sunlight-css .sunlight-pseudoElement { 274 | color: #73D693 !important; 275 | } 276 | 277 | /* bash */ 278 | .sunlight-bash .sunlight-hashBang { 279 | color: #FFFF00 !important; 280 | } 281 | 282 | .sunlight-bash .sunlight-verbatimCommand { 283 | color: #BBA4EE !important; 284 | } 285 | .sunlight-bash .sunlight-variable, 286 | .sunlight-bash .sunlight-specialVariable { 287 | color: #ED8585 !important; 288 | } 289 | 290 | /* python */ 291 | .sunlight-python .sunlight-specialMethod { 292 | font-weight: bold !important; 293 | color: #B0A3C2; 294 | } 295 | 296 | /* ruby */ 297 | .sunlight-ruby .sunlight-symbol { 298 | font-weight: bold !important; 299 | color: #90EEA2 !important; 300 | } 301 | 302 | /* brainfuck */ 303 | .sunlight-brainfuck { 304 | font-weight: bold !important; 305 | color: #000000 !important; 306 | } 307 | .sunlight-brainfuck .sunlight-increment { 308 | background-color: #FF9900 !important; 309 | } 310 | .sunlight-brainfuck .sunlight-decrement { 311 | background-color: #FF99FF !important; 312 | } 313 | .sunlight-brainfuck .sunlight-incrementPointer { 314 | background-color: #FFFF99 !important; 315 | } 316 | .sunlight-brainfuck .sunlight-decrementPointer { 317 | background-color: #66CCFF !important; 318 | } 319 | .sunlight-brainfuck .sunlight-read { 320 | background-color: #FFFFFF !important; 321 | } 322 | .sunlight-brainfuck .sunlight-write { 323 | background-color: #99FF99 !important; 324 | } 325 | .sunlight-brainfuck .sunlight-openLoop, .sunlight-brainfuck .sunlight-closeLoop { 326 | background-color: #FFFFFF !important; 327 | } 328 | 329 | /* 6502 asm */ 330 | .sunlight-6502asm .sunlight-label { 331 | background: none !important; 332 | color: #FFFFFF !important; 333 | text-decoration: underline !important; 334 | } 335 | 336 | /* lisp */ 337 | .sunlight-lisp .sunlight-macro { 338 | font-style: italic !important; 339 | } 340 | 341 | /* erlang */ 342 | .sunlight-erlang .sunlight-atom { 343 | color: #FFFFFF !important; 344 | font-weight: bold !important; 345 | } -------------------------------------------------------------------------------- /docs/styles/sunlight.default.css: -------------------------------------------------------------------------------- 1 | /* global styles */ 2 | .sunlight-container { 3 | clear: both !important; 4 | position: relative !important; 5 | margin: 10px 0 !important; 6 | } 7 | .sunlight-code-container { 8 | clear: both !important; 9 | position: relative !important; 10 | border: none; 11 | border-color: #969696 !important; 12 | background-color: #FFFFFF !important; 13 | } 14 | .sunlight-highlighted, .sunlight-container, .sunlight-container textarea { 15 | font-family: Consolas, Inconsolata, Monaco, "Courier New" !important; 16 | font-size: 12px !important; 17 | line-height: 15px !important; 18 | } 19 | .sunlight-highlighted, .sunlight-container textarea { 20 | color: #000000 !important; 21 | margin: 0 !important; 22 | } 23 | .sunlight-container textarea { 24 | padding-left: 0 !important; 25 | margin-left: 0 !important; 26 | margin-right: 0 !important; 27 | padding-right: 0 !important; 28 | } 29 | .sunlight-code-container > .sunlight-highlighted { 30 | white-space: pre; 31 | overflow-x: auto; 32 | overflow-y: hidden; /* ie requires this wtf? */ 33 | } 34 | .sunlight-highlighted { 35 | z-index: 1; 36 | position: relative; 37 | } 38 | .sunlight-highlighted * { 39 | background: transparent; 40 | } 41 | .sunlight-line-number-margin { 42 | float: left !important; 43 | margin-right: 5px !important; 44 | margin-top: 0 !important; 45 | margin-bottom: 0 !important; 46 | padding: 0 !important; 47 | padding-right: 4px !important; 48 | padding-left: 4px !important; 49 | border-right: 1px solid #CCCCCC !important; 50 | background-color: #EEEEEE !important; 51 | color: #848484 !important; 52 | text-align: right !important; 53 | position: relative; 54 | z-index: 3; 55 | } 56 | .sunlight-highlighted a, .sunlight-line-number-margin a { 57 | border: none !important; 58 | text-decoration: none !important; 59 | font-weight: normal !important; 60 | font-style: normal !important; 61 | padding: 0 !important; 62 | } 63 | .sunlight-line-number-margin a { 64 | color: inherit !important; 65 | } 66 | .sunlight-line-highlight-overlay { 67 | position: absolute; 68 | top: 0; 69 | left: 0; 70 | width: 100%; 71 | z-index: 0; 72 | } 73 | .sunlight-line-highlight-overlay div { 74 | height: 15px; 75 | width: 100%; 76 | } 77 | .sunlight-line-highlight-overlay .sunlight-line-highlight-active { 78 | background-color: #E7FCFA; 79 | } 80 | 81 | /* menu */ 82 | .sunlight-menu { 83 | background-color: #FFFFCC; 84 | color: #000000; 85 | } 86 | .sunlight-menu ul { 87 | margin: 0 !important; 88 | padding: 0 !important; 89 | list-style-type: none !important; 90 | } 91 | .sunlight-menu li { 92 | float: right !important; 93 | margin-left: 5px !important; 94 | } 95 | .sunlight-menu a, .sunlight-menu img { 96 | color: #000099 !important; 97 | text-decoration: none !important; 98 | border: none !important; 99 | } 100 | 101 | 102 | 103 | 104 | .sunlight-string, 105 | .sunlight-char, 106 | .sunlight-heredoc, 107 | .sunlight-heredocDeclaration, 108 | .sunlight-nowdoc, 109 | .sunlight-longString, 110 | .sunlight-rawString, 111 | .sunlight-binaryString, 112 | .sunlight-rawLongString, 113 | .sunlight-binaryLongString, 114 | .sunlight-verbatimString, 115 | .sunlight-diff .sunlight-removed { 116 | color: #990000 !important; 117 | } 118 | 119 | .sunlight-ident, 120 | .sunlight-operator, 121 | .sunlight-punctuation, 122 | .sunlight-delimiter, 123 | .sunlight-diff .sunlight-unchanged { 124 | color: #000000 !important; 125 | } 126 | 127 | .sunlight-comment, 128 | .sunlight-xmlDocCommentContent, 129 | .sunlight-nginx .sunlight-ssiCommand, 130 | .sunlight-sln .sunlight-formatDeclaration, 131 | .sunlight-diff .sunlight-added { 132 | color: #009900 !important; 133 | } 134 | .sunlight-number, 135 | .sunlight-guid, 136 | .sunlight-cdata { 137 | color: #CC6600 !important; 138 | } 139 | 140 | .sunlight-named-ident, 141 | .sunlight-constant, 142 | .sunlight-javascript .sunlight-globalVariable, 143 | .sunlight-globalObject, 144 | .sunlight-python .sunlight-attribute, 145 | .sunlight-nginx .sunlight-context, 146 | .sunlight-httpd .sunlight-context, 147 | .sunlight-haskell .sunlight-class, 148 | .sunlight-haskell .sunlight-type, 149 | .sunlight-lisp .sunlight-declarationSpecifier, 150 | .sunlight-erlang .sunlight-userDefinedFunction, 151 | .sunlight-diff .sunlight-header { 152 | color: #2B91AF !important; 153 | } 154 | .sunlight-keyword, 155 | .sunlight-languageConstruct, 156 | .sunlight-css 157 | .sunlight-element, 158 | .sunlight-bash .sunlight-command, 159 | .sunlight-specialOperator, 160 | .sunlight-erlang .sunlight-moduleAttribute, 161 | .sunlight-xml .sunlight-tagName, 162 | .sunlight-xml .sunlight-operator, 163 | .sunlight-diff .sunlight-modified { 164 | color: #0000FF !important; 165 | } 166 | .sunlight-shortOpenTag, 167 | .sunlight-openTag, 168 | .sunlight-closeTag, 169 | .sunlight-xmlOpenTag, 170 | .sunlight-xmlCloseTag, 171 | .sunlight-aspOpenTag, 172 | .sunlight-aspCloseTag, 173 | .sunlight-label, 174 | .sunlight-css .sunlight-importantFlag { 175 | background-color: #FFFF99 !important; 176 | color: #000000 !important; 177 | } 178 | .sunlight-function, 179 | .sunlight-globalFunction, 180 | .sunlight-ruby .sunlight-specialFunction, 181 | .sunlight-objective-c .sunlight-messageDestination, 182 | .sunlight-6502asm .sunlight-illegalOpcode, 183 | .sunlight-powershell .sunlight-switch, 184 | .sunlight-lisp .sunlight-macro, 185 | .sunlight-lisp .sunlight-specialForm, 186 | .sunlight-lisp .sunlight-type, 187 | .sunlight-sln .sunlight-sectionName, 188 | .sunlight-diff .sunlight-rangeInfo { 189 | color: #B069AF !important; 190 | } 191 | 192 | .sunlight-variable, 193 | .sunlight-specialVariable, 194 | .sunlight-environmentVariable, 195 | .sunlight-objective-c .sunlight-messageArgumentName, 196 | .sunlight-lisp .sunlight-globalVariable, 197 | .sunlight-ruby .sunlight-globalVariable, 198 | .sunlight-ruby .sunlight-instanceVariable, 199 | .sunlight-sln .sunlight-operator { 200 | color: #325484 !important; 201 | } 202 | .sunlight-regexLiteral, 203 | .sunlight-lisp .sunlight-operator, 204 | .sunlight-6502asm .sunlight-pseudoOp, 205 | .sunlight-erlang .sunlight-macro { 206 | color: #FF00B2 !important; 207 | } 208 | .sunlight-specialVariable { 209 | font-style: italic !important; 210 | font-weight: bold !important; 211 | } 212 | .sunlight-csharp .sunlight-pragma, 213 | .sunlight-preprocessorDirective, 214 | .sunlight-vb .sunlight-compilerDirective, 215 | .sunlight-diff .sunlight-mergeHeader, 216 | .sunlight-diff .sunlight-noNewLine { 217 | color: #999999 !important; 218 | font-style: italic !important; 219 | } 220 | .sunlight-xmlDocCommentMeta, 221 | .sunlight-java .sunlight-annotation, 222 | .sunlight-scala .sunlight-annotation, 223 | .sunlight-docComment { 224 | color: #808080 !important; 225 | } 226 | .sunlight-quotedIdent, 227 | .sunlight-ruby .sunlight-subshellCommand, 228 | .sunlight-lisp .sunlight-keywordArgument, 229 | .sunlight-haskell .sunlight-infixOperator, 230 | .sunlight-erlang .sunlight-quotedAtom { 231 | color: #999900 !important; 232 | } 233 | 234 | 235 | 236 | /* xml */ 237 | .sunlight-xml .sunlight-string { 238 | color: #990099 !important; 239 | } 240 | .sunlight-xml .sunlight-attribute { 241 | color: #FF0000 !important; 242 | } 243 | .sunlight-xml .sunlight-entity { 244 | background-color: #EEEEEE !important; 245 | color: #000000 !important; 246 | border: 1px solid #000000 !important; 247 | } 248 | .sunlight-xml .sunlight-doctype { 249 | color: #2B91AF !important; 250 | } 251 | 252 | /* javascript */ 253 | .sunlight-javascript .sunlight-reservedWord { 254 | font-style: italic !important; 255 | } 256 | 257 | /* css */ 258 | .sunlight-css .sunlight-microsoftFilterPrefix { 259 | color: #FF00FF !important; 260 | } 261 | .sunlight-css .sunlight-rule { 262 | color: #0099FF !important; 263 | } 264 | .sunlight-css .sunlight-keyword { 265 | color: #4E65B8 !important; 266 | } 267 | .sunlight-css .sunlight-class { 268 | color: #FF0000 !important; 269 | } 270 | .sunlight-css .sunlight-id { 271 | color: #8A8E13 !important; 272 | } 273 | .sunlight-css .sunlight-pseudoClass, 274 | .sunlight-css .sunlight-pseudoElement { 275 | color: #368B87 !important; 276 | } 277 | 278 | /* bash */ 279 | .sunlight-bash .sunlight-hashBang { 280 | color: #3D97F5 !important; 281 | } 282 | .sunlight-bash .sunlight-verbatimCommand { 283 | color: #999900 !important; 284 | } 285 | .sunlight-bash .sunlight-variable, 286 | .sunlight-bash .sunlight-specialVariable { 287 | color: #FF0000 !important; 288 | } 289 | 290 | /* python */ 291 | .sunlight-python .sunlight-specialMethod { 292 | font-weight: bold !important; 293 | color: #A07DD3; 294 | } 295 | 296 | /* ruby */ 297 | .sunlight-ruby .sunlight-symbol { 298 | font-weight: bold !important; 299 | color: #ED7272 !important; 300 | } 301 | 302 | /* brainfuck */ 303 | .sunlight-brainfuck { 304 | font-weight: bold !important; 305 | color: #000000 !important; 306 | } 307 | .sunlight-brainfuck .sunlight-increment { 308 | background-color: #FF9900 !important; 309 | } 310 | .sunlight-brainfuck .sunlight-decrement { 311 | background-color: #FF99FF !important; 312 | } 313 | .sunlight-brainfuck .sunlight-incrementPointer { 314 | background-color: #FFFF99 !important; 315 | } 316 | .sunlight-brainfuck .sunlight-decrementPointer { 317 | background-color: #66CCFF !important; 318 | } 319 | .sunlight-brainfuck .sunlight-read { 320 | background-color: #FFFFFF !important; 321 | } 322 | .sunlight-brainfuck .sunlight-write { 323 | background-color: #99FF99 !important; 324 | } 325 | .sunlight-brainfuck .sunlight-openLoop, .sunlight-brainfuck .sunlight-closeLoop { 326 | background-color: #FFFFFF !important; 327 | } 328 | 329 | /* 6502 asm */ 330 | .sunlight-6502asm .sunlight-label { 331 | font-weight: bold !important; 332 | color: #000000 !important; 333 | background: none !important; 334 | } 335 | 336 | /* lisp */ 337 | .sunlight-lisp .sunlight-macro { 338 | font-style: italic !important; 339 | } 340 | 341 | /* erlang */ 342 | .sunlight-erlang .sunlight-atom { 343 | font-weight: bold !important; 344 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "phaser-raycaster", 3 | "version": "0.10.11", 4 | "description": "Raycasting plugin for Phaser 3.", 5 | "main": "dist/phaser-raycaster.min.js", 6 | "types": "types/types.d.ts", 7 | "scripts": { 8 | "dev": "webpack --mode development --config config/webpack.config.dev.js --watch", 9 | "build": "webpack --mode development --config config/webpack.config.dev.js && webpack --mode production --config config/webpack.config.js", 10 | "build-typings": "node config/build-typings.js", 11 | "build-docs": "node node_modules/jsdoc/jsdoc.js -c config/jsdoc.conf.json -R README.md" 12 | }, 13 | "browserslist": [ 14 | "last 1 version", 15 | "> 1%" 16 | ], 17 | "keywords": [ 18 | "phaser", 19 | "phaser3", 20 | "phaser-plugin", 21 | "plugin", 22 | "raycast" 23 | ], 24 | "author": "Marcin Walczak", 25 | "license": "ISC", 26 | "repository": { 27 | "type": "git", 28 | "url": "https://github.com/wiserim/phaser-raycaster.git" 29 | }, 30 | "devDependencies": { 31 | "@babel/core": "^7.26.0", 32 | "@babel/preset-env": "^7.26.0", 33 | "@babel/register": "^7.25.9", 34 | "babel-loader": "^9.2.1", 35 | "ink-docstrap": "^1.3.2", 36 | "jsdoc": "^3.6.11", 37 | "tsd-jsdoc": "^2.5.0", 38 | "webpack": "^5.96.1", 39 | "webpack-cli": "^5.1.4" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Marcin Walczak 3 | * @copyright 2023 Marcin Walczak 4 | * @license {@link https://github.com/wiserim/phaser-raycaster/blob/master/LICENSE|MIT License} 5 | */ 6 | 7 | /** 8 | * Point object 9 | * @typedef {Object} Point 10 | * @property {number} x 11 | * @property {number} y 12 | */ 13 | 14 | /** 15 | * @classdesc 16 | * 17 | * Raycaster plugin class. 18 | * 19 | * @namespace PhaserRaycaster 20 | * @class PhaserRaycaster 21 | * @extends Phaser.Plugins.ScenePlugin 22 | * @constructor 23 | * @since 0.6.0 24 | * 25 | * @param {Phaser.Scene} scene 26 | * @param {Phaser.Plugins.PluginManager} pluginManager 27 | */ 28 | 29 | class PhaserRaycaster extends Phaser.Plugins.ScenePlugin { 30 | constructor(scene, pluginManager) { 31 | super(scene, pluginManager); 32 | 33 | this._Raycaster = require('./raycaster-core.js').Raycaster; 34 | } 35 | 36 | /** 37 | * Create Raycaster object. 38 | * 39 | * @method PhaserRaycaster#createRaycaster 40 | * @memberof PhaserRaycaster 41 | * @instance 42 | * @since 0.6.0 43 | * 44 | * @param {object} [options] - Raycaster's congfiguration options. May include: 45 | * @param {number} [options.mapSegmentCount = 0] - Number of segments of circle maps. If set to 0, map will be teste 46 | * @param {(object|object[])} [options.objects] - Game object or array of game objects to map. 47 | * @param {Phaser.Geom.Rectangle} [options.boundingBox] - Raycaster's bounding box. If not passed, {@link Raycaster Raycaster} will set it's bounding box based on Arcade Physics / Matter physics world bounds. 48 | * @param {boolean} [options.autoUpdate = true] - If set true, automatically update dynamic maps on scene update event. 49 | * @param {boolean|object} [options.debug] - Enable debug mode or configure it {@link Raycaster#debugOptions debugOptions}. 50 | * 51 | * @return {Raycaster} {@link Raycaster Raycaster} instance 52 | */ 53 | createRaycaster(options = {}) { 54 | options.scene = this.scene; 55 | return new this._Raycaster(options); 56 | } 57 | } 58 | 59 | //Make sure you export the plugin for webpack to expose 60 | module.exports = PhaserRaycaster; -------------------------------------------------------------------------------- /src/map/boundingBox.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Get mapped object's bounding box. 3 | * 4 | * @method Raycaster.Map#matterBody.getBoundingBox 5 | * @memberof Raycaster.Map 6 | * @instance 7 | * @private 8 | * @since 0.9.0 9 | * 10 | * @return {Phaser.Geom.Rectangle} - Mapped object's bounding box. 11 | */ 12 | export function getBoundingBox() { 13 | return this.object.getBounds(); 14 | } 15 | -------------------------------------------------------------------------------- /src/map/config.js: -------------------------------------------------------------------------------- 1 | let rectangle = require('./map-rectangle-methods.js'); 2 | let line = require('./map-line-methods.js'); 3 | let polygon = require('./map-polygon-methods.js'); 4 | let arc = require('./map-circle-methods.js'); 5 | let container = require('./map-container-methods.js'); 6 | let tilemap = require('./map-tilemap-methods.js'); 7 | let matterBody = require('./map-matterBody-methods.js'); 8 | let segmentCount = require('./segmentsCount.js'); 9 | let boundingBox = require('./boundingBox.js'); 10 | 11 | /** 12 | * Configure map. 13 | * 14 | * @method Raycaster.Map#config 15 | * @memberof Raycaster.Map 16 | * @instance 17 | * @since 0.6.0 18 | * 19 | * @param {object} [options] - Map's congfiguration options. May include: 20 | * @param {object} options.object - Game object to map 21 | * @param {string} [options.type] - Map type. If not defined, it will be determined based on object. 22 | * @param {boolean} [options.dynamic = false] - If set true, map will be dynamic (updated on scene update event). 23 | * @param {boolean} [options.active = true] - If set true, map will be active (will provide points, segments and will be updated). 24 | * @param {number} [options.segmentCount] - Circle map's segment count. If set to 0, map won't be generating segments and relay only on tangent points calculated for currently testing ray. 25 | * @param {object} [options.mapChild] - Container's child. If set, only set child will be mapped. 26 | * @param {boolean} [options.forceConvex] - If set true, matter body map will use convex body (hull) for non-covex bodies. 27 | * @param {boolean} [options.forceVerticesMapping] - If set true, matter body map will use only vertices for mapping circle bodies. 28 | * 29 | * @return {Raycaster.Map} {@link Raycaster.Map Raycaster.Map} instance 30 | */ 31 | export function config(options) { 32 | this.object = options.object; 33 | //object type 34 | if(options.type === undefined) 35 | options.type = options.object.type; 36 | if(options.type === 'body' || options.type === 'composite') 37 | options.type = 'MatterBody'; 38 | this.type = options.type; 39 | 40 | switch(options.type) { 41 | case 'Polygon': 42 | this.getPoints = polygon.getPoints; 43 | this.getSegments = polygon.getSegments; 44 | this.getBoundingBox = boundingBox.getBoundingBox; 45 | this.updateMap = polygon.updateMap; 46 | break; 47 | case 'Arc': 48 | //circle segments count 49 | this.segmentCount = (options.segmentCount) ? options.segmentCount : 0; 50 | this.circle = (options.segmentCount) ? false : true; 51 | this.getPoints = arc.getPoints; 52 | this.getSegments = arc.getSegments; 53 | this.getBoundingBox = boundingBox.getBoundingBox; 54 | this.updateMap = arc.updateMap; 55 | this.setSegmentCount = segmentCount.setSegmentCount; 56 | break; 57 | case 'Line': 58 | this.getPoints = line.getPoints; 59 | this.getSegments = line.getSegments; 60 | this.getBoundingBox = boundingBox.getBoundingBox; 61 | this.updateMap = line.updateMap; 62 | break; 63 | case 'Container': 64 | //container's child 65 | this.mapChild = (options.mapChild) ? options.mapChild : null; 66 | //circle segments count 67 | this.segmentCount = (options.segmentCount) ? options.segmentCount : 0; 68 | //transformed container's circle children 69 | this._circles = []; 70 | this.getPoints = container.getPoints; 71 | this.getSegments = container.getSegments; 72 | this.getBoundingBox = boundingBox.getBoundingBox; 73 | this.updateMap = container.updateMap; 74 | this._updateChildMap = container._updateChildMap; 75 | this.setSegmentCount = segmentCount.setSegmentCount; 76 | break; 77 | case 'StaticTilemapLayer': 78 | //ray colliding tiles 79 | this.collisionTiles = (options.collisionTiles) ? options.collisionTiles : []; 80 | this.getPoints = tilemap.getPoints; 81 | this.getSegments = tilemap.getSegments; 82 | this.getBoundingBox = boundingBox.getBoundingBox; 83 | this.updateMap = tilemap.updateMap; 84 | this.setCollisionTiles = tilemap.setCollisionTiles; 85 | //reset tilemap origin 86 | this.object.setOrigin(0,0); 87 | break; 88 | case 'DynamicTilemapLayer': 89 | //ray colliding tiles 90 | this.collisionTiles = (options.collisionTiles) ? options.collisionTiles : []; 91 | this.getPoints = tilemap.getPoints; 92 | this.getSegments = tilemap.getSegments; 93 | this.getBoundingBox = boundingBox.getBoundingBox; 94 | this.updateMap = tilemap.updateMap; 95 | this.setCollisionTiles = tilemap.setCollisionTiles; 96 | //reset tilemap origin 97 | this.object.setOrigin(0,0); 98 | break; 99 | case 'TilemapLayer': 100 | //ray colliding tiles 101 | this.collisionTiles = (options.collisionTiles) ? options.collisionTiles : []; 102 | this.getPoints = tilemap.getPoints; 103 | this.getSegments = tilemap.getSegments; 104 | this.getBoundingBox = boundingBox.getBoundingBox; 105 | this.updateMap = tilemap.updateMap; 106 | this.setCollisionTiles = tilemap.setCollisionTiles; 107 | //reset tilemap origin 108 | this.object.setOrigin(0,0); 109 | break; 110 | case 'MatterBody': 111 | //force convex body (hull) mapping 112 | this.forceConvex = (options.forceConvex) ? true : false; 113 | //force mapping by vertices 114 | this.forceVerticesMapping = (options.forceVerticesMapping) ? true : false; 115 | this.circle = false; 116 | this.getPoints = matterBody.getPoints; 117 | this.getSegments = matterBody.getSegments; 118 | this.getBoundingBox = matterBody.getBoundingBox; 119 | this.updateMap = matterBody.updateMap; 120 | break; 121 | default: 122 | this.getPoints = rectangle.getPoints; 123 | this.getSegments = rectangle.getSegments; 124 | this.getBoundingBox = boundingBox.getBoundingBox; 125 | this.updateMap = rectangle.updateMap; 126 | } 127 | 128 | //if object is not supported 129 | if(this.type != 'MatterBody' && typeof this.object.getBounds !== 'function') { 130 | this.notSupported = true; 131 | } 132 | 133 | //dynamic map 134 | this.dynamic = (options.dynamic == true) ? true : false; 135 | 136 | //enable/disable map 137 | this.active = (options.active !== undefined) ? options.active : true; 138 | 139 | return this; 140 | } 141 | -------------------------------------------------------------------------------- /src/map/destroy.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Destroy object 3 | * 4 | * @method Raycaster.Map#destroy 5 | * @memberof Raycaster.Map 6 | * @instance 7 | * @since 0.10.3 8 | */ 9 | export function destroy() { 10 | //destroy reference to map object in mapped object 11 | if(this.object.type === 'body' || this.object.type === 'composite') { 12 | delete this.object.raycasterMap; 13 | } 14 | else if(this.object.data) { 15 | this.object.data.remove('raycasterMap'); 16 | } 17 | 18 | for(let key in this) { 19 | delete this[key]; 20 | } 21 | } -------------------------------------------------------------------------------- /src/map/map-circle-methods.js: -------------------------------------------------------------------------------- 1 | /*Map methods for circles*/ 2 | /** 3 | * Get array of mapped circle's vertices used as rays targets. 4 | * If {@link Raycaster.Map#segmentCount Raycaster.Map#segmentCount} is set to 0, it'll calculatoe tangent points for passed ray. 5 | * 6 | * @method Raycaster.Map#arc.getPoints 7 | * @memberof Raycaster.Map 8 | * @instance 9 | * @private 10 | * @since 0.6.0 11 | * 12 | * @param {Raycaster.Ray} [ray] - {Raycaster.Ray} object used in some some types of maps. 13 | * 14 | * @return {Phaser.Geom.Point[]} - Array of mapped object's vertices. 15 | */ 16 | export function getPoints(ray = false) { 17 | if(!this.active) 18 | return []; 19 | 20 | if(this._points.length > 0) 21 | return this._points; 22 | 23 | let points = []; 24 | let offset = new Phaser.Geom.Point(); 25 | offset.x = this.object.x - this.object.displayWidth * (this.object.originX - 0.5); 26 | offset.y = this.object.y - this.object.displayHeight * (this.object.originY - 0.5); 27 | 28 | //calculate tangent rays 29 | if(ray) { 30 | let rayA = new Phaser.Geom.Line(); 31 | let rayB = new Phaser.Geom.Line(); 32 | let c; 33 | 34 | let rotation = this.object.rotation; 35 | 36 | if(rotation !== 0) { 37 | let vector = new Phaser.Geom.Line(this.object.x, this.object.y, offset.x, offset.y); 38 | Phaser.Geom.Line.SetToAngle(vector, this.object.x, this.object.y, Phaser.Geom.Line.Angle(vector) + rotation, Phaser.Geom.Line.Length(vector)); 39 | let cB = vector.getPointB(); 40 | c = new Phaser.Geom.Line(ray.origin.x, ray.origin.y, cB.x, cB.y); 41 | } 42 | else { 43 | c = new Phaser.Geom.Line(ray.origin.x, ray.origin.y, offset.x, offset.y); 44 | } 45 | 46 | let rayLength = Math.sqrt(Math.pow(Phaser.Geom.Line.Length(c), 2) - Math.pow(this.object.radius * this.object.scaleX, 2)); 47 | 48 | //ray angle 49 | let angle = Phaser.Geom.Line.Angle(c); 50 | let dAngle = Math.asin((this.object.radius * this.object.scaleX) / Phaser.Geom.Line.Length(c)); 51 | Phaser.Geom.Line.SetToAngle(rayA, ray.origin.x, ray.origin.y, angle - dAngle, rayLength); 52 | Phaser.Geom.Line.SetToAngle(rayB, ray.origin.x, ray.origin.y, angle + dAngle, rayLength); 53 | 54 | //add tangent points 55 | points.push(rayA.getPointB()); 56 | points.push(rayB.getPointB()); 57 | //assign neighbours 58 | points[0].neighbours = [points[1]]; 59 | points[1].neighbours = [points[0]]; 60 | } 61 | 62 | return points; 63 | }; 64 | 65 | /** 66 | * Get array of mapped circle's segments used to test object's intersection with ray. 67 | * If {@link Raycaster.Map#segmentCount Raycaster.Map#segmentCount} is set to 0, it'll return empty array. 68 | * 69 | * @method Raycaster.Map#arc.getSegments 70 | * @memberof Raycaster.Map 71 | * @instance 72 | * @private 73 | * @since 0.6.0 74 | * 75 | * @return {Phaser.Geom.Line[]} - Array of mapped object's segments. 76 | */ 77 | export function getSegments() { 78 | if(!this.active) 79 | return []; 80 | return this._segments; 81 | }; 82 | 83 | /** 84 | * Update circles's map of points and segments. 85 | * 86 | * @method Raycaster.Map#arc.updateMap 87 | * @memberof Raycaster.Map 88 | * @instance 89 | * @private 90 | * @since 0.6.0 91 | * 92 | * @return {Raycaster.Map} {@link Raycaster.Map Raycaster.Map} instance 93 | */ 94 | export function updateMap() { 95 | if(!this.active) 96 | return this; 97 | 98 | if(!this.segmentCount) { 99 | this._points = []; 100 | this._segments = []; 101 | return this; 102 | } 103 | 104 | //calculate offset based on object position and origin point 105 | let offset = new Phaser.Geom.Point(); 106 | offset.x = this.object.x - this.object.displayWidth * this.object.originX + this.object.radius * this.object.scaleX; 107 | offset.y = this.object.y - this.object.displayHeight * this.object.originY + this.object.radius * this.object.scaleY; 108 | 109 | //get points surrounding circle 110 | let points = this.object.geom.getPoints(this.segmentCount); 111 | let segments = [] 112 | 113 | //set points 114 | //calculate positions after object's rotation 115 | let rotation = this.object.rotation; 116 | if(rotation !== 0) { 117 | let newPoints = []; 118 | for(let point of points) { 119 | let vector = new Phaser.Geom.Line(this.object.x, this.object.y, this.object.x + (point.x + this.object.radius) * this.object.scaleX, this.object.y + (point.y + this.object.radius) * this.object.scaleY); 120 | Phaser.Geom.Line.SetToAngle(vector, this.object.x, this.object.y, Phaser.Geom.Line.Angle(vector) + rotation, Phaser.Geom.Line.Length(vector)); 121 | newPoints.push(vector.getPointB()); 122 | } 123 | points = newPoints; 124 | } 125 | //if rotation === 0 126 | else { 127 | for(let point of points) { 128 | point.x = point.x * this.object.scaleX + offset.x; 129 | point.y = point.y * this.object.scaleY + offset.y; 130 | } 131 | } 132 | 133 | //set segments 134 | for(let i = 0, length = points.length; i < length; i++) { 135 | let prevPoint = i > 0 ? points[i - 1] : points.slice(-1)[0], 136 | nextPoint = i < length - 1 ? points[i + 1] : points[0]; 137 | 138 | segments.push(new Phaser.Geom.Line(points[i].x, points[i].y, nextPoint.x, nextPoint.y)); 139 | 140 | points[i].neighbours = [ 141 | prevPoint, 142 | nextPoint 143 | ]; 144 | } 145 | 146 | this._points = points; 147 | this._segments = segments; 148 | return this; 149 | }; 150 | -------------------------------------------------------------------------------- /src/map/map-core.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @classdesc 3 | * 4 | * Map class responsible for mapping game objects. 5 | * 6 | * @namespace Raycaster.Map 7 | * @class Raycaster.Map 8 | * @constructor 9 | * @since 0.6.0 10 | * 11 | * @param {object} options - Map specific configuration settings. 12 | * @param {Raycaster} [raycaster] - Parent raycaster object. 13 | */ 14 | export function Map(options, raycaster) { 15 | /** 16 | * Reference to parent Raycaster object. 17 | * 18 | * @name Raycaster.Map#_raycaster 19 | * @type {Raycaster} 20 | * @private 21 | * @since 0.9.0 22 | */ 23 | this._raycaster = raycaster ? raycaster : false; 24 | /** 25 | * Mapped object's type 26 | * 27 | * @name Raycaster.Map#type 28 | * @type {string} 29 | * @readonly 30 | * @since 0.6.0 31 | */ 32 | this.type; 33 | /** 34 | * If set true, map will be tested by ray. Otherwise it will be ignored. 35 | * 36 | * @name Raycaster.Map#active 37 | * @type {boolean} 38 | * @default true 39 | * @since 0.7.2 40 | */ 41 | this.active; 42 | /** 43 | * If set true, map will be automatically updated on scene update event. 44 | * 45 | * @name Raycaster.Map_dynamic 46 | * @type {boolean} 47 | * @default false 48 | * @since 0.6.0 49 | */ 50 | this._dynamic = false; 51 | /** 52 | * If set true, map will be treated by ray as circle. Set automaticalyy on map update. 53 | * 54 | * @name Raycaster.Map#circle 55 | * @type {boolean} 56 | * @default false 57 | * @since 0.9.0 58 | */ 59 | this.circle = false; 60 | /** 61 | * Reference to mapped object. 62 | * 63 | * @name Raycaster.Map#object 64 | * @type {object} 65 | * @readonly 66 | * @since 0.6.0 67 | */ 68 | this.object; 69 | /** 70 | * Array of mapped object's vertices used as rays targets. 71 | * 72 | * @name Raycaster.Map#_points 73 | * @type {array} 74 | * @private 75 | * @since 0.6.0 76 | */ 77 | this._points = []; 78 | /** 79 | * Array of mapped object's segments used to test object's intersection with ray. 80 | * 81 | * @name Raycaster.Map#_segments 82 | * @type {array} 83 | * @private 84 | * @since 0.6.0 85 | */ 86 | this._segments = []; 87 | /** 88 | * Get array of mapped object's vertices used as rays targets. 89 | * 90 | * @method Raycaster.Map#getPoints 91 | * @memberof Raycaster.Map 92 | * @instance 93 | * @since 0.6.0 94 | * 95 | * @param {Raycaster.Ray} [ray] - {@link Raycaster.Ray Raycaster.Ray} object used in some some types of maps. 96 | * 97 | * @return {Phaser.Geom.Point[]} Array of mapped object's vertices. 98 | */ 99 | this.getPoints; 100 | /** 101 | * Get array of mapped object's segments used to test object's intersection with ray. 102 | * 103 | * @method Raycaster.Map#getSegments 104 | * @memberof Raycaster.Map 105 | * @instance 106 | * @since 0.6.0 107 | * 108 | * @param {Raycaster.Ray} [ray] - {@link Raycaster.Ray Raycaster.Ray} object used in some some types of maps. 109 | * 110 | * @return {Phaser.Geom.Line[]} Array of mapped object's segments. 111 | */ 112 | this.getSegments; 113 | /** 114 | * Get mapped object's bounding box. 115 | * 116 | * @method Raycaster.Map#getBoundingBox 117 | * @memberof Raycaster.Map 118 | * @instance 119 | * @since 0.9.0 120 | * 121 | * @return {Phaser.Geom.Rectangle} Mapped object's bounding box. 122 | */ 123 | this.getBoundingBox; 124 | /** 125 | * Update object's map of points and segments. 126 | * 127 | * @method Raycaster.Map#updateMap 128 | * @memberof Raycaster.Map 129 | * @instance 130 | * @since 0.6.0 131 | * 132 | * @return {Raycaster.Map} {@link Raycaster.Map Raycaster.Map} instance 133 | */ 134 | this.updateMap; 135 | 136 | this.config(options); 137 | if(!this.notSupported) 138 | this.updateMap(); 139 | 140 | return this; 141 | }; 142 | 143 | Map.prototype = { 144 | config: require('./config.js').config, 145 | destroy: require('./destroy.js').destroy, 146 | get dynamic() { 147 | return this._dynamic; 148 | }, 149 | set dynamic(dynamic) { 150 | if(this._dynamic == dynamic) 151 | return this; 152 | 153 | if(dynamic) { 154 | this._dynamic = true; 155 | 156 | //add object to raycaster's dynamic objects list 157 | if(this._raycaster) { 158 | this._raycaster.dynamicMappedObjects.push(this.object); 159 | 160 | this._raycaster._stats.mappedObjects.dynamic = this._raycaster.dynamicMappedObjects.length; 161 | this._raycaster._stats.mappedObjects.static = this._raycaster._stats.mappedObjects.total - this._raycaster._stats.mappedObjects.dynamic; 162 | } 163 | } 164 | else { 165 | this._dynamic = false; 166 | 167 | //remove object from reycasters' dynamic objects list 168 | if(this._raycaster) { 169 | let index = this._raycaster.dynamicMappedObjects.indexOf(this.object); 170 | if(index >= 0) 171 | this._raycaster.dynamicMappedObjects.splice(index, 1); 172 | 173 | this._raycaster._stats.mappedObjects.dynamic = this._raycaster.dynamicMappedObjects.length; 174 | this._raycaster._stats.mappedObjects.static = this._raycaster._stats.mappedObjects.total - this._raycaster._stats.mappedObjects.dynamic; 175 | } 176 | } 177 | 178 | return this; 179 | } 180 | }; 181 | 182 | Map.prototype.constructor = Map; 183 | -------------------------------------------------------------------------------- /src/map/map-line-methods.js: -------------------------------------------------------------------------------- 1 | /*Map methods for lines*/ 2 | /** 3 | * Get array of mapped line's vertices used as rays targets. 4 | * 5 | * @method Raycaster.Map#line.getPoints 6 | * @memberof Raycaster.Map 7 | * @instance 8 | * @private 9 | * @since 0.6.0 10 | * 11 | * @param {Raycaster.Ray} [ray] - {Raycaster.Ray} object used in some some types of maps. 12 | * 13 | * @return {Phaser.Geom.Point[]} - Array of mapped object's vertices. 14 | */ 15 | export function getPoints(ray = false) { 16 | if(!this.active) 17 | return []; 18 | return this._points; 19 | }; 20 | 21 | /** 22 | * Get array of mapped line's segments used to test object's intersection with ray. 23 | * 24 | * @method Raycaster.Map#line.getSegments 25 | * @memberof Raycaster.Map 26 | * @instance 27 | * @private 28 | * @since 0.6.0 29 | * 30 | * @return {Phaser.Geom.Line[]} - Array of mapped object's segments. 31 | */ 32 | export function getSegments() { 33 | if(!this.active) 34 | return []; 35 | return this._segments; 36 | }; 37 | 38 | /** 39 | * Update line's map of points and segments. 40 | * 41 | * @method Raycaster.Map#line.updateMap 42 | * @memberof Raycaster.Map 43 | * @instance 44 | * @private 45 | * @since 0.6.0 46 | * 47 | * @return {Raycaster.Map} {@link Raycaster.Map Raycaster.Map} instance 48 | */ 49 | export function updateMap() { 50 | if(!this.active) 51 | return this; 52 | 53 | let points = []; 54 | let segments = []; 55 | 56 | //calculate offset based on object position and origin point 57 | let offset = new Phaser.Geom.Point(); 58 | offset.x = this.object.x - this.object.displayWidth * this.object.originX; 59 | offset.y = this.object.y - this.object.displayHeight * this.object.originY; 60 | let pointA = this.object.geom.getPointA(); 61 | let pointB = this.object.geom.getPointB(); 62 | 63 | //calculate positions after object's rotation 64 | let rotation = this.object.rotation; 65 | if(rotation !== 0) { 66 | let vectorA = new Phaser.Geom.Line(this.object.x, this.object.y, pointA.x * this.object.scaleX + offset.x, pointA.y * this.object.scaleY + offset.y); 67 | Phaser.Geom.Line.SetToAngle(vectorA, this.object.x, this.object.y, Phaser.Geom.Line.Angle(vectorA) + rotation, Phaser.Geom.Line.Length(vectorA)); 68 | pointA = vectorA.getPointB(); 69 | 70 | let vectorB = new Phaser.Geom.Line(this.object.x, this.object.y, pointB.x * this.object.scaleX + offset.x, pointB.y * this.object.scaleY + offset.y); 71 | Phaser.Geom.Line.SetToAngle(vectorB, this.object.x, this.object.y, Phaser.Geom.Line.Angle(vectorB) + rotation, Phaser.Geom.Line.Length(vectorB)); 72 | pointB = vectorB.getPointB(); 73 | 74 | //set points 75 | points.push(new Phaser.Geom.Point(pointA.x, pointA.y)); 76 | points.push(new Phaser.Geom.Point(pointB.x, pointB.y)); 77 | //set segment 78 | segments.push(new Phaser.Geom.Line(pointA.x, pointA.y, pointB.x, pointB.y)); 79 | } 80 | //if rotation === 0 81 | else { 82 | //set points 83 | points.push(new Phaser.Geom.Point(pointA.x * this.object.scaleX + offset.x, pointA.y * this.object.scaleY + offset.y)); 84 | points.push(new Phaser.Geom.Point(pointB.x * this.object.scaleX + offset.x, pointB.y * this.object.scaleY + offset.y)); 85 | //set segment 86 | segments.push(new Phaser.Geom.Line(pointA.x * this.object.scaleX + offset.x, pointA.y * this.object.scaleY + offset.y, pointB.x + offset.x * this.object.scaleX, pointB.y * this.object.scaleY + offset.y)); 87 | } 88 | 89 | //assign neighbours 90 | points[0].neighbours = [points[1]]; 91 | points[1].neighbours = [points[0]]; 92 | 93 | this._points = points; 94 | this._segments = segments; 95 | return this; 96 | }; 97 | -------------------------------------------------------------------------------- /src/map/map-matterBody-methods.js: -------------------------------------------------------------------------------- 1 | /*Map methods for matter body*/ 2 | /** 3 | * Get array of mapped matter body's vertices used as rays targets. 4 | * 5 | * @method Raycaster.Map#matterBody.getPoints 6 | * @memberof Raycaster.Map 7 | * @instance 8 | * @private 9 | * @since 0.9.0 10 | * 11 | * @param {Raycaster.Ray} [ray] - {Raycaster.Ray} object used in some some types of maps. 12 | * 13 | * @return {Phaser.Geom.Point[]} - Array of mapped object's vertices. 14 | */ 15 | export function getPoints(ray = false) { 16 | if(!this.active) 17 | return []; 18 | 19 | let body = this.object.type === 'body' || this.object.type === 'composite' ? this.object : this.object.body; 20 | 21 | //calculate tangent rays 22 | if(ray && !this.forceVerticesMapping && body.circleRadius > 0) { 23 | let points = []; 24 | let rayA = new Phaser.Geom.Line(); 25 | let rayB = new Phaser.Geom.Line(); 26 | let c = new Phaser.Geom.Line(ray.origin.x, ray.origin.y, body.position.x, body.position.y); 27 | 28 | let rayLength = Math.sqrt(Math.pow(Phaser.Geom.Line.Length(c), 2) - Math.pow(body.circleRadius * body.scale.x, 2)); 29 | 30 | //ray angle 31 | let angle = Phaser.Geom.Line.Angle(c); 32 | let dAngle = Math.asin((body.circleRadius * body.scale.x) / Phaser.Geom.Line.Length(c)); 33 | Phaser.Geom.Line.SetToAngle(rayA, ray.origin.x, ray.origin.y, angle - dAngle, rayLength); 34 | Phaser.Geom.Line.SetToAngle(rayB, ray.origin.x, ray.origin.y, angle + dAngle, rayLength); 35 | 36 | //adding tangent points 37 | points.push(rayA.getPointB(), rayB.getPointB()); 38 | 39 | return points; 40 | } 41 | 42 | return this._points; 43 | }; 44 | 45 | /** 46 | * Get array of mapped matter body's segments used to test object's intersection with ray. 47 | * 48 | * @method Raycaster.Map#matterBody.getSegments 49 | * @memberof Raycaster.Map 50 | * @instance 51 | * @private 52 | * @since 0.9.0 53 | * 54 | * @return {Phaser.Geom.Line[]} - Array of mapped object's segments. 55 | */ 56 | export function getSegments() { 57 | if(!this.active) 58 | return []; 59 | return this._segments; 60 | }; 61 | 62 | /** 63 | * Update matter body's map of points and segments. 64 | * 65 | * @method Raycaster.Map#matterBody.updateMap 66 | * @memberof Raycaster.Map 67 | * @instance 68 | * @private 69 | * @since 0.9.0 70 | * 71 | * @return {Raycaster.Map} {@link Raycaster.Map Raycaster.Map} instance 72 | */ 73 | export function updateMap() { 74 | if(!this.active) 75 | return this; 76 | 77 | let points = []; 78 | let segments = []; 79 | let body = this.object.type === 'body' || this.object.type === 'composite' ? this.object : this.object.body; 80 | let bodies = [body]; 81 | let generateBounds = false; 82 | 83 | if(body.circleRadius > 0 && !this.forceVerticesMapping) { 84 | this.circle = true; 85 | this._points = points; 86 | this._segments = segments; 87 | 88 | return this; 89 | } 90 | 91 | this.circle = false; 92 | 93 | if(body.type == 'composite') 94 | bodies = body.bodies; 95 | 96 | if( ( body.bounds === undefined && body.type == 'composite' ) || ( body.type == 'composite' && this.dynamic ) ) { 97 | generateBounds = true; 98 | } 99 | 100 | for(let bodyItem of bodies) { 101 | //if convex body 102 | if(bodyItem.parts.length === 1 || this.forceConvex) { 103 | let vertices = bodyItem.parts[0].vertices; 104 | 105 | points.push(new Phaser.Geom.Point(vertices[0].x, vertices[0].y)); 106 | points[0].neighbours = []; 107 | 108 | for(let i = 1, length = vertices.length; i < length; i++) { 109 | let pointA = points.slice(-1)[0], 110 | pointB = new Phaser.Geom.Point(vertices[i].x, vertices[i].y); 111 | 112 | if(!pointA.neighbours) 113 | pointA.neighbours = []; 114 | pointA.neighbours.push(pointB); 115 | pointB.neighbours = [pointA]; 116 | 117 | points.push(pointB); 118 | 119 | //add segment 120 | let segment = new Phaser.Geom.Line(pointA.x, pointA.y, pointB.x, pointB.y); 121 | segments.push(segment); 122 | } 123 | 124 | //closing segment 125 | let segment = new Phaser.Geom.Line(vertices[vertices.length - 1].x, vertices[vertices.length - 1].y, vertices[0].x, vertices[0].y); 126 | segments.push(segment); 127 | 128 | points[0].neighbours.push(points.slice(-1)[0]); 129 | } 130 | 131 | //if concave body 132 | else { 133 | let parts = [], 134 | indexedPoints = []; 135 | 136 | for(let i = 1, iLength = bodyItem.parts.length; i < iLength; i++) { 137 | let vertices = bodyItem.parts[i].vertices, 138 | part = []; 139 | 140 | for(let j = 0, jLength = vertices.length; j < jLength; j++) { 141 | let point = new Phaser.Geom.Point(vertices[j].x, vertices[j].y); 142 | 143 | if(part.length) { 144 | let prevPoint = part.slice(-1)[0]; 145 | point.neighbours = [prevPoint]; 146 | prevPoint.neighbours.push(point); 147 | } 148 | else { 149 | point.neighbours = []; 150 | } 151 | 152 | let index = vertices[j].x + '/' + vertices[j].y; 153 | if(indexedPoints[index] === undefined) { 154 | points.push(point); 155 | indexedPoints[index] = point; 156 | } 157 | else { 158 | indexedPoints[index].neighbours.push(point); 159 | point.neighbours.push(indexedPoints[index]); 160 | } 161 | 162 | part.push(point); 163 | 164 | if(vertices[j].isInternal) { 165 | parts.push(part); 166 | part = []; 167 | } 168 | } 169 | parts.push(part); 170 | } 171 | 172 | for(let part of parts) { 173 | let i = 0, 174 | iLength; 175 | for(i = 0, iLength = part.length - 1; i < iLength; i++) { 176 | segments.push(new Phaser.Geom.Line(part[i].x, part[i].y, part[i+1].x, part[i+1].y)); 177 | } 178 | } 179 | } 180 | } 181 | 182 | this._points = points; 183 | this._segments = segments; 184 | 185 | if(generateBounds) { 186 | let bounds = this._raycaster.scene.matter.composite.bounds(body); 187 | body.bounds = bounds; 188 | } 189 | 190 | return this; 191 | }; 192 | 193 | /** 194 | * Get matter body's bounding box. 195 | * 196 | * @method Raycaster.Map#matterBody.getBoundingBox 197 | * @memberof Raycaster.Map 198 | * @instance 199 | * @private 200 | * @since 0.9.0 201 | * 202 | * @return {Phaser.Geom.Rectangle} - Matter body's bounding box. 203 | */ 204 | export function getBoundingBox() { 205 | let bounds = this.object.type === 'body' || this.object.type === 'composite' ? this.object.bounds : this.object.body.bounds; 206 | 207 | return new Phaser.Geom.Rectangle(bounds.min.x, bounds.min.y, bounds.max.x - bounds.min.x, bounds.max.y - bounds.min.y); 208 | } 209 | 210 | -------------------------------------------------------------------------------- /src/map/map-polygon-methods.js: -------------------------------------------------------------------------------- 1 | /*Map methods for polygons*/ 2 | /** 3 | * Get array of mapped polygon's vertices used as rays targets. 4 | * 5 | * @method Raycaster.Map#polygon.getPoints 6 | * @memberof Raycaster.Map 7 | * @instance 8 | * @private 9 | * @since 0.6.0 10 | * 11 | * @param {Raycaster.Ray} [ray] - {Raycaster.Ray} object used in some some types of maps. 12 | * 13 | * @return {Phaser.Geom.Point[]} - Array of mapped object's vertices. 14 | */ 15 | export function getPoints(ray = false) { 16 | if(!this.active) 17 | return []; 18 | return this._points; 19 | }; 20 | 21 | /** 22 | * Get array of mapped polygon's segments used to test object's intersection with ray. 23 | * 24 | * @method Raycaster.Map#polygon.getSegments 25 | * @memberof Raycaster.Map 26 | * @instance 27 | * @private 28 | * @since 0.6.0 29 | * 30 | * @return {Phaser.Geom.Line[]} - Array of mapped object's segments. 31 | */ 32 | export function getSegments() { 33 | if(!this.active) 34 | return []; 35 | return this._segments; 36 | }; 37 | 38 | /** 39 | * Update polygon's map of points and segments. 40 | * 41 | * @method Raycaster.Map#polygon.updateMap 42 | * @memberof Raycaster.Map 43 | * @instance 44 | * @private 45 | * @since 0.6.0 46 | * 47 | * @return {Raycaster.Map} {@link Raycaster.Map Raycaster.Map} instance 48 | */ 49 | export function updateMap() { 50 | if(!this.active) 51 | return this; 52 | 53 | let points = []; 54 | let segments = []; 55 | 56 | //calculate offset based on object position and origin point 57 | let offset = new Phaser.Geom.Point(); 58 | offset.x = this.object.x - this.object.displayWidth * this.object.originX; 59 | offset.y = this.object.y - this.object.displayHeight * this.object.originY; 60 | //set points 61 | //calculate positions after object's rotation 62 | let rotation = this.object.rotation; 63 | if(rotation !== 0) { 64 | for(let point of this.object.geom.points) { 65 | let vector = new Phaser.Geom.Line(this.object.x, this.object.y, point.x * this.object.scaleX + offset.x, point.y * this.object.scaleY + offset.y); 66 | Phaser.Geom.Line.SetToAngle(vector, this.object.x, this.object.y, Phaser.Geom.Line.Angle(vector) + rotation, Phaser.Geom.Line.Length(vector)); 67 | points.push(vector.getPointB()); 68 | } 69 | } 70 | //if rotation === 0 71 | else { 72 | for(let point of this.object.geom.points) { 73 | points.push(new Phaser.Geom.Point(point.x * this.object.scaleX + offset.x, point.y * this.object.scaleY + offset.y)); 74 | } 75 | } 76 | 77 | for(let i = 0, length = points.length; i < length; i++) { 78 | let prevPoint = i > 0 ? points[i - 1] : points.slice(-1)[0], 79 | nextPoint = i < length - 1 ? points[i + 1] : points[0]; 80 | 81 | segments.push(new Phaser.Geom.Line(points[i].x, points[i].y, nextPoint.x, nextPoint.y)); 82 | 83 | points[i].neighbours = [ 84 | prevPoint, 85 | nextPoint 86 | ]; 87 | } 88 | 89 | //set segments 90 | for(let i = 0, length = points.length; i < length; i++) { 91 | if(i+1 < length) 92 | segments.push(new Phaser.Geom.Line(points[i].x, points[i].y, points[i+1].x, points[i+1].y)); 93 | } 94 | //if polygon is not closed 95 | if(!this.object.closePath) { 96 | segments.pop(); 97 | points[0].neighbours.shift(); 98 | points[points.length - 1].neighbours.pop(); 99 | } 100 | 101 | this._points = points; 102 | this._segments = segments; 103 | 104 | return this; 105 | }; 106 | -------------------------------------------------------------------------------- /src/map/map-rectangle-methods.js: -------------------------------------------------------------------------------- 1 | /*Map methods for rectangles*/ 2 | /** 3 | * Get array of mapped rectangle's vertices used as rays targets. 4 | * 5 | * @method Raycaster.Map#rectangle.getPoints 6 | * @memberof Raycaster.Map 7 | * @instance 8 | * @private 9 | * @since 0.6.0 10 | * 11 | * @param {Raycaster.Ray} [ray] - {Raycaster.Ray} object used in some some types of maps. 12 | * 13 | * @return {Phaser.Geom.Point[]} - Array of mapped object's vertices. 14 | */ 15 | export function getPoints(ray = false) { 16 | if(!this.active) 17 | return []; 18 | return this._points; 19 | }; 20 | 21 | /** 22 | * Get array of mapped rectangle's segments used to test object's intersection with ray. 23 | * 24 | * @method Raycaster.Map#rectangle.getSegments 25 | * @memberof Raycaster.Map 26 | * @instance 27 | * @private 28 | * @since 0.6.0 29 | * 30 | * @return {Phaser.Geom.Line[]} - Array of mapped object's segments. 31 | */ 32 | export function getSegments() { 33 | if(!this.active) 34 | return []; 35 | return this._segments; 36 | }; 37 | 38 | /** 39 | * Update rectangle's map of points and segments. 40 | * 41 | * @method Raycaster.Map#rectangle.updateMap 42 | * @memberof Raycaster.Map 43 | * @instance 44 | * @private 45 | * @since 0.6.0 46 | * 47 | * @return {Raycaster.Map} {@link Raycaster.Map Raycaster.Map} instance 48 | */ 49 | export function updateMap() { 50 | if(!this.active) 51 | return this; 52 | 53 | let points = []; 54 | let segments = []; 55 | 56 | //set points 57 | points = [ 58 | this.object.getTopLeft(), 59 | this.object.getTopRight(), 60 | this.object.getBottomRight(), 61 | this.object.getBottomLeft() 62 | ]; 63 | 64 | //set segments 65 | for(let i = 0, length = points.length; i < length; i++) { 66 | let prevPoint = i > 0 ? points[i - 1] : points.slice(-1)[0], 67 | nextPoint = i < length - 1 ? points[i + 1] : points[0]; 68 | 69 | segments.push(new Phaser.Geom.Line(points[i].x, points[i].y, nextPoint.x, nextPoint.y)); 70 | 71 | points[i].neighbours = [ 72 | prevPoint, 73 | nextPoint 74 | ]; 75 | } 76 | 77 | this._points = points; 78 | this._segments = segments; 79 | 80 | return this; 81 | }; 82 | -------------------------------------------------------------------------------- /src/map/map-tilemap-methods.js: -------------------------------------------------------------------------------- 1 | /*Map methods for tilemaps*/ 2 | /** 3 | * Get array of mapped tilemap's vertices used as rays targets. 4 | * 5 | * @method Raycaster.Map#tilemap.getPoints 6 | * @memberof Raycaster.Map 7 | * @instance 8 | * @private 9 | * @since 0.7.3 10 | * 11 | * @param {Raycaster.Ray} [ray] - {Raycaster.Ray} object used in some some types of maps. 12 | * 13 | * @return {Phaser.Geom.Point[]} - Array of mapped object's vertices. 14 | */ 15 | export function getPoints(ray = false) { 16 | if(!this.active) 17 | return []; 18 | if(!ray || ray && (ray.detectionRange == 0 || ray.detectionRange >= Phaser.Math.MAX_SAFE_INTEGER)) 19 | return this._points; 20 | 21 | let points = []; 22 | for(let point of this._points) { 23 | if(Phaser.Math.Distance.Between(ray.origin.x, ray.origin.y, point.x, point.y) <= ray.detectionRange) 24 | points.push(point); 25 | } 26 | 27 | //get intersections between tilemap's segments and ray's detection range edge 28 | let segments = this.getSegments(ray); 29 | 30 | for(let segment of segments) { 31 | if(Phaser.Math.Distance.Between(ray.origin.x, ray.origin.y, segment.x1, segment.y1) > ray.detectionRange) 32 | points.push(new Phaser.Geom.Point(segment.x1, segment.y1)); 33 | 34 | if(Phaser.Math.Distance.Between(ray.origin.x, ray.origin.y, segment.x2, segment.y2) > ray.detectionRange) 35 | points.push(new Phaser.Geom.Point(segment.x2, segment.y2)); 36 | } 37 | 38 | return points; 39 | }; 40 | 41 | /** 42 | * Get array of mapped tilemap's segments used to test object's intersection with ray. 43 | * 44 | * @method Raycaster.Map#tilemap.getSegments 45 | * @memberof Raycaster.Map 46 | * @instance 47 | * @private 48 | * @since 0.7.3 49 | * 50 | * @param {Raycaster.Ray} [ray] - {Raycaster.Ray} object used in some some types of maps. 51 | * 52 | * @return {Phaser.Geom.Line[]} - Array of mapped object's segments. 53 | */ 54 | export function getSegments(ray = false) { 55 | if(!this.active) 56 | return []; 57 | if(!ray || (ray && (ray.detectionRange == 0 || ray.detectionRange >= Phaser.Math.MAX_SAFE_INTEGER))) 58 | return this._segments; 59 | 60 | let segments = []; 61 | for(let segment of this._segments) { 62 | if(Phaser.Geom.Intersects.LineToCircle(segment, ray.detectionRangeCircle)) { 63 | segments.push(segment); 64 | } 65 | } 66 | 67 | return segments; 68 | }; 69 | 70 | /** 71 | * Update tilemap's map of points and segments. 72 | * 73 | * @method Raycaster.Map#tilemap.updateMap 74 | * @memberof Raycaster.Map 75 | * @instance 76 | * @private 77 | * @since 0.7.3 78 | * 79 | * @return {Raycaster.Map} {@link Raycaster.Map Raycaster.Map} instance 80 | */ 81 | export function updateMap() { 82 | if(!this.active) 83 | return this; 84 | 85 | let points = [], 86 | segments = [], 87 | columns = Array(this.object.layer.data[0].length + 1); 88 | 89 | for(let i = 0, iLength = columns.length; i < iLength; i++) { 90 | columns[i] = []; 91 | } 92 | 93 | 94 | //calculate offset based on object position and origin point 95 | let offset = new Phaser.Geom.Point(this.object.x, this.object.y); 96 | 97 | let row = this.object.layer.data[0], 98 | tileWidth = this.object.layer.tileWidth * this.object.scaleX, 99 | tileHeight = this.object.layer.tileHeight * this.object.scaleY, 100 | startPoint, 101 | endPoint; 102 | 103 | //set top horizontal lines 104 | if(this.collisionTiles.includes(row[0].index)) { 105 | startPoint = new Phaser.Geom.Point(offset.x, offset.y); 106 | endPoint = new Phaser.Geom.Point(tileWidth + offset.x, offset.y); 107 | 108 | columns[0].push(startPoint); 109 | } 110 | 111 | for(let i = 1, iLength = row.length; i < iLength; i++) { 112 | let tile = row[i]; 113 | 114 | if(!this.collisionTiles.includes(tile.index)) { 115 | if(startPoint) { 116 | startPoint.neighbours = [endPoint]; 117 | endPoint.neighbours = [startPoint]; 118 | 119 | points.push(startPoint, endPoint); 120 | segments.push(new Phaser.Geom.Line(startPoint.x, startPoint.y, endPoint.x, endPoint.y)); 121 | 122 | columns[i].push(endPoint); 123 | 124 | startPoint = false; 125 | endPoint = false; 126 | } 127 | continue; 128 | } 129 | 130 | let x = i * tileWidth + offset.x, 131 | y = offset.y; 132 | 133 | if(!startPoint) { 134 | startPoint = new Phaser.Geom.Point(x, y); 135 | columns[i].push(startPoint); 136 | } 137 | 138 | if(!endPoint) { 139 | endPoint = new Phaser.Geom.Point(x + tileWidth, y); 140 | } 141 | else { 142 | endPoint.x = x + tileWidth; 143 | } 144 | } 145 | 146 | if(startPoint) { 147 | startPoint.neighbours = [endPoint]; 148 | endPoint.neighbours = [startPoint]; 149 | 150 | points.push(startPoint, endPoint); 151 | segments.push(new Phaser.Geom.Line(startPoint.x, startPoint.y, endPoint.x, endPoint.y)); 152 | 153 | columns[row.length].push(endPoint); 154 | } 155 | 156 | startPoint = false; 157 | endPoint = false; 158 | 159 | for(let i = 1, iLength = this.object.layer.data.length; i < iLength; i++) { 160 | row = this.object.layer.data[i]; 161 | let higherRow = this.object.layer.data[i - 1]; 162 | 163 | if(this.collisionTiles.includes(row[0].index) != this.collisionTiles.includes(higherRow[0].index)) { 164 | startPoint = new Phaser.Geom.Point(offset.x, i * tileHeight + offset.y); 165 | endPoint = new Phaser.Geom.Point(tileWidth + offset.x, i * tileHeight + offset.y); 166 | 167 | columns[0].push(startPoint); 168 | } 169 | 170 | for(let j = 1, jLength = row.length; j < jLength; j++) { 171 | let tile = row[j], 172 | isCollisionTile = this.collisionTiles.includes(tile.index), 173 | isCollisionHigherTile = this.collisionTiles.includes(higherRow[j].index); 174 | 175 | if(isCollisionTile == isCollisionHigherTile) { 176 | if(startPoint) { 177 | startPoint.neighbours = [endPoint]; 178 | endPoint.neighbours = [startPoint]; 179 | 180 | points.push(startPoint, endPoint); 181 | segments.push(new Phaser.Geom.Line(startPoint.x, startPoint.y, endPoint.x, endPoint.y)); 182 | 183 | columns[j].push(endPoint); 184 | 185 | startPoint = false; 186 | endPoint = false; 187 | } 188 | continue; 189 | } 190 | 191 | let x = j * tileWidth + offset.x, 192 | y = i * tileHeight + offset.y; 193 | 194 | if(!startPoint) { 195 | startPoint = new Phaser.Geom.Point(x, y); 196 | 197 | columns[j].push(startPoint); 198 | } 199 | 200 | if(!endPoint) { 201 | endPoint = new Phaser.Geom.Point(x + tileWidth, y); 202 | } 203 | else { 204 | endPoint.x = x + tileWidth; 205 | } 206 | } 207 | 208 | if(startPoint) { 209 | startPoint.neighbours = [endPoint]; 210 | endPoint.neighbours = [startPoint]; 211 | 212 | points.push(startPoint, endPoint); 213 | segments.push(new Phaser.Geom.Line(startPoint.x, startPoint.y, endPoint.x, endPoint.y)); 214 | 215 | columns[row.length].push(endPoint); 216 | } 217 | 218 | startPoint = false; 219 | endPoint = false; 220 | } 221 | 222 | //set bottom horizontal lines 223 | row = this.object.layer.data.slice(-1)[0]; 224 | let y = this.object.layer.data.length * tileHeight + offset.y; 225 | 226 | if(this.collisionTiles.includes(row[0].index)) { 227 | startPoint = new Phaser.Geom.Point(offset.x, y); 228 | endPoint = new Phaser.Geom.Point(tileWidth + offset.x, y); 229 | 230 | columns[0].push(startPoint); 231 | } 232 | 233 | for(let i = 1, iLength = row.length; i < iLength; i++) { 234 | let tile = row[i]; 235 | 236 | if(!this.collisionTiles.includes(tile.index)) { 237 | if(startPoint) { 238 | startPoint.neighbours = [endPoint]; 239 | endPoint.neighbours = [startPoint]; 240 | 241 | points.push(startPoint, endPoint); 242 | segments.push(new Phaser.Geom.Line(startPoint.x, startPoint.y, endPoint.x, endPoint.y)); 243 | 244 | columns[i].push(endPoint); 245 | 246 | startPoint = false; 247 | endPoint = false; 248 | } 249 | continue; 250 | } 251 | 252 | let x = i * tileWidth + offset.x; 253 | 254 | if(!startPoint) { 255 | startPoint = new Phaser.Geom.Point(x, y); 256 | 257 | columns[i].push(startPoint); 258 | } 259 | 260 | if(!endPoint) { 261 | endPoint = new Phaser.Geom.Point(x + tileWidth, y); 262 | } 263 | else { 264 | endPoint.x = x + tileWidth; 265 | } 266 | } 267 | 268 | if(startPoint) { 269 | startPoint.neighbours = [endPoint]; 270 | endPoint.neighbours = [startPoint]; 271 | 272 | points.push(startPoint, endPoint); 273 | segments.push(new Phaser.Geom.Line(startPoint.x, startPoint.y, endPoint.x, endPoint.y)); 274 | 275 | columns[row.length].push(endPoint); 276 | } 277 | 278 | //set vertical lines 279 | for(let i = 0, iLength = columns.length; i < iLength; i++) { 280 | const column = columns[i]; 281 | 282 | for(let j = 0, jLength = column.length - 1; j < jLength; j++) { 283 | segments.push(new Phaser.Geom.Line(column[j].x, column[j].y, column[j+1].x, column[j+1].y)); 284 | column[j].neighbours.push(column[j+1]); 285 | column[j+1].neighbours.push(column[j]); 286 | j++; 287 | } 288 | } 289 | 290 | this._points = points; 291 | this._segments = segments; 292 | return this; 293 | }; 294 | 295 | /** 296 | * Set tile types which should be mapped (for Phaser.Tilemaps.StaticTilemapLayer and Phaser.Tilemaps.DynamicTilemapLayer maps only). 297 | * 298 | * @method Raycaster.Map#setCollisionTiles 299 | * @memberof Raycaster.Map 300 | * @instance 301 | * @since 0.7.3 302 | * 303 | * @param {array} [tiles = []] - Set of tile's indexes to map. 304 | * 305 | * @return {Raycaster.Map} {@link Raycaster.Map Raycaster.Map} instance 306 | */ 307 | export function setCollisionTiles(tiles = []) { 308 | this.collisionTiles = tiles; 309 | return this; 310 | } 311 | -------------------------------------------------------------------------------- /src/map/segmentsCount.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Set segment count for cirle's map. 3 | * If set to 0, map won't be generating segments and relay only on tangent points calculated for currently testing ray. 4 | * 5 | * @method Raycaster.Map#setSegmentCount 6 | * @memberof Raycaster.Map 7 | * @instance 8 | * @since 0.6.0 9 | * 10 | * @param {number} count - Circle map's segment count. 11 | * 12 | * @return {Raycaster.Map} {@link Raycaster.Map Raycaster.Map} instance 13 | */ 14 | export function setSegmentCount(count) { 15 | this.segmentCount = count; 16 | this.circle = count ? false : true; 17 | 18 | this.updateMap(); 19 | return this; 20 | } 21 | -------------------------------------------------------------------------------- /src/ray/angle.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Set ray's angle (direction) in radians. 3 | * 4 | * @method Raycaster.Ray#setAngle 5 | * @memberof Raycaster.Ray 6 | * @instance 7 | * @since 0.6.0 8 | * 9 | * @param {number} [angle = 0] - Ray's angle in radians. 10 | * 11 | * @return {Raycaster.Ray} {@link Raycaster.Ray Raycaster.Ray} instance 12 | */ 13 | export function setAngle(angle = 0) { 14 | this.angle = Phaser.Math.Angle.Normalize(angle); 15 | Phaser.Geom.Line.SetToAngle(this._ray, this.origin.x, this.origin.y, this.angle, this.rayRange); 16 | return this; 17 | } 18 | 19 | /** 20 | * Set ray's angle (direction) in degrees. 21 | * 22 | * @method Raycaster.Ray#setAngleDeg 23 | * @memberof Raycaster.Ray 24 | * @instance 25 | * @since 0.6.1 26 | * 27 | * @param {number} [angle = 0] - Ray's angle in degrees. 28 | * 29 | * @return {Raycaster.Ray} {@link Raycaster.Ray Raycaster.Ray} instance 30 | */ 31 | export function setAngleDeg(angle = 0) { 32 | this.angle = Phaser.Math.Angle.Normalize(Phaser.Math.DegToRad(angle)); 33 | Phaser.Geom.Line.SetToAngle(this._ray, this.origin.x, this.origin.y, this.angle, this.rayRange); 34 | return this; 35 | } 36 | -------------------------------------------------------------------------------- /src/ray/castCircle.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Cast ray in all directions to find closest intersections with tested mapped objects. 3 | * 4 | * @method Raycaster.Ray#castCircle 5 | * @memberof Raycaster.Ray 6 | * @instance 7 | * @since 0.6.0 8 | * 9 | * @param {object} [options] - options that may include: 10 | * @param {object[]} [options.objects = Raycaster.mappedObjects] - Array of game objects to test. If not provided test all mapped game objects. 11 | * 12 | * @return {Phaser.Geom.Point[]} Array of points of ray's closest intersections with tested objects. Additionally each point contains reference to hit mapped object and it's segment if available. 13 | */ 14 | export function castCircle(options = {}) { 15 | let originalAngle = this.angle; 16 | let intersections = []; 17 | let maps = []; 18 | let rayTargets = []; 19 | let testedObjects = []; 20 | let startTime = performance.now(); 21 | //reset stats 22 | this._stats = { 23 | method: 'castCircle', 24 | rays: 0, 25 | testedMappedObjects: 0, 26 | hitMappedObjects: 0, 27 | segments: 0, 28 | time: 0 29 | }; 30 | 31 | //if no objects to cast ray were passed, use raycasters mapped objects 32 | if(!options.objects) { 33 | if(this._raycaster) 34 | options.objects = this._raycaster.mappedObjects; 35 | else 36 | return intersections; 37 | } 38 | 39 | //if bounding box is defined add bounding box points to 40 | if(this._raycaster && this._raycaster.boundingBox) { 41 | for(let point of this._raycaster.boundingBox.points) { 42 | rayTargets.push({ 43 | point: point, 44 | angle: Phaser.Math.Angle.Between(this.origin.x, this.origin.y, point.x, point.y) 45 | }); 46 | } 47 | } 48 | 49 | for(let i=0, iLength = options.objects.length; i < iLength; i++) { 50 | let object = options.objects[i]; 51 | //if bound in range 52 | if(!this.boundsInRange(object)) 53 | continue; 54 | 55 | testedObjects.push(object); 56 | 57 | let map, boundingBox; 58 | if(object.type === 'body' || object.type === 'composite') 59 | map = object.raycasterMap; 60 | else 61 | map = object.data.get('raycasterMap'); 62 | 63 | //get slightly enlarged bounding box due to fridge cases, when ray "glanced" border box's corner (v0.10.1) 64 | boundingBox = map.getBoundingBox(); 65 | boundingBox.setTo(boundingBox.x - 0.1, boundingBox.y - 0.1, boundingBox.width + 0.2, boundingBox.height + 0.2); 66 | 67 | map._boundingBox = boundingBox; 68 | 69 | maps.push(map); 70 | //get points and angles 71 | for(let point of map.getPoints(this)) { 72 | rayTargets.push({ 73 | point: point, 74 | angle: Phaser.Math.Angle.Between(this.origin.x, this.origin.y, point.x, point.y) 75 | }); 76 | } 77 | 78 | //get objects intersections 79 | for(let j = i+1, jLength = options.objects.length; j < jLength; j++){ 80 | let objectB = options.objects[j]; 81 | let mapB; 82 | if(objectB.type === 'body' || objectB.type === 'composite') 83 | mapB = objectB.raycasterMap; 84 | else { 85 | mapB = objectB.data.get('raycasterMap'); 86 | } 87 | //check if bounding boxes overlap 88 | if(!Phaser.Geom.Intersects.RectangleToRectangle(map.getBoundingBox(), mapB.getBoundingBox())) 89 | continue; 90 | 91 | //find objects intersections 92 | for(let segmentA of map.getSegments(this)) { 93 | for(let segmentB of mapB.getSegments(this)) { 94 | let intersection = []; 95 | if(!Phaser.Geom.Intersects.LineToLine(segmentA, segmentB, intersection)) 96 | continue; 97 | 98 | let target = { 99 | point: new Phaser.Geom.Point(intersection.x, intersection.y), 100 | angle: Phaser.Math.Angle.Between(this.origin.x, this.origin.y, intersection.x, intersection.y) 101 | }; 102 | target.point.intersection = false; 103 | rayTargets.push(target); 104 | } 105 | } 106 | } 107 | } 108 | 109 | //sort target points by angle 110 | rayTargets.sort(function(a, b){ 111 | //if rays towards points have the same angles promote closer one 112 | if(a.angle == b.angle) { 113 | if(Phaser.Math.Distance.Between(this.origin.x, this.origin.y, a.point.x, a.point.y) > Phaser.Math.Distance.Between(this.origin.x, this.origin.y, b.point.x, b.point.y)) 114 | return 1; 115 | else 116 | return -1; 117 | } 118 | 119 | return a.angle - b.angle; 120 | }.bind(this)); 121 | 122 | let previousTarget = { 123 | angle: false 124 | }; 125 | 126 | //cast rays 127 | for(let target of rayTargets){ 128 | //if current target is the same as previous one skip loop 129 | if(target.angle === previousTarget.angle) { 130 | continue; 131 | } 132 | 133 | previousTarget = target; 134 | 135 | this.setAngle(target.angle); 136 | let intersection = this.cast({ 137 | objects: testedObjects, 138 | target: target.point, 139 | internal: true 140 | }); 141 | 142 | if(intersection) { 143 | //if intersection hits target point check if ray "glanced" mapped object. 144 | let castSides = false; 145 | if(this.round) { 146 | let roundedTarget = new Phaser.Geom.Point(Math.round(target.point.x), Math.round(target.point.y)); 147 | castSides = Phaser.Geom.Point.Equals(roundedTarget, intersection) 148 | } 149 | else { 150 | castSides = Phaser.Geom.Point.Equals(target.point, intersection); 151 | } 152 | 153 | if(!castSides) { 154 | //castSides = false; 155 | } 156 | else if(!target.point.neighbours || target.point.neighbours.length < 2) { 157 | //castSides = true; 158 | } 159 | //check if ray and at least one line between target point and it's neighbours are parallel 160 | else if(Phaser.Math.Angle.Normalize(this.angle - Phaser.Math.Angle.BetweenPoints(this.origin, target.point.neighbours[0])) < 0.0001 161 | || Phaser.Math.Angle.Normalize(this.angle - Phaser.Math.Angle.BetweenPoints(this.origin, target.point.neighbours[1])) < 0.0001) { 162 | //castSides = true; 163 | } 164 | //check if ray crossed more than 1 points of triangle created by tatget point and it's neighbours 165 | else { 166 | let triangleIntersections = []; 167 | if(!target.point.neighboursTriangle) { 168 | target.point.neighboursTriangle = new Phaser.Geom.Triangle(target.point.x, target.point.y, target.point.neighbours[0].x, target.point.neighbours[0].y, target.point.neighbours[1].x, target.point.neighbours[1].y); 169 | } 170 | 171 | Phaser.Geom.Intersects.GetTriangleToLine(target.point.neighboursTriangle, this._ray, triangleIntersections); 172 | 173 | //if point of intersection of ray and tirangle are close to target point, assume ray "glanced" triangle. 174 | for(let triangleIntersection of triangleIntersections) { 175 | if(Math.abs(target.point.x - triangleIntersection.x) > 0.0001 && Math.abs(target.point.y - triangleIntersection.y) > 0.0001) { 176 | castSides = false; 177 | break; 178 | } 179 | } 180 | } 181 | 182 | //if ray "glanced" mapped object cast two additional rays 183 | if(castSides) { 184 | this.setAngle(target.angle - 0.0001); 185 | let intersectionA = this.cast({ 186 | objects: testedObjects, 187 | internal: true 188 | }); 189 | 190 | if(intersectionA) { 191 | intersections.push(intersectionA); 192 | } 193 | 194 | intersections.push(intersection); 195 | 196 | this.setAngle(target.angle + 0.0001); 197 | let intersectionB = this.cast({ 198 | objects: testedObjects, 199 | internal: true 200 | }); 201 | 202 | if(intersectionB) { 203 | intersections.push(intersectionB); 204 | } 205 | 206 | continue; 207 | } 208 | 209 | intersections.push(intersection); 210 | } 211 | } 212 | 213 | this.setAngle(originalAngle); 214 | this.intersections = intersections; 215 | 216 | if(this.autoSlice) 217 | this.slicedIntersections = this.slice(); 218 | 219 | this._stats.time = performance.now() - startTime; 220 | 221 | this.drawDebug(intersections); 222 | 223 | return intersections; 224 | } 225 | -------------------------------------------------------------------------------- /src/ray/cone.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Set ray's cone angle (width) in radians. 3 | * 4 | * @method Raycaster.Ray#setCone 5 | * @memberof Raycaster.Ray 6 | * @instance 7 | * @since 0.7.0 8 | * 9 | * @param {number} [cone = 0] - Ray's cone angle in radians. 10 | * 11 | * @return {Raycaster.Ray} {@link Raycaster.Ray Raycaster.Ray} instance 12 | */ 13 | export function setCone(cone = 0) { 14 | this.cone = cone; 15 | return this; 16 | } 17 | 18 | /** 19 | * Set ray's cone angle (width) in degrees. 20 | * 21 | * @method Raycaster.Ray#setConeDeg 22 | * @memberof Raycaster.Ray 23 | * @instance 24 | * @since 0.7.0 25 | * 26 | * @param {number} [cone = 0] - Ray's cone angle in degrees. 27 | * 28 | * @return {Raycaster.Ray} {@link Raycaster.Ray Raycaster.Ray} instance 29 | */ 30 | export function setConeDeg(cone = 0) { 31 | this.cone = Phaser.Math.DegToRad(cone); 32 | return this; 33 | } 34 | -------------------------------------------------------------------------------- /src/ray/config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Configure ray. 3 | * 4 | * @method Raycaster.Ray#config 5 | * @memberof Raycaster.Ray 6 | * @instance 7 | * @since 0.6.0 8 | * 9 | * @param {object} [options] - Ray's congfiguration options. May include: 10 | * @param {Phaser.Geom.Point|Point} [options.origin = {x:0, y:0}] - Ray's position. 11 | * @param {number} [options.angle = 0] - Ray's angle in radians. 12 | * @param {number} [options.angleDeg = 0] - Ray's angle in degrees. 13 | * @param {number} [options.cone = 0] - Ray's cone angle in radians. 14 | * @param {number} [options.coneDeg = 0] - Ray's cone angle in degrees. 15 | * @param {number} [options.range = Phaser.Math.MAX_SAFE_INTEGER] - Ray's range. 16 | * @param {number} [options.collisionRange = Phaser.Math.MAX_SAFE_INTEGER] - Ray's maximum collision range of ray's field of view. 17 | * @param {number} [options.detectionRange = Phaser.Math.MAX_SAFE_INTEGER] - Maximum distance between ray's position and tested objects bounding boxes. 18 | * @param {boolean} [options.ignoreNotIntersectedRays = true] - If set true, ray returns false when it didn't hit anything. Otherwise returns ray's target position. 19 | * @param {boolean} [options.autoSlice = false] - If set true, ray will automatically slice intersections into array of triangles and store it in {@link Raycaster.Ray#slicedIntersections Ray.slicedIntersections}. 20 | * @param {boolean} [options.round = false] - If set true, point where ray hit will be rounded. 21 | * @param {(boolean|'arcade'|'matter')} [options.enablePhysics = false] - Add to ray physics body. Body will be a circle with radius equal to {@link Raycaster.Ray#collisionRange Ray.collisionRange}. If set true, arcade physics body will be added. 22 | * 23 | * @return {Raycaster.Ray} {@link Raycaster.Ray Raycaster.Ray} instance 24 | */ 25 | export function config(options) { 26 | this.object = options.object; 27 | //origin 28 | if(options.origin !== undefined) 29 | this.origin.setTo(options.origin.x, options.origin.y); 30 | 31 | //angle 32 | if(options.angle !== undefined) 33 | this.angle = Phaser.Math.Angle.Normalize(options.angle); 34 | 35 | //angle deg 36 | if(options.angleDeg !== undefined) 37 | this.angle = Phaser.Math.Angle.Normalize(Phaser.Math.DegToRad(options.angleDeg)); 38 | 39 | //cone angle 40 | if(options.cone !== undefined) 41 | this.cone = options.cone; 42 | 43 | //cone angle deg 44 | if(options.coneDeg !== undefined) 45 | this.cone = Phaser.Math.DegToRad(options.coneDeg); 46 | 47 | //ray range (0 = max) 48 | if(options.rayRange !== undefined) 49 | this.rayRange = options.rayRange; 50 | 51 | //collision range (0 = max) 52 | if(options.collisionRange !== undefined) 53 | this.collisionRange = options.collisionRange; 54 | 55 | //detection range (0 = max) 56 | if(options.detectionRange !== undefined) 57 | this.detectionRange = options.detectionRange; 58 | 59 | //ignore not intersected rays 60 | if(options.ignoreNotIntersectedRays !== undefined) 61 | this.ignoreNotIntersectedRays = (options.ignoreNotIntersectedRays == true) 62 | 63 | //round 64 | if(options.round !== undefined) 65 | this.round = (options.round == true) 66 | 67 | //auto slice 68 | if(options.autoSlice !== undefined) 69 | this.autoSlice = (options.autoSlice == true) 70 | 71 | //enable physics 72 | if(options.enablePhysics !== undefined && options.enablePhysics) 73 | this.enablePhysics(options.enablePhysics); 74 | 75 | Phaser.Geom.Line.SetToAngle(this._ray, this.origin.x, this.origin.y, this.angle, this.rayRange); 76 | this.detectionRangeCircle.setTo(this.origin.x, this.origin.y,this.detectionRange); 77 | 78 | if(this._raycaster.debugOptions.enabled && this._raycaster.scene !== undefined) { 79 | this.graphics = this._raycaster.scene.add.graphics({ lineStyle: { width: 1, color: 0x00ff00}, fillStyle: { color: 0xff00ff } }); 80 | this.graphics.setDepth(1000); 81 | } 82 | 83 | return this; 84 | } 85 | -------------------------------------------------------------------------------- /src/ray/debug.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Draw rays in debug mode 3 | * 4 | * @method Raycaster.Ray#drawDebug 5 | * @memberof Raycaster 6 | * @private 7 | * @since 0.10.0 8 | * 9 | * @param {Phaser.Geom.Point[]} Array of points of ray's closest intersections with tested objects. 10 | * 11 | * @return {Raycaster.Ray} {@link Raycaster.Ray Raycaster.Ray} instance 12 | */ 13 | export function drawDebug(intersections) { 14 | if(this.graphics === undefined || !this._raycaster.debugOptions.enabled) 15 | return this; 16 | 17 | //clear 18 | this.graphics.clear(); 19 | 20 | if(!this._raycaster.debugOptions.rays) 21 | return this; 22 | 23 | if(this._raycaster.debugOptions.graphics.ray) { 24 | this.graphics.lineStyle(1, this._raycaster.debugOptions.graphics.ray); 25 | 26 | for(let intersection of intersections) { 27 | this.graphics.strokeLineShape({ 28 | x1: this.origin.x, 29 | y1: this.origin.y, 30 | x2: intersection.x, 31 | y2: intersection.y 32 | }); 33 | } 34 | } 35 | 36 | if(this._raycaster.debugOptions.graphics.rayPoint) { 37 | this.graphics.fillStyle(this._raycaster.debugOptions.graphics.rayPoint); 38 | 39 | this.graphics.fillPoint(this.origin.x, this.origin.y, 3); 40 | 41 | for(let intersection of intersections) { 42 | this.graphics.fillPoint(intersection.x, intersection.y, 3); 43 | } 44 | } 45 | 46 | return this; 47 | } -------------------------------------------------------------------------------- /src/ray/destroy.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Destroy object 3 | * 4 | * @method Raycaster.Ray#destroy 5 | * @memberof Raycaster.Ray 6 | * @instance 7 | * @since 0.10.3 8 | */ 9 | export function destroy() { 10 | if(this.graphics) 11 | this.graphics.destroy(); 12 | 13 | for(let key in this) { 14 | delete this[key]; 15 | } 16 | } -------------------------------------------------------------------------------- /src/ray/enablePhysics.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Add to ray physics body. Body will be a circle with radius equal to {@link Raycaster.Ray#collisionRange Ray.collisionRange}. Physics body can be added only once. 3 | * 4 | * @method Raycaster.Ray#enablePhysics 5 | * @memberof Raycaster.Ray 6 | * @instance 7 | * @since 0.8.0 8 | * 9 | * @param {'arcade'|'matter'} [type = 'arcade'] - Physics type 10 | * 11 | * @return {Raycaster.Ray} {@link Raycaster.Ray Raycaster.Ray} instance 12 | */ 13 | export function enablePhysics(type = 'arcade') { 14 | 15 | if(this.body !== undefined) 16 | return this; 17 | 18 | this.collisionCircle = this._raycaster.scene.add.circle(this.origin.x, this.origin.y, this.collisionRange); 19 | this.collisionCircle._ray = this; 20 | 21 | if(type === 'matter') { 22 | this.bodyType = 'matter'; 23 | 24 | if(this.collisionRange == Phaser.Math.MAX_SAFE_INTEGER) { 25 | let bounds = this._raycaster.boundingBox; 26 | this._raycaster.scene.matter.add.gameObject(this.collisionCircle, { shape: { type: 'rectangle', x:bounds.rectangle.centerX, y:bounds.rectangle.centerY, width:bounds.rectangle.width, height:bounds.rectangle.height }, label: 'phaser-raycaster-ray-body', isSensor: true, ignoreGravity:true }); 27 | } 28 | else { 29 | this._raycaster.scene.matter.add.gameObject(this.collisionCircle, { shape: { type: 'circle' }, label: 'phaser-raycaster-ray-body', isSensor: true, ignoreGravity:true }); 30 | } 31 | 32 | this.body = this.collisionCircle.body; 33 | this.body._ray = this; 34 | this.setOnCollideActive(); 35 | } 36 | else { 37 | this.bodyType = 'arcade'; 38 | this._raycaster.scene.physics.add.existing(this.collisionCircle); 39 | 40 | this.body = this.collisionCircle.body; 41 | this.body 42 | .setCircle(this.collisionRange) 43 | .setAllowGravity(false) 44 | .setImmovable(true); 45 | this.body._ray = this; 46 | } 47 | 48 | return this; 49 | } 50 | -------------------------------------------------------------------------------- /src/ray/matter-physics-methods.js: -------------------------------------------------------------------------------- 1 | /*Matter physics methods for ray body*/ 2 | /** 3 | * Sets the collision category of this ray's Matter Body. This number must be a power of two between 2^0 (= 1) and 2^31. 4 | * Two bodies with different collision groups (see {@link #setCollisionGroup}) will only collide if their collision 5 | * categories are included in their collision masks (see {@link #setCollidesWith}). 6 | * 7 | * @method Raycaster.Ray#setCollisionCategory 8 | * @memberof Raycaster.Ray 9 | * @instance 10 | * @since 0.9.1 11 | * 12 | * @param {number} value - Unique category bitfield. 13 | * 14 | * @return {Raycaster.Ray} {@link Raycaster.Ray Raycaster.Ray} instance 15 | */ 16 | export function setCollisionCategory(value) { 17 | this.body.collisionFilter.category = value; 18 | 19 | return this; 20 | }; 21 | 22 | /** 23 | * Sets the collision group of this ray's Matter Body. If this is zero or two Matter Bodies have different values, 24 | * they will collide according to the usual rules (see {@link #setCollisionCategory} and {@link #setCollisionGroup}). 25 | * If two Matter Bodies have the same positive value, they will always collide; if they have the same negative value, 26 | * they will never collide. 27 | * 28 | * @method Raycaster.Ray#setCollisionCategory 29 | * @memberof Raycaster.Ray 30 | * @instance 31 | * @since 0.9.1 32 | * 33 | * @param {number} value - Unique group index. 34 | * 35 | * @return {Raycaster.Ray} {@link Raycaster.Ray Raycaster.Ray} instance 36 | */ 37 | export function setCollisionGroup(value) { 38 | this.body.collisionFilter.group = value; 39 | 40 | return this; 41 | }; 42 | 43 | /** 44 | * Sets the collision mask for this ray's Matter Body. Two Matter Bodies with different collision groups will only 45 | * collide if each one includes the other's category in its mask based on a bitwise AND, i.e. `(categoryA & maskB) !== 0` 46 | * and `(categoryB & maskA) !== 0` are both true.* 47 | * 48 | * @method Raycaster.Ray#setCollidesWith 49 | * @memberof Raycaster.Ray 50 | * @instance 51 | * @since 0.9.1 52 | * 53 | * @param {(number|number[])} categories - A unique category bitfield, or an array of them. 54 | * 55 | * @return {Raycaster.Ray} {@link Raycaster.Ray Raycaster.Ray} instance 56 | */ 57 | 58 | export function setCollidesWith(categories) { 59 | var flags = 0; 60 | 61 | if (!Array.isArray(categories)) 62 | { 63 | flags = categories; 64 | } 65 | else 66 | { 67 | for (var i = 0; i < categories.length; i++) 68 | { 69 | flags |= categories[i]; 70 | } 71 | } 72 | 73 | this.body.collisionFilter.mask = flags; 74 | 75 | return this; 76 | }; 77 | 78 | /** 79 | * The callback is sent a `Phaser.Types.Physics.Matter.MatterCollisionData` object. 80 | * 81 | * This does not change the bodies collision category, group or filter. Those must be set in addition 82 | * to the callback. 83 | * 84 | * @method Raycaster.Ray#setOnCollide 85 | * @memberof Raycaster.Ray 86 | * @instance 87 | * @since 0.9.1 88 | * 89 | * @param {function} callback - The callback to invoke when this body starts colliding with another. 90 | * 91 | * @return {Raycaster.Ray} {@link Raycaster.Ray Raycaster.Ray} instance 92 | */ 93 | export function setOnCollide(callback) { 94 | let self = this; 95 | this.body.onCollideCallback = function(collisionInfo) { 96 | if(collisionInfo.rayCollided) { 97 | callback(collisionInfo); 98 | } 99 | else if(self.processOverlap(collisionInfo)) { 100 | collisionInfo.rayCollided = true; 101 | callback(collisionInfo); 102 | } 103 | }; 104 | 105 | return this; 106 | }; 107 | 108 | /** 109 | * The callback is sent a `Phaser.Types.Physics.Matter.MatterCollisionData` object. 110 | * 111 | * This does not change the bodies collision category, group or filter. Those must be set in addition 112 | * to the callback. 113 | * 114 | * @method Raycaster.Ray#setOnCollideEnd 115 | * @memberof Raycaster.Ray 116 | * @instance 117 | * @since 0.9.1 118 | * 119 | * @param {function} callback - The callback to invoke when this body stops colliding with another. 120 | * 121 | * @return {Raycaster.Ray} {@link Raycaster.Ray Raycaster.Ray} instance 122 | */ 123 | export function setOnCollideEnd(callback) { 124 | this.body.onCollideEndCallback = function(collisionInfo) { 125 | if(collisionInfo.rayCollided) { 126 | collisionInfo.rayCollided = false; 127 | callback(collisionInfo); 128 | } 129 | } 130 | 131 | return this; 132 | }; 133 | 134 | /** 135 | * The callback is sent a `Phaser.Types.Physics.Matter.MatterCollisionData` object. 136 | * 137 | * This does not change the bodies collision category, group or filter. Those must be set in addition 138 | * to the callback. 139 | * 140 | * @method Raycaster.Ray#setOnCollideActive 141 | * @memberof Raycaster.Ray 142 | * @instance 143 | * @since 0.9.1 144 | * 145 | * @param {function} callback - The callback to invoke for the duration of this body colliding with another. 146 | * 147 | * @return {Raycaster.Ray} {@link Raycaster.Ray Raycaster.Ray} instance 148 | */ 149 | export function setOnCollideActive(callback) { 150 | let self = this; 151 | let func = function(collisionInfo) { 152 | if(self.processOverlap(collisionInfo)) { 153 | let body = collisionInfo.bodyA.label === 'phaser-raycaster-ray-body' ? collisionInfo.bodyB : collisionInfo.bodyA; 154 | 155 | if(collisionInfo.rayCollided !== true) { 156 | collisionInfo.rayCollided = true; 157 | if(self.body.onCollideCallback) { 158 | self.body.onCollideCallback(collisionInfo); 159 | } 160 | 161 | if(self.body.onCollideWith !== undefined && self.body.onCollideWith[body.id]) { 162 | self.body.onCollideWith[body.id](body, collisionInfo); 163 | } 164 | } 165 | if(callback) 166 | callback(collisionInfo); 167 | } 168 | else { 169 | if(self.body.onCollideEndCallback && collisionInfo.rayCollided === true) { 170 | self.body.onCollideEndCallback(collisionInfo); 171 | } 172 | } 173 | } 174 | 175 | this.body.onCollideActiveCallback = func; 176 | 177 | return this; 178 | } 179 | 180 | /** 181 | * The callback is sent a reference to the other body, along with a `Phaser.Types.Physics.Matter.MatterCollisionData` object. 182 | * 183 | * This does not change the bodies collision category, group or filter. Those must be set in addition 184 | * to the callback. 185 | * 186 | * @method Raycaster.Ray#setOnCollideWith 187 | * @memberof Raycaster.Ray 188 | * @instance 189 | * @since 0.9.1 190 | * 191 | * @param {(MatterJS.Body|MatterJS.Body[])} body - The body, or an array of bodies, to test for collisions with. 192 | * @param {function} callback - The callback to invoke when this body collides with the given body or bodies. 193 | * 194 | * @return {Raycaster.Ray} {@link Raycaster.Ray Raycaster.Ray} instance 195 | */ 196 | export function setOnCollideWith(body, callback) { 197 | let self = this; 198 | let func = function(body, collisionInfo) { 199 | if(collisionInfo.rayCollided) { 200 | callback(body, collisionInfo); 201 | } 202 | else if(self.processOverlap(collisionInfo)) { 203 | collisionInfo.rayCollided = true; 204 | callback(body, collisionInfo); 205 | } 206 | } 207 | 208 | if (!Array.isArray(body)) 209 | { 210 | body = [ body ]; 211 | } 212 | 213 | for (var i = 0; i < body.length; i++) 214 | { 215 | var src = (body[i].hasOwnProperty('body')) ? body[i].body : body[i]; 216 | 217 | this.body.setOnCollideWith(src, func); 218 | } 219 | 220 | return this; 221 | }; 222 | -------------------------------------------------------------------------------- /src/ray/origin.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Set ray's source position. 3 | * 4 | * @method Raycaster.Ray#setOrigin 5 | * @memberof Raycaster.Ray 6 | * @instance 7 | * @since 0.6.0 8 | * 9 | * @param {number} x - X coordinate. 10 | * @param {number} y - Y coordinate. 11 | * 12 | * @return {Raycaster.Ray} {@link Raycaster.Ray Raycaster.Ray} instance 13 | */ 14 | export function setOrigin(x, y) { 15 | this.origin.setTo(x, y); 16 | Phaser.Geom.Line.SetToAngle(this._ray, this.origin.x, this.origin.y, this.angle, this.rayRange); 17 | this.detectionRangeCircle.setTo(this.origin.x, this.origin.y,this.detectionRange); 18 | 19 | if(this.bodyType === 'matter' && this.collisionRange !== Phaser.Math.MAX_SAFE_INTEGER) { 20 | this.collisionCircle.x = x; 21 | this.collisionCircle.y = y; 22 | } 23 | else if(this.bodyType === 'arcade') { 24 | this.collisionCircle.x = x; 25 | this.collisionCircle.y = y; 26 | } 27 | 28 | return this; 29 | } 30 | -------------------------------------------------------------------------------- /src/ray/overlap.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Get game objects overlaping field of view. 3 | * 4 | * @method Raycaster.Ray#overlap 5 | * @memberof Raycaster.Ray 6 | * @instance 7 | * @since 0.8.0 8 | * 9 | * @param {object|object[]} [objects] - Game object / array off game objects to test. 10 | * 11 | * @return {object[]} Array of game objects that overlaps with field of view. 12 | */ 13 | export function overlap(objects) { 14 | let targets = []; 15 | let overlapCircle = new Phaser.Geom.Circle(this.origin.x, this.origin.y, this.collisionRange); 16 | 17 | //matter physics 18 | if(this.bodyType === 'matter') { 19 | let isCollisionInfo = false; 20 | if(objects === undefined) { 21 | objects = this._raycaster.scene.matter.query.collides(this.body, this._raycaster.scene.matter.getMatterBodies()); 22 | 23 | for(let object of objects) { 24 | let body = object.bodyA === this.body ? object.bodyB : object.bodyA; 25 | 26 | if(this.testMatterOverlap(body)) 27 | targets.push(body); 28 | } 29 | } 30 | //get object's body 31 | else { 32 | if(!Array.isArray(objects)) 33 | objects = [objects]; 34 | 35 | for(let object of objects) { 36 | if(object === this.body) 37 | continue; 38 | 39 | if(this.testMatterOverlap(object)) 40 | targets.push(object); 41 | } 42 | } 43 | } 44 | //arcade physics 45 | else { 46 | let bodies = false; 47 | //get bodies in range 48 | if(objects === undefined) { 49 | objects = this._raycaster.scene.physics.overlapCirc(this.origin.x, this.origin.y, this.collisionRange, true, true); 50 | bodies = true; 51 | } 52 | //get object's body 53 | else if(!Array.isArray(objects)) { 54 | objects = [objects]; 55 | } 56 | //if objects are bodies 57 | if(bodies) { 58 | for(let body of objects) { 59 | if(body === this.body) 60 | continue; 61 | 62 | let hitbox; 63 | //get physics body hitbox 64 | if(body.isCircle) { 65 | hitbox = new Phaser.Geom.Circle(body.position.x + body.halfWidth, body.position.y + body.halfWidth, body.halfWidth); 66 | } 67 | else { 68 | hitbox = new Phaser.Geom.Rectangle(body.x, body.y, body.width, body.height); 69 | } 70 | 71 | if(this.testArcadeOverlap(hitbox)) 72 | targets.push(body.gameObject); 73 | } 74 | } 75 | //if objects are game objects 76 | else { 77 | for(let object of objects) { 78 | if(object.body === undefined) 79 | continue; 80 | 81 | let hitbox; 82 | //get physics body hitbox 83 | if(object.body.isCircle) { 84 | hitbox = new Phaser.Geom.Circle(object.body.position.x + object.body.halfWidth, object.body.position.y + object.body.halfWidth, object.body.halfWidth); 85 | if(!Phaser.Geom.Intersects.CircleToCircle(overlapCircle, hitbox)) 86 | continue; 87 | } 88 | else { 89 | hitbox = new Phaser.Geom.Rectangle(object.body.x, object.body.y, object.body.width, object.body.height); 90 | if(!Phaser.Geom.Intersects.CircleToRectangle(overlapCircle, hitbox)) 91 | continue; 92 | } 93 | 94 | if(this.testArcadeOverlap(hitbox)) 95 | targets.push(object); 96 | } 97 | } 98 | } 99 | 100 | return targets; 101 | } 102 | 103 | /** 104 | * Process callback for physics collider / overlap. 105 | * 106 | * @method Raycaster.Ray#processOverlap 107 | * @memberof Raycaster.Ray 108 | * @instance 109 | * @since 0.8.0 110 | * 111 | * @param {object} object1 - Game object or matter body passed by collider / overlap or matter CollisionInfo object. 112 | * @param {object} object2 - Game object or matter body passed by collider / overlap. Ignored if matter CollisionInfo object was passed as first argument. 113 | * 114 | * @return {boolean} Return true if game object is overlapping ray's field of view. 115 | */ 116 | export function processOverlap(object1, object2) { 117 | let obj1, obj2, target; 118 | //check if it's matter collisionInfo object 119 | if(object1.bodyA !== undefined && object1.bodyB !== undefined) { 120 | obj1 = object1.bodyA; 121 | obj2 = object1.bodyB; 122 | } 123 | else { 124 | obj1 = object1; 125 | obj2 = object2; 126 | } 127 | 128 | if(obj1._ray !== undefined && obj1._ray === this) 129 | target = obj2; 130 | else if(obj2._ray !== undefined && obj2._ray === this) 131 | target = obj1; 132 | else 133 | return false; 134 | 135 | return (this.overlap(target).length > 0); 136 | } 137 | 138 | /** 139 | * Test if hitbox overlaps with field of view. Method used in {@link Raycaster.Ray#overlap Ray.overlap}. 140 | * 141 | * @method Raycaster.Ray#testArcadeOverlap 142 | * @memberof Raycaster.Ray 143 | * @instance 144 | * @private 145 | * @since 0.8.0 146 | * 147 | * @param {object} hitbox - Game object's hitbox generated inside {@link Raycaster.Ray#overlap Ray.overlap}. 148 | * 149 | * @return {boolean} True if hitbox overlaps with {@link Raycaster.Ray Raycaster.Ray} field of view. 150 | */ 151 | export function testArcadeOverlap(hitbox) { 152 | let overlap = false; 153 | 154 | //iterate through field of view slices to check collisions with target 155 | for(let slice of this.slicedIntersections) { 156 | //if hitbox is a circle 157 | if(hitbox.type == 0) { 158 | overlap = Phaser.Geom.Intersects.TriangleToCircle(slice, hitbox); 159 | } 160 | //if hitbox is a rectangle 161 | else { 162 | overlap = Phaser.Geom.Intersects.RectangleToTriangle(hitbox, slice); 163 | } 164 | 165 | if(overlap) { 166 | return true; 167 | } 168 | } 169 | 170 | return false; 171 | } 172 | 173 | /** 174 | * Test if matter body overlaps with field of view. Method used in {@link Raycaster.Ray#overlap Ray.overlap}. 175 | * 176 | * @method Raycaster.Ray#testMatterOverlap 177 | * @memberof Raycaster.Ray 178 | * @instance 179 | * @private 180 | * @since 0.9.0 181 | * 182 | * @param {object} body - Matter body. 183 | * 184 | * @return {boolean} True if body overlaps with {@link Raycaster.Ray Raycaster.Ray} field of view. 185 | */ 186 | export function testMatterOverlap(object) { 187 | let body; 188 | 189 | if(object.type === 'body') 190 | body = object; 191 | else if(object.body !== undefined) 192 | body = object.body; 193 | else 194 | return false; 195 | 196 | //if body is concave, ignore convex body 197 | let parts = body.parts.length > 1 ? body.parts.splice(1) : body.parts; 198 | //iterate through bodies 199 | for(let part of parts) { 200 | let pointA = part.vertices[0]; 201 | 202 | for(let i = 1, length = part.vertices.length; i < length; i++) { 203 | let pointB = part.vertices[i]; 204 | let segment = new Phaser.Geom.Line(pointA.x, pointA.y, pointB.x, pointB.y); 205 | 206 | //iterate through field of view slices to check collisions with target 207 | for(let slice of this.slicedIntersections) { 208 | let overlap = Phaser.Geom.Intersects.TriangleToLine(slice, segment); 209 | //additional checking if slice contain segment's points due to TriangleToLine bug. 210 | if(!overlap) 211 | overlap = Phaser.Geom.Triangle.ContainsPoint(slice, segment.getPointA()); 212 | if(!overlap) 213 | overlap = Phaser.Geom.Triangle.ContainsPoint(slice, segment.getPointB()); 214 | 215 | if(overlap) { 216 | return true; 217 | } 218 | } 219 | pointA = pointB; 220 | } 221 | 222 | //closing segment 223 | let segment = new Phaser.Geom.Line(part.vertices[part.vertices.length - 1].x, part.vertices[part.vertices.length - 1].y, part.vertices[0].x, part.vertices[0].y); 224 | //iterate through field of view slices to check collisions with target 225 | for(let slice of this.slicedIntersections) { 226 | let overlap = Phaser.Geom.Intersects.TriangleToLine(slice, segment); 227 | 228 | if(overlap) { 229 | return true; 230 | } 231 | } 232 | } 233 | 234 | return false; 235 | } 236 | -------------------------------------------------------------------------------- /src/ray/range.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Set ray's range. 3 | * 4 | * @method Raycaster.Ray#setRayRange 5 | * @memberof Raycaster.Ray 6 | * @instance 7 | * @since 0.6.0 8 | * 9 | * @param {number} [rayRange = Phaser.Math.MAX_SAFE_INTEGER] - Ray's range. 10 | * 11 | * @return {Raycaster.Ray} {@link Raycaster.Ray Raycaster.Ray} instance 12 | */ 13 | export function setRayRange(rayRange = Phaser.Math.MAX_SAFE_INTEGER) { 14 | this.rayRange = rayRange; 15 | Phaser.Geom.Line.SetToAngle(this._ray, this.origin.x, this.origin.y, this.angle, this.rayRange); 16 | return this; 17 | } 18 | 19 | /** 20 | * Set ray's maximum detection range. Objects outside detection range won't be tested. 21 | * Ray tests all objects when set to 0. 22 | * 23 | * @method Raycaster.Ray#setDetectionRange 24 | * @memberof Raycaster.Ray 25 | * @instance 26 | * @since 0.6.0 27 | * 28 | * @param {number} [detectionRange = 0] - Maximum distance between ray's position and tested objects bounding boxes. 29 | * 30 | * @return {Raycaster.Ray} {@link Raycaster.Ray Raycaster.Ray} instance 31 | */ 32 | export function setDetectionRange(detectionRange = 0) { 33 | this.detectionRange = detectionRange; 34 | this.detectionRangeCircle.setTo(this.origin.x, this.origin.y,this.detectionRange); 35 | 36 | return this; 37 | } 38 | 39 | /** 40 | * Set ray's field of view maximum collision range. Objects outside collision range won't be tested by {@link Raycaster.Ray#overlap Raycaster.Ray.overlap} method. 41 | * Determines ray's physics body radius. 42 | * 43 | * @method Raycaster.Ray#setCollisionRange 44 | * @memberof Raycaster.Ray 45 | * @instance 46 | * @since 0.8.0 47 | * 48 | * @param {number} [collisionRange = Phaser.Math.MAX_SAFE_INTEGER] - Ray's collision range and physics body radius. 49 | * 50 | * @return {Raycaster.Ray} {@link Raycaster.Ray Raycaster.Ray} instance 51 | */ 52 | export function setCollisionRange(collisionRange = Phaser.Math.MAX_SAFE_INTEGER) { 53 | let oldRangeMax = this.collisionRange == Phaser.Math.MAX_SAFE_INTEGER; 54 | this.collisionRange = collisionRange; 55 | this.collisionCircle.setRadius(this.collisionRange); 56 | 57 | if(this.bodyType === 'matter') { 58 | if(this.collisionRange == Phaser.Math.MAX_SAFE_INTEGER) { 59 | let bounds = this._raycaster.boundingBox; 60 | 61 | this._raycaster.scene.matter.body.set(this.body, { 62 | shape: { 63 | type: 'rectangle', 64 | x: bounds.rectangle.centerX, 65 | y: bounds.rectangle.centerY, 66 | width: bounds.rectangle.width, 67 | height: bounds.rectangle.height, 68 | circleRadius:0 69 | } 70 | }); 71 | } 72 | else if(oldRangeMax) { 73 | this._raycaster.scene.matter.body.set(this.body, { 74 | shape: { 75 | type: 'circle', 76 | x: this.collisionCircle.x, 77 | y: this.collisionCircle.y 78 | }, 79 | circleRadius: this.collisionRange, 80 | isStatic: false 81 | }); 82 | } 83 | else { 84 | this.collisionCircle.setRadius(this.collisionRange); 85 | } 86 | this._raycaster.scene.matter.body.set(this.body, 'circleRadius', this.collisionRange) 87 | } 88 | else if(this.bodyType === 'arcade') { 89 | this.body.setCircle(this.collisionRange); 90 | } 91 | 92 | return this; 93 | } 94 | 95 | /** 96 | * Test if object's bounding box is in ray's detection range. 97 | * 98 | * @method Raycaster.Ray#boundsInRange 99 | * @memberof Raycaster.Ray 100 | * @instance 101 | * @since 0.6.0 102 | * 103 | * @param {object} object - Tested object 104 | * @param {(Phaser.Geom.Rectangle|boolean)} [bounds = false] - Tested object's bounds. If not passed bounds will be generated automatically. 105 | * 106 | * @return {boolean} Information if object is in ray's detection range. 107 | */ 108 | export function boundsInRange(object, bounds = false) { 109 | if(!this.detectionRange) 110 | return true; 111 | 112 | let objectBounds; 113 | if(bounds) 114 | objectBounds = bounds; 115 | else { 116 | if(object.type === 'body' || object.type === 'composite') 117 | objectBounds = object.raycasterMap.getBoundingBox(); 118 | else 119 | objectBounds = object.data.get('raycasterMap').getBoundingBox(); 120 | } 121 | 122 | if(Phaser.Geom.Intersects.CircleToRectangle(this.detectionRangeCircle, objectBounds)) 123 | return true; 124 | 125 | return false; 126 | } 127 | -------------------------------------------------------------------------------- /src/ray/ray-core.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @classdesc 3 | * 4 | * Ray class responsible for casting ray's and testing their collisions with mapped objects. 5 | * 6 | * @namespace Raycaster.Ray 7 | * @class Raycaster.Ray 8 | * @constructor 9 | * @since 0.6.0 10 | * 11 | * @param {object} [options] - Ray's congfiguration options. May include: 12 | * @param {Phaser.Geom.Point|Point} [options.origin = {x:0, y:0}] - Ray's position. 13 | * @param {number} [options.angle = 0] - Ray's angle in radians. 14 | * @param {number} [options.angleDeg = 0] - Ray's angle in degrees. 15 | * @param {number} [options.cone = 0] - Ray's cone angle in radians. 16 | * @param {number} [options.coneDeg = 0] - Ray's cone angle in degrees. 17 | * @param {number} [options.range = Phaser.Math.MAX_SAFE_INTEGER] - Ray's range. 18 | * @param {number} [options.collisionRange = Phaser.Math.MAX_SAFE_INTEGER] - Ray's maximum collision range of ray's field of view. 19 | * @param {number} [options.detectionRange = Phaser.Math.MAX_SAFE_INTEGER] - Maximum distance between ray's position and tested objects bounding boxes. 20 | * @param {boolean} [options.ignoreNotIntersectedRays = true] - If set true, ray returns false when it didn't hit anything. Otherwise returns ray's target position. 21 | * @param {boolean} [options.autoSlice = false] - If set true, ray will automatically slice intersections into array of triangles and store it in {@link Raycaster.Ray#slicedIntersections Ray.slicedIntersections}. 22 | * @param {boolean} [options.round = false] - If set true, point where ray hit will be rounded. 23 | * @param {(boolean|'arcade'|'matter')} [options.enablePhysics = false] - Add to ray physics body. Body will be a circle with radius equal to {@link Raycaster.Ray#collisionRange Ray.collisionRange}. If set true, arcade physics body will be added. 24 | * @param {Raycaster} [raycaster] - Parent raycaster object. 25 | */ 26 | export function Ray(options, raycaster) { 27 | /** 28 | * Reference to parent Raycaster object. 29 | * 30 | * @name Raycaster.Ray#_raycaster 31 | * @type {Raycaster} 32 | * @private 33 | * @since 0.6.0 34 | */ 35 | this._raycaster = raycaster ? raycaster : false; 36 | /** 37 | * Ray's source position. 38 | * 39 | * @name Raycaster.Ray#origin 40 | * @type {Phaser.Geom.Point} 41 | * @since 0.6.0 42 | */ 43 | this.origin = new Phaser.Geom.Point(); 44 | /** 45 | * Ray's representation used to calculating intersections. 46 | * 47 | * @name Raycaster.Ray#_ray 48 | * @type {Phaser.Geom.Line} 49 | * @private 50 | * @since 0.6.0 51 | */ 52 | this._ray = new Phaser.Geom.Line(); 53 | /** 54 | * Ray's angle in radians. 55 | * 56 | * @name Raycaster.Ray#angle 57 | * @type {number} 58 | * @default 0 59 | * @since 0.6.0 60 | */ 61 | this.angle = 0; 62 | /** 63 | * Ray's cone width angle in radians. 64 | * 65 | * @name Raycaster.Ray#cone 66 | * @type {number} 67 | * @default 0 68 | * @since 0.7.0 69 | */ 70 | this.cone = 0; 71 | /** 72 | * Ray's maximum range 73 | * 74 | * @name Raycaster.Ray#rayRange 75 | * @type {number} 76 | * @default Phaser.Math.MAX_SAFE_INTEGER 77 | * @since 0.6.0 78 | */ 79 | this.rayRange = Phaser.Math.MAX_SAFE_INTEGER; 80 | /** 81 | * Ray's maximum detection range. Objects outside detection range won't be tested. 82 | * Ray tests all objects when set to 0. 83 | * 84 | * @name Raycaster.Ray#detectionRange 85 | * @type {number} 86 | * @default 87 | * @since 0.6.0 88 | */ 89 | this.detectionRange = 0; 90 | /** 91 | * Ray's representation of detection range used in calculating if objects are in range. 92 | * 93 | * @name Raycaster.Ray#detectionRangeCircle 94 | * @type {Phaser.Geom.Circle} 95 | * @private 96 | * @since 0.6.0 97 | */ 98 | this.detectionRangeCircle = new Phaser.Geom.Circle(); 99 | /** 100 | * Ray's maximum collision range of ray's field of view. Radius of {@link Raycaster.Ray#collisionRangeCircle Ray.body}. 101 | * 102 | * @name Raycaster.Ray#collisionRange 103 | * @type {number} 104 | * @default Phaser.Math.MAX_SAFE_INTEGER 105 | * @since 0.8.0 106 | */ 107 | this.collisionRange = Phaser.Math.MAX_SAFE_INTEGER; 108 | /** 109 | * If set true, ray returns false when it didn't hit anything. Otherwise returns ray's target position. 110 | * 111 | * @name Raycaster.Ray#ignoreNotIntersectedRays 112 | * @type {boolean} 113 | * @default true 114 | * @since 0.6.0 115 | */ 116 | this.ignoreNotIntersectedRays = true; 117 | /** 118 | * If set true, ray's hit points will be rounded. 119 | * 120 | * @name Raycaster.Ray#round 121 | * @type {boolean} 122 | * @default false 123 | * @since 0.8.1 124 | */ 125 | this.round = false; 126 | /** 127 | * If set true, ray will automatically slice intersections into array of triangles and store it in {@link Raycaster.Ray#slicedIntersections Ray.slicedIntersections}. 128 | * 129 | * @name Raycaster.Ray#autoSlice 130 | * @type {boolean} 131 | * @default false 132 | * @since 0.8.0 133 | */ 134 | this.autoSlice = false; 135 | /** 136 | * Array of intersections from last raycast representing field of view. 137 | * 138 | * @name Raycaster.Ray#intersections 139 | * @type {object[]} 140 | * @default [] 141 | * @since 0.8.0 142 | */ 143 | this.intersections = []; 144 | /** 145 | * Array of triangles representing slices of field of view from last raycast. 146 | * 147 | * @name Raycaster.Ray#slicedIntersections 148 | * @type {Phaser.Geom.Triangle[]} 149 | * @default [] 150 | * @since 0.8.0 151 | */ 152 | this.slicedIntersections = []; 153 | 154 | /** 155 | * Physics body for testing field of view collisions. 156 | * 157 | * @name Raycaster.Ray#body 158 | * @type {object} 159 | * @default undefined 160 | * @since 0.8.0 161 | */ 162 | //this.body = false; 163 | /** 164 | * Physics body type. 165 | * 166 | * @name Raycaster.Ray#bodyType 167 | * @type {(boolean|'arcade'|'matter')} 168 | * @default false 169 | * @since 0.9.0 170 | */ 171 | this.bodyType = false; 172 | 173 | /** 174 | * Ray casting stats. 175 | * 176 | * @name Raycaster.Ray#_stats 177 | * @type {object} 178 | * @private 179 | * @since 0.10.0 180 | * 181 | * @property {string} method Used casting method (cast, castCircle, castCone). 182 | * @property {number} rays Casted rays. 183 | * @property {number} testedMappedObjects Tested mapped objects. 184 | * @property {number} hitMappedObjects Hit mapped objects. 185 | * @property {number} segments Tested segments. 186 | * @property {number} time Casting time. 187 | */ 188 | this._stats = { 189 | method: 'cast', 190 | rays: 0, 191 | testedMappedObjects: 0, 192 | hitMappedObjects: 0, 193 | segments: 0, 194 | time: 0 195 | }; 196 | 197 | /** 198 | * Ray's graphics object used for debug 199 | * 200 | * @name Raycaster.Ray#graphics 201 | * @type {Phaser.GameObjects.Graphics} 202 | * @private 203 | * @since 0.10.0 204 | */ 205 | this.graphics; 206 | 207 | this.config(options); 208 | }; 209 | 210 | Ray.prototype = { 211 | config: require('./config.js').config, 212 | getStats: require('./stats.js').getStats, 213 | setRay: require('./ray.js').setRay, 214 | setOrigin: require('./origin.js').setOrigin, 215 | setRayRange: require('./range.js').setRayRange, 216 | setAngle: require('./angle.js').setAngle, 217 | setAngleDeg: require('./angle.js').setAngleDeg, 218 | setCone: require('./cone.js').setCone, 219 | setConeDeg: require('./cone.js').setConeDeg, 220 | setDetectionRange: require('./range.js').setDetectionRange, 221 | boundsInRange: require('./range.js').boundsInRange, 222 | cast: require('./cast.js').cast, 223 | castCircle: require('./castCircle.js').castCircle, 224 | castCone: require('./castCone.js').castCone, 225 | slice: require('./slice.js').slice, 226 | setCollisionRange: require('./range.js').setCollisionRange, 227 | enablePhysics: require('./enablePhysics.js').enablePhysics, 228 | overlap: require('./overlap.js').overlap, 229 | processOverlap: require('./overlap.js').processOverlap, 230 | testArcadeOverlap: require('./overlap.js').testArcadeOverlap, 231 | testMatterOverlap: require('./overlap.js').testMatterOverlap, 232 | setCollisionCategory: require('./matter-physics-methods.js').setCollisionCategory, 233 | setCollisionGroup: require('./matter-physics-methods.js').setCollisionGroup, 234 | setCollidesWith: require('./matter-physics-methods.js').setCollidesWith, 235 | setOnCollide: require('./matter-physics-methods.js').setOnCollide, 236 | setOnCollideEnd: require('./matter-physics-methods.js').setOnCollideEnd, 237 | setOnCollideActive: require('./matter-physics-methods.js').setOnCollideActive, 238 | setOnCollideWith: require('./matter-physics-methods.js').setOnCollideWith, 239 | drawDebug: require('./debug.js').drawDebug, 240 | destroy: require('./destroy.js').destroy, 241 | }; 242 | -------------------------------------------------------------------------------- /src/ray/ray.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Set ray's position, direction (angle) and range. 3 | * 4 | * @method Raycaster.Ray#setRay 5 | * @memberof Raycaster.Ray 6 | * @instance 7 | * @since 0.6.0 8 | * 9 | * @param {number} x - X coordinate. 10 | * @param {number} y - Y coordinate. 11 | * @param {number} [angle] - Ray's angle in radians. 12 | * @param {number} [range = Phaser.Math.MAX_SAFE_INTEGER] - Ray's range. 13 | * 14 | * @return {Raycaster.Ray} {@link Raycaster.Ray Raycaster.Ray} instance 15 | */ 16 | export function setRay(x, y, angle, rayRange = Phaser.Math.MAX_SAFE_INTEGER) { 17 | this.origin.setTo(x, y); 18 | this.angle = Phaser.Math.Angle.Normalize(angle); 19 | this.rayRange = rayRange; 20 | 21 | Phaser.Geom.Line.SetToAngle(this._ray, this.origin.x, this.origin.y, this.angle, this.rayRange); 22 | this.detectionRangeCircle.setTo(this.origin.x, this.origin.y, this.detectionRange); 23 | return this; 24 | } 25 | -------------------------------------------------------------------------------- /src/ray/slice.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Slice ray's field of view represented by polygon or array of points into array of triangles. 3 | * 4 | * @method Raycaster.Ray#slice 5 | * @memberof Raycaster.Ray 6 | * @instance 7 | * @since 0.8.0 8 | * 9 | * @param {(object[]|Phaser.Geom.Polygon)} [fov = {Ray#fov}] - Array of points or polygon representing field of view. If not passed, filed of view from last raycaste will be used. 10 | * @param {boolean} [closed = true|{Ray#fov}] - Define if field of view polygon is closed (first and last vertices sholud be connected). If fov was not passed, value depends of last type of casting. 11 | * 12 | * @return {Phaser.Geom.Triangle[]} Array of triangles representing slices of field of view. 13 | */ 14 | export function slice(intersections = this.intersections, closed = true) { 15 | //if intersections is Phaser.Geom.Polygon object 16 | if(!Array.isArray(intersections)) { 17 | if(intersections.type === 4) 18 | intersections = intersections.points; 19 | else 20 | return []; 21 | } 22 | 23 | if(intersections.length === 0) 24 | return []; 25 | 26 | let slices = []; 27 | for(let i = 0, iLength = intersections.length - 1; i < iLength; i++) { 28 | slices.push(new Phaser.Geom.Triangle(this.origin.x, this.origin.y, intersections[i].x, intersections[i].y, intersections[i+1].x, intersections[i+1].y)); 29 | } 30 | 31 | if(closed) 32 | slices.push(new Phaser.Geom.Triangle(this.origin.x, this.origin.y, intersections[0].x, intersections[0].y, intersections[intersections.length-1].x, intersections[intersections.length-1].y)); 33 | 34 | return slices; 35 | } 36 | -------------------------------------------------------------------------------- /src/ray/stats.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Get ray statistics for last casting. Stats include 3 | * * number of casted rays, 4 | * * number of tested mapped objects, 5 | * * number of tested map segments. 6 | * * casting time 7 | * 8 | * @method Raycaster.Ray#getStats 9 | * @memberof Raycaster.Ray 10 | * @instance 11 | * @since 0.10.0 12 | * 13 | * @return {object} Statisticss from last casting. 14 | */ 15 | export function getStats() { 16 | return this._stats; 17 | } 18 | --------------------------------------------------------------------------------