Full Code of cessen/psychopath for AI

master c5965ec8746c cached
115 files
882.7 KB
303.8k tokens
1000 symbols
1 requests
Download .txt
Showing preview only (924K chars total). Download the full file or copy to clipboard to get everything.
Repository: cessen/psychopath
Branch: master
Commit: c5965ec8746c
Files: 115
Total size: 882.7 KB

Directory structure:
gitextract_b8728v5v/

├── .github/
│   └── pull_request_template.md
├── .gitignore
├── Cargo.toml
├── LICENSE.md
├── README.md
├── example_scenes/
│   ├── cornell_box.psy
│   └── cube.psy
├── licenses/
│   ├── Apache-2.0.txt
│   ├── GPL-2.0.txt
│   ├── GPL-3.0.txt
│   └── MIT.txt
├── psychoblend/
│   ├── LICENSE.md
│   ├── __init__.py
│   ├── assembly.py
│   ├── psy_export.py
│   ├── render.py
│   ├── ui.py
│   ├── util.py
│   └── world.py
├── src/
│   ├── accel/
│   │   ├── bvh.rs
│   │   ├── bvh4.rs
│   │   ├── bvh_base.rs
│   │   ├── light_array.rs
│   │   ├── light_tree.rs
│   │   ├── mod.rs
│   │   └── objects_split.rs
│   ├── algorithm.rs
│   ├── bbox.rs
│   ├── bbox4.rs
│   ├── boundable.rs
│   ├── camera.rs
│   ├── color.rs
│   ├── fp_utils.rs
│   ├── hash.rs
│   ├── hilbert.rs
│   ├── image.rs
│   ├── lerp.rs
│   ├── light/
│   │   ├── distant_disk_light.rs
│   │   ├── mod.rs
│   │   ├── rectangle_light.rs
│   │   └── sphere_light.rs
│   ├── main.rs
│   ├── math.rs
│   ├── mis.rs
│   ├── parse/
│   │   ├── basics.rs
│   │   ├── data_tree.rs
│   │   ├── mod.rs
│   │   ├── psy.rs
│   │   ├── psy_assembly.rs
│   │   ├── psy_light.rs
│   │   ├── psy_mesh_surface.rs
│   │   └── psy_surface_shader.rs
│   ├── ray.rs
│   ├── renderer.rs
│   ├── sampling/
│   │   ├── mod.rs
│   │   └── monte_carlo.rs
│   ├── scene/
│   │   ├── assembly.rs
│   │   ├── mod.rs
│   │   └── world.rs
│   ├── shading/
│   │   ├── mod.rs
│   │   └── surface_closure.rs
│   ├── surface/
│   │   ├── bilinear_patch.rs
│   │   ├── micropoly_batch.rs
│   │   ├── mod.rs
│   │   ├── triangle.rs
│   │   └── triangle_mesh.rs
│   ├── timer.rs
│   ├── tracer.rs
│   └── transform_stack.rs
└── sub_crates/
    ├── bvh_order/
    │   ├── Cargo.toml
    │   ├── LICENSE.md
    │   ├── build.rs
    │   └── src/
    │       └── lib.rs
    ├── color/
    │   ├── Cargo.toml
    │   ├── LICENSE.md
    │   ├── build.rs
    │   └── src/
    │       └── lib.rs
    ├── compact/
    │   ├── Cargo.toml
    │   ├── LICENSE.md
    │   ├── benches/
    │   │   └── bench.rs
    │   ├── src/
    │   │   ├── fluv/
    │   │   │   ├── fluv32.rs
    │   │   │   └── mod.rs
    │   │   ├── lib.rs
    │   │   ├── shared_exp/
    │   │   │   ├── mod.rs
    │   │   │   ├── signed48.rs
    │   │   │   ├── unsigned32.rs
    │   │   │   └── unsigned40.rs
    │   │   └── unit_vec/
    │   │       ├── mod.rs
    │   │       └── oct32.rs
    │   └── tests/
    │       └── proptest_tests.rs
    ├── halton/
    │   ├── Cargo.toml
    │   ├── LICENSE.md
    │   ├── build.rs
    │   └── src/
    │       └── lib.rs
    ├── math3d/
    │   ├── Cargo.toml
    │   ├── LICENSE.md
    │   └── src/
    │       ├── lib.rs
    │       ├── normal.rs
    │       ├── point.rs
    │       ├── transform.rs
    │       └── vector.rs
    └── spectral_upsampling/
        ├── Cargo.toml
        ├── LICENSE.md
        ├── build.rs
        ├── jakob_tables/
        │   ├── LICENSE.txt
        │   ├── aces2065_1.coeff
        │   ├── rec2020.coeff
        │   └── srgb.coeff
        └── src/
            ├── jakob.rs
            ├── lib.rs
            ├── meng/
            │   ├── generate_meng_spectra_tables.py
            │   ├── meng_spectra_tables.rs
            │   ├── xyz_5nm_360_830.csv
            │   └── xyz_5nm_380_780.csv
            └── meng.rs

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/pull_request_template.md
================================================
## Sorry, but...

I'm sorry you put the time and effort into creating this pull request, but I am
not currently accepting contributions to Psychopath, as explained in the
project's readme file.

You are more than welcome to fork Psychopath and play with it, or even develop
it independently into something of your own.  But this repo is, at least for
now, a one-man project.


================================================
FILE: .gitignore
================================================
target
*.rs.bk
clippy.toml

# Python Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]

.zedstate
.vscode
test_renders
perf.data*


================================================
FILE: Cargo.toml
================================================
[workspace]
members = [
    "sub_crates/bvh_order",
    "sub_crates/color",
    "sub_crates/compact",
    "sub_crates/halton",
    "sub_crates/math3d",
    "sub_crates/spectral_upsampling",
]

[package]
name = "psychopath"
version = "0.1.0"
authors = ["Nathan Vegdahl <cessen@cessen.com>"]
edition = "2018"
license = "GPL v3"

[profile.release]
debug = true

[dependencies]
# Crates.io dependencies
base64 = "0.9"
clap = "2.30"
copy_in_place = "0.2.0"
crossbeam = "0.3"
half = "1.0"
lazy_static = "1.0"
nom = "5"
num_cpus = "1.8"
openexr = "0.7"
kioku = "0.3"
sobol_burley = "0.3"
png_encode_mini = "0.1.2"
rustc-serialize = "0.3"
scoped_threadpool = "0.1"
time = "0.1"
glam = "0.15"
fastapprox = "0.3"

# Local crate dependencies
[dependencies.bvh_order]
path = "sub_crates/bvh_order"

[dependencies.color]
path = "sub_crates/color"

[dependencies.compact]
path = "sub_crates/compact"
[dependencies.halton]

path = "sub_crates/halton"

[dependencies.math3d]
path = "sub_crates/math3d"

[dependencies.spectral_upsampling]
path = "sub_crates/spectral_upsampling"


================================================
FILE: LICENSE.md
================================================
## Psychopath

With the exception of files under `psychoblend/` and `sub_crates/`, this project is licensed under the GPLv3 as follows:

Copyright (c) 2020 Nathan Vegdahl

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program.  If not, see <https://www.gnu.org/licenses/>.



## Psychoblend

For the license of the files under `psychoblend/`, see `psychoblend/LICENSE.md`.



## Sub-crates

For the license of the files under `sub_crates/`, see the license files in each of its respective subdirectories.


================================================
FILE: README.md
================================================
# Overview

Psychopath is a path tracing 3d renderer.  You can read about its development
at [psychopath.io](http://psychopath.io).

This project is mostly just for me to have fun, learn, and play with ideas in
3d rendering.  I do have vague hopes that it will eventually be useful for real
things, but that's not a hard goal.

Unlike many for-fun 3d rendering projects, Psychopath is being designed with
production rendering in mind.  I think that architecting a renderer to
efficiently handle very large data sets, complex shading, motion blur, color
management, etc. presents a much richer and more challenging problem space to
explore than just writing a basic path tracer.


## Building

Psychopath is written in [Rust](https://www.rust-lang.org), and is pretty
straightforward to build except for its OpenEXR dependency.

If you have OpenEXR 2.2 installed on your system such that pkg-config can find
it, then as long as you have Rust (including Cargo) and a C++ compiler
installed, you should be able to build Psychopath with this command at the
repository root:

```
cargo build --release
```

However, if you are on an OS that doesn't have pkg-config (e.g. OSX, Windows),
or you prefer to do a custom build of OpenEXR, then you will need to download
and build OpenEXR yourself and specify the necessary environment variables as
documented in the [OpenEXR-rs readme](https://github.com/cessen/openexr-rs/blob/master/README.md).

Once those environment variables are set, then you should be able to build using
the same simple cargo command above.


# PsychoBlend

Included in the repository is an add-on for [Blender](http://www.blender.org)
called "PsychoBlend" that lets you use Psychopath for rendering in Blender.
However, most Blender features are not yet supported because Psychopath itself
doesn't support them yet.

## Features Supported
- Polygon meshes.
- Point, area, and sun lamps (exported as sphere, rectangle, and distant disc lights, respectively)
- Simple materials assigned per-object.
- Focal blur / DoF
- Camera, transform, and deformation motion blur
- Exports dupligroups with full hierarchical instancing
- Limited auto-detection of instanced meshes


# License

See LICENSE.md for details.  But the gist is:

* The overall project is licensed under GPLv3.
* PsychoBlend is licensed under GPLv2, for compatibility with Blender.
* Most crates under the `sub_crates` directory are dual-licensed under MIT and Apache 2.0 (but with some exceptions--see each crate for its respective licenses).

The intent of this scheme is to keep Psychopath itself copyleft, while allowing smaller reusable components to be licensed more liberally.


# Contributing

This is a personal, experimental, for-fun project, and I am specifically
not looking for contributions of any kind.  All PRs will be rejected
without review.

However, feel free to fork this into an entirely new project, or examine
the code for ideas for a project of your own.


================================================
FILE: example_scenes/cornell_box.psy
================================================
Scene $Scene_fr1 {
    Output {
        Path ["test_renders/cornell_box.png"]
    }
    RenderSettings {
        Resolution [512 512]
        SamplesPerPixel [16]
        Seed [1]
    }
    Camera {
        Fov [39.449188]
        FocalDistance [10.620000]
        ApertureRadius [0.000000]
        Transform [1.000000 -0.000000 0.000000 0.000000 -0.000000 0.000000 1.000000 0.000000 0.000000 1.000000 -0.000000 0.000000 -2.779998 -8.000000 2.730010 1.000000]
    }
    World {
        BackgroundShader {
            Type [Color]
            Color [rec709, 0.000000 0.000000 0.000000]
        }
    }
    Shaders {
        SurfaceShader $Green {
            Type [Lambert]
            Color [rec709, 0.117000 0.412500 0.115000]
        }
        SurfaceShader $Red {
            Type [Lambert]
            Color [rec709, 0.611000 0.055500 0.062000]
        }
        SurfaceShader $White {
            Type [Lambert]
            Color [rec709, 0.729500 0.735500 0.729000]
        }
    }
    Objects {
        RectangleLight $__Area {
            Color [rec709, 84.300003 53.800003 18.500000]
            Dimensions [1.350000 1.100000]
        }
        MeshSurface $__Plane.010_ {
            SurfaceShaderBind [$White]
            Vertices [-2.649998 2.959996 3.299997 -4.229996 2.469997 3.299997 -3.139998 4.559995 3.299997 -4.719996 4.059995 3.299997 -4.719996 4.059996 0.000000 -3.139998 4.559995 0.000000 -4.229996 2.469997 0.000000 -2.649998 2.959997 0.000000 ]
            FaceVertCounts [4 4 4 4 4 ]
            FaceVertIndices [0 1 3 2 1 0 7 6 3 1 6 4 2 3 4 5 0 2 5 7 ]
        }
        MeshSurface $__Plane.008_ {
            SurfaceShaderBind [$White]
            Vertices [-1.299999 0.649999 1.649998 -0.820000 2.249998 1.649999 -2.899997 1.139998 1.649999 -2.399998 2.719997 1.649999 -1.299999 0.649999 0.000000 -0.820000 2.249998 0.000000 -2.899997 1.139998 0.000000 -2.399998 2.719997 0.000000 ]
            FaceVertCounts [4 4 4 4 4 ]
            FaceVertIndices [0 2 3 1 3 2 6 7 1 3 7 5 0 1 5 4 2 0 4 6 ]
        }
        MeshSurface $__Plane.006_ {
            SurfaceShaderBind [$Red]
            Vertices [-5.495996 5.591994 0.000000 -5.527995 -0.000001 -0.000000 -5.559996 5.591993 5.487995 -5.559995 -0.000001 5.487995 ]
            FaceVertCounts [4 ]
            FaceVertIndices [0 1 3 2 ]
        }
        MeshSurface $__Plane.004_ {
            SurfaceShaderBind [$Green]
            Vertices [-0.000001 5.591995 0.000000 0.000000 0.000000 0.000000 -0.000001 5.591994 5.487995 0.000000 -0.000000 5.487995 ]
            FaceVertCounts [4 ]
            FaceVertIndices [1 0 2 3 ]
        }
        MeshSurface $__Plane.002_ {
            SurfaceShaderBind [$White]
            Vertices [-5.495996 5.591994 0.000000 -0.000001 5.591995 0.000000 -5.559996 5.591993 5.487995 -0.000001 5.591994 5.487995 ]
            FaceVertCounts [4 ]
            FaceVertIndices [0 1 3 2 ]
        }
        MeshSurface $__Plane.001_ {
            SurfaceShaderBind [$White]
            Vertices [-5.559996 5.591993 5.487995 -0.000001 5.591994 5.487995 -5.559995 -0.000001 5.487995 0.000000 -0.000000 5.487995 -3.429997 3.319996 5.487995 -2.129998 3.319996 5.487995 -3.429997 2.269997 5.487995 -2.129998 2.269997 5.487995 ]
            FaceVertCounts [4 4 4 4 ]
            FaceVertIndices [1 5 4 0 0 4 6 2 2 6 7 3 7 5 1 3 ]
        }
        MeshSurface $__Plane_ {
            SurfaceShaderBind [$White]
            Vertices [-5.495996 5.591994 0.000000 -0.000001 5.591995 0.000000 -5.527995 -0.000001 -0.000000 0.000000 0.000000 0.000000 ]
            FaceVertCounts [4 ]
            FaceVertIndices [0 1 3 2 ]
        }
    }
    Assembly {
        Instance {
            Data [$__Area]
            Transform [1.000000 -0.000000 0.000000 -0.000000 -0.000000 1.000000 -0.000000 0.000000 0.000000 -0.000000 1.000000 -0.000000 2.779475 -2.794788 -5.498045 1.000000]
        }
        Instance {
            Data [$__Plane.010_]
            Transform [1.000000 -0.000000 0.000000 -0.000000 -0.000000 1.000000 -0.000000 0.000000 0.000000 -0.000000 1.000000 -0.000000 -0.000000 0.000000 -0.000000 1.000000]
        }
        Instance {
            Data [$__Plane.008_]
            Transform [1.000000 -0.000000 0.000000 -0.000000 -0.000000 1.000000 -0.000000 0.000000 0.000000 -0.000000 1.000000 -0.000000 -0.000000 0.000000 -0.000000 1.000000]
        }
        Instance {
            Data [$__Plane.006_]
            Transform [1.000000 -0.000000 0.000000 -0.000000 -0.000000 1.000000 -0.000000 0.000000 0.000000 -0.000000 1.000000 -0.000000 -0.000000 0.000000 -0.000000 1.000000]
        }
        Instance {
            Data [$__Plane.004_]
            Transform [1.000000 -0.000000 0.000000 -0.000000 -0.000000 1.000000 -0.000000 0.000000 0.000000 -0.000000 1.000000 -0.000000 -0.000000 0.000000 -0.000000 1.000000]
        }
        Instance {
            Data [$__Plane.002_]
            Transform [1.000000 -0.000000 0.000000 -0.000000 -0.000000 1.000000 -0.000000 0.000000 0.000000 -0.000000 1.000000 -0.000000 -0.000000 0.000000 -0.000000 1.000000]
        }
        Instance {
            Data [$__Plane.001_]
            Transform [1.000000 -0.000000 0.000000 -0.000000 -0.000000 1.000000 -0.000000 0.000000 0.000000 -0.000000 1.000000 -0.000000 -0.000000 0.000000 -0.000000 1.000000]
        }
        Instance {
            Data [$__Plane_]
            Transform [1.000000 -0.000000 0.000000 -0.000000 -0.000000 1.000000 -0.000000 0.000000 0.000000 -0.000000 1.000000 -0.000000 -0.000000 0.000000 -0.000000 1.000000]
        }
    }
}


================================================
FILE: example_scenes/cube.psy
================================================
Scene $Scene_fr1 {
    Output {
        Path ["test_renders/cube.png"]
    }
    RenderSettings {
        Resolution [960 540]
        SamplesPerPixel [16]
        Seed [1]
    }
    Camera {
        Fov [49.134342]
        FocalDistance [9.559999]
        ApertureRadius [0.250000]
        Transform [0.685881 0.727634 -0.010817 0.000000 -0.317370 0.312469 0.895343 0.000000 -0.654862 0.610666 -0.445245 0.000000 7.481132 -6.507640 5.343665 1.000000]
    }
    World {
        BackgroundShader {
            Type [Color]
            Color [rec709, 0.050876 0.050876 0.050876]
        }
    }
    Shaders {
        SurfaceShader $Material {
            Type [Lambert]
            Color [rec709, 0.800000 0.800000 0.800000]
        }
    }
    Objects {
        MeshSurface $__Plane_ {
            SurfaceShaderBind [$Material]
            Vertices [-1.000000 -1.000000 0.000000 1.000000 -1.000000 0.000000 -1.000000 1.000000 0.000000 1.000000 1.000000 0.000000]
            FaceVertCounts [4 ]
            FaceVertIndices [0 1 3 2 ]
        }
        MeshSurface $__Cube_ {
            SurfaceShaderBind [$Material]
            Vertices [1.000000 1.000000 -1.000000 1.000000 -1.000000 -1.000000 -1.000000 -1.000000 -1.000000 -1.000000 1.000000 -1.000000 1.000000 0.999999 1.000000 0.999999 -1.000001 1.000000 -1.000000 -1.000000 1.000000 -1.000000 1.000000 1.000000 ]
            FaceVertCounts [4 4 4 4 4 4 ]
            FaceVertIndices [0 1 2 3 4 7 6 5 0 4 5 1 1 5 6 2 2 6 7 3 4 0 3 7 ]
        }
        SphereLight $__Lamp {
            Color [rec709, 50.000000 50.000000 50.000000]
            Radius [0.100000]
        }
    }
    Assembly {
        Instance {
            Data [$__Plane_]
            Transform [0.078868 -0.000000 0.000000 -0.000000 -0.000000 0.078868 -0.000000 0.000000 0.000000 -0.000000 0.078868 -0.000000 -0.000000 0.000000 -0.000000 1.000000]
        }
        Instance {
            Data [$__Cube_]
            Transform [1.000000 -0.000000 0.000000 -0.000000 -0.000000 1.000000 -0.000000 0.000000 0.000000 -0.000000 1.000000 -0.000000 -0.000000 0.000000 -1.000000 1.000000]
        }
        Instance {
            Data [$__Lamp]
            Transform [0.019856 -0.060763 0.000000 -0.000000 0.015191 0.079422 -0.000000 0.000000 0.000000 -0.000000 1.000000 -0.000000 -0.026851 -0.125233 -4.432303 1.000000]
        }
    }
}


================================================
FILE: licenses/Apache-2.0.txt
================================================

                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright [yyyy] [name of copyright owner]

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.


================================================
FILE: licenses/GPL-2.0.txt
================================================
                    GNU GENERAL PUBLIC LICENSE
                       Version 2, June 1991

 Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

                            Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users.  This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it.  (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.)  You can apply it to
your programs, too.

  When we speak of free software, we are referring to freedom, not
price.  Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.

  To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.

  For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have.  You must make sure that they, too, receive or can get the
source code.  And you must show them these terms so they know their
rights.

  We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.

  Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software.  If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.

  Finally, any free program is threatened constantly by software
patents.  We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary.  To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.

  The precise terms and conditions for copying, distribution and
modification follow.

                    GNU GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License.  The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language.  (Hereinafter, translation is included without limitation in
the term "modification".)  Each licensee is addressed as "you".

Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.

  1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.

You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.

  2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:

    a) You must cause the modified files to carry prominent notices
    stating that you changed the files and the date of any change.

    b) You must cause any work that you distribute or publish, that in
    whole or in part contains or is derived from the Program or any
    part thereof, to be licensed as a whole at no charge to all third
    parties under the terms of this License.

    c) If the modified program normally reads commands interactively
    when run, you must cause it, when started running for such
    interactive use in the most ordinary way, to print or display an
    announcement including an appropriate copyright notice and a
    notice that there is no warranty (or else, saying that you provide
    a warranty) and that users may redistribute the program under
    these conditions, and telling the user how to view a copy of this
    License.  (Exception: if the Program itself is interactive but
    does not normally print such an announcement, your work based on
    the Program is not required to print an announcement.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works.  But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.

Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.

In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:

    a) Accompany it with the complete corresponding machine-readable
    source code, which must be distributed under the terms of Sections
    1 and 2 above on a medium customarily used for software interchange; or,

    b) Accompany it with a written offer, valid for at least three
    years, to give any third party, for a charge no more than your
    cost of physically performing source distribution, a complete
    machine-readable copy of the corresponding source code, to be
    distributed under the terms of Sections 1 and 2 above on a medium
    customarily used for software interchange; or,

    c) Accompany it with the information you received as to the offer
    to distribute corresponding source code.  (This alternative is
    allowed only for noncommercial distribution and only if you
    received the program in object code or executable form with such
    an offer, in accord with Subsection b above.)

The source code for a work means the preferred form of the work for
making modifications to it.  For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable.  However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.

If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.

  4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License.  Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.

  5. You are not required to accept this License, since you have not
signed it.  However, nothing else grants you permission to modify or
distribute the Program or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.

  6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions.  You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.

  7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all.  For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.

If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.

It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices.  Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.

This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.

  8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded.  In such case, this License incorporates
the limitation as if written in the body of this License.

  9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time.  Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.

Each version is given a distinguishing version number.  If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation.  If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.

  10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission.  For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this.  Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.

                            NO WARRANTY

  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.

  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.

                     END OF TERMS AND CONDITIONS

            How to Apply These Terms to Your New Programs

  If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.

  To do so, attach the following notices to the program.  It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.

    <one line to give the program's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

Also add information on how to contact you by electronic and paper mail.

If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:

    Gnomovision version 69, Copyright (C) year name of author
    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
    This is free software, and you are welcome to redistribute it
    under certain conditions; type `show c' for details.

The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License.  Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
  `Gnomovision' (which makes passes at compilers) written by James Hacker.

  <signature of Ty Coon>, 1 April 1989
  Ty Coon, President of Vice

This General Public License does not permit incorporating your program into
proprietary programs.  If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library.  If this is what you want to do, use the GNU Lesser General
Public License instead of this License.


================================================
FILE: licenses/GPL-3.0.txt
================================================
                    GNU GENERAL PUBLIC LICENSE
                       Version 3, 29 June 2007

 Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

                            Preamble

  The GNU General Public License is a free, copyleft license for
software and other kinds of works.

  The licenses for most software and other practical works are designed
to take away your freedom to share and change the works.  By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.  We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors.  You can apply it to
your programs, too.

  When we speak of free software, we are referring to freedom, not
price.  Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.

  To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights.  Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.

  For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received.  You must make sure that they, too, receive
or can get the source code.  And you must show them these terms so they
know their rights.

  Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.

  For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software.  For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.

  Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so.  This is fundamentally incompatible with the aim of
protecting users' freedom to change the software.  The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable.  Therefore, we
have designed this version of the GPL to prohibit the practice for those
products.  If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.

  Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary.  To prevent this, the GPL assures that
patents cannot be used to render the program non-free.

  The precise terms and conditions for copying, distribution and
modification follow.

                       TERMS AND CONDITIONS

  0. Definitions.

  "This License" refers to version 3 of the GNU General Public License.

  "Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.

  "The Program" refers to any copyrightable work licensed under this
License.  Each licensee is addressed as "you".  "Licensees" and
"recipients" may be individuals or organizations.

  To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy.  The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.

  A "covered work" means either the unmodified Program or a work based
on the Program.

  To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy.  Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.

  To "convey" a work means any kind of propagation that enables other
parties to make or receive copies.  Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.

  An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License.  If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.

  1. Source Code.

  The "source code" for a work means the preferred form of the work
for making modifications to it.  "Object code" means any non-source
form of a work.

  A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.

  The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form.  A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.

  The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities.  However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work.  For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.

  The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.

  The Corresponding Source for a work in source code form is that
same work.

  2. Basic Permissions.

  All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met.  This License explicitly affirms your unlimited
permission to run the unmodified Program.  The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work.  This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.

  You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force.  You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright.  Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.

  Conveying under any other circumstances is permitted solely under
the conditions stated below.  Sublicensing is not allowed; section 10
makes it unnecessary.

  3. Protecting Users' Legal Rights From Anti-Circumvention Law.

  No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.

  When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.

  4. Conveying Verbatim Copies.

  You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.

  You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.

  5. Conveying Modified Source Versions.

  You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:

    a) The work must carry prominent notices stating that you modified
    it, and giving a relevant date.

    b) The work must carry prominent notices stating that it is
    released under this License and any conditions added under section
    7.  This requirement modifies the requirement in section 4 to
    "keep intact all notices".

    c) You must license the entire work, as a whole, under this
    License to anyone who comes into possession of a copy.  This
    License will therefore apply, along with any applicable section 7
    additional terms, to the whole of the work, and all its parts,
    regardless of how they are packaged.  This License gives no
    permission to license the work in any other way, but it does not
    invalidate such permission if you have separately received it.

    d) If the work has interactive user interfaces, each must display
    Appropriate Legal Notices; however, if the Program has interactive
    interfaces that do not display Appropriate Legal Notices, your
    work need not make them do so.

  A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit.  Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.

  6. Conveying Non-Source Forms.

  You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:

    a) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by the
    Corresponding Source fixed on a durable physical medium
    customarily used for software interchange.

    b) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by a
    written offer, valid for at least three years and valid for as
    long as you offer spare parts or customer support for that product
    model, to give anyone who possesses the object code either (1) a
    copy of the Corresponding Source for all the software in the
    product that is covered by this License, on a durable physical
    medium customarily used for software interchange, for a price no
    more than your reasonable cost of physically performing this
    conveying of source, or (2) access to copy the
    Corresponding Source from a network server at no charge.

    c) Convey individual copies of the object code with a copy of the
    written offer to provide the Corresponding Source.  This
    alternative is allowed only occasionally and noncommercially, and
    only if you received the object code with such an offer, in accord
    with subsection 6b.

    d) Convey the object code by offering access from a designated
    place (gratis or for a charge), and offer equivalent access to the
    Corresponding Source in the same way through the same place at no
    further charge.  You need not require recipients to copy the
    Corresponding Source along with the object code.  If the place to
    copy the object code is a network server, the Corresponding Source
    may be on a different server (operated by you or a third party)
    that supports equivalent copying facilities, provided you maintain
    clear directions next to the object code saying where to find the
    Corresponding Source.  Regardless of what server hosts the
    Corresponding Source, you remain obligated to ensure that it is
    available for as long as needed to satisfy these requirements.

    e) Convey the object code using peer-to-peer transmission, provided
    you inform other peers where the object code and Corresponding
    Source of the work are being offered to the general public at no
    charge under subsection 6d.

  A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.

  A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling.  In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage.  For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product.  A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.

  "Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source.  The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.

  If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information.  But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).

  The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed.  Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.

  Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.

  7. Additional Terms.

  "Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law.  If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.

  When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it.  (Additional permissions may be written to require their own
removal in certain cases when you modify the work.)  You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.

  Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:

    a) Disclaiming warranty or limiting liability differently from the
    terms of sections 15 and 16 of this License; or

    b) Requiring preservation of specified reasonable legal notices or
    author attributions in that material or in the Appropriate Legal
    Notices displayed by works containing it; or

    c) Prohibiting misrepresentation of the origin of that material, or
    requiring that modified versions of such material be marked in
    reasonable ways as different from the original version; or

    d) Limiting the use for publicity purposes of names of licensors or
    authors of the material; or

    e) Declining to grant rights under trademark law for use of some
    trade names, trademarks, or service marks; or

    f) Requiring indemnification of licensors and authors of that
    material by anyone who conveys the material (or modified versions of
    it) with contractual assumptions of liability to the recipient, for
    any liability that these contractual assumptions directly impose on
    those licensors and authors.

  All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10.  If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term.  If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.

  If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.

  Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.

  8. Termination.

  You may not propagate or modify a covered work except as expressly
provided under this License.  Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).

  However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.

  Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.

  Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License.  If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.

  9. Acceptance Not Required for Having Copies.

  You are not required to accept this License in order to receive or
run a copy of the Program.  Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance.  However,
nothing other than this License grants you permission to propagate or
modify any covered work.  These actions infringe copyright if you do
not accept this License.  Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.

  10. Automatic Licensing of Downstream Recipients.

  Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License.  You are not responsible
for enforcing compliance by third parties with this License.

  An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations.  If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.

  You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License.  For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.

  11. Patents.

  A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based.  The
work thus licensed is called the contributor's "contributor version".

  A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version.  For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.

  Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.

  In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement).  To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.

  If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients.  "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.

  If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.

  A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License.  You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.

  Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.

  12. No Surrender of Others' Freedom.

  If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all.  For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.

  13. Use with the GNU Affero General Public License.

  Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work.  The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.

  14. Revised Versions of this License.

  The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time.  Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.

  Each version is given a distinguishing version number.  If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation.  If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.

  If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.

  Later license versions may give you additional or different
permissions.  However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.

  15. Disclaimer of Warranty.

  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. Limitation of Liability.

  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.

  17. Interpretation of Sections 15 and 16.

  If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.

                     END OF TERMS AND CONDITIONS

            How to Apply These Terms to Your New Programs

  If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.

  To do so, attach the following notices to the program.  It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.

    <one line to give the program's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.

Also add information on how to contact you by electronic and paper mail.

  If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:

    <program>  Copyright (C) <year>  <name of author>
    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
    This is free software, and you are welcome to redistribute it
    under certain conditions; type `show c' for details.

The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License.  Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".

  You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.

  The GNU General Public License does not permit incorporating your program
into proprietary programs.  If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library.  If this is what you want to do, use the GNU Lesser General
Public License instead of this License.  But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.


================================================
FILE: licenses/MIT.txt
================================================
Copyright (c) 2020 Nathan Vegdahl

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: psychoblend/LICENSE.md
================================================
Copyright (c) 2020 Nathan Vegdahl

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.


================================================
FILE: psychoblend/__init__.py
================================================
bl_info = {
    "name": "PsychoBlend",
    "version": (0, 1),
    "author": "Nathan Vegdahl",
    "blender": (2, 70, 0),
    "description": "Psychopath renderer integration",
    "location": "",
    "wiki_url": "https://github.com/cessen/psychopath/wiki",
    "tracker_url": "https://github.com/cessen/psychopath/issues",
    "category": "Render"}


if "bpy" in locals():
    import imp
    imp.reload(ui)
    imp.reload(psy_export)
    imp.reload(render)
else:
    from . import ui, psy_export, render

import bpy
from bpy.types import (AddonPreferences,
                       PropertyGroup,
                       Operator,
                       )
from bpy.props import (StringProperty,
                       BoolProperty,
                       IntProperty,
                       FloatProperty,
                       FloatVectorProperty,
                       EnumProperty,
                       PointerProperty,
                       )


# Custom Scene settings
class RenderPsychopathSettingsScene(PropertyGroup):
    spp = IntProperty(
        name="Samples Per Pixel", description="Total number of samples to take per pixel",
        min=1, max=65536, default=16
        )

    max_samples_per_bucket = IntProperty(
        name="Max Samples Per Bucket", description="How many samples to simultaneously calculate per thread; indirectly determines bucket size",
        min=1, max=2**28, soft_max=2**16, default=4096
        )

    dicing_rate = FloatProperty(
        name="Dicing Rate", description="The target microgeometry width in pixels",
        min=0.0001, max=100.0, soft_min=0.125, soft_max=1.0, default=0.25
        )

    motion_blur_segments = IntProperty(
        name="Motion Segments", description="The number of segments to use in motion blur.  Zero means no motion blur.  Will be rounded down to the nearest power of two.",
        min=0, max=256, default=0
        )

    shutter_start = FloatProperty(
        name="Shutter Open", description="The time during the frame that the shutter opens, for motion blur",
        min=-1.0, max=1.0, soft_min=0.0, soft_max=1.0, default=0.0
        )

    shutter_end = FloatProperty(
        name="Shutter Close", description="The time during the frame that the shutter closes, for motion blur",
        min=-1.0, max=1.0, soft_min=0.0, soft_max=1.0, default=0.5
        )

    export_path = StringProperty(
        name="Export Path", description="The path to where the .psy files should be exported when rendering.  If left blank, /tmp or the equivalent is used.",
        subtype='FILE_PATH'
        )

# Custom Camera properties
class PsychopathCamera(bpy.types.PropertyGroup):
    aperture_radius = FloatProperty(
        name="Aperture Radius", description="Size of the camera's aperture, for DoF",
        min=0.0, max=10000.0, soft_min=0.0, soft_max=2.0, default=0.0
        )

# Psychopath material
class PsychopathLight(bpy.types.PropertyGroup):
    color_type = EnumProperty(
        name="Color Type", description="",
        items=[
            ('Rec709', 'Rec709', ""),
            ('Blackbody', 'Blackbody', ""),
            ('ColorTemperature', 'ColorTemperature', "Same as Blackbody, except with brightness kept more even."),
        ],
        default="Rec709"
        )

    color_blackbody_temp = FloatProperty(
        name="Temperature", description="Blackbody temperature in kelvin",
        min=0.0, soft_min=800.0, soft_max=6500.0, default=1200.0
        )

# Custom Mesh properties
class PsychopathMesh(bpy.types.PropertyGroup):
    is_subdivision_surface = BoolProperty(
        name="Is Subdivision Surface", description="Whether this is a sibdivision surface or just a normal mesh",
        default=False
        )

# Psychopath material
class PsychopathMaterial(bpy.types.PropertyGroup):
    surface_shader_type = EnumProperty(
        name="Surface Shader Type", description="",
        items=[('Emit', 'Emit', ""), ('Lambert', 'Lambert', ""), ('GGX', 'GGX', "")],
        default="Lambert"
        )

    color_type = EnumProperty(
        name="Color Type", description="",
        items=[
            ('Rec709', 'Rec709', ""),
            ('Blackbody', 'Blackbody', ""),
            ('ColorTemperature', 'ColorTemperature', "Same as Blackbody, except with brightness kept more even."),
        ],
        default="Rec709"
        )

    color = FloatVectorProperty(
        name="Color", description="",
        subtype='COLOR',
        min=0.0, soft_min=0.0, soft_max = 1.0,
        default=[0.8,0.8,0.8]
        )

    color_blackbody_temp = FloatProperty(
        name="Temperature", description="Blackbody temperature in kelvin",
        min=0.0, soft_min=800.0, soft_max=6500.0, default=1200.0
        )

    roughness = FloatProperty(
        name="Roughness", description="",
        min=-1.0, max=1.0, soft_min=0.0, soft_max=1.0, default=0.1
        )

    tail_shape = FloatProperty(
        name="Tail Shape", description="",
        min=0.0, max=8.0, soft_min=1.0, soft_max=3.0, default=2.0
        )

    fresnel = FloatProperty(
        name="Fresnel", description="",
        min=0.0, max=1.0, soft_min=0.0, soft_max=1.0, default=0.9
        )


# Addon Preferences
class PsychopathPreferences(AddonPreferences):
    bl_idname = __name__

    filepath_psychopath = StringProperty(
                name="Psychopath Location",
                description="Path to renderer executable",
                subtype='DIR_PATH',
                )

    def draw(self, context):
        layout = self.layout
        layout.prop(self, "filepath_psychopath")


##### REGISTER #####
def register():
    bpy.utils.register_class(PsychopathPreferences)
    bpy.utils.register_class(RenderPsychopathSettingsScene)
    bpy.utils.register_class(PsychopathCamera)
    bpy.utils.register_class(PsychopathLight)
    bpy.utils.register_class(PsychopathMesh)
    bpy.utils.register_class(PsychopathMaterial)
    bpy.types.Scene.psychopath = PointerProperty(type=RenderPsychopathSettingsScene)
    bpy.types.Camera.psychopath = PointerProperty(type=PsychopathCamera)
    bpy.types.Lamp.psychopath = PointerProperty(type=PsychopathLight)
    bpy.types.Mesh.psychopath = PointerProperty(type=PsychopathMesh)
    bpy.types.Material.psychopath = PointerProperty(type=PsychopathMaterial)
    render.register()
    ui.register()


def unregister():
    bpy.utils.unregister_class(PsychopathPreferences)
    bpy.utils.unregister_class(RenderPsychopathSettingsScene)
    bpy.utils.unregister_class(PsychopathCamera)
    bpy.utils.unregister_class(PsychopathLight)
    bpy.utils.unregister_class(PsychopathMesh)
    bpy.utils.unregister_class(PsychopathMaterial)
    del bpy.types.Scene.psychopath
    del bpy.types.Camera.psychopath
    del bpy.types.Lamp.psychopath
    del bpy.types.Mesh.psychopath
    del bpy.types.Material.psychopath
    render.unregister()
    ui.unregister()


================================================
FILE: psychoblend/assembly.py
================================================
import bpy

from .util import escape_name, mat2str, needs_def_mb, needs_xform_mb, ExportCancelled

class Assembly:
    def __init__(self, render_engine, objects, visible_layers, group_prefix="", translation_offset=(0,0,0)):
        self.name = group_prefix
        self.translation_offset = translation_offset
        self.render_engine = render_engine
        
        self.materials = []
        self.objects = []
        self.instances = []

        self.material_names = set()
        self.mesh_names = set()
        self.assembly_names = set()

        # Collect all the objects, materials, instances, etc.
        for ob in objects:
            # Check if render is cancelled
            if render_engine.test_break():
                raise ExportCancelled()

            # Check if the object is visible for rendering
            vis_layer = False
            for i in range(len(ob.layers)):
                vis_layer = vis_layer or (ob.layers[i] and visible_layers[i])
            if ob.hide_render or not vis_layer:
                continue

            # Store object data
            name = None

            if ob.type == 'EMPTY':
                if ob.dupli_type == 'GROUP':
                    name = group_prefix + "__" + escape_name(ob.dupli_group.name)
                    if name not in self.assembly_names:
                        self.assembly_names.add(name)
                        self.objects += [Assembly(self.render_engine, ob.dupli_group.objects, ob.dupli_group.layers, name, ob.dupli_group.dupli_offset*-1)]
            elif ob.type == 'MESH':
                name = self.get_mesh(ob, group_prefix)
            elif ob.type == 'LAMP' and ob.data.type == 'POINT':
                name = self.get_sphere_lamp(ob, group_prefix)
            elif ob.type == 'LAMP' and ob.data.type == 'AREA':
                name = self.get_rect_lamp(ob, group_prefix)
            
            # Store instance
            if name != None:
                self.instances += [Instance(render_engine, ob, name)]

    def export(self, render_engine, w):
        if self.name == "":
            w.write("Assembly {\n")
        else:
            w.write("Assembly $%s {\n" % self.name)
        w.indent()

        for mat in self.materials:
            # Check if render is cancelled
            if render_engine.test_break():
                raise ExportCancelled()
            mat.export(render_engine, w)

        for ob in self.objects:
            # Check if render is cancelled
            if render_engine.test_break():
                raise ExportCancelled()
            ob.export(render_engine, w)

        for inst in self.instances:
            # Check if render is cancelled
            if render_engine.test_break():
                raise ExportCancelled()
            inst.export(render_engine, w)

        w.unindent()
        w.write("}\n")
    
    #----------------

    def take_sample(self, render_engine, scene, time):
        for mat in self.materials:
            # Check if render is cancelled
            if render_engine.test_break():
                raise ExportCancelled()
            mat.take_sample(render_engine, scene, time)

        for ob in self.objects:
            # Check if render is cancelled
            if render_engine.test_break():
                raise ExportCancelled()
            ob.take_sample(render_engine, scene, time)

        for inst in self.instances:
            # Check if render is cancelled
            if render_engine.test_break():
                raise ExportCancelled()
            inst.take_sample(render_engine, time, self.translation_offset)
    
    def cleanup(self):
        for mat in self.materials:
            mat.cleanup()
        for ob in self.objects:
            ob.cleanup()

    def get_mesh(self, ob, group_prefix):
        # Figure out if we need to export or not and figure out what name to
        # export with.
        has_modifiers = len(ob.modifiers) > 0
        deform_mb = needs_def_mb(ob)
        if has_modifiers or deform_mb:
            mesh_name = group_prefix + escape_name("__" + ob.name + "__" + ob.data.name + "_")
        else:
            mesh_name = group_prefix + escape_name("__" + ob.data.name + "_")
        has_faces = len(ob.data.polygons) > 0
        should_export_mesh = has_faces and (mesh_name not in self.mesh_names)
        
        # Get mesh
        if should_export_mesh:
            self.mesh_names.add(mesh_name)
            self.objects += [Mesh(self.render_engine, ob, mesh_name)]

            # Get materials
            for ms in ob.material_slots:
                if ms != None:
                    if ms.material.name not in self.material_names:
                        self.material_names.add(ms.material.name)
                        self.materials += [Material(self.render_engine, ms.material)]

            return mesh_name
        else:
            return None


    def get_sphere_lamp(self, ob, group_prefix):
        name = group_prefix + "__" + escape_name(ob.name)
        self.objects += [SphereLamp(self.render_engine, ob, name)]
        return name

    def get_rect_lamp(self, ob, group_prefix):
        name = group_prefix + "__" + escape_name(ob.name)
        self.objects += [RectLamp(self.render_engine, ob, name)]
        return name


#=========================================================================


class Mesh:
    """ Holds data for a mesh to be exported.
    """
    def __init__(self, render_engine, ob, name):
        self.ob = ob
        self.name = name
        self.needs_mb = needs_def_mb(self.ob)
        self.time_meshes = []

    def take_sample(self, render_engine, scene, time):
        if len(self.time_meshes) == 0 or self.needs_mb:
            render_engine.update_stats("", "Psychopath: Collecting '{}' at time {}".format(self.ob.name, time))
            self.time_meshes += [self.ob.to_mesh(scene, True, 'RENDER')]
    
    def cleanup(self):
        for mesh in self.time_meshes:
            bpy.data.meshes.remove(mesh)

    def export(self, render_engine, w):
        render_engine.update_stats("", "Psychopath: Exporting %s" % self.ob.name)

        if self.ob.data.psychopath.is_subdivision_surface == False:
            # Exporting normal mesh
            w.write("MeshSurface $%s {\n" % self.name)
            w.indent()
        else:
            # Exporting subdivision surface cage
            w.write("SubdivisionSurface $%s {\n" % self.name)
            w.indent()

        # Write vertices and (if it's smooth shaded) normals
        for ti in range(len(self.time_meshes)):
            w.write("Vertices [")
            w.write(" ".join([("%f" % i) for vert in self.time_meshes[ti].vertices for i in vert.co]), False)
            w.write("]\n", False)
            if self.time_meshes[0].polygons[0].use_smooth and self.ob.data.psychopath.is_subdivision_surface == False:
                w.write("Normals [")
                w.write(" ".join([("%f" % i) for vert in self.time_meshes[ti].vertices for i in vert.normal]), False)
                w.write("]\n", False)

        # Write face vertex counts
        w.write("FaceVertCounts [")
        w.write(" ".join([("%d" % len(p.vertices)) for p in self.time_meshes[0].polygons]), False)
        w.write("]\n", False)

        # Write face vertex indices
        w.write("FaceVertIndices [")
        w.write(" ".join([("%d"%v) for p in self.time_meshes[0].polygons for v in p.vertices]), False)
        w.write("]\n", False)

        # MeshSurface/SubdivisionSurface section end
        w.unindent()
        w.write("}\n")


class SphereLamp:
    """ Holds data for a sphere light to be exported.
    """
    def __init__(self, render_engine, ob, name):
        self.ob = ob
        self.name = name
        self.time_col = []
        self.time_rad = []

    def take_sample(self, render_engine, scene, time):
        render_engine.update_stats("", "Psychopath: Collecting '{}' at time {}".format(self.ob.name, time))

        if self.ob.data.psychopath.color_type == 'Rec709':
            self.time_col += [('Rec709', self.ob.data.color * self.ob.data.energy)]
        elif self.ob.data.psychopath.color_type == 'Blackbody':
            self.time_col += [('Blackbody', self.ob.data.psychopath.color_blackbody_temp, self.ob.data.energy)]
        elif self.ob.data.psychopath.color_type == 'ColorTemperature':
            self.time_col += [('ColorTemperature', self.ob.data.psychopath.color_blackbody_temp, self.ob.data.energy)]

        self.time_rad += [self.ob.data.shadow_soft_size]

    def cleanup(self):
        pass
    
    def export(self, render_engine, w):
        render_engine.update_stats("", "Psychopath: Exporting %s" % self.ob.name)

        w.write("SphereLight $%s {\n" % self.name)
        w.indent()
        for col in self.time_col:
            if col[0] == 'Rec709':
                w.write("Color [rec709, %f %f %f]\n" % (col[1][0], col[1][1], col[1][2]))
            elif col[0] == 'Blackbody':
                w.write("Color [blackbody, %f %f]\n" % (col[1], col[2]))
            elif col[0] == 'ColorTemperature':
                w.write("Color [color_temperature, %f %f]\n" % (col[1], col[2]))
        for rad in self.time_rad:
            w.write("Radius [%f]\n" % rad)

        w.unindent()
        w.write("}\n")


class RectLamp:
    """ Holds data for a rectangular light to be exported.
    """
    def __init__(self, render_engine, ob, name):
        self.ob = ob
        self.name = name
        self.time_col = []
        self.time_dim = []

    def take_sample(self, render_engine, scene, time):
        render_engine.update_stats("", "Psychopath: Collecting '{}' at time {}".format(self.ob.name, time))

        if self.ob.data.psychopath.color_type == 'Rec709':
            self.time_col += [('Rec709', self.ob.data.color * self.ob.data.energy)]
        elif self.ob.data.psychopath.color_type == 'Blackbody':
            self.time_col += [('Blackbody', self.ob.data.psychopath.color_blackbody_temp, self.ob.data.energy)]
        elif self.ob.data.psychopath.color_type == 'ColorTemperature':
            self.time_col += [('ColorTemperature', self.ob.data.psychopath.color_blackbody_temp, self.ob.data.energy)]

        if self.ob.data.shape == 'RECTANGLE':
            self.time_dim += [(self.ob.data.size, self.ob.data.size_y)]
        else:
            self.time_dim += [(self.ob.data.size, self.ob.data.size)]
    
    def cleanup(self):
        pass
    
    def export(self, render_engine, w):
        render_engine.update_stats("", "Psychopath: Exporting %s" % self.ob.name)

        w.write("RectangleLight $%s {\n" % self.name)
        w.indent()
        for col in self.time_col:
            if col[0] == 'Rec709':
                w.write("Color [rec709, %f %f %f]\n" % (col[1][0], col[1][1], col[1][2]))
            elif col[0] == 'Blackbody':
                w.write("Color [blackbody, %f %f]\n" % (col[1], col[2]))
            elif col[0] == 'ColorTemperature':
                w.write("Color [color_temperature, %f %f]\n" % (col[1], col[2]))
        for dim in self.time_dim:
            w.write("Dimensions [%f %f]\n" % dim)

        w.unindent()
        w.write("}\n")


class Instance:
    def __init__(self, render_engine, ob, data_name):
        self.ob = ob
        self.data_name = data_name
        self.needs_mb = needs_xform_mb(self.ob)
        self.time_xforms = []

    def take_sample(self, render_engine, time, translation_offset):
        if len(self.time_xforms) == 0 or self.needs_mb:
            render_engine.update_stats("", "Psychopath: Collecting '{}' xforms at time {}".format(self.ob.name, time))
            mat = self.ob.matrix_world.copy()
            mat[0][3] += translation_offset[0]
            mat[1][3] += translation_offset[1]
            mat[2][3] += translation_offset[2]
            self.time_xforms += [mat]

    def export(self, render_engine, w):
        render_engine.update_stats("", "Psychopath: Exporting %s" % self.ob.name)

        w.write("Instance {\n")
        w.indent()
        w.write("Data [$%s]\n" % self.data_name)
        for mat in self.time_xforms:
            w.write("Transform [%s]\n" % mat2str(mat.inverted()))
        for ms in self.ob.material_slots:
            if ms != None:
                w.write("SurfaceShaderBind [$%s]\n" % escape_name(ms.material.name))
                break
        w.unindent()
        w.write("}\n")


class Material:
    def __init__(self, render_engine, material):
        self.mat = material

    def take_sample(self, render_engine, time, translation_offset):
        # TODO: motion blur of material settings
        pass

    def export(self, render_engine, w):
        render_engine.update_stats("", "Psychopath: Exporting %s" % self.mat.name)

        w.write("SurfaceShader $%s {\n" % escape_name(self.mat.name))
        w.indent()
        if self.mat.psychopath.surface_shader_type == 'Emit':
            w.write("Type [Emit]\n")
            if self.mat.psychopath.color_type == 'Rec709':
                col = self.mat.psychopath.color
                w.write("Color [rec709, %f %f %f]\n" % (
                    col[0], col[1], col[2],
                ))
            elif self.mat.psychopath.color_type == 'Blackbody':
                w.write("Color [blackbody, %f %f]\n" % (
                    self.mat.psychopath.color_blackbody_temp,
                    1.0,
                ))
            elif self.mat.psychopath.color_type == 'ColorTemperature':
                w.write("Color [color_temperature, %f %f]\n" % (
                    self.mat.psychopath.color_blackbody_temp,
                    1.0,
                ))
        elif self.mat.psychopath.surface_shader_type == 'Lambert':
            w.write("Type [Lambert]\n")
            if self.mat.psychopath.color_type == 'Rec709':
                col = self.mat.psychopath.color
                w.write("Color [rec709, %f %f %f]\n" % (
                    col[0], col[1], col[2],
                ))
            elif self.mat.psychopath.color_type == 'Blackbody':
                w.write("Color [blackbody, %f %f]\n" % (
                    self.mat.psychopath.color_blackbody_temp,
                    1.0,
                ))
            elif self.mat.psychopath.color_type == 'ColorTemperature':
                w.write("Color [color_temperature, %f %f]\n" % (
                    self.mat.psychopath.color_blackbody_temp,
                    1.0,
                ))
        elif self.mat.psychopath.surface_shader_type == 'GGX':
            w.write("Type [GGX]\n")
            if self.mat.psychopath.color_type == 'Rec709':
                col = self.mat.psychopath.color
                w.write("Color [rec709, %f %f %f]\n" % (
                    col[0], col[1], col[2],
                ))
            elif self.mat.psychopath.color_type == 'Blackbody':
                w.write("Color [blackbody, %f %f]\n" % (
                    self.mat.psychopath.color_blackbody_temp,
                    1.0,
                ))
            elif self.mat.psychopath.color_type == 'ColorTemperature':
                w.write("Color [color_temperature, %f %f]\n" % (
                    self.mat.psychopath.color_blackbody_temp,
                    1.0,
                ))
            w.write("Roughness [%f]\n" % self.mat.psychopath.roughness)
            w.write("Fresnel [%f]\n" % self.mat.psychopath.fresnel)
        else:
            raise "Unsupported surface shader type '%s'" % self.mat.psychopath.surface_shader_type
        w.unindent()
        w.write("}\n")

    def cleanup(self):
        pass


================================================
FILE: psychoblend/psy_export.py
================================================
import bpy

from math import log

from .assembly import Assembly
from .util import escape_name, mat2str, ExportCancelled
from .world import World


class IndentedWriter:
    def __init__(self, file_handle):
        self.f = file_handle
        self.indent_level = 0
        self.indent_size = 4

    def indent(self):
        self.indent_level += self.indent_size

    def unindent(self):
        self.indent_level -= self.indent_size
        if self.indent_level < 0:
            self.indent_level = 0

    def write(self, text, do_indent=True):
        if do_indent:
            self.f.write(bytes(' '*self.indent_level + text, "utf-8"))
        else:
            self.f.write(bytes(text, "utf-8"))


class PsychoExporter:
    def __init__(self, f, render_engine, scene):
        self.w = IndentedWriter(f)
        self.render_engine = render_engine
        self.scene = scene

        self.mesh_names = {}
        self.group_names = {}

        # Motion blur segments are rounded down to a power of two
        if scene.psychopath.motion_blur_segments > 0:
            self.time_samples = (2**int(log(scene.psychopath.motion_blur_segments, 2))) + 1
        else:
            self.time_samples = 1

        # pre-calculate useful values for exporting motion blur
        self.shutter_start = scene.psychopath.shutter_start
        self.shutter_diff = (scene.psychopath.shutter_end - scene.psychopath.shutter_start) / max(1, (self.time_samples-1))

        self.fr = scene.frame_current


    def set_frame(self, frame, fraction):
        if fraction >= 0:
            self.scene.frame_set(frame, fraction)
        else:
            self.scene.frame_set(frame-1, 1.0+fraction)

    def export_psy(self):
        try:
            self._export_psy()
        except ExportCancelled:
            # Cleanup
            self.scene.frame_set(self.fr)
            return False
        else:
            # Cleanup
            self.scene.frame_set(self.fr)
            return True

    def _export_psy(self):
        # Info
        self.w.write("# Exported from Blender 2.7x\n")

        # Scene begin
        self.w.write("\n\nScene $%s_fr%d {\n" % (escape_name(self.scene.name), self.fr))
        self.w.indent()

        #######################
        # Output section begin
        self.w.write("Output {\n")
        self.w.indent()

        self.w.write('Path [""]\n')

        # Output section end
        self.w.unindent()
        self.w.write("}\n")

        ###############################
        # RenderSettings section begin
        self.w.write("RenderSettings {\n")
        self.w.indent()

        res_x = int(self.scene.render.resolution_x * (self.scene.render.resolution_percentage / 100))
        res_y = int(self.scene.render.resolution_y * (self.scene.render.resolution_percentage / 100))
        self.w.write('Resolution [%d %d]\n' % (res_x, res_y))
        self.w.write("SamplesPerPixel [%d]\n" % self.scene.psychopath.spp)
        self.w.write("DicingRate [%f]\n" % self.scene.psychopath.dicing_rate)
        self.w.write('Seed [%d]\n' % self.fr)

        # RenderSettings section end
        self.w.unindent()
        self.w.write("}\n")

        ###############################
        # Export world and object data
        world = None
        root_assembly = None
        try:
            # Prep for data collection
            world = World(self.render_engine, self.scene, self.scene.layers, float(res_x) / float(res_y))
            root_assembly = Assembly(self.render_engine, self.scene.objects, self.scene.layers)

            # Collect data for each time sample
            for i in range(self.time_samples):
                time = self.fr + self.shutter_start + (self.shutter_diff*i)
                self.set_frame(self.fr, self.shutter_start + (self.shutter_diff*i))
                world.take_sample(self.render_engine, self.scene, time)
                root_assembly.take_sample(self.render_engine, self.scene, time)

            # Export collected data
            world.export(self.render_engine, self.w)
            root_assembly.export(self.render_engine, self.w)
        finally:
            if world != None:
                world.cleanup()
            if root_assembly != None:
                root_assembly.cleanup()

        # Scene end
        self.w.unindent()
        self.w.write("}\n")


================================================
FILE: psychoblend/render.py
================================================
import bpy
import time
import os
import subprocess
import base64
import struct
from . import psy_export

class PsychopathRender(bpy.types.RenderEngine):
    bl_idname = 'PSYCHOPATH_RENDER'
    bl_label = "Psychopath"
    DELAY = 1.0

    @staticmethod
    def _locate_binary():
        addon_prefs = bpy.context.user_preferences.addons[__package__].preferences

        # Use the system preference if its set.
        psy_binary = addon_prefs.filepath_psychopath
        if psy_binary:
            if os.path.exists(psy_binary):
                return psy_binary
            else:
                print("User Preference to psychopath %r NOT FOUND, checking $PATH" % psy_binary)

        # search the path all os's
        psy_binary_default = "psychopath"

        os_path_ls = os.getenv("PATH").split(':') + [""]

        for dir_name in os_path_ls:
            psy_binary = os.path.join(dir_name, psy_binary_default)
            if os.path.exists(psy_binary):
                return psy_binary
        return ""

    def _start_psychopath(self, scene, psy_filepath, use_stdin, crop):
        psy_binary = PsychopathRender._locate_binary()
        if not psy_binary:
            print("Psychopath: could not execute psychopath, possibly Psychopath isn't installed")
            return False

        # Figure out command line options
        args = []
        if crop != None:
            args += ["--crop", str(crop[0]), str(self.size_y - crop[3]), str(crop[2] - 1), str(self.size_y - crop[1] - 1)]
        if use_stdin:
            args += ["--spb", str(scene.psychopath.max_samples_per_bucket), "--serialized_output", "--use_stdin"]
        else:
            args += ["--spb", str(scene.psychopath.max_samples_per_bucket), "--serialized_output", "-i", psy_filepath]

        # Start Rendering!
        try:
            self._process = subprocess.Popen([psy_binary] + args, bufsize=1, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
        except OSError:
            # TODO, report api
            print("Psychopath: could not execute '%s'" % psy_binary)
            import traceback
            traceback.print_exc()
            print ("***-DONE-***")
            return False

        return True

    def _draw_bucket(self, crop, bucket_info, pixels_encoded):
        if crop != None:
            x = bucket_info[0] - crop[0]
            y = self.size_y - bucket_info[3] - crop[1]
        else:
            x = bucket_info[0]
            y = self.size_y - bucket_info[3]
        width = bucket_info[2] - bucket_info[0]
        height = bucket_info[3] - bucket_info[1]

        # Decode pixel data
        pixels = [p for p in struct.iter_unpack("ffff", base64.b64decode(pixels_encoded))]
        pixels_flipped = []
        for i in range(height):
            n = height - i - 1
            pixels_flipped += pixels[n*width:(n+1)*width]

        # Write pixel data to render image
        result = self.begin_result(x, y, width, height)
        lay = result.layers[0].passes["Combined"]
        lay.rect = pixels_flipped
        self.end_result(result)

    def render(self, scene):
        self._process = None
        try:
            self._render(scene)
        except:
            if self._process != None:
                self._process.terminate()
            raise

    def _render(self, scene):
        # has to be called to update the frame on exporting animations
        scene.frame_set(scene.frame_current)

        export_path = scene.psychopath.export_path.strip()
        use_stdin = False

        r = scene.render
        # compute resolution
        self.size_x = int(r.resolution_x * r.resolution_percentage / 100)
        self.size_y = int(r.resolution_y * r.resolution_percentage / 100)

        # Calculate border cropping, if any.
        if scene.render.use_border == True:
            minx = r.resolution_x * scene.render.border_min_x * (r.resolution_percentage / 100)
            miny = r.resolution_y * scene.render.border_min_y * (r.resolution_percentage / 100)
            maxx = r.resolution_x * scene.render.border_max_x * (r.resolution_percentage / 100)
            maxy = r.resolution_y * scene.render.border_max_y * (r.resolution_percentage / 100)
            crop = (int(minx), int(miny), int(maxx), int(maxy))
        else:
            crop = None

        # Are we using an output file or standard in/out?
        if export_path != "":
            export_path += "_%d.psy" % scene.frame_current
        else:
            # We'll write directly to Psychopath's stdin
            use_stdin = True

        if use_stdin:
            # Start rendering
            if not self._start_psychopath(scene, export_path, use_stdin, crop):
                self.update_stats("", "Psychopath: Not found")
                return

            self.update_stats("", "Psychopath: Collecting...")
            # Export to Psychopath's stdin
            if not psy_export.PsychoExporter(self._process.stdin, self, scene).export_psy():
                # Render cancelled in the middle of exporting,
                # so just return.
                self._process.terminate()
                return
            self._process.stdin.write(bytes("__PSY_EOF__", "utf-8"))
            self._process.stdin.flush()
        else:
            # Export to file
            self.update_stats("", "Psychopath: Exporting data from Blender")
            with open(export_path, 'w+b') as f:
                if not psy_export.PsychoExporter(f, self, scene).export_psy():
                    # Render cancelled in the middle of exporting,
                    # so just return.
                    return

            # Start rendering
            self.update_stats("", "Psychopath: Rendering from %s" % export_path)
            if not self._start_psychopath(scene, export_path, use_stdin, crop):
                self.update_stats("", "Psychopath: Not found")
                return

        self.update_stats("", "Psychopath: Building")

        # If we can, make the render process's stdout non-blocking.  The
        # benefit of this is that canceling the render won't block waiting
        # for the next piece of input.
        try:
            import fcntl
            fd = self._process.stdout.fileno()
            fl = fcntl.fcntl(fd, fcntl.F_GETFL)
            fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
        except:
            print("NOTE: Can't make Psychopath's stdout non-blocking, so canceling renders may take a moment to respond.")

        # Process output from rendering process
        reached_first_bucket = False
        output = b""
        render_process_finished = False
        all_output_consumed = False
        while not (render_process_finished and all_output_consumed):
            if self._process.poll() != None:
                render_process_finished = True

            # Check for render cancel
            if self.test_break():
                self._process.terminate()
                break

            # Get render output from stdin
            tmp = self._process.stdout.read1(2**16)
            if len(tmp) == 0:
                time.sleep(0.0001) # Don't spin on the CPU
                if render_process_finished:
                    all_output_consumed = True
                continue
            output += tmp
            outputs = output.split(b'DIV\n')

            # Skip render process output until we hit the first bucket.
            # (The stuff before it is just informational printouts.)
            if not reached_first_bucket:
                if len(outputs) > 1:
                    reached_first_bucket = True
                    outputs = outputs[1:]
                else:
                    continue

            self.update_stats("", "Psychopath: Rendering")

            # Clear output buffer, since it's all in 'outputs' now.
            output = b""

            # Process buckets
            for bucket in outputs:
                if len(bucket) == 0:
                    continue

                if bucket[-11:] == b'BUCKET_END\n':
                    # Parse bucket text
                    contents = bucket.split(b'\n')
                    percentage = contents[0]
                    bucket_info = [int(i) for i in contents[1].split(b' ')]
                    pixels = contents[2]

                    # Draw the bucket
                    self._draw_bucket(crop, bucket_info, pixels)

                    # Update render progress bar
                    try:
                        progress = float(percentage[:-1])
                    except ValueError:
                        pass
                    finally:
                        self.update_progress(progress/100)
                else:
                    output += bucket

def register():
    bpy.utils.register_class(PsychopathRender)

def unregister():
    bpy.utils.unregister_class(PsychopathRender)


================================================
FILE: psychoblend/ui.py
================================================
import bpy

# Use some of the existing buttons.
from bl_ui import properties_render
properties_render.RENDER_PT_render.COMPAT_ENGINES.add('PSYCHOPATH_RENDER')
properties_render.RENDER_PT_dimensions.COMPAT_ENGINES.add('PSYCHOPATH_RENDER')
properties_render.RENDER_PT_output.COMPAT_ENGINES.add('PSYCHOPATH_RENDER')
del properties_render

from bl_ui import properties_data_camera
properties_data_camera.DATA_PT_lens.COMPAT_ENGINES.add('PSYCHOPATH_RENDER')
properties_data_camera.DATA_PT_camera.COMPAT_ENGINES.add('PSYCHOPATH_RENDER')
properties_data_camera.DATA_PT_camera_display.COMPAT_ENGINES.add('PSYCHOPATH_RENDER')
properties_data_camera.DATA_PT_custom_props_camera.COMPAT_ENGINES.add('PSYCHOPATH_RENDER')
del properties_data_camera

class PsychopathPanel():
    COMPAT_ENGINES = {'PSYCHOPATH_RENDER'}

    @classmethod
    def poll(cls, context):
        rd = context.scene.render
        return (rd.use_game_engine is False) and (rd.engine in cls.COMPAT_ENGINES)


class RENDER_PT_psychopath_render_settings(PsychopathPanel, bpy.types.Panel):
    bl_label = "Render Settings"
    bl_space_type = 'PROPERTIES'
    bl_region_type = 'WINDOW'
    bl_context = "render"

    def draw(self, context):
        scene = context.scene
        layout = self.layout

        col = layout.column()

        col.label(text="Sampling")
        col.prop(scene.psychopath, "spp")

        col.label(text="Dicing")
        col.prop(scene.psychopath, "dicing_rate")

        col.label(text="Motion Blur")
        col.prop(scene.psychopath, "motion_blur_segments")
        col.prop(scene.psychopath, "shutter_start")
        col.prop(scene.psychopath, "shutter_end")

        col.label(text="Performance")
        col.prop(scene.psychopath, "max_samples_per_bucket")


class RENDER_PT_psychopath_export_settings(PsychopathPanel, bpy.types.Panel):
    bl_label = "Export Settings"
    bl_space_type = 'PROPERTIES'
    bl_region_type = 'WINDOW'
    bl_context = "render"

    def draw(self, context):
        scene = context.scene
        layout = self.layout

        col = layout.column()
        col.prop(scene.psychopath, "export_path")


class WORLD_PT_psychopath_background(PsychopathPanel, bpy.types.Panel):
    bl_label = "Background"
    bl_space_type = 'PROPERTIES'
    bl_region_type = 'WINDOW'
    bl_context = "world"

    @classmethod
    def poll(cls, context):
        return context.world and PsychopathPanel.poll(context)

    def draw(self, context):
        layout = self.layout

        world = context.world
        layout.prop(world, "horizon_color", text="Color")


class DATA_PT_psychopath_camera_dof(PsychopathPanel, bpy.types.Panel):
    bl_label = "Depth of Field"
    bl_space_type = 'PROPERTIES'
    bl_region_type = 'WINDOW'
    bl_context = "data"

    @classmethod
    def poll(cls, context):
        engine = context.scene.render.engine
        return context.camera and PsychopathPanel.poll(context)

    def draw(self, context):
        ob = context.active_object
        layout = self.layout

        col = layout.column()

        col.prop(ob.data, "dof_object")
        col.prop(ob.data, "dof_distance")
        col.prop(ob.data.psychopath, "aperture_radius")


class DATA_PT_psychopath_lamp(PsychopathPanel, bpy.types.Panel):
    bl_label = "Lamp"
    bl_space_type = 'PROPERTIES'
    bl_region_type = 'WINDOW'
    bl_context = "data"

    @classmethod
    def poll(cls, context):
        engine = context.scene.render.engine
        return context.lamp and PsychopathPanel.poll(context)

    def draw(self, context):
        ob = context.active_object
        layout = self.layout

        col = layout.column()

        row = col.row()
        row.prop(ob.data, "type", expand=True)

        if ob.data.type != 'HEMI' and ob.data.type != 'AREA':
            col.prop(ob.data, "shadow_soft_size")

        col.prop(ob.data.psychopath, "color_type")

        if ob.data.psychopath.color_type == 'Rec709':
            col.prop(ob.data, "color")
        elif ob.data.psychopath.color_type == 'Blackbody' or ob.data.psychopath.color_type == 'ColorTemperature':
            col.prop(ob.data.psychopath, "color_blackbody_temp")

        col.prop(ob.data, "energy")


class DATA_PT_psychopath_area_lamp(PsychopathPanel, bpy.types.Panel):
    bl_label = "Area Shape"
    bl_space_type = 'PROPERTIES'
    bl_region_type = 'WINDOW'
    bl_context = "data"

    @classmethod
    def poll(cls, context):
        lamp = context.lamp
        engine = context.scene.render.engine
        return (lamp and lamp.type == 'AREA') and (engine in cls.COMPAT_ENGINES)

    def draw(self, context):
        layout = self.layout

        lamp = context.lamp

        col = layout.column()
        col.row().prop(lamp, "shape", expand=True)
        sub = col.row(align=True)

        if lamp.shape == 'SQUARE':
            sub.prop(lamp, "size")
        elif lamp.shape == 'RECTANGLE':
            sub.prop(lamp, "size", text="Size X")
            sub.prop(lamp, "size_y", text="Size Y")


class DATA_PT_psychopath_mesh(PsychopathPanel, bpy.types.Panel):
    bl_label = "Psychopath Mesh Properties"
    bl_space_type = 'PROPERTIES'
    bl_region_type = 'WINDOW'
    bl_context = "data"

    @classmethod
    def poll(cls, context):
        engine = context.scene.render.engine
        return context.mesh and (engine in cls.COMPAT_ENGINES)

    def draw(self, context):
        layout = self.layout

        mesh = context.mesh

        layout.row().prop(mesh.psychopath, "is_subdivision_surface")


class MATERIAL_PT_psychopath_context_material(PsychopathPanel, bpy.types.Panel):
    bl_label = ""
    bl_space_type = "PROPERTIES"
    bl_region_type = "WINDOW"
    bl_context = "material"
    bl_options = {'HIDE_HEADER'}

    @classmethod
    def poll(cls, context):
        return (context.material or context.object) and PsychopathPanel.poll(context)

    def draw(self, context):
        layout = self.layout

        mat = context.material
        ob = context.object
        slot = context.material_slot
        space = context.space_data

        if ob:
            row = layout.row()

            row.template_list("MATERIAL_UL_matslots", "", ob, "material_slots", ob, "active_material_index", rows=1)

            col = row.column(align=True)
            col.operator("object.material_slot_add", icon='ZOOMIN', text="")
            col.operator("object.material_slot_remove", icon='ZOOMOUT', text="")

            col.menu("MATERIAL_MT_specials", icon='DOWNARROW_HLT', text="")

            if ob.mode == 'EDIT':
                row = layout.row(align=True)
                row.operator("object.material_slot_assign", text="Assign")
                row.operator("object.material_slot_select", text="Select")
                row.operator("object.material_slot_deselect", text="Deselect")

        split = layout.split(percentage=0.65)

        if ob:
            split.template_ID(ob, "active_material", new="material.new")
            row = split.row()

            if slot:
                row.prop(slot, "link", text="")
            else:
                row.label()
        elif mat:
            split.template_ID(space, "pin_id")
            split.separator()


class MATERIAL_PT_psychopath_surface(PsychopathPanel, bpy.types.Panel):
    bl_label = "Surface"
    bl_space_type = "PROPERTIES"
    bl_region_type = "WINDOW"
    bl_context = "material"

    @classmethod
    def poll(cls, context):
        return context.material and PsychopathPanel.poll(context)

    def draw(self, context):
        layout = self.layout
        col = layout.column()

        mat = context.material
        col.prop(mat.psychopath, "surface_shader_type")

        col.prop(mat.psychopath, "color_type")
        if mat.psychopath.color_type == 'Rec709':
            col.prop(mat.psychopath, "color")
        elif mat.psychopath.color_type == 'Blackbody' or mat.psychopath.color_type == 'ColorTemperature':
            col.prop(mat.psychopath, "color_blackbody_temp")

        if mat.psychopath.surface_shader_type == 'GTR':
            layout.prop(mat.psychopath, "roughness")
            layout.prop(mat.psychopath, "tail_shape")
            layout.prop(mat.psychopath, "fresnel")

        if mat.psychopath.surface_shader_type == 'GGX':
            layout.prop(mat.psychopath, "roughness")
            layout.prop(mat.psychopath, "fresnel")


def register():
    bpy.utils.register_class(RENDER_PT_psychopath_render_settings)
    bpy.utils.register_class(RENDER_PT_psychopath_export_settings)
    bpy.utils.register_class(WORLD_PT_psychopath_background)
    bpy.utils.register_class(DATA_PT_psychopath_camera_dof)
    bpy.utils.register_class(DATA_PT_psychopath_mesh)
    bpy.utils.register_class(DATA_PT_psychopath_lamp)
    bpy.utils.register_class(DATA_PT_psychopath_area_lamp)
    bpy.utils.register_class(MATERIAL_PT_psychopath_context_material)
    bpy.utils.register_class(MATERIAL_PT_psychopath_surface)

def unregister():
    bpy.utils.unregister_class(RENDER_PT_psychopath_render_settings)
    bpy.utils.unregister_class(RENDER_PT_psychopath_export_settings)
    bpy.utils.unregister_class(WORLD_PT_psychopath_background)
    bpy.utils.unregister_class(DATA_PT_psychopath_camera_dof)
    bpy.utils.register_class(DATA_PT_psychopath_mesh)
    bpy.utils.unregister_class(DATA_PT_psychopath_lamp)
    bpy.utils.unregister_class(DATA_PT_psychopath_area_lamp)
    bpy.utils.unregister_class(MATERIAL_PT_psychopath_context_material)
    bpy.utils.unregister_class(MATERIAL_PT_psychopath_surface)


================================================
FILE: psychoblend/util.py
================================================
class ExportCancelled(Exception):
    """ Indicates that the render was cancelled in the middle of exporting
        the scene file.
    """
    pass


def mat2str(m):
    """ Converts a matrix into a single-line string of values.
    """
    s = ""
    for j in range(4):
        for i in range(4):
            s += (" %f" % m[i][j])
    return s[1:]


def needs_def_mb(ob):
    """ Determines if the given object needs to be exported with
        deformation motion blur or not.
    """
    anim = ob.animation_data
    no_anim_data = anim == None or (anim.action == None and len(anim.nla_tracks) == 0 and len(anim.drivers) == 0)

    for mod in ob.modifiers:
        if mod.type == 'SUBSURF':
            pass
        elif mod.type == 'MULTIRES':
            pass
        elif mod.type == 'MIRROR':
            if mod.mirror_object == None:
                pass
            else:
                return True
        elif mod.type == 'BEVEL' and no_anim_data:
            pass
        elif mod.type == 'EDGE_SPLIT' and no_anim_data:
            pass
        elif mod.type == 'SOLIDIFY' and no_anim_data:
            pass
        elif mod.type == 'MASK' and no_anim_data:
            pass
        elif mod.type == 'REMESH' and no_anim_data:
            pass
        elif mod.type == 'TRIANGULATE' and no_anim_data:
            pass
        elif mod.type == 'WIREFRAME' and no_anim_data:
            pass
        else:
            return True

    if ob.type == 'MESH':
        if ob.data.shape_keys == None:
            pass
        else:
            return True

    return False

def escape_name(name):
    name = name.replace("\\", "\\\\")
    name = name.replace(" ", "\\ ")
    name = name.replace("$", "\\$")
    name = name.replace("[", "\\[")
    name = name.replace("]", "\\]")
    name = name.replace("{", "\\{")
    name = name.replace("}", "\\}")
    return name


def needs_xform_mb(ob):
    """ Determines if the given object needs to be exported with
        transformation motion blur or not.
    """
    if ob.animation_data != None:
        return True

    if len(ob.constraints) > 0:
        return True

    if ob.parent != None:
        return needs_xform_mb(ob.parent)

    return False

================================================
FILE: psychoblend/world.py
================================================
import bpy

from math import degrees, tan, atan
from mathutils import Vector, Matrix

from .util import escape_name, mat2str, ExportCancelled

class World:
    def __init__(self, render_engine, scene, visible_layers, aspect_ratio):
        self.background_shader = BackgroundShader(render_engine, scene.world)
        self.camera = Camera(render_engine, scene.camera, aspect_ratio)
        self.lights = []

        # Collect infinite-extent light sources.
        # TODO: also get sun lamps inside group instances.
        for ob in scene.objects:
            if ob.type == 'LAMP' and ob.data.type == 'SUN':
                name = escape_name(ob.name)
                self.lights += [DistantDiskLamp(ob, name)]

    def take_sample(self, render_engine, scene, time):
        self.camera.take_sample(render_engine, scene, time)

        for light in self.lights:
            # Check if render is cancelled
            if render_engine.test_break():
                raise ExportCancelled()
            light.take_sample(render_engine, scene, time)

    def export(self, render_engine, w):
        self.camera.export(render_engine, w)

        w.write("World {\n")
        w.indent()

        self.background_shader.export(render_engine, w)

        for light in self.lights:
            light.export(render_engine, w)

        w.unindent()
        w.write("}\n")

    def cleanup(self):
        # For future use.  This is run by the calling code when finished,
        # even if export did not succeed.
        pass

#================================================================

class Camera:
    def __init__(self, render_engine, ob, aspect_ratio):
        self.ob = ob
        self.aspect_ratio = aspect_ratio

        self.fovs = []
        self.aperture_radii = []
        self.focal_distances = []
        self.xforms = []

    def take_sample(self, render_engine, scene, time):
        render_engine.update_stats("", "Psychopath: Collecting '{}' at time {}".format(self.ob.name, time))

        # Fov
        if self.aspect_ratio >= 1.0:
            self.fovs += [degrees(self.ob.data.angle)]
        else:
            self.fovs += [degrees(2.0 * atan(tan(self.ob.data.angle * 0.5) * self.aspect_ratio))]

        # Aperture radius
        self.aperture_radii += [self.ob.data.psychopath.aperture_radius]

        # Dof distance
        if self.ob.data.dof_object == None:
            self.focal_distances += [self.ob.data.dof_distance]
        else:
            # TODO: implement DoF object tracking here
            self.focal_distances += [0.0]
            print("WARNING: DoF object tracking not yet implemented.")

        # Transform
        mat = self.ob.matrix_world.copy()
        matz = Matrix()
        matz[2][2] = -1
        self.xforms += [mat * matz]

    def export(self, render_engine, w):
        render_engine.update_stats("", "Psychopath: Exporting %s" % self.ob.name)
        w.write("Camera {\n")
        w.indent()

        for fov in self.fovs:
            w.write("Fov [%f]\n" % fov)

        for rad in self.aperture_radii:
            w.write("ApertureRadius [%f]\n" % rad)

        for dist in self.focal_distances:
            w.write("FocalDistance [%f]\n" % dist)

        for mat in self.xforms:
            w.write("Transform [%s]\n" % mat2str(mat))

        w.unindent()
        w.write("}\n")


class BackgroundShader:
    def __init__(self, render_engine, world):
        self.world = world
        if self.world != None:
            self.color = (world.horizon_color[0], world.horizon_color[1], world.horizon_color[2])

    def export(self, render_engine, w):
        if self.world != None:
            w.write("BackgroundShader {\n")
            w.indent();
            w.write("Type [Color]\n")
            w.write("Color [rec709, %f %f %f]\n" % self.color)
            w.unindent()
            w.write("}\n")


class DistantDiskLamp:
    def __init__(self, ob, name):
        self.ob = ob
        self.name = name
        self.time_col = []
        self.time_dir = []
        self.time_rad = []

    def take_sample(self, render_engine, scene, time):
        render_engine.update_stats("", "Psychopath: Collecting '{}' at time {}".format(self.ob.name, time))
        self.time_dir += [tuple(self.ob.matrix_world.to_3x3() * Vector((0, 0, -1)))]

        if self.ob.data.psychopath.color_type == 'Rec709':
            self.time_col += [('Rec709', self.ob.data.color * self.ob.data.energy)]
        elif self.ob.data.psychopath.color_type == 'Blackbody':
            self.time_col += [('Blackbody', self.ob.data.psychopath.color_blackbody_temp, self.ob.data.energy)]
        elif self.ob.data.psychopath.color_type == 'ColorTemperature':
            self.time_col += [('ColorTemperature', self.ob.data.psychopath.color_blackbody_temp, self.ob.data.energy)]

        self.time_rad += [self.ob.data.shadow_soft_size]

    def export(self, render_engine, w):
        render_engine.update_stats("", "Psychopath: Exporting %s" % self.ob.name)
        w.write("DistantDiskLight $%s {\n" % self.name)
        w.indent()
        for direc in self.time_dir:
            w.write("Direction [%f %f %f]\n" % (direc[0], direc[1], direc[2]))
        for col in self.time_col:
            if col[0] == 'Rec709':
                w.write("Color [rec709, %f %f %f]\n" % (col[1][0], col[1][1], col[1][2]))
            elif col[0] == 'Blackbody':
                w.write("Color [blackbody, %f %f]\n" % (col[1], col[2]))
            elif col[0] == 'ColorTemperature':
                w.write("Color [color_temperature, %f %f]\n" % (col[1], col[2]))
        for rad in self.time_rad:
            w.write("Radius [%f]\n" % rad)

        w.unindent()
        w.write("}\n")


================================================
FILE: src/accel/bvh.rs
================================================
#![allow(dead_code)]

use mem_arena::MemArena;

use crate::{
    algorithm::partition, bbox::BBox, boundable::Boundable, lerp::lerp_slice, ray::AccelRay,
    timer::Timer,
};

use super::{
    bvh_base::{BVHBase, BVHBaseNode, BVH_MAX_DEPTH},
    ACCEL_NODE_RAY_TESTS, ACCEL_TRAV_TIME,
};

#[derive(Copy, Clone, Debug)]
pub struct BVH<'a> {
    root: Option<&'a BVHNode<'a>>,
    depth: usize,
}

#[derive(Copy, Clone, Debug)]
pub enum BVHNode<'a> {
    Internal {
        bounds_len: u16,
        split_axis: u8,
        bounds_start: &'a BBox,
        children: (&'a BVHNode<'a>, &'a BVHNode<'a>),
    },

    Leaf {
        bounds_start: &'a BBox,
        bounds_len: u16,
        object_range: (usize, usize),
    },
}

impl<'a> BVH<'a> {
    pub fn from_objects<'b, T, F>(
        arena: &'a MemArena,
        objects: &mut [T],
        objects_per_leaf: usize,
        bounder: F,
    ) -> BVH<'a>
    where
        F: 'b + Fn(&T) -> &'b [BBox],
    {
        if objects.is_empty() {
            BVH {
                root: None,
                depth: 0,
            }
        } else {
            let base = BVHBase::from_objects(objects, objects_per_leaf, bounder);

            BVH {
                root: Some(BVH::construct_from_base(
                    arena,
                    &base,
                    base.root_node_index(),
                )),
                depth: base.depth,
            }
        }
    }

    pub fn tree_depth(&self) -> usize {
        self.depth
    }

    pub fn traverse<T, F>(&self, rays: &mut [AccelRay], objects: &[T], mut obj_ray_test: F)
    where
        F: FnMut(&T, &mut [AccelRay]),
    {
        if self.root.is_none() {
            return;
        }

        let mut timer = Timer::new();
        let mut trav_time: f64 = 0.0;
        let mut node_tests: u64 = 0;

        let ray_sign = [
            rays[0].dir_inv.x() >= 0.0,
            rays[0].dir_inv.y() >= 0.0,
            rays[0].dir_inv.z() >= 0.0,
        ];

        // +2 of max depth for root and last child
        let mut node_stack = [self.root.unwrap(); BVH_MAX_DEPTH + 2];
        let mut ray_i_stack = [rays.len(); BVH_MAX_DEPTH + 2];
        let mut stack_ptr = 1;

        while stack_ptr > 0 {
            node_tests += ray_i_stack[stack_ptr] as u64;
            match *node_stack[stack_ptr] {
                BVHNode::Internal {
                    children,
                    bounds_start,
                    bounds_len,
                    split_axis,
                } => {
                    let bounds =
                        unsafe { std::slice::from_raw_parts(bounds_start, bounds_len as usize) };
                    let part = partition(&mut rays[..ray_i_stack[stack_ptr]], |r| {
                        (!r.is_done()) && lerp_slice(bounds, r.time).intersect_accel_ray(r)
                    });
                    if part > 0 {
                        ray_i_stack[stack_ptr] = part;
                        ray_i_stack[stack_ptr + 1] = part;
                        if ray_sign[split_axis as usize] {
                            node_stack[stack_ptr] = children.1;
                            node_stack[stack_ptr + 1] = children.0;
                        } else {
                            node_stack[stack_ptr] = children.0;
                            node_stack[stack_ptr + 1] = children.1;
                        }
                        stack_ptr += 1;
                    } else {
                        stack_ptr -= 1;
                    }
                }

                BVHNode::Leaf {
                    object_range,
                    bounds_start,
                    bounds_len,
                } => {
                    let bounds =
                        unsafe { std::slice::from_raw_parts(bounds_start, bounds_len as usize) };
                    let part = partition(&mut rays[..ray_i_stack[stack_ptr]], |r| {
                        (!r.is_done()) && lerp_slice(bounds, r.time).intersect_accel_ray(r)
                    });

                    trav_time += timer.tick() as f64;

                    if part > 0 {
                        for obj in &objects[object_range.0..object_range.1] {
                            obj_ray_test(obj, &mut rays[..part]);
                        }
                    }

                    timer.tick();

                    stack_ptr -= 1;
                }
            }
        }

        trav_time += timer.tick() as f64;
        ACCEL_TRAV_TIME.with(|att| {
            let v = att.get();
            att.set(v + trav_time);
        });
        ACCEL_NODE_RAY_TESTS.with(|anv| {
            let v = anv.get();
            anv.set(v + node_tests);
        });
    }

    #[allow(clippy::mut_from_ref)]
    fn construct_from_base(
        arena: &'a MemArena,
        base: &BVHBase,
        node_index: usize,
    ) -> &'a mut BVHNode<'a> {
        match base.nodes[node_index] {
            BVHBaseNode::Internal {
                bounds_range,
                children_indices,
                split_axis,
            } => {
                let node = unsafe { arena.alloc_uninitialized_with_alignment::<BVHNode>(32) };

                let bounds = arena
                    .copy_slice_with_alignment(&base.bounds[bounds_range.0..bounds_range.1], 32);
                let child1 = BVH::construct_from_base(arena, base, children_indices.0);
                let child2 = BVH::construct_from_base(arena, base, children_indices.1);

                *node = BVHNode::Internal {
                    bounds_len: bounds.len() as u16,
                    split_axis: split_axis,
                    bounds_start: &bounds[0],
                    children: (child1, child2),
                };

                node
            }

            BVHBaseNode::Leaf {
                bounds_range,
                object_range,
            } => {
                let node = unsafe { arena.alloc_uninitialized::<BVHNode>() };
                let bounds = arena.copy_slice(&base.bounds[bounds_range.0..bounds_range.1]);

                *node = BVHNode::Leaf {
                    bounds_start: &bounds[0],
                    bounds_len: bounds.len() as u16,
                    object_range: object_range,
                };

                node
            }
        }
    }
}

lazy_static! {
    static ref DEGENERATE_BOUNDS: [BBox; 1] = [BBox::new()];
}

impl<'a> Boundable for BVH<'a> {
    fn bounds(&self) -> &[BBox] {
        match self.root {
            None => &DEGENERATE_BOUNDS[..],
            Some(root) => match *root {
                BVHNode::Internal {
                    bounds_start,
                    bounds_len,
                    ..
                }
                | BVHNode::Leaf {
                    bounds_start,
                    bounds_len,
                    ..
                } => unsafe { std::slice::from_raw_parts(bounds_start, bounds_len as usize) },
            },
        }
    }
}


================================================
FILE: src/accel/bvh4.rs
================================================
//! This BVH4 implementation is based on the ideas from the paper
//! "Efficient Ray Tracing Kernels for Modern CPU Architectures"
//! by Fuetterling et al.

#![allow(dead_code)]

use std::mem::{transmute, MaybeUninit};

use glam::BVec4A;

use kioku::Arena;

use crate::{
    bbox::BBox,
    bbox4::BBox4,
    boundable::Boundable,
    lerp::lerp_slice,
    math::Vector,
    ray::{RayBatch, RayStack},
};

use super::{
    bvh_base::{BVHBase, BVHBaseNode, BVH_MAX_DEPTH},
    ACCEL_NODE_RAY_TESTS,
};

use bvh_order::{calc_traversal_code, SplitAxes, TRAVERSAL_TABLE};

pub fn ray_code(dir: Vector) -> usize {
    let ray_sign_is_neg = [dir.x() < 0.0, dir.y() < 0.0, dir.z() < 0.0];
    ray_sign_is_neg[0] as usize
        + ((ray_sign_is_neg[1] as usize) << 1)
        + ((ray_sign_is_neg[2] as usize) << 2)
}

#[derive(Copy, Clone, Debug)]
pub struct BVH4<'a> {
    root: Option<&'a BVH4Node<'a>>,
    depth: usize,
    node_count: usize,
    _bounds: Option<&'a [BBox]>,
}

#[derive(Copy, Clone, Debug)]
pub enum BVH4Node<'a> {
    Internal {
        bounds: &'a [BBox4],
        children: &'a [BVH4Node<'a>],
        traversal_code: u8,
    },

    Leaf {
        object_range: (usize, usize),
    },
}

impl<'a> BVH4<'a> {
    pub fn from_objects<'b, T, F>(
        arena: &'a Arena,
        objects: &mut [T],
        objects_per_leaf: usize,
        bounder: F,
    ) -> BVH4<'a>
    where
        F: 'b + Fn(&T) -> &'b [BBox],
    {
        if objects.is_empty() {
            BVH4 {
                root: None,
                depth: 0,
                node_count: 0,
                _bounds: None,
            }
        } else {
            let base = BVHBase::from_objects(objects, objects_per_leaf, bounder);

            let fill_node = arena.alloc_align_uninit::<BVH4Node>(32);
            let node_count = BVH4::construct_from_base(
                arena,
                &base,
                &base.nodes[base.root_node_index()],
                fill_node,
            );

            BVH4 {
                root: Some(unsafe { transmute(fill_node) }),
                depth: (base.depth / 2) + 1,
                node_count: node_count,
                _bounds: {
                    let range = base.nodes[base.root_node_index()].bounds_range();
                    Some(arena.copy_slice(&base.bounds[range.0..range.1]))
                },
            }
        }
    }

    pub fn tree_depth(&self) -> usize {
        self.depth
    }

    pub fn traverse<F>(&self, rays: &mut RayBatch, ray_stack: &mut RayStack, mut obj_ray_test: F)
    where
        F: FnMut(std::ops::Range<usize>, &mut RayBatch, &mut RayStack),
    {
        if self.root.is_none() {
            return;
        }

        let mut node_tests: u64 = 0;

        let traversal_table =
            &TRAVERSAL_TABLE[ray_code(rays.dir_inv_local(ray_stack.next_task_ray_idx(0)))];

        // +2 of max depth for root and last child
        let mut node_stack = [self.root.unwrap(); (BVH_MAX_DEPTH * 3) + 2];
        let mut stack_ptr = 1;

        while stack_ptr > 0 {
            match *node_stack[stack_ptr] {
                BVH4Node::Internal {
                    bounds,
                    children,
                    traversal_code,
                } => {
                    node_tests += ray_stack.ray_count_in_next_task() as u64;
                    let mut all_hits = BVec4A::default();

                    // Ray testing
                    ray_stack.pop_do_next_task_and_push_rays(children.len(), |ray_idx| {
                        if rays.is_done(ray_idx) {
                            BVec4A::default()
                        } else {
                            let hits = if bounds.len() == 1 {
                                bounds[0].intersect_ray(
                                    rays.orig_local(ray_idx),
                                    rays.dir_inv_local(ray_idx),
                                    rays.max_t(ray_idx),
                                )
                            } else {
                                lerp_slice(bounds, rays.time(ray_idx)).intersect_ray(
                                    rays.orig_local(ray_idx),
                                    rays.dir_inv_local(ray_idx),
                                    rays.max_t(ray_idx),
                                )
                            };
                            all_hits |= hits;
                            hits
                        }
                    });

                    // If there were any intersections, create tasks.
                    if all_hits.any() {
                        let order_code = traversal_table[traversal_code as usize];
                        let mut lane_count = 0;
                        let mut i = children.len() as u8;
                        while i > 0 {
                            i -= 1;
                            let child_i = ((order_code >> (i * 2)) & 3) as usize;
                            if ray_stack.push_lane_to_task(child_i) {
                                node_stack[stack_ptr + lane_count] = &children[child_i];
                                lane_count += 1;
                            }
                        }

                        stack_ptr += lane_count - 1;
                    } else {
                        stack_ptr -= 1;
                    }
                }

                BVH4Node::Leaf { object_range } => {
                    // Do the ray tests.
                    obj_ray_test(object_range.0..object_range.1, rays, ray_stack);

                    stack_ptr -= 1;
                }
            }
        }

        ACCEL_NODE_RAY_TESTS.with(|anv| {
            let v = anv.get();
            anv.set(v + node_tests);
        });
    }

    fn construct_from_base(
        arena: &'a Arena,
        base: &BVHBase,
        node: &BVHBaseNode,
        fill_node: &mut MaybeUninit<BVH4Node<'a>>,
    ) -> usize {
        let mut node_count = 0;

        match *node {
            // Create internal node
            BVHBaseNode::Internal {
                children_indices,
                split_axis,
                ..
            } => {
                let child_l = &base.nodes[children_indices.0];
                let child_r = &base.nodes[children_indices.1];

                // Prepare convenient access to the stuff we need.
                let child_count: usize;
                let children; // [Optional, Optional, Optional, Optional]
                let split_info: SplitAxes;
                match *child_l {
                    BVHBaseNode::Internal {
                        children_indices: i_l,
                        split_axis: s_l,
                        ..
                    } => {
                        match *child_r {
                            BVHBaseNode::Internal {
                                children_indices: i_r,
                                split_axis: s_r,
                                ..
                            } => {
                                // Four nodes
                                child_count = 4;
                                children = [
                                    Some(&base.nodes[i_l.0]),
                                    Some(&base.nodes[i_l.1]),
                                    Some(&base.nodes[i_r.0]),
                                    Some(&base.nodes[i_r.1]),
                                ];
                                split_info = SplitAxes::Full((split_axis, s_l, s_r));
                            }
                            BVHBaseNode::Leaf { .. } => {
                                // Three nodes with left split
                                child_count = 3;
                                children = [
                                    Some(&base.nodes[i_l.0]),
                                    Some(&base.nodes[i_l.1]),
                                    Some(child_r),
                                    None,
                                ];
                                split_info = SplitAxes::Left((split_axis, s_l));
                            }
                        }
                    }
                    BVHBaseNode::Leaf { .. } => {
                        match *child_r {
                            BVHBaseNode::Internal {
                                children_indices: i_r,
                                split_axis: s_r,
                                ..
                            } => {
                                // Three nodes with right split
                                child_count = 3;
                                children = [
                                    Some(child_l),
                                    Some(&base.nodes[i_r.0]),
                                    Some(&base.nodes[i_r.1]),
                                    None,
                                ];
                                split_info = SplitAxes::Right((split_axis, s_r));
                            }
                            BVHBaseNode::Leaf { .. } => {
                                // Two nodes
                                child_count = 2;
                                children = [Some(child_l), Some(child_r), None, None];
                                split_info = SplitAxes::TopOnly(split_axis);
                            }
                        }
                    }
                }

                node_count += child_count;

                // Construct bounds
                let bounds = {
                    let bounds_len = children
                        .iter()
                        .map(|c| {
                            if let Some(n) = *c {
                                let len = n.bounds_range().1 - n.bounds_range().0;
                                debug_assert!(len >= 1);
                                len
                            } else {
                                0
                            }
                        })
                        .max()
                        .unwrap();
                    debug_assert!(bounds_len >= 1);
                    let bounds = arena.alloc_array_align_uninit(bounds_len, 32);
                    if bounds_len < 2 {
                        let b1 =
                            children[0].map_or(BBox::new(), |c| base.bounds[c.bounds_range().0]);
                        let b2 =
                            children[1].map_or(BBox::new(), |c| base.bounds[c.bounds_range().0]);
                        let b3 =
                            children[2].map_or(BBox::new(), |c| base.bounds[c.bounds_range().0]);
                        let b4 =
                            children[3].map_or(BBox::new(), |c| base.bounds[c.bounds_range().0]);
                        unsafe {
                            *bounds[0].as_mut_ptr() = BBox4::from_bboxes(b1, b2, b3, b4);
                        }
                    } else {
                        for (i, b) in bounds.iter_mut().enumerate() {
                            let time = i as f32 / (bounds_len - 1) as f32;

                            let b1 = children[0].map_or(BBox::new(), |c| {
                                let (x, y) = c.bounds_range();
                                lerp_slice(&base.bounds[x..y], time)
                            });
                            let b2 = children[1].map_or(BBox::new(), |c| {
                                let (x, y) = c.bounds_range();
                                lerp_slice(&base.bounds[x..y], time)
                            });
                            let b3 = children[2].map_or(BBox::new(), |c| {
                                let (x, y) = c.bounds_range();
                                lerp_slice(&base.bounds[x..y], time)
                            });
                            let b4 = children[3].map_or(BBox::new(), |c| {
                                let (x, y) = c.bounds_range();
                                lerp_slice(&base.bounds[x..y], time)
                            });
                            unsafe {
                                *b.as_mut_ptr() = BBox4::from_bboxes(b1, b2, b3, b4);
                            }
                        }
                    }
                    bounds
                };

                // Construct child nodes
                let child_nodes = arena.alloc_array_align_uninit::<BVH4Node>(child_count, 32);
                for (i, c) in children[0..child_count].iter().enumerate() {
                    node_count +=
                        BVH4::construct_from_base(arena, base, c.unwrap(), &mut child_nodes[i]);
                }

                // Build this node
                unsafe {
                    *fill_node.as_mut_ptr() = BVH4Node::Internal {
                        bounds: transmute(bounds),
                        children: transmute(child_nodes),
                        traversal_code: calc_traversal_code(split_info),
                    };
                }
            }

            // Create internal node
            BVHBaseNode::Leaf { object_range, .. } => {
                unsafe {
                    *fill_node.as_mut_ptr() = BVH4Node::Leaf {
                        object_range: object_range,
                    };
                }
                node_count += 1;
            }
        }

        return node_count;
    }
}

impl<'a> Boundable for BVH4<'a> {
    fn bounds<'b>(&'b self) -> &'b [BBox] {
        self._bounds.unwrap_or(&[])
    }
}


================================================
FILE: src/accel/bvh_base.rs
================================================
#![allow(dead_code)]

use crate::{algorithm::merge_slices_append, bbox::BBox, lerp::lerp_slice, math::log2_64};

use super::objects_split::{median_split, sah_split};

pub const BVH_MAX_DEPTH: usize = 42;

// Amount bigger the union of all time samples can be
// and still use the union rather than preserve the
// individual time samples.
const USE_UNION_FACTOR: f32 = 1.4;

/// An intermediary structure for creating a BVH.
#[derive(Debug)]
pub struct BVHBase {
    pub nodes: Vec<BVHBaseNode>,
    pub bounds: Vec<BBox>,
    pub depth: usize,
    bounds_cache: Vec<BBox>,
}

#[derive(Copy, Clone, Debug)]
pub enum BVHBaseNode {
    Internal {
        bounds_range: (usize, usize),
        children_indices: (usize, usize),
        split_axis: u8,
    },

    Leaf {
        bounds_range: (usize, usize),
        object_range: (usize, usize),
    },
}

impl BVHBaseNode {
    pub fn bounds_range(&self) -> (usize, usize) {
        match *self {
            BVHBaseNode::Internal { bounds_range, .. } | BVHBaseNode::Leaf { bounds_range, .. } => {
                bounds_range
            }
        }
    }
}

impl BVHBase {
    fn new() -> BVHBase {
        BVHBase {
            nodes: Vec::new(),
            bounds: Vec::new(),
            depth: 0,
            bounds_cache: Vec::new(),
        }
    }

    pub fn from_objects<'b, T, F>(objects: &mut [T], objects_per_leaf: usize, bounder: F) -> BVHBase
    where
        F: 'b + Fn(&T) -> &'b [BBox],
    {
        let mut bvh = BVHBase::new();
        bvh.recursive_build(0, 0, objects_per_leaf, objects, &bounder);
        bvh
    }

    pub fn root_node_index(&self) -> usize {
        0
    }

    fn acc_bounds<'a, T, F>(&mut self, objects: &mut [T], bounder: &F)
    where
        F: 'a + Fn(&T) -> &'a [BBox],
    {
        // TODO: do all of this without the temporary cache
        let max_len = objects.iter().map(|obj| bounder(obj).len()).max().unwrap();

        self.bounds_cache.clear();
        self.bounds_cache.resize(max_len, BBox::new());

        for obj in objects.iter() {
            let bounds = bounder(obj);
            debug_assert!(!bounds.is_empty());
            if bounds.len() == max_len {
                for i in 0..bounds.len() {
                    self.bounds_cache[i] |= bounds[i];
                }
            } else {
                let s = (max_len - 1) as f32;
                for (i, bbc) in self.bounds_cache.iter_mut().enumerate() {
                    *bbc |= lerp_slice(bounds, i as f32 / s);
                }
            }
        }
    }

    fn recursive_build<'a, T, F>(
        &mut self,
        offset: usize,
        depth: usize,
        objects_per_leaf: usize,
        objects: &mut [T],
        bounder: &F,
    ) -> (usize, (usize, usize))
    where
        F: 'a + Fn(&T) -> &'a [BBox],
    {
        let me = self.nodes.len();

        if objects.is_empty() {
            return (0, (0, 0));
        } else if objects.len() <= objects_per_leaf {
            // Leaf node
            let bi = self.bounds.len();
            // Get bounds
            {
                // We make sure that it's worth having multiple time samples, and if not
                // we reduce to the union of the time samples.
                self.acc_bounds(objects, bounder);
                let union_bounds = self
                    .bounds_cache
                    .iter()
                    .fold(BBox::new(), |b1, b2| (b1 | *b2));
                let average_area = self
                    .bounds_cache
                    .iter()
                    .fold(0.0, |area, bb| area + bb.surface_area())
                    / self.bounds_cache.len() as f32;
                if union_bounds.surface_area() <= (average_area * USE_UNION_FACTOR) {
                    self.bounds.push(union_bounds);
                } else {
                    self.bounds.extend(&self.bounds_cache);
                }
            }

            // Create node
            self.nodes.push(BVHBaseNode::Leaf {
                bounds_range: (bi, self.bounds.len()),
                object_range: (offset, offset + objects.len()),
            });

            if self.depth < depth {
                self.depth = depth;
            }

            return (me, (bi, self.bounds.len()));
        } else {
            // Not a leaf node
            self.nodes.push(BVHBaseNode::Internal {
                bounds_range: (0, 0),
                children_indices: (0, 0),
                split_axis: 0,
            });

            // Partition objects.
            // If we're too near the max depth, we do balanced building to
            // avoid exceeding max depth.
            // Otherwise we do SAH splitting to build better trees.
            let (split_index, split_axis) =
                if (log2_64(objects.len() as u64) as usize) < (BVH_MAX_DEPTH - depth) {
                    // SAH splitting, when we have room to play
                    sah_split(objects, &bounder)
                } else {
                    // Balanced splitting, when we don't have room to play
                    median_split(objects, &bounder)
                };

            // Create child nodes
            let (c1_index, c1_bounds) = self.recursive_build(
                offset,
                depth + 1,
                objects_per_leaf,
                &mut objects[..split_index],
                bounder,
            );
            let (c2_index, c2_bounds) = self.recursive_build(
                offset + split_index,
                depth + 1,
                objects_per_leaf,
                &mut objects[split_index..],
                bounder,
            );

            // Determine bounds
            // TODO: do merging without the temporary vec.
            let bi = self.bounds.len();
            {
                let mut merged = Vec::new();
                merge_slices_append(
                    &self.bounds[c1_bounds.0..c1_bounds.1],
                    &self.bounds[c2_bounds.0..c2_bounds.1],
                    &mut merged,
                    |b1, b2| *b1 | *b2,
                );
                // We make sure that it's worth having multiple time samples, and if not
                // we reduce to the union of the time samples.
                let union_bounds = merged.iter().fold(BBox::new(), |b1, b2| (b1 | *b2));
                let average_area = merged.iter().fold(0.0, |area, bb| area + bb.surface_area())
                    / merged.len() as f32;
                if union_bounds.surface_area() <= (average_area * USE_UNION_FACTOR) {
                    self.bounds.push(union_bounds);
                } else {
                    self.bounds.extend(merged.drain(0..));
                }
            }

            // Set node
            self.nodes[me] = BVHBaseNode::Internal {
                bounds_range: (bi, self.bounds.len()),
                children_indices: (c1_index, c2_index),
                split_axis: split_axis as u8,
            };

            return (me, (bi, self.bounds.len()));
        }
    }
}


================================================
FILE: src/accel/light_array.rs
================================================
use kioku::Arena;

use crate::{
    bbox::BBox,
    math::{Normal, Point, Vector},
    shading::surface_closure::SurfaceClosure,
};

use super::LightAccel;

#[derive(Debug, Copy, Clone)]
pub struct LightArray<'a> {
    indices: &'a [usize],
    aprx_energy: f32,
}

impl<'a> LightArray<'a> {
    #[allow(dead_code)]
    pub fn from_objects<'b, T, F>(
        arena: &'a Arena,
        objects: &mut [T],
        info_getter: F,
    ) -> LightArray<'a>
    where
        F: 'b + Fn(&T) -> (&'b [BBox], f32),
    {
        let mut indices = Vec::new();
        let mut aprx_energy = 0.0;
        for (i, thing) in objects.iter().enumerate() {
            let (_, power) = info_getter(thing);
            if power > 0.0 {
                indices.push(i);
                aprx_energy += power;
            }
        }

        LightArray {
            indices: arena.copy_slice(&indices),
            aprx_energy: aprx_energy,
        }
    }
}

impl<'a> LightAccel for LightArray<'a> {
    fn select(
        &self,
        inc: Vector,
        pos: Point,
        nor: Normal,
        nor_g: Normal,
        sc: &SurfaceClosure,
        time: f32,
        n: f32,
    ) -> Option<(usize, f32, f32)> {
        let _ = (inc, pos, nor, nor_g, sc, time); // Not using these, silence warnings

        assert!(n >= 0.0 && n <= 1.0);

        if self.indices.is_empty() {
            return None;
        }

        let n2 = n * self.indices.len() as f32;
        let i = if n == 1.0 {
            *self.indices.last().unwrap()
        } else {
            self.indices[n2 as usize]
        };

        let whittled_n = n2 - i as f32;
        let pdf = 1.0 / self.indices.len() as f32;

        Some((i, pdf, whittled_n))
    }

    fn approximate_energy(&self) -> f32 {
        self.aprx_energy
    }
}


================================================
FILE: src/accel/light_tree.rs
================================================
use std::mem::{transmute, MaybeUninit};

use kioku::Arena;

use crate::{
    algorithm::merge_slices_append,
    bbox::BBox,
    lerp::lerp_slice,
    math::{Normal, Point, Vector},
    shading::surface_closure::SurfaceClosure,
};

use super::{objects_split::sah_split, LightAccel};

const ARITY_LOG2: usize = 3; // Determines how much to collapse the binary tree,
                             // implicitly defining the light tree's arity.  1 = no collapsing, leave as binary
                             // tree.
const ARITY: usize = 1 << ARITY_LOG2; // Arity of the final tree

#[derive(Copy, Clone, Debug)]
pub struct LightTree<'a> {
    root: Option<&'a Node<'a>>,
    depth: usize,
}

#[derive(Copy, Clone, Debug)]
enum Node<'a> {
    Inner {
        children: &'a [Node<'a>],
        bounds: &'a [BBox],
        energy: f32,
    },
    Leaf {
        light_index: usize,
        bounds: &'a [BBox],
        energy: f32,
    },
}

impl<'a> Node<'a> {
    fn bounds(&self) -> &'a [BBox] {
        match *self {
            Node::Inner { bounds, .. } | Node::Leaf { bounds, .. } => bounds,
        }
    }

    fn energy(&self) -> f32 {
        match *self {
            Node::Inner { energy, .. } | Node::Leaf { energy, .. } => energy,
        }
    }

    fn light_index(&self) -> usize {
        match *self {
            Node::Inner { .. } => panic!(),
            Node::Leaf { light_index, .. } => light_index,
        }
    }
}

impl<'a> LightTree<'a> {
    pub fn from_objects<'b, T, F>(
        arena: &'a Arena,
        objects: &mut [T],
        info_getter: F,
    ) -> LightTree<'a>
    where
        F: 'b + Fn(&T) -> (&'b [BBox], f32),
    {
        if objects.is_empty() {
            LightTree {
                root: None,
                depth: 0,
            }
        } else {
            let mut builder = LightTreeBuilder::new();
            builder.recursive_build(0, 0, objects, &info_getter);

            let root = arena.alloc_uninit::<Node>();
            LightTree::construct_from_builder(arena, &builder, builder.root_node_index(), root);

            LightTree {
                root: Some(unsafe { transmute(root) }),
                depth: builder.depth,
            }
        }
    }

    fn construct_from_builder(
        arena: &'a Arena,
        base: &LightTreeBuilder,
        node_index: usize,
        node_mem: &mut MaybeUninit<Node<'a>>,
    ) {
        if base.nodes[node_index].is_leaf {
            // Leaf
            let bounds_range = base.nodes[node_index].bounds_range;
            let bounds = arena.copy_slice(&base.bounds[bounds_range.0..bounds_range.1]);

            unsafe {
                *node_mem.as_mut_ptr() = Node::Leaf {
                    light_index: base.nodes[node_index].child_index,
                    bounds: bounds,
                    energy: base.nodes[node_index].energy,
                };
            }
        } else {
            // Inner
            let bounds_range = base.nodes[node_index].bounds_range;
            let bounds = arena.copy_slice(&base.bounds[bounds_range.0..bounds_range.1]);

            let child_count = base.node_child_count(node_index);
            let children = arena.alloc_array_uninit::<Node>(child_count);
            for i in 0..child_count {
                LightTree::construct_from_builder(
                    arena,
                    base,
                    base.node_nth_child_index(node_index, i),
                    &mut children[i],
                );
            }

            unsafe {
                *node_mem.as_mut_ptr() = Node::Inner {
                    children: transmute(children),
                    bounds: bounds,
                    energy: base.nodes[node_index].energy,
                };
            }
        }
    }
}

impl<'a> LightAccel for LightTree<'a> {
    fn select(
        &self,
        inc: Vector,
        pos: Point,
        nor: Normal,
        nor_g: Normal,
        sc: &SurfaceClosure,
        time: f32,
        n: f32,
    ) -> Option<(usize, f32, f32)> {
        // Calculates the selection probability for a node
        let node_prob = |node_ref: &Node| {
            let bbox = lerp_slice(node_ref.bounds(), time);
            let d = bbox.center() - pos;
            let r2 = bbox.diagonal2() * 0.25;
            let inv_surface_area = 1.0 / r2;

            // Get the approximate amount of light contribution from the
            // composite light source.
            let approx_contrib = sc.estimate_eval_over_sphere_light(inc, d, r2, nor, nor_g);
            node_ref.energy() * inv_surface_area * approx_contrib
        };

        // Traverse down the tree, keeping track of the relative probabilities
        let mut node = self.root?;
        let mut tot_prob = 1.0;
        let mut n = n;
        while let Node::Inner { children, .. } = *node {
            // Calculate the relative probabilities of the children
            let ps = {
                let mut ps = [0.0; ARITY];
                let mut total = 0.0;
                for (i, child) in children.iter().enumerate() {
                    let p = node_prob(child);
                    ps[i] = p;
                    total += p;
                }
                if total <= 0.0 {
                    let p = 1.0 / children.len() as f32;
                    for prob in &mut ps[..] {
                        *prob = p;
                    }
                } else {
                    for prob in &mut ps[..] {
                        *prob /= total;
                    }
                }
                ps
            };

            // Pick child and update probabilities
            let mut base = 0.0;
            for (i, &p) in ps.iter().enumerate() {
                if (n <= base + p) || (i == children.len() - 1) {
                    tot_prob *= p;
                    node = &children[i];
                    n = (n - base) / p;
                    break;
                } else {
                    base += p;
                }
            }
        }

        // Found our light!
        Some((node.light_index(), tot_prob, n))
    }

    fn approximate_energy(&self) -> f32 {
        if let Some(node) = self.root {
            node.energy()
        } else {
            0.0
        }
    }
}

struct LightTreeBuilder {
    nodes: Vec<BuilderNode>,
    bounds: Vec<BBox>,
    depth: usize,
}

#[derive(Copy, Clone, Debug)]
struct BuilderNode {
    is_leaf: bool,
    bounds_range: (usize, usize),
    energy: f32,
    child_index: usize,
}

impl LightTreeBuilder {
    fn new() -> LightTreeBuilder {
        LightTreeBuilder {
            nodes: Vec::new(),
            bounds: Vec::new(),
            depth: 0,
        }
    }

    pub fn root_node_index(&self) -> usize {
        0
    }

    // Returns the number of children of this node, assuming a collapse
    // number of `ARITY_LOG2`.
    pub fn node_child_count(&self, node_index: usize) -> usize {
        self.node_child_count_recurse(ARITY_LOG2, node_index)
    }

    // Returns the index of the nth child, assuming a collapse
    // number of `ARITY_LOG2`.
    pub fn node_nth_child_index(&self, node_index: usize, child_n: usize) -> usize {
        self.node_nth_child_index_recurse(ARITY_LOG2, node_index, child_n)
            .0
    }

    // Returns the number of children of this node, assuming a collapse
    // number of `level_collapse`.
    pub fn node_child_count_recurse(&self, level_collapse: usize, node_index: usize) -> usize {
        if level_collapse > 0 {
            if self.nodes[node_index].is_leaf {
                1
            } else {
                let a = self.node_child_count_recurse(level_collapse - 1, node_index + 1);
                let b = self.node_child_count_recurse(
                    level_collapse - 1,
                    self.nodes[node_index].child_index,
                );

                a + b
            }
        } else {
            1
        }
    }

    // Returns the index of the nth child, assuming a collapse
    // number of `level_collapse`.
    pub fn node_nth_child_index_recurse(
        &self,
        level_collapse: usize,
        node_index: usize,
        child_n: usize,
    ) -> (usize, usize) {
        if level_collapse > 0 && !self.nodes[node_index].is_leaf {
            let (index, rem) =
                self.node_nth_child_index_recurse(level_collapse - 1, node_index + 1, child_n);
            if rem == 0 {
                return (index, 0);
            }
            return self.node_nth_child_index_recurse(
                level_collapse - 1,
                self.nodes[node_index].child_index,
                rem - 1,
            );
        } else {
            return (node_index, child_n);
        }
    }

    fn recursive_build<'a, T, F>(
        &mut self,
        offset: usize,
        depth: usize,
        objects: &mut [T],
        info_getter: &F,
    ) -> (usize, (usize, usize))
    where
        F: 'a + Fn(&T) -> (&'a [BBox], f32),
    {
        let me_index = self.nodes.len();

        if objects.is_empty() {
            return (0, (0, 0));
        } else if objects.len() == 1 {
            // Leaf node
            let bi = self.bounds.len();
            let (obj_bounds, energy) = info_getter(&objects[0]);
            self.bounds.extend(obj_bounds);
            self.nodes.push(BuilderNode {
                is_leaf: true,
                bounds_range: (bi, self.bounds.len()),
                energy: energy,
                child_index: offset,
            });

            if self.depth < depth {
                self.depth = depth;
            }

            return (me_index, (bi, self.bounds.len()));
        } else {
            // Not a leaf node
            self.nodes.push(BuilderNode {
                is_leaf: false,
                bounds_range: (0, 0),
                energy: 0.0,
                child_index: 0,
            });

            // Partition objects.
            let (split_index, _) = sah_split(objects, &|obj_ref| info_getter(obj_ref).0);

            // Create child nodes
            let (_, c1_bounds) =
                self.recursive_build(offset, depth + 1, &mut objects[..split_index], info_getter);
            let (c2_index, c2_bounds) = self.recursive_build(
                offset + split_index,
                depth + 1,
                &mut objects[split_index..],
                info_getter,
            );

            // Determine bounds
            // TODO: do merging without the temporary vec.
            let bi = self.bounds.len();
            let mut merged = Vec::new();
            merge_slices_append(
                &self.bounds[c1_bounds.0..c1_bounds.1],
                &self.bounds[c2_bounds.0..c2_bounds.1],
                &mut merged,
                |b1, b2| *b1 | *b2,
            );
            self.bounds.extend(merged.drain(0..));

            // Set node
            let energy = self.nodes[me_index + 1].energy + self.nodes[c2_index].energy;
            self.nodes[me_index] = BuilderNode {
                is_leaf: false,
                bounds_range: (bi, self.bounds.len()),
                energy: energy,
                child_index: c2_index,
            };

            return (me_index, (bi, self.bounds.len()));
        }
    }
}


================================================
FILE: src/accel/mod.rs
================================================
// mod bvh;
mod bvh4;
mod bvh_base;
mod light_array;
mod light_tree;
mod objects_split;

use std::cell::Cell;

use crate::{
    math::{Normal, Point, Vector},
    shading::surface_closure::SurfaceClosure,
};

pub use self::{
    // bvh::{BVHNode, BVH},
    bvh4::{ray_code, BVH4Node, BVH4},
    light_array::LightArray,
    light_tree::LightTree,
};

// Track BVH traversal time
thread_local! {
    pub static ACCEL_NODE_RAY_TESTS: Cell<u64> = Cell::new(0);
}

pub trait LightAccel {
    /// Returns (index_of_light, selection_pdf, whittled_n)
    fn select(
        &self,
        inc: Vector,
        pos: Point,
        nor: Normal,
        nor_g: Normal,
        sc: &SurfaceClosure,
        time: f32,
        n: f32,
    ) -> Option<(usize, f32, f32)>;

    fn approximate_energy(&self) -> f32;
}


================================================
FILE: src/accel/objects_split.rs
================================================
#![allow(dead_code)]

use std::cmp::Ordering;

use crate::{
    algorithm::{partition, quick_select},
    bbox::BBox,
    lerp::lerp_slice,
    math::{dot, Vector},
    sampling::uniform_sample_hemisphere,
};

const SAH_BIN_COUNT: usize = 13; // Prime numbers work best, for some reason
const SPLIT_PLANE_COUNT: usize = 5;

/// Takes a slice of boundable objects and partitions them based on the Surface
/// Area Heuristic, but using arbitrarily oriented planes.
///
/// Returns the index of the partition boundary and the axis that it split on
/// (0 = x, 1 = y, 2 = z).
pub fn free_sah_split<'a, T, F>(seed: u32, objects: &mut [T], bounder: &F) -> (usize, usize)
where
    F: Fn(&T) -> &'a [BBox],
{
    // Generate the planes for splitting
    let planes = {
        let mut planes = [Vector::new(0.0, 0.0, 0.0); SPLIT_PLANE_COUNT];
        let offset = seed * SPLIT_PLANE_COUNT as u32;
        for i in 0..SPLIT_PLANE_COUNT {
            let u = halton::sample(0, offset + i as u32);
            let v = halton::sample(1, offset + i as u32);
            planes[i] = uniform_sample_hemisphere(u, v).normalized();
        }
        planes
    };

    // Get the extents of the objects with respect to the split planes
    let extents = {
        let mut extents = [(std::f32::INFINITY, std::f32::NEG_INFINITY); SPLIT_PLANE_COUNT];
        for obj in &objects[..] {
            let centroid = lerp_slice(bounder(obj), 0.5).center().into_vector();
            for i in 0..SPLIT_PLANE_COUNT {
                let dist = dot(centroid, planes[i]);
                extents[i].0 = extents[i].0.min(dist);
                extents[i].1 = extents[i].1.max(dist);
            }
        }
        extents
    };

    // Pre-calc SAH div distances
    let sah_divs = {
        let mut sah_divs = [[0.0f32; SAH_BIN_COUNT - 1]; SPLIT_PLANE_COUNT];
        for pi in 0..SPLIT_PLANE_COUNT {
            let extent = extents[pi].1 - extents[pi].0;
            for div in 0..(SAH_BIN_COUNT - 1) {
                let part = extent * ((div + 1) as f32 / SAH_BIN_COUNT as f32);
                sah_divs[pi][div] = extents[pi].0 + part;
            }
        }
        sah_divs
    };

    // Build SAH bins
    let sah_bins = {
        let mut sah_bins =
            [[(BBox::new(), BBox::new(), 0, 0); SAH_BIN_COUNT - 1]; SPLIT_PLANE_COUNT];
        for obj in objects.iter() {
            let tb = lerp_slice(bounder(obj), 0.5);
            let centroid = tb.center().into_vector();

            for pi in 0..SPLIT_PLANE_COUNT {
                for div in 0..(SAH_BIN_COUNT - 1) {
                    let dist = dot(centroid, planes[pi]);
                    if dist <= sah_divs[pi][div] {
                        sah_bins[pi][div].0 |= tb;
                        sah_bins[pi][div].2 += 1;
                    } else {
                        sah_bins[pi][div].1 |= tb;
                        sah_bins[pi][div].3 += 1;
                    }
                }
            }
        }
        sah_bins
    };

    // Find best split axis and div point
    let (split_plane_i, div_n) = {
        let mut split_plane_i = 0;
        let mut div_n = 0.0;
        let mut smallest_cost = std::f32::INFINITY;

        for pi in 0..SPLIT_PLANE_COUNT {
            for div in 0..(SAH_BIN_COUNT - 1) {
                let left_cost = sah_bins[pi][div].0.surface_area() * sah_bins[pi][div].2 as f32;
                let right_cost = sah_bins[pi][div].1.surface_area() * sah_bins[pi][div].3 as f32;
                let tot_cost = left_cost + right_cost;
                if tot_cost < smallest_cost {
                    split_plane_i = pi;
                    div_n = sah_divs[pi][div];
                    smallest_cost = tot_cost;
                }
            }
        }

        (split_plane_i, div_n)
    };

    // Calculate the approximate axis-aligned split, along with flipping the split plane as
    // appropriate.
    let (plane, approx_axis, div) = {
        // Find axis with largest value
        let mut largest_axis = 0;
        let mut n = 0.0;
        for d in 0..3 {
            let m = planes[split_plane_i].get_n(d).abs();
            if n < m {
                largest_axis = d;
                n = m;
            }
        }

        // If it's negative, flip
        if planes[split_plane_i].get_n(largest_axis).is_sign_positive() {
            (planes[split_plane_i], largest_axis, div_n)
        } else {
            (planes[split_plane_i] * -1.0, largest_axis, div_n * -1.0)
        }
    };

    // Partition
    let mut split_i = partition(&mut objects[..], |obj| {
        let centroid = lerp_slice(bounder(obj), 0.5).center().into_vector();
        let dist = dot(centroid, plane);
        dist < div
    });

    if split_i < 1 {
        split_i = 1;
    } else if split_i >= objects.len() {
        split_i = objects.len() - 1;
    }

    (split_i, approx_axis)
}

/// Takes a slice of boundable objects and partitions them based on the Surface
/// Area Heuristic.
///
/// Returns the index of the partition boundary and the axis that it split on
/// (0 = x, 1 = y, 2 = z).
pub fn sah_split<'a, T, F>(objects: &mut [T], bounder: &F) -> (usize, usize)
where
    F: Fn(&T) -> &'a [BBox],
{
    // Get combined object centroid extents
    let bounds = {
        let mut bb = BBox::new();
        for obj in &objects[..] {
            bb |= lerp_slice(bounder(obj), 0.5).center();
        }
        bb
    };

    // Pre-calc SAH div points
    let sah_divs = {
        let mut sah_divs = [[0.0f32; SAH_BIN_COUNT - 1]; 3];
        for d in 0..sah_divs.len() {
            let extent = bounds.max.get_n(d) - bounds.min.get_n(d);
            for div in 0..(SAH_BIN_COUNT - 1) {
                let part = extent * ((div + 1) as f32 / SAH_BIN_COUNT as f32);
                sah_divs[d][div] = bounds.min.get_n(d) + part;
            }
        }
        sah_divs
    };

    // Build SAH bins
    let sah_bins = {
        let mut sah_bins = [[(BBox::new(), BBox::new(), 0, 0); SAH_BIN_COUNT - 1]; 3];
        for obj in objects.iter() {
            let tb = lerp_slice(bounder(obj), 0.5);
            let centroid = (tb.min.into_vector() + tb.max.into_vector()) * 0.5;

            for d in 0..3 {
                for div in 0..(SAH_BIN_COUNT - 1) {
                    if centroid.get_n(d) <= sah_divs[d][div] {
                        sah_bins[d][div].0 |= tb;
                        sah_bins[d][div].2 += 1;
                    } else {
                        sah_bins[d][div].1 |= tb;
                        sah_bins[d][div].3 += 1;
                    }
                }
            }
        }
        sah_bins
    };

    // Find best split axis and div point
    let (split_axis, div) = {
        let mut dim = 0;
        let mut div_n = 0.0;
        let mut smallest_cost = std::f32::INFINITY;

        for d in 0..3 {
            for div in 0..(SAH_BIN_COUNT - 1) {
                let left_cost = sah_bins[d][div].0.surface_area() * sah_bins[d][div].2 as f32;
                let right_cost = sah_bins[d][div].1.surface_area() * sah_bins[d][div].3 as f32;
                let left_diag = sah_bins[d][div].0.diagonal();
                let right_diag = sah_bins[d][div].1.diagonal();
                let tot_cost = (left_cost * left_diag) + (right_cost * right_diag);
                if tot_cost < smallest_cost {
                    dim = d;
                    div_n = sah_divs[d][div];
                    smallest_cost = tot_cost;
                }
            }
        }

        (dim, div_n)
    };

    // Partition
    let mut split_i = partition(&mut objects[..], |obj| {
        let tb = lerp_slice(bounder(obj), 0.5);
        let centroid = (tb.min.get_n(split_axis) + tb.max.get_n(split_axis)) * 0.5;
        centroid < div
    });
    if split_i < 1 {
        split_i = 1;
    } else if split_i >= objects.len() {
        split_i = objects.len() - 1;
    }

    (split_i, split_axis)
}

/// Takes a slice of boundable objects and partitions them based on the bounds mean heuristic.
///
/// Returns the index of the partition boundary and the axis that it split on
/// (0 = x, 1 = y, 2 = z).
pub fn bounds_mean_split<'a, T, F>(objects: &mut [T], bounder: &F) -> (usize, usize)
where
    F: Fn(&T) -> &'a [BBox],
{
    // Get combined object bounds
    let bounds = {
        let mut bb = BBox::new();
        for obj in &objects[..] {
            bb |= lerp_slice(bounder(obj), 0.5);
        }
        bb
    };

    let split_axis = {
        let mut axis = 0;
        let mut largest = std::f32::NEG_INFINITY;
        for i in 0..3 {
            let extent = bounds.max.get_n(i) - bounds.min.get_n(i);
            if extent > largest {
                largest = extent;
                axis = i;
            }
        }
        axis
    };

    let div = (bounds.min.get_n(split_axis) + bounds.max.get_n(split_axis)) * 0.5;

    // Partition
    let mut split_i = partition(&mut objects[..], |obj| {
        let tb = lerp_slice(bounder(obj), 0.5);
        let centroid = (tb.min.get_n(split_axis) + tb.max.get_n(split_axis)) * 0.5;
        centroid < div
    });
    if split_i < 1 {
        split_i = 1;
    } else if split_i >= objects.len() {
        split_i = objects.len() - 1;
    }

    (split_i, split_axis)
}

/// Takes a slice of boundable objects and partitions them based on the median heuristic.
///
/// Returns the index of the partition boundary and the axis that it split on
/// (0 = x, 1 = y, 2 = z).
pub fn median_split<'a, T, F>(objects: &mut [T], bounder: &F) -> (usize, usize)
where
    F: Fn(&T) -> &'a [BBox],
{
    // Get combined object bounds
    let bounds = {
        let mut bb = BBox::new();
        for obj in &objects[..] {
            bb |= lerp_slice(bounder(obj), 0.5);
        }
        bb
    };

    let split_axis = {
        let mut axis = 0;
        let mut largest = std::f32::NEG_INFINITY;
        for i in 0..3 {
            let extent = bounds.max.get_n(i) - bounds.min.get_n(i);
            if extent > largest {
                largest = extent;
                axis = i;
            }
        }
        axis
    };

    let place = {
        let place = objects.len() / 2;
        if place > 0 {
            place
        } else {
            1
        }
    };
    quick_select(objects, place, |a, b| {
        let tb_a = lerp_slice(bounder(a), 0.5);
        let tb_b = lerp_slice(bounder(b), 0.5);
        let centroid_a = (tb_a.min.get_n(split_axis) + tb_a.max.get_n(split_axis)) * 0.5;
        let centroid_b = (tb_b.min.get_n(split_axis) + tb_b.max.get_n(split_axis)) * 0.5;

        if centroid_a < centroid_b {
            Ordering::Less
        } else if centroid_a == centroid_b {
            Ordering::Equal
        } else {
            Ordering::Greater
        }
    });

    (place, split_axis)
}


================================================
FILE: src/algorithm.rs
================================================
#![allow(dead_code)]

use std::{
    cmp::{self, Ordering},
    mem::MaybeUninit,
};

use crate::{
    hash::hash_u64,
    lerp::{lerp_slice, Lerp},
};

/// Selects an item from a slice based on a weighting function and a
/// number (n) between 0.0 and 1.0.  Returns the index of the selected
/// item and the probability that it would have been selected with a
/// random n.
pub fn weighted_choice<T, F>(slc: &[T], n: f32, weight: F) -> (usize, f32)
where
    F: Fn(&T) -> f32,
{
    assert!(!slc.is_empty());

    let total_weight = slc.iter().fold(0.0, |sum, v| sum + weight(v));
    let n = n * total_weight;

    let mut x = 0.0;
    for (i, v) in slc.iter().enumerate() {
        let w = weight(v);
        x += w;
        if x > n || i == slc.len() {
            return (i, w / total_weight);
        }
    }

    unreachable!()
}

/// Partitions a slice in-place with the given unary predicate, returning
/// the index of the first element for which the predicate evaluates
/// false.
///
/// The predicate is executed precisely once on every element in
/// the slice, and is allowed to modify the elements.
pub fn partition<T, F>(slc: &mut [T], mut pred: F) -> usize
where
    F: FnMut(&mut T) -> bool,
{
    // This version uses raw pointers and pointer arithmetic to squeeze more
    // performance out of the code.
    unsafe {
        let mut a = slc.as_mut_ptr();
        let mut b = a.add(slc.len());
        let start = a as usize;

        loop {
            loop {
                if a == b {
                    return ((a as usize) - start) / std::mem::size_of::<T>();
                }
                if !pred(&mut *a) {
                    break;
                }
                a = a.offset(1);
            }

            loop {
                b = b.offset(-1);
                if a == b {
                    return ((a as usize) - start) / std::mem::size_of::<T>();
                }
                if pred(&mut *b) {
                    break;
                }
            }

            std::ptr::swap(a, b);

            a = a.offset(1);
        }
    }
}

/// Partitions a slice in-place with the given unary predicate, returning
/// the index of the first element for which the predicate evaluates
/// false.
///
/// The predicate is executed precisely once on every element in
/// the slice, and is allowed to modify the elements.
///
/// The only difference between this and plain partition above, is that
/// the predicate function is passed a bool representing which side
/// of the array we're currently on: left or right.  False means left,
/// True means right.
pub fn partition_with_side<T, F>(slc: &mut [T], mut pred: F) -> usize
where
    F: FnMut(&mut T, bool) -> bool,
{
    // This version uses raw pointers and pointer arithmetic to squeeze more
    // performance out of the code.
    unsafe {
        let mut a = slc.as_mut_ptr();
        let mut b = a.add(slc.len());
        let start = a as usize;

        loop {
            loop {
                if a == b {
                    return ((a as usize) - start) / std::mem::size_of::<T>();
                }
                if !pred(&mut *a, false) {
                    break;
                }
                a = a.offset(1);
            }

            loop {
                b = b.offset(-1);
                if a == b {
                    return ((a as usize) - start) / std::mem::size_of::<T>();
                }
                if pred(&mut *b, true) {
                    break;
                }
            }

            std::ptr::swap(a, b);

            a = a.offset(1);
        }
    }
}

/// Partitions two slices in-place in concert based on the given unary
/// predicate, returning the index of the first element for which the
/// predicate evaluates false.
///
/// Because this runs on two slices at once, they must both be the same
/// length.
///
/// The predicate takes a usize (which will receive the index of the elments
/// being tested), a mutable reference to an element of the first slice's type,
/// and a mutable reference to an element of the last slice's type.
///
/// The predicate is executed precisely once on every element in
/// the slices, and is allowed to modify the elements.
pub fn partition_pair<A, B, F>(slc1: &mut [A], slc2: &mut [B], mut pred: F) -> usize
where
    F: FnMut(usize, &mut A, &mut B) -> bool,
{
    assert_eq!(slc1.len(), slc2.len());

    // This version uses raw pointers and pointer arithmetic to squeeze more
    // performance out of the code.
    unsafe {
        let mut a1 = slc1.as_mut_ptr();
        let mut a2 = slc2.as_mut_ptr();
        let mut b1 = a1.add(slc1.len());
        let mut b2 = a2.add(slc2.len());
        let start = a1 as usize;

        loop {
            loop {
                if a1 == b1 {
                    return ((a1 as usize) - start) / std::mem::size_of::<A>();
                }
                if !pred(
                    ((a1 as usize) - start) / std::mem::size_of::<A>(),
                    &mut *a1,
                    &mut *a2,
                ) {
                    break;
                }
                a1 = a1.offset(1);
                a2 = a2.offset(1);
            }

            loop {
                b1 = b1.offset(-1);
                b2 = b2.offset(-1);
                if a1 == b1 {
                    return ((a1 as usize) - start) / std::mem::size_of::<A>();
                }
                if pred(
                    ((b1 as usize) - start) / std::mem::size_of::<A>(),
                    &mut *b1,
                    &mut *b2,
                ) {
                    break;
                }
            }

            std::ptr::swap(a1, b1);
            std::ptr::swap(a2, b2);

            a1 = a1.offset(1);
            a2 = a2.offset(1);
        }
    }
}

/// Partitions the slice of items to place the nth-ordered item in the nth place,
/// and the items less than it before and the items more than it after.
pub fn quick_select<T, F>(slc: &mut [T], n: usize, mut order: F)
where
    F: FnMut(&T, &T) -> Ordering,
{
    let mut left = 0;
    let mut right = slc.len();
    let mut seed = n as u64;

    loop {
        let i = left + (hash_u64(right as u64, seed) as usize % (right - left));

        slc.swap(i, right - 1);
        let ii = left + {
            let (val, list) = (&mut slc[left..right]).split_last_mut().unwrap();
            partition(list, |n| order(n, val) == Ordering::Less)
        };
        slc.swap(ii, right - 1);

        if ii == n {
            return;
        } else if ii > n {
            right = ii;
        } else {
            left = ii + 1;
        }

        seed += 1;
    }
}

/// Merges two slices of things, appending the result to `vec_out`
pub fn merge_slices_append<T: Lerp + Copy, F>(
    slice1: &[T],
    slice2: &[T],
    vec_out: &mut Vec<T>,
    merge: F,
) where
    F: Fn(&T, &T) -> T,
{
    // Transform the bounding boxes
    if slice1.is_empty() || slice2.is_empty() {
        return;
    } else if slice1.len() == slice2.len() {
        for (xf1, xf2) in Iterator::zip(slice1.iter(), slice2.iter()) {
            vec_out.push(merge(xf1, xf2));
        }
    } else if slice1.len() > slice2.len() {
        let s = (slice1.len() - 1) as f32;
        for (i, xf1) in slice1.iter().enumerate() {
            let xf2 = lerp_slice(slice2, i as f32 / s);
            vec_out.push(merge(xf1, &xf2));
        }
    } else if slice1.len() < slice2.len() {
        let s = (slice2.len() - 1) as f32;
        for (i, xf2) in slice2.iter().enumerate() {
            let xf1 = lerp_slice(slice1, i as f32 / s);
            vec_out.push(merge(&xf1, xf2));
        }
    }
}

/// Merges two slices of things, storing the result in `slice_out`.
/// Panics if `slice_out` is not the right size.
pub fn merge_slices_to<T: Lerp + Copy, F>(
    slice1: &[T],
    slice2: &[T],
    slice_out: &mut [MaybeUninit<T>],
    merge: F,
) where
    F: Fn(&T, &T) -> T,
{
    assert_eq!(slice_out.len(), cmp::max(slice1.len(), slice2.len()));

    // Transform the bounding boxes
    if slice1.is_empty() || slice2.is_empty() {
        return;
    } else if slice1.len() == slice2.len() {
        for (xfo, (xf1, xf2)) in Iterator::zip(
            slice_out.iter_mut(),
            Iterator::zip(slice1.iter(), slice2.iter()),
        ) {
            unsafe {
                *xfo.as_mut_ptr() = merge(xf1, xf2);
            }
        }
    } else if slice1.len() > slice2.len() {
        let s = (slice1.len() - 1) as f32;
        for (i, (xfo, xf1)) in Iterator::zip(slice_out.iter_mut(), slice1.iter()).enumerate() {
            let xf2 = lerp_slice(slice2, i as f32 / s);
            unsafe {
                *xfo.as_mut_ptr() = merge(xf1, &xf2);
            }
        }
    } else if slice1.len() < slice2.len() {
        let s = (slice2.len() - 1) as f32;
        for (i, (xfo, xf2)) in Iterator::zip(slice_out.iter_mut(), slice2.iter()).enumerate() {
            let xf1 = lerp_slice(slice1, i as f32 / s);
            unsafe {
                *xfo.as_mut_ptr() = merge(&xf1, xf2);
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::cmp::Ordering;

    fn quick_select_ints(list: &mut [i32], i: usize) {
        quick_select(list, i, |a, b| {
            if a < b {
                Ordering::Less
            } else if a == b {
                Ordering::Equal
            } else {
                Ordering::Greater
            }
        });
    }

    #[test]
    fn quick_select_1() {
        let mut list = [8, 9, 7, 4, 6, 1, 0, 5, 3, 2];
        quick_select_ints(&mut list, 5);
        assert_eq!(list[5], 5);
    }

    #[test]
    fn quick_select_2() {
        let mut list = [8, 9, 7, 4, 6, 1, 0, 5, 3, 2];
        quick_select_ints(&mut list, 3);
        assert_eq!(list[3], 3);
    }

    #[test]
    fn quick_select_3() {
        let mut list = [8, 9, 7, 4, 6, 1, 0, 5, 3, 2];
        quick_select_ints(&mut list, 0);
        assert_eq!(list[0], 0);
    }

    #[test]
    fn quick_select_4() {
        let mut list = [8, 9, 7, 4, 6, 1, 0, 5, 3, 2];
        quick_select_ints(&mut list, 9);
        assert_eq!(list[9], 9);
    }
}


================================================
FILE: src/bbox.rs
================================================
#![allow(dead_code)]

use std::{
    iter::Iterator,
    ops::{BitOr, BitOrAssign},
};

use crate::{
    lerp::{lerp, lerp_slice, Lerp},
    math::{Point, Transform, Vector},
};

const BBOX_MAXT_ADJUST: f32 = 1.000_000_24;

/// A 3D axis-aligned bounding box.
#[derive(Debug, Copy, Clone)]
pub struct BBox {
    pub min: Point,
    pub max: Point,
}

impl BBox {
    /// Creates a degenerate BBox with +infinity min and -infinity max.
    pub fn new() -> BBox {
        BBox {
            min: Point::new(std::f32::INFINITY, std::f32::INFINITY, std::f32::INFINITY),
            max: Point::new(
                std::f32::NEG_INFINITY,
                std::f32::NEG_INFINITY,
                std::f32::NEG_INFINITY,
            ),
        }
    }

    /// Creates a BBox with min as the minimum extent and max as the maximum
    /// extent.
    pub fn from_points(min: Point, max: Point) -> BBox {
        BBox { min: min, max: max }
    }

    // Returns whether the given ray intersects with the bbox.
    pub fn intersect_ray(&self, orig: Point, dir_inv: Vector, max_t: f32) -> bool {
        // Calculate slab intersections
        let t1 = (self.min.co - orig.co) * dir_inv.co;
        let t2 = (self.max.co - orig.co) * dir_inv.co;

        // Find the far and near intersection
        let far_t = t1.max(t2).extend(std::f32::INFINITY);
       
Download .txt
gitextract_b8728v5v/

├── .github/
│   └── pull_request_template.md
├── .gitignore
├── Cargo.toml
├── LICENSE.md
├── README.md
├── example_scenes/
│   ├── cornell_box.psy
│   └── cube.psy
├── licenses/
│   ├── Apache-2.0.txt
│   ├── GPL-2.0.txt
│   ├── GPL-3.0.txt
│   └── MIT.txt
├── psychoblend/
│   ├── LICENSE.md
│   ├── __init__.py
│   ├── assembly.py
│   ├── psy_export.py
│   ├── render.py
│   ├── ui.py
│   ├── util.py
│   └── world.py
├── src/
│   ├── accel/
│   │   ├── bvh.rs
│   │   ├── bvh4.rs
│   │   ├── bvh_base.rs
│   │   ├── light_array.rs
│   │   ├── light_tree.rs
│   │   ├── mod.rs
│   │   └── objects_split.rs
│   ├── algorithm.rs
│   ├── bbox.rs
│   ├── bbox4.rs
│   ├── boundable.rs
│   ├── camera.rs
│   ├── color.rs
│   ├── fp_utils.rs
│   ├── hash.rs
│   ├── hilbert.rs
│   ├── image.rs
│   ├── lerp.rs
│   ├── light/
│   │   ├── distant_disk_light.rs
│   │   ├── mod.rs
│   │   ├── rectangle_light.rs
│   │   └── sphere_light.rs
│   ├── main.rs
│   ├── math.rs
│   ├── mis.rs
│   ├── parse/
│   │   ├── basics.rs
│   │   ├── data_tree.rs
│   │   ├── mod.rs
│   │   ├── psy.rs
│   │   ├── psy_assembly.rs
│   │   ├── psy_light.rs
│   │   ├── psy_mesh_surface.rs
│   │   └── psy_surface_shader.rs
│   ├── ray.rs
│   ├── renderer.rs
│   ├── sampling/
│   │   ├── mod.rs
│   │   └── monte_carlo.rs
│   ├── scene/
│   │   ├── assembly.rs
│   │   ├── mod.rs
│   │   └── world.rs
│   ├── shading/
│   │   ├── mod.rs
│   │   └── surface_closure.rs
│   ├── surface/
│   │   ├── bilinear_patch.rs
│   │   ├── micropoly_batch.rs
│   │   ├── mod.rs
│   │   ├── triangle.rs
│   │   └── triangle_mesh.rs
│   ├── timer.rs
│   ├── tracer.rs
│   └── transform_stack.rs
└── sub_crates/
    ├── bvh_order/
    │   ├── Cargo.toml
    │   ├── LICENSE.md
    │   ├── build.rs
    │   └── src/
    │       └── lib.rs
    ├── color/
    │   ├── Cargo.toml
    │   ├── LICENSE.md
    │   ├── build.rs
    │   └── src/
    │       └── lib.rs
    ├── compact/
    │   ├── Cargo.toml
    │   ├── LICENSE.md
    │   ├── benches/
    │   │   └── bench.rs
    │   ├── src/
    │   │   ├── fluv/
    │   │   │   ├── fluv32.rs
    │   │   │   └── mod.rs
    │   │   ├── lib.rs
    │   │   ├── shared_exp/
    │   │   │   ├── mod.rs
    │   │   │   ├── signed48.rs
    │   │   │   ├── unsigned32.rs
    │   │   │   └── unsigned40.rs
    │   │   └── unit_vec/
    │   │       ├── mod.rs
    │   │       └── oct32.rs
    │   └── tests/
    │       └── proptest_tests.rs
    ├── halton/
    │   ├── Cargo.toml
    │   ├── LICENSE.md
    │   ├── build.rs
    │   └── src/
    │       └── lib.rs
    ├── math3d/
    │   ├── Cargo.toml
    │   ├── LICENSE.md
    │   └── src/
    │       ├── lib.rs
    │       ├── normal.rs
    │       ├── point.rs
    │       ├── transform.rs
    │       └── vector.rs
    └── spectral_upsampling/
        ├── Cargo.toml
        ├── LICENSE.md
        ├── build.rs
        ├── jakob_tables/
        │   ├── LICENSE.txt
        │   ├── aces2065_1.coeff
        │   ├── rec2020.coeff
        │   └── srgb.coeff
        └── src/
            ├── jakob.rs
            ├── lib.rs
            ├── meng/
            │   ├── generate_meng_spectra_tables.py
            │   ├── meng_spectra_tables.rs
            │   ├── xyz_5nm_360_830.csv
            │   └── xyz_5nm_380_780.csv
            └── meng.rs
Download .txt
SYMBOL INDEX (1000 symbols across 78 files)

FILE: psychoblend/__init__.py
  class RenderPsychopathSettingsScene (line 37) | class RenderPsychopathSettingsScene(PropertyGroup):
  class PsychopathCamera (line 74) | class PsychopathCamera(bpy.types.PropertyGroup):
  class PsychopathLight (line 81) | class PsychopathLight(bpy.types.PropertyGroup):
  class PsychopathMesh (line 98) | class PsychopathMesh(bpy.types.PropertyGroup):
  class PsychopathMaterial (line 105) | class PsychopathMaterial(bpy.types.PropertyGroup):
  class PsychopathPreferences (line 151) | class PsychopathPreferences(AddonPreferences):
    method draw (line 160) | def draw(self, context):
  function register (line 166) | def register():
  function unregister (line 182) | def unregister():

FILE: psychoblend/assembly.py
  class Assembly (line 5) | class Assembly:
    method __init__ (line 6) | def __init__(self, render_engine, objects, visible_layers, group_prefi...
    method export (line 52) | def export(self, render_engine, w):
    method take_sample (line 82) | def take_sample(self, render_engine, scene, time):
    method cleanup (line 101) | def cleanup(self):
    method get_mesh (line 107) | def get_mesh(self, ob, group_prefix):
    method get_sphere_lamp (line 136) | def get_sphere_lamp(self, ob, group_prefix):
    method get_rect_lamp (line 141) | def get_rect_lamp(self, ob, group_prefix):
  class Mesh (line 150) | class Mesh:
    method __init__ (line 153) | def __init__(self, render_engine, ob, name):
    method take_sample (line 159) | def take_sample(self, render_engine, scene, time):
    method cleanup (line 164) | def cleanup(self):
    method export (line 168) | def export(self, render_engine, w):
  class SphereLamp (line 205) | class SphereLamp:
    method __init__ (line 208) | def __init__(self, render_engine, ob, name):
    method take_sample (line 214) | def take_sample(self, render_engine, scene, time):
    method cleanup (line 226) | def cleanup(self):
    method export (line 229) | def export(self, render_engine, w):
  class RectLamp (line 248) | class RectLamp:
    method __init__ (line 251) | def __init__(self, render_engine, ob, name):
    method take_sample (line 257) | def take_sample(self, render_engine, scene, time):
    method cleanup (line 272) | def cleanup(self):
    method export (line 275) | def export(self, render_engine, w):
  class Instance (line 294) | class Instance:
    method __init__ (line 295) | def __init__(self, render_engine, ob, data_name):
    method take_sample (line 301) | def take_sample(self, render_engine, time, translation_offset):
    method export (line 310) | def export(self, render_engine, w):
  class Material (line 326) | class Material:
    method __init__ (line 327) | def __init__(self, render_engine, material):
    method take_sample (line 330) | def take_sample(self, render_engine, time, translation_offset):
    method export (line 334) | def export(self, render_engine, w):
    method cleanup (line 397) | def cleanup(self):

FILE: psychoblend/psy_export.py
  class IndentedWriter (line 10) | class IndentedWriter:
    method __init__ (line 11) | def __init__(self, file_handle):
    method indent (line 16) | def indent(self):
    method unindent (line 19) | def unindent(self):
    method write (line 24) | def write(self, text, do_indent=True):
  class PsychoExporter (line 31) | class PsychoExporter:
    method __init__ (line 32) | def __init__(self, f, render_engine, scene):
    method set_frame (line 53) | def set_frame(self, frame, fraction):
    method export_psy (line 59) | def export_psy(self):
    method _export_psy (line 71) | def _export_psy(self):

FILE: psychoblend/render.py
  class PsychopathRender (line 9) | class PsychopathRender(bpy.types.RenderEngine):
    method _locate_binary (line 15) | def _locate_binary():
    method _start_psychopath (line 37) | def _start_psychopath(self, scene, psy_filepath, use_stdin, crop):
    method _draw_bucket (line 65) | def _draw_bucket(self, crop, bucket_info, pixels_encoded):
    method render (line 88) | def render(self, scene):
    method _render (line 97) | def _render(self, scene):
  function register (line 232) | def register():
  function unregister (line 235) | def unregister():

FILE: psychoblend/ui.py
  class PsychopathPanel (line 17) | class PsychopathPanel():
    method poll (line 21) | def poll(cls, context):
  class RENDER_PT_psychopath_render_settings (line 26) | class RENDER_PT_psychopath_render_settings(PsychopathPanel, bpy.types.Pa...
    method draw (line 32) | def draw(self, context):
  class RENDER_PT_psychopath_export_settings (line 53) | class RENDER_PT_psychopath_export_settings(PsychopathPanel, bpy.types.Pa...
    method draw (line 59) | def draw(self, context):
  class WORLD_PT_psychopath_background (line 67) | class WORLD_PT_psychopath_background(PsychopathPanel, bpy.types.Panel):
    method poll (line 74) | def poll(cls, context):
    method draw (line 77) | def draw(self, context):
  class DATA_PT_psychopath_camera_dof (line 84) | class DATA_PT_psychopath_camera_dof(PsychopathPanel, bpy.types.Panel):
    method poll (line 91) | def poll(cls, context):
    method draw (line 95) | def draw(self, context):
  class DATA_PT_psychopath_lamp (line 106) | class DATA_PT_psychopath_lamp(PsychopathPanel, bpy.types.Panel):
    method poll (line 113) | def poll(cls, context):
    method draw (line 117) | def draw(self, context):
  class DATA_PT_psychopath_area_lamp (line 139) | class DATA_PT_psychopath_area_lamp(PsychopathPanel, bpy.types.Panel):
    method poll (line 146) | def poll(cls, context):
    method draw (line 151) | def draw(self, context):
  class DATA_PT_psychopath_mesh (line 167) | class DATA_PT_psychopath_mesh(PsychopathPanel, bpy.types.Panel):
    method poll (line 174) | def poll(cls, context):
    method draw (line 178) | def draw(self, context):
  class MATERIAL_PT_psychopath_context_material (line 186) | class MATERIAL_PT_psychopath_context_material(PsychopathPanel, bpy.types...
    method poll (line 194) | def poll(cls, context):
    method draw (line 197) | def draw(self, context):
  class MATERIAL_PT_psychopath_surface (line 237) | class MATERIAL_PT_psychopath_surface(PsychopathPanel, bpy.types.Panel):
    method poll (line 244) | def poll(cls, context):
    method draw (line 247) | def draw(self, context):
  function register (line 270) | def register():
  function unregister (line 281) | def unregister():

FILE: psychoblend/util.py
  class ExportCancelled (line 1) | class ExportCancelled(Exception):
  function mat2str (line 8) | def mat2str(m):
  function needs_def_mb (line 18) | def needs_def_mb(ob):
  function escape_name (line 60) | def escape_name(name):
  function needs_xform_mb (line 71) | def needs_xform_mb(ob):

FILE: psychoblend/world.py
  class World (line 8) | class World:
    method __init__ (line 9) | def __init__(self, render_engine, scene, visible_layers, aspect_ratio):
    method take_sample (line 21) | def take_sample(self, render_engine, scene, time):
    method export (line 30) | def export(self, render_engine, w):
    method cleanup (line 44) | def cleanup(self):
  class Camera (line 51) | class Camera:
    method __init__ (line 52) | def __init__(self, render_engine, ob, aspect_ratio):
    method take_sample (line 61) | def take_sample(self, render_engine, scene, time):
    method export (line 87) | def export(self, render_engine, w):
  class BackgroundShader (line 108) | class BackgroundShader:
    method __init__ (line 109) | def __init__(self, render_engine, world):
    method export (line 114) | def export(self, render_engine, w):
  class DistantDiskLamp (line 124) | class DistantDiskLamp:
    method __init__ (line 125) | def __init__(self, ob, name):
    method take_sample (line 132) | def take_sample(self, render_engine, scene, time):
    method export (line 145) | def export(self, render_engine, w):

FILE: src/accel/bvh.rs
  type BVH (line 16) | pub struct BVH<'a> {
  type BVHNode (line 22) | pub enum BVHNode<'a> {
  function from_objects (line 38) | pub fn from_objects<'b, T, F>(
  function tree_depth (line 66) | pub fn tree_depth(&self) -> usize {
  function traverse (line 70) | pub fn traverse<T, F>(&self, rays: &mut [AccelRay], objects: &[T], mut o...
  function construct_from_base (line 161) | fn construct_from_base(
  method bounds (line 213) | fn bounds(&self) -> &[BBox] {

FILE: src/accel/bvh4.rs
  function ray_code (line 29) | pub fn ray_code(dir: Vector) -> usize {
  type BVH4 (line 37) | pub struct BVH4<'a> {
  type BVH4Node (line 45) | pub enum BVH4Node<'a> {
  function from_objects (line 58) | pub fn from_objects<'b, T, F>(
  function tree_depth (line 97) | pub fn tree_depth(&self) -> usize {
  function traverse (line 101) | pub fn traverse<F>(&self, rays: &mut RayBatch, ray_stack: &mut RayStack,...
  function construct_from_base (line 186) | fn construct_from_base(
  method bounds (line 362) | fn bounds<'b>(&'b self) -> &'b [BBox] {

FILE: src/accel/bvh_base.rs
  constant BVH_MAX_DEPTH (line 7) | pub const BVH_MAX_DEPTH: usize = 42;
  constant USE_UNION_FACTOR (line 12) | const USE_UNION_FACTOR: f32 = 1.4;
  type BVHBase (line 16) | pub struct BVHBase {
    method new (line 48) | fn new() -> BVHBase {
    method from_objects (line 57) | pub fn from_objects<'b, T, F>(objects: &mut [T], objects_per_leaf: usi...
    method root_node_index (line 66) | pub fn root_node_index(&self) -> usize {
    method acc_bounds (line 70) | fn acc_bounds<'a, T, F>(&mut self, objects: &mut [T], bounder: &F)
    method recursive_build (line 96) | fn recursive_build<'a, T, F>(
  type BVHBaseNode (line 24) | pub enum BVHBaseNode {
    method bounds_range (line 38) | pub fn bounds_range(&self) -> (usize, usize) {

FILE: src/accel/light_array.rs
  type LightArray (line 12) | pub struct LightArray<'a> {
  function from_objects (line 19) | pub fn from_objects<'b, T, F>(
  method select (line 45) | fn select(
  method approximate_energy (line 76) | fn approximate_energy(&self) -> f32 {

FILE: src/accel/light_tree.rs
  constant ARITY_LOG2 (line 15) | const ARITY_LOG2: usize = 3;
  constant ARITY (line 18) | const ARITY: usize = 1 << ARITY_LOG2;
  type LightTree (line 21) | pub struct LightTree<'a> {
  type Node (line 27) | enum Node<'a> {
  function bounds (line 41) | fn bounds(&self) -> &'a [BBox] {
  function energy (line 47) | fn energy(&self) -> f32 {
  function light_index (line 53) | fn light_index(&self) -> usize {
  function from_objects (line 62) | pub fn from_objects<'b, T, F>(
  function construct_from_builder (line 89) | fn construct_from_builder(
  method select (line 135) | fn select(
  method approximate_energy (line 203) | fn approximate_energy(&self) -> f32 {
  type LightTreeBuilder (line 212) | struct LightTreeBuilder {
    method new (line 227) | fn new() -> LightTreeBuilder {
    method root_node_index (line 235) | pub fn root_node_index(&self) -> usize {
    method node_child_count (line 241) | pub fn node_child_count(&self, node_index: usize) -> usize {
    method node_nth_child_index (line 247) | pub fn node_nth_child_index(&self, node_index: usize, child_n: usize) ...
    method node_child_count_recurse (line 254) | pub fn node_child_count_recurse(&self, level_collapse: usize, node_ind...
    method node_nth_child_index_recurse (line 274) | pub fn node_nth_child_index_recurse(
    method recursive_build (line 296) | fn recursive_build<'a, T, F>(
  type BuilderNode (line 219) | struct BuilderNode {

FILE: src/accel/mod.rs
  type LightAccel (line 27) | pub trait LightAccel {
    method select (line 29) | fn select(
    method approximate_energy (line 40) | fn approximate_energy(&self) -> f32;

FILE: src/accel/objects_split.rs
  constant SAH_BIN_COUNT (line 13) | const SAH_BIN_COUNT: usize = 13;
  constant SPLIT_PLANE_COUNT (line 14) | const SPLIT_PLANE_COUNT: usize = 5;
  function free_sah_split (line 21) | pub fn free_sah_split<'a, T, F>(seed: u32, objects: &mut [T], bounder: &...
  function sah_split (line 153) | pub fn sah_split<'a, T, F>(objects: &mut [T], bounder: &F) -> (usize, us...
  function bounds_mean_split (line 244) | pub fn bounds_mean_split<'a, T, F>(objects: &mut [T], bounder: &F) -> (u...
  function median_split (line 291) | pub fn median_split<'a, T, F>(objects: &mut [T], bounder: &F) -> (usize,...

FILE: src/algorithm.rs
  function weighted_choice (line 17) | pub fn weighted_choice<T, F>(slc: &[T], n: f32, weight: F) -> (usize, f32)
  function partition (line 44) | pub fn partition<T, F>(slc: &mut [T], mut pred: F) -> usize
  function partition_with_side (line 94) | pub fn partition_with_side<T, F>(slc: &mut [T], mut pred: F) -> usize
  function partition_pair (line 146) | pub fn partition_pair<A, B, F>(slc1: &mut [A], slc2: &mut [B], mut pred:...
  function quick_select (line 203) | pub fn quick_select<T, F>(slc: &mut [T], n: usize, mut order: F)
  function merge_slices_append (line 234) | pub fn merge_slices_append<T: Lerp + Copy, F>(
  function merge_slices_to (line 266) | pub fn merge_slices_to<T: Lerp + Copy, F>(
  function quick_select_ints (line 312) | fn quick_select_ints(list: &mut [i32], i: usize) {
  function quick_select_1 (line 325) | fn quick_select_1() {
  function quick_select_2 (line 332) | fn quick_select_2() {
  function quick_select_3 (line 339) | fn quick_select_3() {
  function quick_select_4 (line 346) | fn quick_select_4() {

FILE: src/bbox.rs
  constant BBOX_MAXT_ADJUST (line 13) | const BBOX_MAXT_ADJUST: f32 = 1.000_000_24;
  type BBox (line 17) | pub struct BBox {
    method new (line 24) | pub fn new() -> BBox {
    method from_points (line 37) | pub fn from_points(min: Point, max: Point) -> BBox {
    method intersect_ray (line 42) | pub fn intersect_ray(&self, orig: Point, dir_inv: Vector, max_t: f32) ...
    method transformed (line 58) | pub fn transformed(&self, xform: Transform) -> BBox {
    method surface_area (line 82) | pub fn surface_area(&self) -> f32 {
    method center (line 87) | pub fn center(&self) -> Point {
    method diagonal (line 91) | pub fn diagonal(&self) -> f32 {
    method diagonal2 (line 95) | pub fn diagonal2(&self) -> f32 {
    type Output (line 124) | type Output = BBox;
    method bitor (line 126) | fn bitor(self, rhs: Point) -> BBox {
    method bitor_assign (line 139) | fn bitor_assign(&mut self, rhs: Point) {
  type Output (line 102) | type Output = BBox;
  method bitor (line 104) | fn bitor(self, rhs: BBox) -> BBox {
  method bitor_assign (line 117) | fn bitor_assign(&mut self, rhs: BBox) {
  method lerp (line 145) | fn lerp(self, other: BBox, alpha: f32) -> BBox {
  function transform_bbox_slice_from (line 153) | pub fn transform_bbox_slice_from(bbs_in: &[BBox], xforms: &[Transform], ...

FILE: src/bbox4.rs
  constant BBOX_MAXT_ADJUST (line 14) | const BBOX_MAXT_ADJUST: f32 = 1.000_000_24;
  type BBox4 (line 18) | pub struct BBox4 {
    method new (line 26) | pub fn new() -> BBox4 {
    method from_bboxes (line 45) | pub fn from_bboxes(b1: BBox, b2: BBox, b3: BBox, b4: BBox) -> BBox4 {
    method intersect_ray (line 63) | pub fn intersect_ray(&self, orig: Point, dir_inv: Vector, max_t: f32) ...
  type Output (line 102) | type Output = BBox4;
  method bitor (line 104) | fn bitor(self, rhs: BBox4) -> BBox4 {
  method bitor_assign (line 114) | fn bitor_assign(&mut self, rhs: BBox4) {
  method lerp (line 120) | fn lerp(self, other: BBox4, alpha: f32) -> BBox4 {

FILE: src/boundable.rs
  type Boundable (line 5) | pub trait Boundable {
    method bounds (line 6) | fn bounds(&self) -> &[BBox];

FILE: src/camera.rs
  type Camera (line 13) | pub struct Camera<'a> {
  function new (line 22) | pub fn new(
  function generate_ray (line 74) | pub fn generate_ray(&self, x: f32, y: f32, time: f32, wavelength: f32, u...

FILE: src/color.rs
  constant WL_MIN (line 15) | const WL_MIN: f32 = 380.0;
  constant WL_MAX (line 16) | const WL_MAX: f32 = 700.0;
  constant WL_RANGE (line 17) | const WL_RANGE: f32 = WL_MAX - WL_MIN;
  constant WL_RANGE_Q (line 18) | const WL_RANGE_Q: f32 = WL_RANGE / 4.0;
  function map_0_1_to_wavelength (line 20) | pub fn map_0_1_to_wavelength(n: f32) -> f32 {
  function nth_wavelength (line 25) | fn nth_wavelength(hero_wavelength: f32, n: usize) -> f32 {
  function wavelengths (line 36) | fn wavelengths(hero_wavelength: f32) -> Vec4 {
  type Color (line 48) | pub enum Color {
    method new_xyz (line 64) | pub fn new_xyz(xyz: (f32, f32, f32)) -> Self {
    method new_blackbody (line 69) | pub fn new_blackbody(temp: f32, fac: f32) -> Self {
    method new_temperature (line 77) | pub fn new_temperature(temp: f32, fac: f32) -> Self {
    method to_spectral_sample (line 84) | pub fn to_spectral_sample(self, hero_wavelength: f32) -> SpectralSample {
    method approximate_energy (line 127) | pub fn approximate_energy(self) -> f32 {
    method compressed_size (line 145) | pub fn compressed_size(&self) -> usize {
    method write_compressed (line 161) | pub fn write_compressed(&self, out_data: &mut [u8]) -> usize {
    method from_compressed (line 199) | pub fn from_compressed(in_data: &[u8]) -> (Color, usize) {
    type Output (line 253) | type Output = Self;
    method mul (line 255) | fn mul(self, rhs: f32) -> Self {
    method mul_assign (line 279) | fn mul_assign(&mut self, rhs: f32) {
  method lerp (line 294) | fn lerp(self, other: Self, alpha: f32) -> Self {
  function plancks_law (line 336) | fn plancks_law(temperature: f32, wavelength: f32) -> f32 {
  function plancks_law_normalized (line 380) | fn plancks_law_normalized(temperature: f32, wavelength: f32) -> f32 {
  type SpectralSample (line 388) | pub struct SpectralSample {
    method new (line 394) | pub fn new(wavelength: f32) -> SpectralSample {
    method from_value (line 403) | pub fn from_value(value: f32, wavelength: f32) -> SpectralSample {
    method from_parts (line 411) | pub fn from_parts(e: Vec4, wavelength: f32) -> SpectralSample {
    method wl_n (line 420) | fn wl_n(&self, n: usize) -> f32 {
    type Output (line 467) | type Output = SpectralSample;
    method mul (line 468) | fn mul(self, rhs: f32) -> Self::Output {
    method mul_assign (line 477) | fn mul_assign(&mut self, rhs: f32) {
    type Output (line 483) | type Output = SpectralSample;
    method div (line 484) | fn div(self, rhs: f32) -> Self::Output {
    method div_assign (line 493) | fn div_assign(&mut self, rhs: f32) {
  type Output (line 431) | type Output = SpectralSample;
  method add (line 432) | fn add(self, rhs: SpectralSample) -> Self::Output {
  method add_assign (line 442) | fn add_assign(&mut self, rhs: SpectralSample) {
  type Output (line 449) | type Output = SpectralSample;
  method mul (line 450) | fn mul(self, rhs: SpectralSample) -> Self::Output {
  method mul_assign (line 460) | fn mul_assign(&mut self, rhs: SpectralSample) {
  type XYZ (line 501) | pub struct XYZ {
    method new (line 508) | pub fn new(x: f32, y: f32, z: f32) -> XYZ {
    method from_wavelength (line 512) | pub fn from_wavelength(wavelength: f32, intensity: f32) -> XYZ {
    method from_spectral_sample (line 520) | pub fn from_spectral_sample(ss: &SpectralSample) -> XYZ {
    method to_tuple (line 528) | pub fn to_tuple(&self) -> (f32, f32, f32) {
    type Output (line 559) | type Output = XYZ;
    method mul (line 560) | fn mul(self, rhs: f32) -> Self::Output {
    method mul_assign (line 570) | fn mul_assign(&mut self, rhs: f32) {
    type Output (line 578) | type Output = XYZ;
    method div (line 579) | fn div(self, rhs: f32) -> Self::Output {
    method div_assign (line 589) | fn div_assign(&mut self, rhs: f32) {
  method lerp (line 534) | fn lerp(self, other: XYZ, alpha: f32) -> XYZ {
  type Output (line 540) | type Output = XYZ;
  method add (line 541) | fn add(self, rhs: XYZ) -> Self::Output {
  method add_assign (line 551) | fn add_assign(&mut self, rhs: XYZ) {
  function xyz_to_spectrum_4 (line 602) | fn xyz_to_spectrum_4(xyz: (f32, f32, f32), wavelengths: Vec4) -> Vec4 {
  function x_1931 (line 610) | pub fn x_1931(wavelength: f32) -> f32 {
  function y_1931 (line 618) | pub fn y_1931(wavelength: f32) -> f32 {
  function z_1931 (line 624) | pub fn z_1931(wavelength: f32) -> f32 {

FILE: src/fp_utils.rs
  function fp_gamma (line 9) | pub fn fp_gamma(n: u32) -> f32 {
  function increment_ulp (line 15) | pub fn increment_ulp(v: f32) -> f32 {
  function decrement_ulp (line 30) | pub fn decrement_ulp(v: f32) -> f32 {
  function robust_ray_origin (line 45) | pub fn robust_ray_origin(pos: Point, pos_err: f32, nor: Normal, ray_dir:...
  function inc_ulp (line 89) | fn inc_ulp() {
  function dec_ulp (line 95) | fn dec_ulp() {
  function inc_ulp_zero (line 101) | fn inc_ulp_zero() {
  function dec_ulp_zero (line 109) | fn dec_ulp_zero() {
  function inc_dec_ulp (line 117) | fn inc_dec_ulp() {
  function dec_inc_ulp (line 125) | fn dec_inc_ulp() {

FILE: src/hash.rs
  function hash_u32 (line 1) | pub fn hash_u32(n: u32, seed: u32) -> u32 {
  function hash_u64 (line 12) | pub fn hash_u64(n: u64, seed: u64) -> u64 {
  function hash_u32_to_f32 (line 26) | pub fn hash_u32_to_f32(n: u32, seed: u32) -> f32 {

FILE: src/hilbert.rs
  constant N (line 3) | const N: u32 = 1 << 16;
  function hil_rot (line 6) | fn hil_rot(n: u32, rx: u32, ry: u32, x: &mut u32, y: &mut u32) {
  function xy2d (line 23) | pub fn xy2d(x: u32, y: u32) -> u32 {
  function d2xy (line 47) | pub fn d2xy(d: u32) -> (u32, u32) {
  function reversible (line 70) | fn reversible() {

FILE: src/image.rs
  type Image (line 20) | pub struct Image {
    method new (line 29) | pub fn new(width: usize, height: usize) -> Image {
    method width (line 37) | pub fn width(&self) -> usize {
    method height (line 41) | pub fn height(&self) -> usize {
    method get (line 45) | pub fn get(&mut self, x: usize, y: usize) -> XYZ {
    method set (line 53) | pub fn set(&mut self, x: usize, y: usize, value: XYZ) {
    method get_bucket (line 61) | pub fn get_bucket<'a>(&'a self, min: (u32, u32), max: (u32, u32)) -> B...
    method write_ascii_ppm (line 98) | pub fn write_ascii_ppm(&mut self, path: &Path) -> io::Result<()> {
    method write_binary_ppm (line 118) | pub fn write_binary_ppm(&mut self, path: &Path) -> io::Result<()> {
    method write_png (line 138) | pub fn write_png(&mut self, path: &Path) -> io::Result<()> {
    method write_exr (line 167) | pub fn write_exr(&mut self, path: &Path) {
  type Bucket (line 199) | pub struct Bucket<'a> {
  function get (line 207) | pub fn get(&mut self, x: u32, y: u32) -> XYZ {
  function set (line 217) | pub fn set(&mut self, x: u32, y: u32, value: XYZ) {
  function rgba_base64 (line 235) | pub fn rgba_base64<F>(&mut self, color_convert: F) -> String
  method drop (line 259) | fn drop(&mut self) {
  function srgb_gamma (line 275) | fn srgb_gamma(n: f32) -> f32 {
  function srgb_inv_gamma (line 283) | fn srgb_inv_gamma(n: f32) -> f32 {
  function xyz_to_srgbe (line 291) | fn xyz_to_srgbe(xyz: (f32, f32, f32)) -> (f32, f32, f32) {
  function quantize_tri_255 (line 296) | fn quantize_tri_255(tri: (f32, f32, f32)) -> (u8, u8, u8) {

FILE: src/lerp.rs
  type Lerp (line 6) | pub trait Lerp: Copy {
    method lerp (line 7) | fn lerp(self, other: Self, alpha: f32) -> Self;
    method lerp (line 59) | fn lerp(self, other: f32, alpha: f32) -> f32 {
    method lerp (line 65) | fn lerp(self, other: f64, alpha: f32) -> f64 {
    method lerp (line 71) | fn lerp(self, other: (T, T), alpha: f32) -> (T, T) {
    method lerp (line 77) | fn lerp(self, other: Self, alpha: f32) -> Self {
    method lerp (line 83) | fn lerp(self, other: Self, alpha: f32) -> Self {
    method lerp (line 93) | fn lerp(self, other: Self, alpha: f32) -> Self {
    method lerp (line 104) | fn lerp(self, other: glam::Vec4, alpha: f32) -> glam::Vec4 {
    method lerp (line 110) | fn lerp(self, other: Transform, alpha: f32) -> Transform {
    method lerp (line 116) | fn lerp(self, other: Normal, alpha: f32) -> Normal {
    method lerp (line 122) | fn lerp(self, other: Point, alpha: f32) -> Point {
    method lerp (line 132) | fn lerp(self, other: Vector, alpha: f32) -> Vector {
  function lerp (line 11) | pub fn lerp<T: Lerp>(a: T, b: T, alpha: f32) -> T {
  function lerp_slice (line 20) | pub fn lerp_slice<T: Lerp>(s: &[T], alpha: f32) -> T {
  function lerp_slice_with (line 37) | pub fn lerp_slice_with<T, F>(s: &[T], alpha: f32, f: F) -> T
  function lerp1 (line 142) | fn lerp1() {
  function lerp2 (line 151) | fn lerp2() {
  function lerp3 (line 160) | fn lerp3() {
  function lerp_slice1 (line 169) | fn lerp_slice1() {
  function lerp_slice2 (line 177) | fn lerp_slice2() {
  function lerp_slice3 (line 185) | fn lerp_slice3() {
  function lerp_slice4 (line 193) | fn lerp_slice4() {
  function lerp_slice5 (line 201) | fn lerp_slice5() {
  function lerp_slice6 (line 209) | fn lerp_slice6() {
  function lerp_matrix (line 217) | fn lerp_matrix() {
  function lerp_point_1 (line 243) | fn lerp_point_1() {
  function lerp_point_2 (line 252) | fn lerp_point_2() {
  function lerp_point_3 (line 261) | fn lerp_point_3() {
  function lerp_normal_1 (line 270) | fn lerp_normal_1() {
  function lerp_normal_2 (line 279) | fn lerp_normal_2() {
  function lerp_normal_3 (line 288) | fn lerp_normal_3() {
  function lerp_vector_1 (line 297) | fn lerp_vector_1() {
  function lerp_vector_2 (line 306) | fn lerp_vector_2() {
  function lerp_vector_3 (line 315) | fn lerp_vector_3() {

FILE: src/light/distant_disk_light.rs
  type DistantDiskLight (line 17) | pub struct DistantDiskLight<'a> {
  function new (line 24) | pub fn new(
  method sample_from_point (line 59) | fn sample_from_point(
  method is_delta (line 87) | fn is_delta(&self) -> bool {
  method approximate_energy (line 91) | fn approximate_energy(&self) -> f32 {

FILE: src/light/mod.rs
  type SurfaceLight (line 19) | pub trait SurfaceLight: Surface {
    method sample_from_point (line 35) | fn sample_from_point(
    method is_delta (line 50) | fn is_delta(&self) -> bool;
    method approximate_energy (line 57) | fn approximate_energy(&self) -> f32;
  type WorldLightSource (line 62) | pub trait WorldLightSource: Debug + Sync {
    method sample_from_point (line 72) | fn sample_from_point(
    method is_delta (line 85) | fn is_delta(&self) -> bool;
    method approximate_energy (line 93) | fn approximate_energy(&self) -> f32;

FILE: src/light/rectangle_light.rs
  constant SIMPLE_SAMPLING_THRESHOLD (line 21) | const SIMPLE_SAMPLING_THRESHOLD: f32 = 0.01;
  type RectangleLight (line 24) | pub struct RectangleLight<'a> {
  function new (line 31) | pub fn new<'b>(
  function sample_pdf (line 52) | fn sample_pdf(
  method sample_from_point (line 121) | fn sample_from_point(
  method is_delta (line 245) | fn is_delta(&self) -> bool {
  method approximate_energy (line 249) | fn approximate_energy(&self) -> f32 {
  method intersect_rays (line 258) | fn intersect_rays(
  method bounds (line 341) | fn bounds(&self) -> &[BBox] {

FILE: src/light/sphere_light.rs
  constant SAMPLE_POINT_FUDGE (line 22) | const SAMPLE_POINT_FUDGE: f32 = 0.001;
  type SphereLight (line 27) | pub struct SphereLight<'a> {
  function new (line 34) | pub fn new<'b>(arena: &'b Arena, radii: &[f32], colors: &[Color]) -> Sph...
  function sample_pdf (line 51) | fn sample_pdf(
  method sample_from_point (line 85) | fn sample_from_point(
  method is_delta (line 194) | fn is_delta(&self) -> bool {
  method approximate_energy (line 198) | fn approximate_energy(&self) -> f32 {
  method intersect_rays (line 207) | fn intersect_rays(
  method bounds (line 342) | fn bounds(&self) -> &[BBox] {

FILE: src/main.rs
  constant VERSION (line 59) | const VERSION: &str = env!("CARGO_PKG_VERSION");
  function main (line 62) | fn main() {

FILE: src/math.rs
  function log2_64 (line 8) | pub fn log2_64(n: u64) -> u64 {
  function coordinate_system_from_vector (line 26) | pub fn coordinate_system_from_vector(v: Vector) -> (Vector, Vector, Vect...
  function zup_to_vec (line 47) | pub fn zup_to_vec(from: Vector, toz: Vector) -> Vector {
  function probit (line 67) | pub fn probit(n: f32, width: f32) -> f32 {
  function fast_ln (line 134) | pub fn fast_ln(x: f32) -> f32 {
  function fast_pow2 (line 138) | pub fn fast_pow2(p: f32) -> f32 {
  function fast_log2 (line 142) | pub fn fast_log2(x: f32) -> f32 {
  function fast_exp (line 146) | pub fn fast_exp(p: f32) -> f32 {
  function fast_pow (line 150) | pub fn fast_pow(x: f32, p: f32) -> f32 {
  function faster_pow2 (line 154) | pub fn faster_pow2(p: f32) -> f32 {
  function faster_exp (line 158) | pub fn faster_exp(p: f32) -> f32 {
  function log2_64_test (line 169) | fn log2_64_test() {

FILE: src/mis.rs
  function balance_heuristic (line 3) | pub fn balance_heuristic(a: f32, b: f32) -> f32 {
  function power_heuristic (line 12) | pub fn power_heuristic(a: f32, b: f32) -> f32 {

FILE: src/parse/basics.rs
  function ws_f32 (line 16) | pub fn ws_f32(input: &str) -> IResult<&str, f32, ()> {
  function ws_u32 (line 20) | pub fn ws_u32(input: &str) -> IResult<&str, u32, ()> {
  function ws_usize (line 24) | pub fn ws_usize(input: &str) -> IResult<&str, usize, ()> {
  function ws_i32 (line 28) | pub fn ws_i32(input: &str) -> IResult<&str, i32, ()> {
  function ws_u32_1 (line 47) | fn ws_u32_1() {
  function ws_usize_1 (line 56) | fn ws_usize_1() {
  function ws_i32_1 (line 65) | fn ws_i32_1() {
  function ws_i32_2 (line 74) | fn ws_i32_2() {
  function ws_f32_1 (line 84) | fn ws_f32_1() {
  function ws_f32_2 (line 93) | fn ws_f32_2() {
  function ws_f32_3 (line 102) | fn ws_f32_3() {
  function ws_f32_4 (line 111) | fn ws_f32_4() {

FILE: src/parse/data_tree.rs
  type DataTree (line 6) | pub enum DataTree<'a> {
  function from_str (line 22) | pub fn from_str(source_text: &'a str) -> Result<DataTree<'a>, ParseError> {
  function type_name (line 46) | pub fn type_name(&'a self) -> &'a str {
  function byte_offset (line 52) | pub fn byte_offset(&'a self) -> usize {
  function is_internal (line 60) | pub fn is_internal(&self) -> bool {
  function is_leaf (line 67) | pub fn is_leaf(&self) -> bool {
  function leaf_contents (line 74) | pub fn leaf_contents(&'a self) -> Option<&'a str> {
  function iter_children (line 81) | pub fn iter_children(&'a self) -> slice::Iter<'a, DataTree<'a>> {
  function iter_children_with_type (line 89) | pub fn iter_children_with_type(&'a self, type_name: &'static str) -> Dat...
  function iter_internal_children_with_type (line 103) | pub fn iter_internal_children_with_type(
  function iter_leaf_children_with_type (line 120) | pub fn iter_leaf_children_with_type(
  function internal_data_or_panic (line 138) | fn internal_data_or_panic(&'a self) -> (&'a str, Option<&'a str>, &'a Ve...
  function leaf_data_or_panic (line 151) | fn leaf_data_or_panic(&'a self) -> (&'a str, &'a str) {
  type DataTreeFilterIter (line 167) | pub struct DataTreeFilterIter<'a> {
  type Item (line 173) | type Item = &'a DataTree<'a>;
  method next (line 175) | fn next(&mut self) -> Option<&'a DataTree<'a>> {
  type DataTreeFilterInternalIter (line 193) | pub struct DataTreeFilterInternalIter<'a> {
  type Item (line 199) | type Item = (&'a str, Option<&'a str>, &'a Vec<DataTree<'a>>, usize);
  method next (line 201) | fn next(&mut self) -> Option<(&'a str, Option<&'a str>, &'a Vec<DataTree...
  type DataTreeFilterLeafIter (line 232) | pub struct DataTreeFilterLeafIter<'a> {
  type Item (line 238) | type Item = (&'a str, &'a str, usize);
  method next (line 240) | fn next(&mut self) -> Option<(&'a str, &'a str, usize)> {
  type ParseError (line 268) | pub enum ParseError {
  type Token (line 283) | enum Token<'a> {
  type ParseResult (line 294) | type ParseResult<'a> = Result<Option<(DataTree<'a>, (usize, &'a str))>, ...
  function parse_node (line 296) | fn parse_node<'a>(source_text: (usize, &'a str)) -> ParseResult<'a> {
  function parse_leaf_content (line 378) | fn parse_leaf_content(source_text: (usize, &str)) -> (&str, (usize, &str...
  function next_token (line 404) | fn next_token<'a>(source_text: (usize, &'a str)) -> (Token<'a>, (usize, ...
  function is_ws (line 484) | fn is_ws(c: char) -> bool {
  function is_nl (line 491) | fn is_nl(c: char) -> bool {
  function is_reserved_char (line 498) | fn is_reserved_char(c: char) -> bool {
  function is_ident_char (line 505) | fn is_ident_char(c: char) -> bool {
  function skip_ws (line 510) | fn skip_ws(text: &str) -> &str {
  function skip_comment (line 528) | fn skip_comment(text: &str) -> &str {
  function skip_ws_and_comments (line 548) | fn skip_ws_and_comments(text: (usize, &str)) -> (usize, &str) {
  function tokenize_1 (line 573) | fn tokenize_1() {
  function tokenize_2 (line 580) | fn tokenize_2() {
  function tokenize_3 (line 587) | fn tokenize_3() {
  function tokenize_4 (line 599) | fn tokenize_4() {
  function tokenize_5 (line 606) | fn tokenize_5() {
  function tokenize_6 (line 616) | fn tokenize_6() {
  function tokenize_7 (line 632) | fn tokenize_7() {
  function parse_1 (line 672) | fn parse_1() {
  function iter_1 (line 693) | fn iter_1() {
  function iter_2 (line 710) | fn iter_2() {
  function iter_3 (line 727) | fn iter_3() {

FILE: src/parse/psy.rs
  type PsyParseError (line 27) | pub enum PsyParseError {
    method print (line 41) | pub fn print(&self, psy_content: &str) {
  function line_count_to_byte_offset (line 90) | fn line_count_to_byte_offset(text: &str, offset: usize) -> usize {
  function parse_scene (line 95) | pub fn parse_scene<'a>(
  function parse_output_info (line 202) | fn parse_output_info(tree: &DataTree) -> Result<String, PsyParseError> {
  function parse_render_settings (line 260) | fn parse_render_settings(tree: &DataTree) -> Result<((u32, u32), u32, u3...
  function parse_camera (line 353) | fn parse_camera<'a>(arena: &'a Arena, tree: &'a DataTree) -> Result<Came...
  function parse_world (line 455) | fn parse_world<'a>(arena: &'a Arena, tree: &'a DataTree) -> Result<World...
  function parse_matrix (line 556) | pub fn parse_matrix(contents: &str) -> Result<Transform, PsyParseError> {
  function make_transform_format_error (line 574) | pub fn make_transform_format_error(byte_offset: usize) -> PsyParseError {
  function parse_color (line 582) | pub fn parse_color(contents: &str) -> Result<Color, PsyParseError> {

FILE: src/parse/psy_assembly.rs
  function parse_assembly (line 17) | pub fn parse_assembly<'a>(

FILE: src/parse/psy_light.rs
  function parse_distant_disk_light (line 20) | pub fn parse_distant_disk_light<'a>(
  function parse_sphere_light (line 86) | pub fn parse_sphere_light<'a>(
  function parse_rectangle_light (line 135) | pub fn parse_rectangle_light<'a>(

FILE: src/parse/psy_mesh_surface.rs
  function parse_mesh_surface (line 27) | pub fn parse_mesh_surface<'a>(

FILE: src/parse/psy_surface_shader.rs
  function parse_surface_shader (line 24) | pub fn parse_surface_shader<'a>(

FILE: src/ray.rs
  type RayIndexType (line 7) | type RayIndexType = u16;
  type FlagType (line 8) | type FlagType = u8;
  constant OCCLUSION_FLAG (line 9) | const OCCLUSION_FLAG: FlagType = 1;
  constant DONE_FLAG (line 10) | const DONE_FLAG: FlagType = 1 << 1;
  type Ray (line 15) | pub struct Ray {
  type RayHot (line 25) | struct RayHot {
  type RayCold (line 35) | struct RayCold {
  type RayBatch (line 43) | pub struct RayBatch {
    method new (line 50) | pub fn new() -> RayBatch {
    method with_capacity (line 59) | pub fn with_capacity(n: usize) -> RayBatch {
    method push (line 66) | pub fn push(&mut self, ray: Ray, is_occlusion: bool) {
    method swap (line 81) | pub fn swap(&mut self, a: usize, b: usize) {
    method set_from_ray (line 86) | pub fn set_from_ray(&mut self, ray: &Ray, is_occlusion: bool, idx: usi...
    method truncate (line 100) | pub fn truncate(&mut self, len: usize) {
    method clear (line 108) | pub fn clear(&mut self) {
    method len (line 113) | pub fn len(&self) -> usize {
    method update_local (line 122) | pub fn update_local(&mut self, idx: usize, xform: &Transform) {
    method orig (line 133) | pub fn orig(&self, idx: usize) -> Point {
    method dir (line 138) | pub fn dir(&self, idx: usize) -> Vector {
    method orig_local (line 143) | pub fn orig_local(&self, idx: usize) -> Point {
    method dir_inv_local (line 148) | pub fn dir_inv_local(&self, idx: usize) -> Vector {
    method time (line 153) | pub fn time(&self, idx: usize) -> f32 {
    method max_t (line 158) | pub fn max_t(&self, idx: usize) -> f32 {
    method set_max_t (line 163) | pub fn set_max_t(&mut self, idx: usize, new_max_t: f32) {
    method wavelength (line 168) | pub fn wavelength(&self, idx: usize) -> f32 {
    method is_occlusion (line 174) | pub fn is_occlusion(&self, idx: usize) -> bool {
    method is_done (line 180) | pub fn is_done(&self, idx: usize) -> bool {
    method mark_occlusion (line 186) | pub fn mark_occlusion(&mut self, idx: usize) {
    method mark_done (line 192) | pub fn mark_done(&mut self, idx: usize) {
  type RayStack (line 199) | pub struct RayStack {
    method new (line 205) | pub fn new() -> RayStack {
    method is_empty (line 213) | pub fn is_empty(&self) -> bool {
    method ensure_lane_count (line 218) | pub fn ensure_lane_count(&mut self, count: usize) {
    method ray_count_in_next_task (line 227) | pub fn ray_count_in_next_task(&self) -> usize {
    method next_task_ray_idx (line 233) | pub fn next_task_ray_idx(&self, i: usize) -> usize {
    method clear (line 247) | pub fn clear(&mut self) {
    method push_ray_index (line 257) | pub fn push_ray_index(&mut self, ray_idx: usize, lane: usize) {
    method push_lane_to_task (line 267) | pub fn push_lane_to_task(&mut self, lane_idx: usize) -> bool {
    method push_lanes_to_tasks (line 282) | pub fn push_lanes_to_tasks(&mut self, lane_idxs: &[usize]) {
    method duplicate_next_task (line 288) | pub fn duplicate_next_task(&mut self) {
    method pop_task (line 315) | pub fn pop_task(&mut self) {
    method do_next_task (line 322) | pub fn do_next_task<F>(&mut self, mut handle_ray: F)
    method pop_do_next_task (line 339) | pub fn pop_do_next_task<F>(&mut self, handle_ray: F)
    method pop_do_next_task_and_push_rays (line 350) | pub fn pop_do_next_task_and_push_rays<F>(&mut self, output_lane_count:...
  type Lane (line 387) | struct Lane {
  type RayTask (line 398) | struct RayTask {

FILE: src/renderer.rs
  type Renderer (line 32) | pub struct Renderer<'a> {
  type RenderStats (line 41) | pub struct RenderStats {
    method new (line 52) | fn new() -> RenderStats {
    method collect (line 64) | fn collect(&mut self, other: RenderStats) {
  function render (line 76) | pub fn render(
  function render_job (line 194) | fn render_job(
  type LightPathEvent (line 363) | enum LightPathEvent {
  type LightPath (line 370) | pub struct LightPath {
    method new (line 392) | fn new(
    method next_lds_sequence (line 433) | fn next_lds_sequence(&mut self) {
    method next_lds_samp (line 438) | fn next_lds_samp(&mut self) -> (f32, f32, f32, f32) {
    method next (line 449) | fn next(
  function get_sample_4d (line 698) | fn get_sample_4d(
  function golden_ratio_sample (line 728) | fn golden_ratio_sample(i: u32, scramble: u32) -> f32 {
  type BucketJob (line 740) | struct BucketJob {

FILE: src/sampling/monte_carlo.rs
  function square_to_circle (line 10) | pub fn square_to_circle(x: f32, y: f32) -> (f32, f32) {
  function cosine_sample_hemisphere (line 34) | pub fn cosine_sample_hemisphere(u: f32, v: f32) -> Vector {
  function uniform_sample_hemisphere (line 40) | pub fn uniform_sample_hemisphere(u: f32, v: f32) -> Vector {
  function uniform_sample_sphere (line 49) | pub fn uniform_sample_sphere(u: f32, v: f32) -> Vector {
  function uniform_sample_cone (line 64) | pub fn uniform_sample_cone(u: f32, v: f32, cos_theta_max: f64) -> Vector {
  function uniform_sample_cone_pdf (line 75) | pub fn uniform_sample_cone_pdf(cos_theta_max: f64) -> f64 {
  function uniform_sample_triangle (line 82) | pub fn uniform_sample_triangle(va: Vector, vb: Vector, vc: Vector, i: f3...
  function triangle_surface_area (line 92) | pub fn triangle_surface_area(p0: Point, p1: Point, p2: Point) -> f32 {
  function spherical_triangle_solid_angle (line 99) | pub fn spherical_triangle_solid_angle(va: Vector, vb: Vector, vc: Vector...
  function uniform_sample_spherical_triangle (line 136) | pub fn uniform_sample_spherical_triangle(

FILE: src/scene/assembly.rs
  type Assembly (line 20) | pub struct Assembly<'a> {
  function sample_lights (line 46) | pub fn sample_lights(
  method bounds (line 144) | fn bounds(&self) -> &[BBox] {
  type AssemblyBuilder (line 150) | pub struct AssemblyBuilder<'a> {
  function new (line 171) | pub fn new(arena: &'a Arena) -> AssemblyBuilder<'a> {
  function add_surface_shader (line 185) | pub fn add_surface_shader(&mut self, name: &str, shader: &'a dyn Surface...
  function add_object (line 197) | pub fn add_object(&mut self, name: &str, obj: Object<'a>) {
  function add_assembly (line 208) | pub fn add_assembly(&mut self, name: &str, asmb: Assembly<'a>) {
  function add_instance (line 223) | pub fn add_instance(
  function name_exists (line 284) | pub fn name_exists(&self, name: &str) -> bool {
  function build (line 288) | pub fn build(mut self) -> Assembly<'a> {
  function instance_bounds (line 354) | fn instance_bounds(&self) -> (Vec<usize>, Vec<BBox>) {
  type Object (line 399) | pub enum Object<'a> {
  type Instance (line 405) | pub struct Instance {
  type InstanceType (line 414) | pub enum InstanceType {

FILE: src/scene/mod.rs
  type Scene (line 20) | pub struct Scene<'a> {
  function sample_lights (line 28) | pub fn sample_lights(
  type SceneLightSample (line 103) | pub enum SceneLightSample {
    method is_none (line 120) | pub fn is_none(&self) -> bool {
    method color (line 128) | pub fn color(&self) -> SpectralSample {
    method pdf (line 136) | pub fn pdf(&self) -> f32 {
    method selection_pdf (line 144) | pub fn selection_pdf(&self) -> f32 {

FILE: src/scene/world.rs
  type World (line 4) | pub struct World<'a> {

FILE: src/shading/mod.rs
  type SurfaceShader (line 10) | pub trait SurfaceShader: Debug + Sync {
    method shade (line 13) | fn shade(&self, data: &SurfaceIntersectionData, time: f32) -> SurfaceC...
    method shade (line 43) | fn shade(&self, data: &SurfaceIntersectionData, time: f32) -> SurfaceC...
  type SimpleSurfaceShader (line 28) | pub enum SimpleSurfaceShader {

FILE: src/shading/surface_closure.rs
  constant INV_PI (line 14) | const INV_PI: f32 = 1.0 / PI_32;
  constant H_PI (line 15) | const H_PI: f32 = PI_32 / 2.0;
  type SurfaceClosure (line 19) | pub enum SurfaceClosure {
    method is_delta (line 39) | pub fn is_delta(&self) -> bool {
    method sample (line 57) | pub fn sample(
    method evaluate (line 88) | pub fn evaluate(
    method estimate_eval_over_sphere_light (line 115) | pub fn estimate_eval_over_sphere_light(
    method compressed_size (line 158) | pub fn compressed_size(&self) -> usize {
    method write_compressed (line 176) | pub fn write_compressed(&self, out_data: &mut [u8]) -> usize {
    method from_compressed (line 213) | pub fn from_compressed(in_data: &[u8]) -> (SurfaceClosure, usize) {
  method lerp (line 254) | fn lerp(self, other: SurfaceClosure, alpha: f32) -> SurfaceClosure {
  function sample (line 284) | pub fn sample(
  function evaluate (line 312) | pub fn evaluate(
  function estimate_eval_over_sphere_light (line 334) | pub fn estimate_eval_over_sphere_light(
  function validate (line 415) | pub fn validate(roughness: f32, fresnel: f32) {
  function sample (line 420) | pub fn sample(
  function evaluate (line 456) | pub fn evaluate(
  function estimate_eval_over_sphere_light (line 535) | pub fn estimate_eval_over_sphere_light(
  function half_theta_sample (line 597) | fn half_theta_sample(u: f32, rough: f32) -> f32 {
  function ggx_d (line 612) | fn ggx_d(nh: f32, rough: f32) -> f32 {
  function ggx_g (line 626) | fn ggx_g(vh: f32, vn: f32, rough: f32) -> f32 {
  function sample (line 642) | pub fn sample(
  function evaluate (line 659) | pub fn evaluate(
  function estimate_eval_over_sphere_light (line 672) | pub fn estimate_eval_over_sphere_light(
  function dielectric_fresnel_from_fac (line 696) | fn dielectric_fresnel_from_fac(fresnel_fac: f32, c: f32) -> f32 {
  function schlick_fresnel_from_fac (line 714) | fn schlick_fresnel_from_fac(frensel_fac: f32, c: f32) -> f32 {
  function dielectric_fresnel (line 729) | fn dielectric_fresnel(ior_ratio: f32, c: f32) -> f32 {
  function schlick_fresnel (line 747) | fn schlick_fresnel(ior_ratio: f32, c: f32) -> f32 {

FILE: src/surface/bilinear_patch.rs
  type BilinearPatch (line 8) | pub struct BilinearPatch<'a> {
  function bilerp_point (line 29) | fn bilerp_point(patch: [Point; 4], uv: (f32, f32)) -> Point {
  type BilinearSubPatch (line 36) | pub struct BilinearSubPatch<'a> {
  method split (line 43) | fn split<F>(&self, metric: F) -> Option<(Self, Self)>

FILE: src/surface/micropoly_batch.rs
  constant MAX_LEAF_TRIANGLE_COUNT (line 19) | const MAX_LEAF_TRIANGLE_COUNT: usize = 3;
  type MicropolyBatch (line 28) | pub struct MicropolyBatch<'a> {
  function from_verts_and_indices (line 49) | pub fn from_verts_and_indices<'b>(
  method bounds (line 142) | fn bounds(&self) -> &[BBox] {
  function intersect_rays (line 148) | fn intersect_rays(

FILE: src/surface/mod.rs
  constant MAX_EDGE_DICE (line 19) | const MAX_EDGE_DICE: u32 = 128;
  type Surface (line 21) | pub trait Surface: Boundable + Debug + Sync {
    method intersect_rays (line 22) | fn intersect_rays(
  type Splitable (line 32) | pub trait Splitable: Copy {
    method split (line 34) | fn split<F>(&self, metric: F) -> Option<(Self, Self)>
  type PointOrder (line 40) | pub enum PointOrder {
  function point_order (line 45) | pub fn point_order(p1: Point, p2: Point) -> PointOrder {
  type SurfaceIntersection (line 72) | pub enum SurfaceIntersection {
  type SurfaceIntersectionData (line 82) | pub struct SurfaceIntersectionData {

FILE: src/surface/triangle.rs
  type RayTriPrecompute (line 9) | pub struct RayTriPrecompute {
    method new (line 15) | pub fn new(ray_dir: Vector) -> RayTriPrecompute {
  function intersect_ray (line 58) | pub fn intersect_ray(
  function surface_point (line 155) | pub fn surface_point(tri: (Point, Point, Point), bary: (f32, f32, f32)) ...
  function max_abs_3 (line 171) | fn max_abs_3(a: f32, b: f32, c: f32) -> f32 {

FILE: src/surface/triangle_mesh.rs
  constant MAX_LEAF_TRIANGLE_COUNT (line 17) | const MAX_LEAF_TRIANGLE_COUNT: usize = 3;
  type TriangleMesh (line 20) | pub struct TriangleMesh<'a> {
  function from_verts_and_indices (line 29) | pub fn from_verts_and_indices<'b>(
  method bounds (line 119) | fn bounds(&self) -> &[BBox] {
  method intersect_rays (line 125) | fn intersect_rays(

FILE: src/timer.rs
  type Timer (line 6) | pub struct Timer {
    method new (line 11) | pub fn new() -> Timer {
    method tick (line 19) | pub fn tick(&mut self) -> f32 {
    method elapsed (line 28) | pub fn elapsed(self) -> f32 {
    method sleep_until (line 34) | pub fn sleep_until(self, n: f32) {

FILE: src/tracer.rs
  type Tracer (line 15) | pub struct Tracer<'a> {
  function from_assembly (line 22) | pub fn from_assembly(assembly: &'a Assembly) -> Tracer<'a> {
  function trace (line 34) | pub fn trace<'b>(&'b mut self, rays: &mut RayBatch) -> &'b [SurfaceInter...
  function rays_traced (line 39) | pub fn rays_traced(&self) -> u64 {
  type TracerInner (line 44) | struct TracerInner<'a> {
  function trace (line 51) | fn trace<'b>(
  function trace_assembly (line 87) | fn trace_assembly<'b>(
  function trace_object (line 152) | fn trace_object<'b>(

FILE: src/transform_stack.rs
  type TransformStack (line 8) | pub struct TransformStack {
    method new (line 14) | pub fn new() -> TransformStack {
    method clear (line 26) | pub fn clear(&mut self) {
    method push (line 33) | pub fn push(&mut self, xforms: &[Transform]) {
    method pop (line 64) | pub fn pop(&mut self) {
    method top (line 76) | pub fn top(&self) -> &[Transform] {

FILE: sub_crates/bvh_order/build.rs
  function main (line 5) | fn main() {

FILE: sub_crates/bvh_order/src/lib.rs
  type SplitAxes (line 20) | pub enum SplitAxes {
  function calc_traversal_code (line 30) | pub fn calc_traversal_code(split: SplitAxes) -> u8 {

FILE: sub_crates/color/build.rs
  type Chromaticities (line 4) | struct Chromaticities {
  function main (line 11) | fn main() {
  function write_conversion_functions (line 72) | fn write_conversion_functions(space_name: &str, chroma: Chromaticities, ...
  function rgb_to_xyz (line 200) | fn rgb_to_xyz(chroma: Chromaticities, y: f64) -> [[f64; 3]; 3] {
  function inverse (line 246) | fn inverse(m: [[f64; 3]; 3]) -> [[f64; 3]; 3] {

FILE: sub_crates/color/src/lib.rs
  type Space (line 6) | pub enum Space {

FILE: sub_crates/compact/benches/bench.rs
  function unsigned32_encode_1000_values (line 11) | fn unsigned32_encode_1000_values(bench: &mut Bencher) {
  function unsigned32_decode_1000_values (line 23) | fn unsigned32_decode_1000_values(bench: &mut Bencher) {
  function unsigned40_encode_1000_values (line 33) | fn unsigned40_encode_1000_values(bench: &mut Bencher) {
  function unsigned40_decode_1000_values (line 45) | fn unsigned40_decode_1000_values(bench: &mut Bencher) {
  function signed48_encode_1000_values (line 61) | fn signed48_encode_1000_values(bench: &mut Bencher) {
  function signed48_decode_1000_values (line 73) | fn signed48_decode_1000_values(bench: &mut Bencher) {
  function fluv32_encode_1000_values (line 90) | fn fluv32_encode_1000_values(bench: &mut Bencher) {
  function fluv32_decode_1000_values (line 102) | fn fluv32_decode_1000_values(bench: &mut Bencher) {
  function fluv32_decode_yuv_1000_values (line 112) | fn fluv32_decode_yuv_1000_values(bench: &mut Bencher) {
  function oct32_encode_1000_values (line 122) | fn oct32_encode_1000_values(bench: &mut Bencher) {
  function oct32_encode_precise_1000_values (line 134) | fn oct32_encode_precise_1000_values(bench: &mut Bencher) {
  function oct32_decode_1000_values (line 146) | fn oct32_decode_1000_values(bench: &mut Bencher) {

FILE: sub_crates/compact/src/fluv/fluv32.rs
  constant EXP_BIAS (line 56) | const EXP_BIAS: i32 = 63;
  constant BIAS_OFFSET (line 57) | const BIAS_OFFSET: u32 = 127 - EXP_BIAS as u32;
  constant U_SCALE (line 60) | pub const U_SCALE: f32 = 817.0 / 2.0;
  constant V_SCALE (line 63) | pub const V_SCALE: f32 = 1235.0 / 3.0;
  constant Y_MAX (line 66) | pub const Y_MAX: f32 = ((1u128 << (128 - EXP_BIAS)) - (1u128 << (128 - E...
  constant Y_MIN (line 69) | pub const Y_MIN: f32 = 1.0 / (1u128 << (EXP_BIAS - 1)) as f32;
  constant Y_EPSILON (line 72) | pub const Y_EPSILON: f32 = 1.0 / 512.0;
  function encode (line 76) | pub fn encode(xyz: (f32, f32, f32)) -> u32 {
  function decode (line 131) | pub fn decode(fluv32: u32) -> (f32, f32, f32) {
  function decode_yuv (line 160) | pub fn decode_yuv(fluv32: u32) -> (f32, u8, u8) {
  function round_trip (line 176) | fn round_trip(floats: (f32, f32, f32)) -> (f32, f32, f32) {
  function all_zeros (line 181) | fn all_zeros() {
  function all_ones (line 192) | fn all_ones() {
  function powers_of_two (line 205) | fn powers_of_two() {
  function accuracy_01 (line 223) | fn accuracy_01() {
  function accuracy_02 (line 242) | fn accuracy_02() {
  function integers (line 253) | fn integers() {
  function precision_floor (line 268) | fn precision_floor() {
  function decode_yuv_01 (line 274) | fn decode_yuv_01() {
  function saturate_y (line 282) | fn saturate_y() {
  function inf_saturate (line 290) | fn inf_saturate() {
  function smallest_value_and_underflow (line 299) | fn smallest_value_and_underflow() {
  function negative_z_impossible (line 310) | fn negative_z_impossible() {
  function nans_01 (line 320) | fn nans_01() {
  function nans_02 (line 326) | fn nans_02() {
  function nans_03 (line 332) | fn nans_03() {
  function negative_01 (line 338) | fn negative_01() {
  function negative_02 (line 344) | fn negative_02() {
  function negative_03 (line 350) | fn negative_03() {
  function negative_04 (line 355) | fn negative_04() {

FILE: sub_crates/compact/src/shared_exp/mod.rs
  function fiddle_exp2 (line 15) | fn fiddle_exp2(exp: i32) -> f32 {
  function fiddle_log2 (line 28) | fn fiddle_log2(n: f32) -> i32 {

FILE: sub_crates/compact/src/shared_exp/signed48.rs
  constant MAX (line 21) | pub const MAX: f32 = ((1u128 << (64 - EXP_BIAS)) - (1 << (64 - EXP_BIAS ...
  constant MIN (line 27) | pub const MIN: f32 = -MAX;
  constant MIN_POSITIVE (line 32) | pub const MIN_POSITIVE: f32 = 1.0 / (1u128 << (EXP_BIAS + 12)) as f32;
  constant EPSILON (line 35) | pub const EPSILON: f32 = 1.0 / 4096.0;
  constant EXP_BIAS (line 37) | const EXP_BIAS: i32 = 26;
  function encode (line 48) | pub fn encode(floats: (f32, f32, f32)) -> [u8; 6] {
  function decode (line 56) | pub fn decode(trifloat: [u8; 6]) -> (f32, f32, f32) {
  function encode_64 (line 62) | fn encode_64(floats: (f32, f32, f32)) -> u64 {
  function decode_64 (line 102) | fn decode_64(trifloat: u64) -> (f32, f32, f32) {
  function u64_to_bytes (line 124) | fn u64_to_bytes(n: u64) -> [u8; 6] {
  function bytes_to_u64 (line 136) | fn bytes_to_u64(a: [u8; 6]) -> u64 {
  function round_trip (line 150) | fn round_trip(floats: (f32, f32, f32)) -> (f32, f32, f32) {
  function all_zeros (line 155) | fn all_zeros() {
  function powers_of_two (line 166) | fn powers_of_two() {
  function signs (line 172) | fn signs() {
  function accuracy (line 193) | fn accuracy() {
  function integers (line 203) | fn integers() {
  function precision_floor (line 211) | fn precision_floor() {
  function saturate (line 219) | fn saturate() {
  function inf_saturate (line 238) | fn inf_saturate() {
  function partial_saturate (line 250) | fn partial_saturate() {
  function smallest_value (line 259) | fn smallest_value() {
  function underflow (line 269) | fn underflow() {
  function garbage_upper_bits_decode (line 276) | fn garbage_upper_bits_decode() {
  function nans_01 (line 292) | fn nans_01() {
  function nans_02 (line 298) | fn nans_02() {
  function nans_03 (line 304) | fn nans_03() {

FILE: sub_crates/compact/src/shared_exp/unsigned32.rs
  constant MAX (line 17) | pub const MAX: f32 = ((1u64 << (32 - EXP_BIAS)) - (1 << (32 - EXP_BIAS -...
  constant MIN (line 20) | pub const MIN: f32 = 1.0 / (1 << (EXP_BIAS + 8)) as f32;
  constant EPSILON (line 23) | pub const EPSILON: f32 = 1.0 / 256.0;
  constant EXP_BIAS (line 25) | const EXP_BIAS: i32 = 11;
  function encode (line 36) | pub fn encode(floats: (f32, f32, f32)) -> u32 {
  function decode (line 71) | pub fn decode(trifloat: u32) -> (f32, f32, f32) {
  function round_trip (line 91) | fn round_trip(floats: (f32, f32, f32)) -> (f32, f32, f32) {
  function all_zeros (line 96) | fn all_zeros() {
  function powers_of_two (line 107) | fn powers_of_two() {
  function accuracy_01 (line 113) | fn accuracy_01() {
  function accuracy_02 (line 124) | fn accuracy_02() {
  function integers (line 134) | fn integers() {
  function precision_floor (line 142) | fn precision_floor() {
  function saturate (line 148) | fn saturate() {
  function inf_saturate (line 156) | fn inf_saturate() {
  function partial_saturate (line 165) | fn partial_saturate() {
  function smallest_value (line 172) | fn smallest_value() {
  function underflow (line 179) | fn underflow() {
  function nans_01 (line 187) | fn nans_01() {
  function nans_02 (line 193) | fn nans_02() {
  function nans_03 (line 199) | fn nans_03() {
  function negative_01 (line 205) | fn negative_01() {
  function negative_02 (line 211) | fn negative_02() {
  function negative_03 (line 217) | fn negative_03() {
  function negative_04 (line 222) | fn negative_04() {

FILE: sub_crates/compact/src/shared_exp/unsigned40.rs
  constant MAX (line 17) | pub const MAX: f32 = ((1u128 << (128 - EXP_BIAS)) - (1 << (128 - EXP_BIA...
  constant MIN (line 20) | pub const MIN: f32 = 1.0 / (1u128 << (EXP_BIAS + 10)) as f32;
  constant EPSILON (line 23) | pub const EPSILON: f32 = 1.0 / 1024.0;
  constant EXP_BIAS (line 25) | const EXP_BIAS: i32 = 32;
  function encode (line 36) | pub fn encode(floats: (f32, f32, f32)) -> [u8; 5] {
  function decode (line 44) | pub fn decode(trifloat: [u8; 5]) -> (f32, f32, f32) {
  function encode_64 (line 50) | fn encode_64(floats: (f32, f32, f32)) -> u64 {
  function decode_64 (line 83) | fn decode_64(trifloat: u64) -> (f32, f32, f32) {
  function u64_to_bytes (line 100) | fn u64_to_bytes(n: u64) -> [u8; 5] {
  function bytes_to_u64 (line 112) | fn bytes_to_u64(a: [u8; 5]) -> u64 {
  function round_trip (line 126) | fn round_trip(floats: (f32, f32, f32)) -> (f32, f32, f32) {
  function all_zeros (line 131) | fn all_zeros() {
  function powers_of_two (line 142) | fn powers_of_two() {
  function accuracy_01 (line 148) | fn accuracy_01() {
  function accuracy_02 (line 159) | fn accuracy_02() {
  function integers (line 169) | fn integers() {
  function precision_floor (line 177) | fn precision_floor() {
  function saturate (line 183) | fn saturate() {
  function inf_saturate (line 191) | fn inf_saturate() {
  function partial_saturate (line 200) | fn partial_saturate() {
  function smallest_value (line 214) | fn smallest_value() {
  function underflow (line 221) | fn underflow() {
  function nans_01 (line 229) | fn nans_01() {
  function nans_02 (line 235) | fn nans_02() {
  function nans_03 (line 241) | fn nans_03() {
  function negative_01 (line 247) | fn negative_01() {
  function negative_02 (line 253) | fn negative_02() {
  function negative_03 (line 259) | fn negative_03() {
  function negative_04 (line 264) | fn negative_04() {

FILE: sub_crates/compact/src/unit_vec/oct32.rs
  constant STEP_SIZE (line 7) | const STEP_SIZE: f32 = 1.0 / STEPS;
  constant STEPS (line 8) | const STEPS: f32 = ((1 << (16 - 1)) - 1) as f32;
  function encode (line 15) | pub fn encode(vec: (f32, f32, f32)) -> u32 {
  function encode_precise (line 24) | pub fn encode_precise(vec: (f32, f32, f32)) -> u32 {
  function decode (line 68) | pub fn decode(n: u32) -> (f32, f32, f32) {
  function vec3_to_oct (line 73) | fn vec3_to_oct(vec: (f32, f32, f32)) -> (f32, f32) {
  function oct_to_vec3 (line 86) | fn oct_to_vec3(oct: (f32, f32)) -> (f32, f32, f32) {
  function to_snorm_16 (line 101) | fn to_snorm_16(n: f32) -> u16 {
  function from_snorm_16 (line 106) | fn from_snorm_16(n: u16) -> f32 {
  function sign (line 111) | fn sign(n: f32) -> f32 {
  function axis_directions (line 124) | fn axis_directions() {

FILE: sub_crates/compact/tests/proptest_tests.rs
  function cos_gt (line 9) | fn cos_gt(a: (f32, f32, f32), b: (f32, f32, f32), cos: f64) -> bool {
  function l1_delta_lt (line 30) | fn l1_delta_lt(a: (f32, f32, f32), b: (f32, f32, f32), delta: f32) -> bo...

FILE: sub_crates/halton/build.rs
  constant NUM_DIMENSIONS (line 28) | const NUM_DIMENSIONS: usize = 128;
  function main (line 30) | fn main() {
  function is_prime (line 237) | fn is_prime(p: usize) -> bool {
  function get_faure_permutation (line 247) | fn get_faure_permutation(faure: &Vec<Vec<usize>>, b: usize) -> Vec<usize> {
  function invert (line 283) | fn invert(faure: &Vec<Vec<usize>>, base: usize, mut index: usize, digits...

FILE: sub_crates/math3d/src/lib.rs
  type DotProduct (line 11) | pub trait DotProduct {
    method dot (line 12) | fn dot(self, other: Self) -> f32;
  function dot (line 16) | pub fn dot<T: DotProduct>(a: T, b: T) -> f32 {
  type CrossProduct (line 21) | pub trait CrossProduct {
    method cross (line 22) | fn cross(self, other: Self) -> Self;
  function cross (line 26) | pub fn cross<T: CrossProduct>(a: T, b: T) -> T {

FILE: sub_crates/math3d/src/normal.rs
  type Normal (line 14) | pub struct Normal {
    method new (line 20) | pub fn new(x: f32, y: f32, z: f32) -> Normal {
    method length (line 27) | pub fn length(&self) -> f32 {
    method length2 (line 32) | pub fn length2(&self) -> f32 {
    method normalized (line 37) | pub fn normalized(&self) -> Normal {
    method into_vector (line 44) | pub fn into_vector(self) -> Vector {
    method get_n (line 49) | pub fn get_n(&self, n: usize) -> f32 {
    method x (line 59) | pub fn x(&self) -> f32 {
    method y (line 64) | pub fn y(&self) -> f32 {
    method z (line 69) | pub fn z(&self) -> f32 {
    method set_x (line 74) | pub fn set_x(&mut self, x: f32) {
    method set_y (line 79) | pub fn set_y(&mut self, y: f32) {
    method set_z (line 84) | pub fn set_z(&mut self, z: f32) {
    type Output (line 119) | type Output = Normal;
    method mul (line 122) | fn mul(self, other: f32) -> Normal {
    type Output (line 130) | type Output = Normal;
    method mul (line 133) | fn mul(self, other: Transform) -> Normal {
    type Output (line 141) | type Output = Normal;
    method div (line 144) | fn div(self, other: f32) -> Normal {
  method eq (line 91) | fn eq(&self, other: &Normal) -> bool {
  type Output (line 97) | type Output = Normal;
  method add (line 100) | fn add(self, other: Normal) -> Normal {
  type Output (line 108) | type Output = Normal;
  method sub (line 111) | fn sub(self, other: Normal) -> Normal {
  type Output (line 152) | type Output = Normal;
  method neg (line 155) | fn neg(self) -> Normal {
  method dot (line 162) | fn dot(self, other: Normal) -> f32 {
  method cross (line 169) | fn cross(self, other: Normal) -> Normal {
  function add (line 183) | fn add() {
  function sub (line 192) | fn sub() {
  function mul_scalar (line 201) | fn mul_scalar() {
  function mul_matrix_1 (line 210) | fn mul_matrix_1() {
  function div (line 223) | fn div() {
  function length (line 232) | fn length() {
  function length2 (line 238) | fn length2() {
  function normalized (line 244) | fn normalized() {
  function dot_test (line 254) | fn dot_test() {
  function cross_test (line 263) | fn cross_test() {

FILE: sub_crates/math3d/src/point.rs
  type Point (line 14) | pub struct Point {
    method new (line 20) | pub fn new(x: f32, y: f32, z: f32) -> Point {
    method min (line 27) | pub fn min(&self, other: Point) -> Point {
    method max (line 37) | pub fn max(&self, other: Point) -> Point {
    method into_vector (line 47) | pub fn into_vector(self) -> Vector {
    method get_n (line 52) | pub fn get_n(&self, n: usize) -> f32 {
    method x (line 62) | pub fn x(&self) -> f32 {
    method y (line 67) | pub fn y(&self) -> f32 {
    method z (line 72) | pub fn z(&self) -> f32 {
    method set_x (line 77) | pub fn set_x(&mut self, x: f32) {
    method set_y (line 82) | pub fn set_y(&mut self, y: f32) {
    method set_z (line 87) | pub fn set_z(&mut self, z: f32) {
    type Output (line 100) | type Output = Point;
    method add (line 103) | fn add(self, other: Vector) -> Point {
    type Output (line 122) | type Output = Point;
    method sub (line 125) | fn sub(self, other: Vector) -> Point {
    type Output (line 133) | type Output = Point;
    method mul (line 136) | fn mul(self, other: Transform) -> Point {
  method eq (line 94) | fn eq(&self, other: &Point) -> bool {
  type Output (line 111) | type Output = Vector;
  method sub (line 114) | fn sub(self, other: Point) -> Vector {
  function add (line 149) | fn add() {
  function sub (line 158) | fn sub() {
  function mul_matrix_1 (line 167) | fn mul_matrix_1() {
  function mul_matrix_2 (line 177) | fn mul_matrix_2() {
  function mul_matrix_3 (line 187) | fn mul_matrix_3() {

FILE: sub_crates/math3d/src/transform.rs
  type Transform (line 12) | pub struct Transform(pub Affine3A);
    method new (line 17) | pub fn new() -> Transform {
    method new_from_values (line 29) | pub fn new_from_values(
    method from_location (line 50) | pub fn from_location(loc: Point) -> Transform {
    method aprx_eq (line 58) | pub fn aprx_eq(&self, other: Transform, epsilon: f32) -> bool {
    method inverse (line 77) | pub fn inverse(&self) -> Transform {
    type Output (line 100) | type Output = Self;
    method mul (line 103) | fn mul(self, other: f32) -> Self {
  method default (line 83) | fn default() -> Self {
  type Output (line 90) | type Output = Self;
  method mul (line 93) | fn mul(self, other: Self) -> Self {
  type Output (line 110) | type Output = Self;
  method add (line 113) | fn add(self, other: Self) -> Self {
  function equality_test (line 125) | fn equality_test() {
  function approximate_equality_test (line 136) | fn approximate_equality_test() {
  function multiply_test (line 154) | fn multiply_test() {
  function inverse_test (line 169) | fn inverse_test() {

FILE: sub_crates/math3d/src/vector.rs
  type Vector (line 14) | pub struct Vector {
    method new (line 20) | pub fn new(x: f32, y: f32, z: f32) -> Vector {
    method length (line 27) | pub fn length(&self) -> f32 {
    method length2 (line 32) | pub fn length2(&self) -> f32 {
    method normalized (line 37) | pub fn normalized(&self) -> Vector {
    method abs (line 44) | pub fn abs(&self) -> Vector {
    method into_point (line 51) | pub fn into_point(self) -> Point {
    method into_normal (line 56) | pub fn into_normal(self) -> Normal {
    method get_n (line 61) | pub fn get_n(&self, n: usize) -> f32 {
    method x (line 71) | pub fn x(&self) -> f32 {
    method y (line 76) | pub fn y(&self) -> f32 {
    method z (line 81) | pub fn z(&self) -> f32 {
    method set_x (line 86) | pub fn set_x(&mut self, x: f32) {
    method set_y (line 91) | pub fn set_y(&mut self, y: f32) {
    method set_z (line 96) | pub fn set_z(&mut self, z: f32) {
    type Output (line 131) | type Output = Vector;
    method mul (line 134) | fn mul(self, other: f32) -> Vector {
    type Output (line 142) | type Output = Vector;
    method mul (line 145) | fn mul(self, other: Transform) -> Vector {
    type Output (line 153) | type Output = Vector;
    method div (line 156) | fn div(self, other: f32) -> Vector {
  method eq (line 103) | fn eq(&self, other: &Vector) -> bool {
  type Output (line 109) | type Output = Vector;
  method add (line 112) | fn add(self, other: Vector) -> Vector {
  type Output (line 120) | type Output = Vector;
  method sub (line 123) | fn sub(self, other: Vector) -> Vector {
  type Output (line 164) | type Output = Vector;
  method neg (line 167) | fn neg(self) -> Vector {
  method dot (line 174) | fn dot(self, other: Vector) -> f32 {
  method cross (line 181) | fn cross(self, other: Vector) -> Vector {
  function add (line 194) | fn add() {
  function sub (line 203) | fn sub() {
  function mul_scalar (line 212) | fn mul_scalar() {
  function mul_matrix_1 (line 221) | fn mul_matrix_1() {
  function mul_matrix_2 (line 230) | fn mul_matrix_2() {
  function div (line 239) | fn div() {
  function length (line 248) | fn length() {
  function length2 (line 254) | fn length2() {
  function normalized (line 260) | fn normalized() {
  function dot_test (line 270) | fn dot_test() {
  function cross_test (line 279) | fn cross_test() {

FILE: sub_crates/spectral_upsampling/build.rs
  constant RGB2SPEC_N_COEFFS (line 11) | const RGB2SPEC_N_COEFFS: usize = 3;
  constant TABLE_RES (line 14) | const TABLE_RES: usize = 64;
  constant MID_VALUE (line 17) | const MID_VALUE: f32 = 0.5;
  function main (line 19) | fn main() {
  type RGB2Spec (line 93) | pub struct RGB2Spec {
  function rgb2spec_load (line 99) | pub fn rgb2spec_load(filepath: &str) -> RGB2Spec {
  function rgb2spec_load_small (line 170) | pub fn rgb2spec_load_small(filepath: &str) -> Vec<[(f32, f32, f32); 2]> {
  function rgb2spec_find_interval (line 206) | fn rgb2spec_find_interval(values: &[f32], x: f32) -> usize {

FILE: sub_crates/spectral_upsampling/src/jakob.rs
  constant RGB2SPEC_N_COEFFS (line 12) | const RGB2SPEC_N_COEFFS: usize = 3;
  function rec709_to_spectrum_p4 (line 18) | pub fn rec709_to_spectrum_p4(lambdas: Vec4, rgb: (f32, f32, f32)) -> Vec4 {
  function rec2020_to_spectrum_p4 (line 29) | pub fn rec2020_to_spectrum_p4(lambdas: Vec4, rgb: (f32, f32, f32)) -> Ve...
  function aces_to_spectrum_p4 (line 40) | pub fn aces_to_spectrum_p4(lambdas: Vec4, rgb: (f32, f32, f32)) -> Vec4 {
  function small_rgb_to_spectrum_p4 (line 55) | fn small_rgb_to_spectrum_p4(
  function rgb2spec_fma_4 (line 136) | fn rgb2spec_fma_4(a: Vec4, b: Vec4, c: Vec4) -> Vec4 {
  function rgb2spec_eval_4 (line 140) | fn rgb2spec_eval_4(coeff: [f32; RGB2SPEC_N_COEFFS], lambda: Vec4) -> Vec4 {

FILE: sub_crates/spectral_upsampling/src/meng.rs
  function spectrum_xyz_to_p (line 35) | pub fn spectrum_xyz_to_p(lambda: f32, xyz: (f32, f32, f32)) -> f32 {
  function spectrum_xyz_to_p_4 (line 177) | pub fn spectrum_xyz_to_p_4(lambdas: Vec4, xyz: (f32, f32, f32)) -> Vec4 {
  function spectrum_apply_3x2 (line 332) | fn spectrum_apply_3x2(matrix: &[f32; 6], src: (f32, f32)) -> (f32, f32) {
  function spectrum_xy_to_uv (line 348) | fn spectrum_xy_to_uv(xy: (f32, f32)) -> (f32, f32) {

FILE: sub_crates/spectral_upsampling/src/meng/generate_meng_spectra_tables.py
  class Cmf (line 51) | class Cmf:
    method load (line 55) | def load(cls, filename):
    method num_bins (line 60) | def num_bins(cls):
    method bin_size (line 64) | def bin_size(cls):
    method wavelength (line 68) | def wavelength(cls):
    method x_bar (line 72) | def x_bar(cls):
    method y_bar (line 76) | def y_bar(cls):
    method z_bar (line 80) | def z_bar(cls):
    method xyz_from_spectrum (line 84) | def xyz_from_spectrum(cls, spectrum):
    method xyz_ee_white (line 98) | def xyz_ee_white(cls):
  class Transform (line 105) | class Transform:
    method hom (line 110) | def hom(v2):
    method dehom (line 115) | def dehom(v3):
    method xyz_from_xyy (line 126) | def xyz_from_xyy(xyy):
    method xyy_from_xyz (line 133) | def xyy_from_xyz(xyz):
    method xyz_from_srgb (line 140) | def xyz_from_srgb(srgb):
    method srgb_from_xyz (line 153) | def srgb_from_xyz(xyz):
    method xyz_from_ergb (line 161) | def xyz_from_ergb(ergb):
    method init_xystar (line 176) | def init_xystar(cls):
    method xystar_from_xy (line 208) | def xystar_from_xy(cls, xy):
    method xy_from_xystar (line 212) | def xy_from_xystar(cls, xystar):
    method init_uv (line 224) | def init_uv(cls, xystar_bbox, grid_res):
    method uv_from_xy (line 246) | def uv_from_xy(cls, xy):
    method xy_from_uv (line 250) | def xy_from_uv(cls, uv):
    method uv_from_xystar (line 254) | def uv_from_xystar(cls, xystar):
    method xystar_from_uv (line 258) | def xystar_from_uv(cls, uv):
  function multiprocess_progress (line 265) | def multiprocess_progress(data, functor, finished, data_size, early_clip...
  function find_spectrum (line 355) | def find_spectrum(xyz):
  function horseshoe_path (line 393) | def horseshoe_path():
  class DataPoint (line 419) | class DataPoint:
    method __init__ (line 420) | def __init__(self):
    method update_uv (line 430) | def update_uv(self):
  class GridCell (line 433) | class GridCell:
    method __init__ (line 434) | def __init__(self):
  function find_intersection (line 440) | def find_intersection(p0, p1, i0, i1, clip_path):
  function clip_edge (line 461) | def clip_edge(d0, d1, clip_path):
  function generate_xystar_grid (line 476) | def generate_xystar_grid(scale):
  function plot_grid (line 601) | def plot_grid(filename, data_points, grid, bbox_xystar, xystar=True):
  function compute_spectrum (line 670) | def compute_spectrum(data_point):
  function compute_spectra (line 702) | def compute_spectra(data_points):
  function plot_spectra (line 716) | def plot_spectra(data_points):
  function write_output (line 772) | def write_output(data_points, grid, grid_res, filename):
  function create_triangle_fans (line 907) | def create_triangle_fans(grid):
  function compute_max_brightness (line 945) | def compute_max_brightness(point):
  function compute_reflectance_map (line 961) | def compute_reflectance_map(res):

FILE: sub_crates/spectral_upsampling/src/meng/meng_spectra_tables.rs
  constant EQUAL_ENERGY_REFLECTANCE (line 10) | pub const EQUAL_ENERGY_REFLECTANCE: f32 = 0.009355121400914532;
  constant SPECTRUM_GRID_WIDTH (line 13) | pub(crate) const SPECTRUM_GRID_WIDTH: i32 = 12;
  constant SPECTRUM_GRID_HEIGHT (line 14) | pub(crate) const SPECTRUM_GRID_HEIGHT: i32 = 14;
  constant SPECTRUM_SAMPLE_MIN (line 17) | pub const SPECTRUM_SAMPLE_MIN: f32 = 360.0;
  constant SPECTRUM_SAMPLE_MAX (line 18) | pub const SPECTRUM_SAMPLE_MAX: f32 = 830.0;
  constant SPECTRUM_BIN_SIZE (line 19) | pub(crate) const SPECTRUM_BIN_SIZE: f32 = 5.0;
  constant SPECTRUM_NUM_SAMPLES (line 20) | pub(crate) const SPECTRUM_NUM_SAMPLES: i32 = 95;
  constant SPECTRUM_MAT_XY_TO_XYSTAR (line 23) | pub(crate) const SPECTRUM_MAT_XY_TO_XYSTAR: [f32; 6] = [
  constant SPECTRUM_MAT_XYSTAR_TO_XY (line 27) | pub(crate) const SPECTRUM_MAT_XYSTAR_TO_XY: [f32; 6] = [
  constant SPECTRUM_MAT_XY_TO_UV (line 32) | pub(crate) const SPECTRUM_MAT_XY_TO_UV: [f32; 6] = [
  constant SPECTRUM_MAT_UV_TO_XY (line 36) | pub(crate) const SPECTRUM_MAT_UV_TO_XY: [f32; 6] = [
  type SpectrumGridCell (line 43) | pub(crate) struct SpectrumGridCell {
  constant SPECTRUM_GRID (line 49) | pub(crate) const SPECTRUM_GRID: [SpectrumGridCell; 168] = [
  type SpectrumDataPoint (line 222) | pub(crate) struct SpectrumDataPoint {
  constant SPECTRUM_DATA_POINTS (line 228) | pub(crate) const SPECTRUM_DATA_POINTS: [SpectrumDataPoint; 186] = [
  constant CMF_WAVELENGTH (line 1162) | pub(crate) const CMF_WAVELENGTH: [f32; 95] = [
  constant CMF_X (line 1165) | pub(crate) const CMF_X: [f32; 95] = [
  constant CMF_Y (line 1168) | pub(crate) const CMF_Y: [f32; 95] = [
  constant CMF_Z (line 1171) | pub(crate) const CMF_Z: [f32; 95] = [
Condensed preview — 115 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (934K chars).
[
  {
    "path": ".github/pull_request_template.md",
    "chars": 375,
    "preview": "## Sorry, but...\n\nI'm sorry you put the time and effort into creating this pull request, but I am\nnot currently acceptin"
  },
  {
    "path": ".gitignore",
    "chars": 141,
    "preview": "target\n*.rs.bk\nclippy.toml\n\n# Python Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n\n.zedstate\n.vscode\ntes"
  },
  {
    "path": "Cargo.toml",
    "chars": 1062,
    "preview": "[workspace]\nmembers = [\n    \"sub_crates/bvh_order\",\n    \"sub_crates/color\",\n    \"sub_crates/compact\",\n    \"sub_crates/ha"
  },
  {
    "path": "LICENSE.md",
    "chars": 1017,
    "preview": "## Psychopath\n\nWith the exception of files under `psychoblend/` and `sub_crates/`, this project is licensed under the GP"
  },
  {
    "path": "README.md",
    "chars": 2957,
    "preview": "# Overview\n\nPsychopath is a path tracing 3d renderer.  You can read about its development\nat [psychopath.io](http://psyc"
  },
  {
    "path": "example_scenes/cornell_box.psy",
    "chars": 5557,
    "preview": "Scene $Scene_fr1 {\n    Output {\n        Path [\"test_renders/cornell_box.png\"]\n    }\n    RenderSettings {\n        Resolut"
  },
  {
    "path": "example_scenes/cube.psy",
    "chars": 2355,
    "preview": "Scene $Scene_fr1 {\n    Output {\n        Path [\"test_renders/cube.png\"]\n    }\n    RenderSettings {\n        Resolution [96"
  },
  {
    "path": "licenses/Apache-2.0.txt",
    "chars": 11358,
    "preview": "\n                                 Apache License\n                           Version 2.0, January 2004\n                  "
  },
  {
    "path": "licenses/GPL-2.0.txt",
    "chars": 18092,
    "preview": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 2, June 1991\n\n Copyright (C) 1989, 1991 Fr"
  },
  {
    "path": "licenses/GPL-3.0.txt",
    "chars": 35149,
    "preview": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free "
  },
  {
    "path": "licenses/MIT.txt",
    "chars": 1058,
    "preview": "Copyright (c) 2020 Nathan Vegdahl\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this "
  },
  {
    "path": "psychoblend/LICENSE.md",
    "chars": 717,
    "preview": "Copyright (c) 2020 Nathan Vegdahl\n\nThis program is free software; you can redistribute it and/or\nmodify it under the ter"
  },
  {
    "path": "psychoblend/__init__.py",
    "chars": 6870,
    "preview": "bl_info = {\n    \"name\": \"PsychoBlend\",\n    \"version\": (0, 1),\n    \"author\": \"Nathan Vegdahl\",\n    \"blender\": (2, 70, 0),"
  },
  {
    "path": "psychoblend/assembly.py",
    "chars": 15552,
    "preview": "import bpy\n\nfrom .util import escape_name, mat2str, needs_def_mb, needs_xform_mb, ExportCancelled\n\nclass Assembly:\n    d"
  },
  {
    "path": "psychoblend/psy_export.py",
    "chars": 4330,
    "preview": "import bpy\n\nfrom math import log\n\nfrom .assembly import Assembly\nfrom .util import escape_name, mat2str, ExportCancelled"
  },
  {
    "path": "psychoblend/render.py",
    "chars": 8853,
    "preview": "import bpy\nimport time\nimport os\nimport subprocess\nimport base64\nimport struct\nfrom . import psy_export\n\nclass Psychopat"
  },
  {
    "path": "psychoblend/ui.py",
    "chars": 9497,
    "preview": "import bpy\n\n# Use some of the existing buttons.\nfrom bl_ui import properties_render\nproperties_render.RENDER_PT_render.C"
  },
  {
    "path": "psychoblend/util.py",
    "chars": 2210,
    "preview": "class ExportCancelled(Exception):\n    \"\"\" Indicates that the render was cancelled in the middle of exporting\n        the"
  },
  {
    "path": "psychoblend/world.py",
    "chars": 5697,
    "preview": "import bpy\n\nfrom math import degrees, tan, atan\nfrom mathutils import Vector, Matrix\n\nfrom .util import escape_name, mat"
  },
  {
    "path": "src/accel/bvh.rs",
    "chars": 6943,
    "preview": "#![allow(dead_code)]\n\nuse mem_arena::MemArena;\n\nuse crate::{\n    algorithm::partition, bbox::BBox, boundable::Boundable,"
  },
  {
    "path": "src/accel/bvh4.rs",
    "chars": 13491,
    "preview": "//! This BVH4 implementation is based on the ideas from the paper\n//! \"Efficient Ray Tracing Kernels for Modern CPU Arch"
  },
  {
    "path": "src/accel/bvh_base.rs",
    "chars": 7035,
    "preview": "#![allow(dead_code)]\n\nuse crate::{algorithm::merge_slices_append, bbox::BBox, lerp::lerp_slice, math::log2_64};\n\nuse sup"
  },
  {
    "path": "src/accel/light_array.rs",
    "chars": 1796,
    "preview": "use kioku::Arena;\n\nuse crate::{\n    bbox::BBox,\n    math::{Normal, Point, Vector},\n    shading::surface_closure::Surface"
  },
  {
    "path": "src/accel/light_tree.rs",
    "chars": 11252,
    "preview": "use std::mem::{transmute, MaybeUninit};\n\nuse kioku::Arena;\n\nuse crate::{\n    algorithm::merge_slices_append,\n    bbox::B"
  },
  {
    "path": "src/accel/mod.rs",
    "chars": 803,
    "preview": "// mod bvh;\nmod bvh4;\nmod bvh_base;\nmod light_array;\nmod light_tree;\nmod objects_split;\n\nuse std::cell::Cell;\n\nuse crate"
  },
  {
    "path": "src/accel/objects_split.rs",
    "chars": 10786,
    "preview": "#![allow(dead_code)]\n\nuse std::cmp::Ordering;\n\nuse crate::{\n    algorithm::{partition, quick_select},\n    bbox::BBox,\n  "
  },
  {
    "path": "src/algorithm.rs",
    "chars": 10160,
    "preview": "#![allow(dead_code)]\n\nuse std::{\n    cmp::{self, Ordering},\n    mem::MaybeUninit,\n};\n\nuse crate::{\n    hash::hash_u64,\n "
  },
  {
    "path": "src/bbox.rs",
    "chars": 4895,
    "preview": "#![allow(dead_code)]\n\nuse std::{\n    iter::Iterator,\n    ops::{BitOr, BitOrAssign},\n};\n\nuse crate::{\n    lerp::{lerp, le"
  },
  {
    "path": "src/bbox4.rs",
    "chars": 4123,
    "preview": "#![allow(dead_code)]\n\nuse std;\nuse std::ops::{BitOr, BitOrAssign};\n\nuse crate::{\n    bbox::BBox,\n    lerp::{lerp, Lerp},"
  },
  {
    "path": "src/boundable.rs",
    "chars": 103,
    "preview": "#![allow(dead_code)]\n\nuse crate::bbox::BBox;\n\npub trait Boundable {\n    fn bounds(&self) -> &[BBox];\n}\n"
  },
  {
    "path": "src/camera.rs",
    "chars": 3293,
    "preview": "#![allow(dead_code)]\n\nuse kioku::Arena;\n\nuse crate::{\n    lerp::lerp_slice,\n    math::{Point, Transform, Vector},\n    ra"
  },
  {
    "path": "src/color.rs",
    "chars": 19353,
    "preview": "use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign};\n\npub use color::{\n    rec709_e_to_xyz, rec709_to_xyz, xy"
  },
  {
    "path": "src/fp_utils.rs",
    "chars": 3234,
    "preview": "//! Utilities for handling floating point precision issues\n//!\n//! This is based on the work in section 3.9 of \"Physical"
  },
  {
    "path": "src/hash.rs",
    "chars": 757,
    "preview": "pub fn hash_u32(n: u32, seed: u32) -> u32 {\n    let mut hash = n;\n    for _ in 0..3 {\n        hash = hash.wrapping_mul(0"
  },
  {
    "path": "src/hilbert.rs",
    "chars": 1674,
    "preview": "#![allow(dead_code)]\n\nconst N: u32 = 1 << 16;\n\n// Utility function used by the functions below.\nfn hil_rot(n: u32, rx: u"
  },
  {
    "path": "src/image.rs",
    "chars": 9018,
    "preview": "#![allow(dead_code)]\n\nuse std::{\n    cell::{RefCell, UnsafeCell},\n    cmp,\n    fs::File,\n    io,\n    io::Write,\n    mark"
  },
  {
    "path": "src/lerp.rs",
    "chars": 7796,
    "preview": "#![allow(dead_code)]\n\nuse math3d::{Normal, Point, Transform, Vector};\n\n/// Trait for allowing a type to be linearly inte"
  },
  {
    "path": "src/light/distant_disk_light.rs",
    "chars": 3032,
    "preview": "use std::f64::consts::PI as PI_64;\n\nuse kioku::Arena;\n\nuse crate::{\n    color::{Color, SpectralSample},\n    lerp::lerp_s"
  },
  {
    "path": "src/light/mod.rs",
    "chars": 3246,
    "preview": "mod distant_disk_light;\nmod rectangle_light;\nmod sphere_light;\n\nuse std::fmt::Debug;\n\nuse crate::{\n    color::SpectralSa"
  },
  {
    "path": "src/light/rectangle_light.rs",
    "chars": 12880,
    "preview": "use kioku::Arena;\n\nuse crate::{\n    bbox::BBox,\n    boundable::Boundable,\n    color::{Color, SpectralSample},\n    lerp::"
  },
  {
    "path": "src/light/sphere_light.rs",
    "chars": 12642,
    "preview": "use std::f64::consts::PI as PI_64;\n\nuse kioku::Arena;\n\nuse crate::{\n    bbox::BBox,\n    boundable::Boundable,\n    color:"
  },
  {
    "path": "src/main.rs",
    "chars": 12384,
    "preview": "#![allow(clippy::float_cmp)]\n#![allow(clippy::inline_always)]\n#![allow(clippy::many_single_char_names)]\n#![allow(clippy:"
  },
  {
    "path": "src/math.rs",
    "chars": 5667,
    "preview": "#![allow(dead_code)]\n\nuse std::f32;\n\npub use math3d::{cross, dot, CrossProduct, DotProduct, Normal, Point, Transform, Ve"
  },
  {
    "path": "src/mis.rs",
    "chars": 394,
    "preview": "#![allow(dead_code)]\n\npub fn balance_heuristic(a: f32, b: f32) -> f32 {\n    if a.is_infinite() {\n        a\n    } else {\n"
  },
  {
    "path": "src/parse/basics.rs",
    "chars": 3798,
    "preview": "//! Some basic nom parsers\n#![allow(dead_code)]\n\nuse std::str::{self, FromStr};\n\nuse nom::{\n    character::complete::{di"
  },
  {
    "path": "src/parse/data_tree.rs",
    "chars": 20610,
    "preview": "#![allow(dead_code)]\n\nuse std::{iter::Iterator, result::Result, slice};\n\n#[derive(Debug, Eq, PartialEq)]\npub enum DataTr"
  },
  {
    "path": "src/parse/mod.rs",
    "chars": 175,
    "preview": "pub mod basics;\nmod data_tree;\nmod psy;\nmod psy_assembly;\nmod psy_light;\nmod psy_mesh_surface;\nmod psy_surface_shader;\n\n"
  },
  {
    "path": "src/parse/psy.rs",
    "chars": 21681,
    "preview": "#![allow(dead_code)]\n\nuse std::{f32, result::Result};\n\nuse nom::{combinator::all_consuming, sequence::tuple, IResult};\n\n"
  },
  {
    "path": "src/parse/psy_assembly.rs",
    "chars": 7174,
    "preview": "#![allow(dead_code)]\n\nuse std::result::Result;\n\nuse kioku::Arena;\n\nuse crate::scene::{Assembly, AssemblyBuilder, Object}"
  },
  {
    "path": "src/parse/psy_light.rs",
    "chars": 6117,
    "preview": "#![allow(dead_code)]\n\nuse std::result::Result;\n\nuse nom::{combinator::all_consuming, sequence::tuple, IResult};\n\nuse kio"
  },
  {
    "path": "src/parse/psy_mesh_surface.rs",
    "chars": 3588,
    "preview": "#![allow(dead_code)]\n\nuse std::result::Result;\n\nuse nom::{sequence::tuple, IResult};\n\nuse kioku::Arena;\n\nuse crate::{\n  "
  },
  {
    "path": "src/parse/psy_surface_shader.rs",
    "chars": 4552,
    "preview": "#![allow(dead_code)]\n\nuse std::result::Result;\n\nuse nom::{combinator::all_consuming, IResult};\n\nuse kioku::Arena;\n\nuse c"
  },
  {
    "path": "src/ray.rs",
    "chars": 12164,
    "preview": "#![allow(dead_code)]\n\nuse glam::BVec4A;\n\nuse crate::math::{Point, Transform, Vector};\n\ntype RayIndexType = u16;\ntype Fla"
  },
  {
    "path": "src/renderer.rs",
    "chars": 27911,
    "preview": "use std::{\n    cell::Cell,\n    cmp,\n    cmp::min,\n    io::{self, Write},\n    sync::{Mutex, RwLock},\n};\n\nuse crossbeam::s"
  },
  {
    "path": "src/sampling/mod.rs",
    "chars": 317,
    "preview": "mod monte_carlo;\n\npub use self::monte_carlo::{\n    cosine_sample_hemisphere, spherical_triangle_solid_angle, square_to_c"
  },
  {
    "path": "src/sampling/monte_carlo.rs",
    "chars": 6656,
    "preview": "#![allow(dead_code)]\n\nuse std::{f32::consts::FRAC_PI_4 as QPI_32, f32::consts::PI as PI_32, f64::consts::PI as PI_64};\n\n"
  },
  {
    "path": "src/scene/assembly.rs",
    "chars": 14061,
    "preview": "use std::collections::HashMap;\n\nuse kioku::Arena;\n\nuse crate::{\n    accel::BVH4,\n    accel::{LightAccel, LightTree},\n   "
  },
  {
    "path": "src/scene/mod.rs",
    "chars": 4290,
    "preview": "mod assembly;\nmod world;\n\nuse crate::{\n    accel::LightAccel,\n    algorithm::weighted_choice,\n    camera::Camera,\n    co"
  },
  {
    "path": "src/scene/world.rs",
    "chars": 176,
    "preview": "use crate::{color::Color, light::WorldLightSource};\n\n#[derive(Debug)]\npub struct World<'a> {\n    pub background_color: C"
  },
  {
    "path": "src/shading/mod.rs",
    "chars": 2046,
    "preview": "pub mod surface_closure;\n\nuse std::fmt::Debug;\n\nuse crate::{color::Color, surface::SurfaceIntersectionData};\n\npub use se"
  },
  {
    "path": "src/shading/surface_closure.rs",
    "chars": 24693,
    "preview": "#![allow(dead_code)]\n\nuse std::f32::consts::PI as PI_32;\n\nuse glam::Vec4;\n\nuse crate::{\n    color::{Color, SpectralSampl"
  },
  {
    "path": "src/surface/bilinear_patch.rs",
    "chars": 4532,
    "preview": "use super::{point_order, PointOrder, Splitable, MAX_EDGE_DICE};\nuse crate::{\n    lerp::{lerp, lerp_slice},\n    math::Poi"
  },
  {
    "path": "src/surface/micropoly_batch.rs",
    "chars": 15367,
    "preview": "#![allow(dead_code)]\n\nuse std::collections::HashMap;\n\nuse kioku::Arena;\n\nuse crate::{\n    accel::BVH4,\n    bbox::BBox,\n "
  },
  {
    "path": "src/surface/mod.rs",
    "chars": 2338,
    "preview": "#![allow(dead_code)]\n\n// pub mod micropoly_batch;\npub mod bilinear_patch;\npub mod micropoly_batch;\npub mod triangle;\npub"
  },
  {
    "path": "src/surface/triangle.rs",
    "chars": 5522,
    "preview": "#![allow(dead_code)]\n\nuse crate::{\n    fp_utils::fp_gamma,\n    math::{Point, Vector},\n};\n\n#[derive(Debug, Copy, Clone)]\n"
  },
  {
    "path": "src/surface/triangle_mesh.rs",
    "chars": 13885,
    "preview": "#![allow(dead_code)]\n\nuse kioku::Arena;\n\nuse crate::{\n    accel::BVH4,\n    bbox::BBox,\n    boundable::Boundable,\n    ler"
  },
  {
    "path": "src/timer.rs",
    "chars": 1242,
    "preview": "#![allow(dead_code)]\n\nuse std::{thread, time::Duration};\n\n#[derive(Copy, Clone)]\npub struct Timer {\n    last_time: u64,\n"
  },
  {
    "path": "src/tracer.rs",
    "chars": 6047,
    "preview": "use std::iter;\n\nuse crate::{\n    accel::ray_code,\n    color::{rec709_to_xyz, Color},\n    lerp::lerp_slice,\n    math::Tra"
  },
  {
    "path": "src/transform_stack.rs",
    "chars": 2385,
    "preview": "use std::{\n    cmp,\n    mem::{transmute, MaybeUninit},\n};\n\nuse crate::{algorithm::merge_slices_to, math::Transform};\n\npu"
  },
  {
    "path": "sub_crates/bvh_order/Cargo.toml",
    "chars": 206,
    "preview": "[package]\nname = \"bvh_order\"\nversion = \"0.1.0\"\nauthors = [\"Nathan Vegdahl <cessen@cessen.com>\"]\nedition = \"2018\"\nlicense"
  },
  {
    "path": "sub_crates/bvh_order/LICENSE.md",
    "chars": 268,
    "preview": "Copyright (c) 2020 Nathan Vegdahl\n\nThis project is licensed under either of\n\n* MIT license (licenses/MIT.txt or http://o"
  },
  {
    "path": "sub_crates/bvh_order/build.rs",
    "chars": 3014,
    "preview": "// Generate table for traversal order of quad BVHs.\n\nuse std::{env, fs::File, io::Write, path::Path};\n\nfn main() {\n    /"
  },
  {
    "path": "sub_crates/bvh_order/src/lib.rs",
    "chars": 1504,
    "preview": "#![allow(dead_code)]\n\n// Include TRAVERSAL_TABLE generated by the build.rs script\ninclude!(concat!(env!(\"OUT_DIR\"), \"/ta"
  },
  {
    "path": "sub_crates/color/Cargo.toml",
    "chars": 198,
    "preview": "[package]\nname = \"color\"\nversion = \"0.1.0\"\nauthors = [\"Nathan Vegdahl <cessen@cessen.com>\"]\nedition = \"2018\"\nlicense = \""
  },
  {
    "path": "sub_crates/color/LICENSE.md",
    "chars": 268,
    "preview": "Copyright (c) 2020 Nathan Vegdahl\n\nThis project is licensed under either of\n\n* MIT license (licenses/MIT.txt or http://o"
  },
  {
    "path": "sub_crates/color/build.rs",
    "chars": 8455,
    "preview": "use std::{env, fs::File, io::Write, path::Path};\n\n#[derive(Copy, Clone)]\nstruct Chromaticities {\n    r: (f64, f64),\n    "
  },
  {
    "path": "sub_crates/color/src/lib.rs",
    "chars": 509,
    "preview": "#![allow(clippy::excessive_precision)]\n#![allow(clippy::unreadable_literal)]\n\n#[allow(non_camel_case_types)]\n#[derive(Co"
  },
  {
    "path": "sub_crates/compact/Cargo.toml",
    "chars": 292,
    "preview": "[package]\nname = \"compact\"\nversion = \"0.1.0\"\nauthors = [\"Nathan Vegdahl <cessen@cessen.com>\"]\nedition = \"2018\"\nlicense ="
  },
  {
    "path": "sub_crates/compact/LICENSE.md",
    "chars": 268,
    "preview": "Copyright (c) 2020 Nathan Vegdahl\n\nThis project is licensed under either of\n\n* MIT license (licenses/MIT.txt or http://o"
  },
  {
    "path": "sub_crates/compact/benches/bench.rs",
    "chars": 4590,
    "preview": "use bencher::{benchmark_group, benchmark_main, black_box, Bencher};\nuse compact::{\n    fluv::fluv32,\n    shared_exp::{si"
  },
  {
    "path": "sub_crates/compact/src/fluv/fluv32.rs",
    "chars": 10676,
    "preview": "//! Encoding/decoding for the 32-bit FLuv32 color format.\n//!\n//! This encoding is based on, but is slightly different t"
  },
  {
    "path": "sub_crates/compact/src/fluv/mod.rs",
    "chars": 159,
    "preview": "//! Fluv, a set of formats for compactly storing HDR XYZ colors.\n//!\n//! At the moment, only a 32-bit variant of the flu"
  },
  {
    "path": "sub_crates/compact/src/lib.rs",
    "chars": 249,
    "preview": "//! Functions for storing various kinds of data compactly, using domain\n//! knowledge of how that data is used.\n//!\n//! "
  },
  {
    "path": "sub_crates/compact/src/shared_exp/mod.rs",
    "chars": 992,
    "preview": "//! Shared-exponent float triplet formats.\n\npub mod signed48;\npub mod unsigned32;\npub mod unsigned40;\n\n//==============="
  },
  {
    "path": "sub_crates/compact/src/shared_exp/signed48.rs",
    "chars": 9488,
    "preview": "//! Encoding/decoding for signed 48-bit trifloat numbers.\n//!\n//! The encoding uses 13 bits of mantissa and 1 sign bit p"
  },
  {
    "path": "sub_crates/compact/src/shared_exp/unsigned32.rs",
    "chars": 5893,
    "preview": "//! Encoding/decoding for unsigned 32-bit trifloat numbers.\n//!\n//! The encoding uses 9 bits of mantissa per number, and"
  },
  {
    "path": "sub_crates/compact/src/shared_exp/unsigned40.rs",
    "chars": 6970,
    "preview": "//! Encoding/decoding for unsigned 40-bit trifloat numbers.\n//!\n//! The encoding uses 11 bits of mantissa per number, an"
  },
  {
    "path": "sub_crates/compact/src/unit_vec/mod.rs",
    "chars": 44,
    "preview": "//! 3d unit vector formats.\n\npub mod oct32;\n"
  },
  {
    "path": "sub_crates/compact/src/unit_vec/oct32.rs",
    "chars": 4634,
    "preview": "//! Encoding/decoding for a 32-bit representation of unit 3d vectors.\n//!\n//! Follows the Oct32 encoding specified in th"
  },
  {
    "path": "sub_crates/compact/tests/proptest_tests.rs",
    "chars": 2371,
    "preview": "#[macro_use]\nextern crate proptest;\n\nuse compact::unit_vec::oct32::{decode, encode, encode_precise};\nuse proptest::test_"
  },
  {
    "path": "sub_crates/halton/Cargo.toml",
    "chars": 188,
    "preview": "[package]\nname = \"halton\"\nversion = \"0.1.0\"\nauthors = [\"Nathan Vegdahl <cessen@cessen.com>\"]\nedition = \"2018\"\nlicense = "
  },
  {
    "path": "sub_crates/halton/LICENSE.md",
    "chars": 1174,
    "preview": "The code in this project is adapted from code written by Leonhard Gruenschloss:\n\nCopyright (c) 2012 Leonhard Gruenschlos"
  },
  {
    "path": "sub_crates/halton/build.rs",
    "chars": 8913,
    "preview": "// Copyright (c) 2012 Leonhard Gruenschloss (leonhard@gruenschloss.org)\n//\n// Permission is hereby granted, free of char"
  },
  {
    "path": "sub_crates/halton/src/lib.rs",
    "chars": 295,
    "preview": "#![allow(dead_code)]\n#![allow(unused_parens)]\n#![allow(clippy::cast_lossless)]\n#![allow(clippy::excessive_precision)]\n#!"
  },
  {
    "path": "sub_crates/math3d/Cargo.toml",
    "chars": 253,
    "preview": "[package]\nname = \"math3d\"\nversion = \"0.1.0\"\nauthors = [\"Nathan Vegdahl <cessen@cessen.com>\"]\nedition = \"2018\"\nlicense = "
  },
  {
    "path": "sub_crates/math3d/LICENSE.md",
    "chars": 268,
    "preview": "Copyright (c) 2020 Nathan Vegdahl\n\nThis project is licensed under either of\n\n* MIT license (licenses/MIT.txt or http://o"
  },
  {
    "path": "sub_crates/math3d/src/lib.rs",
    "chars": 522,
    "preview": "#![allow(dead_code)]\n\nmod normal;\nmod point;\nmod transform;\nmod vector;\n\npub use self::{normal::Normal, point::Point, tr"
  },
  {
    "path": "sub_crates/math3d/src/normal.rs",
    "chars": 5529,
    "preview": "#![allow(dead_code)]\n\nuse std::{\n    cmp::PartialEq,\n    ops::{Add, Div, Mul, Neg, Sub},\n};\n\nuse glam::Vec3A;\n\nuse super"
  },
  {
    "path": "sub_crates/math3d/src/point.rs",
    "chars": 4211,
    "preview": "#![allow(dead_code)]\n\nuse std::{\n    cmp::PartialEq,\n    ops::{Add, Mul, Sub},\n};\n\nuse glam::Vec3A;\n\nuse super::{Transfo"
  },
  {
    "path": "sub_crates/math3d/src/transform.rs",
    "chars": 4532,
    "preview": "#![allow(dead_code)]\n\nuse std::ops::{Add, Mul};\n\nuse approx::relative_eq;\nuse glam::{Affine3A, Mat3, Mat4, Vec3};\n\nuse s"
  },
  {
    "path": "sub_crates/math3d/src/vector.rs",
    "chars": 5868,
    "preview": "#![allow(dead_code)]\n\nuse std::{\n    cmp::PartialEq,\n    ops::{Add, Div, Mul, Neg, Sub},\n};\n\nuse glam::Vec3A;\n\nuse super"
  },
  {
    "path": "sub_crates/spectral_upsampling/Cargo.toml",
    "chars": 236,
    "preview": "[package]\nname = \"spectral_upsampling\"\nversion = \"0.1.0\"\nauthors = [\"Nathan Vegdahl <cessen@cessen.com>\"]\nedition = \"201"
  },
  {
    "path": "sub_crates/spectral_upsampling/LICENSE.md",
    "chars": 1045,
    "preview": "## Adapted code and data files\n\nThis crate includes code adapted from the supplemental material of the paper [\"Physicall"
  },
  {
    "path": "sub_crates/spectral_upsampling/build.rs",
    "chars": 7236,
    "preview": "// Get Jakob tables into a native rust format.\n\nuse std::{\n    env,\n    fs::File,\n    io::{self, Read, Write},\n    path:"
  },
  {
    "path": "sub_crates/spectral_upsampling/jakob_tables/LICENSE.txt",
    "chars": 1521,
    "preview": "Copyright (c) 2020 Wenzel Jakob <wenzel.jakob@epfl.ch>, All rights reserved.\n\nRedistribution and use in source and binar"
  },
  {
    "path": "sub_crates/spectral_upsampling/src/jakob.rs",
    "chars": 4985,
    "preview": "/// This file implements a lighter alternative version of the Jakob\n/// 2019 spectral upsampling method.  Instead of usi"
  },
  {
    "path": "sub_crates/spectral_upsampling/src/lib.rs",
    "chars": 254,
    "preview": "// Since this is basicallt translated from C, silence a bunch of\n// clippy warnings that stem from the C code.\n#![allow("
  },
  {
    "path": "sub_crates/spectral_upsampling/src/meng/generate_meng_spectra_tables.py",
    "chars": 39952,
    "preview": "#!/usr/bin/env python3\n\n# This file is originally from the supplemental material of the paper\n# \"Physically Meaningful R"
  },
  {
    "path": "sub_crates/spectral_upsampling/src/meng/meng_spectra_tables.rs",
    "chars": 224563,
    "preview": "// This file is auto-generated by generate_spectra_tables.py\n#![allow(dead_code)]\n#![cfg_attr(rustfmt, rustfmt_skip)]\n#!"
  },
  {
    "path": "sub_crates/spectral_upsampling/src/meng/xyz_5nm_360_830.csv",
    "chars": 4748,
    "preview": "360,0.000129900000,0.000003917000,0.000606100000\r\n365,0.000232100000,0.000006965000,0.001086000000\r\n370,0.000414900000,0"
  },
  {
    "path": "sub_crates/spectral_upsampling/src/meng/xyz_5nm_380_780.csv",
    "chars": 2511,
    "preview": "380,0.001368,0.000039,0.006450\n385,0.002236,0.000064,0.010550\n390,0.004243,0.000120,0.020050\n395,0.007650,0.000217,0.036"
  },
  {
    "path": "sub_crates/spectral_upsampling/src/meng.rs",
    "chars": 13091,
    "preview": "// Since this is basicallt translated from C, silence a bunch of\n// clippy warnings that stem from the C code.\n#![allow("
  }
]

// ... and 3 more files (download for full content)

About this extraction

This page contains the full source code of the cessen/psychopath GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 115 files (882.7 KB), approximately 303.8k tokens, and a symbol index with 1000 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!