├── .gitignore ├── .readthedocs.yaml ├── LICENSE.txt ├── MANIFEST.in ├── README.md ├── docs ├── Makefile ├── build │ └── html │ │ ├── .buildinfo │ │ ├── _sources │ │ ├── index.rst.txt │ │ └── nerfvis.rst.txt │ │ ├── _static │ │ ├── basic.css │ │ ├── css │ │ │ ├── badge_only.css │ │ │ ├── fonts │ │ │ │ ├── Roboto-Slab-Bold.woff │ │ │ │ ├── Roboto-Slab-Bold.woff2 │ │ │ │ ├── Roboto-Slab-Regular.woff │ │ │ │ ├── Roboto-Slab-Regular.woff2 │ │ │ │ ├── fontawesome-webfont.eot │ │ │ │ ├── fontawesome-webfont.svg │ │ │ │ ├── fontawesome-webfont.ttf │ │ │ │ ├── fontawesome-webfont.woff │ │ │ │ ├── fontawesome-webfont.woff2 │ │ │ │ ├── lato-bold-italic.woff │ │ │ │ ├── lato-bold-italic.woff2 │ │ │ │ ├── lato-bold.woff │ │ │ │ ├── lato-bold.woff2 │ │ │ │ ├── lato-normal-italic.woff │ │ │ │ ├── lato-normal-italic.woff2 │ │ │ │ ├── lato-normal.woff │ │ │ │ └── lato-normal.woff2 │ │ │ └── theme.css │ │ ├── doctools.js │ │ ├── documentation_options.js │ │ ├── file.png │ │ ├── jquery-3.5.1.js │ │ ├── jquery.js │ │ ├── js │ │ │ ├── badge_only.js │ │ │ ├── html5shiv-printshiv.min.js │ │ │ ├── html5shiv.min.js │ │ │ └── theme.js │ │ ├── language_data.js │ │ ├── minus.png │ │ ├── plus.png │ │ ├── pygments.css │ │ ├── searchtools.js │ │ ├── underscore-1.13.1.js │ │ └── underscore.js │ │ ├── genindex.html │ │ ├── index.html │ │ ├── nerfvis.html │ │ ├── objects.inv │ │ ├── search.html │ │ └── searchindex.js ├── make.bat ├── requirements.txt └── source │ ├── conf.py │ ├── index.rst │ └── nerfvis.rst ├── examples ├── bulldozer.jpg ├── hierarchy.py ├── image.py ├── load_plenoctree_ckpt.py └── nerf_pl │ ├── README.md │ ├── img │ └── silica.png │ └── visualize.py ├── img ├── silica_low.gif ├── skull.gif └── youtube_drone.gif ├── nerfvis ├── __init__.py ├── index.html ├── scene.py ├── utils │ ├── __init__.py │ ├── _rotation.c │ ├── _rotation.pyx │ └── sh.py └── version.py ├── pyproject.toml ├── requirements.txt ├── setup.cfg └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | nerf_pl/ 2 | nerfvis_scenes/ 3 | 4 | # Direnv stuffs # 5 | .direnv 6 | .envrc 7 | 8 | # Compiled source # 9 | build/ 10 | .cache/ 11 | *.class 12 | *.dll 13 | *.exe 14 | *.o 15 | *.so 16 | *.pyc 17 | **/__pycache__/ 18 | 19 | # Packages # 20 | # It's better to unpack these files and commit the raw source because 21 | # git has its own built in compression methods. 22 | *.7z 23 | *.jar 24 | *.rar 25 | *.zip 26 | *.gz 27 | *.bzip 28 | *.xz 29 | *.lzma 30 | 31 | # packing-only formats 32 | *.iso 33 | *.tar 34 | 35 | # package management formats 36 | *.dmg 37 | *.xpi 38 | *.gem 39 | *.egg 40 | *.egg-info 41 | *.deb 42 | *.rpm 43 | 44 | # Logs and databases # 45 | *.log 46 | *.sqlite 47 | 48 | # OS generated files # 49 | .DS_Store 50 | .Spotlight-V100 51 | .Trashes 52 | ._* 53 | 54 | # Linux 55 | .fuse_hidden* 56 | .nfs* 57 | 58 | # Windows image file caches 59 | Thumbs.db 60 | 61 | # Folder config file 62 | Desktop.ini 63 | 64 | # Vim 65 | .*.s[a-w][a-z] 66 | 67 | # IDE stuffs 68 | .idea/ 69 | *.iml 70 | .project 71 | .classpath 72 | .settings/ 73 | .ipynb_checkpoints/ 74 | 75 | # Tex 76 | *.aux 77 | *.bbl 78 | *.blg 79 | *.brf 80 | *.fdb_latexmk 81 | *.fls 82 | *.log 83 | *.out 84 | *.pdf 85 | *.synctex.gz 86 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | python: 4 | version: 3.8 5 | install: 6 | - requirements: docs/requirements.txt 7 | - method: pip 8 | path: . 9 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright Alex Yu 2021 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, 7 | this list of conditions and the following disclaimer. 8 | 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 17 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 23 | POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.md 2 | include LICENSE.txt 3 | include MANIFEST.in 4 | include pyproject.toml 5 | include setup.py 6 | include nerfvis/scene.py 7 | include nerfvis/utils/sh.py 8 | include nerfvis/utils/__init__.py 9 | include nerfvis/utils/_rotation.pyx 10 | include nerfvis/utils/_rotation.c 11 | include nerfvis/__init__.py 12 | include nerfvis/index.html 13 | include nerfvis/version.py 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | In-browser 3D visualization library for rapid prototyping with built-in support for 2 | diffuse view-dependent sparse volumes (PlenOctrees). 3 | 4 | Install with: 5 | `pip install nerfvis` 6 | 7 | **Note:** this is purely Python + webasm/js/css/html and installs instantly (does not need any C++ compilation). 8 | 9 | **Docs:** https://nerfvis.readthedocs.org 10 | 11 | **Instant example:** `pip install nerfvis`, then 12 | ``` 13 | >>> from nerfvis import scene 14 | >>> scene.add_cube("Cube1", color=[1.0, 0.0, 0.0]) 15 | >>> scene.display(port=8888) 16 | ``` 17 | 18 | Inside a Jupyter notebook, you can try using `scene.embed()` instead. 19 | 20 | 21 | For example of use with data visualization (not NeRF): 22 | http://alexyu.net/nerfvis_examples/bicycle_vis/ 23 | Data from Mip-NeRF 360 (Barron et al. CVPR 2022) 24 | 25 | 26 | Please also see examples/nerf_pl for an example of how to visualize your own NeRF: 27 | . 28 | You may also refer to the as the `scene.add_nerf` function doc: https://nerfvis.readthedocs.io/en/latest/nerfvis.html#nerfvis.Scene.add_nerf 29 | 30 | Based on PlenOctrees: https://github.com/sxyu/plenoctrees 31 | 32 | The following screenshots are out of date but still convey some of the functionality: 33 | 34 | ![Screenshot DTU skull](https://raw.githubusercontent.com/sxyu/nerfvis/master/img/skull.gif) 35 | 36 | ![Screenshot NeRF-- Drone](https://raw.githubusercontent.com/sxyu/nerfvis/master/img/youtube_drone.gif) 37 | 38 | 39 | Tips: 40 | 41 | - A list of all objects with the names you gave them will be displayed in a tree view on the left side of the screen, where you can toggle them. F-strings are recommended for automatically generating object names 42 | - Use "/" inside names for example `image/0` to create nested trees. 43 | - For convenience, we accept numpy arrays, torch Tensors, and lists in general for any arguments marked at `np.ndarray` (by default torch is not imported to avoid having it as a dependency). 44 | - The initial camera pose will be automatically determined. Pass `center=[x, y, z]` (camera position) , 45 | `origin=[x,y,z]` (camera target), `forward=[x,y,z]` (forward vector), `world_up=[x,y,z]` (world space up vector) to display() or export() or embed() to manually set an initial pose. 46 | A convenience function `scene.set_opencv()` is given to set the world up axis to `-y` (this also changes the default behavior of `add_image`). 47 | - Use `scene.export("path")` to manually generate a directory you can open in the browser or upload somewhere 48 | 49 | 50 | ## Examples 51 | 52 | ### Viewing a volume 53 | 54 | ```python 55 | from nerfvis import scene 56 | import numpy as np 57 | 58 | density = 1.0 / (np.linalg.norm((np.mgrid[:100, :100, :100].transpose(1, 2, 3, 0) - 45.5) / 50, 59 | axis=-1) + 1e-5) # (Dx, Dy, Dz) 60 | color = np.zeros((100, 100, 100, 3), dtype=np.float32) # (Dx, Dy, Dz, 3) 61 | color[..., 0] = 1.0 62 | color[..., 1] = 0.5 63 | scene.add_volume('My volume 1', density, color, scale=0.2, translation=[-1, 0, 0]) 64 | 65 | color[..., 1] = 0.0 66 | scene.add_volume('My volume 2', density, color, scale=0.2, translation=[1, 0, 0]) 67 | scene.display() # or embed(), etc 68 | ``` 69 | 70 | For an example with a few more objects, see `examples/hierarchy.py`, which outputs 71 | http://alexyu.net/nerfvis_examples/basic_scene_with_volume/ 72 | 73 | ### Load a PlenOctrees checkpoint 74 | 75 | For directly displaying a plenoctree checkpoint: `examples/load_plenoctree_ckpt.py`. 76 | Note that the checkpoint better be reasonably small or this will take forever... 77 | 78 | ```python 79 | from nerfvis import scene 80 | # Download from 81 | # https://drive.google.com/drive/u/1/folders/1vGXEjb3yhbClrZH1vLdl2iKtowfinWOg 82 | scene.set_title("Lego Bulldozer using nerfvis") 83 | scene.add_volume_from_npz('Lego', "lego.npz", scale=1.0) 84 | scene.display() # or embed(), etc 85 | ``` 86 | 87 | ### Visualizing SfM data 88 | 89 | Given: 90 | - camera-to-world poses `c2w` in OpenCV convention `(n_images, 4, 4)` 91 | (also easy to use OpenGL convention, it is default: just omit `set_opencv()`) 92 | - focal length, image size 93 | - SfM point cloud `(n_points, 3)` (optional), optionally with errors `(n_points,)` 94 | - Images `(n_images, h, w)` 95 | 96 | Note that OpenCV poses are now preferrred, 97 | although the original NeRF/PlenOctrees used OpenGL. 98 | 99 | ```python 100 | from nerfvis import scene 101 | import numpy as np 102 | 103 | scene.set_title("My Scene") 104 | 105 | # Set -y up camera space (opencv coordinates) 106 | scene.set_opencv() 107 | 108 | # Alt set y up camera space (opengl coordinates, default) 109 | #scene.set_opengl() 110 | 111 | 112 | # Example data 113 | f = 1111.0 114 | images = np.random.rand(1, 800, 800, 3) 115 | c2ws = np.eye(4)[None] 116 | point_cloud = np.random.randn(10000, 3) * 0.1 117 | point_cloud_errs = np.random.rand(10000) 118 | 119 | # To show errors as colors 120 | colors = np.zeros_like(point_cloud) 121 | colors[:, 0] = point_cloud_errs / point_cloud_errs.max() 122 | scene.add_points("points", point_cloud, vert_color=colors) 123 | # Else 124 | # scene.add_points("points", point_cloud, color=[0.0, 0.0, 0.0]) 125 | 126 | for i in range(len(c2ws)): 127 | scene.add_images( 128 | f"images/i", 129 | images, # Can be a list of paths too (requires joblib for that) 130 | r=c2ws[:, :3, :3], 131 | t=c2ws[:, :3, 3], 132 | # Alternatively: from nerfvis.utils import split_mat4; **split_mat4(c2ws) 133 | focal_length=f, 134 | z=0.5, 135 | with_camera_frustum=True, 136 | ) 137 | # r: c2w rotation (N, 3, 3) or (N, 4) etc 138 | # t: c2w translation (N, 3,) 139 | # focal_length: focal length (in pixels, real image size as loaded will be used) 140 | # z: size of camera 141 | 142 | # Old way for reference 143 | # scene.add_camera_frustum("cameras", r=c2ws[:, :3, :3], t=c2ws[:, :3, 3], focal_length=f, 144 | # image_width=images.shape[2], image_height=images.shape[1], 145 | # z=0.5, connect=False, color=[1.0, 0.0, 0.0]) 146 | 147 | # for i in range(len(c2ws)): 148 | # scene.add_image( 149 | # f"images/i", 150 | # images[i], # Can be path too 151 | # r=c2ws[i, :3, :3], 152 | # t=c2ws[i, :3, 3], 153 | # focal_length=f, 154 | # z=0.5) 155 | # # r: c2w rotation (3, 3) 156 | # # t: c2w translation (3,) 157 | # # focal_length: focal length (in pixels, real image size as loaded will be used) 158 | # # z: distance along z to place the camera 159 | scene.add_axes() 160 | scene.display() 161 | ``` 162 | 163 | Example outputs (not quite the same code): 164 | http://alexyu.net/nerfvis_examples/basic_scene_with_volume/ 165 | http://alexyu.net/nerfvis_examples/bicycle_vis/ 166 | Data from Mip-NeRF 360 (Barron et al. CVPR 2022) 167 | 168 | ### Time slider 169 | 170 | Nerfvis has a time slider feature (top of the left sidebar). 171 | All `add_*` functions support a `time=` kwarg which is default to `-1` (visible at all times). 172 | You can set it to another number to enable the time slider and only show the object when it is set to that time. 173 | The time slider will be automatically updated to be between 0 and the max time amont all objects. 174 | 175 | ### Visualizing NeRF directly through svox 176 | 177 | This is the most flexible way to directly discretize and show a NeRF, 178 | albeit a bit clunky to use and requiring extra dependencies. 179 | 180 | Example: please see `examples/` for how to view NeRF models; currently contains an example for nerf_pl (https://github.com/kwea123/nerf_pl): 181 | ![Basic silica low ](https://raw.githubusercontent.com/sxyu/nerfvis/master/img/silica_low.gif) 182 | 183 | ```python 184 | import nerfvis 185 | scene = nerfvis.Scene("My title") 186 | scene.add_cube("Cube1", color=[1.0, 0.0, 0.0], translation=[-1.0, -1.0, 0.0]) 187 | scene.add_axes() 188 | scene.add_nerf("NeRF", nerf_func, center=[0.0, 0.0, 0.0], radius=1.5, use_dirs=True) 189 | scene.display(port=8889) 190 | # Tries to open the scene in your browser 191 | # (you may have to forward the port and enter localhost:8889 manually if over ssh) 192 | ``` 193 | 194 | Use `display(open_browser=False)` to prevent opening the browser (while serving the website) 195 | 196 | You can also add meshes, points, lines (see docs). 197 | Note that each object e.g. cube, mesh, points, etc. must have a unique name to identify it 198 | right now. You may programmatically generate this. They will show up in the *layers* pane 199 | (top right of the html viewer) 200 | 201 | 202 | Please also `pip install torch svox tqdm scipy` for adding NeRF (`set_nerf`) 203 | or `pip install trimesh` for using `add_mesh_from_file(path)`. 204 | 205 | To add cameras (also used for scaling scene, initializing camera etc), use 206 | `add_camera_frustum(focal_length=.., image_width=.., image_height=.., z=.., r=.., t=..)` 207 | 208 | ## Viewer Controls 209 | 210 | - Left click and drag to orbit 211 | - Right click and drag, or CTRL+left click and drag to pan 212 | - Mouse wheel, middle click and drag, or ALT+left click and drag to zoom; alternatively use =/SHIFT+= 213 | - Number keys 1-6 to change coordinate systems: Z up/down Y up/down X up/down resp. 214 | 215 | 216 | ## Source of pre-compiled binaries 217 | 218 | This project contains a index.html containing inlined wasm, which comes from `volrend`, the branch `nerfvis_base`, compiled using Emscripten as per the instructions in that repo. 219 | 220 | https://github.com/sxyu/volrend/tree/nerfvis_base 221 | 222 | ## Citation 223 | 224 | If you find this useful please consider citing 225 | ``` 226 | @inproceedings{yu2021plenoctrees, 227 | title={{PlenOctrees} for Real-time Rendering of Neural Radiance Fields}, 228 | author={Alex Yu and Ruilong Li and Matthew Tancik and Hao Li and Ren Ng and Angjoo Kanazawa}, 229 | year={2021}, 230 | booktitle={ICCV}, 231 | } 232 | ``` 233 | 234 | License: BSD 2-clause 235 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = source 9 | BUILDDIR = build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /docs/build/html/.buildinfo: -------------------------------------------------------------------------------- 1 | # Sphinx build info version 1 2 | # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. 3 | config: ef5e72d3ddc7a24f11f51838045e1911 4 | tags: 645f666f9bcd5a90fca523b33c5a78b7 5 | -------------------------------------------------------------------------------- /docs/build/html/_sources/index.rst.txt: -------------------------------------------------------------------------------- 1 | .. nerfvis documentation master file, created by 2 | sphinx-quickstart on Wed Sep 15 10:48:24 2021. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to nerfvis's documentation! 7 | =================================== 8 | 9 | This is a PyTorch NeRF visualization utility based on PlenOctrees, as well as a 10 | general web-based 3D visualization library. 11 | 12 | Install with :code:`pip install nerfvis`. This is a pure-Python library. The base library has no dependencies except for :code:`numpy`. 13 | Adding meshes from a file requires :code:`trimesh`. 14 | Adding NeRF requires :code:`torch, svox, tqdm, scipy`. 15 | 16 | Basic usage example: 17 | 18 | .. code:: python 19 | 20 | import nerfvis 21 | scene = nerfvis.Scene("My title") 22 | scene.add_cube("Cube/1", color=[1.0, 0.0, 0.0], translation=[-1.0, -1.0, 0.0]) 23 | scene.add_wireframe_cube("Cube/2", color=[1.0, 0.0, 0.0], 24 | translation=[-1.0, -1.0, 0.0], scale=2.0) 25 | scene.add_axes() 26 | scene.set_nerf(nerf_func, center=[0.0, 0.0, 0.0], radius=1.5, use_dirs=True) # Optional 27 | scene.display() # Serves at localhost:8888 or first port available after that 28 | scene.export("folder") # Exports sources to location 29 | scene.embed() # Embed in IPython notebook 30 | 31 | Scene: holds objects. If you only use one scene, feel free to use :code:`from nerfvis import scene` instead. 32 | 33 | Names separated by :code:`/` will be collapsed into a tree in the layers panel in the output. 34 | 35 | Other common functions include `add_camera_frustum`, `add_mesh`, `add_line`, `add_lines`, 36 | `add_image` 37 | 38 | Please see :ref:`nerfvis` for details. 39 | 40 | .. toctree:: 41 | :maxdepth: 2 42 | :caption: Contents: 43 | 44 | nerfvis 45 | 46 | 47 | Viewer controls 48 | ==================== 49 | 50 | * Left click and drag to orbit 51 | * Right click and drag, or :code:`CTRL+left` click and drag to pan 52 | * Mouse wheel, middle click and drag, or :code:`ALT+left` click and drag to zoom; alternatively use :code:`=`/:code:`SHIFT` + :code:`=` 53 | * Number keys 1-6 to change coordinate systems: Z up/down Y up/down X up/down resp. 54 | 55 | -------------------------------------------------------------------------------- /docs/build/html/_sources/nerfvis.rst.txt: -------------------------------------------------------------------------------- 1 | .. _nerfvis: 2 | 3 | Reference 4 | ============ 5 | 6 | nerfvis.scene 7 | -------------- 8 | 9 | .. autoclass:: nerfvis.Scene 10 | :members: 11 | :show-inheritance: 12 | 13 | 14 | nerfvis.utils 15 | -------------- 16 | 17 | .. autofunction:: nerfvis.utils.split_mat4 18 | -------------------------------------------------------------------------------- /docs/build/html/_static/basic.css: -------------------------------------------------------------------------------- 1 | /* 2 | * basic.css 3 | * ~~~~~~~~~ 4 | * 5 | * Sphinx stylesheet -- basic theme. 6 | * 7 | * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. 8 | * :license: BSD, see LICENSE for details. 9 | * 10 | */ 11 | 12 | /* -- main layout ----------------------------------------------------------- */ 13 | 14 | div.clearer { 15 | clear: both; 16 | } 17 | 18 | div.section::after { 19 | display: block; 20 | content: ''; 21 | clear: left; 22 | } 23 | 24 | /* -- relbar ---------------------------------------------------------------- */ 25 | 26 | div.related { 27 | width: 100%; 28 | font-size: 90%; 29 | } 30 | 31 | div.related h3 { 32 | display: none; 33 | } 34 | 35 | div.related ul { 36 | margin: 0; 37 | padding: 0 0 0 10px; 38 | list-style: none; 39 | } 40 | 41 | div.related li { 42 | display: inline; 43 | } 44 | 45 | div.related li.right { 46 | float: right; 47 | margin-right: 5px; 48 | } 49 | 50 | /* -- sidebar --------------------------------------------------------------- */ 51 | 52 | div.sphinxsidebarwrapper { 53 | padding: 10px 5px 0 10px; 54 | } 55 | 56 | div.sphinxsidebar { 57 | float: left; 58 | width: 230px; 59 | margin-left: -100%; 60 | font-size: 90%; 61 | word-wrap: break-word; 62 | overflow-wrap : break-word; 63 | } 64 | 65 | div.sphinxsidebar ul { 66 | list-style: none; 67 | } 68 | 69 | div.sphinxsidebar ul ul, 70 | div.sphinxsidebar ul.want-points { 71 | margin-left: 20px; 72 | list-style: square; 73 | } 74 | 75 | div.sphinxsidebar ul ul { 76 | margin-top: 0; 77 | margin-bottom: 0; 78 | } 79 | 80 | div.sphinxsidebar form { 81 | margin-top: 10px; 82 | } 83 | 84 | div.sphinxsidebar input { 85 | border: 1px solid #98dbcc; 86 | font-family: sans-serif; 87 | font-size: 1em; 88 | } 89 | 90 | div.sphinxsidebar #searchbox form.search { 91 | overflow: hidden; 92 | } 93 | 94 | div.sphinxsidebar #searchbox input[type="text"] { 95 | float: left; 96 | width: 80%; 97 | padding: 0.25em; 98 | box-sizing: border-box; 99 | } 100 | 101 | div.sphinxsidebar #searchbox input[type="submit"] { 102 | float: left; 103 | width: 20%; 104 | border-left: none; 105 | padding: 0.25em; 106 | box-sizing: border-box; 107 | } 108 | 109 | 110 | img { 111 | border: 0; 112 | max-width: 100%; 113 | } 114 | 115 | /* -- search page ----------------------------------------------------------- */ 116 | 117 | ul.search { 118 | margin: 10px 0 0 20px; 119 | padding: 0; 120 | } 121 | 122 | ul.search li { 123 | padding: 5px 0 5px 20px; 124 | background-image: url(file.png); 125 | background-repeat: no-repeat; 126 | background-position: 0 7px; 127 | } 128 | 129 | ul.search li a { 130 | font-weight: bold; 131 | } 132 | 133 | ul.search li p.context { 134 | color: #888; 135 | margin: 2px 0 0 30px; 136 | text-align: left; 137 | } 138 | 139 | ul.keywordmatches li.goodmatch a { 140 | font-weight: bold; 141 | } 142 | 143 | /* -- index page ------------------------------------------------------------ */ 144 | 145 | table.contentstable { 146 | width: 90%; 147 | margin-left: auto; 148 | margin-right: auto; 149 | } 150 | 151 | table.contentstable p.biglink { 152 | line-height: 150%; 153 | } 154 | 155 | a.biglink { 156 | font-size: 1.3em; 157 | } 158 | 159 | span.linkdescr { 160 | font-style: italic; 161 | padding-top: 5px; 162 | font-size: 90%; 163 | } 164 | 165 | /* -- general index --------------------------------------------------------- */ 166 | 167 | table.indextable { 168 | width: 100%; 169 | } 170 | 171 | table.indextable td { 172 | text-align: left; 173 | vertical-align: top; 174 | } 175 | 176 | table.indextable ul { 177 | margin-top: 0; 178 | margin-bottom: 0; 179 | list-style-type: none; 180 | } 181 | 182 | table.indextable > tbody > tr > td > ul { 183 | padding-left: 0em; 184 | } 185 | 186 | table.indextable tr.pcap { 187 | height: 10px; 188 | } 189 | 190 | table.indextable tr.cap { 191 | margin-top: 10px; 192 | background-color: #f2f2f2; 193 | } 194 | 195 | img.toggler { 196 | margin-right: 3px; 197 | margin-top: 3px; 198 | cursor: pointer; 199 | } 200 | 201 | div.modindex-jumpbox { 202 | border-top: 1px solid #ddd; 203 | border-bottom: 1px solid #ddd; 204 | margin: 1em 0 1em 0; 205 | padding: 0.4em; 206 | } 207 | 208 | div.genindex-jumpbox { 209 | border-top: 1px solid #ddd; 210 | border-bottom: 1px solid #ddd; 211 | margin: 1em 0 1em 0; 212 | padding: 0.4em; 213 | } 214 | 215 | /* -- domain module index --------------------------------------------------- */ 216 | 217 | table.modindextable td { 218 | padding: 2px; 219 | border-collapse: collapse; 220 | } 221 | 222 | /* -- general body styles --------------------------------------------------- */ 223 | 224 | div.body { 225 | min-width: 360px; 226 | max-width: 800px; 227 | } 228 | 229 | div.body p, div.body dd, div.body li, div.body blockquote { 230 | -moz-hyphens: auto; 231 | -ms-hyphens: auto; 232 | -webkit-hyphens: auto; 233 | hyphens: auto; 234 | } 235 | 236 | a.headerlink { 237 | visibility: hidden; 238 | } 239 | 240 | h1:hover > a.headerlink, 241 | h2:hover > a.headerlink, 242 | h3:hover > a.headerlink, 243 | h4:hover > a.headerlink, 244 | h5:hover > a.headerlink, 245 | h6:hover > a.headerlink, 246 | dt:hover > a.headerlink, 247 | caption:hover > a.headerlink, 248 | p.caption:hover > a.headerlink, 249 | div.code-block-caption:hover > a.headerlink { 250 | visibility: visible; 251 | } 252 | 253 | div.body p.caption { 254 | text-align: inherit; 255 | } 256 | 257 | div.body td { 258 | text-align: left; 259 | } 260 | 261 | .first { 262 | margin-top: 0 !important; 263 | } 264 | 265 | p.rubric { 266 | margin-top: 30px; 267 | font-weight: bold; 268 | } 269 | 270 | img.align-left, figure.align-left, .figure.align-left, object.align-left { 271 | clear: left; 272 | float: left; 273 | margin-right: 1em; 274 | } 275 | 276 | img.align-right, figure.align-right, .figure.align-right, object.align-right { 277 | clear: right; 278 | float: right; 279 | margin-left: 1em; 280 | } 281 | 282 | img.align-center, figure.align-center, .figure.align-center, object.align-center { 283 | display: block; 284 | margin-left: auto; 285 | margin-right: auto; 286 | } 287 | 288 | img.align-default, figure.align-default, .figure.align-default { 289 | display: block; 290 | margin-left: auto; 291 | margin-right: auto; 292 | } 293 | 294 | .align-left { 295 | text-align: left; 296 | } 297 | 298 | .align-center { 299 | text-align: center; 300 | } 301 | 302 | .align-default { 303 | text-align: center; 304 | } 305 | 306 | .align-right { 307 | text-align: right; 308 | } 309 | 310 | /* -- sidebars -------------------------------------------------------------- */ 311 | 312 | div.sidebar, 313 | aside.sidebar { 314 | margin: 0 0 0.5em 1em; 315 | border: 1px solid #ddb; 316 | padding: 7px; 317 | background-color: #ffe; 318 | width: 40%; 319 | float: right; 320 | clear: right; 321 | overflow-x: auto; 322 | } 323 | 324 | p.sidebar-title { 325 | font-weight: bold; 326 | } 327 | 328 | nav.contents, 329 | aside.topic, 330 | div.admonition, div.topic, blockquote { 331 | clear: left; 332 | } 333 | 334 | /* -- topics ---------------------------------------------------------------- */ 335 | 336 | nav.contents, 337 | aside.topic, 338 | div.topic { 339 | border: 1px solid #ccc; 340 | padding: 7px; 341 | margin: 10px 0 10px 0; 342 | } 343 | 344 | p.topic-title { 345 | font-size: 1.1em; 346 | font-weight: bold; 347 | margin-top: 10px; 348 | } 349 | 350 | /* -- admonitions ----------------------------------------------------------- */ 351 | 352 | div.admonition { 353 | margin-top: 10px; 354 | margin-bottom: 10px; 355 | padding: 7px; 356 | } 357 | 358 | div.admonition dt { 359 | font-weight: bold; 360 | } 361 | 362 | p.admonition-title { 363 | margin: 0px 10px 5px 0px; 364 | font-weight: bold; 365 | } 366 | 367 | div.body p.centered { 368 | text-align: center; 369 | margin-top: 25px; 370 | } 371 | 372 | /* -- content of sidebars/topics/admonitions -------------------------------- */ 373 | 374 | div.sidebar > :last-child, 375 | aside.sidebar > :last-child, 376 | nav.contents > :last-child, 377 | aside.topic > :last-child, 378 | div.topic > :last-child, 379 | div.admonition > :last-child { 380 | margin-bottom: 0; 381 | } 382 | 383 | div.sidebar::after, 384 | aside.sidebar::after, 385 | nav.contents::after, 386 | aside.topic::after, 387 | div.topic::after, 388 | div.admonition::after, 389 | blockquote::after { 390 | display: block; 391 | content: ''; 392 | clear: both; 393 | } 394 | 395 | /* -- tables ---------------------------------------------------------------- */ 396 | 397 | table.docutils { 398 | margin-top: 10px; 399 | margin-bottom: 10px; 400 | border: 0; 401 | border-collapse: collapse; 402 | } 403 | 404 | table.align-center { 405 | margin-left: auto; 406 | margin-right: auto; 407 | } 408 | 409 | table.align-default { 410 | margin-left: auto; 411 | margin-right: auto; 412 | } 413 | 414 | table caption span.caption-number { 415 | font-style: italic; 416 | } 417 | 418 | table caption span.caption-text { 419 | } 420 | 421 | table.docutils td, table.docutils th { 422 | padding: 1px 8px 1px 5px; 423 | border-top: 0; 424 | border-left: 0; 425 | border-right: 0; 426 | border-bottom: 1px solid #aaa; 427 | } 428 | 429 | th { 430 | text-align: left; 431 | padding-right: 5px; 432 | } 433 | 434 | table.citation { 435 | border-left: solid 1px gray; 436 | margin-left: 1px; 437 | } 438 | 439 | table.citation td { 440 | border-bottom: none; 441 | } 442 | 443 | th > :first-child, 444 | td > :first-child { 445 | margin-top: 0px; 446 | } 447 | 448 | th > :last-child, 449 | td > :last-child { 450 | margin-bottom: 0px; 451 | } 452 | 453 | /* -- figures --------------------------------------------------------------- */ 454 | 455 | div.figure, figure { 456 | margin: 0.5em; 457 | padding: 0.5em; 458 | } 459 | 460 | div.figure p.caption, figcaption { 461 | padding: 0.3em; 462 | } 463 | 464 | div.figure p.caption span.caption-number, 465 | figcaption span.caption-number { 466 | font-style: italic; 467 | } 468 | 469 | div.figure p.caption span.caption-text, 470 | figcaption span.caption-text { 471 | } 472 | 473 | /* -- field list styles ----------------------------------------------------- */ 474 | 475 | table.field-list td, table.field-list th { 476 | border: 0 !important; 477 | } 478 | 479 | .field-list ul { 480 | margin: 0; 481 | padding-left: 1em; 482 | } 483 | 484 | .field-list p { 485 | margin: 0; 486 | } 487 | 488 | .field-name { 489 | -moz-hyphens: manual; 490 | -ms-hyphens: manual; 491 | -webkit-hyphens: manual; 492 | hyphens: manual; 493 | } 494 | 495 | /* -- hlist styles ---------------------------------------------------------- */ 496 | 497 | table.hlist { 498 | margin: 1em 0; 499 | } 500 | 501 | table.hlist td { 502 | vertical-align: top; 503 | } 504 | 505 | /* -- object description styles --------------------------------------------- */ 506 | 507 | .sig { 508 | font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; 509 | } 510 | 511 | .sig-name, code.descname { 512 | background-color: transparent; 513 | font-weight: bold; 514 | } 515 | 516 | .sig-name { 517 | font-size: 1.1em; 518 | } 519 | 520 | code.descname { 521 | font-size: 1.2em; 522 | } 523 | 524 | .sig-prename, code.descclassname { 525 | background-color: transparent; 526 | } 527 | 528 | .optional { 529 | font-size: 1.3em; 530 | } 531 | 532 | .sig-paren { 533 | font-size: larger; 534 | } 535 | 536 | .sig-param.n { 537 | font-style: italic; 538 | } 539 | 540 | /* C++ specific styling */ 541 | 542 | .sig-inline.c-texpr, 543 | .sig-inline.cpp-texpr { 544 | font-family: unset; 545 | } 546 | 547 | .sig.c .k, .sig.c .kt, 548 | .sig.cpp .k, .sig.cpp .kt { 549 | color: #0033B3; 550 | } 551 | 552 | .sig.c .m, 553 | .sig.cpp .m { 554 | color: #1750EB; 555 | } 556 | 557 | .sig.c .s, .sig.c .sc, 558 | .sig.cpp .s, .sig.cpp .sc { 559 | color: #067D17; 560 | } 561 | 562 | 563 | /* -- other body styles ----------------------------------------------------- */ 564 | 565 | ol.arabic { 566 | list-style: decimal; 567 | } 568 | 569 | ol.loweralpha { 570 | list-style: lower-alpha; 571 | } 572 | 573 | ol.upperalpha { 574 | list-style: upper-alpha; 575 | } 576 | 577 | ol.lowerroman { 578 | list-style: lower-roman; 579 | } 580 | 581 | ol.upperroman { 582 | list-style: upper-roman; 583 | } 584 | 585 | :not(li) > ol > li:first-child > :first-child, 586 | :not(li) > ul > li:first-child > :first-child { 587 | margin-top: 0px; 588 | } 589 | 590 | :not(li) > ol > li:last-child > :last-child, 591 | :not(li) > ul > li:last-child > :last-child { 592 | margin-bottom: 0px; 593 | } 594 | 595 | ol.simple ol p, 596 | ol.simple ul p, 597 | ul.simple ol p, 598 | ul.simple ul p { 599 | margin-top: 0; 600 | } 601 | 602 | ol.simple > li:not(:first-child) > p, 603 | ul.simple > li:not(:first-child) > p { 604 | margin-top: 0; 605 | } 606 | 607 | ol.simple p, 608 | ul.simple p { 609 | margin-bottom: 0; 610 | } 611 | 612 | aside.footnote > span, 613 | div.citation > span { 614 | float: left; 615 | } 616 | aside.footnote > span:last-of-type, 617 | div.citation > span:last-of-type { 618 | padding-right: 0.5em; 619 | } 620 | aside.footnote > p { 621 | margin-left: 2em; 622 | } 623 | div.citation > p { 624 | margin-left: 4em; 625 | } 626 | aside.footnote > p:last-of-type, 627 | div.citation > p:last-of-type { 628 | margin-bottom: 0em; 629 | } 630 | aside.footnote > p:last-of-type:after, 631 | div.citation > p:last-of-type:after { 632 | content: ""; 633 | clear: both; 634 | } 635 | 636 | dl.field-list { 637 | display: grid; 638 | grid-template-columns: fit-content(30%) auto; 639 | } 640 | 641 | dl.field-list > dt { 642 | font-weight: bold; 643 | word-break: break-word; 644 | padding-left: 0.5em; 645 | padding-right: 5px; 646 | } 647 | 648 | dl.field-list > dd { 649 | padding-left: 0.5em; 650 | margin-top: 0em; 651 | margin-left: 0em; 652 | margin-bottom: 0em; 653 | } 654 | 655 | dl { 656 | margin-bottom: 15px; 657 | } 658 | 659 | dd > :first-child { 660 | margin-top: 0px; 661 | } 662 | 663 | dd ul, dd table { 664 | margin-bottom: 10px; 665 | } 666 | 667 | dd { 668 | margin-top: 3px; 669 | margin-bottom: 10px; 670 | margin-left: 30px; 671 | } 672 | 673 | dl > dd:last-child, 674 | dl > dd:last-child > :last-child { 675 | margin-bottom: 0; 676 | } 677 | 678 | dt:target, span.highlighted { 679 | background-color: #fbe54e; 680 | } 681 | 682 | rect.highlighted { 683 | fill: #fbe54e; 684 | } 685 | 686 | dl.glossary dt { 687 | font-weight: bold; 688 | font-size: 1.1em; 689 | } 690 | 691 | .versionmodified { 692 | font-style: italic; 693 | } 694 | 695 | .system-message { 696 | background-color: #fda; 697 | padding: 5px; 698 | border: 3px solid red; 699 | } 700 | 701 | .footnote:target { 702 | background-color: #ffa; 703 | } 704 | 705 | .line-block { 706 | display: block; 707 | margin-top: 1em; 708 | margin-bottom: 1em; 709 | } 710 | 711 | .line-block .line-block { 712 | margin-top: 0; 713 | margin-bottom: 0; 714 | margin-left: 1.5em; 715 | } 716 | 717 | .guilabel, .menuselection { 718 | font-family: sans-serif; 719 | } 720 | 721 | .accelerator { 722 | text-decoration: underline; 723 | } 724 | 725 | .classifier { 726 | font-style: oblique; 727 | } 728 | 729 | .classifier:before { 730 | font-style: normal; 731 | margin: 0 0.5em; 732 | content: ":"; 733 | display: inline-block; 734 | } 735 | 736 | abbr, acronym { 737 | border-bottom: dotted 1px; 738 | cursor: help; 739 | } 740 | 741 | /* -- code displays --------------------------------------------------------- */ 742 | 743 | pre { 744 | overflow: auto; 745 | overflow-y: hidden; /* fixes display issues on Chrome browsers */ 746 | } 747 | 748 | pre, div[class*="highlight-"] { 749 | clear: both; 750 | } 751 | 752 | span.pre { 753 | -moz-hyphens: none; 754 | -ms-hyphens: none; 755 | -webkit-hyphens: none; 756 | hyphens: none; 757 | white-space: nowrap; 758 | } 759 | 760 | div[class*="highlight-"] { 761 | margin: 1em 0; 762 | } 763 | 764 | td.linenos pre { 765 | border: 0; 766 | background-color: transparent; 767 | color: #aaa; 768 | } 769 | 770 | table.highlighttable { 771 | display: block; 772 | } 773 | 774 | table.highlighttable tbody { 775 | display: block; 776 | } 777 | 778 | table.highlighttable tr { 779 | display: flex; 780 | } 781 | 782 | table.highlighttable td { 783 | margin: 0; 784 | padding: 0; 785 | } 786 | 787 | table.highlighttable td.linenos { 788 | padding-right: 0.5em; 789 | } 790 | 791 | table.highlighttable td.code { 792 | flex: 1; 793 | overflow: hidden; 794 | } 795 | 796 | .highlight .hll { 797 | display: block; 798 | } 799 | 800 | div.highlight pre, 801 | table.highlighttable pre { 802 | margin: 0; 803 | } 804 | 805 | div.code-block-caption + div { 806 | margin-top: 0; 807 | } 808 | 809 | div.code-block-caption { 810 | margin-top: 1em; 811 | padding: 2px 5px; 812 | font-size: small; 813 | } 814 | 815 | div.code-block-caption code { 816 | background-color: transparent; 817 | } 818 | 819 | table.highlighttable td.linenos, 820 | span.linenos, 821 | div.highlight span.gp { /* gp: Generic.Prompt */ 822 | user-select: none; 823 | -webkit-user-select: text; /* Safari fallback only */ 824 | -webkit-user-select: none; /* Chrome/Safari */ 825 | -moz-user-select: none; /* Firefox */ 826 | -ms-user-select: none; /* IE10+ */ 827 | } 828 | 829 | div.code-block-caption span.caption-number { 830 | padding: 0.1em 0.3em; 831 | font-style: italic; 832 | } 833 | 834 | div.code-block-caption span.caption-text { 835 | } 836 | 837 | div.literal-block-wrapper { 838 | margin: 1em 0; 839 | } 840 | 841 | code.xref, a code { 842 | background-color: transparent; 843 | font-weight: bold; 844 | } 845 | 846 | h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { 847 | background-color: transparent; 848 | } 849 | 850 | .viewcode-link { 851 | float: right; 852 | } 853 | 854 | .viewcode-back { 855 | float: right; 856 | font-family: sans-serif; 857 | } 858 | 859 | div.viewcode-block:target { 860 | margin: -1px -10px; 861 | padding: 0 10px; 862 | } 863 | 864 | /* -- math display ---------------------------------------------------------- */ 865 | 866 | img.math { 867 | vertical-align: middle; 868 | } 869 | 870 | div.body div.math p { 871 | text-align: center; 872 | } 873 | 874 | span.eqno { 875 | float: right; 876 | } 877 | 878 | span.eqno a.headerlink { 879 | position: absolute; 880 | z-index: 1; 881 | } 882 | 883 | div.math:hover a.headerlink { 884 | visibility: visible; 885 | } 886 | 887 | /* -- printout stylesheet --------------------------------------------------- */ 888 | 889 | @media print { 890 | div.document, 891 | div.documentwrapper, 892 | div.bodywrapper { 893 | margin: 0 !important; 894 | width: 100%; 895 | } 896 | 897 | div.sphinxsidebar, 898 | div.related, 899 | div.footer, 900 | #top-link { 901 | display: none; 902 | } 903 | } -------------------------------------------------------------------------------- /docs/build/html/_static/css/badge_only.css: -------------------------------------------------------------------------------- 1 | .clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-style:normal;font-weight:400;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#FontAwesome) format("svg")}.fa:before{font-family:FontAwesome;font-style:normal;font-weight:400;line-height:1}.fa:before,a .fa{text-decoration:inherit}.fa:before,a .fa,li .fa{display:inline-block}li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-.8em}ul.fas li .fa{width:.8em}ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before,.icon-book:before{content:"\f02d"}.fa-caret-down:before,.icon-caret-down:before{content:"\f0d7"}.fa-caret-up:before,.icon-caret-up:before{content:"\f0d8"}.fa-caret-left:before,.icon-caret-left:before{content:"\f0d9"}.fa-caret-right:before,.icon-caret-right:before{content:"\f0da"}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60}.rst-versions .rst-current-version:after{clear:both;content:"";display:block}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}} -------------------------------------------------------------------------------- /docs/build/html/_static/css/fonts/Roboto-Slab-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxyu/nerfvis/4bf2e9dfdc07b9d9648ebfe698c82f04200d1ff0/docs/build/html/_static/css/fonts/Roboto-Slab-Bold.woff -------------------------------------------------------------------------------- /docs/build/html/_static/css/fonts/Roboto-Slab-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxyu/nerfvis/4bf2e9dfdc07b9d9648ebfe698c82f04200d1ff0/docs/build/html/_static/css/fonts/Roboto-Slab-Bold.woff2 -------------------------------------------------------------------------------- /docs/build/html/_static/css/fonts/Roboto-Slab-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxyu/nerfvis/4bf2e9dfdc07b9d9648ebfe698c82f04200d1ff0/docs/build/html/_static/css/fonts/Roboto-Slab-Regular.woff -------------------------------------------------------------------------------- /docs/build/html/_static/css/fonts/Roboto-Slab-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxyu/nerfvis/4bf2e9dfdc07b9d9648ebfe698c82f04200d1ff0/docs/build/html/_static/css/fonts/Roboto-Slab-Regular.woff2 -------------------------------------------------------------------------------- /docs/build/html/_static/css/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxyu/nerfvis/4bf2e9dfdc07b9d9648ebfe698c82f04200d1ff0/docs/build/html/_static/css/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /docs/build/html/_static/css/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxyu/nerfvis/4bf2e9dfdc07b9d9648ebfe698c82f04200d1ff0/docs/build/html/_static/css/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /docs/build/html/_static/css/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxyu/nerfvis/4bf2e9dfdc07b9d9648ebfe698c82f04200d1ff0/docs/build/html/_static/css/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /docs/build/html/_static/css/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxyu/nerfvis/4bf2e9dfdc07b9d9648ebfe698c82f04200d1ff0/docs/build/html/_static/css/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /docs/build/html/_static/css/fonts/lato-bold-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxyu/nerfvis/4bf2e9dfdc07b9d9648ebfe698c82f04200d1ff0/docs/build/html/_static/css/fonts/lato-bold-italic.woff -------------------------------------------------------------------------------- /docs/build/html/_static/css/fonts/lato-bold-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxyu/nerfvis/4bf2e9dfdc07b9d9648ebfe698c82f04200d1ff0/docs/build/html/_static/css/fonts/lato-bold-italic.woff2 -------------------------------------------------------------------------------- /docs/build/html/_static/css/fonts/lato-bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxyu/nerfvis/4bf2e9dfdc07b9d9648ebfe698c82f04200d1ff0/docs/build/html/_static/css/fonts/lato-bold.woff -------------------------------------------------------------------------------- /docs/build/html/_static/css/fonts/lato-bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxyu/nerfvis/4bf2e9dfdc07b9d9648ebfe698c82f04200d1ff0/docs/build/html/_static/css/fonts/lato-bold.woff2 -------------------------------------------------------------------------------- /docs/build/html/_static/css/fonts/lato-normal-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxyu/nerfvis/4bf2e9dfdc07b9d9648ebfe698c82f04200d1ff0/docs/build/html/_static/css/fonts/lato-normal-italic.woff -------------------------------------------------------------------------------- /docs/build/html/_static/css/fonts/lato-normal-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxyu/nerfvis/4bf2e9dfdc07b9d9648ebfe698c82f04200d1ff0/docs/build/html/_static/css/fonts/lato-normal-italic.woff2 -------------------------------------------------------------------------------- /docs/build/html/_static/css/fonts/lato-normal.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxyu/nerfvis/4bf2e9dfdc07b9d9648ebfe698c82f04200d1ff0/docs/build/html/_static/css/fonts/lato-normal.woff -------------------------------------------------------------------------------- /docs/build/html/_static/css/fonts/lato-normal.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxyu/nerfvis/4bf2e9dfdc07b9d9648ebfe698c82f04200d1ff0/docs/build/html/_static/css/fonts/lato-normal.woff2 -------------------------------------------------------------------------------- /docs/build/html/_static/doctools.js: -------------------------------------------------------------------------------- 1 | /* 2 | * doctools.js 3 | * ~~~~~~~~~~~ 4 | * 5 | * Base JavaScript utilities for all Sphinx HTML documentation. 6 | * 7 | * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. 8 | * :license: BSD, see LICENSE for details. 9 | * 10 | */ 11 | "use strict"; 12 | 13 | const BLACKLISTED_KEY_CONTROL_ELEMENTS = new Set([ 14 | "TEXTAREA", 15 | "INPUT", 16 | "SELECT", 17 | "BUTTON", 18 | ]); 19 | 20 | const _ready = (callback) => { 21 | if (document.readyState !== "loading") { 22 | callback(); 23 | } else { 24 | document.addEventListener("DOMContentLoaded", callback); 25 | } 26 | }; 27 | 28 | /** 29 | * Small JavaScript module for the documentation. 30 | */ 31 | const Documentation = { 32 | init: () => { 33 | Documentation.initDomainIndexTable(); 34 | Documentation.initOnKeyListeners(); 35 | }, 36 | 37 | /** 38 | * i18n support 39 | */ 40 | TRANSLATIONS: {}, 41 | PLURAL_EXPR: (n) => (n === 1 ? 0 : 1), 42 | LOCALE: "unknown", 43 | 44 | // gettext and ngettext don't access this so that the functions 45 | // can safely bound to a different name (_ = Documentation.gettext) 46 | gettext: (string) => { 47 | const translated = Documentation.TRANSLATIONS[string]; 48 | switch (typeof translated) { 49 | case "undefined": 50 | return string; // no translation 51 | case "string": 52 | return translated; // translation exists 53 | default: 54 | return translated[0]; // (singular, plural) translation tuple exists 55 | } 56 | }, 57 | 58 | ngettext: (singular, plural, n) => { 59 | const translated = Documentation.TRANSLATIONS[singular]; 60 | if (typeof translated !== "undefined") 61 | return translated[Documentation.PLURAL_EXPR(n)]; 62 | return n === 1 ? singular : plural; 63 | }, 64 | 65 | addTranslations: (catalog) => { 66 | Object.assign(Documentation.TRANSLATIONS, catalog.messages); 67 | Documentation.PLURAL_EXPR = new Function( 68 | "n", 69 | `return (${catalog.plural_expr})` 70 | ); 71 | Documentation.LOCALE = catalog.locale; 72 | }, 73 | 74 | /** 75 | * helper function to focus on search bar 76 | */ 77 | focusSearchBar: () => { 78 | document.querySelectorAll("input[name=q]")[0]?.focus(); 79 | }, 80 | 81 | /** 82 | * Initialise the domain index toggle buttons 83 | */ 84 | initDomainIndexTable: () => { 85 | const toggler = (el) => { 86 | const idNumber = el.id.substr(7); 87 | const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`); 88 | if (el.src.substr(-9) === "minus.png") { 89 | el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`; 90 | toggledRows.forEach((el) => (el.style.display = "none")); 91 | } else { 92 | el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`; 93 | toggledRows.forEach((el) => (el.style.display = "")); 94 | } 95 | }; 96 | 97 | const togglerElements = document.querySelectorAll("img.toggler"); 98 | togglerElements.forEach((el) => 99 | el.addEventListener("click", (event) => toggler(event.currentTarget)) 100 | ); 101 | togglerElements.forEach((el) => (el.style.display = "")); 102 | if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler); 103 | }, 104 | 105 | initOnKeyListeners: () => { 106 | // only install a listener if it is really needed 107 | if ( 108 | !DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS && 109 | !DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS 110 | ) 111 | return; 112 | 113 | document.addEventListener("keydown", (event) => { 114 | // bail for input elements 115 | if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; 116 | // bail with special keys 117 | if (event.altKey || event.ctrlKey || event.metaKey) return; 118 | 119 | if (!event.shiftKey) { 120 | switch (event.key) { 121 | case "ArrowLeft": 122 | if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; 123 | 124 | const prevLink = document.querySelector('link[rel="prev"]'); 125 | if (prevLink && prevLink.href) { 126 | window.location.href = prevLink.href; 127 | event.preventDefault(); 128 | } 129 | break; 130 | case "ArrowRight": 131 | if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; 132 | 133 | const nextLink = document.querySelector('link[rel="next"]'); 134 | if (nextLink && nextLink.href) { 135 | window.location.href = nextLink.href; 136 | event.preventDefault(); 137 | } 138 | break; 139 | } 140 | } 141 | 142 | // some keyboard layouts may need Shift to get / 143 | switch (event.key) { 144 | case "/": 145 | if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break; 146 | Documentation.focusSearchBar(); 147 | event.preventDefault(); 148 | } 149 | }); 150 | }, 151 | }; 152 | 153 | // quick alias for translations 154 | const _ = Documentation.gettext; 155 | 156 | _ready(Documentation.init); 157 | -------------------------------------------------------------------------------- /docs/build/html/_static/documentation_options.js: -------------------------------------------------------------------------------- 1 | var DOCUMENTATION_OPTIONS = { 2 | URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'), 3 | VERSION: '0.1.10', 4 | LANGUAGE: 'en', 5 | COLLAPSE_INDEX: false, 6 | BUILDER: 'html', 7 | FILE_SUFFIX: '.html', 8 | LINK_SUFFIX: '.html', 9 | HAS_SOURCE: true, 10 | SOURCELINK_SUFFIX: '.txt', 11 | NAVIGATION_WITH_KEYS: false, 12 | SHOW_SEARCH_SUMMARY: true, 13 | ENABLE_SEARCH_SHORTCUTS: true, 14 | }; -------------------------------------------------------------------------------- /docs/build/html/_static/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxyu/nerfvis/4bf2e9dfdc07b9d9648ebfe698c82f04200d1ff0/docs/build/html/_static/file.png -------------------------------------------------------------------------------- /docs/build/html/_static/js/badge_only.js: -------------------------------------------------------------------------------- 1 | !function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=4)}({4:function(e,t,r){}}); -------------------------------------------------------------------------------- /docs/build/html/_static/js/html5shiv-printshiv.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @preserve HTML5 Shiv 3.7.3-pre | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed 3 | */ 4 | !function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=y.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=y.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),y.elements=c+" "+a,j(b)}function f(a){var b=x[a[v]];return b||(b={},w++,a[v]=w,x[w]=b),b}function g(a,c,d){if(c||(c=b),q)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():u.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||t.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),q)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return y.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(y,b.frag)}function j(a){a||(a=b);var d=f(a);return!y.shivCSS||p||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),q||i(a,d),a}function k(a){for(var b,c=a.getElementsByTagName("*"),e=c.length,f=RegExp("^(?:"+d().join("|")+")$","i"),g=[];e--;)b=c[e],f.test(b.nodeName)&&g.push(b.applyElement(l(b)));return g}function l(a){for(var b,c=a.attributes,d=c.length,e=a.ownerDocument.createElement(A+":"+a.nodeName);d--;)b=c[d],b.specified&&e.setAttribute(b.nodeName,b.nodeValue);return e.style.cssText=a.style.cssText,e}function m(a){for(var b,c=a.split("{"),e=c.length,f=RegExp("(^|[\\s,>+~])("+d().join("|")+")(?=[[\\s,>+~#.:]|$)","gi"),g="$1"+A+"\\:$2";e--;)b=c[e]=c[e].split("}"),b[b.length-1]=b[b.length-1].replace(f,g),c[e]=b.join("}");return c.join("{")}function n(a){for(var b=a.length;b--;)a[b].removeNode()}function o(a){function b(){clearTimeout(g._removeSheetTimer),d&&d.removeNode(!0),d=null}var d,e,g=f(a),h=a.namespaces,i=a.parentWindow;return!B||a.printShived?a:("undefined"==typeof h[A]&&h.add(A),i.attachEvent("onbeforeprint",function(){b();for(var f,g,h,i=a.styleSheets,j=[],l=i.length,n=Array(l);l--;)n[l]=i[l];for(;h=n.pop();)if(!h.disabled&&z.test(h.media)){try{f=h.imports,g=f.length}catch(o){g=0}for(l=0;g>l;l++)n.push(f[l]);try{j.push(h.cssText)}catch(o){}}j=m(j.reverse().join("")),e=k(a),d=c(a,j)}),i.attachEvent("onafterprint",function(){n(e),clearTimeout(g._removeSheetTimer),g._removeSheetTimer=setTimeout(b,500)}),a.printShived=!0,a)}var p,q,r="3.7.3",s=a.html5||{},t=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,u=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,v="_html5shiv",w=0,x={};!function(){try{var a=b.createElement("a");a.innerHTML="",p="hidden"in a,q=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){p=!0,q=!0}}();var y={elements:s.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:r,shivCSS:s.shivCSS!==!1,supportsUnknownElements:q,shivMethods:s.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=y,j(b);var z=/^$|\b(?:all|print)\b/,A="html5shiv",B=!q&&function(){var c=b.documentElement;return!("undefined"==typeof b.namespaces||"undefined"==typeof b.parentWindow||"undefined"==typeof c.applyElement||"undefined"==typeof c.removeNode||"undefined"==typeof a.attachEvent)}();y.type+=" print",y.shivPrint=o,o(b),"object"==typeof module&&module.exports&&(module.exports=y)}("undefined"!=typeof window?window:this,document); -------------------------------------------------------------------------------- /docs/build/html/_static/js/html5shiv.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @preserve HTML5 Shiv 3.7.3 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed 3 | */ 4 | !function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=t.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=t.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),t.elements=c+" "+a,j(b)}function f(a){var b=s[a[q]];return b||(b={},r++,a[q]=r,s[r]=b),b}function g(a,c,d){if(c||(c=b),l)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():p.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||o.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),l)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return t.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(t,b.frag)}function j(a){a||(a=b);var d=f(a);return!t.shivCSS||k||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),l||i(a,d),a}var k,l,m="3.7.3-pre",n=a.html5||{},o=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,q="_html5shiv",r=0,s={};!function(){try{var a=b.createElement("a");a.innerHTML="",k="hidden"in a,l=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){k=!0,l=!0}}();var t={elements:n.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:m,shivCSS:n.shivCSS!==!1,supportsUnknownElements:l,shivMethods:n.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=t,j(b),"object"==typeof module&&module.exports&&(module.exports=t)}("undefined"!=typeof window?window:this,document); -------------------------------------------------------------------------------- /docs/build/html/_static/js/theme.js: -------------------------------------------------------------------------------- 1 | !function(n){var e={};function t(i){if(e[i])return e[i].exports;var o=e[i]={i:i,l:!1,exports:{}};return n[i].call(o.exports,o,o.exports,t),o.l=!0,o.exports}t.m=n,t.c=e,t.d=function(n,e,i){t.o(n,e)||Object.defineProperty(n,e,{enumerable:!0,get:i})},t.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},t.t=function(n,e){if(1&e&&(n=t(n)),8&e)return n;if(4&e&&"object"==typeof n&&n&&n.__esModule)return n;var i=Object.create(null);if(t.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:n}),2&e&&"string"!=typeof n)for(var o in n)t.d(i,o,function(e){return n[e]}.bind(null,o));return i},t.n=function(n){var e=n&&n.__esModule?function(){return n.default}:function(){return n};return t.d(e,"a",e),e},t.o=function(n,e){return Object.prototype.hasOwnProperty.call(n,e)},t.p="",t(t.s=0)}([function(n,e,t){t(1),n.exports=t(3)},function(n,e,t){(function(){var e="undefined"!=typeof window?window.jQuery:t(2);n.exports.ThemeNav={navBar:null,win:null,winScroll:!1,winResize:!1,linkScroll:!1,winPosition:0,winHeight:null,docHeight:null,isRunning:!1,enable:function(n){var t=this;void 0===n&&(n=!0),t.isRunning||(t.isRunning=!0,e((function(e){t.init(e),t.reset(),t.win.on("hashchange",t.reset),n&&t.win.on("scroll",(function(){t.linkScroll||t.winScroll||(t.winScroll=!0,requestAnimationFrame((function(){t.onScroll()})))})),t.win.on("resize",(function(){t.winResize||(t.winResize=!0,requestAnimationFrame((function(){t.onResize()})))})),t.onResize()})))},enableSticky:function(){this.enable(!0)},init:function(n){n(document);var e=this;this.navBar=n("div.wy-side-scroll:first"),this.win=n(window),n(document).on("click","[data-toggle='wy-nav-top']",(function(){n("[data-toggle='wy-nav-shift']").toggleClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift")})).on("click",".wy-menu-vertical .current ul li a",(function(){var t=n(this);n("[data-toggle='wy-nav-shift']").removeClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift"),e.toggleCurrent(t),e.hashChange()})).on("click","[data-toggle='rst-current-version']",(function(){n("[data-toggle='rst-versions']").toggleClass("shift-up")})),n("table.docutils:not(.field-list,.footnote,.citation)").wrap("
"),n("table.docutils.footnote").wrap("
"),n("table.docutils.citation").wrap("
"),n(".wy-menu-vertical ul").not(".simple").siblings("a").each((function(){var t=n(this);expand=n(''),expand.on("click",(function(n){return e.toggleCurrent(t),n.stopPropagation(),!1})),t.prepend(expand)}))},reset:function(){var n=encodeURI(window.location.hash)||"#";try{var e=$(".wy-menu-vertical"),t=e.find('[href="'+n+'"]');if(0===t.length){var i=$('.document [id="'+n.substring(1)+'"]').closest("div.section");0===(t=e.find('[href="#'+i.attr("id")+'"]')).length&&(t=e.find('[href="#"]'))}if(t.length>0){$(".wy-menu-vertical .current").removeClass("current").attr("aria-expanded","false"),t.addClass("current").attr("aria-expanded","true"),t.closest("li.toctree-l1").parent().addClass("current").attr("aria-expanded","true");for(let n=1;n<=10;n++)t.closest("li.toctree-l"+n).addClass("current").attr("aria-expanded","true");t[0].scrollIntoView()}}catch(n){console.log("Error expanding nav for anchor",n)}},onScroll:function(){this.winScroll=!1;var n=this.win.scrollTop(),e=n+this.winHeight,t=this.navBar.scrollTop()+(n-this.winPosition);n<0||e>this.docHeight||(this.navBar.scrollTop(t),this.winPosition=n)},onResize:function(){this.winResize=!1,this.winHeight=this.win.height(),this.docHeight=$(document).height()},hashChange:function(){this.linkScroll=!0,this.win.one("hashchange",(function(){this.linkScroll=!1}))},toggleCurrent:function(n){var e=n.closest("li");e.siblings("li.current").removeClass("current").attr("aria-expanded","false"),e.siblings().find("li.current").removeClass("current").attr("aria-expanded","false");var t=e.find("> ul li");t.length&&(t.removeClass("current").attr("aria-expanded","false"),e.toggleClass("current").attr("aria-expanded",(function(n,e){return"true"==e?"false":"true"})))}},"undefined"!=typeof window&&(window.SphinxRtdTheme={Navigation:n.exports.ThemeNav,StickyNav:n.exports.ThemeNav}),function(){for(var n=0,e=["ms","moz","webkit","o"],t=0;t0 63 | var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 64 | var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 65 | var s_v = "^(" + C + ")?" + v; // vowel in stem 66 | 67 | this.stemWord = function (w) { 68 | var stem; 69 | var suffix; 70 | var firstch; 71 | var origword = w; 72 | 73 | if (w.length < 3) 74 | return w; 75 | 76 | var re; 77 | var re2; 78 | var re3; 79 | var re4; 80 | 81 | firstch = w.substr(0,1); 82 | if (firstch == "y") 83 | w = firstch.toUpperCase() + w.substr(1); 84 | 85 | // Step 1a 86 | re = /^(.+?)(ss|i)es$/; 87 | re2 = /^(.+?)([^s])s$/; 88 | 89 | if (re.test(w)) 90 | w = w.replace(re,"$1$2"); 91 | else if (re2.test(w)) 92 | w = w.replace(re2,"$1$2"); 93 | 94 | // Step 1b 95 | re = /^(.+?)eed$/; 96 | re2 = /^(.+?)(ed|ing)$/; 97 | if (re.test(w)) { 98 | var fp = re.exec(w); 99 | re = new RegExp(mgr0); 100 | if (re.test(fp[1])) { 101 | re = /.$/; 102 | w = w.replace(re,""); 103 | } 104 | } 105 | else if (re2.test(w)) { 106 | var fp = re2.exec(w); 107 | stem = fp[1]; 108 | re2 = new RegExp(s_v); 109 | if (re2.test(stem)) { 110 | w = stem; 111 | re2 = /(at|bl|iz)$/; 112 | re3 = new RegExp("([^aeiouylsz])\\1$"); 113 | re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); 114 | if (re2.test(w)) 115 | w = w + "e"; 116 | else if (re3.test(w)) { 117 | re = /.$/; 118 | w = w.replace(re,""); 119 | } 120 | else if (re4.test(w)) 121 | w = w + "e"; 122 | } 123 | } 124 | 125 | // Step 1c 126 | re = /^(.+?)y$/; 127 | if (re.test(w)) { 128 | var fp = re.exec(w); 129 | stem = fp[1]; 130 | re = new RegExp(s_v); 131 | if (re.test(stem)) 132 | w = stem + "i"; 133 | } 134 | 135 | // Step 2 136 | re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; 137 | if (re.test(w)) { 138 | var fp = re.exec(w); 139 | stem = fp[1]; 140 | suffix = fp[2]; 141 | re = new RegExp(mgr0); 142 | if (re.test(stem)) 143 | w = stem + step2list[suffix]; 144 | } 145 | 146 | // Step 3 147 | re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; 148 | if (re.test(w)) { 149 | var fp = re.exec(w); 150 | stem = fp[1]; 151 | suffix = fp[2]; 152 | re = new RegExp(mgr0); 153 | if (re.test(stem)) 154 | w = stem + step3list[suffix]; 155 | } 156 | 157 | // Step 4 158 | re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; 159 | re2 = /^(.+?)(s|t)(ion)$/; 160 | if (re.test(w)) { 161 | var fp = re.exec(w); 162 | stem = fp[1]; 163 | re = new RegExp(mgr1); 164 | if (re.test(stem)) 165 | w = stem; 166 | } 167 | else if (re2.test(w)) { 168 | var fp = re2.exec(w); 169 | stem = fp[1] + fp[2]; 170 | re2 = new RegExp(mgr1); 171 | if (re2.test(stem)) 172 | w = stem; 173 | } 174 | 175 | // Step 5 176 | re = /^(.+?)e$/; 177 | if (re.test(w)) { 178 | var fp = re.exec(w); 179 | stem = fp[1]; 180 | re = new RegExp(mgr1); 181 | re2 = new RegExp(meq1); 182 | re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); 183 | if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) 184 | w = stem; 185 | } 186 | re = /ll$/; 187 | re2 = new RegExp(mgr1); 188 | if (re.test(w) && re2.test(w)) { 189 | re = /.$/; 190 | w = w.replace(re,""); 191 | } 192 | 193 | // and turn initial Y back to y 194 | if (firstch == "y") 195 | w = firstch.toLowerCase() + w.substr(1); 196 | return w; 197 | } 198 | } 199 | 200 | -------------------------------------------------------------------------------- /docs/build/html/_static/minus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxyu/nerfvis/4bf2e9dfdc07b9d9648ebfe698c82f04200d1ff0/docs/build/html/_static/minus.png -------------------------------------------------------------------------------- /docs/build/html/_static/plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxyu/nerfvis/4bf2e9dfdc07b9d9648ebfe698c82f04200d1ff0/docs/build/html/_static/plus.png -------------------------------------------------------------------------------- /docs/build/html/_static/pygments.css: -------------------------------------------------------------------------------- 1 | pre { line-height: 125%; } 2 | td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } 3 | span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } 4 | td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } 5 | span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } 6 | .highlight .hll { background-color: #ffffcc } 7 | .highlight { background: #f8f8f8; } 8 | .highlight .c { color: #3D7B7B; font-style: italic } /* Comment */ 9 | .highlight .err { border: 1px solid #FF0000 } /* Error */ 10 | .highlight .k { color: #008000; font-weight: bold } /* Keyword */ 11 | .highlight .o { color: #666666 } /* Operator */ 12 | .highlight .ch { color: #3D7B7B; font-style: italic } /* Comment.Hashbang */ 13 | .highlight .cm { color: #3D7B7B; font-style: italic } /* Comment.Multiline */ 14 | .highlight .cp { color: #9C6500 } /* Comment.Preproc */ 15 | .highlight .cpf { color: #3D7B7B; font-style: italic } /* Comment.PreprocFile */ 16 | .highlight .c1 { color: #3D7B7B; font-style: italic } /* Comment.Single */ 17 | .highlight .cs { color: #3D7B7B; font-style: italic } /* Comment.Special */ 18 | .highlight .gd { color: #A00000 } /* Generic.Deleted */ 19 | .highlight .ge { font-style: italic } /* Generic.Emph */ 20 | .highlight .gr { color: #E40000 } /* Generic.Error */ 21 | .highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ 22 | .highlight .gi { color: #008400 } /* Generic.Inserted */ 23 | .highlight .go { color: #717171 } /* Generic.Output */ 24 | .highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ 25 | .highlight .gs { font-weight: bold } /* Generic.Strong */ 26 | .highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ 27 | .highlight .gt { color: #0044DD } /* Generic.Traceback */ 28 | .highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */ 29 | .highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ 30 | .highlight .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */ 31 | .highlight .kp { color: #008000 } /* Keyword.Pseudo */ 32 | .highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ 33 | .highlight .kt { color: #B00040 } /* Keyword.Type */ 34 | .highlight .m { color: #666666 } /* Literal.Number */ 35 | .highlight .s { color: #BA2121 } /* Literal.String */ 36 | .highlight .na { color: #687822 } /* Name.Attribute */ 37 | .highlight .nb { color: #008000 } /* Name.Builtin */ 38 | .highlight .nc { color: #0000FF; font-weight: bold } /* Name.Class */ 39 | .highlight .no { color: #880000 } /* Name.Constant */ 40 | .highlight .nd { color: #AA22FF } /* Name.Decorator */ 41 | .highlight .ni { color: #717171; font-weight: bold } /* Name.Entity */ 42 | .highlight .ne { color: #CB3F38; font-weight: bold } /* Name.Exception */ 43 | .highlight .nf { color: #0000FF } /* Name.Function */ 44 | .highlight .nl { color: #767600 } /* Name.Label */ 45 | .highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ 46 | .highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */ 47 | .highlight .nv { color: #19177C } /* Name.Variable */ 48 | .highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ 49 | .highlight .w { color: #bbbbbb } /* Text.Whitespace */ 50 | .highlight .mb { color: #666666 } /* Literal.Number.Bin */ 51 | .highlight .mf { color: #666666 } /* Literal.Number.Float */ 52 | .highlight .mh { color: #666666 } /* Literal.Number.Hex */ 53 | .highlight .mi { color: #666666 } /* Literal.Number.Integer */ 54 | .highlight .mo { color: #666666 } /* Literal.Number.Oct */ 55 | .highlight .sa { color: #BA2121 } /* Literal.String.Affix */ 56 | .highlight .sb { color: #BA2121 } /* Literal.String.Backtick */ 57 | .highlight .sc { color: #BA2121 } /* Literal.String.Char */ 58 | .highlight .dl { color: #BA2121 } /* Literal.String.Delimiter */ 59 | .highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */ 60 | .highlight .s2 { color: #BA2121 } /* Literal.String.Double */ 61 | .highlight .se { color: #AA5D1F; font-weight: bold } /* Literal.String.Escape */ 62 | .highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */ 63 | .highlight .si { color: #A45A77; font-weight: bold } /* Literal.String.Interpol */ 64 | .highlight .sx { color: #008000 } /* Literal.String.Other */ 65 | .highlight .sr { color: #A45A77 } /* Literal.String.Regex */ 66 | .highlight .s1 { color: #BA2121 } /* Literal.String.Single */ 67 | .highlight .ss { color: #19177C } /* Literal.String.Symbol */ 68 | .highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */ 69 | .highlight .fm { color: #0000FF } /* Name.Function.Magic */ 70 | .highlight .vc { color: #19177C } /* Name.Variable.Class */ 71 | .highlight .vg { color: #19177C } /* Name.Variable.Global */ 72 | .highlight .vi { color: #19177C } /* Name.Variable.Instance */ 73 | .highlight .vm { color: #19177C } /* Name.Variable.Magic */ 74 | .highlight .il { color: #666666 } /* Literal.Number.Integer.Long */ -------------------------------------------------------------------------------- /docs/build/html/_static/searchtools.js: -------------------------------------------------------------------------------- 1 | /* 2 | * searchtools.js 3 | * ~~~~~~~~~~~~~~~~ 4 | * 5 | * Sphinx JavaScript utilities for the full-text search. 6 | * 7 | * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. 8 | * :license: BSD, see LICENSE for details. 9 | * 10 | */ 11 | "use strict"; 12 | 13 | /** 14 | * Simple result scoring code. 15 | */ 16 | if (typeof Scorer === "undefined") { 17 | var Scorer = { 18 | // Implement the following function to further tweak the score for each result 19 | // The function takes a result array [docname, title, anchor, descr, score, filename] 20 | // and returns the new score. 21 | /* 22 | score: result => { 23 | const [docname, title, anchor, descr, score, filename] = result 24 | return score 25 | }, 26 | */ 27 | 28 | // query matches the full name of an object 29 | objNameMatch: 11, 30 | // or matches in the last dotted part of the object name 31 | objPartialMatch: 6, 32 | // Additive scores depending on the priority of the object 33 | objPrio: { 34 | 0: 15, // used to be importantResults 35 | 1: 5, // used to be objectResults 36 | 2: -5, // used to be unimportantResults 37 | }, 38 | // Used when the priority is not in the mapping. 39 | objPrioDefault: 0, 40 | 41 | // query found in title 42 | title: 15, 43 | partialTitle: 7, 44 | // query found in terms 45 | term: 5, 46 | partialTerm: 2, 47 | }; 48 | } 49 | 50 | const _removeChildren = (element) => { 51 | while (element && element.lastChild) element.removeChild(element.lastChild); 52 | }; 53 | 54 | /** 55 | * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping 56 | */ 57 | const _escapeRegExp = (string) => 58 | string.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string 59 | 60 | const _displayItem = (item, searchTerms) => { 61 | const docBuilder = DOCUMENTATION_OPTIONS.BUILDER; 62 | const docUrlRoot = DOCUMENTATION_OPTIONS.URL_ROOT; 63 | const docFileSuffix = DOCUMENTATION_OPTIONS.FILE_SUFFIX; 64 | const docLinkSuffix = DOCUMENTATION_OPTIONS.LINK_SUFFIX; 65 | const showSearchSummary = DOCUMENTATION_OPTIONS.SHOW_SEARCH_SUMMARY; 66 | 67 | const [docName, title, anchor, descr, score, _filename] = item; 68 | 69 | let listItem = document.createElement("li"); 70 | let requestUrl; 71 | let linkUrl; 72 | if (docBuilder === "dirhtml") { 73 | // dirhtml builder 74 | let dirname = docName + "/"; 75 | if (dirname.match(/\/index\/$/)) 76 | dirname = dirname.substring(0, dirname.length - 6); 77 | else if (dirname === "index/") dirname = ""; 78 | requestUrl = docUrlRoot + dirname; 79 | linkUrl = requestUrl; 80 | } else { 81 | // normal html builders 82 | requestUrl = docUrlRoot + docName + docFileSuffix; 83 | linkUrl = docName + docLinkSuffix; 84 | } 85 | let linkEl = listItem.appendChild(document.createElement("a")); 86 | linkEl.href = linkUrl + anchor; 87 | linkEl.dataset.score = score; 88 | linkEl.innerHTML = title; 89 | if (descr) 90 | listItem.appendChild(document.createElement("span")).innerHTML = 91 | " (" + descr + ")"; 92 | else if (showSearchSummary) 93 | fetch(requestUrl) 94 | .then((responseData) => responseData.text()) 95 | .then((data) => { 96 | if (data) 97 | listItem.appendChild( 98 | Search.makeSearchSummary(data, searchTerms) 99 | ); 100 | }); 101 | Search.output.appendChild(listItem); 102 | }; 103 | const _finishSearch = (resultCount) => { 104 | Search.stopPulse(); 105 | Search.title.innerText = _("Search Results"); 106 | if (!resultCount) 107 | Search.status.innerText = Documentation.gettext( 108 | "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories." 109 | ); 110 | else 111 | Search.status.innerText = _( 112 | `Search finished, found ${resultCount} page(s) matching the search query.` 113 | ); 114 | }; 115 | const _displayNextItem = ( 116 | results, 117 | resultCount, 118 | searchTerms 119 | ) => { 120 | // results left, load the summary and display it 121 | // this is intended to be dynamic (don't sub resultsCount) 122 | if (results.length) { 123 | _displayItem(results.pop(), searchTerms); 124 | setTimeout( 125 | () => _displayNextItem(results, resultCount, searchTerms), 126 | 5 127 | ); 128 | } 129 | // search finished, update title and status message 130 | else _finishSearch(resultCount); 131 | }; 132 | 133 | /** 134 | * Default splitQuery function. Can be overridden in ``sphinx.search`` with a 135 | * custom function per language. 136 | * 137 | * The regular expression works by splitting the string on consecutive characters 138 | * that are not Unicode letters, numbers, underscores, or emoji characters. 139 | * This is the same as ``\W+`` in Python, preserving the surrogate pair area. 140 | */ 141 | if (typeof splitQuery === "undefined") { 142 | var splitQuery = (query) => query 143 | .split(/[^\p{Letter}\p{Number}_\p{Emoji_Presentation}]+/gu) 144 | .filter(term => term) // remove remaining empty strings 145 | } 146 | 147 | /** 148 | * Search Module 149 | */ 150 | const Search = { 151 | _index: null, 152 | _queued_query: null, 153 | _pulse_status: -1, 154 | 155 | htmlToText: (htmlString) => { 156 | const htmlElement = new DOMParser().parseFromString(htmlString, 'text/html'); 157 | htmlElement.querySelectorAll(".headerlink").forEach((el) => { el.remove() }); 158 | const docContent = htmlElement.querySelector('[role="main"]'); 159 | if (docContent !== undefined) return docContent.textContent; 160 | console.warn( 161 | "Content block not found. Sphinx search tries to obtain it via '[role=main]'. Could you check your theme or template." 162 | ); 163 | return ""; 164 | }, 165 | 166 | init: () => { 167 | const query = new URLSearchParams(window.location.search).get("q"); 168 | document 169 | .querySelectorAll('input[name="q"]') 170 | .forEach((el) => (el.value = query)); 171 | if (query) Search.performSearch(query); 172 | }, 173 | 174 | loadIndex: (url) => 175 | (document.body.appendChild(document.createElement("script")).src = url), 176 | 177 | setIndex: (index) => { 178 | Search._index = index; 179 | if (Search._queued_query !== null) { 180 | const query = Search._queued_query; 181 | Search._queued_query = null; 182 | Search.query(query); 183 | } 184 | }, 185 | 186 | hasIndex: () => Search._index !== null, 187 | 188 | deferQuery: (query) => (Search._queued_query = query), 189 | 190 | stopPulse: () => (Search._pulse_status = -1), 191 | 192 | startPulse: () => { 193 | if (Search._pulse_status >= 0) return; 194 | 195 | const pulse = () => { 196 | Search._pulse_status = (Search._pulse_status + 1) % 4; 197 | Search.dots.innerText = ".".repeat(Search._pulse_status); 198 | if (Search._pulse_status >= 0) window.setTimeout(pulse, 500); 199 | }; 200 | pulse(); 201 | }, 202 | 203 | /** 204 | * perform a search for something (or wait until index is loaded) 205 | */ 206 | performSearch: (query) => { 207 | // create the required interface elements 208 | const searchText = document.createElement("h2"); 209 | searchText.textContent = _("Searching"); 210 | const searchSummary = document.createElement("p"); 211 | searchSummary.classList.add("search-summary"); 212 | searchSummary.innerText = ""; 213 | const searchList = document.createElement("ul"); 214 | searchList.classList.add("search"); 215 | 216 | const out = document.getElementById("search-results"); 217 | Search.title = out.appendChild(searchText); 218 | Search.dots = Search.title.appendChild(document.createElement("span")); 219 | Search.status = out.appendChild(searchSummary); 220 | Search.output = out.appendChild(searchList); 221 | 222 | const searchProgress = document.getElementById("search-progress"); 223 | // Some themes don't use the search progress node 224 | if (searchProgress) { 225 | searchProgress.innerText = _("Preparing search..."); 226 | } 227 | Search.startPulse(); 228 | 229 | // index already loaded, the browser was quick! 230 | if (Search.hasIndex()) Search.query(query); 231 | else Search.deferQuery(query); 232 | }, 233 | 234 | /** 235 | * execute search (requires search index to be loaded) 236 | */ 237 | query: (query) => { 238 | const filenames = Search._index.filenames; 239 | const docNames = Search._index.docnames; 240 | const titles = Search._index.titles; 241 | const allTitles = Search._index.alltitles; 242 | const indexEntries = Search._index.indexentries; 243 | 244 | // stem the search terms and add them to the correct list 245 | const stemmer = new Stemmer(); 246 | const searchTerms = new Set(); 247 | const excludedTerms = new Set(); 248 | const highlightTerms = new Set(); 249 | const objectTerms = new Set(splitQuery(query.toLowerCase().trim())); 250 | splitQuery(query.trim()).forEach((queryTerm) => { 251 | const queryTermLower = queryTerm.toLowerCase(); 252 | 253 | // maybe skip this "word" 254 | // stopwords array is from language_data.js 255 | if ( 256 | stopwords.indexOf(queryTermLower) !== -1 || 257 | queryTerm.match(/^\d+$/) 258 | ) 259 | return; 260 | 261 | // stem the word 262 | let word = stemmer.stemWord(queryTermLower); 263 | // select the correct list 264 | if (word[0] === "-") excludedTerms.add(word.substr(1)); 265 | else { 266 | searchTerms.add(word); 267 | highlightTerms.add(queryTermLower); 268 | } 269 | }); 270 | 271 | if (SPHINX_HIGHLIGHT_ENABLED) { // set in sphinx_highlight.js 272 | localStorage.setItem("sphinx_highlight_terms", [...highlightTerms].join(" ")) 273 | } 274 | 275 | // console.debug("SEARCH: searching for:"); 276 | // console.info("required: ", [...searchTerms]); 277 | // console.info("excluded: ", [...excludedTerms]); 278 | 279 | // array of [docname, title, anchor, descr, score, filename] 280 | let results = []; 281 | _removeChildren(document.getElementById("search-progress")); 282 | 283 | const queryLower = query.toLowerCase(); 284 | for (const [title, foundTitles] of Object.entries(allTitles)) { 285 | if (title.toLowerCase().includes(queryLower) && (queryLower.length >= title.length/2)) { 286 | for (const [file, id] of foundTitles) { 287 | let score = Math.round(100 * queryLower.length / title.length) 288 | results.push([ 289 | docNames[file], 290 | titles[file] !== title ? `${titles[file]} > ${title}` : title, 291 | id !== null ? "#" + id : "", 292 | null, 293 | score, 294 | filenames[file], 295 | ]); 296 | } 297 | } 298 | } 299 | 300 | // search for explicit entries in index directives 301 | for (const [entry, foundEntries] of Object.entries(indexEntries)) { 302 | if (entry.includes(queryLower) && (queryLower.length >= entry.length/2)) { 303 | for (const [file, id] of foundEntries) { 304 | let score = Math.round(100 * queryLower.length / entry.length) 305 | results.push([ 306 | docNames[file], 307 | titles[file], 308 | id ? "#" + id : "", 309 | null, 310 | score, 311 | filenames[file], 312 | ]); 313 | } 314 | } 315 | } 316 | 317 | // lookup as object 318 | objectTerms.forEach((term) => 319 | results.push(...Search.performObjectSearch(term, objectTerms)) 320 | ); 321 | 322 | // lookup as search terms in fulltext 323 | results.push(...Search.performTermsSearch(searchTerms, excludedTerms)); 324 | 325 | // let the scorer override scores with a custom scoring function 326 | if (Scorer.score) results.forEach((item) => (item[4] = Scorer.score(item))); 327 | 328 | // now sort the results by score (in opposite order of appearance, since the 329 | // display function below uses pop() to retrieve items) and then 330 | // alphabetically 331 | results.sort((a, b) => { 332 | const leftScore = a[4]; 333 | const rightScore = b[4]; 334 | if (leftScore === rightScore) { 335 | // same score: sort alphabetically 336 | const leftTitle = a[1].toLowerCase(); 337 | const rightTitle = b[1].toLowerCase(); 338 | if (leftTitle === rightTitle) return 0; 339 | return leftTitle > rightTitle ? -1 : 1; // inverted is intentional 340 | } 341 | return leftScore > rightScore ? 1 : -1; 342 | }); 343 | 344 | // remove duplicate search results 345 | // note the reversing of results, so that in the case of duplicates, the highest-scoring entry is kept 346 | let seen = new Set(); 347 | results = results.reverse().reduce((acc, result) => { 348 | let resultStr = result.slice(0, 4).concat([result[5]]).map(v => String(v)).join(','); 349 | if (!seen.has(resultStr)) { 350 | acc.push(result); 351 | seen.add(resultStr); 352 | } 353 | return acc; 354 | }, []); 355 | 356 | results = results.reverse(); 357 | 358 | // for debugging 359 | //Search.lastresults = results.slice(); // a copy 360 | // console.info("search results:", Search.lastresults); 361 | 362 | // print the results 363 | _displayNextItem(results, results.length, searchTerms); 364 | }, 365 | 366 | /** 367 | * search for object names 368 | */ 369 | performObjectSearch: (object, objectTerms) => { 370 | const filenames = Search._index.filenames; 371 | const docNames = Search._index.docnames; 372 | const objects = Search._index.objects; 373 | const objNames = Search._index.objnames; 374 | const titles = Search._index.titles; 375 | 376 | const results = []; 377 | 378 | const objectSearchCallback = (prefix, match) => { 379 | const name = match[4] 380 | const fullname = (prefix ? prefix + "." : "") + name; 381 | const fullnameLower = fullname.toLowerCase(); 382 | if (fullnameLower.indexOf(object) < 0) return; 383 | 384 | let score = 0; 385 | const parts = fullnameLower.split("."); 386 | 387 | // check for different match types: exact matches of full name or 388 | // "last name" (i.e. last dotted part) 389 | if (fullnameLower === object || parts.slice(-1)[0] === object) 390 | score += Scorer.objNameMatch; 391 | else if (parts.slice(-1)[0].indexOf(object) > -1) 392 | score += Scorer.objPartialMatch; // matches in last name 393 | 394 | const objName = objNames[match[1]][2]; 395 | const title = titles[match[0]]; 396 | 397 | // If more than one term searched for, we require other words to be 398 | // found in the name/title/description 399 | const otherTerms = new Set(objectTerms); 400 | otherTerms.delete(object); 401 | if (otherTerms.size > 0) { 402 | const haystack = `${prefix} ${name} ${objName} ${title}`.toLowerCase(); 403 | if ( 404 | [...otherTerms].some((otherTerm) => haystack.indexOf(otherTerm) < 0) 405 | ) 406 | return; 407 | } 408 | 409 | let anchor = match[3]; 410 | if (anchor === "") anchor = fullname; 411 | else if (anchor === "-") anchor = objNames[match[1]][1] + "-" + fullname; 412 | 413 | const descr = objName + _(", in ") + title; 414 | 415 | // add custom score for some objects according to scorer 416 | if (Scorer.objPrio.hasOwnProperty(match[2])) 417 | score += Scorer.objPrio[match[2]]; 418 | else score += Scorer.objPrioDefault; 419 | 420 | results.push([ 421 | docNames[match[0]], 422 | fullname, 423 | "#" + anchor, 424 | descr, 425 | score, 426 | filenames[match[0]], 427 | ]); 428 | }; 429 | Object.keys(objects).forEach((prefix) => 430 | objects[prefix].forEach((array) => 431 | objectSearchCallback(prefix, array) 432 | ) 433 | ); 434 | return results; 435 | }, 436 | 437 | /** 438 | * search for full-text terms in the index 439 | */ 440 | performTermsSearch: (searchTerms, excludedTerms) => { 441 | // prepare search 442 | const terms = Search._index.terms; 443 | const titleTerms = Search._index.titleterms; 444 | const filenames = Search._index.filenames; 445 | const docNames = Search._index.docnames; 446 | const titles = Search._index.titles; 447 | 448 | const scoreMap = new Map(); 449 | const fileMap = new Map(); 450 | 451 | // perform the search on the required terms 452 | searchTerms.forEach((word) => { 453 | const files = []; 454 | const arr = [ 455 | { files: terms[word], score: Scorer.term }, 456 | { files: titleTerms[word], score: Scorer.title }, 457 | ]; 458 | // add support for partial matches 459 | if (word.length > 2) { 460 | const escapedWord = _escapeRegExp(word); 461 | Object.keys(terms).forEach((term) => { 462 | if (term.match(escapedWord) && !terms[word]) 463 | arr.push({ files: terms[term], score: Scorer.partialTerm }); 464 | }); 465 | Object.keys(titleTerms).forEach((term) => { 466 | if (term.match(escapedWord) && !titleTerms[word]) 467 | arr.push({ files: titleTerms[word], score: Scorer.partialTitle }); 468 | }); 469 | } 470 | 471 | // no match but word was a required one 472 | if (arr.every((record) => record.files === undefined)) return; 473 | 474 | // found search word in contents 475 | arr.forEach((record) => { 476 | if (record.files === undefined) return; 477 | 478 | let recordFiles = record.files; 479 | if (recordFiles.length === undefined) recordFiles = [recordFiles]; 480 | files.push(...recordFiles); 481 | 482 | // set score for the word in each file 483 | recordFiles.forEach((file) => { 484 | if (!scoreMap.has(file)) scoreMap.set(file, {}); 485 | scoreMap.get(file)[word] = record.score; 486 | }); 487 | }); 488 | 489 | // create the mapping 490 | files.forEach((file) => { 491 | if (fileMap.has(file) && fileMap.get(file).indexOf(word) === -1) 492 | fileMap.get(file).push(word); 493 | else fileMap.set(file, [word]); 494 | }); 495 | }); 496 | 497 | // now check if the files don't contain excluded terms 498 | const results = []; 499 | for (const [file, wordList] of fileMap) { 500 | // check if all requirements are matched 501 | 502 | // as search terms with length < 3 are discarded 503 | const filteredTermCount = [...searchTerms].filter( 504 | (term) => term.length > 2 505 | ).length; 506 | if ( 507 | wordList.length !== searchTerms.size && 508 | wordList.length !== filteredTermCount 509 | ) 510 | continue; 511 | 512 | // ensure that none of the excluded terms is in the search result 513 | if ( 514 | [...excludedTerms].some( 515 | (term) => 516 | terms[term] === file || 517 | titleTerms[term] === file || 518 | (terms[term] || []).includes(file) || 519 | (titleTerms[term] || []).includes(file) 520 | ) 521 | ) 522 | break; 523 | 524 | // select one (max) score for the file. 525 | const score = Math.max(...wordList.map((w) => scoreMap.get(file)[w])); 526 | // add result to the result list 527 | results.push([ 528 | docNames[file], 529 | titles[file], 530 | "", 531 | null, 532 | score, 533 | filenames[file], 534 | ]); 535 | } 536 | return results; 537 | }, 538 | 539 | /** 540 | * helper function to return a node containing the 541 | * search summary for a given text. keywords is a list 542 | * of stemmed words. 543 | */ 544 | makeSearchSummary: (htmlText, keywords) => { 545 | const text = Search.htmlToText(htmlText); 546 | if (text === "") return null; 547 | 548 | const textLower = text.toLowerCase(); 549 | const actualStartPosition = [...keywords] 550 | .map((k) => textLower.indexOf(k.toLowerCase())) 551 | .filter((i) => i > -1) 552 | .slice(-1)[0]; 553 | const startWithContext = Math.max(actualStartPosition - 120, 0); 554 | 555 | const top = startWithContext === 0 ? "" : "..."; 556 | const tail = startWithContext + 240 < text.length ? "..." : ""; 557 | 558 | let summary = document.createElement("p"); 559 | summary.classList.add("context"); 560 | summary.textContent = top + text.substr(startWithContext, 240).trim() + tail; 561 | 562 | return summary; 563 | }, 564 | }; 565 | 566 | _ready(Search.init); 567 | -------------------------------------------------------------------------------- /docs/build/html/_static/underscore.js: -------------------------------------------------------------------------------- 1 | !function(n,r){"object"==typeof exports&&"undefined"!=typeof module?module.exports=r():"function"==typeof define&&define.amd?define("underscore",r):(n="undefined"!=typeof globalThis?globalThis:n||self,function(){var t=n._,e=n._=r();e.noConflict=function(){return n._=t,e}}())}(this,(function(){ 2 | // Underscore.js 1.13.1 3 | // https://underscorejs.org 4 | // (c) 2009-2021 Jeremy Ashkenas, Julian Gonggrijp, and DocumentCloud and Investigative Reporters & Editors 5 | // Underscore may be freely distributed under the MIT license. 6 | var n="1.13.1",r="object"==typeof self&&self.self===self&&self||"object"==typeof global&&global.global===global&&global||Function("return this")()||{},t=Array.prototype,e=Object.prototype,u="undefined"!=typeof Symbol?Symbol.prototype:null,o=t.push,i=t.slice,a=e.toString,f=e.hasOwnProperty,c="undefined"!=typeof ArrayBuffer,l="undefined"!=typeof DataView,s=Array.isArray,p=Object.keys,v=Object.create,h=c&&ArrayBuffer.isView,y=isNaN,d=isFinite,g=!{toString:null}.propertyIsEnumerable("toString"),b=["valueOf","isPrototypeOf","toString","propertyIsEnumerable","hasOwnProperty","toLocaleString"],m=Math.pow(2,53)-1;function j(n,r){return r=null==r?n.length-1:+r,function(){for(var t=Math.max(arguments.length-r,0),e=Array(t),u=0;u=0&&t<=m}}function J(n){return function(r){return null==r?void 0:r[n]}}var G=J("byteLength"),H=K(G),Q=/\[object ((I|Ui)nt(8|16|32)|Float(32|64)|Uint8Clamped|Big(I|Ui)nt64)Array\]/;var X=c?function(n){return h?h(n)&&!q(n):H(n)&&Q.test(a.call(n))}:C(!1),Y=J("length");function Z(n,r){r=function(n){for(var r={},t=n.length,e=0;e":">",'"':""","'":"'","`":"`"},Cn=Ln($n),Kn=Ln(_n($n)),Jn=tn.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g},Gn=/(.)^/,Hn={"'":"'","\\":"\\","\r":"r","\n":"n","\u2028":"u2028","\u2029":"u2029"},Qn=/\\|'|\r|\n|\u2028|\u2029/g;function Xn(n){return"\\"+Hn[n]}var Yn=/^\s*(\w|\$)+\s*$/;var Zn=0;function nr(n,r,t,e,u){if(!(e instanceof r))return n.apply(t,u);var o=Mn(n.prototype),i=n.apply(o,u);return _(i)?i:o}var rr=j((function(n,r){var t=rr.placeholder,e=function(){for(var u=0,o=r.length,i=Array(o),a=0;a1)ur(a,r-1,t,e),u=e.length;else for(var f=0,c=a.length;f0&&(t=r.apply(this,arguments)),n<=1&&(r=null),t}}var lr=rr(cr,2);function sr(n,r,t){r=qn(r,t);for(var e,u=nn(n),o=0,i=u.length;o0?0:u-1;o>=0&&o0?a=o>=0?o:Math.max(o+f,a):f=o>=0?Math.min(o+1,f):o+f+1;else if(t&&o&&f)return e[o=t(e,u)]===u?o:-1;if(u!=u)return(o=r(i.call(e,a,f),$))>=0?o+a:-1;for(o=n>0?a:f-1;o>=0&&o0?0:i-1;for(u||(e=r[o?o[a]:a],a+=n);a>=0&&a=3;return r(n,Fn(t,u,4),e,o)}}var Ar=wr(1),xr=wr(-1);function Sr(n,r,t){var e=[];return r=qn(r,t),jr(n,(function(n,t,u){r(n,t,u)&&e.push(n)})),e}function Or(n,r,t){r=qn(r,t);for(var e=!er(n)&&nn(n),u=(e||n).length,o=0;o=0}var Br=j((function(n,r,t){var e,u;return D(r)?u=r:(r=Nn(r),e=r.slice(0,-1),r=r[r.length-1]),_r(n,(function(n){var o=u;if(!o){if(e&&e.length&&(n=In(n,e)),null==n)return;o=n[r]}return null==o?o:o.apply(n,t)}))}));function Nr(n,r){return _r(n,Rn(r))}function Ir(n,r,t){var e,u,o=-1/0,i=-1/0;if(null==r||"number"==typeof r&&"object"!=typeof n[0]&&null!=n)for(var a=0,f=(n=er(n)?n:jn(n)).length;ao&&(o=e);else r=qn(r,t),jr(n,(function(n,t,e){((u=r(n,t,e))>i||u===-1/0&&o===-1/0)&&(o=n,i=u)}));return o}function Tr(n,r,t){if(null==r||t)return er(n)||(n=jn(n)),n[Wn(n.length-1)];var e=er(n)?En(n):jn(n),u=Y(e);r=Math.max(Math.min(r,u),0);for(var o=u-1,i=0;i1&&(e=Fn(e,r[1])),r=an(n)):(e=qr,r=ur(r,!1,!1),n=Object(n));for(var u=0,o=r.length;u1&&(t=r[1])):(r=_r(ur(r,!1,!1),String),e=function(n,t){return!Er(r,t)}),Ur(n,e,t)}));function zr(n,r,t){return i.call(n,0,Math.max(0,n.length-(null==r||t?1:r)))}function Lr(n,r,t){return null==n||n.length<1?null==r||t?void 0:[]:null==r||t?n[0]:zr(n,n.length-r)}function $r(n,r,t){return i.call(n,null==r||t?1:r)}var Cr=j((function(n,r){return r=ur(r,!0,!0),Sr(n,(function(n){return!Er(r,n)}))})),Kr=j((function(n,r){return Cr(n,r)}));function Jr(n,r,t,e){A(r)||(e=t,t=r,r=!1),null!=t&&(t=qn(t,e));for(var u=[],o=[],i=0,a=Y(n);ir?(e&&(clearTimeout(e),e=null),a=c,i=n.apply(u,o),e||(u=o=null)):e||!1===t.trailing||(e=setTimeout(f,l)),i};return c.cancel=function(){clearTimeout(e),a=0,e=u=o=null},c},debounce:function(n,r,t){var e,u,o,i,a,f=function(){var c=zn()-u;r>c?e=setTimeout(f,r-c):(e=null,t||(i=n.apply(a,o)),e||(o=a=null))},c=j((function(c){return a=this,o=c,u=zn(),e||(e=setTimeout(f,r),t&&(i=n.apply(a,o))),i}));return c.cancel=function(){clearTimeout(e),e=o=a=null},c},wrap:function(n,r){return rr(r,n)},negate:fr,compose:function(){var n=arguments,r=n.length-1;return function(){for(var t=r,e=n[r].apply(this,arguments);t--;)e=n[t].call(this,e);return e}},after:function(n,r){return function(){if(--n<1)return r.apply(this,arguments)}},before:cr,once:lr,findKey:sr,findIndex:vr,findLastIndex:hr,sortedIndex:yr,indexOf:gr,lastIndexOf:br,find:mr,detect:mr,findWhere:function(n,r){return mr(n,Dn(r))},each:jr,forEach:jr,map:_r,collect:_r,reduce:Ar,foldl:Ar,inject:Ar,reduceRight:xr,foldr:xr,filter:Sr,select:Sr,reject:function(n,r,t){return Sr(n,fr(qn(r)),t)},every:Or,all:Or,some:Mr,any:Mr,contains:Er,includes:Er,include:Er,invoke:Br,pluck:Nr,where:function(n,r){return Sr(n,Dn(r))},max:Ir,min:function(n,r,t){var e,u,o=1/0,i=1/0;if(null==r||"number"==typeof r&&"object"!=typeof n[0]&&null!=n)for(var a=0,f=(n=er(n)?n:jn(n)).length;ae||void 0===t)return 1;if(t 2 | 3 | 4 | 5 | 6 | Index — nerfvis 0.1.10 documentation 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 93 | 94 |
98 | 99 |
100 |
101 |
102 |
    103 |
  • 104 | 105 |
  • 106 |
  • 107 |
108 |
109 |
110 |
111 |
112 | 113 | 114 |

Index

115 | 116 |
117 | A 118 | | C 119 | | D 120 | | E 121 | | R 122 | | S 123 | | W 124 | 125 |
126 |

A

127 | 128 | 146 | 164 |
165 | 166 |

C

167 | 168 | 172 |
173 | 174 |

D

175 | 176 | 180 |
181 | 182 |

E

183 | 184 | 188 | 192 |
193 | 194 |

R

195 | 196 | 200 | 204 |
205 | 206 |

S

207 | 208 | 220 | 232 |
233 | 234 |

W

235 | 236 | 240 |
241 | 242 | 243 | 244 |
245 |
246 |
247 | 248 |
249 | 250 |
251 |

© Copyright 2021, Alex Yu.

252 |
253 | 254 | Built with Sphinx using a 255 | theme 256 | provided by Read the Docs. 257 | 258 | 259 |
260 |
261 |
262 |
263 |
264 | 269 | 270 | 271 | -------------------------------------------------------------------------------- /docs/build/html/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Welcome to nerfvis’s documentation! — nerfvis 0.1.10 documentation 8 | 9 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 95 | 96 |
100 | 101 |
102 |
103 |
104 |
    105 |
  • 106 | 107 |
  • 108 | View page source 109 |
  • 110 |
111 |
112 |
113 |
114 |
115 | 116 |
117 |

Welcome to nerfvis’s documentation!

118 |

This is a PyTorch NeRF visualization utility based on PlenOctrees, as well as a 119 | general web-based 3D visualization library.

120 |

Install with pip install nerfvis. This is a pure-Python library. The base library has no dependencies except for numpy. 121 | Adding meshes from a file requires trimesh. 122 | Adding NeRF requires torch, svox, tqdm, scipy.

123 |

Basic usage example:

124 |
import nerfvis
125 | scene = nerfvis.Scene("My title")
126 | scene.add_cube("Cube/1", color=[1.0, 0.0, 0.0], translation=[-1.0, -1.0, 0.0])
127 | scene.add_wireframe_cube("Cube/2", color=[1.0, 0.0, 0.0],
128 |                          translation=[-1.0, -1.0, 0.0], scale=2.0)
129 | scene.add_axes()
130 | scene.set_nerf(nerf_func, center=[0.0, 0.0, 0.0], radius=1.5, use_dirs=True) # Optional
131 | scene.display()  # Serves at localhost:8888 or first port available after that
132 | scene.export("folder") # Exports sources to location
133 | scene.embed() # Embed in IPython notebook
134 | 
135 |
136 |

Scene: holds objects. If you only use one scene, feel free to use from nerfvis import scene instead.

137 |

Names separated by / will be collapsed into a tree in the layers panel in the output.

138 |

Other common functions include add_camera_frustum, add_mesh, add_line, add_lines, 139 | add_image

140 |

Please see Reference for details.

141 |
142 |

Contents:

143 | 150 |
151 |
152 |
153 |

Viewer controls

154 |
    155 |
  • Left click and drag to orbit

  • 156 |
  • Right click and drag, or CTRL+left click and drag to pan

  • 157 |
  • Mouse wheel, middle click and drag, or ALT+left click and drag to zoom; alternatively use =/SHIFT + =

  • 158 |
  • Number keys 1-6 to change coordinate systems: Z up/down Y up/down X up/down resp.

  • 159 |
160 |
161 | 162 | 163 |
164 |
165 |
168 | 169 |
170 | 171 |
172 |

© Copyright 2021, Alex Yu.

173 |
174 | 175 | Built with Sphinx using a 176 | theme 177 | provided by Read the Docs. 178 | 179 | 180 |
181 |
182 |
183 |
184 |
185 | 190 | 191 | 192 | -------------------------------------------------------------------------------- /docs/build/html/objects.inv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxyu/nerfvis/4bf2e9dfdc07b9d9648ebfe698c82f04200d1ff0/docs/build/html/objects.inv -------------------------------------------------------------------------------- /docs/build/html/search.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Search — nerfvis 0.1.10 documentation 7 | 8 | 9 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 96 | 97 |
101 | 102 |
103 |
104 |
105 |
    106 |
  • 107 | 108 |
  • 109 |
  • 110 |
111 |
112 |
113 |
114 |
115 | 116 | 123 | 124 | 125 |
126 | 127 |
128 | 129 |
130 |
131 |
132 | 133 |
134 | 135 |
136 |

© Copyright 2021, Alex Yu.

137 |
138 | 139 | Built with Sphinx using a 140 | theme 141 | provided by Read the Docs. 142 | 143 | 144 |
145 |
146 |
147 |
148 |
149 | 154 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | -------------------------------------------------------------------------------- /docs/build/html/searchindex.js: -------------------------------------------------------------------------------- 1 | Search.setIndex({"docnames": ["index", "nerfvis"], "filenames": ["index.rst", "nerfvis.rst"], "titles": ["Welcome to nerfvis\u2019s documentation!", "Reference"], "terms": {"thi": [0, 1], "i": [0, 1], "pytorch": [0, 1], "nerf": [0, 1], "visual": [0, 1], "util": 0, "base": [0, 1], "plenoctre": [0, 1], "well": [0, 1], "gener": [0, 1], "web": [0, 1], "3d": [0, 1], "librari": 0, "instal": 0, "pip": 0, "pure": 0, "python": 0, "The": [0, 1], "ha": [0, 1], "depend": [0, 1], "except": 0, "numpi": 0, "ad": [0, 1], "mesh": [0, 1], "from": [0, 1], "file": [0, 1], "requir": [0, 1], "trimesh": [0, 1], "torch": [0, 1], "svox": [0, 1], "tqdm": [0, 1], "scipi": [0, 1], "basic": [0, 1], "usag": 0, "exampl": [0, 1], "import": [0, 1], "scene": 0, "my": 0, "titl": [0, 1], "add_cub": [0, 1], "cube": [0, 1], "1": [0, 1], "color": [0, 1], "0": [0, 1], "translat": [0, 1], "add_wireframe_cub": [0, 1], "2": [0, 1], "scale": [0, 1], "add_ax": [0, 1], "set_nerf": [0, 1], "nerf_func": 0, "center": [0, 1], "radiu": [0, 1], "5": [0, 1], "use_dir": [0, 1], "true": [0, 1], "option": [0, 1], "displai": [0, 1], "serv": [0, 1], "localhost": [0, 1], "8888": [0, 1], "first": [0, 1], "port": [0, 1], "avail": [0, 1], "after": [0, 1], "export": [0, 1], "folder": [0, 1], "sourc": 0, "locat": 0, "emb": [0, 1], "ipython": 0, "notebook": [0, 1], "hold": [0, 1], "object": [0, 1], "If": [0, 1], "you": [0, 1], "onli": [0, 1], "us": [0, 1], "one": [0, 1], "feel": 0, "free": 0, "instead": [0, 1], "name": [0, 1], "separ": 0, "collaps": 0, "tree": [0, 1], "layer": 0, "panel": 0, "output": [0, 1], "other": 0, "common": [0, 1], "function": [0, 1], "includ": 0, "add_camera_frustum": [0, 1], "add_mesh": [0, 1], "add_lin": [0, 1], "add_imag": [0, 1], "pleas": [0, 1], "see": [0, 1], "refer": 0, "detail": 0, "left": 0, "click": 0, "drag": 0, "orbit": [0, 1], "right": [0, 1], "ctrl": 0, "pan": 0, "mous": [0, 1], "wheel": 0, "middl": 0, "alt": 0, "zoom": 0, "altern": 0, "shift": 0, "number": [0, 1], "kei": 0, "6": [0, 1], "chang": [0, 1], "coordin": 0, "system": 0, "z": [0, 1], "up": [0, 1], "down": [0, 1], "y": [0, 1], "x": [0, 1], "resp": 0, "class": 1, "str": 1, "default_opencv": 1, "bool": 1, "fals": 1, "radianc": 1, "field": 1, "volum": 1, "point": 1, "cloud": 1, "line": 1, "add": 1, "add_": 1, "seen": 1, "below": 1, "creat": 1, "standalon": 1, "viewer": 1, "can": 1, "open": 1, "browser": 1, "singl": 1, "quick": 1, "nerfvi": [], "multipl": 1, "paramet": 1, "show": 1, "when": 1, "save": 1, "default": 1, "later": 1, "set": 1, "whether": 1, "opencv": 1, "camera": 1, "space": 1, "opengl": 1, "set_opencv": 1, "set_opengl": 1, "kwarg": 1, "side": 1, "length": 1, "vert": 1, "3": 1, "an": 1, "identifi": 1, "orang": 1, "param": 1, "vert_color": 1, "36": 1, "vertex": 1, "advanc": 1, "model": 1, "rotat": 1, "axi": 1, "angl": 1, "float": 1, "visibl": 1, "should": 1, "init": 1, "get": 1, "version": 1, "unlit": 1, "render": 1, "time": 1, "int": 1, "which": 1, "alwai": 1, "set_world_up": 1, "world_up": 1, "ndarrai": 1, "world": 1, "vector": 1, "affect": 1, "all": 1, "frustum": 1, "imag": 1, "set_opencv_world": 1, "set_opengl_world": 1, "set_blender_world": 1, "blender": 1, "set_titl": 1, "page": 1, "update_bb": 1, "wirefram": 1, "updat": 1, "bound": 1, "box": 1, "sinc": 1, "ar": 1, "usual": 1, "add_arrow": 1, "list": 1, "b": 1, "arrow_s": 1, "12": 1, "arrow": 1, "current": 1, "made": 1, "start": 1, "end": 1, "size": 1, "head": 1, "ring": 1, "sector": 1, "add_spher": 1, "sphere": 1, "none": 1, "uv": 1, "15": 1, "30": 1, "segment": 1, "second": 1, "overrid": 1, "seg": 1, "seri": 1, "now": 1, "n": 1, "indic": 1, "between": 1, "draw": 1, "given": 1, "4": 1, "add_point": 1, "point_siz": 1, "item": 1, "face": 1, "face_s": 1, "7": 1, "8": 1, "etc": 1, "gldrawarrai": 1, "mean": 1, "triangl": 1, "By": 1, "determin": 1, "want": 1, "directli": 1, "without": 1, "light": 1, "r": 1, "t": 1, "focal_length": 1, "1111": 1, "11": 1, "image_s": 1, "512": 1, "jpeg_qual": 1, "80": 1, "textur": 1, "plane": 1, "jpeg": 1, "compress": 1, "run": 1, "befor": 1, "pillow": 1, "path": 1, "c2w": 1, "each": 1, "either": 1, "xyzw": 1, "quaternion": 1, "matrix": 1, "similar": 1, "appli": 1, "alia": 1, "distanc": 1, "max": 1, "NOT": 1, "input": 1, "note": 1, "do": 1, "make": 1, "too": 1, "larg": 1, "memori": 1, "qualiti": 1, "100": 1, "name_prefix": 1, "n_job": 1, "16": 1, "update_view": 1, "with_camera_frustum": 1, "connect": 1, "camera_color": 1, "prefix": 1, "joblib": 1, "parallel": 1, "load": 1, "9": 1, "focal": 1, "tri": 1, "infer": 1, "job": 1, "_frustum": 1, "posit": 1, "origin": 1, "image_width": 1, "image_height": 1, "more": 1, "ideal": 1, "perspect": 1, "unnorm": 1, "width": 1, "sensor": 1, "800": 1, "height": 1, "good": 1, "valu": 1, "els": 1, "ident": 1, "through": 1, "add_mesh_from_fil": 1, "name_suffix": 1, "basenam": 1, "rest": 1, "keyword": 1, "argument": 1, "pass": 1, "ax": 1, "rgb": 1, "xyz": 1, "hardcod": 1, "remov": 1, "overwrit": 1, "same": 1, "type": 1, "remove_al": 1, "clear": 1, "add_volum": 1, "densiti": 1, "density_threshold": 1, "data_format": 1, "rgba": 1, "dx": 1, "dy": 1, "dz": 1, "dimens": 1, "need": 1, "power": 1, "nor": 1, "equal": 1, "channel_s": 1, "data": 1, "last": 1, "dim": 1, "threshold": 1, "ignor": 1, "standard": 1, "format": 1, "string": 1, "sh1": 1, "sh4": 1, "sh9": 1, "sh16": 1, "respect": 1, "add_volume_from_npz": 1, "alreadi": 1, "npz": 1, "checkpoint": 1, "download": 1, "websit": 1, "arg": 1, "deprec": 1, "add_nerf": 1, "eval_fn": 1, "callabl": 1, "tupl": 1, "ani": 1, "reso": 1, "256": 1, "sh_deg": 1, "sh_proj_sample_count": 1, "sh_proj_use_spars": 1, "sigma_thresh": 1, "weight_thresh": 1, "001": 1, "sigma_multipli": 1, "chunk": 1, "720720": 1, "devic": 1, "cuda": 1, "discret": 1, "low": 1, "purpos": 1, "support": 1, "take": 1, "batch": 1, "return": 1, "sigma": 1, "activ": 1, "dir": 1, "must": 1, "have": 1, "sh": 1, "project": 1, "work": 1, "correctli": 1, "try": 1, "half": 1, "edg": 1, "provid": 1, "conveni": 1, "manual": 1, "boundari": 1, "often": 1, "automat": 1, "e": 1, "specifi": 1, "resolut": 1, "assum": 1, "normal": 1, "viewdir": 1, "recov": 1, "degre": 1, "Will": 1, "view": 1, "direct": 1, "expect": 1, "pre": 1, "addit": 1, "descript": 1, "abov": 1, "info": 1, "spec": 1, "sampl": 1, "spars": 1, "via": 1, "least": 1, "squar": 1, "rather": 1, "than": 1, "mont": 1, "carlo": 1, "inner": 1, "product": 1, "simpl": 1, "weight": 1, "fx": 1, "fy": 1, "nerf_scal": 1, "differ": 1, "due": 1, "legaci": 1, "conflict": 1, "write": 1, "drawlist": 1, "volrend": 1, "nerfvis_bas": 1, "branch": 1, "recommend": 1, "date": 1, "experi": 1, "": 1, "easier": 1, "emebd": 1, "dirnam": 1, "cam_cent": 1, "cam_forward": 1, "cam_origin": 1, "instruct": 1, "css": 1, "url": 1, "open_brows": 1, "output_html_nam": 1, "index": 1, "html": 1, "embed_output": 1, "serve_nonblock": 1, "nerfvis_scen": 1, "9a": 1, "za": 1, "z_": 1, "self": 1, "http": 1, "server": 1, "unit": 1, "forward": 1, "slower": 1, "smaller": 1, "javascript": 1, "execut": 1, "inject": 1, "next": 1, "32": 1, "possibl": 1, "base64": 1, "blob": 1, "mai": 1, "caus": 1, "veri": 1, "slowli": 1, "initi": 1, "combin": 1, "anoth": 1, "thread": 1, "continu": 1, "wa": 1, "1100": 1, "600": 1, "call": 1, "insid": 1, "root": 1, "futur": 1, "still": 1, "overtim": 1, "clean": 1, "split_mat4": 1, "mat4": 1, "dict": 1, "tini": 1, "split": 1, "4x4": 1, "3x4": 1, "affin": 1}, "objects": {"nerfvis": [[1, 0, 1, "", "Scene"]], "nerfvis.Scene": [[1, 1, 1, "", "add_arrow"], [1, 1, 1, "", "add_axes"], [1, 1, 1, "", "add_camera_frustum"], [1, 1, 1, "", "add_cube"], [1, 1, 1, "", "add_image"], [1, 1, 1, "", "add_images"], [1, 1, 1, "", "add_line"], [1, 1, 1, "", "add_lines"], [1, 1, 1, "", "add_mesh"], [1, 1, 1, "", "add_mesh_from_file"], [1, 1, 1, "", "add_nerf"], [1, 1, 1, "", "add_points"], [1, 1, 1, "", "add_sphere"], [1, 1, 1, "", "add_volume"], [1, 1, 1, "", "add_volume_from_npz"], [1, 1, 1, "", "add_wireframe_cube"], [1, 1, 1, "", "clear"], [1, 1, 1, "", "display"], [1, 1, 1, "", "embed"], [1, 1, 1, "", "export"], [1, 1, 1, "", "remove"], [1, 1, 1, "", "remove_all"], [1, 1, 1, "", "set_blender_world"], [1, 1, 1, "", "set_nerf"], [1, 1, 1, "", "set_opencv"], [1, 1, 1, "", "set_opencv_world"], [1, 1, 1, "", "set_opengl"], [1, 1, 1, "", "set_opengl_world"], [1, 1, 1, "", "set_title"], [1, 1, 1, "", "set_world_up"], [1, 1, 1, "", "write"]], "nerfvis.utils": [[1, 2, 1, "", "split_mat4"]]}, "objtypes": {"0": "py:class", "1": "py:method", "2": "py:function"}, "objnames": {"0": ["py", "class", "Python class"], "1": ["py", "method", "Python method"], "2": ["py", "function", "Python function"]}, "titleterms": {"welcom": 0, "nerfvi": [0, 1], "": 0, "document": 0, "content": 0, "viewer": 0, "control": 0, "refer": 1, "util": 1, "scene": 1}, "envversion": {"sphinx.domains.c": 2, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 8, "sphinx.domains.index": 1, "sphinx.domains.javascript": 2, "sphinx.domains.math": 2, "sphinx.domains.python": 3, "sphinx.domains.rst": 2, "sphinx.domains.std": 2, "sphinx": 57}, "alltitles": {"Welcome to nerfvis\u2019s documentation!": [[0, "welcome-to-nerfvis-s-documentation"]], "Contents:": [[0, null]], "Viewer controls": [[0, "viewer-controls"]], "Reference": [[1, "reference"]], "nerfvis.scene": [[1, "nerfvis-scene"]], "nerfvis.utils": [[1, "nerfvis-utils"]]}, "indexentries": {"scene (class in nerfvis)": [[1, "nerfvis.Scene"]], "add_arrow() (scene method)": [[1, "nerfvis.Scene.add_arrow"]], "add_axes() (scene method)": [[1, "nerfvis.Scene.add_axes"]], "add_camera_frustum() (scene method)": [[1, "nerfvis.Scene.add_camera_frustum"]], "add_cube() (scene method)": [[1, "nerfvis.Scene.add_cube"]], "add_image() (scene method)": [[1, "nerfvis.Scene.add_image"]], "add_images() (scene method)": [[1, "nerfvis.Scene.add_images"]], "add_line() (scene method)": [[1, "nerfvis.Scene.add_line"]], "add_lines() (scene method)": [[1, "nerfvis.Scene.add_lines"]], "add_mesh() (scene method)": [[1, "nerfvis.Scene.add_mesh"]], "add_mesh_from_file() (scene method)": [[1, "nerfvis.Scene.add_mesh_from_file"]], "add_nerf() (scene method)": [[1, "nerfvis.Scene.add_nerf"]], "add_points() (scene method)": [[1, "nerfvis.Scene.add_points"]], "add_sphere() (scene method)": [[1, "nerfvis.Scene.add_sphere"]], "add_volume() (scene method)": [[1, "nerfvis.Scene.add_volume"]], "add_volume_from_npz() (scene method)": [[1, "nerfvis.Scene.add_volume_from_npz"]], "add_wireframe_cube() (scene method)": [[1, "nerfvis.Scene.add_wireframe_cube"]], "clear() (scene method)": [[1, "nerfvis.Scene.clear"]], "display() (scene method)": [[1, "nerfvis.Scene.display"]], "embed() (scene method)": [[1, "nerfvis.Scene.embed"]], "export() (scene method)": [[1, "nerfvis.Scene.export"]], "remove() (scene method)": [[1, "nerfvis.Scene.remove"]], "remove_all() (scene method)": [[1, "nerfvis.Scene.remove_all"]], "set_blender_world() (scene method)": [[1, "nerfvis.Scene.set_blender_world"]], "set_nerf() (scene method)": [[1, "nerfvis.Scene.set_nerf"]], "set_opencv() (scene method)": [[1, "nerfvis.Scene.set_opencv"]], "set_opencv_world() (scene method)": [[1, "nerfvis.Scene.set_opencv_world"]], "set_opengl() (scene method)": [[1, "nerfvis.Scene.set_opengl"]], "set_opengl_world() (scene method)": [[1, "nerfvis.Scene.set_opengl_world"]], "set_title() (scene method)": [[1, "nerfvis.Scene.set_title"]], "set_world_up() (scene method)": [[1, "nerfvis.Scene.set_world_up"]], "split_mat4() (in module nerfvis.utils)": [[1, "nerfvis.utils.split_mat4"]], "write() (scene method)": [[1, "nerfvis.Scene.write"]]}}) -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=source 11 | set BUILDDIR=build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.https://www.sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | sphinx 2 | sphinx-rtd-theme 3 | sphinxcontrib-fulltoc 4 | recommonmark 5 | nerfvis 6 | numpy 7 | -------------------------------------------------------------------------------- /docs/source/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # This file only contains a selection of the most common options. For a full 4 | # list see the documentation: 5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 6 | 7 | # -- Path setup -------------------------------------------------------------- 8 | 9 | # If extensions (or modules to document with autodoc) are in another directory, 10 | # add these directories to sys.path here. If the directory is relative to the 11 | # documentation root, use os.path.abspath to make it absolute, like shown here. 12 | # 13 | import os 14 | import sys 15 | 16 | import sphinx_rtd_theme 17 | from nerfvis import __version__ 18 | 19 | sys.path.insert(0, os.path.join(os.path.abspath("."), "..")) 20 | 21 | 22 | # -- Project information ----------------------------------------------------- 23 | 24 | project = "nerfvis" 25 | copyright = "2021, Alex Yu" 26 | author = "Alex Yu" 27 | 28 | # The full version, including alpha/beta/rc tags 29 | release = __version__ 30 | 31 | 32 | # -- General configuration --------------------------------------------------- 33 | 34 | # Add any Sphinx extension module names here, as strings. They can be 35 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 36 | # ones. 37 | extensions = [ 38 | "recommonmark", 39 | "sphinx.ext.autodoc", 40 | "sphinx_rtd_theme", 41 | ] 42 | 43 | # Add any paths that contain templates here, relative to this directory. 44 | templates_path = ["_templates"] 45 | 46 | # List of patterns, relative to source directory, that match files and 47 | # directories to ignore when looking for source files. 48 | # This pattern also affects html_static_path and html_extra_path. 49 | exclude_patterns = [] 50 | 51 | 52 | # -- Options for HTML output ------------------------------------------------- 53 | 54 | # The theme to use for HTML and HTML Help pages. See the documentation for 55 | # a list of builtin themes. 56 | # 57 | html_theme = "sphinx_rtd_theme" 58 | 59 | # Add any paths that contain custom static files (such as style sheets) here, 60 | # relative to this directory. They are copied after the builtin static files, 61 | # so a file named "default.css" will overwrite the builtin "default.css". 62 | html_static_path = [] 63 | 64 | add_module_names = False 65 | 66 | 67 | def setup(app): 68 | pass 69 | 70 | 71 | autodoc_member_order = "bysource" 72 | 73 | html_theme_options = { 74 | "collapse_navigation": False, 75 | "sticky_navigation": True, 76 | } 77 | -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | .. nerfvis documentation master file, created by 2 | sphinx-quickstart on Wed Sep 15 10:48:24 2021. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to nerfvis's documentation! 7 | =================================== 8 | 9 | This is a PyTorch NeRF visualization utility based on PlenOctrees, as well as a 10 | general web-based 3D visualization library. 11 | 12 | Install with :code:`pip install nerfvis`. This is a pure-Python library. The base library has no dependencies except for :code:`numpy`. 13 | Adding meshes from a file requires :code:`trimesh`. 14 | Adding NeRF requires :code:`torch, svox, tqdm, scipy`. 15 | 16 | Basic usage example: 17 | 18 | .. code:: python 19 | 20 | import nerfvis 21 | scene = nerfvis.Scene("My title") 22 | scene.add_cube("Cube/1", color=[1.0, 0.0, 0.0], translation=[-1.0, -1.0, 0.0]) 23 | scene.add_wireframe_cube("Cube/2", color=[1.0, 0.0, 0.0], 24 | translation=[-1.0, -1.0, 0.0], scale=2.0) 25 | scene.add_axes() 26 | scene.set_nerf(nerf_func, center=[0.0, 0.0, 0.0], radius=1.5, use_dirs=True) # Optional 27 | scene.display() # Serves at localhost:8888 or first port available after that 28 | scene.export("folder") # Exports sources to location 29 | scene.embed() # Embed in IPython notebook 30 | 31 | Scene: holds objects. If you only use one scene, feel free to use :code:`from nerfvis import scene` instead. 32 | 33 | Names separated by :code:`/` will be collapsed into a tree in the layers panel in the output. 34 | 35 | Other common functions include `add_camera_frustum`, `add_mesh`, `add_line`, `add_lines`, 36 | `add_image` 37 | 38 | Please see :ref:`nerfvis` for details. 39 | 40 | .. toctree:: 41 | :maxdepth: 2 42 | :caption: Contents: 43 | 44 | nerfvis 45 | 46 | 47 | Viewer controls 48 | ==================== 49 | 50 | * Left click and drag to orbit 51 | * Right click and drag, or :code:`CTRL+left` click and drag to pan 52 | * Mouse wheel, middle click and drag, or :code:`ALT+left` click and drag to zoom; alternatively use :code:`=`/:code:`SHIFT` + :code:`=` 53 | * Number keys 1-6 to change coordinate systems: Z up/down Y up/down X up/down resp. 54 | 55 | -------------------------------------------------------------------------------- /docs/source/nerfvis.rst: -------------------------------------------------------------------------------- 1 | .. _nerfvis: 2 | 3 | Reference 4 | ============ 5 | 6 | nerfvis.scene 7 | -------------- 8 | 9 | .. autoclass:: nerfvis.Scene 10 | :members: 11 | :show-inheritance: 12 | 13 | 14 | nerfvis.utils 15 | -------------- 16 | 17 | .. autofunction:: nerfvis.utils.split_mat4 18 | -------------------------------------------------------------------------------- /examples/bulldozer.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxyu/nerfvis/4bf2e9dfdc07b9d9648ebfe698c82f04200d1ff0/examples/bulldozer.jpg -------------------------------------------------------------------------------- /examples/hierarchy.py: -------------------------------------------------------------------------------- 1 | # A basic dumb scene to demonstrate hierarchy in the left side tree view 2 | import numpy as np 3 | 4 | from nerfvis import scene 5 | 6 | scene.add_cube("My cube/0", translation=[2, 0, 0]) 7 | scene.add_cube("My cube/1", color=[1, 0, 0]) 8 | 9 | # Move time slider to see spheres 10 | scene.add_sphere("My sphere/0/0", color=[0, 1, 0], translation=[0, 2, 0], time=1) 11 | scene.add_sphere("My sphere/1", color=[0, 0, 1], translation=[0, 0, 2], time=2) 12 | 13 | # Randomly throw in a volume for varienty 14 | density = 1.0 / ( 15 | np.linalg.norm( 16 | (np.mgrid[:128, :128, :128].transpose(1, 2, 3, 0) - 63.5) / 64.0, 17 | axis=-1, 18 | ) 19 | ) 20 | color = np.full(list(density.shape) + [3], fill_value=0.5, dtype=np.float32) 21 | scene.add_volume("My volume", density, color, scale=2.0, translation=[-2, 0, 0]) 22 | scene.display(port=6006) 23 | -------------------------------------------------------------------------------- /examples/image.py: -------------------------------------------------------------------------------- 1 | # Example to add an image 2 | from nerfvis import scene 3 | import numpy as np 4 | import imageio.v2 as imageio 5 | from scipy.spatial.transform import Rotation 6 | 7 | bulldozer = imageio.imread("bulldozer.jpg") 8 | R = Rotation.from_rotvec(np.pi / 2 * np.array([0, 1, 0])).as_matrix() 9 | t = np.random.randn(3) * 0.1 10 | f = 358 / 800 * 1111 11 | Z = 1.0 12 | scene.set_opencv() 13 | 14 | scene.add_cube("My cube/0", translation=R[:, 2] * 2, color=[1, 0, 0]) 15 | scene.add_camera_frustum( 16 | "My camera/0", 17 | r=R[None], 18 | t=t[None], 19 | focal_length=f, 20 | image_width=bulldozer.shape[1], 21 | image_height=bulldozer.shape[0], 22 | z=Z, 23 | ) 24 | scene.add_image( 25 | "My bulldozer image", bulldozer, r=R, t=t, focal_length=f, z=Z 26 | ) 27 | scene.display(port=6006) 28 | -------------------------------------------------------------------------------- /examples/load_plenoctree_ckpt.py: -------------------------------------------------------------------------------- 1 | # Load and show a plenoctree checkpoint 2 | from nerfvis import scene 3 | 4 | # Download from 5 | # https://drive.google.com/drive/u/1/folders/1vGXEjb3yhbClrZH1vLdl2iKtowfinWOg 6 | 7 | scene.set_title("Lego Bulldozer using nerfvis") 8 | scene.add_volume_from_npz("Lego", "lego.npz") 9 | 10 | scene.display(port=6006) 11 | -------------------------------------------------------------------------------- /examples/nerf_pl/README.md: -------------------------------------------------------------------------------- 1 | # nerf_pl Silica Visualization Example 2 | 3 | ![Screenshot](https://raw.githubusercontent.com/sxyu/nerfvis/master/examples/nerf_pl/img/silica.png) 4 | 5 | This will visualize the silica model provided in the `nerf_pl` repo using `nerfvis`. 6 | 7 | nerf_pl repo link: https://github.com/kwea123/nerf_pl 8 | 9 | - A hardcoded bounding box will be used for the NeRF 10 | - SH projection will be used to visualize the view-dependency (by simply setting `use_dirs=True` with default SH projection parameters) 11 | - Default sigma threshold is used; to use the more advanced weight thresholding, you can try to grab `r, t, focal_length, image_width, image_height` from the dataset and pass them to `set_nerf` 12 | 13 | Setup: download https://github.com/kwea123/nerf_pl to a directory `$ROOT_DIR` and install all the dependencies; or use your existing repo. To grab the silica checkpoints from Github: 14 | 15 | ```sh 16 | ROOT_DIR= 17 | cp visualize.py $ROOT_DIR 18 | mkdir $ROOT_DIR/silica 19 | wget https://github.com/kwea123/nerf_pl/releases/download/v2.0.1/silica.ckpt -P $ROOT_DIR/silica 20 | wget https://github.com/kwea123/nerf_pl/releases/download/v2.0.1/poses_bounds.npy -P $ROOT_DIR/silica 21 | ``` 22 | 23 | **Important**: For SH projection to work correctly, the network's output RGB must not pass through the sigmoid. You can very temporarily fix this by removing the `nn.Sigmoid()` in line 81 of `models/nerf.py`, in the definition of `self.rgb` of the `NeRF` class before running the demo below (note this will break training/eval). 24 | A more permanent fix is by applying the sigmoid using `torch.sigmoid` in `NeRF.forward` instead, which you can control using a Boolean argument to `NeRF.forward`. 25 | 26 | Then you can run the demo as 27 | 28 | ```sh 29 | python $ROOT_DIR/visualize.py 30 | ``` 31 | 32 | If working locally (and port 8889 is not used), this will open the web browser to show the silica NeRF model. 33 | If you're working over SSH, this should launch a server at `localhost:8889`. If you set up the SSH port forwarding, you can go to the forwarded port locally to view it. 34 | 35 | -------------------------------------------------------------------------------- /examples/nerf_pl/img/silica.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxyu/nerfvis/4bf2e9dfdc07b9d9648ebfe698c82f04200d1ff0/examples/nerf_pl/img/silica.png -------------------------------------------------------------------------------- /examples/nerf_pl/visualize.py: -------------------------------------------------------------------------------- 1 | import os 2 | from argparse import ArgumentParser 3 | from collections import defaultdict 4 | 5 | import numpy as np 6 | import torch 7 | from tqdm import tqdm 8 | 9 | import imageio 10 | import nerfvis 11 | from datasets import dataset_dict 12 | from datasets.depth_utils import * 13 | from models.nerf import * 14 | from models.rendering import render_rays 15 | from utils import load_ckpt 16 | 17 | dir_path = os.path.dirname(os.path.realpath(__file__)) 18 | 19 | def get_opts(): 20 | parser = ArgumentParser() 21 | parser.add_argument('--root_dir', type=str, 22 | default=os.path.join(dir_path, 'silica'), 23 | help='root directory of dataset') 24 | parser.add_argument('--dataset_name', type=str, default='llff', 25 | choices=['blender', 'llff'], 26 | help='which dataset to validate') 27 | parser.add_argument('--scene_name', type=str, default='silica', 28 | help='scene name, used as output folder name') 29 | parser.add_argument('--split', type=str, default='test', 30 | help='test or test_train') 31 | parser.add_argument('--img_wh', nargs="+", type=int, default=[504, 378], 32 | help='resolution (img_w, img_h) of the image') 33 | parser.add_argument('--spheric_poses', default=True, action="store_true", 34 | help='whether images are taken in spheric poses (for llff)') 35 | 36 | parser.add_argument('--chunk', type=int, default=32*1024*4, 37 | help='chunk size to split the input to avoid OOM') 38 | 39 | parser.add_argument('--ckpt_path', type=str, default=os.path.join(dir_path, 'silica/silica.ckpt'), 40 | help='pretrained checkpoint path to load') 41 | parser.add_argument('--port', type=int, default=8889, 42 | help='port to run server') 43 | 44 | return parser.parse_args() 45 | 46 | 47 | if __name__ == "__main__": 48 | args = get_opts() 49 | w, h = args.img_wh 50 | 51 | scene = nerfvis.Scene(args.scene_name) 52 | 53 | kwargs = {'root_dir': args.root_dir, 54 | 'split': args.split, 55 | 'img_wh': tuple(args.img_wh)} 56 | if args.dataset_name == 'llff': 57 | kwargs['spheric_poses'] = args.spheric_poses 58 | dataset = dataset_dict[args.dataset_name](**kwargs) 59 | 60 | embedding_xyz = Embedding(3, 10) 61 | embedding_dir = Embedding(3, 4) 62 | 63 | nerf_fine = NeRF() 64 | load_ckpt(nerf_fine, args.ckpt_path, model_name='nerf_fine') 65 | nerf_fine.cuda().eval() 66 | 67 | @torch.no_grad() 68 | def nerf_func(points, dirs): 69 | # points [B, 1, 3] 70 | # dirs [1, sh_proj_sample_count, 3] 71 | xyz_embedded = embedding_xyz(points) 72 | dir_embedded = embedding_dir(dirs) 73 | xyzdir_embedded = torch.cat([xyz_embedded.expand(-1, dirs.size(1), -1), 74 | dir_embedded.expand(points.size(0), -1, -1)], -1) 75 | result = nerf_fine(xyzdir_embedded) 76 | return result[..., :3], result[..., 3:] 77 | 78 | 79 | # This will project to SH. You can change sh_deg to use higher-degree SH 80 | # or increase sh_proj_sample_count to improve the accuracy of the projection 81 | scene.set_nerf(nerf_func, center=[0.0, 0.0, -1.5], radius=0.9, use_dirs=True) 82 | scene.display(port=args.port) 83 | -------------------------------------------------------------------------------- /img/silica_low.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxyu/nerfvis/4bf2e9dfdc07b9d9648ebfe698c82f04200d1ff0/img/silica_low.gif -------------------------------------------------------------------------------- /img/skull.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxyu/nerfvis/4bf2e9dfdc07b9d9648ebfe698c82f04200d1ff0/img/skull.gif -------------------------------------------------------------------------------- /img/youtube_drone.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sxyu/nerfvis/4bf2e9dfdc07b9d9648ebfe698c82f04200d1ff0/img/youtube_drone.gif -------------------------------------------------------------------------------- /nerfvis/__init__.py: -------------------------------------------------------------------------------- 1 | from .scene import Scene 2 | from .version import __version__ 3 | from . import utils 4 | 5 | scene = Scene() 6 | -------------------------------------------------------------------------------- /nerfvis/utils/__init__.py: -------------------------------------------------------------------------------- 1 | from typing import Dict 2 | 3 | import numpy as np 4 | 5 | try: 6 | from ._rotation import Rotation 7 | except: 8 | from scipy.spatial.transform import ( 9 | Rotation, 10 | ) # If cython not available, requires scipy 11 | 12 | 13 | def _expand_bits(v): 14 | v = (v | (v << 16)) & 0x030000FF 15 | v = (v | (v << 8)) & 0x0300F00F 16 | v = (v | (v << 4)) & 0x030C30C3 17 | v = (v | (v << 2)) & 0x09249249 18 | return v 19 | 20 | 21 | def _unexpand_bits(v): 22 | v &= 0x49249249 23 | v = (v | (v >> 2)) & 0xC30C30C3 24 | v = (v | (v >> 4)) & 0xF00F00F 25 | v = (v | (v >> 8)) & 0xFF0000FF 26 | v = (v | (v >> 16)) & 0x0000FFFF 27 | return v 28 | 29 | 30 | def morton(x, y, z): 31 | xx = _expand_bits(x) 32 | yy = _expand_bits(y) 33 | zz = _expand_bits(z) 34 | return (xx << 2) + (yy << 1) + zz 35 | 36 | 37 | def inv_morton(code): 38 | x = _unexpand_bits(code >> 2) 39 | y = _unexpand_bits(code >> 1) 40 | z = _unexpand_bits(code) 41 | return x, y, z 42 | 43 | 44 | def morton_grid(pow2) -> np.ndarray: 45 | mg = np.mgrid[:pow2, :pow2, :pow2].reshape(3, -1) 46 | return morton(*mg) 47 | 48 | 49 | def inv_morton_grid(pow2: int) -> np.ndarray: 50 | mg = np.arange(pow2**3) 51 | x, y, z = inv_morton(mg) 52 | return (x * pow2 + y) * pow2 + z 53 | 54 | 55 | def vol2plenoctree( 56 | density: np.ndarray, 57 | colors: np.ndarray, 58 | radius: float = 1.0, 59 | density_threshold: float = 1.0, 60 | data_format: str = "RGBA", 61 | ) -> Dict[str, np.ndarray]: 62 | """ 63 | Convert arbirary volume to PlenOctree 64 | 65 | :param density: (Dx, Dy, Dz); dimensions need not be powers 66 | of 2 nor equal 67 | :param colors: (Dx, Dy, Dz, (3, channel_size)); 68 | color data, 69 | last dim is size 3 * channel_size 70 | :param radius: 1/2 side length of volume 71 | :param density_threshold: threshold below which 72 | density is ignored 73 | :param data_format: standard PlenOctree data format string, 74 | one of :code:`RGBA | SH1 | SH4 | SH9 | SH16`. 75 | The channel_size should be respectively 76 | :code:`1 | 1 | 4 | 9 | 16```. 77 | 78 | :return: Dict, generated PlenOctree data with all keys required 79 | by the renderer. You can save this with 80 | :code:`np.savez_compressed("name.npz", **returned_data)` 81 | or 82 | :code:`np.savez("name.npz", **returned_data)` 83 | and open it with volrend. 84 | """ 85 | # Check dimensions 86 | assert density.ndim == 3 87 | assert colors.ndim == 4 and tuple(colors.shape[:3]) == tuple( 88 | density.shape 89 | ), f"{density.shape} != {colors.shape[:3]}" 90 | 91 | dims = list(density.shape) 92 | maxdim = max(dims) 93 | assert maxdim <= 1024, "Voxel grid too large" 94 | n_levels = (maxdim - 1).bit_length() 95 | 96 | # Check data formats 97 | valid_data_formats = { 98 | "RGBA": 4, 99 | "SH1": 4, 100 | "SH4": 13, 101 | "SH9": 28, 102 | "SH16": 49, 103 | } 104 | assert data_format in valid_data_formats, f"Invalid ddata format {data_format}" 105 | data_dim = valid_data_formats[data_format] 106 | 107 | # Check if given shape matches promised format 108 | assert colors.shape[-1] + 1 == data_dim 109 | 110 | result = {} 111 | result["data_dim"] = np.int64(data_dim) 112 | result["data_format"] = data_format 113 | result["invradius3"] = np.array( 114 | [0.5 / radius, 0.5 / radius, 0.5 / radius], dtype=np.float32 115 | ) 116 | 117 | require_pad = dims != [2**n_levels] * 3 118 | center = np.array( 119 | [radius * (1.0 - dim / 2**n_levels) for dim in dims], 120 | dtype=np.float32, 121 | ) 122 | 123 | result["offset"] = (0.5 * (1.0 - center / radius)).astype(np.float32) 124 | 125 | # Construct mask hierarchy 126 | hierarchy = [] 127 | pow2 = 2**n_levels 128 | mask = np.zeros((pow2, pow2, pow2), dtype=bool) 129 | density_mask = density > density_threshold 130 | mask[: dims[0], : dims[1], : dims[2]] = density_mask 131 | 132 | hierarchy.append(mask) 133 | while pow2 > 1: 134 | mask = mask.reshape((pow2 // 2, 2, pow2 // 2, 2, pow2 // 2, 2)) 135 | mask = mask.any(axis=(1, 3, 5)) 136 | pow2 //= 2 137 | hierarchy.append(mask) 138 | 139 | hierarchy = hierarchy[::-1] 140 | 141 | # PlenOctree standard format data arrays 142 | all_child = [] 143 | all_data = [] 144 | mg, img = morton_grid(1), inv_morton_grid(1) 145 | curr_indices = np.zeros(1, dtype=np.uint32) 146 | for i, (mask, next_mask) in enumerate(zip(hierarchy[:-1], hierarchy[1:])): 147 | nnodes = mask.sum() 148 | pow2 = mask.shape[0] 149 | 150 | if i == len(hierarchy) - 2: 151 | # Construct the last tree level 152 | child = np.zeros((nnodes, 2, 2, 2), dtype=np.uint32) 153 | if require_pad: 154 | # Data is not power of 2, pad it 155 | npow2 = 2 * pow2 156 | density = np.pad( 157 | density, 158 | [ 159 | (0, npow2 - dims[0]), 160 | (0, npow2 - dims[1]), 161 | (0, npow2 - dims[2]), 162 | ], 163 | ) 164 | colors = np.pad( 165 | colors, 166 | [ 167 | (0, npow2 - dims[0]), 168 | (0, npow2 - dims[1]), 169 | (0, npow2 - dims[2]), 170 | (0, 0), 171 | ], 172 | ) 173 | 174 | mask_indices = curr_indices[mask.reshape(-1)] 175 | density_i = np.empty((nnodes, 2, 2, 2, 1), dtype=np.float16) 176 | density_i[mask_indices] = ( 177 | density.reshape(pow2, 2, pow2, 2, pow2, 2, 1) 178 | .transpose(0, 2, 4, 1, 3, 5, 6) 179 | .reshape(-1, 2, 2, 2, 1)[mask.flatten()] 180 | .astype(np.float16) 181 | ) 182 | colors_i = np.empty((nnodes, 2, 2, 2, data_dim - 1), dtype=np.float16) 183 | colors_i[mask_indices] = ( 184 | colors.reshape(pow2, 2, pow2, 2, pow2, 2, data_dim - 1) 185 | .transpose(0, 2, 4, 1, 3, 5, 6) 186 | .reshape(-1, 2, 2, 2, data_dim - 1)[mask.flatten()] 187 | .astype(np.float16) 188 | ) 189 | data = np.concatenate( 190 | [colors_i.astype(np.float16), density_i.astype(np.float16)], -1 191 | ) 192 | else: 193 | # Construct an internal level 194 | mg, img = morton_grid(pow2 * 2), inv_morton_grid(pow2 * 2) 195 | next_indices = ( 196 | np.cumsum(next_mask.reshape(-1)[img], dtype=np.uint32)[mg] - 1 197 | ) 198 | next_indices[~next_mask.reshape(-1)] = 0 199 | 200 | child = ( 201 | next_indices.reshape(pow2, 2, pow2, 2, pow2, 2) 202 | + nnodes 203 | - curr_indices.reshape(pow2, 1, pow2, 1, pow2, 1) 204 | ) 205 | child = child.reshape(pow2 * 2, pow2 * 2, pow2 * 2) 206 | child[~next_mask] = 0 207 | child = child.reshape(pow2, 2, pow2, 2, pow2, 2) 208 | child = child.transpose(0, 2, 4, 1, 3, 5).reshape(pow2**3, 2, 2, 2) 209 | 210 | child_tmp = np.empty((nnodes, 2, 2, 2), dtype=np.uint32) 211 | child_tmp[curr_indices[mask.reshape(-1)]] = child[mask.reshape(-1)] 212 | child = child_tmp 213 | 214 | # For now, all interior nodes will be empty 215 | data = np.zeros((nnodes, 2, 2, 2, data_dim), dtype=np.float16) 216 | curr_indices = next_indices 217 | 218 | all_child.append(child) 219 | all_data.append(data) 220 | 221 | child = np.concatenate(all_child, 0) 222 | data = np.concatenate(all_data, 0) 223 | result["child"] = child.view(np.int32) 224 | result["data"] = data 225 | return result 226 | 227 | 228 | def split_mat4(mat4: np.ndarray) -> Dict[str, np.ndarray]: 229 | """ 230 | Tiny utility to split a 4x4 or 3x4 affine matrix into translation, rotation 231 | outputs r, t 232 | """ 233 | return { 234 | "r": mat4[..., :3, :3], 235 | "t": mat4[..., :3, 3], 236 | } 237 | 238 | 239 | 240 | __all__ = ("Rotation", "vol2plenoctree", "split_mat4") 241 | -------------------------------------------------------------------------------- /nerfvis/utils/sh.py: -------------------------------------------------------------------------------- 1 | # Modifications Copyright 2021 The PlenOctree Authors. 2 | # Original Copyright 2015 Google Inc. All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | """Sperical harmonics projection related functions 16 | 17 | Some codes are borrowed from: 18 | https://github.com/google/spherical-harmonics/blob/master/sh/spherical_harmonics.cc 19 | """ 20 | import math 21 | from typing import Callable 22 | 23 | import torch 24 | 25 | kHardCodedOrderLimit = 4 26 | 27 | 28 | def spher2cart(theta, phi): 29 | """Convert spherical coordinates into Cartesian coordinates (radius 1).""" 30 | r = torch.sin(theta) 31 | x = r * torch.cos(phi) 32 | y = r * torch.sin(phi) 33 | z = torch.cos(theta) 34 | return torch.stack([x, y, z], dim=-1) 35 | 36 | 37 | # Get the total number of coefficients for a function represented by 38 | # all spherical harmonic basis of degree <= @order (it is a point of 39 | # confusion that the order of an SH refers to its degree and not the order). 40 | def GetCoefficientCount(order: int): 41 | return (order + 1) ** 2 42 | 43 | 44 | # Get the one dimensional index associated with a particular degree @l 45 | # and order @m. This is the index that can be used to access the Coeffs 46 | # returned by SHSolver. 47 | def GetIndex(l: int, m: int): 48 | return l * (l + 1) + m 49 | 50 | 51 | # Hardcoded spherical harmonic functions for low orders (l is first number 52 | # and m is second number (sign encoded as preceeding 'p' or 'n')). 53 | # 54 | # As polynomials they are evaluated more efficiently in cartesian coordinates, 55 | # assuming that @{dx, dy, dz} is unit. This is not verified for efficiency. 56 | 57 | 58 | def HardcodedSH00(dx, dy, dz): 59 | # 0.5 * sqrt(1/pi) 60 | return 0.28209479177387814 + (dx * 0) # keep the shape 61 | 62 | 63 | def HardcodedSH1n1(dx, dy, dz): 64 | # -sqrt(3/(4pi)) * y 65 | return -0.4886025119029199 * dy 66 | 67 | 68 | def HardcodedSH10(dx, dy, dz): 69 | # sqrt(3/(4pi)) * z 70 | return 0.4886025119029199 * dz 71 | 72 | 73 | def HardcodedSH1p1(dx, dy, dz): 74 | # -sqrt(3/(4pi)) * x 75 | return -0.4886025119029199 * dx 76 | 77 | 78 | def HardcodedSH2n2(dx, dy, dz): 79 | # 0.5 * sqrt(15/pi) * x * y 80 | return 1.0925484305920792 * dx * dy 81 | 82 | 83 | def HardcodedSH2n1(dx, dy, dz): 84 | # -0.5 * sqrt(15/pi) * y * z 85 | return -1.0925484305920792 * dy * dz 86 | 87 | 88 | def HardcodedSH20(dx, dy, dz): 89 | # 0.25 * sqrt(5/pi) * (-x^2-y^2+2z^2) 90 | return 0.31539156525252005 * (-dx * dx - dy * dy + 2.0 * dz * dz) 91 | 92 | 93 | def HardcodedSH2p1(dx, dy, dz): 94 | # -0.5 * sqrt(15/pi) * x * z 95 | return -1.0925484305920792 * dx * dz 96 | 97 | 98 | def HardcodedSH2p2(dx, dy, dz): 99 | # 0.25 * sqrt(15/pi) * (x^2 - y^2) 100 | return 0.5462742152960396 * (dx * dx - dy * dy) 101 | 102 | 103 | def HardcodedSH3n3(dx, dy, dz): 104 | # -0.25 * sqrt(35/(2pi)) * y * (3x^2 - y^2) 105 | return -0.5900435899266435 * dy * (3.0 * dx * dx - dy * dy) 106 | 107 | 108 | def HardcodedSH3n2(dx, dy, dz): 109 | # 0.5 * sqrt(105/pi) * x * y * z 110 | return 2.890611442640554 * dx * dy * dz 111 | 112 | 113 | def HardcodedSH3n1(dx, dy, dz): 114 | # -0.25 * sqrt(21/(2pi)) * y * (4z^2-x^2-y^2) 115 | return -0.4570457994644658 * dy * (4.0 * dz * dz - dx * dx - dy * dy) 116 | 117 | 118 | def HardcodedSH30(dx, dy, dz): 119 | # 0.25 * sqrt(7/pi) * z * (2z^2 - 3x^2 - 3y^2) 120 | return 0.3731763325901154 * dz * (2.0 * dz * dz - 3.0 * dx * dx - 3.0 * dy * dy) 121 | 122 | 123 | def HardcodedSH3p1(dx, dy, dz): 124 | # -0.25 * sqrt(21/(2pi)) * x * (4z^2-x^2-y^2) 125 | return -0.4570457994644658 * dx * (4.0 * dz * dz - dx * dx - dy * dy) 126 | 127 | 128 | def HardcodedSH3p2(dx, dy, dz): 129 | # 0.25 * sqrt(105/pi) * z * (x^2 - y^2) 130 | return 1.445305721320277 * dz * (dx * dx - dy * dy) 131 | 132 | 133 | def HardcodedSH3p3(dx, dy, dz): 134 | # -0.25 * sqrt(35/(2pi)) * x * (x^2-3y^2) 135 | return -0.5900435899266435 * dx * (dx * dx - 3.0 * dy * dy) 136 | 137 | 138 | def HardcodedSH4n4(dx, dy, dz): 139 | # 0.75 * sqrt(35/pi) * x * y * (x^2-y^2) 140 | return 2.5033429417967046 * dx * dy * (dx * dx - dy * dy) 141 | 142 | 143 | def HardcodedSH4n3(dx, dy, dz): 144 | # -0.75 * sqrt(35/(2pi)) * y * z * (3x^2-y^2) 145 | return -1.7701307697799304 * dy * dz * (3.0 * dx * dx - dy * dy) 146 | 147 | 148 | def HardcodedSH4n2(dx, dy, dz): 149 | # 0.75 * sqrt(5/pi) * x * y * (7z^2-1) 150 | return 0.9461746957575601 * dx * dy * (7.0 * dz * dz - 1.0) 151 | 152 | 153 | def HardcodedSH4n1(dx, dy, dz): 154 | # -0.75 * sqrt(5/(2pi)) * y * z * (7z^2-3) 155 | return -0.6690465435572892 * dy * dz * (7.0 * dz * dz - 3.0) 156 | 157 | 158 | def HardcodedSH40(dx, dy, dz): 159 | # 3/16 * sqrt(1/pi) * (35z^4-30z^2+3) 160 | z2 = dz * dz 161 | return 0.10578554691520431 * (35.0 * z2 * z2 - 30.0 * z2 + 3.0) 162 | 163 | 164 | def HardcodedSH4p1(dx, dy, dz): 165 | # -0.75 * sqrt(5/(2pi)) * x * z * (7z^2-3) 166 | return -0.6690465435572892 * dx * dz * (7.0 * dz * dz - 3.0) 167 | 168 | 169 | def HardcodedSH4p2(dx, dy, dz): 170 | # 3/8 * sqrt(5/pi) * (x^2 - y^2) * (7z^2 - 1) 171 | return 0.47308734787878004 * (dx * dx - dy * dy) * (7.0 * dz * dz - 1.0) 172 | 173 | 174 | def HardcodedSH4p3(dx, dy, dz): 175 | # -0.75 * sqrt(35/(2pi)) * x * z * (x^2 - 3y^2) 176 | return -1.7701307697799304 * dx * dz * (dx * dx - 3.0 * dy * dy) 177 | 178 | 179 | def HardcodedSH4p4(dx, dy, dz): 180 | # 3/16*sqrt(35/pi) * (x^2 * (x^2 - 3y^2) - y^2 * (3x^2 - y^2)) 181 | x2 = dx * dx 182 | y2 = dy * dy 183 | return 0.6258357354491761 * (x2 * (x2 - 3.0 * y2) - y2 * (3.0 * x2 - y2)) 184 | 185 | 186 | def EvalSH(l: int, m: int, dirs): 187 | """ 188 | Args: 189 | dirs: array [..., 3]. works with torch/jnp/np 190 | Return: 191 | array [...] 192 | """ 193 | if l <= kHardCodedOrderLimit: 194 | # Validate l and m here (don't do it generally since EvalSHSlow also 195 | # checks it if we delegate to that function). 196 | assert l >= 0, "l must be at least 0." 197 | assert -l <= m and m <= l, "m must be between -l and l." 198 | dx = dirs[..., 0] 199 | dy = dirs[..., 1] 200 | dz = dirs[..., 2] 201 | 202 | if l == 0: 203 | return HardcodedSH00(dx, dy, dz) 204 | elif l == 1: 205 | if m == -1: 206 | return HardcodedSH1n1(dx, dy, dz) 207 | elif m == 0: 208 | return HardcodedSH10(dx, dy, dz) 209 | elif m == 1: 210 | return HardcodedSH1p1(dx, dy, dz) 211 | elif l == 2: 212 | if m == -2: 213 | return HardcodedSH2n2(dx, dy, dz) 214 | elif m == -1: 215 | return HardcodedSH2n1(dx, dy, dz) 216 | elif m == 0: 217 | return HardcodedSH20(dx, dy, dz) 218 | elif m == 1: 219 | return HardcodedSH2p1(dx, dy, dz) 220 | elif m == 2: 221 | return HardcodedSH2p2(dx, dy, dz) 222 | elif l == 3: 223 | if m == -3: 224 | return HardcodedSH3n3(dx, dy, dz) 225 | elif m == -2: 226 | return HardcodedSH3n2(dx, dy, dz) 227 | elif m == -1: 228 | return HardcodedSH3n1(dx, dy, dz) 229 | elif m == 0: 230 | return HardcodedSH30(dx, dy, dz) 231 | elif m == 1: 232 | return HardcodedSH3p1(dx, dy, dz) 233 | elif m == 2: 234 | return HardcodedSH3p2(dx, dy, dz) 235 | elif m == 3: 236 | return HardcodedSH3p3(dx, dy, dz) 237 | elif l == 4: 238 | if m == -4: 239 | return HardcodedSH4n4(dx, dy, dz) 240 | elif m == -3: 241 | return HardcodedSH4n3(dx, dy, dz) 242 | elif m == -2: 243 | return HardcodedSH4n2(dx, dy, dz) 244 | elif m == -1: 245 | return HardcodedSH4n1(dx, dy, dz) 246 | elif m == 0: 247 | return HardcodedSH40(dx, dy, dz) 248 | elif m == 1: 249 | return HardcodedSH4p1(dx, dy, dz) 250 | elif m == 2: 251 | return HardcodedSH4p2(dx, dy, dz) 252 | elif m == 3: 253 | return HardcodedSH4p3(dx, dy, dz) 254 | elif m == 4: 255 | return HardcodedSH4p4(dx, dy, dz) 256 | 257 | # This is unreachable given the CHECK's above but the compiler can't tell. 258 | return None 259 | 260 | else: 261 | # Not hard-coded so use the recurrence relation (which will convert this 262 | # to spherical coordinates). 263 | # return EvalSHSlow(l, m, dx, dy, dz) 264 | raise NotImplementedError 265 | 266 | 267 | def spherical_uniform_sampling(sample_count, device="cpu"): 268 | # See: https://www.bogotobogo.com/Algorithms/uniform_distribution_sphere.php 269 | u1 = torch.arange(0, sample_count, dtype=torch.float32, device=device) + torch.rand( 270 | [sample_count], dtype=torch.float32, device=device 271 | ) 272 | u1 /= sample_count 273 | u2 = torch.arange(0, sample_count, dtype=torch.float32, device=device) + torch.rand( 274 | [sample_count], dtype=torch.float32, device=device 275 | ) 276 | u2 /= sample_count 277 | u2 = u2[torch.randperm(sample_count, device=device)] 278 | theta = torch.acos(2.0 * u1 - 1.0) 279 | phi = 2.0 * math.pi * u2 280 | return theta.to(device), phi.to(device) 281 | 282 | 283 | def project_function( 284 | order: int, 285 | spherical_func: Callable, 286 | sample_count: int, 287 | device="cpu", 288 | ): 289 | assert order >= 0, "Order must be at least zero." 290 | assert sample_count > 0, "Sample count must be at least one." 291 | C = 3 # rgb channels 292 | 293 | # generate sample_count uniformly and stratified samples over the sphere 294 | # See http://www.bogotobogo.com/Algorithms/uniform_distribution_sphere.php 295 | theta, phi = spherical_uniform_sampling(sample_count, device=device) 296 | dirs = spher2cart(theta, phi) # [sample_count, 3] 297 | 298 | # evaluate the analytic function for the current spherical coords 299 | func_value, others = spherical_func( 300 | dirs[None] 301 | ) # func_value [batch_size, sample_count, C] 302 | 303 | batch_size = func_value.shape[0] 304 | 305 | # This is the approach demonstrated in [1] and is useful for arbitrary 306 | # functions on the sphere that are represented analytically. 307 | coeffs = torch.empty( 308 | [batch_size, C, GetCoefficientCount(order)], dtype=torch.float32 309 | ).to(device) 310 | 311 | # evaluate the SH basis functions up to band O, scale them by the 312 | # function's value and accumulate them over all generated samples 313 | for l in range(order + 1): # end inclusive 314 | for m in range(-l, l + 1): # end inclusive 315 | coeffs[:, :, GetIndex(l, m)] = torch.einsum( 316 | "bsc,s->bc", func_value, EvalSH(l, m, dirs) 317 | ) 318 | 319 | # scale by the probability of a particular sample, which is 320 | # 4pi/sample_count. 4pi for the surface area of a unit sphere, and 321 | # 1/sample_count for the number of samples drawn uniformly. 322 | weight = 4.0 * math.pi / sample_count 323 | coeffs *= weight 324 | coeffs = coeffs.reshape(batch_size, -1) 325 | others = others.reshape(batch_size, -1) 326 | return coeffs, others 327 | 328 | 329 | def project_function_sparse( 330 | order: int, 331 | spherical_func: Callable, 332 | sample_count: int, 333 | device="cpu", 334 | ): 335 | assert order >= 0, "Order must be at least zero." 336 | assert sample_count > 0, "Sample count must be at least one." 337 | C = 3 # rgb channels 338 | 339 | # generate sample_count uniformly and stratified samples over the sphere 340 | # See http://www.bogotobogo.com/Algorithms/uniform_distribution_sphere.php 341 | theta, phi = spherical_uniform_sampling(sample_count, device=device) 342 | dirs = spher2cart(theta, phi) # [sample_count, 3] 343 | 344 | # evaluate the analytic function for the current spherical coords 345 | func_value, others = spherical_func( 346 | dirs[None] 347 | ) # func_value [batch_size, sample_count, C] 348 | batch_size = func_value.shape[0] 349 | 350 | coeff_count = GetCoefficientCount(order) 351 | basis_vals = torch.empty([sample_count, coeff_count], dtype=torch.float32).to( 352 | device 353 | ) 354 | 355 | # evaluate the SH basis functions up to band O, scale them by the 356 | # function's value and accumulate them over all generated samples 357 | for l in range(order + 1): # end inclusive 358 | for m in range(-l, l + 1): # end inclusive 359 | basis_vals[:, GetIndex(l, m)] = EvalSH(l, m, dirs) 360 | 361 | basis_vals = basis_vals.view( 362 | sample_count, coeff_count 363 | ) # [sample_count, coeff_count] 364 | func_value = func_value.transpose(0, 1).reshape( 365 | sample_count, batch_size * C 366 | ) # [sample_count, batch_size * C] 367 | soln = torch.linalg.lstsq(basis_vals, func_value).solution 368 | soln = soln.T.reshape(batch_size, -1) 369 | others = others[:, :1, :] 370 | others = others.reshape(batch_size, -1) 371 | return soln, others 372 | 373 | 374 | if __name__ == "__main__": 375 | batch_size = 2 376 | 377 | def sphfunc_one(x): 378 | return (EvalSH(1, -1, x) + EvalSH(1, 1, x) * 0.5)[None, :, None].expand( 379 | batch_size, -1, 3 380 | ), None 381 | # return torch.ones([batch_size] + list(x.shape[:-1]) + [3]), None 382 | 383 | coeffs, others = project_function_sparse(1, sphfunc_one, 10) 384 | print(coeffs) 385 | -------------------------------------------------------------------------------- /nerfvis/version.py: -------------------------------------------------------------------------------- 1 | __version__ = "0.1.10" 2 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools", "wheel", "numpy"] 3 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | numpy 2 | torch 3 | tqdm 4 | svox 5 | scipy 6 | trimesh 7 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | description-file = README.md 3 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from distutils.errors import ( 3 | CCompilerError, 4 | DistutilsExecError, 5 | DistutilsPlatformError, 6 | ) 7 | 8 | import numpy 9 | 10 | from setuptools import Extension, setup 11 | from setuptools.command.build_ext import build_ext 12 | 13 | ext_errors = ( 14 | CCompilerError, 15 | DistutilsExecError, 16 | DistutilsPlatformError, 17 | IOError, 18 | SystemExit, 19 | ) 20 | 21 | __version__ = None 22 | exec(open("nerfvis/version.py", "r").read()) 23 | 24 | logging.basicConfig() 25 | log = logging.getLogger(__file__) 26 | 27 | cython_args = {} 28 | cython_args["ext_modules"] = [ 29 | Extension( 30 | "nerfvis.utils._rotation", 31 | ["nerfvis/utils/_rotation.c"], 32 | include_dirs=[numpy.get_include()], 33 | ) 34 | ] 35 | cython_args["cmdclass"] = {"build_ext": build_ext} 36 | 37 | try: 38 | setup( 39 | name="nerfvis", 40 | version=__version__, 41 | author="Alex Yu", 42 | author_email="alexyu99126@gmail.com", 43 | description="NeRF visualization library", 44 | long_description="NeRF visualization library based on PlenOctrees. See https://github.com/sxyu/nerfvis", 45 | packages=["nerfvis"], 46 | include_package_data=True, 47 | package_data={"nerfvis": ["index.html"]}, 48 | **cython_args, 49 | ) 50 | except ext_errors as ex: 51 | log.warn(ex) 52 | log.warn("The C extension could not be compiled") 53 | setup( 54 | name="nerfvis", 55 | version=__version__, 56 | author="Alex Yu", 57 | author_email="alexyu99126@gmail.com", 58 | description="NeRF visualization library", 59 | long_description="NeRF visualization library based on PlenOctrees. See https://github.com/sxyu/nerfvis", 60 | packages=["nerfvis"], 61 | include_package_data=True, 62 | package_data={"nerfvis": ["index.html"]}, 63 | cmdclass={}, 64 | ) 65 | --------------------------------------------------------------------------------