[yt-svn] commit/yt: 4 new changesets

commits-noreply at bitbucket.org commits-noreply at bitbucket.org
Mon Nov 9 15:01:49 PST 2015


4 new commits in yt:

https://bitbucket.org/yt_analysis/yt/commits/36a295fa58ee/
Changeset:   36a295fa58ee
Branch:      yt
User:        atmyers
Date:        2015-10-29 21:16:31+00:00
Summary:     merging the development and experimental heads (!)
Affected #:  107 files

diff -r 9a49e5d6da9fc23bc7a2858bb5a304b9c4f5537b -r 36a295fa58ee77b259f22525200cde006b19283b .hgignore
--- a/.hgignore
+++ b/.hgignore
@@ -32,6 +32,7 @@
 yt/utilities/lib/CICDeposit.c
 yt/utilities/lib/ContourFinding.c
 yt/utilities/lib/DepthFirstOctree.c
+yt/utilities/lib/element_mappings.c
 yt/utilities/lib/FixedInterpolator.c
 yt/utilities/lib/fortran_reader.c
 yt/utilities/lib/freetype_writer.c

diff -r 9a49e5d6da9fc23bc7a2858bb5a304b9c4f5537b -r 36a295fa58ee77b259f22525200cde006b19283b doc/source/cookbook/amrkdtree_downsampling.py
--- a/doc/source/cookbook/amrkdtree_downsampling.py
+++ b/doc/source/cookbook/amrkdtree_downsampling.py
@@ -4,6 +4,13 @@
 # In this example we will show how to use the AMRKDTree to take a simulation
 # with 8 levels of refinement and only use levels 0-3 to render the dataset.
 
+# Currently this cookbook is flawed in that the data that is covered by the
+# higher resolution data gets masked during the rendering.  This should be
+# fixed by changing either the data source or the code in
+# yt/utilities/amr_kdtree.py where data is being masked for the partitioned
+# grid.  Right now the quick fix is to create a data_collection, but this
+# will only work for patch based simulations that have ds.index.grids.
+
 # We begin by loading up yt, and importing the AMRKDTree
 import numpy as np
 
@@ -12,58 +19,58 @@
 
 # Load up a dataset and define the kdtree
 ds = yt.load('IsolatedGalaxy/galaxy0030/galaxy0030')
-kd = AMRKDTree(ds)
+im, sc = yt.volume_render(ds, 'density', fname='v0.png')
+sc.camera.set_width(ds.arr(100, 'kpc'))
+render_source = sc.get_source(0)
+kd=render_source.volume
 
 # Print out specifics of KD Tree
 print("Total volume of all bricks = %i" % kd.count_volume())
 print("Total number of cells = %i" % kd.count_cells())
 
-# Define a camera and take an volume rendering.
-tf = yt.ColorTransferFunction((-30, -22))
-cam = ds.camera([0.5, 0.5, 0.5], [0.2, 0.3, 0.4], 0.10, 256,
-                  tf, volume=kd)
-tf.add_layers(4, 0.01, col_bounds=[-27.5, -25.5], colormap='RdBu_r')
-cam.snapshot("v1.png", clip_ratio=6.0)
-
-# This rendering is okay, but lets say I'd like to improve it, and I don't want
-# to spend the time rendering the high resolution data.  What we can do is
-# generate a low resolution version of the AMRKDTree and pass that in to the
-# camera.  We do this by specifying a maximum refinement level of 6.
-
-kd_low_res = AMRKDTree(ds, max_level=6)
+new_source = ds.all_data()
+new_source.max_level=3
+kd_low_res = AMRKDTree(ds, data_source=new_source)
 print(kd_low_res.count_volume())
 print(kd_low_res.count_cells())
 
 # Now we pass this in as the volume to our camera, and render the snapshot
 # again.
 
-cam.volume = kd_low_res
-cam.snapshot("v4.png", clip_ratio=6.0)
+render_source.set_volume(kd_low_res)
+render_source.set_fields('density')
+sc.render()
+sc.save("v1.png", sigma_clip=6.0)
 
 # This operation was substantiall faster.  Now lets modify the low resolution
 # rendering until we find something we like.
 
+tf = render_source.transfer_function
 tf.clear()
 tf.add_layers(4, 0.01, col_bounds=[-27.5, -25.5],
               alpha=np.ones(4, dtype='float64'), colormap='RdBu_r')
-cam.snapshot("v2.png", clip_ratio=6.0)
+sc.render()
+sc.save("v2.png", sigma_clip=6.0)
 
 # This looks better.  Now let's try turning on opacity.
 
 tf.grey_opacity = True
-cam.snapshot("v4.png", clip_ratio=6.0)
-
-# That seemed to pick out som interesting structures.  Now let's bump up the
-# opacity.
-
+sc.render()
+sc.save("v3.png", sigma_clip=6.0)
+#
+## That seemed to pick out som interesting structures.  Now let's bump up the
+## opacity.
+#
 tf.clear()
 tf.add_layers(4, 0.01, col_bounds=[-27.5, -25.5],
               alpha=10.0 * np.ones(4, dtype='float64'), colormap='RdBu_r')
-cam.snapshot("v3.png", clip_ratio=6.0)
-
-# This looks pretty good, now lets go back to the full resolution AMRKDTree
-
-cam.volume = kd
-cam.snapshot("v4.png", clip_ratio=6.0)
+sc.render()
+sc.save("v4.png", sigma_clip=6.0)
+#
+## This looks pretty good, now lets go back to the full resolution AMRKDTree
+#
+render_source.set_volume(kd)
+sc.render()
+sc.save("v5.png", sigma_clip=6.0)
 
 # This looks great!

diff -r 9a49e5d6da9fc23bc7a2858bb5a304b9c4f5537b -r 36a295fa58ee77b259f22525200cde006b19283b doc/source/cookbook/camera_movement.py
--- a/doc/source/cookbook/camera_movement.py
+++ b/doc/source/cookbook/camera_movement.py
@@ -3,40 +3,29 @@
 
 # Follow the simple_volume_rendering cookbook for the first part of this.
 ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030")  # load data
-ad = ds.all_data()
-mi, ma = ad.quantities.extrema("density")
-
-# Set up transfer function
-tf = yt.ColorTransferFunction((np.log10(mi), np.log10(ma)))
-tf.add_layers(6, w=0.05)
-
-# Set up camera paramters
-c = [0.5, 0.5, 0.5]  # Center
-L = [1, 1, 1]  # Normal Vector
-W = 1.0  # Width
-Nvec = 512  # Pixels on a side
-
-# Specify a north vector, which helps with rotations.
-north_vector = [0., 0., 1.]
+sc = yt.create_scene(ds)
+cam = sc.camera
+cam.resolution = (512, 512)
+cam.set_width(ds.domain_width/20.0)
 
 # Find the maximum density location, store it in max_c
 v, max_c = ds.find_max('density')
 
-# Initialize the Camera
-cam = ds.camera(c, L, W, (Nvec, Nvec), tf, north_vector=north_vector)
 frame = 0
-
-# Do a rotation over 5 frames
-for i, snapshot in enumerate(cam.rotation(np.pi, 5, clip_ratio=8.0)):
-    snapshot.write_png('camera_movement_%04i.png' % frame)
-    frame += 1
-
 # Move to the maximum density location over 5 frames
-for i, snapshot in enumerate(cam.move_to(max_c, 5, clip_ratio=8.0)):
-    snapshot.write_png('camera_movement_%04i.png' % frame)
+for _ in cam.iter_move(max_c, 5):
+    sc.render()
+    sc.save('camera_movement_%04i.png' % frame, sigma_clip=8.0)
     frame += 1
 
 # Zoom in by a factor of 10 over 5 frames
-for i, snapshot in enumerate(cam.zoomin(10.0, 5, clip_ratio=8.0)):
-    snapshot.write_png('camera_movement_%04i.png' % frame)
+for _ in cam.iter_zoom(10.0, 5):
+    sc.render()
+    sc.save('camera_movement_%04i.png' % frame, sigma_clip=8.0)
     frame += 1
+
+# Do a rotation over 5 frames
+for _ in cam.iter_rotate(np.pi, 5):
+    sc.render()
+    sc.save('camera_movement_%04i.png' % frame, sigma_clip=8.0)
+    frame += 1

diff -r 9a49e5d6da9fc23bc7a2858bb5a304b9c4f5537b -r 36a295fa58ee77b259f22525200cde006b19283b doc/source/cookbook/complex_plots.rst
--- a/doc/source/cookbook/complex_plots.rst
+++ b/doc/source/cookbook/complex_plots.rst
@@ -196,10 +196,39 @@
 
 In this recipe, we move a camera through a domain and take multiple volume
 rendering snapshots.
-See :ref:`volume_rendering` for more information.
+See :ref:`camera_movement` for more information.
 
 .. yt_cookbook:: camera_movement.py
 
+Volume Rendering with Custom Camera
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In this recipe we modify the :ref:`cookbook-simple_volume_rendering` recipe to
+use customized camera properties. See :ref:`volume_rendering` for more
+information.
+
+.. yt_cookbook:: custom_camera_volume_rendering.py
+
+Volume Rendering with a Custom Transfer Function
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In this recipe we modify the :ref:`cookbook-simple_volume_rendering` recipe to
+use customized camera properties. See :ref:`volume_rendering` for more
+information.
+
+.. yt_cookbook:: custom_transfer_function_volume_rendering.py
+
+.. _cookbook-sigma_clip:
+
+Volume Rendering with Sigma Clipping
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In this recipe we output several images with different values of sigma_clip
+set in order to change the contrast of the resulting image.  See 
+:ref:`sigma_clip` for more information.
+
+.. yt_cookbook:: sigma_clip.py
+
 Zooming into an Image
 ~~~~~~~~~~~~~~~~~~~~~
 
@@ -212,6 +241,15 @@
 
 .. yt_cookbook:: zoomin_frames.py
 
+.. _cookbook-various_lens:
+
+Various Lens Types for Volume Rendering
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This example illustrates the usage and feature of different lenses for volume rendering.
+
+.. yt_cookbook:: various_lens.py
+
 .. _cookbook-opaque_rendering:
 
 Opaque Volume Rendering
@@ -220,7 +258,7 @@
 This recipe demonstrates how to make semi-opaque volume renderings, but also
 how to step through and try different things to identify the type of volume
 rendering you want.
-See :ref:`volume_rendering` for more information.
+See :ref:`opaque_rendering` for more information.
 
 .. yt_cookbook:: opaque_rendering.py
 
@@ -235,23 +273,27 @@
 
 .. yt_cookbook:: amrkdtree_downsampling.py
 
+.. _cookbook-volume_rendering_annotations:
+
 Volume Rendering with Bounding Box and Overlaid Grids
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 This recipe demonstrates how to overplot a bounding box on a volume rendering
 as well as overplotting grids representing the level of refinement achieved
 in different regions of the code.
-See :ref:`volume_rendering` for more information.
+See :ref:`volume_rendering_annotations` for more information.
 
 .. yt_cookbook:: rendering_with_box_and_grids.py
 
 Volume Rendering with Annotation
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 This recipe demonstrates how to write the simulation time, show an
 axis triad indicating the direction of the coordinate system, and show
-the transfer function on a volume rendering.
-See :ref:`volume_rendering` for more information.
+the transfer function on a volume rendering.  Please note that this 
+recipe relies on the old volume rendering interface.  While one can
+continue to use this interface, it may be incompatible with some of the
+new developments and the infrastructure described in :ref:`volume_rendering`.
 
 .. yt_cookbook:: vol-annotated.py
 

diff -r 9a49e5d6da9fc23bc7a2858bb5a304b9c4f5537b -r 36a295fa58ee77b259f22525200cde006b19283b doc/source/cookbook/custom_camera_volume_rendering.py
--- /dev/null
+++ b/doc/source/cookbook/custom_camera_volume_rendering.py
@@ -0,0 +1,22 @@
+import yt
+
+# Load the dataset
+ds = yt.load("Enzo_64/DD0043/data0043")
+
+# Create a volume rendering
+sc = yt.create_scene(ds, field=('gas', 'density'))
+
+# Now increase the resolution
+sc.camera.resolution = (1024, 1024)
+
+# Set the camera focus to a position that is offset from the center of
+# the domain
+sc.camera.focus = ds.arr([0.3, 0.3, 0.3], 'unitary')
+
+# Move the camera position to the other side of the dataset
+sc.camera.position = ds.arr([0, 0, 0], 'unitary')
+
+# save to disk with a custom filename and apply sigma clipping to eliminate
+# very bright pixels, producing an image with better contrast.
+sc.render()
+sc.save('custom.png', sigma_clip=4)

diff -r 9a49e5d6da9fc23bc7a2858bb5a304b9c4f5537b -r 36a295fa58ee77b259f22525200cde006b19283b doc/source/cookbook/custom_transfer_function_volume_rendering.py
--- /dev/null
+++ b/doc/source/cookbook/custom_transfer_function_volume_rendering.py
@@ -0,0 +1,24 @@
+import yt
+import numpy as np
+
+# Load the dataset
+ds = yt.load("Enzo_64/DD0043/data0043")
+
+# Create a volume rendering
+sc = yt.create_scene(ds, field=('gas', 'density'))
+
+# Modify the transfer function
+
+# First get the render source, in this case the entire domain, with field ('gas','density')
+render_source = sc.get_source(0)
+
+# Clear the transfer function
+render_source.transfer_function.clear()
+
+# Map a range of density values (in log space) to the Reds_r colormap
+render_source.transfer_function.map_to_colormap(
+    np.log10(ds.quan(5.0e-31, 'g/cm**3')),
+    np.log10(ds.quan(1.0e-29, 'g/cm**3')),
+    scale=30.0, colormap='RdBu_r')
+
+sc.save('new_tf.png')

diff -r 9a49e5d6da9fc23bc7a2858bb5a304b9c4f5537b -r 36a295fa58ee77b259f22525200cde006b19283b doc/source/cookbook/image_background_colors.py
--- a/doc/source/cookbook/image_background_colors.py
+++ b/doc/source/cookbook/image_background_colors.py
@@ -2,27 +2,14 @@
 # volume renderings, to pngs with varying backgrounds.
 
 # First we use the simple_volume_rendering.py recipe from above to generate
-# a standard volume rendering.  The only difference is that we use 
-# grey_opacity=True with our TransferFunction, as the colored background 
-# functionality requires images with an opacity between 0 and 1. 
-
-# We have removed all the comments from the volume rendering recipe for 
-# brevity here, but consult the recipe for more details.
+# a standard volume rendering.
 
 import yt
 import numpy as np
 
 ds = yt.load("Enzo_64/DD0043/data0043")
-ad = ds.all_data()
-mi, ma = ad.quantities.extrema("density")
-tf = yt.ColorTransferFunction((np.log10(mi)+1, np.log10(ma)), grey_opacity=True)
-tf.add_layers(5, w=0.02, colormap="spectral")
-c = [0.5, 0.5, 0.5]
-L = [0.5, 0.2, 0.7]
-W = 1.0
-Npixels = 512
-cam = ds.camera(c, L, W, Npixels, tf)
-im = cam.snapshot("original.png" % ds, clip_ratio=8.0)
+im, sc = yt.volume_render(ds, 'density')
+im.write_png("original.png", sigma_clip=8.0)
 
 # Our image array can now be transformed to include different background
 # colors.  By default, the background color is black.  The following
@@ -35,10 +22,10 @@
 # None  (0.,0.,0.,0.) <-- Transparent!
 # any rgba list/array: [r,g,b,a], bounded by 0..1
 
-# We include the clip_ratio=8 keyword here to bring out more contrast between
+# We include the sigma_clip=8 keyword here to bring out more contrast between
 # the background and foreground, but it is entirely optional.
 
-im.write_png('black_bg.png', background='black', clip_ratio=8.0)
-im.write_png('white_bg.png', background='white', clip_ratio=8.0)
-im.write_png('green_bg.png', background=[0.,1.,0.,1.], clip_ratio=8.0)
-im.write_png('transparent_bg.png', background=None, clip_ratio=8.0)
+im.write_png('black_bg.png', background='black', sigma_clip=8.0)
+im.write_png('white_bg.png', background='white', sigma_clip=8.0)
+im.write_png('green_bg.png', background=[0.,1.,0.,1.], sigma_clip=8.0)
+im.write_png('transparent_bg.png', background=None, sigma_clip=8.0)

diff -r 9a49e5d6da9fc23bc7a2858bb5a304b9c4f5537b -r 36a295fa58ee77b259f22525200cde006b19283b doc/source/cookbook/index.rst
--- a/doc/source/cookbook/index.rst
+++ b/doc/source/cookbook/index.rst
@@ -44,6 +44,7 @@
    embedded_webm_animation
    gadget_notebook
    owls_notebook
+   ../visualizing/transfer_function_helper
    ../analyzing/analysis_modules/sunyaev_zeldovich
    fits_radio_cubes
    fits_xray_images

diff -r 9a49e5d6da9fc23bc7a2858bb5a304b9c4f5537b -r 36a295fa58ee77b259f22525200cde006b19283b doc/source/cookbook/offaxis_projection.py
--- a/doc/source/cookbook/offaxis_projection.py
+++ b/doc/source/cookbook/offaxis_projection.py
@@ -11,7 +11,7 @@
 # objects, you could set it the way you would a cutting plane -- but for this
 # dataset, we'll just choose an off-axis value at random.  This gets normalized
 # automatically.
-L = [0.5, 0.4, 0.7]
+L = [1.0, 0.0, 0.0]
 
 # Our "width" is the width of the image plane as well as the depth.
 # The first element is the left to right width, the second is the
@@ -26,7 +26,7 @@
 # Create the off axis projection.
 # Setting no_ghost to False speeds up the process, but makes a
 # slighly lower quality image.
-image = yt.off_axis_projection(ds, c, L, W, Npixels, "density", no_ghost=False)
+image, sc= yt.off_axis_projection(ds, c, L, W, Npixels, "density", no_ghost=False)
 
 # Write out the final image and give it a name
 # relating to what our dataset is called.

diff -r 9a49e5d6da9fc23bc7a2858bb5a304b9c4f5537b -r 36a295fa58ee77b259f22525200cde006b19283b doc/source/cookbook/offaxis_projection_colorbar.py
--- a/doc/source/cookbook/offaxis_projection_colorbar.py
+++ b/doc/source/cookbook/offaxis_projection_colorbar.py
@@ -32,7 +32,7 @@
 # Also note that we set the field which we want to project as "density", but
 # really we could use any arbitrary field like "temperature", "metallicity"
 # or whatever.
-image = yt.off_axis_projection(ds, c, L, W, Npixels, "density", no_ghost=False)
+image, sc = yt.off_axis_projection(ds, c, L, W, Npixels, "density", no_ghost=False)
 
 # Image is now an NxN array representing the intensities of the various pixels.
 # And now, we call our direct image saver.  We save the log of the result.

diff -r 9a49e5d6da9fc23bc7a2858bb5a304b9c4f5537b -r 36a295fa58ee77b259f22525200cde006b19283b doc/source/cookbook/opaque_rendering.py
--- a/doc/source/cookbook/opaque_rendering.py
+++ b/doc/source/cookbook/opaque_rendering.py
@@ -3,44 +3,51 @@
 
 ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030")
 
-# We start by building a transfer function, and initializing a camera.
+# We start by building a default volume rendering scene 
 
-tf = yt.ColorTransferFunction((-30, -22))
-cam = ds.camera([0.5, 0.5, 0.5], [0.2, 0.3, 0.4], 0.10, 256, tf)
+im, sc = yt.volume_render(ds, field=("gas","density"), fname="v0.png", sigma_clip=6.0)
 
-# Now let's add some isocontours, and take a snapshot.
-
-tf.add_layers(4, 0.01, col_bounds = [-27.5,-25.5], colormap = 'RdBu_r')
-cam.snapshot("v1.png", clip_ratio=6.0)
+sc.camera.set_width(ds.arr(0.1,'code_length'))
+tf = sc.get_source(0).transfer_function 
+tf.clear()
+tf.add_layers(4, 0.01, col_bounds = [-27.5,-25.5],
+        alpha=np.logspace(-3,0,4), colormap = 'RdBu_r')
+sc.render()
+sc.save("v1.png", sigma_clip=6.0)
 
 # In this case, the default alphas used (np.logspace(-3,0,Nbins)) does not
 # accentuate the outer regions of the galaxy. Let's start by bringing up the
 # alpha values for each contour to go between 0.1 and 1.0
 
+tf = sc.get_source(0).transfer_function 
 tf.clear()
 tf.add_layers(4, 0.01, col_bounds = [-27.5,-25.5],
         alpha=np.logspace(0,0,4), colormap = 'RdBu_r')
-cam.snapshot("v2.png", clip_ratio=6.0)
+sc.render()
+sc.save("v2.png", sigma_clip=6.0)
 
 # Now let's set the grey_opacity to True.  This should make the inner portions
 # start to be obcured
 
 tf.grey_opacity = True
-cam.snapshot("v3.png", clip_ratio=6.0)
+sc.render()
+sc.save("v3.png", sigma_clip=6.0)
 
 # That looks pretty good, but let's start bumping up the opacity.
 
 tf.clear()
 tf.add_layers(4, 0.01, col_bounds = [-27.5,-25.5],
         alpha=10.0*np.ones(4,dtype='float64'), colormap = 'RdBu_r')
-cam.snapshot("v4.png", clip_ratio=6.0)
+sc.render()
+sc.save("v4.png", sigma_clip=6.0)
 
 # Let's bump up again to see if we can obscure the inner contour.
 
 tf.clear()
 tf.add_layers(4, 0.01, col_bounds = [-27.5,-25.5],
         alpha=30.0*np.ones(4,dtype='float64'), colormap = 'RdBu_r')
-cam.snapshot("v5.png", clip_ratio=6.0)
+sc.render()
+sc.save("v5.png", sigma_clip=6.0)
 
 # Now we are losing sight of everything.  Let's see if we can obscure the next
 # layer
@@ -48,13 +55,15 @@
 tf.clear()
 tf.add_layers(4, 0.01, col_bounds = [-27.5,-25.5],
         alpha=100.0*np.ones(4,dtype='float64'), colormap = 'RdBu_r')
-cam.snapshot("v6.png", clip_ratio=6.0)
+sc.render()
+sc.save("v6.png", sigma_clip=6.0)
 
 # That is very opaque!  Now lets go back and see what it would look like with
 # grey_opacity = False
 
 tf.grey_opacity=False
-cam.snapshot("v7.png", clip_ratio=6.0)
+sc.render()
+sc.save("v7.png", sigma_clip=6.0)
 
 # That looks pretty different, but the main thing is that you can see that the
 # inner contours are somewhat visible again.  

diff -r 9a49e5d6da9fc23bc7a2858bb5a304b9c4f5537b -r 36a295fa58ee77b259f22525200cde006b19283b doc/source/cookbook/rendering_with_box_and_grids.py
--- a/doc/source/cookbook/rendering_with_box_and_grids.py
+++ b/doc/source/cookbook/rendering_with_box_and_grids.py
@@ -1,61 +1,22 @@
 import yt
 import numpy as np
+from yt.visualization.volume_rendering.api import BoxSource, CoordinateVectorSource
 
 # Load the dataset.
 ds = yt.load("Enzo_64/DD0043/data0043")
+sc = yt.create_scene(ds, ('gas','density'))
+sc.get_source(0).transfer_function.grey_opacity=True
 
-# Create a data container (like a sphere or region) that
-# represents the entire domain.
-ad = ds.all_data()
+sc.annotate_domain(ds)
+sc.render()
+sc.save("%s_vr_domain.png" % ds)
 
-# Get the minimum and maximum densities.
-mi, ma = ad.quantities.extrema("density")
-
-# Create a transfer function to map field values to colors.
-# We bump up our minimum to cut out some of the background fluid
-tf = yt.ColorTransferFunction((np.log10(mi)+2.0, np.log10(ma)))
-
-# Add three Gaussians, evenly spaced between the min and
-# max specified above with widths of 0.02 and using the
-# gist_stern colormap.
-tf.add_layers(3, w=0.02, colormap="gist_stern")
-
-# Choose a center for the render.
-c = [0.5, 0.5, 0.5]
-
-# Choose a vector representing the viewing direction.
-L = [0.5, 0.2, 0.7]
-
-# Set the width of the image.
-# Decreasing or increasing this value
-# results in a zoom in or out.
-W = 1.0
-
-# The number of pixels along one side of the image.
-# The final image will have Npixel^2 pixels.
-Npixels = 512
-
-# Create a camera object.
-# This object creates the images and
-# can be moved and rotated.
-cam = ds.camera(c, L, W, Npixels, tf)
-
-# Create a snapshot.
-# The return value of this function could also be accepted, modified (or saved
-# for later manipulation) and then put written out using write_bitmap.
-# clip_ratio applies a maximum to the function, which is set to that value
-# times the .std() of the array.
-im = cam.snapshot("%s_volume_rendered.png" % ds, clip_ratio=8.0)
-
-# Add the domain edges, with an alpha blending of 0.3:
-nim = cam.draw_domain(im, alpha=0.3)
-nim.write_png('%s_vr_domain.png' % ds)
-
-# Add the grids, colored by the grid level with the algae colormap
-nim = cam.draw_grids(im, alpha=0.3, cmap='algae')
-nim.write_png('%s_vr_grids.png' % ds)
+sc.annotate_grids(ds)
+sc.render()
+sc.save("%s_vr_grids.png" % ds)
 
 # Here we can draw the coordinate vectors on top of the image by processing
 # it through the camera. Then save it out.
-cam.draw_coordinate_vectors(nim)
-nim.write_png("%s_vr_vectors.png" % ds)
+sc.annotate_axes()
+sc.render()
+sc.save("%s_vr_coords.png" % ds)

diff -r 9a49e5d6da9fc23bc7a2858bb5a304b9c4f5537b -r 36a295fa58ee77b259f22525200cde006b19283b doc/source/cookbook/sigma_clip.py
--- /dev/null
+++ b/doc/source/cookbook/sigma_clip.py
@@ -0,0 +1,17 @@
+import yt
+
+# Load the dataset.
+ds = yt.load("enzo_tiny_cosmology/RD0009/RD0009")
+
+# Create a volume rendering, which will determine data bounds, use the first
+# acceptable field in the field_list, and set up a default transfer function.
+
+# Render and save output images with different levels of sigma clipping.
+# Sigma clipping removes the highest intensity pixels in a volume render, 
+# which affects the overall contrast of the image.
+sc = yt.create_scene(ds, field=('gas', 'density'))
+sc.render()
+sc.save('clip_0.png')
+sc.save('clip_2.png', sigma_clip=2)
+sc.save('clip_4.png', sigma_clip=4)
+sc.save('clip_6.png', sigma_clip=6)

diff -r 9a49e5d6da9fc23bc7a2858bb5a304b9c4f5537b -r 36a295fa58ee77b259f22525200cde006b19283b doc/source/cookbook/simple_volume_rendering.py
--- a/doc/source/cookbook/simple_volume_rendering.py
+++ b/doc/source/cookbook/simple_volume_rendering.py
@@ -1,48 +1,10 @@
 import yt
-import numpy as np
 
 # Load the dataset.
 ds = yt.load("Enzo_64/DD0043/data0043")
 
-# Create a data container (like a sphere or region) that
-# represents the entire domain.
-ad = ds.all_data()
+# Create a volume rendering, which will determine data bounds, use the first
+# acceptable field in the field_list, and set up a default transfer function.
 
-# Get the minimum and maximum densities.
-mi, ma = ad.quantities.extrema("density")
-
-# Create a transfer function to map field values to colors.
-# We bump up our minimum to cut out some of the background fluid
-tf = yt.ColorTransferFunction((np.log10(mi)+1, np.log10(ma)))
-
-# Add five Gaussians, evenly spaced between the min and
-# max specified above with widths of 0.02 and using the
-# spectral colormap.
-tf.add_layers(5, w=0.02, colormap="spectral")
-
-# Choose a center for the render.
-c = [0.5, 0.5, 0.5]
-
-# Choose a vector representing the viewing direction.
-L = [0.5, 0.2, 0.7]
-
-# Set the width of the image.
-# Decreasing or increasing this value
-# results in a zoom in or out.
-W = 1.0
-
-# The number of pixels along one side of the image.
-# The final image will have Npixel^2 pixels.
-Npixels = 512
-
-# Create a camera object.
-# This object creates the images and
-# can be moved and rotated.
-cam = ds.camera(c, L, W, Npixels, tf)
-
-# Create a snapshot.
-# The return value of this function could also be accepted, modified (or saved
-# for later manipulation) and then put written out using write_bitmap.
-# clip_ratio applies a maximum to the function, which is set to that value
-# times the .std() of the array.
-cam.snapshot("%s_volume_rendered.png" % ds, clip_ratio=8.0)
+# This will save a file named 'data0043_Render_density.png' to disk.
+im, sc = yt.volume_render(ds, field=('gas', 'density'))

diff -r 9a49e5d6da9fc23bc7a2858bb5a304b9c4f5537b -r 36a295fa58ee77b259f22525200cde006b19283b doc/source/cookbook/various_lens.py
--- /dev/null
+++ b/doc/source/cookbook/various_lens.py
@@ -0,0 +1,120 @@
+import yt
+from yt.visualization.volume_rendering.api import Scene, Camera, VolumeSource
+import numpy as np
+
+field = ("gas", "density")
+
+# normal_vector points from camera to the center of tbe final projection.
+# Now we look at the positive x direction.
+normal_vector = [1., 0., 0.]
+# north_vector defines the "top" direction of the projection, which is
+# positive z direction here.
+north_vector = [0., 0., 1.]
+
+# Follow the simple_volume_rendering cookbook for the first part of this.
+ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030")
+sc = Scene()
+vol = VolumeSource(ds, field=field)
+tf = vol.transfer_function
+tf.grey_opacity = True
+
+# Plane-parallel lens
+cam = Camera(ds, lens_type='plane-parallel')
+# Set the resolution of tbe final projection.
+cam.resolution = [500, 500]
+# Set the location of the camera to be (x=0.2, y=0.5, z=0.5)
+# For plane-parallel lens, the location info along the normal_vector (here
+# is x=0.2) is ignored. 
+cam.position = ds.arr(np.array([0.2, 0.5, 0.5]), 'code_length')
+# Set the orientation of the camera.
+cam.switch_orientation(normal_vector=normal_vector,
+                       north_vector=north_vector)
+# Set the width of the camera, where width[0] and width[1] specify the length and
+# height of final projection, while width[2] in plane-parallel lens is not used.
+cam.set_width(ds.domain_width * 0.5)
+sc.camera = cam
+sc.add_source(vol)
+sc.render()
+sc.save('lens_plane-parallel.png', sigma_clip=6.0)
+
+# Perspective lens
+cam = Camera(ds, lens_type='perspective')
+cam.resolution = [500, 500]
+# Standing at (x=0.2, y=0.5, z=0.5), we look at the area of x>0.2 (with some open angle
+# specified by camera width) along the positive x direction.
+cam.position = ds.arr([0.2, 0.5, 0.5], 'code_length')
+cam.switch_orientation(normal_vector=normal_vector,
+                       north_vector=north_vector)
+# Set the width of the camera, where width[0] and width[1] specify the length and
+# height of the final projection, while width[2] specifies the distance between the
+# camera and the final image.
+cam.set_width(ds.domain_width * 0.5)
+sc.camera = cam
+sc.add_source(vol)
+sc.render()
+sc.save('lens_perspective.png', sigma_clip=6.0)
+
+# Stereo-perspective lens
+cam = Camera(ds, lens_type='stereo-perspective')
+# Set the size ratio of the final projection to be 2:1, since stereo-perspective lens
+# will generate the final image with both left-eye and right-eye ones jointed together.
+cam.resolution = [1000, 500]
+cam.position = ds.arr([0.2, 0.5, 0.5], 'code_length')
+cam.switch_orientation(normal_vector=normal_vector,
+                       north_vector=north_vector)
+cam.set_width(ds.domain_width*0.5)
+# Set the distance between left-eye and right-eye.
+cam.lens.disparity = ds.domain_width[0] * 1.e-3
+sc.camera = cam
+sc.add_source(vol)
+sc.render()
+sc.save('lens_stereo-perspective.png', sigma_clip=6.0)
+
+# Fisheye lens
+dd = ds.sphere(ds.domain_center, ds.domain_width[0] / 10)
+cam = Camera(dd, lens_type='fisheye')
+cam.resolution = [500, 500]
+v, c = ds.find_max(field)
+cam.set_position(c - 0.0005 * ds.domain_width)
+cam.switch_orientation(normal_vector=normal_vector,
+                       north_vector=north_vector)
+cam.set_width(ds.domain_width)
+cam.lens.fov = 360.0
+sc.camera = cam
+sc.add_source(vol)
+sc.render()
+sc.save('lens_fisheye.png', sigma_clip=6.0)
+
+# Spherical lens
+cam = Camera(ds, lens_type='spherical')
+# Set the size ratio of the final projection to be 2:1, since spherical lens
+# will generate the final image with length of 2*pi and height of pi.
+cam.resolution = [1000, 500]
+# Standing at (x=0.4, y=0.5, z=0.5), we look in all the radial directions
+# from this point in spherical coordinate.
+cam.position = ds.arr([0.4, 0.5, 0.5], 'code_length')
+cam.switch_orientation(normal_vector=normal_vector,
+                       north_vector=north_vector)
+# In (stereo)spherical camera, width[0] specifies the radius of the sphere (the
+# depth of your line of sight), while width[1] or width[2] are not used.
+cam.set_width(ds.domain_width * 0.5)
+sc.camera = cam
+sc.add_source(vol)
+sc.render()
+sc.save('lens_spherical.png', sigma_clip=6.0)
+
+# Stereo-spherical lens
+cam = Camera(ds, lens_type='stereo-spherical')
+# Set the size ratio of the final projection to be 4:1, since spherical-perspective lens
+# will generate the final image with both left-eye and right-eye ones jointed together.
+cam.resolution = [2000, 500]
+cam.position = ds.arr([0.4, 0.5, 0.5], 'code_length')
+cam.switch_orientation(normal_vector=normal_vector,
+                       north_vector=north_vector)
+cam.set_width(ds.domain_width * 0.5)
+# Set the distance between left-eye and right-eye.
+cam.lens.disparity = ds.domain_width[0] * 1.e-3
+sc.camera = cam
+sc.add_source(vol)
+sc.render()
+sc.save('lens_stereo-spherical.png', sigma_clip=6.0)

diff -r 9a49e5d6da9fc23bc7a2858bb5a304b9c4f5537b -r 36a295fa58ee77b259f22525200cde006b19283b doc/source/cookbook/vol-annotated.py
--- a/doc/source/cookbook/vol-annotated.py
+++ b/doc/source/cookbook/vol-annotated.py
@@ -4,7 +4,7 @@
 import pylab
 
 import yt
-import yt.visualization.volume_rendering.api as vr
+import yt.visualization.volume_rendering.old_camera as vr
 
 ds = yt.load("maestro_subCh_plt00248")
 
@@ -17,11 +17,11 @@
 # centered on these with width sigma        
 vals = [-1.e7, -5.e6, -2.5e6, 2.5e6, 5.e6, 1.e7]
 sigma = 2.e5
-        
+
 mi, ma = min(vals), max(vals)
 
 # Instantiate the ColorTransferfunction.
-tf =  vr.ColorTransferFunction((mi, ma))
+tf =  yt.ColorTransferFunction((mi, ma))
 
 for v in vals:
     tf.sample_colormap(v, sigma**2, colormap="coolwarm")
@@ -69,7 +69,7 @@
 
 # tell the camera to use our figure
 cam._render_figure = f
-    
+
 # save annotated -- this added the transfer function values, 
 # and the clear_fig=False ensures it writes onto our existing figure.
 cam.save_annotated("vol_annotated.png", nim, dpi=145, clear_fig=False)

diff -r 9a49e5d6da9fc23bc7a2858bb5a304b9c4f5537b -r 36a295fa58ee77b259f22525200cde006b19283b doc/source/examining/loading_data.rst
--- a/doc/source/examining/loading_data.rst
+++ b/doc/source/examining/loading_data.rst
@@ -1026,6 +1026,60 @@
 * Some functions may behave oddly or not work at all.
 * Data must already reside in memory.
 
+Unstructured Grid Data
+----------------------
+
+See :ref:`loading-numpy-array`,
+:func:`~yt.frontends.stream.data_structures.load_unstructured_mesh` for
+more detail.
+
+In addition to the above grid types, you can also load data stored on
+unstructured meshes. This type of mesh is used, for example, in many
+finite element calculations. Currently, hexahedral, tetrahedral, and
+wedge-shaped mesh element are supported.
+
+To load an unstructured mesh, you need to specify the following. First,
+you need to have a coordinates array, which should be an (L, 3) array
+that stores the (x, y, z) positions of all of the vertices in the mesh.
+Second, you need to specify a connectivity array, which describes how
+those vertices are connected into mesh elements. The connectivity array
+should be (N, M), where N is the number of elements and M is the
+connectivity length, i.e. the number of vertices per element. Finally,
+you must also specify a data dictionary, where the keys should be
+the names of the fields and the values should be numpy arrays that
+contain the field data. These arrays can either supply the cell-averaged
+data for each element, in which case they would be (N, 1), or they
+can have node-centered data, in which case they would also be (N, M).
+
+Here is an example of how to load an in-memory, unstructured mesh dataset:
+
+.. code-block:: python
+
+   import yt
+   import numpy
+   from yt.utilities.exodusII_reader import get_data
+
+   coords, connectivity, data = get_data("MOOSE_sample_data/out.e-s010")
+
+This uses a publically available `MOOSE <http://mooseframework.org/>` 
+dataset along with the get_data function to parse the coords, connectivity, 
+and data. Then, these can be loaded as an in-memory dataset as follows:
+
+.. code-block:: python
+
+    mesh_id = 0
+    ds = yt.load_unstructured_mesh(data[mesh_id], connectivity[mesh_id], coords[mesh_id])
+
+Note that load_unstructured_mesh can take either a single or a list of meshes.
+Here, we have selected only the first mesh to load.
+
+.. rubric:: Caveats
+
+* Units will be incorrect unless the data has already been converted to cgs.
+* Integration is not implemented.
+* Some functions may behave oddly or not work at all.
+* Data must already reside in memory.
+
 Generic Particle Data
 ---------------------
 

diff -r 9a49e5d6da9fc23bc7a2858bb5a304b9c4f5537b -r 36a295fa58ee77b259f22525200cde006b19283b doc/source/quickstart/6)_Volume_Rendering.ipynb
--- a/doc/source/quickstart/6)_Volume_Rendering.ipynb
+++ b/doc/source/quickstart/6)_Volume_Rendering.ipynb
@@ -56,14 +56,14 @@
      "cell_type": "markdown",
      "metadata": {},
      "source": [
-      "If we want to apply a clipping, we can specify the `clip_ratio`.  This will clip the upper bounds to this value times the standard deviation of the values in the image array."
+      "If we want to apply a clipping, we can specify the `sigma_clip`.  This will clip the upper bounds to this value times the standard deviation of the values in the image array."
      ]
     },
     {
      "cell_type": "code",
      "collapsed": false,
      "input": [
-      "cam.show(clip_ratio=4)"
+      "cam.show(sigma_clip=4)"
      ],
      "language": "python",
      "metadata": {},
@@ -83,7 +83,7 @@
       "tf = yt.ColorTransferFunction((-28, -25))\n",
       "tf.add_layers(4, w=0.03)\n",
       "cam = ds.camera([0.5, 0.5, 0.5], [1.0, 1.0, 1.0], (20.0, 'kpc'), 512, tf, no_ghost=False)\n",
-      "cam.show(clip_ratio=4.0)"
+      "cam.show(sigma_clip=4.0)"
      ],
      "language": "python",
      "metadata": {},
@@ -93,4 +93,4 @@
    "metadata": {}
   }
  ]
-}
\ No newline at end of file
+}

diff -r 9a49e5d6da9fc23bc7a2858bb5a304b9c4f5537b -r 36a295fa58ee77b259f22525200cde006b19283b doc/source/reference/api/api.rst
--- a/doc/source/reference/api/api.rst
+++ b/doc/source/reference/api/api.rst
@@ -395,6 +395,7 @@
    ~yt.frontends.stream.data_structures.load_amr_grids
    ~yt.frontends.stream.data_structures.load_particles
    ~yt.frontends.stream.data_structures.load_hexahedral_mesh
+   ~yt.frontends.stream.data_structures.load_unstructured_mesh
 
 Derived Datatypes
 -----------------
@@ -588,9 +589,13 @@
 .. autosummary::
    :toctree: generated/
 
+   ~yt.visualization.volume_rendering.volume_rendering.volume_render
+   ~yt.visualization.volume_rendering.volume_rendering.create_scene
+   ~yt.visualization.volume_rendering.off_axis_projection.off_axis_projection
+   ~yt.visualization.volume_rendering.scene.Scene
    ~yt.visualization.volume_rendering.camera.Camera
-   ~yt.visualization.volume_rendering.camera.off_axis_projection
-   ~yt.visualization.volume_rendering.camera.allsky_projection
+   ~yt.visualization.volume_rendering.lens.Lens
+   ~yt.visualization.volume_rendering.render_source.RenderSource
 
 These objects set up the way the image looks:
 
@@ -614,6 +619,19 @@
    ~yt.utilities.amr_kdtree.amr_kdtree.AMRKDTree
    ~yt.visualization.volume_rendering.camera.StereoPairCamera
 
+Additional sources can be added to a scene:
+
+.. autosummary::
+   :toctree: generated/
+
+   ~yt.visualization.volume_rendering.api.VolumeSource
+   ~yt.visualization.volume_rendering.api.PointSource
+   ~yt.visualization.volume_rendering.api.LineSource
+   ~yt.visualization.volume_rendering.api.BoxSource
+   ~yt.visualization.volume_rendering.api.GridSource
+   ~yt.visualization.volume_rendering.api.CoordinateVectorSource
+   ~yt.visualization.volume_rendering.render_source.MeshSource
+
 Streamlining
 ^^^^^^^^^^^^
 

diff -r 9a49e5d6da9fc23bc7a2858bb5a304b9c4f5537b -r 36a295fa58ee77b259f22525200cde006b19283b doc/source/visualizing/TransferFunctionHelper_Tutorial.ipynb
--- a/doc/source/visualizing/TransferFunctionHelper_Tutorial.ipynb
+++ b/doc/source/visualizing/TransferFunctionHelper_Tutorial.ipynb
@@ -1,7 +1,7 @@
 {
  "metadata": {
   "name": "",
-  "signature": "sha256:5a1547973517987ff047f1b2405277a0e98392e8fd5ffe04521cb2dc372d32d3"
+  "signature": "sha256:ed09405c56bab51abd351d107a4354726709d289b965f274106f4451b387f5ba"
  },
  "nbformat": 3,
  "nbformat_minor": 0,
@@ -25,6 +25,8 @@
       "import numpy as np\n",
       "from IPython.core.display import Image\n",
       "from yt.visualization.volume_rendering.transfer_function_helper import TransferFunctionHelper\n",
+      "from yt.visualization.volume_rendering.render_source import VolumeSource\n",
+      "from yt.visualization.volume_rendering.camera import Camera\n",
       "\n",
       "def showme(im):\n",
       "    # screen out NaNs\n",
@@ -66,7 +68,7 @@
      "cell_type": "code",
      "collapsed": false,
      "input": [
-      "tfh = yt.TransferFunctionHelper(ds)"
+      "tfh = TransferFunctionHelper(ds)"
      ],
      "language": "python",
      "metadata": {},
@@ -84,7 +86,7 @@
      "collapsed": false,
      "input": [
       "# Build a transfer function that is a multivariate gaussian in temperature\n",
-      "tfh = yt.TransferFunctionHelper(ds)\n",
+      "tfh = TransferFunctionHelper(ds)\n",
       "tfh.set_field('temperature')\n",
       "tfh.set_log(True)\n",
       "tfh.set_bounds()\n",
@@ -124,7 +126,7 @@
      "cell_type": "code",
      "collapsed": false,
      "input": [
-      "tfh = yt.TransferFunctionHelper(ds)\n",
+      "tfh = TransferFunctionHelper(ds)\n",
       "tfh.set_field('temperature')\n",
       "tfh.set_bounds()\n",
       "tfh.set_log(True)\n",
@@ -143,27 +145,20 @@
      "cell_type": "markdown",
      "metadata": {},
      "source": [
-      "Finally, let's take a look at the volume rendering."
+      "Finally, let's take a look at the volume rendering. First use the helper function to create a default rendering, then we override this with the transfer function we just created."
      ]
     },
     {
      "cell_type": "code",
      "collapsed": false,
      "input": [
-      "L = [-0.1, -1.0, -0.1]\n",
-      "c = ds.domain_center\n",
-      "W = 1.5*ds.domain_width\n",
-      "Npixels = 512 \n",
-      "cam = ds.camera(c, L, W, Npixels, tfh.tf, fields=['temperature'],\n",
-      "                  north_vector=[1.,0.,0.], steady_north=True, \n",
-      "                  sub_samples=5, no_ghost=False)\n",
+      "im, sc = yt.volume_render(ds, ['temperature'])\n",
       "\n",
-      "# Here we substitute the TransferFunction we constructed earlier.\n",
-      "cam.transfer_function = tfh.tf\n",
+      "source = sc.get_source(0)\n",
+      "source.set_transfer_function(tfh.tf)\n",
+      "im2 = sc.render()\n",
       "\n",
-      "\n",
-      "im = cam.snapshot()\n",
-      "showme(im[:,:,:3])"
+      "showme(im2[:,:,:3])"
      ],
      "language": "python",
      "metadata": {},

diff -r 9a49e5d6da9fc23bc7a2858bb5a304b9c4f5537b -r 36a295fa58ee77b259f22525200cde006b19283b doc/source/visualizing/_images/scene_diagram.svg
--- /dev/null
+++ b/doc/source/visualizing/_images/scene_diagram.svg
@@ -0,0 +1,512 @@
+<?xml version="1.0" standalone="yes"?>
+
+<svg version="1.1" viewBox="0.0 0.0 710.0 462.0" fill="none" stroke="none"
+stroke-linecap="square" stroke-miterlimit="10"
+xmlns="http://www.w3.org/2000/svg"
+xmlns:xlink="http://www.w3.org/1999/xlink"><clipPath id="p.0"><path d="m0
+0l710.0 0l0 462.0l-710.0 0l0 -462.0z" clip-rule="nonzero"></path></clipPath><g
+clip-path="url(#p.0)"><path fill="#000000" fill-opacity="0.0" d="m0 0l710.4252
+0l0 462.4462l-710.4252 0z" fill-rule="nonzero"></path><path fill="#000000"
+fill-opacity="0.0" d="m50.425198 121.13386l489.73227 0l0 289.6063l-489.73227
+0z" fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0"
+d="m540.1575 121.13386l96.53546 -96.53543l0 289.6063l-96.53546 96.53543z"
+fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0"
+d="m50.425198 121.13386l96.53543 -96.53543l489.7323 0l-96.53546 96.53543z"
+fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0"
+d="m50.425198 121.13386l96.53543 -96.53543l489.7323 0l0 289.6063l-96.53546
+96.53543l-489.73227 0zm0 0l489.73227 0l96.53546 -96.53543m-96.53546 96.53543l0
+289.6063" fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.2"
+d="m540.1575 121.13386l96.53546 -96.53543l0 289.6063l-96.53546 96.53543z"
+fill-rule="nonzero"></path><path fill="#ffffff" fill-opacity="0.2"
+d="m50.425198 121.13386l96.53543 -96.53543l489.7323 0l-96.53546 96.53543z"
+fill-rule="nonzero"></path><path stroke="#000000" stroke-width="2.0"
+stroke-linejoin="round" stroke-linecap="butt" d="m50.425198 121.13386l96.53543
+-96.53543l489.7323 0l0 289.6063l-96.53546 96.53543l-489.73227 0zm0 0l489.73227
+0l96.53546 -96.53543m-96.53546 96.53543l0 289.6063"
+fill-rule="nonzero"></path><path fill="#cccccc" d="m399.52493 268.74014l0 0c0
+-6.521576 14.789307 -11.80838 33.032806 -11.80838c18.24353 0 33.032806 5.286804
+33.032806 11.80838l0 47.233612c0 6.5216064 -14.789276 11.808411 -33.032806
+11.808411c-18.2435 0 -33.032806 -5.286804 -33.032806 -11.808411z"
+fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0"
+d="m465.59055 268.74014l0 0c0 6.5216064 -14.789276 11.808411 -33.032806
+11.808411c-18.2435 0 -33.032806 -5.286804 -33.032806 -11.808411"
+fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0"
+d="m399.52493 268.74014l0 0c0 -6.521576 14.789307 -11.80838 33.032806
+-11.80838c18.24353 0 33.032806 5.286804 33.032806 11.80838l0 47.233612c0
+6.5216064 -14.789276 11.808411 -33.032806 11.808411c-18.2435 0 -33.032806
+-5.286804 -33.032806 -11.808411z" fill-rule="nonzero"></path><path
+stroke="#000000" stroke-width="2.0" stroke-linejoin="round"
+stroke-linecap="butt" d="m465.59055 268.74014l0 0c0 6.5216064 -14.789276
+11.808411 -33.032806 11.808411c-18.2435 0 -33.032806 -5.286804 -33.032806
+-11.808411" fill-rule="nonzero"></path><path stroke="#000000"
+stroke-width="2.0" stroke-linejoin="round" stroke-linecap="butt" d="m399.52493
+268.74014l0 0c0 -6.521576 14.789307 -11.80838 33.032806 -11.80838c18.24353 0
+33.032806 5.286804 33.032806 11.80838l0 47.233612c0 6.5216064 -14.789276
+11.808411 -33.032806 11.808411c-18.2435 0 -33.032806 -5.286804 -33.032806
+-11.808411z" fill-rule="nonzero"></path><path fill="#cccccc" d="m333.45938
+255.11298l20.896912 1.2207031E-4l6.457367 -18.286331l6.457367
+18.286331l20.896881 -1.2207031E-4l-16.906006 11.301453l6.457611
+18.286224l-16.905853 -11.301636l-16.905884 11.301636l6.4576416 -18.286224z"
+fill-rule="nonzero"></path><path stroke="#000000" stroke-width="2.0"
+stroke-linejoin="round" stroke-linecap="butt" d="m333.45938 255.11298l20.896912
+1.2207031E-4l6.457367 -18.286331l6.457367 18.286331l20.896881
+-1.2207031E-4l-16.906006 11.301453l6.457611 18.286224l-16.905853
+-11.301636l-16.905884 11.301636l6.4576416 -18.286224z"
+fill-rule="nonzero"></path><path fill="#cccccc" d="m408.14435
+214.56168l38.53543 0l0 22.251968l-38.53543 0z" fill-rule="nonzero"></path><path
+fill="#a3a3a3" d="m446.67978 214.56168l7.417328 -7.4173126l0
+22.251968l-7.417328 7.4173126z" fill-rule="nonzero"></path><path fill="#d6d6d6"
+d="m408.14435 214.56168l7.417328 -7.4173126l38.53543 0l-7.417328 7.4173126z"
+fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0"
+d="m408.14435 214.56168l7.417328 -7.4173126l38.53543 0l0 22.251968l-7.417328
+7.4173126l-38.53543 0zm0 0l38.53543 0l7.417328 -7.4173126m-7.417328 7.4173126l0
+22.251968" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="2.0"
+stroke-linejoin="round" stroke-linecap="butt" d="m408.14435 214.56168l7.417328
+-7.4173126l38.53543 0l0 22.251968l-7.417328 7.4173126l-38.53543 0zm0 0l38.53543
+0l7.417328 -7.4173126m-7.417328 7.4173126l0 22.251968"
+fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0"
+d="m329.62204 327.78217l135.9685 0l0 54.582672l-135.9685 0z"
+fill-rule="nonzero"></path><path fill="#000000" d="m354.4149 362.55652q-0.15625
+0.0625 -0.375 0.0625q-0.234375 0 -0.5 -0.140625q-2.796875 -3.015625 -3.734375
+-7.765625q-0.328125 -1.640625 -0.328125 -3.40625q0 -3.375 1.203125
+-6.4375q1.046875 -2.703125 2.859375 -4.671875q0.125 -0.109375 0.515625
+-0.109375q0.375 0 0.640625 0.25q0.265625 0.25 0.265625 0.5625q-2.4375 4.171875
+-2.859375 7.734375q-0.140625 1.25 -0.140625 2.625q-0.015625 0.0625 -0.015625
+0.109375q0 1.34375 0.15625 2.5625q0.140625 1.25 0.5 2.515625q0.6875 2.453125
+2.359375 5.328125q0 0.515625 -0.546875 0.78125zm5.5 -4.3125l1.34375 0q1.625 0
+2.1875 -0.984375q0.171875 -0.3125 0.171875 -0.671875q0 -0.359375 -0.171875
+-0.625q-0.171875 -0.265625 -0.53125 -0.484375q-0.5625 -0.34375 -1.84375
+-0.734375q-1.28125 -0.390625 -1.953125 -0.671875q-0.65625 -0.296875 -1.15625
+-0.703125q-1.0 -0.84375 -1.0 -2.3125q0 -2.171875 2.421875 -2.921875q0.90625
+-0.28125 1.796875 -0.28125q0.890625 0 1.640625 0.125q0.765625 0.109375 1.375
+0.390625q1.390625 0.640625 1.390625 1.84375l0 0.0625q0 0.65625 -0.453125
+1.109375q-0.4375 0.4375 -1.140625 0.453125q-1.109375 0 -1.5 -0.953125q-0.125
+-0.296875 -0.125 -0.484375q0 -0.1875 0.140625 -0.890625l-1.15625 0q-0.953125 0
+-1.40625 0.328125q-0.828125 0.578125 -0.828125 1.265625q0 0.90625 1.640625
+1.46875l0.890625 0.3125q2.25 0.75 2.890625 1.28125q0.65625 0.515625 0.9375
+1.140625q0.296875 0.625 0.296875 1.421875q0 0.8125 -0.3125 1.390625q-0.296875
+0.59375 -0.859375 1.015625q-1.21875 0.921875 -3.140625 0.921875q-1.71875 0
+-3.078125 -0.515625q-1.0 -0.375 -1.421875 -1.078125q-0.21875 -0.359375 -0.21875
+-0.765625q0 -0.390625 0.125 -0.6875q0.140625 -0.3125 0.34375 -0.515625q0.453125
+-0.453125 1.15625 -0.453125q0.671875 0 1.140625 0.46875q0.46875 0.4375 0.46875
+1.0l0 0.015625q-0.015625 0.578125 -0.0625 0.71875zm12.8515625 1.8125q-1.25 0
+-2.203125 -0.46875q-0.953125 -0.46875 -1.609375 -1.3125q-1.3125 -1.703125
+-1.3125 -4.453125q0 -2.890625 1.78125 -4.5q1.625 -1.46875 4.078125
+-1.46875l0.03125 0q2.296875 0 3.703125 1.75q1.328125 1.65625 1.328125 4.3125q0
+2.953125 -1.640625 4.59375q-1.546875 1.546875 -4.15625 1.546875zm-1.703125
+-9.578125q-0.4375 0.46875 -0.734375 1.28125q-0.28125 0.8125 -0.28125 2.0q0
+1.203125 0.21875 2.03125q0.234375 0.8125 0.640625 1.359375q0.796875 1.09375
+2.234375 1.09375q1.46875 0 2.28125 -1.28125q0.734375 -1.171875 0.734375
+-3.078125q0 -3.171875 -1.859375 -3.984375q-0.5625 -0.234375 -1.140625
+-0.234375q-0.578125 0 -1.109375 0.171875q-0.53125 0.171875 -0.984375
+0.640625zm18.230469 8.21875q-1.875 1.359375 -3.375 1.359375q-2.953125 0
+-3.796875 -2.375q-0.28125 -0.78125 -0.28125 -1.828125l0 -5.953125l-1.640625
+0q-0.4375 0 -0.4375 -0.890625q0 -0.59375 0.234375 -0.65625q1.96875 -0.5 3.59375
+-0.5q0.515625 0 0.546875 0.9375l0 6.21875q0 2.28125 1.171875 2.71875q0.34375
+0.140625 0.875 0.140625q0.515625 0 0.984375 -0.109375q0.484375 -0.125 0.859375
+-0.28125q0.34375 -0.171875 0.625 -0.375l0.5 -0.375l0 -6.828125l-1.59375
+0q-0.484375 0 -0.484375 -0.875q0 -0.546875 0.3125 -0.671875q0.3125 -0.125 0.75
+-0.21875q0.453125 -0.078125 0.921875 -0.15625q0.921875 -0.109375 1.46875
+-0.125l0.03125 0q0.515625 0 0.625 0.125q0.140625 0.125 0.1875 0.328125q0.078125
+0.296875 0.078125 1.03125l0 8.828125l1.96875 0q0.3125 0 0.3125 0.765625q0
+0.8125 -0.484375 0.890625q-1.1875 0.1875 -2.125 0.1875q-0.9375 0 -1.109375
+-0.09375q-0.15625 -0.09375 -0.28125 -0.265625q-0.1875 -0.25 -0.4375
+-0.953125zm9.6328125 -9.96875l0 0.46875q0 0.140625 -0.015625 0.28125q0 0.140625
+0 0.25q1.375 -1.25 2.75 -1.71875q0.453125 -0.140625 0.921875 -0.15625l0.03125
+0q0.453125 0 0.8125 0.171875q0.375 0.1875 0.609375 0.453125q0.390625 0.46875
+0.390625 0.96875q0 0.515625 -0.125 0.8125q-0.125 0.296875 -0.359375 0.5q-0.5
+0.453125 -0.984375 0.453125q-0.484375 0 -0.75 -0.125q-0.25 -0.140625 -0.453125
+-0.34375q-0.421875 -0.453125 -0.421875 -1.0q-0.671875 0.3125 -1.03125
+0.71875q-0.34375 0.421875 -0.578125 0.765625l-0.515625 0.796875l0
+6.140625l2.84375 0q0.265625 0 0.265625 0.71875q0 0.578125 -0.171875
+0.765625q-0.171875 0.171875 -0.3125 0.171875l-6.8125 0q-0.328125 0 -0.328125
+-0.734375q0.015625 -0.546875 0.3125 -0.84375q0.09375 -0.078125 0.40625
+-0.078125q0.328125 0 0.65625 -0.03125q0.328125 -0.03125 0.515625
+-0.078125q0.328125 -0.109375 0.328125 -0.390625l0 -7.71875l-2.0625 0q-0.453125
+0 -0.453125 -0.921875q0 -0.578125 0.296875 -0.703125q0.296875 -0.140625 0.78125
+-0.21875q0.5 -0.09375 1.015625 -0.140625q0.984375 -0.09375 1.546875
+-0.109375l0.09375 0q0.484375 0 0.578125 0.0625q0.125 0.046875 0.171875
+0.15625q0.046875 0.125 0.046875 0.515625l0 0.140625zm13.277344
+0.78125l-1.078125 0q-1.21875 0 -2.109375 0.921875q-1.125 1.1875 -1.125
+3.40625q0 3.265625 1.828125 4.125q0.5625 0.28125 1.171875 0.28125q0.59375 0
+1.0625 -0.125q0.484375 -0.125 0.953125 -0.34375q0.46875 -0.1875 0.890625
+-0.453125l0.828125 -0.5q0.046875 -0.03125 0.15625 -0.03125q0.109375 0 0.265625
+0.140625q0.171875 0.125 0.3125 0.3125q0.3125 0.4375 0.3125 0.734375q0 0.296875
+-0.140625 0.40625q-2.234375 1.671875 -4.875 1.671875q-2.4375 0 -3.828125
+-1.703125q-1.328125 -1.640625 -1.328125 -4.515625q0 -2.8125 1.6875
+-4.453125q1.59375 -1.515625 4.078125 -1.53125q2.640625 0.015625 3.65625
+1.15625q0.390625 0.453125 0.390625 0.953125q0 0.5 -0.109375 0.796875q-0.140625
+0.296875 -0.359375 0.5q-0.46875 0.46875 -1.140625 0.46875q-0.65625 0 -1.125
+-0.46875q-0.46875 -0.4375 -0.46875 -0.890625q0 -0.4375 0.09375
+-0.859375zm5.046875 4.28125q0 -1.453125 0.484375 -2.578125q0.46875 -1.09375
+1.265625 -1.84375q1.609375 -1.5 3.984375 -1.515625l0.046875 0q2.171875 0
+3.359375 1.375q1.1875 1.359375 1.1875 3.921875q0 0.546875 -0.515625
+0.953125q-0.484375 0.390625 -1.234375 0.390625l-6.171875 0q0.203125 2.734375
+1.90625 3.515625q0.53125 0.234375 1.140625 0.234375q0.59375 0 1.0625
+-0.125q0.46875 -0.140625 0.890625 -0.34375q0.4375 -0.203125 0.859375
+-0.46875q0.84375 -0.53125 1.0 -0.53125q0.140625 0.015625 0.3125
+0.140625q0.140625 0.140625 0.28125 0.328125q0.296875 0.4375 0.3125 0.75l0
+0.015625q0 0.28125 -0.125 0.375q-2.15625 1.671875 -4.875 1.671875q-2.453125 0
+-3.828125 -1.71875q-1.34375 -1.65625 -1.34375 -4.546875zm7.921875 -1.0625l0
+-0.046875q0 -2.3125 -1.34375 -2.859375q-0.421875 -0.15625 -0.9375
+-0.15625q-0.515625 0 -1.078125 0.1875q-0.5625 0.1875 -1.03125 0.5625q-1.046875
+0.859375 -1.125 2.3125l5.515625 0zm7.3867188 5.515625l1.34375 0q1.625 0 2.1875
+-0.984375q0.171875 -0.3125 0.171875 -0.671875q0 -0.359375 -0.171875
+-0.625q-0.171875 -0.265625 -0.53125 -0.484375q-0.5625 -0.34375 -1.84375
+-0.734375q-1.28125 -0.390625 -1.953125 -0.671875q-0.65625 -0.296875 -1.15625
+-0.703125q-1.0 -0.84375 -1.0 -2.3125q0 -2.171875 2.421875 -2.921875q0.90625
+-0.28125 1.796875 -0.28125q0.890625 0 1.640625 0.125q0.765625 0.109375 1.375
+0.390625q1.390625 0.640625 1.390625 1.84375l0 0.0625q0 0.65625 -0.453125
+1.109375q-0.4375 0.4375 -1.140625 0.453125q-1.109375 0 -1.5 -0.953125q-0.125
+-0.296875 -0.125 -0.484375q0 -0.1875 0.140625 -0.890625l-1.15625 0q-0.953125 0
+-1.40625 0.328125q-0.828125 0.578125 -0.828125 1.265625q0 0.90625 1.640625
+1.46875l0.890625 0.3125q2.25 0.75 2.890625 1.28125q0.65625 0.515625 0.9375
+1.140625q0.296875 0.625 0.296875 1.421875q0 0.8125 -0.3125 1.390625q-0.296875
+0.59375 -0.859375 1.015625q-1.21875 0.921875 -3.140625 0.921875q-1.71875 0
+-3.078125 -0.515625q-1.0 -0.375 -1.421875 -1.078125q-0.21875 -0.359375 -0.21875
+-0.765625q0 -0.390625 0.125 -0.6875q0.140625 -0.3125 0.34375 -0.515625q0.453125
+-0.453125 1.15625 -0.453125q0.671875 0 1.140625 0.46875q0.46875 0.4375 0.46875
+1.0l0 0.015625q-0.015625 0.578125 -0.0625 0.71875zm9.1015625 4.234375q-0.265625
+0.140625 -0.484375 0.140625q-0.234375 0 -0.390625 -0.0625q-0.140625 -0.0625
+-0.265625 -0.171875q-0.28125 -0.25 -0.28125 -0.609375q2.4375 -4.140625 2.859375
+-7.84375q0.140625 -1.265625 0.15625 -2.671875l0 -0.09375q0 -1.328125 -0.140625
+-2.53125q-0.15625 -1.25 -0.5 -2.484375q-0.6875 -2.390625 -2.375 -5.25q0 -0.3125
+0.265625 -0.5625q0.28125 -0.25 0.5625 -0.25q0.46875 0 0.59375 0.109375q2.828125
+3.046875 3.734375 7.6875q0.328125 1.625 0.328125 3.421875q0 5.0625 -2.5
+9.109375q-0.734375 1.1875 -1.5625 2.0625z" fill-rule="nonzero"></path><path
+fill="#000000" fill-opacity="0.0" d="m147.41995 24.598425l135.9685 0l0
+54.582672l-135.9685 0z" fill-rule="nonzero"></path><path fill="#000000"
+d="m187.11708 54.654053q0.75 0.40625 1.875 0.40625q1.125 0 1.8125
+-0.203125q0.671875 -0.1875 1.15625 -0.5625q1.03125 -0.78125 1.046875
+-2.078125l0 -0.015625q0 -1.46875 -2.40625 -2.859375q-0.671875 -0.390625
+-1.46875 -0.78125l-1.6875 -0.859375q-3.1875 -1.6875 -3.1875 -4.5q0 -2.171875
+1.75 -3.375q1.578125 -1.0625 4.0 -1.0625l0.109375 0q1.796875 0 3.34375
+0.8125q1.859375 0.953125 1.859375 2.515625q0 0.75 -0.515625 1.296875q-0.515625
+0.515625 -1.28125 0.515625q-0.765625 0 -1.28125 -0.515625q-0.53125 -0.53125
+-0.53125 -1.296875q0 -0.625 0.34375 -1.09375q-0.6875 -0.421875 -1.65625
+-0.421875q-0.96875 0 -1.59375 0.171875q-0.640625 0.15625 -1.109375
+0.484375q-0.953125 0.671875 -0.96875 1.921875l0 0.015625q0 0.921875 1.015625
+1.65625q0.5 0.375 1.21875 0.75l1.578125 0.8125q2.59375 1.296875 3.640625
+2.390625q1.296875 1.34375 1.296875 3.234375q0 2.15625 -1.6875 3.515625q-1.6875
+1.34375 -4.28125 1.34375q-1.984375 0 -3.6875 -0.828125q-1.984375 -0.984375
+-1.984375 -2.453125q0 -0.78125 0.53125 -1.296875q0.53125 -0.53125 1.28125
+-0.53125q0.765625 0 1.28125 0.53125q0.515625 0.53125 0.515625 1.15625q0 0.625
+-0.328125 1.203125zm17.214844 -8.328125l-1.078125 0q-1.21875 0 -2.109375
+0.921875q-1.125 1.1875 -1.125 3.40625q0 3.265625 1.828125 4.125q0.5625 0.28125
+1.171875 0.28125q0.59375 0 1.0625 -0.125q0.484375 -0.125 0.953125
+-0.34375q0.46875 -0.1875 0.890625 -0.453125l0.828125 -0.5q0.046875 -0.03125
+0.15625 -0.03125q0.109375 0 0.265625 0.140625q0.171875 0.125 0.3125
+0.3125q0.3125 0.4375 0.3125 0.734375q0 0.296875 -0.140625 0.40625q-2.234375
+1.671875 -4.875 1.671875q-2.4375 0 -3.828125 -1.703125q-1.328125 -1.640625
+-1.328125 -4.515625q0 -2.8125 1.6875 -4.453125q1.59375 -1.515625 4.078125
+-1.53125q2.640625 0.015625 3.65625 1.15625q0.390625 0.453125 0.390625
+0.953125q0 0.5 -0.109375 0.796875q-0.140625 0.296875 -0.359375 0.5q-0.46875
+0.46875 -1.140625 0.46875q-0.65625 0 -1.125 -0.46875q-0.46875 -0.4375 -0.46875
+-0.890625q0 -0.4375 0.09375 -0.859375zm5.046875 4.28125q0 -1.453125 0.484375
+-2.578125q0.46875 -1.09375 1.265625 -1.84375q1.6093903 -1.5 3.9843903
+-1.515625l0.046875 0q2.171875 0 3.359375 1.375q1.1875 1.359375 1.1875
+3.921875q0 0.546875 -0.515625 0.953125q-0.484375 0.390625 -1.234375
+0.390625l-6.171875 0q0.203125 2.734375 1.90625 3.515625q0.53125 0.234375
+1.140625 0.234375q0.59375 0 1.0625 -0.125q0.46875 -0.140625 0.890625
+-0.34375q0.4375 -0.203125 0.859375 -0.46875q0.84375 -0.53125 1.0
+-0.53125q0.140625 0.015625 0.3125 0.140625q0.140625 0.140625 0.28125
+0.328125q0.296875 0.4375 0.3125 0.75l0 0.015625q0 0.28125 -0.125 0.375q-2.15625
+1.671875 -4.875 1.671875q-2.453125 0 -3.8281403 -1.71875q-1.34375 -1.65625
+-1.34375 -4.546875zm7.9218903 -1.0625l0 -0.046875q0 -2.3125 -1.34375
+-2.859375q-0.421875 -0.15625 -0.9375 -0.15625q-0.515625 0 -1.078125
+0.1875q-0.5625 0.1875 -1.03125 0.5625q-1.046875 0.859375 -1.125 2.3125l5.515625
+0zm12.386719 7.09375q-0.3125 0 -0.3125 -0.71875q0 -0.5625 0.171875 -0.75q0.1875
+-0.1875 0.3125 -0.1875q1.21875 0 1.21875 -0.5l0 -3.484375q0 -2.265625 -0.265625
+-2.96875q-0.25 -0.71875 -0.65625 -1.015625q-0.40625 -0.296875 -1.09375
+-0.296875q-1.296875 0 -3.203125 1.5l0 6.765625l1.96875 0q0.296875 0 0.296875
+0.71875q0 0.5625 -0.171875 0.75q-0.171875 0.1875 -0.3125 0.1875l-5.96875
+0q-0.3125 0 -0.3125 -0.71875q0 -0.5625 0.171875 -0.75q0.1875 -0.1875 0.3125
+-0.1875l0.125 0q1.34375 0 1.53125 -0.265625q0.0625 -0.09375 0.0625 -0.234375l0
+-7.765625l-2.0625 0q-0.359375 -0.015625 -0.453125 -0.578125q-0.015625 -0.171875
+-0.015625 -0.34375q0 -0.53125 0.296875 -0.65625q0.3125 -0.125 0.796875
+-0.21875q0.484375 -0.09375 1.015625 -0.140625q1.046875 -0.125 1.578125
+-0.125l0.046875 0q0.515625 0.015625 0.625 0.125q0.109375 0.109375 0.15625
+0.296875q0.0625 0.203125 0.0625 0.84375l0 0.21875q1.765625 -0.984375 3.21875
+-1.34375q0.46875 -0.125 1.203125 -0.125q0.703125 0 1.421875 0.3125q0.734375
+0.3125 1.15625 0.890625q0.75 1.03125 0.75 3.28125l0 5.828125l1.828125 0q0.3125
+0 0.3125 0.71875q0 0.5625 -0.1875 0.75q-0.171875 0.1875 -0.296875
+0.1875l-5.328125 0zm7.0078125 -6.03125q0 -1.453125 0.484375 -2.578125q0.46875
+-1.09375 1.265625 -1.84375q1.609375 -1.5 3.984375 -1.515625l0.046875 0q2.171875
+0 3.359375 1.375q1.1875 1.359375 1.1875 3.921875q0 0.546875 -0.515625
+0.953125q-0.484375 0.390625 -1.234375 0.390625l-6.171875 0q0.203125 2.734375
+1.90625 3.515625q0.53125 0.234375 1.140625 0.234375q0.59375 0 1.0625
+-0.125q0.46875 -0.140625 0.890625 -0.34375q0.4375 -0.203125 0.859375
+-0.46875q0.84375 -0.53125 1.0 -0.53125q0.140625 0.015625 0.3125
+0.140625q0.140625 0.140625 0.28125 0.328125q0.296875 0.4375 0.3125 0.75l0
+0.015625q0 0.28125 -0.125 0.375q-2.15625 1.671875 -4.875 1.671875q-2.453125 0
+-3.828125 -1.71875q-1.34375 -1.65625 -1.34375 -4.546875zm7.921875 -1.0625l0
+-0.046875q0 -2.3125 -1.34375 -2.859375q-0.421875 -0.15625 -0.9375
+-0.15625q-0.515625 0 -1.078125 0.1875q-0.5625 0.1875 -1.03125 0.5625q-1.046875
+0.859375 -1.125 2.3125l5.515625 0z" fill-rule="nonzero"></path><path
+fill="#000000" fill-opacity="0.0" d="m133.71216 260.5739l55.118057
+-122.19269l60.047882 27.08609l-12.547058 67.867874l-42.571 54.324814z"
+fill-rule="nonzero"></path><path stroke="#000000" stroke-width="2.0"
+stroke-linejoin="round" stroke-linecap="butt" d="m133.71216 260.5739l55.118057
+-122.19269l60.047882 27.08609l-12.547058 67.867874l-42.571 54.324814z"
+fill-rule="nonzero"></path><path fill="#000000" d="m171.84547
+238.87013q-0.6672516 0.4532318 -1.1298218 1.4787292q-0.4625702 1.0254974
+-0.43501282 2.066391q0.033996582 1.0266571 0.52685547 1.9860382q1.046875
+2.0491943 3.6611786 3.2455902q5.4123535 2.4413757 7.995346 0.40112305q0.8348236
+-0.63475037 1.2331543 -1.5178223q0.00642395 -0.01423645 -0.0078125
+-0.0206604q0.3983307 -0.8830719 0.5014343 -1.4536438q0.09527588 -0.59124756
+0.0821991 -1.0942383q-0.013092041 -0.5029907 -0.07897949 -0.9269562q-0.15101624
+-0.80519104 -0.08035278 -0.961853q0.07067871 -0.15667725 0.24887085
+-0.24771118q0.18461609 -0.105285645 0.41334534 -0.15637207q0.5130615
+-0.11138916 0.78367615 0.010681152q0.28486633 0.12849426 0.38008118
+0.41140747q0.080963135 0.27650452 0.16526794 0.811615q0.09072876 0.520874
+0.07725525 1.234726q-0.0713501 1.7162018 -0.7523651 3.2259674q-0.68743896
+1.5240021 -1.6971283 2.5084076q-1.023941 0.97798157 -2.4385529
+1.4540405q-3.0437317 1.0096588 -7.003296 -0.7763977q-4.1162415 -1.8567352
+-5.4350586 -4.9370728q-1.2121582 -2.8608093 0.22697449 -6.0512543l0.0128479
+-0.02848816q0.7581177 -1.6806793 2.199585 -2.7102814q1.5976105 -1.1477356
+2.9222107 -0.5502472q0.6836548 0.30838013 0.9701538 1.0032654q0.25801086
+0.68203735 -0.05680847 1.3799438q-0.31480408 0.6979065 -0.99684143
+0.95591736q-0.70269775 0.26582336 -1.3578796 -0.029708862q-0.64094543
+-0.28910828 -0.93052673 -0.71113586zm8.361542 -5.578766q0.32844543
+-0.0061187744 0.6845093 0.1545105q0.3560791 0.16061401 0.5753021
+0.3966217q0.19854736 0.24383545 0.31443787 0.51893616q0.21247864 0.5929413
+-0.05595398 1.226059q-0.44973755 0.9970093 -1.4791718 0.96118164q-0.32202148
+-0.008117676 -0.7208252 -0.1880188q-0.384552 -0.17346191 -0.6235962
+-0.555542q-0.23904419 -0.38208008 -0.32055664 -0.8473816q-0.0750885 -0.47953796
+-0.005508423 -1.013794q0.055343628 -0.5406952 0.18190002 -1.0492706q0.22880554
+-0.9252472 0.6785431 -1.9222565q0.4497223 -0.99702454 1.1049652
+-1.5756531q0.6474304 -0.59928894 1.3814087 -0.7824402q1.2819519 -0.2959442
+3.0195923 0.48786926l5.526306 2.4927673l0.8095093 -1.7946167q0.13491821
+-0.29910278 0.76161194 -0.016418457q0.28485107 0.12849426 0.5040741
+0.36450195q0.22564697 0.22177124 0.17982483 0.47535706q-0.09892273 0.6753082
+-0.6000366 1.7862701q-0.5075531 1.1251984 -0.6081085 1.2341156q-0.08630371
+0.11532593 -0.23098755 0.1700592q-0.28152466 0.13012695 -0.9269409
+0.07896423q0.3696289 1.7265778 -0.45915222 3.563919q-0.7452698 1.6521912
+-2.052353 2.1939087q-1.2101898 0.51686096 -2.5205536 -0.07420349l-0.02848816
+-0.0128479q-1.6379395 -0.7388458 -2.0034027 -2.3606873q-0.33642578 -1.4201813
+0.37670898 -3.0011597l1.1885681 -2.634964q-1.7946167 -0.8095093 -2.4422913
+-0.7416992q-0.64123535 0.05357361 -1.0269623 0.37667847q-0.38571167 0.32310486
+-0.6940918 1.0067749l-0.48828125 1.0824585zm8.212418 3.0359192q0.6039276
+-1.3388367 0.11392212 -3.1025696l-2.079483 -0.93800354l-1.047226
+2.3216248q-0.3854828 0.8545685 -0.28694153 1.3961182q0.17866516 0.9719238
+0.8623352 1.280304q1.2533875 0.5653839 2.0563965 -0.34083557q0.22680664
+-0.27479553 0.3809967 -0.6166382zm11.960602 -26.70578q0.13491821 -0.29910278
+0.889801 0.041412354q0.7548828 0.34049988 0.57499695 0.73931885l-2.2100983
+4.899597q-0.12849426 0.28486633 -0.78367615 -0.010681152q-0.5269928 -0.23770142
+-0.66770935 -0.60972595q-0.039093018 -0.10333252 -5.493164E-4
+-0.188797l0.0128479 -0.02848816q0.50112915 -1.1109467 0.04534912
+-1.3165436l-3.1761932 -1.4327087q-2.0652466 -0.9315796 -2.8153992
+-0.97854614q-0.75798035 -0.06765747 -1.1956329 0.18060303q-0.43766785
+0.24824524 -0.7203522 0.87493896q-0.5332489 1.1821747 0.04385376
+3.5508423l6.1672363 2.7818756l0.75167847 -1.6664276q0.12849426 -0.28486633
+0.8833771 0.055648804q0.7548828 0.34049988 0.5557251 0.7820282l-2.1908264
+4.856888q-0.12849426 0.28486633 -0.755188 0.002166748l-0.04272461
+-0.01927185q-0.49850464 -0.22485352 -0.64704895 -0.61753845q-0.039093018
+-0.10334778 -5.493164E-4 -0.188797l0.00642395 -0.014251709q0.50112915
+-1.1109619 0.04534912 -1.3165436l-3.1761932 -1.4327087q-2.0652466 -0.9315796
+-2.8153992 -0.97854614q-0.75798035 -0.06765747 -1.1956329
+0.18060303q-0.43766785 0.24824524 -0.7203522 0.87493896q-0.5332489 1.1821747
+0.05027771 3.5365906l6.1672363 2.7818909l0.8095093 -1.794632q0.12207031
+-0.27061462 0.74876404 0.012069702q0.5269928 0.23771667 0.63505554
+0.49215698q0.100234985 0.23376465 0.048843384 0.34770203l-2.4477997
+5.426605q-0.13491821 0.29910278 -0.7473755 0.022842407q-0.55548096 -0.25056458
+-0.65571594 -0.48432922q-0.093826294 -0.24801636 -0.042434692
+-0.36195374l0.05140686 -0.11395264q0.55252075 -1.2248993 0.3874817
+-1.5050354q-0.059753418 -0.09552002 -0.1879425 -0.15333557l-7.0787964
+-3.1930695l-0.848053 1.8800812q-0.14915466 0.29267883 -0.7417908
+0.16249084q-0.14886475 -0.05001831 -0.27703857 -0.10783386q-0.4985199
+-0.22486877 -0.49038696 -0.546875q0.014541626 -0.33625793 0.12825012
+-0.8163452q0.113708496 -0.48008728 0.2894287 -0.98361206q0.3164978 -1.0056915
+0.5349426 -1.4899445l0.01927185 -0.04273987q0.22625732 -0.46359253 0.3709259
+-0.5183258q0.14468384 -0.054718018 0.33486938 -0.020355225q0.2108612
+0.026550293 0.7948303 0.28996277l0.19940186 0.08995056q-0.17132568 -2.0142212
+0.09857178 -3.4866028q0.07879639 -0.47868347 0.20729065 -0.76353455q0.8930359
+-1.9797821 2.7947083 -2.1676025q-0.2232666 -2.0890656 0.07040405
+-3.8421173q0.08381653 -0.5278473 0.38578796 -1.1972656q0.30195618 -0.66941833
+0.88235474 -1.196106q0.58680725 -0.54093933 1.2872772 -0.6877899q1.2484283
+-0.2596283 3.2994232 0.66552734l5.3126526 2.396408l0.7452545
+-1.6522064zm-3.3632507 -3.1840363q-1.3246002 -0.59750366 -2.1509247
+-1.5016022q-0.8042755 -0.8770294 -1.1602936 -1.9118042q-0.70558167 -2.0838013
+0.25672913 -4.2551727l0.01927185 -0.04272461q0.8930359 -1.9797821 2.6346893
+-2.4968872q1.727417 -0.5235138 4.063278 0.5301361q0.49850464 0.22485352
+0.6568146 0.8619232q0.15690613 0.60214233 -0.151474 1.2858124l-2.5377502
+5.626007q2.5760498 0.93914795 3.9884949 -0.292099q0.43208313 -0.38789368
+0.6826477 -0.94337463q0.24414062 -0.5412445 0.322937 -1.019928q0.06454468
+-0.48510742 0.052856445 -0.9532013q-0.005264282 -0.48231506 -0.07392883
+-0.97610474q-0.1373291 -0.9875641 -0.0730896 -1.1299896q0.07206726 -0.12176514
+0.25668335 -0.22703552q0.18600464 -0.070373535 0.41474915 -0.12145996q0.520874
+-0.09072876 0.8121643 0.023529053l0.01423645 0.00642395q0.25637817 0.11564636
+0.2904358 0.26812744q0.6374054 2.6529846 -0.48049927 5.1312714q-1.008667
+2.2361603 -3.1407776 2.782837q-2.0622864 0.5438843 -4.6972504
+-0.64468384zm2.2887878 -7.6580963l-0.04272461 -0.01927185q-2.1079712 -0.9508667
+-3.1589966 0.049179077q-0.3159027 0.3203125 -0.5279083 0.790329q-0.21202087
+0.47003174 -0.2723999 1.0598755q-0.06036377 0.58984375 0.08872986
+1.1713257q0.35290527 1.3076477 1.6453857 1.9763489l2.2679138
+-5.0277863zm-0.24273682 -9.189972l0.42729187 0.19273376q0.12817383 0.05783081
+0.24993896 0.12989807q0.12818909 0.05781555 0.22789001 0.10279846q-0.57406616
+-1.7673645 -0.43598938 -3.213501q0.058135986 -0.47087097 0.2366333
+-0.9045868l0.0128479 -0.02848816q0.18630981 -0.41304016 0.49075317
+-0.6699524q0.32510376 -0.26474 0.66360474 -0.36917114q0.5879059 -0.16333008
+1.0436859 0.042251587q0.47003174 0.21202087 0.6892395 0.44802856q0.21922302
+0.23602295 0.30801392 0.53318787q0.2074585 0.64208984 0.008300781
+1.0836182q-0.19917297 0.44154358 -0.42233276 0.63227844q-0.23098755 0.1700592
+-0.4996643 0.271698q-0.58651733 0.19824219 -1.085022 -0.026611328q0.008605957
+0.7409363 0.2311554 1.2355652q0.24320984 0.4868164 0.46018982
+0.84181213l0.51438904 0.79766846l5.5975037 2.5249023l1.1692963
+-2.5922241q0.10922241 -0.24214172 0.7644043 0.053390503q0.5269928 0.23771667
+0.6272278 0.47148132q0.085998535 0.22735596 0.028182983 0.35554504l-2.8011627
+6.209961q-0.13491821 0.29910278 -0.80433655 -0.0028533936q-0.4920807
+-0.23910522 -0.640625 -0.6318054q-0.032669067 -0.11756897 0.095825195
+-0.4024353q0.13491821 -0.29910278 0.24134827 -0.61105347q0.10643005 -0.31195068
+0.14079285 -0.5021515q0.035217285 -0.34407043 -0.22116089 -0.4597168l-7.0360565
+-3.1737976l-0.848053 1.8800812q-0.18632507 0.41305542 -1.0266571
+0.033996582q-0.5269928 -0.23771667 -0.5188751 -0.5597229q-0.0061035156
+-0.32844543 0.12184143 -0.8020935q0.12013245 -0.494339 0.28941345
+-0.9836273q0.31929016 -0.93585205 0.53634644 -1.4550323l0.0385437
+-0.08546448q0.19917297 -0.44152832 0.294693 -0.50128174q0.09411621 -0.094680786
+0.21308899 -0.092437744q0.13322449 0.008666992 0.4893036 0.16929626l0.12818909
+0.05781555zm4.841614 -8.833481q0.32843018 -0.0061187744 0.6845093
+0.15449524q0.3560791 0.16062927 0.5753021 0.39663696q0.19854736 0.24383545
+0.3144226 0.51893616q0.2124939 0.5929413 -0.05593872 1.226059q-0.44973755
+0.9970093 -1.4791718 0.96118164q-0.32202148 -0.008117676 -0.7208252
+-0.1880188q-0.38456726 -0.17346191 -0.6235962 -0.555542q-0.23904419 -0.38208008
+-0.32055664 -0.8473816q-0.0750885 -0.47953796 -0.005508423
+-1.0138092q0.055343628 -0.54067993 0.18190002 -1.0492554q0.22880554 -0.9252472
+0.67852783 -1.9222717q0.44973755 -0.9970093 1.1049805 -1.5756378q0.6474304
+-0.59928894 1.3814087 -0.7824402q1.2819366 -0.2959442 3.0195923
+0.48786926l5.526291 2.4927673l0.8095093 -1.7946167q0.13491821 -0.29910278
+0.76161194 -0.016418457q0.28486633 0.12849426 0.50408936 0.36450195q0.22564697
+0.22177124 0.17982483 0.47535706q-0.09892273 0.6753082 -0.6000519
+1.7862701q-0.5075531 1.1251984 -0.60809326 1.2341156q-0.08631897 0.11532593
+-0.23098755 0.1700592q-0.28152466 0.13012695 -0.9269562 0.07896423q0.36964417
+1.7265778 -0.45913696 3.563919q-0.7452698 1.6521912 -2.0523682
+2.1939087q-1.2101746 0.51686096 -2.5205383 -0.07421875l-0.02848816
+-0.0128479q-1.6379547 -0.73883057 -2.0034027 -2.360672q-0.33642578 -1.4201965
+0.37670898 -3.0011597l1.1885681 -2.634964q-1.794632 -0.8095093 -2.4422913
+-0.7416992q-0.6412506 0.05357361 -1.0269623 0.37667847q-0.38571167 0.32310486
+-0.69410706 1.0067596l-0.488266 1.0824738zm8.212418 3.0359192q0.60391235
+-1.3388519 0.11390686 -3.1025696l-2.079483 -0.93800354l-1.047226
+2.3216095q-0.38546753 0.85458374 -0.28692627 1.3961334q0.17866516 0.9719238
+0.8623352 1.280304q1.2533875 0.56536865 2.0563965 -0.34083557q0.22680664
+-0.2748108 0.3809967 -0.6166382z" fill-rule="nonzero"></path><path
+fill="#f3f3f3" d="m276.30206 161.02286l24.433777 11.021454l0 0c13.494385
+6.0869904 9.273712 44.63011 -9.427124 86.0885c-18.700836 41.458374 -44.800217
+70.13254 -58.294617 64.04556l-24.433746 -11.021454z"
+fill-rule="nonzero"></path><path stroke="#000000" stroke-width="2.0"
+stroke-linejoin="round" stroke-linecap="butt" d="m276.30206 161.02286l24.433777
+11.021454l0 0c13.494385 6.0869904 9.273712 44.63011 -9.427124
+86.0885c-18.700836 41.458374 -44.800217 70.13254 -58.294617 64.04556l-24.433746
+-11.021454z" fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0"
+d="m213.59521 297.66257l55.910248 -123.94888l49.75757 22.444397l-55.910248
+123.94888z" fill-rule="nonzero"></path><path fill="#000000" d="m247.30704
+257.94562q0.12207031 -0.27062988 0.8769531 0.069885254q0.7548828 0.34051514
+0.5878296 0.7108154l-0.8673248 1.922821l12.918442 5.827179l2.2614746
+-5.01355l-1.2147522 -1.5249939q-0.16226196 -0.21029663 -0.02734375
+-0.5093994q0.12850952 -0.2848816 0.24188232 -0.42227173q0.10559082 -0.15808105
+0.22039795 -0.26055908q0.26309204 -0.24127197 0.4482727 -0.15774536q0.0569458
+0.0256958 0.06478882 0.0463562l2.484314 2.3204956l-4.6514587
+10.311951q-0.1156311 0.25637817 -0.7423401 -0.026306152l-0.04272461
+-0.019256592q-0.47003174 -0.21203613 -0.6342163 -0.6460571q-0.046905518
+-0.12399292 0.0687561 -0.3803711l0.0128479 -0.028503418q0.0335083 -0.0362854
+0.0592041 -0.09326172q0.10922241 -0.24212646 0.19638062 -0.51135254q0.112854004
+-0.3262024 0.1472168 -0.51638794q0.035217285 -0.3440857 -0.22116089
+-0.4597168l-12.434158 -5.6087646q-0.1709137 -0.0770874 -0.23516846
+0.065338135l-0.7838135 1.7376709q-0.13491821 0.29910278 -0.77027893
+0.14962769q-0.14884949 -0.05001831 -0.27703857 -0.1078186q-0.03491211
+0.001373291 -0.06340027 -0.011474609q-0.09970093 -0.044952393 -0.1787262
+-0.0977478q-0.09327698 -0.05923462 -0.16589355 -0.12625122q-0.1750946
+-0.18182373 -0.1315155 -0.31643677l2.8525543 -6.3239136zm13.346802
+-1.3545227q-1.3246155 -0.59750366 -2.15094 -1.5016174q-0.80426025 -0.87701416
+-1.1602783 -1.9118042q-0.7055969 -2.0838013 0.25671387 -4.2551575l0.01928711
+-0.04273987q0.8930054 -1.9797821 2.634674 -2.496872q1.727417 -0.52352905
+4.0632935 0.53012085q0.49850464 0.22486877 0.6567993 0.8619232q0.15692139
+0.6021576 -0.15145874 1.2858124l-2.5377502 5.626007q2.5760498 0.9391632
+3.9884949 -0.292099q0.43206787 -0.38789368 0.6826477 -0.94337463q0.24414062
+-0.54122925 0.322937 -1.019928q0.06454468 -0.48510742 0.052856445
+-0.95318604q-0.005279541 -0.48233032 -0.07394409 -0.97610474q-0.1373291
+-0.9875641 -0.0730896 -1.1300049q0.07208252 -0.12176514 0.25668335
+-0.22703552q0.18600464 -0.07035828 0.4147644 -0.12145996q0.520874 -0.09072876
+0.8121338 0.023529053l0.014251709 0.00642395q0.25637817 0.11564636 0.2904358
+0.2681427q0.63739014 2.6529694 -0.48049927 5.131256q-1.008667 2.2361603
+-3.1407776 2.7828217q-2.0622864 0.5439148 -4.697235 -0.6446533zm2.2887878
+-7.6581116l-0.04272461 -0.01927185q-2.1079712 -0.95085144 -3.1589966
+0.049179077q-0.31591797 0.32032776 -0.5279236 0.79034424q-0.21200562 0.47001648
+-0.2723999 1.0598602q-0.06036377 0.58984375 0.08874512 1.1713257q0.35290527
+1.3076477 1.6453857 1.9763489l2.2679138 -5.0277863zm11.559509
+-8.374359q-0.12850952 0.28485107 -0.7836914 -0.010681152q-0.51275635
+-0.23129272 -0.6129761 -0.46505737q-0.09384155 -0.24801636 -0.04244995
+-0.36195374q0.50112915 -1.1109619 0.04534912 -1.3165436l-3.176178
+-1.4327087q-2.0652466 -0.9315796 -2.8153992 -0.9785614q-0.7579651 -0.06764221
+-1.1956482 0.18060303q-0.4376526 0.2482605 -0.7203369 0.8749542q-0.53323364
+1.1821747 0.05026245 3.5365906l6.1672363 2.7818756l0.8095093
+-1.7946167q0.12207031 -0.27061462 0.7772522 0.024917603q0.51275635 0.23129272
+0.6130066 0.46505737q0.100250244 0.23376465 0.042419434 0.36195374l-2.4542236
+5.4408417q-0.12850952 0.28486633 -0.7836914 -0.010665894q-0.51272583
+-0.23129272 -0.6129761 -0.46505737q-0.09384155 -0.24801636 -0.042419434
+-0.361969l0.0513916 -0.11393738q0.55252075 -1.2248993 0.3874817
+-1.5050354q-0.059753418 -0.09552002 -0.18795776 -0.15335083l-7.078766
+-3.1930542l-0.8480835 1.8800812q-0.1619873 0.321167 -0.71328735
+0.17532349q-0.16311646 -0.056427002 -0.31976318 -0.12709045q-0.48428345
+-0.21844482 -0.47616577 -0.54045105q0.014556885 -0.33625793 0.12826538
+-0.8163452q0.113708496 -0.48008728 0.2894287 -0.9836273q0.3164978 -1.0056763
+0.5349426 -1.4899445l0.019256592 -0.04272461q0.22625732 -0.46359253 0.37094116
+-0.5183258q0.14468384 -0.054718018 0.33486938 -0.020355225q0.21084595
+0.026550293 0.7948303 0.28996277l0.19940186 0.0899353q-0.17132568 -2.014206
+0.09857178 -3.4865875q0.07879639 -0.47868347 0.38076782 -1.1481018q0.28909302
+-0.64094543 0.86950684 -1.167633q0.5868225 -0.5409241 1.287262
+-0.68777466q1.2484436 -0.2596283 3.2994385 0.6655121l5.3126526
+2.396408l0.75167847 -1.6664276q0.12850952 -0.28486633 0.7836609
+0.010665894q0.51275635 0.23129272 0.6065979 0.47930908q0.10021973 0.23376465
+0.048828125 0.3477173l-2.190796 4.8568726zm2.721405 -9.871262l0.55252075
+-1.2248993q0.6681824 -1.4812775 0.002166748 -2.3987885q-0.21420288 -0.28515625
+-0.54177856 -0.43293762q-0.3276062 -0.14776611 -0.6404114
+-0.10031128q-0.31280518 0.047454834 -0.65997314 0.28511047q-0.5446167
+0.37139893 -1.4275208 1.3787079q-0.88290405 1.007309 -1.4155273
+1.5041199q-0.5404663 0.47613525 -1.1163635 0.7648773q-1.1803284 0.564621
+-2.519165 -0.03929138q-1.9797668 -0.8930359 -1.667633 -3.4090881q0.11627197
+-0.94174194 0.4824829 -1.7536011q0.36621094 -0.8118439 0.7885437
+-1.4441223q0.41448975 -0.65293884 0.92144775 -1.0927734q1.1557617 -1.0042114
+2.252472 -0.5095062l0.05697632 0.0256958q0.59820557 0.26983643 0.82492065
+0.86920166q0.2189331 0.5786896 -0.05593872 1.226059q-0.45617676 1.0112457
+-1.4855957 0.9754181q-0.32202148 -0.008117676 -0.49295044
+-0.08522034q-0.17089844 -0.0770874 -0.7540283 -0.49438477l-0.47543335
+1.0539703q-0.39187622 0.86883545 -0.27911377 1.4167938q0.18649292 0.9925995
+0.8132019 1.2752838q0.8260803 0.3726349 2.0134277 -0.89160156l0.651062
+-0.6833496q1.6088257 -1.7426147 2.3565063 -2.108139q0.73983765 -0.38619995
+1.4252014 -0.3855896q0.691803 -0.013626099 1.4181824 0.31402588q0.7406616
+0.3340912 1.1391602 0.85665894q0.41915894 0.51475525 0.5724182
+1.2009735q0.3392334 1.4900208 -0.4510193 3.2419128q-0.70669556 1.5667267
+-1.7356567 2.5938568q-0.75302124 0.75737 -1.5674438 0.8528137q-0.417511
+0.051635742 -0.7878418 -0.11540222q-0.3560791 -0.16061401 -0.57528687
+-0.39663696q-0.22705078 -0.25668335 -0.32867432 -0.5253601q-0.2267456
+-0.59936523 0.06237793 -1.2402954q0.27624512 -0.6124573 0.89627075
+-0.8470001q0.59155273 -0.247406 1.1043091 -0.016113281l0.014251709
+0.00642395q0.52056885 0.25195312 0.62945557 0.35250854z"
+fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0"
+d="m125.19179 263.8607l33.637787 -74.55118" fill-rule="nonzero"></path><path
+stroke="#434343" stroke-width="1.0" stroke-linejoin="round"
+stroke-linecap="butt" d="m125.19179 263.8607l32.228317 -71.42735"
+fill-rule="evenodd"></path><path fill="#434343" stroke="#434343"
+stroke-width="1.0" stroke-linecap="butt" d="m157.4201 192.43333l0.5625458
+1.4875793l0.24568176 -3.2788696l-2.2958221 2.353836z"
+fill-rule="evenodd"></path><path fill="#000000" fill-opacity="0.0"
+d="m125.190956 262.63293l74.56457 33.634216" fill-rule="nonzero"></path><path
+stroke="#434343" stroke-width="1.0" stroke-linejoin="round"
+stroke-linecap="butt" d="m125.190956 262.63293l71.440605 32.225098"
+fill-rule="evenodd"></path><path fill="#434343" stroke="#434343"
+stroke-width="1.0" stroke-linecap="butt" d="m196.63156 294.858l-1.4875183
+0.5627136l3.278885 0.24533081l-2.3540802 -2.2955627z"
+fill-rule="evenodd"></path><path fill="#000000" fill-opacity="0.0"
+d="m129.13316 262.30618l-61.102364 6.1102295" fill-rule="nonzero"></path><path
+stroke="#434343" stroke-width="1.0" stroke-linejoin="round"
+stroke-linecap="butt" d="m125.36197 262.6833l-53.92109 5.3921204"
+fill-rule="evenodd"></path><path fill="#434343" stroke="#434343"
+stroke-width="1.0" stroke-linecap="butt" d="m128.63564 262.35593c0.090408325
+0.9040222 -0.56915283 1.710144 -1.4731445 1.8005371c-0.9039993 0.09039307
+-1.7101212 -0.56915283 -1.8005219 -1.473175c-0.090400696 -0.9039917 0.56915283
+-1.7101135 1.4731522 -1.8005066c0.9039993 -0.09039307 1.7101212 0.56915283
+1.8005142 1.4731445z" fill-rule="nonzero"></path><path fill="#434343"
+stroke="#434343" stroke-width="1.0" stroke-linecap="butt" d="m71.44088
+268.0754l1.007103 -1.230896l-2.9625397 1.4264526l3.1863403 0.81155396z"
+fill-rule="evenodd"></path></g></svg>
+

diff -r 9a49e5d6da9fc23bc7a2858bb5a304b9c4f5537b -r 36a295fa58ee77b259f22525200cde006b19283b doc/source/visualizing/index.rst
--- a/doc/source/visualizing/index.rst
+++ b/doc/source/visualizing/index.rst
@@ -15,6 +15,8 @@
    callbacks
    manual_plotting
    volume_rendering
+   unstructured_mesh_rendering
+   hardware_volume_rendering
    sketchfab
    mapserver
    streamlines

diff -r 9a49e5d6da9fc23bc7a2858bb5a304b9c4f5537b -r 36a295fa58ee77b259f22525200cde006b19283b doc/source/visualizing/plots.rst
--- a/doc/source/visualizing/plots.rst
+++ b/doc/source/visualizing/plots.rst
@@ -311,7 +311,8 @@
 .. _off-axis-projection-function:
 
 To avoid manually creating a camera and setting the transfer
-function, yt provides the :func:`~yt.visualization.volume_rendering.camera.off_axis_projection`
+function, yt provides the
+:func:`~yt.visualization.volume_rendering.off_axis_projection.off_axis_projection`
 function, which wraps the camera interface to create an off axis
 projection image buffer.  These images can be saved to disk or
 used in custom plots.  This snippet creates an off axis
@@ -327,7 +328,7 @@
    W = [0.02, 0.02, 0.02]
    c = [0.5, 0.5, 0.5]
    N = 512
-   image = yt.off_axis_projection(ds, c, L, W, N, "density")
+   image, sc = yt.off_axis_projection(ds, c, L, W, N, "density")
    yt.write_image(np.log10(image), "%s_offaxis_projection.png" % ds)
 
 Here, ``W`` is the width of the projection in the x, y, *and* z

diff -r 9a49e5d6da9fc23bc7a2858bb5a304b9c4f5537b -r 36a295fa58ee77b259f22525200cde006b19283b doc/source/visualizing/transfer_function_helper.rst
--- /dev/null
+++ b/doc/source/visualizing/transfer_function_helper.rst
@@ -0,0 +1,6 @@
+.. _transfer-function-helper-tutorial:
+
+Transfer Function Helper Tutorial
+=================================
+
+.. notebook:: TransferFunctionHelper_Tutorial.ipynb

diff -r 9a49e5d6da9fc23bc7a2858bb5a304b9c4f5537b -r 36a295fa58ee77b259f22525200cde006b19283b doc/source/visualizing/unstructured_mesh_rendering.rst
--- /dev/null
+++ b/doc/source/visualizing/unstructured_mesh_rendering.rst
@@ -0,0 +1,174 @@
+.. _unstructured_mesh_rendering:
+
+Unstructured Mesh Rendering
+===========================
+
+Beginning with version 3.3, yt has the ability to volume render unstructured
+meshes from, for example, finite element calculations. In order to use this
+capability, a few additional dependencies are required beyond those you get
+when you run the install script. First, `embree <https://embree.github.io>`
+(a fast software ray-tracing library from Intel) must be installed, either
+by compiling from source or by using one of the pre-built binaries available
+at Embree's `downloads <https://embree.github.io/downloads.html>` page. Once
+Embree is installed, you must also create a symlink next to the library. For
+example, if the libraries were installed at /usr/local/lib/, you must do
+
+.. code-block:: bash
+
+    sudo ln -s /usr/local/lib/libembree.2.6.1.dylib /usr/local/lib/libembree.so
+
+Second, the python bindings for embree (called 
+`pyembree <https://github.com/scopatz/pyembree>`) must also be installed. To
+do so, first obtain a copy, by .e.g. cloning the repo:
+
+.. code-block:: bash
+
+    git clone https://github.com/scopatz/pyembree
+
+To install, navigate to the root directory and run the setup script:
+
+.. code-block:: bash
+
+    python setup.py develop
+
+If Embree was installed to some location that is not in your path by default,
+you will need to pass in CFLAGS and LDFLAGS to the setup.py script. For example,
+the Mac OS package installer puts the installation at /opt/local/ instead of 
+usr/local. To account for this, you would do:
+
+.. code-block:: bash
+
+    CFLAGS='-I/opt/local/include' LDFLAGS='-L/opt/local/lib' python setup.py develop
+
+You must also use these flags when building any part of yt that links against
+pyembree.
+
+Once the pre-requisites are installed, unstructured mesh data can be rendered
+much like any other dataset. In particular, a new type of 
+:class:`~yt.visualization.volume_rendering.render_source.RenderSource` object
+has been defined, called the 
+:class:`~yt.visualization.volume_rendering.render_source.MeshSource`, that
+represents the unstructured mesh data that will be rendered. The user creates 
+this object, and also defines a
+:class:`~yt.visualization.volume_rendering.camera.Camera` 
+that specifies your viewpoint into the scene. When 
+:class:`~yt.visualization.volume_rendering.render_source.RenderSource` is called,
+a set of rays are cast at the source. Each time a ray strikes the source mesh,
+the data is sampled at the intersection point at the resulting value gets 
+saved into an image.
+
+See below for examples. First, here is an example of rendering a hexahedral mesh.
+
+.. python-script::
+
+   import yt
+   import pylab as plt
+   from yt.visualization.volume_rendering.render_source import MeshSource
+   from yt.visualization.volume_rendering.camera import Camera
+   from yt.utilities.exodusII_reader import get_data
+
+   # load the data
+   coords, connectivity, data = get_data("MOOSE_sample_data/out.e-s010")
+   mesh_id = 0
+   field_name = ('gas', 'diffused')
+   ds = yt.load_unstructured_mesh(data[mesh_id], connectivity[mesh_id], coords[mesh_id])
+
+   # create the RenderSource
+   ms = MeshSource(ds, field_name)
+
+   # set up camera
+   cam = Camera(ds)
+   camera_position = ds.arr([-3.0, 3.0, -3.0], 'code_length')
+   north_vector = ds.arr([0.0, 1.0, 0.0], 'dimensionless')
+   cam.resolution = (800, 800)
+   cam.set_position(camera_position, north_vector)
+
+   # make the image
+   im = ms.render(cam)
+
+   # plot and save
+   plt.imshow(im, cmap='Eos A', origin='lower', vmin=0, vmax=2.0)
+   plt.gca().axes.get_xaxis().set_visible(False)
+   plt.gca().axes.get_yaxis().set_visible(False)
+   cb = plt.colorbar()
+   cb.set_label(field_name[1])
+   plt.savefig('hex_mesh_render.png')
+
+Next, here is an example of rendering a dataset with tetrahedral mesh elements.
+
+.. python-script::
+
+   import yt
+   import pylab as plt
+   from yt.visualization.volume_rendering.render_source import MeshSource
+   from yt.visualization.volume_rendering.camera import Camera
+   from yt.utilities.exodusII_reader import get_data
+
+   # load the data
+   filename = "MOOSE_sample_data/high_order_elems_tet4_refine_out.e"
+   coords, connectivity, data = get_data(filename)
+   mesh_id = 0
+   field_name = ('gas', 'u')
+   ds = yt.load_unstructured_mesh(data[mesh_id], connectivity[mesh_id], coords[mesh_id])
+
+   # create the RenderSource
+   ms = MeshSource(ds, field_name)
+
+   # set up camera
+   cam = Camera(ds)
+   camera_position = ds.arr([3.0, 3.0, 3.0], 'code_length')
+   cam.set_width(ds.arr([2.0, 2.0, 2.0], 'code_length'))
+   north_vector = ds.arr([0.0, 1.0, 0.0], 'dimensionless')
+   cam.resolution = (800, 800)
+   cam.set_position(camera_position, north_vector)
+
+   # make the image
+   im = ms.render(cam)
+
+   # plot and save
+   plt.imshow(im, cmap='Eos A', origin='lower', vmin=0.0, vmax=1.0)
+   plt.gca().axes.get_xaxis().set_visible(False)
+   plt.gca().axes.get_yaxis().set_visible(False)
+   cb = plt.colorbar()
+   cb.set_label(field_name[1])
+   plt.savefig('tet_mesh_render.png')
+
+Finally, here is a script that creates frames of a movie. It calls the rotate()
+method 300 times, saving a new image to the disk each time.
+
+.. code-block:: python
+
+   import yt
+   import pylab as plt
+   from yt.visualization.volume_rendering.render_source import MeshSource
+   from yt.visualization.volume_rendering.camera import Camera
+   from yt.utilities.exodusII_reader import get_data
+
+   # load dataset
+   coords, connectivity, data = get_data("MOOSE_sample_data/out.e-s010")
+   mesh_id = 0
+   field_name = ('gas', 'diffused')
+   ds = yt.load_unstructured_mesh(data[mesh_id], connectivity[mesh_id], coords[mesh_id])
+
+   # create the RenderSource
+   ms = MeshSource(ds, field_name)
+
+   # set up camera
+   cam = Camera(ds)
+   camera_position = ds.arr([-3.0, 3.0, -3.0], 'code_length')
+   north_vector = ds.arr([0.0, 1.0, 0.0], 'dimensionless')
+   cam.set_position(camera_position, north_vector)
+   cam.steady_north = True
+
+   # make movie frames
+   num_frames = 301
+   for i in range(num_frames):
+       cam.rotate(2.0*np.pi/num_frames)
+       im = ms.render(cam)
+       plt.imshow(im, cmap='Eos A', origin='lower',vmin=0.0, vmax=2.0)
+       plt.gca().axes.get_xaxis().set_visible(False)
+       plt.gca().axes.get_yaxis().set_visible(False)
+       cb = plt.colorbar()
+       cb.set_label('diffused')
+       plt.savefig('movie_frames/surface_render_%.4d.png' % i)
+       plt.clf()

This diff is so big that we needed to truncate the remainder.

https://bitbucket.org/yt_analysis/yt/commits/fdf41578efe6/
Changeset:   fdf41578efe6
Branch:      yt
User:        atmyers
Date:        2015-11-09 19:30:34+00:00
Summary:     merging
Affected #:  19 files

diff -r 36a295fa58ee77b259f22525200cde006b19283b -r fdf41578efe618b66c30cb258b55a14fbfb12f3f doc/source/cookbook/complex_plots.rst
--- a/doc/source/cookbook/complex_plots.rst
+++ b/doc/source/cookbook/complex_plots.rst
@@ -209,6 +209,8 @@
 
 .. yt_cookbook:: custom_camera_volume_rendering.py
 
+.. _cookbook-custom-transfer-function:
+
 Volume Rendering with a Custom Transfer Function
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 

diff -r 36a295fa58ee77b259f22525200cde006b19283b -r fdf41578efe618b66c30cb258b55a14fbfb12f3f doc/source/cookbook/index.rst
--- a/doc/source/cookbook/index.rst
+++ b/doc/source/cookbook/index.rst
@@ -50,3 +50,4 @@
    fits_xray_images
    tipsy_notebook
    halo_analysis_example
+   ../visualizing/volume_rendering_tutorial

diff -r 36a295fa58ee77b259f22525200cde006b19283b -r fdf41578efe618b66c30cb258b55a14fbfb12f3f doc/source/cookbook/various_lens.py
--- a/doc/source/cookbook/various_lens.py
+++ b/doc/source/cookbook/various_lens.py
@@ -95,9 +95,8 @@
 cam.position = ds.arr([0.4, 0.5, 0.5], 'code_length')
 cam.switch_orientation(normal_vector=normal_vector,
                        north_vector=north_vector)
-# In (stereo)spherical camera, width[0] specifies the radius of the sphere (the
-# depth of your line of sight), while width[1] or width[2] are not used.
-cam.set_width(ds.domain_width * 0.5)
+# In (stereo)spherical camera, camera width is not used since the entire volume
+# will be rendered
 sc.camera = cam
 sc.add_source(vol)
 sc.render()
@@ -111,7 +110,8 @@
 cam.position = ds.arr([0.4, 0.5, 0.5], 'code_length')
 cam.switch_orientation(normal_vector=normal_vector,
                        north_vector=north_vector)
-cam.set_width(ds.domain_width * 0.5)
+# In (stereo)spherical camera, camera width is not used since the entire volume
+# will be rendered
 # Set the distance between left-eye and right-eye.
 cam.lens.disparity = ds.domain_width[0] * 1.e-3
 sc.camera = cam

diff -r 36a295fa58ee77b259f22525200cde006b19283b -r fdf41578efe618b66c30cb258b55a14fbfb12f3f doc/source/reference/api/api.rst
--- a/doc/source/reference/api/api.rst
+++ b/doc/source/reference/api/api.rst
@@ -584,7 +584,8 @@
 
 See also :ref:`volume_rendering`.
 
-Here are the primary entry points:
+Here are the primary entry points and the main classes involved in the 
+Scene infrastructure:
 
 .. autosummary::
    :toctree: generated/
@@ -594,43 +595,46 @@
    ~yt.visualization.volume_rendering.off_axis_projection.off_axis_projection
    ~yt.visualization.volume_rendering.scene.Scene
    ~yt.visualization.volume_rendering.camera.Camera
-   ~yt.visualization.volume_rendering.lens.Lens
-   ~yt.visualization.volume_rendering.render_source.RenderSource
+   ~yt.utilities.amr_kdtree.amr_kdtree.AMRKDTree
 
-These objects set up the way the image looks:
+The different kinds of sources:
 
 .. autosummary::
    :toctree: generated/
 
-   ~yt.visualization.volume_rendering.transfer_functions.ColorTransferFunction
-   ~yt.visualization.volume_rendering.transfer_functions.MultiVariateTransferFunction
-   ~yt.visualization.volume_rendering.transfer_functions.PlanckTransferFunction
-   ~yt.visualization.volume_rendering.transfer_functions.ProjectionTransferFunction
-   ~yt.visualization.volume_rendering.transfer_functions.TransferFunction
+   ~yt.visualization.volume_rendering.render_source.RenderSource
+   ~yt.visualization.volume_rendering.render_source.VolumeSource
+   ~yt.visualization.volume_rendering.render_source.PointSource
+   ~yt.visualization.volume_rendering.render_source.LineSource
+   ~yt.visualization.volume_rendering.render_source.BoxSource
+   ~yt.visualization.volume_rendering.render_source.GridSource
+   ~yt.visualization.volume_rendering.render_source.CoordinateVectorSource
+   ~yt.visualization.volume_rendering.render_source.MeshSource
 
-There are also advanced objects for particular use cases:
+The different kinds of transfer functions:
 
 .. autosummary::
    :toctree: generated/
 
-   ~yt.visualization.volume_rendering.camera.FisheyeCamera
-   ~yt.visualization.volume_rendering.camera.MosaicCamera
-   ~yt.visualization.volume_rendering.camera.PerspectiveCamera
-   ~yt.utilities.amr_kdtree.amr_kdtree.AMRKDTree
-   ~yt.visualization.volume_rendering.camera.StereoPairCamera
-
-Additional sources can be added to a scene:
+   ~yt.visualization.volume_rendering.transfer_functions.TransferFunction
+   ~yt.visualization.volume_rendering.transfer_functions.ColorTransferFunction
+   ~yt.visualization.volume_rendering.transfer_functions.ProjectionTransferFunction
+   ~yt.visualization.volume_rendering.transfer_functions.PlanckTransferFunction
+   ~yt.visualization.volume_rendering.transfer_functions.MultiVariateTransferFunction
+   ~yt.visualization.volume_rendering.transfer_function_helper.TransferFunctionHelper
+ 
+The different kinds of lenses:
 
 .. autosummary::
    :toctree: generated/
 
-   ~yt.visualization.volume_rendering.api.VolumeSource
-   ~yt.visualization.volume_rendering.api.PointSource
-   ~yt.visualization.volume_rendering.api.LineSource
-   ~yt.visualization.volume_rendering.api.BoxSource
-   ~yt.visualization.volume_rendering.api.GridSource
-   ~yt.visualization.volume_rendering.api.CoordinateVectorSource
-   ~yt.visualization.volume_rendering.render_source.MeshSource
+   ~yt.visualization.volume_rendering.lens.Lens
+   ~yt.visualization.volume_rendering.lens.PlaneParallelLens
+   ~yt.visualization.volume_rendering.lens.PerspectiveLens
+   ~yt.visualization.volume_rendering.lens.StereoPerspectiveLens
+   ~yt.visualization.volume_rendering.lens.FisheyeLens
+   ~yt.visualization.volume_rendering.lens.SphericalLens
+   ~yt.visualization.volume_rendering.lens.StereoSphericalLens
 
 Streamlining
 ^^^^^^^^^^^^

diff -r 36a295fa58ee77b259f22525200cde006b19283b -r fdf41578efe618b66c30cb258b55a14fbfb12f3f doc/source/visualizing/Volume_Rendering_Tutorial.ipynb
--- /dev/null
+++ b/doc/source/visualizing/Volume_Rendering_Tutorial.ipynb
@@ -0,0 +1,272 @@
+{
+ "metadata": {
+  "name": "",
+  "signature": "sha256:16b0b0137841594b135cb8e07f6029e0cd646630816929435e584cbbf10555b4"
+ },
+ "nbformat": 3,
+ "nbformat_minor": 0,
+ "worksheets": [
+  {
+   "cells": [
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "This notebook shows how to use the new (in version 3.3) Scene interface to create custom volume renderings. To begin, we load up a dataset and use the yt.create_scene method to set up a basic Scene. We store the Scene in a variable called 'sc' and render the default ('gas', 'density') field."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "import yt\n",
+      "import numpy as np\n",
+      "from yt.visualization.volume_rendering.transfer_function_helper import TransferFunctionHelper\n",
+      "from yt.visualization.volume_rendering.api import Scene, Camera, VolumeSource\n",
+      "\n",
+      "ds = yt.load(\"IsolatedGalaxy/galaxy0030/galaxy0030\")\n",
+      "sc = yt.create_scene(ds)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": []
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "Now we can look at some information about the Scene we just created using the python print keyword:"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "print sc"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": []
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "This prints out information about the Sources, Camera, and Lens associated with this Scene. Each of these can also be printed individually. For example, to print only the information about the first (and currently, only) Source, we can do:"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "print sc.get_source(0)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": []
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "We can see that the yt.create_source has created a VolumeSource with default values for the center, bounds, and transfer function. Now, let's see what this Scene looks like. In the notebook, we can do this by calling sc.show(). "
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "sc.show()"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": []
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "That looks okay, but it's a little too zoomed-out. To fix this, let's modify the Camera associated with our Scene. This next bit of code will zoom in the camera (i.e. decrease the width of the view) by a factor of 3."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "sc.camera.zoom(3.0)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": []
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "Now when we print the Scene, we see that the Camera width has decreased by a factor of 3:"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "print sc"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": []
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "To see what this looks like, we re-render the image and display the scene again. Note that we don't actually have to call sc.show() here - we can just have Ipython evaluate the Scene and that will display it automatically."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "sc.render()\n",
+      "sc"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": []
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "That's better! The image looks a little washed-out though, so we use the sigma_clip argument to sc.show() to improve the contrast:"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "sc.show(sigma_clip=4.0)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": []
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "Next, we demonstrate how to change the mapping between the field values and the colors in the image. We use the TransferFunctionHelper to create a new transfer function using the \"gist_rainbow\" colormap, and then re-create the image as follows:"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "# Set up a custom transfer function using the TransferFunctionHelper. \n",
+      "# We use 10 Gaussians evenly spaced logarithmically between the min and max\n",
+      "# field values.\n",
+      "tfh = TransferFunctionHelper(ds)\n",
+      "tfh.set_field('density')\n",
+      "tfh.set_log(True)\n",
+      "tfh.set_bounds()\n",
+      "tfh.build_transfer_function()\n",
+      "tfh.tf.add_layers(10, colormap='gist_rainbow')\n",
+      "\n",
+      "# Grab the first render source and set it to use the new transfer function\n",
+      "render_source = sc.get_source(0)\n",
+      "render_source.transfer_function = tfh.tf\n",
+      "\n",
+      "sc.render()\n",
+      "sc.show(sigma_clip=4.0)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": []
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "Now, let's try using a different lens type. We can give a sense of depth to the image by using the perspective lens. To do, we create a new Camera below. We also demonstrate how to switch the camera to a new position and orientation."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "cam = Camera(ds, lens_type='perspective')\n",
+      "\n",
+      "# Standing at (x=0.05, y=0.5, z=0.5), we look at the area of x>0.05 (with some open angle\n",
+      "# specified by camera width) along the positive x direction.\n",
+      "cam.position = ds.arr([0.05, 0.5, 0.5], 'code_length')\n",
+      "\n",
+      "normal_vector = [1., 0., 0.]\n",
+      "north_vector = [0., 0., 1.]\n",
+      "cam.switch_orientation(normal_vector=normal_vector,\n",
+      "                       north_vector=north_vector)\n",
+      "\n",
+      "# The width determines the opening angle\n",
+      "cam.set_width(ds.domain_width * 0.5)\n",
+      "\n",
+      "sc.camera = cam\n",
+      "print sc.camera"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": []
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "The resulting image looks like:"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "sc.render()\n",
+      "sc.show(sigma_clip=4.0)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": []
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "Finally, the next cell restores the lens and the transfer function to the defaults, moves the camera, and adds an opaque source  that shows the axes of the simulation coordinate system."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "# set the lens type back to plane-parallel\n",
+      "sc.camera.set_lens('plane-parallel')\n",
+      "\n",
+      "# move the camera to the left edge of the domain\n",
+      "sc.camera.set_position(ds.domain_left_edge)\n",
+      "sc.camera.switch_orientation()\n",
+      "\n",
+      "# reset the transfer function to the default\n",
+      "render_source = sc.get_source(0)\n",
+      "render_source.build_default_transfer_function()\n",
+      "\n",
+      "# add an opaque source to the scene\n",
+      "sc.annotate_axes()\n",
+      "\n",
+      "sc.render()\n",
+      "sc.show(sigma_clip=4.0)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": []
+    }
+   ],
+   "metadata": {}
+  }
+ ]
+}
\ No newline at end of file

diff -r 36a295fa58ee77b259f22525200cde006b19283b -r fdf41578efe618b66c30cb258b55a14fbfb12f3f doc/source/visualizing/volume_rendering.rst
--- a/doc/source/visualizing/volume_rendering.rst
+++ b/doc/source/visualizing/volume_rendering.rst
@@ -3,10 +3,18 @@
 3D Visualization and Volume Rendering
 =====================================
 
-yt has the ability to create 3D visualizations, using a process known as volume
-rendering.  Currently all of the rendering capabilities are implemented in
-software, requiring no specialized hardware. Optimized versions implemented
-with OpenGL and utilizing graphics processors are being actively developed.
+yt has the ability to create 3D visualizations using a process known as *volume
+rendering* (oftentimes abbreviated VR).  This volume rendering code differs 
+from the standard yt infrastructure for generating :ref:`simple-inspection` 
+in that it evaluates the radiative transfer equations through the volume with 
+user-defined transfer functions for each ray.  Thus it can accommodate both
+opaque and transparent structures appropriately.  Currently all of the 
+rendering capabilities are implemented in software, requiring no specialized 
+hardware. Optimized versions implemented with OpenGL and utilizing graphics 
+processors are being actively developed.
+
+Volume Rendering Introduction
+-----------------------------
 
 Constructing a 3D visualization is a process of describing the "scene" that
 will be rendered.  This includes the location of the viewing point (i.e., where
@@ -15,7 +23,7 @@
 on) and the components that will be rendered (render "sources," such as volume
 elements, lines, annotations, and opaque surfaces).  The 3D plotting
 infrastructure then develops a resultant image from this scene, which can be
-saved to a file or viewed inline.
+saved to a file or viewed inline.  
 
 By constructing the scene in this programmatic way, full control can be had
 over each component in the scene as well as the method by which the scene is
@@ -23,54 +31,327 @@
 as grid or continent lines, and then to render a production-quality
 visualization.  By changing the "lens" used, a single camera path can output
 images suitable for planetarium domes, immersive and head tracking systems
-(such as the Occulus Rift or recent "spherical" movie viewers such as the
-mobile YouTube app), as well as standard screens.
+(such as the Oculus Rift or recent "spherical" movie viewers such as the
+mobile YouTube app), as well as standard screens.  
 
 .. image:: _images/scene_diagram.svg
    :width: 50%
    :align: center
    :alt: Diagram of a 3D Scene
 
-In versions of yt prior to 3.3, the only volume rendering interface accessible
-was through the "camera" object.  This presented a number of problems,
-principle of which was the inability to describe new scene elements or to
-develop complex visualizations that were independent of the specific elements
-being rendered.  The new "scene" based interface present in yt 3.3 and beyond
-enables both more complex visualizations to be constructed as well as a new,
-more intuitive interface for very simple 3D visualizations.
+.. _scene-description:
+
+Volume Rendering Components
+---------------------------
+
+The Scene class and its subcomponents are organized as follows.  Indented
+objects *hang* off of their parent object.  
+
+* :ref:`Scene <scene>` - container object describing a volume and its contents
+    * :ref:`Sources <render-sources>` - objects to be rendered
+        * :ref:`VolumeSource <volume-sources>` - simulation volume tied to a dataset
+            * :ref:`TransferFunction <transfer_functions>` - mapping of simulation field values to color, brightness, and transparency
+        * :ref:`OpaqueSource <opaque-sources>` - Opaque structures like lines, dots, etc.
+        * :ref:`Annotations <volume_rendering_annotations>` - Annotated structures like grid cells, simulation boundaries, etc.
+    * :ref:`Camera <camera>` - object for rendering; consists of a location, focus, orientation, and resolution
+        * :ref:`Lens <lenses>` - object describing method for distributing rays through Sources
+
+.. _scene:
+
+Scene
+^^^^^
+
+The :class:`~yt.visualization.volume_rendering.scene.Scene`
+is the container class which encompasses the whole of the volume
+rendering interface.  At its base level, it describes an infinite volume, 
+with a series of 
+:class:`~yt.visualization.volume_rendering.render_source.RenderSource` objects
+hanging off of it that describe the contents
+of that volume.  It also contains a 
+:class:`~yt.visualization.volume_rendering.camera.Camera` for rendering that
+volume..  All of its classes can be
+accessed and modified as properties hanging off of the scene.
+The scene's most important functions are 
+:meth:`~yt.visualization.volume_rendering.scene.Scene.render` for 
+casting rays through the scene and 
+:meth:`~yt.visualization.volume_rendering.scene.Scene.save` for saving the
+resulting rendered image to disk.
+
+The easiest way to create a scene with sensible defaults is to use the 
+functions:
+:func:`~yt.visualization.volume_rendering.volume_rendering.create_scene` 
+(creates the scene) or 
+:func:`~yt.visualization.volume_rendering.volume_rendering.volume_render`
+(creates the scene and then triggers ray tracing to produce an image).
+See the :ref:`annotated-vr-example` for details.
+
+.. _render-sources:
+
+RenderSources
+^^^^^^^^^^^^^
+
+:class:`~yt.visualization.volume_rendering.render_source.RenderSource` objects
+comprise the contents of what is actually *rendered*.  One can add several 
+different RenderSources to a Scene and the ray-tracing step will pass rays
+through all of them to produce the final rendered image.  
+
+.. _volume-sources:
+
+VolumeSources
++++++++++++++
+
+:class:`~yt.visualization.volume_rendering.render_source.VolumeSource` objects
+are 3D :ref:`geometric-objects` of individual datasets placed into the scene 
+for rendering.  Each VolumeSource requires a 
+:ref:`TransferFunction <transfer_functions>` to describe how the fields in 
+the VolumeSource dataset produce different colors and brightnesses in the
+resulting image.
+
+.. _opaque-sources:
+
+OpaqueSources
++++++++++++++
+
+In addition to semi-transparent objects, fully opaque structures can be added 
+to a scene as 
+:class:`~yt.visualization.volume_rendering.render_source.OpaqueSource` objects 
+including 
+:class:`~yt.visualization.volume_rendering.render_source.LineSource` objects 
+and 
+:class:`~yt.visualization.volume_rendering.render_source.PointSource` objects.
+These are useful if you want to annotate locations or particles in an image, 
+or if you want to draw lines connecting different regions or
+vertices.  For instance, lines can be used to draw outlines of regions or
+continents.
+
+.. _volume_rendering_annotations:
+
+Annotations
++++++++++++
+
+Similar to OpaqueSources, annotations enable the user to highlight 
+certain information with opaque structures.  Examples include 
+:class:`~yt.visualization.volume_rendering.api.BoxSource`,
+:class:`~yt.visualization.volume_rendering.api.GridSource`, and
+:class:`~yt.visualization.volume_rendering.api.CoordinateVectorSource`.  These
+annotations will operate in data space and can draw boxes, grid information,
+and also provide a vector orientation within the image.
+
+For example scripts using these features, 
+see :ref:`cookbook-volume_rendering_annotations`.
+
+.. _transfer_functions:
+
+Transfer Functions
+^^^^^^^^^^^^^^^^^^
+
+A transfer function describes how rays that pass through the domain of a
+:class:`~yt.visualization.volume_rendering.render_source.VolumeSource` are
+mapped from simulation field values to color, brightness, and opacity in the
+resulting rendered image.  A transfer function consists of an array over 
+the x and y dimensions.  The x dimension typically represents field values in 
+your underlying dataset to which you want your rendering to be sensitive (e.g. 
+density from 1e20 to 1e23).  The y dimension consists of 4 channels for red, 
+green, blue, and alpha (opacity).  A transfer function starts with all zeros 
+for its y dimension values, implying that rays traversing the VolumeSource 
+will not show up at all in the final image.  However, you can add features to 
+the transfer function that will highlight certain field values in your 
+rendering.
+
+.. _transfer-function-helper:
+
+TransferFunctionHelper
+++++++++++++++++++++++
+
+Because good transfer functions can be difficult to generate, the 
+:class:`~yt.visualization.volume_rendering.transfer_function_helper.TransferFunctionHelper`
+exists in order to help create and modify transfer functions with smart 
+defaults for your datasets.  To see a full example on how to use this 
+interface, follow the annotated :ref:`transfer-function-helper-tutorial`.
+
+Color Transfer Functions
+++++++++++++++++++++++++
+
+A :class:`~yt.visualization.volume_rendering.transfer_functions.ColorTransferFunction`
+is the standard way to map dataset field values to colors, brightnesses, 
+and opacities in the rendered rays.  One can add discrete features to the
+transfer function, which will render isocontours in the field data and 
+works well for visualizing nested structures in a simulation.  Alternatively,
+one can add continuous features to the transfer function, which tends to 
+produce better results for most datasets.
+
+In order to modify a 
+:class:`~yt.visualization.volume_rendering.transfer_functions.ColorTransferFunction`
+use 
+:meth:`~yt.visualization.volume_rendering.transfer_functions.ColorTransferFunction.add_layers`,
+which will add evenly spaced isocontours along the transfer
+function; use 
+:meth:`~yt.visualization.volume_rendering.transfer_functions.ColorTransferFunction.sample_colormap`,
+which will sample a colormap at a given value; 
+use
+:meth:`~yt.visualization.volume_rendering.transfer_functions.ColorTransferFunction.add_gaussian`,
+which will allow you to specify the colors directly on the transfer function,
+and use 
+:meth:`~yt.visualization.volume_rendering.transfer_functions.ColorTransferFunction.map_to_colormap`,
+where you can map a segment of the transfer function space to an entire
+colormap at a single alpha value.  
+
+See :ref:`cookbook-custom-transfer-function` for an example usage.
+
+Projection Transfer Function
+++++++++++++++++++++++++++++
+
+This is designed to allow you to generate projections like what you obtain
+from the standard :ref:`projection-plots`, and it forms the basis of 
+:ref:`off-axis-projections`.  See :ref:`cookbook-offaxis_projection` for a 
+simple example.  Note that the integration here is scaled to a width of 1.0; 
+this means that if you want to apply a colorbar, you will have to multiply by 
+the integration width (specified when you initialize the volume renderer) in 
+whatever units are appropriate.
+
+Planck Transfer Function
+++++++++++++++++++++++++
+
+This transfer function is designed to apply a semi-realistic color field based
+on temperature, emission weighted by density, and approximate scattering based
+on the density.  This class is currently under-documented, and it may be best
+to examine the source code to use it.
+
+More Complicated Transfer Functions
++++++++++++++++++++++++++++++++++++
+
+For more complicated transfer functions, you can use the
+:class:`~yt.visualization.volume_rendering.transfer_functions.MultiVariateTransferFunction`
+object.  This allows for a set of weightings, linkages and so on.
+All of the information about how all transfer functions are used and values are
+extracted is contained in the sourcefile ``utilities/lib/grid_traversal.pyx``.
+For more information on how the transfer function is actually applied, look
+over the source code there.
+
+.. _camera:
+
+Camera
+^^^^^^
+
+The :class:`~yt.visualization.volume_rendering.camera.Camera` object
+is what it sounds like, a camera within the Scene.  It possesses the 
+quantities:
+ * :meth:`~yt.visualization.volume_rendering.camera.Camera.position` - the position of the camera in scene-space
+ * :meth:`~yt.visualization.volume_rendering.camera.Camera.width` - the width of the plane the camera can see
+ * :meth:`~yt.visualization.volume_rendering.camera.Camera.focus` - the point in space the camera is looking at
+ * :meth:`~yt.visualization.volume_rendering.camera.Camera.resolution` - the image resolution
+ * ``north_vector`` - a vector defining the "up" direction in an image
+ * :ref:`lens <lenses>` - an object controlling how rays traverse the Scene
+
+.. _camera_movement:
+
+Moving and Orienting the Camera
++++++++++++++++++++++++++++++++
+
+There are multiple ways to manipulate the camera viewpoint and orientation.
+One can set the properties listed above explicitly, or one can use the
+:class:`~yt.visualization.volume_rendering.camera.Camera` helper methods.  
+In either case, any change triggers an update of all of the other properties.
+Note that the camera exists in a right-handed coordinate system centered on
+the camera.
+
+Rotation-related methods
+ * :meth:`~yt.visualization.volume_rendering.camera.Camera.pitch` - rotate about the lateral axis
+ * :meth:`~yt.visualization.volume_rendering.camera.Camera.yaw` - rotate about the vertical axis (i.e. ``north_vector``)
+ * :meth:`~yt.visualization.volume_rendering.camera.Camera.roll` - rotate about the longitudinal axis (i.e. ``normal_vector``)
+ * :meth:`~yt.visualization.volume_rendering.camera.Camera.rotate` - rotate about an arbitrary axis
+ * :meth:`~yt.visualization.volume_rendering.camera.Camera.iter_rotate` - iteratively rotate about an arbitrary axis
+
+For the rotation methods, the camera pivots around the ``rot_center`` rotation 
+center.  By default, this is the camera position, which means that the 
+camera doesn't change its position at all, it just changes its orientation.  
+
+Zoom-related methods
+ * :meth:`~yt.visualization.volume_rendering.camera.Camera.set_width` - change the width of the FOV
+ * :meth:`~yt.visualization.volume_rendering.camera.Camera.zoom` - change the width of the FOV
+ * :meth:`~yt.visualization.volume_rendering.camera.Camera.iter_zoom` - iteratively change the width of the FOV
+
+Perhaps counterintuitively, the camera does not get closer to the focus
+during a zoom; it simply reduces the width of the field of view.
+
+Translation-related methods
+ * :meth:`~yt.visualization.volume_rendering.camera.Camera.set_position` - change the location of the camera keeping the focus fixed
+ * :meth:`~yt.visualization.volume_rendering.camera.Camera.iter_move` - iteratively change the location of the camera keeping the focus fixed
+
+The iterative methods provide iteration over a series of changes in the 
+position or orientation of the camera.  These can be used within a loop.
+For an example on how to use all of these camera movement functions, see 
+:ref:`cookbook-camera_movement`.  
+
+.. _lenses:
+
+Camera Lenses
+^^^^^^^^^^^^^
+
+Cameras possess :class:`~yt.visualization.volume_rendering.lens.Lens` objects, 
+which control the geometric path in which rays travel to the camera.  These
+lenses can be swapped in and out of an existing camera to produce different
+views of the same Scene.  For a full demonstration of a Scene object 
+rendered with different lenses, see :ref:`cookbook-various_lens`.
+
+Plane Parallel
+++++++++++++++
+
+The :class:`~yt.visualization.volume_rendering.lens.PlaneParallelLens` is the
+standard lens type used for orthographic projections.  All rays emerge 
+parallel to each other, arranged along a plane.
+
+Perspective and Stereo Perspective
+++++++++++++++++++++++++++++++++++
+
+The :class:`~yt.visualization.volume_rendering.lens.PerspectiveLens` 
+adjusts for an opening view angle, so that the scene will have an 
+element of perspective to it.
+:class:`~yt.visualization.volume_rendering.lens.StereoPerspectiveLens`
+is identical to PerspectiveLens, but it produces two images from nearby 
+camera positions for use in 3D viewing.
+
+Fisheye or Dome
++++++++++++++++
+
+The :class:`~yt.visualization.volume_rendering.lens.FisheyeLens` 
+is appropriate for viewing an arbitrary field of view.  Fisheye images 
+are typically used for dome-based presentations; the Hayden planetarium 
+for instance has a field of view of 194.6.  The images returned by this 
+camera will be flat pixel images that can and should be reshaped to the 
+resolution.
+
+Spherical and Stereo Spherical
+++++++++++++++++++++++++++++++
+
+The :class:`~yt.visualization.volume_rendering.lens.SphericalLens` produces
+a cylindrical-spherical projection.  Movies rendered in this way can be 
+displayed in head-tracking devices (e.g. Oculus Rift) or in YouTube 360 view
+(for more information see `the YouTube help
+<https://support.google.com/youtube/answer/6178631?hl=en>`, but it's a
+simple matter of running a script on an encoded movie file.)
+:class:`~yt.visualization.volume_rendering.lens.StereoSphericalLens` 
+is identical to :class:`~yt.visualization.volume_rendering.lens.SphericalLens` 
+but it produces two images from nearby camera positions for use in 3D viewing.
+
+.. _annotated-vr-example:
+
+Annotated Examples
+------------------
 
 .. warning:: 3D visualizations can be fun but frustrating!  Tuning the
              parameters to both look nice and convey useful scientific
              information can be hard.  We've provided information about best
              practices and tried to make the interface easy to develop nice
              visualizations, but getting them *just right* is often
-             time-consuming.
+             time-consuming.  It's usually best to start out simple with the 
+             built-in helper interface, and expand on that as you need.
 
-Tutorial
---------
-
-The scene interface provides a more modular interface for creating renderings
-of arbitrary data sources. As such, manual composition of a scene can require a
-bit more work, but we will also provide several helper functions that attempt
+The scene interface provides a modular interface for creating renderings
+of arbitrary data sources. As such, manual composition of a scene can require 
+a bit more work, but we will also provide several helper functions that attempt
 to create satisfactory default volume renderings.
 
-.. note:: It's usually best to start out simple with the built-in helper
-          interface, and expand on that if you need to.
-
-Here is a working example for rendering the IsolatedGalaxy dataset.
-
-.. python-script::
-
-  import yt
-  # load the data
-  ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030")
-  # volume render the 'density' field, and save the resulting image
-  im, sc = yt.volume_render(ds, 'density', fname='rendering.png')
-
-  # im is the image array generated. it is also saved to 'rendering.png'.
-  # sc is an instance of a Scene object, which allows you to further refine
-  # your renderings, and later save them.
-
 When the 
 :func:`~yt.visualization.volume_rendering.volume_rendering.volume_render` 
 function is called, first an empty 
@@ -83,15 +364,18 @@
 :class:`~yt.visualization.volume_rendering.api.VolumeSource`
 object is created, by default it will create a transfer function
 based on the extrema of the field that you are rendering. The transfer function
-describes how rays that pass through the domain are "transfered" and thus how
+describes how rays that pass through the domain are "transferred" and thus how
 brightness and color correlates to the field values.  Modifying and adjusting
 the transfer function is the primary way to modify the appearance of an image
 based on volumes.
 
-Once the basic set of objects to be rendered is constructed, a
-:class:`~yt.visualization.volume_rendering.camera.Camera` object is created and
+Once the basic set of objects to be rendered is constructed (e.g. 
+:class:`~yt.visualization.volume_rendering.scene.Scene`, 
+:class:`~yt.visualization.volume_rendering.render_source.RenderSource`, and
+:class:`~yt.visualization.volume_rendering.api.VolumeSource` objects) , a
+:class:`~yt.visualization.volume_rendering.camera.Camera` is created and
 added to the scene.  By default the creation of a camera also creates a
-default, plane-parallel :class:`~yt.visualization.volume_rendering.lens.Lens`
+plane-parallel :class:`~yt.visualization.volume_rendering.lens.Lens`
 object. The analog to a real camera is intentional -- a camera can take a
 picture of a scene from a particular point in time and space, but different
 lenses can be swapped in and out.  For example, this might include a fisheye
@@ -100,20 +384,35 @@
 call the main methods of the
 :class:`~yt.visualization.volume_rendering.scene.Scene` class,
 :meth:`~yt.visualization.volume_rendering.scene.Scene.render` and 
-:meth:`~yt.visualization.volume_rendering.scene.Scene.save`.  When called,
+:meth:`~yt.visualization.volume_rendering.scene.Scene.save`.  When rendered,
 the scene will loop through all of the
 :class:`~yt.visualization.volume_rendering.render_source.RenderSource` objects
-that have been added and integrate the radiative transfer equation through the
-volume. Finally, the image and scene object is returned to the user.
+that have been added and integrate the radiative transfer equations through the
+volume. Finally, the image and scene object is returned to the user. An example
+script the uses the high-level :func:`~yt.visualization.volume_rendering.volume_rendering.volume_render`
+function to quickly set up defaults is:
 
-In this example, we don't add on any non-volume rendering sources; however, if
-such sources are added, they will be integrated as well.
+.. python-script::
+
+  import yt
+  # load the data
+  ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030")
+
+  # volume render the 'density' field, and save the resulting image
+  im, sc = yt.volume_render(ds, 'density', fname='rendering.png')
+
+  # im is the image array generated. it is also saved to 'rendering.png'.
+  # sc is an instance of a Scene object, which allows you to further refine
+  # your renderings and later save them.
+
 
 Alternatively, if you don't want to immediately generate an image of your
 volume rendering, and you just want access to the default scene object, 
-you can skip this expensive operation by just running the
-:func:`~yt.visualization.volume_rendering.volume_rendering.create_scene` function in lieu of the
-:func:`~yt.visualization.volume_rendering.volume_rendering.volume_render` function. Example:
+you can skip the expensive operation of rendering by just running the
+:func:`~yt.visualization.volume_rendering.volume_rendering.create_scene` 
+function in lieu of the
+:func:`~yt.visualization.volume_rendering.volume_rendering.volume_render` 
+function. Example:
 
 .. python-script::
 
@@ -121,298 +420,12 @@
   ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030")
   sc = yt.create_scene(ds, 'density')
 
-Modifying and Saving the Scene
-------------------------------
 
-Once a basic scene has been created with default render sources and
-camera operations, deeper modifications are possible. These
-modifications can tune the appearance of the render sources (such as which
-colors correspond to which values in the data) as well as the shape of the
-rendered image, the position of the camera in the scene, and other elements
-present in the scene.  Below, we describe a few of the aspects of tuning a
-scene to create a visualization that is communicative and pleasing.
+For a more in-depth tutorial on how to create a Scene and modify its contents,
+see this annotated :ref:`volume-rendering-tutorial`.
 
-.. _rendering_scene:
 
-Rendering and Saving
-++++++++++++++++++++
-
-Whenever you want a rendering of your current scene configuration, use the
-:meth:`~yt.visualization.volume_rendering.scene.Scene.render` method to
-trigger the scene to actually do the ray-tracing step.  After that, you can
-use the :meth:`~yt.visualization.volume_rendering.scene.Scene.save` method
-to save it to disk.  Alternatively, 
-:meth:`~yt.visualization.volume_rendering.scene.Scene.render` will return an 
-:class:`~yt.data_objects.image_array.ImageArray` object if you want to further 
-process it in Python (potentially writing it out with 
-:meth:`~yt.data_objects.image_array.ImageArray.write_png`).  You can continue 
-modifying your :class:`~yt.visualization.volume_rendering.scene.Scene` object,
-and render it as you make changes to see how those changes affect the resulting
-image.  
-
-.. python-script::
-
-  import yt
-  ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030")
-  sc = yt.create_scene(ds, 'density')
-  sc.render() 
-  sc.save()
-  <make changes to scene>
-  sc.render()
-  sc.save('changes.png')
-
-.. _sigma_clip:
-
-Improving Image Contrast with Sigma Clipping
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-If your images appear to be too dark, you can try using the ``sigma_clip``
-keyword in the :meth:`~yt.visualization.volume_rendering.scene.Scene.save` 
-or :func:`~yt.visualization.volume_rendering.volume_rendering.volume_render` 
-functions.  Because the brightness range in an image is scaled to match the 
-range of emissivity values of underlying rendering, if you have a few really 
-high-emissivity points, they will scale the rest of your image to be quite 
-dark.  ``sigma_clip = N`` can address this by removing values that are more
-than ``N`` standard deviations brighter than the mean of your image.  
-Typically, a choice of 4 to 6 will help dramatically with your resulting image.
-See the cookbook recipe :ref:`cookbook-sigma_clip` for a demonstration.
-
-.. python-script::
-
-  sc = yt.create_scene(ds, 'density')
-  sc.render()
-  sc.save(sigma_clip=4)
-
-.. _transfer_functions:
-
-Transfer Functions
-++++++++++++++++++
-
-Transfer functions are the most essential component of a rendering that
-includes volume sources.  Several different fundamental types have been
-provided, but there are many different ways to construct complicated
-expressions that produce visualizations and images using the underlying
-machinery.
-
-.. note::
-   All of the information about how transfer functions are used and values
-   extracted is contained in the functions `TransferFunctionProxy.eval_transfer`
-   and `FIT_get_value` in the file `yt/_amr_utils/VolumeIntegrator.pyx`.  If
-   you're curious about how to construct your own, or why you get the values
-   you do, you should read the source!
-
-There are three ready-to-go transfer functions implemented in yt.
-:class:`~yt.visualization.volume_rendering.transfer_functions.ColorTransferFunction`,
-:class:`~yt.visualization.volume_rendering.transfer_functions.ProjectionTransferFunction`,
-and
-:class:`~yt.visualization.volume_rendering.transfer_functions.PlanckTransferFunction`.
-
-Color Transfer Functions
-^^^^^^^^^^^^^^^^^^^^^^^^
-
-These transfer functions are the standard way to apply colors to specific
-values in the field being rendered.  For instance, applying isocontours at
-specific densities.  They have several different mechanisms that can be used.
-The easiest mechanism is to use
-:meth:`~yt.visualization.volume_rendering.transfer_functions.ColorTransferFunction.add_layers`,
-which will add evenly spaced isocontours between the bounds of the transfer
-function.  However, you can also use
-:meth:`~yt.visualization.volume_rendering.transfer_functions.ColorTransferFunction.sample_colormap`,
-which will sample a colormap at a given value.  Additionally, you can directly
-call
-:meth:`~yt.visualization.volume_rendering.transfer_functions.ColorTransferFunction.add_gaussian`,
-which will allow you to specify the colors directly.
-
-An alternate method for modifying the colormap is
-:meth:`~yt.visualization.volume_rendering.transfer_functions.ColorTransferFunction.map_to_colormap`,
-where you can map a segment of the transfer function space to an entire
-colormap at a single alpha value.  This is sometimes useful for very opaque
-renderings.
-
-See :ref:`cookbook-simple_volume_rendering` for an example usage.
-
-Projection Transfer Function
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-This is designed to allow you to very easily project off-axis through a region.
-See :ref:`cookbook-offaxis_projection` for a simple example.  Note that the
-integration here is scaled to a width of 1.0; this means that if you want to
-apply a colorbar, you will have to multiply by the integration width (specified
-when you initialize the volume renderer) in whatever units are appropriate.
-
-Planck Transfer Function
-^^^^^^^^^^^^^^^^^^^^^^^^
-
-This transfer function is designed to apply a semi-realistic color field based
-on temperature, emission weighted by density, and approximate scattering based
-on the density.  This class is currently under-documented, and it may be best
-to examine the source code to use it.
-
-More Complicated Transfer Functions
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-For more complicated transfer functions, you can use the
-:class:`~yt.visualization.volume_rendering.transfer_functions.MultiVariateTransferFunction`
-object.  This allows for a set of weightings, linkages and so on.
-
-.. _transfer-function-helper:
-
-TransferFunctionHelper
-----------------------
-
-Because good transfer functions can be difficult to generate, the 
-TransferFunctionHelper exists in order to help create and modify transfer
-functions with smart defaults for your datasets.  To follow a full example
-on how to use this interface, follow the
-:ref:`transfer-function-helper-tutorial`.
-
-Adding New Sources
-++++++++++++++++++
-
-The resulting image of a rendering process is a combination of the different
-sources present in a scene.  While at present there are only a few sources
-available, in principle new sources can be defined and added to yt over time.
-
-By default, the scene will construct a volume object that includes the fluid
-components of a data source. 
-
-Volume Objects
-++++++++++++++
-
-When a volume object is added to a scene, rays that cross it will be
-integrated.  The volume object is affiliated with a transfer function, a set of
-voxels (drawn from a data source) and is integrated in a front-to-back manner.
-Depending on whether or not other opaque objects are in the scene, the volume
-may or may not be traversed in its entirety.
-
-.. note:: Behavior is undefined for volume sources that overlap that are added
-          to a scene.
-
-Hard and Opaque Objects
-+++++++++++++++++++++++
-
-In addition to semi-transparent objects, hard surfaces can be added to a scene.
-Currently these surfaces are limited to lines and annotations, but in future
-versions of yt surfaces and texture mapped objects will be included.
-
-The primary objects now available for hard and opaque objects are 
-:class:`~yt.visualization.volume_rendering.api.PointSource` and
-:class:`~yt.visualization.volume_rendering.api.LineSource`.  These are useful
-if you want to annotate points, for instance by splatting a set of particles
-onto an image, or if you want to draw lines connecting different regions or
-vertices.  For instance, lines can be used to draw outlines of regions or
-continents.
-
-.. _volume_rendering_annotations:
-
-Annotations
-+++++++++++
-
-By annotating a visualization, additional information can be drawn out.  yt
-provides three annotations:
-:class:`~yt.visualization.volume_rendering.api.BoxSource`,
-:class:`~yt.visualization.volume_rendering.api.GridSource`, and
-:class:`~yt.visualization.volume_rendering.api.CoordinateVectorSource`.  These
-annotations will operate in data space and can draw boxes, grid information,
-and also provide a vector orientation within the image.
-
-For example scripts using these features, 
-see :ref:`cookbook-volume_rendering_annotations`.
-
-Care and Usage of the Camera
-----------------------------
-
-When constructing a movie or utilizing volume rendering to visualize particular
-objects or phenomena, control over the exact position of the camera is
-necessary for both aesthetic and scientific reasons.
-
-yt provides methods for moving the camera by altering its position and
-orientation in space.  There are helper methods that can provide easier ways if
-you are guiding visualization based on quantities in the data.
-
-Cameras also posses "lens" objects, which control the manner in which rays are
-shot out of the camera.  Some of these make some camera properties
-(specifically the width property) irrelevant.
-
-.. _camera_movement:
-
-Moving and Orienting the Camera
-+++++++++++++++++++++++++++++++
-
-There are multiple ways to manipulate the camera viewpoint to create a series of
-renderings.  For an example, see this cookbook:
-:ref:`cookbook-camera_movement`.  For a current list of
-motion helper functions, see the docstrings associated with
-:class:`~yt.visualization.volume_rendering.camera.Camera`.  In short, the
-camera possesses a number of properties and methods that make changing its
-position easy.  These properties can be set, and will automatically trigger an
-update of the other properties of the camera:
-
- * `position` - the position of the camera in scene-space
- * `width` - the width of the plane the camera can see
- * `focus` - the point in space the camera is looking at
- * `resolution` - the image resolution
-
-In addition to this, methods such as
-:meth:`~yt.visualization.volume_rendering.camera.Camera.rotate`,
-:meth:`~yt.visualization.volume_rendering.camera.Camera.pitch`,
-:meth:`~yt.visualization.volume_rendering.camera.Camera.yaw`, and
-:meth:`~yt.visualization.volume_rendering.camera.Camera.roll` can rotate the
-camera in space. The center around which the camera rotates can be specified by
-the optional parameter `rot_center` (very useful for perspective and spherical
-lenses), or by default `rot_center` is set to be at camera location (i.e. the 
-camera will rotate about its current position).
-
-When examining a particular point in space, 
-:meth:`~yt.visualization.volume_rendering.camera.Camera.zoom` can be of
-assistance, as it will move the camera toward the focal point by a factor
-related to the current distance between them.
-
-In addition to manual control, the camera also has iteration methods that help
-with moving and rotating.  The 
-:meth:`~yt.visualization.volume_rendering.camera.Camera.rotation`,
-:meth:`~yt.visualization.volume_rendering.camera.Camera.zoomin`, and
-:meth:`~yt.visualization.volume_rendering.camera.Camera.move_to` methods
-provide iteration over a sequence of positions and orientations.  These can be
-used within a loop:
-
-.. python-script::
-
-   for i in sc.camera.zoomin(100, 5):
-       sc.render()
-       sc.save("frame_%03i.png" % i)
-
-The variable ``i`` is the frame number in the particular loop being called.  In
-this case, this will zoom in by a factor of 100 over the course of 5 frames.
-
-Changing Lenses
-+++++++++++++++
-
-Setting a lens on a camera changes the resulting image.  These lenses can be
-changed at run time or at the time when a camera is initialized by specifying
-the `lens_type` argument with a string.
-
-At the present time, there are a few cameras that can be used:
-`plane-parallel`, `(stereo)perspective`, `fisheye`, and `(stereo)spherical`.
-
- * Plane parallel: This lens type is the standard type used for orthographic
-   projections.  All rays emerge parallel to each other, arranged along a
-   plane.
- * Perspective: This lens type adjusts for an opening view angle, so that the
-   scene will have an element of perspective to it.
- * Fisheye: This lens type accepts a field-of-view property, `fov`, that
-   describes how wide an angle the fisheye can see.  Fisheye images are
-   typically used for dome-based presentations; the Hayden planetarium for
-   instance has a field of view of 194.6.  The images returned by this camera
-   will be flat pixel images that can and should be reshaped to the resolution.
- * Spherical: This is a cylindrical-spherical projection.  Movies rendered in
-   this way can be displayed in head-tracking devices or in YouTube 360 view
-   (for more information see `the YouTube help
-   <https://support.google.com/youtube/answer/6178631?hl=en>`, but it's a
-   simple matter of running a script on an encoded movie file.)
-
-For more information on the usage of different lenses and their features, see the
-cookbook example :ref:`cookbook-various_lens`.
+.. _volume-rendering-method:
 
 Volume Rendering Method
 -----------------------
@@ -543,10 +556,15 @@
 
 For more information about enabling parallelism, see :ref:`parallel-computation`.
 
+.. _vr-faq:
+
+Volume Rendering Frequently Asked Questions
+-------------------------------------------
+
 .. _opaque_rendering:
 
 Opacity
--------
+^^^^^^^
 
 There are currently two models for opacity when rendering a volume, which are
 controlled in the ColorTransferFunction with the keyword
@@ -560,3 +578,19 @@
 
 For an in-depth example, please see the cookbook example on opaque renders here: 
 :ref:`cookbook-opaque_rendering`.
+
+.. _sigma_clip:
+
+Improving Image Contrast with Sigma Clipping
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+If your images appear to be too dark, you can try using the ``sigma_clip``
+keyword in the :meth:`~yt.visualization.volume_rendering.scene.Scene.save` 
+or :func:`~yt.visualization.volume_rendering.volume_rendering.volume_render` 
+functions.  Because the brightness range in an image is scaled to match the 
+range of emissivity values of underlying rendering, if you have a few really 
+high-emissivity points, they will scale the rest of your image to be quite 
+dark.  ``sigma_clip = N`` can address this by removing values that are more
+than ``N`` standard deviations brighter than the mean of your image.  
+Typically, a choice of 4 to 6 will help dramatically with your resulting image.
+See the cookbook recipe :ref:`cookbook-sigma_clip` for a demonstration.

diff -r 36a295fa58ee77b259f22525200cde006b19283b -r fdf41578efe618b66c30cb258b55a14fbfb12f3f doc/source/visualizing/volume_rendering_tutorial.rst
--- /dev/null
+++ b/doc/source/visualizing/volume_rendering_tutorial.rst
@@ -0,0 +1,6 @@
+.. _volume-rendering-tutorial:
+
+Volume Rendering Tutorial
+=========================
+
+.. notebook:: Volume_Rendering_Tutorial.ipynb

diff -r 36a295fa58ee77b259f22525200cde006b19283b -r fdf41578efe618b66c30cb258b55a14fbfb12f3f yt/geometry/setup.py
--- a/yt/geometry/setup.py
+++ b/yt/geometry/setup.py
@@ -44,6 +44,7 @@
                 include_dirs=["yt/utilities/lib/"],
                 libraries=["m"],
                 depends=["yt/utilities/lib/fp_utils.pxd",
+                         "yt/utilities/lib/grid_traversal.pxd",
                          "yt/geometry/oct_container.pxd",
                          "yt/geometry/oct_visitors.pxd",
                          "yt/geometry/grid_container.pxd",

diff -r 36a295fa58ee77b259f22525200cde006b19283b -r fdf41578efe618b66c30cb258b55a14fbfb12f3f yt/utilities/lib/grid_traversal.pxd
--- a/yt/utilities/lib/grid_traversal.pxd
+++ b/yt/utilities/lib/grid_traversal.pxd
@@ -20,17 +20,14 @@
 cimport kdtree_utils
 
 cdef struct ImageContainer:
-    np.float64_t *vp_pos
-    np.float64_t *vp_dir
+    np.float64_t[:,:,:] vp_pos
+    np.float64_t[:,:,:] vp_dir
     np.float64_t *center
-    np.float64_t *image
-    np.float64_t *zbuffer
+    np.float64_t[:,:,:] image
+    np.float64_t[:,:] zbuffer
     np.float64_t pdx, pdy
     np.float64_t bounds[4]
     int nv[2]
-    int vp_strides[3]
-    int im_strides[3]
-    int vd_strides[3]
     np.float64_t *x_vec
     np.float64_t *y_vec
 
@@ -43,19 +40,29 @@
                 int index[3],
                 void *data) nogil
 
+ctypedef void calculate_extent_function(ImageContainer *image,
+            VolumeContainer *vc, np.int64_t rv[4]) nogil
+
+cdef calculate_extent_function calculate_extent_plane_parallel
+
+ctypedef void generate_vector_info_function(ImageContainer *im,
+            np.int64_t vi, np.int64_t vj,
+            np.float64_t width[2],
+            np.float64_t v_dir[3], np.float64_t v_pos[3]) nogil
+
+cdef generate_vector_info_function generate_vector_info_plane_parallel
+cdef generate_vector_info_function generate_vector_info_null
 
 cdef class ImageSampler:
     cdef ImageContainer *image
     cdef sampler_function *sampler
-    cdef public object avp_pos, avp_dir, acenter, aimage, ax_vec, ay_vec
+    cdef public object acenter, aimage, ax_vec, ay_vec
     cdef public object azbuffer
     cdef void *supp_data
     cdef np.float64_t width[3]
-
-    cdef void get_start_stop(self, np.float64_t *ex, np.int64_t *rv)
-
-    cdef void calculate_extent(self, np.float64_t extrema[4],
-                               VolumeContainer *vc) nogil
+    cdef public object lens_type
+    cdef calculate_extent_function *extent_function
+    cdef generate_vector_info_function *vector_function
 
     cdef void setup(self, PartitionedGrid pg)
 

diff -r 36a295fa58ee77b259f22525200cde006b19283b -r fdf41578efe618b66c30cb258b55a14fbfb12f3f yt/utilities/lib/grid_traversal.pyx
--- a/yt/utilities/lib/grid_traversal.pyx
+++ b/yt/utilities/lib/grid_traversal.pyx
@@ -17,7 +17,7 @@
 cimport numpy as np
 cimport cython
 #cimport healpix_interface
-from libc.stdlib cimport malloc, free, abs
+from libc.stdlib cimport malloc, calloc, free, abs
 from libc.math cimport exp, floor, log2, \
     lrint, fabs, atan, atan2, asin, cos, sin, sqrt
 from fp_utils cimport imax, fmax, imin, fmin, iclip, fclip, i64clip
@@ -174,6 +174,85 @@
             for i in range(3):
                 vel[i] /= vel_mag[0]
 
+ at cython.boundscheck(False)
+ at cython.wraparound(False)
+cdef void calculate_extent_plane_parallel(ImageContainer *image,
+            VolumeContainer *vc, np.int64_t rv[4]) nogil:
+    # We do this for all eight corners
+    cdef np.float64_t temp
+    cdef np.float64_t *edges[2]
+    cdef np.float64_t cx, cy
+    cdef np.float64_t extrema[4]
+    cdef int i, j, k
+    edges[0] = vc.left_edge
+    edges[1] = vc.right_edge
+    extrema[0] = extrema[2] = 1e300; extrema[1] = extrema[3] = -1e300
+    for i in range(2):
+        for j in range(2):
+            for k in range(2):
+                # This should rotate it into the vector plane
+                temp  = edges[i][0] * image.x_vec[0]
+                temp += edges[j][1] * image.x_vec[1]
+                temp += edges[k][2] * image.x_vec[2]
+                if temp < extrema[0]: extrema[0] = temp
+                if temp > extrema[1]: extrema[1] = temp
+                temp  = edges[i][0] * image.y_vec[0]
+                temp += edges[j][1] * image.y_vec[1]
+                temp += edges[k][2] * image.y_vec[2]
+                if temp < extrema[2]: extrema[2] = temp
+                if temp > extrema[3]: extrema[3] = temp
+    cx = cy = 0.0
+    for i in range(3):
+        cx += image.center[i] * image.x_vec[i]
+        cy += image.center[i] * image.y_vec[i]
+    rv[0] = lrint((extrema[0] - cx - image.bounds[0])/image.pdx)
+    rv[1] = rv[0] + lrint((extrema[1] - extrema[0])/image.pdx)
+    rv[2] = lrint((extrema[2] - cy - image.bounds[2])/image.pdy)
+    rv[3] = rv[2] + lrint((extrema[3] - extrema[2])/image.pdy)
+
+# We do this for a bunch of lenses.  Fallback is to grab them from the vector
+# info supplied.
+
+ at cython.boundscheck(False)
+ at cython.wraparound(False)
+cdef void calculate_extent_null(ImageContainer *image,
+            VolumeContainer *vc, np.int64_t rv[4]) nogil:
+    rv[0] = 0
+    rv[1] = image.nv[0]
+    rv[2] = 0
+    rv[3] = image.nv[1]
+
+ at cython.boundscheck(False)
+ at cython.wraparound(False)
+cdef void generate_vector_info_plane_parallel(ImageContainer *im,
+            np.int64_t vi, np.int64_t vj,
+            np.float64_t width[2],
+            # Now outbound
+            np.float64_t v_dir[3], np.float64_t v_pos[3]) nogil:
+    cdef int i
+    cdef np.float64_t px, py
+    px = width[0] * (<np.float64_t>vi)/(<np.float64_t>im.nv[0]-1) - width[0]/2.0
+    py = width[1] * (<np.float64_t>vj)/(<np.float64_t>im.nv[1]-1) - width[1]/2.0
+    # atleast_3d will add to beginning and end
+    v_pos[0] = im.vp_pos[0,0,0]*px + im.vp_pos[0,3,0]*py + im.vp_pos[0,9,0]
+    v_pos[1] = im.vp_pos[0,1,0]*px + im.vp_pos[0,4,0]*py + im.vp_pos[0,10,0]
+    v_pos[2] = im.vp_pos[0,2,0]*px + im.vp_pos[0,5,0]*py + im.vp_pos[0,11,0]
+    for i in range(3): v_dir[i] = im.vp_dir[0,i,0]
+
+ at cython.boundscheck(False)
+ at cython.wraparound(False)
+cdef void generate_vector_info_null(ImageContainer *im,
+            np.int64_t vi, np.int64_t vj,
+            np.float64_t width[2],
+            # Now outbound
+            np.float64_t v_dir[3], np.float64_t v_pos[3]) nogil:
+    cdef int i
+    for i in range(3):
+        # Here's a funny thing: we use vi here because our *image* will be
+        # flattened.  That means that im.nv will be a better one-d offset,
+        # since vp_pos has funny strides.
+        v_pos[i] = im.vp_pos[vi, vj, i]
+        v_dir[i] = im.vp_dir[vi, vj, i]
 
 cdef struct ImageAccumulator:
     np.float64_t rgba[Nch]
@@ -181,8 +260,8 @@
 
 cdef class ImageSampler:
     def __init__(self,
-                  np.ndarray vp_pos,
-                  np.ndarray vp_dir,
+                  np.float64_t[:,:,:] vp_pos,
+                  np.float64_t[:,:,:] vp_dir,
                   np.ndarray[np.float64_t, ndim=1] center,
                   bounds,
                   np.ndarray[np.float64_t, ndim=3] image,
@@ -190,91 +269,49 @@
                   np.ndarray[np.float64_t, ndim=1] y_vec,
                   np.ndarray[np.float64_t, ndim=1] width,
                   *args, **kwargs):
-        self.image = <ImageContainer *> malloc(sizeof(ImageContainer))
-        cdef ImageContainer *imagec = self.image
-        cdef np.ndarray[np.float64_t, ndim=2] zbuffer
+        self.image = <ImageContainer *> calloc(sizeof(ImageContainer), 1)
+        cdef np.float64_t[:,:] zbuffer
         zbuffer = kwargs.pop("zbuffer", None)
+        if zbuffer is None:
+            zbuffer = np.ones((image.shape[0], image.shape[1]), "float64")
+        self.lens_type = kwargs.pop("lens_type", None)
+        if self.lens_type == "plane-parallel":
+            self.extent_function = calculate_extent_plane_parallel
+            self.vector_function = generate_vector_info_plane_parallel
+        else:
+            if not (vp_pos.shape[0] == vp_dir.shape[0] == image.shape[0]) or \
+               not (vp_pos.shape[1] == vp_dir.shape[1] == image.shape[1]):
+                print "Bad lense shape / direction for %s" % (self.lens_type)
+                print "Shapes: (%s - %s - %s) and (%s - %s - %s)" % (
+                    vp_pos.shape[0], vp_dir.shape[0], image.shape[0],
+                    vp_pos.shape[1], vp_dir.shape[1], image.shape[1])
+                raise RuntimeError
+            self.extent_function = calculate_extent_null
+            self.vector_function = generate_vector_info_null
         self.sampler = NULL
         cdef int i, j
         # These assignments are so we can track the objects and prevent their
-        # de-allocation from reference counts.
-        self.avp_pos = vp_pos
-        self.avp_dir = vp_dir
+        # de-allocation from reference counts.  Note that we do this to the
+        # "atleast_3d" versions.  Also, note that we re-assign the input
+        # arguments.
+        self.image.vp_pos = vp_pos
+        self.image.vp_dir = vp_dir
+        self.image.image = self.aimage = image
         self.acenter = center
-        self.aimage = image
+        self.image.center = <np.float64_t *> center.data
         self.ax_vec = x_vec
+        self.image.x_vec = <np.float64_t *> x_vec.data
         self.ay_vec = y_vec
-        self.azbuffer = zbuffer
-        imagec.vp_pos = <np.float64_t *> vp_pos.data
-        imagec.vp_dir = <np.float64_t *> vp_dir.data
-        imagec.center = <np.float64_t *> center.data
-        imagec.image = <np.float64_t *> image.data
-        imagec.x_vec = <np.float64_t *> x_vec.data
-        imagec.y_vec = <np.float64_t *> y_vec.data
-        imagec.zbuffer = NULL
-        if zbuffer is not None:
-            imagec.zbuffer = <np.float64_t *> zbuffer.data
-        imagec.nv[0] = image.shape[0]
-        imagec.nv[1] = image.shape[1]
-        for i in range(4): imagec.bounds[i] = bounds[i]
-        imagec.pdx = (bounds[1] - bounds[0])/imagec.nv[0]
-        imagec.pdy = (bounds[3] - bounds[2])/imagec.nv[1]
+        self.image.y_vec = <np.float64_t *> y_vec.data
+        self.image.zbuffer = zbuffer
+        self.image.nv[0] = image.shape[0]
+        self.image.nv[1] = image.shape[1]
+        for i in range(4): self.image.bounds[i] = bounds[i]
+        self.image.pdx = (bounds[1] - bounds[0])/self.image.nv[0]
+        self.image.pdy = (bounds[3] - bounds[2])/self.image.nv[1]
         for i in range(3):
-            imagec.vp_strides[i] = vp_pos.strides[i] / 8
-            imagec.im_strides[i] = image.strides[i] / 8
             self.width[i] = width[i]
 
-        if vp_dir.ndim > 1:
-            for i in range(3):
-                imagec.vd_strides[i] = vp_dir.strides[i] / 8
-        elif vp_pos.ndim == 1:
-            imagec.vd_strides[0] = imagec.vd_strides[1] = imagec.vd_strides[2] = -1
-        else:
-            raise RuntimeError
-
-    @cython.boundscheck(False)
-    @cython.wraparound(False)
-    @cython.cdivision(True)
-    cdef void get_start_stop(self, np.float64_t *ex, np.int64_t *rv):
-        # Extrema need to be re-centered
-        cdef np.float64_t cx, cy
-        cdef ImageContainer *im = self.image
-        cdef int i
-        cx = cy = 0.0
-        for i in range(3):
-            cx += im.center[i] * im.x_vec[i]
-            cy += im.center[i] * im.y_vec[i]
-        rv[0] = lrint((ex[0] - cx - im.bounds[0])/im.pdx)
-        rv[1] = rv[0] + lrint((ex[1] - ex[0])/im.pdx)
-        rv[2] = lrint((ex[2] - cy - im.bounds[2])/im.pdy)
-        rv[3] = rv[2] + lrint((ex[3] - ex[2])/im.pdy)
-
-    @cython.boundscheck(False)
-    @cython.wraparound(False)
-    cdef void calculate_extent(self, np.float64_t extrema[4],
-                               VolumeContainer *vc) nogil:
-        # We do this for all eight corners
-        cdef np.float64_t temp
-        cdef np.float64_t *edges[2]
-        edges[0] = vc.left_edge
-        edges[1] = vc.right_edge
-        extrema[0] = extrema[2] = 1e300; extrema[1] = extrema[3] = -1e300
-        cdef int i, j, k
-        for i in range(2):
-            for j in range(2):
-                for k in range(2):
-                    # This should rotate it into the vector plane
-                    temp  = edges[i][0] * self.image.x_vec[0]
-                    temp += edges[j][1] * self.image.x_vec[1]
-                    temp += edges[k][2] * self.image.x_vec[2]
-                    if temp < extrema[0]: extrema[0] = temp
-                    if temp > extrema[1]: extrema[1] = temp
-                    temp  = edges[i][0] * self.image.y_vec[0]
-                    temp += edges[j][1] * self.image.y_vec[1]
-                    temp += edges[k][2] * self.image.y_vec[2]
-                    if temp < extrema[2]: extrema[2] = temp
-                    if temp > extrema[3]: extrema[3] = temp
-
     @cython.boundscheck(False)
     @cython.wraparound(False)
     @cython.cdivision(True)
@@ -282,7 +319,7 @@
         # This routine will iterate over all of the vectors and cast each in
         # turn.  Might benefit from a more sophisticated intersection check,
         # like http://courses.csusm.edu/cs697exz/ray_box.htm
-        cdef int vi, vj, hit, i, j, ni, nj, nn
+        cdef int vi, vj, hit, i, j, k, ni, nj, nn, xi, yi
         cdef np.int64_t offset
         cdef np.int64_t iter[4]
         cdef VolumeContainer *vc = pg.container
@@ -292,83 +329,43 @@
         cdef np.float64_t *v_pos
         cdef np.float64_t *v_dir
         cdef np.float64_t rgba[6]
-        cdef np.float64_t extrema[4]
         cdef np.float64_t max_t
         hit = 0
         cdef np.int64_t nx, ny, size
-        if im.vd_strides[0] == -1:
-            self.calculate_extent(extrema, vc)
-            self.get_start_stop(extrema, iter)
-            iter[0] = i64clip(iter[0]-1, 0, im.nv[0])
-            iter[1] = i64clip(iter[1]+1, 0, im.nv[0])
-            iter[2] = i64clip(iter[2]-1, 0, im.nv[1])
-            iter[3] = i64clip(iter[3]+1, 0, im.nv[1])
-            nx = (iter[1] - iter[0])
-            ny = (iter[3] - iter[2])
-            size = nx * ny
-        else:
-            nx = im.nv[0]
-            ny = 1
-            iter[0] = iter[1] = iter[2] = iter[3] = 0
-            size = nx
+        self.extent_function(self.image, vc, iter)
+        iter[0] = i64clip(iter[0]-1, 0, im.nv[0])
+        iter[1] = i64clip(iter[1]+1, 0, im.nv[0])
+        iter[2] = i64clip(iter[2]-1, 0, im.nv[1])
+        iter[3] = i64clip(iter[3]+1, 0, im.nv[1])
+        nx = (iter[1] - iter[0])
+        ny = (iter[3] - iter[2])
+        size = nx * ny
         cdef ImageAccumulator *idata
-        cdef np.float64_t px, py
         cdef np.float64_t width[3]
+        cdef int use_vec, max_i
         for i in range(3):
             width[i] = self.width[i]
-        if im.vd_strides[0] == -1:
-            with nogil, parallel(num_threads = num_threads):
-                idata = <ImageAccumulator *> malloc(sizeof(ImageAccumulator))
-                idata.supp_data = self.supp_data
-                v_pos = <np.float64_t *> malloc(3 * sizeof(np.float64_t))
-                for j in prange(size, schedule="static",chunksize=1):
-                    vj = j % ny
-                    vi = (j - vj) / ny + iter[0]
-                    vj = vj + iter[2]
-                    # Dynamically calculate the position
-                    px = width[0] * (<np.float64_t>vi)/(<np.float64_t>im.nv[0]-1) - width[0]/2.0
-                    py = width[1] * (<np.float64_t>vj)/(<np.float64_t>im.nv[1]-1) - width[1]/2.0
-                    v_pos[0] = im.vp_pos[0]*px + im.vp_pos[3]*py + im.vp_pos[9]
-                    v_pos[1] = im.vp_pos[1]*px + im.vp_pos[4]*py + im.vp_pos[10]
-                    v_pos[2] = im.vp_pos[2]*px + im.vp_pos[5]*py + im.vp_pos[11]
-                    offset = im.im_strides[0] * vi + im.im_strides[1] * vj
-                    for i in range(Nch): idata.rgba[i] = im.image[i + offset]
-                    if im.zbuffer != NULL:
-                        max_t = im.zbuffer[im.nv[0] * vi + vj]
-                    else:
-                        max_t = 1.0
-                    walk_volume(vc, v_pos, im.vp_dir, self.sampler,
-                                (<void *> idata), NULL, max_t)
-                    for i in range(Nch): im.image[i + offset] = idata.rgba[i]
-                free(idata)
-                free(v_pos)
-        else:
-            with nogil, parallel(num_threads = num_threads):
-                idata = <ImageAccumulator *> malloc(sizeof(ImageAccumulator))
-                idata.supp_data = self.supp_data
-                v_pos = <np.float64_t *> malloc(3 * sizeof(np.float64_t))
-                v_dir = <np.float64_t *> malloc(3 * sizeof(np.float64_t))
-                # If we do not have a simple image plane, we have to cast all
-                # our rays 
-                for j in prange(size, schedule="dynamic", chunksize=100):
-                    offset = j * 3
-                    for i in range(3): v_pos[i] = im.vp_pos[i + offset]
-                    for i in range(3): v_dir[i] = im.vp_dir[i + offset]
-                    if v_dir[0] == v_dir[1] == v_dir[2] == 0.0:
-                        continue
-                    # Note that for Nch != 3 we need a different offset into
-                    # the image object than for the vectors!
-                    for i in range(Nch): idata.rgba[i] = im.image[i + Nch*j]
-                    if im.zbuffer != NULL:
-                        max_t = fclip(im.zbuffer[j], 0.0, 1.0)
-                    else:
-                        max_t = 1.0
-                    walk_volume(vc, v_pos, v_dir, self.sampler, 
-                                (<void *> idata), NULL, max_t)
-                    for i in range(Nch): im.image[i + Nch*j] = idata.rgba[i]
-                free(v_dir)
-                free(idata)
-                free(v_pos)
+        with nogil, parallel(num_threads = num_threads):
+            idata = <ImageAccumulator *> malloc(sizeof(ImageAccumulator))
+            idata.supp_data = self.supp_data
+            v_pos = <np.float64_t *> malloc(3 * sizeof(np.float64_t))
+            v_dir = <np.float64_t *> malloc(3 * sizeof(np.float64_t))
+            for j in prange(size, schedule="static", chunksize=100):
+                vj = j % ny
+                vi = (j - vj) / ny + iter[0]
+                vj = vj + iter[2]
+                # Dynamically calculate the position
+                self.vector_function(im, vi, vj, width, v_dir, v_pos)
+                for i in range(Nch):
+                    idata.rgba[i] = im.image[vi, vj, i]
+                max_t = fclip(im.zbuffer[vi, vj], 0.0, 1.0)
+                walk_volume(vc, v_pos, v_dir, self.sampler,
+                            (<void *> idata), NULL, max_t)
+                for i in range(Nch):
+                    im.image[vi, vj, i] = idata.rgba[i]
+            free(idata)
+            free(v_pos)
+            free(v_dir)
         return hit
 
     cdef void setup(self, PartitionedGrid pg):

diff -r 36a295fa58ee77b259f22525200cde006b19283b -r fdf41578efe618b66c30cb258b55a14fbfb12f3f yt/utilities/lib/mesh_traversal.pyx
--- a/yt/utilities/lib/mesh_traversal.pyx
+++ b/yt/utilities/lib/mesh_traversal.pyx
@@ -75,53 +75,25 @@
         size = nx * ny
         data = np.empty(size, dtype="float64")
         cdef rtcr.RTCRay ray
-        if im.vd_strides[0] == -1:
-            v_pos = <np.float64_t *> malloc(3 * sizeof(np.float64_t))
-            for j in range(size):
-                vj = j % ny
-                vi = (j - vj) / ny
-                vj = vj
-                # Dynamically calculate the position
-                px = width[0] * (<np.float64_t>vi)/(<np.float64_t>im.nv[0]-1) - width[0]/2.0
-                py = width[1] * (<np.float64_t>vj)/(<np.float64_t>im.nv[1]-1) - width[1]/2.0
-                v_pos[0] = im.vp_pos[0]*px + im.vp_pos[3]*py + im.vp_pos[9]
-                v_pos[1] = im.vp_pos[1]*px + im.vp_pos[4]*py + im.vp_pos[10]
-                v_pos[2] = im.vp_pos[2]*px + im.vp_pos[5]*py + im.vp_pos[11]
-                for i in range(3):
-                    ray.org[i] = v_pos[i]
-                    ray.dir[i] = im.vp_dir[i]
-                ray.tnear = 0.0
-                ray.tfar = 1e37
-                ray.geomID = rtcg.RTC_INVALID_GEOMETRY_ID
-                ray.primID = rtcg.RTC_INVALID_GEOMETRY_ID
-                ray.instID = rtcg.RTC_INVALID_GEOMETRY_ID
-                ray.mask = -1
-                ray.time = 0
-                rtcs.rtcIntersect(scene.scene_i, ray)
-                data[j] = ray.time
-            self.aimage = data.reshape(self.image.nv[0], self.image.nv[1])
-            free(v_pos)
-        else:
-            v_pos = <np.float64_t *> malloc(3 * sizeof(np.float64_t))
-            v_dir = <np.float64_t *> malloc(3 * sizeof(np.float64_t))
-            # If we do not have a simple image plane, we have to cast all
-            # our rays 
-            for j in range(size):
-                offset = j * 3
-                for i in range(3): v_pos[i] = im.vp_pos[i + offset]
-                for i in range(3): v_dir[i] = im.vp_dir[i + offset]
-                for i in range(3):
-                    ray.org[i] = v_pos[i]
-                    ray.dir[i] = v_dir[i]
-                ray.tnear = 0.0
-                ray.tfar = 1e37
-                ray.geomID = rtcg.RTC_INVALID_GEOMETRY_ID
-                ray.primID = rtcg.RTC_INVALID_GEOMETRY_ID
-                ray.instID = rtcg.RTC_INVALID_GEOMETRY_ID
-                ray.mask = -1
-                ray.time = 0
-                rtcs.rtcIntersect(scene.scene_i, ray)
-                data[j] = ray.time
-            self.aimage = data.reshape(self.image.nv[0], self.image.nv[1])
-            free(v_pos)
-            free(v_dir)
+        v_pos = <np.float64_t *> malloc(3 * sizeof(np.float64_t))
+        v_dir = <np.float64_t *> malloc(3 * sizeof(np.float64_t))
+        for j in range(size):
+            vj = j % ny
+            vi = (j - vj) / ny
+            vj = vj
+            self.vector_function(im, vi, vj, width, v_dir, v_pos)
+            for i in range(3):
+                ray.org[i] = v_pos[i]
+                ray.dir[i] = v_dir[i]
+            ray.tnear = 0.0
+            ray.tfar = 1e37
+            ray.geomID = rtcg.RTC_INVALID_GEOMETRY_ID
+            ray.primID = rtcg.RTC_INVALID_GEOMETRY_ID
+            ray.instID = rtcg.RTC_INVALID_GEOMETRY_ID
+            ray.mask = -1
+            ray.time = 0
+            rtcs.rtcIntersect(scene.scene_i, ray)
+            data[j] = ray.time
+        self.aimage = data.reshape(self.image.nv[0], self.image.nv[1])
+        free(v_pos)
+        free(v_dir)

diff -r 36a295fa58ee77b259f22525200cde006b19283b -r fdf41578efe618b66c30cb258b55a14fbfb12f3f yt/utilities/lib/setup.py
--- a/yt/utilities/lib/setup.py
+++ b/yt/utilities/lib/setup.py
@@ -79,6 +79,7 @@
                 depends=["yt/utilities/lib/fp_utils.pxd",
                          "yt/utilities/lib/amr_kdtools.pxd",
                          "yt/utilities/lib/ContourFinding.pxd",
+                         "yt/utilities/lib/grid_traversal.pxd",
                          "yt/geometry/oct_container.pxd"])
     config.add_extension("DepthFirstOctree", 
                 ["yt/utilities/lib/DepthFirstOctree.pyx"],
@@ -184,7 +185,8 @@
                              ["yt/utilities/lib/mesh_traversal.pyx"],
                              include_dirs=["yt/utilities/lib", include_dirs],
                              libraries=["m", "embree"], language="c++",
-                             depends=["yt/utilities/lib/mesh_traversal.pxd"])
+                             depends=["yt/utilities/lib/mesh_traversal.pxd",
+                                      "yt/utilities/lib/grid_traversal.pxd"])
         config.add_extension("mesh_samplers",
                              ["yt/utilities/lib/mesh_samplers.pyx"],
                              include_dirs=["yt/utilities/lib", include_dirs],

diff -r 36a295fa58ee77b259f22525200cde006b19283b -r fdf41578efe618b66c30cb258b55a14fbfb12f3f yt/visualization/volume_rendering/camera.py
--- a/yt/visualization/volume_rendering/camera.py
+++ b/yt/visualization/volume_rendering/camera.py
@@ -93,20 +93,20 @@
         self._width = np.array([1.0, 1.0, 1.0])
         self._focus = np.array([0.0]*3)
         self._position = np.array([1.0]*3)
-        self.set_lens(lens_type)
         if data_source is not None:
             data_source = data_source_or_all(data_source)
             self._focus = data_source.ds.domain_center
             self._position = data_source.ds.domain_right_edge
             self._width = 1.5*data_source.ds.domain_width
+            self._domain_center = data_source.ds.domain_center
+            self._domain_width = data_source.ds.domain_width
         if auto:
             self.set_defaults_from_data_source(data_source)
 
         super(Camera, self).__init__(self.focus - self.position,
                                      self.north_vector, steady_north=False)
 
-        # This should be run on-demand if certain attributes are not set.
-        self.lens.setup_box_properties(self)
+        self.set_lens(lens_type)
 
     def position():
         doc = '''The position is the location of the camera in
@@ -217,13 +217,14 @@
             mylog.error("Lens type not available")
             raise RuntimeError()
         self.lens = lenses[lens_type]()
-        self.lens.camera = self
+        self.lens.set_camera(self)
 
     def set_defaults_from_data_source(self, data_source):
         """Resets the camera attributes to their default values"""
-        self.position = data_source.pf.domain_right_edge
 
-        width = 1.5 * data_source.pf.domain_width.max()
+        position = data_source.ds.domain_right_edge
+
+        width = 1.5 * data_source.ds.domain_width.max()
         (xmi, xma), (ymi, yma), (zmi, zma) = \
             data_source.quantities['Extrema'](['x', 'y', 'z'])
         width = np.sqrt((xma - xmi) ** 2 + (yma - ymi) ** 2 +
@@ -231,20 +232,26 @@
         focus = data_source.get_field_parameter('center')
 
         if iterable(width) and len(width) > 1 and isinstance(width[1], str):
-            width = data_source.pf.quan(width[0], input_units=width[1])
+            width = data_source.ds.quan(width[0], input_units=width[1])
             # Now convert back to code length for subsequent manipulation
             width = width.in_units("code_length")  # .value
         if not iterable(width):
-            width = data_source.pf.arr([width, width, width],
+            width = data_source.ds.arr([width, width, width],
                                        input_units='code_length')
             # left/right, top/bottom, front/back
         if not isinstance(width, YTArray):
-            width = data_source.pf.arr(width, input_units="code_length")
+            width = data_source.ds.arr(width, input_units="code_length")
         if not isinstance(focus, YTArray):
-            focus = self.pf.arr(focus, input_units="code_length")
+            focus = self.ds.arr(focus, input_units="code_length")
 
-        self.set_width(width)
-        self.focus = focus
+        # We can't use the property setters yet, since they rely on attributes
+        # that will not be set up until the base class initializer is called.
+        # See Issue #1131.
+        self._width = width
+        self._focus = focus
+        self._position = position
+        self._domain_center = data_source.ds.domain_center
+        self._domain_width = data_source.ds.domain_width
 
         super(Camera, self).__init__(self.focus - self.position,
                                      self.north_vector, steady_north=False)
@@ -300,7 +307,7 @@
         Parameters
         ----------
         normal_vector: array_like, optional
-            The new looking vector.
+            The new looking vector from the camera to the focus.
         north_vector : array_like, optional
             The 'up' direction for the plane of rays.  If not specific,
             calculated automatically.
@@ -321,7 +328,7 @@
         Parameters
         ----------
         normal_vector: array_like, optional
-            The new looking vector.
+            The new looking vector from the camera to the focus.
         north_vector : array_like, optional
             The 'up' direction for the plane of rays.  If not specific,
             calculated automatically.
@@ -561,16 +568,16 @@
             yield i
 
     def zoom(self, factor):
-        r"""Change the distance to the focal point.
+        r"""Change the width of the FOV of the camera.
 
-        This will zoom the camera in by some `factor` toward the focal point,
-        along the current view direction, modifying the left/right and up/down
-        extents as well.
+        This will appear to zoom the camera in by some `factor` toward the 
+        focal point along the current view direction, but really it's just
+        changing the width of the field of view.
 
         Parameters
         ----------
         factor : float
-            The factor by which to reduce the distance to the focal point.
+            The factor by which to divide the width
 
         Examples
         --------

This diff is so big that we needed to truncate the remainder.

https://bitbucket.org/yt_analysis/yt/commits/bf50a317e2b2/
Changeset:   bf50a317e2b2
Branch:      yt
User:        atmyers
Date:        2015-11-09 19:34:00+00:00
Summary:     merging
Affected #:  49 files

diff -r fdf41578efe618b66c30cb258b55a14fbfb12f3f -r bf50a317e2b28de85e33520e98fdc58cecbeab91 doc/get_yt.sh
--- a/doc/get_yt.sh
+++ b/doc/get_yt.sh
@@ -23,7 +23,7 @@
 DEST_SUFFIX="yt-conda"
 DEST_DIR="`pwd`/${DEST_SUFFIX/ /}"   # Installation location
 BRANCH="yt" # This is the branch to which we will forcibly update.
-INST_YT_SOURCE=1 # Do we do a source install of yt?
+INST_YT_SOURCE=0 # Do we do a source install of yt?
 
 ##################################################################
 #                                                                #
@@ -37,7 +37,7 @@
 # ( SOMECOMMAND 2>&1 ) 1>> ${LOG_FILE} || do_exit
 
 MINICONDA_URLBASE="http://repo.continuum.io/miniconda"
-MINICONDA_VERSION="1.9.1"
+MINICONDA_VERSION="latest"
 YT_RECIPE_REPO="https://bitbucket.org/yt_analysis/yt_conda/raw/default"
 
 function do_exit
@@ -61,12 +61,14 @@
     ( $* 2>&1 ) 1>> ${LOG_FILE} || do_exit
 }
 
-function get_ytproject
-{
-    [ -e $1 ] && return
-    echo "Downloading $1 from yt-project.org"
-    ${GETFILE} "http://yt-project.org/dependencies/$1" || do_exit
-    ( ${SHASUM} -c $1.sha512 2>&1 ) 1>> ${LOG_FILE} || do_exit
+# These are needed to prevent pushd and popd from printing to stdout
+
+function pushd () {
+    command pushd "$@" > /dev/null
+}
+
+function popd () {
+    command popd "$@" > /dev/null
 }
 
 function get_ytdata
@@ -101,122 +103,125 @@
 echo "This will install Miniconda from Continuum Analytics, the necessary"
 echo "packages to run yt, and create a self-contained environment for you to"
 echo "use yt.  Additionally, Conda itself provides the ability to install"
-echo "many other packages that can be used for other purposes."
+echo "many other packages that can be used for other purposes using the"
+echo "'conda install' command."
 echo
 MYOS=`uname -s`       # A guess at the OS
-if [ "${MYOS##Darwin}" != "${MYOS}" ]
+if [ $INST_YT_SOURCE -ne 0 ]
 then
-  echo "Looks like you're running on Mac OSX."
-  echo
-  echo "NOTE: you must have the Xcode command line tools installed."
-  echo
-  echo "The instructions for obtaining these tools varies according"
-  echo "to your exact OS version.  On older versions of OS X, you"
-  echo "must register for an account on the apple developer tools"
-  echo "website: https://developer.apple.com/downloads to obtain the"
-  echo "download link."
-  echo
-  echo "We have gathered some additional instructions for each"
-  echo "version of OS X below. If you have trouble installing yt"
-  echo "after following these instructions, don't hesitate to contact"
-  echo "the yt user's e-mail list."
-  echo
-  echo "You can see which version of OSX you are running by clicking"
-  echo "'About This Mac' in the apple menu on the left hand side of"
-  echo "menu bar.  We're assuming that you've installed all operating"
-  echo "system updates; if you have an older version, we suggest"
-  echo "running software update and installing all available updates."
-  echo
-  echo "OS X 10.5.8: search for and download Xcode 3.1.4 from the"
-  echo "Apple developer tools website."
-  echo
-  echo "OS X 10.6.8: search for and download Xcode 3.2 from the Apple"
-  echo "developer tools website.  You can either download the"
-  echo "Xcode 3.2.2 Developer Tools package (744 MB) and then use"
-  echo "Software Update to update to XCode 3.2.6 or"
-  echo "alternatively, you can download the Xcode 3.2.6/iOS SDK"
-  echo "bundle (4.1 GB)."
-  echo
-  echo "OS X 10.7.5: download Xcode 4.2 from the mac app store"
-  echo "(search for Xcode)."
-  echo "Alternatively, download the Xcode command line tools from"
-  echo "the Apple developer tools website."
-  echo
-  echo "OS X 10.8.2: download Xcode 4.6.1 from the mac app store."
-  echo "(search for Xcode)."
-  echo "Additionally, you will have to manually install the Xcode"
-  echo "command line tools, see:"
-  echo "http://stackoverflow.com/questions/9353444"
-  echo "Alternatively, download the Xcode command line tools from"
-  echo "the Apple developer tools website."
-  echo
-  echo "NOTE: It's possible that the installation will fail, if so,"
-  echo "please set the following environment variables, remove any"
-  echo "broken installation tree, and re-run this script verbatim."
-  echo
-  echo "$ export CC=gcc"
-  echo "$ export CXX=g++"
-  echo
-  MINICONDA_OS="MacOSX-x86_64"
+    if [ "${MYOS##Darwin}" != "${MYOS}" ]
+    then
+        echo "Looks like you're running on Mac OSX."
+        echo
+        echo "NOTE: you must have the Xcode command line tools installed."
+        echo
+        echo "The instructions for obtaining these tools varies according"
+        echo "to your exact OS version.  On older versions of OS X, you"
+        echo "must register for an account on the apple developer tools"
+        echo "website: https://developer.apple.com/downloads to obtain the"
+        echo "download link."
+        echo
+        echo "We have gathered some additional instructions for each"
+        echo "version of OS X below. If you have trouble installing yt"
+        echo "after following these instructions, don't hesitate to contact"
+        echo "the yt user's e-mail list."
+        echo
+        echo "You can see which version of OSX you are running by clicking"
+        echo "'About This Mac' in the apple menu on the left hand side of"
+        echo "menu bar.  We're assuming that you've installed all operating"
+        echo "system updates; if you have an older version, we suggest"
+        echo "running software update and installing all available updates."
+        echo
+        echo "OS X 10.5.8: search for and download Xcode 3.1.4 from the"
+        echo "Apple developer tools website."
+        echo
+        echo "OS X 10.6.8: search for and download Xcode 3.2 from the Apple"
+        echo "developer tools website.  You can either download the"
+        echo "Xcode 3.2.2 Developer Tools package (744 MB) and then use"
+        echo "Software Update to update to XCode 3.2.6 or"
+        echo "alternatively, you can download the Xcode 3.2.6/iOS SDK"
+        echo "bundle (4.1 GB)."
+        echo
+        echo "OS X 10.7.5: download Xcode 4.2 from the mac app store"
+        echo "(search for Xcode)."
+        echo "Alternatively, download the Xcode command line tools from"
+        echo "the Apple developer tools website."
+        echo
+        echo "OS X 10.8.4, 10.9, 10.10, and 10.11:"
+        echo "download the appropriate version of Xcode from the"
+        echo "mac app store (search for Xcode)."
+        echo
+        echo "Additionally, you will have to manually install the Xcode"
+        echo "command line tools."
+        echo
+        echo "For OS X 10.8, see:"
+        echo "http://stackoverflow.com/questions/9353444"
+        echo
+        echo "For OS X 10.9 and newer the command line tools can be installed"
+        echo "with the following command:"
+        echo "    xcode-select --install"
+    fi
+    if [ "${MYOS##Linux}" != "${MYOS}" ]
+    then
+        echo "Looks like you're on Linux."
+        echo
+        echo "Please make sure you have the developer tools for your OS "
+        echo "installed."
+        echo
+        if [ -f /etc/SuSE-release ] && [ `grep --count SUSE /etc/SuSE-release` -gt 0 ]
+        then
+            echo "Looks like you're on an OpenSUSE-compatible machine."
+            echo
+            echo "You need to have these packages installed:"
+            echo
+            echo "  * devel_C_C++"
+            echo "  * libuuid-devel"
+            echo "  * gcc-c++"
+            echo "  * chrpath"
+            echo
+            echo "You can accomplish this by executing:"
+            echo
+            echo "$ sudo zypper install -t pattern devel_C_C++"
+            echo "$ sudo zypper install gcc-c++ libuuid-devel zip"
+            echo "$ sudo zypper install chrpath"
+        fi
+        if [ -f /etc/lsb-release ] && [ `grep --count buntu /etc/lsb-release` -gt 0 ]
+        then
+            echo "Looks like you're on an Ubuntu-compatible machine."
+            echo
+            echo "You need to have these packages installed:"
+            echo
+            echo "  * libssl-dev"
+            echo "  * build-essential"
+            echo "  * libncurses5"
+            echo "  * libncurses5-dev"
+            echo "  * uuid-dev"
+            echo "  * chrpath"
+            echo
+            echo "You can accomplish this by executing:"
+            echo
+            echo "$ sudo apt-get install libssl-dev build-essential libncurses5 libncurses5-dev zip uuid-dev chrpath"
+            echo
+        fi
+        echo
+        echo "If you are running on a supercomputer or other module-enabled"
+        echo "system, please make sure that the GNU module has been loaded."
+        echo
+    fi
 fi
-if [ "${MYOS##Linux}" != "${MYOS}" ]
+if [ "${MYOS##x86_64}" != "${MYOS}" ]
 then
-  echo "Looks like you're on Linux."
-  echo
-  echo "Please make sure you have the developer tools for your OS installed."
-  echo
-  if [ -f /etc/SuSE-release ] && [ `grep --count SUSE /etc/SuSE-release` -gt 0 ]
-  then
-    echo "Looks like you're on an OpenSUSE-compatible machine."
-    echo
-    echo "You need to have these packages installed:"
-    echo
-    echo "  * devel_C_C++"
-    echo "  * libopenssl-devel"
-    echo "  * libuuid-devel"
-    echo "  * zip"
-    echo "  * gcc-c++"
-    echo "  * chrpath"
-    echo
-    echo "You can accomplish this by executing:"
-    echo
-    echo "$ sudo zypper install -t pattern devel_C_C++"
-    echo "$ sudo zypper install gcc-c++ libopenssl-devel libuuid-devel zip"
-    echo "$ sudo zypper install chrpath"
-  fi
-  if [ -f /etc/lsb-release ] && [ `grep --count buntu /etc/lsb-release` -gt 0 ]
-  then
-    echo "Looks like you're on an Ubuntu-compatible machine."
-    echo
-    echo "You need to have these packages installed:"
-    echo
-    echo "  * libssl-dev"
-    echo "  * build-essential"
-    echo "  * libncurses5"
-    echo "  * libncurses5-dev"
-    echo "  * zip"
-    echo "  * uuid-dev"
-    echo "  * chrpath"
-    echo
-    echo "You can accomplish this by executing:"
-    echo
-    echo "$ sudo apt-get install libssl-dev build-essential libncurses5 libncurses5-dev zip uuid-dev chrpath"
-    echo
-  fi
-  echo
-  echo "If you are running on a supercomputer or other module-enabled"
-  echo "system, please make sure that the GNU module has been loaded."
-  echo
-  if [ "${MYOS##x86_64}" != "${MYOS}" ]
-  then
     MINICONDA_OS="Linux-x86_64"
-  elif [ "${MYOS##i386}" != "${MYOS}" ]
-  then
+elif [ "${MYOS##i386}" != "${MYOS}" ]
+then
     MINICONDA_OS="Linux-x86"
-  else
-    echo "Not sure which type of Linux you're on.  Going with x86_64."
+elif [ "${MYOS##Darwin}" != "${MYOS}" ]
+then
+     MINICONDA_OS="MacOSX-x86_64"
+else
+    echo "Not sure which Linux distro you are running."
+    echo "Going with x86_64 architecture."
     MINICONDA_OS="Linux-x86_64"
-  fi
 fi
 echo
 echo "If you'd rather not continue, hit Ctrl-C."
@@ -233,7 +238,7 @@
 if type -P wget &>/dev/null
 then
     echo "Using wget"
-    export GETFILE="wget -nv"
+    export GETFILE="wget -nv -nc"
 else
     echo "Using curl"
     export GETFILE="curl -sSO"
@@ -250,9 +255,6 @@
 
 log_cmd bash ./${MINICONDA_PKG} -b -p $DEST_DIR
 
-# I don't think we need OR want this anymore:
-#export LD_LIBRARY_PATH=${DEST_DIR}/lib:$LD_LIBRARY_PATH
-
 # This we *do* need.
 export PATH=${DEST_DIR}/bin:$PATH
 
@@ -261,51 +263,40 @@
 
 declare -a YT_DEPS
 YT_DEPS+=('python')
-YT_DEPS+=('distribute')
-YT_DEPS+=('libpng')
+YT_DEPS+=('setuptools')
 YT_DEPS+=('numpy')
-YT_DEPS+=('pygments')
-YT_DEPS+=('jinja2')
-YT_DEPS+=('tornado')
-YT_DEPS+=('pyzmq')
+YT_DEPS+=('jupyter')
 YT_DEPS+=('ipython')
 YT_DEPS+=('sphinx')
 YT_DEPS+=('h5py')
 YT_DEPS+=('matplotlib')
 YT_DEPS+=('cython')
 YT_DEPS+=('nose')
+YT_DEPS+=('conda-build')
+YT_DEPS+=('mercurial')
+YT_DEPS+=('sympy')
 
 # Here is our dependency list for yt
-log_cmd conda config --system --add channels http://repo.continuum.io/pkgs/free
-log_cmd conda config --system --add channels http://repo.continuum.io/pkgs/dev
-log_cmd conda config --system --add channels http://repo.continuum.io/pkgs/gpl
 log_cmd conda update --yes conda
 
-echo "Current dependencies: ${YT_DEPS[@]}"
 log_cmd echo "DEPENDENCIES" ${YT_DEPS[@]}
-log_cmd conda install --yes ${YT_DEPS[@]}
-
-echo "Installing mercurial."
-get_ytrecipe mercurial
+for YT_DEP in "${YT_DEPS[@]}"; do
+    echo "Installing $YT_DEP"
+    log_cmd conda install --yes ${YT_DEP}
+done
 
 if [ $INST_YT_SOURCE -eq 0 ]
 then
-  echo "Installing yt as a package."
-  get_ytrecipe yt
+  echo "Installing yt"
+  log_cmd conda install --yes yt
 else
-  # We do a source install.
-  YT_DIR="${DEST_DIR}/src/yt-hg"
-  export PNG_DIR=${DEST_DIR}
-  export FTYPE_DIR=${DEST_DIR}
-  export HDF5_DIR=${DEST_DIR}
-  log_cmd hg clone -r ${BRANCH} https://bitbucket.org/yt_analysis/yt ${YT_DIR}
-  pushd ${YT_DIR}
-  log_cmd python setup.py develop
-  popd
-  log_cmd cp ${YT_DIR}/doc/activate ${DEST_DIR}/bin/activate 
-  log_cmd sed -i.bak -e "s,__YT_DIR__,${DEST_DIR}," ${DEST_DIR}/bin/activate
-  log_cmd cp ${YT_DIR}/doc/activate.csh ${DEST_DIR}/bin/activate.csh
-  log_cmd sed -i.bak -e "s,__YT_DIR__,${DEST_DIR}," ${DEST_DIR}/bin/activate.csh
+    # We do a source install.
+    echo "Installing yt from source"
+    YT_DIR="${DEST_DIR}/src/yt-hg"
+    log_cmd hg clone -r ${BRANCH} https://bitbucket.org/yt_analysis/yt ${YT_DIR}
+    pushd ${YT_DIR}
+    log_cmd python setup.py develop
+    popd
 fi
 
 echo
@@ -314,34 +305,26 @@
 echo
 echo "yt and the Conda system are now installed in $DEST_DIR ."
 echo
-if [ $INST_YT_SOURCE -eq 0 ]
-then
-  echo "You must now modify your PATH variable by prepending:"
-  echo 
-  echo "   $DEST_DIR/bin"
-  echo
-  echo "For example, if you use bash, place something like this at the end"
-  echo "of your ~/.bashrc :"
-  echo
-  echo "   export PATH=$DEST_DIR/bin:$PATH"
-else
-  echo "To run from this new installation, use the activate script for this "
-  echo "environment."
-  echo
-  echo "    $ source $DEST_DIR/bin/activate"
-  echo
-  echo "This modifies the environment variables YT_DEST, PATH, PYTHONPATH, and"
-  echo "LD_LIBRARY_PATH to match your new yt install.  If you use csh, just"
-  echo "append .csh to the above."
-fi
+echo "You must now modify your PATH variable by prepending:"
+echo 
+echo "   $DEST_DIR/bin"
+echo
+echo "On Bash-style shells you can copy/paste the following command to "
+echo "temporarily activate the yt installtion:"
+echo
+echo "    export PATH=$DEST_DIR/bin:\$PATH"
+echo
+echo "and on csh-style shells"
+echo
+echo "    setenv PATH $DEST_DIR/bin:\$PATH"
+echo
+echo "You can also the init file appropriate for your shell to include the same"
+echo "command."
 echo
 echo "To get started with yt, check out the orientation:"
 echo
 echo "    http://yt-project.org/doc/orientation/"
 echo
-echo "or just activate your environment and run 'yt serve' to bring up the"
-echo "yt GUI."
-echo
 echo "For support, see the website and join the mailing list:"
 echo
 echo "    http://yt-project.org/"

diff -r fdf41578efe618b66c30cb258b55a14fbfb12f3f -r bf50a317e2b28de85e33520e98fdc58cecbeab91 doc/install_script.sh
--- a/doc/install_script.sh
+++ b/doc/install_script.sh
@@ -233,53 +233,61 @@
         echo
         echo "NOTE: you must have the Xcode command line tools installed."
         echo
-	echo "The instructions for obtaining these tools varies according"
-	echo "to your exact OS version.  On older versions of OS X, you"
-	echo "must register for an account on the apple developer tools"
-	echo "website: https://developer.apple.com/downloads to obtain the"
-	echo "download link."
-	echo
-	echo "We have gathered some additional instructions for each"
-	echo "version of OS X below. If you have trouble installing yt"
-	echo "after following these instructions, don't hesitate to contact"
-	echo "the yt user's e-mail list."
-	echo
-	echo "You can see which version of OSX you are running by clicking"
-	echo "'About This Mac' in the apple menu on the left hand side of"
-	echo "menu bar.  We're assuming that you've installed all operating"
-	echo "system updates; if you have an older version, we suggest"
-	echo "running software update and installing all available updates."
-	echo
+        echo "The instructions for obtaining these tools varies according"
+        echo "to your exact OS version.  On older versions of OS X, you"
+        echo "must register for an account on the apple developer tools"
+        echo "website: https://developer.apple.com/downloads to obtain the"
+        echo "download link."
+        echo
+        echo "We have gathered some additional instructions for each"
+        echo "version of OS X below. If you have trouble installing yt"
+        echo "after following these instructions, don't hesitate to contact"
+        echo "the yt user's e-mail list."
+        echo
+        echo "You can see which version of OSX you are running by clicking"
+        echo "'About This Mac' in the apple menu on the left hand side of"
+        echo "menu bar.  We're assuming that you've installed all operating"
+        echo "system updates; if you have an older version, we suggest"
+        echo "running software update and installing all available updates."
+        echo
         echo "OS X 10.5.8: search for and download Xcode 3.1.4 from the"
-	echo "Apple developer tools website."
+        echo "Apple developer tools website."
         echo
         echo "OS X 10.6.8: search for and download Xcode 3.2 from the Apple"
-	echo "developer tools website.  You can either download the"
-	echo "Xcode 3.2.2 Developer Tools package (744 MB) and then use"
-	echo "Software Update to update to XCode 3.2.6 or"
-	echo "alternatively, you can download the Xcode 3.2.6/iOS SDK"
-	echo "bundle (4.1 GB)."
+        echo "developer tools website.  You can either download the"
+        echo "Xcode 3.2.2 Developer Tools package (744 MB) and then use"
+        echo "Software Update to update to XCode 3.2.6 or"
+        echo "alternatively, you can download the Xcode 3.2.6/iOS SDK"
+        echo "bundle (4.1 GB)."
         echo
         echo "OS X 10.7.5: download Xcode 4.2 from the mac app store"
-	echo "(search for Xcode)."
+        echo "(search for Xcode)."
         echo "Alternatively, download the Xcode command line tools from"
         echo "the Apple developer tools website."
         echo
-	echo "OS X 10.8.4, 10.9, and 10.10: download the appropriate version of"
-	echo "Xcode from the mac app store (search for Xcode)."
-    echo
-	echo "Additionally, you will have to manually install the Xcode"
-	echo "command line tools."
-    echo
-    echo "For OS X 10.8, see:"
-   	echo "http://stackoverflow.com/questions/9353444"
-	echo
-    echo "For OS X 10.9 and 10.10, the command line tools can be installed"
-    echo "with the following command:"
-    echo "    xcode-select --install"
-    echo
-    OSX_VERSION=`sw_vers -productVersion`
-    if [ "${OSX_VERSION##10.8}" != "${OSX_VERSION}" ]
+        echo "OS X 10.8.4, 10.9, 10.10, and 10.11:"
+        echo "download the appropriate version of Xcode from the"
+        echo "mac app store (search for Xcode)."
+        echo
+        echo "Additionally, you will have to manually install the Xcode"
+        echo "command line tools."
+        echo
+        echo "For OS X 10.8, see:"
+        echo "http://stackoverflow.com/questions/9353444"
+        echo
+        echo "For OS X 10.9 and newer the command line tools can be installed"
+        echo "with the following command:"
+        echo "    xcode-select --install"
+        echo
+        echo "For OS X 10.11, you will additionally need to install the OpenSSL"
+        echo "library using a package manager like homebrew or macports."
+        echo "If you install fails with a message like"
+        echo "    ImportError: cannot import HTTPSHandler"
+        echo "then you do not have the OpenSSL headers available in a location"
+        echo "visible to your C compiler. Consider installing yt using the"
+        echo "get_yt.sh script instead, as that bundles OpenSSL."
+        OSX_VERSION=`sw_vers -productVersion`
+        if [ "${OSX_VERSION##10.8}" != "${OSX_VERSION}" ]
         then
             MPL_SUPP_CFLAGS="${MPL_SUPP_CFLAGS} -mmacosx-version-min=10.7"
             MPL_SUPP_CXXFLAGS="${MPL_SUPP_CXXFLAGS} -mmacosx-version-min=10.7"
@@ -358,17 +366,17 @@
     fi
     if [ $INST_SCIPY -eq 1 ]
     then
-	echo
-	echo "Looks like you've requested that the install script build SciPy."
-	echo
-	echo "If the SciPy build fails, please uncomment one of the the lines"
-	echo "at the top of the install script that sets NUMPY_ARGS, delete"
-	echo "any broken installation tree, and re-run the install script"
-	echo "verbatim."
-	echo
-	echo "If that doesn't work, don't hesitate to ask for help on the yt"
-	echo "user's mailing list."
-	echo
+    echo
+    echo "Looks like you've requested that the install script build SciPy."
+    echo
+    echo "If the SciPy build fails, please uncomment one of the the lines"
+    echo "at the top of the install script that sets NUMPY_ARGS, delete"
+    echo "any broken installation tree, and re-run the install script"
+    echo "verbatim."
+    echo
+    echo "If that doesn't work, don't hesitate to ask for help on the yt"
+    echo "user's mailing list."
+    echo
     fi
     if [ ! -z "${CFLAGS}" ]
     then
@@ -490,9 +498,9 @@
 
 if [ $INST_PY3 -eq 1 ]
 then
-	 PYTHON_EXEC='python3.4'
+     PYTHON_EXEC='python3.4'
 else 
-	 PYTHON_EXEC='python2.7'
+     PYTHON_EXEC='python2.7'
 fi
 
 function do_setup_py
@@ -899,28 +907,28 @@
 else
     if [ ! -e $SCIPY/done ]
     then
-	if [ ! -e BLAS/done ]
-	then
-	    tar xfz blas.tar.gz
-	    echo "Building BLAS"
-	    cd BLAS
-	    gfortran -O2 -fPIC -fno-second-underscore -c *.f
-	    ( ar r libfblas.a *.o 2>&1 ) 1>> ${LOG_FILE}
-	    ( ranlib libfblas.a 2>&1 ) 1>> ${LOG_FILE}
-	    rm -rf *.o
-	    touch done
-	    cd ..
-	fi
-	if [ ! -e $LAPACK/done ]
-	then
-	    tar xfz $LAPACK.tar.gz
-	    echo "Building LAPACK"
-	    cd $LAPACK/
-	    cp INSTALL/make.inc.gfortran make.inc
-	    ( make lapacklib OPTS="-fPIC -O2" NOOPT="-fPIC -O0" CFLAGS=-fPIC LDFLAGS=-fPIC 2>&1 ) 1>> ${LOG_FILE} || do_exit
-	    touch done
-	    cd ..
-	fi
+    if [ ! -e BLAS/done ]
+    then
+        tar xfz blas.tar.gz
+        echo "Building BLAS"
+        cd BLAS
+        gfortran -O2 -fPIC -fno-second-underscore -c *.f
+        ( ar r libfblas.a *.o 2>&1 ) 1>> ${LOG_FILE}
+        ( ranlib libfblas.a 2>&1 ) 1>> ${LOG_FILE}
+        rm -rf *.o
+        touch done
+        cd ..
+    fi
+    if [ ! -e $LAPACK/done ]
+    then
+        tar xfz $LAPACK.tar.gz
+        echo "Building LAPACK"
+        cd $LAPACK/
+        cp INSTALL/make.inc.gfortran make.inc
+        ( make lapacklib OPTS="-fPIC -O2" NOOPT="-fPIC -O0" CFLAGS=-fPIC LDFLAGS=-fPIC 2>&1 ) 1>> ${LOG_FILE} || do_exit
+        touch done
+        cd ..
+    fi
     fi
     export BLAS=$PWD/BLAS/libfblas.a
     export LAPACK=$PWD/$LAPACK/liblapack.a
@@ -1030,7 +1038,7 @@
 cd $MY_PWD
 
 if !( ( ${DEST_DIR}/bin/${PYTHON_EXEC} -c "import readline" 2>&1 )>> ${LOG_FILE}) || \
-	[[ "${MYOS##Darwin}" != "${MYOS}" && $INST_PY3 -eq 1 ]] 
+    [[ "${MYOS##Darwin}" != "${MYOS}" && $INST_PY3 -eq 1 ]] 
 then
     if !( ( ${DEST_DIR}/bin/${PYTHON_EXEC} -c "import gnureadline" 2>&1 )>> ${LOG_FILE})
     then

diff -r fdf41578efe618b66c30cb258b55a14fbfb12f3f -r bf50a317e2b28de85e33520e98fdc58cecbeab91 doc/source/analyzing/fields.rst
--- a/doc/source/analyzing/fields.rst
+++ b/doc/source/analyzing/fields.rst
@@ -374,6 +374,17 @@
 "Gas_smoothed_Temperature")``, which in most cases would be aliased to the
 field ``("gas", "temperature")`` for convenience.
 
+Other smoothing kernels besides the cubic spline one are available through a
+keyword argument ``kernel_name`` of the method ``add_smoothed_particle_field``.
+Current available kernel names include:
+
+* ``cubic``, ``quartic``, and ``quintic`` - spline kernels.
+* ``wendland2``, ``wendland4`` and ``wendland6`` - Wendland kernels.
+
+The added smoothed particle field can be accessed by
+``("deposit", "particletype_kernelname_smoothed_fieldname")`` (except for the
+cubic spline kernel, which obeys the naming scheme given above).
+
 Computing the Nth Nearest Neighbor
 ----------------------------------
 

diff -r fdf41578efe618b66c30cb258b55a14fbfb12f3f -r bf50a317e2b28de85e33520e98fdc58cecbeab91 doc/source/analyzing/generating_processed_data.rst
--- a/doc/source/analyzing/generating_processed_data.rst
+++ b/doc/source/analyzing/generating_processed_data.rst
@@ -54,10 +54,13 @@
  
 .. code-block:: python
 
-   frb.export_hdf5("my_images.h5", fields=["density","temperature"])
+   frb.save_as_dataset("my_images.h5", fields=["density","temperature"])
    frb.export_fits("my_images.fits", fields=["density","temperature"],
                    clobber=True, units="kpc")
 
+In the HDF5 case, the created file can be reloaded just like a regular dataset with
+``yt.load`` and will, itself, be a first-class dataset.  For more information on
+this, see :ref:`saving-grid-data-containers`.
 In the FITS case, there is an option for setting the ``units`` of the coordinate system in
 the file. If you want to overwrite a file with the same name, set ``clobber=True``. 
 

diff -r fdf41578efe618b66c30cb258b55a14fbfb12f3f -r bf50a317e2b28de85e33520e98fdc58cecbeab91 doc/source/analyzing/index.rst
--- a/doc/source/analyzing/index.rst
+++ b/doc/source/analyzing/index.rst
@@ -20,5 +20,6 @@
    units/index
    filtering
    generating_processed_data
+   saving_data
    time_series_analysis
    parallel_computation

diff -r fdf41578efe618b66c30cb258b55a14fbfb12f3f -r bf50a317e2b28de85e33520e98fdc58cecbeab91 doc/source/analyzing/objects.rst
--- a/doc/source/analyzing/objects.rst
+++ b/doc/source/analyzing/objects.rst
@@ -457,69 +457,9 @@
 ---------------------------
 
 Often, when operating interactively or via the scripting interface, it is
-convenient to save an object or multiple objects out to disk and then restart
-the calculation later.  For example, this is useful after clump finding 
-(:ref:`clump_finding`), which can be very time consuming.  
-Typically, the save and load operations are used on 3D data objects.  yt
-has a separate set of serialization operations for 2D objects such as
-projections.
-
-yt will save out objects to disk under the presupposition that the
-construction of the objects is the difficult part, rather than the generation
-of the data -- this means that you can save out an object as a description of
-how to recreate it in space, but not the actual data arrays affiliated with
-that object.  The information that is saved includes the dataset off of
-which the object "hangs."  It is this piece of information that is the most
-difficult; the object, when reloaded, must be able to reconstruct a dataset
-from whatever limited information it has in the save file.
-
-You can save objects to an output file using the function 
-:func:`~yt.data_objects.index.save_object`: 
-
-.. code-block:: python
-
-   import yt
-   ds = yt.load("my_data")
-   sp = ds.sphere([0.5, 0.5, 0.5], (10.0, 'kpc'))
-   sp.save_object("sphere_name", "save_file.cpkl")
-
-This will store the object as ``sphere_name`` in the file
-``save_file.cpkl``, which will be created or accessed using the standard
-python module :mod:`shelve`.  
-
-To re-load an object saved this way, you can use the shelve module directly:
-
-.. code-block:: python
-
-   import yt
-   import shelve
-   ds = yt.load("my_data") 
-   saved_fn = shelve.open("save_file.cpkl")
-   ds, sp = saved_fn["sphere_name"]
-
-Additionally, we can store multiple objects in a single shelve file, so we 
-have to call the sphere by name.
-
-For certain data objects such as projections, serialization can be performed
-automatically if ``serialize`` option is set to ``True`` in :ref:`the
-configuration file <configuration-file>` or set directly in the script:
-
-.. code-block:: python
-
-   from yt.config import ytcfg; ytcfg["yt", "serialize"] = "True"
-
-.. note:: Use serialization with caution. Enabling serialization means that
-   once a projection of a dataset has been created (and stored in the .yt file
-   in the same directory), any subsequent changes to that dataset will be
-   ignored when attempting to create the same projection. So if you take a
-   density projection of your dataset in the 'x' direction, then somehow tweak
-   that dataset significantly, and take the density projection again, yt will
-   default to finding the original projection and 
-   :ref:`not your new one <faq-old-data>`.
-
-.. note:: It's also possible to use the standard :mod:`cPickle` module for
-          loading and storing objects -- so in theory you could even save a
-          list of objects!
-
-This method works for clumps, as well, and the entire clump index will be
-stored and restored upon load.
+convenient to save an object to disk and then restart the calculation later or
+transfer the data from a container to another filesystem.  This can be
+particularly useful when working with extremely large datasets.  Field data
+can be saved to disk in a format that allows for it to be reloaded just like
+a regular dataset.  For information on how to do this, see
+:ref:`saving-data-containers`.

diff -r fdf41578efe618b66c30cb258b55a14fbfb12f3f -r bf50a317e2b28de85e33520e98fdc58cecbeab91 doc/source/analyzing/saving_data.rst
--- /dev/null
+++ b/doc/source/analyzing/saving_data.rst
@@ -0,0 +1,243 @@
+.. _saving_data
+
+Saving Reloadable Data
+======================
+
+Most of the data loaded into or generated with yt can be saved to a
+format that can be reloaded as a first-class dataset.  This includes
+the following:
+
+  * geometric data containers (regions, spheres, disks, rays, etc.)
+
+  * grid data containers (covering grids, arbitrary grids, fixed
+    resolution buffers)
+
+  * spatial plots (projections, slices, cutting planes)
+
+  * profiles
+
+  * generic array data
+
+In the case of projections, slices, and profiles, reloaded data can be
+used to remake plots.  For information on this, see :ref:`remaking-plots`.
+
+.. _saving-data-containers:
+
+Geometric Data Containers
+-------------------------
+
+Data from geometric data containers can be saved with the
+:func:`~yt.data_objects.data_containers.save_as_dataset`` function.
+
+.. notebook-cell::
+
+   import yt
+   ds = yt.load("enzo_tiny_cosmology/DD0046/DD0046")
+
+   sphere = ds.sphere([0.5]*3, (10, "Mpc"))
+   fn = sphere.save_as_dataset(fields=["density", "particle_mass"])
+   print (fn)
+
+This function will return the name of the file to which the dataset
+was saved.  The filename will be a combination of the name of the
+original dataset and the type of data container.  Optionally, a
+specific filename can be given with the ``filename`` keyword.  If no
+fields are given, the fields that have previously been queried will
+be saved.
+
+The newly created dataset can be loaded like all other supported
+data through ``yt.load``.  Once loaded, field data can be accessed
+through the traditional data containers or through the ``data``
+attribute, which will be a data container configured like the
+original data container used to make the dataset.  Grid data is
+accessed by the ``grid`` data type and particle data is accessed
+with the original particle type.  As with the original dataset, grid
+positions and cell sizes are accessible with, for example,
+("grid", "x") and ("grid", "dx").  Particle positions are
+accessible as (<particle_type>, "particle_position_x").  All original
+simulation parameters are accessible in the ``parameters``
+dictionary, normally associated with all datasets.
+
+.. code-block:: python
+
+   sphere_ds = yt.load("DD0046_sphere.h5")
+
+   # use the original data container
+   print (sphere_ds.data["grid", "density"])
+
+   # create a new data container
+   ad = sphere_ds.all_data()
+
+   # grid data
+   print (ad["grid", "density"])
+   print (ad["grid", "x"])
+   print (ad["grid", "dx"])
+
+   # particle data
+   print (ad["all", "particle_mass"])
+   print (ad["all", "particle_position_x"])
+
+Note that because field data queried from geometric containers is
+returned as unordered 1D arrays, data container datasets are treated,
+effectively, as particle data.  Thus, 3D indexing of grid data from
+these datasets is not possible.
+
+.. _saving-grid-data-containers:
+
+Grid Data Containers
+--------------------
+
+Data containers that return field data as multidimensional arrays
+can be saved so as to preserve this type of access.  This includes
+covering grids, arbitrary grids, and fixed resolution buffers.
+Saving data from these containers works just as with geometric data
+containers.  Field data can be accessed through geometric data
+containers.
+
+.. code-block:: python
+
+   cg = ds.covering_grid(level=0, left_edge=[0.25]*3, dims=[16]*3)
+   fn = cg.save_as_dataset(fields=["density", "particle_mass"])
+
+   cg_ds = yt.load(fn)
+   ad = cg_ds.all_data()
+   print (ad["grid", "density"])
+
+Multidimensional indexing of field data is also available through
+the ``data`` attribute.
+
+.. code-block:: python
+
+   print (cg_ds.data["grid", "density"])
+
+Fixed resolution buffers work just the same.
+
+.. code-block:: python
+
+   my_proj = ds.proj("density", "x", weight_field="density")
+   frb = my_proj.to_frb(1.0, (800, 800))
+   fn = frb.save_as_dataset(fields=["density"])
+   frb_ds = yt.load(fn)
+   print (frb_ds.data["density"])
+
+.. _saving-spatial-plots:
+
+Spatial Plots
+-------------
+
+Spatial plots, such as projections, slices, and off-axis slices
+(cutting planes) can also be saved and reloaded.
+
+.. code-block:: python
+
+   proj = ds.proj("density", "x", weight_field="density")
+   proj.save_as_dataset()
+
+Once reloaded, they can be handed to their associated plotting
+functions to make images.
+
+.. code-block:: python
+
+   proj_ds = yt.load("DD0046_proj.h5")
+   p = yt.ProjectionPlot(proj_ds, "x", "density",
+                         weight_field="density")
+   p.save()
+
+.. _saving-profile-data:
+
+Profiles
+--------
+
+Profiles created with :func:`~yt.data_objects.profiles.create_profile`,
+:class:`~yt.visualization.profile_plotter.ProfilePlot`, and
+:class:`~yt.visualization.profile_plotter.PhasePlot` can be saved with
+the :func:`~yt.data_objects.profiles.save_as_dataset` function, which
+works just as above.  Profile datasets are a type of non-spatial grid
+datasets.  Geometric selection is not possible, but data can be
+accessed through the ``.data`` attribute.
+
+.. notebook-cell::
+
+   import yt
+   ds = yt.load("enzo_tiny_cosmology/DD0046/DD0046")
+   ad = ds.all_data()
+
+   profile_2d = yt.create_profile(ad, ["density", "temperature"],
+                                  "cell_mass", weight_field=None,
+                                  n_bins=(128, 128))
+   profile_2d.save_as_dataset()
+
+   prof_2d_ds = yt.load("DD0046_Profile2D.h5")
+   print (prof_2d_ds.data["cell_mass"])
+
+The x, y (if at least 2D), and z (if 3D) bin fields can be accessed as 1D
+arrays with "x", "y", and "z".
+
+.. code-block:: python
+
+   print (prof_2d_ds.data["x"])
+
+The bin fields can also be returned with the same shape as the profile
+data by accessing them with their original names.  This allows for
+boolean masking of profile data using the bin fields.
+
+.. code-block:: python
+
+   # density is the x bin field
+   print (prof_2d_ds.data["density"])
+
+For 1, 2, and 3D profile datasets, a fake profile object will be
+constructed by accessing the ".profile" attribute.  This is used
+primarily in the case of 1 and 2D profiles to create figures using
+:class:`~yt.visualization.profile_plotter.ProfilePlot` and
+:class:`~yt.visualization.profile_plotter.PhasePlot`.
+
+.. code-block:: python
+
+   p = yt.PhasePlot(prof_2d_ds.data, "density", "temperature",
+                    "cell_mass", weight_field=None)
+   p.save()
+
+.. _saving-array-data:
+
+Generic Array Data
+------------------
+
+Generic arrays can be saved and reloaded as non-spatial data using
+the :func:`~yt.frontends.ytdata.utilities.save_as_dataset` function,
+also available as ``yt.save_as_dataset``.  As with profiles, geometric
+selection is not possible, but the data can be accessed through the
+``.data`` attribute.
+
+.. notebook-cell::
+
+   import yt
+   ds = yt.load("enzo_tiny_cosmology/DD0046/DD0046")
+
+   region = ds.box([0.25]*3, [0.75]*3)
+   sphere = ds.sphere(ds.domain_center, (10, "Mpc"))
+   my_data = {}
+   my_data["region_density"] = region["density"]
+   my_data["sphere_density"] = sphere["density"]
+   yt.save_as_dataset(ds, "test_data.h5", my_data)
+
+   array_ds = yt.load("test_data.h5")
+   print (array_ds.data["region_density"])
+   print (array_ds.data["sphere_density"])
+
+Array data can be saved with or without a dataset loaded.  If no
+dataset has been loaded, as fake dataset can be provided as a
+dictionary.
+
+.. notebook-cell::
+
+   import numpy as np
+   import yt
+
+   my_data = {"density": yt.YTArray(np.random.random(10), "g/cm**3"),
+              "temperature": yt.YTArray(np.random.random(10), "K")}
+   fake_ds = {"current_time": yt.YTQuantity(10, "Myr")}
+   yt.save_as_dataset(fake_ds, "random_data.h5", my_data)
+
+   new_ds = yt.load("random_data.h5")
+   print (new_ds.data["density"])

diff -r fdf41578efe618b66c30cb258b55a14fbfb12f3f -r bf50a317e2b28de85e33520e98fdc58cecbeab91 doc/source/installing.rst
--- a/doc/source/installing.rst
+++ b/doc/source/installing.rst
@@ -290,14 +290,14 @@
 
 .. code-block:: bash
 
-  $ pip install -r requirements.txt
+  $ pip install numpy matplotlib cython cython h5py nose sympy
 
 If you're using IPython notebooks, you can install its dependencies
 with ``pip`` as well:
 
 .. code-block:: bash
 
-  $ pip install -r optional-requirements.txt
+  $ pip install ipython[notebook]
 
 From here, you can use ``pip`` (which comes with ``Python``) to install the latest
 stable version of yt:

diff -r fdf41578efe618b66c30cb258b55a14fbfb12f3f -r bf50a317e2b28de85e33520e98fdc58cecbeab91 doc/source/reference/api/api.rst
--- a/doc/source/reference/api/api.rst
+++ b/doc/source/reference/api/api.rst
@@ -72,6 +72,7 @@
 .. autosummary::
    :toctree: generated/
 
+   ~yt.data_objects.data_containers.YTDataContainer
    ~yt.data_objects.data_containers.YTSelectionContainer
    ~yt.data_objects.data_containers.YTSelectionContainer0D
    ~yt.data_objects.data_containers.YTSelectionContainer1D
@@ -383,6 +384,28 @@
    ~yt.frontends.stream.io.IOHandlerStreamOctree
    ~yt.frontends.stream.io.StreamParticleIOHandler
 
+ytdata
+^^^^^^
+
+.. autosummary::
+   :toctree: generated/
+
+   ~yt.frontends.ytdata.data_structures.YTDataContainerDataset
+   ~yt.frontends.ytdata.data_structures.YTSpatialPlotDataset
+   ~yt.frontends.ytdata.data_structures.YTGridDataset
+   ~yt.frontends.ytdata.data_structures.YTGridHierarchy
+   ~yt.frontends.ytdata.data_structures.YTGrid
+   ~yt.frontends.ytdata.data_structures.YTNonspatialDataset
+   ~yt.frontends.ytdata.data_structures.YTNonspatialHierarchy
+   ~yt.frontends.ytdata.data_structures.YTNonspatialGrid
+   ~yt.frontends.ytdata.data_structures.YTProfileDataset
+   ~yt.frontends.ytdata.fields.YTDataContainerFieldInfo
+   ~yt.frontends.ytdata.fields.YTGridFieldInfo
+   ~yt.frontends.ytdata.io.IOHandlerYTDataContainerHDF5
+   ~yt.frontends.ytdata.io.IOHandlerYTGridHDF5
+   ~yt.frontends.ytdata.io.IOHandlerYTSpatialPlotHDF5
+   ~yt.frontends.ytdata.io.IOHandlerYTNonspatialhdf5
+
 Loading Data
 ------------
 
@@ -761,6 +784,7 @@
    :toctree: generated/
 
    ~yt.convenience.load
+   ~yt.frontends.ytdata.utilities.save_as_dataset
    ~yt.data_objects.static_output.Dataset.all_data
    ~yt.data_objects.static_output.Dataset.box
    ~yt.funcs.deprecate

diff -r fdf41578efe618b66c30cb258b55a14fbfb12f3f -r bf50a317e2b28de85e33520e98fdc58cecbeab91 doc/source/visualizing/plots.rst
--- a/doc/source/visualizing/plots.rst
+++ b/doc/source/visualizing/plots.rst
@@ -1285,6 +1285,81 @@
    bananas_Slice_z_kT.eps
    bananas_Slice_z_density.eps
 
+.. _remaking-plots:
+
+Remaking Figures from Plot Datasets
+-----------------------------------
+
+When working with datasets that are too large to be stored locally,
+making figures just right can be cumbersome as it requires continuously
+moving images somewhere they can be viewed.  However, image creation is
+actually a two step process of first creating the projection, slice,
+or profile object, and then converting that object into an actual image.
+Fortunately, the hard part (creating slices, projections, profiles) can
+be separated from the easy part (generating images).  The intermediate
+slice, projection, and profile objects can be saved as reloadable
+datasets, then handed back to the plotting machinery discussed here.
+
+For slices and projections, the savable object is associated with the
+plot object as ``data_source``.  This can be saved with the
+:func:`~yt.data_objects.data_containers.save_as_dataset`` function.  For
+more information, see :ref:`saving_data`.
+
+.. code-block:: python
+
+   p = yt.ProjectionPlot(ds, "x", "density",
+                         weight_field="density")
+   fn = p.data_source.save_as_dataset()
+
+This function will optionally take a ``filename`` keyword that follows
+the same logic as dicussed above in :ref:`saving_plots`.  The filename
+to which the dataset was written will be returned.
+
+Once saved, this file can be reloaded completely independently of the
+original dataset and given back to the plot function with the same
+arguments.  One can now continue to tweak the figure to one's liking.
+
+.. code-block:: python
+
+   new_ds = yt.load(fn)
+   new_p = yt.ProjectionPlot(new_ds, "x", "density",
+                             weight_field="density")
+   new_p.save()
+
+The same functionality is available for profile and phase plots.  In
+each case, a special data container, ``data``, is given to the plotting
+functions.
+
+For ``ProfilePlot``:
+
+.. code-block:: python
+
+   ad = ds.all_data()
+   p1 = yt.ProfilePlot(ad, "density", "temperature",
+                       weight_field="cell_mass")
+
+   # note that ProfilePlots can hold a list of profiles
+   fn = p1.profiles[0].save_as_dataset()
+
+   new_ds = yt.load(fn)
+   p2 = yt.ProfilePlot(new_ds.data, "density", "temperature",
+                       weight_field="cell_mass")
+   p2.save()
+
+For ``PhasePlot``:
+
+.. code-block:: python
+
+   ad = ds.all_data()
+   p1 = yt.PhasePlot(ad, "density", "temperature",
+                     "cell_mass", weight_field=None)
+   fn = p1.profile.save_as_dataset()
+
+   new_ds = yt.load(fn)
+   p2 = yt.PhasePlot(new_ds.data, "density", "temperature",
+                     "cell_mass", weight_field=None)
+   p2.save()
+
 .. _eps-writer:
 
 Publication-ready Figures

diff -r fdf41578efe618b66c30cb258b55a14fbfb12f3f -r bf50a317e2b28de85e33520e98fdc58cecbeab91 yt/__init__.py
--- a/yt/__init__.py
+++ b/yt/__init__.py
@@ -138,6 +138,9 @@
     load_particles, load_hexahedral_mesh, load_octree, \
     hexahedral_connectivity, load_unstructured_mesh
 
+from yt.frontends.ytdata.api import \
+    save_as_dataset
+
 # For backwards compatibility
 GadgetDataset = frontends.gadget.GadgetDataset
 GadgetStaticOutput = deprecated_class(GadgetDataset)

diff -r fdf41578efe618b66c30cb258b55a14fbfb12f3f -r bf50a317e2b28de85e33520e98fdc58cecbeab91 yt/analysis_modules/absorption_spectrum/absorption_spectrum.py
--- a/yt/analysis_modules/absorption_spectrum/absorption_spectrum.py
+++ b/yt/analysis_modules/absorption_spectrum/absorption_spectrum.py
@@ -20,6 +20,7 @@
 
 from .absorption_line import tau_profile
 
+from yt.convenience import load
 from yt.funcs import get_pbar, mylog
 from yt.units.yt_array import YTArray, YTQuantity
 from yt.utilities.physical_constants import \
@@ -121,8 +122,8 @@
         Parameters
         ----------
 
-        input_file : string
-           path to input ray data.
+        input_file : string or dataset
+           path to input ray data or a loaded ray dataset
         output_file : optional, string
            path for output file.  File formats are chosen based on the
            filename extension.  ``.h5`` for hdf5, ``.fits`` for fits,
@@ -156,7 +157,6 @@
 
         input_fields = ['dl', 'redshift', 'temperature']
         field_units = {"dl": "cm", "redshift": "", "temperature": "K"}
-        field_data = {}
         if use_peculiar_velocity:
             input_fields.append('velocity_los')
             input_fields.append('redshift_eff')
@@ -167,10 +167,11 @@
                 input_fields.append(feature['field_name'])
                 field_units[feature["field_name"]] = "cm**-3"
 
-        input = h5py.File(input_file, 'r')
-        for field in input_fields:
-            field_data[field] = YTArray(input[field].value, field_units[field])
-        input.close()
+        if isinstance(input_file, str):
+            input_ds = load(input_file)
+        else:
+            input_ds = input_file
+        field_data = input_ds.all_data()
 
         self.tau_field = np.zeros(self.lambda_bins.size)
         self.spectrum_line_list = []
@@ -337,6 +338,8 @@
         """
         Write out list of spectral lines.
         """
+        if filename is None:
+            return
         mylog.info("Writing spectral line list: %s." % filename)
         self.spectrum_line_list.sort(key=lambda obj: obj['wavelength'])
         f = open(filename, 'w')

diff -r fdf41578efe618b66c30cb258b55a14fbfb12f3f -r bf50a317e2b28de85e33520e98fdc58cecbeab91 yt/analysis_modules/cosmological_observation/light_ray/light_ray.py
--- a/yt/analysis_modules/cosmological_observation/light_ray/light_ray.py
+++ b/yt/analysis_modules/cosmological_observation/light_ray/light_ray.py
@@ -13,19 +13,20 @@
 # The full license is in the file COPYING.txt, distributed with this software.
 #-----------------------------------------------------------------------------
 
-from yt.utilities.on_demand_imports import _h5py as h5py
 import numpy as np
 
 from yt.analysis_modules.cosmological_observation.cosmology_splice import \
     CosmologySplice
 from yt.convenience import \
     load
-from yt.funcs import \
-    mylog
+from yt.frontends.ytdata.utilities import \
+    save_as_dataset
 from yt.units.yt_array import \
     YTArray
 from yt.utilities.cosmology import \
     Cosmology
+from yt.utilities.logger import \
+    ytLogger as mylog
 from yt.utilities.parallel_tools.parallel_analysis_interface import \
     parallel_objects, \
     parallel_root_only
@@ -48,7 +49,7 @@
     synthetic QSO lines of sight.
 
     Light rays can also be made from single datasets.
-    
+
     Once the LightRay object is set up, use LightRay.make_light_ray to
     begin making rays.  Different randomizations can be created with a
     single object by providing different random seeds to make_light_ray.
@@ -58,17 +59,17 @@
     parameter_filename : string
         The path to the simulation parameter file or dataset.
     simulation_type : optional, string
-        The simulation type.  If None, the first argument is assumed to 
+        The simulation type.  If None, the first argument is assumed to
         refer to a single dataset.
         Default: None
     near_redshift : optional, float
-        The near (lowest) redshift for a light ray containing multiple 
-        datasets.  Do not use is making a light ray from a single 
+        The near (lowest) redshift for a light ray containing multiple
+        datasets.  Do not use is making a light ray from a single
         dataset.
         Default: None
     far_redshift : optional, float
-        The far (highest) redshift for a light ray containing multiple 
-        datasets.  Do not use is making a light ray from a single 
+        The far (highest) redshift for a light ray containing multiple
+        datasets.  Do not use is making a light ray from a single
         dataset.
         Default: None
     use_minimum_datasets : optional, bool
@@ -98,11 +99,11 @@
         datasets for time series.
         Default: True.
     find_outputs : optional, bool
-        Whether or not to search for datasets in the current 
+        Whether or not to search for datasets in the current
         directory.
         Default: False.
     load_kwargs : optional, dict
-        Optional dictionary of kwargs to be passed to the "load" 
+        Optional dictionary of kwargs to be passed to the "load"
         function, appropriate for use of certain frontends.  E.g.
         Tipsy using "bounding_box"
         Gadget using "unit_base", etc.
@@ -129,8 +130,9 @@
         self.light_ray_solution = []
         self._data = {}
 
-        # Make a light ray from a single, given dataset.        
+        # Make a light ray from a single, given dataset.
         if simulation_type is None:
+            self.simulation_type = simulation_type
             ds = load(parameter_filename, **self.load_kwargs)
             if ds.cosmological_simulation:
                 redshift = ds.current_redshift
@@ -156,7 +158,7 @@
                                            time_data=time_data,
                                            redshift_data=redshift_data)
 
-    def _calculate_light_ray_solution(self, seed=None, 
+    def _calculate_light_ray_solution(self, seed=None,
                                       start_position=None, end_position=None,
                                       trajectory=None, filename=None):
         "Create list of datasets to be added together to make the light ray."
@@ -172,9 +174,9 @@
             if not ((end_position is None) ^ (trajectory is None)):
                 raise RuntimeError("LightRay Error: must specify either end_position " + \
                                    "or trajectory, but not both.")
-            self.light_ray_solution[0]['start'] = np.array(start_position)
+            self.light_ray_solution[0]['start'] = np.asarray(start_position)
             if end_position is not None:
-                self.light_ray_solution[0]['end'] = np.array(end_position)
+                self.light_ray_solution[0]['end'] = np.asarray(end_position)
             else:
                 # assume trajectory given as r, theta, phi
                 if len(trajectory) != 3:
@@ -185,12 +187,12 @@
                                 np.sin(phi) * np.sin(theta),
                                 np.cos(theta)])
             self.light_ray_solution[0]['traversal_box_fraction'] = \
-              vector_length(self.light_ray_solution[0]['start'], 
+              vector_length(self.light_ray_solution[0]['start'],
                             self.light_ray_solution[0]['end'])
 
         # the normal way (random start positions and trajectories for each dataset)
         else:
-            
+
             # For box coherence, keep track of effective depth travelled.
             box_fraction_used = 0.0
 
@@ -285,15 +287,15 @@
             Default: None.
         trajectory : optional, list of floats
             Used only if creating a light ray from a single dataset.
-            The (r, theta, phi) direction of the light ray.  Use either 
+            The (r, theta, phi) direction of the light ray.  Use either
             end_position or trajectory, not both.
             Default: None.
         fields : optional, list
             A list of fields for which to get data.
             Default: None.
         setup_function : optional, callable, accepts a ds
-            This function will be called on each dataset that is loaded 
-            to create the light ray.  For, example, this can be used to 
+            This function will be called on each dataset that is loaded
+            to create the light ray.  For, example, this can be used to
             add new derived fields.
             Default: None.
         solution_filename : optional, string
@@ -308,13 +310,13 @@
             each point in the ray.
             Default: True.
         redshift : optional, float
-            Used with light rays made from single datasets to specify a 
-            starting redshift for the ray.  If not used, the starting 
-            redshift will be 0 for a non-cosmological dataset and 
+            Used with light rays made from single datasets to specify a
+            starting redshift for the ray.  If not used, the starting
+            redshift will be 0 for a non-cosmological dataset and
             the dataset redshift for a cosmological dataset.
             Default: None.
         njobs : optional, int
-            The number of parallel jobs over which the segments will 
+            The number of parallel jobs over which the segments will
             be split.  Choose -1 for one processor per segment.
             Default: -1.
 
@@ -322,7 +324,7 @@
         --------
 
         Make a light ray from multiple datasets:
-        
+
         >>> import yt
         >>> from yt.analysis_modules.cosmological_observation.light_ray.api import \
         ...     LightRay
@@ -348,12 +350,12 @@
         ...                       data_filename="my_ray.h5",
         ...                       fields=["temperature", "density"],
         ...                       get_los_velocity=True)
-        
+
         """
 
         # Calculate solution.
-        self._calculate_light_ray_solution(seed=seed, 
-                                           start_position=start_position, 
+        self._calculate_light_ray_solution(seed=seed,
+                                           start_position=start_position,
                                            end_position=end_position,
                                            trajectory=trajectory,
                                            filename=solution_filename)
@@ -364,6 +366,8 @@
         data_fields = fields[:]
         all_fields = fields[:]
         all_fields.extend(['dl', 'dredshift', 'redshift'])
+        all_fields.extend(['x', 'y', 'z', 'dx', 'dy', 'dz'])
+        data_fields.extend(['x', 'y', 'z', 'dx', 'dy', 'dz'])
         if get_los_velocity:
             all_fields.extend(['velocity_x', 'velocity_y',
                                'velocity_z', 'velocity_los', 'redshift_eff'])
@@ -399,10 +403,15 @@
             if not ds.cosmological_simulation:
                 next_redshift = my_segment["redshift"]
             elif self.near_redshift == self.far_redshift:
+                if isinstance(my_segment["traversal_box_fraction"], YTArray):
+                    segment_length = \
+                      my_segment["traversal_box_fraction"].in_units("Mpccm / h")
+                else:
+                    segment_length = my_segment["traversal_box_fraction"] * \
+                      ds.domain_width[0].in_units("Mpccm / h")
                 next_redshift = my_segment["redshift"] - \
-                  self._deltaz_forward(my_segment["redshift"], 
-                                       ds.domain_width[0].in_units("Mpccm / h") *
-                                       my_segment["traversal_box_fraction"])
+                  self._deltaz_forward(my_segment["redshift"],
+                                       segment_length)
             elif my_segment.get("next", None) is None:
                 next_redshift = self.near_redshift
             else:
@@ -454,7 +463,7 @@
 
             # Get redshift for each lixel.  Assume linear relation between l and z.
             sub_data['dredshift'] = (my_segment['redshift'] - next_redshift) * \
-                (sub_data['dl'] / vector_length(my_segment['start'], 
+                (sub_data['dl'] / vector_length(my_segment['start'],
                                                 my_segment['end']).in_cgs())
             sub_data['redshift'] = my_segment['redshift'] - \
               sub_data['dredshift'].cumsum() + sub_data['dredshift']
@@ -500,12 +509,17 @@
         # Flatten the list into a single dictionary containing fields
         # for the whole ray.
         all_data = _flatten_dict_list(all_data, exceptions=['segment_redshift'])
+        self._data = all_data
 
         if data_filename is not None:
             self._write_light_ray(data_filename, all_data)
+            ray_ds = load(data_filename)
+            return ray_ds
+        else:
+            return None
 
-        self._data = all_data
-        return all_data
+    def __getitem__(self, field):
+        return self._data[field]
 
     @parallel_root_only
     def _write_light_ray(self, filename, data):
@@ -514,19 +528,24 @@
 
         Write light ray data to hdf5 file.
         """
-
-        mylog.info("Saving light ray data to %s." % filename)
-        output = h5py.File(filename, 'w')
-        for field in data.keys():
-            # if the field is a tuple, only use the second part of the tuple
-            # in the hdf5 output (i.e. ('gas', 'density') -> 'density')
-            if isinstance(field, tuple):
-                fieldname = field[1]
-            else:
-                fieldname = field
-            output.create_dataset(fieldname, data=data[field])
-            output[fieldname].attrs["units"] = str(data[field].units)
-        output.close()
+        if self.simulation_type is None:
+            ds = load(self.parameter_filename, **self.load_kwargs)
+        else:
+            ds = {}
+            ds["dimensionality"] = self.simulation.dimensionality
+            ds["domain_left_edge"] = self.simulation.domain_left_edge
+            ds["domain_right_edge"] = self.simulation.domain_right_edge
+            ds["cosmological_simulation"] = self.simulation.cosmological_simulation
+            ds["periodicity"] = (True, True, True)
+            ds["current_redshift"] = self.near_redshift
+            for attr in ["omega_lambda", "omega_matter", "hubble_constant"]:
+                ds[attr] = getattr(self.cosmology, attr)
+            ds["current_time"] = \
+              self.cosmology.t_from_z(ds["current_redshift"])
+        extra_attrs = {"data_type": "yt_light_ray"}
+        field_types = dict([(field, "grid") for field in data.keys()])
+        save_as_dataset(ds, filename, data, field_types=field_types,
+                        extra_attrs=extra_attrs)
 
     @parallel_root_only
     def _write_light_ray_solution(self, filename, extra_info=None):
@@ -573,7 +592,7 @@
 def vector_length(start, end):
     """
     vector_length(start, end)
-    
+
     Calculate vector length.
     """
 
@@ -600,15 +619,15 @@
     """
     periodic_ray(start, end, left=None, right=None)
 
-    Break up periodic ray into non-periodic segments. 
+    Break up periodic ray into non-periodic segments.
     Accepts start and end points of periodic ray as YTArrays.
     Accepts optional left and right edges of periodic volume as YTArrays.
-    Returns a list of lists of coordinates, where each element of the 
-    top-most list is a 2-list of start coords and end coords of the 
-    non-periodic ray: 
+    Returns a list of lists of coordinates, where each element of the
+    top-most list is a 2-list of start coords and end coords of the
+    non-periodic ray:
 
-    [[[x0start,y0start,z0start], [x0end, y0end, z0end]], 
-     [[x1start,y1start,z1start], [x1end, y1end, z1end]], 
+    [[[x0start,y0start,z0start], [x0end, y0end, z0end]],
+     [[x1start,y1start,z1start], [x1end, y1end, z1end]],
      ...,]
 
     """

diff -r fdf41578efe618b66c30cb258b55a14fbfb12f3f -r bf50a317e2b28de85e33520e98fdc58cecbeab91 yt/analysis_modules/halo_analysis/halo_callbacks.py
--- a/yt/analysis_modules/halo_analysis/halo_callbacks.py
+++ b/yt/analysis_modules/halo_analysis/halo_callbacks.py
@@ -21,6 +21,9 @@
     periodic_distance
 from yt.data_objects.profiles import \
     create_profile
+from yt.frontends.ytdata.utilities import \
+    _hdf5_yt_array, \
+    _yt_array_hdf5
 from yt.units.yt_array import \
     YTArray
 from yt.utilities.exceptions import \
@@ -584,21 +587,3 @@
     del sphere
     
 add_callback("iterative_center_of_mass", iterative_center_of_mass)
-
-def _yt_array_hdf5(fh, fieldname, data):
-    dataset = fh.create_dataset(fieldname, data=data)
-    units = ""
-    if isinstance(data, YTArray):
-        units = str(data.units)
-    dataset.attrs["units"] = units
-
-def _hdf5_yt_array(fh, fieldname, ds=None):
-    if ds is None:
-        new_arr = YTArray
-    else:
-        new_arr = ds.arr
-    units = ""
-    if "units" in fh[fieldname].attrs:
-        units = fh[fieldname].attrs["units"]
-    if units == "dimensionless": units = ""
-    return new_arr(fh[fieldname].value, units)

diff -r fdf41578efe618b66c30cb258b55a14fbfb12f3f -r bf50a317e2b28de85e33520e98fdc58cecbeab91 yt/analysis_modules/sunyaev_zeldovich/projection.py
--- a/yt/analysis_modules/sunyaev_zeldovich/projection.py
+++ b/yt/analysis_modules/sunyaev_zeldovich/projection.py
@@ -106,7 +106,7 @@
         self.display_names["Tau"] = r"$\mathrm{\tau}$"
 
         for f, field in zip(self.freqs, self.freq_fields):
-            self.display_names[field] = r"$\mathrm{\Delta{I}_{%d\ GHz}}$" % (int(f))
+            self.display_names[field] = r"$\mathrm{\Delta{I}_{%d\ GHz}}$" % int(f)
 
     def on_axis(self, axis, center="c", width=(1, "unitary"), nx=800, source=None):
         r""" Make an on-axis projection of the SZ signal.
@@ -125,9 +125,26 @@
             as a tuple containing a coordinate and string unit name or by passing
             in a YTArray. If a list or unitless array is supplied, code units are
             assumed.
-        width : float, tuple, or YTQuantity.
-            The width of the projection. A float will assume the width is in code units.
-            A (value, unit) tuple or YTQuantity allows for the units of the width to be specified.
+        width : tuple or a float.
+            Width can have four different formats to support windows with variable
+            x and y widths.  They are:
+
+            ==================================     =======================
+            format                                 example
+            ==================================     =======================
+            (float, string)                        (10,'kpc')
+            ((float, string), (float, string))     ((10,'kpc'),(15,'kpc'))
+            float                                  0.2
+            (float, float)                         (0.2, 0.3)
+            ==================================     =======================
+
+            For example, (10, 'kpc') requests a plot window that is 10 kiloparsecs
+            wide in the x and y directions, ((10,'kpc'),(15,'kpc')) requests a
+            window that is 10 kiloparsecs wide along the x axis and 15
+            kiloparsecs wide along the y axis.  In the other two examples, code
+            units are assumed, for example (0.2, 0.3) requests a plot that has an
+            x width of 0.2 and a y width of 0.3 in code units.  If units are
+            provided the resulting plot axis labels will use the supplied units.
         nx : integer, optional
             The dimensions on a side of the projection image.
         source : yt.data_objects.data_containers.YTSelectionContainer, optional
@@ -141,7 +158,7 @@
         ctr, dctr = self.ds.coordinates.sanitize_center(center, axis)
         width = self.ds.coordinates.sanitize_width(axis, width, None)
 
-        L = np.zeros((3))
+        L = np.zeros(3)
         L[axis] = 1.0
 
         beta_par = generate_beta_par(L)
@@ -174,7 +191,8 @@
 
         self.ds.field_info.pop(("gas","beta_par"))
 
-    def off_axis(self, L, center="c", width=(1, "unitary"), nx=800, source=None):
+    def off_axis(self, L, center="c", width=(1.0, "unitary"), depth=(1.0,"unitary"),
+                 nx=800, nz=800, north_vector=None, no_ghost=False, source=None):
         r""" Make an off-axis projection of the SZ signal.
 
         Parameters
@@ -191,11 +209,46 @@
             as a tuple containing a coordinate and string unit name or by passing
             in a YTArray. If a list or unitless array is supplied, code units are
             assumed.
-        width : float, tuple, or YTQuantity.
-            The width of the projection. A float will assume the width is in code units.
-            A (value, unit) tuple or YTQuantity allows for the units of the width to be specified.
+        width : tuple or a float.
+            Width can have four different formats to support windows with variable
+            x and y widths.  They are:
+
+            ==================================     =======================
+            format                                 example
+            ==================================     =======================
+            (float, string)                        (10,'kpc')
+            ((float, string), (float, string))     ((10,'kpc'),(15,'kpc'))
+            float                                  0.2
+            (float, float)                         (0.2, 0.3)
+            ==================================     =======================
+
+            For example, (10, 'kpc') requests a plot window that is 10 kiloparsecs
+            wide in the x and y directions, ((10,'kpc'),(15,'kpc')) requests a
+            window that is 10 kiloparsecs wide along the x axis and 15
+            kiloparsecs wide along the y axis.  In the other two examples, code
+            units are assumed, for example (0.2, 0.3) requests a plot that has an
+            x width of 0.2 and a y width of 0.3 in code units.  If units are
+            provided the resulting plot axis labels will use the supplied units.
+        depth : A tuple or a float
+            A tuple containing the depth to project through and the string
+            key of the unit: (width, 'unit').  If set to a float, code units
+            are assumed
         nx : integer, optional
             The dimensions on a side of the projection image.
+        nz : integer, optional
+            The number of elements along the integration path length.
+        north_vector : a sequence of floats
+            A vector defining the 'up' direction in the plot.  This
+            option sets the orientation of the slicing plane.  If not
+            set, an arbitrary grid-aligned north-vector is chosen.
+        no_ghost: bool, optional
+            Optimization option for off-axis cases. If True, homogenized bricks will
+            extrapolate out from grid instead of interpolating from
+            ghost zones that have to first be calculated.  This can
+            lead to large speed improvements, but at a loss of
+            accuracy/smoothness in resulting image.  The effects are
+            less notable when the transfer function is smooth and
+            broad. Default: True
         source : yt.data_objects.data_containers.YTSelectionContainer, optional
             If specified, this will be the data source used for selecting regions to project.
             Currently unsupported in yt 2.x.
@@ -205,13 +258,10 @@
         >>> L = np.array([0.5, 1.0, 0.75])
         >>> szprj.off_axis(L, center="c", width=(2.0, "Mpc"))
         """
-        if iterable(width):
-            w = self.ds.quan(width[0], width[1]).in_units("code_length").value
-        elif isinstance(width, YTQuantity):
-            w = width.in_units("code_length").value
-        else:
-            w = width
+        wd = self.ds.coordinates.sanitize_width(L, width, depth)
+        w = tuple(el.in_units('code_length').v for el in wd)
         ctr, dctr = self.ds.coordinates.sanitize_center(center, L)
+        res = (nx, nx, nz)
 
         if source is not None:
             mylog.error("Source argument is not currently supported for off-axis S-Z projections.")
@@ -221,16 +271,23 @@
         self.ds.add_field(("gas","beta_par"), function=beta_par, units="g/cm**3")
         setup_sunyaev_zeldovich_fields(self.ds)
 
-        dens   = off_axis_projection(self.ds, ctr, L, w, nx, "density")[0]
-        Te     = off_axis_projection(self.ds, ctr, L, w, nx, "t_sz")[0]/dens
-        bpar   = off_axis_projection(self.ds, ctr, L, w, nx, "beta_par"[0])/dens
-        omega1 = off_axis_projection(self.ds, ctr, L, w, nx, "t_squared")[0]/dens
+        dens = off_axis_projection(self.ds, ctr, L, w, res, "density",
+                                   north_vector=north_vector, no_ghost=no_ghost)
+        Te = off_axis_projection(self.ds, ctr, L, w, res, "t_sz",
+                                 north_vector=north_vector, no_ghost=no_ghost)/dens
+        bpar = off_axis_projection(self.ds, ctr, L, w, res, "beta_par",
+                                   north_vector=north_vector, no_ghost=no_ghost)/dens
+        omega1 = off_axis_projection(self.ds, ctr, L, w, res, "t_squared",
+                                     north_vector=north_vector, no_ghost=no_ghost)/dens
         omega1 = omega1/(Te*Te) - 1.
         if self.high_order:
-            bperp2 = off_axis_projection(self.ds, ctr, L, w, nx, "beta_perp_squared")[0]/dens
-            sigma1 = off_axis_projection(self.ds, ctr, L, w, nx, "t_beta_par")[0]/dens
+            bperp2 = off_axis_projection(self.ds, ctr, L, w, res, "beta_perp_squared", 
+                                         north_vector=north_vector, no_ghost=no_ghost)/dens
+            sigma1 = off_axis_projection(self.ds, ctr, L, w, res, "t_beta_par", 
+                                         north_vector=north_vector, no_ghost=no_ghost)/dens
             sigma1 = sigma1/Te - bpar
-            kappa1 = off_axis_projection(self.ds, ctr, L, w, nx, "beta_par_squared")[0]/dens
+            kappa1 = off_axis_projection(self.ds, ctr, L, w, res, "beta_par_squared", 
+                                         north_vector=north_vector, no_ghost=no_ghost)/dens
             kappa1 -= bpar
         else:
             bperp2 = np.zeros((nx,nx))
@@ -238,9 +295,9 @@
             kappa1 = np.zeros((nx,nx))
         tau = sigma_thompson*dens*self.mueinv/mh
 
-        self.bounds = np.array([-0.5*w, 0.5*w, -0.5*w, 0.5*w])
-        self.dx = w/nx
-        self.dy = w/nx
+        self.bounds = (-0.5*wd[0], 0.5*wd[0], -0.5*wd[1], 0.5*wd[1])
+        self.dx = wd[0]/nx
+        self.dy = wd[1]/nx
         self.nx = nx
 
         self._compute_intensity(np.array(tau), np.array(Te), np.array(bpar),
@@ -332,7 +389,7 @@
         if sky_scale is not None and sky_center is not None:
             fib.create_sky_wcs(sky_center, sky_scale)
         fib.writeto(filename, clobber=clobber)
-        
+
     @parallel_root_only
     def write_png(self, filename_prefix, cmap_name="algae",
                   axes_units="kpc", log_fields=None):
@@ -362,13 +419,13 @@
             crossover = False
             if vmin < 0 and vmax < 0:
                 data *= -1
-                negative = True                                        
+                negative = True
             if field in log_fields:
                 log_field = log_fields[field]
             else:
                 log_field = True
             if log_field:
-                formatter = matplotlib.ticker.LogFormatterMathtext()        
+                formatter = matplotlib.ticker.LogFormatterMathtext()
                 norm = matplotlib.colors.LogNorm()
                 if vmin < 0 and vmax > 0:
                     crossover = True
@@ -385,13 +442,13 @@
                 cbar_label += r'$\ \ ('+units+r')$'
             fig = plt.figure(figsize=(10.0,8.0))
             ax = fig.add_subplot(111)
-            cax = ax.imshow(data.ndarray_view(), norm=norm, extent=extent, cmap=cmap_name, origin="lower")
+            cax = ax.imshow(data.d, norm=norm, extent=extent, cmap=cmap_name, origin="lower")
             for label in ax.get_xticklabels():
                 label.set_fontproperties(ticks_font)
             for label in ax.get_yticklabels():
-                label.set_fontproperties(ticks_font)                      
-            ax.set_xlabel(r"$\mathrm{x\ (%s)}$" % (axes_units), fontsize=16)
-            ax.set_ylabel(r"$\mathrm{y\ (%s)}$" % (axes_units), fontsize=16)
+                label.set_fontproperties(ticks_font)
+            ax.set_xlabel(r"$\mathrm{x\ (%s)}$" % axes_units, fontsize=16)
+            ax.set_ylabel(r"$\mathrm{y\ (%s)}$" % axes_units, fontsize=16)
             cbar = fig.colorbar(cax, format=formatter)
             cbar.ax.set_ylabel(cbar_label, fontsize=16)
             if negative:
@@ -404,10 +461,10 @@
                                             np.ceil(np.log10(vmax))+1))
                 cbar.set_ticks(yticks)
             for label in cbar.ax.get_yticklabels():
-                label.set_fontproperties(ticks_font)                 
+                label.set_fontproperties(ticks_font)
             fig.tight_layout()
             plt.savefig(filename)
-            
+
     @parallel_root_only
     def write_hdf5(self, filename):
         r"""Export the set of S-Z fields to a set of HDF5 datasets.
@@ -421,11 +478,8 @@
         --------
         >>> szprj.write_hdf5("SZsloshing.h5")
         """
-        import h5py
-        f = h5py.File(filename, "w")
         for field, data in self.items():
-            f.create_dataset(field,data=data.ndarray_view())
-        f.close()
+            data.write_hdf5(filename, dataset_name=field)
 
     def keys(self):
         return self.data.keys()

diff -r fdf41578efe618b66c30cb258b55a14fbfb12f3f -r bf50a317e2b28de85e33520e98fdc58cecbeab91 yt/data_objects/data_containers.py
--- a/yt/data_objects/data_containers.py
+++ b/yt/data_objects/data_containers.py
@@ -13,7 +13,9 @@
 # The full license is in the file COPYING.txt, distributed with this software.
 #-----------------------------------------------------------------------------
 
+import h5py
 import itertools
+import os
 import types
 import uuid
 from yt.extern.six import string_types
@@ -25,9 +27,12 @@
 import shelve
 from contextlib import contextmanager
 
+from yt.funcs import get_output_filename
 from yt.funcs import *
 
 from yt.data_objects.particle_io import particle_handler_registry
+from yt.frontends.ytdata.utilities import \
+    save_as_dataset
 from yt.units.unit_object import UnitParseError
 from yt.utilities.exceptions import \
     YTUnitConversionError, \
@@ -98,6 +103,8 @@
     _con_args = ()
     _skip_add = False
     _container_fields = ()
+    _tds_attrs = ()
+    _tds_fields = ()
     _field_cache = None
     _index = None
 
@@ -463,6 +470,117 @@
         df = pd.DataFrame(data)
         return df
 
+    def save_as_dataset(self, filename=None, fields=None):
+        r"""Export a data object to a reloadable yt dataset.
+
+        This function will take a data object and output a dataset 
+        containing either the fields presently existing or fields 
+        given in the ``fields`` list.  The resulting dataset can be
+        reloaded as a yt dataset.
+
+        Parameters
+        ----------
+        filename : str, optional
+            The name of the file to be written.  If None, the name 
+            will be a combination of the original dataset and the type 
+            of data container.
+        fields : list of strings or tuples, optional
+            If this is supplied, it is the list of fields to be saved to
+            disk.  If not supplied, all the fields that have been queried
+            will be saved.
+
+        Returns
+        -------
+        filename : str
+            The name of the file that has been created.
+
+        Examples
+        --------
+
+        >>> import yt
+        >>> ds = yt.load("enzo_tiny_cosmology/DD0046/DD0046")
+        >>> sp = ds.sphere(ds.domain_center, (10, "Mpc"))
+        >>> fn = sp.save_as_dataset(fields=["density", "temperature"])
+        >>> sphere_ds = yt.load(fn)
+        >>> # the original data container is available as the data attribute
+        >>> print (sds.data["density"])
+        [  4.46237613e-32   4.86830178e-32   4.46335118e-32 ...,   6.43956165e-30
+           3.57339907e-30   2.83150720e-30] g/cm**3
+        >>> ad = sphere_ds.all_data()
+        >>> print (ad["temperature"])
+        [  1.00000000e+00   1.00000000e+00   1.00000000e+00 ...,   4.40108359e+04
+           4.54380547e+04   4.72560117e+04] K
+
+        """
+
+        keyword = "%s_%s" % (str(self.ds), self._type_name)
+        filename = get_output_filename(filename, keyword, ".h5")
+
+        data = {}
+        if fields is not None:
+            for f in self._determine_fields(fields):
+                data[f] = self[f]
+        else:
+            data.update(self.field_data)
+        # get the extra fields needed to reconstruct the container
+        tds_fields = tuple(self._determine_fields(list(self._tds_fields)))
+        for f in [f for f in self._container_fields + tds_fields \
+                  if f not in data]:
+            data[f] = self[f]
+        data_fields = list(data.keys())
+
+        need_grid_positions = False
+        need_particle_positions = False
+        ptypes = []
+        ftypes = {}
+        for field in data_fields:
+            if field in self._container_fields:
+                ftypes[field] = "grid"
+                need_grid_positions = True
+            elif self.ds.field_info[field].particle_type:
+                if field[0] not in ptypes:
+                    ptypes.append(field[0])
+                ftypes[field] = field[0]
+                need_particle_positions = True
+            else:
+                ftypes[field] = "grid"
+                need_grid_positions = True
+        # projections and slices use px and py, so don't need positions
+        if self._type_name in ["cutting", "proj", "slice"]:
+            need_grid_positions = False
+
+        if need_particle_positions:
+            for ax in "xyz":
+                for ptype in ptypes:
+                    p_field = (ptype, "particle_position_%s" % ax)
+                    if p_field in self.ds.field_info and p_field not in data:
+                        data_fields.append(field)
+                        ftypes[p_field] = p_field[0]
+                        data[p_field] = self[p_field]
+        if need_grid_positions:
+            for ax in "xyz":
+                g_field = ("index", ax)
+                if g_field in self.ds.field_info and g_field not in data:
+                    data_fields.append(g_field)
+                    ftypes[g_field] = "grid"
+                    data[g_field] = self[g_field]
+                g_field = ("index", "d" + ax)
+                if g_field in self.ds.field_info and g_field not in data:
+                    data_fields.append(g_field)
+                    ftypes[g_field] = "grid"
+                    data[g_field] = self[g_field]
+
+        extra_attrs = dict([(arg, getattr(self, arg, None))
+                            for arg in self._con_args + self._tds_attrs])
+        extra_attrs["con_args"] = self._con_args
+        extra_attrs["data_type"] = "yt_data_container"
+        extra_attrs["container_type"] = self._type_name
+        extra_attrs["dimensionality"] = self._dimensionality
+        save_as_dataset(self.ds, filename, data, field_types=ftypes,
+                        extra_attrs=extra_attrs)
+
+        return filename
+        
     def to_glue(self, fields, label="yt", data_collection=None):
         """
         Takes specific *fields* in the container and exports them to

diff -r fdf41578efe618b66c30cb258b55a14fbfb12f3f -r bf50a317e2b28de85e33520e98fdc58cecbeab91 yt/data_objects/derived_quantities.py
--- a/yt/data_objects/derived_quantities.py
+++ b/yt/data_objects/derived_quantities.py
@@ -256,7 +256,8 @@
           (("all", "particle_mass") in self.data_source.ds.field_info)
         vals = []
         if use_gas:
-            vals += [(data[ax] * data["gas", "cell_mass"]).sum(dtype=np.float64)
+            vals += [(data["gas", ax] *
+                      data["gas", "cell_mass"]).sum(dtype=np.float64)
                      for ax in 'xyz']
             vals.append(data["gas", "cell_mass"].sum(dtype=np.float64))
         if use_particles:
@@ -657,7 +658,7 @@
         m = data.ds.quan(0., "g")
         if use_gas:
             e += (data["gas", "kinetic_energy"] *
-                  data["index", "cell_volume"]).sum(dtype=np.float64)
+                  data["gas", "cell_volume"]).sum(dtype=np.float64)
             j += data["gas", "angular_momentum_magnitude"].sum(dtype=np.float64)
             m += data["gas", "cell_mass"].sum(dtype=np.float64)
         if use_particles:

This diff is so big that we needed to truncate the remainder.

https://bitbucket.org/yt_analysis/yt/commits/25a0dbd51257/
Changeset:   25a0dbd51257
Branch:      yt
User:        ngoldbaum
Date:        2015-11-09 23:01:39+00:00
Summary:     Merged in atmyers/yt (pull request #1837)

Merging the Experimental and Development Heads
Affected #:  110 files

diff -r 11079ba03a6dd7c5e0214efe0b06e14667d9366a -r 25a0dbd51257bb3fd355338fd4594985c5963f77 .hgignore
--- a/.hgignore
+++ b/.hgignore
@@ -32,6 +32,7 @@
 yt/utilities/lib/CICDeposit.c
 yt/utilities/lib/ContourFinding.c
 yt/utilities/lib/DepthFirstOctree.c
+yt/utilities/lib/element_mappings.c
 yt/utilities/lib/FixedInterpolator.c
 yt/utilities/lib/fortran_reader.c
 yt/utilities/lib/freetype_writer.c

diff -r 11079ba03a6dd7c5e0214efe0b06e14667d9366a -r 25a0dbd51257bb3fd355338fd4594985c5963f77 doc/source/cookbook/amrkdtree_downsampling.py
--- a/doc/source/cookbook/amrkdtree_downsampling.py
+++ b/doc/source/cookbook/amrkdtree_downsampling.py
@@ -4,6 +4,13 @@
 # In this example we will show how to use the AMRKDTree to take a simulation
 # with 8 levels of refinement and only use levels 0-3 to render the dataset.
 
+# Currently this cookbook is flawed in that the data that is covered by the
+# higher resolution data gets masked during the rendering.  This should be
+# fixed by changing either the data source or the code in
+# yt/utilities/amr_kdtree.py where data is being masked for the partitioned
+# grid.  Right now the quick fix is to create a data_collection, but this
+# will only work for patch based simulations that have ds.index.grids.
+
 # We begin by loading up yt, and importing the AMRKDTree
 import numpy as np
 
@@ -12,58 +19,58 @@
 
 # Load up a dataset and define the kdtree
 ds = yt.load('IsolatedGalaxy/galaxy0030/galaxy0030')
-kd = AMRKDTree(ds)
+im, sc = yt.volume_render(ds, 'density', fname='v0.png')
+sc.camera.set_width(ds.arr(100, 'kpc'))
+render_source = sc.get_source(0)
+kd=render_source.volume
 
 # Print out specifics of KD Tree
 print("Total volume of all bricks = %i" % kd.count_volume())
 print("Total number of cells = %i" % kd.count_cells())
 
-# Define a camera and take an volume rendering.
-tf = yt.ColorTransferFunction((-30, -22))
-cam = ds.camera([0.5, 0.5, 0.5], [0.2, 0.3, 0.4], 0.10, 256,
-                  tf, volume=kd)
-tf.add_layers(4, 0.01, col_bounds=[-27.5, -25.5], colormap='RdBu_r')
-cam.snapshot("v1.png", clip_ratio=6.0)
-
-# This rendering is okay, but lets say I'd like to improve it, and I don't want
-# to spend the time rendering the high resolution data.  What we can do is
-# generate a low resolution version of the AMRKDTree and pass that in to the
-# camera.  We do this by specifying a maximum refinement level of 6.
-
-kd_low_res = AMRKDTree(ds, max_level=6)
+new_source = ds.all_data()
+new_source.max_level=3
+kd_low_res = AMRKDTree(ds, data_source=new_source)
 print(kd_low_res.count_volume())
 print(kd_low_res.count_cells())
 
 # Now we pass this in as the volume to our camera, and render the snapshot
 # again.
 
-cam.volume = kd_low_res
-cam.snapshot("v4.png", clip_ratio=6.0)
+render_source.set_volume(kd_low_res)
+render_source.set_fields('density')
+sc.render()
+sc.save("v1.png", sigma_clip=6.0)
 
 # This operation was substantiall faster.  Now lets modify the low resolution
 # rendering until we find something we like.
 
+tf = render_source.transfer_function
 tf.clear()
 tf.add_layers(4, 0.01, col_bounds=[-27.5, -25.5],
               alpha=np.ones(4, dtype='float64'), colormap='RdBu_r')
-cam.snapshot("v2.png", clip_ratio=6.0)
+sc.render()
+sc.save("v2.png", sigma_clip=6.0)
 
 # This looks better.  Now let's try turning on opacity.
 
 tf.grey_opacity = True
-cam.snapshot("v4.png", clip_ratio=6.0)
-
-# That seemed to pick out som interesting structures.  Now let's bump up the
-# opacity.
-
+sc.render()
+sc.save("v3.png", sigma_clip=6.0)
+#
+## That seemed to pick out som interesting structures.  Now let's bump up the
+## opacity.
+#
 tf.clear()
 tf.add_layers(4, 0.01, col_bounds=[-27.5, -25.5],
               alpha=10.0 * np.ones(4, dtype='float64'), colormap='RdBu_r')
-cam.snapshot("v3.png", clip_ratio=6.0)
-
-# This looks pretty good, now lets go back to the full resolution AMRKDTree
-
-cam.volume = kd
-cam.snapshot("v4.png", clip_ratio=6.0)
+sc.render()
+sc.save("v4.png", sigma_clip=6.0)
+#
+## This looks pretty good, now lets go back to the full resolution AMRKDTree
+#
+render_source.set_volume(kd)
+sc.render()
+sc.save("v5.png", sigma_clip=6.0)
 
 # This looks great!

diff -r 11079ba03a6dd7c5e0214efe0b06e14667d9366a -r 25a0dbd51257bb3fd355338fd4594985c5963f77 doc/source/cookbook/camera_movement.py
--- a/doc/source/cookbook/camera_movement.py
+++ b/doc/source/cookbook/camera_movement.py
@@ -3,40 +3,29 @@
 
 # Follow the simple_volume_rendering cookbook for the first part of this.
 ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030")  # load data
-ad = ds.all_data()
-mi, ma = ad.quantities.extrema("density")
-
-# Set up transfer function
-tf = yt.ColorTransferFunction((np.log10(mi), np.log10(ma)))
-tf.add_layers(6, w=0.05)
-
-# Set up camera paramters
-c = [0.5, 0.5, 0.5]  # Center
-L = [1, 1, 1]  # Normal Vector
-W = 1.0  # Width
-Nvec = 512  # Pixels on a side
-
-# Specify a north vector, which helps with rotations.
-north_vector = [0., 0., 1.]
+sc = yt.create_scene(ds)
+cam = sc.camera
+cam.resolution = (512, 512)
+cam.set_width(ds.domain_width/20.0)
 
 # Find the maximum density location, store it in max_c
 v, max_c = ds.find_max('density')
 
-# Initialize the Camera
-cam = ds.camera(c, L, W, (Nvec, Nvec), tf, north_vector=north_vector)
 frame = 0
-
-# Do a rotation over 5 frames
-for i, snapshot in enumerate(cam.rotation(np.pi, 5, clip_ratio=8.0)):
-    snapshot.write_png('camera_movement_%04i.png' % frame)
-    frame += 1
-
 # Move to the maximum density location over 5 frames
-for i, snapshot in enumerate(cam.move_to(max_c, 5, clip_ratio=8.0)):
-    snapshot.write_png('camera_movement_%04i.png' % frame)
+for _ in cam.iter_move(max_c, 5):
+    sc.render()
+    sc.save('camera_movement_%04i.png' % frame, sigma_clip=8.0)
     frame += 1
 
 # Zoom in by a factor of 10 over 5 frames
-for i, snapshot in enumerate(cam.zoomin(10.0, 5, clip_ratio=8.0)):
-    snapshot.write_png('camera_movement_%04i.png' % frame)
+for _ in cam.iter_zoom(10.0, 5):
+    sc.render()
+    sc.save('camera_movement_%04i.png' % frame, sigma_clip=8.0)
     frame += 1
+
+# Do a rotation over 5 frames
+for _ in cam.iter_rotate(np.pi, 5):
+    sc.render()
+    sc.save('camera_movement_%04i.png' % frame, sigma_clip=8.0)
+    frame += 1

diff -r 11079ba03a6dd7c5e0214efe0b06e14667d9366a -r 25a0dbd51257bb3fd355338fd4594985c5963f77 doc/source/cookbook/complex_plots.rst
--- a/doc/source/cookbook/complex_plots.rst
+++ b/doc/source/cookbook/complex_plots.rst
@@ -196,10 +196,41 @@
 
 In this recipe, we move a camera through a domain and take multiple volume
 rendering snapshots.
-See :ref:`volume_rendering` for more information.
+See :ref:`camera_movement` for more information.
 
 .. yt_cookbook:: camera_movement.py
 
+Volume Rendering with Custom Camera
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In this recipe we modify the :ref:`cookbook-simple_volume_rendering` recipe to
+use customized camera properties. See :ref:`volume_rendering` for more
+information.
+
+.. yt_cookbook:: custom_camera_volume_rendering.py
+
+.. _cookbook-custom-transfer-function:
+
+Volume Rendering with a Custom Transfer Function
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In this recipe we modify the :ref:`cookbook-simple_volume_rendering` recipe to
+use customized camera properties. See :ref:`volume_rendering` for more
+information.
+
+.. yt_cookbook:: custom_transfer_function_volume_rendering.py
+
+.. _cookbook-sigma_clip:
+
+Volume Rendering with Sigma Clipping
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In this recipe we output several images with different values of sigma_clip
+set in order to change the contrast of the resulting image.  See 
+:ref:`sigma_clip` for more information.
+
+.. yt_cookbook:: sigma_clip.py
+
 Zooming into an Image
 ~~~~~~~~~~~~~~~~~~~~~
 
@@ -212,6 +243,15 @@
 
 .. yt_cookbook:: zoomin_frames.py
 
+.. _cookbook-various_lens:
+
+Various Lens Types for Volume Rendering
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This example illustrates the usage and feature of different lenses for volume rendering.
+
+.. yt_cookbook:: various_lens.py
+
 .. _cookbook-opaque_rendering:
 
 Opaque Volume Rendering
@@ -220,7 +260,7 @@
 This recipe demonstrates how to make semi-opaque volume renderings, but also
 how to step through and try different things to identify the type of volume
 rendering you want.
-See :ref:`volume_rendering` for more information.
+See :ref:`opaque_rendering` for more information.
 
 .. yt_cookbook:: opaque_rendering.py
 
@@ -235,23 +275,27 @@
 
 .. yt_cookbook:: amrkdtree_downsampling.py
 
+.. _cookbook-volume_rendering_annotations:
+
 Volume Rendering with Bounding Box and Overlaid Grids
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 This recipe demonstrates how to overplot a bounding box on a volume rendering
 as well as overplotting grids representing the level of refinement achieved
 in different regions of the code.
-See :ref:`volume_rendering` for more information.
+See :ref:`volume_rendering_annotations` for more information.
 
 .. yt_cookbook:: rendering_with_box_and_grids.py
 
 Volume Rendering with Annotation
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 This recipe demonstrates how to write the simulation time, show an
 axis triad indicating the direction of the coordinate system, and show
-the transfer function on a volume rendering.
-See :ref:`volume_rendering` for more information.
+the transfer function on a volume rendering.  Please note that this 
+recipe relies on the old volume rendering interface.  While one can
+continue to use this interface, it may be incompatible with some of the
+new developments and the infrastructure described in :ref:`volume_rendering`.
 
 .. yt_cookbook:: vol-annotated.py
 

diff -r 11079ba03a6dd7c5e0214efe0b06e14667d9366a -r 25a0dbd51257bb3fd355338fd4594985c5963f77 doc/source/cookbook/custom_camera_volume_rendering.py
--- /dev/null
+++ b/doc/source/cookbook/custom_camera_volume_rendering.py
@@ -0,0 +1,22 @@
+import yt
+
+# Load the dataset
+ds = yt.load("Enzo_64/DD0043/data0043")
+
+# Create a volume rendering
+sc = yt.create_scene(ds, field=('gas', 'density'))
+
+# Now increase the resolution
+sc.camera.resolution = (1024, 1024)
+
+# Set the camera focus to a position that is offset from the center of
+# the domain
+sc.camera.focus = ds.arr([0.3, 0.3, 0.3], 'unitary')
+
+# Move the camera position to the other side of the dataset
+sc.camera.position = ds.arr([0, 0, 0], 'unitary')
+
+# save to disk with a custom filename and apply sigma clipping to eliminate
+# very bright pixels, producing an image with better contrast.
+sc.render()
+sc.save('custom.png', sigma_clip=4)

diff -r 11079ba03a6dd7c5e0214efe0b06e14667d9366a -r 25a0dbd51257bb3fd355338fd4594985c5963f77 doc/source/cookbook/custom_transfer_function_volume_rendering.py
--- /dev/null
+++ b/doc/source/cookbook/custom_transfer_function_volume_rendering.py
@@ -0,0 +1,24 @@
+import yt
+import numpy as np
+
+# Load the dataset
+ds = yt.load("Enzo_64/DD0043/data0043")
+
+# Create a volume rendering
+sc = yt.create_scene(ds, field=('gas', 'density'))
+
+# Modify the transfer function
+
+# First get the render source, in this case the entire domain, with field ('gas','density')
+render_source = sc.get_source(0)
+
+# Clear the transfer function
+render_source.transfer_function.clear()
+
+# Map a range of density values (in log space) to the Reds_r colormap
+render_source.transfer_function.map_to_colormap(
+    np.log10(ds.quan(5.0e-31, 'g/cm**3')),
+    np.log10(ds.quan(1.0e-29, 'g/cm**3')),
+    scale=30.0, colormap='RdBu_r')
+
+sc.save('new_tf.png')

diff -r 11079ba03a6dd7c5e0214efe0b06e14667d9366a -r 25a0dbd51257bb3fd355338fd4594985c5963f77 doc/source/cookbook/image_background_colors.py
--- a/doc/source/cookbook/image_background_colors.py
+++ b/doc/source/cookbook/image_background_colors.py
@@ -2,27 +2,14 @@
 # volume renderings, to pngs with varying backgrounds.
 
 # First we use the simple_volume_rendering.py recipe from above to generate
-# a standard volume rendering.  The only difference is that we use 
-# grey_opacity=True with our TransferFunction, as the colored background 
-# functionality requires images with an opacity between 0 and 1. 
-
-# We have removed all the comments from the volume rendering recipe for 
-# brevity here, but consult the recipe for more details.
+# a standard volume rendering.
 
 import yt
 import numpy as np
 
 ds = yt.load("Enzo_64/DD0043/data0043")
-ad = ds.all_data()
-mi, ma = ad.quantities.extrema("density")
-tf = yt.ColorTransferFunction((np.log10(mi)+1, np.log10(ma)), grey_opacity=True)
-tf.add_layers(5, w=0.02, colormap="spectral")
-c = [0.5, 0.5, 0.5]
-L = [0.5, 0.2, 0.7]
-W = 1.0
-Npixels = 512
-cam = ds.camera(c, L, W, Npixels, tf)
-im = cam.snapshot("original.png" % ds, clip_ratio=8.0)
+im, sc = yt.volume_render(ds, 'density')
+im.write_png("original.png", sigma_clip=8.0)
 
 # Our image array can now be transformed to include different background
 # colors.  By default, the background color is black.  The following
@@ -35,10 +22,10 @@
 # None  (0.,0.,0.,0.) <-- Transparent!
 # any rgba list/array: [r,g,b,a], bounded by 0..1
 
-# We include the clip_ratio=8 keyword here to bring out more contrast between
+# We include the sigma_clip=8 keyword here to bring out more contrast between
 # the background and foreground, but it is entirely optional.
 
-im.write_png('black_bg.png', background='black', clip_ratio=8.0)
-im.write_png('white_bg.png', background='white', clip_ratio=8.0)
-im.write_png('green_bg.png', background=[0.,1.,0.,1.], clip_ratio=8.0)
-im.write_png('transparent_bg.png', background=None, clip_ratio=8.0)
+im.write_png('black_bg.png', background='black', sigma_clip=8.0)
+im.write_png('white_bg.png', background='white', sigma_clip=8.0)
+im.write_png('green_bg.png', background=[0.,1.,0.,1.], sigma_clip=8.0)
+im.write_png('transparent_bg.png', background=None, sigma_clip=8.0)

diff -r 11079ba03a6dd7c5e0214efe0b06e14667d9366a -r 25a0dbd51257bb3fd355338fd4594985c5963f77 doc/source/cookbook/index.rst
--- a/doc/source/cookbook/index.rst
+++ b/doc/source/cookbook/index.rst
@@ -44,8 +44,10 @@
    embedded_webm_animation
    gadget_notebook
    owls_notebook
+   ../visualizing/transfer_function_helper
    ../analyzing/analysis_modules/sunyaev_zeldovich
    fits_radio_cubes
    fits_xray_images
    tipsy_notebook
    halo_analysis_example
+   ../visualizing/volume_rendering_tutorial

diff -r 11079ba03a6dd7c5e0214efe0b06e14667d9366a -r 25a0dbd51257bb3fd355338fd4594985c5963f77 doc/source/cookbook/offaxis_projection.py
--- a/doc/source/cookbook/offaxis_projection.py
+++ b/doc/source/cookbook/offaxis_projection.py
@@ -11,7 +11,7 @@
 # objects, you could set it the way you would a cutting plane -- but for this
 # dataset, we'll just choose an off-axis value at random.  This gets normalized
 # automatically.
-L = [0.5, 0.4, 0.7]
+L = [1.0, 0.0, 0.0]
 
 # Our "width" is the width of the image plane as well as the depth.
 # The first element is the left to right width, the second is the
@@ -26,7 +26,7 @@
 # Create the off axis projection.
 # Setting no_ghost to False speeds up the process, but makes a
 # slighly lower quality image.
-image = yt.off_axis_projection(ds, c, L, W, Npixels, "density", no_ghost=False)
+image, sc= yt.off_axis_projection(ds, c, L, W, Npixels, "density", no_ghost=False)
 
 # Write out the final image and give it a name
 # relating to what our dataset is called.

diff -r 11079ba03a6dd7c5e0214efe0b06e14667d9366a -r 25a0dbd51257bb3fd355338fd4594985c5963f77 doc/source/cookbook/offaxis_projection_colorbar.py
--- a/doc/source/cookbook/offaxis_projection_colorbar.py
+++ b/doc/source/cookbook/offaxis_projection_colorbar.py
@@ -32,7 +32,7 @@
 # Also note that we set the field which we want to project as "density", but
 # really we could use any arbitrary field like "temperature", "metallicity"
 # or whatever.
-image = yt.off_axis_projection(ds, c, L, W, Npixels, "density", no_ghost=False)
+image, sc = yt.off_axis_projection(ds, c, L, W, Npixels, "density", no_ghost=False)
 
 # Image is now an NxN array representing the intensities of the various pixels.
 # And now, we call our direct image saver.  We save the log of the result.

diff -r 11079ba03a6dd7c5e0214efe0b06e14667d9366a -r 25a0dbd51257bb3fd355338fd4594985c5963f77 doc/source/cookbook/opaque_rendering.py
--- a/doc/source/cookbook/opaque_rendering.py
+++ b/doc/source/cookbook/opaque_rendering.py
@@ -3,44 +3,51 @@
 
 ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030")
 
-# We start by building a transfer function, and initializing a camera.
+# We start by building a default volume rendering scene 
 
-tf = yt.ColorTransferFunction((-30, -22))
-cam = ds.camera([0.5, 0.5, 0.5], [0.2, 0.3, 0.4], 0.10, 256, tf)
+im, sc = yt.volume_render(ds, field=("gas","density"), fname="v0.png", sigma_clip=6.0)
 
-# Now let's add some isocontours, and take a snapshot.
-
-tf.add_layers(4, 0.01, col_bounds = [-27.5,-25.5], colormap = 'RdBu_r')
-cam.snapshot("v1.png", clip_ratio=6.0)
+sc.camera.set_width(ds.arr(0.1,'code_length'))
+tf = sc.get_source(0).transfer_function 
+tf.clear()
+tf.add_layers(4, 0.01, col_bounds = [-27.5,-25.5],
+        alpha=np.logspace(-3,0,4), colormap = 'RdBu_r')
+sc.render()
+sc.save("v1.png", sigma_clip=6.0)
 
 # In this case, the default alphas used (np.logspace(-3,0,Nbins)) does not
 # accentuate the outer regions of the galaxy. Let's start by bringing up the
 # alpha values for each contour to go between 0.1 and 1.0
 
+tf = sc.get_source(0).transfer_function 
 tf.clear()
 tf.add_layers(4, 0.01, col_bounds = [-27.5,-25.5],
         alpha=np.logspace(0,0,4), colormap = 'RdBu_r')
-cam.snapshot("v2.png", clip_ratio=6.0)
+sc.render()
+sc.save("v2.png", sigma_clip=6.0)
 
 # Now let's set the grey_opacity to True.  This should make the inner portions
 # start to be obcured
 
 tf.grey_opacity = True
-cam.snapshot("v3.png", clip_ratio=6.0)
+sc.render()
+sc.save("v3.png", sigma_clip=6.0)
 
 # That looks pretty good, but let's start bumping up the opacity.
 
 tf.clear()
 tf.add_layers(4, 0.01, col_bounds = [-27.5,-25.5],
         alpha=10.0*np.ones(4,dtype='float64'), colormap = 'RdBu_r')
-cam.snapshot("v4.png", clip_ratio=6.0)
+sc.render()
+sc.save("v4.png", sigma_clip=6.0)
 
 # Let's bump up again to see if we can obscure the inner contour.
 
 tf.clear()
 tf.add_layers(4, 0.01, col_bounds = [-27.5,-25.5],
         alpha=30.0*np.ones(4,dtype='float64'), colormap = 'RdBu_r')
-cam.snapshot("v5.png", clip_ratio=6.0)
+sc.render()
+sc.save("v5.png", sigma_clip=6.0)
 
 # Now we are losing sight of everything.  Let's see if we can obscure the next
 # layer
@@ -48,13 +55,15 @@
 tf.clear()
 tf.add_layers(4, 0.01, col_bounds = [-27.5,-25.5],
         alpha=100.0*np.ones(4,dtype='float64'), colormap = 'RdBu_r')
-cam.snapshot("v6.png", clip_ratio=6.0)
+sc.render()
+sc.save("v6.png", sigma_clip=6.0)
 
 # That is very opaque!  Now lets go back and see what it would look like with
 # grey_opacity = False
 
 tf.grey_opacity=False
-cam.snapshot("v7.png", clip_ratio=6.0)
+sc.render()
+sc.save("v7.png", sigma_clip=6.0)
 
 # That looks pretty different, but the main thing is that you can see that the
 # inner contours are somewhat visible again.  

diff -r 11079ba03a6dd7c5e0214efe0b06e14667d9366a -r 25a0dbd51257bb3fd355338fd4594985c5963f77 doc/source/cookbook/rendering_with_box_and_grids.py
--- a/doc/source/cookbook/rendering_with_box_and_grids.py
+++ b/doc/source/cookbook/rendering_with_box_and_grids.py
@@ -1,61 +1,22 @@
 import yt
 import numpy as np
+from yt.visualization.volume_rendering.api import BoxSource, CoordinateVectorSource
 
 # Load the dataset.
 ds = yt.load("Enzo_64/DD0043/data0043")
+sc = yt.create_scene(ds, ('gas','density'))
+sc.get_source(0).transfer_function.grey_opacity=True
 
-# Create a data container (like a sphere or region) that
-# represents the entire domain.
-ad = ds.all_data()
+sc.annotate_domain(ds)
+sc.render()
+sc.save("%s_vr_domain.png" % ds)
 
-# Get the minimum and maximum densities.
-mi, ma = ad.quantities.extrema("density")
-
-# Create a transfer function to map field values to colors.
-# We bump up our minimum to cut out some of the background fluid
-tf = yt.ColorTransferFunction((np.log10(mi)+2.0, np.log10(ma)))
-
-# Add three Gaussians, evenly spaced between the min and
-# max specified above with widths of 0.02 and using the
-# gist_stern colormap.
-tf.add_layers(3, w=0.02, colormap="gist_stern")
-
-# Choose a center for the render.
-c = [0.5, 0.5, 0.5]
-
-# Choose a vector representing the viewing direction.
-L = [0.5, 0.2, 0.7]
-
-# Set the width of the image.
-# Decreasing or increasing this value
-# results in a zoom in or out.
-W = 1.0
-
-# The number of pixels along one side of the image.
-# The final image will have Npixel^2 pixels.
-Npixels = 512
-
-# Create a camera object.
-# This object creates the images and
-# can be moved and rotated.
-cam = ds.camera(c, L, W, Npixels, tf)
-
-# Create a snapshot.
-# The return value of this function could also be accepted, modified (or saved
-# for later manipulation) and then put written out using write_bitmap.
-# clip_ratio applies a maximum to the function, which is set to that value
-# times the .std() of the array.
-im = cam.snapshot("%s_volume_rendered.png" % ds, clip_ratio=8.0)
-
-# Add the domain edges, with an alpha blending of 0.3:
-nim = cam.draw_domain(im, alpha=0.3)
-nim.write_png('%s_vr_domain.png' % ds)
-
-# Add the grids, colored by the grid level with the algae colormap
-nim = cam.draw_grids(im, alpha=0.3, cmap='algae')
-nim.write_png('%s_vr_grids.png' % ds)
+sc.annotate_grids(ds)
+sc.render()
+sc.save("%s_vr_grids.png" % ds)
 
 # Here we can draw the coordinate vectors on top of the image by processing
 # it through the camera. Then save it out.
-cam.draw_coordinate_vectors(nim)
-nim.write_png("%s_vr_vectors.png" % ds)
+sc.annotate_axes()
+sc.render()
+sc.save("%s_vr_coords.png" % ds)

diff -r 11079ba03a6dd7c5e0214efe0b06e14667d9366a -r 25a0dbd51257bb3fd355338fd4594985c5963f77 doc/source/cookbook/sigma_clip.py
--- /dev/null
+++ b/doc/source/cookbook/sigma_clip.py
@@ -0,0 +1,17 @@
+import yt
+
+# Load the dataset.
+ds = yt.load("enzo_tiny_cosmology/RD0009/RD0009")
+
+# Create a volume rendering, which will determine data bounds, use the first
+# acceptable field in the field_list, and set up a default transfer function.
+
+# Render and save output images with different levels of sigma clipping.
+# Sigma clipping removes the highest intensity pixels in a volume render, 
+# which affects the overall contrast of the image.
+sc = yt.create_scene(ds, field=('gas', 'density'))
+sc.render()
+sc.save('clip_0.png')
+sc.save('clip_2.png', sigma_clip=2)
+sc.save('clip_4.png', sigma_clip=4)
+sc.save('clip_6.png', sigma_clip=6)

diff -r 11079ba03a6dd7c5e0214efe0b06e14667d9366a -r 25a0dbd51257bb3fd355338fd4594985c5963f77 doc/source/cookbook/simple_volume_rendering.py
--- a/doc/source/cookbook/simple_volume_rendering.py
+++ b/doc/source/cookbook/simple_volume_rendering.py
@@ -1,48 +1,10 @@
 import yt
-import numpy as np
 
 # Load the dataset.
 ds = yt.load("Enzo_64/DD0043/data0043")
 
-# Create a data container (like a sphere or region) that
-# represents the entire domain.
-ad = ds.all_data()
+# Create a volume rendering, which will determine data bounds, use the first
+# acceptable field in the field_list, and set up a default transfer function.
 
-# Get the minimum and maximum densities.
-mi, ma = ad.quantities.extrema("density")
-
-# Create a transfer function to map field values to colors.
-# We bump up our minimum to cut out some of the background fluid
-tf = yt.ColorTransferFunction((np.log10(mi)+1, np.log10(ma)))
-
-# Add five Gaussians, evenly spaced between the min and
-# max specified above with widths of 0.02 and using the
-# spectral colormap.
-tf.add_layers(5, w=0.02, colormap="spectral")
-
-# Choose a center for the render.
-c = [0.5, 0.5, 0.5]
-
-# Choose a vector representing the viewing direction.
-L = [0.5, 0.2, 0.7]
-
-# Set the width of the image.
-# Decreasing or increasing this value
-# results in a zoom in or out.
-W = 1.0
-
-# The number of pixels along one side of the image.
-# The final image will have Npixel^2 pixels.
-Npixels = 512
-
-# Create a camera object.
-# This object creates the images and
-# can be moved and rotated.
-cam = ds.camera(c, L, W, Npixels, tf)
-
-# Create a snapshot.
-# The return value of this function could also be accepted, modified (or saved
-# for later manipulation) and then put written out using write_bitmap.
-# clip_ratio applies a maximum to the function, which is set to that value
-# times the .std() of the array.
-cam.snapshot("%s_volume_rendered.png" % ds, clip_ratio=8.0)
+# This will save a file named 'data0043_Render_density.png' to disk.
+im, sc = yt.volume_render(ds, field=('gas', 'density'))

diff -r 11079ba03a6dd7c5e0214efe0b06e14667d9366a -r 25a0dbd51257bb3fd355338fd4594985c5963f77 doc/source/cookbook/various_lens.py
--- /dev/null
+++ b/doc/source/cookbook/various_lens.py
@@ -0,0 +1,120 @@
+import yt
+from yt.visualization.volume_rendering.api import Scene, Camera, VolumeSource
+import numpy as np
+
+field = ("gas", "density")
+
+# normal_vector points from camera to the center of tbe final projection.
+# Now we look at the positive x direction.
+normal_vector = [1., 0., 0.]
+# north_vector defines the "top" direction of the projection, which is
+# positive z direction here.
+north_vector = [0., 0., 1.]
+
+# Follow the simple_volume_rendering cookbook for the first part of this.
+ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030")
+sc = Scene()
+vol = VolumeSource(ds, field=field)
+tf = vol.transfer_function
+tf.grey_opacity = True
+
+# Plane-parallel lens
+cam = Camera(ds, lens_type='plane-parallel')
+# Set the resolution of tbe final projection.
+cam.resolution = [500, 500]
+# Set the location of the camera to be (x=0.2, y=0.5, z=0.5)
+# For plane-parallel lens, the location info along the normal_vector (here
+# is x=0.2) is ignored. 
+cam.position = ds.arr(np.array([0.2, 0.5, 0.5]), 'code_length')
+# Set the orientation of the camera.
+cam.switch_orientation(normal_vector=normal_vector,
+                       north_vector=north_vector)
+# Set the width of the camera, where width[0] and width[1] specify the length and
+# height of final projection, while width[2] in plane-parallel lens is not used.
+cam.set_width(ds.domain_width * 0.5)
+sc.camera = cam
+sc.add_source(vol)
+sc.render()
+sc.save('lens_plane-parallel.png', sigma_clip=6.0)
+
+# Perspective lens
+cam = Camera(ds, lens_type='perspective')
+cam.resolution = [500, 500]
+# Standing at (x=0.2, y=0.5, z=0.5), we look at the area of x>0.2 (with some open angle
+# specified by camera width) along the positive x direction.
+cam.position = ds.arr([0.2, 0.5, 0.5], 'code_length')
+cam.switch_orientation(normal_vector=normal_vector,
+                       north_vector=north_vector)
+# Set the width of the camera, where width[0] and width[1] specify the length and
+# height of the final projection, while width[2] specifies the distance between the
+# camera and the final image.
+cam.set_width(ds.domain_width * 0.5)
+sc.camera = cam
+sc.add_source(vol)
+sc.render()
+sc.save('lens_perspective.png', sigma_clip=6.0)
+
+# Stereo-perspective lens
+cam = Camera(ds, lens_type='stereo-perspective')
+# Set the size ratio of the final projection to be 2:1, since stereo-perspective lens
+# will generate the final image with both left-eye and right-eye ones jointed together.
+cam.resolution = [1000, 500]
+cam.position = ds.arr([0.2, 0.5, 0.5], 'code_length')
+cam.switch_orientation(normal_vector=normal_vector,
+                       north_vector=north_vector)
+cam.set_width(ds.domain_width*0.5)
+# Set the distance between left-eye and right-eye.
+cam.lens.disparity = ds.domain_width[0] * 1.e-3
+sc.camera = cam
+sc.add_source(vol)
+sc.render()
+sc.save('lens_stereo-perspective.png', sigma_clip=6.0)
+
+# Fisheye lens
+dd = ds.sphere(ds.domain_center, ds.domain_width[0] / 10)
+cam = Camera(dd, lens_type='fisheye')
+cam.resolution = [500, 500]
+v, c = ds.find_max(field)
+cam.set_position(c - 0.0005 * ds.domain_width)
+cam.switch_orientation(normal_vector=normal_vector,
+                       north_vector=north_vector)
+cam.set_width(ds.domain_width)
+cam.lens.fov = 360.0
+sc.camera = cam
+sc.add_source(vol)
+sc.render()
+sc.save('lens_fisheye.png', sigma_clip=6.0)
+
+# Spherical lens
+cam = Camera(ds, lens_type='spherical')
+# Set the size ratio of the final projection to be 2:1, since spherical lens
+# will generate the final image with length of 2*pi and height of pi.
+cam.resolution = [1000, 500]
+# Standing at (x=0.4, y=0.5, z=0.5), we look in all the radial directions
+# from this point in spherical coordinate.
+cam.position = ds.arr([0.4, 0.5, 0.5], 'code_length')
+cam.switch_orientation(normal_vector=normal_vector,
+                       north_vector=north_vector)
+# In (stereo)spherical camera, camera width is not used since the entire volume
+# will be rendered
+sc.camera = cam
+sc.add_source(vol)
+sc.render()
+sc.save('lens_spherical.png', sigma_clip=6.0)
+
+# Stereo-spherical lens
+cam = Camera(ds, lens_type='stereo-spherical')
+# Set the size ratio of the final projection to be 4:1, since spherical-perspective lens
+# will generate the final image with both left-eye and right-eye ones jointed together.
+cam.resolution = [2000, 500]
+cam.position = ds.arr([0.4, 0.5, 0.5], 'code_length')
+cam.switch_orientation(normal_vector=normal_vector,
+                       north_vector=north_vector)
+# In (stereo)spherical camera, camera width is not used since the entire volume
+# will be rendered
+# Set the distance between left-eye and right-eye.
+cam.lens.disparity = ds.domain_width[0] * 1.e-3
+sc.camera = cam
+sc.add_source(vol)
+sc.render()
+sc.save('lens_stereo-spherical.png', sigma_clip=6.0)

diff -r 11079ba03a6dd7c5e0214efe0b06e14667d9366a -r 25a0dbd51257bb3fd355338fd4594985c5963f77 doc/source/cookbook/vol-annotated.py
--- a/doc/source/cookbook/vol-annotated.py
+++ b/doc/source/cookbook/vol-annotated.py
@@ -4,7 +4,7 @@
 import pylab
 
 import yt
-import yt.visualization.volume_rendering.api as vr
+import yt.visualization.volume_rendering.old_camera as vr
 
 ds = yt.load("maestro_subCh_plt00248")
 
@@ -17,11 +17,11 @@
 # centered on these with width sigma        
 vals = [-1.e7, -5.e6, -2.5e6, 2.5e6, 5.e6, 1.e7]
 sigma = 2.e5
-        
+
 mi, ma = min(vals), max(vals)
 
 # Instantiate the ColorTransferfunction.
-tf =  vr.ColorTransferFunction((mi, ma))
+tf =  yt.ColorTransferFunction((mi, ma))
 
 for v in vals:
     tf.sample_colormap(v, sigma**2, colormap="coolwarm")
@@ -69,7 +69,7 @@
 
 # tell the camera to use our figure
 cam._render_figure = f
-    
+
 # save annotated -- this added the transfer function values, 
 # and the clear_fig=False ensures it writes onto our existing figure.
 cam.save_annotated("vol_annotated.png", nim, dpi=145, clear_fig=False)

diff -r 11079ba03a6dd7c5e0214efe0b06e14667d9366a -r 25a0dbd51257bb3fd355338fd4594985c5963f77 doc/source/examining/loading_data.rst
--- a/doc/source/examining/loading_data.rst
+++ b/doc/source/examining/loading_data.rst
@@ -1026,6 +1026,60 @@
 * Some functions may behave oddly or not work at all.
 * Data must already reside in memory.
 
+Unstructured Grid Data
+----------------------
+
+See :ref:`loading-numpy-array`,
+:func:`~yt.frontends.stream.data_structures.load_unstructured_mesh` for
+more detail.
+
+In addition to the above grid types, you can also load data stored on
+unstructured meshes. This type of mesh is used, for example, in many
+finite element calculations. Currently, hexahedral, tetrahedral, and
+wedge-shaped mesh element are supported.
+
+To load an unstructured mesh, you need to specify the following. First,
+you need to have a coordinates array, which should be an (L, 3) array
+that stores the (x, y, z) positions of all of the vertices in the mesh.
+Second, you need to specify a connectivity array, which describes how
+those vertices are connected into mesh elements. The connectivity array
+should be (N, M), where N is the number of elements and M is the
+connectivity length, i.e. the number of vertices per element. Finally,
+you must also specify a data dictionary, where the keys should be
+the names of the fields and the values should be numpy arrays that
+contain the field data. These arrays can either supply the cell-averaged
+data for each element, in which case they would be (N, 1), or they
+can have node-centered data, in which case they would also be (N, M).
+
+Here is an example of how to load an in-memory, unstructured mesh dataset:
+
+.. code-block:: python
+
+   import yt
+   import numpy
+   from yt.utilities.exodusII_reader import get_data
+
+   coords, connectivity, data = get_data("MOOSE_sample_data/out.e-s010")
+
+This uses a publically available `MOOSE <http://mooseframework.org/>` 
+dataset along with the get_data function to parse the coords, connectivity, 
+and data. Then, these can be loaded as an in-memory dataset as follows:
+
+.. code-block:: python
+
+    mesh_id = 0
+    ds = yt.load_unstructured_mesh(data[mesh_id], connectivity[mesh_id], coords[mesh_id])
+
+Note that load_unstructured_mesh can take either a single or a list of meshes.
+Here, we have selected only the first mesh to load.
+
+.. rubric:: Caveats
+
+* Units will be incorrect unless the data has already been converted to cgs.
+* Integration is not implemented.
+* Some functions may behave oddly or not work at all.
+* Data must already reside in memory.
+
 Generic Particle Data
 ---------------------
 

diff -r 11079ba03a6dd7c5e0214efe0b06e14667d9366a -r 25a0dbd51257bb3fd355338fd4594985c5963f77 doc/source/quickstart/6)_Volume_Rendering.ipynb
--- a/doc/source/quickstart/6)_Volume_Rendering.ipynb
+++ b/doc/source/quickstart/6)_Volume_Rendering.ipynb
@@ -56,14 +56,14 @@
      "cell_type": "markdown",
      "metadata": {},
      "source": [
-      "If we want to apply a clipping, we can specify the `clip_ratio`.  This will clip the upper bounds to this value times the standard deviation of the values in the image array."
+      "If we want to apply a clipping, we can specify the `sigma_clip`.  This will clip the upper bounds to this value times the standard deviation of the values in the image array."
      ]
     },
     {
      "cell_type": "code",
      "collapsed": false,
      "input": [
-      "cam.show(clip_ratio=4)"
+      "cam.show(sigma_clip=4)"
      ],
      "language": "python",
      "metadata": {},
@@ -83,7 +83,7 @@
       "tf = yt.ColorTransferFunction((-28, -25))\n",
       "tf.add_layers(4, w=0.03)\n",
       "cam = ds.camera([0.5, 0.5, 0.5], [1.0, 1.0, 1.0], (20.0, 'kpc'), 512, tf, no_ghost=False)\n",
-      "cam.show(clip_ratio=4.0)"
+      "cam.show(sigma_clip=4.0)"
      ],
      "language": "python",
      "metadata": {},
@@ -93,4 +93,4 @@
    "metadata": {}
   }
  ]
-}
\ No newline at end of file
+}

diff -r 11079ba03a6dd7c5e0214efe0b06e14667d9366a -r 25a0dbd51257bb3fd355338fd4594985c5963f77 doc/source/reference/api/api.rst
--- a/doc/source/reference/api/api.rst
+++ b/doc/source/reference/api/api.rst
@@ -418,6 +418,7 @@
    ~yt.frontends.stream.data_structures.load_amr_grids
    ~yt.frontends.stream.data_structures.load_particles
    ~yt.frontends.stream.data_structures.load_hexahedral_mesh
+   ~yt.frontends.stream.data_structures.load_unstructured_mesh
 
 Derived Datatypes
 -----------------
@@ -606,36 +607,57 @@
 
 See also :ref:`volume_rendering`.
 
-Here are the primary entry points:
+Here are the primary entry points and the main classes involved in the 
+Scene infrastructure:
 
 .. autosummary::
    :toctree: generated/
 
+   ~yt.visualization.volume_rendering.volume_rendering.volume_render
+   ~yt.visualization.volume_rendering.volume_rendering.create_scene
+   ~yt.visualization.volume_rendering.off_axis_projection.off_axis_projection
+   ~yt.visualization.volume_rendering.scene.Scene
    ~yt.visualization.volume_rendering.camera.Camera
-   ~yt.visualization.volume_rendering.camera.off_axis_projection
-   ~yt.visualization.volume_rendering.camera.allsky_projection
+   ~yt.utilities.amr_kdtree.amr_kdtree.AMRKDTree
 
-These objects set up the way the image looks:
+The different kinds of sources:
 
 .. autosummary::
    :toctree: generated/
 
-   ~yt.visualization.volume_rendering.transfer_functions.ColorTransferFunction
-   ~yt.visualization.volume_rendering.transfer_functions.MultiVariateTransferFunction
-   ~yt.visualization.volume_rendering.transfer_functions.PlanckTransferFunction
-   ~yt.visualization.volume_rendering.transfer_functions.ProjectionTransferFunction
-   ~yt.visualization.volume_rendering.transfer_functions.TransferFunction
+   ~yt.visualization.volume_rendering.render_source.RenderSource
+   ~yt.visualization.volume_rendering.render_source.VolumeSource
+   ~yt.visualization.volume_rendering.render_source.PointSource
+   ~yt.visualization.volume_rendering.render_source.LineSource
+   ~yt.visualization.volume_rendering.render_source.BoxSource
+   ~yt.visualization.volume_rendering.render_source.GridSource
+   ~yt.visualization.volume_rendering.render_source.CoordinateVectorSource
+   ~yt.visualization.volume_rendering.render_source.MeshSource
 
-There are also advanced objects for particular use cases:
+The different kinds of transfer functions:
 
 .. autosummary::
    :toctree: generated/
 
-   ~yt.visualization.volume_rendering.camera.FisheyeCamera
-   ~yt.visualization.volume_rendering.camera.MosaicCamera
-   ~yt.visualization.volume_rendering.camera.PerspectiveCamera
-   ~yt.utilities.amr_kdtree.amr_kdtree.AMRKDTree
-   ~yt.visualization.volume_rendering.camera.StereoPairCamera
+   ~yt.visualization.volume_rendering.transfer_functions.TransferFunction
+   ~yt.visualization.volume_rendering.transfer_functions.ColorTransferFunction
+   ~yt.visualization.volume_rendering.transfer_functions.ProjectionTransferFunction
+   ~yt.visualization.volume_rendering.transfer_functions.PlanckTransferFunction
+   ~yt.visualization.volume_rendering.transfer_functions.MultiVariateTransferFunction
+   ~yt.visualization.volume_rendering.transfer_function_helper.TransferFunctionHelper
+ 
+The different kinds of lenses:
+
+.. autosummary::
+   :toctree: generated/
+
+   ~yt.visualization.volume_rendering.lens.Lens
+   ~yt.visualization.volume_rendering.lens.PlaneParallelLens
+   ~yt.visualization.volume_rendering.lens.PerspectiveLens
+   ~yt.visualization.volume_rendering.lens.StereoPerspectiveLens
+   ~yt.visualization.volume_rendering.lens.FisheyeLens
+   ~yt.visualization.volume_rendering.lens.SphericalLens
+   ~yt.visualization.volume_rendering.lens.StereoSphericalLens
 
 Streamlining
 ^^^^^^^^^^^^

diff -r 11079ba03a6dd7c5e0214efe0b06e14667d9366a -r 25a0dbd51257bb3fd355338fd4594985c5963f77 doc/source/visualizing/TransferFunctionHelper_Tutorial.ipynb
--- a/doc/source/visualizing/TransferFunctionHelper_Tutorial.ipynb
+++ b/doc/source/visualizing/TransferFunctionHelper_Tutorial.ipynb
@@ -1,7 +1,7 @@
 {
  "metadata": {
   "name": "",
-  "signature": "sha256:5a1547973517987ff047f1b2405277a0e98392e8fd5ffe04521cb2dc372d32d3"
+  "signature": "sha256:ed09405c56bab51abd351d107a4354726709d289b965f274106f4451b387f5ba"
  },
  "nbformat": 3,
  "nbformat_minor": 0,
@@ -25,6 +25,8 @@
       "import numpy as np\n",
       "from IPython.core.display import Image\n",
       "from yt.visualization.volume_rendering.transfer_function_helper import TransferFunctionHelper\n",
+      "from yt.visualization.volume_rendering.render_source import VolumeSource\n",
+      "from yt.visualization.volume_rendering.camera import Camera\n",
       "\n",
       "def showme(im):\n",
       "    # screen out NaNs\n",
@@ -66,7 +68,7 @@
      "cell_type": "code",
      "collapsed": false,
      "input": [
-      "tfh = yt.TransferFunctionHelper(ds)"
+      "tfh = TransferFunctionHelper(ds)"
      ],
      "language": "python",
      "metadata": {},
@@ -84,7 +86,7 @@
      "collapsed": false,
      "input": [
       "# Build a transfer function that is a multivariate gaussian in temperature\n",
-      "tfh = yt.TransferFunctionHelper(ds)\n",
+      "tfh = TransferFunctionHelper(ds)\n",
       "tfh.set_field('temperature')\n",
       "tfh.set_log(True)\n",
       "tfh.set_bounds()\n",
@@ -124,7 +126,7 @@
      "cell_type": "code",
      "collapsed": false,
      "input": [
-      "tfh = yt.TransferFunctionHelper(ds)\n",
+      "tfh = TransferFunctionHelper(ds)\n",
       "tfh.set_field('temperature')\n",
       "tfh.set_bounds()\n",
       "tfh.set_log(True)\n",
@@ -143,27 +145,20 @@
      "cell_type": "markdown",
      "metadata": {},
      "source": [
-      "Finally, let's take a look at the volume rendering."
+      "Finally, let's take a look at the volume rendering. First use the helper function to create a default rendering, then we override this with the transfer function we just created."
      ]
     },
     {
      "cell_type": "code",
      "collapsed": false,
      "input": [
-      "L = [-0.1, -1.0, -0.1]\n",
-      "c = ds.domain_center\n",
-      "W = 1.5*ds.domain_width\n",
-      "Npixels = 512 \n",
-      "cam = ds.camera(c, L, W, Npixels, tfh.tf, fields=['temperature'],\n",
-      "                  north_vector=[1.,0.,0.], steady_north=True, \n",
-      "                  sub_samples=5, no_ghost=False)\n",
+      "im, sc = yt.volume_render(ds, ['temperature'])\n",
       "\n",
-      "# Here we substitute the TransferFunction we constructed earlier.\n",
-      "cam.transfer_function = tfh.tf\n",
+      "source = sc.get_source(0)\n",
+      "source.set_transfer_function(tfh.tf)\n",
+      "im2 = sc.render()\n",
       "\n",
-      "\n",
-      "im = cam.snapshot()\n",
-      "showme(im[:,:,:3])"
+      "showme(im2[:,:,:3])"
      ],
      "language": "python",
      "metadata": {},

diff -r 11079ba03a6dd7c5e0214efe0b06e14667d9366a -r 25a0dbd51257bb3fd355338fd4594985c5963f77 doc/source/visualizing/Volume_Rendering_Tutorial.ipynb
--- /dev/null
+++ b/doc/source/visualizing/Volume_Rendering_Tutorial.ipynb
@@ -0,0 +1,272 @@
+{
+ "metadata": {
+  "name": "",
+  "signature": "sha256:16b0b0137841594b135cb8e07f6029e0cd646630816929435e584cbbf10555b4"
+ },
+ "nbformat": 3,
+ "nbformat_minor": 0,
+ "worksheets": [
+  {
+   "cells": [
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "This notebook shows how to use the new (in version 3.3) Scene interface to create custom volume renderings. To begin, we load up a dataset and use the yt.create_scene method to set up a basic Scene. We store the Scene in a variable called 'sc' and render the default ('gas', 'density') field."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "import yt\n",
+      "import numpy as np\n",
+      "from yt.visualization.volume_rendering.transfer_function_helper import TransferFunctionHelper\n",
+      "from yt.visualization.volume_rendering.api import Scene, Camera, VolumeSource\n",
+      "\n",
+      "ds = yt.load(\"IsolatedGalaxy/galaxy0030/galaxy0030\")\n",
+      "sc = yt.create_scene(ds)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": []
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "Now we can look at some information about the Scene we just created using the python print keyword:"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "print sc"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": []
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "This prints out information about the Sources, Camera, and Lens associated with this Scene. Each of these can also be printed individually. For example, to print only the information about the first (and currently, only) Source, we can do:"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "print sc.get_source(0)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": []
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "We can see that the yt.create_source has created a VolumeSource with default values for the center, bounds, and transfer function. Now, let's see what this Scene looks like. In the notebook, we can do this by calling sc.show(). "
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "sc.show()"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": []
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "That looks okay, but it's a little too zoomed-out. To fix this, let's modify the Camera associated with our Scene. This next bit of code will zoom in the camera (i.e. decrease the width of the view) by a factor of 3."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "sc.camera.zoom(3.0)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": []
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "Now when we print the Scene, we see that the Camera width has decreased by a factor of 3:"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "print sc"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": []
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "To see what this looks like, we re-render the image and display the scene again. Note that we don't actually have to call sc.show() here - we can just have Ipython evaluate the Scene and that will display it automatically."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "sc.render()\n",
+      "sc"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": []
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "That's better! The image looks a little washed-out though, so we use the sigma_clip argument to sc.show() to improve the contrast:"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "sc.show(sigma_clip=4.0)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": []
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "Next, we demonstrate how to change the mapping between the field values and the colors in the image. We use the TransferFunctionHelper to create a new transfer function using the \"gist_rainbow\" colormap, and then re-create the image as follows:"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "# Set up a custom transfer function using the TransferFunctionHelper. \n",
+      "# We use 10 Gaussians evenly spaced logarithmically between the min and max\n",
+      "# field values.\n",
+      "tfh = TransferFunctionHelper(ds)\n",
+      "tfh.set_field('density')\n",
+      "tfh.set_log(True)\n",
+      "tfh.set_bounds()\n",
+      "tfh.build_transfer_function()\n",
+      "tfh.tf.add_layers(10, colormap='gist_rainbow')\n",
+      "\n",
+      "# Grab the first render source and set it to use the new transfer function\n",
+      "render_source = sc.get_source(0)\n",
+      "render_source.transfer_function = tfh.tf\n",
+      "\n",
+      "sc.render()\n",
+      "sc.show(sigma_clip=4.0)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": []
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "Now, let's try using a different lens type. We can give a sense of depth to the image by using the perspective lens. To do, we create a new Camera below. We also demonstrate how to switch the camera to a new position and orientation."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "cam = Camera(ds, lens_type='perspective')\n",
+      "\n",
+      "# Standing at (x=0.05, y=0.5, z=0.5), we look at the area of x>0.05 (with some open angle\n",
+      "# specified by camera width) along the positive x direction.\n",
+      "cam.position = ds.arr([0.05, 0.5, 0.5], 'code_length')\n",
+      "\n",
+      "normal_vector = [1., 0., 0.]\n",
+      "north_vector = [0., 0., 1.]\n",
+      "cam.switch_orientation(normal_vector=normal_vector,\n",
+      "                       north_vector=north_vector)\n",
+      "\n",
+      "# The width determines the opening angle\n",
+      "cam.set_width(ds.domain_width * 0.5)\n",
+      "\n",
+      "sc.camera = cam\n",
+      "print sc.camera"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": []
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "The resulting image looks like:"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "sc.render()\n",
+      "sc.show(sigma_clip=4.0)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": []
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "Finally, the next cell restores the lens and the transfer function to the defaults, moves the camera, and adds an opaque source  that shows the axes of the simulation coordinate system."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "# set the lens type back to plane-parallel\n",
+      "sc.camera.set_lens('plane-parallel')\n",
+      "\n",
+      "# move the camera to the left edge of the domain\n",
+      "sc.camera.set_position(ds.domain_left_edge)\n",
+      "sc.camera.switch_orientation()\n",
+      "\n",
+      "# reset the transfer function to the default\n",
+      "render_source = sc.get_source(0)\n",
+      "render_source.build_default_transfer_function()\n",
+      "\n",
+      "# add an opaque source to the scene\n",
+      "sc.annotate_axes()\n",
+      "\n",
+      "sc.render()\n",
+      "sc.show(sigma_clip=4.0)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": []
+    }
+   ],
+   "metadata": {}
+  }
+ ]
+}
\ No newline at end of file

diff -r 11079ba03a6dd7c5e0214efe0b06e14667d9366a -r 25a0dbd51257bb3fd355338fd4594985c5963f77 doc/source/visualizing/_images/scene_diagram.svg
--- /dev/null
+++ b/doc/source/visualizing/_images/scene_diagram.svg
@@ -0,0 +1,512 @@
+<?xml version="1.0" standalone="yes"?>
+
+<svg version="1.1" viewBox="0.0 0.0 710.0 462.0" fill="none" stroke="none"
+stroke-linecap="square" stroke-miterlimit="10"
+xmlns="http://www.w3.org/2000/svg"
+xmlns:xlink="http://www.w3.org/1999/xlink"><clipPath id="p.0"><path d="m0
+0l710.0 0l0 462.0l-710.0 0l0 -462.0z" clip-rule="nonzero"></path></clipPath><g
+clip-path="url(#p.0)"><path fill="#000000" fill-opacity="0.0" d="m0 0l710.4252
+0l0 462.4462l-710.4252 0z" fill-rule="nonzero"></path><path fill="#000000"
+fill-opacity="0.0" d="m50.425198 121.13386l489.73227 0l0 289.6063l-489.73227
+0z" fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0"
+d="m540.1575 121.13386l96.53546 -96.53543l0 289.6063l-96.53546 96.53543z"
+fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0"
+d="m50.425198 121.13386l96.53543 -96.53543l489.7323 0l-96.53546 96.53543z"
+fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0"
+d="m50.425198 121.13386l96.53543 -96.53543l489.7323 0l0 289.6063l-96.53546
+96.53543l-489.73227 0zm0 0l489.73227 0l96.53546 -96.53543m-96.53546 96.53543l0
+289.6063" fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.2"
+d="m540.1575 121.13386l96.53546 -96.53543l0 289.6063l-96.53546 96.53543z"
+fill-rule="nonzero"></path><path fill="#ffffff" fill-opacity="0.2"
+d="m50.425198 121.13386l96.53543 -96.53543l489.7323 0l-96.53546 96.53543z"
+fill-rule="nonzero"></path><path stroke="#000000" stroke-width="2.0"
+stroke-linejoin="round" stroke-linecap="butt" d="m50.425198 121.13386l96.53543
+-96.53543l489.7323 0l0 289.6063l-96.53546 96.53543l-489.73227 0zm0 0l489.73227
+0l96.53546 -96.53543m-96.53546 96.53543l0 289.6063"
+fill-rule="nonzero"></path><path fill="#cccccc" d="m399.52493 268.74014l0 0c0
+-6.521576 14.789307 -11.80838 33.032806 -11.80838c18.24353 0 33.032806 5.286804
+33.032806 11.80838l0 47.233612c0 6.5216064 -14.789276 11.808411 -33.032806
+11.808411c-18.2435 0 -33.032806 -5.286804 -33.032806 -11.808411z"
+fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0"
+d="m465.59055 268.74014l0 0c0 6.5216064 -14.789276 11.808411 -33.032806
+11.808411c-18.2435 0 -33.032806 -5.286804 -33.032806 -11.808411"
+fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0"
+d="m399.52493 268.74014l0 0c0 -6.521576 14.789307 -11.80838 33.032806
+-11.80838c18.24353 0 33.032806 5.286804 33.032806 11.80838l0 47.233612c0
+6.5216064 -14.789276 11.808411 -33.032806 11.808411c-18.2435 0 -33.032806
+-5.286804 -33.032806 -11.808411z" fill-rule="nonzero"></path><path
+stroke="#000000" stroke-width="2.0" stroke-linejoin="round"
+stroke-linecap="butt" d="m465.59055 268.74014l0 0c0 6.5216064 -14.789276
+11.808411 -33.032806 11.808411c-18.2435 0 -33.032806 -5.286804 -33.032806
+-11.808411" fill-rule="nonzero"></path><path stroke="#000000"
+stroke-width="2.0" stroke-linejoin="round" stroke-linecap="butt" d="m399.52493
+268.74014l0 0c0 -6.521576 14.789307 -11.80838 33.032806 -11.80838c18.24353 0
+33.032806 5.286804 33.032806 11.80838l0 47.233612c0 6.5216064 -14.789276
+11.808411 -33.032806 11.808411c-18.2435 0 -33.032806 -5.286804 -33.032806
+-11.808411z" fill-rule="nonzero"></path><path fill="#cccccc" d="m333.45938
+255.11298l20.896912 1.2207031E-4l6.457367 -18.286331l6.457367
+18.286331l20.896881 -1.2207031E-4l-16.906006 11.301453l6.457611
+18.286224l-16.905853 -11.301636l-16.905884 11.301636l6.4576416 -18.286224z"
+fill-rule="nonzero"></path><path stroke="#000000" stroke-width="2.0"
+stroke-linejoin="round" stroke-linecap="butt" d="m333.45938 255.11298l20.896912
+1.2207031E-4l6.457367 -18.286331l6.457367 18.286331l20.896881
+-1.2207031E-4l-16.906006 11.301453l6.457611 18.286224l-16.905853
+-11.301636l-16.905884 11.301636l6.4576416 -18.286224z"
+fill-rule="nonzero"></path><path fill="#cccccc" d="m408.14435
+214.56168l38.53543 0l0 22.251968l-38.53543 0z" fill-rule="nonzero"></path><path
+fill="#a3a3a3" d="m446.67978 214.56168l7.417328 -7.4173126l0
+22.251968l-7.417328 7.4173126z" fill-rule="nonzero"></path><path fill="#d6d6d6"
+d="m408.14435 214.56168l7.417328 -7.4173126l38.53543 0l-7.417328 7.4173126z"
+fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0"
+d="m408.14435 214.56168l7.417328 -7.4173126l38.53543 0l0 22.251968l-7.417328
+7.4173126l-38.53543 0zm0 0l38.53543 0l7.417328 -7.4173126m-7.417328 7.4173126l0
+22.251968" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="2.0"
+stroke-linejoin="round" stroke-linecap="butt" d="m408.14435 214.56168l7.417328
+-7.4173126l38.53543 0l0 22.251968l-7.417328 7.4173126l-38.53543 0zm0 0l38.53543
+0l7.417328 -7.4173126m-7.417328 7.4173126l0 22.251968"
+fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0"
+d="m329.62204 327.78217l135.9685 0l0 54.582672l-135.9685 0z"
+fill-rule="nonzero"></path><path fill="#000000" d="m354.4149 362.55652q-0.15625
+0.0625 -0.375 0.0625q-0.234375 0 -0.5 -0.140625q-2.796875 -3.015625 -3.734375
+-7.765625q-0.328125 -1.640625 -0.328125 -3.40625q0 -3.375 1.203125
+-6.4375q1.046875 -2.703125 2.859375 -4.671875q0.125 -0.109375 0.515625
+-0.109375q0.375 0 0.640625 0.25q0.265625 0.25 0.265625 0.5625q-2.4375 4.171875
+-2.859375 7.734375q-0.140625 1.25 -0.140625 2.625q-0.015625 0.0625 -0.015625
+0.109375q0 1.34375 0.15625 2.5625q0.140625 1.25 0.5 2.515625q0.6875 2.453125
+2.359375 5.328125q0 0.515625 -0.546875 0.78125zm5.5 -4.3125l1.34375 0q1.625 0
+2.1875 -0.984375q0.171875 -0.3125 0.171875 -0.671875q0 -0.359375 -0.171875
+-0.625q-0.171875 -0.265625 -0.53125 -0.484375q-0.5625 -0.34375 -1.84375
+-0.734375q-1.28125 -0.390625 -1.953125 -0.671875q-0.65625 -0.296875 -1.15625
+-0.703125q-1.0 -0.84375 -1.0 -2.3125q0 -2.171875 2.421875 -2.921875q0.90625
+-0.28125 1.796875 -0.28125q0.890625 0 1.640625 0.125q0.765625 0.109375 1.375
+0.390625q1.390625 0.640625 1.390625 1.84375l0 0.0625q0 0.65625 -0.453125
+1.109375q-0.4375 0.4375 -1.140625 0.453125q-1.109375 0 -1.5 -0.953125q-0.125
+-0.296875 -0.125 -0.484375q0 -0.1875 0.140625 -0.890625l-1.15625 0q-0.953125 0
+-1.40625 0.328125q-0.828125 0.578125 -0.828125 1.265625q0 0.90625 1.640625
+1.46875l0.890625 0.3125q2.25 0.75 2.890625 1.28125q0.65625 0.515625 0.9375
+1.140625q0.296875 0.625 0.296875 1.421875q0 0.8125 -0.3125 1.390625q-0.296875
+0.59375 -0.859375 1.015625q-1.21875 0.921875 -3.140625 0.921875q-1.71875 0
+-3.078125 -0.515625q-1.0 -0.375 -1.421875 -1.078125q-0.21875 -0.359375 -0.21875
+-0.765625q0 -0.390625 0.125 -0.6875q0.140625 -0.3125 0.34375 -0.515625q0.453125
+-0.453125 1.15625 -0.453125q0.671875 0 1.140625 0.46875q0.46875 0.4375 0.46875
+1.0l0 0.015625q-0.015625 0.578125 -0.0625 0.71875zm12.8515625 1.8125q-1.25 0
+-2.203125 -0.46875q-0.953125 -0.46875 -1.609375 -1.3125q-1.3125 -1.703125
+-1.3125 -4.453125q0 -2.890625 1.78125 -4.5q1.625 -1.46875 4.078125
+-1.46875l0.03125 0q2.296875 0 3.703125 1.75q1.328125 1.65625 1.328125 4.3125q0
+2.953125 -1.640625 4.59375q-1.546875 1.546875 -4.15625 1.546875zm-1.703125
+-9.578125q-0.4375 0.46875 -0.734375 1.28125q-0.28125 0.8125 -0.28125 2.0q0
+1.203125 0.21875 2.03125q0.234375 0.8125 0.640625 1.359375q0.796875 1.09375
+2.234375 1.09375q1.46875 0 2.28125 -1.28125q0.734375 -1.171875 0.734375
+-3.078125q0 -3.171875 -1.859375 -3.984375q-0.5625 -0.234375 -1.140625
+-0.234375q-0.578125 0 -1.109375 0.171875q-0.53125 0.171875 -0.984375
+0.640625zm18.230469 8.21875q-1.875 1.359375 -3.375 1.359375q-2.953125 0
+-3.796875 -2.375q-0.28125 -0.78125 -0.28125 -1.828125l0 -5.953125l-1.640625
+0q-0.4375 0 -0.4375 -0.890625q0 -0.59375 0.234375 -0.65625q1.96875 -0.5 3.59375
+-0.5q0.515625 0 0.546875 0.9375l0 6.21875q0 2.28125 1.171875 2.71875q0.34375
+0.140625 0.875 0.140625q0.515625 0 0.984375 -0.109375q0.484375 -0.125 0.859375
+-0.28125q0.34375 -0.171875 0.625 -0.375l0.5 -0.375l0 -6.828125l-1.59375
+0q-0.484375 0 -0.484375 -0.875q0 -0.546875 0.3125 -0.671875q0.3125 -0.125 0.75
+-0.21875q0.453125 -0.078125 0.921875 -0.15625q0.921875 -0.109375 1.46875
+-0.125l0.03125 0q0.515625 0 0.625 0.125q0.140625 0.125 0.1875 0.328125q0.078125
+0.296875 0.078125 1.03125l0 8.828125l1.96875 0q0.3125 0 0.3125 0.765625q0
+0.8125 -0.484375 0.890625q-1.1875 0.1875 -2.125 0.1875q-0.9375 0 -1.109375
+-0.09375q-0.15625 -0.09375 -0.28125 -0.265625q-0.1875 -0.25 -0.4375
+-0.953125zm9.6328125 -9.96875l0 0.46875q0 0.140625 -0.015625 0.28125q0 0.140625
+0 0.25q1.375 -1.25 2.75 -1.71875q0.453125 -0.140625 0.921875 -0.15625l0.03125
+0q0.453125 0 0.8125 0.171875q0.375 0.1875 0.609375 0.453125q0.390625 0.46875
+0.390625 0.96875q0 0.515625 -0.125 0.8125q-0.125 0.296875 -0.359375 0.5q-0.5
+0.453125 -0.984375 0.453125q-0.484375 0 -0.75 -0.125q-0.25 -0.140625 -0.453125
+-0.34375q-0.421875 -0.453125 -0.421875 -1.0q-0.671875 0.3125 -1.03125
+0.71875q-0.34375 0.421875 -0.578125 0.765625l-0.515625 0.796875l0
+6.140625l2.84375 0q0.265625 0 0.265625 0.71875q0 0.578125 -0.171875
+0.765625q-0.171875 0.171875 -0.3125 0.171875l-6.8125 0q-0.328125 0 -0.328125
+-0.734375q0.015625 -0.546875 0.3125 -0.84375q0.09375 -0.078125 0.40625
+-0.078125q0.328125 0 0.65625 -0.03125q0.328125 -0.03125 0.515625
+-0.078125q0.328125 -0.109375 0.328125 -0.390625l0 -7.71875l-2.0625 0q-0.453125
+0 -0.453125 -0.921875q0 -0.578125 0.296875 -0.703125q0.296875 -0.140625 0.78125
+-0.21875q0.5 -0.09375 1.015625 -0.140625q0.984375 -0.09375 1.546875
+-0.109375l0.09375 0q0.484375 0 0.578125 0.0625q0.125 0.046875 0.171875
+0.15625q0.046875 0.125 0.046875 0.515625l0 0.140625zm13.277344
+0.78125l-1.078125 0q-1.21875 0 -2.109375 0.921875q-1.125 1.1875 -1.125
+3.40625q0 3.265625 1.828125 4.125q0.5625 0.28125 1.171875 0.28125q0.59375 0
+1.0625 -0.125q0.484375 -0.125 0.953125 -0.34375q0.46875 -0.1875 0.890625
+-0.453125l0.828125 -0.5q0.046875 -0.03125 0.15625 -0.03125q0.109375 0 0.265625
+0.140625q0.171875 0.125 0.3125 0.3125q0.3125 0.4375 0.3125 0.734375q0 0.296875
+-0.140625 0.40625q-2.234375 1.671875 -4.875 1.671875q-2.4375 0 -3.828125
+-1.703125q-1.328125 -1.640625 -1.328125 -4.515625q0 -2.8125 1.6875
+-4.453125q1.59375 -1.515625 4.078125 -1.53125q2.640625 0.015625 3.65625
+1.15625q0.390625 0.453125 0.390625 0.953125q0 0.5 -0.109375 0.796875q-0.140625
+0.296875 -0.359375 0.5q-0.46875 0.46875 -1.140625 0.46875q-0.65625 0 -1.125
+-0.46875q-0.46875 -0.4375 -0.46875 -0.890625q0 -0.4375 0.09375
+-0.859375zm5.046875 4.28125q0 -1.453125 0.484375 -2.578125q0.46875 -1.09375
+1.265625 -1.84375q1.609375 -1.5 3.984375 -1.515625l0.046875 0q2.171875 0
+3.359375 1.375q1.1875 1.359375 1.1875 3.921875q0 0.546875 -0.515625
+0.953125q-0.484375 0.390625 -1.234375 0.390625l-6.171875 0q0.203125 2.734375
+1.90625 3.515625q0.53125 0.234375 1.140625 0.234375q0.59375 0 1.0625
+-0.125q0.46875 -0.140625 0.890625 -0.34375q0.4375 -0.203125 0.859375
+-0.46875q0.84375 -0.53125 1.0 -0.53125q0.140625 0.015625 0.3125
+0.140625q0.140625 0.140625 0.28125 0.328125q0.296875 0.4375 0.3125 0.75l0
+0.015625q0 0.28125 -0.125 0.375q-2.15625 1.671875 -4.875 1.671875q-2.453125 0
+-3.828125 -1.71875q-1.34375 -1.65625 -1.34375 -4.546875zm7.921875 -1.0625l0
+-0.046875q0 -2.3125 -1.34375 -2.859375q-0.421875 -0.15625 -0.9375
+-0.15625q-0.515625 0 -1.078125 0.1875q-0.5625 0.1875 -1.03125 0.5625q-1.046875
+0.859375 -1.125 2.3125l5.515625 0zm7.3867188 5.515625l1.34375 0q1.625 0 2.1875
+-0.984375q0.171875 -0.3125 0.171875 -0.671875q0 -0.359375 -0.171875
+-0.625q-0.171875 -0.265625 -0.53125 -0.484375q-0.5625 -0.34375 -1.84375
+-0.734375q-1.28125 -0.390625 -1.953125 -0.671875q-0.65625 -0.296875 -1.15625
+-0.703125q-1.0 -0.84375 -1.0 -2.3125q0 -2.171875 2.421875 -2.921875q0.90625
+-0.28125 1.796875 -0.28125q0.890625 0 1.640625 0.125q0.765625 0.109375 1.375
+0.390625q1.390625 0.640625 1.390625 1.84375l0 0.0625q0 0.65625 -0.453125
+1.109375q-0.4375 0.4375 -1.140625 0.453125q-1.109375 0 -1.5 -0.953125q-0.125
+-0.296875 -0.125 -0.484375q0 -0.1875 0.140625 -0.890625l-1.15625 0q-0.953125 0
+-1.40625 0.328125q-0.828125 0.578125 -0.828125 1.265625q0 0.90625 1.640625
+1.46875l0.890625 0.3125q2.25 0.75 2.890625 1.28125q0.65625 0.515625 0.9375
+1.140625q0.296875 0.625 0.296875 1.421875q0 0.8125 -0.3125 1.390625q-0.296875
+0.59375 -0.859375 1.015625q-1.21875 0.921875 -3.140625 0.921875q-1.71875 0
+-3.078125 -0.515625q-1.0 -0.375 -1.421875 -1.078125q-0.21875 -0.359375 -0.21875
+-0.765625q0 -0.390625 0.125 -0.6875q0.140625 -0.3125 0.34375 -0.515625q0.453125
+-0.453125 1.15625 -0.453125q0.671875 0 1.140625 0.46875q0.46875 0.4375 0.46875
+1.0l0 0.015625q-0.015625 0.578125 -0.0625 0.71875zm9.1015625 4.234375q-0.265625
+0.140625 -0.484375 0.140625q-0.234375 0 -0.390625 -0.0625q-0.140625 -0.0625
+-0.265625 -0.171875q-0.28125 -0.25 -0.28125 -0.609375q2.4375 -4.140625 2.859375
+-7.84375q0.140625 -1.265625 0.15625 -2.671875l0 -0.09375q0 -1.328125 -0.140625
+-2.53125q-0.15625 -1.25 -0.5 -2.484375q-0.6875 -2.390625 -2.375 -5.25q0 -0.3125
+0.265625 -0.5625q0.28125 -0.25 0.5625 -0.25q0.46875 0 0.59375 0.109375q2.828125
+3.046875 3.734375 7.6875q0.328125 1.625 0.328125 3.421875q0 5.0625 -2.5
+9.109375q-0.734375 1.1875 -1.5625 2.0625z" fill-rule="nonzero"></path><path
+fill="#000000" fill-opacity="0.0" d="m147.41995 24.598425l135.9685 0l0
+54.582672l-135.9685 0z" fill-rule="nonzero"></path><path fill="#000000"
+d="m187.11708 54.654053q0.75 0.40625 1.875 0.40625q1.125 0 1.8125
+-0.203125q0.671875 -0.1875 1.15625 -0.5625q1.03125 -0.78125 1.046875
+-2.078125l0 -0.015625q0 -1.46875 -2.40625 -2.859375q-0.671875 -0.390625
+-1.46875 -0.78125l-1.6875 -0.859375q-3.1875 -1.6875 -3.1875 -4.5q0 -2.171875
+1.75 -3.375q1.578125 -1.0625 4.0 -1.0625l0.109375 0q1.796875 0 3.34375
+0.8125q1.859375 0.953125 1.859375 2.515625q0 0.75 -0.515625 1.296875q-0.515625
+0.515625 -1.28125 0.515625q-0.765625 0 -1.28125 -0.515625q-0.53125 -0.53125
+-0.53125 -1.296875q0 -0.625 0.34375 -1.09375q-0.6875 -0.421875 -1.65625
+-0.421875q-0.96875 0 -1.59375 0.171875q-0.640625 0.15625 -1.109375
+0.484375q-0.953125 0.671875 -0.96875 1.921875l0 0.015625q0 0.921875 1.015625
+1.65625q0.5 0.375 1.21875 0.75l1.578125 0.8125q2.59375 1.296875 3.640625
+2.390625q1.296875 1.34375 1.296875 3.234375q0 2.15625 -1.6875 3.515625q-1.6875
+1.34375 -4.28125 1.34375q-1.984375 0 -3.6875 -0.828125q-1.984375 -0.984375
+-1.984375 -2.453125q0 -0.78125 0.53125 -1.296875q0.53125 -0.53125 1.28125
+-0.53125q0.765625 0 1.28125 0.53125q0.515625 0.53125 0.515625 1.15625q0 0.625
+-0.328125 1.203125zm17.214844 -8.328125l-1.078125 0q-1.21875 0 -2.109375
+0.921875q-1.125 1.1875 -1.125 3.40625q0 3.265625 1.828125 4.125q0.5625 0.28125
+1.171875 0.28125q0.59375 0 1.0625 -0.125q0.484375 -0.125 0.953125
+-0.34375q0.46875 -0.1875 0.890625 -0.453125l0.828125 -0.5q0.046875 -0.03125
+0.15625 -0.03125q0.109375 0 0.265625 0.140625q0.171875 0.125 0.3125
+0.3125q0.3125 0.4375 0.3125 0.734375q0 0.296875 -0.140625 0.40625q-2.234375
+1.671875 -4.875 1.671875q-2.4375 0 -3.828125 -1.703125q-1.328125 -1.640625
+-1.328125 -4.515625q0 -2.8125 1.6875 -4.453125q1.59375 -1.515625 4.078125
+-1.53125q2.640625 0.015625 3.65625 1.15625q0.390625 0.453125 0.390625
+0.953125q0 0.5 -0.109375 0.796875q-0.140625 0.296875 -0.359375 0.5q-0.46875
+0.46875 -1.140625 0.46875q-0.65625 0 -1.125 -0.46875q-0.46875 -0.4375 -0.46875
+-0.890625q0 -0.4375 0.09375 -0.859375zm5.046875 4.28125q0 -1.453125 0.484375
+-2.578125q0.46875 -1.09375 1.265625 -1.84375q1.6093903 -1.5 3.9843903
+-1.515625l0.046875 0q2.171875 0 3.359375 1.375q1.1875 1.359375 1.1875
+3.921875q0 0.546875 -0.515625 0.953125q-0.484375 0.390625 -1.234375
+0.390625l-6.171875 0q0.203125 2.734375 1.90625 3.515625q0.53125 0.234375
+1.140625 0.234375q0.59375 0 1.0625 -0.125q0.46875 -0.140625 0.890625
+-0.34375q0.4375 -0.203125 0.859375 -0.46875q0.84375 -0.53125 1.0
+-0.53125q0.140625 0.015625 0.3125 0.140625q0.140625 0.140625 0.28125
+0.328125q0.296875 0.4375 0.3125 0.75l0 0.015625q0 0.28125 -0.125 0.375q-2.15625
+1.671875 -4.875 1.671875q-2.453125 0 -3.8281403 -1.71875q-1.34375 -1.65625
+-1.34375 -4.546875zm7.9218903 -1.0625l0 -0.046875q0 -2.3125 -1.34375
+-2.859375q-0.421875 -0.15625 -0.9375 -0.15625q-0.515625 0 -1.078125
+0.1875q-0.5625 0.1875 -1.03125 0.5625q-1.046875 0.859375 -1.125 2.3125l5.515625
+0zm12.386719 7.09375q-0.3125 0 -0.3125 -0.71875q0 -0.5625 0.171875 -0.75q0.1875
+-0.1875 0.3125 -0.1875q1.21875 0 1.21875 -0.5l0 -3.484375q0 -2.265625 -0.265625
+-2.96875q-0.25 -0.71875 -0.65625 -1.015625q-0.40625 -0.296875 -1.09375
+-0.296875q-1.296875 0 -3.203125 1.5l0 6.765625l1.96875 0q0.296875 0 0.296875
+0.71875q0 0.5625 -0.171875 0.75q-0.171875 0.1875 -0.3125 0.1875l-5.96875
+0q-0.3125 0 -0.3125 -0.71875q0 -0.5625 0.171875 -0.75q0.1875 -0.1875 0.3125
+-0.1875l0.125 0q1.34375 0 1.53125 -0.265625q0.0625 -0.09375 0.0625 -0.234375l0
+-7.765625l-2.0625 0q-0.359375 -0.015625 -0.453125 -0.578125q-0.015625 -0.171875
+-0.015625 -0.34375q0 -0.53125 0.296875 -0.65625q0.3125 -0.125 0.796875
+-0.21875q0.484375 -0.09375 1.015625 -0.140625q1.046875 -0.125 1.578125
+-0.125l0.046875 0q0.515625 0.015625 0.625 0.125q0.109375 0.109375 0.15625
+0.296875q0.0625 0.203125 0.0625 0.84375l0 0.21875q1.765625 -0.984375 3.21875
+-1.34375q0.46875 -0.125 1.203125 -0.125q0.703125 0 1.421875 0.3125q0.734375
+0.3125 1.15625 0.890625q0.75 1.03125 0.75 3.28125l0 5.828125l1.828125 0q0.3125
+0 0.3125 0.71875q0 0.5625 -0.1875 0.75q-0.171875 0.1875 -0.296875
+0.1875l-5.328125 0zm7.0078125 -6.03125q0 -1.453125 0.484375 -2.578125q0.46875
+-1.09375 1.265625 -1.84375q1.609375 -1.5 3.984375 -1.515625l0.046875 0q2.171875
+0 3.359375 1.375q1.1875 1.359375 1.1875 3.921875q0 0.546875 -0.515625
+0.953125q-0.484375 0.390625 -1.234375 0.390625l-6.171875 0q0.203125 2.734375
+1.90625 3.515625q0.53125 0.234375 1.140625 0.234375q0.59375 0 1.0625
+-0.125q0.46875 -0.140625 0.890625 -0.34375q0.4375 -0.203125 0.859375
+-0.46875q0.84375 -0.53125 1.0 -0.53125q0.140625 0.015625 0.3125
+0.140625q0.140625 0.140625 0.28125 0.328125q0.296875 0.4375 0.3125 0.75l0
+0.015625q0 0.28125 -0.125 0.375q-2.15625 1.671875 -4.875 1.671875q-2.453125 0
+-3.828125 -1.71875q-1.34375 -1.65625 -1.34375 -4.546875zm7.921875 -1.0625l0
+-0.046875q0 -2.3125 -1.34375 -2.859375q-0.421875 -0.15625 -0.9375
+-0.15625q-0.515625 0 -1.078125 0.1875q-0.5625 0.1875 -1.03125 0.5625q-1.046875
+0.859375 -1.125 2.3125l5.515625 0z" fill-rule="nonzero"></path><path
+fill="#000000" fill-opacity="0.0" d="m133.71216 260.5739l55.118057
+-122.19269l60.047882 27.08609l-12.547058 67.867874l-42.571 54.324814z"
+fill-rule="nonzero"></path><path stroke="#000000" stroke-width="2.0"
+stroke-linejoin="round" stroke-linecap="butt" d="m133.71216 260.5739l55.118057
+-122.19269l60.047882 27.08609l-12.547058 67.867874l-42.571 54.324814z"
+fill-rule="nonzero"></path><path fill="#000000" d="m171.84547
+238.87013q-0.6672516 0.4532318 -1.1298218 1.4787292q-0.4625702 1.0254974
+-0.43501282 2.066391q0.033996582 1.0266571 0.52685547 1.9860382q1.046875
+2.0491943 3.6611786 3.2455902q5.4123535 2.4413757 7.995346 0.40112305q0.8348236
+-0.63475037 1.2331543 -1.5178223q0.00642395 -0.01423645 -0.0078125
+-0.0206604q0.3983307 -0.8830719 0.5014343 -1.4536438q0.09527588 -0.59124756
+0.0821991 -1.0942383q-0.013092041 -0.5029907 -0.07897949 -0.9269562q-0.15101624
+-0.80519104 -0.08035278 -0.961853q0.07067871 -0.15667725 0.24887085
+-0.24771118q0.18461609 -0.105285645 0.41334534 -0.15637207q0.5130615
+-0.11138916 0.78367615 0.010681152q0.28486633 0.12849426 0.38008118
+0.41140747q0.080963135 0.27650452 0.16526794 0.811615q0.09072876 0.520874
+0.07725525 1.234726q-0.0713501 1.7162018 -0.7523651 3.2259674q-0.68743896
+1.5240021 -1.6971283 2.5084076q-1.023941 0.97798157 -2.4385529
+1.4540405q-3.0437317 1.0096588 -7.003296 -0.7763977q-4.1162415 -1.8567352
+-5.4350586 -4.9370728q-1.2121582 -2.8608093 0.22697449 -6.0512543l0.0128479
+-0.02848816q0.7581177 -1.6806793 2.199585 -2.7102814q1.5976105 -1.1477356
+2.9222107 -0.5502472q0.6836548 0.30838013 0.9701538 1.0032654q0.25801086
+0.68203735 -0.05680847 1.3799438q-0.31480408 0.6979065 -0.99684143
+0.95591736q-0.70269775 0.26582336 -1.3578796 -0.029708862q-0.64094543
+-0.28910828 -0.93052673 -0.71113586zm8.361542 -5.578766q0.32844543
+-0.0061187744 0.6845093 0.1545105q0.3560791 0.16061401 0.5753021
+0.3966217q0.19854736 0.24383545 0.31443787 0.51893616q0.21247864 0.5929413
+-0.05595398 1.226059q-0.44973755 0.9970093 -1.4791718 0.96118164q-0.32202148
+-0.008117676 -0.7208252 -0.1880188q-0.384552 -0.17346191 -0.6235962
+-0.555542q-0.23904419 -0.38208008 -0.32055664 -0.8473816q-0.0750885 -0.47953796
+-0.005508423 -1.013794q0.055343628 -0.5406952 0.18190002 -1.0492706q0.22880554
+-0.9252472 0.6785431 -1.9222565q0.4497223 -0.99702454 1.1049652
+-1.5756531q0.6474304 -0.59928894 1.3814087 -0.7824402q1.2819519 -0.2959442
+3.0195923 0.48786926l5.526306 2.4927673l0.8095093 -1.7946167q0.13491821
+-0.29910278 0.76161194 -0.016418457q0.28485107 0.12849426 0.5040741
+0.36450195q0.22564697 0.22177124 0.17982483 0.47535706q-0.09892273 0.6753082
+-0.6000366 1.7862701q-0.5075531 1.1251984 -0.6081085 1.2341156q-0.08630371
+0.11532593 -0.23098755 0.1700592q-0.28152466 0.13012695 -0.9269409
+0.07896423q0.3696289 1.7265778 -0.45915222 3.563919q-0.7452698 1.6521912
+-2.052353 2.1939087q-1.2101898 0.51686096 -2.5205536 -0.07420349l-0.02848816
+-0.0128479q-1.6379395 -0.7388458 -2.0034027 -2.3606873q-0.33642578 -1.4201813
+0.37670898 -3.0011597l1.1885681 -2.634964q-1.7946167 -0.8095093 -2.4422913
+-0.7416992q-0.64123535 0.05357361 -1.0269623 0.37667847q-0.38571167 0.32310486
+-0.6940918 1.0067749l-0.48828125 1.0824585zm8.212418 3.0359192q0.6039276
+-1.3388367 0.11392212 -3.1025696l-2.079483 -0.93800354l-1.047226
+2.3216248q-0.3854828 0.8545685 -0.28694153 1.3961182q0.17866516 0.9719238
+0.8623352 1.280304q1.2533875 0.5653839 2.0563965 -0.34083557q0.22680664
+-0.27479553 0.3809967 -0.6166382zm11.960602 -26.70578q0.13491821 -0.29910278
+0.889801 0.041412354q0.7548828 0.34049988 0.57499695 0.73931885l-2.2100983
+4.899597q-0.12849426 0.28486633 -0.78367615 -0.010681152q-0.5269928 -0.23770142
+-0.66770935 -0.60972595q-0.039093018 -0.10333252 -5.493164E-4
+-0.188797l0.0128479 -0.02848816q0.50112915 -1.1109467 0.04534912
+-1.3165436l-3.1761932 -1.4327087q-2.0652466 -0.9315796 -2.8153992
+-0.97854614q-0.75798035 -0.06765747 -1.1956329 0.18060303q-0.43766785
+0.24824524 -0.7203522 0.87493896q-0.5332489 1.1821747 0.04385376
+3.5508423l6.1672363 2.7818756l0.75167847 -1.6664276q0.12849426 -0.28486633
+0.8833771 0.055648804q0.7548828 0.34049988 0.5557251 0.7820282l-2.1908264
+4.856888q-0.12849426 0.28486633 -0.755188 0.002166748l-0.04272461
+-0.01927185q-0.49850464 -0.22485352 -0.64704895 -0.61753845q-0.039093018
+-0.10334778 -5.493164E-4 -0.188797l0.00642395 -0.014251709q0.50112915
+-1.1109619 0.04534912 -1.3165436l-3.1761932 -1.4327087q-2.0652466 -0.9315796
+-2.8153992 -0.97854614q-0.75798035 -0.06765747 -1.1956329
+0.18060303q-0.43766785 0.24824524 -0.7203522 0.87493896q-0.5332489 1.1821747
+0.05027771 3.5365906l6.1672363 2.7818909l0.8095093 -1.794632q0.12207031
+-0.27061462 0.74876404 0.012069702q0.5269928 0.23771667 0.63505554
+0.49215698q0.100234985 0.23376465 0.048843384 0.34770203l-2.4477997
+5.426605q-0.13491821 0.29910278 -0.7473755 0.022842407q-0.55548096 -0.25056458
+-0.65571594 -0.48432922q-0.093826294 -0.24801636 -0.042434692
+-0.36195374l0.05140686 -0.11395264q0.55252075 -1.2248993 0.3874817
+-1.5050354q-0.059753418 -0.09552002 -0.1879425 -0.15333557l-7.0787964
+-3.1930695l-0.848053 1.8800812q-0.14915466 0.29267883 -0.7417908
+0.16249084q-0.14886475 -0.05001831 -0.27703857 -0.10783386q-0.4985199
+-0.22486877 -0.49038696 -0.546875q0.014541626 -0.33625793 0.12825012
+-0.8163452q0.113708496 -0.48008728 0.2894287 -0.98361206q0.3164978 -1.0056915
+0.5349426 -1.4899445l0.01927185 -0.04273987q0.22625732 -0.46359253 0.3709259
+-0.5183258q0.14468384 -0.054718018 0.33486938 -0.020355225q0.2108612
+0.026550293 0.7948303 0.28996277l0.19940186 0.08995056q-0.17132568 -2.0142212
+0.09857178 -3.4866028q0.07879639 -0.47868347 0.20729065 -0.76353455q0.8930359
+-1.9797821 2.7947083 -2.1676025q-0.2232666 -2.0890656 0.07040405
+-3.8421173q0.08381653 -0.5278473 0.38578796 -1.1972656q0.30195618 -0.66941833
+0.88235474 -1.196106q0.58680725 -0.54093933 1.2872772 -0.6877899q1.2484283
+-0.2596283 3.2994232 0.66552734l5.3126526 2.396408l0.7452545
+-1.6522064zm-3.3632507 -3.1840363q-1.3246002 -0.59750366 -2.1509247
+-1.5016022q-0.8042755 -0.8770294 -1.1602936 -1.9118042q-0.70558167 -2.0838013
+0.25672913 -4.2551727l0.01927185 -0.04272461q0.8930359 -1.9797821 2.6346893
+-2.4968872q1.727417 -0.5235138 4.063278 0.5301361q0.49850464 0.22485352
+0.6568146 0.8619232q0.15690613 0.60214233 -0.151474 1.2858124l-2.5377502
+5.626007q2.5760498 0.93914795 3.9884949 -0.292099q0.43208313 -0.38789368
+0.6826477 -0.94337463q0.24414062 -0.5412445 0.322937 -1.019928q0.06454468
+-0.48510742 0.052856445 -0.9532013q-0.005264282 -0.48231506 -0.07392883
+-0.97610474q-0.1373291 -0.9875641 -0.0730896 -1.1299896q0.07206726 -0.12176514
+0.25668335 -0.22703552q0.18600464 -0.070373535 0.41474915 -0.12145996q0.520874
+-0.09072876 0.8121643 0.023529053l0.01423645 0.00642395q0.25637817 0.11564636
+0.2904358 0.26812744q0.6374054 2.6529846 -0.48049927 5.1312714q-1.008667
+2.2361603 -3.1407776 2.782837q-2.0622864 0.5438843 -4.6972504
+-0.64468384zm2.2887878 -7.6580963l-0.04272461 -0.01927185q-2.1079712 -0.9508667
+-3.1589966 0.049179077q-0.3159027 0.3203125 -0.5279083 0.790329q-0.21202087
+0.47003174 -0.2723999 1.0598755q-0.06036377 0.58984375 0.08872986
+1.1713257q0.35290527 1.3076477 1.6453857 1.9763489l2.2679138
+-5.0277863zm-0.24273682 -9.189972l0.42729187 0.19273376q0.12817383 0.05783081
+0.24993896 0.12989807q0.12818909 0.05781555 0.22789001 0.10279846q-0.57406616
+-1.7673645 -0.43598938 -3.213501q0.058135986 -0.47087097 0.2366333
+-0.9045868l0.0128479 -0.02848816q0.18630981 -0.41304016 0.49075317
+-0.6699524q0.32510376 -0.26474 0.66360474 -0.36917114q0.5879059 -0.16333008
+1.0436859 0.042251587q0.47003174 0.21202087 0.6892395 0.44802856q0.21922302
+0.23602295 0.30801392 0.53318787q0.2074585 0.64208984 0.008300781
+1.0836182q-0.19917297 0.44154358 -0.42233276 0.63227844q-0.23098755 0.1700592
+-0.4996643 0.271698q-0.58651733 0.19824219 -1.085022 -0.026611328q0.008605957
+0.7409363 0.2311554 1.2355652q0.24320984 0.4868164 0.46018982
+0.84181213l0.51438904 0.79766846l5.5975037 2.5249023l1.1692963
+-2.5922241q0.10922241 -0.24214172 0.7644043 0.053390503q0.5269928 0.23771667
+0.6272278 0.47148132q0.085998535 0.22735596 0.028182983 0.35554504l-2.8011627
+6.209961q-0.13491821 0.29910278 -0.80433655 -0.0028533936q-0.4920807
+-0.23910522 -0.640625 -0.6318054q-0.032669067 -0.11756897 0.095825195
+-0.4024353q0.13491821 -0.29910278 0.24134827 -0.61105347q0.10643005 -0.31195068
+0.14079285 -0.5021515q0.035217285 -0.34407043 -0.22116089 -0.4597168l-7.0360565
+-3.1737976l-0.848053 1.8800812q-0.18632507 0.41305542 -1.0266571
+0.033996582q-0.5269928 -0.23771667 -0.5188751 -0.5597229q-0.0061035156
+-0.32844543 0.12184143 -0.8020935q0.12013245 -0.494339 0.28941345
+-0.9836273q0.31929016 -0.93585205 0.53634644 -1.4550323l0.0385437
+-0.08546448q0.19917297 -0.44152832 0.294693 -0.50128174q0.09411621 -0.094680786
+0.21308899 -0.092437744q0.13322449 0.008666992 0.4893036 0.16929626l0.12818909
+0.05781555zm4.841614 -8.833481q0.32843018 -0.0061187744 0.6845093
+0.15449524q0.3560791 0.16062927 0.5753021 0.39663696q0.19854736 0.24383545
+0.3144226 0.51893616q0.2124939 0.5929413 -0.05593872 1.226059q-0.44973755
+0.9970093 -1.4791718 0.96118164q-0.32202148 -0.008117676 -0.7208252
+-0.1880188q-0.38456726 -0.17346191 -0.6235962 -0.555542q-0.23904419 -0.38208008
+-0.32055664 -0.8473816q-0.0750885 -0.47953796 -0.005508423
+-1.0138092q0.055343628 -0.54067993 0.18190002 -1.0492554q0.22880554 -0.9252472
+0.67852783 -1.9222717q0.44973755 -0.9970093 1.1049805 -1.5756378q0.6474304
+-0.59928894 1.3814087 -0.7824402q1.2819366 -0.2959442 3.0195923
+0.48786926l5.526291 2.4927673l0.8095093 -1.7946167q0.13491821 -0.29910278
+0.76161194 -0.016418457q0.28486633 0.12849426 0.50408936 0.36450195q0.22564697
+0.22177124 0.17982483 0.47535706q-0.09892273 0.6753082 -0.6000519
+1.7862701q-0.5075531 1.1251984 -0.60809326 1.2341156q-0.08631897 0.11532593
+-0.23098755 0.1700592q-0.28152466 0.13012695 -0.9269562 0.07896423q0.36964417
+1.7265778 -0.45913696 3.563919q-0.7452698 1.6521912 -2.0523682
+2.1939087q-1.2101746 0.51686096 -2.5205383 -0.07421875l-0.02848816
+-0.0128479q-1.6379547 -0.73883057 -2.0034027 -2.360672q-0.33642578 -1.4201965
+0.37670898 -3.0011597l1.1885681 -2.634964q-1.794632 -0.8095093 -2.4422913
+-0.7416992q-0.6412506 0.05357361 -1.0269623 0.37667847q-0.38571167 0.32310486
+-0.69410706 1.0067596l-0.488266 1.0824738zm8.212418 3.0359192q0.60391235
+-1.3388519 0.11390686 -3.1025696l-2.079483 -0.93800354l-1.047226
+2.3216095q-0.38546753 0.85458374 -0.28692627 1.3961334q0.17866516 0.9719238
+0.8623352 1.280304q1.2533875 0.56536865 2.0563965 -0.34083557q0.22680664
+-0.2748108 0.3809967 -0.6166382z" fill-rule="nonzero"></path><path
+fill="#f3f3f3" d="m276.30206 161.02286l24.433777 11.021454l0 0c13.494385
+6.0869904 9.273712 44.63011 -9.427124 86.0885c-18.700836 41.458374 -44.800217
+70.13254 -58.294617 64.04556l-24.433746 -11.021454z"
+fill-rule="nonzero"></path><path stroke="#000000" stroke-width="2.0"
+stroke-linejoin="round" stroke-linecap="butt" d="m276.30206 161.02286l24.433777
+11.021454l0 0c13.494385 6.0869904 9.273712 44.63011 -9.427124
+86.0885c-18.700836 41.458374 -44.800217 70.13254 -58.294617 64.04556l-24.433746
+-11.021454z" fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0"
+d="m213.59521 297.66257l55.910248 -123.94888l49.75757 22.444397l-55.910248
+123.94888z" fill-rule="nonzero"></path><path fill="#000000" d="m247.30704
+257.94562q0.12207031 -0.27062988 0.8769531 0.069885254q0.7548828 0.34051514
+0.5878296 0.7108154l-0.8673248 1.922821l12.918442 5.827179l2.2614746
+-5.01355l-1.2147522 -1.5249939q-0.16226196 -0.21029663 -0.02734375
+-0.5093994q0.12850952 -0.2848816 0.24188232 -0.42227173q0.10559082 -0.15808105
+0.22039795 -0.26055908q0.26309204 -0.24127197 0.4482727 -0.15774536q0.0569458
+0.0256958 0.06478882 0.0463562l2.484314 2.3204956l-4.6514587
+10.311951q-0.1156311 0.25637817 -0.7423401 -0.026306152l-0.04272461
+-0.019256592q-0.47003174 -0.21203613 -0.6342163 -0.6460571q-0.046905518
+-0.12399292 0.0687561 -0.3803711l0.0128479 -0.028503418q0.0335083 -0.0362854
+0.0592041 -0.09326172q0.10922241 -0.24212646 0.19638062 -0.51135254q0.112854004
+-0.3262024 0.1472168 -0.51638794q0.035217285 -0.3440857 -0.22116089
+-0.4597168l-12.434158 -5.6087646q-0.1709137 -0.0770874 -0.23516846
+0.065338135l-0.7838135 1.7376709q-0.13491821 0.29910278 -0.77027893
+0.14962769q-0.14884949 -0.05001831 -0.27703857 -0.1078186q-0.03491211
+0.001373291 -0.06340027 -0.011474609q-0.09970093 -0.044952393 -0.1787262
+-0.0977478q-0.09327698 -0.05923462 -0.16589355 -0.12625122q-0.1750946
+-0.18182373 -0.1315155 -0.31643677l2.8525543 -6.3239136zm13.346802
+-1.3545227q-1.3246155 -0.59750366 -2.15094 -1.5016174q-0.80426025 -0.87701416
+-1.1602783 -1.9118042q-0.7055969 -2.0838013 0.25671387 -4.2551575l0.01928711
+-0.04273987q0.8930054 -1.9797821 2.634674 -2.496872q1.727417 -0.52352905
+4.0632935 0.53012085q0.49850464 0.22486877 0.6567993 0.8619232q0.15692139
+0.6021576 -0.15145874 1.2858124l-2.5377502 5.626007q2.5760498 0.9391632
+3.9884949 -0.292099q0.43206787 -0.38789368 0.6826477 -0.94337463q0.24414062
+-0.54122925 0.322937 -1.019928q0.06454468 -0.48510742 0.052856445
+-0.95318604q-0.005279541 -0.48233032 -0.07394409 -0.97610474q-0.1373291
+-0.9875641 -0.0730896 -1.1300049q0.07208252 -0.12176514 0.25668335
+-0.22703552q0.18600464 -0.07035828 0.4147644 -0.12145996q0.520874 -0.09072876
+0.8121338 0.023529053l0.014251709 0.00642395q0.25637817 0.11564636 0.2904358
+0.2681427q0.63739014 2.6529694 -0.48049927 5.131256q-1.008667 2.2361603
+-3.1407776 2.7828217q-2.0622864 0.5439148 -4.697235 -0.6446533zm2.2887878
+-7.6581116l-0.04272461 -0.01927185q-2.1079712 -0.95085144 -3.1589966
+0.049179077q-0.31591797 0.32032776 -0.5279236 0.79034424q-0.21200562 0.47001648
+-0.2723999 1.0598602q-0.06036377 0.58984375 0.08874512 1.1713257q0.35290527
+1.3076477 1.6453857 1.9763489l2.2679138 -5.0277863zm11.559509
+-8.374359q-0.12850952 0.28485107 -0.7836914 -0.010681152q-0.51275635
+-0.23129272 -0.6129761 -0.46505737q-0.09384155 -0.24801636 -0.04244995
+-0.36195374q0.50112915 -1.1109619 0.04534912 -1.3165436l-3.176178
+-1.4327087q-2.0652466 -0.9315796 -2.8153992 -0.9785614q-0.7579651 -0.06764221
+-1.1956482 0.18060303q-0.4376526 0.2482605 -0.7203369 0.8749542q-0.53323364
+1.1821747 0.05026245 3.5365906l6.1672363 2.7818756l0.8095093
+-1.7946167q0.12207031 -0.27061462 0.7772522 0.024917603q0.51275635 0.23129272
+0.6130066 0.46505737q0.100250244 0.23376465 0.042419434 0.36195374l-2.4542236
+5.4408417q-0.12850952 0.28486633 -0.7836914 -0.010665894q-0.51272583
+-0.23129272 -0.6129761 -0.46505737q-0.09384155 -0.24801636 -0.042419434
+-0.361969l0.0513916 -0.11393738q0.55252075 -1.2248993 0.3874817
+-1.5050354q-0.059753418 -0.09552002 -0.18795776 -0.15335083l-7.078766
+-3.1930542l-0.8480835 1.8800812q-0.1619873 0.321167 -0.71328735
+0.17532349q-0.16311646 -0.056427002 -0.31976318 -0.12709045q-0.48428345
+-0.21844482 -0.47616577 -0.54045105q0.014556885 -0.33625793 0.12826538
+-0.8163452q0.113708496 -0.48008728 0.2894287 -0.9836273q0.3164978 -1.0056763
+0.5349426 -1.4899445l0.019256592 -0.04272461q0.22625732 -0.46359253 0.37094116
+-0.5183258q0.14468384 -0.054718018 0.33486938 -0.020355225q0.21084595
+0.026550293 0.7948303 0.28996277l0.19940186 0.0899353q-0.17132568 -2.014206
+0.09857178 -3.4865875q0.07879639 -0.47868347 0.38076782 -1.1481018q0.28909302
+-0.64094543 0.86950684 -1.167633q0.5868225 -0.5409241 1.287262
+-0.68777466q1.2484436 -0.2596283 3.2994385 0.6655121l5.3126526
+2.396408l0.75167847 -1.6664276q0.12850952 -0.28486633 0.7836609
+0.010665894q0.51275635 0.23129272 0.6065979 0.47930908q0.10021973 0.23376465
+0.048828125 0.3477173l-2.190796 4.8568726zm2.721405 -9.871262l0.55252075
+-1.2248993q0.6681824 -1.4812775 0.002166748 -2.3987885q-0.21420288 -0.28515625
+-0.54177856 -0.43293762q-0.3276062 -0.14776611 -0.6404114
+-0.10031128q-0.31280518 0.047454834 -0.65997314 0.28511047q-0.5446167
+0.37139893 -1.4275208 1.3787079q-0.88290405 1.007309 -1.4155273
+1.5041199q-0.5404663 0.47613525 -1.1163635 0.7648773q-1.1803284 0.564621
+-2.519165 -0.03929138q-1.9797668 -0.8930359 -1.667633 -3.4090881q0.11627197
+-0.94174194 0.4824829 -1.7536011q0.36621094 -0.8118439 0.7885437
+-1.4441223q0.41448975 -0.65293884 0.92144775 -1.0927734q1.1557617 -1.0042114
+2.252472 -0.5095062l0.05697632 0.0256958q0.59820557 0.26983643 0.82492065
+0.86920166q0.2189331 0.5786896 -0.05593872 1.226059q-0.45617676 1.0112457
+-1.4855957 0.9754181q-0.32202148 -0.008117676 -0.49295044
+-0.08522034q-0.17089844 -0.0770874 -0.7540283 -0.49438477l-0.47543335
+1.0539703q-0.39187622 0.86883545 -0.27911377 1.4167938q0.18649292 0.9925995
+0.8132019 1.2752838q0.8260803 0.3726349 2.0134277 -0.89160156l0.651062
+-0.6833496q1.6088257 -1.7426147 2.3565063 -2.108139q0.73983765 -0.38619995
+1.4252014 -0.3855896q0.691803 -0.013626099 1.4181824 0.31402588q0.7406616
+0.3340912 1.1391602 0.85665894q0.41915894 0.51475525 0.5724182
+1.2009735q0.3392334 1.4900208 -0.4510193 3.2419128q-0.70669556 1.5667267
+-1.7356567 2.5938568q-0.75302124 0.75737 -1.5674438 0.8528137q-0.417511
+0.051635742 -0.7878418 -0.11540222q-0.3560791 -0.16061401 -0.57528687
+-0.39663696q-0.22705078 -0.25668335 -0.32867432 -0.5253601q-0.2267456
+-0.59936523 0.06237793 -1.2402954q0.27624512 -0.6124573 0.89627075
+-0.8470001q0.59155273 -0.247406 1.1043091 -0.016113281l0.014251709
+0.00642395q0.52056885 0.25195312 0.62945557 0.35250854z"
+fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0"
+d="m125.19179 263.8607l33.637787 -74.55118" fill-rule="nonzero"></path><path
+stroke="#434343" stroke-width="1.0" stroke-linejoin="round"
+stroke-linecap="butt" d="m125.19179 263.8607l32.228317 -71.42735"
+fill-rule="evenodd"></path><path fill="#434343" stroke="#434343"
+stroke-width="1.0" stroke-linecap="butt" d="m157.4201 192.43333l0.5625458
+1.4875793l0.24568176 -3.2788696l-2.2958221 2.353836z"
+fill-rule="evenodd"></path><path fill="#000000" fill-opacity="0.0"
+d="m125.190956 262.63293l74.56457 33.634216" fill-rule="nonzero"></path><path
+stroke="#434343" stroke-width="1.0" stroke-linejoin="round"
+stroke-linecap="butt" d="m125.190956 262.63293l71.440605 32.225098"
+fill-rule="evenodd"></path><path fill="#434343" stroke="#434343"
+stroke-width="1.0" stroke-linecap="butt" d="m196.63156 294.858l-1.4875183
+0.5627136l3.278885 0.24533081l-2.3540802 -2.2955627z"
+fill-rule="evenodd"></path><path fill="#000000" fill-opacity="0.0"
+d="m129.13316 262.30618l-61.102364 6.1102295" fill-rule="nonzero"></path><path
+stroke="#434343" stroke-width="1.0" stroke-linejoin="round"
+stroke-linecap="butt" d="m125.36197 262.6833l-53.92109 5.3921204"
+fill-rule="evenodd"></path><path fill="#434343" stroke="#434343"
+stroke-width="1.0" stroke-linecap="butt" d="m128.63564 262.35593c0.090408325
+0.9040222 -0.56915283 1.710144 -1.4731445 1.8005371c-0.9039993 0.09039307
+-1.7101212 -0.56915283 -1.8005219 -1.473175c-0.090400696 -0.9039917 0.56915283
+-1.7101135 1.4731522 -1.8005066c0.9039993 -0.09039307 1.7101212 0.56915283
+1.8005142 1.4731445z" fill-rule="nonzero"></path><path fill="#434343"
+stroke="#434343" stroke-width="1.0" stroke-linecap="butt" d="m71.44088
+268.0754l1.007103 -1.230896l-2.9625397 1.4264526l3.1863403 0.81155396z"
+fill-rule="evenodd"></path></g></svg>
+

diff -r 11079ba03a6dd7c5e0214efe0b06e14667d9366a -r 25a0dbd51257bb3fd355338fd4594985c5963f77 doc/source/visualizing/index.rst
--- a/doc/source/visualizing/index.rst
+++ b/doc/source/visualizing/index.rst
@@ -15,6 +15,8 @@
    callbacks
    manual_plotting
    volume_rendering
+   unstructured_mesh_rendering
+   hardware_volume_rendering
    sketchfab
    mapserver
    streamlines

diff -r 11079ba03a6dd7c5e0214efe0b06e14667d9366a -r 25a0dbd51257bb3fd355338fd4594985c5963f77 doc/source/visualizing/plots.rst
--- a/doc/source/visualizing/plots.rst
+++ b/doc/source/visualizing/plots.rst
@@ -311,7 +311,8 @@
 .. _off-axis-projection-function:
 
 To avoid manually creating a camera and setting the transfer
-function, yt provides the :func:`~yt.visualization.volume_rendering.camera.off_axis_projection`
+function, yt provides the
+:func:`~yt.visualization.volume_rendering.off_axis_projection.off_axis_projection`
 function, which wraps the camera interface to create an off axis
 projection image buffer.  These images can be saved to disk or
 used in custom plots.  This snippet creates an off axis
@@ -327,7 +328,7 @@
    W = [0.02, 0.02, 0.02]
    c = [0.5, 0.5, 0.5]
    N = 512
-   image = yt.off_axis_projection(ds, c, L, W, N, "density")
+   image, sc = yt.off_axis_projection(ds, c, L, W, N, "density")
    yt.write_image(np.log10(image), "%s_offaxis_projection.png" % ds)
 
 Here, ``W`` is the width of the projection in the x, y, *and* z

diff -r 11079ba03a6dd7c5e0214efe0b06e14667d9366a -r 25a0dbd51257bb3fd355338fd4594985c5963f77 doc/source/visualizing/transfer_function_helper.rst
--- /dev/null
+++ b/doc/source/visualizing/transfer_function_helper.rst
@@ -0,0 +1,6 @@
+.. _transfer-function-helper-tutorial:
+
+Transfer Function Helper Tutorial
+=================================
+
+.. notebook:: TransferFunctionHelper_Tutorial.ipynb

This diff is so big that we needed to truncate the remainder.

Repository URL: https://bitbucket.org/yt_analysis/yt/

--

This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.



More information about the yt-svn mailing list