├── .clang-format ├── .github └── workflows │ └── build.yml ├── .gitignore ├── .vscode └── settings.json ├── CMakeLists.txt ├── LICENSE ├── README.md ├── cxx ├── CMakeLists.txt ├── cuda_utils.h ├── grid_interp_cpu.cpp ├── grid_interp_cuda.cu ├── helper_math.h ├── macros.h ├── mcubes.cpp ├── mcubes_cpu.cpp └── mcubes_cuda.cu ├── mcubes.py ├── metaball.png ├── pyproject.toml └── torchmcubes └── __init__.py /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: Google 2 | 3 | ColumnLimit: 120 4 | IndentWidth: 4 5 | Standard: Cpp11 6 | AccessModifierOffset: -4 7 | IndentCaseLabels: false 8 | 9 | AlignAfterOpenBracket: Align 10 | AlignConsecutiveAssignments: false 11 | AlignTrailingComments: true 12 | AlignOperands: true 13 | AllowAllParametersOfDeclarationOnNextLine: false 14 | AllowAllConstructorInitializersOnNextLine: false 15 | AllowShortBlocksOnASingleLine: false 16 | AllowShortFunctionsOnASingleLine: false 17 | BinPackArguments: true 18 | BreakConstructorInitializers: BeforeColon 19 | BreakConstructorInitializersBeforeComma: true 20 | Cpp11BracedListStyle: false 21 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 22 | DerivePointerAlignment: false 23 | MaxEmptyLinesToKeep: 1 24 | PointerAlignment: Right 25 | ReflowComments: false 26 | SortIncludes: false 27 | 28 | UseTab: Never 29 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build (CPU) 2 | on: 3 | push: 4 | branches: [master, dev] 5 | 6 | jobs: 7 | build: 8 | strategy: 9 | matrix: 10 | os: 11 | - ubuntu-latest 12 | - windows-latest 13 | - macos-latest 14 | version: 15 | - "3.9" 16 | - "3.10" 17 | - "3.11" 18 | - "3.12" 19 | runs-on: ${{ matrix.os }} 20 | steps: 21 | - name: Check out repository code 22 | uses: actions/checkout@v4 23 | - name: Setup Python 24 | uses: actions/setup-python@v4 25 | with: 26 | python-version: ${{ matrix.version }} 27 | - name: Install requirements 28 | run: | 29 | python -m pip install --upgrade pip 30 | python -m pip install numpy "pybind11[global]" 31 | python -m pip install torch --index-url https://download.pytorch.org/whl/cpu 32 | - name: Build 33 | run: pip install . 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | _skbuild 3 | dist 4 | .mypy_cache 5 | 6 | __pycache__ 7 | *.pyd 8 | *.so 9 | *.egg-info 10 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "flake8.path": "pflake8", 3 | "[python]": { 4 | "editor.formatOnSave": true, 5 | "editor.formatOnPaste": false, 6 | "editor.formatOnType": false, 7 | "editor.defaultFormatter": "eeyore.yapf", 8 | "editor.codeActionsOnSave": { 9 | "source.organizeImports": "always", 10 | } 11 | }, 12 | "[c][cxx][cuda]": { 13 | "editor.formatOnSave": true, 14 | "editor.formatOnPaste": false, 15 | "editor.formatOnType": false, 16 | "editor.defaultFormatter": "xaver.clang-format" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.18) 2 | project(${SKBUILD_PROJECT_NAME} VERSION ${SKBUILD_PROJECT_VERSION} LANGUAGES CXX) 3 | 4 | # General settings 5 | set(BUILD_TARGET torchmcubes_module) 6 | 7 | # CUDA settings 8 | include(CheckLanguage) 9 | check_language(CUDA) 10 | 11 | if(CMAKE_CUDA_COMPILER) 12 | enable_language(CUDA) 13 | 14 | add_definitions(-DWITH_CUDA) 15 | find_package(CUDAToolkit REQUIRED) 16 | set(CMAKE_CUDA_STANDARD 17) 17 | set(CMAKE_CUDA_STANDARD_REQUIRED ON) 18 | 19 | message(STATUS "INSTALLING EXTENSIONS WITH CUDA!") 20 | string(REGEX REPLACE ".[0-9][0-9]|\\." "" CUDA_V ${CMAKE_CUDA_COMPILER_VERSION}) 21 | message(STATUS "CMAKE_CUDA_COMPILER = ${CMAKE_CUDA_COMPILER}") 22 | message(STATUS "CMAKE_CUDA_COMPILER_ID = ${CMAKE_CUDA_COMPILER_ID}") 23 | message(STATUS "CMAKE_CUDA_COMPILER_VERSION = ${CUDA_V}") 24 | else() 25 | message(WARNING "NO CUDA INSTALLATION FOUND, TRYING TO INSTALL CPU VERSION ONLY!") 26 | endif() 27 | 28 | set(CMAKE_CXX_STANDARD 17) 29 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 30 | set(CMAKE_POSITION_INDEPENDENT_CODE ON) 31 | 32 | if(MSVC) 33 | add_compile_options("$<$:/utf-8>") 34 | add_compile_options("$<$:-Xcompiler=/utf-8>") 35 | add_definitions(-DNOMINMAX -D_CRT_SECURE_NO_WARNINGS) 36 | endif() 37 | 38 | # Find packages 39 | find_package(Python COMPONENTS Interpreter Development.Module REQUIRED) 40 | find_package(pybind11 CONFIG REQUIRED) 41 | find_package(OpenMP) 42 | 43 | find_package(Torch CONFIG REQUIRED) 44 | find_library(TORCH_PYTHON_LIBRARY torch_python PATH "${TORCH_INSTALL_PREFIX}/lib") 45 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${TORCH_CXX_FLAGS}") 46 | 47 | add_subdirectory(cxx) 48 | install(TARGETS ${BUILD_TARGET} LIBRARY DESTINATION .) 49 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Mozilla Public License Version 2.0 2 | ================================== 3 | 4 | 1. Definitions 5 | -------------- 6 | 7 | 1.1. "Contributor" 8 | means each individual or legal entity that creates, contributes to 9 | the creation of, or owns Covered Software. 10 | 11 | 1.2. "Contributor Version" 12 | means the combination of the Contributions of others (if any) used 13 | by a Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | means Covered Software of a particular Contributor. 17 | 18 | 1.4. "Covered Software" 19 | means Source Code Form to which the initial Contributor has attached 20 | the notice in Exhibit A, the Executable Form of such Source Code 21 | Form, and Modifications of such Source Code Form, in each case 22 | including portions thereof. 23 | 24 | 1.5. "Incompatible With Secondary Licenses" 25 | means 26 | 27 | (a) that the initial Contributor has attached the notice described 28 | in Exhibit B to the Covered Software; or 29 | 30 | (b) that the Covered Software was made available under the terms of 31 | version 1.1 or earlier of the License, but not also under the 32 | terms of a Secondary License. 33 | 34 | 1.6. "Executable Form" 35 | means any form of the work other than Source Code Form. 36 | 37 | 1.7. "Larger Work" 38 | means a work that combines Covered Software with other material, in 39 | a separate file or files, that is not Covered Software. 40 | 41 | 1.8. "License" 42 | means this document. 43 | 44 | 1.9. "Licensable" 45 | means having the right to grant, to the maximum extent possible, 46 | whether at the time of the initial grant or subsequently, any and 47 | all of the rights conveyed by this License. 48 | 49 | 1.10. "Modifications" 50 | means any of the following: 51 | 52 | (a) any file in Source Code Form that results from an addition to, 53 | deletion from, or modification of the contents of Covered 54 | Software; or 55 | 56 | (b) any new file in Source Code Form that contains any Covered 57 | Software. 58 | 59 | 1.11. "Patent Claims" of a Contributor 60 | means any patent claim(s), including without limitation, method, 61 | process, and apparatus claims, in any patent Licensable by such 62 | Contributor that would be infringed, but for the grant of the 63 | License, by the making, using, selling, offering for sale, having 64 | made, import, or transfer of either its Contributions or its 65 | Contributor Version. 66 | 67 | 1.12. "Secondary License" 68 | means either the GNU General Public License, Version 2.0, the GNU 69 | Lesser General Public License, Version 2.1, the GNU Affero General 70 | Public License, Version 3.0, or any later versions of those 71 | licenses. 72 | 73 | 1.13. "Source Code Form" 74 | means the form of the work preferred for making modifications. 75 | 76 | 1.14. "You" (or "Your") 77 | means an individual or a legal entity exercising rights under this 78 | License. For legal entities, "You" includes any entity that 79 | controls, is controlled by, or is under common control with You. For 80 | purposes of this definition, "control" means (a) the power, direct 81 | or indirect, to cause the direction or management of such entity, 82 | whether by contract or otherwise, or (b) ownership of more than 83 | fifty percent (50%) of the outstanding shares or beneficial 84 | ownership of such entity. 85 | 86 | 2. License Grants and Conditions 87 | -------------------------------- 88 | 89 | 2.1. Grants 90 | 91 | Each Contributor hereby grants You a world-wide, royalty-free, 92 | non-exclusive license: 93 | 94 | (a) under intellectual property rights (other than patent or trademark) 95 | Licensable by such Contributor to use, reproduce, make available, 96 | modify, display, perform, distribute, and otherwise exploit its 97 | Contributions, either on an unmodified basis, with Modifications, or 98 | as part of a Larger Work; and 99 | 100 | (b) under Patent Claims of such Contributor to make, use, sell, offer 101 | for sale, have made, import, and otherwise transfer either its 102 | Contributions or its Contributor Version. 103 | 104 | 2.2. Effective Date 105 | 106 | The licenses granted in Section 2.1 with respect to any Contribution 107 | become effective for each Contribution on the date the Contributor first 108 | distributes such Contribution. 109 | 110 | 2.3. Limitations on Grant Scope 111 | 112 | The licenses granted in this Section 2 are the only rights granted under 113 | this License. No additional rights or licenses will be implied from the 114 | distribution or licensing of Covered Software under this License. 115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 116 | Contributor: 117 | 118 | (a) for any code that a Contributor has removed from Covered Software; 119 | or 120 | 121 | (b) for infringements caused by: (i) Your and any other third party's 122 | modifications of Covered Software, or (ii) the combination of its 123 | Contributions with other software (except as part of its Contributor 124 | Version); or 125 | 126 | (c) under Patent Claims infringed by Covered Software in the absence of 127 | its Contributions. 128 | 129 | This License does not grant any rights in the trademarks, service marks, 130 | or logos of any Contributor (except as may be necessary to comply with 131 | the notice requirements in Section 3.4). 132 | 133 | 2.4. Subsequent Licenses 134 | 135 | No Contributor makes additional grants as a result of Your choice to 136 | distribute the Covered Software under a subsequent version of this 137 | License (see Section 10.2) or under the terms of a Secondary License (if 138 | permitted under the terms of Section 3.3). 139 | 140 | 2.5. Representation 141 | 142 | Each Contributor represents that the Contributor believes its 143 | Contributions are its original creation(s) or it has sufficient rights 144 | to grant the rights to its Contributions conveyed by this License. 145 | 146 | 2.6. Fair Use 147 | 148 | This License is not intended to limit any rights You have under 149 | applicable copyright doctrines of fair use, fair dealing, or other 150 | equivalents. 151 | 152 | 2.7. Conditions 153 | 154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 155 | in Section 2.1. 156 | 157 | 3. Responsibilities 158 | ------------------- 159 | 160 | 3.1. Distribution of Source Form 161 | 162 | All distribution of Covered Software in Source Code Form, including any 163 | Modifications that You create or to which You contribute, must be under 164 | the terms of this License. You must inform recipients that the Source 165 | Code Form of the Covered Software is governed by the terms of this 166 | License, and how they can obtain a copy of this License. You may not 167 | attempt to alter or restrict the recipients' rights in the Source Code 168 | Form. 169 | 170 | 3.2. Distribution of Executable Form 171 | 172 | If You distribute Covered Software in Executable Form then: 173 | 174 | (a) such Covered Software must also be made available in Source Code 175 | Form, as described in Section 3.1, and You must inform recipients of 176 | the Executable Form how they can obtain a copy of such Source Code 177 | Form by reasonable means in a timely manner, at a charge no more 178 | than the cost of distribution to the recipient; and 179 | 180 | (b) You may distribute such Executable Form under the terms of this 181 | License, or sublicense it under different terms, provided that the 182 | license for the Executable Form does not attempt to limit or alter 183 | the recipients' rights in the Source Code Form under this License. 184 | 185 | 3.3. Distribution of a Larger Work 186 | 187 | You may create and distribute a Larger Work under terms of Your choice, 188 | provided that You also comply with the requirements of this License for 189 | the Covered Software. If the Larger Work is a combination of Covered 190 | Software with a work governed by one or more Secondary Licenses, and the 191 | Covered Software is not Incompatible With Secondary Licenses, this 192 | License permits You to additionally distribute such Covered Software 193 | under the terms of such Secondary License(s), so that the recipient of 194 | the Larger Work may, at their option, further distribute the Covered 195 | Software under the terms of either this License or such Secondary 196 | License(s). 197 | 198 | 3.4. Notices 199 | 200 | You may not remove or alter the substance of any license notices 201 | (including copyright notices, patent notices, disclaimers of warranty, 202 | or limitations of liability) contained within the Source Code Form of 203 | the Covered Software, except that You may alter any license notices to 204 | the extent required to remedy known factual inaccuracies. 205 | 206 | 3.5. Application of Additional Terms 207 | 208 | You may choose to offer, and to charge a fee for, warranty, support, 209 | indemnity or liability obligations to one or more recipients of Covered 210 | Software. However, You may do so only on Your own behalf, and not on 211 | behalf of any Contributor. You must make it absolutely clear that any 212 | such warranty, support, indemnity, or liability obligation is offered by 213 | You alone, and You hereby agree to indemnify every Contributor for any 214 | liability incurred by such Contributor as a result of warranty, support, 215 | indemnity or liability terms You offer. You may include additional 216 | disclaimers of warranty and limitations of liability specific to any 217 | jurisdiction. 218 | 219 | 4. Inability to Comply Due to Statute or Regulation 220 | --------------------------------------------------- 221 | 222 | If it is impossible for You to comply with any of the terms of this 223 | License with respect to some or all of the Covered Software due to 224 | statute, judicial order, or regulation then You must: (a) comply with 225 | the terms of this License to the maximum extent possible; and (b) 226 | describe the limitations and the code they affect. Such description must 227 | be placed in a text file included with all distributions of the Covered 228 | Software under this License. Except to the extent prohibited by statute 229 | or regulation, such description must be sufficiently detailed for a 230 | recipient of ordinary skill to be able to understand it. 231 | 232 | 5. Termination 233 | -------------- 234 | 235 | 5.1. The rights granted under this License will terminate automatically 236 | if You fail to comply with any of its terms. However, if You become 237 | compliant, then the rights granted under this License from a particular 238 | Contributor are reinstated (a) provisionally, unless and until such 239 | Contributor explicitly and finally terminates Your grants, and (b) on an 240 | ongoing basis, if such Contributor fails to notify You of the 241 | non-compliance by some reasonable means prior to 60 days after You have 242 | come back into compliance. Moreover, Your grants from a particular 243 | Contributor are reinstated on an ongoing basis if such Contributor 244 | notifies You of the non-compliance by some reasonable means, this is the 245 | first time You have received notice of non-compliance with this License 246 | from such Contributor, and You become compliant prior to 30 days after 247 | Your receipt of the notice. 248 | 249 | 5.2. If You initiate litigation against any entity by asserting a patent 250 | infringement claim (excluding declaratory judgment actions, 251 | counter-claims, and cross-claims) alleging that a Contributor Version 252 | directly or indirectly infringes any patent, then the rights granted to 253 | You by any and all Contributors for the Covered Software under Section 254 | 2.1 of this License shall terminate. 255 | 256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all 257 | end user license agreements (excluding distributors and resellers) which 258 | have been validly granted by You or Your distributors under this License 259 | prior to termination shall survive termination. 260 | 261 | ************************************************************************ 262 | * * 263 | * 6. Disclaimer of Warranty * 264 | * ------------------------- * 265 | * * 266 | * Covered Software is provided under this License on an "as is" * 267 | * basis, without warranty of any kind, either expressed, implied, or * 268 | * statutory, including, without limitation, warranties that the * 269 | * Covered Software is free of defects, merchantable, fit for a * 270 | * particular purpose or non-infringing. The entire risk as to the * 271 | * quality and performance of the Covered Software is with You. * 272 | * Should any Covered Software prove defective in any respect, You * 273 | * (not any Contributor) assume the cost of any necessary servicing, * 274 | * repair, or correction. This disclaimer of warranty constitutes an * 275 | * essential part of this License. No use of any Covered Software is * 276 | * authorized under this License except under this disclaimer. * 277 | * * 278 | ************************************************************************ 279 | 280 | ************************************************************************ 281 | * * 282 | * 7. Limitation of Liability * 283 | * -------------------------- * 284 | * * 285 | * Under no circumstances and under no legal theory, whether tort * 286 | * (including negligence), contract, or otherwise, shall any * 287 | * Contributor, or anyone who distributes Covered Software as * 288 | * permitted above, be liable to You for any direct, indirect, * 289 | * special, incidental, or consequential damages of any character * 290 | * including, without limitation, damages for lost profits, loss of * 291 | * goodwill, work stoppage, computer failure or malfunction, or any * 292 | * and all other commercial damages or losses, even if such party * 293 | * shall have been informed of the possibility of such damages. This * 294 | * limitation of liability shall not apply to liability for death or * 295 | * personal injury resulting from such party's negligence to the * 296 | * extent applicable law prohibits such limitation. Some * 297 | * jurisdictions do not allow the exclusion or limitation of * 298 | * incidental or consequential damages, so this exclusion and * 299 | * limitation may not apply to You. * 300 | * * 301 | ************************************************************************ 302 | 303 | 8. Litigation 304 | ------------- 305 | 306 | Any litigation relating to this License may be brought only in the 307 | courts of a jurisdiction where the defendant maintains its principal 308 | place of business and such litigation shall be governed by laws of that 309 | jurisdiction, without reference to its conflict-of-law provisions. 310 | Nothing in this Section shall prevent a party's ability to bring 311 | cross-claims or counter-claims. 312 | 313 | 9. Miscellaneous 314 | ---------------- 315 | 316 | This License represents the complete agreement concerning the subject 317 | matter hereof. If any provision of this License is held to be 318 | unenforceable, such provision shall be reformed only to the extent 319 | necessary to make it enforceable. Any law or regulation which provides 320 | that the language of a contract shall be construed against the drafter 321 | shall not be used to construe this License against a Contributor. 322 | 323 | 10. Versions of the License 324 | --------------------------- 325 | 326 | 10.1. New Versions 327 | 328 | Mozilla Foundation is the license steward. Except as provided in Section 329 | 10.3, no one other than the license steward has the right to modify or 330 | publish new versions of this License. Each version will be given a 331 | distinguishing version number. 332 | 333 | 10.2. Effect of New Versions 334 | 335 | You may distribute the Covered Software under the terms of the version 336 | of the License under which You originally received the Covered Software, 337 | or under the terms of any subsequent version published by the license 338 | steward. 339 | 340 | 10.3. Modified Versions 341 | 342 | If you create software not governed by this License, and you want to 343 | create a new license for such software, you may create and use a 344 | modified version of this License if you rename the license and remove 345 | any references to the name of the license steward (except to note that 346 | such modified license differs from this License). 347 | 348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 349 | Licenses 350 | 351 | If You choose to distribute Source Code Form that is Incompatible With 352 | Secondary Licenses under the terms of this version of the License, the 353 | notice described in Exhibit B of this License must be attached. 354 | 355 | Exhibit A - Source Code Form License Notice 356 | ------------------------------------------- 357 | 358 | This Source Code Form is subject to the terms of the Mozilla Public 359 | License, v. 2.0. If a copy of the MPL was not distributed with this 360 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 361 | 362 | If it is not possible or desirable to put the notice in a particular 363 | file, then You may include the notice in a location (such as a LICENSE 364 | file in a relevant directory) where a recipient would be likely to look 365 | for such a notice. 366 | 367 | You may add additional accurate notices of copyright ownership. 368 | 369 | Exhibit B - "Incompatible With Secondary Licenses" Notice 370 | --------------------------------------------------------- 371 | 372 | This Source Code Form is "Incompatible With Secondary Licenses", as 373 | defined by the Mozilla Public License, v. 2.0. 374 | 375 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # torchmcubes: marching cubes for PyTorch 2 | 3 | [![Build (CPU)](https://github.com/tatsy/torchmcubes/actions/workflows/build.yml/badge.svg?branch=master)](https://github.com/tatsy/torchmcubes/actions/workflows/build.yml) 4 | 5 | > Marching cubes for PyTorch environment. Backend is implemented with C++ and CUDA. 6 | 7 | ## Install 8 | 9 | ### Requirements 10 | 11 | - Python (3.9 or later) 12 | - NumPy (1.x is preferable) 13 | - PyTorch 14 | - CMake (3.18 or later) 15 | 16 | Make sure that you have nvcc CUDA compiler with the following command. 17 | 18 | ```shell 19 | nvcc --version 20 | ``` 21 | 22 | If you have CUDA installed but not able to run nvcc, you migth need to add it to your path: 23 | 24 | ```shell 25 | export CUDA_HOME=/usr/local/cuda/ 26 | export PATH=$CUDA_HOME/bin:$PATH 27 | ``` 28 | 29 | ### Pip installation 30 | 31 | ```shell 32 | # Make sure that your environment meets the requirements above 33 | pip install git+https://github.com/tatsy/torchmcubes.git 34 | ``` 35 | 36 | ## Usage 37 | 38 | See [mcubes.py](./mcubes.py) for more details. 39 | 40 | ```python 41 | import time 42 | import numpy as np 43 | 44 | import torch 45 | from torchmcubes import marching_cubes, grid_interp 46 | 47 | # Grid data 48 | N = 128 49 | xs = np.linspace(-1.0, 1.0, N, endpoint=True, dtype="float32") 50 | ys = np.linspace(-1.0, 1.0, N, endpoint=True, dtype="float32") 51 | zs = np.linspace(-1.0, 1.0, N, endpoint=True, dtype="float32") 52 | zs, ys, xs = np.meshgrid(zs, ys, xs) 53 | 54 | # Implicit function (metaball) 55 | f0 = (xs - 0.35)**2 + (ys - 0.35)**2 + (zs - 0.35)**2 56 | f1 = (xs + 0.35)**2 + (ys + 0.35)**2 + (zs + 0.35)**2 57 | u = 4.0 / (f0 + 1.0e-6) + 4.0 / (f1 + 1.0e-6) 58 | 59 | rgb = np.stack((xs, ys, zs), axis=-1) * 0.5 + 0.5 60 | rgb = np.transpose(rgb, axes=(3, 2, 1, 0)) 61 | rgb = np.ascontiguousarray(rgb) 62 | 63 | # Test 64 | u = torch.from_numpy(u) 65 | rgb = torch.from_numpy(rgb) 66 | u = u.cuda() 67 | rgb = rgb.cuda() 68 | 69 | t_start = time.time() 70 | verts, faces = marching_cubes(u, 15.0) 71 | colors = grid_interp(rgb, verts) 72 | t_end = time.time() 73 | print(f"verts: {verts.size(0)}, faces: {faces.size(0)}, time: {t_end - t_start:.2f}s") 74 | 75 | verts = verts.detach().cpu().numpy() 76 | faces = faces.detach().cpu().numpy() 77 | colors = colors.detach().cpu().numpy() 78 | verts = (verts / (N - 1)) * 2.0 - 1.0 # Get back to the original space 79 | visualize(verts, faces, colors) 80 | ``` 81 | 82 | ## Screen shot 83 | 84 | ![metaball.png](./metaball.png) 85 | 86 | ## Copyright 87 | 88 | MIT License 2019-2024 (c) Tatsuya Yatagawa 89 | -------------------------------------------------------------------------------- /cxx/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories (${TORCH_INCLUDE_DIRS}) 2 | Python_add_library(${BUILD_TARGET} 3 | mcubes.cpp 4 | mcubes_cpu.cpp 5 | grid_interp_cpu.cpp) 6 | 7 | if (CMAKE_CUDA_COMPILER) 8 | target_sources(${BUILD_TARGET} PRIVATE 9 | cuda_utils.h 10 | helper_math.h 11 | macros.h 12 | mcubes_cuda.cu 13 | grid_interp_cuda.cu) 14 | endif() 15 | 16 | target_link_libraries(${BUILD_TARGET} PRIVATE 17 | pybind11::headers 18 | ${TORCH_LIBRARIES} 19 | ${TORCH_PYTHON_LIBRARY} 20 | ) 21 | 22 | if (OPENMP_FOUND) 23 | target_link_libraries(${BUILD_TARGET} PRIVATE OpenMP::OpenMP_CXX) 24 | endif() 25 | 26 | set_target_properties(${BUILD_TARGET} PROPERTIES 27 | INTERPROCEDURAL_OPTIMIZATION ON 28 | CXX_VISIBILITY_PRESET "hidden") 29 | 30 | target_compile_definitions(${BUILD_TARGET} PRIVATE VERSION_INFO=${PROJECT_VERSION}) 31 | 32 | pybind11_extension(${BUILD_TARGET}) 33 | pybind11_strip(${BUILD_TARGET}) 34 | -------------------------------------------------------------------------------- /cxx/cuda_utils.h: -------------------------------------------------------------------------------- 1 | #ifdef _MSC_VER 2 | #pragma once 3 | #endif 4 | 5 | #ifndef _CUDA_UTILS_H_ 6 | #define _CUDA_UTILS_H_ 7 | 8 | #include 9 | 10 | #define CUDA_CHECK_ERRORS() \ 11 | do { \ 12 | cudaError_t err = cudaGetLastError(); \ 13 | if (cudaSuccess != err) { \ 14 | fprintf(stderr, "CUDA kernel failed : %s\n%s at L:%d in %s\n", \ 15 | cudaGetErrorString(err), __PRETTY_FUNCTION__, \ 16 | __LINE__, __FILE__); \ 17 | std::exit(1); \ 18 | } \ 19 | } while(0) 20 | 21 | #endif // _CUDA_UTILS_H_ 22 | -------------------------------------------------------------------------------- /cxx/grid_interp_cpu.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define NOMIMAX 4 | #include 5 | #include 6 | 7 | #ifdef _OPENMP 8 | #include 9 | #endif 10 | 11 | #include "macros.h" 12 | 13 | torch::Tensor grid_interp_cpu(torch::Tensor vol, torch::Tensor points) { 14 | CHECK_CPU(vol); 15 | CHECK_CONTIGUOUS(vol); 16 | CHECK_N_DIM(vol, 4); 17 | 18 | CHECK_CPU(points); 19 | CHECK_CONTIGUOUS(points); 20 | CHECK_N_DIM(points, 2); 21 | 22 | const int Nx = vol.size(3); 23 | const int Ny = vol.size(2); 24 | const int Nz = vol.size(1); 25 | const int C = vol.size(0); 26 | const int Np = points.size(0); 27 | 28 | torch::Tensor output = torch::zeros({ Np, C }, torch::TensorOptions().dtype(torch::kFloat32).device(vol.device())); 29 | 30 | auto vol_ascr = vol.accessor(); 31 | auto pts_ascr = points.accessor(); 32 | auto out_ascr = output.accessor(); 33 | 34 | #ifdef _OPENMP 35 | #pragma omp parallel for 36 | #endif 37 | for (int i = 0; i < Np; i++) { 38 | const float x = pts_ascr[i][0]; 39 | const float y = pts_ascr[i][1]; 40 | const float z = pts_ascr[i][2]; 41 | 42 | const int ix = (int)x; 43 | const int iy = (int)y; 44 | const int iz = (int)z; 45 | const float fx = x - ix; 46 | const float fy = y - iy; 47 | const float fz = z - iz; 48 | 49 | for (int c = 0; c < C; c++) { 50 | const int x0 = std::max(0, std::min(ix, Nx - 1)); 51 | const int x1 = std::max(0, std::min(ix + 1, Nx - 1)); 52 | const int y0 = std::max(0, std::min(iy, Ny - 1)); 53 | const int y1 = std::max(0, std::min(iy + 1, Ny - 1)); 54 | const int z0 = std::max(0, std::min(iz, Nx - 1)); 55 | const int z1 = std::max(0, std::min(iz + 1, Nz - 1)); 56 | 57 | const float v00 = (1.0 - fx) * vol_ascr[c][z0][y0][x0] + fx * vol_ascr[c][z0][y0][x1]; 58 | const float v01 = (1.0 - fx) * vol_ascr[c][z0][y1][x0] + fx * vol_ascr[c][z0][y1][x1]; 59 | const float v10 = (1.0 - fx) * vol_ascr[c][z1][y0][x0] + fx * vol_ascr[c][z1][y0][x1]; 60 | const float v11 = (1.0 - fx) * vol_ascr[c][z1][y1][x0] + fx * vol_ascr[c][z1][y1][x1]; 61 | 62 | const float v0 = (1.0 - fy) * v00 + fy * v01; 63 | const float v1 = (1.0 - fy) * v10 + fy * v11; 64 | 65 | out_ascr[i][c] = (1.0 - fz) * v0 + fz * v1; 66 | } 67 | } 68 | 69 | return output; 70 | } 71 | -------------------------------------------------------------------------------- /cxx/grid_interp_cuda.cu: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | #include "macros.h" 8 | #include "cuda_utils.h" 9 | 10 | __global__ void grid_interp_cuda_kernel( 11 | const torch::PackedTensorAccessor32 vol, 12 | const torch::PackedTensorAccessor32 points, 13 | torch::PackedTensorAccessor32 output, 14 | int channels, int3 nGrids, size_t size) { 15 | 16 | const int tx = blockIdx.x * blockDim.x + threadIdx.x; 17 | const int ty = blockIdx.y * blockDim.y + threadIdx.y; 18 | const int tz = blockIdx.z * blockDim.z + threadIdx.z; 19 | const int index = (tz * blockDim.y * gridDim.y + ty) * blockDim.x * gridDim.x + tx; 20 | if (index >= size) { 21 | return; 22 | } 23 | 24 | const float x = points[index][0]; 25 | const float y = points[index][1]; 26 | const float z = points[index][2]; 27 | 28 | const int ix = (int)x; 29 | const int iy = (int)y; 30 | const int iz = (int)z; 31 | const float fx = x - ix; 32 | const float fy = y - iy; 33 | const float fz = z - iz; 34 | 35 | for (int c = 0; c < channels; c++) { 36 | const int x0 = max(0, min(ix, nGrids.x - 1)); 37 | const int x1 = max(0, min(ix + 1, nGrids.x - 1)); 38 | const int y0 = max(0, min(iy, nGrids.y - 1)); 39 | const int y1 = max(0, min(iy + 1, nGrids.y - 1)); 40 | const int z0 = max(0, min(iz, nGrids.z - 1)); 41 | const int z1 = max(0, min(iz + 1, nGrids.z - 1)); 42 | 43 | const float v00 = (1.0 - fx) * vol[c][z0][y0][x0] + fx * vol[c][z0][y0][x1]; 44 | const float v01 = (1.0 - fx) * vol[c][z0][y1][x0] + fx * vol[c][z0][y1][x1]; 45 | const float v10 = (1.0 - fx) * vol[c][z1][y0][x0] + fx * vol[c][z1][y0][x1]; 46 | const float v11 = (1.0 - fx) * vol[c][z1][y1][x0] + fx * vol[c][z1][y1][x1]; 47 | 48 | const float v0 = (1.0 - fy) * v00 + fy * v01; 49 | const float v1 = (1.0 - fy) * v10 + fy * v11; 50 | 51 | output[index][c] = (1.0 - fz) * v0 + fz * v1; 52 | } 53 | } 54 | 55 | torch::Tensor grid_interp_cuda(torch::Tensor vol, torch::Tensor points) { 56 | // Check input tensors 57 | CHECK_CUDA(vol); 58 | CHECK_CONTIGUOUS(vol); 59 | CHECK_IS_FLOAT(vol); 60 | CHECK_N_DIM(vol, 4); 61 | 62 | CHECK_CUDA(points); 63 | CHECK_CONTIGUOUS(points); 64 | CHECK_IS_FLOAT(vol); 65 | CHECK_N_DIM(points, 2); 66 | 67 | // Size parameters 68 | const int Nx = vol.size(3); 69 | const int Ny = vol.size(2); 70 | const int Nz = vol.size(1); 71 | const int C = vol.size(0); 72 | const int Np = points.size(0); 73 | const int deviceId = vol.device().index(); 74 | 75 | torch::Tensor output = torch::zeros({Np, C}, 76 | torch::TensorOptions().dtype(torch::kFloat32).device(torch::kCUDA, deviceId)); 77 | 78 | auto volAscr = vol.packed_accessor32(); 79 | auto ptsAscr = points.packed_accessor32(); 80 | auto outAscr = output.packed_accessor32(); 81 | 82 | const uint32_t MAX_THREADS_AXIS = 128; 83 | const uint32_t MAX_THREADS_AXIS2 = MAX_THREADS_AXIS * MAX_THREADS_AXIS; 84 | const uint32_t blockx = MAX_THREADS_AXIS; 85 | const uint32_t blocky = MAX_THREADS_AXIS; 86 | const uint32_t blockz = (Np + MAX_THREADS_AXIS2 - 1) / MAX_THREADS_AXIS2; 87 | 88 | const uint32_t BLOCK_SIZE = 8; 89 | const uint32_t gridx = (blockx + BLOCK_SIZE - 1) / BLOCK_SIZE; 90 | const uint32_t gridy = (blocky + BLOCK_SIZE - 1) / BLOCK_SIZE; 91 | const uint32_t gridz = (blockz + BLOCK_SIZE - 1) / BLOCK_SIZE; 92 | const int3 nGrids = make_int3(Nx, Ny, Nz); 93 | 94 | const dim3 blocks = { gridx, gridy, gridz }; 95 | const dim3 threads = { BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE }; 96 | const cudaStream_t stream = at::cuda::getCurrentCUDAStream(); 97 | 98 | grid_interp_cuda_kernel<<>>(volAscr, ptsAscr, outAscr, C, nGrids, Np); 99 | 100 | CUDA_CHECK_ERRORS(); 101 | 102 | return output; 103 | } 104 | -------------------------------------------------------------------------------- /cxx/helper_math.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 1993-2012 NVIDIA Corporation. All rights reserved. 3 | * 4 | * Please refer to the NVIDIA end user license agreement (EULA) associated 5 | * with this source code for terms and conditions that govern your use of 6 | * this software. Any use, reproduction, disclosure, or distribution of 7 | * this software and related documentation outside the terms of the EULA 8 | * is strictly prohibited. 9 | * 10 | */ 11 | 12 | /* 13 | * This file implements common mathematical operations on vector types 14 | * (float3, float4 etc.) since these are not provided as standard by CUDA. 15 | * 16 | * The syntax is modeled on the Cg standard library. 17 | * 18 | * This is part of the Helper library includes 19 | * 20 | * Thanks to Linh Hah for additions and fixes. 21 | */ 22 | 23 | #ifndef HELPER_MATH_H 24 | #define HELPER_MATH_H 25 | 26 | #include "cuda_runtime.h" 27 | 28 | typedef unsigned int uint; 29 | typedef unsigned short ushort; 30 | 31 | //////////////////////////////////////////////////////////////////////////////// 32 | // constructors 33 | //////////////////////////////////////////////////////////////////////////////// 34 | 35 | inline __host__ __device__ float2 make_float2(float s) { 36 | return make_float2(s, s); 37 | } 38 | inline __host__ __device__ float2 make_float2(float3 a) { 39 | return make_float2(a.x, a.y); 40 | } 41 | inline __host__ __device__ float2 make_float2(int2 a) { 42 | return make_float2(float(a.x), float(a.y)); 43 | } 44 | inline __host__ __device__ float2 make_float2(uint2 a) { 45 | return make_float2(float(a.x), float(a.y)); 46 | } 47 | 48 | inline __host__ __device__ int2 make_int2(int s) { 49 | return make_int2(s, s); 50 | } 51 | inline __host__ __device__ int2 make_int2(int3 a) { 52 | return make_int2(a.x, a.y); 53 | } 54 | inline __host__ __device__ int2 make_int2(uint2 a) { 55 | return make_int2(int(a.x), int(a.y)); 56 | } 57 | inline __host__ __device__ int2 make_int2(float2 a) { 58 | return make_int2(int(a.x), int(a.y)); 59 | } 60 | 61 | inline __host__ __device__ uint2 make_uint2(uint s) { 62 | return make_uint2(s, s); 63 | } 64 | inline __host__ __device__ uint2 make_uint2(uint3 a) { 65 | return make_uint2(a.x, a.y); 66 | } 67 | inline __host__ __device__ uint2 make_uint2(int2 a) { 68 | return make_uint2(uint(a.x), uint(a.y)); 69 | } 70 | 71 | inline __host__ __device__ float3 make_float3(float s) { 72 | return make_float3(s, s, s); 73 | } 74 | inline __host__ __device__ float3 make_float3(float2 a) { 75 | return make_float3(a.x, a.y, 0.0f); 76 | } 77 | inline __host__ __device__ float3 make_float3(float2 a, float s) { 78 | return make_float3(a.x, a.y, s); 79 | } 80 | inline __host__ __device__ float3 make_float3(float4 a) { 81 | return make_float3(a.x, a.y, a.z); 82 | } 83 | inline __host__ __device__ float3 make_float3(int3 a) { 84 | return make_float3(float(a.x), float(a.y), float(a.z)); 85 | } 86 | inline __host__ __device__ float3 make_float3(uint3 a) { 87 | return make_float3(float(a.x), float(a.y), float(a.z)); 88 | } 89 | 90 | inline __host__ __device__ int3 make_int3(int s) { 91 | return make_int3(s, s, s); 92 | } 93 | inline __host__ __device__ int3 make_int3(int2 a) { 94 | return make_int3(a.x, a.y, 0); 95 | } 96 | inline __host__ __device__ int3 make_int3(int2 a, int s) { 97 | return make_int3(a.x, a.y, s); 98 | } 99 | inline __host__ __device__ int3 make_int3(uint3 a) { 100 | return make_int3(int(a.x), int(a.y), int(a.z)); 101 | } 102 | inline __host__ __device__ int3 make_int3(float3 a) { 103 | return make_int3(int(a.x), int(a.y), int(a.z)); 104 | } 105 | 106 | inline __host__ __device__ uint3 make_uint3(uint s) { 107 | return make_uint3(s, s, s); 108 | } 109 | inline __host__ __device__ uint3 make_uint3(uint2 a) { 110 | return make_uint3(a.x, a.y, 0); 111 | } 112 | inline __host__ __device__ uint3 make_uint3(uint2 a, uint s) { 113 | return make_uint3(a.x, a.y, s); 114 | } 115 | inline __host__ __device__ uint3 make_uint3(uint4 a) { 116 | return make_uint3(a.x, a.y, a.z); 117 | } 118 | inline __host__ __device__ uint3 make_uint3(int3 a) { 119 | return make_uint3(uint(a.x), uint(a.y), uint(a.z)); 120 | } 121 | 122 | inline __host__ __device__ float4 make_float4(float s) { 123 | return make_float4(s, s, s, s); 124 | } 125 | inline __host__ __device__ float4 make_float4(float3 a) { 126 | return make_float4(a.x, a.y, a.z, 0.0f); 127 | } 128 | inline __host__ __device__ float4 make_float4(float3 a, float w) { 129 | return make_float4(a.x, a.y, a.z, w); 130 | } 131 | inline __host__ __device__ float4 make_float4(int4 a) { 132 | return make_float4(float(a.x), float(a.y), float(a.z), float(a.w)); 133 | } 134 | inline __host__ __device__ float4 make_float4(uint4 a) { 135 | return make_float4(float(a.x), float(a.y), float(a.z), float(a.w)); 136 | } 137 | 138 | inline __host__ __device__ int4 make_int4(int s) { 139 | return make_int4(s, s, s, s); 140 | } 141 | inline __host__ __device__ int4 make_int4(int3 a) { 142 | return make_int4(a.x, a.y, a.z, 0); 143 | } 144 | inline __host__ __device__ int4 make_int4(int3 a, int w) { 145 | return make_int4(a.x, a.y, a.z, w); 146 | } 147 | inline __host__ __device__ int4 make_int4(uint4 a) { 148 | return make_int4(int(a.x), int(a.y), int(a.z), int(a.w)); 149 | } 150 | inline __host__ __device__ int4 make_int4(float4 a) { 151 | return make_int4(int(a.x), int(a.y), int(a.z), int(a.w)); 152 | } 153 | 154 | inline __host__ __device__ uint4 make_uint4(uint s) { 155 | return make_uint4(s, s, s, s); 156 | } 157 | inline __host__ __device__ uint4 make_uint4(uint3 a) { 158 | return make_uint4(a.x, a.y, a.z, 0); 159 | } 160 | inline __host__ __device__ uint4 make_uint4(uint3 a, uint w) { 161 | return make_uint4(a.x, a.y, a.z, w); 162 | } 163 | inline __host__ __device__ uint4 make_uint4(int4 a) { 164 | return make_uint4(uint(a.x), uint(a.y), uint(a.z), uint(a.w)); 165 | } 166 | 167 | //////////////////////////////////////////////////////////////////////////////// 168 | // negate 169 | //////////////////////////////////////////////////////////////////////////////// 170 | 171 | inline __host__ __device__ float2 operator-(float2 &a) { 172 | return make_float2(-a.x, -a.y); 173 | } 174 | inline __host__ __device__ int2 operator-(int2 &a) { 175 | return make_int2(-a.x, -a.y); 176 | } 177 | inline __host__ __device__ float3 operator-(float3 &a) { 178 | return make_float3(-a.x, -a.y, -a.z); 179 | } 180 | inline __host__ __device__ int3 operator-(int3 &a) { 181 | return make_int3(-a.x, -a.y, -a.z); 182 | } 183 | inline __host__ __device__ float4 operator-(float4 &a) { 184 | return make_float4(-a.x, -a.y, -a.z, -a.w); 185 | } 186 | inline __host__ __device__ int4 operator-(int4 &a) { 187 | return make_int4(-a.x, -a.y, -a.z, -a.w); 188 | } 189 | 190 | //////////////////////////////////////////////////////////////////////////////// 191 | // addition 192 | //////////////////////////////////////////////////////////////////////////////// 193 | 194 | inline __host__ __device__ float2 operator+(float2 a, float2 b) { 195 | return make_float2(a.x + b.x, a.y + b.y); 196 | } 197 | inline __host__ __device__ void operator+=(float2 &a, float2 b) { 198 | a.x += b.x; 199 | a.y += b.y; 200 | } 201 | inline __host__ __device__ float2 operator+(float2 a, float b) { 202 | return make_float2(a.x + b, a.y + b); 203 | } 204 | inline __host__ __device__ float2 operator+(float b, float2 a) { 205 | return make_float2(a.x + b, a.y + b); 206 | } 207 | inline __host__ __device__ void operator+=(float2 &a, float b) { 208 | a.x += b; 209 | a.y += b; 210 | } 211 | 212 | inline __host__ __device__ int2 operator+(int2 a, int2 b) { 213 | return make_int2(a.x + b.x, a.y + b.y); 214 | } 215 | inline __host__ __device__ void operator+=(int2 &a, int2 b) { 216 | a.x += b.x; 217 | a.y += b.y; 218 | } 219 | inline __host__ __device__ int2 operator+(int2 a, int b) { 220 | return make_int2(a.x + b, a.y + b); 221 | } 222 | inline __host__ __device__ int2 operator+(int b, int2 a) { 223 | return make_int2(a.x + b, a.y + b); 224 | } 225 | inline __host__ __device__ void operator+=(int2 &a, int b) { 226 | a.x += b; 227 | a.y += b; 228 | } 229 | 230 | inline __host__ __device__ uint2 operator+(uint2 a, uint2 b) { 231 | return make_uint2(a.x + b.x, a.y + b.y); 232 | } 233 | inline __host__ __device__ void operator+=(uint2 &a, uint2 b) { 234 | a.x += b.x; 235 | a.y += b.y; 236 | } 237 | inline __host__ __device__ uint2 operator+(uint2 a, uint b) { 238 | return make_uint2(a.x + b, a.y + b); 239 | } 240 | inline __host__ __device__ uint2 operator+(uint b, uint2 a) { 241 | return make_uint2(a.x + b, a.y + b); 242 | } 243 | inline __host__ __device__ void operator+=(uint2 &a, uint b) { 244 | a.x += b; 245 | a.y += b; 246 | } 247 | 248 | inline __host__ __device__ float3 operator+(float3 a, float3 b) { 249 | return make_float3(a.x + b.x, a.y + b.y, a.z + b.z); 250 | } 251 | inline __host__ __device__ void operator+=(float3 &a, float3 b) { 252 | a.x += b.x; 253 | a.y += b.y; 254 | a.z += b.z; 255 | } 256 | inline __host__ __device__ float3 operator+(float3 a, float b) { 257 | return make_float3(a.x + b, a.y + b, a.z + b); 258 | } 259 | inline __host__ __device__ void operator+=(float3 &a, float b) { 260 | a.x += b; 261 | a.y += b; 262 | a.z += b; 263 | } 264 | 265 | inline __host__ __device__ int3 operator+(int3 a, int3 b) { 266 | return make_int3(a.x + b.x, a.y + b.y, a.z + b.z); 267 | } 268 | inline __host__ __device__ void operator+=(int3 &a, int3 b) { 269 | a.x += b.x; 270 | a.y += b.y; 271 | a.z += b.z; 272 | } 273 | inline __host__ __device__ int3 operator+(int3 a, int b) { 274 | return make_int3(a.x + b, a.y + b, a.z + b); 275 | } 276 | inline __host__ __device__ void operator+=(int3 &a, int b) { 277 | a.x += b; 278 | a.y += b; 279 | a.z += b; 280 | } 281 | 282 | inline __host__ __device__ uint3 operator+(uint3 a, uint3 b) { 283 | return make_uint3(a.x + b.x, a.y + b.y, a.z + b.z); 284 | } 285 | inline __host__ __device__ void operator+=(uint3 &a, uint3 b) { 286 | a.x += b.x; 287 | a.y += b.y; 288 | a.z += b.z; 289 | } 290 | inline __host__ __device__ uint3 operator+(uint3 a, uint b) { 291 | return make_uint3(a.x + b, a.y + b, a.z + b); 292 | } 293 | inline __host__ __device__ void operator+=(uint3 &a, uint b) { 294 | a.x += b; 295 | a.y += b; 296 | a.z += b; 297 | } 298 | 299 | inline __host__ __device__ int3 operator+(int b, int3 a) { 300 | return make_int3(a.x + b, a.y + b, a.z + b); 301 | } 302 | inline __host__ __device__ uint3 operator+(uint b, uint3 a) { 303 | return make_uint3(a.x + b, a.y + b, a.z + b); 304 | } 305 | inline __host__ __device__ float3 operator+(float b, float3 a) { 306 | return make_float3(a.x + b, a.y + b, a.z + b); 307 | } 308 | 309 | inline __host__ __device__ float4 operator+(float4 a, float4 b) { 310 | return make_float4(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w); 311 | } 312 | inline __host__ __device__ void operator+=(float4 &a, float4 b) { 313 | a.x += b.x; 314 | a.y += b.y; 315 | a.z += b.z; 316 | a.w += b.w; 317 | } 318 | inline __host__ __device__ float4 operator+(float4 a, float b) { 319 | return make_float4(a.x + b, a.y + b, a.z + b, a.w + b); 320 | } 321 | inline __host__ __device__ float4 operator+(float b, float4 a) { 322 | return make_float4(a.x + b, a.y + b, a.z + b, a.w + b); 323 | } 324 | inline __host__ __device__ void operator+=(float4 &a, float b) { 325 | a.x += b; 326 | a.y += b; 327 | a.z += b; 328 | a.w += b; 329 | } 330 | 331 | inline __host__ __device__ int4 operator+(int4 a, int4 b) { 332 | return make_int4(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w); 333 | } 334 | inline __host__ __device__ void operator+=(int4 &a, int4 b) { 335 | a.x += b.x; 336 | a.y += b.y; 337 | a.z += b.z; 338 | a.w += b.w; 339 | } 340 | inline __host__ __device__ int4 operator+(int4 a, int b) { 341 | return make_int4(a.x + b, a.y + b, a.z + b, a.w + b); 342 | } 343 | inline __host__ __device__ int4 operator+(int b, int4 a) { 344 | return make_int4(a.x + b, a.y + b, a.z + b, a.w + b); 345 | } 346 | inline __host__ __device__ void operator+=(int4 &a, int b) { 347 | a.x += b; 348 | a.y += b; 349 | a.z += b; 350 | a.w += b; 351 | } 352 | 353 | inline __host__ __device__ uint4 operator+(uint4 a, uint4 b) { 354 | return make_uint4(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w); 355 | } 356 | inline __host__ __device__ void operator+=(uint4 &a, uint4 b) { 357 | a.x += b.x; 358 | a.y += b.y; 359 | a.z += b.z; 360 | a.w += b.w; 361 | } 362 | inline __host__ __device__ uint4 operator+(uint4 a, uint b) { 363 | return make_uint4(a.x + b, a.y + b, a.z + b, a.w + b); 364 | } 365 | inline __host__ __device__ uint4 operator+(uint b, uint4 a) { 366 | return make_uint4(a.x + b, a.y + b, a.z + b, a.w + b); 367 | } 368 | inline __host__ __device__ void operator+=(uint4 &a, uint b) { 369 | a.x += b; 370 | a.y += b; 371 | a.z += b; 372 | a.w += b; 373 | } 374 | 375 | //////////////////////////////////////////////////////////////////////////////// 376 | // subtract 377 | //////////////////////////////////////////////////////////////////////////////// 378 | 379 | inline __host__ __device__ float2 operator-(float2 a, float2 b) { 380 | return make_float2(a.x - b.x, a.y - b.y); 381 | } 382 | inline __host__ __device__ void operator-=(float2 &a, float2 b) { 383 | a.x -= b.x; 384 | a.y -= b.y; 385 | } 386 | inline __host__ __device__ float2 operator-(float2 a, float b) { 387 | return make_float2(a.x - b, a.y - b); 388 | } 389 | inline __host__ __device__ float2 operator-(float b, float2 a) { 390 | return make_float2(b - a.x, b - a.y); 391 | } 392 | inline __host__ __device__ void operator-=(float2 &a, float b) { 393 | a.x -= b; 394 | a.y -= b; 395 | } 396 | 397 | inline __host__ __device__ int2 operator-(int2 a, int2 b) { 398 | return make_int2(a.x - b.x, a.y - b.y); 399 | } 400 | inline __host__ __device__ void operator-=(int2 &a, int2 b) { 401 | a.x -= b.x; 402 | a.y -= b.y; 403 | } 404 | inline __host__ __device__ int2 operator-(int2 a, int b) { 405 | return make_int2(a.x - b, a.y - b); 406 | } 407 | inline __host__ __device__ int2 operator-(int b, int2 a) { 408 | return make_int2(b - a.x, b - a.y); 409 | } 410 | inline __host__ __device__ void operator-=(int2 &a, int b) { 411 | a.x -= b; 412 | a.y -= b; 413 | } 414 | 415 | inline __host__ __device__ uint2 operator-(uint2 a, uint2 b) { 416 | return make_uint2(a.x - b.x, a.y - b.y); 417 | } 418 | inline __host__ __device__ void operator-=(uint2 &a, uint2 b) { 419 | a.x -= b.x; 420 | a.y -= b.y; 421 | } 422 | inline __host__ __device__ uint2 operator-(uint2 a, uint b) { 423 | return make_uint2(a.x - b, a.y - b); 424 | } 425 | inline __host__ __device__ uint2 operator-(uint b, uint2 a) { 426 | return make_uint2(b - a.x, b - a.y); 427 | } 428 | inline __host__ __device__ void operator-=(uint2 &a, uint b) { 429 | a.x -= b; 430 | a.y -= b; 431 | } 432 | 433 | inline __host__ __device__ float3 operator-(float3 a, float3 b) { 434 | return make_float3(a.x - b.x, a.y - b.y, a.z - b.z); 435 | } 436 | inline __host__ __device__ void operator-=(float3 &a, float3 b) { 437 | a.x -= b.x; 438 | a.y -= b.y; 439 | a.z -= b.z; 440 | } 441 | inline __host__ __device__ float3 operator-(float3 a, float b) { 442 | return make_float3(a.x - b, a.y - b, a.z - b); 443 | } 444 | inline __host__ __device__ float3 operator-(float b, float3 a) { 445 | return make_float3(b - a.x, b - a.y, b - a.z); 446 | } 447 | inline __host__ __device__ void operator-=(float3 &a, float b) { 448 | a.x -= b; 449 | a.y -= b; 450 | a.z -= b; 451 | } 452 | 453 | inline __host__ __device__ int3 operator-(int3 a, int3 b) { 454 | return make_int3(a.x - b.x, a.y - b.y, a.z - b.z); 455 | } 456 | inline __host__ __device__ void operator-=(int3 &a, int3 b) { 457 | a.x -= b.x; 458 | a.y -= b.y; 459 | a.z -= b.z; 460 | } 461 | inline __host__ __device__ int3 operator-(int3 a, int b) { 462 | return make_int3(a.x - b, a.y - b, a.z - b); 463 | } 464 | inline __host__ __device__ int3 operator-(int b, int3 a) { 465 | return make_int3(b - a.x, b - a.y, b - a.z); 466 | } 467 | inline __host__ __device__ void operator-=(int3 &a, int b) { 468 | a.x -= b; 469 | a.y -= b; 470 | a.z -= b; 471 | } 472 | 473 | inline __host__ __device__ uint3 operator-(uint3 a, uint3 b) { 474 | return make_uint3(a.x - b.x, a.y - b.y, a.z - b.z); 475 | } 476 | inline __host__ __device__ void operator-=(uint3 &a, uint3 b) { 477 | a.x -= b.x; 478 | a.y -= b.y; 479 | a.z -= b.z; 480 | } 481 | inline __host__ __device__ uint3 operator-(uint3 a, uint b) { 482 | return make_uint3(a.x - b, a.y - b, a.z - b); 483 | } 484 | inline __host__ __device__ uint3 operator-(uint b, uint3 a) { 485 | return make_uint3(b - a.x, b - a.y, b - a.z); 486 | } 487 | inline __host__ __device__ void operator-=(uint3 &a, uint b) { 488 | a.x -= b; 489 | a.y -= b; 490 | a.z -= b; 491 | } 492 | 493 | inline __host__ __device__ float4 operator-(float4 a, float4 b) { 494 | return make_float4(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w); 495 | } 496 | inline __host__ __device__ void operator-=(float4 &a, float4 b) { 497 | a.x -= b.x; 498 | a.y -= b.y; 499 | a.z -= b.z; 500 | a.w -= b.w; 501 | } 502 | inline __host__ __device__ float4 operator-(float4 a, float b) { 503 | return make_float4(a.x - b, a.y - b, a.z - b, a.w - b); 504 | } 505 | inline __host__ __device__ void operator-=(float4 &a, float b) { 506 | a.x -= b; 507 | a.y -= b; 508 | a.z -= b; 509 | a.w -= b; 510 | } 511 | 512 | inline __host__ __device__ int4 operator-(int4 a, int4 b) { 513 | return make_int4(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w); 514 | } 515 | inline __host__ __device__ void operator-=(int4 &a, int4 b) { 516 | a.x -= b.x; 517 | a.y -= b.y; 518 | a.z -= b.z; 519 | a.w -= b.w; 520 | } 521 | inline __host__ __device__ int4 operator-(int4 a, int b) { 522 | return make_int4(a.x - b, a.y - b, a.z - b, a.w - b); 523 | } 524 | inline __host__ __device__ int4 operator-(int b, int4 a) { 525 | return make_int4(b - a.x, b - a.y, b - a.z, b - a.w); 526 | } 527 | inline __host__ __device__ void operator-=(int4 &a, int b) { 528 | a.x -= b; 529 | a.y -= b; 530 | a.z -= b; 531 | a.w -= b; 532 | } 533 | 534 | inline __host__ __device__ uint4 operator-(uint4 a, uint4 b) { 535 | return make_uint4(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w); 536 | } 537 | inline __host__ __device__ void operator-=(uint4 &a, uint4 b) { 538 | a.x -= b.x; 539 | a.y -= b.y; 540 | a.z -= b.z; 541 | a.w -= b.w; 542 | } 543 | inline __host__ __device__ uint4 operator-(uint4 a, uint b) { 544 | return make_uint4(a.x - b, a.y - b, a.z - b, a.w - b); 545 | } 546 | inline __host__ __device__ uint4 operator-(uint b, uint4 a) { 547 | return make_uint4(b - a.x, b - a.y, b - a.z, b - a.w); 548 | } 549 | inline __host__ __device__ void operator-=(uint4 &a, uint b) { 550 | a.x -= b; 551 | a.y -= b; 552 | a.z -= b; 553 | a.w -= b; 554 | } 555 | 556 | //////////////////////////////////////////////////////////////////////////////// 557 | // multiply 558 | //////////////////////////////////////////////////////////////////////////////// 559 | 560 | inline __host__ __device__ float2 operator*(float2 a, float2 b) { 561 | return make_float2(a.x * b.x, a.y * b.y); 562 | } 563 | inline __host__ __device__ void operator*=(float2 &a, float2 b) { 564 | a.x *= b.x; 565 | a.y *= b.y; 566 | } 567 | inline __host__ __device__ float2 operator*(float2 a, float b) { 568 | return make_float2(a.x * b, a.y * b); 569 | } 570 | inline __host__ __device__ float2 operator*(float b, float2 a) { 571 | return make_float2(b * a.x, b * a.y); 572 | } 573 | inline __host__ __device__ void operator*=(float2 &a, float b) { 574 | a.x *= b; 575 | a.y *= b; 576 | } 577 | 578 | inline __host__ __device__ int2 operator*(int2 a, int2 b) { 579 | return make_int2(a.x * b.x, a.y * b.y); 580 | } 581 | inline __host__ __device__ void operator*=(int2 &a, int2 b) { 582 | a.x *= b.x; 583 | a.y *= b.y; 584 | } 585 | inline __host__ __device__ int2 operator*(int2 a, int b) { 586 | return make_int2(a.x * b, a.y * b); 587 | } 588 | inline __host__ __device__ int2 operator*(int b, int2 a) { 589 | return make_int2(b * a.x, b * a.y); 590 | } 591 | inline __host__ __device__ void operator*=(int2 &a, int b) { 592 | a.x *= b; 593 | a.y *= b; 594 | } 595 | 596 | inline __host__ __device__ uint2 operator*(uint2 a, uint2 b) { 597 | return make_uint2(a.x * b.x, a.y * b.y); 598 | } 599 | inline __host__ __device__ void operator*=(uint2 &a, uint2 b) { 600 | a.x *= b.x; 601 | a.y *= b.y; 602 | } 603 | inline __host__ __device__ uint2 operator*(uint2 a, uint b) { 604 | return make_uint2(a.x * b, a.y * b); 605 | } 606 | inline __host__ __device__ uint2 operator*(uint b, uint2 a) { 607 | return make_uint2(b * a.x, b * a.y); 608 | } 609 | inline __host__ __device__ void operator*=(uint2 &a, uint b) { 610 | a.x *= b; 611 | a.y *= b; 612 | } 613 | 614 | inline __host__ __device__ float3 operator*(float3 a, float3 b) { 615 | return make_float3(a.x * b.x, a.y * b.y, a.z * b.z); 616 | } 617 | inline __host__ __device__ void operator*=(float3 &a, float3 b) { 618 | a.x *= b.x; 619 | a.y *= b.y; 620 | a.z *= b.z; 621 | } 622 | inline __host__ __device__ float3 operator*(float3 a, float b) { 623 | return make_float3(a.x * b, a.y * b, a.z * b); 624 | } 625 | inline __host__ __device__ float3 operator*(float b, float3 a) { 626 | return make_float3(b * a.x, b * a.y, b * a.z); 627 | } 628 | inline __host__ __device__ void operator*=(float3 &a, float b) { 629 | a.x *= b; 630 | a.y *= b; 631 | a.z *= b; 632 | } 633 | 634 | inline __host__ __device__ int3 operator*(int3 a, int3 b) { 635 | return make_int3(a.x * b.x, a.y * b.y, a.z * b.z); 636 | } 637 | inline __host__ __device__ void operator*=(int3 &a, int3 b) { 638 | a.x *= b.x; 639 | a.y *= b.y; 640 | a.z *= b.z; 641 | } 642 | inline __host__ __device__ int3 operator*(int3 a, int b) { 643 | return make_int3(a.x * b, a.y * b, a.z * b); 644 | } 645 | inline __host__ __device__ int3 operator*(int b, int3 a) { 646 | return make_int3(b * a.x, b * a.y, b * a.z); 647 | } 648 | inline __host__ __device__ void operator*=(int3 &a, int b) { 649 | a.x *= b; 650 | a.y *= b; 651 | a.z *= b; 652 | } 653 | 654 | inline __host__ __device__ uint3 operator*(uint3 a, uint3 b) { 655 | return make_uint3(a.x * b.x, a.y * b.y, a.z * b.z); 656 | } 657 | inline __host__ __device__ void operator*=(uint3 &a, uint3 b) { 658 | a.x *= b.x; 659 | a.y *= b.y; 660 | a.z *= b.z; 661 | } 662 | inline __host__ __device__ uint3 operator*(uint3 a, uint b) { 663 | return make_uint3(a.x * b, a.y * b, a.z * b); 664 | } 665 | inline __host__ __device__ uint3 operator*(uint b, uint3 a) { 666 | return make_uint3(b * a.x, b * a.y, b * a.z); 667 | } 668 | inline __host__ __device__ void operator*=(uint3 &a, uint b) { 669 | a.x *= b; 670 | a.y *= b; 671 | a.z *= b; 672 | } 673 | 674 | inline __host__ __device__ float4 operator*(float4 a, float4 b) { 675 | return make_float4(a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w); 676 | } 677 | inline __host__ __device__ void operator*=(float4 &a, float4 b) { 678 | a.x *= b.x; 679 | a.y *= b.y; 680 | a.z *= b.z; 681 | a.w *= b.w; 682 | } 683 | inline __host__ __device__ float4 operator*(float4 a, float b) { 684 | return make_float4(a.x * b, a.y * b, a.z * b, a.w * b); 685 | } 686 | inline __host__ __device__ float4 operator*(float b, float4 a) { 687 | return make_float4(b * a.x, b * a.y, b * a.z, b * a.w); 688 | } 689 | inline __host__ __device__ void operator*=(float4 &a, float b) { 690 | a.x *= b; 691 | a.y *= b; 692 | a.z *= b; 693 | a.w *= b; 694 | } 695 | 696 | inline __host__ __device__ int4 operator*(int4 a, int4 b) { 697 | return make_int4(a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w); 698 | } 699 | inline __host__ __device__ void operator*=(int4 &a, int4 b) { 700 | a.x *= b.x; 701 | a.y *= b.y; 702 | a.z *= b.z; 703 | a.w *= b.w; 704 | } 705 | inline __host__ __device__ int4 operator*(int4 a, int b) { 706 | return make_int4(a.x * b, a.y * b, a.z * b, a.w * b); 707 | } 708 | inline __host__ __device__ int4 operator*(int b, int4 a) { 709 | return make_int4(b * a.x, b * a.y, b * a.z, b * a.w); 710 | } 711 | inline __host__ __device__ void operator*=(int4 &a, int b) { 712 | a.x *= b; 713 | a.y *= b; 714 | a.z *= b; 715 | a.w *= b; 716 | } 717 | 718 | inline __host__ __device__ uint4 operator*(uint4 a, uint4 b) { 719 | return make_uint4(a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w); 720 | } 721 | inline __host__ __device__ void operator*=(uint4 &a, uint4 b) { 722 | a.x *= b.x; 723 | a.y *= b.y; 724 | a.z *= b.z; 725 | a.w *= b.w; 726 | } 727 | inline __host__ __device__ uint4 operator*(uint4 a, uint b) { 728 | return make_uint4(a.x * b, a.y * b, a.z * b, a.w * b); 729 | } 730 | inline __host__ __device__ uint4 operator*(uint b, uint4 a) { 731 | return make_uint4(b * a.x, b * a.y, b * a.z, b * a.w); 732 | } 733 | inline __host__ __device__ void operator*=(uint4 &a, uint b) { 734 | a.x *= b; 735 | a.y *= b; 736 | a.z *= b; 737 | a.w *= b; 738 | } 739 | 740 | //////////////////////////////////////////////////////////////////////////////// 741 | // divide 742 | //////////////////////////////////////////////////////////////////////////////// 743 | 744 | inline __host__ __device__ float2 operator/(float2 a, float2 b) { 745 | return make_float2(a.x / b.x, a.y / b.y); 746 | } 747 | inline __host__ __device__ void operator/=(float2 &a, float2 b) { 748 | a.x /= b.x; 749 | a.y /= b.y; 750 | } 751 | inline __host__ __device__ float2 operator/(float2 a, float b) { 752 | return make_float2(a.x / b, a.y / b); 753 | } 754 | inline __host__ __device__ void operator/=(float2 &a, float b) { 755 | a.x /= b; 756 | a.y /= b; 757 | } 758 | inline __host__ __device__ float2 operator/(float b, float2 a) { 759 | return make_float2(b / a.x, b / a.y); 760 | } 761 | 762 | inline __host__ __device__ float3 operator/(float3 a, float3 b) { 763 | return make_float3(a.x / b.x, a.y / b.y, a.z / b.z); 764 | } 765 | inline __host__ __device__ void operator/=(float3 &a, float3 b) { 766 | a.x /= b.x; 767 | a.y /= b.y; 768 | a.z /= b.z; 769 | } 770 | inline __host__ __device__ float3 operator/(float3 a, float b) { 771 | return make_float3(a.x / b, a.y / b, a.z / b); 772 | } 773 | inline __host__ __device__ void operator/=(float3 &a, float b) { 774 | a.x /= b; 775 | a.y /= b; 776 | a.z /= b; 777 | } 778 | inline __host__ __device__ float3 operator/(float b, float3 a) { 779 | return make_float3(b / a.x, b / a.y, b / a.z); 780 | } 781 | 782 | inline __host__ __device__ float4 operator/(float4 a, float4 b) { 783 | return make_float4(a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w); 784 | } 785 | inline __host__ __device__ void operator/=(float4 &a, float4 b) { 786 | a.x /= b.x; 787 | a.y /= b.y; 788 | a.z /= b.z; 789 | a.w /= b.w; 790 | } 791 | inline __host__ __device__ float4 operator/(float4 a, float b) { 792 | return make_float4(a.x / b, a.y / b, a.z / b, a.w / b); 793 | } 794 | inline __host__ __device__ void operator/=(float4 &a, float b) { 795 | a.x /= b; 796 | a.y /= b; 797 | a.z /= b; 798 | a.w /= b; 799 | } 800 | inline __host__ __device__ float4 operator/(float b, float4 a) { 801 | return make_float4(b / a.x, b / a.y, b / a.z, b / a.w); 802 | } 803 | 804 | //////////////////////////////////////////////////////////////////////////////// 805 | // min 806 | //////////////////////////////////////////////////////////////////////////////// 807 | 808 | inline __host__ __device__ float2 fminf(float2 a, float2 b) { 809 | return make_float2(fminf(a.x, b.x), fminf(a.y, b.y)); 810 | } 811 | inline __host__ __device__ float3 fminf(float3 a, float3 b) { 812 | return make_float3(fminf(a.x, b.x), fminf(a.y, b.y), fminf(a.z, b.z)); 813 | } 814 | inline __host__ __device__ float4 fminf(float4 a, float4 b) { 815 | return make_float4(fminf(a.x, b.x), fminf(a.y, b.y), fminf(a.z, b.z), fminf(a.w, b.w)); 816 | } 817 | 818 | inline __host__ __device__ int2 min(int2 a, int2 b) { 819 | return make_int2(min(a.x, b.x), min(a.y, b.y)); 820 | } 821 | inline __host__ __device__ int3 min(int3 a, int3 b) { 822 | return make_int3(min(a.x, b.x), min(a.y, b.y), min(a.z, b.z)); 823 | } 824 | inline __host__ __device__ int4 min(int4 a, int4 b) { 825 | return make_int4(min(a.x, b.x), min(a.y, b.y), min(a.z, b.z), min(a.w, b.w)); 826 | } 827 | 828 | inline __host__ __device__ uint2 min(uint2 a, uint2 b) { 829 | return make_uint2(min(a.x, b.x), min(a.y, b.y)); 830 | } 831 | inline __host__ __device__ uint3 min(uint3 a, uint3 b) { 832 | return make_uint3(min(a.x, b.x), min(a.y, b.y), min(a.z, b.z)); 833 | } 834 | inline __host__ __device__ uint4 min(uint4 a, uint4 b) { 835 | return make_uint4(min(a.x, b.x), min(a.y, b.y), min(a.z, b.z), min(a.w, b.w)); 836 | } 837 | 838 | //////////////////////////////////////////////////////////////////////////////// 839 | // max 840 | //////////////////////////////////////////////////////////////////////////////// 841 | 842 | inline __host__ __device__ float2 fmaxf(float2 a, float2 b) { 843 | return make_float2(fmaxf(a.x, b.x), fmaxf(a.y, b.y)); 844 | } 845 | inline __host__ __device__ float3 fmaxf(float3 a, float3 b) { 846 | return make_float3(fmaxf(a.x, b.x), fmaxf(a.y, b.y), fmaxf(a.z, b.z)); 847 | } 848 | inline __host__ __device__ float4 fmaxf(float4 a, float4 b) { 849 | return make_float4(fmaxf(a.x, b.x), fmaxf(a.y, b.y), fmaxf(a.z, b.z), fmaxf(a.w, b.w)); 850 | } 851 | 852 | inline __host__ __device__ int2 max(int2 a, int2 b) { 853 | return make_int2(max(a.x, b.x), max(a.y, b.y)); 854 | } 855 | inline __host__ __device__ int3 max(int3 a, int3 b) { 856 | return make_int3(max(a.x, b.x), max(a.y, b.y), max(a.z, b.z)); 857 | } 858 | inline __host__ __device__ int4 max(int4 a, int4 b) { 859 | return make_int4(max(a.x, b.x), max(a.y, b.y), max(a.z, b.z), max(a.w, b.w)); 860 | } 861 | 862 | inline __host__ __device__ uint2 max(uint2 a, uint2 b) { 863 | return make_uint2(max(a.x, b.x), max(a.y, b.y)); 864 | } 865 | inline __host__ __device__ uint3 max(uint3 a, uint3 b) { 866 | return make_uint3(max(a.x, b.x), max(a.y, b.y), max(a.z, b.z)); 867 | } 868 | inline __host__ __device__ uint4 max(uint4 a, uint4 b) { 869 | return make_uint4(max(a.x, b.x), max(a.y, b.y), max(a.z, b.z), max(a.w, b.w)); 870 | } 871 | 872 | //////////////////////////////////////////////////////////////////////////////// 873 | // lerp 874 | // - linear interpolation between a and b, based on value t in [0, 1] range 875 | //////////////////////////////////////////////////////////////////////////////// 876 | 877 | inline __device__ __host__ float lerp(float a, float b, float t) { 878 | return a + t * (b - a); 879 | } 880 | inline __device__ __host__ float2 lerp(float2 a, float2 b, float t) { 881 | return a + t * (b - a); 882 | } 883 | inline __device__ __host__ float3 lerp(float3 a, float3 b, float t) { 884 | return a + t * (b - a); 885 | } 886 | inline __device__ __host__ float4 lerp(float4 a, float4 b, float t) { 887 | return a + t * (b - a); 888 | } 889 | 890 | //////////////////////////////////////////////////////////////////////////////// 891 | // clamp 892 | // - clamp the value v to be in the range [a, b] 893 | //////////////////////////////////////////////////////////////////////////////// 894 | 895 | inline __device__ __host__ float clamp(float f, float a, float b) { 896 | return fmaxf(a, fminf(f, b)); 897 | } 898 | inline __device__ __host__ int clamp(int f, int a, int b) { 899 | return max(a, min(f, b)); 900 | } 901 | inline __device__ __host__ uint clamp(uint f, uint a, uint b) { 902 | return max(a, min(f, b)); 903 | } 904 | 905 | inline __device__ __host__ float2 clamp(float2 v, float a, float b) { 906 | return make_float2(clamp(v.x, a, b), clamp(v.y, a, b)); 907 | } 908 | inline __device__ __host__ float2 clamp(float2 v, float2 a, float2 b) { 909 | return make_float2(clamp(v.x, a.x, b.x), clamp(v.y, a.y, b.y)); 910 | } 911 | inline __device__ __host__ float3 clamp(float3 v, float a, float b) { 912 | return make_float3(clamp(v.x, a, b), clamp(v.y, a, b), clamp(v.z, a, b)); 913 | } 914 | inline __device__ __host__ float3 clamp(float3 v, float3 a, float3 b) { 915 | return make_float3(clamp(v.x, a.x, b.x), clamp(v.y, a.y, b.y), clamp(v.z, a.z, b.z)); 916 | } 917 | inline __device__ __host__ float4 clamp(float4 v, float a, float b) { 918 | return make_float4(clamp(v.x, a, b), clamp(v.y, a, b), clamp(v.z, a, b), clamp(v.w, a, b)); 919 | } 920 | inline __device__ __host__ float4 clamp(float4 v, float4 a, float4 b) { 921 | return make_float4(clamp(v.x, a.x, b.x), clamp(v.y, a.y, b.y), clamp(v.z, a.z, b.z), clamp(v.w, a.w, b.w)); 922 | } 923 | 924 | inline __device__ __host__ int2 clamp(int2 v, int a, int b) { 925 | return make_int2(clamp(v.x, a, b), clamp(v.y, a, b)); 926 | } 927 | inline __device__ __host__ int2 clamp(int2 v, int2 a, int2 b) { 928 | return make_int2(clamp(v.x, a.x, b.x), clamp(v.y, a.y, b.y)); 929 | } 930 | inline __device__ __host__ int3 clamp(int3 v, int a, int b) { 931 | return make_int3(clamp(v.x, a, b), clamp(v.y, a, b), clamp(v.z, a, b)); 932 | } 933 | inline __device__ __host__ int3 clamp(int3 v, int3 a, int3 b) { 934 | return make_int3(clamp(v.x, a.x, b.x), clamp(v.y, a.y, b.y), clamp(v.z, a.z, b.z)); 935 | } 936 | inline __device__ __host__ int4 clamp(int4 v, int a, int b) { 937 | return make_int4(clamp(v.x, a, b), clamp(v.y, a, b), clamp(v.z, a, b), clamp(v.w, a, b)); 938 | } 939 | inline __device__ __host__ int4 clamp(int4 v, int4 a, int4 b) { 940 | return make_int4(clamp(v.x, a.x, b.x), clamp(v.y, a.y, b.y), clamp(v.z, a.z, b.z), clamp(v.w, a.w, b.w)); 941 | } 942 | 943 | inline __device__ __host__ uint2 clamp(uint2 v, uint a, uint b) { 944 | return make_uint2(clamp(v.x, a, b), clamp(v.y, a, b)); 945 | } 946 | inline __device__ __host__ uint2 clamp(uint2 v, uint2 a, uint2 b) { 947 | return make_uint2(clamp(v.x, a.x, b.x), clamp(v.y, a.y, b.y)); 948 | } 949 | inline __device__ __host__ uint3 clamp(uint3 v, uint a, uint b) { 950 | return make_uint3(clamp(v.x, a, b), clamp(v.y, a, b), clamp(v.z, a, b)); 951 | } 952 | inline __device__ __host__ uint3 clamp(uint3 v, uint3 a, uint3 b) { 953 | return make_uint3(clamp(v.x, a.x, b.x), clamp(v.y, a.y, b.y), clamp(v.z, a.z, b.z)); 954 | } 955 | inline __device__ __host__ uint4 clamp(uint4 v, uint a, uint b) { 956 | return make_uint4(clamp(v.x, a, b), clamp(v.y, a, b), clamp(v.z, a, b), clamp(v.w, a, b)); 957 | } 958 | inline __device__ __host__ uint4 clamp(uint4 v, uint4 a, uint4 b) { 959 | return make_uint4(clamp(v.x, a.x, b.x), clamp(v.y, a.y, b.y), clamp(v.z, a.z, b.z), clamp(v.w, a.w, b.w)); 960 | } 961 | 962 | //////////////////////////////////////////////////////////////////////////////// 963 | // dot product 964 | //////////////////////////////////////////////////////////////////////////////// 965 | 966 | inline __host__ __device__ float dot(float2 a, float2 b) { 967 | return a.x * b.x + a.y * b.y; 968 | } 969 | inline __host__ __device__ float dot(float3 a, float3 b) { 970 | return a.x * b.x + a.y * b.y + a.z * b.z; 971 | } 972 | inline __host__ __device__ float dot(float4 a, float4 b) { 973 | return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w; 974 | } 975 | 976 | inline __host__ __device__ int dot(int2 a, int2 b) { 977 | return a.x * b.x + a.y * b.y; 978 | } 979 | inline __host__ __device__ int dot(int3 a, int3 b) { 980 | return a.x * b.x + a.y * b.y + a.z * b.z; 981 | } 982 | inline __host__ __device__ int dot(int4 a, int4 b) { 983 | return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w; 984 | } 985 | 986 | inline __host__ __device__ uint dot(uint2 a, uint2 b) { 987 | return a.x * b.x + a.y * b.y; 988 | } 989 | inline __host__ __device__ uint dot(uint3 a, uint3 b) { 990 | return a.x * b.x + a.y * b.y + a.z * b.z; 991 | } 992 | inline __host__ __device__ uint dot(uint4 a, uint4 b) { 993 | return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w; 994 | } 995 | 996 | //////////////////////////////////////////////////////////////////////////////// 997 | // length 998 | //////////////////////////////////////////////////////////////////////////////// 999 | 1000 | inline __host__ __device__ float length(float2 v) { 1001 | return sqrtf(dot(v, v)); 1002 | } 1003 | inline __host__ __device__ float length(float3 v) { 1004 | return sqrtf(dot(v, v)); 1005 | } 1006 | inline __host__ __device__ float length(float4 v) { 1007 | return sqrtf(dot(v, v)); 1008 | } 1009 | 1010 | //////////////////////////////////////////////////////////////////////////////// 1011 | // normalize 1012 | //////////////////////////////////////////////////////////////////////////////// 1013 | 1014 | inline __host__ __device__ float2 normalize(float2 v) { 1015 | float invLen = rsqrtf(dot(v, v)); 1016 | return v * invLen; 1017 | } 1018 | inline __host__ __device__ float3 normalize(float3 v) { 1019 | float invLen = rsqrtf(dot(v, v)); 1020 | return v * invLen; 1021 | } 1022 | inline __host__ __device__ float4 normalize(float4 v) { 1023 | float invLen = rsqrtf(dot(v, v)); 1024 | return v * invLen; 1025 | } 1026 | 1027 | //////////////////////////////////////////////////////////////////////////////// 1028 | // floor 1029 | //////////////////////////////////////////////////////////////////////////////// 1030 | 1031 | inline __host__ __device__ float2 floorf(float2 v) { 1032 | return make_float2(floorf(v.x), floorf(v.y)); 1033 | } 1034 | inline __host__ __device__ float3 floorf(float3 v) { 1035 | return make_float3(floorf(v.x), floorf(v.y), floorf(v.z)); 1036 | } 1037 | inline __host__ __device__ float4 floorf(float4 v) { 1038 | return make_float4(floorf(v.x), floorf(v.y), floorf(v.z), floorf(v.w)); 1039 | } 1040 | 1041 | //////////////////////////////////////////////////////////////////////////////// 1042 | // frac - returns the fractional portion of a scalar or each vector component 1043 | //////////////////////////////////////////////////////////////////////////////// 1044 | 1045 | inline __host__ __device__ float fracf(float v) { 1046 | return v - floorf(v); 1047 | } 1048 | inline __host__ __device__ float2 fracf(float2 v) { 1049 | return make_float2(fracf(v.x), fracf(v.y)); 1050 | } 1051 | inline __host__ __device__ float3 fracf(float3 v) { 1052 | return make_float3(fracf(v.x), fracf(v.y), fracf(v.z)); 1053 | } 1054 | inline __host__ __device__ float4 fracf(float4 v) { 1055 | return make_float4(fracf(v.x), fracf(v.y), fracf(v.z), fracf(v.w)); 1056 | } 1057 | 1058 | //////////////////////////////////////////////////////////////////////////////// 1059 | // fmod 1060 | //////////////////////////////////////////////////////////////////////////////// 1061 | 1062 | inline __host__ __device__ float2 fmodf(float2 a, float2 b) { 1063 | return make_float2(fmodf(a.x, b.x), fmodf(a.y, b.y)); 1064 | } 1065 | inline __host__ __device__ float3 fmodf(float3 a, float3 b) { 1066 | return make_float3(fmodf(a.x, b.x), fmodf(a.y, b.y), fmodf(a.z, b.z)); 1067 | } 1068 | inline __host__ __device__ float4 fmodf(float4 a, float4 b) { 1069 | return make_float4(fmodf(a.x, b.x), fmodf(a.y, b.y), fmodf(a.z, b.z), fmodf(a.w, b.w)); 1070 | } 1071 | 1072 | //////////////////////////////////////////////////////////////////////////////// 1073 | // absolute value 1074 | //////////////////////////////////////////////////////////////////////////////// 1075 | 1076 | inline __host__ __device__ float2 fabs(float2 v) { 1077 | return make_float2(fabs(v.x), fabs(v.y)); 1078 | } 1079 | inline __host__ __device__ float3 fabs(float3 v) { 1080 | return make_float3(fabs(v.x), fabs(v.y), fabs(v.z)); 1081 | } 1082 | inline __host__ __device__ float4 fabs(float4 v) { 1083 | return make_float4(fabs(v.x), fabs(v.y), fabs(v.z), fabs(v.w)); 1084 | } 1085 | 1086 | inline __host__ __device__ int2 abs(int2 v) { 1087 | return make_int2(abs(v.x), abs(v.y)); 1088 | } 1089 | inline __host__ __device__ int3 abs(int3 v) { 1090 | return make_int3(abs(v.x), abs(v.y), abs(v.z)); 1091 | } 1092 | inline __host__ __device__ int4 abs(int4 v) { 1093 | return make_int4(abs(v.x), abs(v.y), abs(v.z), abs(v.w)); 1094 | } 1095 | 1096 | //////////////////////////////////////////////////////////////////////////////// 1097 | // reflect 1098 | // - returns reflection of incident ray I around surface normal N 1099 | // - N should be normalized, reflected vector's length is equal to length of I 1100 | //////////////////////////////////////////////////////////////////////////////// 1101 | 1102 | inline __host__ __device__ float3 reflect(float3 i, float3 n) { 1103 | return i - 2.0f * n * dot(n, i); 1104 | } 1105 | 1106 | //////////////////////////////////////////////////////////////////////////////// 1107 | // cross product 1108 | //////////////////////////////////////////////////////////////////////////////// 1109 | 1110 | inline __host__ __device__ float3 cross(float3 a, float3 b) { 1111 | return make_float3(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x); 1112 | } 1113 | 1114 | //////////////////////////////////////////////////////////////////////////////// 1115 | // smoothstep 1116 | // - returns 0 if x < a 1117 | // - returns 1 if x > b 1118 | // - otherwise returns smooth interpolation between 0 and 1 based on x 1119 | //////////////////////////////////////////////////////////////////////////////// 1120 | 1121 | inline __device__ __host__ float smoothstep(float a, float b, float x) { 1122 | float y = clamp((x - a) / (b - a), 0.0f, 1.0f); 1123 | return (y * y * (3.0f - (2.0f * y))); 1124 | } 1125 | inline __device__ __host__ float2 smoothstep(float2 a, float2 b, float2 x) { 1126 | float2 y = clamp((x - a) / (b - a), 0.0f, 1.0f); 1127 | return (y * y * (make_float2(3.0f) - (make_float2(2.0f) * y))); 1128 | } 1129 | inline __device__ __host__ float3 smoothstep(float3 a, float3 b, float3 x) { 1130 | float3 y = clamp((x - a) / (b - a), 0.0f, 1.0f); 1131 | return (y * y * (make_float3(3.0f) - (make_float3(2.0f) * y))); 1132 | } 1133 | inline __device__ __host__ float4 smoothstep(float4 a, float4 b, float4 x) { 1134 | float4 y = clamp((x - a) / (b - a), 0.0f, 1.0f); 1135 | return (y * y * (make_float4(3.0f) - (make_float4(2.0f) * y))); 1136 | } 1137 | 1138 | #endif 1139 | -------------------------------------------------------------------------------- /cxx/macros.h: -------------------------------------------------------------------------------- 1 | #ifdef _MSC_VER 2 | #pragma once 3 | #endif 4 | 5 | #ifndef _MACROS_H_ 6 | #define _MACROS_H_ 7 | 8 | #include 9 | 10 | #include 11 | 12 | #define CHECK_CUDA(x) \ 13 | do { \ 14 | TORCH_CHECK(vol.type().is_cuda(), #x " must be a CUDA tensor"); \ 15 | } while (0) 16 | 17 | #define CHECK_CPU(x) \ 18 | do { \ 19 | TORCH_CHECK(x.device() == torch::kCPU, #x " must be a CPU tensor"); \ 20 | } while (0) 21 | 22 | #define CHECK_CONTIGUOUS(x) \ 23 | do { \ 24 | TORCH_CHECK(x.is_contiguous(), #x " must be contiguous"); \ 25 | } while (0) 26 | 27 | #define CHECK_IS_INT(x) \ 28 | do { \ 29 | TORCH_CHECK(x.scalar_type() == at::ScalarType::Int, #x " must be an int tensor"); \ 30 | } while (0) 31 | 32 | #define CHECK_IS_FLOAT(x) \ 33 | do { \ 34 | TORCH_CHECK(x.scalar_type() == at::ScalarType::Float, #x " must be a float tensor"); \ 35 | } while (0) 36 | 37 | #define CHECK_N_DIM(x, n) \ 38 | do { \ 39 | char msg[256]; \ 40 | sprintf(msg, "%s must be %d-dimension", #x, n); \ 41 | TORCH_CHECK(x.sizes().size() == n, msg); \ 42 | } while (0) 43 | 44 | #endif // _MACROS_H_ 45 | -------------------------------------------------------------------------------- /cxx/mcubes.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | // Forward declarations 7 | 8 | std::vector mcubes_cpu(torch::Tensor func, float threshold); 9 | 10 | #if defined(WITH_CUDA) 11 | std::vector mcubes_cuda(torch::Tensor func, float threshold); 12 | #endif 13 | 14 | torch::Tensor grid_interp_cpu(torch::Tensor vol, torch::Tensor points); 15 | 16 | #if defined(WITH_CUDA) 17 | torch::Tensor grid_interp_cuda(torch::Tensor vol, torch::Tensor points); 18 | #endif 19 | 20 | // Pybind11 exports 21 | PYBIND11_MODULE(torchmcubes_module, m) { 22 | m.def("mcubes_cpu", &mcubes_cpu, "Marching cubes (CPU)", py::arg("vol"), py::arg("threshold")); 23 | m.def("grid_interp_cpu", &grid_interp_cpu, "Grid interpolation (CPU)", py::arg("vol"), py::arg("points")); 24 | #if defined(WITH_CUDA) 25 | m.def("mcubes_cuda", &mcubes_cuda, "Marching cubes (CUDA)", py::arg("vol"), py::arg("threshold")); 26 | m.def("grid_interp_cuda", &grid_interp_cuda, "Grid interpolation (CUDA)", py::arg("vol"), py::arg("points")); 27 | #endif 28 | } 29 | -------------------------------------------------------------------------------- /cxx/mcubes_cpu.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #ifdef _OPENMP 11 | #include 12 | #endif 13 | 14 | #include "macros.h" 15 | 16 | struct XYZ { 17 | explicit XYZ(float x = 0.0f, float y = 0.0f, float z = 0.0f) 18 | : x(x) 19 | , y(y) 20 | , z(z) { 21 | } 22 | bool operator==(const XYZ &xyz) const { 23 | return x == xyz.x && y == xyz.y && z == xyz.z; 24 | } 25 | XYZ operator+(const XYZ &xyz) const { 26 | return XYZ(x + xyz.x, y + xyz.y, z + xyz.z); 27 | } 28 | XYZ operator*(float s) const { 29 | return XYZ(x * s, y * s, z * s); 30 | } 31 | float x, y, z; 32 | }; 33 | 34 | namespace std { 35 | 36 | inline void hash_combine(size_t &seed, size_t hash) { 37 | hash += 0x9e3779b9 + (seed << 6) + (seed >> 2); 38 | seed ^= hash; 39 | } 40 | 41 | template <> 42 | struct hash { 43 | size_t operator()(const XYZ &xyz) const { 44 | size_t seed = 0; 45 | hash hasher; 46 | hash_combine(seed, hasher(xyz.x)); 47 | hash_combine(seed, hasher(xyz.y)); 48 | hash_combine(seed, hasher(xyz.z)); 49 | return seed; 50 | } 51 | }; 52 | 53 | } // namespace std 54 | 55 | struct TRIANGLE { 56 | XYZ p[3]; 57 | }; 58 | 59 | struct GRIDCELL { 60 | XYZ p[8]; 61 | float val[8]; 62 | }; 63 | 64 | /* 65 | * Linearly interpolate the position where an isosurface cuts 66 | * an edge between two vertices, each with their own scalar value 67 | */ 68 | XYZ VertexInterp(float isolevel, XYZ p1, XYZ p2, float valp1, float valp2) { 69 | float mu; 70 | XYZ p; 71 | 72 | if (std::abs(isolevel - valp1) < 1.0e-4f) return p1; 73 | if (std::abs(isolevel - valp2) < 1.0e-4f) return p2; 74 | if (std::abs(valp1 - valp2) < 1.0e-4f) return p1; 75 | 76 | mu = (isolevel - valp1) / (valp2 - valp1); 77 | p.x = p1.x + mu * (p2.x - p1.x); 78 | p.y = p1.y + mu * (p2.y - p1.y); 79 | p.z = p1.z + mu * (p2.z - p1.z); 80 | 81 | return p; 82 | } 83 | 84 | /* 85 | Given a grid cell and an isolevel, calculate the triangular 86 | facets required to represent the isosurface through the cell. 87 | Return the number of triangular facets, the array "triangles" 88 | will be loaded up with the vertices at most 5 triangular facets. 89 | 0 will be returned if the grid cell is either totally above 90 | of totally below the isolevel. 91 | */ 92 | int Polygonise(GRIDCELL grid, float isolevel, TRIANGLE *triangles) { 93 | int i, ntriang; 94 | int cubeindex; 95 | XYZ vertlist[12]; 96 | 97 | static int edgeTable[256] = { 98 | 0x0, 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c, 0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00, 99 | 0x190, 0x99, 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c, 0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90, 100 | 0x230, 0x339, 0x33, 0x13a, 0x636, 0x73f, 0x435, 0x53c, 0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30, 101 | 0x3a0, 0x2a9, 0x1a3, 0xaa, 0x7a6, 0x6af, 0x5a5, 0x4ac, 0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0, 102 | 0x460, 0x569, 0x663, 0x76a, 0x66, 0x16f, 0x265, 0x36c, 0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60, 103 | 0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff, 0x3f5, 0x2fc, 0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0, 104 | 0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55, 0x15c, 0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950, 105 | 0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc, 0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0, 106 | 0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc, 0xcc, 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0, 107 | 0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c, 0x15c, 0x55, 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650, 108 | 0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc, 0x2fc, 0x3f5, 0xff, 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0, 109 | 0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c, 0x36c, 0x265, 0x16f, 0x66, 0x76a, 0x663, 0x569, 0x460, 110 | 0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac, 0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa, 0x1a3, 0x2a9, 0x3a0, 111 | 0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c, 0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33, 0x339, 0x230, 112 | 0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c, 0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99, 0x190, 113 | 0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c, 0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x0 114 | }; 115 | 116 | static int triTable[256][16] = { { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 117 | { 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 118 | { 0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 119 | { 1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 120 | { 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 121 | { 0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 122 | { 9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 123 | { 2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1 }, 124 | { 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 125 | { 0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 126 | { 1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 127 | { 1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1 }, 128 | { 3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 129 | { 0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1 }, 130 | { 3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1 }, 131 | { 9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 132 | { 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 133 | { 4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 134 | { 0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 135 | { 4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1 }, 136 | { 1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 137 | { 3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1 }, 138 | { 9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1 }, 139 | { 2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1 }, 140 | { 8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 141 | { 11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1 }, 142 | { 9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1 }, 143 | { 4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1 }, 144 | { 3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1 }, 145 | { 1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1 }, 146 | { 4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1 }, 147 | { 4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1 }, 148 | { 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 149 | { 9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 150 | { 0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 151 | { 8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1 }, 152 | { 1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 153 | { 3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1 }, 154 | { 5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1 }, 155 | { 2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1 }, 156 | { 9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 157 | { 0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1 }, 158 | { 0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1 }, 159 | { 2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1 }, 160 | { 10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1 }, 161 | { 4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1 }, 162 | { 5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1 }, 163 | { 5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1 }, 164 | { 9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 165 | { 9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1 }, 166 | { 0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1 }, 167 | { 1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 168 | { 9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1 }, 169 | { 10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1 }, 170 | { 8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1 }, 171 | { 2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1 }, 172 | { 7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1 }, 173 | { 9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1 }, 174 | { 2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1 }, 175 | { 11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1 }, 176 | { 9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1 }, 177 | { 5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1 }, 178 | { 11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1 }, 179 | { 11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 180 | { 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 181 | { 0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 182 | { 9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 183 | { 1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1 }, 184 | { 1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 185 | { 1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1 }, 186 | { 9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1 }, 187 | { 5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1 }, 188 | { 2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 189 | { 11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1 }, 190 | { 0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1 }, 191 | { 5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1 }, 192 | { 6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1 }, 193 | { 0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1 }, 194 | { 3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1 }, 195 | { 6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1 }, 196 | { 5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 197 | { 4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1 }, 198 | { 1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1 }, 199 | { 10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1 }, 200 | { 6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1 }, 201 | { 1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1 }, 202 | { 8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1 }, 203 | { 7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1 }, 204 | { 3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1 }, 205 | { 5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1 }, 206 | { 0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1 }, 207 | { 9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1 }, 208 | { 8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1 }, 209 | { 5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1 }, 210 | { 0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1 }, 211 | { 6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1 }, 212 | { 10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 213 | { 4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1 }, 214 | { 10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1 }, 215 | { 8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1 }, 216 | { 1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1 }, 217 | { 3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1 }, 218 | { 0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 219 | { 8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1 }, 220 | { 10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1 }, 221 | { 0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1 }, 222 | { 3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1 }, 223 | { 6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1 }, 224 | { 9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1 }, 225 | { 8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1 }, 226 | { 3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1 }, 227 | { 6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 228 | { 7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1 }, 229 | { 0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1 }, 230 | { 10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1 }, 231 | { 10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1 }, 232 | { 1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1 }, 233 | { 2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1 }, 234 | { 7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1 }, 235 | { 7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 236 | { 2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1 }, 237 | { 2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1 }, 238 | { 1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1 }, 239 | { 11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1 }, 240 | { 8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1 }, 241 | { 0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 242 | { 7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1 }, 243 | { 7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 244 | { 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 245 | { 3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 246 | { 0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 247 | { 8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1 }, 248 | { 10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 249 | { 1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1 }, 250 | { 2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1 }, 251 | { 6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1 }, 252 | { 7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 253 | { 7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1 }, 254 | { 2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1 }, 255 | { 1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1 }, 256 | { 10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1 }, 257 | { 10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1 }, 258 | { 0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1 }, 259 | { 7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1 }, 260 | { 6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 261 | { 3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1 }, 262 | { 8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1 }, 263 | { 9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1 }, 264 | { 6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1 }, 265 | { 1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1 }, 266 | { 4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1 }, 267 | { 10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1 }, 268 | { 8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1 }, 269 | { 0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 270 | { 1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1 }, 271 | { 1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1 }, 272 | { 8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1 }, 273 | { 10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1 }, 274 | { 4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1 }, 275 | { 10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 276 | { 4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 277 | { 0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1 }, 278 | { 5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1 }, 279 | { 11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1 }, 280 | { 9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1 }, 281 | { 6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1 }, 282 | { 7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1 }, 283 | { 3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1 }, 284 | { 7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1 }, 285 | { 9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1 }, 286 | { 3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1 }, 287 | { 6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1 }, 288 | { 9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1 }, 289 | { 1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1 }, 290 | { 4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1 }, 291 | { 7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1 }, 292 | { 6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1 }, 293 | { 3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1 }, 294 | { 0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1 }, 295 | { 6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1 }, 296 | { 1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1 }, 297 | { 0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1 }, 298 | { 11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1 }, 299 | { 6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1 }, 300 | { 5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1 }, 301 | { 9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1 }, 302 | { 1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1 }, 303 | { 1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 304 | { 1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1 }, 305 | { 10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1 }, 306 | { 0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 307 | { 10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 308 | { 11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 309 | { 11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1 }, 310 | { 5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1 }, 311 | { 10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1 }, 312 | { 11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1 }, 313 | { 0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1 }, 314 | { 9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1 }, 315 | { 7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1 }, 316 | { 2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1 }, 317 | { 8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1 }, 318 | { 9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1 }, 319 | { 9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1 }, 320 | { 1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 321 | { 0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1 }, 322 | { 9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1 }, 323 | { 9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 324 | { 5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1 }, 325 | { 5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1 }, 326 | { 0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1 }, 327 | { 10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1 }, 328 | { 2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1 }, 329 | { 0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1 }, 330 | { 0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1 }, 331 | { 9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 332 | { 2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1 }, 333 | { 5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1 }, 334 | { 3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1 }, 335 | { 5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1 }, 336 | { 8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1 }, 337 | { 0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 338 | { 8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1 }, 339 | { 9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 340 | { 4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1 }, 341 | { 0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1 }, 342 | { 1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1 }, 343 | { 3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1 }, 344 | { 4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1 }, 345 | { 9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1 }, 346 | { 11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1 }, 347 | { 11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1 }, 348 | { 2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1 }, 349 | { 9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1 }, 350 | { 3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1 }, 351 | { 1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 352 | { 4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1 }, 353 | { 4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1 }, 354 | { 4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 355 | { 4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 356 | { 9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 357 | { 3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1 }, 358 | { 0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1 }, 359 | { 3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 360 | { 1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1 }, 361 | { 3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1 }, 362 | { 0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 363 | { 3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 364 | { 2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1 }, 365 | { 9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 366 | { 2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1 }, 367 | { 1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 368 | { 1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 369 | { 0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 370 | { 0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 371 | { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 } }; 372 | 373 | /* 374 | Determine the index into the edge table which 375 | tells us which vertices are inside of the surface 376 | */ 377 | cubeindex = 0; 378 | if (grid.val[0] < isolevel) cubeindex |= 1; 379 | if (grid.val[1] < isolevel) cubeindex |= 2; 380 | if (grid.val[2] < isolevel) cubeindex |= 4; 381 | if (grid.val[3] < isolevel) cubeindex |= 8; 382 | if (grid.val[4] < isolevel) cubeindex |= 16; 383 | if (grid.val[5] < isolevel) cubeindex |= 32; 384 | if (grid.val[6] < isolevel) cubeindex |= 64; 385 | if (grid.val[7] < isolevel) cubeindex |= 128; 386 | 387 | /* Cube is entirely in/out of the surface */ 388 | if (edgeTable[cubeindex] == 0) return (0); 389 | 390 | /* Find the vertices where the surface intersects the cube */ 391 | if (edgeTable[cubeindex] & 1) vertlist[0] = VertexInterp(isolevel, grid.p[0], grid.p[1], grid.val[0], grid.val[1]); 392 | if (edgeTable[cubeindex] & 2) vertlist[1] = VertexInterp(isolevel, grid.p[1], grid.p[2], grid.val[1], grid.val[2]); 393 | if (edgeTable[cubeindex] & 4) vertlist[2] = VertexInterp(isolevel, grid.p[2], grid.p[3], grid.val[2], grid.val[3]); 394 | if (edgeTable[cubeindex] & 8) vertlist[3] = VertexInterp(isolevel, grid.p[3], grid.p[0], grid.val[3], grid.val[0]); 395 | if (edgeTable[cubeindex] & 16) vertlist[4] = VertexInterp(isolevel, grid.p[4], grid.p[5], grid.val[4], grid.val[5]); 396 | if (edgeTable[cubeindex] & 32) vertlist[5] = VertexInterp(isolevel, grid.p[5], grid.p[6], grid.val[5], grid.val[6]); 397 | if (edgeTable[cubeindex] & 64) vertlist[6] = VertexInterp(isolevel, grid.p[6], grid.p[7], grid.val[6], grid.val[7]); 398 | if (edgeTable[cubeindex] & 128) 399 | vertlist[7] = VertexInterp(isolevel, grid.p[7], grid.p[4], grid.val[7], grid.val[4]); 400 | if (edgeTable[cubeindex] & 256) 401 | vertlist[8] = VertexInterp(isolevel, grid.p[0], grid.p[4], grid.val[0], grid.val[4]); 402 | if (edgeTable[cubeindex] & 512) 403 | vertlist[9] = VertexInterp(isolevel, grid.p[1], grid.p[5], grid.val[1], grid.val[5]); 404 | if (edgeTable[cubeindex] & 1024) 405 | vertlist[10] = VertexInterp(isolevel, grid.p[2], grid.p[6], grid.val[2], grid.val[6]); 406 | if (edgeTable[cubeindex] & 2048) 407 | vertlist[11] = VertexInterp(isolevel, grid.p[3], grid.p[7], grid.val[3], grid.val[7]); 408 | 409 | /* Create the triangle */ 410 | ntriang = 0; 411 | for (i = 0; triTable[cubeindex][i] != -1; i += 3) { 412 | triangles[ntriang].p[0] = vertlist[triTable[cubeindex][i]]; 413 | triangles[ntriang].p[1] = vertlist[triTable[cubeindex][i + 1]]; 414 | triangles[ntriang].p[2] = vertlist[triTable[cubeindex][i + 2]]; 415 | ntriang++; 416 | } 417 | 418 | return ntriang; 419 | } 420 | 421 | void mcubes_method(torch::TensorAccessor vol, std::vector &vertices, std::vector &faces, 422 | float threshold) { 423 | // Marching cubes 424 | static int indexTable[8] = { 0, 1, 4, 5, 3, 2, 7, 6 }; 425 | 426 | // Process all cells in parallel 427 | std::vector all_vertices; 428 | 429 | #ifdef _OPENMP 430 | #pragma omp parallel for 431 | #endif 432 | for (int z = 0; z < vol.size(0) - 1; z++) { 433 | std::vector sub_vertices; 434 | for (int y = 0; y < vol.size(1) - 1; y++) { 435 | for (int x = 0; x < vol.size(2) - 1; x++) { 436 | // Process a single cell 437 | GRIDCELL cell; 438 | TRIANGLE tris[16]; 439 | for (int i = 0; i < 8; i++) { 440 | const int dx = i % 2; 441 | const int dy = (i / 2) % 2; 442 | const int dz = i / 4; 443 | 444 | cell.p[indexTable[i]] = XYZ(x + dx, y + dy, z + dz); 445 | cell.val[indexTable[i]] = vol[z + dz][y + dy][x + dx]; 446 | } 447 | 448 | std::memset(tris, 0, sizeof(tris)); 449 | const int ntris = Polygonise(cell, threshold, tris); 450 | 451 | for (int i = 0; i < ntris; i++) { 452 | for (int j = 0; j < 3; j++) { 453 | sub_vertices.push_back(tris[i].p[j]); 454 | } 455 | } 456 | } 457 | } 458 | 459 | #ifdef _OPENMP 460 | #pragma omp critical 461 | #endif 462 | { all_vertices.insert(all_vertices.end(), sub_vertices.begin(), sub_vertices.end()); } 463 | } 464 | 465 | // Unique vertices 466 | std::unordered_map uniqueVertices; 467 | for (const auto &v : all_vertices) { 468 | if (uniqueVertices.count(v) == 0) { 469 | uniqueVertices[v] = vertices.size(); 470 | vertices.push_back(v); 471 | } 472 | faces.push_back(uniqueVertices[v]); 473 | } 474 | } 475 | 476 | std::vector mcubes_cpu(torch::Tensor vol, float threshold) { 477 | // Check input tensor 478 | CHECK_CPU(vol); 479 | CHECK_CONTIGUOUS(vol); 480 | CHECK_N_DIM(vol, 3); 481 | 482 | std::vector vertices; 483 | std::vector faces; 484 | mcubes_method(vol.accessor(), vertices, faces, threshold); 485 | 486 | const int Nv = (int)vertices.size(); 487 | const int Nf = (int)faces.size() / 3; 488 | auto vert_tensor = torch::zeros({ Nv, 3 }, at::kFloat); 489 | auto face_tensor = torch::zeros({ Nf, 3 }, at::kInt); 490 | 491 | auto vert_accs = vert_tensor.accessor(); 492 | for (int i = 0; i < Nv; i++) { 493 | vert_accs[i][0] = vertices[i].x; 494 | vert_accs[i][1] = vertices[i].y; 495 | vert_accs[i][2] = vertices[i].z; 496 | } 497 | 498 | auto face_accs = face_tensor.accessor(); 499 | for (int i = 0; i < Nf; i++) { 500 | face_accs[i][0] = faces[i * 3 + 0]; 501 | face_accs[i][1] = faces[i * 3 + 1]; 502 | face_accs[i][2] = faces[i * 3 + 2]; 503 | } 504 | 505 | return { vert_tensor, face_tensor }; 506 | } 507 | -------------------------------------------------------------------------------- /cxx/mcubes_cuda.cu: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | #include "macros.h" 13 | #include "cuda_utils.h" 14 | #include "helper_math.h" 15 | 16 | static int edgeTable[256] = { 17 | 0x0, 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c, 0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00, 18 | 0x190, 0x99, 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c, 0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90, 19 | 0x230, 0x339, 0x33, 0x13a, 0x636, 0x73f, 0x435, 0x53c, 0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30, 20 | 0x3a0, 0x2a9, 0x1a3, 0xaa, 0x7a6, 0x6af, 0x5a5, 0x4ac, 0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0, 21 | 0x460, 0x569, 0x663, 0x76a, 0x66, 0x16f, 0x265, 0x36c, 0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60, 22 | 0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff, 0x3f5, 0x2fc, 0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0, 23 | 0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55, 0x15c, 0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950, 24 | 0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc, 0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0, 25 | 0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc, 0xcc, 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0, 26 | 0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c, 0x15c, 0x55, 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650, 27 | 0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc, 0x2fc, 0x3f5, 0xff, 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0, 28 | 0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c, 0x36c, 0x265, 0x16f, 0x66, 0x76a, 0x663, 0x569, 0x460, 29 | 0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac, 0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa, 0x1a3, 0x2a9, 0x3a0, 30 | 0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c, 0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33, 0x339, 0x230, 31 | 0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c, 0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99, 0x190, 32 | 0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c, 0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x0 33 | }; 34 | 35 | static int triTable[256][16] = { { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 36 | { 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 37 | { 0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 38 | { 1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 39 | { 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 40 | { 0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 41 | { 9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 42 | { 2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1 }, 43 | { 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 44 | { 0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 45 | { 1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 46 | { 1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1 }, 47 | { 3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 48 | { 0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1 }, 49 | { 3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1 }, 50 | { 9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 51 | { 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 52 | { 4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 53 | { 0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 54 | { 4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1 }, 55 | { 1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 56 | { 3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1 }, 57 | { 9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1 }, 58 | { 2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1 }, 59 | { 8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 60 | { 11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1 }, 61 | { 9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1 }, 62 | { 4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1 }, 63 | { 3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1 }, 64 | { 1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1 }, 65 | { 4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1 }, 66 | { 4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1 }, 67 | { 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 68 | { 9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 69 | { 0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 70 | { 8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1 }, 71 | { 1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 72 | { 3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1 }, 73 | { 5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1 }, 74 | { 2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1 }, 75 | { 9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 76 | { 0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1 }, 77 | { 0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1 }, 78 | { 2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1 }, 79 | { 10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1 }, 80 | { 4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1 }, 81 | { 5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1 }, 82 | { 5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1 }, 83 | { 9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 84 | { 9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1 }, 85 | { 0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1 }, 86 | { 1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 87 | { 9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1 }, 88 | { 10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1 }, 89 | { 8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1 }, 90 | { 2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1 }, 91 | { 7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1 }, 92 | { 9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1 }, 93 | { 2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1 }, 94 | { 11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1 }, 95 | { 9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1 }, 96 | { 5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1 }, 97 | { 11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1 }, 98 | { 11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 99 | { 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 100 | { 0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 101 | { 9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 102 | { 1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1 }, 103 | { 1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 104 | { 1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1 }, 105 | { 9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1 }, 106 | { 5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1 }, 107 | { 2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 108 | { 11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1 }, 109 | { 0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1 }, 110 | { 5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1 }, 111 | { 6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1 }, 112 | { 0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1 }, 113 | { 3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1 }, 114 | { 6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1 }, 115 | { 5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 116 | { 4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1 }, 117 | { 1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1 }, 118 | { 10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1 }, 119 | { 6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1 }, 120 | { 1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1 }, 121 | { 8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1 }, 122 | { 7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1 }, 123 | { 3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1 }, 124 | { 5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1 }, 125 | { 0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1 }, 126 | { 9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1 }, 127 | { 8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1 }, 128 | { 5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1 }, 129 | { 0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1 }, 130 | { 6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1 }, 131 | { 10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 132 | { 4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1 }, 133 | { 10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1 }, 134 | { 8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1 }, 135 | { 1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1 }, 136 | { 3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1 }, 137 | { 0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 138 | { 8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1 }, 139 | { 10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1 }, 140 | { 0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1 }, 141 | { 3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1 }, 142 | { 6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1 }, 143 | { 9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1 }, 144 | { 8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1 }, 145 | { 3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1 }, 146 | { 6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 147 | { 7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1 }, 148 | { 0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1 }, 149 | { 10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1 }, 150 | { 10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1 }, 151 | { 1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1 }, 152 | { 2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1 }, 153 | { 7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1 }, 154 | { 7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 155 | { 2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1 }, 156 | { 2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1 }, 157 | { 1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1 }, 158 | { 11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1 }, 159 | { 8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1 }, 160 | { 0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 161 | { 7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1 }, 162 | { 7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 163 | { 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 164 | { 3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 165 | { 0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 166 | { 8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1 }, 167 | { 10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 168 | { 1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1 }, 169 | { 2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1 }, 170 | { 6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1 }, 171 | { 7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 172 | { 7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1 }, 173 | { 2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1 }, 174 | { 1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1 }, 175 | { 10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1 }, 176 | { 10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1 }, 177 | { 0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1 }, 178 | { 7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1 }, 179 | { 6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 180 | { 3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1 }, 181 | { 8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1 }, 182 | { 9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1 }, 183 | { 6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1 }, 184 | { 1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1 }, 185 | { 4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1 }, 186 | { 10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1 }, 187 | { 8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1 }, 188 | { 0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 189 | { 1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1 }, 190 | { 1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1 }, 191 | { 8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1 }, 192 | { 10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1 }, 193 | { 4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1 }, 194 | { 10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 195 | { 4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 196 | { 0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1 }, 197 | { 5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1 }, 198 | { 11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1 }, 199 | { 9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1 }, 200 | { 6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1 }, 201 | { 7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1 }, 202 | { 3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1 }, 203 | { 7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1 }, 204 | { 9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1 }, 205 | { 3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1 }, 206 | { 6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1 }, 207 | { 9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1 }, 208 | { 1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1 }, 209 | { 4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1 }, 210 | { 7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1 }, 211 | { 6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1 }, 212 | { 3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1 }, 213 | { 0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1 }, 214 | { 6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1 }, 215 | { 1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1 }, 216 | { 0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1 }, 217 | { 11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1 }, 218 | { 6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1 }, 219 | { 5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1 }, 220 | { 9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1 }, 221 | { 1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1 }, 222 | { 1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 223 | { 1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1 }, 224 | { 10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1 }, 225 | { 0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 226 | { 10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 227 | { 11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 228 | { 11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1 }, 229 | { 5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1 }, 230 | { 10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1 }, 231 | { 11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1 }, 232 | { 0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1 }, 233 | { 9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1 }, 234 | { 7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1 }, 235 | { 2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1 }, 236 | { 8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1 }, 237 | { 9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1 }, 238 | { 9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1 }, 239 | { 1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 240 | { 0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1 }, 241 | { 9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1 }, 242 | { 9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 243 | { 5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1 }, 244 | { 5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1 }, 245 | { 0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1 }, 246 | { 10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1 }, 247 | { 2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1 }, 248 | { 0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1 }, 249 | { 0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1 }, 250 | { 9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 251 | { 2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1 }, 252 | { 5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1 }, 253 | { 3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1 }, 254 | { 5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1 }, 255 | { 8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1 }, 256 | { 0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 257 | { 8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1 }, 258 | { 9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 259 | { 4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1 }, 260 | { 0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1 }, 261 | { 1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1 }, 262 | { 3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1 }, 263 | { 4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1 }, 264 | { 9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1 }, 265 | { 11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1 }, 266 | { 11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1 }, 267 | { 2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1 }, 268 | { 9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1 }, 269 | { 3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1 }, 270 | { 1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 271 | { 4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1 }, 272 | { 4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1 }, 273 | { 4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 274 | { 4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 275 | { 9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 276 | { 3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1 }, 277 | { 0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1 }, 278 | { 3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 279 | { 1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1 }, 280 | { 3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1 }, 281 | { 0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 282 | { 3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 283 | { 2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1 }, 284 | { 9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 285 | { 2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1 }, 286 | { 1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 287 | { 1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 288 | { 0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 289 | { 0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 290 | { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 } }; 291 | 292 | __device__ float3 vertexInterp(float isolevel, float3 p1, float3 p2, float valp1, float valp2) { 293 | float mu; 294 | float3 p; 295 | 296 | if (fabsf(isolevel - valp1) < 1.0e-4f) { 297 | return p1; 298 | } 299 | 300 | if (fabsf(isolevel - valp2) < 1.0e-4f) { 301 | return p2; 302 | } 303 | 304 | if (fabsf(valp1 - valp2) < 1.0e-4f) { 305 | return p1; 306 | } 307 | 308 | mu = (isolevel - valp1) / (valp2 - valp1); 309 | p.x = p1.x + mu * (p2.x - p1.x); 310 | p.y = p1.y + mu * (p2.y - p1.y); 311 | p.z = p1.z + mu * (p2.z - p1.z); 312 | 313 | return p; 314 | } 315 | 316 | __global__ void mcubes_cuda_kernel(const torch::PackedTensorAccessor64 vol, 317 | torch::PackedTensorAccessor64 vertices, 318 | torch::PackedTensorAccessor64 ntris_in_cells, 319 | int3 nGrids, float threshold, 320 | const torch::PackedTensorAccessor64 edgeTable, 321 | const torch::PackedTensorAccessor64 triTable) { 322 | const int ix = blockIdx.x * blockDim.x + threadIdx.x; 323 | const int iy = blockIdx.y * blockDim.y + threadIdx.y; 324 | const int iz = blockIdx.z * blockDim.z + threadIdx.z; 325 | if (ix >= nGrids.x - 1 || iy >= nGrids.y - 1 || iz >= nGrids.z - 1) { 326 | return; 327 | } 328 | 329 | int cubeindex = 0; 330 | int bits = 0; 331 | float val[8]; 332 | float3 p[8]; 333 | 334 | int indexTable[8]{ 0, 1, 4, 5, 3, 2, 7, 6 }; 335 | 336 | for (int k = 0; k <= 1; k++) { 337 | for (int j = 0; j <= 1; j++) { 338 | for (int i = 0; i <= 1; i++) { 339 | const int jx = ix + i; 340 | const int jy = iy + j; 341 | const int jz = iz + k; 342 | if (vol[jz][jy][jx] < threshold) { 343 | cubeindex |= 0x01 << indexTable[bits]; 344 | } 345 | 346 | val[indexTable[bits]] = vol[jz][jy][jx]; 347 | p[indexTable[bits]] = make_float3(jx, jy, jz); 348 | bits += 1; 349 | } 350 | } 351 | } 352 | 353 | /* Find the vertices where the surface intersects the cube */ 354 | float3 vertlist[12]; 355 | if ((edgeTable[cubeindex] & 1) != 0) { 356 | vertlist[0] = vertexInterp(threshold, p[0], p[1], val[0], val[1]); 357 | } 358 | 359 | if ((edgeTable[cubeindex] & 2) != 0) { 360 | vertlist[1] = vertexInterp(threshold, p[1], p[2], val[1], val[2]); 361 | } 362 | 363 | if ((edgeTable[cubeindex] & 4) != 0) { 364 | vertlist[2] = vertexInterp(threshold, p[2], p[3], val[2], val[3]); 365 | } 366 | 367 | if ((edgeTable[cubeindex] & 8) != 0) { 368 | vertlist[3] = vertexInterp(threshold, p[3], p[0], val[3], val[0]); 369 | } 370 | 371 | if ((edgeTable[cubeindex] & 16) != 0) { 372 | vertlist[4] = vertexInterp(threshold, p[4], p[5], val[4], val[5]); 373 | } 374 | 375 | if ((edgeTable[cubeindex] & 32) != 0) { 376 | vertlist[5] = vertexInterp(threshold, p[5], p[6], val[5], val[6]); 377 | } 378 | 379 | if ((edgeTable[cubeindex] & 64) != 0) { 380 | vertlist[6] = vertexInterp(threshold, p[6], p[7], val[6], val[7]); 381 | } 382 | 383 | if ((edgeTable[cubeindex] & 128) != 0) { 384 | vertlist[7] = vertexInterp(threshold, p[7], p[4], val[7], val[4]); 385 | } 386 | 387 | if ((edgeTable[cubeindex] & 256) != 0) { 388 | vertlist[8] = vertexInterp(threshold, p[0], p[4], val[0], val[4]); 389 | } 390 | 391 | if ((edgeTable[cubeindex] & 512) != 0) { 392 | vertlist[9] = vertexInterp(threshold, p[1], p[5], val[1], val[5]); 393 | } 394 | 395 | if ((edgeTable[cubeindex] & 1024) != 0) { 396 | vertlist[10] = vertexInterp(threshold, p[2], p[6], val[2], val[6]); 397 | } 398 | 399 | if ((edgeTable[cubeindex] & 2048) != 0) { 400 | vertlist[11] = vertexInterp(threshold, p[3], p[7], val[3], val[7]); 401 | } 402 | 403 | for (int i = 0; i < 4; i++) { 404 | if (triTable[cubeindex][i * 3 + 0] >= 0) { 405 | for (int k = 0; k < 3; k++) { 406 | const float3 &v = vertlist[triTable[cubeindex][i * 3 + k]]; 407 | vertices[iz][iy][ix][i * 3 + k][0] = v.x; 408 | vertices[iz][iy][ix][i * 3 + k][1] = v.y; 409 | vertices[iz][iy][ix][i * 3 + k][2] = v.z; 410 | } 411 | ntris_in_cells[iz][iy][ix] += 1; 412 | } 413 | } 414 | } 415 | 416 | __global__ void compaction(const torch::PackedTensorAccessor64 vertBuf, 417 | const torch::PackedTensorAccessor64 ntris, 418 | const torch::PackedTensorAccessor64 offsets, int3 nGrids, 419 | torch::PackedTensorAccessor64 verts, 420 | torch::PackedTensorAccessor64 faces) { 421 | const int ix = blockIdx.x * blockDim.x + threadIdx.x; 422 | const int iy = blockIdx.y * blockDim.y + threadIdx.y; 423 | const int iz = blockIdx.z * blockDim.z + threadIdx.z; 424 | const int index = (iz * nGrids.y + iy) * nGrids.x + ix; 425 | const int size = nGrids.x * nGrids.y * nGrids.z; 426 | 427 | if (index < size) { 428 | const int start = offsets[iz][iy][ix]; 429 | const int n = ntris[iz][iy][ix]; 430 | for (int i = 0; i < n; i++) { 431 | for (int k = 0; k < 3; k++) { 432 | verts[(start + i) * 3 + k][0] = vertBuf[iz][iy][ix][i * 3 + k][0]; 433 | verts[(start + i) * 3 + k][1] = vertBuf[iz][iy][ix][i * 3 + k][1]; 434 | verts[(start + i) * 3 + k][2] = vertBuf[iz][iy][ix][i * 3 + k][2]; 435 | faces[start + i][k] = (start + i) * 3 + k; 436 | } 437 | } 438 | } 439 | } 440 | 441 | std::vector mcubes_cuda(torch::Tensor vol, float threshold) { 442 | // Check input tensor 443 | CHECK_CUDA(vol); 444 | CHECK_CONTIGUOUS(vol); 445 | CHECK_IS_FLOAT(vol); 446 | CHECK_N_DIM(vol, 3); 447 | 448 | // Transfer table data to device 449 | torch::Tensor edgeTableTensor = torch::zeros({ 256 }, torch::TensorOptions().dtype(at::kInt).device(at::kCPU)); 450 | { 451 | auto acsr = edgeTableTensor.accessor(); 452 | for (int i = 0; i < 256; i++) { 453 | acsr[i] = edgeTable[i]; 454 | } 455 | } 456 | torch::Tensor edgeTableTensorCuda = edgeTableTensor.to(vol.device()); 457 | 458 | torch::Tensor triTableTensor = torch::zeros({ 256, 16 }, torch::TensorOptions().dtype(at::kInt).device(at::kCPU)); 459 | { 460 | auto acsr = triTableTensor.accessor(); 461 | for (int i = 0; i < 256; i++) { 462 | for (int j = 0; j < 16; j++) { 463 | acsr[i][j] = triTable[i][j]; 464 | } 465 | } 466 | } 467 | torch::Tensor triTableTensorCuda = triTableTensor.to(vol.device()); 468 | 469 | // Size parameters 470 | const uint32_t Nx = vol.size(2); 471 | const uint32_t Ny = vol.size(1); 472 | const uint32_t Nz = vol.size(0); 473 | 474 | const uint32_t BLOCK_SIZE = 8; 475 | const uint32_t gridx = (Nx + BLOCK_SIZE - 1) / BLOCK_SIZE; 476 | const uint32_t gridy = (Ny + BLOCK_SIZE - 1) / BLOCK_SIZE; 477 | const uint32_t gridz = (Nz + BLOCK_SIZE - 1) / BLOCK_SIZE; 478 | 479 | const dim3 blocks = { gridx, gridy, gridz }; 480 | const dim3 threads = { BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE }; 481 | const int3 nGrids = make_int3(Nx, Ny, Nz); 482 | 483 | const int deviceId = vol.device().index(); 484 | const cudaStream_t stream = at::cuda::getCurrentCUDAStream(deviceId); 485 | 486 | // Allocate vertex buffer 487 | torch::Tensor vert_buffer = torch::zeros( 488 | { Nz, Ny, Nx, 12, 3 }, torch::TensorOptions().dtype(torch::kFloat32).device(torch::kCUDA, deviceId)); 489 | torch::Tensor ntris_in_cells = 490 | torch::zeros({ Nz, Ny, Nx }, torch::TensorOptions().dtype(torch::kInt32).device(torch::kCUDA, deviceId)); 491 | torch::Tensor offsets = 492 | torch::zeros({ Nz, Ny, Nx }, torch::TensorOptions().dtype(torch::kInt32).device(torch::kCUDA, deviceId)); 493 | 494 | // Kernel call 495 | cudaSetDevice(deviceId); 496 | mcubes_cuda_kernel<<>>( 497 | vol.packed_accessor64(), 498 | vert_buffer.packed_accessor64(), 499 | ntris_in_cells.packed_accessor64(), nGrids, threshold, 500 | edgeTableTensorCuda.packed_accessor64(), 501 | triTableTensorCuda.packed_accessor64()); 502 | CUDA_CHECK_ERRORS(); 503 | cudaDeviceSynchronize(); 504 | 505 | // Compute number of triangles 506 | thrust::device_ptr ntris_in_cells_ptr((int *)ntris_in_cells.data_ptr()); 507 | thrust::device_ptr offsets_ptr((int *)offsets.data_ptr()); 508 | thrust::exclusive_scan(thrust::cuda::par.on(stream), ntris_in_cells_ptr, 509 | ntris_in_cells_ptr + ntris_in_cells.numel(), offsets_ptr); 510 | CUDA_CHECK_ERRORS(); 511 | cudaDeviceSynchronize(); 512 | 513 | const int ntri_last = ntris_in_cells[Nz - 1][Ny - 1][Nx - 1].cpu().item(); 514 | const int offset_last = offsets[Nz - 1][Ny - 1][Nx - 1].cpu().item(); 515 | const int ntris = std::max(1, ntri_last + offset_last); 516 | 517 | // Triangle list compaction 518 | torch::Tensor verts = 519 | torch::zeros({ ntris * 3, 3 }, torch::TensorOptions().dtype(torch::kFloat32).device(torch::kCUDA, deviceId)); 520 | torch::Tensor faces = 521 | torch::zeros({ ntris, 3 }, torch::TensorOptions().dtype(torch::kInt32).device(torch::kCUDA, deviceId)); 522 | 523 | cudaSetDevice(deviceId); 524 | compaction<<>>(vert_buffer.packed_accessor64(), 525 | ntris_in_cells.packed_accessor64(), 526 | offsets.packed_accessor64(), nGrids, 527 | verts.packed_accessor64(), 528 | faces.packed_accessor64()); 529 | CUDA_CHECK_ERRORS(); 530 | cudaDeviceSynchronize(); 531 | 532 | return { verts, faces }; 533 | } 534 | -------------------------------------------------------------------------------- /mcubes.py: -------------------------------------------------------------------------------- 1 | import time 2 | import argparse 3 | 4 | import numpy as np 5 | import torch 6 | import matplotlib.pyplot as plt 7 | from matplotlib.collections import PolyCollection 8 | 9 | from torchmcubes import grid_interp, marching_cubes 10 | 11 | 12 | def frustum(left, right, bottom, top, znear, zfar): 13 | M = np.zeros((4, 4), dtype=np.float32) 14 | M[0, 0] = +2.0 * znear / (right - left) 15 | M[1, 1] = +2.0 * znear / (top - bottom) 16 | M[2, 2] = -(zfar + znear) / (zfar - znear) 17 | M[0, 2] = (right + left) / (right - left) 18 | M[2, 1] = (top + bottom) / (top - bottom) 19 | M[2, 3] = -2.0 * znear * zfar / (zfar - znear) 20 | M[3, 2] = -1.0 21 | return M 22 | 23 | 24 | def perspective(fovy, aspect, znear, zfar): 25 | h = np.tan(0.5 * np.radians(fovy)) * znear 26 | w = h * aspect 27 | return frustum(-w, w, -h, h, znear, zfar) 28 | 29 | 30 | def translate(x, y, z): 31 | return np.array( 32 | [ 33 | [1, 0, 0, x], 34 | [0, 1, 0, y], 35 | [0, 0, 1, z], 36 | [0, 0, 0, 1], 37 | ], 38 | dtype=float, 39 | ) 40 | 41 | 42 | def xrotate(theta): 43 | t = np.pi * theta / 180 44 | c, s = np.cos(t), np.sin(t) 45 | return np.array( 46 | [ 47 | [1, 0, 0, 0], 48 | [0, c, -s, 0], 49 | [0, s, c, 0], 50 | [0, 0, 0, 1], 51 | ], 52 | dtype=float, 53 | ) 54 | 55 | 56 | def yrotate(theta): 57 | t = np.pi * theta / 180 58 | c, s = np.cos(t), np.sin(t) 59 | return np.array( 60 | [ 61 | [c, 0, s, 0], 62 | [0, 1, 0, 0], 63 | [-s, 0, c, 0], 64 | [0, 0, 0, 1], 65 | ], 66 | dtype=float, 67 | ) 68 | 69 | 70 | def visualize(V, F, C): 71 | """ 72 | This function is inspired by the following URL: 73 | https://blog.scientific-python.org/matplotlib/custom-3d-engine/ 74 | """ 75 | 76 | V = (V - (V.max(0) + V.min(0)) / 2) / max(V.max(0) - V.min(0)) 77 | MVP = (perspective(40, 1, 1, 100) @ translate(0, 0, -2.5) @ xrotate(0.0) @ yrotate(0.0)) 78 | 79 | V = np.c_[V, np.ones(len(V))] @ MVP.T 80 | V /= V[:, 3].reshape(-1, 1) 81 | V = V[F] 82 | C = C[F].mean(axis=1) 83 | 84 | T = V[:, :, :2] 85 | Z = -V[:, :, 2].mean(axis=1) 86 | zmin, zmax = Z.min(), Z.max() 87 | Z = (Z - zmin) / (zmax - zmin) 88 | I = np.argsort(Z) 89 | T, C = T[I, :], C[I, :] 90 | fig = plt.figure(figsize=(6, 6)) 91 | ax = fig.add_axes( 92 | (0, 0, 1, 1), 93 | xlim=(-1, 1), 94 | ylim=(-1, 1), 95 | aspect=1, 96 | frameon=False, 97 | ) 98 | 99 | collection = PolyCollection(T, closed=True, linewidth=0.1, facecolor=C, edgecolor="#00000033") 100 | ax.add_collection(collection) 101 | 102 | plt.show() 103 | 104 | 105 | def main(): 106 | # Arguments 107 | parser = argparse.ArgumentParser() 108 | parser.add_argument("--gpu", type=int, default=0) 109 | args = parser.parse_args() 110 | 111 | # Grid data 112 | N = 128 113 | xs = np.linspace(-1.0, 1.0, N, endpoint=True, dtype="float32") 114 | ys = np.linspace(-1.0, 1.0, N, endpoint=True, dtype="float32") 115 | zs = np.linspace(-1.0, 1.0, N, endpoint=True, dtype="float32") 116 | zs, ys, xs = np.meshgrid(zs, ys, xs) 117 | 118 | # Implicit function (metaball) 119 | f0 = (xs - 0.35)**2 + (ys - 0.35)**2 + (zs - 0.35)**2 120 | f1 = (xs + 0.35)**2 + (ys + 0.35)**2 + (zs + 0.35)**2 121 | u = 4.0 / (f0 + 1.0e-6) + 4.0 / (f1 + 1.0e-6) 122 | 123 | rgb = np.stack((xs, ys, zs), axis=-1) * 0.5 + 0.5 124 | rgb = np.transpose(rgb, axes=(3, 2, 1, 0)) 125 | rgb = np.ascontiguousarray(rgb) 126 | 127 | # Test (CPU) 128 | u = torch.from_numpy(u) 129 | rgb = torch.from_numpy(rgb) 130 | 131 | t_start = time.time() 132 | verts, faces = marching_cubes(u, 15.0) 133 | colors = grid_interp(rgb, verts) 134 | t_end = time.time() 135 | print(f"verts: {verts.size(0)}, faces: {faces.size(0)}, time: {t_end - t_start:.2f}s") 136 | 137 | verts = verts.numpy() 138 | faces = faces.numpy() 139 | colors = colors.numpy() 140 | verts = (verts / (N - 1)) * 2.0 - 1.0 # Get back to the original space 141 | visualize(verts, faces, colors) 142 | 143 | # Test (GPU) 144 | if torch.cuda.is_available(): 145 | device = torch.device("cuda", args.gpu) 146 | u = u.to(device) 147 | rgb = rgb.to(device) 148 | 149 | t_start = time.time() 150 | verts, faces = marching_cubes(u, 15.0) 151 | colors = grid_interp(rgb, verts) 152 | t_end = time.time() 153 | print(f"verts: {verts.size(0)}, faces: {faces.size(0)}, time: {t_end - t_start:.2f}s") 154 | 155 | verts = verts.detach().cpu().numpy() 156 | faces = faces.detach().cpu().numpy() 157 | colors = colors.detach().cpu().numpy() 158 | verts = (verts / (N - 1)) * 2.0 - 1.0 # Get back to the original space 159 | visualize(verts, faces, colors) 160 | 161 | else: 162 | print("CUDA is not available in this environment. Skip testing.") 163 | 164 | 165 | if __name__ == "__main__": 166 | main() 167 | -------------------------------------------------------------------------------- /metaball.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tatsy/torchmcubes/3381600ddc3d2e4d74222f8495866be5fafbace4/metaball.png -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "torchmcubes" 3 | version = "0.1.0" 4 | description = "torchmcubes: Marching Cubes for PyTorch" 5 | readme = "README.md" 6 | authors = [ 7 | {name = "Tatsuya Yatagawa", email = "tatsy.mail@gmail.com"} 8 | ] 9 | license = {file = "LICENSE"} 10 | classifiers = [ 11 | "Development Status :: 4 - Beta", 12 | 'Programming Language :: Python :: 3.9', 13 | 'Programming Language :: Python :: 3.10', 14 | 'Programming Language :: Python :: 3.11', 15 | 'Programming Language :: Python :: 3.12', 16 | ] 17 | requires-python = ">=3.9" 18 | dependencies=["numpy", "torch"] 19 | 20 | [tool.scikit-build] 21 | build-dir = "build" 22 | minimum-version = "0.10" 23 | ninja.make-fallback = true 24 | logging.level = "INFO" 25 | build.verbose = true 26 | messages.after-success = "SUCCESS!!" 27 | messages.after-failure = "FAILURE!!" 28 | 29 | [tool.scikit-build.cmake] 30 | version = ">=3.15" 31 | source-dir = "." 32 | args = [] 33 | 34 | [tool.scikit-build.wheel] 35 | license-files = ["LICENSE"] 36 | exclude = ["**/.mypy_cache/**", "**/build/**", "**/.vscode/**"] 37 | 38 | [build-system] 39 | requires = ["scikit-build-core>=0.10", "pybind11>=2.10", "cmake", "ninja"] 40 | build-backend = "scikit_build_core.build" 41 | 42 | [tool.isort] 43 | length_sort = true 44 | line_length = 120 45 | 46 | [tool.mypy] 47 | python_version = "3.11" 48 | follow_imports = "silent" 49 | disallow_any_generics = true 50 | strict_optional = true 51 | check_untyped_defs = true 52 | allow_redefinition = true 53 | 54 | [tool.flake8] 55 | max-line-length = 120 56 | ignore = "Q000,E203,W503,W504" 57 | 58 | [tool.yapf] 59 | based_on_style = "pep8" 60 | column_limit = 120 61 | indent_width = 4 62 | 63 | ALIGN_CLOSING_BRACKET_WITH_VISUAL_INDENT = true 64 | COALESCE_BRACKETS = false 65 | DEDENT_CLOSING_BRACKETS = false 66 | DISABLE_ENDING_COMMA_HEURISTIC = false 67 | INDENT_DICTIONARY_VALUE = false 68 | ALLOW_SPLIT_BEFORE_DICT_VALUE = false 69 | SPLIT_ARGUMENTS_WHEN_COMMA_TERMINATED = true 70 | -------------------------------------------------------------------------------- /torchmcubes/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE" 4 | 5 | from typing import Tuple 6 | 7 | import torch 8 | import torchmcubes_module as mc 9 | 10 | 11 | def marching_cubes(vol: torch.Tensor, thresh: float) -> Tuple[torch.Tensor, torch.Tensor]: 12 | """ 13 | vol: 3D torch tensor 14 | thresh: threshold 15 | """ 16 | 17 | if vol.is_cuda: 18 | return mc.mcubes_cuda(vol, thresh) 19 | else: 20 | return mc.mcubes_cpu(vol, thresh) 21 | 22 | 23 | def grid_interp(vol: torch.Tensor, points: torch.Tensor) -> torch.Tensor: 24 | """ 25 | Interpolate volume data at given points 26 | 27 | Inputs: 28 | vol: 4D torch tensor (C, Nz, Ny, Nx) 29 | points: point locations (Np, 3) 30 | Outputs: 31 | output: interpolated data (Np, C) 32 | """ 33 | 34 | if vol.is_cuda: 35 | return mc.grid_interp_cuda(vol, points) 36 | else: 37 | return mc.grid_interp_cpu(vol, points) 38 | --------------------------------------------------------------------------------