Repository: cadop/dhart (language: python)

Issue:
Fix naming of python package



Discussion / hints:

Fix patch:
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 86373887d..57ff55171 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -7,8 +7,6 @@ test cases if any source file is changed.
 Documentation is vital and must be done throughout the project, not the end.<br>
 This includes examples for the use of each function.<br>
 
-*Non-unity users*
-
 <h1>Python</h1>
 * Sphinx
 * Numpydocs formatting
@@ -45,418 +43,5 @@ It should also be in the documentation.
 * Code general architecture should be discussed and well maintained.
 
 <h1>Doxygen Guidelines</h1>
-<b>Overview</b><br>
-* Methods and xml format to use in methods
-* Algorithms should be documented inline with source rather than headers
-* Interface details should remain with headers
-
-<b>Table of contents</b><br>
-- Using TODO
-- Documenting the heads of each file
-- Documenting member fields
-- Snippets and Code Samples
-- Adding images for headers/sources, and markdown files (<code>.md</code>)
-
-<h2>Using <code>TODO</code></h2>
-To have a TODO automatically pull to documentation use:<br>
-<code>/// TODO:</code>
-
-<h2>Documenting the heads of each file (headers and sources)</h2>
-
-<b>The head of each file should contain the following tags:</b><br>
-
-<code>\file</code><br>
-Name of a file<br>
-e.g. <b><code>/// \file     node.h</code></b><br>
-https://www.doxygen.nl/manual/commands.html#cmdfile
-
-<code>\brief</code><br>
-A one-line description describing the contents of the file<br>
-e.g. <b><code>/// \brief    Header file for a node data structure, used in a graph ADT</code></b><br>
-https://www.doxygen.nl/manual/commands.html#cmdbrief
-
-<code>\author</code><br>
-The author of the file<br>
-If there are multiple authors,<br>
-you may use one <code>\author</code> tag for all of the authors,<br>
-or one <code>\author</code> tag per person/entity.<br>
-e.g. <b><code>/// \author   John Doe</code></b><br>
-https://www.doxygen.nl/manual/commands.html#cmdauthor
-
-<code>\date</code><br>
-This may be the date of the file's last update,<br>
-or the date the file was created -- whichever is relevant.<br>
-e.g. <b><code>/// \date   06 Jun 2020</code></b><br>
-https://www.doxygen.nl/manual/commands.html#cmddate
-
-An example of a file head would look like this:
-
-<code>///
-/// \file       node.h
-/// \brief      Header file for a node data structure, used in a graph ADT
-///
-/// \author     John Doe
-/// \date       06 Jun 2020
-///</code>
-
-Other notes:
-- Follow each <code>///</code> with a <b>single</b> space, before using a tag.
-- Follow each tag with <b>two</b> tabs, then provide the description.
-
-<h2>Documenting member fields</h2>
-
-Here is a simple example:<br>
-<code>struct position {
-    float x, y, z;  ///< Cartesian coordinates x, y, and z
-    int id;         ///< Unique identifier
-};</code>
-
-The general format is <code>///< Description goes here</code>,<br>
-notice that a <b>single whitespace character</b> follows <code>///<</code><br>
-and the field descriptions are in alignment with each other, using tabs.<br>
-Sometimes, this is not always possible,<br>
-but do your best to keep the formatting <b>neat</b> and <b>consistent</b>.
-
-<h2>Snippets and code samples</h2>
-
-<h3>The <code>\snippet</code> tag should be used for long examples that are over 10 lines.</h3>
-https://www.doxygen.nl/manual/commands.html#cmdsnippet
-
-<b>Step 0:</b><br>
-In a header file, at the line where you want the snippet to appear:
-
-<code>/// \snippet PATH_TO_FILE\filename.cpp SnippetName</code>
-
-If you would like to include line numbers for the snippet, you may use the tag
-<code>\snippet{lineno}</code>
-This is best used for directly referencing source files from within this repo,
-since the line numbers that appear are the actual line numbers of the code used
-for the snippet.
-
-<b>Step 1:</b><br>
-In <code>PATH_TO_FILE\filename.cpp</code> (where the snippet sample is),
-
-<code>/// [SnippetName]
-std::cout << "My sample snippet" << std::endl;
-/// [SnippetName]
-</code>
-
-Notice that the code is not a comment.
-
-<b>Step 2:</b><br>
-The following steps utilize the Doxygen GUI Frontend.
-
-Load the Doxyfile from this repo into Doxygen, using the File menu.
-The Doxyfile is in <code>dhart\src\Doxyfile</code>
-
-<b>Step 3:</b><br>
-- Navigate to the Expert tab on the left hand side.
-- Under topics, click 'Input'.
-- On the right side, scroll down until you see EXAMPLE_PATH.
-- In the EXAMPLE_PATH section, click the folder button to reveal a file prompt.
-- Within the file prompt, select the file path where the snippet resides. (<code>PATH_TO_FILE\filename.cpp</code>)
-- Add any more directories/paths where your snippets will come from.
-
-Snippets will now appear when you navigate to the Run tab, and click 'Run Doxygen'.
-
-<h3>The <code>\code</code> tag should be used for code block examples under 10 lines.</h3>
-https://www.doxygen.nl/manual/commands.html#cmdcode
-
-Here is an example for using the <code>\code</code> tag:
-
-```
-/*!
-    \code
-        // Use the '//' comment style.
-        std::cout << "Here is a test statement." << std::endl;
-        
-        // You may indent your code sample as needed.
-        struct my_point {
-            double m_x;
-            double m_y;
-        };
-    \endcode
-*/
-```
-
-Follow these steps to ensure consistency within the codebase:
-0. Start an example block with <code>/*!</code>, then hit <b>ENTER</b> to go to the next line.
-1. Hit <b>TAB</b> once, then use the <code>\code</code> tag. Then, hit <b>ENTER</b>.
-2. Hit <b>TAB</b> twice, then begin your code example. You may indent as necessary for your example.
- 
-3. When your example is finished, hit <b>ENTER</b>, then <b>BACKSPACE</b> so that you are inline with the <code>\code</code> tag.
-4. Use the <code>\endcode</code> tag to end your example.<br>Then hit <b>ENTER</b>, then hit <b>BACKSPACE</b> as many times as necessary so that you are inline with <code>/*!</code>.
-
-5. Finally, end the example block with <code>*/</code>.
-
-- Note that any comments that occur within the sample must begin with <code>//</code>, followed by a whitespace.
-
-This procedure strikes the best compromise between:
-- Making sure that comments work within a code example
-- Not having to remove <code>//</code> or <code>///</code> per line of the example
-
-<br>This ensures ease of viewing<br>
-for anyone reading the source code -- <br>
-and also assists Doxygen with the<br>
-formatting process for the HTML export.
-
-You may now use <b>'Run Doxygen'</b> to export your documentation.<br>
-(There are no extra steps, unlike with the snippet section)
-
-<h2>Adding images for headers/sources, and markdown files (<code>.md</code>)</h2>
-
-For <b>headers/sources</b>,<br>
-we will be using the <code>\image</code> tag to add images.<br>
-https://www.doxygen.nl/manual/commands.html#cmdimage
-
-For <b>markdown files (<code>.md</code>)</b>,<br>
-we will be using the markdown syntax for images.<br>
-https://www.doxygen.nl/manual/markdown.html#md_images
-
-In <b>dhart</b>,<br>
-we have a subdirectory named <b>docs</b>.<br>
-(<code>dhart\docs</code>)<br>
-
-The Doxyfile will already be configured to have it's <code>IMAGE_PATH</code><br>
-configured such that its working directory for images begins at<br>
-<code>dhart\docs</code>.<br>
-When we proceed to later steps, and we are to insert the path of an image file,<br>
-assume that the working directory for images begins at<br>
-<code>dhart\docs.</code>
-
-For example, if a file is saved at:<br>
-<code>dhart\docs\C++\images\spatialstructures\node\test-image_node-h.png</code>,<br>
-you will specify the path:<br>
-<code>C++\images\spatialstructures\node\test-image_node-h.png</code>.
-
-Notice that the <b>dhart\docs</b> portion is omitted when specifying the image,<br>
-since our Doxyfile will already its <code>IMAGE_PATH</code> configured to begin at <b>dhart\docs</b>.
-
-<b>Step 0:</b><br>
-Begin by saving copy of <b>image_file</b>,<br>
-which is the filename of the desired image,<br>
-into the following directory:<br>
-
-```
-dhart\docs\language\img\enclosing_folder\module_name
-```
-<code><b>language</b></code>,<br>
-<code><b>enclosing_folder</b></code>,<br>
-and <code><b>module_name</b></code> are <b>placeholders</b>.<br>
-
-<code>images</code> will be a subdirectory within <code><b>language</b></code>.
-
-<b>Note:</b><br>
-In the <b>dhart\docs</b> directory,<br>
-there are <b>three</b> subdirectories:<br>
-for which the <code><b>language</b></code> placeholder applies:
-- C# Documentation
-- C++
-- Python Docs
-
-![DocsSubdirs](docs/contributing-guides/images/file_explorer_docs_subdirs.png)
-<b>The contributing-guides subdirectory is for the images in <i>this</i> file.</b>
-
-For <code><b>language</b></code>,<br>
-<b>be sure to choose the appropriate subdirectory</b><br>
-when saving your image file.
-
-Although this particular example deals with inserting an image<br>
-into documentation for a C++ header file,<br>
-the same steps will apply for C# or Python sources.<br>
-
-For instance, the full filepath of <code>node.h</code> is<br>
-<code><b>dhart\src\Cpp\spatialstructures\src\node.h</b></code>
-
-<code><b>language</b></code> is <b>C++</b><br>
-<code><b>enclosing_folder</b></code> is <b>spatialstructures</b><br>
-<code><b>module_name</b></code> is <b>node</b><br>
-
-So, if we wanted to insert an image for any documentation<br>
-for <code>node.h</code> or <code>node.cpp</code>,<br>
-the image would be saved into the following directory:<br>
-
-<code><b>dhart\docs\C++\images\spatialstructures\node</b></code>
-
-![NodePictureSample](docs/contributing-guides/images/file_explorer_docs_img_node-h.png)
-
-If directories <code><b>img</b></code>, <code><b>enclosing_folder</b></code>,<br>
-or <code><b>module_name</b></code><br>
-do not already exist, you may create them.<br>
-
-We can now proceed to editing the file where we want to insert the desired image.<br>
-This could be a header/source file (step 1a), or a markdown file (step 1b).<br>
-
-Scroll down to the instructions that apply to your use case.<br>
-
-<h3>Adding an image to a header/source file</h3>
-
-<b>Step 1a:</b><br>
-In the header/source file where you want the image to appear,<br>
-navigate to the line of code where you want the image to appear.<br>
-
-We will now utilize the <code>\image</code> tag.
-
-At the line where you want the image to appear,<br>
-enter the following:
-
-```
-/// \image html language\images\enclosing_folder\module_name\image_file
-```
-
-Remember this example is using placeholders;<br>
-be sure that <b>language</b>, <b>enclosing_folder</b>, <b>module_name</b>,<br>
-and <b>image_file</b> correspond to where your image is saved.<br>
-
-Also note that <b>images</b> is a subdirectory within your chosen <b>language</b> directory.<br>
-
-An example use of the <code>\image</code> tag would be:<br>
-
-<code>/// \image html C++\images\spatialstructures\node\test-image_node-h.png</code>
-
-![VSImageNode-h](docs/contributing-guides/images/vs_imagetag_node-h.png)
-<br>See line 42 - example use of the <code>\image</code> tag.
-
-We will now move on to the <b>Doxygen GUI frontend (Doxywizard)</b> application.
-
-<h3>Adding an image to a markdown file (<code>.md</code>)</h3>
-
-<b>Step 1b:</b><br>
-In the markdown file where you want the image to appear,<br>
-navigate to the line at which the image should be inserted.<br>
-
-We will now utilize the markdown syntax for images.<br>
-
-At the line where you want the image to appear,<br>
-enter the following:
-
-```
-![image_name](language\images\enclosing_folder\module_name\image_file)
-```
-Remember this example is using placeholders;<br>
-be sure that <b>language</b>, <b>enclosing_folder</b>, <b>module_name</b>,<br>
-and <b>image_file</b> correspond to where your image is saved.<br>
-
-An example use of the syntax would be:<br>
-
-```
-![TestImage](C++\images\spatialstructures\node\test.png)
-```
-<code><b>language</b></code> is <b>C++</b><br>
-<code><b>enclosing_folder</b></code> is <b>spatialstructures</b><br>
-<code><b>module_name</b></code> is <b>node</b><br>
-<code><b>image_file</b></code> is <b>test.png</b>
-
-![VSImageMDMainpage](docs/contributing-guides/images/vs_imagemd_mainpage.png)
-<b>This particular example is of mainpage.md, in dhart\src\Cpp</b>
-
-We will now move on to the <b>Doxygen GUI frontend (Doxywizard)</b> application.
-
-<b>Step 2:</b><br>
-Open Doxywizard.<br>
-![DoxywizardOpen](docs/contributing-guides/images/startmenu_doxygen.png)
-
-In <b>Doxygen GUI frontend (Doxywizard)</b> -- load the repository's Doxyfile.<br>
-
-Click <b>File > Open...</b><br>
-
-![DoxygenFileOpen](docs/contributing-guides/images/doxygen_fileopen.png)
-
-The 'Open configuration file' file explorer will appear.<br>
-Navigate to your local copy of the dhart repository,<br>
-then click the <b>Doxyfile</b> icon.<br>
-
-![DoxygenOpenDoxyfile0](docs/contributing-guides/images/doxygen_open_doxyfile_0.png)
-<b>Your path to the dhart repository will differ, depending where it resides locally on your machine.</b>
-
-Upon loading the Doxyfile, it will be preconfigured for <b>C++</b>.
-
-Doxygen's <b>working directory</b> will be the location of the <b>Doxyfile</b>,
-which is the root directory of the dhart repository.
-
-The <b>Source code directory</b> will be configured for <code>./src/Cpp</code><br>
-and the <b>Destination directory</b> will be configured for <code>./docs/C++</code>.<br>
-
-![DoxygenWizardCpp](docs/contributing-guides/images/doxygen_wizard_cpp.png)
-<b>This is how the 'Wizard' page for Doxywizard may look for a C++ documentation export.</b>
-
-![DoxygenWizardSource](docs/contributing-guides/images/doxygen_wizard_source.png)
-<b>Change the highlighted portion to Csharp for C# sources, or Python for Python sources.</b>
-
-![DoxygenWizardDest](docs/contributing-guides/images/doxygen_wizard_dest.png)
-<b>Change the highlighed portion to C# Documentation for C# exports, or Python for Python exports.</b>
-
-Suggestion:<br>
-Before exporting documentation and pushing it to the repository,<br>
-change the <b>Destination directory</b> to a test directory of your choice<br>
-(relative to your local copy of dhart on your machine)
-
-To do this,<br>
-create a directory on your Desktop, named <b>test_docs</b>.<br>
-Then, for <b>Destination directory</b>,<br>
-you will want to provide a <b>relative path</b> to <b>test_docs</b>,<br>
-from Doxywizard's working directory.<br>
-(this will differ, depending on where your copy of dhart resides on your machine)
-
-![DoxygenWizardDestTest](docs/contributing-guides/images/doxygen_wizard_dest_test.png)
-<b>For this example, the destination directory is set to move up two parent directories, so that it can reach Desktop/test_docs.</b>
-
-<b>Step 3:</b><br>
-The dhart Doxyfile will be preconfigured to search for images from<br>
-<code>dhart\docs</code>, via the <code>IMAGE_PATH</code> variable.<br>
-
-The current <code>IMAGE_PATH</code> value, <code>./docs</code> <b>should not be removed</b>,<br>
-but you may <b>add to it</b> by doing the following:
-
-Click the <b>Expert</b> tab.<br>
-
-![DoxygenExpertTab](docs/contributing-guides/images/doxygen_expert_tab.png)
-
-Underneath <b>Topics</b> (on the left hand side of the window),<br>
-select <b>Input</b>,<br>
-
-![DoxygenExpertTabTopicsInput](docs/contributing-guides/images/doxygen_expert_tab_topics_input.png)
-
-then on the right side,<br>
-scroll down until you see the text field for <b>IMAGE_PATH</b>.<br>
-
-![DoxygenExpertTabImagePath0](docs/contributing-guides/images/doxygen_expert_tab_image_path_0.png)
-
-Click the folder icon for <b>IMAGE_PATH</b>.<br>
-
-![DoxygenExpertTabImagePath1](docs/contributing-guides/images/doxygen_expert_tab_image_path_1.png)
-
-In the file explorer window that appears,<br>
-navigate to <b>a directory of your choice.</b>.<br>
-Then, click <b>Select Folder</b>.<br>
-
-![DoxygenExpertTabImagePath2](docs/contributing-guides/images/doxygen_expert_tab_image_path_2.png)
-<b>You can add to the IMAGE_PATH by selecting any directory of your choice.</b><br>
-<b>The result will be a filepath relative to Doxygen's working directory.</b>
-
-We have now configured another image path for Doxygen.<br>
-
-![DoxygenExpertTabImagePath3](docs/contributing-guides/images/doxygen_expert_tab_image_path_3.png)
-<b>When Doxygen inserts images into documentation, it will begin its search in the dhart\docs directory.</b><br>
-<b>If you have added another path to IMAGE_PATH (not shown here), it will also search this path.</b>
-
-<b>Step 4:</b><br>
-
-![DoxygenRun0](docs/contributing-guides/images/doxygen_run_0.png)
-
-If there is nothing else you would like to configure,<br>
-select the <b>Run</b> tab, then click <b>Run doxygen</b>.<br>
-
-Your documentation will now be generated.
-
-![DoxygenRun1](docs/contributing-guides/images/doxygen_run_1.png)
-
-Here are some examples for inserting an image into <code>node.h</code>,<br>
-and <code>mainpage.md</code>.
-
-![DoxygenPreviewNodeH](docs/contributing-guides/images/doxygen_preview_node-h.png)
-<b><code>\image</code> tag was used within <code>node.h</code>.</b>
-
-![DoxygenPreviewMainpage](docs/contributing-guides/images/doxygen_preview_mainpage.png)
-<b>Markdown syntax for images was used for <code>mainpage.md</code>.</b>
 
+For complete guidelines, refer to the online documentation pages. The C++ docs have the most complete instructions for building doxygen (which includes C++ and C#)
\ No newline at end of file
diff --git a/README.md b/README.md
index 0f6a06c65..762dac344 100644
--- a/README.md
+++ b/README.md
@@ -121,6 +121,8 @@ For Python you can use
 
 `pip install dhart`
 
+In the latest release you can additionally use `pip install dhart[dev]` to install packages needed for visualization of examples and documentation. 
+
 - However, if you would like to use Python with Rhino Grasshopper, you will need to follow the install instructions in the Python Docs. 
 
 For C# you can download from the Releases page. 
diff --git a/docs/Python Docs/README.md b/docs/Python Docs/README.md
index 7c5aedb73..1ba957736 100644
--- a/docs/Python Docs/README.md
+++ b/docs/Python Docs/README.md
@@ -14,6 +14,8 @@ Then cd to dhart\build\Python
 
 `pip install .`
 
+For extra requirements to build docs use `pip install .[dev]`.
+
 Then cd to dhart\docs\Python Docs
 
 `.\make clean`
diff --git a/docs/Python Docs/source/_theme/scipy/layout.html b/docs/Python Docs/source/_theme/scipy/layout.html
index 9d359d547..0689dd7b7 100644
--- a/docs/Python Docs/source/_theme/scipy/layout.html
+++ b/docs/Python Docs/source/_theme/scipy/layout.html
@@ -102,10 +102,11 @@
     <script type="text/javascript" src="{{ pathto('_static/js/copybutton.js', 1) }}"></script>
 {%- endmacro %}
 
+{# commented out the style keyword #}
 {%- macro css() %}
     <link rel="stylesheet" type="text/css" href="{{ pathto('_static/css/spc-bootstrap.css', 1) }}">
     <link rel="stylesheet" type="text/css" href="{{ pathto('_static/css/spc-extend.css', 1) }}">
-    <link rel="stylesheet" href="{{ pathto('_static/' + style, 1) }}" type="text/css" >
+    {#<link rel="stylesheet" href="{{ pathto('_static/' + style, 1) }}" type="text/css" >#}
     <link rel="stylesheet" href="{{ pathto('_static/pygments.css', 1) }}" type="text/css" >
     {%- for cssfile in css_files %}
     <link rel="stylesheet" href="{{ pathto(cssfile, 1) }}" type="text/css" >
diff --git a/docs/Python Docs/source/conf.py b/docs/Python Docs/source/conf.py
index 695f68684..cf5d6decc 100644
--- a/docs/Python Docs/source/conf.py
+++ b/docs/Python Docs/source/conf.py
@@ -114,7 +114,7 @@
 html_domain_indices = False
 html_file_suffix = '.html'
 
-html4_writer = True
+# html4_writer = True
 
 # Add any paths that contain custom static files (such as style sheets) here,
 # relative to this directory. They are copied after the builtin static files,
diff --git a/docs/Python Docs/source/contributing.rst b/docs/Python Docs/source/contributing.rst
index f6f9f0a5b..66d7f7bbf 100644
--- a/docs/Python Docs/source/contributing.rst
+++ b/docs/Python Docs/source/contributing.rst
@@ -5,6 +5,9 @@ Contributing and Testing
 ========================
 
 
+The process assumes the repo has been cloned and was built using visual studio (with cmake), then using the install. This generates a 
+build folder with the python package in it. The following instructions rely on this build folder (not the src folder).  
+
 Check dependencies
 ------------------
 
@@ -12,7 +15,9 @@ Currently tested on ``Python 3.8+``.
 
 For the package: ``numpy>=1.18``, ``scipy>=1.4.1``
 
-For building documentation: ``sphinx>=3.0.3``, ``scipy`` html_theme
+For building documentation: ``sphinx>=3.0.3`` (latest version uses ``sphinx>=7``), ``scipy`` html_theme, ``matplotlib``.
+
+All packages should be automatically installed if using ``pip install dhart[dev]`` (or ``pip install .[dev]`` if cloning the repo).
 
 
 Building Documentation
@@ -20,10 +25,12 @@ Building Documentation
 
 Documentation is built using sphinx with the scipy html_theme in the `docs` folder. 
 
+
 ``make clean``
 
 ``make html``
 
+For windows, you may have to use ``.\make.bat html``.
 
 Testing
 --------
diff --git a/docs/Python Docs/source/examples/graph/visualize.rst b/docs/Python Docs/source/examples/graph/visualize.rst
index 7f15549b2..26dde6660 100644
--- a/docs/Python Docs/source/examples/graph/visualize.rst
+++ b/docs/Python Docs/source/examples/graph/visualize.rst
@@ -3,6 +3,15 @@ Visualize the Graph
 ===================
 
 
+Overview
+--------
+
+This example uses a simple obj file included in example models. 
+For context of what the top-down view is based on, see the figure below.
+
+.. image:: ../imgs/energy_hill.png
+
+
 Basic Graph Visualization
 -------------------------
 
diff --git a/docs/Python Docs/source/examples/imgs/energy_hill.png b/docs/Python Docs/source/examples/imgs/energy_hill.png
new file mode 100644
index 000000000..190a9fdce
Binary files /dev/null and b/docs/Python Docs/source/examples/imgs/energy_hill.png differ
diff --git a/docs/Python Docs/source/examples/imgs/visibility_ex_rendered.png b/docs/Python Docs/source/examples/imgs/visibility_ex_rendered.png
new file mode 100644
index 000000000..538c918c3
Binary files /dev/null and b/docs/Python Docs/source/examples/imgs/visibility_ex_rendered.png differ
diff --git a/docs/Python Docs/source/examples/imgs/visibility_ex_set_rendered.png b/docs/Python Docs/source/examples/imgs/visibility_ex_set_rendered.png
new file mode 100644
index 000000000..ab157fc35
Binary files /dev/null and b/docs/Python Docs/source/examples/imgs/visibility_ex_set_rendered.png differ
diff --git a/docs/Python Docs/source/examples/path/limit.rst b/docs/Python Docs/source/examples/path/limit.rst
index f3abe33a0..3318c2735 100644
--- a/docs/Python Docs/source/examples/path/limit.rst
+++ b/docs/Python Docs/source/examples/path/limit.rst
@@ -2,6 +2,16 @@
 Limit a Path 
 ============
 
+
+Overview
+--------
+
+This example uses a simple obj file included in example models. 
+For context of what the top-down view is based on, see the figure below.
+
+.. image:: ../imgs/energy_hill.png
+
+
 In this example, we limit the path of the previous example by changes the graph significantly.
 
 .. currentmodule:: dhart
diff --git a/docs/Python Docs/source/examples/path/multi_cost.rst b/docs/Python Docs/source/examples/path/multi_cost.rst
index ad6a6072a..177645995 100644
--- a/docs/Python Docs/source/examples/path/multi_cost.rst
+++ b/docs/Python Docs/source/examples/path/multi_cost.rst
@@ -2,6 +2,16 @@
 Path Plan with Different Costs
 ==============================
 
+
+Overview
+--------
+
+This example uses a simple obj file included in example models. 
+For context of what the top-down view is based on, see the figure below.
+
+.. image:: ../imgs/energy_hill.png
+
+
 .. currentmodule:: dhart
 
 .. automodule:: dhart.Examples.PathPlanningCosts
diff --git a/docs/Python Docs/source/examples/path/visualize.rst b/docs/Python Docs/source/examples/path/visualize.rst
index 93fcc4007..6490f2ad7 100644
--- a/docs/Python Docs/source/examples/path/visualize.rst
+++ b/docs/Python Docs/source/examples/path/visualize.rst
@@ -2,6 +2,16 @@
 Visualize a Path
 ================
 
+
+Overview
+--------
+
+This example uses a simple obj file included in example models. 
+For context of what the top-down view is based on, see the figure below.
+
+.. image:: ../imgs/energy_hill.png
+
+    
 .. currentmodule:: dhart
 
 .. automodule:: dhart.Examples.PathPlanning
diff --git a/docs/Python Docs/source/examples/view/basic.rst b/docs/Python Docs/source/examples/view/basic.rst
index d834611e7..f14287346 100644
--- a/docs/Python Docs/source/examples/view/basic.rst
+++ b/docs/Python Docs/source/examples/view/basic.rst
@@ -2,6 +2,14 @@
 Visualize Spatial View
 ======================
 
+Overview
+--------
+
+This example uses a simple obj file included in example models. 
+For context of what the top-down view is based on, see the figure below.
+
+.. image:: ../imgs/visibility_ex_set_rendered.png
+
 .. currentmodule:: dhart
   
 .. automodule:: dhart.Examples.ViewAnalysisPlots
diff --git a/docs/Python Docs/source/examples/view/spherical.rst b/docs/Python Docs/source/examples/view/spherical.rst
new file mode 100644
index 000000000..dd7795379
--- /dev/null
+++ b/docs/Python Docs/source/examples/view/spherical.rst
@@ -0,0 +1,20 @@
+=============================
+Visualize Spatial View Points
+=============================
+
+Overview
+--------
+
+This example uses a simple obj file included in example models. 
+For context of what the points are showing in the example, the model itself looks like the image displayed below.
+
+.. image:: ../imgs/visibility_ex_rendered.png
+
+
+Example
+-------
+
+.. currentmodule:: dhart
+  
+.. automodule:: dhart.Examples.ViewAnalysis3D
+    :members:
\ No newline at end of file
diff --git a/docs/Python Docs/source/examples/view_analysis.rst b/docs/Python Docs/source/examples/view_analysis.rst
index 994f4a791..29b61a9c3 100644
--- a/docs/Python Docs/source/examples/view_analysis.rst
+++ b/docs/Python Docs/source/examples/view_analysis.rst
@@ -6,6 +6,7 @@ Spatial View Analysis
    :maxdepth: 2
 
    view/basic.rst
+   view/spherical.rst
 
 .. seealso::
     These examples use methods found in the following modules:
diff --git a/docs/Python Docs/source/examples/visibility/basic.rst b/docs/Python Docs/source/examples/visibility/basic.rst
index 2dd823af1..df0fd9fd3 100644
--- a/docs/Python Docs/source/examples/visibility/basic.rst
+++ b/docs/Python Docs/source/examples/visibility/basic.rst
@@ -2,6 +2,14 @@
 Visualize a Visbility Graph Connections
 =======================================
 
+Overview
+--------
+
+This example uses a simple obj file included in example models. 
+For context of what the points are showing in the example, the model itself looks like the image displayed below.
+
+.. image:: ../imgs/visibility_ex_rendered.png
+
 .. currentmodule:: dhart
   
 .. automodule:: dhart.Examples.VisibilityGraphPlots
diff --git a/docs/Python Docs/source/examples/visibility/groups.rst b/docs/Python Docs/source/examples/visibility/groups.rst
index 425c44863..66d309288 100644
--- a/docs/Python Docs/source/examples/visibility/groups.rst
+++ b/docs/Python Docs/source/examples/visibility/groups.rst
@@ -2,6 +2,14 @@
 Visibility Graph Grouping
 =========================
 
+Overview
+--------
+
+This example uses a simple obj file included in example models. 
+For context of what the points are showing in the example, the model itself looks like the image displayed below.
+
+.. image:: ../imgs/visibility_ex_rendered.png
+
 .. currentmodule:: dhart
   
 .. automodule:: dhart.Examples.VisibilityGraphGroups
diff --git a/docs/Python Docs/source/user_install.rst b/docs/Python Docs/source/user_install.rst
index 25e8580e5..75ce5aed8 100644
--- a/docs/Python Docs/source/user_install.rst
+++ b/docs/Python Docs/source/user_install.rst
@@ -73,6 +73,8 @@ you are in the correct location by typing ``dir`` to see the ``setup.py`` file.
 
 ``pip install .``
 
+If you are going to run examples and/or docs, use ``pip install .[dev]``.
+
 To do a basic check that the package installed in the correct location, navigate to a different directory. 
 For example:
 
diff --git a/src/Cpp/docs/img/contributing/doxygen_expert_tab.png b/src/Cpp/docs/img/contributing/doxygen_expert_tab.png
new file mode 100644
index 000000000..93d489056
Binary files /dev/null and b/src/Cpp/docs/img/contributing/doxygen_expert_tab.png differ
diff --git a/src/Cpp/docs/img/contributing/doxygen_expert_tab_image_path_0.png b/src/Cpp/docs/img/contributing/doxygen_expert_tab_image_path_0.png
new file mode 100644
index 000000000..3a873e6c3
Binary files /dev/null and b/src/Cpp/docs/img/contributing/doxygen_expert_tab_image_path_0.png differ
diff --git a/src/Cpp/docs/img/contributing/doxygen_expert_tab_image_path_1.png b/src/Cpp/docs/img/contributing/doxygen_expert_tab_image_path_1.png
new file mode 100644
index 000000000..0284f53c1
Binary files /dev/null and b/src/Cpp/docs/img/contributing/doxygen_expert_tab_image_path_1.png differ
diff --git a/src/Cpp/docs/img/contributing/doxygen_expert_tab_image_path_2.png b/src/Cpp/docs/img/contributing/doxygen_expert_tab_image_path_2.png
new file mode 100644
index 000000000..1e9c97281
Binary files /dev/null and b/src/Cpp/docs/img/contributing/doxygen_expert_tab_image_path_2.png differ
diff --git a/src/Cpp/docs/img/contributing/doxygen_expert_tab_image_path_3.png b/src/Cpp/docs/img/contributing/doxygen_expert_tab_image_path_3.png
new file mode 100644
index 000000000..edfa9389e
Binary files /dev/null and b/src/Cpp/docs/img/contributing/doxygen_expert_tab_image_path_3.png differ
diff --git a/src/Cpp/docs/img/contributing/doxygen_expert_tab_topics_input.png b/src/Cpp/docs/img/contributing/doxygen_expert_tab_topics_input.png
new file mode 100644
index 000000000..77f8212d9
Binary files /dev/null and b/src/Cpp/docs/img/contributing/doxygen_expert_tab_topics_input.png differ
diff --git a/src/Cpp/docs/img/contributing/doxygen_fileopen.png b/src/Cpp/docs/img/contributing/doxygen_fileopen.png
new file mode 100644
index 000000000..6715dd299
Binary files /dev/null and b/src/Cpp/docs/img/contributing/doxygen_fileopen.png differ
diff --git a/src/Cpp/docs/img/contributing/doxygen_open_doxyfile_0.png b/src/Cpp/docs/img/contributing/doxygen_open_doxyfile_0.png
new file mode 100644
index 000000000..735aae70b
Binary files /dev/null and b/src/Cpp/docs/img/contributing/doxygen_open_doxyfile_0.png differ
diff --git a/src/Cpp/docs/img/contributing/doxygen_preview_mainpage.png b/src/Cpp/docs/img/contributing/doxygen_preview_mainpage.png
new file mode 100644
index 000000000..28b50dfde
Binary files /dev/null and b/src/Cpp/docs/img/contributing/doxygen_preview_mainpage.png differ
diff --git a/src/Cpp/docs/img/contributing/doxygen_preview_node-h.png b/src/Cpp/docs/img/contributing/doxygen_preview_node-h.png
new file mode 100644
index 000000000..405b11257
Binary files /dev/null and b/src/Cpp/docs/img/contributing/doxygen_preview_node-h.png differ
diff --git a/src/Cpp/docs/img/contributing/doxygen_run_0.png b/src/Cpp/docs/img/contributing/doxygen_run_0.png
new file mode 100644
index 000000000..a32daf8a2
Binary files /dev/null and b/src/Cpp/docs/img/contributing/doxygen_run_0.png differ
diff --git a/src/Cpp/docs/img/contributing/doxygen_run_1.png b/src/Cpp/docs/img/contributing/doxygen_run_1.png
new file mode 100644
index 000000000..953bc939f
Binary files /dev/null and b/src/Cpp/docs/img/contributing/doxygen_run_1.png differ
diff --git a/src/Cpp/docs/img/contributing/doxygen_wizard_cpp.png b/src/Cpp/docs/img/contributing/doxygen_wizard_cpp.png
new file mode 100644
index 000000000..e0e65d257
Binary files /dev/null and b/src/Cpp/docs/img/contributing/doxygen_wizard_cpp.png differ
diff --git a/src/Cpp/docs/img/contributing/doxygen_wizard_dest.png b/src/Cpp/docs/img/contributing/doxygen_wizard_dest.png
new file mode 100644
index 000000000..28f32aa56
Binary files /dev/null and b/src/Cpp/docs/img/contributing/doxygen_wizard_dest.png differ
diff --git a/src/Cpp/docs/img/contributing/doxygen_wizard_source.png b/src/Cpp/docs/img/contributing/doxygen_wizard_source.png
new file mode 100644
index 000000000..20c7fc8be
Binary files /dev/null and b/src/Cpp/docs/img/contributing/doxygen_wizard_source.png differ
diff --git a/src/Cpp/docs/img/contributing/file_explorer_cpp_spatialstructures-node-h.png b/src/Cpp/docs/img/contributing/file_explorer_cpp_spatialstructures-node-h.png
new file mode 100644
index 000000000..8862183a9
Binary files /dev/null and b/src/Cpp/docs/img/contributing/file_explorer_cpp_spatialstructures-node-h.png differ
diff --git a/src/Cpp/docs/img/contributing/file_explorer_docs_img_node-h.png b/src/Cpp/docs/img/contributing/file_explorer_docs_img_node-h.png
new file mode 100644
index 000000000..4b5c9b75a
Binary files /dev/null and b/src/Cpp/docs/img/contributing/file_explorer_docs_img_node-h.png differ
diff --git a/src/Cpp/docs/img/contributing/file_explorer_docs_subdirs.png b/src/Cpp/docs/img/contributing/file_explorer_docs_subdirs.png
new file mode 100644
index 000000000..8b0e15bd9
Binary files /dev/null and b/src/Cpp/docs/img/contributing/file_explorer_docs_subdirs.png differ
diff --git a/src/Cpp/docs/img/contributing/startmenu_doxygen.png b/src/Cpp/docs/img/contributing/startmenu_doxygen.png
new file mode 100644
index 000000000..47e7e8689
Binary files /dev/null and b/src/Cpp/docs/img/contributing/startmenu_doxygen.png differ
diff --git a/src/Cpp/docs/img/contributing/vs_imagemd_mainpage.png b/src/Cpp/docs/img/contributing/vs_imagemd_mainpage.png
new file mode 100644
index 000000000..57d187e99
Binary files /dev/null and b/src/Cpp/docs/img/contributing/vs_imagemd_mainpage.png differ
diff --git a/src/Cpp/docs/img/contributing/vs_imagetag_node-h.png b/src/Cpp/docs/img/contributing/vs_imagetag_node-h.png
new file mode 100644
index 000000000..75d3a9d42
Binary files /dev/null and b/src/Cpp/docs/img/contributing/vs_imagetag_node-h.png differ
diff --git a/src/Cpp/raytracer/src/embree_raytracer.cpp b/src/Cpp/raytracer/src/embree_raytracer.cpp
index 96cd95914..fd15eab83 100644
--- a/src/Cpp/raytracer/src/embree_raytracer.cpp
+++ b/src/Cpp/raytracer/src/embree_raytracer.cpp
@@ -488,7 +488,7 @@ namespace HF::RayTracer {
 
 		// Allow users to shoot multiple rays with a single direction or origin
 		if (origins.size() > 1 && directions.size() > 1) {
-#pragma omp parallel for if(use_parallel) schedule(dynamic)
+#pragma omp parallel for if(use_parallel) schedule(dynamic, 128)
 			for (int i = 0; i < origins.size(); i++) {
 				auto& org = origins[i];
 				auto& dir = directions[i];
@@ -502,7 +502,7 @@ namespace HF::RayTracer {
 
 		else if (origins.size() > 1 && directions.size() == 1) {
 			const auto& dir = directions[0];
-#pragma omp parallel for if(use_parallel) schedule(dynamic)
+#pragma omp parallel for if(use_parallel) schedule(dynamic, 128)
 			for (int i = 0; i < origins.size(); i++) {
 				auto& org = origins[i];
 				out_results[i] = PointIntersection(
@@ -515,7 +515,7 @@ namespace HF::RayTracer {
 
 		else if (origins.size() == 1 && directions.size() > 1) {
 			out_results.resize(directions.size());
-#pragma omp parallel for if(use_parallel) schedule(dynamic)
+#pragma omp parallel for if(use_parallel) schedule(dynamic, 128)
 			for (int i = 0; i < directions.size(); i++) {
 				auto org = origins[0];
 				auto& dir = directions[i];
diff --git a/src/Csharp/packages/base/src/NativeUtils.cs b/src/Csharp/packages/base/src/NativeUtils.cs
index cf14673a0..4ce11d7a6 100644
--- a/src/Csharp/packages/base/src/NativeUtils.cs
+++ b/src/Csharp/packages/base/src/NativeUtils.cs
@@ -15,84 +15,84 @@
 
 namespace DHARTAPI.NativeUtils
 {
-	
-	/*! 
-		\brief Pointers to a C++ vector's data, the vector itself, and the size of the vector
-		
-		\details
-		Carries all data necessary to create an instance of NativeArray{T}.
-
-		\note
-		usage of the name "vector" here corresponds to the C++ Standard Library's `std::vector`
-		object, not the 3D vector described in Vector3D. 
-	*/
-	internal struct CVectorAndData
-	{
-		/*! \brief Pointer to vector's data in C++. This is what .array maps the span to.*/
-		public IntPtr data;
-
-		/*! \brief Pointer to the vector itself in unmanaged memory Used for deletion. */
-		public IntPtr vector;
-
-		/*! \brief The number of elements held in unmanaged memory */
-		public int size;
-
-		/*! \brief The size of the second dimension of this vector (if any). */
-		public int size2;
-
-		/*! 
-			\brief Construct a new instance of CVectorAndData.
-
-			\param in_data Pointer to the vector's data.
-			\param in_vector Pointer to the vector itself.
-			\param size The number of elements within this vector.
-			\param size2
-			The number of elements held in this vector's second dimension. If set to -1, the array
-			is considered 1 dimensional.
-		*/
-		public CVectorAndData(IntPtr in_data, IntPtr in_vector, int size = -1, int size2 = -1)
-		{
-			data = in_data;
-			vector = in_vector;
-			this.size = size;
-			this.size2 = size2;
-		}
-
-		/*!
-			\brief Determine whether or not this CVectorAndData points to a actual vector.
-		
-			\details
-			A vector is considered invalid if any of its pointers are null, or if its size is less than or equal to zero. 
-		
-			\returns 
-			true if size > 0 and both the data and vector pointers are not equal to IntPtr.Zero otherwise, false
-		*/
-		public bool IsValid() => (size > 0 && data != IntPtr.Zero && vector != IntPtr.Zero);
-	}
-
-	/*! 
+    
+    /*! 
+        \brief Pointers to a C++ vector's data, the vector itself, and the size of the vector
+        
+        \details
+        Carries all data necessary to create an instance of NativeArray{T}.
+
+        \note
+        usage of the name "vector" here corresponds to the C++ Standard Library's `std::vector`
+        object, not the 3D vector described in Vector3D. 
+    */
+    internal struct CVectorAndData
+    {
+        /*! \brief Pointer to vector's data in C++. This is what .array maps the span to.*/
+        public IntPtr data;
+
+        /*! \brief Pointer to the vector itself in unmanaged memory Used for deletion. */
+        public IntPtr vector;
+
+        /*! \brief The number of elements held in unmanaged memory */
+        public int size;
+
+        /*! \brief The size of the second dimension of this vector (if any). */
+        public int size2;
+
+        /*! 
+            \brief Construct a new instance of CVectorAndData.
+
+            \param in_data Pointer to the vector's data.
+            \param in_vector Pointer to the vector itself.
+            \param size The number of elements within this vector.
+            \param size2
+            The number of elements held in this vector's second dimension. If set to -1, the array
+            is considered 1 dimensional.
+        */
+        public CVectorAndData(IntPtr in_data, IntPtr in_vector, int size = -1, int size2 = -1)
+        {
+            data = in_data;
+            vector = in_vector;
+            this.size = size;
+            this.size2 = size2;
+        }
+
+        /*!
+            \brief Determine whether or not this CVectorAndData points to a actual vector.
+        
+            \details
+            A vector is considered invalid if any of its pointers are null, or if its size is less than or equal to zero. 
+        
+            \returns 
+            true if size > 0 and both the data and vector pointers are not equal to IntPtr.Zero otherwise, false
+        */
+        public bool IsValid() => (size > 0 && data != IntPtr.Zero && vector != IntPtr.Zero);
+    }
+
+    /*! 
         \brief Holds global information relevant to interop.
     */
-	internal static class NativeConstants
-	{
-		/*! \brief Relative path to the DHART_API DLL. 
+    internal static class NativeConstants
+    {
+        /*! \brief Relative path to the DHART_API DLL. 
             
             \remarks
             This is used in the DLL imports section of every function that calls into native code in this library.
         */
-		public const string DLLPath = "DHARTAPI.dll";
-	}
+        public const string DLLPath = "DHARTAPI.dll";
+    }
 
-	/*!
+    /*!
         \brief Contains some functions useful for converting data for interop.
         
         \remarks
         These functions are frequently called internally in multiple DHARTAPI
         namespaces. 
     */
-	public static class HelperFunctions
-	{
-		/*! 
+    public static class HelperFunctions
+    {
+        /*! 
             \brief Copy an array of structs from unmanaged memory into managed memory </summary>
             
             \tparam T The type of struct to copy.
@@ -103,28 +103,28 @@ public static class HelperFunctions
             \pre `length` is equal to the length of `unmanagedArray` in native memory
             \post `managedArray` is filled with copies of the elements from `unmanagedArray`
        */
-		internal static void MarshalUnmananagedArray2Struct<T>(IntPtr unmanagedArray, int length, out T[] mangagedArray) where T : struct
-		{
-			// Calculate the size T in bytes
-			var size = Marshal.SizeOf(typeof(T));
-
-			// Construct a new managed array of T that can fit all elements of UnmanagedArray
-			mangagedArray = new T[length];
-
-			// Iterate through every element in UnmanagedArray
-			for (int i = 0; i < length; i++)
-			{
-				// Calculate the position of the element in unnmanagedArray at index i
-				IntPtr ins = new IntPtr(unmanagedArray.ToInt64() + i * size);
-
-				// Marshal this pointer to a struct, then insert it into the same index in
-				// the managed array
-				mangagedArray[i] = (T)Marshal.PtrToStructure(ins, typeof(T));
-			}
-		}
-
-		/*! 
-            \brief Convert a  vector array into an array of floats. 
+        internal static void MarshalUnmananagedArray2Struct<T>(IntPtr unmanagedArray, int length, out T[] mangagedArray) where T : struct
+        {
+            // Calculate the size T in bytes
+            var size = Marshal.SizeOf(typeof(T));
+
+            // Construct a new managed array of T that can fit all elements of UnmanagedArray
+            mangagedArray = new T[length];
+
+            // Iterate through every element in UnmanagedArray
+            for (int i = 0; i < length; i++)
+            {
+                // Calculate the position of the element in unnmanagedArray at index i
+                IntPtr ins = new IntPtr(unmanagedArray.ToInt64() + i * size);
+
+                // Marshal this pointer to a struct, then insert it into the same index in
+                // the managed array
+                mangagedArray[i] = (T)Marshal.PtrToStructure(ins, typeof(T));
+            }
+        }
+
+        /*! 
+            \brief Convert a  vector array into an array of floats. Slower than passing a Vector3D array.
             
             \param vectors The array of vectors to convert
             
@@ -134,31 +134,57 @@ internal static void MarshalUnmananagedArray2Struct<T>(IntPtr unmanagedArray, in
                 \remarks Useful for preparing for Pinvoke
             \endinternal
         */
-		public static float[] FlattenVectorArray(IEnumerable<Vector3D> vectors)
-		{
-			// Create a new output array of ffloats 3x the size of vectors.
-			float[] out_array = new float[vectors.Count() * 3];
-			int i = 0;
-
-			// Iterate through every vector in the input vectors
-			foreach (var vector in vectors)
-			{
-				// Calculate the index of the first element in the
-				// output array
-				int os = i * 3;
-
-				// fill the next 3 elements with the x,y and z parameters of this vector
-				out_array[os] = vector.x;
-				out_array[os + 1] = vector.y;
-				out_array[os + 2] = vector.z;
-
-				// Increment loop variable
-				i++;
-			}
-
-			// Return result
-			return out_array;
-		}
+        public static float[] FlattenVectorArray(IEnumerable<Vector3D> vectors)
+        {
+            // Create a new output array of ffloats 3x the size of vectors.
+            float[] out_array = new float[vectors.Count() * 3];
+            int i = 0;
+
+            // Iterate through every vector in the input vectors
+            foreach (var vector in vectors)
+            {
+                // Calculate the index of the first element in the
+                // output array
+                int os = i * 3;
+
+                // fill the next 3 elements with the x,y and z parameters of this vector
+                out_array[os] = vector.x;
+                out_array[os + 1] = vector.y;
+                out_array[os + 2] = vector.z;
+
+                // Increment loop variable
+                i++;
+            }
+
+            // Return result
+            return out_array;
+        }
+
+        /*! 
+            \brief Convert a vector3d array into an array of floats. A faster overload to the enumerable method.
+            
+            \param vectors The array of vectors to convert
+            
+            \returns An array of floats 3x as big as the array of vectors.
+            
+            \internal
+                \remarks Useful for preparing for Pinvoke
+            \endinternal
+        */
+        public static float[] FlattenVectorArray(Vector3D[] vectors)
+        {
+            // Create a new output array of floats 3x the size of vectors.
+            float[] out_array = new float[vectors.Length * 3];
+
+            for (int i = 0, j = 0; i < vectors.Length; i++, j += 3)
+            {
+                out_array[j] = vectors[i].x;
+                out_array[j + 1] = vectors[i].y;
+                out_array[j + 2] = vectors[i].z;
+            }
+
+            return out_array;
+        }
 
         /*! 
             \brief Convert a flat array of floats into a vector of points where result_array is true
@@ -171,7 +197,7 @@ The array of floats. Every 3 values should correspond to a single Vector3D
            
             \returns An array of Vector3D where every 3 floats in float_array is a single vector3D.
             
-			\pre The length of `result_array` is equal to 1/3 the length of `float_array`
+            \pre The length of `result_array` is equal to 1/3 the length of `float_array`
 
             \remarks
             This function is used frequently internally to handle results of ray intersections.
@@ -180,35 +206,36 @@ This function is used frequently internally to handle results of ray intersectio
             The number of elements in result_array is not equal to the number of
             elements in float_array / 3
        */
-		public static Vector3D[] FloatArrayToVectorArray(float[] float_array, bool[] result_array)
-		{
-			// If our precondition isn't met, throw
-			int size = float_array.Length / 3;
-			if (size != result_array.Length)
-				throw new ArgumentException("Size of float_array was not equal to the size of the result array!");
-
-			Vector3D[] out_points = new Vector3D[size];
-
-			// Iterate through every result
-			for (int i = 0; i < size; i++)
-			{
-
-				// Calculate the index of this point in float_array
-				int os = i * 3;
-
-				// If the result array indicated an intersection here, insert a new vector
-				// at this point containing the x,y, and z coordinates of the new point
-				if (result_array[i])
-				{
-					float x = float_array[os];
-					float y = float_array[os + 1];
-					float z = float_array[os + 2];
-					out_points[i] = new Vector3D(x, y, z);
-				}
-				else // Return an invalid point to signify that no geometry was intersected
-					out_points[i] = new Vector3D(Single.NaN, Single.NaN, Single.NaN);
-			}
-			return out_points;
-		}
-	}
+
+        public static Vector3D[] FloatArrayToVectorArray(float[] float_array, bool[] result_array)
+        {
+            // If our precondition isn't met, throw
+            int size = float_array.Length / 3;
+            if (size != result_array.Length)
+                throw new ArgumentException("Size of float_array was not equal to the size of the result array!");
+
+            Vector3D[] out_points = new Vector3D[size];
+
+            // Iterate through every result
+            for (int i = 0; i < size; i++)
+            {
+
+                // Calculate the index of this point in float_array
+                int os = i * 3;
+
+                // If the result array indicated an intersection here, insert a new vector
+                // at this point containing the x,y, and z coordinates of the new point
+                if (result_array[i])
+                {
+                    float x = float_array[os];
+                    float y = float_array[os + 1];
+                    float z = float_array[os + 2];
+                    out_points[i] = new Vector3D(x, y, z);
+                }
+                else // Return an invalid point to signify that no geometry was intersected
+                    out_points[i] = new Vector3D(Single.NaN, Single.NaN, Single.NaN);
+            }
+            return out_points;
+        }
+    }
 }
\ No newline at end of file
diff --git a/src/Csharp/packages/raytracer/src/EmbreeRayTracer.cs b/src/Csharp/packages/raytracer/src/EmbreeRayTracer.cs
index 128b3343f..7612a8751 100644
--- a/src/Csharp/packages/raytracer/src/EmbreeRayTracer.cs
+++ b/src/Csharp/packages/raytracer/src/EmbreeRayTracer.cs
@@ -111,7 +111,7 @@ results of every ray.
 		public static Vector3D IntersectForPoint(EmbreeBVH bvh, Vector3D origin, Vector3D direction, float max_distance = -1)
 			=> IntersectForPoint(bvh, origin.x, origin.y, origin.z, direction.x, direction.y, direction.z, max_distance);
 
-		/*!
+        /*!
             \brief Cast multiple rays and recieve hitpoints in return.
 
             \param bvh  A valid BVH containing geometry to intersect with.
@@ -122,6 +122,10 @@ public static Vector3D IntersectForPoint(EmbreeBVH bvh, Vector3D origin, Vector3
             \returns
             An array of Vector3D for the hitpoint of each ray casted in order. If a ray didn't intersect any
             geometry, then its point will be invalid, checkable using Vector3D.IsValid()
+        
+            \details
+            There are three overloads on this function, one for passing a Vector3D array (instead of enumerable)
+            one for passing an IEnumerable, and one for passing a pre-flattened list of floats.
 
             \details
             Can be casted in 3 configurations: </para>
@@ -155,14 +159,28 @@ Length of directions and origins did not match any of the valid cases.
             ```
         */
 
-		public static Vector3D[] IntersectForPoints(
-			EmbreeBVH bvh,
-			IEnumerable<Vector3D> origins,
-			IEnumerable<Vector3D> directions,
-			float max_distance = -1
-		) => NativeMethods.C_IntersectPoints(bvh.Pointer, origins, directions, max_distance);
-
-		/*!
+        public static Vector3D[] IntersectForPoints(
+            EmbreeBVH bvh,
+            IEnumerable<Vector3D> origins,
+            IEnumerable<Vector3D> directions,
+            float max_distance = -1
+        ) => NativeMethods.C_IntersectPoints(bvh.Pointer, origins, directions, max_distance);
+
+        public static Vector3D[] IntersectForPoints(
+            EmbreeBVH bvh,
+            float[] origins,
+            float[] directions,
+            float max_distance = -1
+        ) => NativeMethods.C_IntersectPoints(bvh.Pointer, origins, directions, max_distance);
+
+        public static Vector3D[] IntersectForPoints(
+            EmbreeBVH bvh,
+            Vector3D[] origins,
+            Vector3D[] directions,
+            float max_distance = -1
+        ) => NativeMethods.C_IntersectPoints(bvh.Pointer, origins, directions, max_distance);
+
+        /*!
             \brief Cast a single ray and get the distance to its hit and the meshID if it hit anything
 
             \param bvh A valid BVH containing the geometry to intersect with.
@@ -170,6 +188,10 @@ public static Vector3D[] IntersectForPoints(
             \param direction x,y,z coordinates for the direction for the ray to be casted in.
             \param max_distance Maximum distance to consider for intersection. Set to -1 for infinite.
 
+            \details
+            There are two overloads on this function, one for passing a Vector3D 
+            and one for passing a pre-flattened list of floats.
+
             \remarks
             This can be faster than \link IntersectForPoints \endlink,  as the data returned is lighter. If you only need
             the distance or meshID, this function is recommended.
@@ -183,15 +205,21 @@ A RayResult containing the distance to the hitpoint and meshid it hit. If the
             \snippet raytracer\test_raytracer.cs EX_IntersectForDistance
             `[1,0]`
         */
-
-		public static RayResult IntersectForDistance(
-			EmbreeBVH bvh,
-			Vector3D origin,
-			Vector3D direction,
-			float max_distance = -1
-		) => NativeMethods.C_IntersectRay(bvh.Pointer, origin, direction, max_distance);
-
-		/*!
+        public static RayResult IntersectForDistance(
+            EmbreeBVH bvh,
+            Vector3D origin,
+            Vector3D direction,
+            float max_distance = -1
+        ) => NativeMethods.C_IntersectRay(bvh.Pointer, origin, direction, max_distance);
+
+        public static RayResult IntersectForDistance(
+            EmbreeBVH bvh,
+            float[] origin,
+            float[] direction,
+            float max_distance = -1
+        ) => NativeMethods.C_IntersectRay(bvh.Pointer, origin, direction, max_distance);
+
+        /*!
             \brief Cast multiple rays and recieve the distance and meshid of geometry intersected by each in return.
 
             \param bvh  A valid BVH containing geometry to intersect with.
@@ -234,15 +262,28 @@ Length of directions and origins did not match any of the valid cases.
 			Origin: (0, 0, 5), Result: [5,0]
             ```
         */
-
-		public static RayResults IntersectForDistances(
-			EmbreeBVH bvh,
-			IEnumerable<Vector3D> origins,
-			IEnumerable<Vector3D> directions,
-			float max_distance = -1
-		) => new RayResults(NativeMethods.C_IntersectRays(bvh.Pointer, origins, directions, max_distance));
-
-		/*!
+        public static RayResults IntersectForDistances(
+            EmbreeBVH bvh,
+            IEnumerable<Vector3D> origins,
+            IEnumerable<Vector3D> directions,
+            float max_distance = -1
+        ) => new RayResults(NativeMethods.C_IntersectRays(bvh.Pointer, origins, directions, max_distance));
+
+        public static RayResults IntersectForDistances(
+            EmbreeBVH bvh,
+            float[] origins,
+            float[] directions,
+            float max_distance = -1
+        ) => new RayResults(NativeMethods.C_IntersectRays(bvh.Pointer, origins, directions, max_distance));
+
+        public static RayResults IntersectForDistances(
+            EmbreeBVH bvh,
+            Vector3D[] origins,
+            Vector3D[] directions,
+            float max_distance = -1
+        ) => new RayResults(NativeMethods.C_IntersectRays(bvh.Pointer, origins, directions, max_distance));
+
+        /*!
             \brief Determine if any geometry occludes a point from a direction.
 
             \param bvh A valid Embree BVH.
@@ -253,6 +294,9 @@ public static RayResults IntersectForDistances(
             \returns An array of true or false values indicating hits or misses respectively.
 
             \details
+            There are three overloads on this function, one for passing a Vector3D array (instead of enumerable)
+             one for passing an IEnumerable, and one for passing a pre-flattened list of floats.
+
             Can be casted in 3 configurations:
             </para>
             <list type = "bullet" >
@@ -280,12 +324,25 @@ in each direction in directions
             \snippet raytracer\test_raytracer.cs EX_Occlusion
             `Ray 1: True, Ray 2 : False`
         */
-
-		public static bool[] IntersectOccluded(
-			EmbreeBVH bvh,
-			IEnumerable<Vector3D> origin,
-			IEnumerable<Vector3D> direction,
-			float max_distance = -1
-		) => NativeMethods.C_CastOcclusionRays(bvh.Pointer, origin, direction, max_distance);
-	}
+        public static bool[] IntersectOccluded(
+            EmbreeBVH bvh,
+            IEnumerable<Vector3D> origin,
+            IEnumerable<Vector3D> direction,
+            float max_distance = -1
+        ) => NativeMethods.C_CastOcclusionRays(bvh.Pointer, origin, direction, max_distance);
+
+        public static bool[] IntersectOccluded(
+            EmbreeBVH bvh,
+            float[] origin,
+            float[] direction,
+            float max_distance = -1
+        ) => NativeMethods.C_CastOcclusionRays(bvh.Pointer, origin, direction, max_distance);
+        
+        public static bool[] IntersectOccluded(
+            EmbreeBVH bvh,
+            Vector3D[] origin,
+            Vector3D[] direction,
+            float max_distance = -1
+        ) => NativeMethods.C_CastOcclusionRays(bvh.Pointer, origin, direction, max_distance);
+    }
 }
\ No newline at end of file
diff --git a/src/Csharp/packages/raytracer/src/RayTracerNative.cs b/src/Csharp/packages/raytracer/src/RayTracerNative.cs
index 4b2cf8cd2..41d9864d3 100644
--- a/src/Csharp/packages/raytracer/src/RayTracerNative.cs
+++ b/src/Csharp/packages/raytracer/src/RayTracerNative.cs
@@ -164,6 +164,7 @@ in each direction in directions
             \throws System.ArgumentException Arguments did not match one of the predefined cases!</exception>
             \throws System.Exception Multiple rays failed to cast 
         */
+
 		internal static Vector3D[] C_IntersectPoints(
 			IntPtr ert,
 			IEnumerable<Vector3D> origins,
@@ -175,45 +176,70 @@ float max_distance
 			float[] flat_origins = NativeUtils.HelperFunctions.FlattenVectorArray(origins);
 			float[] flat_dirs = NativeUtils.HelperFunctions.FlattenVectorArray(directions);
 
-			// Select C function to use based on number of origins/directions
-			int num_origins = origins.Count();
-			int num_directions = directions.Count();
-
-			// Declare result here, since it needs to be written to later
-			HF_STATUS res = HF_STATUS.GENERIC_ERROR;
-			Vector3D[] out_points;
-
-			// Depending on the configuration, this will call different functions in the 
-			// C-Interface. All accomplish the same thing: Call a function, interpret the results,
-			// capture the return code.
-			if (num_origins == num_directions)
-			{
-				bool[] result_array = new bool[num_origins];
-				res = CastMultipleRays(ert, flat_origins, flat_dirs, num_origins, max_distance, result_array);
-				out_points = HelperFunctions.FloatArrayToVectorArray(flat_origins, result_array);
-			}
-			else if (num_origins > num_directions && num_directions == 1)
-			{
-				bool[] result_array = new bool[num_origins];
-				res = CastMultipleOriginsOneDirection(ert, flat_origins, flat_dirs, num_origins, max_distance, result_array);
-				out_points = HelperFunctions.FloatArrayToVectorArray(flat_origins, result_array);
-			}
-			else if (num_directions > num_origins && num_origins == 1)
-			{
-				bool[] result_array = new bool[num_directions];
-				res = CastMultipleDirectionsOneOrigin(ert, flat_origins, flat_dirs, num_directions, max_distance, result_array);
-				out_points = HelperFunctions.FloatArrayToVectorArray(flat_dirs, result_array);
-			}
-			else // If it doesn't match any configuration, throw
-				throw new ArgumentException("Arguments did not match one of the predefined cases!");
-
-			// If this is ever not HF_STATUS.OK, then something changed in the C-Interface that wasn't reflected here.
-			if (res != HF_STATUS.OK) Debug.Assert(false);
-
-			return out_points;
+			return C_IntersectPoints(ert, flat_origins, flat_dirs, max_distance);
 		}
 
-		/*!
+		internal static Vector3D[] C_IntersectPoints(
+			IntPtr ert,
+			Vector3D[] origins,
+			Vector3D[] directions,
+			float max_distance
+		)
+		{
+			// Convert directions and origins to arrays. 
+			float[] flat_origins = NativeUtils.HelperFunctions.FlattenVectorArray(origins);
+			float[] flat_dirs = NativeUtils.HelperFunctions.FlattenVectorArray(directions);
+
+			return C_IntersectPoints(ert, flat_origins, flat_dirs, max_distance);
+		}
+
+		internal static Vector3D[] C_IntersectPoints(
+			IntPtr ert,
+			float[] origins,
+            float[] directions,
+			float max_distance
+)
+        {
+            // Get the size of both arrays
+            int num_origins = origins.Length / 3;
+            int num_directions = directions.Length / 3;
+
+            // Declare result here, since it needs to be written to later
+            HF_STATUS res = HF_STATUS.GENERIC_ERROR;
+            Vector3D[] out_points;
+
+            // Depending on the configuration, this will call different functions in the 
+            // C-Interface. All accomplish the same thing: Call a function, interpret the results,
+            // capture the return code.
+            if (num_origins == num_directions)
+            {
+                bool[] result_array = new bool[num_origins];
+                res = CastMultipleRays(ert, origins, directions, num_origins, max_distance, result_array);
+                out_points = HelperFunctions.FloatArrayToVectorArray(origins, result_array);
+            }
+            else if (num_origins > num_directions && num_directions == 1)
+            {
+                bool[] result_array = new bool[num_origins];
+                res = CastMultipleOriginsOneDirection(ert, origins, directions, num_origins, max_distance, result_array);
+                out_points = HelperFunctions.FloatArrayToVectorArray(origins, result_array);
+            }
+            else if (num_directions > num_origins && num_origins == 1)
+            {
+                bool[] result_array = new bool[num_directions];
+                res = CastMultipleDirectionsOneOrigin(ert, origins, directions, num_directions, max_distance, result_array);
+                out_points = HelperFunctions.FloatArrayToVectorArray(directions, result_array);
+            }
+            else // If it doesn't match any configuration, throw
+                throw new ArgumentException("Arguments did not match one of the predefined cases!");
+
+            // If this is ever not HF_STATUS.OK, then something changed in the C-Interface that wasn't reflected here.
+            if (res != HF_STATUS.OK) Debug.Assert(false);
+
+            return out_points;
+        }
+
+
+        /*!
             \brief Cast multiple rays in C++ then recieve the distance and meshid of geometry intersected
                    by each in return.
 
@@ -246,26 +272,53 @@ in each direction in directions
             \throws System.ArgumentException
             Length of directions and origins did not match any of the valid cases.
         */
-		internal static CVectorAndData C_IntersectRays(
+        internal static CVectorAndData C_IntersectRays(
 			IntPtr ray_tracer,
 			IEnumerable<Vector3D> origins,
 			IEnumerable<Vector3D> directions,
 			float max_distance
 		)
 		{
+			// convert data and then use float method
 			var flat_origins = HelperFunctions.FlattenVectorArray(origins);
 			var flat_directions = HelperFunctions.FlattenVectorArray(directions);
-			int num_origins = origins.Count();
-			int num_directions = directions.Count();
+
+			return C_IntersectRays(ray_tracer, flat_origins, flat_directions, max_distance);
+		}
+
+		internal static CVectorAndData C_IntersectRays(
+			IntPtr ray_tracer,
+			Vector3D[] origins,
+			Vector3D[] directions,
+			float max_distance
+		)
+		{
+			// convert data and then use float method
+			var flat_origins = HelperFunctions.FlattenVectorArray(origins);
+			var flat_directions = HelperFunctions.FlattenVectorArray(directions);
+
+			return C_IntersectRays(ray_tracer, flat_origins, flat_directions, max_distance);
+		}
+
+		internal static CVectorAndData C_IntersectRays(
+			IntPtr ray_tracer,
+			float[] origins,
+			float[] directions,
+			float max_distance
+		)
+		{
+			// Get the size of both arrays
+			int num_origins = origins.Length / 3;
+			int num_directions = directions.Length / 3;
 
 			IntPtr vector_ptr = new IntPtr();
 			IntPtr data_ptr = new IntPtr();
 
 			HF_STATUS res = CastRaysDistance(
 				ray_tracer,
-				flat_origins,
+				origins,
 				num_origins,
-				flat_directions,
+				directions,
 				num_directions,
 				ref vector_ptr,
 				ref data_ptr
@@ -274,7 +327,7 @@ ref data_ptr
 			if (res != HF_STATUS.OK)
 				throw new Exception("RAYS FOR DISTANCE FAIL");
 
-			return new CVectorAndData(data_ptr, vector_ptr, origins.Count());
+			return new CVectorAndData(data_ptr, vector_ptr, num_origins);
 		}
 
 		/*!
@@ -312,11 +365,40 @@ internal static bool[] C_CastOcclusionRays(
 			IEnumerable<Vector3D> directions,
 			float max_distance
 		)
+		{
+			// Convert data and use the float method
+			float[] origin_array = HelperFunctions.FlattenVectorArray(origins);
+			float[] direction_array = HelperFunctions.FlattenVectorArray(directions);
+
+			return C_CastOcclusionRays(rt_ptr, origin_array, direction_array, max_distance);
+			
+		}
+
+		internal static bool[] C_CastOcclusionRays(
+			IntPtr rt_ptr,
+			Vector3D[] origins,
+			Vector3D[] directions,
+			float max_distance
+		)
+		{
+			// Convert data and use the float method
+			float[] origin_array = HelperFunctions.FlattenVectorArray(origins);
+			float[] direction_array = HelperFunctions.FlattenVectorArray(directions);
+
+			return C_CastOcclusionRays(rt_ptr, origin_array, direction_array, max_distance);
+
+		}
+		internal static bool[] C_CastOcclusionRays(
+			IntPtr rt_ptr,
+			float[] origins,
+			float[] directions,
+			float max_distance
+		)
 		{
 
 			// Get the size of both arrays
-			int num_origins = origins.Count();
-			int num_directions = directions.Count();
+			int num_origins = origins.Length / 3;
+			int num_directions = directions.Length / 3;
 
 			// The number of results will be equal to the length
 			// of the lonest input
@@ -324,14 +406,12 @@ float max_distance
 
 			// Create output array and convert origin/directions to arrays
 			bool[] result_array = new bool[result_size];
-			float[] origin_array = HelperFunctions.FlattenVectorArray(origins);
-			float[] direction_array = HelperFunctions.FlattenVectorArray(directions);
 
 			// Call to C++ and get results. This will update the result array. 
 			HF_STATUS res = CastOcclusionRays(
 				rt_ptr,
-				origin_array,
-				direction_array,
+				origins,
+				directions,
 				num_origins,
 				num_directions,
 				max_distance,
@@ -366,16 +446,28 @@ float max_distance
 			float[] origin_arr = new float[3] { origin.x, origin.y, origin.z };
 			float[] direction_arr = new float[3] { direction.x, direction.y, direction.z };
 
-			// Setup output parameters
-			int out_meshid = 0;
-			float out_distance = 0.0f;
+			return C_IntersectRay(rt_ptr, origin_arr, direction_arr, max_distance);
+		}
+
+		internal static RayResult C_IntersectRay(
+            IntPtr rt_ptr,
+            float[] origin,
+            float[] direction,
+            float max_distance
+        )
+        {
+            // Setup output parameters
+            int out_meshid = 0;
+            float out_distance = 0.0f;
+
+            // Cast the ray in C++. This will update out_distance, and out_meshid
+            CastSingleRayDistance(rt_ptr, origin, direction, max_distance, ref out_distance, ref out_meshid);
+
+            // Return the results
+            return new RayResult(out_distance, out_meshid);
+        }
 
-			// Cast the ray in C++. This will update out_distance, and out_meshid
-			CastSingleRayDistance(rt_ptr, origin_arr, direction_arr, max_distance, ref out_distance, ref out_meshid);
 
-			// Return the results
-			return new RayResult(out_distance, out_meshid);
-		}
 
 		/*! \brief Free the memory allocated by a raytracer in C++.
             \param rt_ptr A pointer to an EmbreeRayTracer in C++
diff --git a/src/Python/dhart/Example Models/VisibilityTestCase.obj b/src/Python/dhart/Example Models/VisibilityTestCase.obj
new file mode 100644
index 000000000..3775f7def
--- /dev/null
+++ b/src/Python/dhart/Example Models/VisibilityTestCase.obj
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:bfc401a9f63ffe1e2ddd2e47e04bc2a42834374fa6a405fa8750283b88d774e0
+size 3239
diff --git a/src/Python/dhart/Examples/GraphGeneratorPlot.py b/src/Python/dhart/Examples/GraphGeneratorPlot.py
index 33c6d3144..f51fd4075 100644
--- a/src/Python/dhart/Examples/GraphGeneratorPlot.py
+++ b/src/Python/dhart/Examples/GraphGeneratorPlot.py
@@ -5,7 +5,6 @@
 
     
     import matplotlib.pyplot as plt
-    from mpl_toolkits.mplot3d import Axes3D
 
     from dhart.geometry import LoadOBJ
     from dhart.raytracer import EmbreeBVH
@@ -54,7 +53,7 @@
 
     # Plot the graph in 3D
     fig = plt.figure()
-    ax = Axes3D(fig)
+    ax = fig.add_subplot(projection='3d')
     ax.view_init(azim=-123, elev=15)
 
     ax.scatter(x, y, z)
@@ -72,7 +71,6 @@
 """
 
 import matplotlib.pyplot as plt
-from mpl_toolkits.mplot3d import Axes3D
 
 from dhart.geometry import LoadOBJ
 from dhart.raytracer import EmbreeBVH
@@ -116,7 +114,7 @@
 
 # Plot the graph in 3D
 fig = plt.figure()
-ax = Axes3D(fig)
+ax = fig.add_subplot(projection='3d')
 ax.view_init(azim=-123, elev=15)
 
 ax.scatter(x, y, z)
diff --git a/src/Python/dhart/Examples/ViewAnalysis3D.py b/src/Python/dhart/Examples/ViewAnalysis3D.py
new file mode 100644
index 000000000..1c697d4ac
--- /dev/null
+++ b/src/Python/dhart/Examples/ViewAnalysis3D.py
@@ -0,0 +1,203 @@
+"""
+
+
+.. plot::
+    :context: reset
+
+    
+    import matplotlib.pyplot as plt
+    import numpy as np
+
+    from dhart.geometry import LoadOBJ
+    from dhart.raytracer import EmbreeBVH
+    from dhart.viewanalysis import SphericalViewAnalysis, SphericallyDistributeRays
+    import dhart as hfpy
+
+    # Get a sample model path
+    obj_path = hfpy.get_sample_model("VisibilityTestCase.obj")
+
+    # Load the obj file
+    obj = LoadOBJ(obj_path)
+
+    # Create a BVH
+    bvh = EmbreeBVH(obj)
+
+    ray_count = 10000 # Set the number of rays to use per node
+
+    # Set the up and down field of view limits
+    upfov, downfov = 40, 40
+
+    # Set a height offset to cast rays from the points
+    height = 5 
+    query_point = [(25,25,0)]
+
+    
+    # Get distances from the desired point to the model (bvh)
+    hit_points = SphericalViewAnalysis(bvh, query_point, ray_count, height, upward_fov=upfov, downward_fov=downfov)
+
+    
+    # Get the directions that were used for the view analysis function
+    hit_dirs = SphericallyDistributeRays(ray_count, upward_fov=upfov, downward_fov=downfov)
+
+    # Get index where hitpoints are not -1
+    hit_idx = np.where(hit_points['distance'] != -1)
+    #pull out just the distances and the directions
+    hit_dirs_valid = hit_dirs[hit_idx]
+    hit_points = hit_points['distance'][hit_idx]
+
+    # reshape to get the correct axis 
+    hit_pos = hit_dirs_valid * hit_points.reshape(-1,1)
+
+    
+.. plot::
+    :context: close-figs
+
+    
+    # Plot the graph in 3D
+    fig = plt.figure()
+    ax = fig.add_subplot(projection='3d')
+    ax.view_init(azim=-140, elev=30)
+    # Set limits
+    ax.axes.set_xlim3d(left=-15, right=10)
+    ax.axes.set_ylim3d(bottom=-15, top=10) 
+    ax.axes.set_zlim3d(bottom=-0, top=25) 
+
+    # Pull out hitpoints
+    x,y,z = hit_pos[:,0], hit_pos[:,1], hit_pos[:,2]
+    # Assign points
+    ax.scatter(x,y,z, s = 0.5)
+
+    # Draw figure
+    plt.show()
+
+    #Repeat with a smaller fov
+
+    upfov, downfov = 15, 15
+    hit_points = SphericalViewAnalysis(bvh, query_point, ray_count, height, upward_fov=upfov, downward_fov=downfov)
+    hit_dirs = SphericallyDistributeRays(ray_count, upward_fov=upfov, downward_fov=downfov)
+
+    # Get index where hitpoints are not -1
+    hit_idx = np.where(hit_points['distance'] != -1)
+    #pull out just the distances and the directions
+    hit_points = hit_points['distance'][hit_idx]
+    hit_dirs_valid = hit_dirs[hit_idx]
+
+    # reshape to get the correct axis 
+    hit_pos = hit_dirs_valid * hit_points.reshape(-1,1)
+
+    
+.. plot::
+    :context: close-figs
+
+    
+    # Plot the graph in 3D
+    fig = plt.figure()
+    ax = fig.add_subplot(projection='3d')
+    ax.view_init(azim=-140, elev=30)
+    # Set limits
+    ax.axes.set_xlim3d(left=-15, right=10)
+    ax.axes.set_ylim3d(bottom=-15, top=10) 
+    ax.axes.set_zlim3d(bottom=-0, top=25) 
+
+    # Pull out hitpoints
+    x,y,z = hit_pos[:,0], hit_pos[:,1], hit_pos[:,2]
+    # Assign points
+    ax.scatter(x,y,z, s = 0.5)
+
+    # Draw figure
+    plt.show()
+
+"""
+
+
+
+import matplotlib.pyplot as plt
+import numpy as np
+
+from dhart.geometry import LoadOBJ
+from dhart.raytracer import EmbreeBVH
+from dhart.viewanalysis import SphericalViewAnalysis, SphericallyDistributeRays
+import dhart as hfpy
+
+# Get a sample model path
+obj_path = hfpy.get_sample_model("VisibilityTestCase.obj")
+
+# Load the obj file
+obj = LoadOBJ(obj_path)
+
+# Create a BVH
+bvh = EmbreeBVH(obj)
+
+ray_count = 10000 # Set the number of rays to use per node
+
+# Set the up and down field of view limits
+upfov, downfov = 40, 40
+
+# Set a height offset to cast rays from the points
+height = 5 
+query_point = [(25,25,0)]
+
+# Get distances from the desired point to the model (bvh)
+hit_points = SphericalViewAnalysis(bvh, query_point, ray_count, height, upward_fov=upfov, downward_fov=downfov)
+# Get the directions that were used for the view analysis function
+hit_dirs = SphericallyDistributeRays(ray_count, upward_fov=upfov, downward_fov=downfov)
+
+# Get index where hitpoints are not -1
+hit_idx = np.where(hit_points['distance'] != -1)
+#pull out just the distances and the directions
+hit_dirs_valid = hit_dirs[hit_idx]
+hit_points = hit_points['distance'][hit_idx]
+
+# reshape to get the correct axis 
+hit_pos = hit_dirs_valid * hit_points.reshape(-1,1)
+
+# Plot the graph in 3D
+fig = plt.figure()
+ax = fig.add_subplot(projection='3d')
+ax.view_init(azim=-140, elev=30)
+# Set limits
+ax.axes.set_xlim3d(left=-15, right=10)
+ax.axes.set_ylim3d(bottom=-15, top=10) 
+ax.axes.set_zlim3d(bottom=-0, top=25) 
+
+# Pull out hitpoints
+x,y,z = hit_pos[:,0], hit_pos[:,1], hit_pos[:,2]
+# Assign points
+ax.scatter(x,y,z, s = 0.5)
+
+# Draw figure
+plt.show()
+plt.clf()
+
+
+# Repeat with a smaller fov
+upfov, downfov = 15, 15
+hit_points = SphericalViewAnalysis(bvh, query_point, ray_count, height, upward_fov=upfov, downward_fov=downfov)
+hit_dirs = SphericallyDistributeRays(ray_count, upward_fov=upfov, downward_fov=downfov)
+
+# Get index where hitpoints are not -1
+hit_idx = np.where(hit_points['distance'] != -1)
+#pull out just the distances and the directions
+hit_points = hit_points['distance'][hit_idx]
+hit_dirs_valid = hit_dirs[hit_idx]
+
+# reshape to get the correct axis 
+hit_pos = hit_dirs_valid * hit_points.reshape(-1,1)
+
+# Plot the graph in 3D
+fig = plt.figure()
+ax = fig.add_subplot(projection='3d')
+ax.view_init(azim=-140, elev=30)
+# Set limits
+ax.axes.set_xlim3d(left=-15, right=10)
+ax.axes.set_ylim3d(bottom=-15, top=10) 
+ax.axes.set_zlim3d(bottom=-0, top=25) 
+
+# Pull out hitpoints
+x,y,z = hit_pos[:,0], hit_pos[:,1], hit_pos[:,2]
+# Assign points
+ax.scatter(x,y,z, s = 0.5)
+
+# Draw figure
+plt.show()
+plt.clf()
diff --git a/src/Python/setup.py b/src/Python/setup.py
index fa7087190..b6d803d8a 100644
--- a/src/Python/setup.py
+++ b/src/Python/setup.py
@@ -28,8 +28,8 @@
       install_requires=[
             'numpy',
             'scipy',
-            'matplotlib',
       ],
+      extras_require={'dev': ['sphinx', 'numpydoc','matplotlib','pytest']},
       zip_safe=True,
       include_package_data=True
 )


Test patch:
diff --git a/src/Cpp/docs/img/contributing/doxygen_wizard_dest_test.png b/src/Cpp/docs/img/contributing/doxygen_wizard_dest_test.png
new file mode 100644
index 000000000..d298af7f6
Binary files /dev/null and b/src/Cpp/docs/img/contributing/doxygen_wizard_dest_test.png differ
diff --git a/src/Csharp/packages/base/test_base.cs b/src/Csharp/packages/base/test_base.cs
index 814d4b943..685ed8561 100644
--- a/src/Csharp/packages/base/test_base.cs
+++ b/src/Csharp/packages/base/test_base.cs
@@ -1,4 +1,12 @@
 using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+using System.Diagnostics;
+using System;
+using DHARTAPI.NativeUtils;
+using System.Collections.Generic;
+using System.Linq;
+using DHARTAPI.Geometry;
+
 namespace DHARTAPI.Tests.Base
 {
     [TestClass]
@@ -11,4 +19,87 @@ public void TestTrue()
         }
     
        }
+
+    [TestClass]
+    public class OcclusionPerformance
+        /// Compare calling intersectOccluded with a float array (which then has its own
+        /// overload for C_CastOcclusionRays) vs. using IntersectOccluded with a Vector3D array
+    {
+        private const string plane_path = "ExampleModels/plane.obj";
+
+        [TestMethod]
+        public void VectorThreeD()
+        {
+            var stopwatch = new Stopwatch();
+
+            MeshInfo Mesh = OBJLoader.LoadOBJ(plane_path, CommonRotations.Yup_To_Zup);
+            DHARTAPI.RayTracing.EmbreeBVH BVH = new DHARTAPI.RayTracing.EmbreeBVH(Mesh);
+
+            // Size of test
+            const int arraysize = 1000000;
+
+            Vector3D[] origin_vector = new Vector3D[arraysize];
+            Vector3D[] direction_vector = new Vector3D[1];
+
+            direction_vector[0] = new Vector3D(0, 0, -1);
+
+            for (int i = 0; i < arraysize; i++)
+            {
+                origin_vector[i] = new Vector3D(0, 0, 1);
+            }
+
+            // Start the stopwatch test
+            stopwatch.Start();
+
+            bool[] results = DHARTAPI.RayTracing.EmbreeRaytracer.IntersectOccluded(BVH, origin_vector, direction_vector, -1);
+
+            // End the stopwatch
+            stopwatch.Stop();
+
+            foreach (bool result in results)
+                Assert.IsTrue(result);
+
+            Console.WriteLine(" Passing Vector3D array took {0} milliseconds", stopwatch.Elapsed.TotalMilliseconds);
+        }
+
+        [TestMethod]
+        public void Flat()
+        {
+            var stopwatch = new Stopwatch();
+
+            MeshInfo Mesh = OBJLoader.LoadOBJ(plane_path, CommonRotations.Yup_To_Zup);
+            DHARTAPI.RayTracing.EmbreeBVH BVH = new DHARTAPI.RayTracing.EmbreeBVH(Mesh);
+
+            // Size of test
+            const int arraysize = 1000000;
+
+            float[] origin_vector = new float[arraysize * 3];
+            float[] direction_vector = new float[3];
+
+            direction_vector[0] = 0.0f;
+            direction_vector[1] = 0.0f;
+            direction_vector[2] = -1.0f;
+
+            for (int i = 0; i < arraysize; i++)
+            {
+                int stride = i * 3;
+                origin_vector[stride] = 0f;
+                origin_vector[stride + 1] = 0f;
+                origin_vector[stride + 2] = 1f;
+            }
+
+            // Start the stopwatch test
+            stopwatch.Start();
+
+            bool[] results = DHARTAPI.RayTracing.EmbreeRaytracer.IntersectOccluded(BVH, origin_vector, direction_vector, -1);
+
+            // End the stopwatch
+            stopwatch.Stop();
+
+            foreach (bool result in results)
+                Assert.IsTrue(result);
+
+            Console.WriteLine(" Passing already Flat array took {0} milliseconds", stopwatch.Elapsed.TotalMilliseconds);
+        }
+    }
 }
\ No newline at end of file