├── .babelrc ├── .gitignore ├── LICENSE ├── README.md ├── changelog.md ├── package.json ├── quads.js ├── test.js ├── three.js └── types.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["env"] 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | index.js 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 Greg Tatum 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `quads` - Geometry Tools 2 | 3 | This package is a collection of quad geometry creation and manipulation tools. They can be used agnostic to any given library as they only operate on simple arrays and objects. Please note that this package is an early release, and the APIs may stabilize over time. More rigorous testing is also in the works. 4 | 5 | ## Types 6 | 7 | 8 | 9 | ### SimplicialComplex 10 | 11 | This is a complicated math word that means an object with `{ positions, cells }`. The 12 | word `mesh` is used for convenience in this module, and `normals` are included with 13 | this object. 14 | 15 | ```javascript 16 | // A single quad oriented facing up. 17 | const mesh = { 18 | positions: [[-1, 0, -1], [-1, 0, 1], [1, 0, 1], [1, 0, -1]], 19 | normals: [[0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0]], 20 | cells: [[0, 1, 2, 3]] 21 | } 22 | ``` 23 | 24 | Additional attributes may be added for one's own applications. For example: 25 | 26 | ```javascript 27 | mesh.colors = mesh.positions.map(p => [0, p.y, 0]) 28 | ``` 29 | 30 | Type: [Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object) 31 | 32 | **Properties** 33 | 34 | - `positions` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)<[Position](#position)>** 35 | - `cells` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)<[Cell](#cell)>** 36 | - `normals` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)<[Normal](#normal)>** 37 | 38 | ### Position 39 | 40 | An array of 3 values representing a position [x, y, z]. 41 | 42 | Type: [Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array) 43 | 44 | ### Cell 45 | 46 | In a simplicial complex, a cell is an array of of indices that refer to a position or 47 | some other attribute like normals. Quads have 4 indices, and this module uses the 48 | convention of `[a, b, c, d]` with clockwise winding order. 49 | 50 | b-------c 51 | | | 52 | | | 53 | a-------d 54 | 55 | Type: [Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array) 56 | 57 | ### Normal 58 | 59 | An array of 3 values, [x, y, z] representing a surface normal. A valid normal has a 60 | length of 1. Normals are used for lighting calculation, and for knowing which way a 61 | surface is oriented in space. Many operation rely on valid normals. 62 | 63 | Type: [Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array) 64 | 65 | ## API 66 | 67 | 68 | 69 | ### averageNormalForPosition 70 | 71 | Computes the average normal for a position given the connected cells. 72 | 73 | **Parameters** 74 | 75 | - `mesh` **SimplicialComplex** 76 | - `positionIndex` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** 77 | - `target` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)?** = \[] 78 | - `normalCache` **[Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map)?** A Map can be provided to cache intermediate normal computations. 79 | - `positionIndexToCells` **[Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map)?** A Map where positionIndex is mapped to its cell, used primarily internally. 80 | 81 | Returns **Normal** target 82 | 83 | ### clone 84 | 85 | Clones a cell. Returns the new cell. 86 | 87 | **Parameters** 88 | 89 | - `mesh` **SimplicialComplex** 90 | - `cell` **Cell** 91 | 92 | Returns **Cell** cloned cell 93 | 94 | ### computeCellCenter 95 | 96 | Computes the center of a single cell. 97 | 98 | **Parameters** 99 | 100 | - `mesh` **SimplicialComplex** 101 | - `cell` **Cell** 102 | 103 | Returns **Position** center 104 | 105 | ### computeCenterPositions 106 | 107 | Computes all of the centers of all the cells. 108 | 109 | **Parameters** 110 | 111 | - `mesh` **SimplicialComplex** 112 | 113 | Returns **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)<Position>** centers 114 | 115 | ### computeNormals 116 | 117 | Updates all of the normals for all the positions using 118 | [#averageNormalForPosition](#averageNormalForPosition). If a normal doesn't exist, 119 | then it is created. 120 | 121 | **Parameters** 122 | 123 | - `mesh` **SimplicialComplex** 124 | 125 | Returns **SimplicialComplex** 126 | 127 | ### createBox 128 | 129 | Creates a quad box of the given dimensions. This box will render as a 130 | smoothed out box, as the normals are averaged. This is typically used for a 131 | starting place for subdividing or extrusion operations. If the 132 | `optionalMesh` object is passed, then the box will be created inside of 133 | that simplicial complex, otherwise a new mesh simplicial complex will be 134 | generated. 135 | 136 | **Parameters** 137 | 138 | - `x` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** = 1 139 | - `y` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** = 1 140 | - `z` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** = 1 141 | - `optionalMesh` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)?** 142 | 143 | Returns **SimplicialComplex** 144 | 145 | ### createBoxDisjoint 146 | 147 | Creates a quad box of the given dimensions, but with non-joined positions. 148 | This box renders as a flat shaded box. If the optionalMesh object is 149 | passed, then the box will be created inside of that simplicial complex, 150 | otherwise a new mesh simplicial complex will be generated. 151 | 152 | **Parameters** 153 | 154 | - `x` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?= 1** 155 | - `y` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?= 1** 156 | - `z` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?= 1** 157 | - `optionalMesh` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)?** 158 | 159 | Returns **SimplicialComplex** 160 | 161 | ### createQuad 162 | 163 | Create a quad with options. If the optionalMesh object is passed, then the 164 | quad will be created inside of that simplicial complex, otherwise a new 165 | mesh simplicial complex will be generated. Both the mesh simplicial 166 | complex and the created cell are returned in an object. 167 | 168 | **Parameters** 169 | 170 | - `options` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** 171 | - `mesh` **SimplicialComplex?= {}** 172 | 173 | **Examples** 174 | 175 | _Usage:_ 176 | 177 | ```javascript 178 | const {mesh, cell} = createQuad({ positions: [[-1, 0, -1], [-1, 0, 1], [1, 0, 1], [1, 0, -1]] }) 179 | const {mesh, cell} = createQuad({ w: 1, h: 1 }) 180 | const {mesh, cell} = createQuad() 181 | ``` 182 | 183 | Returns **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** `{mesh, cell}` 184 | 185 | ### elementsFromQuads 186 | 187 | Returns an elements array using the given `ArrayType`, which can be used by WebGL. 188 | 189 | **Parameters** 190 | 191 | - `mesh` **SimplicialComplex** 192 | - `drawMode` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)?= 'triangles'** 193 | - `ArrayType` **typeof?= Uint16Array** 194 | 195 | Returns **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)** Elements using the given `ArrayType`, which can be used by WebGL. 196 | 197 | ### extrude 198 | 199 | Given a target cell, first inset it, then move it along the cell's normal 200 | outwards by a given distance. 201 | 202 | **Parameters** 203 | 204 | - `mesh` **SimplicialComplex** 205 | - `cell` **Cell** 206 | - `insetT` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** Value ranged from `0` to `1`, defaults to `0` 207 | - `extrude` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** Distance to extrude, defaults to `0` 208 | 209 | ### extrudeDisjoint 210 | 211 | Given a target cell, first inset it, then move it along the cell's normal 212 | outwards by a given distance, but all new geometry generated will not 213 | share positions. 214 | 215 | **Parameters** 216 | 217 | - `mesh` **SimplicialComplex** 218 | - `cell` **Cell** 219 | - `insetT` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** = 0, ranged from `0` (the edge) to `1` (the center). 220 | - `extrude` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** = 0, the distance to extrude out. 221 | 222 | ### flip 223 | 224 | Flip a cell's normal to point the other way. Returns the cell. 225 | 226 | **Parameters** 227 | 228 | - `mesh` **SimplicialComplex** 229 | - `cell` **Cell** 230 | 231 | Returns **Cell** The cell 232 | 233 | ### getCellFromEdge 234 | 235 | Find a cell given two position indices. Optionally provide a `previousCell` 236 | that will not be matched against. Returns the first cell that matches. 237 | 238 | **Parameters** 239 | 240 | - `mesh` **SimplicialComplex** 241 | - `positionIndexA` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** 242 | - `positionIndexB` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** 243 | - `previousCell` **Cell?** Optional will not be matched against 244 | 245 | Returns **Cell** 246 | 247 | ### getCellNormal 248 | 249 | Compute a cell's normal regardless of it's neighboring cells. 250 | 251 | **Parameters** 252 | 253 | - `mesh` **SimplicialComplex** 254 | - `cell` **Cell** 255 | - `target` **Normal?** **= \[]** 256 | 257 | Returns **Normal** The target normal. 258 | 259 | ### getCellsFromPositionIndex 260 | 261 | Given a position index, find any cells that include it. 262 | 263 | **Parameters** 264 | 265 | - `mesh` **SimplicialComplex** 266 | - `index` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** 267 | - `target` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)<Cell>?= \[]** 268 | 269 | Returns **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)<Cell>** The target cells. 270 | 271 | ### getCenter 272 | 273 | Computes the center of a cell. 274 | 275 | **Parameters** 276 | 277 | - `mesh` **SimplicialComplex** 278 | - `cell` **Cell** 279 | - `target` **Position?= \[]** 280 | 281 | Returns **Position** center 282 | 283 | ### getLoop 284 | 285 | Gets a loop of cells. Given a single cell, start walking in both 286 | directions to select a loop. . 287 | 288 | **Parameters** 289 | 290 | - `mesh` **SimplicialComplex** 291 | - `cell` **Cell** 292 | - `type` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** can either be `"cells"`, `"positions"`, or `"normals"`. 293 | - `opposite` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** will walk in the opposite direction, e.g. up and down, versus left and right 294 | 295 | Returns **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)** an array according to the `type`. 296 | 297 | ### getNewGeometry 298 | 299 | Get all newly created geometry of the given type from whatever arbitrary 300 | operations were done on the mesh. This assumes new geometry was created 301 | and not destroyed. 302 | 303 | **Parameters** 304 | 305 | - `mesh` **SimplicialComplex** 306 | - `key` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** 307 | - `callback` **[Function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function)** 308 | 309 | **Examples** 310 | 311 | _Usage:_ 312 | 313 | ```javascript 314 | const extrudedCells = quad.getNewGeometry(mesh, "cells", () => { 315 | quad.extrude(mesh, tipCell, 0.5, 3) 316 | }); 317 | ``` 318 | 319 | Returns **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)** 320 | 321 | ### inset 322 | 323 | Inset a cell some value between `0` (its edges) and `1` (its center). 324 | 325 | b----------c 326 | |\ q1 /| 327 | | \ / | 328 | | f----g | 329 | |q0| tC |q2| tc = targetCell 330 | | e----h | 331 | | / \ | 332 | |/ q3 \| 333 | a----------d 334 | 335 | **Parameters** 336 | 337 | - `mesh` **SimplicialComplex** 338 | - `cell` **Cell** 339 | - `t` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** Specifies where the split should be. Ranged from `0` to `1`, defaults to `0`. 340 | 341 | Returns **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)<Cell>** cells `[q0, q1, q2, q3, targetCell]` 342 | 343 | ### insetDisjoint 344 | 345 | Inset a cell some value between `0` (its edges) and `1` (its center), but 346 | keep the new cells disjoint so they do not share any positions. 347 | 348 | bT----------cT 349 | bL \ qT / cR 350 | |\ \ / /| 351 | | \ fT----gT / | 352 | | fL fM----gM gR | 353 | |qL| | tC | |qR| tC = targetCell 354 | | eL eM----hM hR | 355 | | / eB----hB \ | 356 | |/ / \ \| 357 | aL / qB \ dR 358 | aB----------dB 359 | 360 | **Parameters** 361 | 362 | - `mesh` **SimplicialComplex** 363 | - `cell` **Cell** 364 | - `t` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** value between 0 - 1 (optional, default `0`) 365 | 366 | Returns **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)<Cell>** cells `[qL, qT, qR, qB, targetCell]`. 367 | 368 | ### insetLoop 369 | 370 | Given a cell, walk a loop and inset the loop, where 0 is the inset being on 371 | the edge, and 1 the inset being in the enter. Setting opposite to true will 372 | make the cell walk the loop in the opposite direction, e.g. up/down rather 373 | than left/right. 374 | 375 | *----*----*----*----*----*----*----*----*----* 376 | | | | | | | | | | | 377 | | | |<---|----|----|----|--->| | | 378 | | | | | |cell| | | | | 379 | | | |<---|----|----|----|--->| | | 380 | | | | | | | | | | | 381 | *----*----*----*----*----*----*----*----*----* 382 | 383 | **Parameters** 384 | 385 | - `mesh` **SimplicialComplex** 386 | - `cell` **Cell** 387 | - `t` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?= 0.5** Specifies where the split should be. Ranged from `0` to `1` 388 | - `opposite` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** will walk in the opposite direction, e.g. up and down, versus left and right 389 | 390 | Returns **SimplicialComplex** 391 | 392 | ### mergePositions 393 | 394 | Combine all positions together and recompute the normals. 395 | 396 | **Parameters** 397 | 398 | - `mesh` **SimplicialComplex** 399 | 400 | Returns **SimplicialComplex** 401 | 402 | ### mirror 403 | 404 | Clone all existing geometry, and mirror it about the given axis. 405 | 406 | **Parameters** 407 | 408 | - `mesh` **SimplicialComplex** 409 | - `cells` **Cell** 410 | - `axis` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** is either `0`, `1`, or `2`, which represents the `x`, `y`, and `z` axis respectively. 411 | 412 | ### splitHorizontal 413 | 414 | Split a cell horizontally. 415 | 416 | b--------c 417 | | | 418 | ab------cd 419 | | | 420 | a--------d 421 | 422 | **Parameters** 423 | 424 | - `mesh` **SimplicialComplex** 425 | - `cell` **Cell** 426 | - `t` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?= 0.5** Specifies where the split should be. Ranged from `0` to `1` 427 | - `targetCell` 428 | 429 | ### splitHorizontalDisjoint 430 | 431 | Split a cell horizontally into two new disconnected cells. 432 | 433 | b--------c 434 | | | 435 | ab1----cd1 436 | ab2----cd2 437 | | target | 438 | a--------d 439 | 440 | **Parameters** 441 | 442 | - `mesh` **SimplicialComplex** 443 | - `cell` **Cell** 444 | - `t` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?= 0.5** Specifies where the split should be. Ranged from `0` to `1` 445 | - `targetCell` 446 | 447 | ### splitLoop 448 | 449 | Given a cell, walk along the mesh in both directions and split the cell. 450 | 451 | *--------*--------*--------*--------*--------*--------*--------* 452 | | | | | | | | | 453 | * *<-------*--------*--cell--*--------*------->* * 454 | | | | | | | | | 455 | *--------*--------*--------*--------*--------*--------*--------* 456 | 457 | **Parameters** 458 | 459 | - `mesh` **SimplicialComplex** 460 | - `cell` **Cell** 461 | - `t` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?= 0.5** Specifies where the split should be. Ranged from `0` to `1` 462 | - `opposite` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** will walk in the opposite direction, e.g. up and down, versus left and right 463 | 464 | Returns **SimplicialComplex** 465 | 466 | ### splitVertical 467 | 468 | Split a cell horizontally. 469 | 470 | b---bc---c 471 | | | | 472 | | | | 473 | a---ad---d 474 | 475 | **Parameters** 476 | 477 | - `mesh` **SimplicialComplex** 478 | - `cell` **Cell** 479 | - `t` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?= 0.5** Specifies where the split should be. Ranged from `0` to `1`, defaults to `0.5`. 480 | - `targetCell` 481 | 482 | ### splitVerticalDisjoint 483 | 484 | Split a cell horizontally into two new disconnected cells. 485 | 486 | b---bc1 bc2---c 487 | | | | | 488 | | | | | 489 | a---ad1 ad2---d 490 | 491 | **Parameters** 492 | 493 | - `mesh` **SimplicialComplex** 494 | - `cell` **Cell** 495 | - `t` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?= 0.5** Specifies where the split should be. Ranged from `0` to `1`, defaults to `0.5`. 496 | - `targetCell` 497 | 498 | ### subdivide 499 | 500 | Use catmull clark subdivision to smooth out the geometry. All normals will 501 | be recomputed. Under the hood this is a convenience function for the 502 | module [gl-catmull-clark](https://www.npmjs.com/package/gl-catmull-clark). 503 | 504 | **Parameters** 505 | 506 | - `mesh` **SimplicialComplex** 507 | - `subdivisions` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** 508 | - `positions` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)<Position>?= mesh.positions** 509 | - `cells` **Cell?= mesh.cells** 510 | 511 | Returns **SimplicialComplex** 512 | 513 | ### updateNormals 514 | 515 | Updates all of the normals for all the positions using 516 | [#averageNormalForPosition](#averageNormalForPosition). If a normal doesn't exist, 517 | then it is created. 518 | 519 | **Parameters** 520 | 521 | - `mesh` **SimplicialComplex** 522 | - `cell` **Cell** 523 | 524 | Returns **SimplicialComplex** 525 | 526 | ## Three 527 | 528 | 529 | 530 | ### splitVertical 531 | 532 | Split a cell horizontally. 533 | 534 | b---bc---c 535 | | | | 536 | | | | 537 | a---ad---d 538 | 539 | **Parameters** 540 | 541 | - `$0` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** quad 542 | - `$0.positions` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)** 543 | - `$0.cells` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)** 544 | - `targetCell` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)** 545 | - `t` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** Specifies where the split should be. Ranged from `0` to `1` 546 | - `_ref` 547 | 548 | ### splitVerticalDisjoint 549 | 550 | Split a cell horizontally into two new disconnected cells. 551 | 552 | b---bc1 bc2---c 553 | | | | | 554 | | | | | 555 | a---ad1 ad2---d 556 | 557 | **Parameters** 558 | 559 | - `mesh` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** 560 | - `targetCell` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)** 561 | - `t` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** Specifies where the split should be. Ranged from `0` to `1` 562 | 563 | ### splitHorizontal 564 | 565 | Split a cell horizontally. 566 | 567 | b--------c 568 | | | 569 | ab------cd 570 | | | 571 | a--------d 572 | 573 | **Parameters** 574 | 575 | - `$0.positions` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)** 576 | - `$0.cells` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)** 577 | - `targetCell` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)** 578 | - `t` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** Specifies where the split should be. Ranged from `0` to `1` 579 | - `_ref2` 580 | 581 | ### splitHorizontalDisjoint 582 | 583 | Split a cell horizontally into two new disconnected cells. 584 | 585 | b--------c 586 | | | 587 | ab1----cd1 588 | ab2----cd2 589 | | target | 590 | a--------d 591 | 592 | **Parameters** 593 | 594 | - `mesh` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** 595 | - `targetCell` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)** 596 | - `t` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** Specifies where the split should be. Ranged from `0` to `1` 597 | 598 | ### inset 599 | 600 | Inset a cell some value between `0` (its edges) and `1` (its center). 601 | 602 | b----------c 603 | |\ q1 /| 604 | | \ / | 605 | | f----g | 606 | |q0| tQ |q2| 607 | | e----h | 608 | | / \ | 609 | |/ q3 \| 610 | a----------d 611 | 612 | **Parameters** 613 | 614 | - `mesh` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** 615 | - `targetCell` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)** 616 | - `t` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** Specifies where the split should be. Ranged from `0` to `1` 617 | 618 | Returns **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)** cells `[q0, q1, q2, q3, tC]` where `tC` is the `targetCell`. 619 | 620 | ### extrude 621 | 622 | Given a target cell, first inset it, then move it along the cell's normal 623 | outwards by a given distance. 624 | 625 | **Parameters** 626 | 627 | - `mesh` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** 628 | - `targetCell` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)** 629 | - `insetT` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** 630 | - `extrude` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** 631 | 632 | ### averageNormalForPosition 633 | 634 | Computes the average normal for a position given the connected cells. 635 | 636 | **Parameters** 637 | 638 | - `mesh` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** 639 | - `positionIndex` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** 640 | - `target` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)?** = \[] 641 | - `normalCache` **[Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map)?** = new Map() can be provided to cache intermediate normal computations. 642 | - `positionIndexToCells` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** 643 | 644 | Returns **any** the `targetNormal` 645 | 646 | ### insetDisjoint 647 | 648 | Inset a cell some value between `0` (its edges) and `1` (its center), but 649 | keep the new cells disjoint so they do not share any positions. 650 | 651 | bT----------cT 652 | bL \ qT / cR 653 | |\ \ / /| 654 | | \ fT----gT / | 655 | | fL fM----gM gR | 656 | |qL| | tC | |qR| tC = targetCell 657 | | eL eM----hM hR | 658 | | / eB----hB \ | 659 | |/ / \ \| 660 | aL / qB \ dR 661 | aB----------dB 662 | 663 | **Parameters** 664 | 665 | - `mesh` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** 666 | - `targetCell` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)** 667 | - `t` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** **=0** 668 | 669 | Returns **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)** cells `[q0, q1, q2, q3, tC]` where `tC` is the `targetCell`. 670 | 671 | ### extrudeDisjoint 672 | 673 | Given a target cell, first inset it, then move it along the cell's normal 674 | outwards by a given distance, but all new geometry generated will not 675 | share positions. 676 | 677 | **Parameters** 678 | 679 | - `mesh` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** 680 | - `targetCell` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)** 681 | - `insetT` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** 682 | - `extrude` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** 683 | 684 | ### getCenter 685 | 686 | Computes the center of a cell 687 | 688 | **Parameters** 689 | 690 | - `mesh` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** 691 | - `cell` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)** 692 | - `target` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)** 693 | 694 | Returns **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)** the targetPosition 695 | 696 | ### clone 697 | 698 | Clones a cell. Returns the new cell. 699 | 700 | **Parameters** 701 | 702 | - `mesh` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** 703 | - `cell` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)** 704 | 705 | Returns **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)** a new cell 706 | 707 | ### updateNormals 708 | 709 | Updates all of the normals for all the positions using 710 | [#averageNormalForPosition](#averageNormalForPosition). If a normal doesn't exist, 711 | then it is created. 712 | 713 | **Parameters** 714 | 715 | - `mesh` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** 716 | - `cell` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)** 717 | 718 | Returns **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** mesh 719 | 720 | ### getCellNormal 721 | 722 | Compute a cell's normal regardless of it's neighboring cells. 723 | 724 | **Parameters** 725 | 726 | - `mesh` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** 727 | - `cell` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)** 728 | - `target` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)?** **= \[]** 729 | 730 | Returns **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)** The target normal. 731 | 732 | ### getCellsFromPositionIndex 733 | 734 | Given a position index, find any cells that include it. 735 | 736 | **Parameters** 737 | 738 | - `mesh` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** 739 | - `index` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** 740 | - `target` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)** 741 | 742 | Returns **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)** The target cells. 743 | 744 | ### flip 745 | 746 | Flip a cell's normal to point the other way. Returns the cell. 747 | 748 | **Parameters** 749 | 750 | - `mesh` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** 751 | - `cell` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)** 752 | 753 | Returns **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)** The cell 754 | 755 | ### createQuad 756 | 757 | Create a quad with options. If the optionalMesh object is passed, then the 758 | quad will be created inside of that simplicial complex, otherwise a new 759 | mesh simplicial complex will be generated. Both the mesh simplicial 760 | complex and the created cell are returned in an object. 761 | 762 | **Parameters** 763 | 764 | - `options` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** 765 | - `mesh` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** 766 | 767 | **Examples** 768 | 769 | _Usage:_ 770 | 771 | ```javascript 772 | const {mesh, cell} = createQuad({ positions: [[-1, 0, -1], [-1, 0, 1], [1, 0, 1], [1, 0, -1]] }) 773 | const {mesh, cell} = createQuad({ w: 1, h: 1 }) 774 | const {mesh, cell} = createQuad() 775 | ``` 776 | 777 | Returns **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** `{mesh, cell}` 778 | 779 | ### createBoxDisjoint 780 | 781 | Creates a quad box of the given dimensions, but with non-joined positions. 782 | This box renders as a flat shaded box. If the optionalMesh object is 783 | passed, then the box will be created inside of that simplicial complex, 784 | otherwise a new mesh simplicial complex will be generated. 785 | 786 | **Parameters** 787 | 788 | - `x` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** 789 | - `y` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** 790 | - `z` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** 791 | - `optionalMesh` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** 792 | 793 | Returns **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** a simplicial complex 794 | 795 | ### createBox 796 | 797 | Creates a quad box of the given dimensions. This box will render as a 798 | smoothed out box, as the normals are averaged. This is typically used for a 799 | starting place for subdividing or extrusion operations. If the 800 | `optionalMesh` object is passed, then the box will be created inside of 801 | that simplicial complex, otherwise a new mesh simplicial complex will be 802 | generated. 803 | 804 | **Parameters** 805 | 806 | - `x` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** 807 | - `y` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** 808 | - `z` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** 809 | - `optionalMesh` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** 810 | 811 | Returns **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** a simplicial complex 812 | 813 | ### mergePositions 814 | 815 | Combine all positions together and recompute the normals. 816 | 817 | **Parameters** 818 | 819 | - `mesh` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** 820 | 821 | Returns **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** mesh 822 | 823 | ### elementsFromQuads 824 | 825 | Returns an elements array using the given `ArrayType`, which can be used by WebGL. 826 | 827 | **Parameters** 828 | 829 | - `mesh` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** 830 | - `drawMode` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** 831 | - `ArrayType` **typeof** 832 | 833 | Returns **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)** Elements using the given `ArrayType`, which can be used by WebGL. 834 | 835 | ### computeNormals 836 | 837 | Updates all of the normals for all the positions using 838 | [#averageNormalForPosition](#averageNormalForPosition). If a normal doesn't exist, 839 | then it is created. 840 | 841 | **Parameters** 842 | 843 | - `mesh` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** 844 | 845 | Returns **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** The mesh 846 | 847 | ### splitLoop 848 | 849 | Given a cell, walk along the mesh in both directions and split the cell. 850 | 851 | *--------*--------*--------*--------*--------*--------*--------* 852 | | | | | | | | | 853 | * *<-------*--------*--cell--*--------*------->* * 854 | | | | | | | | | 855 | *--------*--------*--------*--------*--------*--------*--------* 856 | 857 | **Parameters** 858 | 859 | - `mesh` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** 860 | - `cell` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)** 861 | - `t` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** Specifies where the split should be. Ranged from `0` to `1` 862 | - `opposite` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** will walk in the opposite direction, e.g. up and down, versus left and right 863 | 864 | Returns **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** mesh 865 | 866 | ### getCellFromEdge 867 | 868 | Find a cell given two position indices. Optionally provide a `previousCell` 869 | that will not be matched against. Returns the first cell that matches. 870 | 871 | **Parameters** 872 | 873 | - `mesh` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** 874 | - `positionIndexA` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** 875 | - `positionIndexB` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** 876 | - `previousCell` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)?** Optional will not be matched against 877 | 878 | Returns **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)** Elements using the given `ArrayType`, which can be used by WebGL. 879 | 880 | ### getNewGeometry 881 | 882 | Get all newly created geometry of the given type from whatever arbitrary 883 | operations were done on the mesh. This assumes new geometry was created 884 | and not destroyed. 885 | 886 | **Parameters** 887 | 888 | - `mesh` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** 889 | - `key` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** 890 | - `callback` **[Function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function)** 891 | 892 | **Examples** 893 | 894 | _Usage:_ 895 | 896 | ```javascript 897 | const extrudedCells = quad.getNewGeometry(mesh, "cells", () => { 898 | quad.extrude(mesh, tipCell, 0.5, 3) 899 | }); 900 | ``` 901 | 902 | Returns **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)** 903 | 904 | ### subdivide 905 | 906 | Use catmull clark subdivision to smooth out the geometry. All normals will 907 | be recomputed. Under the hood this is a convenience function for the 908 | module [gl-catmull-clark](https://www.npmjs.com/package/gl-catmull-clark). 909 | 910 | **Parameters** 911 | 912 | - `mesh` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** 913 | - `subdivisions` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** 914 | - `positions` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)** 915 | - `cells` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)** 916 | 917 | Returns **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** mesh 918 | 919 | ### computeCenterPositions 920 | 921 | Computes all of the centers of all the cells. 922 | 923 | **Parameters** 924 | 925 | - `mesh` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** 926 | 927 | Returns **any** A new array 928 | 929 | ### computeCellCenter 930 | 931 | Computes the center of a single cell. 932 | 933 | **Parameters** 934 | 935 | - `mesh` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** 936 | - `cell` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)** 937 | 938 | Returns **any** A new array 939 | 940 | ### insetLoop 941 | 942 | Given a cell, walk a loop and inset the loop, where 0 is the inset being on 943 | the edge, and 1 the inset being in the enter. Setting opposite to true will 944 | make the cell walk the loop in the opposite direction, e.g. up/down rather 945 | than left/right. 946 | 947 | *----*----*----*----*----*----*----*----*----* 948 | | | | | | | | | | | 949 | | | |<---|----|----|----|--->| | | 950 | | | | | |cell| | | | | 951 | | | |<---|----|----|----|--->| | | 952 | | | | | | | | | | | 953 | *----*----*----*----*----*----*----*----*----* 954 | 955 | **Parameters** 956 | 957 | - `mesh` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** 958 | - `cell` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)** 959 | - `t` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** Specifies where the split should be. Ranged from `0` to `1` 960 | - `opposite` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** will walk in the opposite direction, e.g. up and down, versus left and right 961 | 962 | Returns **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** mesh 963 | 964 | ### getLoop 965 | 966 | Gets a loop of cells. Given a single cell, start walking in both 967 | directions to select a loop. . 968 | 969 | **Parameters** 970 | 971 | - `mesh` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** 972 | - `cell` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)** 973 | - `type` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** can either be `"cells"`, `"positions"`, or `"normals"`. 974 | - `opposite` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** will walk in the opposite direction, e.g. up and down, versus left and right 975 | 976 | Returns **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)** an array according to the `type`. 977 | 978 | ### mirror 979 | 980 | Clone all existing geometry, and mirror it about the given axis. 981 | 982 | **Parameters** 983 | 984 | - `mesh` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** 985 | - `cells` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)** 986 | - `axis` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** is either `0`, `1`, or `2`, which represents the `x`, `y`, and `z` axis respectively. 987 | -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 2017-02-09 4 | 5 | Added a cloneCells command. 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "quads", 3 | "version": "1.2.0", 4 | "description": "Quad geometry tools", 5 | "main": "index.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/gregtatum/quads" 9 | }, 10 | "scripts": { 11 | "test": "node test | faucet", 12 | "build": "babel quads.js > index.js", 13 | "prepublish": "npm run build", 14 | "docs": "npm run docs-source && npm run docs-types && npm run docs-three", 15 | "docs-source": "documentation readme --sort-order=alpha --section=API quads.js", 16 | "docs-types": "documentation readme --sort-order=source --section=Types types.js", 17 | "docs-three": "documentation readme --sort-order=source --section=Three three.js", 18 | "lint": "standard" 19 | }, 20 | "keywords": [ 21 | "webgl", 22 | "stackgl", 23 | "quads", 24 | "gl", 25 | "geometry", 26 | "model", 27 | "mesh" 28 | ], 29 | "author": "Greg Tatum", 30 | "license": "MIT", 31 | "dependencies": { 32 | "gl-catmull-clark": "^1.0.0", 33 | "gl-vec3": "^1.0.3" 34 | }, 35 | "devDependencies": { 36 | "babel-cli": "^6.22.2", 37 | "babel-preset-env": "^1.1.8", 38 | "documentation": "^4.0.0-beta.18", 39 | "faucet": "0.0.1", 40 | "tape": "^4.6.3" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /quads.js: -------------------------------------------------------------------------------- 1 | const vec3 = require('gl-vec3') 2 | const catmullClark = require('gl-catmull-clark') 3 | 4 | /** 5 | * Split a cell horizontally. 6 | * 7 | * ``` 8 | * b---bc---c 9 | * | | | 10 | * | | | 11 | * a---ad---d 12 | * ``` 13 | * 14 | * @param {SimplicialComplex} mesh 15 | * @param {Cell} cell 16 | * @param {Number} t Specifies where the split should be. Ranged from `0` to `1`, defaults to `0.5`. 17 | */ 18 | function splitVertical (mesh, targetCell, t = 0.5) { 19 | const {positions, cells} = mesh 20 | const [a, b, c, d] = targetCell 21 | const positionA = positions[a] 22 | const positionB = positions[b] 23 | const positionC = positions[c] 24 | const positionD = positions[d] 25 | const bcPosition = vec3.lerp([], positionB, positionC, t) 26 | const adPosition = vec3.lerp([], positionA, positionD, t) 27 | const bc = positions.length 28 | const ad = bc + 1 29 | positions[bc] = bcPosition 30 | positions[ad] = adPosition 31 | targetCell[2] = bc 32 | targetCell[3] = ad 33 | cells.push([ad, bc, c, d]) 34 | } 35 | 36 | /** 37 | * Split a cell horizontally into two new disconnected cells. 38 | * 39 | * ``` 40 | * b---bc1 bc2---c 41 | * | | | | 42 | * | | | | 43 | * a---ad1 ad2---d 44 | * ``` 45 | * 46 | * @param {SimplicialComplex} mesh 47 | * @param {Cell} cell 48 | * @param {Number} t Specifies where the split should be. Ranged from `0` to `1`, defaults to `0.5`. 49 | */ 50 | function splitVerticalDisjoint (mesh, targetCell, t = 0.5) { 51 | const {positions, cells, normals} = mesh 52 | const [a, b, c, d] = targetCell 53 | const bc1 = positions.length 54 | const ad1 = bc1 + 1 55 | const bc2 = bc1 + 2 56 | const ad2 = bc1 + 3 57 | 58 | // Add the positions 59 | const bcPosition = vec3.lerp([], positions[b], positions[c], t) 60 | const adPosition = vec3.lerp([], positions[a], positions[d], t) 61 | positions[bc1] = bcPosition 62 | positions[ad1] = adPosition 63 | positions[bc2] = bcPosition.slice() 64 | positions[ad2] = adPosition.slice() 65 | 66 | // Update the cells 67 | targetCell[2] = bc1 68 | targetCell[3] = ad1 69 | cells.push([ad2, bc2, c, d]) 70 | 71 | // Normals - assume that disjoint splits all share the same normal. 72 | const normal = normals[a] 73 | normals[ad1] = normal.slice() 74 | normals[ad2] = normal.slice() 75 | normals[bc1] = normal.slice() 76 | normals[bc2] = normal.slice() 77 | } 78 | 79 | /** 80 | * Split a cell horizontally. 81 | * 82 | * ``` 83 | * b--------c 84 | * | | 85 | * ab------cd 86 | * | | 87 | * a--------d 88 | * ``` 89 | * 90 | * @param {SimplicialComplex} mesh 91 | * @param {Cell} cell 92 | * @param {Number} t Specifies where the split should be. Ranged from `0` to `1` 93 | */ 94 | function splitHorizontal (mesh, targetCell, t = 0.5) { 95 | const {positions, cells} = mesh 96 | const [a, b, c, d] = targetCell 97 | const positionA = positions[a] 98 | const positionB = positions[b] 99 | const positionC = positions[c] 100 | const positionD = positions[d] 101 | const abPosition = vec3.lerp([], positionB, positionA, t) 102 | const cdPosition = vec3.lerp([], positionC, positionD, t) 103 | const ab = positions.length 104 | const cd = ab + 1 105 | positions[ab] = abPosition 106 | positions[cd] = cdPosition 107 | targetCell[1] = ab 108 | targetCell[2] = cd 109 | cells.push([ab, b, c, cd]) 110 | } 111 | 112 | /** 113 | * Split a cell horizontally into two new disconnected cells. 114 | * 115 | * ``` 116 | * b--------c 117 | * | | 118 | * ab1----cd1 119 | * ab2----cd2 120 | * | target | 121 | * a--------d 122 | * ``` 123 | * 124 | * @param {SimplicialComplex} mesh 125 | * @param {Cell} cell 126 | * @param {Number} t Specifies where the split should be. Ranged from `0` to `1` 127 | */ 128 | function splitHorizontalDisjoint (mesh, targetCell, t = 0.5) { 129 | const {positions, cells, normals} = mesh 130 | const [a, b, c, d] = targetCell 131 | const ab1 = positions.length 132 | const cd1 = ab1 + 1 133 | const ab2 = ab1 + 2 134 | const cd2 = ab1 + 3 135 | 136 | // Positions 137 | const abPosition = vec3.lerp([], positions[a], positions[b], t) 138 | const cdPosition = vec3.lerp([], positions[d], positions[c], t) 139 | positions[ab1] = abPosition 140 | positions[cd1] = cdPosition 141 | positions[ab2] = abPosition.slice() 142 | positions[cd2] = cdPosition.slice() 143 | 144 | // Cells 145 | targetCell[0] = ab1 146 | targetCell[3] = cd1 147 | cells.push([a, ab2, cd2, d]) 148 | 149 | // Normals - assume that disjoint splits all share the same normal. 150 | const normal = normals[a] 151 | normals[ab1] = normal.slice() 152 | normals[cd1] = normal.slice() 153 | normals[ab2] = normal.slice() 154 | normals[cd2] = normal.slice() 155 | } 156 | 157 | /** 158 | * Inset a cell some value between `0` (its edges) and `1` (its center). 159 | * 160 | * ``` 161 | * b----------c 162 | * |\ q1 /| 163 | * | \ / | 164 | * | f----g | 165 | * |q0| tC |q2| tc = targetCell 166 | * | e----h | 167 | * | / \ | 168 | * |/ q3 \| 169 | * a----------d 170 | * ``` 171 | * 172 | * @param {SimplicialComplex} mesh 173 | * @param {Cell} cell 174 | * @param {Number} t Specifies where the split should be. Ranged from `0` to `1`, defaults to `0`. 175 | * @returns {Cell[]} cells `[q0, q1, q2, q3, targetCell]` 176 | */ 177 | var inset = (() => { 178 | var center = [0, 0, 0] 179 | return function (mesh, targetCell, t = 0) { 180 | const {positions, cells, normals} = mesh 181 | const [a, b, c, d] = targetCell 182 | const e = positions.length 183 | const f = e + 1 184 | const g = f + 1 185 | const h = g + 1 186 | const positionA = positions[a] 187 | const positionB = positions[b] 188 | const positionC = positions[c] 189 | const positionD = positions[d] 190 | 191 | // Update positions 192 | center[0] = (positionA[0] + positionB[0] + positionC[0] + positionD[0]) / 4 193 | center[1] = (positionA[1] + positionB[1] + positionC[1] + positionD[1]) / 4 194 | center[2] = (positionA[2] + positionB[2] + positionC[2] + positionD[2]) / 4 195 | positions.push(vec3.lerp([], positionA, center, t)) 196 | positions.push(vec3.lerp([], positionB, center, t)) 197 | positions.push(vec3.lerp([], positionC, center, t)) 198 | positions.push(vec3.lerp([], positionD, center, t)) 199 | normals.push(normals[a].slice()) 200 | normals.push(normals[b].slice()) 201 | normals.push(normals[c].slice()) 202 | normals.push(normals[d].slice()) 203 | 204 | // Update cells 205 | targetCell[0] = e 206 | targetCell[1] = f 207 | targetCell[2] = g 208 | targetCell[3] = h 209 | const q0 = [a, b, f, e] 210 | const q1 = [f, b, c, g] 211 | const q2 = [h, g, c, d] 212 | const q3 = [a, e, h, d] 213 | cells.push(q0) 214 | cells.push(q1) 215 | cells.push(q2) 216 | cells.push(q3) 217 | return [q0, q1, q2, q3, targetCell] 218 | } 219 | })() 220 | 221 | /** 222 | * Given a target cell, first inset it, then move it along the cell's normal 223 | * outwards by a given distance. 224 | * 225 | * @param {SimplicialComplex} mesh 226 | * @param {Cell} cell 227 | * @param {Number} insetT Value ranged from `0` to `1`, defaults to `0` 228 | * @param {Number} extrude Distance to extrude, defaults to `0` 229 | */ 230 | var extrude = (() => { 231 | const toTranslate = [] 232 | const translation = [] 233 | const targetCellNormal = [] 234 | return function (mesh, targetCell, insetT = 0, extrude = 0) { 235 | const {positions, normals} = mesh 236 | const ring = inset(mesh, targetCell, insetT) 237 | const [qL, qT, qR, qB] = ring 238 | 239 | // Enumerate which positions to translate 240 | toTranslate[0] = targetCell[0] 241 | toTranslate[1] = targetCell[1] 242 | toTranslate[2] = targetCell[2] 243 | toTranslate[3] = targetCell[3] 244 | 245 | toTranslate[4] = qL[2] 246 | toTranslate[5] = qL[3] 247 | 248 | toTranslate[6] = qT[0] 249 | toTranslate[7] = qT[3] 250 | 251 | toTranslate[8] = qR[0] 252 | toTranslate[9] = qR[1] 253 | 254 | toTranslate[10] = qB[1] 255 | toTranslate[11] = qB[2] 256 | 257 | getCellNormal(mesh, targetCell, targetCellNormal) 258 | vec3.scale(translation, targetCellNormal, extrude) 259 | 260 | for (let i = 0; i < toTranslate.length; i++) { 261 | const position = positions[toTranslate[i]] 262 | vec3.add(position, position, translation) 263 | } 264 | 265 | // Update all of the affected normals by averaging a position's neighboring 266 | // cell's normals. This will create some intermediate allocations, that will 267 | // then be GCed. 268 | const normalCache = new Map() 269 | normalCache.set(targetCell, targetCellNormal) 270 | const [a, b, c, d] = targetCell 271 | const e = positions.length - 4 272 | const f = positions.length - 3 273 | const g = positions.length - 2 274 | const h = positions.length - 1 275 | averageNormalForPosition(mesh, a, normals[a], normalCache) 276 | averageNormalForPosition(mesh, b, normals[b], normalCache) 277 | averageNormalForPosition(mesh, c, normals[c], normalCache) 278 | averageNormalForPosition(mesh, d, normals[d], normalCache) 279 | averageNormalForPosition(mesh, e, normals[e], normalCache) 280 | averageNormalForPosition(mesh, f, normals[f], normalCache) 281 | averageNormalForPosition(mesh, g, normals[g], normalCache) 282 | averageNormalForPosition(mesh, h, normals[h], normalCache) 283 | } 284 | })() 285 | 286 | function _calculatePositionIndexToCells (mesh) { 287 | const toCells = new Map() 288 | for (let i = 0; i < mesh.cells.length; i++) { 289 | const cell = mesh.cells[i] 290 | for (let j = 0; j < cell.length; j++) { 291 | const index = cell[j] 292 | let arr = toCells.get(index) 293 | if (!arr) { 294 | arr = [] 295 | toCells.set(index, arr) 296 | } 297 | arr.push(cell) 298 | } 299 | } 300 | return toCells 301 | } 302 | 303 | /** 304 | * Computes the average normal for a position given the connected cells. 305 | * 306 | * @param {SimplicialComplex} mesh 307 | * @param {Number} positionIndex 308 | * @param {Array?} target = [] 309 | * @param {Map?} normalCache A Map can be provided to cache intermediate normal computations. 310 | * @param {Map?} positionIndexToCells A Map where positionIndex is mapped to its cell, used primarily internally. 311 | * @returns {Normal} target 312 | */ 313 | var averageNormalForPosition = (() => { 314 | const cellsCache = [] 315 | 316 | return function averageNormalForPosition (mesh, positionIndex, target = [], normalCache = new Map(), positionIndexToCells) { 317 | let cells 318 | if (positionIndexToCells) { 319 | cells = positionIndexToCells.get(positionIndex) 320 | } else { 321 | cells = getCellsFromPositionIndex(mesh, positionIndex, cellsCache) 322 | } 323 | vec3.set(target, 0, 0, 0) 324 | 325 | // Add neighboring cells' normals 326 | for (let i = 0; i < cells.length; i++) { 327 | const cell = cells[i] 328 | let normal 329 | if (normalCache) { 330 | normal = normalCache.get(cell) 331 | } 332 | if (!normal) { 333 | normal = getCellNormal(mesh, cell, []) 334 | if (normalCache) { 335 | normalCache.set(normal) 336 | } 337 | } 338 | vec3.add(target, target, normal) 339 | } 340 | vec3.normalize(target, target) 341 | 342 | // Clean out the cellsCache. 343 | while (cellsCache.length) { 344 | cellsCache.pop() 345 | } 346 | return target 347 | } 348 | })() 349 | 350 | /** 351 | * Inset a cell some value between `0` (its edges) and `1` (its center), but 352 | * keep the new cells disjoint so they do not share any positions. 353 | * 354 | * ``` 355 | * bT----------cT 356 | * bL \ qT / cR 357 | * |\ \ / /| 358 | * | \ fT----gT / | 359 | * | fL fM----gM gR | 360 | * |qL| | tC | |qR| tC = targetCell 361 | * | eL eM----hM hR | 362 | * | / eB----hB \ | 363 | * |/ / \ \| 364 | * aL / qB \ dR 365 | * aB----------dB 366 | * ``` 367 | * 368 | * @param {SimplicialComplex} mesh 369 | * @param {Cell} cell 370 | * @param {Number} [t=0] value between 0 - 1 371 | * @returns {Cell[]} cells `[qL, qT, qR, qB, targetCell]`. 372 | */ 373 | var insetDisjoint = (() => { 374 | var center = [0, 0, 0] 375 | return function (mesh, targetCell, t = 0) { 376 | const {positions, cells, normals} = mesh 377 | const [a, b, c, d] = targetCell 378 | const positionA = positions[a] 379 | const positionB = positions[b] 380 | const positionC = positions[c] 381 | const positionD = positions[d] 382 | 383 | // Calculate inset positions 384 | center[0] = (positionA[0] + positionB[0] + positionC[0] + positionD[0]) / 4 385 | center[1] = (positionA[1] + positionB[1] + positionC[1] + positionD[1]) / 4 386 | center[2] = (positionA[2] + positionB[2] + positionC[2] + positionD[2]) / 4 387 | const positionE = vec3.lerp([], positionA, center, t) 388 | const positionF = vec3.lerp([], positionB, center, t) 389 | const positionG = vec3.lerp([], positionC, center, t) 390 | const positionH = vec3.lerp([], positionD, center, t) 391 | 392 | // Assign indices 393 | const offset = positions.length 394 | const aB = offset 395 | const aL = a 396 | const bL = b 397 | const bT = offset + 1 398 | const cT = offset + 2 399 | const cR = c 400 | const dR = d 401 | const dB = offset + 3 402 | const eM = offset + 4 403 | const eB = offset + 5 404 | const eL = offset + 6 405 | const fM = offset + 7 406 | const fL = offset + 8 407 | const fT = offset + 9 408 | const gM = offset + 10 409 | const gT = offset + 11 410 | const gR = offset + 12 411 | const hM = offset + 13 412 | const hR = offset + 14 413 | const hB = offset + 15 414 | 415 | // Update cells 416 | targetCell[0] = eM 417 | targetCell[1] = fM 418 | targetCell[2] = gM 419 | targetCell[3] = hM 420 | const qL = [aL, bL, fL, eL] 421 | const qT = [fT, bT, cT, gT] 422 | const qR = [hR, gR, cR, dR] 423 | const qB = [aB, eB, hB, dB] 424 | cells.push(qL) 425 | cells.push(qT) 426 | cells.push(qR) 427 | cells.push(qB) 428 | 429 | // Update positions 430 | positions[aB] = positionA.slice() 431 | positions[aL] = positionA 432 | positions[bL] = positionB 433 | positions[bT] = positionB.slice() 434 | positions[cT] = positionC.slice() 435 | positions[cR] = positionC 436 | positions[dR] = positionD 437 | positions[dB] = positionD.slice() 438 | positions[eM] = positionE 439 | positions[eB] = positionE.slice() 440 | positions[eL] = positionE.slice() 441 | positions[fM] = positionF 442 | positions[fL] = positionF.slice() 443 | positions[fT] = positionF.slice() 444 | positions[gM] = positionG 445 | positions[gT] = positionG.slice() 446 | positions[gR] = positionG.slice() 447 | positions[hM] = positionH 448 | positions[hR] = positionH.slice() 449 | positions[hB] = positionH.slice() 450 | 451 | // Normals - assume that disjoint mesh all share the same normal. 452 | const normal = normals[a] 453 | normals[aB] = normal.slice() 454 | normals[aL] = normals[a] 455 | normals[bL] = normals[b] 456 | normals[bT] = normal.slice() 457 | normals[cT] = normal.slice() 458 | normals[cR] = normals[c] 459 | normals[dR] = normals[d] 460 | normals[dB] = normal.slice() 461 | normals[eM] = normal.slice() 462 | normals[eB] = normal.slice() 463 | normals[eL] = normal.slice() 464 | normals[fM] = normal.slice() 465 | normals[fL] = normal.slice() 466 | normals[fT] = normal.slice() 467 | normals[gM] = normal.slice() 468 | normals[gT] = normal.slice() 469 | normals[gR] = normal.slice() 470 | normals[hM] = normal.slice() 471 | normals[hR] = normal.slice() 472 | normals[hB] = normal.slice() 473 | 474 | return [qL, qT, qR, qB, targetCell] 475 | } 476 | })() 477 | 478 | /** 479 | * Given a target cell, first inset it, then move it along the cell's normal 480 | * outwards by a given distance, but all new geometry generated will not 481 | * share positions. 482 | * 483 | * @param {SimplicialComplex} mesh 484 | * @param {Cell} cell 485 | * @param {Number} insetT = 0, ranged from `0` (the edge) to `1` (the center). 486 | * @param {Number} extrude = 0, the distance to extrude out. 487 | */ 488 | var extrudeDisjoint = (() => { 489 | const toTranslate = [] 490 | const translation = [] 491 | return function (mesh, targetCell, insetT = 0, extrude = 0) { 492 | const {positions, normals} = mesh 493 | const ring = insetDisjoint(mesh, targetCell, insetT) 494 | const [qL, qT, qR, qB] = ring 495 | 496 | // Enumerate which positions to translate 497 | toTranslate[0] = targetCell[0] 498 | toTranslate[1] = targetCell[1] 499 | toTranslate[2] = targetCell[2] 500 | toTranslate[3] = targetCell[3] 501 | 502 | toTranslate[4] = qL[2] 503 | toTranslate[5] = qL[3] 504 | 505 | toTranslate[6] = qT[0] 506 | toTranslate[7] = qT[3] 507 | 508 | toTranslate[8] = qR[0] 509 | toTranslate[9] = qR[1] 510 | 511 | toTranslate[10] = qB[1] 512 | toTranslate[11] = qB[2] 513 | 514 | // Assume that disjoint cells all share the same normal. 515 | const targetCellNormal = normals[targetCell[0]] 516 | vec3.scale(translation, targetCellNormal, extrude) 517 | 518 | for (let i = 0; i < toTranslate.length; i++) { 519 | const position = positions[toTranslate[i]] 520 | vec3.add(position, position, translation) 521 | } 522 | 523 | // Calculate the normals for the translated rings. 524 | for (let i = 0; i < ring.length; i++) { 525 | updateNormals(mesh, ring[i]) 526 | } 527 | } 528 | })() 529 | 530 | /** 531 | * Computes the center of a cell. 532 | * 533 | * @param {SimplicialComplex} mesh 534 | * @param {Cell} cell 535 | * @param {Position} target 536 | * @returns {Position} center 537 | */ 538 | function getCenter (mesh, cell, target = []) { 539 | const a = mesh.positions[cell[0]] 540 | const b = mesh.positions[cell[1]] 541 | const c = mesh.positions[cell[2]] 542 | const d = mesh.positions[cell[3]] 543 | target[0] = (a[0] + b[0] + c[0] + d[0]) * 0.25 544 | target[1] = (a[1] + b[1] + c[1] + d[1]) * 0.25 545 | target[2] = (a[2] + b[2] + c[2] + d[2]) * 0.25 546 | return target 547 | } 548 | 549 | /** 550 | * Clones a cell. Returns the new cell. 551 | * 552 | * @param {SimplicialComplex} mesh 553 | * @param {Cell} cell 554 | * @returns {Cell} cloned cell 555 | */ 556 | function clone (mesh, cell) { 557 | const index = mesh.positions.length 558 | const clonedCell = [index, index + 1, index + 2, index + 3] 559 | mesh.cells.push(clonedCell) 560 | mesh.positions.push(mesh.positions[cell[0]].slice()) 561 | mesh.positions.push(mesh.positions[cell[1]].slice()) 562 | mesh.positions.push(mesh.positions[cell[2]].slice()) 563 | mesh.positions.push(mesh.positions[cell[3]].slice()) 564 | mesh.normals.push(mesh.normals[cell[0]].slice()) 565 | mesh.normals.push(mesh.normals[cell[1]].slice()) 566 | mesh.normals.push(mesh.normals[cell[2]].slice()) 567 | mesh.normals.push(mesh.normals[cell[3]].slice()) 568 | return clonedCell 569 | } 570 | 571 | /** 572 | * Clones a group of cells and their geometry. Use getNewGeometry to capture the newly 573 | * created geometry. 574 | * 575 | * @param {SimplicialComplex} mesh 576 | * @param {Cell[]} cells 577 | * @returns {SimplicialComplex} the original mesh. 578 | */ 579 | function cloneCells (mesh, cells) { 580 | // Get a list of the position indices used 581 | const positions = [] 582 | for (let i = 0; i < cells.length; i++) { 583 | const cell = cells[i] 584 | for (let j = 0; j < cell.length; j++) { 585 | const positionIndex = cell[j] 586 | positions[positionIndex] = positionIndex 587 | } 588 | } 589 | const indices = positions.filter(i => i !== undefined) 590 | 591 | // Clone the cells. 592 | const cellIndexOffset = mesh.positions.length 593 | const cellsLength = cells.length 594 | for (let i = 0; i < cellsLength; i++) { 595 | const cell = cells[i] 596 | mesh.cells.push( 597 | cell.map(cellIndex => indices.indexOf(cellIndex) + cellIndexOffset) 598 | ) 599 | } 600 | 601 | // Clone the positions. 602 | for (let i = 0; i < indices.length; i++) { 603 | mesh.positions.push(mesh.positions[indices[i]].slice()) 604 | } 605 | 606 | // Clone the normals. 607 | for (let i = 0; i < indices.length; i++) { 608 | mesh.normals.push(mesh.normals[indices[i]].slice()) 609 | } 610 | 611 | return mesh 612 | } 613 | 614 | /** 615 | * Updates all of the normals for all the positions using 616 | * {@link #averageNormalForPosition}. If a normal doesn't exist, 617 | * then it is created. 618 | * 619 | * @param {SimplicialComplex} mesh 620 | * @param {Cell} cell 621 | * @returns {SimplicialComplex} 622 | */ 623 | function updateNormals (mesh, cell) { 624 | let normal = mesh.normals[cell[0]] 625 | getCellNormal(mesh, cell, normal) 626 | vec3.copy(mesh.normals[cell[1]], normal) 627 | vec3.copy(mesh.normals[cell[2]], normal) 628 | vec3.copy(mesh.normals[cell[3]], normal) 629 | } 630 | 631 | var getCellNormal = (() => { 632 | const edgeA = [] 633 | const edgeB = [] 634 | /** 635 | * Compute a cell's normal regardless of it's neighboring cells. 636 | * 637 | * @param {SimplicialComplex} mesh 638 | * @param {Cell} cell 639 | * @param {Normal?} target **= []** 640 | * @returns {Normal} The target normal. 641 | */ 642 | return function getCellNormal (mesh, cell, target = []) { 643 | const positionA = mesh.positions[cell[0]] 644 | const positionB = mesh.positions[cell[1]] 645 | const positionC = mesh.positions[cell[2]] 646 | vec3.subtract(edgeA, positionB, positionA) 647 | vec3.subtract(edgeB, positionC, positionB) 648 | vec3.normalize(target, vec3.cross(target, edgeA, edgeB)) 649 | return target 650 | } 651 | })() 652 | 653 | /** 654 | * Given a position index, find any cells that include it. 655 | * 656 | * @param {SimplicialComplex} mesh 657 | * @param {Number} index 658 | * @param {Cell[]} target 659 | * @returns {Cell[]} The target cells. 660 | */ 661 | function getCellsFromPositionIndex (mesh, index, target = []) { 662 | for (let i = 0; i < mesh.cells.length; i++) { 663 | const cell = mesh.cells[i] 664 | if (cell.indexOf(index) >= 0) { 665 | target.push(cell) 666 | } 667 | } 668 | return target 669 | } 670 | 671 | /** 672 | * Flip a cell's normal to point the other way. Returns the cell. 673 | * 674 | * @param {SimplicialComplex} mesh 675 | * @param {Cell} cell 676 | * @returns {Cell} The cell 677 | */ 678 | function flip (mesh, cell) { 679 | const [a, b, c, d] = cell 680 | cell.reverse() 681 | const nA = mesh.normals[a] 682 | const nB = mesh.normals[b] 683 | const nC = mesh.normals[c] 684 | const nD = mesh.normals[d] 685 | vec3.scale(nA, nA, -1) 686 | vec3.scale(nB, nB, -1) 687 | vec3.scale(nC, nC, -1) 688 | vec3.scale(nD, nD, -1) 689 | return cell 690 | } 691 | 692 | /** 693 | * Create a quad with options. If the optionalMesh object is passed, then the 694 | * quad will be created inside of that simplicial complex, otherwise a new 695 | * mesh simplicial complex will be generated. Both the mesh simplicial 696 | * complex and the created cell are returned in an object. 697 | * 698 | * @example