├── .gitignore ├── package.json ├── LICENSE ├── test └── test.js ├── topology.js └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | *.seed 3 | *.log 4 | *.csv 5 | *.dat 6 | *.out 7 | *.pid 8 | *.gz 9 | 10 | pids 11 | logs 12 | results 13 | 14 | npm-debug.log 15 | node_modules/* 16 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "simplicial-complex", 3 | "version": "1.0.0", 4 | "description": "Topological indexing for simplicial complexes", 5 | "main": "topology.js", 6 | "dependencies": { 7 | "bit-twiddle": "^1.0.0", 8 | "union-find": "^1.0.0" 9 | }, 10 | "scripts": { 11 | "test": "tape test/*.js" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git://github.com/mikolalysenko/simplicial-complex.git" 16 | }, 17 | "keywords": [ 18 | "mesh", 19 | "cell", 20 | "complex", 21 | "simplex", 22 | "simplicial", 23 | "topology", 24 | "vertex", 25 | "triangle", 26 | "star", 27 | "edge", 28 | "graph", 29 | "winged", 30 | "edge", 31 | "half", 32 | "edge" 33 | ], 34 | "author": "Mikola Lysenko", 35 | "license": "MIT", 36 | "readmeFilename": "README.md", 37 | "gitHead": "24aa75407005c898a4171dcc1e97215a261b3b79", 38 | "devDependencies": { 39 | "tape": "^2.12.3" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2013 Mikola Lysenko 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | var test = require("tape") 2 | , top = require("../topology.js"); 3 | 4 | function arr_equals(t, a, b) { 5 | console.log(a, "---", b); 6 | t.equals(a.length, b.length, "length match"); 7 | for(var i=0; i 0); 149 | t.equals(tris[idx][0], 4); 150 | t.equals(tris[idx][1], 5); 151 | t.equals(tris[idx][2], 6); 152 | 153 | //Test lower extreme 154 | t.equals(top.findCell(tris, [0], true), 0); 155 | 156 | //Test upper extreme 157 | t.equals(top.findCell(tris, [6,7,8]), tris.length-1); 158 | 159 | t.end(); 160 | }); 161 | 162 | test("buildIndex", function(t) { 163 | var from_cells = top.normalize([ 164 | [0,1], 165 | [0,2], 166 | [1,2], 167 | [1,3], 168 | [2,3] 169 | ]); 170 | var to_cells = top.normalize([ 171 | [0,1,2], 172 | [0,1,3], 173 | [0,2,3], 174 | [1,2,3] 175 | ]); 176 | 177 | var index = top.incidence(from_cells, to_cells); 178 | t.equals(index.length, from_cells.length); 179 | 180 | console.log(from_cells, to_cells); 181 | 182 | for(var i=0; i> 1 147 | , s = compareCells(cells[mid], c) 148 | if(s <= 0) { 149 | if(s === 0) { 150 | r = mid 151 | } 152 | lo = mid + 1 153 | } else if(s > 0) { 154 | hi = mid - 1 155 | } 156 | } 157 | return r 158 | } 159 | exports.findCell = findCell; 160 | 161 | //Builds an index for an n-cell. This is more general than dual, but less efficient 162 | function incidence(from_cells, to_cells) { 163 | var index = new Array(from_cells.length) 164 | for(var i=0, il=index.length; i= from_cells.length || compareCells(from_cells[idx], b) !== 0) { 186 | break 187 | } 188 | } 189 | } 190 | } 191 | return index 192 | } 193 | exports.incidence = incidence 194 | 195 | //Computes the dual of the mesh. This is basically an optimized version of buildIndex for the situation where from_cells is just the list of vertices 196 | function dual(cells, vertex_count) { 197 | if(!vertex_count) { 198 | return incidence(unique(skeleton(cells, 0)), cells, 0) 199 | } 200 | var res = new Array(vertex_count) 201 | for(var i=0; i>> k) & 1) { 224 | b.push(c[k]) 225 | } 226 | } 227 | result.push(b) 228 | } 229 | } 230 | return normalize(result) 231 | } 232 | exports.explode = explode 233 | 234 | //Enumerates all of the n-cells of a cell complex 235 | function skeleton(cells, n) { 236 | if(n < 0) { 237 | return [] 238 | } 239 | var result = [] 240 | , k0 = (1<<(n+1))-1 241 | for(var i=0; i 0 : `b` comes before `a` 121 | 122 | **Time complexity:** `O( a.length * log(a.length) )` 123 | 124 | ### `normalize(cells[, attr])` 125 | Canonicalizes a cell complex so that it is possible to compute `findCell` queries. Note that this function is done **in place**. `cells` will be mutated. If this is not acceptable, you should make a copy first using `cloneCells`. 126 | 127 | * `cells` is a complex. 128 | * `attr` is an optional array of per-cell properties which is permuted alongside `cells` 129 | 130 | **Returns:** `cells` 131 | 132 | **Time complexity:** `O(dimension(cells) * cells.length * log(cells.length) )` 133 | 134 | ### `unique(cells)` 135 | Removes all duplicate cells from the complex. Note that this is done **in place**. `cells` will be mutated. If this is not acceptable, make a copy of `cells` first. 136 | 137 | * `cells` is a `normalize`d complex 138 | 139 | **Returns:** `cells` 140 | 141 | **Time complexity:** `O(cells.length)` 142 | 143 | ### `findCell(cells, c)` 144 | Finds a lower bound on the first index of cell `c` in a `normalize`d array of cells. 145 | 146 | * `cells` is a `normalize`'d array of cells 147 | * `c` is a cell represented by an array of vertex indices 148 | 149 | **Returns:** The index of `c` in the array if it exists, otherwise -1 150 | 151 | **Time complexity:** `O(d * log(d) * log(cells.length))`, where `d = max(dimension(cells), c.length)` 152 | 153 | Topological 154 | ----------- 155 | 156 | ### `explode(cells)` 157 | Enumerates all cells in the complex, with duplicates 158 | 159 | * `cells` is an array of cells 160 | 161 | **Returns:** A normalized list of all cells in the complex 162 | 163 | **Time complexity:** `O(2^dimension(cells) * dimension(cells) * cells.length * log(cells.length))` 164 | 165 | ### `skeleton(cells, n)` 166 | Enumerates all n cells in the complex, with duplicates 167 | 168 | * `cells` is an array of cells 169 | * `n` is the dimension of the cycles to compute 170 | 171 | **Returns:** A normalized list of all n-cells 172 | 173 | **Time complexity:** `O(dimension(cells)^n * cells.length * log(cells.length))` 174 | 175 | ### `boundary(cells)` 176 | Enumerates all boundary cells of the cell complex 177 | 178 | * `cells` is an array of cells 179 | 180 | **Returns:** A normalized list of cells 181 | 182 | **Time complexity:** `O(dimension(cells) * cells.length * log(cells.length))` 183 | 184 | ### `incidence(from_cells, to_cells)` 185 | Builds an index for [neighborhood queries](http://en.wikipedia.org/wiki/Polygon_mesh#Summary_of_mesh_representation) (aka a sparse incidence matrix). This allows you to quickly find the cells in `to_cells` which are incident to cells in `from_cells`. 186 | 187 | * `from_cells` a `normalize`d array of cells 188 | * `to_cells` a list of cells which we are going to query against 189 | 190 | **Returns:** An array with the same length as `from_cells`, the `i`th entry of which is an array of indices into `to_cells` which are incident to `from_cells[i]`. 191 | 192 | **Time complexity:** `O(from_cells.length + d * 2^d * log(from_cells.length) * to_cells.length)`, where `d = max(dimension(from_cells), dimension(to_cells))`. 193 | 194 | ### `dual(cells[, vertex_count])` 195 | Computes the [dual](http://en.wikipedia.org/wiki/Hypergraph#Incidence_matrix) of the complex. An important application of this is that it gives a more optimized way to build an index for vertices for cell complexes with sequentially enumerated vertices. For example, 196 | 197 | ```javascript 198 | dual(cells) 199 | ``` 200 | 201 | Is equivalent to finding the incidence relation for all vertices, or in other words doing: 202 | 203 | ```javascript 204 | incidence(unique(skeleton(cells, 0)), cells) 205 | ``` 206 | 207 | For the arguments: 208 | 209 | * `cells` is a cell complex 210 | * `vertex_count` is an optional parameter giving the number of vertices in the cell complex. If not specified, then it calls `top.incidence(top.unique(top.skeleton(cells, 0)), cells)` 211 | 212 | **Returns:** An array of elements with the same length as `vertex_count` (if specified) or `unique(skeleton(cells,0))` otherwise giving the [vertex stars of the mesh](http://en.wikipedia.org/wiki/Star_\(graph_theory\)) as indexed arrays of cells. 213 | 214 | **Time complexity:** `O(dimension(cells) * cells.length)` 215 | 216 | ### `connectedComponents(cells[, vertex_count])` 217 | Splits a simplicial complex into its connected components. If `vertex_count` is specified, we assume that the cell complex is dense -- or in other words the vertices of the cell complex is the set of integers [0, vertex_count). This allows for a slightly more efficient implementation. If unspecified, a more general but less efficient sparse algorithm is used. 218 | 219 | * `cells` is an array of cells 220 | * `vertex_count` (optional) is the result of calling `countVertices(cells)` or in other words is the total number of vertices. 221 | 222 | **Returns:** An array of cell complexes, one per each connected component. Note that these complexes are not normalized. 223 | 224 | **Time complexity:** 225 | 226 | * If `vertex_count` is specified: `O(vertex_count + dimension(cells)^2 * cells.length)` 227 | * If `vertex_count` is not specified: `O(dimension(cells)^3 * log(cells.length) * cells.length)` 228 | 229 | Credits 230 | ======= 231 | (c) 2013-2014 Mikola Lysenko. MIT License 232 | --------------------------------------------------------------------------------