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

commits-noreply at bitbucket.org commits-noreply at bitbucket.org
Wed Apr 6 19:53:55 PDT 2016


90 new commits in yt:

https://bitbucket.org/yt_analysis/yt/commits/5afe73926412/
Changeset:   5afe73926412
Branch:      yt
User:        ChuckRoz
Date:        2015-05-12 19:07:49+00:00
Summary:     Added get_perspective_matrix function and removed inconsistent whitespace while I was here
Affected #:  1 file

diff -r 7fe9f94e6991070c0888113d7995d543538836b7 -r 5afe73926412b623620525339d66fa1819d263e4 yt/utilities/math_utils.py
--- a/yt/utilities/math_utils.py
+++ b/yt/utilities/math_utils.py
@@ -63,7 +63,7 @@
 
     ds : Dataset
         A simulation static output.
-    
+
     Examples
     --------
     >>> a = np.array([1.1, 0.5, 0.5])
@@ -73,23 +73,23 @@
     >>> ppos
     array([ 0.1,  0.5,  0.5])
     """
- 
+
     off = (pos - ds.domain_left_edge) % ds.domain_width
     return ds.domain_left_edge + off
 
 def periodic_dist(a, b, period, periodicity=(True, True, True)):
     r"""Find the Euclidean periodic distance between two sets of points.
-    
+
     Parameters
     ----------
     a : array or list
         Either an ndim long list of coordinates corresponding to a single point
         or an (ndim, npoints) list of coordinates for many points in space.
-    
+
     b : array of list
         Either an ndim long list of coordinates corresponding to a single point
         or an (ndim, npoints) list of coordinates for many points in space.
-    
+
     period : float or array or list
         If the volume is symmetrically periodic, this can be a single float,
         otherwise an array or list of floats giving the periodic size of the
@@ -115,9 +115,9 @@
     if period.size == 1:
         period = np.array([period, period, period])
 
-    if a.shape != b.shape: 
+    if a.shape != b.shape:
         raise RuntimeError("Arrays must be the same shape.")
-    
+
     if period.shape != a.shape and len(a.shape) > 1:
         n_tup = tuple([1 for i in range(a.ndim-1)])
         period = np.tile(np.reshape(period, (a.shape[0],)+n_tup), (1,)+a.shape[1:])
@@ -128,7 +128,7 @@
 
     c = np.empty((2,) + a.shape, dtype="float64")
     c[0,:] = np.abs(a - b)
-    
+
     p_directions = [i for i,p in enumerate(periodicity) if p == True]
     np_directions = [i for i,p in enumerate(periodicity) if p == False]
     for d in p_directions:
@@ -181,25 +181,25 @@
 
 def rotate_vector_3D(a, dim, angle):
     r"""Rotates the elements of an array around an axis by some angle.
-    
+
     Given an array of 3D vectors a, this rotates them around a coordinate axis
     by a clockwise angle. An alternative way to think about it is the
     coordinate axes are rotated counterclockwise, which changes the directions
     of the vectors accordingly.
-    
+
     Parameters
     ----------
     a : array
         An array of 3D vectors with dimension Nx3.
-    
+
     dim : integer
         A integer giving the axis around which the vectors will be rotated.
         (x, y, z) = (0, 1, 2).
-    
+
     angle : float
         The angle in radians through which the vectors will be rotated
         clockwise.
-    
+
     Examples
     --------
     >>> a = np.array([[1, 1, 0], [1, 0, 1], [0, 1, 1], [1, 1, 1], [3, 4, 5]])
@@ -210,7 +210,7 @@
     [  1.00000000e+00   6.12323400e-17   1.00000000e+00]
     [  1.00000000e+00  -1.00000000e+00   1.00000000e+00]
     [  4.00000000e+00  -3.00000000e+00   5.00000000e+00]]
-    
+
     """
     mod = False
     if len(a.shape) == 1:
@@ -236,12 +236,11 @@
         return np.dot(R, a.T).T[0]
     else:
         return np.dot(R, a.T).T
-    
 
 def modify_reference_frame(CoM, L, P=None, V=None):
     r"""Rotates and translates data into a new reference frame to make
     calculations easier.
-    
+
     This is primarily useful for calculations of halo data.
     The data is translated into the center of mass frame.
     Next, it is rotated such that the angular momentum vector for the data
@@ -249,18 +248,18 @@
     momentum vector on the data that comes out of this function, it will
     always be along the positive z-axis.
     If the center of mass is re-calculated, it will be at the origin.
-    
+
     Parameters
     ----------
     CoM : array
         The center of mass in 3D.
-    
+
     L : array
         The angular momentum vector.
-    
+
     Optional
     --------
-        
+
     P : array
         The positions of the data to be modified (i.e. particle or grid cell
         postions). The array should be Nx3.
@@ -268,18 +267,18 @@
     V : array
         The velocities of the data to be modified (i.e. particle or grid cell
         velocities). The array should be Nx3.
-    
+
     Returns
     -------
     L : array
         The angular momentum vector equal to [0, 0, 1] modulo machine error.
-    
+
     P : array
         The modified positional data. Only returned if P is not None
-    
+
     V : array
         The modified velocity data. Only returned if V is not None
-    
+
     Examples
     --------
     >>> CoM = np.array([0.5, 0.5, 0.5])
@@ -342,36 +341,36 @@
 
 def compute_rotational_velocity(CoM, L, P, V):
     r"""Computes the rotational velocity for some data around an axis.
-    
+
     This is primarily for halo computations.
     Given some data, this computes the circular rotational velocity of each
     point (particle) in reference to the axis defined by the angular momentum
     vector.
     This is accomplished by converting the reference frame of the center of
     mass of the halo.
-    
+
     Parameters
     ----------
     CoM : array
         The center of mass in 3D.
-    
+
     L : array
         The angular momentum vector.
-    
+
     P : array
         The positions of the data to be modified (i.e. particle or grid cell
         postions). The array should be Nx3.
-    
+
     V : array
         The velocities of the data to be modified (i.e. particle or grid cell
         velocities). The array should be Nx3.
-    
+
     Returns
     -------
     v : array
         An array N elements long that gives the circular rotational velocity
         for each datum (particle).
-    
+
     Examples
     --------
     >>> CoM = np.array([0, 0, 0])
@@ -395,38 +394,38 @@
         temp = np.dot(rp, V[i]) / np.dot(rp, rp) * rp
         res[i] = np.dot(temp, temp)**0.5
     return res
-    
+
 def compute_parallel_velocity(CoM, L, P, V):
     r"""Computes the parallel velocity for some data around an axis.
-    
+
     This is primarily for halo computations.
     Given some data, this computes the velocity component along the angular
     momentum vector.
     This is accomplished by converting the reference frame of the center of
     mass of the halo.
-    
+
     Parameters
     ----------
     CoM : array
         The center of mass in 3D.
-    
+
     L : array
         The angular momentum vector.
-    
+
     P : array
         The positions of the data to be modified (i.e. particle or grid cell
         postions). The array should be Nx3.
-    
+
     V : array
         The velocities of the data to be modified (i.e. particle or grid cell
         velocities). The array should be Nx3.
-    
+
     Returns
     -------
     v : array
         An array N elements long that gives the parallel velocity for
         each datum (particle).
-    
+
     Examples
     --------
     >>> CoM = np.array([0, 0, 0])
@@ -436,7 +435,7 @@
     >>> paraV = compute_parallel_velocity(CoM, L, P, V)
     >>> paraV
     array([10, -1,  1, -1])
-    
+
     """
     # First we translate into the simple coordinates.
     L, P, V = modify_reference_frame(CoM, L, P, V)
@@ -445,34 +444,34 @@
 
 def compute_radial_velocity(CoM, L, P, V):
     r"""Computes the radial velocity for some data around an axis.
-    
+
     This is primarily for halo computations.
     Given some data, this computes the radial velocity component for the data.
     This is accomplished by converting the reference frame of the center of
     mass of the halo.
-    
+
     Parameters
     ----------
     CoM : array
         The center of mass in 3D.
-    
+
     L : array
         The angular momentum vector.
-    
+
     P : array
         The positions of the data to be modified (i.e. particle or grid cell
         postions). The array should be Nx3.
-    
+
     V : array
         The velocities of the data to be modified (i.e. particle or grid cell
         velocities). The array should be Nx3.
-    
+
     Returns
     -------
     v : array
         An array N elements long that gives the radial velocity for
         each datum (particle).
-    
+
     Examples
     --------
     >>> CoM = np.array([0, 0, 0])
@@ -482,7 +481,7 @@
     >>> radV = compute_radial_velocity(CoM, L, P, V)
     >>> radV
     array([ 1.        ,  1.41421356 ,  0.        ,  0.])
-    
+
     """
     # First we translate into the simple coordinates.
     L, P, V = modify_reference_frame(CoM, L, P, V)
@@ -499,34 +498,34 @@
 def compute_cylindrical_radius(CoM, L, P, V):
     r"""Compute the radius for some data around an axis in cylindrical
     coordinates.
-    
+
     This is primarily for halo computations.
     Given some data, this computes the cylindrical radius for each point.
     This is accomplished by converting the reference frame of the center of
     mass of the halo.
-    
+
     Parameters
     ----------
     CoM : array
         The center of mass in 3D.
-    
+
     L : array
         The angular momentum vector.
-    
+
     P : array
         The positions of the data to be modified (i.e. particle or grid cell
         postions). The array should be Nx3.
-    
+
     V : array
         The velocities of the data to be modified (i.e. particle or grid cell
         velocities). The array should be Nx3.
-    
+
     Returns
     -------
     cyl_r : array
         An array N elements long that gives the radial velocity for
         each datum (particle).
-    
+
     Examples
     --------
     >>> CoM = np.array([0, 0, 0])
@@ -536,6 +535,7 @@
     >>> cyl_r = compute_cylindrical_radius(CoM, L, P, V)
     >>> cyl_r
     array([ 1.        ,  1.41421356,  0.        ,  1.41421356])
+
     """
     # First we translate into the simple coordinates.
     L, P, V = modify_reference_frame(CoM, L, P, V)
@@ -543,19 +543,19 @@
     # calculation very easy.
     P[:,2] = 0
     return np.sqrt((P * P).sum(axis=1))
-    
+
 def ortho_find(vec1):
     r"""Find two complementary orthonormal vectors to a given vector.
 
-    For any given non-zero vector, there are infinite pairs of vectors 
-    orthonormal to it.  This function gives you one arbitrary pair from 
+    For any given non-zero vector, there are infinite pairs of vectors
+    orthonormal to it.  This function gives you one arbitrary pair from
     that set along with the normalized version of the original vector.
 
     Parameters
     ----------
     vec1 : array_like
            An array or list to represent a 3-vector.
-        
+
     Returns
     -------
     vec1 : array
@@ -576,30 +576,30 @@
     -----
     Our initial vector is `vec1` which consists of 3 components: `x1`, `y1`,
     and `z1`.  ortho_find determines a vector, `vec2`, which is orthonormal
-    to `vec1` by finding a vector which has a zero-value dot-product with 
+    to `vec1` by finding a vector which has a zero-value dot-product with
     `vec1`.
 
-    .. math:: 
+    .. math::
 
        vec1 \cdot vec2 = x_1 x_2 + y_1 y_2 + z_1 z_2 = 0
 
-    As a starting point, we arbitrarily choose `vec2` to have `x2` = 1, 
+    As a starting point, we arbitrarily choose `vec2` to have `x2` = 1,
     `y2` = 0:
 
-    .. math:: 
+    .. math::
 
-       vec1 \cdot vec2 = x_1 + (z_1 z_2) = 0 
+       vec1 \cdot vec2 = x_1 + (z_1 z_2) = 0
 
        \rightarrow z_2 = -(x_1 / z_1)
 
-    Of course, this will fail if `z1` = 0, in which case, let's say use 
+    Of course, this will fail if `z1` = 0, in which case, let's say use
     `z2` = 1 and `x2` = 0:
 
     .. math::
-    
+
        \rightarrow y_2 = -(z_1 / y_1)
 
-    Similarly, if `y1` = 0, this case will fail, in which case we use 
+    Similarly, if `y1` = 0, this case will fail, in which case we use
     `y2` = 1 and `z2` = 0:
 
     .. math::
@@ -658,7 +658,7 @@
     alone a specified axis.  Check numpy.median for details, as it is
     virtually the same algorithm.
 
-    Returns an array of the quartiles of the array elements [lower quartile, 
+    Returns an array of the quartiles of the array elements [lower quartile,
     upper quartile].
 
     Parameters
@@ -696,9 +696,9 @@
 
     Notes
     -----
-    Given a vector V of length N, the quartiles of V are the 25% and 75% values 
-    of a sorted copy of V, ``V_sorted`` - i.e., ``V_sorted[(N-1)/4]`` and 
-    ``3*V_sorted[(N-1)/4]``, when N is odd.  When N is even, it is the average 
+    Given a vector V of length N, the quartiles of V are the 25% and 75% values
+    of a sorted copy of V, ``V_sorted`` - i.e., ``V_sorted[(N-1)/4]`` and
+    ``3*V_sorted[(N-1)/4]``, when N is odd.  When N is even, it is the average
     of the two values bounding these values of ``V_sorted``.
 
     Examples
@@ -755,6 +755,62 @@
         result.append(np.mean(sorted[indexer], axis=axis, out=out))
     return np.array(result)
 
+def get_perspective_matrix(fovy, aspect, z_near, z_far):
+    """
+    Given a field of view in radians, an aspect ratio, and a near
+    and far plane distance, this routine computes the rotation matrix
+    corresponding to a projection onto the corresponding perspective
+    frustum using homogenous coordinates.
+
+    Parameters
+    ----------
+    fovy : scaler
+        The angle in radians of the field of view.
+
+    aspect : scaler
+        The aspect ratio of width / height for the projection.
+
+    z_near : scaler
+        The distance of the near plane from the camera.
+
+    z_far : scaler
+        The distance of the far plane from the camera.
+
+    Returns
+    -------
+    persp_matrix : ndarray
+        A new 4x4 3D array. Represents a perspective transformation
+        in homogeneous coordinates. Note that this matrix does not
+        actually perform the projection. After multiplying a 4D
+        vector of the form (x_0, y_0, z_0, 1.0), the point will be
+        transformed to some (x_1, y_1, z_1, w). The final projection
+        is applied by performing a divide by w, that is
+        (x_1/w, y_1/w, z_1/w, w/w). The matrix uses a row-major
+        ordering, rather than the column major ordering typically
+        used by OpenGL.
+
+    Notes
+    -----
+    The usage of 4D homogeneous coordinates is for OpenGL and GPU
+    hardware that automatically performs the divide by w operation.
+    See the following for more details about the OpenGL perpective matrices.
+
+    http://www.tomdalling.com/blog/modern-opengl/explaining-homogenous-coordinates-and-projective-geometry/
+    http://www.songho.ca/opengl/gl_projectionmatrix.html
+
+    """
+
+    tan_half_fovy = np.tan(fovy / 2)
+
+    result = np.zeros( (4, 4), dtype = 'float32', order = 'C')
+    result[0][0] = 1 / (aspect * tan_half_fovy)
+    result[1][1] = 1 / tan_half_fovy
+    result[2][2] = - (z_far + z_near) / (z_far - z_near)
+    result[3][2] = -1
+    result[2][3] = -(2 * z_far * z_near) / (z_far - z_near)
+
+    return result
+
 def get_rotation_matrix(theta, rot_vector):
     """
     Given an angle theta and a 3D vector rot_vector, this routine
@@ -769,11 +825,11 @@
     rot_vector : array_like
         The axis of rotation.  Must be 3D.
 
-    Returns 
-    ------- 
-    rot_matrix : ndarray 
-         A new 3x3 2D array.  This is the representation of a 
-         rotation of theta radians about rot_vector in the simulation 
+    Returns
+    -------
+    rot_matrix : ndarray
+         A new 3x3 2D array.  This is the representation of a
+         rotation of theta radians about rot_vector in the simulation
          box coordinate frame
 
     See Also
@@ -801,11 +857,11 @@
     uz = rot_vector[2]
     cost = np.cos(theta)
     sint = np.sin(theta)
-    
+
     R = np.array([[cost+ux**2*(1-cost), ux*uy*(1-cost)-uz*sint, ux*uz*(1-cost)+uy*sint],
                   [uy*ux*(1-cost)+uz*sint, cost+uy**2*(1-cost), uy*uz*(1-cost)-ux*sint],
                   [uz*ux*(1-cost)-uy*sint, uz*uy*(1-cost)+ux*sint, cost+uz**2*(1-cost)]])
-    
+
     return R
 
 def get_ortho_basis(normal):
@@ -832,15 +888,15 @@
     # The angle (theta) with respect to the normal (J), is the arccos
     # of the dot product of the normal with the normalized coordinate
     # vector.
-    
+
     res_normal = resize_vector(normal, coords)
 
     tile_shape = [1] + list(coords.shape)[1:]
-    
+
     J = np.tile(res_normal,tile_shape)
 
     JdotCoords = np.sum(J*coords,axis=0)
-    
+
     return np.arccos( JdotCoords / np.sqrt(np.sum(coords**2,axis=0)) )
 
 def get_sph_phi(coords, normal):
@@ -851,7 +907,7 @@
     # normal == z-hat (as is typical), then xprime == x-hat.
     #
     # The angle is then given by the arctan of the ratio of the
-    # yprime-component and the xprime-component of the coordinate 
+    # yprime-component and the xprime-component of the coordinate
     # vector.
 
     (xprime, yprime, zprime) = get_ortho_basis(normal)
@@ -865,7 +921,7 @@
 
     Px = np.sum(Jx*coords,axis=0)
     Py = np.sum(Jy*coords,axis=0)
-    
+
     return np.arctan2(Py,Px)
 
 def get_cyl_r(coords, normal):
@@ -876,20 +932,20 @@
 
     tile_shape = [1] + list(coords.shape)[1:]
     J = np.tile(res_normal, tile_shape)
-    
+
     JcrossCoords = np.cross(J, coords, axisa=0, axisb=0, axisc=0)
     return np.sqrt(np.sum(JcrossCoords**2, axis=0))
 
 def get_cyl_z(coords, normal):
-    # The dot product of the normal (J) with the coordinate vector 
+    # The dot product of the normal (J) with the coordinate vector
     # gives the cylindrical height.
 
     res_normal = resize_vector(normal, coords)
-    
+
     tile_shape = [1] + list(coords.shape)[1:]
     J = np.tile(res_normal, tile_shape)
 
-    return np.sum(J*coords, axis=0)  
+    return np.sum(J*coords, axis=0)
 
 def get_cyl_theta(coords, normal):
     # This is identical to the spherical phi component
@@ -915,7 +971,7 @@
 
 def get_cyl_theta_component(vectors, theta, normal):
     # The theta component of a vector is the vector dotted with thetahat
-    
+
     (xprime, yprime, zprime) = get_ortho_basis(normal)
 
     res_xprime = resize_vector(xprime, vectors)
@@ -942,7 +998,7 @@
 
 def get_sph_r_component(vectors, theta, phi, normal):
     # The r component of a vector is the vector dotted with rhat
-    
+
     (xprime, yprime, zprime) = get_ortho_basis(normal)
 
     res_xprime = resize_vector(xprime, vectors)
@@ -979,7 +1035,7 @@
 
 def get_sph_theta_component(vectors, theta, phi, normal):
     # The theta component of a vector is the vector dotted with thetahat
-    
+
     (xprime, yprime, zprime) = get_ortho_basis(normal)
 
     res_xprime = resize_vector(xprime, vectors)
@@ -991,7 +1047,7 @@
         YTArray(np.tile(rprime, tile_shape), "")
         for rprime in (res_xprime, res_yprime, res_zprime))
 
-    
+
     thetahat = Jx*np.cos(theta)*np.cos(phi) + \
                Jy*np.cos(theta)*np.sin(phi) - \
                Jz*np.sin(theta)


https://bitbucket.org/yt_analysis/yt/commits/b3ff96ff06c8/
Changeset:   b3ff96ff06c8
Branch:      yt
User:        ChuckRoz
Date:        2015-05-12 19:32:09+00:00
Summary:     Added get_lookat_matrix function to math_utils
Affected #:  1 file

diff -r 5afe73926412b623620525339d66fa1819d263e4 -r b3ff96ff06c8c64815b15d6132d7037fc1fbbd4f yt/utilities/math_utils.py
--- a/yt/utilities/math_utils.py
+++ b/yt/utilities/math_utils.py
@@ -811,6 +811,59 @@
 
     return result
 
+def get_lookat_matrix(eye, center, up):
+    """
+    Given the position of a camera, the point it is looking at, and
+    an up-direction. Computes the lookat matrix that moves all vectors
+    such that the camera is at the origin of the coordinate system,
+    looking down the z-axis.
+
+    Parameters
+    ----------
+    eye : array_like
+        The position of the camera. Must be 3D.
+
+    center : array_like
+        The location that the camera is looking at. Must be 3D.
+
+    up : array_like
+        The direction that is considered up for the camera. Must be
+        3D.
+
+    Returns
+    -------
+        A new 4x4 `3D` array in homogeneous coordinates. This matrix
+        moves all vectors in the same way required to move the camera
+        to the origin of the coordinate system, with it pointing down
+        the negative z-axis.
+
+    """
+
+    eye = np.array(eye)
+    center = np.array(center)
+    up = np.array(up)
+
+    f = normalize(center - eye)
+    s = normalize(np.cross(f, up))
+    u = np.cross(s, f)
+
+    result = np.zeros ( (4, 4), dtype = 'float32', order = 'C')
+
+    result[0][0] = s[0]
+    result[0][1] = s[1]
+    result[0][2] = s[2]
+    result[1][0] = u[0]
+    result[1][1] = u[1]
+    result[1][2] = u[2]
+    result[2][0] =-f[0]
+    result[2][1] =-f[1]
+    result[2][2] =-f[2]
+    result[0][3] =-np.dot(s, eye)
+    result[1][3] =-np.dot(u, eye)
+    result[2][3] = np.dot(f, eye)
+    result[3][3] = 1.0
+    return result
+
 def get_rotation_matrix(theta, rot_vector):
     """
     Given an angle theta and a 3D vector rot_vector, this routine


https://bitbucket.org/yt_analysis/yt/commits/1138e3e2b415/
Changeset:   1138e3e2b415
Branch:      yt
User:        ChuckRoz
Date:        2015-05-12 19:45:36+00:00
Summary:     Added scale and translate function to math_utils
Affected #:  1 file

diff -r b3ff96ff06c8c64815b15d6132d7037fc1fbbd4f -r 1138e3e2b4154e0ef02bdd7d66f4dedc1363292a yt/utilities/math_utils.py
--- a/yt/utilities/math_utils.py
+++ b/yt/utilities/math_utils.py
@@ -832,6 +832,7 @@
 
     Returns
     -------
+    lookat_matrix : ndarray
         A new 4x4 `3D` array in homogeneous coordinates. This matrix
         moves all vectors in the same way required to move the camera
         to the origin of the coordinate system, with it pointing down
@@ -843,8 +844,8 @@
     center = np.array(center)
     up = np.array(up)
 
-    f = normalize(center - eye)
-    s = normalize(np.cross(f, up))
+    f = (center - eye) / np.linalg.norm(center - eye)
+    s = np.cross(f, up) / np.linalg.norm(np.cross(f, up))
     u = np.cross(s, f)
 
     result = np.zeros ( (4, 4), dtype = 'float32', order = 'C')
@@ -864,6 +865,73 @@
     result[3][3] = 1.0
     return result
 
+
+def get_translate_matrix(dx, dy, dz):
+    """
+    Given a movement amount for each coordinate, creates a translation
+    matrix that moves the vector by each amount.
+
+    Parameters
+    ----------
+    dx : scaler
+        A translation amount for the x-coordinate
+
+    dy : scaler
+        A translation amount for the y-coordinate
+
+    dz : scaler
+        A translation amount for the z-coordinate
+
+    Returns
+    -------
+    trans_matrix : ndarray
+        A new 4x4 `3D` array. Represents a translation by dx, dy
+        and dz in each coordinate respectively.
+    """
+    result = np.zeros( (4, 4), dtype = 'float32', order = 'C')
+
+    result[0][0] = 1.0
+    result[1][1] = 1.0
+    result[2][2] = 1.0
+    result[3][3] = 1.0
+
+    result[0][3] = dx
+    result[1][3] = dy
+    result[2][3] = dz
+
+    return result
+
+def get_scale_matrix(dx, dy, dz):
+    """
+    Given a scaling factor for each coordinate, returns a matrix that
+    corresponds to the given scaling amounts.
+
+    Parameters
+    ----------
+    dx : scaler
+        A scaling factor for the x-coordinate.
+
+    dy : scaler
+        A scaling factor for the y-coordinate.
+
+    dz : scaler
+        A scaling factor for the z-coordinate.
+
+    Returns
+    -------
+    scale_matrix : ndarray
+        A new 4x4 `3D` array. Represents a scaling by dx, dy, and dz
+        in each coordinate respectively.
+    """
+    result = np.zeros( (4, 4), dtype = 'float32', order = 'C')
+
+    result[0][0] = dx
+    result[1][1] = dy
+    result[2][2] = dz
+    result[3][3] = 1
+
+    return result
+
 def get_rotation_matrix(theta, rot_vector):
     """
     Given an angle theta and a 3D vector rot_vector, this routine


https://bitbucket.org/yt_analysis/yt/commits/be600de97283/
Changeset:   be600de97283
Branch:      yt
User:        ChuckRoz
Date:        2015-05-12 19:56:48+00:00
Summary:     Added OpenGL volume rendering files, interactive_vr.py and interactive_loop.py
Affected #:  4 files

diff -r 1138e3e2b4154e0ef02bdd7d66f4dedc1363292a -r be600de97283a67f1076dac84c8e4029b83edf0f yt/visualization/volume_rendering/interactive_loop.py
--- /dev/null
+++ b/yt/visualization/volume_rendering/interactive_loop.py
@@ -0,0 +1,114 @@
+import cyglfw3 as glfw
+import yt
+import numpy as np
+import sys
+import time
+
+from interactive_vr import BlockCollection, SceneGraph, Camera
+
+draw = True
+start = None
+rotation = False
+window = None
+
+c = None
+
+def mouse_button_callback(window, button, action, mods):
+    global draw, start, rotation, c
+
+    if (button == glfw.MOUSE_BUTTON_LEFT and action == glfw.PRESS):
+        start_screen = glfw.GetCursorPos(window) # Screen coordinates
+        window_size = glfw.GetWindowSize(window)
+
+        norm_x = -1.0 + 2.0 * start_screen[0] / window_size[0]
+        norm_y = 1.0 - 2.0 * start_screen[1] / window_size[1]
+        start = (norm_x, norm_y)
+        rotation = True
+
+    if (button == glfw.MOUSE_BUTTON_LEFT and action == glfw.RELEASE):
+        end_screen = glfw.GetCursorPos(window)
+        window_size = glfw.GetWindowSize(window)
+
+        norm_x = -1.0 + 2.0 * end_screen[0] / window_size[0]
+        norm_y = 1.0 - 2.0 * end_screen[1] / window_size[1]
+        end = (norm_x, norm_y)
+
+        c.update_position( (end[0] - start[0]), (end[1] - start[1]))
+
+        rotation = False
+        draw = True
+
+def framebuffer_size_callback(window, width, height):
+    global draw
+    glViewport(0, 0, width, height)
+    draw = True
+
+def key_callback(window, key, scancode, action, mods):
+    global draw, c
+    if key == glfw.KEY_ESCAPE and action == glfw.PRESS:
+        glfw.SetWindowShouldClose(window, True)
+    if key == glfw.KEY_W and action == glfw.PRESS:
+        c.position -= (c.position - c.focus) / np.linalg.norm(c.position - c.focus)
+        draw = True
+    if key == glfw.KEY_S and action == glfw.PRESS:
+        c.position += (c.position - c.focus) / np.linalg.norm(c.position - c.focus)
+        draw = True
+
+def start_context():
+    global window
+    glfw.Init()
+
+    glfw.WindowHint(glfw.CONTEXT_VERSION_MAJOR, 3)
+    glfw.WindowHint(glfw.CONTEXT_VERSION_MINOR, 3)
+    glfw.WindowHint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)
+    window = glfw.CreateWindow(800, 600, 'vol_render')
+    if not window:
+        glfw.Terminate()
+        exit()
+
+    glfw.MakeContextCurrent(window)
+    glfw.SetKeyCallback(window, key_callback)
+    glfw.SetFramebufferSizeCallback(window, framebuffer_size_callback)
+    glfw.SetMouseButtonCallback(window, mouse_button_callback)
+
+def start_loop(scene, camera):
+    global draw, c, start
+    c = camera
+    scene.set_camera(camera)
+    scene.add_shader_from_file("max_intensity_frag.glsl")
+    frame_start = glfw.GetTime()
+    fps_start = glfw.GetTime()
+    f = 0
+    N = 10.0
+    print "Starting rendering..."
+
+    while not glfw.WindowShouldClose(window):
+        if rotation:
+            new_end_screen = glfw.GetCursorPos(window)
+            window_size = glfw.GetWindowSize(window)
+
+            norm_x = -1.0 + 2.0 * new_end_screen[0] / window_size[0]
+            norm_y = 1.0 - 2.0 * new_end_screen[1] / window_size[1]
+            new_end = (norm_x, norm_y)
+
+            c.update_position( (new_end[0] - start[0]),
+                    (new_end[1] - start[1]))
+            start = new_end
+            draw = True
+
+        if draw:
+            scene.set_camera(c)
+            scene.render()
+            draw = False
+        glfw.SwapBuffers(window)
+        glfw.PollEvents()
+
+        frame_start = glfw.GetTime()
+        f += 1
+        if f == N:
+            print "FPS:", N / float(frame_start - fps_start)
+            fps_start = glfw.GetTime()
+            f = 0
+
+    print "Finished rendering"
+    glfw.Terminate()

diff -r 1138e3e2b4154e0ef02bdd7d66f4dedc1363292a -r be600de97283a67f1076dac84c8e4029b83edf0f yt/visualization/volume_rendering/interactive_vr.py
--- /dev/null
+++ b/yt/visualization/volume_rendering/interactive_vr.py
@@ -0,0 +1,342 @@
+from OpenGL.GL import *
+from OpenGL.GL import shaders
+from OpenGL.GLUT import *
+
+import sys
+
+import numpy as np
+from yt.funcs import ensure_list
+from yt.utilities.math_utils import get_translate_matrix, get_scale_matrix,
+    get_lookat_matrix, get_perspective_matrix
+import yt
+
+bbox_vertices = np.array(
+      [[ 0.,  0.,  0.,  1.],
+       [ 1.,  1.,  0.,  1.],
+       [ 1.,  0.,  0.,  1.],
+       [ 0.,  0.,  0.,  1.],
+       [ 0.,  1.,  0.,  1.],
+       [ 1.,  1.,  0.,  1.],
+       [ 0.,  0.,  0.,  1.],
+       [ 0.,  0.,  1.,  1.],
+       [ 0.,  1.,  0.,  1.],
+       [ 0.,  1.,  0.,  1.],
+       [ 0.,  0.,  1.,  1.],
+       [ 0.,  1.,  1.,  1.],
+       [ 0.,  0.,  0.,  1.],
+       [ 1.,  0.,  0.,  1.],
+       [ 1.,  0.,  1.,  1.],
+       [ 0.,  0.,  0.,  1.],
+       [ 1.,  0.,  1.,  1.],
+       [ 0.,  0.,  1.,  1.],
+       [ 0.,  1.,  0.,  1.],
+       [ 1.,  1.,  1.,  1.],
+       [ 1.,  1.,  0.,  1.],
+       [ 0.,  1.,  0.,  1.],
+       [ 0.,  1.,  1.,  1.],
+       [ 1.,  1.,  1.,  1.],
+       [ 1.,  0.,  0.,  1.],
+       [ 1.,  1.,  0.,  1.],
+       [ 1.,  1.,  1.,  1.],
+       [ 1.,  0.,  0.,  1.],
+       [ 1.,  1.,  1.,  1.],
+       [ 1.,  0.,  1.,  1.],
+       [ 0.,  0.,  1.,  1.],
+       [ 1.,  0.,  1.,  1.],
+       [ 1.,  1.,  1.,  1.],
+       [ 0.,  0.,  1.,  1.],
+       [ 1.,  1.,  1.,  1.],
+       [ 0.,  1.,  1.,  1.]])
+
+class Camera:
+    def __init__(self, position = (0, 0, 0), fov = 60.0, near_plane = 1.0,
+            far_plane = 20, aspect_ratio = 8.0 / 6.0, focus = (0, 0, 0),
+            up = (0, 0, 1)):
+        self.position = np.array(position)
+        self.fov = fov
+        self.near_plane = near_plane
+        self.far_plane = far_plane
+        self.aspect_ratio = aspect_ratio
+        self.up = np.array(up)
+        self.focus = np.array(focus)
+
+    def get_viewpoint(self):
+        return position
+    def get_view_matrix(self):
+        return get_lookat_matrix(self.position, self.focus, self.up)
+    def get_projection_matrix(self):
+        return get_perspective_matrix(np.radians(self.fov), self.aspect_ratio, self.near_plane,
+                self.far_plane)
+    def update_position(self, theta, phi):
+        rho = np.linalg.norm(self.position)
+        curr_theta = np.arctan( self.position[1] / self.position[0] )
+        curr_phi = np.arctan( np.linalg.norm(self.position[:2]) / self.position[2])
+
+        curr_theta += theta
+        curr_phi += phi
+
+        self.position[0] = rho * np.sin(curr_phi) * np.cos(curr_theta)
+        self.position[1] = rho * np.sin(curr_phi) * np.sin(curr_theta)
+        self.position[2] = rho * np.cos(curr_phi)
+
+class BlockCollection:
+    def __init__(self):
+        self.data_source = None
+
+        self.blocks = [] # A collection of PartionedGrid objects
+
+        self.gl_buffer_name = None
+        self.gl_texture_names = []
+        self.gl_vao_name = None
+
+        self.redraw = True
+
+        self.geometry_loaded = False
+
+        self.camera = None
+        glEnable(GL_CULL_FACE)
+        glEnable(GL_DEPTH_TEST)
+        glDepthFunc(GL_LESS)
+
+        self._init_blending()
+
+    def _init_blending(self):
+        glEnable(GL_BLEND)
+        glBlendColor(1.0, 1.0, 1.0, 1.0)
+        glBlendFunc(GL_ONE, GL_ONE)
+        glBlendEquation(GL_MAX)
+
+    def add_data(self, data_source, field):
+        r"""Adds a source of data for the block collection.
+
+        Given a `data_source` and a `field` to populate from, adds the data
+        to the block collection so that is able to be rendered.
+
+        Parameters
+        ----------
+        data_source : YTRegion
+            A YTRegion object to use as a data source.
+        field - String
+            A field to populate from.
+
+        """
+        self.data_source = data_source
+        self.data_source.tiles.set_fields([field], [True], True)
+
+    def set_camera(self, camera):
+        r"""Sets the camera for the block collection.
+
+        Parameters
+        ----------
+        camera : Camera
+            A simple camera object.
+
+        """
+        self.blocks = []
+        vert = []
+        self.min_val = 1e60
+        self.max_val = -1e60
+        for block in self.data_source.tiles.traverse(viewpoint = camera.position):
+            self.min_val = min(self.min_val, block.my_data[0].min())
+            self.max_val = max(self.max_val, block.my_data[0].max())
+            self.blocks.append(block)
+            vert.append(self._compute_geometry(block, bbox_vertices))
+        self.camera = camera
+
+        # Now we set up our buffer
+        vert = np.concatenate(vert)
+        if self.gl_buffer_name != None:
+            glDeleteBuffers(1, [self.gl_buffer_name])
+        self.gl_buffer_name = glGenBuffers(1)
+        if self.gl_vao_name != None:
+            glDeleteVertexArrays(1, [self.gl_vao_name])
+        self.gl_vao_name = glGenVertexArrays(1)
+        glBindBuffer(GL_ARRAY_BUFFER, self.gl_buffer_name)
+        glBufferData(GL_ARRAY_BUFFER, vert.nbytes, vert, GL_STATIC_DRAW)
+        redraw = True
+
+    def run_program(self, shader_program):
+        r"""Runs a given shader program on the block collection.
+
+        Parameters
+        ----------
+        shader_program : int
+            An integer name for an OpenGL shader program.
+
+        """
+        glUseProgram(shader_program)
+
+        glBindVertexArray(self.gl_vao_name)
+        glBindBuffer(GL_ARRAY_BUFFER, self.gl_buffer_name)
+
+        vert_location = glGetAttribLocation(shader_program, "model_vertex")
+
+        glVertexAttribPointer(vert_location, 4, GL_FLOAT, False, 0, None)
+        glEnableVertexAttribArray(vert_location)
+        glClear(GL_COLOR_BUFFER_BIT)
+        glClear(GL_DEPTH_BUFFER_BIT)
+
+        self._set_uniforms(shader_program)
+        glActiveTexture(GL_TEXTURE0)
+        self._load_textures()
+        dx_loc = glGetUniformLocation(shader_program, "dx")
+        left_edge_loc = glGetUniformLocation(shader_program, "left_edge")
+        right_edge_loc = glGetUniformLocation(shader_program, "right_edge")
+        camera_loc = glGetUniformLocation(shader_program, "camera_pos")
+        glUniform3fv(camera_loc, 1, self.camera.position)
+
+        glBindBuffer(GL_ARRAY_BUFFER, self.gl_buffer_name)
+        glActiveTexture(GL_TEXTURE0)
+        for i in range(0, len(self.blocks)):
+            self._set_bounds(self.blocks[i], shader_program,
+                dx_loc, left_edge_loc, right_edge_loc)
+            glBindTexture(GL_TEXTURE_3D, self.gl_texture_names[i])
+            glDrawArrays(GL_TRIANGLES, i*36, 36)
+
+        glDisableVertexAttribArray(vert_location)
+        glBindVertexArray(0)
+        glBindBuffer(GL_ARRAY_BUFFER, 0)
+
+    def _set_bounds(self, block, shader_program, dx, le, re):
+        dds = (block.RightEdge - block.LeftEdge)/block.my_data[0].shape
+        glUniform3fv(dx, 1, dds)
+        glUniform3fv(le, 1, block.LeftEdge)
+        glUniform3fv(re, 1, block.RightEdge)
+
+    def _set_uniforms(self, shader_program):
+        project_loc = glGetUniformLocation(shader_program, "projection")
+        lookat_loc = glGetUniformLocation(shader_program, "lookat")
+        viewport_loc = glGetUniformLocation(shader_program, "viewport")
+
+        project = self.camera.get_projection_matrix()
+        view = self.camera.get_view_matrix()
+
+        viewport = np.array(glGetIntegerv(GL_VIEWPORT), dtype = 'float32')
+
+        glUniformMatrix4fv(project_loc, 1, GL_TRUE, project)
+        glUniformMatrix4fv(lookat_loc, 1, GL_TRUE, view)
+        glUniform4fv(viewport_loc, 1, viewport)
+
+    def _compute_geometry(self, block, bbox_vertices):
+        move = get_translate_matrix(*block.LeftEdge)
+        dds = (block.RightEdge - block.LeftEdge)
+        scale = get_scale_matrix(*dds)
+
+        transformed_box = bbox_vertices.dot(scale.T).dot(move.T).astype("float32")
+        return transformed_box
+
+    def _load_textures(self):
+        if len(self.gl_texture_names) == 0:
+            self.gl_texture_names = glGenTextures(len(self.blocks))
+            if len(self.blocks) == 1:
+                self.gl_texture_names = [self.gl_texture_names]
+            glPixelStorei(GL_UNPACK_ALIGNMENT, 1)
+            glTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)
+            glTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)
+            glTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE)
+            glTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
+            glTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER,
+                    GL_LINEAR)
+
+        else:
+            for texture in self.gl_texture_names:
+                glDeleteTextures([texture])
+
+            self.gl_texture_names = glGenTextures(len(self.blocks))
+            if len(self.blocks) == 1:
+                self.gl_texture_names = [self.gl_texture_names]
+
+        for block, texture_name in zip(self.blocks, self.gl_texture_names):
+            dx, dy, dz = block.my_data[0].shape
+            n_data = block.my_data[0].copy(order="F").astype("float32")
+            n_data = (n_data - self.min_val) / (self.max_val - self.min_val)
+            glBindTexture(GL_TEXTURE_3D, texture_name)
+            glTexStorage3D(GL_TEXTURE_3D, 1, GL_R32F,
+                *block.my_data[0].shape)
+            glTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 0, dx, dy, dz,
+                        GL_RED, GL_FLOAT, n_data.T)
+            glGenerateMipmap(GL_TEXTURE_3D)
+
+class SceneGraph:
+    def __init__(self):
+        self.collections = []
+        self.camera = None
+
+        self.gl_vert_shader = None
+        self.gl_frag_shader = None
+        self.shader_program = None
+
+        self.camera = None
+
+    def add_collection(self, collection):
+        r"""Adds a block collection to the scene. Collections must not overlap.
+
+        Although it hasn't been tested, this should allow one to add multiple
+        datasets to a single scene.
+
+        Parameters
+        ----------
+        collection : BlockCollection
+            A block collection object representing a data-set. Behavior is
+            undefined if any collection overlaps with another.
+
+        """
+        self.collections.append(collection)
+
+    def set_camera(self, camera):
+        r""" Sets the camera orientation for the entire scene.
+
+        Upon calling this function a kd-tree is constructed for each collection.
+        This function simply calls the BlockCollection set_camera function on
+        each collection in the scene.
+
+        Parameters
+        ----------
+        camera : Camera
+
+        """
+        self.camera = camera
+        for collection in self.collections:
+            collection.set_camera(camera)
+
+    def add_shader_from_file(self, filename):
+        r""" Compiles and links a fragment shader.
+
+        Given a `filename`, compiles and links the given fragment shader. This
+        function also compiles a fixed vertex shader. This function then attaches
+        these shaders to the current scene.
+
+        Parameters
+        ----------
+        filename : String
+            The location of the shader source file to read.
+
+        """
+        vr_directory = os.path.dirname(__file__)
+        frag_path = "shaders/" + filename
+        vert_path = "shaders/vert.glsl"
+        frag_abs = os.path.join(vr_directory, frag_path)
+        vert_abs = os.path.join(vr_directory, vert_path)
+
+        shader_file = open(frag_abs, 'r')
+        self.gl_frag_shader = shaders.compileShader(shader_file.read(), GL_FRAGMENT_SHADER)
+
+        vert_file = open(vert_abs, 'r')
+        self.gl_vert_shader = shaders.compileShader(vert_file.read(), GL_VERTEX_SHADER)
+
+        self.shader_program = glCreateProgram()
+        glAttachShader(self.shader_program, self.gl_vert_shader)
+        glAttachShader(self.shader_program, self.gl_frag_shader)
+
+        glLinkProgram(self.shader_program)
+
+    def render(self):
+        """ Renders one frame of the scene.
+
+        Renders the scene using the current collection and camera set by calls
+        to add_collection and set_camera respectively. Also uses the last shader
+        provided to the add_shader_from_file function.
+
+        """
+        for collection in self.collections:
+            collection.run_program(self.shader_program)

diff -r 1138e3e2b4154e0ef02bdd7d66f4dedc1363292a -r be600de97283a67f1076dac84c8e4029b83edf0f yt/visualization/volume_rendering/shaders/max_intensity_frag.glsl
--- /dev/null
+++ b/yt/visualization/volume_rendering/shaders/max_intensity_frag.glsl
@@ -0,0 +1,56 @@
+#version 330
+in vec4 v_model;
+in vec3 v_camera_pos;
+flat in mat4 inverse_proj;
+flat in mat4 inverse_view;
+out vec4 output_color;
+
+uniform sampler3D ds_tex;
+//layout (binding = 1) uniform sampler2D depth_tex;
+uniform vec3 dx;
+uniform vec3 left_edge;
+uniform vec3 right_edge;
+uniform vec4 viewport; // (offset_x, offset_y, 1 / screen_x, 1 / screen_y)
+
+bool within_bb(vec3 pos)
+{
+    bvec3 left =  greaterThanEqual(pos + .01, left_edge);
+    bvec3 right = lessThanEqual(pos - .01, right_edge);
+    return all(left) && all(right);
+}
+
+void main()
+{
+    // Obtain screen coordinates
+    // https://www.opengl.org/wiki/Compute_eye_space_from_window_space#From_gl_FragCoord
+    vec4 ndcPos;
+    ndcPos.xy = ((2.0 * gl_FragCoord.xy) - (2.0 * viewport.xy)) / (viewport.zw) - 1;
+    ndcPos.z = (2.0 * gl_FragCoord.z - 1.0);
+    ndcPos.w = 1.0;
+
+    vec4 clipPos = ndcPos / gl_FragCoord.w;
+    vec4 eyePos = inverse_proj * clipPos;
+    eyePos /= eyePos.w;
+
+    vec4 world_location = inverse_view * eyePos;
+
+    float step_size = length(right_edge - left_edge) / dx.x / 100000.0;
+    vec3 dir = normalize(world_location.xyz - v_camera_pos.xyz);
+    vec3 curr_color = vec3(0.0);
+
+    vec3 ray_position = world_location.xyz;
+
+    vec3 tex_curr_pos = vec3(0.0);
+    vec3 range = right_edge - left_edge;
+    while (within_bb(ray_position)) {
+        tex_curr_pos = (ray_position - left_edge)/range;
+
+        vec3 tex_sample = texture(ds_tex, tex_curr_pos).rgb;
+        if (length(curr_color) < length(tex_sample)) {
+            curr_color = tex_sample;
+        }
+
+        ray_position += dir * step_size;
+    }
+    output_color = vec4(curr_color.rrr, 1.0);
+}

diff -r 1138e3e2b4154e0ef02bdd7d66f4dedc1363292a -r be600de97283a67f1076dac84c8e4029b83edf0f yt/visualization/volume_rendering/shaders/vert.glsl
--- /dev/null
+++ b/yt/visualization/volume_rendering/shaders/vert.glsl
@@ -0,0 +1,20 @@
+#version 330
+in vec4 model_vertex; // The location of the vertex in model space
+out vec4 v_model;
+out vec3 v_camera_pos;
+flat out mat4 inverse_proj;
+flat out mat4 inverse_view;
+
+//// Uniforms
+uniform vec3 camera_pos;
+uniform mat4 lookat;
+uniform mat4 projection;
+
+void main()
+{
+    v_model = model_vertex;
+    v_camera_pos = camera_pos;
+    inverse_proj = inverse(projection);
+    inverse_view = inverse(lookat);
+    gl_Position = projection * lookat * model_vertex;
+}


https://bitbucket.org/yt_analysis/yt/commits/05fc74c4028e/
Changeset:   05fc74c4028e
Branch:      yt
User:        atmyers
Date:        2016-01-11 02:33:36+00:00
Summary:     correct syntax error in import
Affected #:  1 file

diff -r be600de97283a67f1076dac84c8e4029b83edf0f -r 05fc74c4028e838988bd4febed5a206b989fa41f yt/visualization/volume_rendering/interactive_vr.py
--- a/yt/visualization/volume_rendering/interactive_vr.py
+++ b/yt/visualization/volume_rendering/interactive_vr.py
@@ -1,12 +1,11 @@
 from OpenGL.GL import *
 from OpenGL.GL import shaders
 from OpenGL.GLUT import *
-
 import sys
 
 import numpy as np
 from yt.funcs import ensure_list
-from yt.utilities.math_utils import get_translate_matrix, get_scale_matrix,
+from yt.utilities.math_utils import get_translate_matrix, get_scale_matrix, \
     get_lookat_matrix, get_perspective_matrix
 import yt
 


https://bitbucket.org/yt_analysis/yt/commits/a990aab1ae96/
Changeset:   a990aab1ae96
Branch:      yt
User:        atmyers
Date:        2016-01-12 18:03:41+00:00
Summary:     making a few docstring corrections
Affected #:  1 file

diff -r 05fc74c4028e838988bd4febed5a206b989fa41f -r a990aab1ae9657214e735e10bbcd7823650da5e8 yt/utilities/math_utils.py
--- a/yt/utilities/math_utils.py
+++ b/yt/utilities/math_utils.py
@@ -758,28 +758,27 @@
 def get_perspective_matrix(fovy, aspect, z_near, z_far):
     """
     Given a field of view in radians, an aspect ratio, and a near
-    and far plane distance, this routine computes the rotation matrix
-    corresponding to a projection onto the corresponding perspective
-    frustum using homogenous coordinates.
+    and far plane distance, this routine computes the transformation matrix
+    corresponding to perspective projection using homogenous coordinates.
 
     Parameters
     ----------
-    fovy : scaler
+    fovy : scalar
         The angle in radians of the field of view.
 
-    aspect : scaler
+    aspect : scalar
         The aspect ratio of width / height for the projection.
 
-    z_near : scaler
+    z_near : scalar
         The distance of the near plane from the camera.
 
-    z_far : scaler
+    z_far : scalar
         The distance of the far plane from the camera.
 
     Returns
     -------
     persp_matrix : ndarray
-        A new 4x4 3D array. Represents a perspective transformation
+        A new 4x4 2D array. Represents a perspective transformation
         in homogeneous coordinates. Note that this matrix does not
         actually perform the projection. After multiplying a 4D
         vector of the form (x_0, y_0, z_0, 1.0), the point will be
@@ -833,7 +832,7 @@
     Returns
     -------
     lookat_matrix : ndarray
-        A new 4x4 `3D` array in homogeneous coordinates. This matrix
+        A new 4x4 2D array in homogeneous coordinates. This matrix
         moves all vectors in the same way required to move the camera
         to the origin of the coordinate system, with it pointing down
         the negative z-axis.
@@ -873,19 +872,19 @@
 
     Parameters
     ----------
-    dx : scaler
+    dx : scalar
         A translation amount for the x-coordinate
 
-    dy : scaler
+    dy : scalar
         A translation amount for the y-coordinate
 
-    dz : scaler
+    dz : scalar
         A translation amount for the z-coordinate
 
     Returns
     -------
     trans_matrix : ndarray
-        A new 4x4 `3D` array. Represents a translation by dx, dy
+        A new 4x4 2D array. Represents a translation by dx, dy
         and dz in each coordinate respectively.
     """
     result = np.zeros( (4, 4), dtype = 'float32', order = 'C')
@@ -908,19 +907,19 @@
 
     Parameters
     ----------
-    dx : scaler
+    dx : scalar
         A scaling factor for the x-coordinate.
 
-    dy : scaler
+    dy : scalar
         A scaling factor for the y-coordinate.
 
-    dz : scaler
+    dz : scalar
         A scaling factor for the z-coordinate.
 
     Returns
     -------
     scale_matrix : ndarray
-        A new 4x4 `3D` array. Represents a scaling by dx, dy, and dz
+        A new 4x4 2D array. Represents a scaling by dx, dy, and dz
         in each coordinate respectively.
     """
     result = np.zeros( (4, 4), dtype = 'float32', order = 'C')


https://bitbucket.org/yt_analysis/yt/commits/5ba87edc33c2/
Changeset:   5ba87edc33c2
Branch:      yt
User:        atmyers
Date:        2016-01-12 18:07:29+00:00
Summary:     removing some unused imports
Affected #:  2 files

diff -r a990aab1ae9657214e735e10bbcd7823650da5e8 -r 5ba87edc33c2ed49987c8d7711b35ac1880dc6ee yt/visualization/volume_rendering/interactive_loop.py
--- a/yt/visualization/volume_rendering/interactive_loop.py
+++ b/yt/visualization/volume_rendering/interactive_loop.py
@@ -1,8 +1,5 @@
 import cyglfw3 as glfw
-import yt
 import numpy as np
-import sys
-import time
 
 from interactive_vr import BlockCollection, SceneGraph, Camera
 

diff -r a990aab1ae9657214e735e10bbcd7823650da5e8 -r 5ba87edc33c2ed49987c8d7711b35ac1880dc6ee yt/visualization/volume_rendering/interactive_vr.py
--- a/yt/visualization/volume_rendering/interactive_vr.py
+++ b/yt/visualization/volume_rendering/interactive_vr.py
@@ -1,13 +1,10 @@
 from OpenGL.GL import *
 from OpenGL.GL import shaders
 from OpenGL.GLUT import *
-import sys
 
 import numpy as np
-from yt.funcs import ensure_list
 from yt.utilities.math_utils import get_translate_matrix, get_scale_matrix, \
     get_lookat_matrix, get_perspective_matrix
-import yt
 
 bbox_vertices = np.array(
       [[ 0.,  0.,  0.,  1.],


https://bitbucket.org/yt_analysis/yt/commits/b1223126b44b/
Changeset:   b1223126b44b
Branch:      yt
User:        MatthewTurk
Date:        2016-02-03 16:12:01+00:00
Summary:     Merging
Affected #:  838 files

diff -r 5ba87edc33c2ed49987c8d7711b35ac1880dc6ee -r b1223126b44ba6e518cecb897837b73913ea62a3 .hgchurn
--- a/.hgchurn
+++ b/.hgchurn
@@ -22,4 +22,21 @@
 ngoldbau at ucsc.edu = goldbaum at ucolick.org
 biondo at wisc.edu = Biondo at wisc.edu
 samgeen at googlemail.com = samgeen at gmail.com
-fbogert = fbogert at ucsc.edu
\ No newline at end of file
+fbogert = fbogert at ucsc.edu
+bwoshea = oshea at msu.edu
+mornkr at slac.stanford.edu = me at jihoonkim.org
+kbarrow = kssbarrow at gatech.edu
+kssbarrow at gmail.com = kssbarrow at gatech.edu
+kassbarrow at gmail.com = kssbarrow at gatech.edu
+antoine.strugarek at cea.fr = strugarek at astro.umontreal.ca
+rosen at ucolick.org = alrosen at ucsc.edu
+jzuhone = jzuhone at gmail.com
+karraki at nmsu.edu = karraki at gmail.com
+hckr at eml.cc = astrohckr at gmail.com
+julian3 at illinois.edu = astrohckr at gmail.com
+cosmosquark = bthompson2090 at gmail.com
+chris.m.malone at lanl.gov = chris.m.malone at gmail.com
+jnaiman at ucolick.org = jnaiman
+migueld.deval = miguel at archlinux.net
+slevy at ncsa.illinois.edu = salevy at illinois.edu
+malzraa at gmail.com = kellerbw at mcmaster.ca
\ No newline at end of file

diff -r 5ba87edc33c2ed49987c8d7711b35ac1880dc6ee -r b1223126b44ba6e518cecb897837b73913ea62a3 .hgignore
--- a/.hgignore
+++ b/.hgignore
@@ -10,9 +10,12 @@
 yt/analysis_modules/halo_finding/rockstar/rockstar_groupies.c
 yt/analysis_modules/halo_finding/rockstar/rockstar_interface.c
 yt/analysis_modules/ppv_cube/ppv_utils.c
+yt/analysis_modules/photon_simulator/utils.c
 yt/frontends/ramses/_ramses_reader.cpp
+yt/frontends/sph/smoothing_kernel.c
 yt/geometry/fake_octree.c
 yt/geometry/grid_container.c
+yt/geometry/grid_visitors.c
 yt/geometry/oct_container.c
 yt/geometry/oct_visitors.c
 yt/geometry/particle_deposit.c
@@ -25,26 +28,33 @@
 yt/utilities/spatial/ckdtree.c
 yt/utilities/lib/alt_ray_tracers.c
 yt/utilities/lib/amr_kdtools.c
-yt/utilities/lib/CICDeposit.c
-yt/utilities/lib/ContourFinding.c
-yt/utilities/lib/DepthFirstOctree.c
-yt/utilities/lib/FixedInterpolator.c
+yt/utilities/lib/basic_octree.c
+yt/utilities/lib/bitarray.c
+yt/utilities/lib/contour_finding.c
+yt/utilities/lib/depth_first_octree.c
+yt/utilities/lib/element_mappings.c
 yt/utilities/lib/fortran_reader.c
 yt/utilities/lib/freetype_writer.c
 yt/utilities/lib/geometry_utils.c
 yt/utilities/lib/image_utilities.c
-yt/utilities/lib/Interpolators.c
+yt/utilities/lib/interpolators.c
 yt/utilities/lib/kdtree.c
+yt/utilities/lib/line_integral_convolution.c
+yt/utilities/lib/mesh_construction.cpp
+yt/utilities/lib/mesh_intersection.cpp
+yt/utilities/lib/mesh_samplers.cpp
+yt/utilities/lib/mesh_traversal.cpp
 yt/utilities/lib/mesh_utilities.c
 yt/utilities/lib/misc_utilities.c
-yt/utilities/lib/Octree.c
+yt/utilities/lib/particle_mesh_operations.c
 yt/utilities/lib/origami.c
+yt/utilities/lib/particle_mesh_operations.c
+yt/utilities/lib/pixelization_routines.c
 yt/utilities/lib/png_writer.c
-yt/utilities/lib/PointsInVolume.c
-yt/utilities/lib/QuadTree.c
-yt/utilities/lib/RayIntegrators.c
+yt/utilities/lib/points_in_volume.c
+yt/utilities/lib/quad_tree.c
+yt/utilities/lib/ray_integrators.c
 yt/utilities/lib/ragged_arrays.c
-yt/utilities/lib/VolumeIntegrator.c
 yt/utilities/lib/grid_traversal.c
 yt/utilities/lib/marching_cubes.c
 yt/utilities/lib/png_writer.h
@@ -59,3 +69,4 @@
 doc/source/reference/api/generated/*
 doc/_temp/*
 doc/source/bootcamp/.ipynb_checkpoints/
+dist

diff -r 5ba87edc33c2ed49987c8d7711b35ac1880dc6ee -r b1223126b44ba6e518cecb897837b73913ea62a3 .python-version
--- /dev/null
+++ b/.python-version
@@ -0,0 +1,1 @@
+2.7.9

diff -r 5ba87edc33c2ed49987c8d7711b35ac1880dc6ee -r b1223126b44ba6e518cecb897837b73913ea62a3 CONTRIBUTING.rst
--- /dev/null
+++ b/CONTRIBUTING.rst
@@ -0,0 +1,970 @@
+.. This document is rendered in HTML with cross-reference links filled in at
+   http://yt-project.org/doc/developing/
+
+.. _getting-involved:
+
+Getting Involved
+================
+
+There are *lots* of ways to get involved with yt, as a community and as a
+technical system -- not all of them just contributing code, but also
+participating in the community, helping us with designing the websites, adding
+documentation, and sharing your scripts with others.
+
+Coding is only one way to be involved!
+
+Communication Channels
+----------------------
+
+There are five main communication channels for yt:
+
+ * We have an IRC channel, on ``irc.freenode.net`` in ``#yt``.
+   You can connect through our web
+   gateway without any special client, at http://yt-project.org/irc.html .
+   *IRC is the first stop for conversation!*
+ * Many yt developers participate in the yt Slack community. Slack is a free 
+   chat service that many teams use to organize their work. You can get an
+   invite to yt's Slack organization by clicking the "Join us @ Slack" button
+   on this page: http://yt-project.org/community.html
+ * `yt-users <http://lists.spacepope.org/listinfo.cgi/yt-users-spacepope.org>`_
+   is a relatively high-traffic mailing list where people are encouraged to ask
+   questions about the code, figure things out and so on.
+ * `yt-dev <http://lists.spacepope.org/listinfo.cgi/yt-dev-spacepope.org>`_ is
+   a much lower-traffic mailing list designed to focus on discussions of
+   improvements to the code, ideas about planning, development issues, and so
+   on.
+ * `yt-svn <http://lists.spacepope.org/listinfo.cgi/yt-svn-spacepope.org>`_ is
+   the (now-inaccurately titled) mailing list where all pushes to the primary
+   repository are sent.
+
+The easiest way to get involved with yt is to read the mailing lists, hang out
+in IRC or slack chat, and participate.  If someone asks a question you know the
+answer to (or have your own question about!) write back and answer it.
+
+If you have an idea about something, suggest it!  We not only welcome
+participation, we encourage it.
+
+Documentation
+-------------
+
+The yt documentation is constantly being updated, and it is a task we would very
+much appreciate assistance with.  Whether that is adding a section, updating an
+outdated section, contributing typo or grammatical fixes, adding a FAQ, or
+increasing coverage of functionality, it would be very helpful if you wanted to
+help out.
+
+The easiest way to help out is to fork the main yt repository (where the
+documentation lives in the ``doc`` directory in the root of the yt mercurial
+repository) and then make your changes in your own fork.  When you are done,
+issue a pull request through the website for your new fork, and we can comment
+back and forth and eventually accept your changes. See :ref:`sharing-changes` for
+more information about contributing your changes to yt on bitbucket.
+
+Gallery Images and Videos
+-------------------------
+
+If you have an image or video you'd like to display in the image or video
+galleries, getting it included it easy!  You can either fork the `yt homepage
+repository <http://bitbucket.org/yt_analysis/website>`_ and add it there, or
+email it to us and we'll add it to the `Gallery
+<http://yt-project.org/gallery.html>`_.
+
+We're eager to show off the images and movies you make with yt, so please feel
+free to drop `us <http://lists.spacepope.org/listinfo.cgi/yt-dev-spacepope.org>`_
+a line and let us know if you've got something great!
+
+Technical Contributions
+-----------------------
+
+Contributing code is another excellent way to participate -- whether it's
+bug fixes, new features, analysis modules, or a new code frontend.  See
+:ref:`creating_frontend` for more details.
+
+The process is pretty simple: fork on BitBucket, make changes, issue a pull
+request.  We can then go back and forth with comments in the pull request, but
+usually we end up accepting.
+
+For more information, see :ref:`contributing-code`, where we spell out how to
+get up and running with a development environment, how to commit, and how to
+use BitBucket.
+
+Online Presence
+---------------
+
+Some of these fall under the other items, but if you'd like to help out with
+the website or any of the other ways yt is presented online, please feel free!
+Almost everything is kept in hg repositories on BitBucket, and it is very easy
+to fork and contribute back changes.
+
+Please feel free to dig in and contribute changes.
+
+Word of Mouth
+-------------
+
+If you're using yt and it has increased your productivity, please feel
+encouraged to share that information.  Cite our `paper
+<http://adsabs.harvard.edu/abs/2011ApJS..192....9T>`_, tell your colleagues,
+and just spread word of mouth.  By telling people about your successes, you'll
+help bring more eyes and hands to the table -- in this manner, by increasing
+participation, collaboration, and simply spreading the limits of what the code
+is asked to do, we hope to help scale the utility and capability of yt with the
+community size.
+
+Feel free to `blog <http://blog.yt-project.org/>`_ about, `tweet
+<http://twitter.com/yt_astro>`_ about and talk about what you are up to!
+
+Long-Term Projects
+------------------
+
+There are some wild-eyed, out-there ideas that have been bandied about for the
+future directions of yt -- some of them even written into the mission
+statement.  The ultimate goal is to move past simple analysis and visualization
+of data and begin to approach it from the other side, of generating data,
+running solvers.  We also hope to increase its ability to act as an in situ
+analysis code, by presenting a unified protocol.  Other projects include
+interfacing with ParaView and VisIt, creating a web GUI for running
+simulations, creating a run-tracker that follows simulations in progress, a
+federated database for simulation outputs, and so on and so forth.
+
+yt is an ambitious project.  Let's be ambitious together.
+
+yt Community Code of Conduct
+----------------------------
+
+The community of participants in open source
+Scientific projects is made up of members from around the
+globe with a diverse set of skills, personalities, and
+experiences. It is through these differences that our
+community experiences success and continued growth. We
+expect everyone in our community to follow these guidelines
+when interacting with others both inside and outside of our
+community. Our goal is to keep ours a positive, inclusive,
+successful, and growing community.
+
+As members of the community,
+
+- We pledge to treat all people with respect and
+  provide a harassment- and bullying-free environment,
+  regardless of sex, sexual orientation and/or gender
+  identity, disability, physical appearance, body size,
+  race, nationality, ethnicity, and religion. In
+  particular, sexual language and imagery, sexist,
+  racist, or otherwise exclusionary jokes are not
+  appropriate.
+
+- We pledge to respect the work of others by
+  recognizing acknowledgment/citation requests of
+  original authors. As authors, we pledge to be explicit
+  about how we want our own work to be cited or
+  acknowledged.
+
+- We pledge to welcome those interested in joining the
+  community, and realize that including people with a
+  variety of opinions and backgrounds will only serve to
+  enrich our community. In particular, discussions
+  relating to pros/cons of various technologies,
+  programming languages, and so on are welcome, but
+  these should be done with respect, taking proactive
+  measure to ensure that all participants are heard and
+  feel confident that they can freely express their
+  opinions.
+
+- We pledge to welcome questions and answer them
+  respectfully, paying particular attention to those new
+  to the community. We pledge to provide respectful
+  criticisms and feedback in forums, especially in
+  discussion threads resulting from code
+  contributions.
+
+- We pledge to be conscientious of the perceptions of
+  the wider community and to respond to criticism
+  respectfully. We will strive to model behaviors that
+  encourage productive debate and disagreement, both
+  within our community and where we are criticized. We
+  will treat those outside our community with the same
+  respect as people within our community.
+
+- We pledge to help the entire community follow the
+  code of conduct, and to not remain silent when we see
+  violations of the code of conduct. We will take action
+  when members of our community violate this code such as
+  contacting confidential at yt-project.org (all emails sent to
+  this address will be treated with the strictest
+  confidence) or talking privately with the person.
+
+This code of conduct applies to all
+community situations online and offline, including mailing
+lists, forums, social media, conferences, meetings,
+associated social events, and one-to-one interactions.
+
+The yt Community Code of Conduct was adapted from the
+`Astropy Community Code of Conduct
+<http://www.astropy.org/about.html#codeofconduct>`_,
+which was partially inspired by the PSF code of conduct.
+
+.. _contributing-code:
+
+How to Develop yt
+=================
+
+yt is a community project!
+
+We are very happy to accept patches, features, and bugfixes from any member of
+the community!  yt is developed using mercurial, primarily because it enables
+very easy and straightforward submission of changesets.  We're eager to hear
+from you, and if you are developing yt, we encourage you to subscribe to the
+`developer mailing list
+<http://lists.spacepope.org/listinfo.cgi/yt-dev-spacepope.org>`_. Please feel
+free to hack around, commit changes, and send them upstream.
+
+.. note:: If you already know how to use the `mercurial version control system
+   <http://mercurial-scm.org>`_ and are comfortable with handling it yourself,
+   the quickest way to contribute to yt is to `fork us on BitBucket
+   <http://bitbucket.org/yt_analysis/yt/fork>`_, make your changes, push the
+   changes to your fork and issue a `pull request
+   <http://bitbucket.org/yt_analysis/yt/pull-requests>`_.  The rest of this
+   document is just an explanation of how to do that.
+
+See :ref:`code-style-guide` for more information about coding style in yt and
+:ref:`docstrings` for an example docstring.  Please read them before hacking on
+the codebase, and feel free to email any of the mailing lists for help with the
+codebase.
+
+Keep in touch, and happy hacking!
+
+.. _open-issues:
+
+Open Issues
+-----------
+
+If you're interested in participating in yt development, take a look at the
+`issue tracker on bitbucket
+<https://bitbucket.org/yt_analysis/yt/issues?milestone=easy?status=new>`_.
+Issues are marked with a milestone of "easy", "moderate", or "difficult"
+depending on the estimated level of difficulty for fixing the issue. While we
+try to triage the issue tracker regularly, it may be the case that issues marked
+"moderate" are actually easier than their milestone label indicates since that
+is the default value.
+
+Here are some predefined issue searches that might be useful:
+
+* Unresolved issues `marked "easy" <https://bitbucket.org/yt_analysis/yt/issues?milestone=easy&status=open&status=new>`_.
+* Unresolved issues `marked "easy" or "moderate" <https://bitbucket.org/yt_analysis/yt/issues?milestone=easy&milestone=moderate&status=open&status=new>`_
+* `All unresolved issues <https://bitbucket.org/yt_analysis/yt/issues?status=open&status=new>`_
+
+Submitting Changes
+------------------
+
+We provide a brief introduction to submitting changes here.  yt thrives on the
+strength of its communities (http://arxiv.org/abs/1301.7064 has further
+discussion) and we encourage contributions from any user.  While we do not
+discuss version control, mercurial or the advanced usage of BitBucket in detail
+here, we do provide an outline of how to submit changes and we are happy to
+provide further assistance or guidance.
+
+Licensing
++++++++++
+
+yt is `licensed <http://blog.yt-project.org/post/Relicensing.html>`_ under the
+BSD 3-clause license.  Versions previous to yt-2.6 were released under the GPLv3.
+
+All contributed code must be BSD-compatible.  If you'd rather not license in
+this manner, but still want to contribute, please consider creating an external
+package, which we'll happily link to.
+
+How To Get The Source Code For Editing
+++++++++++++++++++++++++++++++++++++++
+
+yt is hosted on BitBucket, and you can see all of the yt repositories at
+http://bitbucket.org/yt_analysis/.  With the yt installation script you should have a
+copy of Mercurial for checking out pieces of code.  Make sure you have followed
+the steps above for bootstrapping your development (to assure you have a
+bitbucket account, etc.)
+
+In order to modify the source code for yt, we ask that you make a "fork" of the
+main yt repository on bitbucket.  A fork is simply an exact copy of the main
+repository (along with its history) that you will now own and can make
+modifications as you please.  You can create a personal fork by visiting the yt
+bitbucket webpage at https://bitbucket.org/yt_analysis/yt/ .  After logging in,
+you should see an option near the top right labeled "fork".  Click this option,
+and then click the fork repository button on the subsequent page.  You now have
+a forked copy of the yt repository for your own personal modification.
+
+This forked copy exists on the bitbucket repository, so in order to access
+it locally, follow the instructions at the top of that webpage for that
+forked repository, namely run at a local command line:
+
+.. code-block:: bash
+
+   $ hg clone http://bitbucket.org/<USER>/<REPOSITORY_NAME>
+
+This downloads that new forked repository to your local machine, so that you
+can access it, read it, make modifications, etc.  It will put the repository in
+a local directory of the same name as the repository in the current working
+directory.  You can see any past state of the code by using the hg log command.
+For example, the following command would show you the last 5 changesets
+(modifications to the code) that were submitted to that repository.
+
+.. code-block:: bash
+
+   $ cd <REPOSITORY_NAME>
+   $ hg log -l 5
+
+Using the revision specifier (the number or hash identifier next to each
+changeset), you can update the local repository to any past state of the
+code (a previous changeset or version) by executing the command:
+
+.. code-block:: bash
+
+   $ hg up revision_specifier
+
+Lastly, if you want to use this new downloaded version of your yt repository as
+the *active* version of yt on your computer (i.e. the one which is executed when
+you run yt from the command line or the one that is loaded when you do ``import
+yt``), then you must "activate" it using the following commands from within the
+repository directory.
+
+.. code-block:: bash
+
+   $ cd <REPOSITORY_NAME>
+   $ python2.7 setup.py develop
+
+This will rebuild all C modules as well.
+
+.. _reading-source:
+
+How To Read The Source Code
++++++++++++++++++++++++++++
+
+If you just want to *look* at the source code, you may already have it on your
+computer.  If you build yt using the install script, the source is available at
+``$YT_DEST/src/yt-hg``.  See :ref:`source-installation` for more details about
+to obtain the yt source code if you did not build yt using the install
+script.
+
+The root directory of the yt mercurial repository contains a number of
+subdirectories with different components of the code.  Most of the yt source
+code is contained in the yt subdirectory.  This directory its self contains
+the following subdirectories:
+
+``frontends``
+   This is where interfaces to codes are created.  Within each subdirectory of
+   yt/frontends/ there must exist the following files, even if empty:
+
+   * ``data_structures.py``, where subclasses of AMRGridPatch, Dataset
+     and AMRHierarchy are defined.
+   * ``io.py``, where a subclass of IOHandler is defined.
+   * ``fields.py``, where fields we expect to find in datasets are defined
+   * ``misc.py``, where any miscellaneous functions or classes are defined.
+   * ``definitions.py``, where any definitions specific to the frontend are
+     defined.  (i.e., header formats, etc.)
+
+``fields``
+   This is where all of the derived fields that ship with yt are defined.
+
+``geometry``
+   This is where geometric helpler routines are defined. Handlers
+   for grid and oct data, as well as helpers for coordinate transformations
+   can be found here.
+
+``visualization``
+   This is where all visualization modules are stored.  This includes plot
+   collections, the volume rendering interface, and pixelization frontends.
+
+``data_objects``
+   All objects that handle data, processed or unprocessed, not explicitly
+   defined as visualization are located in here.  This includes the base
+   classes for data regions, covering grids, time series, and so on.  This
+   also includes derived fields and derived quantities.
+
+``analysis_modules``
+   This is where all mechanisms for processing data live.  This includes
+   things like clump finding, halo profiling, halo finding, and so on.  This
+   is something of a catchall, but it serves as a level of greater
+   abstraction that simply data selection and modification.
+
+``gui``
+   This is where all GUI components go.  Typically this will be some small
+   tool used for one or two things, which contains a launching mechanism on
+   the command line.
+
+``utilities``
+   All broadly useful code that doesn't clearly fit in one of the other
+   categories goes here.
+
+``extern``
+   Bundled external modules (i.e. code that was not written by one of
+   the yt authors but that yt depends on) lives here.
+
+
+If you're looking for a specific file or function in the yt source code, use
+the unix find command:
+
+.. code-block:: bash
+
+   $ find <DIRECTORY_TREE_TO_SEARCH> -name '<FILENAME>'
+
+The above command will find the FILENAME in any subdirectory in the
+DIRECTORY_TREE_TO_SEARCH.  Alternatively, if you're looking for a function
+call or a keyword in an unknown file in a directory tree, try:
+
+.. code-block:: bash
+
+   $ grep -R <KEYWORD_TO_FIND><DIRECTORY_TREE_TO_SEARCH>
+
+This can be very useful for tracking down functions in the yt source.
+
+.. _building-yt:
+
+Building yt
++++++++++++
+
+If you have made changes to any C or Cython (``.pyx``) modules, you have to
+rebuild yt.  If your changes have exclusively been to Python modules, you will
+not need to re-build, but (see below) you may need to re-install.
+
+If you are running from a clone that is executable in-place (i.e., has been
+installed via the installation script or you have run ``setup.py develop``) you
+can rebuild these modules by executing:
+
+.. code-block:: bash
+
+  $ python2.7 setup.py develop
+
+If you have previously "installed" via ``setup.py install`` you have to
+re-install:
+
+.. code-block:: bash
+
+  $ python2.7 setup.py install
+
+Only one of these two options is needed.
+
+.. _windows-developing:
+
+Developing yt on Windows
+------------------------
+
+If you plan to develop yt on Windows, it is necessary to use the `MinGW
+<http://www.mingw.org/>`_ gcc compiler that can be installed using the `Anaconda
+Python Distribution <https://store.continuum.io/cshop/anaconda/>`_. The libpython package must be
+installed from Anaconda as well. These can both be installed with a single command:
+
+.. code-block:: bash
+
+  $ conda install libpython mingw
+
+Additionally, the syntax for the setup command is slightly different; you must type:
+
+.. code-block:: bash
+
+  $ python2.7 setup.py build --compiler=mingw32 develop
+
+or
+
+.. code-block:: bash
+
+  $ python2.7 setup.py build --compiler=mingw32 install
+
+.. _requirements-for-code-submission:
+
+Requirements for Code Submission
+--------------------------------
+
+Modifications to the code typically fall into one of three categories, each of
+which have different requirements for acceptance into the code base.  These
+requirements are in place for a few reasons -- to make sure that the code is
+maintainable, testable, and that we can easily include information about
+changes in changelogs during the release procedure.  (See `YTEP-0008
+<https://ytep.readthedocs.org/en/latest/YTEPs/YTEP-0008.html>`_ for more
+detail.)
+
+* New Features
+
+  * New unit tests (possibly new answer tests) (See :ref:`testing`)
+  * Docstrings in the source code for the public API
+  * Addition of new feature to the narrative documentation (See :ref:`writing_documentation`)
+  * Addition of cookbook recipe (See :ref:`writing_documentation`)
+  * Issue created on issue tracker, to ensure this is added to the changelog
+
+* Extension or Breakage of API in Existing Features
+
+  * Update existing narrative docs and docstrings (See :ref:`writing_documentation`)
+  * Update existing cookbook recipes (See :ref:`writing_documentation`)
+  * Modify of create new unit tests (See :ref:`testing`)
+  * Issue created on issue tracker, to ensure this is added to the changelog
+
+* Bug fixes
+
+  * Unit test is encouraged, to ensure breakage does not happen again in the
+    future. (See :ref:`testing`)
+  * Issue created on issue tracker, to ensure this is added to the changelog
+
+When submitting, you will be asked to make sure that your changes meet all of
+these requirements.  They are pretty easy to meet, and we're also happy to help
+out with them.  In :ref:`code-style-guide` there is a list of handy tips for
+how to structure and write your code.
+
+.. _mercurial-with-yt:
+
+How to Use Mercurial with yt
+----------------------------
+
+If you're new to Mercurial, these three resources are pretty great for learning
+the ins and outs:
+
+* http://hginit.com/
+* http://hgbook.red-bean.com/read/
+* http://mercurial-scm.org/
+* http://mercurial-scm.org/wiki
+
+The commands that are essential for using mercurial include:
+
+* ``hg help`` which provides help for any mercurial command. For example, you
+  can learn more about the ``log`` command by doing ``hg help log``. Other useful
+  topics to use with ``hg help`` are ``hg help glossary``, ``hg help config``,
+  ``hg help extensions``, and ``hg help revsets``.
+* ``hg commit`` which commits changes in the working directory to the
+  repository, creating a new "changeset object."
+* ``hg add`` which adds a new file to be tracked by mercurial.  This does
+  not change the working directory.
+* ``hg pull`` which pulls (from an optional path specifier) changeset
+  objects from a remote source.  The working directory is not modified.
+* ``hg push`` which sends (to an optional path specifier) changeset objects
+  to a remote source.  The working directory is not modified.
+* ``hg log`` which shows a log of all changeset objects in the current
+  repository.  Use ``-G`` to show a graph of changeset objects and their
+  relationship.
+* ``hg update`` which (with an optional "revision" specifier) updates the
+  state of the working directory to match a changeset object in the
+  repository.
+* ``hg merge`` which combines two changesets to make a union of their lines
+  of development.  This updates the working directory.
+
+We are happy to asnswers questions about mercurial use on our IRC, slack
+chat or on the mailing list to walk you through any troubles you might have.
+Here are some general suggestions for using mercurial with yt:
+
+* Named branches are to be avoided.  Try using bookmarks (``see hg help
+  bookmark``) to track work.  (`More info about bookmarks is available on the
+  mercurial wiki <http://mercurial-scm.org/wiki/Bookmarks>`_)
+* Make sure you set a username in your ``~/.hgrc`` before you commit any
+  changes!  All of the tutorials above will describe how to do this as one of
+  the very first steps.
+* When contributing changes, you might be asked to make a handful of
+  modifications to your source code.  We'll work through how to do this with
+  you, and try to make it as painless as possible.
+* Your test may fail automated style checks. See :ref:`code-style-guide` for
+  more information about automatically verifying your code style.
+* Please avoid deleting your yt forks, as that deletes the pull request
+  discussion from process from BitBucket's website, even if your pull request
+  is merged.
+* You should only need one fork.  To keep it in sync, you can sync from the
+  website. See Bitbucket's `Blog Post
+  <https://blog.bitbucket.org/2013/02/04/syncing-and-merging-come-to-bitbucket/>`_
+  about this. See :ref:`sharing-changes` for a description of the basic workflow
+  and :ref:`multiple-PRs` for a discussion about what to do when you want to
+  have multiple open pull requests at the same time.
+* If you run into any troubles, stop by IRC (see :ref:`irc`) or the mailing
+  list.
+
+.. _sharing-changes:
+
+Making and Sharing Changes
+--------------------------
+
+The simplest way to submit changes to yt is to do the following:
+
+* Build yt from the mercurial repository
+* Navigate to the root of the yt repository
+* Make some changes and commit them
+* Fork the `yt repository on BitBucket <https://bitbucket.org/yt_analysis/yt>`_
+* Push the changesets to your fork
+* Issue a pull request.
+
+Here's a more detailed flowchart of how to submit changes.
+
+#. If you have used the installation script, the source code for yt can be
+   found in ``$YT_DEST/src/yt-hg``.  Alternatively see
+   :ref:`source-installation` for instructions on how to build yt from the
+   mercurial repository. (Below, in :ref:`reading-source`, we describe how to
+   find items of interest.)
+#. Edit the source file you are interested in and
+   test your changes.  (See :ref:`testing` for more information.)
+#. Fork yt on BitBucket.  (This step only has to be done once.)  You can do
+   this at: https://bitbucket.org/yt_analysis/yt/fork.  Call this repository
+   yt.
+#. Create a bookmark to track your work. For example: ``hg bookmark
+   my-first-pull-request``
+#. Commit these changes, using ``hg commit``.  This can take an argument
+   which is a series of filenames, if you have some changes you do not want
+   to commit.
+#. Remember that this is a large development effort and to keep the code
+   accessible to everyone, good documentation is a must.  Add in source code
+   comments for what you are doing.  Add in docstrings
+   if you are adding a new function or class or keyword to a function.
+   Add documentation to the appropriate section of the online docs so that
+   people other than yourself know how to use your new code.
+#. If your changes include new functionality or cover an untested area of the
+   code, add a test.  (See :ref:`testing` for more information.)  Commit
+   these changes as well.
+#. Push your changes to your new fork using the command::
+
+      hg push -B my-first-pull-request https://bitbucket.org/YourUsername/yt/
+
+   Where you should substitute the name of the bookmark you are working on for
+   ``my-first-pull-request``. If you end up doing considerable development, you
+   can set an alias in the file ``.hg/hgrc`` to point to this path.
+
+   .. note::
+     Note that the above approach uses HTTPS as the transfer protocol
+     between your machine and BitBucket.  If you prefer to use SSH - or
+     perhaps you're behind a proxy that doesn't play well with SSL via
+     HTTPS - you may want to set up an `SSH key`_ on BitBucket.  Then, you use
+     the syntax ``ssh://hg@bitbucket.org/YourUsername/yt``, or equivalent, in
+     place of ``https://bitbucket.org/YourUsername/yt`` in Mercurial commands.
+     For consistency, all commands we list in this document will use the HTTPS
+     protocol.
+
+     .. _SSH key: https://confluence.atlassian.com/display/BITBUCKET/Set+up+SSH+for+Mercurial
+
+#. Issue a pull request at
+   https://bitbucket.org/YourUsername/yt/pull-request/new
+   A pull request is essentially just asking people to review and accept the
+   modifications you have made to your personal version of the code.
+
+
+During the course of your pull request you may be asked to make changes.  These
+changes may be related to style issues, correctness issues, or even requesting
+tests.  The process for responding to pull request code review is relatively
+straightforward.
+
+#. Make requested changes, or leave a comment indicating why you don't think
+   they should be made.
+#. Commit those changes to your local repository.
+#. Push the changes to your fork:
+
+      hg push https://bitbucket.org/YourUsername/yt/
+
+#. Your pull request will be automatically updated.
+
+.. _multiple-PRs:
+
+Working with Multiple BitBucket Pull Requests
++++++++++++++++++++++++++++++++++++++++++++++
+
+Once you become active developing for yt, you may be working on
+various aspects of the code or bugfixes at the same time.  Currently,
+BitBucket's *modus operandi* for pull requests automatically updates
+your active pull request with every ``hg push`` of commits that are a
+descendant of the head of your pull request.  In a normal workflow,
+this means that if you have an active pull request, make some changes
+locally for, say, an unrelated bugfix, then push those changes back to
+your fork in the hopes of creating a *new* pull request, you'll
+actually end up updating your current pull request!
+
+There are a few ways around this feature of BitBucket that will allow
+for multiple pull requests to coexist; we outline one such method
+below.  We assume that you have a fork of yt at
+``http://bitbucket.org/YourUsername/Your_yt`` (see
+:ref:`sharing-changes` for instructions on creating a fork) and that
+you have an active pull request to the main repository.
+
+The main issue with starting another pull request is to make sure that
+your push to BitBucket doesn't go to the same head as your
+existing pull request and trigger BitBucket's auto-update feature.
+Here's how to get your local repository away from your current pull
+request head using `revsets <http://www.selenic.com/hg/help/revsets>`_
+and your ``hgrc`` file:
+
+#. Set up a Mercurial path for the main yt repository (note this is a convenience
+   step and only needs to be done once).  Add the following to your
+   ``Your_yt/.hg/hgrc``::
+
+     [paths]
+     upstream = https://bitbucket.org/yt_analysis/yt
+
+   This will create a path called ``upstream`` that is aliased to the URL of the
+   main yt repository.
+#. Now we'll use revsets_ to update your local repository to the tip of the
+   ``upstream`` path:
+
+   .. code-block:: bash
+
+      $ hg pull upstream
+      $ hg update -r "remote(yt, 'upstream')"
+
+After the above steps, your local repository should be at the current head of
+the ``yt`` branch in the main yt repository.  If you find yourself doing this a
+lot, it may be worth aliasing this task in your ``hgrc`` file by adding
+something like::
+
+  [alias]
+  ytupdate = update -r "remote(yt, 'upstream')"
+
+And then you can just issue ``hg ytupdate`` to get at the current head of the
+``yt`` branch on main yt repository.
+
+Make sure you are on the branch you want to be on, and then you can make changes
+and ``hg commit`` them.  If you prefer working with `bookmarks
+<http://mercurial-scm.org/wiki/Bookmarks>`_, you may want to make a bookmark
+before committing your changes, such as ``hg bookmark mybookmark``.
+
+To push your changes on a bookmark to bitbucket, you can issue the following
+command:
+
+.. code-block:: bash
+
+    $ hg push -B myfeature https://bitbucket.org/YourUsername/Your_yt
+
+The ``-B`` means "publish my bookmark, the changeset the bookmark is pointing
+at, and any ancestors of that changeset that aren't already on the remote
+server".
+
+To push to your fork on BitBucket if you didn't use a bookmark, you issue the
+following:
+
+.. code-block:: bash
+
+  $ hg push -r . -f https://bitbucket.org/YourUsername/Your_yt
+
+The ``-r .`` means "push only the commit I'm standing on and any ancestors."
+The ``-f`` is to force Mecurial to do the push since we are creating a new
+remote head without a bookmark.
+
+You can then go to the BitBucket interface and issue a new pull request based on
+your last changes, as usual.
+
+.. _code-style-guide:
+
+Coding Style Guide
+==================
+
+Automatically checking code style
+---------------------------------
+
+Below are a list of rules for coding style in yt. Some of these rules are
+suggestions are not explicitly enforced, while some are enforced via automated
+testing. The yt project uses a subset of the rules checked by ``flake8`` to
+verify our code. The ``flake8`` tool is a combination of the ``pyflakes`` and
+``pep8`` tools. To check the coding style of your contributions locally you will
+need to install the ``flake8`` tool from ``pip``:
+
+.. code-block:: bash
+
+    $ pip install flake8
+
+And then navigate to the root of the yt repository and run ``flake8`` on the
+``yt`` folder:
+
+.. code-block:: bash
+
+    $ cd $YT_HG
+    $ flake8 ./yt
+
+This will print out any ``flake8`` errors or warnings that your newly added code
+triggers. The errors will be in your newly added code because we have already
+cleaned up the rest of the yt codebase of the errors and warnings detected by
+the `flake8` tool. Note that this will only trigger a subset of the `full flake8
+error and warning list
+<http://flake8.readthedocs.org/en/latest/warnings.html>`_, since we explicitly
+blacklist a large number of the full list of rules that are checked by
+``flake8`` by default.
+
+Source code style guide
+-----------------------
+
+ * In general, follow PEP-8 guidelines.
+   http://www.python.org/dev/peps/pep-0008/
+ * Classes are ``ConjoinedCapitals``, methods and functions are
+   ``lowercase_with_underscores``.
+ * Use 4 spaces, not tabs, to represent indentation.
+ * Line widths should not be more than 80 characters.
+ * Do not use nested classes unless you have a very good reason to, such as
+   requiring a namespace or class-definition modification.  Classes should live
+   at the top level.  ``__metaclass__`` is exempt from this.
+ * Do not use unnecessary parenthesis in conditionals.  ``if((something) and
+   (something_else))`` should be rewritten as
+   ``if something and something_else``. Python is more forgiving than C.
+ * Avoid copying memory when possible. For example, don't do
+   ``a = a.reshape(3,4)`` when ``a.shape = (3,4)`` will do, and ``a = a * 3``
+   should be ``np.multiply(a, 3, a)``.
+ * In general, avoid all double-underscore method names: ``__something`` is
+   usually unnecessary.
+ * When writing a subclass, use the super built-in to access the super class,
+   rather than explicitly. Ex: ``super(SpecialGridSubclass, self).__init__()``
+   rather than ``SpecialGrid.__init__()``.
+ * Docstrings should describe input, output, behavior, and any state changes
+   that occur on an object.  See the file ``doc/docstring_example.txt`` for a
+   fiducial example of a docstring.
+ * Use only one top-level import per line. Unless there is a good reason not to,
+   imports should happen at the top of the file, after the copyright blurb.
+ * Never compare with ``True`` or ``False`` using ``==`` or ``!=``, always use
+   ``is`` or ``is not``.
+ * If you are comparing with a numpy boolean array, just refer to the array.
+   Ex: do ``np.all(array)`` instead of ``np.all(array == True)``.
+ * Never comapre with None using ``==`` or ``!=``, use ``is None`` or
+   ``is not None``.
+ * Use ``statement is not True`` instead of ``not statement is True``
+ * Only one statement per line, do not use semicolons to put two or more
+   statements on a single line.
+ * Only declare local variables if they will be used later. If you do not use the
+   return value of a function, do not store it in a variable.
+ * Add tests for new functionality. When fixing a bug, consider adding a test to
+   prevent the bug from recurring.
+
+API Style Guide
+---------------
+
+ * Do not use ``from some_module import *``
+ * Internally, only import from source files directly -- instead of:
+
+     ``from yt.visualization.api import ProjectionPlot``
+
+   do:
+
+     ``from yt.visualization.plot_window import ProjectionPlot``
+
+ * Import symbols from the module where they are defined, avoid transitive
+   imports.
+ * Import standard library modules, functions, and classes from builtins, do not
+   import them from other yt files.
+ * Numpy is to be imported as ``np``.
+ * Do not use too many keyword arguments.  If you have a lot of keyword
+   arguments, then you are doing too much in ``__init__`` and not enough via
+   parameter setting.
+ * In function arguments, place spaces before commas.  ``def something(a,b,c)``
+   should be ``def something(a, b, c)``.
+ * Don't create a new class to replicate the functionality of an old class --
+   replace the old class.  Too many options makes for a confusing user
+   experience.
+ * Parameter files external to yt are a last resort.
+ * The usage of the ``**kwargs`` construction should be avoided.  If they cannot
+   be avoided, they must be explained, even if they are only to be passed on to
+   a nested function.
+
+.. _docstrings
+
+Docstrings
+----------
+
+The following is an example docstring. You can use it as a template for
+docstrings in your code and as a guide for how we expect docstrings to look and
+the level of detail we are looking for. Note that we use NumPy style docstrings
+written in `Sphinx restructured text format <http://sphinx-doc.org/rest.html>`_.
+
+.. code-block:: rest
+
+    r"""A one-line summary that does not use variable names or the
+    function name.
+
+    Several sentences providing an extended description. Refer to
+    variables using back-ticks, e.g. ``var``.
+
+    Parameters
+    ----------
+    var1 : array_like
+        Array_like means all those objects -- lists, nested lists, etc. --
+        that can be converted to an array.  We can also refer to
+        variables like ``var1``.
+    var2 : int
+        The type above can either refer to an actual Python type
+        (e.g. ``int``), or describe the type of the variable in more
+        detail, e.g. ``(N,) ndarray`` or ``array_like``.
+    Long_variable_name : {'hi', 'ho'}, optional
+        Choices in brackets, default first when optional.
+
+    Returns
+    -------
+    describe : type
+        Explanation
+    output : type
+        Explanation
+    tuple : type
+        Explanation
+    items : type
+        even more explaining
+
+    Other Parameters
+    ----------------
+    only_seldom_used_keywords : type
+        Explanation
+    common_parameters_listed_above : type
+        Explanation
+
+    Raises
+    ------
+    BadException
+        Because you shouldn't have done that.
+
+    See Also
+    --------
+    otherfunc : relationship (optional)
+    newfunc : Relationship (optional), which could be fairly long, in which
+              case the line wraps here.
+    thirdfunc, fourthfunc, fifthfunc
+
+    Notes
+    -----
+    Notes about the implementation algorithm (if needed).
+
+    This can have multiple paragraphs.
+
+    You may include some math:
+
+    .. math:: X(e^{j\omega } ) = x(n)e^{ - j\omega n}
+
+    And even use a greek symbol like :math:`omega` inline.
+
+    References
+    ----------
+    Cite the relevant literature, e.g. [1]_.  You may also cite these
+    references in the notes section above.
+
+    .. [1] O. McNoleg, "The integration of GIS, remote sensing,
+       expert systems and adaptive co-kriging for environmental habitat
+       modelling of the Highland Haggis using object-oriented, fuzzy-logic
+       and neural-network techniques," Computers & Geosciences, vol. 22,
+       pp. 585-588, 1996.
+
+    Examples
+    --------
+    These are written in doctest format, and should illustrate how to
+    use the function.  Use the variables 'ds' for the dataset, 'pc' for
+    a plot collection, 'c' for a center, and 'L' for a vector.
+
+    >>> a=[1,2,3]
+    >>> print [x + 3 for x in a]
+    [4, 5, 6]
+    >>> print "a\n\nb"
+    a
+    b
+
+    """
+
+Variable Names and Enzo-isms
+----------------------------
+Avoid Enzo-isms.  This includes but is not limited to:
+
+ * Hard-coding parameter names that are the same as those in Enzo.  The
+   following translation table should be of some help.  Note that the
+   parameters are now properties on a ``Dataset`` subclass: you access them
+   like ds.refine_by .
+
+    - ``RefineBy `` => `` refine_by``
+    - ``TopGridRank `` => `` dimensionality``
+    - ``TopGridDimensions `` => `` domain_dimensions``
+    - ``InitialTime `` => `` current_time``
+    - ``DomainLeftEdge `` => `` domain_left_edge``
+    - ``DomainRightEdge `` => `` domain_right_edge``
+    - ``CurrentTimeIdentifier `` => `` unique_identifier``
+    - ``CosmologyCurrentRedshift `` => `` current_redshift``
+    - ``ComovingCoordinates `` => `` cosmological_simulation``
+    - ``CosmologyOmegaMatterNow `` => `` omega_matter``
+    - ``CosmologyOmegaLambdaNow `` => `` omega_lambda``
+    - ``CosmologyHubbleConstantNow `` => `` hubble_constant``
+
+ * Do not assume that the domain runs from 0 .. 1.  This is not true
+   everywhere.
+ * Variable names should be short but descriptive.
+ * No globals!

diff -r 5ba87edc33c2ed49987c8d7711b35ac1880dc6ee -r b1223126b44ba6e518cecb897837b73913ea62a3 CREDITS
--- a/CREDITS
+++ b/CREDITS
@@ -4,20 +4,30 @@
                 Tom Abel (tabel at stanford.edu)
                 Gabriel Altay (gabriel.altay at gmail.com)
                 Kenza Arraki (karraki at gmail.com)
+                Kirk Barrow (kssbarrow at gatech.edu)
+                Ricarda Beckmann (Ricarda.Beckmann at astro.ox.ac.uk)
                 Elliott Biondo (biondo at wisc.edu)
                 Alex Bogert (fbogert at ucsc.edu)
+                André-Patrick Bubel (code at andre-bubel.de)
                 Pengfei Chen (madcpf at gmail.com)
                 David Collins (dcollins4096 at gmail.com)
                 Brian Crosby (crosby.bd at gmail.com)
                 Andrew Cunningham (ajcunn at gmail.com)
                 Miguel de Val-Borro (miguel.deval at gmail.com)
+                Bili Dong (qobilidop at gmail.com)
+                Nicholas Earl (nchlsearl at gmail.com)
                 Hilary Egan (hilaryye at gmail.com)
+                Daniel Fenn (df11c at my.fsu.edu)
                 John Forces (jforbes at ucolick.org)
+                Adam Ginsburg (keflavich at gmail.com)
                 Sam Geen (samgeen at gmail.com)
                 Nathan Goldbaum (goldbaum at ucolick.org)
+                William Gray (graywilliamj at gmail.com)
                 Markus Haider (markus.haider at uibk.ac.at)
                 Eric Hallman (hallman13 at gmail.com)
                 Cameron Hummels (chummels at gmail.com)
+                Anni Järvenpää (anni.jarvenpaa at gmail.com)
+                Allyson Julian (astrohckr at gmail.com)
                 Christian Karch (chiffre at posteo.de)
                 Ben W. Keller (kellerbw at mcmaster.ca)
                 Ji-hoon Kim (me at jihoonkim.org)
@@ -25,11 +35,15 @@
                 Kacper Kowalik (xarthisius.kk at gmail.com)
                 Mark Krumholz (mkrumhol at ucsc.edu)
                 Michael Kuhlen (mqk at astro.berkeley.edu)
+                Meagan Lang (langmm.astro at gmail.com)
+                Doris Lee (dorislee at berkeley.edu)
                 Eve Lee (elee at cita.utoronto.ca)
                 Sam Leitner (sam.leitner at gmail.com)
+                Stuart Levy (salevy at illinois.edu)
                 Yuan Li (yuan at astro.columbia.edu)
                 Chris Malone (chris.m.malone at gmail.com)
                 Josh Maloney (joshua.moloney at colorado.edu)
+                Jonah Miller (jonah.maxwell.miller at gmail.com)
                 Chris Moody (cemoody at ucsc.edu)
                 Stuart Mumford (stuart at mumford.me.uk)
                 Andrew Myers (atmyers at astro.berkeley.edu)
@@ -44,6 +58,7 @@
                 Mark Richardson (Mark.L.Richardson at asu.edu)
                 Thomas Robitaille (thomas.robitaille at gmail.com)
                 Anna Rosen (rosen at ucolick.org)
+                Chuck Rozhon (rozhon2 at illinois.edu)
                 Douglas Rudd (drudd at uchicago.edu)
                 Anthony Scopatz (scopatz at gmail.com)
                 Noel Scudder (noel.scudder at stonybrook.edu)
@@ -59,6 +74,7 @@
                 Ji Suoqing (jisuoqing at gmail.com)
                 Elizabeth Tasker (tasker at astro1.sci.hokudai.ac.jp)
                 Benjamin Thompson (bthompson2090 at gmail.com)
+                Robert Thompson (rthompsonj at gmail.com)
                 Stephanie Tonnesen (stonnes at gmail.com)
                 Matthew Turk (matthewturk at gmail.com)
                 Rich Wagner (rwagner at physics.ucsd.edu)

diff -r 5ba87edc33c2ed49987c8d7711b35ac1880dc6ee -r b1223126b44ba6e518cecb897837b73913ea62a3 MANIFEST.in
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,14 +1,15 @@
-include distribute_setup.py README* CREDITS COPYING.txt CITATION
-recursive-include yt/gui/reason/html *.html *.png *.ico *.js *.gif *.css
-recursive-include yt *.py *.pyx *.pxd *.h README* *.txt LICENSE*
-recursive-include doc *.rst *.txt *.py *.ipynb *.png *.jpg *.css *.inc *.html
+include README* CREDITS COPYING.txt CITATION requirements.txt optional-requirements.txt
+include yt/visualization/mapserver/html/map_index.html
+include yt/visualization/mapserver/html/leaflet/*.css
+include yt/visualization/mapserver/html/leaflet/*.js
+include yt/visualization/mapserver/html/leaflet/images/*.png
+exclude scripts/pr_backport.py
+recursive-include yt *.py *.pyx *.pxd *.h README* *.txt LICENSE* *.cu
+recursive-include doc *.rst *.txt *.py *.ipynb *.png *.jpg *.css *.html
 recursive-include doc *.h *.c *.sh *.svgz *.pdf *.svg *.pyx
 include doc/README doc/activate doc/activate.csh doc/cheatsheet.tex
 include doc/extensions/README doc/Makefile
 prune doc/source/reference/api/generated
-prune doc/build/
+prune doc/build
 recursive-include yt/analysis_modules/halo_finding/rockstar *.py *.pyx
 prune yt/frontends/_skeleton
-prune tests
-graft yt/gui/reason/html/resources
-exclude clean.sh .hgchurn

diff -r 5ba87edc33c2ed49987c8d7711b35ac1880dc6ee -r b1223126b44ba6e518cecb897837b73913ea62a3 README
--- a/README
+++ b/README
@@ -21,3 +21,4 @@
 ways to help development, please visit our website.
 
 Enjoy!
+

diff -r 5ba87edc33c2ed49987c8d7711b35ac1880dc6ee -r b1223126b44ba6e518cecb897837b73913ea62a3 distribute_setup.py
--- a/distribute_setup.py
+++ /dev/null
@@ -1,541 +0,0 @@
-#!python
-"""Bootstrap distribute installation
-
-If you want to use setuptools in your package's setup.py, just include this
-file in the same directory with it, and add this to the top of your setup.py::
-
-    from distribute_setup import use_setuptools
-    use_setuptools()
-
-If you want to require a specific version of setuptools, set a download
-mirror, or use an alternate download directory, you can do so by supplying
-the appropriate options to ``use_setuptools()``.
-
-This file can also be run as a script to install or upgrade setuptools.
-"""
-import os
-import shutil
-import sys
-import time
-import fnmatch
-import tempfile
-import tarfile
-import optparse
-
-from distutils import log
-
-try:
-    from site import USER_SITE
-except ImportError:
-    USER_SITE = None
-
-try:
-    import subprocess
-
-    def _python_cmd(*args):
-        args = (sys.executable,) + args
-        return subprocess.call(args) == 0
-
-except ImportError:
-    # will be used for python 2.3
-    def _python_cmd(*args):
-        args = (sys.executable,) + args
-        # quoting arguments if windows
-        if sys.platform == 'win32':
-            def quote(arg):
-                if ' ' in arg:
-                    return '"%s"' % arg
-                return arg
-            args = [quote(arg) for arg in args]
-        return os.spawnl(os.P_WAIT, sys.executable, *args) == 0
-
-DEFAULT_VERSION = "0.6.32"
-DEFAULT_URL = "http://pypi.python.org/packages/source/d/distribute/"
-SETUPTOOLS_FAKED_VERSION = "0.6c11"
-
-SETUPTOOLS_PKG_INFO = """\
-Metadata-Version: 1.0
-Name: setuptools
-Version: %s
-Summary: xxxx
-Home-page: xxx
-Author: xxx
-Author-email: xxx
-License: xxx
-Description: xxx
-""" % SETUPTOOLS_FAKED_VERSION
-
-
-def _install(tarball, install_args=()):
-    # extracting the tarball
-    tmpdir = tempfile.mkdtemp()
-    log.warn('Extracting in %s', tmpdir)
-    old_wd = os.getcwd()
-    try:
-        os.chdir(tmpdir)
-        tar = tarfile.open(tarball)
-        _extractall(tar)
-        tar.close()
-
-        # going in the directory
-        subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
-        os.chdir(subdir)
-        log.warn('Now working in %s', subdir)
-
-        # installing
-        log.warn('Installing Distribute')
-        if not _python_cmd('setup.py', 'install', *install_args):
-            log.warn('Something went wrong during the installation.')
-            log.warn('See the error message above.')
-            # exitcode will be 2
-            return 2
-    finally:
-        os.chdir(old_wd)
-        shutil.rmtree(tmpdir)
-
-
-def _build_egg(egg, tarball, to_dir):
-    # extracting the tarball
-    tmpdir = tempfile.mkdtemp()
-    log.warn('Extracting in %s', tmpdir)
-    old_wd = os.getcwd()
-    try:
-        os.chdir(tmpdir)
-        tar = tarfile.open(tarball)
-        _extractall(tar)
-        tar.close()
-
-        # going in the directory
-        subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
-        os.chdir(subdir)
-        log.warn('Now working in %s', subdir)
-
-        # building an egg
-        log.warn('Building a Distribute egg in %s', to_dir)
-        _python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir)
-
-    finally:
-        os.chdir(old_wd)
-        shutil.rmtree(tmpdir)
-    # returning the result
-    log.warn(egg)
-    if not os.path.exists(egg):
-        raise IOError('Could not build the egg.')
-
-
-def _do_download(version, download_base, to_dir, download_delay):
-    egg = os.path.join(to_dir, 'distribute-%s-py%d.%d.egg'
-                       % (version, sys.version_info[0], sys.version_info[1]))
-    if not os.path.exists(egg):
-        tarball = download_setuptools(version, download_base,
-                                      to_dir, download_delay)
-        _build_egg(egg, tarball, to_dir)
-    sys.path.insert(0, egg)
-    import setuptools
-    setuptools.bootstrap_install_from = egg
-
-
-def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
-                   to_dir=os.curdir, download_delay=15, no_fake=True):
-    # making sure we use the absolute path
-    to_dir = os.path.abspath(to_dir)
-    was_imported = 'pkg_resources' in sys.modules or \
-        'setuptools' in sys.modules
-    try:
-        try:
-            import pkg_resources
-            if not hasattr(pkg_resources, '_distribute'):
-                if not no_fake:
-                    _fake_setuptools()
-                raise ImportError
-        except ImportError:
-            return _do_download(version, download_base, to_dir, download_delay)
-        try:
-            pkg_resources.require("distribute>=" + version)
-            return
-        except pkg_resources.VersionConflict:
-            e = sys.exc_info()[1]
-            if was_imported:
-                sys.stderr.write(
-                "The required version of distribute (>=%s) is not available,\n"
-                "and can't be installed while this script is running. Please\n"
-                "install a more recent version first, using\n"
-                "'easy_install -U distribute'."
-                "\n\n(Currently using %r)\n" % (version, e.args[0]))
-                sys.exit(2)
-            else:
-                del pkg_resources, sys.modules['pkg_resources']    # reload ok
-                return _do_download(version, download_base, to_dir,
-                                    download_delay)
-        except pkg_resources.DistributionNotFound:
-            return _do_download(version, download_base, to_dir,
-                                download_delay)
-    finally:
-        if not no_fake:
-            _create_fake_setuptools_pkg_info(to_dir)
-
-
-def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
-                        to_dir=os.curdir, delay=15):
-    """Download distribute from a specified location and return its filename
-
-    `version` should be a valid distribute version number that is available
-    as an egg for download under the `download_base` URL (which should end
-    with a '/'). `to_dir` is the directory where the egg will be downloaded.
-    `delay` is the number of seconds to pause before an actual download
-    attempt.
-    """
-    # making sure we use the absolute path
-    to_dir = os.path.abspath(to_dir)
-    try:
-        from urllib.request import urlopen
-    except ImportError:
-        from urllib2 import urlopen
-    tgz_name = "distribute-%s.tar.gz" % version
-    url = download_base + tgz_name
-    saveto = os.path.join(to_dir, tgz_name)
-    src = dst = None
-    if not os.path.exists(saveto):  # Avoid repeated downloads
-        try:
-            log.warn("Downloading %s", url)
-            src = urlopen(url)
-            # Read/write all in one block, so we don't create a corrupt file
-            # if the download is interrupted.
-            data = src.read()
-            dst = open(saveto, "wb")
-            dst.write(data)
-        finally:
-            if src:
-                src.close()
-            if dst:
-                dst.close()
-    return os.path.realpath(saveto)
-
-
-def _no_sandbox(function):
-    def __no_sandbox(*args, **kw):
-        try:
-            from setuptools.sandbox import DirectorySandbox
-            if not hasattr(DirectorySandbox, '_old'):
-                def violation(*args):
-                    pass
-                DirectorySandbox._old = DirectorySandbox._violation
-                DirectorySandbox._violation = violation
-                patched = True
-            else:
-                patched = False
-        except ImportError:
-            patched = False
-
-        try:
-            return function(*args, **kw)
-        finally:
-            if patched:
-                DirectorySandbox._violation = DirectorySandbox._old
-                del DirectorySandbox._old
-
-    return __no_sandbox
-
-
-def _patch_file(path, content):
-    """Will backup the file then patch it"""
-    existing_content = open(path).read()
-    if existing_content == content:
-        # already patched
-        log.warn('Already patched.')
-        return False
-    log.warn('Patching...')
-    _rename_path(path)
-    f = open(path, 'w')
-    try:
-        f.write(content)
-    finally:
-        f.close()
-    return True
-
-_patch_file = _no_sandbox(_patch_file)
-
-
-def _same_content(path, content):
-    return open(path).read() == content
-
-
-def _rename_path(path):
-    new_name = path + '.OLD.%s' % time.time()
-    log.warn('Renaming %s to %s', path, new_name)
-    os.rename(path, new_name)
-    return new_name
-
-
-def _remove_flat_installation(placeholder):
-    if not os.path.isdir(placeholder):
-        log.warn('Unknown installation at %s', placeholder)
-        return False
-    found = False
-    for file in os.listdir(placeholder):
-        if fnmatch.fnmatch(file, 'setuptools*.egg-info'):
-            found = True
-            break
-    if not found:
-        log.warn('Could not locate setuptools*.egg-info')
-        return
-
-    log.warn('Moving elements out of the way...')
-    pkg_info = os.path.join(placeholder, file)
-    if os.path.isdir(pkg_info):
-        patched = _patch_egg_dir(pkg_info)
-    else:
-        patched = _patch_file(pkg_info, SETUPTOOLS_PKG_INFO)
-
-    if not patched:
-        log.warn('%s already patched.', pkg_info)
-        return False
-    # now let's move the files out of the way
-    for element in ('setuptools', 'pkg_resources.py', 'site.py'):
-        element = os.path.join(placeholder, element)
-        if os.path.exists(element):
-            _rename_path(element)
-        else:
-            log.warn('Could not find the %s element of the '
-                     'Setuptools distribution', element)
-    return True
-
-_remove_flat_installation = _no_sandbox(_remove_flat_installation)
-
-
-def _after_install(dist):
-    log.warn('After install bootstrap.')
-    placeholder = dist.get_command_obj('install').install_purelib
-    _create_fake_setuptools_pkg_info(placeholder)
-
-
-def _create_fake_setuptools_pkg_info(placeholder):
-    if not placeholder or not os.path.exists(placeholder):
-        log.warn('Could not find the install location')
-        return
-    pyver = '%s.%s' % (sys.version_info[0], sys.version_info[1])
-    setuptools_file = 'setuptools-%s-py%s.egg-info' % \
-            (SETUPTOOLS_FAKED_VERSION, pyver)
-    pkg_info = os.path.join(placeholder, setuptools_file)
-    if os.path.exists(pkg_info):
-        log.warn('%s already exists', pkg_info)
-        return
-
-    log.warn('Creating %s', pkg_info)
-    try:
-        f = open(pkg_info, 'w')
-    except EnvironmentError:
-        log.warn("Don't have permissions to write %s, skipping", pkg_info)
-        return
-    try:
-        f.write(SETUPTOOLS_PKG_INFO)
-    finally:
-        f.close()
-
-    pth_file = os.path.join(placeholder, 'setuptools.pth')
-    log.warn('Creating %s', pth_file)
-    f = open(pth_file, 'w')
-    try:
-        f.write(os.path.join(os.curdir, setuptools_file))
-    finally:
-        f.close()
-
-_create_fake_setuptools_pkg_info = _no_sandbox(
-    _create_fake_setuptools_pkg_info
-)
-
-
-def _patch_egg_dir(path):
-    # let's check if it's already patched
-    pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO')
-    if os.path.exists(pkg_info):
-        if _same_content(pkg_info, SETUPTOOLS_PKG_INFO):
-            log.warn('%s already patched.', pkg_info)
-            return False
-    _rename_path(path)
-    os.mkdir(path)
-    os.mkdir(os.path.join(path, 'EGG-INFO'))
-    pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO')
-    f = open(pkg_info, 'w')
-    try:
-        f.write(SETUPTOOLS_PKG_INFO)
-    finally:
-        f.close()
-    return True
-
-_patch_egg_dir = _no_sandbox(_patch_egg_dir)
-
-
-def _before_install():
-    log.warn('Before install bootstrap.')
-    _fake_setuptools()
-
-
-def _under_prefix(location):
-    if 'install' not in sys.argv:
-        return True
-    args = sys.argv[sys.argv.index('install') + 1:]
-    for index, arg in enumerate(args):
-        for option in ('--root', '--prefix'):
-            if arg.startswith('%s=' % option):
-                top_dir = arg.split('root=')[-1]
-                return location.startswith(top_dir)
-            elif arg == option:
-                if len(args) > index:
-                    top_dir = args[index + 1]
-                    return location.startswith(top_dir)
-        if arg == '--user' and USER_SITE is not None:
-            return location.startswith(USER_SITE)
-    return True
-
-
-def _fake_setuptools():
-    log.warn('Scanning installed packages')
-    try:
-        import pkg_resources
-    except ImportError:
-        # we're cool
-        log.warn('Setuptools or Distribute does not seem to be installed.')
-        return
-    ws = pkg_resources.working_set
-    try:
-        setuptools_dist = ws.find(
-            pkg_resources.Requirement.parse('setuptools', replacement=False)
-            )
-    except TypeError:
-        # old distribute API
-        setuptools_dist = ws.find(
-            pkg_resources.Requirement.parse('setuptools')
-        )
-
-    if setuptools_dist is None:
-        log.warn('No setuptools distribution found')
-        return
-    # detecting if it was already faked
-    setuptools_location = setuptools_dist.location
-    log.warn('Setuptools installation detected at %s', setuptools_location)
-
-    # if --root or --preix was provided, and if
-    # setuptools is not located in them, we don't patch it
-    if not _under_prefix(setuptools_location):
-        log.warn('Not patching, --root or --prefix is installing Distribute'
-                 ' in another location')
-        return
-
-    # let's see if its an egg
-    if not setuptools_location.endswith('.egg'):
-        log.warn('Non-egg installation')
-        res = _remove_flat_installation(setuptools_location)
-        if not res:
-            return
-    else:
-        log.warn('Egg installation')
-        pkg_info = os.path.join(setuptools_location, 'EGG-INFO', 'PKG-INFO')
-        if (os.path.exists(pkg_info) and
-            _same_content(pkg_info, SETUPTOOLS_PKG_INFO)):
-            log.warn('Already patched.')
-            return
-        log.warn('Patching...')
-        # let's create a fake egg replacing setuptools one
-        res = _patch_egg_dir(setuptools_location)
-        if not res:
-            return
-    log.warn('Patching complete.')
-    _relaunch()
-
-
-def _relaunch():
-    log.warn('Relaunching...')
-    # we have to relaunch the process
-    # pip marker to avoid a relaunch bug
-    _cmd1 = ['-c', 'install', '--single-version-externally-managed']
-    _cmd2 = ['-c', 'install', '--record']
-    if sys.argv[:3] == _cmd1 or sys.argv[:3] == _cmd2:
-        sys.argv[0] = 'setup.py'
-    args = [sys.executable] + sys.argv
-    sys.exit(subprocess.call(args))
-
-
-def _extractall(self, path=".", members=None):
-    """Extract all members from the archive to the current working
-       directory and set owner, modification time and permissions on
-       directories afterwards. `path' specifies a different directory
-       to extract to. `members' is optional and must be a subset of the
-       list returned by getmembers().
-    """
-    import copy
-    import operator
-    from tarfile import ExtractError
-    directories = []
-
-    if members is None:
-        members = self
-
-    for tarinfo in members:
-        if tarinfo.isdir():
-            # Extract directories with a safe mode.
-            directories.append(tarinfo)
-            tarinfo = copy.copy(tarinfo)
-            tarinfo.mode = 448  # decimal for oct 0700
-        self.extract(tarinfo, path)
-
-    # Reverse sort directories.
-    if sys.version_info < (2, 4):
-        def sorter(dir1, dir2):
-            return cmp(dir1.name, dir2.name)
-        directories.sort(sorter)
-        directories.reverse()
-    else:
-        directories.sort(key=operator.attrgetter('name'), reverse=True)
-
-    # Set correct owner, mtime and filemode on directories.
-    for tarinfo in directories:
-        dirpath = os.path.join(path, tarinfo.name)
-        try:
-            self.chown(tarinfo, dirpath)
-            self.utime(tarinfo, dirpath)
-            self.chmod(tarinfo, dirpath)
-        except ExtractError:
-            e = sys.exc_info()[1]
-            if self.errorlevel > 1:
-                raise
-            else:
-                self._dbg(1, "tarfile: %s" % e)
-
-
-def _build_install_args(options):
-    """
-    Build the arguments to 'python setup.py install' on the distribute package
-    """
-    install_args = []
-    if options.user_install:
-        if sys.version_info < (2, 6):
-            log.warn("--user requires Python 2.6 or later")
-            raise SystemExit(1)
-        install_args.append('--user')
-    return install_args
-
-def _parse_args():
-    """
-    Parse the command line for options
-    """
-    parser = optparse.OptionParser()
-    parser.add_option(
-        '--user', dest='user_install', action='store_true', default=False,
-        help='install in user site package (requires Python 2.6 or later)')
-    parser.add_option(
-        '--download-base', dest='download_base', metavar="URL",
-        default=DEFAULT_URL,
-        help='alternative URL from where to download the distribute package')
-    options, args = parser.parse_args()
-    # positional arguments are ignored
-    return options
-
-def main(version=DEFAULT_VERSION):
-    """Install or upgrade setuptools and EasyInstall"""
-    options = _parse_args()
-    tarball = download_setuptools(download_base=options.download_base)
-    return _install(tarball, _build_install_args(options))
-
-if __name__ == '__main__':
-    sys.exit(main())

diff -r 5ba87edc33c2ed49987c8d7711b35ac1880dc6ee -r b1223126b44ba6e518cecb897837b73913ea62a3 doc/README
--- a/doc/README
+++ b/doc/README
@@ -7,4 +7,4 @@
 Because the documentation requires a number of dependencies, we provide
 pre-built versions online, accessible here:
 
-http://yt-project.org/docs/dev-3.0/
+http://yt-project.org/docs/dev/

diff -r 5ba87edc33c2ed49987c8d7711b35ac1880dc6ee -r b1223126b44ba6e518cecb897837b73913ea62a3 doc/coding_styleguide.txt
--- a/doc/coding_styleguide.txt
+++ /dev/null
@@ -1,80 +0,0 @@
-Style Guide for Coding in yt
-============================
-
-Coding Style Guide
-------------------
-
- * In general, follow PEP-8 guidelines.
-   http://www.python.org/dev/peps/pep-0008/
- * Classes are ConjoinedCapitals, methods and functions are
-   lowercase_with_underscores.
- * Use 4 spaces, not tabs, to represent indentation.
- * Line widths should not be more than 80 characters.
- * Do not use nested classes unless you have a very good reason to, such as
-   requiring a namespace or class-definition modification.  Classes should live
-   at the top level.  __metaclass__ is exempt from this.
- * Do not use unnecessary parenthesis in conditionals.  if((something) and
-   (something_else)) should be rewritten as if something and something_else.
-   Python is more forgiving than C.
- * Avoid copying memory when possible. For example, don't do 
-   "a = a.reshape(3,4)" when "a.shape = (3,4)" will do, and "a = a * 3" should
-   be "np.multiply(a, 3, a)".
- * In general, avoid all double-underscore method names: __something is usually
-   unnecessary.
- * When writing a subclass, use the super built-in to access the super class,
-   rather than explicitly. Ex: "super(SpecialGrid, self).__init__()" rather than
-   "SpecialGrid.__init__()".
- * Doc strings should describe input, output, behavior, and any state changes
-   that occur on an object.  See the file `doc/docstring_example.txt` for a
-   fiducial example of a docstring.
-
-API Guide
----------
-
- * Do not import "*" from anything other than "yt.funcs".
- * Internally, only import from source files directly -- instead of:
-
-   from yt.visualization.api import ProjectionPlot
-
-   do:
-
-   from yt.visualization.plot_window import ProjectionPlot
-
- * Numpy is to be imported as "np", after a long time of using "na".
- * Do not use too many keyword arguments.  If you have a lot of keyword
-   arguments, then you are doing too much in __init__ and not enough via
-   parameter setting.
- * In function arguments, place spaces before commas.  def something(a,b,c)
-   should be def something(a, b, c).
- * Don't create a new class to replicate the functionality of an old class --
-   replace the old class.  Too many options makes for a confusing user
-   experience.
- * Parameter files external to yt are a last resort.
- * The usage of the **kwargs construction should be avoided.  If they cannot
-   be avoided, they must be explained, even if they are only to be passed on to
-   a nested function.
-
-Variable Names and Enzo-isms
-----------------------------
-
- * Avoid Enzo-isms.  This includes but is not limited to:
-   * Hard-coding parameter names that are the same as those in Enzo.  The
-     following translation table should be of some help.  Note that the
-     parameters are now properties on a Dataset subclass: you access them
-     like ds.refine_by .
-     * RefineBy => refine_by
-     * TopGridRank => dimensionality
-     * TopGridDimensions => domain_dimensions
-     * InitialTime => current_time
-     * DomainLeftEdge => domain_left_edge
-     * DomainRightEdge => domain_right_edge
-     * CurrentTimeIdentifier => unique_identifier
-     * CosmologyCurrentRedshift => current_redshift
-     * ComovingCoordinates => cosmological_simulation
-     * CosmologyOmegaMatterNow => omega_matter
-     * CosmologyOmegaLambdaNow => omega_lambda
-     * CosmologyHubbleConstantNow => hubble_constant
-   * Do not assume that the domain runs from 0 .. 1.  This is not true
-     everywhere.
- * Variable names should be short but descriptive.
- * No globals!

diff -r 5ba87edc33c2ed49987c8d7711b35ac1880dc6ee -r b1223126b44ba6e518cecb897837b73913ea62a3 doc/docstring_example.txt
--- a/doc/docstring_example.txt
+++ b/doc/docstring_example.txt
@@ -1,86 +0,0 @@
-    r"""A one-line summary that does not use variable names or the
-    function name.
-
-    Several sentences providing an extended description. Refer to
-    variables using back-ticks, e.g. `var`.
-
-    Parameters
-    ----------
-    var1 : array_like
-        Array_like means all those objects -- lists, nested lists, etc. --
-        that can be converted to an array.  We can also refer to
-        variables like `var1`.
-    var2 : int
-        The type above can either refer to an actual Python type
-        (e.g. ``int``), or describe the type of the variable in more
-        detail, e.g. ``(N,) ndarray`` or ``array_like``.
-    Long_variable_name : {'hi', 'ho'}, optional
-        Choices in brackets, default first when optional.
-
-    Returns
-    -------
-    describe : type
-        Explanation
-    output : type
-        Explanation
-    tuple : type
-        Explanation
-    items : type
-        even more explaining
-
-    Other Parameters
-    ----------------
-    only_seldom_used_keywords : type
-        Explanation
-    common_parameters_listed_above : type
-        Explanation
-
-    Raises
-    ------
-    BadException
-        Because you shouldn't have done that.
-
-    See Also
-    --------
-    otherfunc : relationship (optional)
-    newfunc : Relationship (optional), which could be fairly long, in which
-              case the line wraps here.
-    thirdfunc, fourthfunc, fifthfunc
-
-    Notes
-    -----
-    Notes about the implementation algorithm (if needed).
-
-    This can have multiple paragraphs.
-
-    You may include some math:
-
-    .. math:: X(e^{j\omega } ) = x(n)e^{ - j\omega n}
-
-    And even use a greek symbol like :math:`omega` inline.
-
-    References
-    ----------
-    Cite the relevant literature, e.g. [1]_.  You may also cite these
-    references in the notes section above.
-
-    .. [1] O. McNoleg, "The integration of GIS, remote sensing,
-       expert systems and adaptive co-kriging for environmental habitat
-       modelling of the Highland Haggis using object-oriented, fuzzy-logic
-       and neural-network techniques," Computers & Geosciences, vol. 22,
-       pp. 585-588, 1996.
-
-    Examples
-    --------
-    These are written in doctest format, and should illustrate how to
-    use the function.  Use the variables 'ds' for the dataset, 'pc' for
-    a plot collection, 'c' for a center, and 'L' for a vector. 
-
-    >>> a=[1,2,3]
-    >>> print [x + 3 for x in a]
-    [4, 5, 6]
-    >>> print "a\n\nb"
-    a
-    b
-
-    """

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

https://bitbucket.org/yt_analysis/yt/commits/d94ce531f130/
Changeset:   d94ce531f130
Branch:      yt
User:        MatthewTurk
Date:        2016-02-03 16:20:54+00:00
Summary:     Adding forward compat line here.
Affected #:  1 file

diff -r b1223126b44ba6e518cecb897837b73913ea62a3 -r d94ce531f1305d9b4e10aa4f9670f4ade752625f yt/visualization/volume_rendering/interactive_loop.py
--- a/yt/visualization/volume_rendering/interactive_loop.py
+++ b/yt/visualization/volume_rendering/interactive_loop.py
@@ -57,6 +57,7 @@
 
     glfw.WindowHint(glfw.CONTEXT_VERSION_MAJOR, 3)
     glfw.WindowHint(glfw.CONTEXT_VERSION_MINOR, 3)
+    glfw.WindowHint(glfw.OPENGL_FORWARD_COMPAT, True)
     glfw.WindowHint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)
     window = glfw.CreateWindow(800, 600, 'vol_render')
     if not window:


https://bitbucket.org/yt_analysis/yt/commits/ad99acadb08d/
Changeset:   ad99acadb08d
Branch:      yt
User:        MatthewTurk
Date:        2016-02-03 16:41:08+00:00
Summary:     Adding a simple cookbook recipe that loads fake data and renders.
Affected #:  1 file

diff -r d94ce531f1305d9b4e10aa4f9670f4ade752625f -r ad99acadb08dee1146031fa6a743c8021b7aa9aa doc/source/cookbook/opengl_vr.py
--- /dev/null
+++ b/doc/source/cookbook/opengl_vr.py
@@ -0,0 +1,28 @@
+import yt
+import numpy as np
+from yt.visualization.volume_rendering.interactive_loop import \
+    start_context, SceneGraph, BlockCollection, Camera, start_loop
+
+start_context()
+
+scene = SceneGraph()
+collection = BlockCollection()
+
+N = 64
+x, y, z = np.mgrid[0:1:N*1j, 0:1:N*1j, 0:1:N*1j]
+c = [-0.05, -0.05, -0.05]
+oor2 = ((x-c[0])**2 + (y-c[1])**2 + (z-c[2])**2)**-0.5
+np.clip(oor2, 1e-6, 1e60, oor2)
+data = {'x_field': 10**x, 'y_field': 10**y, 'z_field': 10**z, 'sphere':oor2}
+
+ds = yt.load_uniform_grid(data, [N, N, N], 1.0, nprocs=1)
+
+dd = ds.all_data()
+collection.add_data(dd, "z_field")
+
+scene.add_collection(collection)
+
+position = (-5.0, -5.0, 5.0)
+c = Camera(position = position, focus = ds.domain_center)
+
+start_loop(scene, c)


https://bitbucket.org/yt_analysis/yt/commits/a4a1db33b5a9/
Changeset:   a4a1db33b5a9
Branch:      yt
User:        MatthewTurk
Date:        2016-02-03 16:54:36+00:00
Summary:     Move the texture parameters to after we bind and before we load them.
Affected #:  1 file

diff -r ad99acadb08dee1146031fa6a743c8021b7aa9aa -r a4a1db33b5a9db6b977e58b6f744b8bc13033254 yt/visualization/volume_rendering/interactive_vr.py
--- a/yt/visualization/volume_rendering/interactive_vr.py
+++ b/yt/visualization/volume_rendering/interactive_vr.py
@@ -227,9 +227,6 @@
             if len(self.blocks) == 1:
                 self.gl_texture_names = [self.gl_texture_names]
             glPixelStorei(GL_UNPACK_ALIGNMENT, 1)
-            glTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)
-            glTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)
-            glTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE)
             glTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
             glTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER,
                     GL_LINEAR)
@@ -247,6 +244,9 @@
             n_data = block.my_data[0].copy(order="F").astype("float32")
             n_data = (n_data - self.min_val) / (self.max_val - self.min_val)
             glBindTexture(GL_TEXTURE_3D, texture_name)
+            glTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)
+            glTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)
+            glTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE)
             glTexStorage3D(GL_TEXTURE_3D, 1, GL_R32F,
                 *block.my_data[0].shape)
             glTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 0, dx, dy, dz,


https://bitbucket.org/yt_analysis/yt/commits/db048f316416/
Changeset:   db048f316416
Branch:      yt
User:        MatthewTurk
Date:        2016-02-03 20:32:10+00:00
Summary:     Fix viewport import.
Affected #:  1 file

diff -r a4a1db33b5a9db6b977e58b6f744b8bc13033254 -r db048f316416b1a7a54c33cb6e83514fdfcc1575 yt/visualization/volume_rendering/interactive_loop.py
--- a/yt/visualization/volume_rendering/interactive_loop.py
+++ b/yt/visualization/volume_rendering/interactive_loop.py
@@ -1,5 +1,6 @@
 import cyglfw3 as glfw
 import numpy as np
+from OpenGL.GL import glViewport
 
 from interactive_vr import BlockCollection, SceneGraph, Camera
 


https://bitbucket.org/yt_analysis/yt/commits/e9e6d8c94c61/
Changeset:   e9e6d8c94c61
Branch:      yt
User:        MatthewTurk
Date:        2016-02-03 20:36:18+00:00
Summary:     Always iterate at least once to see if we're in a box.
Affected #:  1 file

diff -r db048f316416b1a7a54c33cb6e83514fdfcc1575 -r e9e6d8c94c610a0db8c88dadd756d6c107ad494a yt/visualization/volume_rendering/shaders/max_intensity_frag.glsl
--- a/yt/visualization/volume_rendering/shaders/max_intensity_frag.glsl
+++ b/yt/visualization/volume_rendering/shaders/max_intensity_frag.glsl
@@ -14,8 +14,8 @@
 
 bool within_bb(vec3 pos)
 {
-    bvec3 left =  greaterThanEqual(pos + .01, left_edge);
-    bvec3 right = lessThanEqual(pos - .01, right_edge);
+    bvec3 left =  greaterThanEqual(pos, left_edge);
+    bvec3 right = lessThanEqual(pos, right_edge);
     return all(left) && all(right);
 }
 
@@ -42,7 +42,8 @@
 
     vec3 tex_curr_pos = vec3(0.0);
     vec3 range = right_edge - left_edge;
-    while (within_bb(ray_position)) {
+    bool ray_in_bb = true;
+    while (ray_in_bb) {
         tex_curr_pos = (ray_position - left_edge)/range;
 
         vec3 tex_sample = texture(ds_tex, tex_curr_pos).rgb;
@@ -51,6 +52,7 @@
         }
 
         ray_position += dir * step_size;
+        ray_in_bb = within_bb(ray_position);
     }
     output_color = vec4(curr_color.rrr, 1.0);
 }


https://bitbucket.org/yt_analysis/yt/commits/d4f9b609dfc3/
Changeset:   d4f9b609dfc3
Branch:      yt
User:        MatthewTurk
Date:        2016-02-04 00:09:23+00:00
Summary:     Switch vertex order, fix culling.
Affected #:  1 file

diff -r e9e6d8c94c610a0db8c88dadd756d6c107ad494a -r d4f9b609dfc37bd269273d4475b065db0294ece4 yt/visualization/volume_rendering/interactive_vr.py
--- a/yt/visualization/volume_rendering/interactive_vr.py
+++ b/yt/visualization/volume_rendering/interactive_vr.py
@@ -8,44 +8,44 @@
 
 bbox_vertices = np.array(
       [[ 0.,  0.,  0.,  1.],
+       [ 0.,  0.,  1.,  1.],
+       [ 0.,  1.,  1.,  1.],
+       [ 1.,  1.,  0.,  1.],
+       [ 0.,  0.,  0.,  1.],
+       [ 0.,  1.,  0.,  1.],
+       [ 1.,  0.,  1.,  1.],
+       [ 0.,  0.,  0.,  1.],
+       [ 1.,  0.,  0.,  1.],
        [ 1.,  1.,  0.,  1.],
        [ 1.,  0.,  0.,  1.],
        [ 0.,  0.,  0.,  1.],
+       [ 0.,  0.,  0.,  1.],
+       [ 0.,  1.,  1.,  1.],
        [ 0.,  1.,  0.,  1.],
-       [ 1.,  1.,  0.,  1.],
-       [ 0.,  0.,  0.,  1.],
-       [ 0.,  0.,  1.,  1.],
-       [ 0.,  1.,  0.,  1.],
-       [ 0.,  1.,  0.,  1.],
-       [ 0.,  0.,  1.,  1.],
-       [ 0.,  1.,  1.,  1.],
-       [ 0.,  0.,  0.,  1.],
-       [ 1.,  0.,  0.,  1.],
-       [ 1.,  0.,  1.,  1.],
-       [ 0.,  0.,  0.,  1.],
        [ 1.,  0.,  1.,  1.],
        [ 0.,  0.,  1.,  1.],
-       [ 0.,  1.,  0.,  1.],
+       [ 0.,  0.,  0.,  1.],
+       [ 0.,  1.,  1.,  1.],
+       [ 0.,  0.,  1.,  1.],
+       [ 1.,  0.,  1.,  1.],
+       [ 1.,  1.,  1.,  1.],
+       [ 1.,  0.,  0.,  1.],
+       [ 1.,  1.,  0.,  1.],
+       [ 1.,  0.,  0.,  1.],
+       [ 1.,  1.,  1.,  1.],
+       [ 1.,  0.,  1.,  1.],
        [ 1.,  1.,  1.,  1.],
        [ 1.,  1.,  0.,  1.],
        [ 0.,  1.,  0.,  1.],
+       [ 1.,  1.,  1.,  1.],
+       [ 0.,  1.,  0.,  1.],
        [ 0.,  1.,  1.,  1.],
        [ 1.,  1.,  1.,  1.],
-       [ 1.,  0.,  0.,  1.],
-       [ 1.,  1.,  0.,  1.],
-       [ 1.,  1.,  1.,  1.],
-       [ 1.,  0.,  0.,  1.],
-       [ 1.,  1.,  1.,  1.],
-       [ 1.,  0.,  1.,  1.],
-       [ 0.,  0.,  1.,  1.],
-       [ 1.,  0.,  1.,  1.],
-       [ 1.,  1.,  1.,  1.],
-       [ 0.,  0.,  1.,  1.],
-       [ 1.,  1.,  1.,  1.],
-       [ 0.,  1.,  1.,  1.]])
+       [ 0.,  1.,  1.,  1.],
+       [ 1.,  0.,  1.,  1.]])
 
 class Camera:
-    def __init__(self, position = (0, 0, 0), fov = 60.0, near_plane = 1.0,
+    def __init__(self, position = (0, 0, 0), fov = 60.0, near_plane = 0.01,
             far_plane = 20, aspect_ratio = 8.0 / 6.0, focus = (0, 0, 0),
             up = (0, 0, 1)):
         self.position = np.array(position)
@@ -91,6 +91,7 @@
 
         self.camera = None
         glEnable(GL_CULL_FACE)
+        glCullFace(GL_BACK)
         glEnable(GL_DEPTH_TEST)
         glDepthFunc(GL_LESS)
 


https://bitbucket.org/yt_analysis/yt/commits/1c8c6154a4e6/
Changeset:   1c8c6154a4e6
Branch:      yt
User:        atmyers
Date:        2016-02-03 18:11:52+00:00
Summary:     use atan2 function instead of atan to get the right quadrant
Affected #:  1 file

diff -r ad99acadb08dee1146031fa6a743c8021b7aa9aa -r 1c8c6154a4e6577f806ba11f9f949ee3103d25d6 yt/visualization/volume_rendering/interactive_vr.py
--- a/yt/visualization/volume_rendering/interactive_vr.py
+++ b/yt/visualization/volume_rendering/interactive_vr.py
@@ -65,8 +65,8 @@
                 self.far_plane)
     def update_position(self, theta, phi):
         rho = np.linalg.norm(self.position)
-        curr_theta = np.arctan( self.position[1] / self.position[0] )
-        curr_phi = np.arctan( np.linalg.norm(self.position[:2]) / self.position[2])
+        curr_theta = np.arctan2( self.position[1], self.position[0] )
+        curr_phi = np.arctan2( np.linalg.norm(self.position[:2]), self.position[2])
 
         curr_theta += theta
         curr_phi += phi


https://bitbucket.org/yt_analysis/yt/commits/3d752dabe169/
Changeset:   3d752dabe169
Branch:      yt
User:        atmyers
Date:        2016-02-03 21:45:23+00:00
Summary:     use "is not" instrad of !=
Affected #:  1 file

diff -r 1c8c6154a4e6577f806ba11f9f949ee3103d25d6 -r 3d752dabe1696ce1e6edd683ceecd9fe7d511335 yt/visualization/volume_rendering/interactive_vr.py
--- a/yt/visualization/volume_rendering/interactive_vr.py
+++ b/yt/visualization/volume_rendering/interactive_vr.py
@@ -141,10 +141,10 @@
 
         # Now we set up our buffer
         vert = np.concatenate(vert)
-        if self.gl_buffer_name != None:
+        if self.gl_buffer_name is not None:
             glDeleteBuffers(1, [self.gl_buffer_name])
         self.gl_buffer_name = glGenBuffers(1)
-        if self.gl_vao_name != None:
+        if self.gl_vao_name is not None:
             glDeleteVertexArrays(1, [self.gl_vao_name])
         self.gl_vao_name = glGenVertexArrays(1)
         glBindBuffer(GL_ARRAY_BUFFER, self.gl_buffer_name)


https://bitbucket.org/yt_analysis/yt/commits/87da59529f2d/
Changeset:   87da59529f2d
Branch:      yt
User:        atmyers
Date:        2016-02-03 21:45:35+00:00
Summary:     merging from Matt
Affected #:  3 files

diff -r 3d752dabe1696ce1e6edd683ceecd9fe7d511335 -r 87da59529f2d6da3257bce8050f6a621ee641500 yt/visualization/volume_rendering/interactive_loop.py
--- a/yt/visualization/volume_rendering/interactive_loop.py
+++ b/yt/visualization/volume_rendering/interactive_loop.py
@@ -1,5 +1,6 @@
 import cyglfw3 as glfw
 import numpy as np
+from OpenGL.GL import glViewport
 
 from interactive_vr import BlockCollection, SceneGraph, Camera
 

diff -r 3d752dabe1696ce1e6edd683ceecd9fe7d511335 -r 87da59529f2d6da3257bce8050f6a621ee641500 yt/visualization/volume_rendering/interactive_vr.py
--- a/yt/visualization/volume_rendering/interactive_vr.py
+++ b/yt/visualization/volume_rendering/interactive_vr.py
@@ -227,9 +227,6 @@
             if len(self.blocks) == 1:
                 self.gl_texture_names = [self.gl_texture_names]
             glPixelStorei(GL_UNPACK_ALIGNMENT, 1)
-            glTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)
-            glTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)
-            glTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE)
             glTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
             glTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER,
                     GL_LINEAR)
@@ -247,6 +244,9 @@
             n_data = block.my_data[0].copy(order="F").astype("float32")
             n_data = (n_data - self.min_val) / (self.max_val - self.min_val)
             glBindTexture(GL_TEXTURE_3D, texture_name)
+            glTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)
+            glTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)
+            glTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE)
             glTexStorage3D(GL_TEXTURE_3D, 1, GL_R32F,
                 *block.my_data[0].shape)
             glTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 0, dx, dy, dz,

diff -r 3d752dabe1696ce1e6edd683ceecd9fe7d511335 -r 87da59529f2d6da3257bce8050f6a621ee641500 yt/visualization/volume_rendering/shaders/max_intensity_frag.glsl
--- a/yt/visualization/volume_rendering/shaders/max_intensity_frag.glsl
+++ b/yt/visualization/volume_rendering/shaders/max_intensity_frag.glsl
@@ -14,8 +14,8 @@
 
 bool within_bb(vec3 pos)
 {
-    bvec3 left =  greaterThanEqual(pos + .01, left_edge);
-    bvec3 right = lessThanEqual(pos - .01, right_edge);
+    bvec3 left =  greaterThanEqual(pos, left_edge);
+    bvec3 right = lessThanEqual(pos, right_edge);
     return all(left) && all(right);
 }
 
@@ -42,7 +42,8 @@
 
     vec3 tex_curr_pos = vec3(0.0);
     vec3 range = right_edge - left_edge;
-    while (within_bb(ray_position)) {
+    bool ray_in_bb = true;
+    while (ray_in_bb) {
         tex_curr_pos = (ray_position - left_edge)/range;
 
         vec3 tex_sample = texture(ds_tex, tex_curr_pos).rgb;
@@ -51,6 +52,7 @@
         }
 
         ray_position += dir * step_size;
+        ray_in_bb = within_bb(ray_position);
     }
     output_color = vec4(curr_color.rrr, 1.0);
 }


https://bitbucket.org/yt_analysis/yt/commits/ece7fa334faf/
Changeset:   ece7fa334faf
Branch:      yt
User:        atmyers
Date:        2016-02-03 21:53:12+00:00
Summary:     some helper functions for doing quaternion math.
Affected #:  2 files

diff -r 87da59529f2d6da3257bce8050f6a621ee641500 -r ece7fa334faf9a2c552eb0303753f8ecf73edc92 yt/utilities/math_utils.py
--- a/yt/utilities/math_utils.py
+++ b/yt/utilities/math_utils.py
@@ -1001,6 +1001,50 @@
 
     return R
 
+def quaternion_mult(q1, q2):
+    '''
+
+    Multiply two quaternions. The inputs are 4-component numpy arrays
+    in the order [w, x, y, z].
+
+    '''
+    w = q1[0]*q2[0] - q1[1]*q2[1] - q1[2]*q2[2] - q1[3]*q2[3]
+    x = q1[0]*q2[1] + q1[1]*q2[0] + q1[2]*q2[3] - q1[3]*q2[2]
+    y = q1[0]*q2[2] + q1[2]*q2[0] + q1[3]*q2[1] - q1[1]*q2[3]
+    z = q1[0]*q2[3] + q1[3]*q2[0] + q1[1]*q2[2] - q1[2]*q2[1]
+    return np.array([w, x, y, z])
+
+def quaternion_to_rotation_matrix(quaternion):
+    """
+
+    This converts a quaternion representation of on orientation to
+    a rotation matrix. The input is a 4-component numpy array in
+    the order [w, x, y, z], and the output is a 3x3 matrix stored
+    as a 2D numpy array.
+
+    """
+
+    w = quaternion[0]
+    x = quaternion[1]
+    y = quaternion[2]
+    z = quaternion[3]
+
+    R = np.empty((3, 3), dtype=np.float64)
+
+    R[0][0] = 1.0 - 2.0*y**2 - 2.0*z**2
+    R[0][1] = 2.0*x*y + 2.0*w*z
+    R[0][2] = 2.0*x*z - 2.0*w*y
+
+    R[1][0] = 2.0*x*y - 2.0*w*z
+    R[1][1] = 1.0 - 2.0*x**2 - 2.0*z**2
+    R[1][2] = 2.0*y*z + 2.0*w*x
+
+    R[2][0] = 2.0*x*z + 2.0*w*y
+    R[2][1] = 2.0*y*z - 2.0*w*x
+    R[2][2] = 1.0 - 2.0*x**2 - 2.0*y**2
+
+    return R
+
 def get_ortho_basis(normal):
     xprime = np.cross([0.0,1.0,0.0],normal)
     if np.sum(xprime) == 0: xprime = np.array([0.0, 0.0, 1.0])

diff -r 87da59529f2d6da3257bce8050f6a621ee641500 -r ece7fa334faf9a2c552eb0303753f8ecf73edc92 yt/visualization/volume_rendering/interactive_vr.py
--- a/yt/visualization/volume_rendering/interactive_vr.py
+++ b/yt/visualization/volume_rendering/interactive_vr.py
@@ -3,8 +3,13 @@
 from OpenGL.GLUT import *
 
 import numpy as np
-from yt.utilities.math_utils import get_translate_matrix, get_scale_matrix, \
-    get_lookat_matrix, get_perspective_matrix
+from yt.utilities.math_utils import \
+    get_translate_matrix, \
+    get_scale_matrix, \
+    get_lookat_matrix, \
+    get_perspective_matrix, \
+    
+
 
 bbox_vertices = np.array(
       [[ 0.,  0.,  0.,  1.],


https://bitbucket.org/yt_analysis/yt/commits/de0eedb8d552/
Changeset:   de0eedb8d552
Branch:      yt
User:        atmyers
Date:        2016-02-04 00:06:33+00:00
Summary:     adding Trackball Camera to yt.
Affected #:  2 files

diff -r ece7fa334faf9a2c552eb0303753f8ecf73edc92 -r de0eedb8d552c25f36a3ee82dfc9c56c844591dc yt/visualization/volume_rendering/interactive_loop.py
--- a/yt/visualization/volume_rendering/interactive_loop.py
+++ b/yt/visualization/volume_rendering/interactive_loop.py
@@ -2,7 +2,7 @@
 import numpy as np
 from OpenGL.GL import glViewport
 
-from interactive_vr import BlockCollection, SceneGraph, Camera
+from interactive_vr import BlockCollection, SceneGraph, TrackballCamera
 
 draw = True
 start = None
@@ -31,7 +31,7 @@
         norm_y = 1.0 - 2.0 * end_screen[1] / window_size[1]
         end = (norm_x, norm_y)
 
-        c.update_position( (end[0] - start[0]), (end[1] - start[1]))
+        c.update_orientation(start[0], start[1], end[0], end[1])
 
         rotation = False
         draw = True
@@ -90,11 +90,13 @@
             norm_y = 1.0 - 2.0 * new_end_screen[1] / window_size[1]
             new_end = (norm_x, norm_y)
 
-            c.update_position( (new_end[0] - start[0]),
-                    (new_end[1] - start[1]))
+            c.update_orientation(start[0], start[1], new_end[0], new_end[1])
+
             start = new_end
             draw = True
 
+        c.compute_matrices()
+
         if draw:
             scene.set_camera(c)
             scene.render()

diff -r ece7fa334faf9a2c552eb0303753f8ecf73edc92 -r de0eedb8d552c25f36a3ee82dfc9c56c844591dc yt/visualization/volume_rendering/interactive_vr.py
--- a/yt/visualization/volume_rendering/interactive_vr.py
+++ b/yt/visualization/volume_rendering/interactive_vr.py
@@ -8,9 +8,9 @@
     get_scale_matrix, \
     get_lookat_matrix, \
     get_perspective_matrix, \
+    quaternion_mult, \
+    quaternion_to_rotation_matrix
     
-
-
 bbox_vertices = np.array(
       [[ 0.,  0.,  0.,  1.],
        [ 1.,  1.,  0.,  1.],
@@ -49,6 +49,83 @@
        [ 1.,  1.,  1.,  1.],
        [ 0.,  1.,  1.,  1.]])
 
+
+class TrackballCamera(object):
+    def __init__(self, 
+                 position=np.array([0.0, 0.0, 1.0]),
+                 focus=np.array([0.0, 0.0, 0.0]),
+                 fov=45.0, near_plane=1.0, far_plane=20.0, aspect_ratio=8.0/6.0):
+        self.view_matrix = np.zeros((4, 4), dtype=np.float32)
+        self.proj_matrix = np.zeros((4, 4), dtype=np.float32)
+        self.position = np.array(position)
+        self.focus = np.array(focus)
+        self.fov = fov
+        self.near_plane = near_plane
+        self.far_plane = far_plane
+        self.aspect_ratio = aspect_ratio
+        self.orientation = np.array([1.0, 0.0, 0.0, 0.0])
+
+    def _map_to_surface(self, mouse_x, mouse_y):
+        # right now this just maps to the surface of
+        # the unit sphere
+        x, y = mouse_x, mouse_y
+        mag = np.sqrt(x*x + y*y)
+        if (mag > 1.0):
+            x /= mag
+            y /= mag
+            z = 0.0
+        else:
+            z = np.sqrt(1.0 - mag**2)
+        return np.array([x, y, z])
+
+    def update_orientation(self, start_x, start_y, end_x, end_y):
+        old = self._map_to_surface(start_x, start_y)
+        new = self._map_to_surface(end_x, end_y)
+
+        # dot product
+        w = old[0]*new[0] + old[1]*new[1] + old[2]*new[2]
+
+        # cross product
+        x = old[1]*new[2] - old[2]*new[1]
+        y = old[2]*new[0] - old[0]*new[2]
+        z = old[0]*new[1] - old[1]*new[0]
+
+        q = np.array([w, x, y, z])
+
+        #renormalize to prevent floating point issues
+        mag = np.sqrt(w**2 + x**2 + y**2 + z**2)
+        q /= mag
+
+        self.orientation = quaternion_mult(self.orientation, q)
+
+    def compute_matrices(self):
+        rotation_matrix = quaternion_to_rotation_matrix(self.orientation)
+        self.position = np.dot(rotation_matrix, self.position)
+
+        self.view_matrix = np.zeros ( (4, 4), dtype = 'float32', order = 'C')
+        
+        # the upper-left 3x3 submatrix is the rotation matrix
+        self.view_matrix[0:3,0:3] = rotation_matrix
+
+        # fill in remaining components
+        self.view_matrix[2][3] = -np.linalg.norm(self.position)
+        self.view_matrix[3][3] = 1.0
+
+        self.projection_matrix = get_perspective_matrix(self.fov,
+                                                        self.aspect_ratio,
+                                                        self.near_plane,
+                                                        self.far_plane)
+
+    def get_viewpoint(self):
+        return self.position
+
+    def get_view_matrix(self):
+        return self.view_matrix
+
+    def get_projection_matrix(self):
+        return self.projection_matrix
+
+
 class Camera:
     def __init__(self, position = (0, 0, 0), fov = 60.0, near_plane = 1.0,
             far_plane = 20, aspect_ratio = 8.0 / 6.0, focus = (0, 0, 0),


https://bitbucket.org/yt_analysis/yt/commits/3b13744c5031/
Changeset:   3b13744c5031
Branch:      yt
User:        MatthewTurk
Date:        2016-02-04 00:38:58+00:00
Summary:     Merged in atmyers/yt (pull request #99)

Adding a Trackball Camera
Affected #:  3 files

diff -r d4f9b609dfc37bd269273d4475b065db0294ece4 -r 3b13744c503164e3a3b6086382313684560dd74e yt/utilities/math_utils.py
--- a/yt/utilities/math_utils.py
+++ b/yt/utilities/math_utils.py
@@ -1001,6 +1001,50 @@
 
     return R
 
+def quaternion_mult(q1, q2):
+    '''
+
+    Multiply two quaternions. The inputs are 4-component numpy arrays
+    in the order [w, x, y, z].
+
+    '''
+    w = q1[0]*q2[0] - q1[1]*q2[1] - q1[2]*q2[2] - q1[3]*q2[3]
+    x = q1[0]*q2[1] + q1[1]*q2[0] + q1[2]*q2[3] - q1[3]*q2[2]
+    y = q1[0]*q2[2] + q1[2]*q2[0] + q1[3]*q2[1] - q1[1]*q2[3]
+    z = q1[0]*q2[3] + q1[3]*q2[0] + q1[1]*q2[2] - q1[2]*q2[1]
+    return np.array([w, x, y, z])
+
+def quaternion_to_rotation_matrix(quaternion):
+    """
+
+    This converts a quaternion representation of on orientation to
+    a rotation matrix. The input is a 4-component numpy array in
+    the order [w, x, y, z], and the output is a 3x3 matrix stored
+    as a 2D numpy array.
+
+    """
+
+    w = quaternion[0]
+    x = quaternion[1]
+    y = quaternion[2]
+    z = quaternion[3]
+
+    R = np.empty((3, 3), dtype=np.float64)
+
+    R[0][0] = 1.0 - 2.0*y**2 - 2.0*z**2
+    R[0][1] = 2.0*x*y + 2.0*w*z
+    R[0][2] = 2.0*x*z - 2.0*w*y
+
+    R[1][0] = 2.0*x*y - 2.0*w*z
+    R[1][1] = 1.0 - 2.0*x**2 - 2.0*z**2
+    R[1][2] = 2.0*y*z + 2.0*w*x
+
+    R[2][0] = 2.0*x*z + 2.0*w*y
+    R[2][1] = 2.0*y*z - 2.0*w*x
+    R[2][2] = 1.0 - 2.0*x**2 - 2.0*y**2
+
+    return R
+
 def get_ortho_basis(normal):
     xprime = np.cross([0.0,1.0,0.0],normal)
     if np.sum(xprime) == 0: xprime = np.array([0.0, 0.0, 1.0])

diff -r d4f9b609dfc37bd269273d4475b065db0294ece4 -r 3b13744c503164e3a3b6086382313684560dd74e yt/visualization/volume_rendering/interactive_loop.py
--- a/yt/visualization/volume_rendering/interactive_loop.py
+++ b/yt/visualization/volume_rendering/interactive_loop.py
@@ -2,7 +2,7 @@
 import numpy as np
 from OpenGL.GL import glViewport
 
-from interactive_vr import BlockCollection, SceneGraph, Camera
+from interactive_vr import BlockCollection, SceneGraph, TrackballCamera
 
 draw = True
 start = None
@@ -31,7 +31,7 @@
         norm_y = 1.0 - 2.0 * end_screen[1] / window_size[1]
         end = (norm_x, norm_y)
 
-        c.update_position( (end[0] - start[0]), (end[1] - start[1]))
+        c.update_orientation(start[0], start[1], end[0], end[1])
 
         rotation = False
         draw = True
@@ -90,11 +90,13 @@
             norm_y = 1.0 - 2.0 * new_end_screen[1] / window_size[1]
             new_end = (norm_x, norm_y)
 
-            c.update_position( (new_end[0] - start[0]),
-                    (new_end[1] - start[1]))
+            c.update_orientation(start[0], start[1], new_end[0], new_end[1])
+
             start = new_end
             draw = True
 
+        c.compute_matrices()
+
         if draw:
             scene.set_camera(c)
             scene.render()

diff -r d4f9b609dfc37bd269273d4475b065db0294ece4 -r 3b13744c503164e3a3b6086382313684560dd74e yt/visualization/volume_rendering/interactive_vr.py
--- a/yt/visualization/volume_rendering/interactive_vr.py
+++ b/yt/visualization/volume_rendering/interactive_vr.py
@@ -3,9 +3,14 @@
 from OpenGL.GLUT import *
 
 import numpy as np
-from yt.utilities.math_utils import get_translate_matrix, get_scale_matrix, \
-    get_lookat_matrix, get_perspective_matrix
-
+from yt.utilities.math_utils import \
+    get_translate_matrix, \
+    get_scale_matrix, \
+    get_lookat_matrix, \
+    get_perspective_matrix, \
+    quaternion_mult, \
+    quaternion_to_rotation_matrix
+    
 bbox_vertices = np.array(
       [[ 0.,  0.,  0.,  1.],
        [ 0.,  0.,  1.,  1.],
@@ -44,6 +49,83 @@
        [ 0.,  1.,  1.,  1.],
        [ 1.,  0.,  1.,  1.]])
 
+
+class TrackballCamera(object):
+    def __init__(self, 
+                 position=np.array([0.0, 0.0, 1.0]),
+                 focus=np.array([0.0, 0.0, 0.0]),
+                 fov=45.0, near_plane=1.0, far_plane=20.0, aspect_ratio=8.0/6.0):
+        self.view_matrix = np.zeros((4, 4), dtype=np.float32)
+        self.proj_matrix = np.zeros((4, 4), dtype=np.float32)
+        self.position = np.array(position)
+        self.focus = np.array(focus)
+        self.fov = fov
+        self.near_plane = near_plane
+        self.far_plane = far_plane
+        self.aspect_ratio = aspect_ratio
+        self.orientation = np.array([1.0, 0.0, 0.0, 0.0])
+
+    def _map_to_surface(self, mouse_x, mouse_y):
+        # right now this just maps to the surface of
+        # the unit sphere
+        x, y = mouse_x, mouse_y
+        mag = np.sqrt(x*x + y*y)
+        if (mag > 1.0):
+            x /= mag
+            y /= mag
+            z = 0.0
+        else:
+            z = np.sqrt(1.0 - mag**2)
+        return np.array([x, y, z])
+
+    def update_orientation(self, start_x, start_y, end_x, end_y):
+        old = self._map_to_surface(start_x, start_y)
+        new = self._map_to_surface(end_x, end_y)
+
+        # dot product
+        w = old[0]*new[0] + old[1]*new[1] + old[2]*new[2]
+
+        # cross product
+        x = old[1]*new[2] - old[2]*new[1]
+        y = old[2]*new[0] - old[0]*new[2]
+        z = old[0]*new[1] - old[1]*new[0]
+
+        q = np.array([w, x, y, z])
+
+        #renormalize to prevent floating point issues
+        mag = np.sqrt(w**2 + x**2 + y**2 + z**2)
+        q /= mag
+
+        self.orientation = quaternion_mult(self.orientation, q)
+
+    def compute_matrices(self):
+        rotation_matrix = quaternion_to_rotation_matrix(self.orientation)
+        self.position = np.dot(rotation_matrix, self.position)
+
+        self.view_matrix = np.zeros ( (4, 4), dtype = 'float32', order = 'C')
+        
+        # the upper-left 3x3 submatrix is the rotation matrix
+        self.view_matrix[0:3,0:3] = rotation_matrix
+
+        # fill in remaining components
+        self.view_matrix[2][3] = -np.linalg.norm(self.position)
+        self.view_matrix[3][3] = 1.0
+
+        self.projection_matrix = get_perspective_matrix(self.fov,
+                                                        self.aspect_ratio,
+                                                        self.near_plane,
+                                                        self.far_plane)
+
+    def get_viewpoint(self):
+        return self.position
+
+    def get_view_matrix(self):
+        return self.view_matrix
+
+    def get_projection_matrix(self):
+        return self.projection_matrix
+
+
 class Camera:
     def __init__(self, position = (0, 0, 0), fov = 60.0, near_plane = 0.01,
             far_plane = 20, aspect_ratio = 8.0 / 6.0, focus = (0, 0, 0),
@@ -65,8 +147,8 @@
                 self.far_plane)
     def update_position(self, theta, phi):
         rho = np.linalg.norm(self.position)
-        curr_theta = np.arctan( self.position[1] / self.position[0] )
-        curr_phi = np.arctan( np.linalg.norm(self.position[:2]) / self.position[2])
+        curr_theta = np.arctan2( self.position[1], self.position[0] )
+        curr_phi = np.arctan2( np.linalg.norm(self.position[:2]), self.position[2])
 
         curr_theta += theta
         curr_phi += phi
@@ -142,10 +224,10 @@
 
         # Now we set up our buffer
         vert = np.concatenate(vert)
-        if self.gl_buffer_name != None:
+        if self.gl_buffer_name is not None:
             glDeleteBuffers(1, [self.gl_buffer_name])
         self.gl_buffer_name = glGenBuffers(1)
-        if self.gl_vao_name != None:
+        if self.gl_vao_name is not None:
             glDeleteVertexArrays(1, [self.gl_vao_name])
         self.gl_vao_name = glGenVertexArrays(1)
         glBindBuffer(GL_ARRAY_BUFFER, self.gl_buffer_name)


https://bitbucket.org/yt_analysis/yt/commits/eda34ab2f40b/
Changeset:   eda34ab2f40b
Branch:      yt
User:        MatthewTurk
Date:        2016-02-04 01:19:08+00:00
Summary:     Update for new camera
Affected #:  1 file

diff -r 3b13744c503164e3a3b6086382313684560dd74e -r eda34ab2f40be548dfde70c6df551ce3956a360e doc/source/cookbook/opengl_vr.py
--- a/doc/source/cookbook/opengl_vr.py
+++ b/doc/source/cookbook/opengl_vr.py
@@ -1,7 +1,7 @@
 import yt
 import numpy as np
 from yt.visualization.volume_rendering.interactive_loop import \
-    start_context, SceneGraph, BlockCollection, Camera, start_loop
+    start_context, SceneGraph, BlockCollection, TrackballCamera, start_loop
 
 start_context()
 
@@ -23,6 +23,6 @@
 scene.add_collection(collection)
 
 position = (-5.0, -5.0, 5.0)
-c = Camera(position = position, focus = ds.domain_center)
+c = TrackballCamera(position = position, focus = ds.domain_center)
 
 start_loop(scene, c)


https://bitbucket.org/yt_analysis/yt/commits/19d26a510adb/
Changeset:   19d26a510adb
Branch:      yt
User:        atmyers
Date:        2016-02-04 00:17:58+00:00
Summary:     merging
Affected #:  1 file

diff -r de0eedb8d552c25f36a3ee82dfc9c56c844591dc -r 19d26a510adb254e1a3a73dc13644bc403c18af7 yt/visualization/volume_rendering/interactive_vr.py
--- a/yt/visualization/volume_rendering/interactive_vr.py
+++ b/yt/visualization/volume_rendering/interactive_vr.py
@@ -13,41 +13,41 @@
     
 bbox_vertices = np.array(
       [[ 0.,  0.,  0.,  1.],
+       [ 0.,  0.,  1.,  1.],
+       [ 0.,  1.,  1.,  1.],
+       [ 1.,  1.,  0.,  1.],
+       [ 0.,  0.,  0.,  1.],
+       [ 0.,  1.,  0.,  1.],
+       [ 1.,  0.,  1.,  1.],
+       [ 0.,  0.,  0.,  1.],
+       [ 1.,  0.,  0.,  1.],
        [ 1.,  1.,  0.,  1.],
        [ 1.,  0.,  0.,  1.],
        [ 0.,  0.,  0.,  1.],
+       [ 0.,  0.,  0.,  1.],
+       [ 0.,  1.,  1.,  1.],
        [ 0.,  1.,  0.,  1.],
-       [ 1.,  1.,  0.,  1.],
-       [ 0.,  0.,  0.,  1.],
-       [ 0.,  0.,  1.,  1.],
-       [ 0.,  1.,  0.,  1.],
-       [ 0.,  1.,  0.,  1.],
-       [ 0.,  0.,  1.,  1.],
-       [ 0.,  1.,  1.,  1.],
-       [ 0.,  0.,  0.,  1.],
-       [ 1.,  0.,  0.,  1.],
-       [ 1.,  0.,  1.,  1.],
-       [ 0.,  0.,  0.,  1.],
        [ 1.,  0.,  1.,  1.],
        [ 0.,  0.,  1.,  1.],
-       [ 0.,  1.,  0.,  1.],
+       [ 0.,  0.,  0.,  1.],
+       [ 0.,  1.,  1.,  1.],
+       [ 0.,  0.,  1.,  1.],
+       [ 1.,  0.,  1.,  1.],
+       [ 1.,  1.,  1.,  1.],
+       [ 1.,  0.,  0.,  1.],
+       [ 1.,  1.,  0.,  1.],
+       [ 1.,  0.,  0.,  1.],
+       [ 1.,  1.,  1.,  1.],
+       [ 1.,  0.,  1.,  1.],
        [ 1.,  1.,  1.,  1.],
        [ 1.,  1.,  0.,  1.],
        [ 0.,  1.,  0.,  1.],
+       [ 1.,  1.,  1.,  1.],
+       [ 0.,  1.,  0.,  1.],
        [ 0.,  1.,  1.,  1.],
        [ 1.,  1.,  1.,  1.],
-       [ 1.,  0.,  0.,  1.],
-       [ 1.,  1.,  0.,  1.],
-       [ 1.,  1.,  1.,  1.],
-       [ 1.,  0.,  0.,  1.],
-       [ 1.,  1.,  1.,  1.],
-       [ 1.,  0.,  1.,  1.],
-       [ 0.,  0.,  1.,  1.],
-       [ 1.,  0.,  1.,  1.],
-       [ 1.,  1.,  1.,  1.],
-       [ 0.,  0.,  1.,  1.],
-       [ 1.,  1.,  1.,  1.],
-       [ 0.,  1.,  1.,  1.]])
+       [ 0.,  1.,  1.,  1.],
+       [ 1.,  0.,  1.,  1.]])
 
 
 class TrackballCamera(object):
@@ -127,7 +127,7 @@
 
 
 class Camera:
-    def __init__(self, position = (0, 0, 0), fov = 60.0, near_plane = 1.0,
+    def __init__(self, position = (0, 0, 0), fov = 60.0, near_plane = 0.01,
             far_plane = 20, aspect_ratio = 8.0 / 6.0, focus = (0, 0, 0),
             up = (0, 0, 1)):
         self.position = np.array(position)
@@ -173,6 +173,7 @@
 
         self.camera = None
         glEnable(GL_CULL_FACE)
+        glCullFace(GL_BACK)
         glEnable(GL_DEPTH_TEST)
         glDepthFunc(GL_LESS)
 


https://bitbucket.org/yt_analysis/yt/commits/399cdfdaee9f/
Changeset:   399cdfdaee9f
Branch:      yt
User:        atmyers
Date:        2016-02-04 05:50:31+00:00
Summary:     fix position computation.
Affected #:  1 file

diff -r 19d26a510adb254e1a3a73dc13644bc403c18af7 -r 399cdfdaee9f2cfb688ba49d10362be443927292 yt/visualization/volume_rendering/interactive_vr.py
--- a/yt/visualization/volume_rendering/interactive_vr.py
+++ b/yt/visualization/volume_rendering/interactive_vr.py
@@ -100,7 +100,7 @@
 
     def compute_matrices(self):
         rotation_matrix = quaternion_to_rotation_matrix(self.orientation)
-        self.position = np.dot(rotation_matrix, self.position)
+        self.position = np.linalg.norm(self.position)*rotation_matrix[2]
 
         self.view_matrix = np.zeros ( (4, 4), dtype = 'float32', order = 'C')
         


https://bitbucket.org/yt_analysis/yt/commits/eb0e1d486c53/
Changeset:   eb0e1d486c53
Branch:      yt
User:        atmyers
Date:        2016-02-04 05:50:43+00:00
Summary:     invert y axis
Affected #:  1 file

diff -r 399cdfdaee9f2cfb688ba49d10362be443927292 -r eb0e1d486c534a0af9222ba91215e831f69f048f yt/visualization/volume_rendering/interactive_vr.py
--- a/yt/visualization/volume_rendering/interactive_vr.py
+++ b/yt/visualization/volume_rendering/interactive_vr.py
@@ -76,7 +76,7 @@
             z = 0.0
         else:
             z = np.sqrt(1.0 - mag**2)
-        return np.array([x, y, z])
+        return np.array([x, -y, z])
 
     def update_orientation(self, start_x, start_y, end_x, end_y):
         old = self._map_to_surface(start_x, start_y)


https://bitbucket.org/yt_analysis/yt/commits/de9b62947b22/
Changeset:   de9b62947b22
Branch:      yt
User:        atmyers
Date:        2016-02-04 07:09:32+00:00
Summary:     just use get_lookat_matrix to compute the correct matrix, given the orientation
Affected #:  1 file

diff -r eb0e1d486c534a0af9222ba91215e831f69f048f -r de9b62947b2207729b3c8ec5818dd7c487c7b7dc yt/visualization/volume_rendering/interactive_vr.py
--- a/yt/visualization/volume_rendering/interactive_vr.py
+++ b/yt/visualization/volume_rendering/interactive_vr.py
@@ -85,7 +85,7 @@
         # dot product
         w = old[0]*new[0] + old[1]*new[1] + old[2]*new[2]
 
-        # cross product
+        # cross product gives the rotation axis
         x = old[1]*new[2] - old[2]*new[1]
         y = old[2]*new[0] - old[0]*new[2]
         z = old[0]*new[1] - old[1]*new[0]
@@ -101,15 +101,11 @@
     def compute_matrices(self):
         rotation_matrix = quaternion_to_rotation_matrix(self.orientation)
         self.position = np.linalg.norm(self.position)*rotation_matrix[2]
+        up = rotation_matrix[1]
 
-        self.view_matrix = np.zeros ( (4, 4), dtype = 'float32', order = 'C')
-        
-        # the upper-left 3x3 submatrix is the rotation matrix
-        self.view_matrix[0:3,0:3] = rotation_matrix
-
-        # fill in remaining components
-        self.view_matrix[2][3] = -np.linalg.norm(self.position)
-        self.view_matrix[3][3] = 1.0
+        self.view_matrix = get_lookat_matrix(self.focus + self.position, 
+                                             self.focus, 
+                                             up)
 
         self.projection_matrix = get_perspective_matrix(self.fov,
                                                         self.aspect_ratio,


https://bitbucket.org/yt_analysis/yt/commits/54cfc0cb5a97/
Changeset:   54cfc0cb5a97
Branch:      yt
User:        MatthewTurk
Date:        2016-02-04 13:03:44+00:00
Summary:     Merged in atmyers/yt (pull request #100)

Fixing issue with view matrix
Affected #:  1 file

diff -r eda34ab2f40be548dfde70c6df551ce3956a360e -r 54cfc0cb5a97d803af5b3c2994cd7733cb76afa0 yt/visualization/volume_rendering/interactive_vr.py
--- a/yt/visualization/volume_rendering/interactive_vr.py
+++ b/yt/visualization/volume_rendering/interactive_vr.py
@@ -76,7 +76,7 @@
             z = 0.0
         else:
             z = np.sqrt(1.0 - mag**2)
-        return np.array([x, y, z])
+        return np.array([x, -y, z])
 
     def update_orientation(self, start_x, start_y, end_x, end_y):
         old = self._map_to_surface(start_x, start_y)
@@ -85,7 +85,7 @@
         # dot product
         w = old[0]*new[0] + old[1]*new[1] + old[2]*new[2]
 
-        # cross product
+        # cross product gives the rotation axis
         x = old[1]*new[2] - old[2]*new[1]
         y = old[2]*new[0] - old[0]*new[2]
         z = old[0]*new[1] - old[1]*new[0]
@@ -100,16 +100,12 @@
 
     def compute_matrices(self):
         rotation_matrix = quaternion_to_rotation_matrix(self.orientation)
-        self.position = np.dot(rotation_matrix, self.position)
+        self.position = np.linalg.norm(self.position)*rotation_matrix[2]
+        up = rotation_matrix[1]
 
-        self.view_matrix = np.zeros ( (4, 4), dtype = 'float32', order = 'C')
-        
-        # the upper-left 3x3 submatrix is the rotation matrix
-        self.view_matrix[0:3,0:3] = rotation_matrix
-
-        # fill in remaining components
-        self.view_matrix[2][3] = -np.linalg.norm(self.position)
-        self.view_matrix[3][3] = 1.0
+        self.view_matrix = get_lookat_matrix(self.focus + self.position, 
+                                             self.focus, 
+                                             up)
 
         self.projection_matrix = get_perspective_matrix(self.fov,
                                                         self.aspect_ratio,


https://bitbucket.org/yt_analysis/yt/commits/a717c54f5f3e/
Changeset:   a717c54f5f3e
Branch:      yt
User:        MatthewTurk
Date:        2016-02-04 15:14:18+00:00
Summary:     Starting process of refactoring callbacks
Affected #:  1 file

diff -r d4f9b609dfc37bd269273d4475b065db0294ece4 -r a717c54f5f3e35998ded39ee82135ce2bddd590a yt/visualization/volume_rendering/interactive_loop.py
--- a/yt/visualization/volume_rendering/interactive_loop.py
+++ b/yt/visualization/volume_rendering/interactive_loop.py
@@ -1,6 +1,7 @@
 import cyglfw3 as glfw
 import numpy as np
 from OpenGL.GL import glViewport
+from collections import defaultdict
 
 from interactive_vr import BlockCollection, SceneGraph, Camera
 
@@ -41,6 +42,52 @@
     glViewport(0, 0, width, height)
     draw = True
 
+class KeyCallbacks(object):
+    def __init__(self):
+        self.callbacks = defaultdict(list)
+
+    def __call__(self, window, key, scancode, action, mods):
+        for f in self.callbacks[None] + self.callbacks[key, action, mods]:
+            f(window, key, scancode, action, mods)
+
+    def add_default(self, func):
+        self.callbacks[None].append(func)
+
+    def add(self, func, key, action = "press", mods = None):
+        if isinstance(key, str):
+            key = getattr(glfw, "KEY_%s" % key.upper())
+        if isinstance(action, str):
+            action = getattr(glfw, action.upper())
+        if not isinstance(mods, tuple):
+            mods = (mods, )
+        mod = 0
+        for m in mods:
+            if isinstance(m, str):
+                m = getattr(glfw, "MOD_%s" % m.upper())
+            elif m is None:
+                m = 0
+            mod |= m
+        # We can allow for multiple
+        self.callbacks[key, action, mod].append(func)
+
+callbacks = KeyCallbacks()
+
+def close_window(window, key, scancode, action, mods):
+    glfw.SetWindowShouldClose(window, True)
+callbacks.add(close_window, "escape")
+
+def zoomin(window, key, scancode, action, mods):
+    global draw, c
+    c.position -= (c.position - c.focus) / np.linalg.norm(c.position - c.focus)
+    draw = True
+callbacks.add(zoomin, "w")
+
+def zoomout(window, key, scancode, action, mods):
+    global draw, c
+    c.position += (c.position - c.focus) / np.linalg.norm(c.position - c.focus)
+    draw = True
+callbacks.add(zoomout, "s")
+
 def key_callback(window, key, scancode, action, mods):
     global draw, c
     if key == glfw.KEY_ESCAPE and action == glfw.PRESS:
@@ -66,7 +113,7 @@
         exit()
 
     glfw.MakeContextCurrent(window)
-    glfw.SetKeyCallback(window, key_callback)
+    glfw.SetKeyCallback(window, callbacks)
     glfw.SetFramebufferSizeCallback(window, framebuffer_size_callback)
     glfw.SetMouseButtonCallback(window, mouse_button_callback)
 


https://bitbucket.org/yt_analysis/yt/commits/ae68c5f0ca7e/
Changeset:   ae68c5f0ca7e
Branch:      yt
User:        MatthewTurk
Date:        2016-02-04 15:17:56+00:00
Summary:     Cointinue refactoring
Affected #:  1 file

diff -r a717c54f5f3e35998ded39ee82135ce2bddd590a -r ae68c5f0ca7e459ae4361732772d48e79c481613 yt/visualization/volume_rendering/interactive_loop.py
--- a/yt/visualization/volume_rendering/interactive_loop.py
+++ b/yt/visualization/volume_rendering/interactive_loop.py
@@ -43,12 +43,13 @@
     draw = True
 
 class KeyCallbacks(object):
-    def __init__(self):
+    def __init__(self, camera):
         self.callbacks = defaultdict(list)
+        self.camera = camera
 
     def __call__(self, window, key, scancode, action, mods):
         for f in self.callbacks[None] + self.callbacks[key, action, mods]:
-            f(window, key, scancode, action, mods)
+            f(self.camera, window, key, scancode, action, mods)
 
     def add_default(self, func):
         self.callbacks[None].append(func)
@@ -70,34 +71,20 @@
         # We can allow for multiple
         self.callbacks[key, action, mod].append(func)
 
-callbacks = KeyCallbacks()
+def close_window(camera, window, key, scancode, action, mods):
+    glfw.SetWindowShouldClose(window, True)
 
-def close_window(window, key, scancode, action, mods):
-    glfw.SetWindowShouldClose(window, True)
-callbacks.add(close_window, "escape")
+def zoomin(camera, window, key, scancode, action, mods):
+    global draw
+    camera.position -= (camera.position - camera.focus) / \
+                np.linalg.norm(camera.position - camera.focus)
+    draw = True
 
-def zoomin(window, key, scancode, action, mods):
-    global draw, c
-    c.position -= (c.position - c.focus) / np.linalg.norm(c.position - c.focus)
+def zoomout(camera, window, key, scancode, action, mods):
+    global draw
+    camera.position += (camera.position - camera.focus) / \
+        np.linalg.norm(camera.position - camera.focus)
     draw = True
-callbacks.add(zoomin, "w")
-
-def zoomout(window, key, scancode, action, mods):
-    global draw, c
-    c.position += (c.position - c.focus) / np.linalg.norm(c.position - c.focus)
-    draw = True
-callbacks.add(zoomout, "s")
-
-def key_callback(window, key, scancode, action, mods):
-    global draw, c
-    if key == glfw.KEY_ESCAPE and action == glfw.PRESS:
-        glfw.SetWindowShouldClose(window, True)
-    if key == glfw.KEY_W and action == glfw.PRESS:
-        c.position -= (c.position - c.focus) / np.linalg.norm(c.position - c.focus)
-        draw = True
-    if key == glfw.KEY_S and action == glfw.PRESS:
-        c.position += (c.position - c.focus) / np.linalg.norm(c.position - c.focus)
-        draw = True
 
 def start_context():
     global window
@@ -113,7 +100,6 @@
         exit()
 
     glfw.MakeContextCurrent(window)
-    glfw.SetKeyCallback(window, callbacks)
     glfw.SetFramebufferSizeCallback(window, framebuffer_size_callback)
     glfw.SetMouseButtonCallback(window, mouse_button_callback)
 
@@ -127,6 +113,11 @@
     f = 0
     N = 10.0
     print "Starting rendering..."
+    callbacks = KeyCallbacks(camera)
+    callbacks.add(close_window, "escape")
+    callbacks.add(zoomin, "w")
+    callbacks.add(zoomout, "s")
+    glfw.SetKeyCallback(window, callbacks)
 
     while not glfw.WindowShouldClose(window):
         if rotation:


https://bitbucket.org/yt_analysis/yt/commits/5e39ae856a61/
Changeset:   5e39ae856a61
Branch:      yt
User:        MatthewTurk
Date:        2016-02-04 15:20:47+00:00
Summary:     Continue moving things out of global space
Affected #:  1 file

diff -r ae68c5f0ca7e459ae4361732772d48e79c481613 -r 5e39ae856a612ee3b84d058c74eea2beb4eb4334 yt/visualization/volume_rendering/interactive_loop.py
--- a/yt/visualization/volume_rendering/interactive_loop.py
+++ b/yt/visualization/volume_rendering/interactive_loop.py
@@ -43,13 +43,16 @@
     draw = True
 
 class KeyCallbacks(object):
+    draw = True
     def __init__(self, camera):
         self.callbacks = defaultdict(list)
         self.camera = camera
 
     def __call__(self, window, key, scancode, action, mods):
+        draw = False
         for f in self.callbacks[None] + self.callbacks[key, action, mods]:
-            f(self.camera, window, key, scancode, action, mods)
+            draw = f(self.camera, window, key, scancode, action, mods) or draw
+        self.draw = draw
 
     def add_default(self, func):
         self.callbacks[None].append(func)
@@ -75,16 +78,14 @@
     glfw.SetWindowShouldClose(window, True)
 
 def zoomin(camera, window, key, scancode, action, mods):
-    global draw
     camera.position -= (camera.position - camera.focus) / \
                 np.linalg.norm(camera.position - camera.focus)
-    draw = True
+    return True
 
 def zoomout(camera, window, key, scancode, action, mods):
-    global draw
     camera.position += (camera.position - camera.focus) / \
         np.linalg.norm(camera.position - camera.focus)
-    draw = True
+    return True
 
 def start_context():
     global window
@@ -133,10 +134,10 @@
             start = new_end
             draw = True
 
-        if draw:
+        if draw or callbacks.draw:
             scene.set_camera(c)
             scene.render()
-            draw = False
+            draw = callbacks.draw = False
         glfw.SwapBuffers(window)
         glfw.PollEvents()
 


https://bitbucket.org/yt_analysis/yt/commits/21e2163f13ad/
Changeset:   21e2163f13ad
Branch:      yt
User:        MatthewTurk
Date:        2016-02-04 16:27:51+00:00
Summary:     Start moving things into a RenderingContext.
Affected #:  2 files

diff -r 5e39ae856a612ee3b84d058c74eea2beb4eb4334 -r 21e2163f13ade25e199f029c1e610721d2423f89 doc/source/cookbook/opengl_vr.py
--- a/doc/source/cookbook/opengl_vr.py
+++ b/doc/source/cookbook/opengl_vr.py
@@ -1,9 +1,9 @@
 import yt
 import numpy as np
 from yt.visualization.volume_rendering.interactive_loop import \
-    start_context, SceneGraph, BlockCollection, Camera, start_loop
+    SceneGraph, BlockCollection, Camera, RenderingContext
 
-start_context()
+rc = RenderingContext(1280, 960)
 
 scene = SceneGraph()
 collection = BlockCollection()
@@ -25,4 +25,4 @@
 position = (-5.0, -5.0, 5.0)
 c = Camera(position = position, focus = ds.domain_center)
 
-start_loop(scene, c)
+rc.start_loop(scene, c)

diff -r 5e39ae856a612ee3b84d058c74eea2beb4eb4334 -r 21e2163f13ade25e199f029c1e610721d2423f89 yt/visualization/volume_rendering/interactive_loop.py
--- a/yt/visualization/volume_rendering/interactive_loop.py
+++ b/yt/visualization/volume_rendering/interactive_loop.py
@@ -87,66 +87,70 @@
         np.linalg.norm(camera.position - camera.focus)
     return True
 
-def start_context():
-    global window
-    glfw.Init()
+def printit(*args):
+    print args
 
-    glfw.WindowHint(glfw.CONTEXT_VERSION_MAJOR, 3)
-    glfw.WindowHint(glfw.CONTEXT_VERSION_MINOR, 3)
-    glfw.WindowHint(glfw.OPENGL_FORWARD_COMPAT, True)
-    glfw.WindowHint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)
-    window = glfw.CreateWindow(800, 600, 'vol_render')
-    if not window:
+class RenderingContext(object):
+    def __init__(self, width = 600, height = 800, title = "vol_render"):
+        glfw.Init()
+        glfw.WindowHint(glfw.CONTEXT_VERSION_MAJOR, 3)
+        glfw.WindowHint(glfw.CONTEXT_VERSION_MINOR, 3)
+        glfw.WindowHint(glfw.OPENGL_FORWARD_COMPAT, True)
+        glfw.WindowHint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)
+        self.window = glfw.CreateWindow(800, 600, 'vol_render')
+        if not self.window:
+            glfw.Terminate()
+            exit()
+
+        glfw.MakeContextCurrent(self.window)
+        glfw.SetFramebufferSizeCallback(self.window, framebuffer_size_callback)
+        glfw.SetMouseButtonCallback(self.window, mouse_button_callback)
+        global window
+        window = self.window
+
+    def start_loop(self, scene, camera):
+        global draw, c, start
+        c = camera
+        scene.set_camera(camera)
+        scene.add_shader_from_file("max_intensity_frag.glsl")
+        frame_start = glfw.GetTime()
+        fps_start = glfw.GetTime()
+        f = 0
+        N = 10.0
+        print "Starting rendering..."
+        callbacks = KeyCallbacks(camera)
+        callbacks.add(close_window, "escape")
+        callbacks.add(zoomin, "w")
+        callbacks.add(zoomout, "s")
+        glfw.SetKeyCallback(window, callbacks)
+
+        while not glfw.WindowShouldClose(self.window):
+            if rotation:
+                new_end_screen = glfw.GetCursorPos(self.window)
+                window_size = glfw.GetWindowSize(self.window)
+
+                norm_x = -1.0 + 2.0 * new_end_screen[0] / window_size[0]
+                norm_y = 1.0 - 2.0 * new_end_screen[1] / window_size[1]
+                new_end = (norm_x, norm_y)
+
+                c.update_position( (new_end[0] - start[0]),
+                        (new_end[1] - start[1]))
+                start = new_end
+                draw = True
+
+            if draw or callbacks.draw:
+                scene.set_camera(c)
+                scene.render()
+                draw = callbacks.draw = False
+            glfw.SwapBuffers(self.window)
+            glfw.PollEvents()
+
+            frame_start = glfw.GetTime()
+            f += 1
+            if f == N:
+                print "FPS:", N / float(frame_start - fps_start)
+                fps_start = glfw.GetTime()
+                f = 0
+
+        print "Finished rendering"
         glfw.Terminate()
-        exit()
-
-    glfw.MakeContextCurrent(window)
-    glfw.SetFramebufferSizeCallback(window, framebuffer_size_callback)
-    glfw.SetMouseButtonCallback(window, mouse_button_callback)
-
-def start_loop(scene, camera):
-    global draw, c, start
-    c = camera
-    scene.set_camera(camera)
-    scene.add_shader_from_file("max_intensity_frag.glsl")
-    frame_start = glfw.GetTime()
-    fps_start = glfw.GetTime()
-    f = 0
-    N = 10.0
-    print "Starting rendering..."
-    callbacks = KeyCallbacks(camera)
-    callbacks.add(close_window, "escape")
-    callbacks.add(zoomin, "w")
-    callbacks.add(zoomout, "s")
-    glfw.SetKeyCallback(window, callbacks)
-
-    while not glfw.WindowShouldClose(window):
-        if rotation:
-            new_end_screen = glfw.GetCursorPos(window)
-            window_size = glfw.GetWindowSize(window)
-
-            norm_x = -1.0 + 2.0 * new_end_screen[0] / window_size[0]
-            norm_y = 1.0 - 2.0 * new_end_screen[1] / window_size[1]
-            new_end = (norm_x, norm_y)
-
-            c.update_position( (new_end[0] - start[0]),
-                    (new_end[1] - start[1]))
-            start = new_end
-            draw = True
-
-        if draw or callbacks.draw:
-            scene.set_camera(c)
-            scene.render()
-            draw = callbacks.draw = False
-        glfw.SwapBuffers(window)
-        glfw.PollEvents()
-
-        frame_start = glfw.GetTime()
-        f += 1
-        if f == N:
-            print "FPS:", N / float(frame_start - fps_start)
-            fps_start = glfw.GetTime()
-            f = 0
-
-    print "Finished rendering"
-    glfw.Terminate()


https://bitbucket.org/yt_analysis/yt/commits/03205d95c4ce/
Changeset:   03205d95c4ce
Branch:      yt
User:        MatthewTurk
Date:        2016-02-04 16:53:41+00:00
Summary:     Moving mouse callbacks into the loop; framebuffer callbacks are left.
Affected #:  1 file

diff -r 21e2163f13ade25e199f029c1e610721d2423f89 -r 03205d95c4ce760a2df9268390be253daa75ce83 yt/visualization/volume_rendering/interactive_loop.py
--- a/yt/visualization/volume_rendering/interactive_loop.py
+++ b/yt/visualization/volume_rendering/interactive_loop.py
@@ -5,59 +5,47 @@
 
 from interactive_vr import BlockCollection, SceneGraph, Camera
 
-draw = True
-start = None
-rotation = False
-window = None
-
-c = None
-
-def mouse_button_callback(window, button, action, mods):
-    global draw, start, rotation, c
-
-    if (button == glfw.MOUSE_BUTTON_LEFT and action == glfw.PRESS):
-        start_screen = glfw.GetCursorPos(window) # Screen coordinates
-        window_size = glfw.GetWindowSize(window)
-
-        norm_x = -1.0 + 2.0 * start_screen[0] / window_size[0]
-        norm_y = 1.0 - 2.0 * start_screen[1] / window_size[1]
-        start = (norm_x, norm_y)
-        rotation = True
-
-    if (button == glfw.MOUSE_BUTTON_LEFT and action == glfw.RELEASE):
-        end_screen = glfw.GetCursorPos(window)
-        window_size = glfw.GetWindowSize(window)
-
-        norm_x = -1.0 + 2.0 * end_screen[0] / window_size[0]
-        norm_y = 1.0 - 2.0 * end_screen[1] / window_size[1]
-        end = (norm_x, norm_y)
-
-        c.update_position( (end[0] - start[0]), (end[1] - start[1]))
-
-        rotation = False
-        draw = True
-
 def framebuffer_size_callback(window, width, height):
     global draw
     glViewport(0, 0, width, height)
     draw = True
 
-class KeyCallbacks(object):
-    draw = True
+class Events(object):
     def __init__(self, camera):
-        self.callbacks = defaultdict(list)
+        self.key_callbacks = defaultdict(list)
+        self.mouse_callbacks = defaultdict(list)
+        self.render_events = []
         self.camera = camera
+        self.draw = True
 
-    def __call__(self, window, key, scancode, action, mods):
+    def key_call(self, window, key, scancode, action, mods):
         draw = False
-        for f in self.callbacks[None] + self.callbacks[key, action, mods]:
+        for f in self.key_callbacks[key, action, mods]:
             draw = f(self.camera, window, key, scancode, action, mods) or draw
-        self.draw = draw
+        self.draw = self.draw or draw
+
+    def mouse_call(self, window, key, action, mods):
+        draw = False
+        for f in self.mouse_callbacks[key, action, mods]:
+            draw = f(self.camera, window, key, action, mods) or draw
+        self.draw = self.draw or draw
+
+    def __call__(self, window):
+        draw = False
+        for f in self.render_events:
+            draw = f(self.camera, window) or draw
+        self.draw = self.draw or draw
 
     def add_default(self, func):
-        self.callbacks[None].append(func)
+        self.render_events.append(func)
 
-    def add(self, func, key, action = "press", mods = None):
+    def add_key_callback(self, func, key, action = "press", mods = None):
+        self._add_callback(self.key_callbacks, func, key, action, mods)
+
+    def add_mouse_callback(self, func, key, action = "press", mods = None):
+        self._add_callback(self.mouse_callbacks, func, key, action, mods)
+
+    def _add_callback(self, d, func, key, action, mods):
         if isinstance(key, str):
             key = getattr(glfw, "KEY_%s" % key.upper())
         if isinstance(action, str):
@@ -72,7 +60,7 @@
                 m = 0
             mod |= m
         # We can allow for multiple
-        self.callbacks[key, action, mod].append(func)
+        d[key, action, mod].append(func)
 
 def close_window(camera, window, key, scancode, action, mods):
     glfw.SetWindowShouldClose(window, True)
@@ -90,6 +78,48 @@
 def printit(*args):
     print args
 
+class MouseRotation(object):
+    def __init__(self):
+        self.start = None
+        self.rotation = False
+
+    def start_rotation(self, camera, window, key, action, mods):
+        start_screen = glfw.GetCursorPos(window) # Screen coordinates
+        window_size = glfw.GetWindowSize(window)
+
+        norm_x = -1.0 + 2.0 * start_screen[0] / window_size[0]
+        norm_y = 1.0 - 2.0 * start_screen[1] / window_size[1]
+        self.start = (norm_x, norm_y)
+        self.rotation = True
+        return False
+
+    def stop_rotation(self, camera, window, key, action, mods):
+        end_screen = glfw.GetCursorPos(window)
+        window_size = glfw.GetWindowSize(window)
+
+        norm_x = -1.0 + 2.0 * end_screen[0] / window_size[0]
+        norm_y = 1.0 - 2.0 * end_screen[1] / window_size[1]
+        end = (norm_x, norm_y)
+
+        camera.update_position( (end[0] - self.start[0]),
+                                (end[1] - self.start[1]))
+        self.rotation = False
+        return True
+
+    def do_rotation(self, camera, window):
+        if not self.rotation: return False
+        new_end_screen = glfw.GetCursorPos(window)
+        window_size = glfw.GetWindowSize(window)
+
+        norm_x = -1.0 + 2.0 * new_end_screen[0] / window_size[0]
+        norm_y = 1.0 - 2.0 * new_end_screen[1] / window_size[1]
+        new_end = (norm_x, norm_y)
+
+        camera.update_position( (new_end[0] - self.start[0]),
+                (new_end[1] - self.start[1]))
+        self.start = new_end
+        return True
+        
 class RenderingContext(object):
     def __init__(self, width = 600, height = 800, title = "vol_render"):
         glfw.Init()
@@ -103,13 +133,10 @@
             exit()
 
         glfw.MakeContextCurrent(self.window)
-        glfw.SetFramebufferSizeCallback(self.window, framebuffer_size_callback)
-        glfw.SetMouseButtonCallback(self.window, mouse_button_callback)
-        global window
+        #glfw.SetFramebufferSizeCallback(self.window, framebuffer_size_callback)
         window = self.window
 
     def start_loop(self, scene, camera):
-        global draw, c, start
         c = camera
         scene.set_camera(camera)
         scene.add_shader_from_file("max_intensity_frag.glsl")
@@ -118,30 +145,25 @@
         f = 0
         N = 10.0
         print "Starting rendering..."
-        callbacks = KeyCallbacks(camera)
-        callbacks.add(close_window, "escape")
-        callbacks.add(zoomin, "w")
-        callbacks.add(zoomout, "s")
-        glfw.SetKeyCallback(window, callbacks)
-
+        callbacks = Events(camera)
+        callbacks.add_key_callback(close_window, "escape")
+        callbacks.add_key_callback(zoomin, "w")
+        callbacks.add_key_callback(zoomout, "s")
+        mouse_callbacks = MouseRotation()
+        callbacks.add_mouse_callback(mouse_callbacks.start_rotation,
+            glfw.MOUSE_BUTTON_LEFT)
+        callbacks.add_mouse_callback(mouse_callbacks.stop_rotation,
+            glfw.MOUSE_BUTTON_LEFT, action="release")
+        callbacks.add_default(mouse_callbacks.do_rotation)
+        glfw.SetKeyCallback(self.window, callbacks.key_call)
+        glfw.SetMouseButtonCallback(self.window, callbacks.mouse_call)
+        callbacks.draw = True
         while not glfw.WindowShouldClose(self.window):
-            if rotation:
-                new_end_screen = glfw.GetCursorPos(self.window)
-                window_size = glfw.GetWindowSize(self.window)
-
-                norm_x = -1.0 + 2.0 * new_end_screen[0] / window_size[0]
-                norm_y = 1.0 - 2.0 * new_end_screen[1] / window_size[1]
-                new_end = (norm_x, norm_y)
-
-                c.update_position( (new_end[0] - start[0]),
-                        (new_end[1] - start[1]))
-                start = new_end
-                draw = True
-
-            if draw or callbacks.draw:
+            callbacks(self.window)
+            if callbacks.draw:
                 scene.set_camera(c)
                 scene.render()
-                draw = callbacks.draw = False
+                callbacks.draw = False
             glfw.SwapBuffers(self.window)
             glfw.PollEvents()
 


https://bitbucket.org/yt_analysis/yt/commits/a656893bfdcf/
Changeset:   a656893bfdcf
Branch:      yt
User:        MatthewTurk
Date:        2016-02-04 17:19:57+00:00
Summary:     Moving framebuffer callback into object
Affected #:  1 file

diff -r 03205d95c4ce760a2df9268390be253daa75ce83 -r a656893bfdcf9e0056e000a08620715d55bf8b36 yt/visualization/volume_rendering/interactive_loop.py
--- a/yt/visualization/volume_rendering/interactive_loop.py
+++ b/yt/visualization/volume_rendering/interactive_loop.py
@@ -5,15 +5,12 @@
 
 from interactive_vr import BlockCollection, SceneGraph, Camera
 
-def framebuffer_size_callback(window, width, height):
-    global draw
-    glViewport(0, 0, width, height)
-    draw = True
 
 class Events(object):
     def __init__(self, camera):
         self.key_callbacks = defaultdict(list)
         self.mouse_callbacks = defaultdict(list)
+        self.framebuffer_callbacks = []
         self.render_events = []
         self.camera = camera
         self.draw = True
@@ -30,6 +27,12 @@
             draw = f(self.camera, window, key, action, mods) or draw
         self.draw = self.draw or draw
 
+    def framebuffer_call(self, window, width, height):
+        draw = False
+        for f in self.framebuffer_callbacks:
+            draw = f(window, width, height) or draw
+        self.draw = self.draw or draw
+
     def __call__(self, window):
         draw = False
         for f in self.render_events:
@@ -45,6 +48,9 @@
     def add_mouse_callback(self, func, key, action = "press", mods = None):
         self._add_callback(self.mouse_callbacks, func, key, action, mods)
 
+    def add_framebuffer_callback(self, func):
+        self.framebuffer_callbacks.append(func)
+
     def _add_callback(self, d, func, key, action, mods):
         if isinstance(key, str):
             key = getattr(glfw, "KEY_%s" % key.upper())
@@ -62,6 +68,10 @@
         # We can allow for multiple
         d[key, action, mod].append(func)
 
+def framebuffer_size_callback(window, width, height):
+    glViewport(0, 0, width, height)
+    return True
+
 def close_window(camera, window, key, scancode, action, mods):
     glfw.SetWindowShouldClose(window, True)
 
@@ -133,7 +143,6 @@
             exit()
 
         glfw.MakeContextCurrent(self.window)
-        #glfw.SetFramebufferSizeCallback(self.window, framebuffer_size_callback)
         window = self.window
 
     def start_loop(self, scene, camera):
@@ -154,7 +163,10 @@
             glfw.MOUSE_BUTTON_LEFT)
         callbacks.add_mouse_callback(mouse_callbacks.stop_rotation,
             glfw.MOUSE_BUTTON_LEFT, action="release")
+        callbacks.add_framebuffer_callback(framebuffer_size_callback)
         callbacks.add_default(mouse_callbacks.do_rotation)
+        glfw.SetFramebufferSizeCallback(self.window,
+            callbacks.framebuffer_call)
         glfw.SetKeyCallback(self.window, callbacks.key_call)
         glfw.SetMouseButtonCallback(self.window, callbacks.mouse_call)
         callbacks.draw = True


https://bitbucket.org/yt_analysis/yt/commits/452d2fdd8e4a/
Changeset:   452d2fdd8e4a
Branch:      yt
User:        MatthewTurk
Date:        2016-02-04 17:22:30+00:00
Summary:     Starting to remove global imports
Affected #:  2 files

diff -r a656893bfdcf9e0056e000a08620715d55bf8b36 -r 452d2fdd8e4a5e9a0e1bbc1b90c646a5224adc67 yt/visualization/volume_rendering/interactive_loop.py
--- a/yt/visualization/volume_rendering/interactive_loop.py
+++ b/yt/visualization/volume_rendering/interactive_loop.py
@@ -1,6 +1,6 @@
 import cyglfw3 as glfw
 import numpy as np
-from OpenGL.GL import glViewport
+import OpenGL.GL as GL
 from collections import defaultdict
 
 from interactive_vr import BlockCollection, SceneGraph, Camera
@@ -69,7 +69,7 @@
         d[key, action, mod].append(func)
 
 def framebuffer_size_callback(window, width, height):
-    glViewport(0, 0, width, height)
+    GL.glViewport(0, 0, width, height)
     return True
 
 def close_window(camera, window, key, scancode, action, mods):

diff -r a656893bfdcf9e0056e000a08620715d55bf8b36 -r 452d2fdd8e4a5e9a0e1bbc1b90c646a5224adc67 yt/visualization/volume_rendering/interactive_vr.py
--- a/yt/visualization/volume_rendering/interactive_vr.py
+++ b/yt/visualization/volume_rendering/interactive_vr.py
@@ -1,6 +1,6 @@
-from OpenGL.GL import *
+import os
+import OpenGL.GL as GL
 from OpenGL.GL import shaders
-from OpenGL.GLUT import *
 
 import numpy as np
 from yt.utilities.math_utils import get_translate_matrix, get_scale_matrix, \
@@ -90,18 +90,18 @@
         self.geometry_loaded = False
 
         self.camera = None
-        glEnable(GL_CULL_FACE)
-        glCullFace(GL_BACK)
-        glEnable(GL_DEPTH_TEST)
-        glDepthFunc(GL_LESS)
+        GL.glEnable(GL.GL_CULL_FACE)
+        GL.glCullFace(GL.GL_BACK)
+        GL.glEnable(GL.GL_DEPTH_TEST)
+        GL.glDepthFunc(GL.GL_LESS)
 
         self._init_blending()
 
     def _init_blending(self):
-        glEnable(GL_BLEND)
-        glBlendColor(1.0, 1.0, 1.0, 1.0)
-        glBlendFunc(GL_ONE, GL_ONE)
-        glBlendEquation(GL_MAX)
+        GL.glEnable(GL.GL_BLEND)
+        GL.glBlendColor(1.0, 1.0, 1.0, 1.0)
+        GL.glBlendFunc(GL.GL_ONE, GL.GL_ONE)
+        GL.glBlendEquation(GL.GL_MAX)
 
     def add_data(self, data_source, field):
         r"""Adds a source of data for the block collection.
@@ -143,13 +143,13 @@
         # Now we set up our buffer
         vert = np.concatenate(vert)
         if self.gl_buffer_name != None:
-            glDeleteBuffers(1, [self.gl_buffer_name])
-        self.gl_buffer_name = glGenBuffers(1)
+            GL.glDeleteBuffers(1, [self.gl_buffer_name])
+        self.gl_buffer_name = GL.glGenBuffers(1)
         if self.gl_vao_name != None:
-            glDeleteVertexArrays(1, [self.gl_vao_name])
-        self.gl_vao_name = glGenVertexArrays(1)
-        glBindBuffer(GL_ARRAY_BUFFER, self.gl_buffer_name)
-        glBufferData(GL_ARRAY_BUFFER, vert.nbytes, vert, GL_STATIC_DRAW)
+            GL.glDeleteVertexArrays(1, [self.gl_vao_name])
+        self.gl_vao_name = GL.glGenVertexArrays(1)
+        GL.glBindBuffer(GL.GL_ARRAY_BUFFER, self.gl_buffer_name)
+        GL.glBufferData(GL.GL_ARRAY_BUFFER, vert.nbytes, vert, GL.GL_STATIC_DRAW)
         redraw = True
 
     def run_program(self, shader_program):
@@ -161,58 +161,58 @@
             An integer name for an OpenGL shader program.
 
         """
-        glUseProgram(shader_program)
+        GL.glUseProgram(shader_program)
 
-        glBindVertexArray(self.gl_vao_name)
-        glBindBuffer(GL_ARRAY_BUFFER, self.gl_buffer_name)
+        GL.glBindVertexArray(self.gl_vao_name)
+        GL.glBindBuffer(GL.GL_ARRAY_BUFFER, self.gl_buffer_name)
 
-        vert_location = glGetAttribLocation(shader_program, "model_vertex")
+        vert_location = GL.glGetAttribLocation(shader_program, "model_vertex")
 
-        glVertexAttribPointer(vert_location, 4, GL_FLOAT, False, 0, None)
-        glEnableVertexAttribArray(vert_location)
-        glClear(GL_COLOR_BUFFER_BIT)
-        glClear(GL_DEPTH_BUFFER_BIT)
+        GL.glVertexAttribPointer(vert_location, 4, GL.GL_FLOAT, False, 0, None)
+        GL.glEnableVertexAttribArray(vert_location)
+        GL.glClear(GL.GL_COLOR_BUFFER_BIT)
+        GL.glClear(GL.GL_DEPTH_BUFFER_BIT)
 
         self._set_uniforms(shader_program)
-        glActiveTexture(GL_TEXTURE0)
+        GL.glActiveTexture(GL.GL_TEXTURE0)
         self._load_textures()
-        dx_loc = glGetUniformLocation(shader_program, "dx")
-        left_edge_loc = glGetUniformLocation(shader_program, "left_edge")
-        right_edge_loc = glGetUniformLocation(shader_program, "right_edge")
-        camera_loc = glGetUniformLocation(shader_program, "camera_pos")
-        glUniform3fv(camera_loc, 1, self.camera.position)
+        dx_loc = GL.glGetUniformLocation(shader_program, "dx")
+        left_edge_loc = GL.glGetUniformLocation(shader_program, "left_edge")
+        right_edge_loc = GL.glGetUniformLocation(shader_program, "right_edge")
+        camera_loc = GL.glGetUniformLocation(shader_program, "camera_pos")
+        GL.glUniform3fv(camera_loc, 1, self.camera.position)
 
-        glBindBuffer(GL_ARRAY_BUFFER, self.gl_buffer_name)
-        glActiveTexture(GL_TEXTURE0)
+        GL.glBindBuffer(GL.GL_ARRAY_BUFFER, self.gl_buffer_name)
+        GL.glActiveTexture(GL.GL_TEXTURE0)
         for i in range(0, len(self.blocks)):
             self._set_bounds(self.blocks[i], shader_program,
                 dx_loc, left_edge_loc, right_edge_loc)
-            glBindTexture(GL_TEXTURE_3D, self.gl_texture_names[i])
-            glDrawArrays(GL_TRIANGLES, i*36, 36)
+            GL.glBindTexture(GL.GL_TEXTURE_3D, self.gl_texture_names[i])
+            GL.glDrawArrays(GL.GL_TRIANGLES, i*36, 36)
 
-        glDisableVertexAttribArray(vert_location)
-        glBindVertexArray(0)
-        glBindBuffer(GL_ARRAY_BUFFER, 0)
+        GL.glDisableVertexAttribArray(vert_location)
+        GL.glBindVertexArray(0)
+        GL.glBindBuffer(GL.GL_ARRAY_BUFFER, 0)
 
     def _set_bounds(self, block, shader_program, dx, le, re):
         dds = (block.RightEdge - block.LeftEdge)/block.my_data[0].shape
-        glUniform3fv(dx, 1, dds)
-        glUniform3fv(le, 1, block.LeftEdge)
-        glUniform3fv(re, 1, block.RightEdge)
+        GL.glUniform3fv(dx, 1, dds)
+        GL.glUniform3fv(le, 1, block.LeftEdge)
+        GL.glUniform3fv(re, 1, block.RightEdge)
 
     def _set_uniforms(self, shader_program):
-        project_loc = glGetUniformLocation(shader_program, "projection")
-        lookat_loc = glGetUniformLocation(shader_program, "lookat")
-        viewport_loc = glGetUniformLocation(shader_program, "viewport")
+        project_loc = GL.glGetUniformLocation(shader_program, "projection")
+        lookat_loc = GL.glGetUniformLocation(shader_program, "lookat")
+        viewport_loc = GL.glGetUniformLocation(shader_program, "viewport")
 
         project = self.camera.get_projection_matrix()
         view = self.camera.get_view_matrix()
 
-        viewport = np.array(glGetIntegerv(GL_VIEWPORT), dtype = 'float32')
+        viewport = np.array(GL.glGetIntegerv(GL.GL_VIEWPORT), dtype = 'float32')
 
-        glUniformMatrix4fv(project_loc, 1, GL_TRUE, project)
-        glUniformMatrix4fv(lookat_loc, 1, GL_TRUE, view)
-        glUniform4fv(viewport_loc, 1, viewport)
+        GL.glUniformMatrix4fv(project_loc, 1, GL.GL_TRUE, project)
+        GL.glUniformMatrix4fv(lookat_loc, 1, GL.GL_TRUE, view)
+        GL.glUniform4fv(viewport_loc, 1, viewport)
 
     def _compute_geometry(self, block, bbox_vertices):
         move = get_translate_matrix(*block.LeftEdge)
@@ -224,19 +224,19 @@
 
     def _load_textures(self):
         if len(self.gl_texture_names) == 0:
-            self.gl_texture_names = glGenTextures(len(self.blocks))
+            self.gl_texture_names = GL.glGenTextures(len(self.blocks))
             if len(self.blocks) == 1:
                 self.gl_texture_names = [self.gl_texture_names]
-            glPixelStorei(GL_UNPACK_ALIGNMENT, 1)
-            glTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
-            glTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER,
-                    GL_LINEAR)
+            GL.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1)
+            GL.glTexParameterf(GL.GL_TEXTURE_3D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR)
+            GL.glTexParameterf(GL.GL_TEXTURE_3D, GL.GL_TEXTURE_MIN_FILTER,
+                    GL.GL_LINEAR)
 
         else:
             for texture in self.gl_texture_names:
-                glDeleteTextures([texture])
+                GL.glDeleteTextures([texture])
 
-            self.gl_texture_names = glGenTextures(len(self.blocks))
+            self.gl_texture_names = GL.glGenTextures(len(self.blocks))
             if len(self.blocks) == 1:
                 self.gl_texture_names = [self.gl_texture_names]
 
@@ -244,15 +244,15 @@
             dx, dy, dz = block.my_data[0].shape
             n_data = block.my_data[0].copy(order="F").astype("float32")
             n_data = (n_data - self.min_val) / (self.max_val - self.min_val)
-            glBindTexture(GL_TEXTURE_3D, texture_name)
-            glTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)
-            glTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)
-            glTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE)
-            glTexStorage3D(GL_TEXTURE_3D, 1, GL_R32F,
+            GL.glBindTexture(GL.GL_TEXTURE_3D, texture_name)
+            GL.glTexParameterf(GL.GL_TEXTURE_3D, GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP_TO_EDGE)
+            GL.glTexParameterf(GL.GL_TEXTURE_3D, GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP_TO_EDGE)
+            GL.glTexParameterf(GL.GL_TEXTURE_3D, GL.GL_TEXTURE_WRAP_R, GL.GL_CLAMP_TO_EDGE)
+            GL.glTexStorage3D(GL.GL_TEXTURE_3D, 1, GL.GL_R32F,
                 *block.my_data[0].shape)
-            glTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 0, dx, dy, dz,
-                        GL_RED, GL_FLOAT, n_data.T)
-            glGenerateMipmap(GL_TEXTURE_3D)
+            GL.glTexSubImage3D(GL.GL_TEXTURE_3D, 0, 0, 0, 0, dx, dy, dz,
+                        GL.GL_RED, GL.GL_FLOAT, n_data.T)
+            GL.glGenerateMipmap(GL.GL_TEXTURE_3D)
 
 class SceneGraph:
     def __init__(self):
@@ -316,16 +316,16 @@
         vert_abs = os.path.join(vr_directory, vert_path)
 
         shader_file = open(frag_abs, 'r')
-        self.gl_frag_shader = shaders.compileShader(shader_file.read(), GL_FRAGMENT_SHADER)
+        self.gl_frag_shader = shaders.compileShader(shader_file.read(), GL.GL_FRAGMENT_SHADER)
 
         vert_file = open(vert_abs, 'r')
-        self.gl_vert_shader = shaders.compileShader(vert_file.read(), GL_VERTEX_SHADER)
+        self.gl_vert_shader = shaders.compileShader(vert_file.read(), GL.GL_VERTEX_SHADER)
 
-        self.shader_program = glCreateProgram()
-        glAttachShader(self.shader_program, self.gl_vert_shader)
-        glAttachShader(self.shader_program, self.gl_frag_shader)
+        self.shader_program = GL.glCreateProgram()
+        GL.glAttachShader(self.shader_program, self.gl_vert_shader)
+        GL.glAttachShader(self.shader_program, self.gl_frag_shader)
 
-        glLinkProgram(self.shader_program)
+        GL.glLinkProgram(self.shader_program)
 
     def render(self):
         """ Renders one frame of the scene.


https://bitbucket.org/yt_analysis/yt/commits/55dbaa6e5c7b/
Changeset:   55dbaa6e5c7b
Branch:      yt
User:        MatthewTurk
Date:        2016-02-04 17:30:22+00:00
Summary:     Merging from Andrew
Affected #:  4 files

diff -r 452d2fdd8e4a5e9a0e1bbc1b90c646a5224adc67 -r 55dbaa6e5c7b63e0777c9f217bbe926a240142ac doc/source/cookbook/opengl_vr.py
--- a/doc/source/cookbook/opengl_vr.py
+++ b/doc/source/cookbook/opengl_vr.py
@@ -1,7 +1,7 @@
 import yt
 import numpy as np
 from yt.visualization.volume_rendering.interactive_loop import \
-    SceneGraph, BlockCollection, Camera, RenderingContext
+    SceneGraph, BlockCollection, TrackballCamera, RenderingContext
 
 rc = RenderingContext(1280, 960)
 
@@ -23,6 +23,6 @@
 scene.add_collection(collection)
 
 position = (-5.0, -5.0, 5.0)
-c = Camera(position = position, focus = ds.domain_center)
+c = TrackballCamera(position = position, focus = ds.domain_center)
 
 rc.start_loop(scene, c)

diff -r 452d2fdd8e4a5e9a0e1bbc1b90c646a5224adc67 -r 55dbaa6e5c7b63e0777c9f217bbe926a240142ac yt/utilities/math_utils.py
--- a/yt/utilities/math_utils.py
+++ b/yt/utilities/math_utils.py
@@ -1001,6 +1001,50 @@
 
     return R
 
+def quaternion_mult(q1, q2):
+    '''
+
+    Multiply two quaternions. The inputs are 4-component numpy arrays
+    in the order [w, x, y, z].
+
+    '''
+    w = q1[0]*q2[0] - q1[1]*q2[1] - q1[2]*q2[2] - q1[3]*q2[3]
+    x = q1[0]*q2[1] + q1[1]*q2[0] + q1[2]*q2[3] - q1[3]*q2[2]
+    y = q1[0]*q2[2] + q1[2]*q2[0] + q1[3]*q2[1] - q1[1]*q2[3]
+    z = q1[0]*q2[3] + q1[3]*q2[0] + q1[1]*q2[2] - q1[2]*q2[1]
+    return np.array([w, x, y, z])
+
+def quaternion_to_rotation_matrix(quaternion):
+    """
+
+    This converts a quaternion representation of on orientation to
+    a rotation matrix. The input is a 4-component numpy array in
+    the order [w, x, y, z], and the output is a 3x3 matrix stored
+    as a 2D numpy array.
+
+    """
+
+    w = quaternion[0]
+    x = quaternion[1]
+    y = quaternion[2]
+    z = quaternion[3]
+
+    R = np.empty((3, 3), dtype=np.float64)
+
+    R[0][0] = 1.0 - 2.0*y**2 - 2.0*z**2
+    R[0][1] = 2.0*x*y + 2.0*w*z
+    R[0][2] = 2.0*x*z - 2.0*w*y
+
+    R[1][0] = 2.0*x*y - 2.0*w*z
+    R[1][1] = 1.0 - 2.0*x**2 - 2.0*z**2
+    R[1][2] = 2.0*y*z + 2.0*w*x
+
+    R[2][0] = 2.0*x*z + 2.0*w*y
+    R[2][1] = 2.0*y*z - 2.0*w*x
+    R[2][2] = 1.0 - 2.0*x**2 - 2.0*y**2
+
+    return R
+
 def get_ortho_basis(normal):
     xprime = np.cross([0.0,1.0,0.0],normal)
     if np.sum(xprime) == 0: xprime = np.array([0.0, 0.0, 1.0])

diff -r 452d2fdd8e4a5e9a0e1bbc1b90c646a5224adc67 -r 55dbaa6e5c7b63e0777c9f217bbe926a240142ac yt/visualization/volume_rendering/interactive_loop.py
--- a/yt/visualization/volume_rendering/interactive_loop.py
+++ b/yt/visualization/volume_rendering/interactive_loop.py
@@ -111,8 +111,9 @@
         norm_y = 1.0 - 2.0 * end_screen[1] / window_size[1]
         end = (norm_x, norm_y)
 
-        camera.update_position( (end[0] - self.start[0]),
-                                (end[1] - self.start[1]))
+        camera.update_orientation(self.start[0],
+                                  self.start[1],
+                                  end[0], end[1])
         self.rotation = False
         return True
 

diff -r 452d2fdd8e4a5e9a0e1bbc1b90c646a5224adc67 -r 55dbaa6e5c7b63e0777c9f217bbe926a240142ac yt/visualization/volume_rendering/interactive_vr.py
--- a/yt/visualization/volume_rendering/interactive_vr.py
+++ b/yt/visualization/volume_rendering/interactive_vr.py
@@ -3,9 +3,14 @@
 from OpenGL.GL import shaders
 
 import numpy as np
-from yt.utilities.math_utils import get_translate_matrix, get_scale_matrix, \
-    get_lookat_matrix, get_perspective_matrix
-
+from yt.utilities.math_utils import \
+    get_translate_matrix, \
+    get_scale_matrix, \
+    get_lookat_matrix, \
+    get_perspective_matrix, \
+    quaternion_mult, \
+    quaternion_to_rotation_matrix
+    
 bbox_vertices = np.array(
       [[ 0.,  0.,  0.,  1.],
        [ 0.,  0.,  1.,  1.],
@@ -44,6 +49,79 @@
        [ 0.,  1.,  1.,  1.],
        [ 1.,  0.,  1.,  1.]])
 
+
+class TrackballCamera(object):
+    def __init__(self, 
+                 position=np.array([0.0, 0.0, 1.0]),
+                 focus=np.array([0.0, 0.0, 0.0]),
+                 fov=45.0, near_plane=1.0, far_plane=20.0, aspect_ratio=8.0/6.0):
+        self.view_matrix = np.zeros((4, 4), dtype=np.float32)
+        self.proj_matrix = np.zeros((4, 4), dtype=np.float32)
+        self.position = np.array(position)
+        self.focus = np.array(focus)
+        self.fov = fov
+        self.near_plane = near_plane
+        self.far_plane = far_plane
+        self.aspect_ratio = aspect_ratio
+        self.orientation = np.array([1.0, 0.0, 0.0, 0.0])
+
+    def _map_to_surface(self, mouse_x, mouse_y):
+        # right now this just maps to the surface of
+        # the unit sphere
+        x, y = mouse_x, mouse_y
+        mag = np.sqrt(x*x + y*y)
+        if (mag > 1.0):
+            x /= mag
+            y /= mag
+            z = 0.0
+        else:
+            z = np.sqrt(1.0 - mag**2)
+        return np.array([x, -y, z])
+
+    def update_orientation(self, start_x, start_y, end_x, end_y):
+        old = self._map_to_surface(start_x, start_y)
+        new = self._map_to_surface(end_x, end_y)
+
+        # dot product
+        w = old[0]*new[0] + old[1]*new[1] + old[2]*new[2]
+
+        # cross product gives the rotation axis
+        x = old[1]*new[2] - old[2]*new[1]
+        y = old[2]*new[0] - old[0]*new[2]
+        z = old[0]*new[1] - old[1]*new[0]
+
+        q = np.array([w, x, y, z])
+
+        #renormalize to prevent floating point issues
+        mag = np.sqrt(w**2 + x**2 + y**2 + z**2)
+        q /= mag
+
+        self.orientation = quaternion_mult(self.orientation, q)
+
+    def compute_matrices(self):
+        rotation_matrix = quaternion_to_rotation_matrix(self.orientation)
+        self.position = np.linalg.norm(self.position)*rotation_matrix[2]
+        up = rotation_matrix[1]
+
+        self.view_matrix = get_lookat_matrix(self.focus + self.position, 
+                                             self.focus, 
+                                             up)
+
+        self.projection_matrix = get_perspective_matrix(self.fov,
+                                                        self.aspect_ratio,
+                                                        self.near_plane,
+                                                        self.far_plane)
+
+    def get_viewpoint(self):
+        return self.position
+
+    def get_view_matrix(self):
+        return self.view_matrix
+
+    def get_projection_matrix(self):
+        return self.projection_matrix
+
+
 class Camera:
     def __init__(self, position = (0, 0, 0), fov = 60.0, near_plane = 0.01,
             far_plane = 20, aspect_ratio = 8.0 / 6.0, focus = (0, 0, 0),
@@ -65,8 +143,8 @@
                 self.far_plane)
     def update_position(self, theta, phi):
         rho = np.linalg.norm(self.position)
-        curr_theta = np.arctan( self.position[1] / self.position[0] )
-        curr_phi = np.arctan( np.linalg.norm(self.position[:2]) / self.position[2])
+        curr_theta = np.arctan2( self.position[1], self.position[0] )
+        curr_phi = np.arctan2( np.linalg.norm(self.position[:2]), self.position[2])
 
         curr_theta += theta
         curr_phi += phi


https://bitbucket.org/yt_analysis/yt/commits/a35f83362416/
Changeset:   a35f83362416
Branch:      yt
User:        atmyers
Date:        2016-02-04 18:34:46+00:00
Summary:     need to convert to radians here
Affected #:  1 file

diff -r de9b62947b2207729b3c8ec5818dd7c487c7b7dc -r a35f833624169283601f76d0efd1e1b303c15a62 yt/visualization/volume_rendering/interactive_vr.py
--- a/yt/visualization/volume_rendering/interactive_vr.py
+++ b/yt/visualization/volume_rendering/interactive_vr.py
@@ -107,7 +107,7 @@
                                              self.focus, 
                                              up)
 
-        self.projection_matrix = get_perspective_matrix(self.fov,
+        self.projection_matrix = get_perspective_matrix(np.radians(self.fov),
                                                         self.aspect_ratio,
                                                         self.near_plane,
                                                         self.far_plane)


https://bitbucket.org/yt_analysis/yt/commits/2119d00fcf4b/
Changeset:   2119d00fcf4b
Branch:      yt
User:        MatthewTurk
Date:        2016-02-04 18:52:22+00:00
Summary:     Merged in atmyers/yt (pull request #101)

Fixing perspective matrix
Affected #:  1 file

diff -r 55dbaa6e5c7b63e0777c9f217bbe926a240142ac -r 2119d00fcf4b00541c31d2faa5742d728252c62d yt/visualization/volume_rendering/interactive_vr.py
--- a/yt/visualization/volume_rendering/interactive_vr.py
+++ b/yt/visualization/volume_rendering/interactive_vr.py
@@ -107,7 +107,7 @@
                                              self.focus, 
                                              up)
 
-        self.projection_matrix = get_perspective_matrix(self.fov,
+        self.projection_matrix = get_perspective_matrix(np.radians(self.fov),
                                                         self.aspect_ratio,
                                                         self.near_plane,
                                                         self.far_plane)


https://bitbucket.org/yt_analysis/yt/commits/1366267dc708/
Changeset:   1366267dc708
Branch:      yt
User:        MatthewTurk
Date:        2016-02-04 18:53:30+00:00
Summary:     Fixing missing import
Affected #:  1 file

diff -r 2119d00fcf4b00541c31d2faa5742d728252c62d -r 1366267dc708ac907c0520e0703054a4d93ce5ff yt/visualization/volume_rendering/interactive_loop.py
--- a/yt/visualization/volume_rendering/interactive_loop.py
+++ b/yt/visualization/volume_rendering/interactive_loop.py
@@ -3,7 +3,7 @@
 import OpenGL.GL as GL
 from collections import defaultdict
 
-from interactive_vr import BlockCollection, SceneGraph, Camera
+from interactive_vr import BlockCollection, SceneGraph, TrackballCamera
 
 
 class Events(object):
@@ -126,8 +126,10 @@
         norm_y = 1.0 - 2.0 * new_end_screen[1] / window_size[1]
         new_end = (norm_x, norm_y)
 
-        camera.update_position( (new_end[0] - self.start[0]),
-                (new_end[1] - self.start[1]))
+        camera.update_orientation(self.start[0],
+                                  self.start[1],
+                                  new_end[0],
+                                  new_end[1])
         self.start = new_end
         return True
         
@@ -147,9 +149,9 @@
         window = self.window
 
     def start_loop(self, scene, camera):
-        c = camera
         scene.set_camera(camera)
         scene.add_shader_from_file("max_intensity_frag.glsl")
+        camera.compute_matrices()
         frame_start = glfw.GetTime()
         fps_start = glfw.GetTime()
         f = 0
@@ -174,11 +176,12 @@
         while not glfw.WindowShouldClose(self.window):
             callbacks(self.window)
             if callbacks.draw:
-                scene.set_camera(c)
+                scene.set_camera(camera)
                 scene.render()
                 callbacks.draw = False
             glfw.SwapBuffers(self.window)
             glfw.PollEvents()
+            camera.compute_matrices()
 
             frame_start = glfw.GetTime()
             f += 1


https://bitbucket.org/yt_analysis/yt/commits/45672692e4fd/
Changeset:   45672692e4fd
Branch:      yt
User:        MatthewTurk
Date:        2016-02-04 18:56:46+00:00
Summary:     Making nprocs=4 to show problems.
Affected #:  1 file

diff -r 1366267dc708ac907c0520e0703054a4d93ce5ff -r 45672692e4fdcbf748046fd36f6cb7f0e4e3d409 doc/source/cookbook/opengl_vr.py
--- a/doc/source/cookbook/opengl_vr.py
+++ b/doc/source/cookbook/opengl_vr.py
@@ -15,7 +15,7 @@
 np.clip(oor2, 1e-6, 1e60, oor2)
 data = {'x_field': 10**x, 'y_field': 10**y, 'z_field': 10**z, 'sphere':oor2}
 
-ds = yt.load_uniform_grid(data, [N, N, N], 1.0, nprocs=1)
+ds = yt.load_uniform_grid(data, [N, N, N], 1.0, nprocs=4)
 
 dd = ds.all_data()
 collection.add_data(dd, "z_field")


https://bitbucket.org/yt_analysis/yt/commits/295f248a1823/
Changeset:   295f248a1823
Branch:      yt
User:        MatthewTurk
Date:        2016-02-04 20:26:05+00:00
Summary:     With this change, the artifacting goes away.
Affected #:  1 file

diff -r 45672692e4fdcbf748046fd36f6cb7f0e4e3d409 -r 295f248a1823881415b4b19921b2e85985e109dd yt/visualization/volume_rendering/interactive_vr.py
--- a/yt/visualization/volume_rendering/interactive_vr.py
+++ b/yt/visualization/volume_rendering/interactive_vr.py
@@ -100,12 +100,10 @@
 
     def compute_matrices(self):
         rotation_matrix = quaternion_to_rotation_matrix(self.orientation)
-        self.position = np.linalg.norm(self.position)*rotation_matrix[2]
+        self.position = self.position*rotation_matrix[2]
         up = rotation_matrix[1]
 
-        self.view_matrix = get_lookat_matrix(self.focus + self.position, 
-                                             self.focus, 
-                                             up)
+        self.view_matrix = get_lookat_matrix(self.position, self.focus, up)
 
         self.projection_matrix = get_perspective_matrix(np.radians(self.fov),
                                                         self.aspect_ratio,


https://bitbucket.org/yt_analysis/yt/commits/5b4df7ca607f/
Changeset:   5b4df7ca607f
Branch:      yt
User:        MatthewTurk
Date:        2016-02-04 20:31:56+00:00
Summary:     Make these immutables
Affected #:  1 file

diff -r 295f248a1823881415b4b19921b2e85985e109dd -r 5b4df7ca607f980de2371512a15086c631715a43 yt/visualization/volume_rendering/interactive_vr.py
--- a/yt/visualization/volume_rendering/interactive_vr.py
+++ b/yt/visualization/volume_rendering/interactive_vr.py
@@ -51,9 +51,7 @@
 
 
 class TrackballCamera(object):
-    def __init__(self, 
-                 position=np.array([0.0, 0.0, 1.0]),
-                 focus=np.array([0.0, 0.0, 0.0]),
+    def __init__(self, position=(0.0, 0.0, 1.0), focus=(0.0, 0.0, 0.0),
                  fov=45.0, near_plane=1.0, far_plane=20.0, aspect_ratio=8.0/6.0):
         self.view_matrix = np.zeros((4, 4), dtype=np.float32)
         self.proj_matrix = np.zeros((4, 4), dtype=np.float32)


https://bitbucket.org/yt_analysis/yt/commits/e71d8588be63/
Changeset:   e71d8588be63
Branch:      yt
User:        MatthewTurk
Date:        2016-02-04 20:32:16+00:00
Summary:     Add some reset functions, zoom slower, and use width/height/title.
Affected #:  1 file

diff -r 5b4df7ca607f980de2371512a15086c631715a43 -r e71d8588be635b855af2cb0e608689669f2cdfa4 yt/visualization/volume_rendering/interactive_loop.py
--- a/yt/visualization/volume_rendering/interactive_loop.py
+++ b/yt/visualization/volume_rendering/interactive_loop.py
@@ -76,13 +76,23 @@
     glfw.SetWindowShouldClose(window, True)
 
 def zoomin(camera, window, key, scancode, action, mods):
-    camera.position -= (camera.position - camera.focus) / \
+    camera.position -= 0.05 * (camera.position - camera.focus) / \
                 np.linalg.norm(camera.position - camera.focus)
+    print camera.position, camera.focus
     return True
 
 def zoomout(camera, window, key, scancode, action, mods):
-    camera.position += (camera.position - camera.focus) / \
+    camera.position += 0.05 * (camera.position - camera.focus) / \
         np.linalg.norm(camera.position - camera.focus)
+    print camera.position, camera.focus
+    return True
+
+def closeup(camera, window, key, scancode, action, mods):
+    camera.position = (0.01, 0.01, 0.01)
+    return True
+
+def reset(camera, window, key, scancode, action, mods):
+    camera.position = (-1.0, -1.0, -1.0)
     return True
 
 def printit(*args):
@@ -134,13 +144,13 @@
         return True
         
 class RenderingContext(object):
-    def __init__(self, width = 600, height = 800, title = "vol_render"):
+    def __init__(self, width = 800, height = 600, title = "vol_render"):
         glfw.Init()
         glfw.WindowHint(glfw.CONTEXT_VERSION_MAJOR, 3)
         glfw.WindowHint(glfw.CONTEXT_VERSION_MINOR, 3)
         glfw.WindowHint(glfw.OPENGL_FORWARD_COMPAT, True)
         glfw.WindowHint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)
-        self.window = glfw.CreateWindow(800, 600, 'vol_render')
+        self.window = glfw.CreateWindow(width, height, title)
         if not self.window:
             glfw.Terminate()
             exit()
@@ -161,6 +171,8 @@
         callbacks.add_key_callback(close_window, "escape")
         callbacks.add_key_callback(zoomin, "w")
         callbacks.add_key_callback(zoomout, "s")
+        callbacks.add_key_callback(closeup, "z")
+        callbacks.add_key_callback(reset, "r")
         mouse_callbacks = MouseRotation()
         callbacks.add_mouse_callback(mouse_callbacks.start_rotation,
             glfw.MOUSE_BUTTON_LEFT)


https://bitbucket.org/yt_analysis/yt/commits/c2ec865dc274/
Changeset:   c2ec865dc274
Branch:      yt
User:        MatthewTurk
Date:        2016-02-04 20:38:48+00:00
Summary:     Update cookbook receipt to show galaxy0030
Affected #:  1 file

diff -r e71d8588be635b855af2cb0e608689669f2cdfa4 -r c2ec865dc274a629868c19271913f1f99d215ff3 doc/source/cookbook/opengl_vr.py
--- a/doc/source/cookbook/opengl_vr.py
+++ b/doc/source/cookbook/opengl_vr.py
@@ -16,13 +16,16 @@
 data = {'x_field': 10**x, 'y_field': 10**y, 'z_field': 10**z, 'sphere':oor2}
 
 ds = yt.load_uniform_grid(data, [N, N, N], 1.0, nprocs=4)
+ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030")
 
 dd = ds.all_data()
-collection.add_data(dd, "z_field")
+#collection.add_data(dd, "sphere")
+collection.add_data(dd, "density")
 
 scene.add_collection(collection)
 
-position = (-5.0, -5.0, 5.0)
-c = TrackballCamera(position = position, focus = ds.domain_center)
+position = (1.0, 1.0, 1.0)
+c = TrackballCamera(position = position, focus = ds.domain_center,
+                    near_plane = 0.1)
 
 rc.start_loop(scene, c)


https://bitbucket.org/yt_analysis/yt/commits/d6d8ba003097/
Changeset:   d6d8ba003097
Branch:      yt
User:        atmyers
Date:        2016-02-04 21:01:14+00:00
Summary:     trying to fix the trackball camera
Affected #:  2 files

diff -r c2ec865dc274a629868c19271913f1f99d215ff3 -r d6d8ba003097a3f0e9ba86dae414d24487d3297f yt/utilities/math_utils.py
--- a/yt/utilities/math_utils.py
+++ b/yt/utilities/math_utils.py
@@ -1020,7 +1020,9 @@
     This converts a quaternion representation of on orientation to
     a rotation matrix. The input is a 4-component numpy array in
     the order [w, x, y, z], and the output is a 3x3 matrix stored
-    as a 2D numpy array.
+    as a 2D numpy array.  We follow the approach in
+    "3D Math Primer for Graphics and Game Development" by
+    Dunn and Parberry.
 
     """
 
@@ -1045,6 +1047,69 @@
 
     return R
 
+def rotation_matrix_to_quaternion(rot_matrix):
+    '''
+
+    Convert a rotation matrix-based representation of an 
+    orientation to a quaternion. The input should be a 
+    3x3 rotation matrix, while the output will be a 
+    4-component numpy array. We follow the approach in
+    "3D Math Primer for Graphics and Game Development" by
+    Dunn and Parberry.
+
+    '''
+    m11 = rot_matrix[0][0]
+    m12 = rot_matrix[0][1]
+    m13 = rot_matrix[0][2]
+    m21 = rot_matrix[1][0]
+    m22 = rot_matrix[1][1]
+    m23 = rot_matrix[1][2]
+    m31 = rot_matrix[2][0]
+    m32 = rot_matrix[2][1]
+    m33 = rot_matrix[2][2]
+
+    four_w_squared_minus_1 = m11 + m22 + m33
+    four_x_squared_minus_1 = m11 - m22 - m33 
+    four_y_squared_minus_1 = m22 - m11 - m33 
+    four_z_squared_minus_1 = m33 - m11 - m22
+    max_index = 0
+    four_max_squared_minus_1 = four_w_squared_minus_1
+    if (four_x_squared_minus_1 > four_max_squared_minus_1):
+        four_max_squared_minus_1 = four_x_squared_minus_1
+        max_index = 1
+    if (four_y_squared_minus_1 > four_max_squared_minus_1):
+        four_max_squared_minus_1 = four_y_squared_minus_1
+        max_index = 2
+    if (four_z_squared_minus_1 > four_max_squared_minus_1):
+        four_max_squared_minus_1 = four_z_squared_minus_1
+        max_index = 3
+
+    max_val = 0.5*np.sqrt(four_max_squared_minus_1 + 1.0)
+    mult = 0.25 / max_val
+
+    if (max_index == 0):
+        w = max_val
+        x = (m23 - m32) * mult
+        y = (m31 - m13) * mult 
+        z = (m12 - m21) * mult
+    elif (max_index == 1):
+        x = max_val
+        w = (m23 - m32) * mult
+        y = (m12 + m21) * mult
+        z = (m31 + m13) * mult
+    elif (max_index == 2):
+        y = max_val
+        w = (m31 - m13) * mult
+        x = (m12 + m21) * mult
+        z = (m23 + m32) * mult
+    elif (max_index == 3):
+        z = max_val
+        w = (m12 - m21) * mult
+        x = (m31 + m13) * mult
+        y = (m23 + m32) * mult
+ 
+    return np.array([w, x, y, z])
+
 def get_ortho_basis(normal):
     xprime = np.cross([0.0,1.0,0.0],normal)
     if np.sum(xprime) == 0: xprime = np.array([0.0, 0.0, 1.0])

diff -r c2ec865dc274a629868c19271913f1f99d215ff3 -r d6d8ba003097a3f0e9ba86dae414d24487d3297f yt/visualization/volume_rendering/interactive_vr.py
--- a/yt/visualization/volume_rendering/interactive_vr.py
+++ b/yt/visualization/volume_rendering/interactive_vr.py
@@ -9,7 +9,8 @@
     get_lookat_matrix, \
     get_perspective_matrix, \
     quaternion_mult, \
-    quaternion_to_rotation_matrix
+    quaternion_to_rotation_matrix, \
+    rotation_matrix_to_quaternion
     
 bbox_vertices = np.array(
       [[ 0.,  0.,  0.,  1.],
@@ -51,8 +52,11 @@
 
 
 class TrackballCamera(object):
-    def __init__(self, position=(0.0, 0.0, 1.0), focus=(0.0, 0.0, 0.0),
-                 fov=45.0, near_plane=1.0, far_plane=20.0, aspect_ratio=8.0/6.0):
+    def __init__(self, 
+                 position=np.array([0.0, 0.0, 1.0]),
+                 focus=np.array([0.0, 0.0, 0.0]),
+                 up=np.array([0.0, 1.0, 0.0]),
+                 fov=45.0, near_plane=0.01, far_plane=20.0, aspect_ratio=8.0/6.0):
         self.view_matrix = np.zeros((4, 4), dtype=np.float32)
         self.proj_matrix = np.zeros((4, 4), dtype=np.float32)
         self.position = np.array(position)
@@ -61,7 +65,14 @@
         self.near_plane = near_plane
         self.far_plane = far_plane
         self.aspect_ratio = aspect_ratio
-        self.orientation = np.array([1.0, 0.0, 0.0, 0.0])
+        self.up = up
+
+        self.view_matrix = get_lookat_matrix(self.position, 
+                                             self.focus,
+                                             self.up)
+
+        rotation_matrix = self.view_matrix[0:3,0:3]
+        self.orientation = rotation_matrix_to_quaternion(rotation_matrix)
 
     def _map_to_surface(self, mouse_x, mouse_y):
         # right now this just maps to the surface of
@@ -98,8 +109,9 @@
 
     def compute_matrices(self):
         rotation_matrix = quaternion_to_rotation_matrix(self.orientation)
-        self.position = self.position*rotation_matrix[2]
-        up = rotation_matrix[1]
+        dp = np.linalg.norm(self.position - self.focus)*rotation_matrix[2]
+        self.position = dp + self.focus
+        self.up = rotation_matrix[1]
 
         self.view_matrix = get_lookat_matrix(self.position, self.focus, up)
 


https://bitbucket.org/yt_analysis/yt/commits/a4a2e8edb438/
Changeset:   a4a2e8edb438
Branch:      yt
User:        atmyers
Date:        2016-02-04 21:10:40+00:00
Summary:     put these back to immutables.
Affected #:  1 file

diff -r d6d8ba003097a3f0e9ba86dae414d24487d3297f -r a4a2e8edb43830df80867724a82b96c4fd57adea yt/visualization/volume_rendering/interactive_vr.py
--- a/yt/visualization/volume_rendering/interactive_vr.py
+++ b/yt/visualization/volume_rendering/interactive_vr.py
@@ -53,9 +53,9 @@
 
 class TrackballCamera(object):
     def __init__(self, 
-                 position=np.array([0.0, 0.0, 1.0]),
-                 focus=np.array([0.0, 0.0, 0.0]),
-                 up=np.array([0.0, 1.0, 0.0]),
+                 position=(0.0, 0.0, 1.0),
+                 focus=(0.0, 0.0, 0.0),
+                 up=(0.0, 1.0, 0.0),
                  fov=45.0, near_plane=0.01, far_plane=20.0, aspect_ratio=8.0/6.0):
         self.view_matrix = np.zeros((4, 4), dtype=np.float32)
         self.proj_matrix = np.zeros((4, 4), dtype=np.float32)
@@ -65,7 +65,7 @@
         self.near_plane = near_plane
         self.far_plane = far_plane
         self.aspect_ratio = aspect_ratio
-        self.up = up
+        self.up = np.array(up)
 
         self.view_matrix = get_lookat_matrix(self.position, 
                                              self.focus,
@@ -113,7 +113,9 @@
         self.position = dp + self.focus
         self.up = rotation_matrix[1]
 
-        self.view_matrix = get_lookat_matrix(self.position, self.focus, up)
+        self.view_matrix = get_lookat_matrix(self.position, 
+                                             self.focus, 
+                                             self.up)
 
         self.projection_matrix = get_perspective_matrix(np.radians(self.fov),
                                                         self.aspect_ratio,


https://bitbucket.org/yt_analysis/yt/commits/555a544beefe/
Changeset:   555a544beefe
Branch:      yt
User:        MatthewTurk
Date:        2016-02-04 21:31:17+00:00
Summary:     Only compute matrices when we have changed.  Remove FPS printout.
Affected #:  2 files

diff -r a4a2e8edb43830df80867724a82b96c4fd57adea -r 555a544beefe6ebe3e200177e617d6ffa54d1afb yt/visualization/volume_rendering/interactive_loop.py
--- a/yt/visualization/volume_rendering/interactive_loop.py
+++ b/yt/visualization/volume_rendering/interactive_loop.py
@@ -188,19 +188,12 @@
         while not glfw.WindowShouldClose(self.window):
             callbacks(self.window)
             if callbacks.draw:
+                camera.compute_matrices()
                 scene.set_camera(camera)
                 scene.render()
                 callbacks.draw = False
-            glfw.SwapBuffers(self.window)
+                glfw.SwapBuffers(self.window)
             glfw.PollEvents()
-            camera.compute_matrices()
-
-            frame_start = glfw.GetTime()
-            f += 1
-            if f == N:
-                print "FPS:", N / float(frame_start - fps_start)
-                fps_start = glfw.GetTime()
-                f = 0
 
         print "Finished rendering"
         glfw.Terminate()

diff -r a4a2e8edb43830df80867724a82b96c4fd57adea -r 555a544beefe6ebe3e200177e617d6ffa54d1afb yt/visualization/volume_rendering/interactive_vr.py
--- a/yt/visualization/volume_rendering/interactive_vr.py
+++ b/yt/visualization/volume_rendering/interactive_vr.py
@@ -258,8 +258,7 @@
 
         GL.glVertexAttribPointer(vert_location, 4, GL.GL_FLOAT, False, 0, None)
         GL.glEnableVertexAttribArray(vert_location)
-        GL.glClear(GL.GL_COLOR_BUFFER_BIT)
-        GL.glClear(GL.GL_DEPTH_BUFFER_BIT)
+        GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT)
 
         self._set_uniforms(shader_program)
         GL.glActiveTexture(GL.GL_TEXTURE0)


https://bitbucket.org/yt_analysis/yt/commits/c26951999208/
Changeset:   c26951999208
Branch:      yt
User:        MatthewTurk
Date:        2016-02-04 21:41:18+00:00
Summary:     Change dx to five samples per cell
Affected #:  1 file

diff -r 555a544beefe6ebe3e200177e617d6ffa54d1afb -r c26951999208d143466a0b47fd83a56dfcf343e0 yt/visualization/volume_rendering/shaders/max_intensity_frag.glsl
--- a/yt/visualization/volume_rendering/shaders/max_intensity_frag.glsl
+++ b/yt/visualization/volume_rendering/shaders/max_intensity_frag.glsl
@@ -34,7 +34,8 @@
 
     vec4 world_location = inverse_view * eyePos;
 
-    float step_size = length(right_edge - left_edge) / dx.x / 100000.0;
+    // Five samples
+    vec3 step_size = dx / 5.0;
     vec3 dir = normalize(world_location.xyz - v_camera_pos.xyz);
     vec3 curr_color = vec3(0.0);
 


https://bitbucket.org/yt_analysis/yt/commits/31d92c493d56/
Changeset:   31d92c493d56
Branch:      yt
User:        MatthewTurk
Date:        2016-02-04 21:41:43+00:00
Summary:     Attempt to clear the buffer initially.
Affected #:  1 file

diff -r c26951999208d143466a0b47fd83a56dfcf343e0 -r 31d92c493d568324cdbf00314c6d8edc9f98cd99 yt/visualization/volume_rendering/interactive_loop.py
--- a/yt/visualization/volume_rendering/interactive_loop.py
+++ b/yt/visualization/volume_rendering/interactive_loop.py
@@ -156,7 +156,10 @@
             exit()
 
         glfw.MakeContextCurrent(self.window)
-        window = self.window
+        GL.glClearColor(0.0, 0.0, 0.0, 0.0)
+        GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT)
+        glfw.SwapBuffers(self.window)
+        glfw.PollEvents()
 
     def start_loop(self, scene, camera):
         scene.set_camera(camera)


https://bitbucket.org/yt_analysis/yt/commits/d41eccb002b8/
Changeset:   d41eccb002b8
Branch:      yt
User:        MatthewTurk
Date:        2016-02-04 21:52:45+00:00
Summary:     Reinstate FPS, remove printing.
Affected #:  1 file

diff -r 31d92c493d568324cdbf00314c6d8edc9f98cd99 -r d41eccb002b808424581b97342af8769374cdbc5 yt/visualization/volume_rendering/interactive_loop.py
--- a/yt/visualization/volume_rendering/interactive_loop.py
+++ b/yt/visualization/volume_rendering/interactive_loop.py
@@ -84,7 +84,6 @@
 def zoomout(camera, window, key, scancode, action, mods):
     camera.position += 0.05 * (camera.position - camera.focus) / \
         np.linalg.norm(camera.position - camera.focus)
-    print camera.position, camera.focus
     return True
 
 def closeup(camera, window, key, scancode, action, mods):
@@ -194,9 +193,15 @@
                 camera.compute_matrices()
                 scene.set_camera(camera)
                 scene.render()
+                glfw.SwapBuffers(self.window)
                 callbacks.draw = False
-                glfw.SwapBuffers(self.window)
             glfw.PollEvents()
+            frame_start = glfw.GetTime()
+            f += 1
+            if f == N:
+                #print "FPS:", N / float(frame_start - fps_start)
+                fps_start = glfw.GetTime()
+                f = 0
 
         print "Finished rendering"
         glfw.Terminate()


https://bitbucket.org/yt_analysis/yt/commits/5b36ed1f199c/
Changeset:   5b36ed1f199c
Branch:      yt
User:        MatthewTurk
Date:        2016-02-04 23:19:40+00:00
Summary:     Only load textures and vertices as needed.
Affected #:  2 files

diff -r d41eccb002b808424581b97342af8769374cdbc5 -r 5b36ed1f199c7aea758f189fc598990d6e4bd162 yt/visualization/volume_rendering/interactive_loop.py
--- a/yt/visualization/volume_rendering/interactive_loop.py
+++ b/yt/visualization/volume_rendering/interactive_loop.py
@@ -30,7 +30,7 @@
     def framebuffer_call(self, window, width, height):
         draw = False
         for f in self.framebuffer_callbacks:
-            draw = f(window, width, height) or draw
+            draw = f(self.camera, window, width, height) or draw
         self.draw = self.draw or draw
 
     def __call__(self, window):
@@ -68,8 +68,9 @@
         # We can allow for multiple
         d[key, action, mod].append(func)
 
-def framebuffer_size_callback(window, width, height):
+def framebuffer_size_callback(camera, window, width, height):
     GL.glViewport(0, 0, width, height)
+    camera.aspect_ratio = float(width)/height
     return True
 
 def close_window(camera, window, key, scancode, action, mods):

diff -r d41eccb002b808424581b97342af8769374cdbc5 -r 5b36ed1f199c7aea758f189fc598990d6e4bd162 yt/visualization/volume_rendering/interactive_vr.py
--- a/yt/visualization/volume_rendering/interactive_vr.py
+++ b/yt/visualization/volume_rendering/interactive_vr.py
@@ -167,7 +167,8 @@
     def __init__(self):
         self.data_source = None
 
-        self.blocks = [] # A collection of PartionedGrid objects
+        self.blocks = {} # A collection of PartionedGrid objects
+        self.block_order = []
 
         self.gl_buffer_name = None
         self.gl_texture_names = []
@@ -207,26 +208,19 @@
         """
         self.data_source = data_source
         self.data_source.tiles.set_fields([field], [True], True)
-
-    def set_camera(self, camera):
-        r"""Sets the camera for the block collection.
-
-        Parameters
-        ----------
-        camera : Camera
-            A simple camera object.
-
-        """
-        self.blocks = []
+        self.blocks = {}
+        self.block_order = []
+        # Every time we change our data source, we wipe all existing ones.
+        # We now set up our vertices into our current data source.
         vert = []
         self.min_val = 1e60
         self.max_val = -1e60
-        for block in self.data_source.tiles.traverse(viewpoint = camera.position):
+        for i, block in enumerate(self.data_source.tiles.traverse()):
             self.min_val = min(self.min_val, block.my_data[0].min())
             self.max_val = max(self.max_val, block.my_data[0].max())
-            self.blocks.append(block)
+            self.blocks[id(block)] = (i, block)
             vert.append(self._compute_geometry(block, bbox_vertices))
-        self.camera = camera
+            self.block_order.append(id(block))
 
         # Now we set up our buffer
         vert = np.concatenate(vert)
@@ -238,8 +232,24 @@
         self.gl_vao_name = GL.glGenVertexArrays(1)
         GL.glBindBuffer(GL.GL_ARRAY_BUFFER, self.gl_buffer_name)
         GL.glBufferData(GL.GL_ARRAY_BUFFER, vert.nbytes, vert, GL.GL_STATIC_DRAW)
+        self._load_textures()
         redraw = True
 
+    def set_camera(self, camera):
+        r"""Sets the camera for the block collection.
+
+        Parameters
+        ----------
+        camera : Camera
+            A simple camera object.
+
+        """
+        self.block_order = []
+        for block in self.data_source.tiles.traverse(viewpoint = camera.position):
+            self.block_order.append(id(block))
+        self.camera = camera
+        self.redraw = True
+
     def run_program(self, shader_program):
         r"""Runs a given shader program on the block collection.
 
@@ -262,7 +272,6 @@
 
         self._set_uniforms(shader_program)
         GL.glActiveTexture(GL.GL_TEXTURE0)
-        self._load_textures()
         dx_loc = GL.glGetUniformLocation(shader_program, "dx")
         left_edge_loc = GL.glGetUniformLocation(shader_program, "left_edge")
         right_edge_loc = GL.glGetUniformLocation(shader_program, "right_edge")
@@ -271,11 +280,13 @@
 
         GL.glBindBuffer(GL.GL_ARRAY_BUFFER, self.gl_buffer_name)
         GL.glActiveTexture(GL.GL_TEXTURE0)
-        for i in range(0, len(self.blocks)):
-            self._set_bounds(self.blocks[i], shader_program,
+        for bi in self.block_order:
+            tex_i, block = self.blocks[bi]
+            ti = self.gl_texture_names[tex_i]
+            self._set_bounds(block, shader_program,
                 dx_loc, left_edge_loc, right_edge_loc)
-            GL.glBindTexture(GL.GL_TEXTURE_3D, self.gl_texture_names[i])
-            GL.glDrawArrays(GL.GL_TRIANGLES, i*36, 36)
+            GL.glBindTexture(GL.GL_TEXTURE_3D, ti)
+            GL.glDrawArrays(GL.GL_TRIANGLES, tex_i*36, 36)
 
         GL.glDisableVertexAttribArray(vert_location)
         GL.glBindVertexArray(0)
@@ -327,7 +338,9 @@
             if len(self.blocks) == 1:
                 self.gl_texture_names = [self.gl_texture_names]
 
-        for block, texture_name in zip(self.blocks, self.gl_texture_names):
+        for block_id in sorted(self.blocks):
+            tex_i, block = self.blocks[block_id]
+            texture_name = self.gl_texture_names[tex_i]
             dx, dy, dz = block.my_data[0].shape
             n_data = block.my_data[0].copy(order="F").astype("float32")
             n_data = (n_data - self.min_val) / (self.max_val - self.min_val)


https://bitbucket.org/yt_analysis/yt/commits/d0137f5ee6cb/
Changeset:   d0137f5ee6cb
Branch:      yt
User:        MatthewTurk
Date:        2016-02-05 04:43:25+00:00
Summary:     Beginning process of using vertex attributes.
Affected #:  3 files

diff -r 5b36ed1f199c7aea758f189fc598990d6e4bd162 -r d0137f5ee6cb245c2cf752c275a815e911433cd4 yt/visualization/volume_rendering/interactive_vr.py
--- a/yt/visualization/volume_rendering/interactive_vr.py
+++ b/yt/visualization/volume_rendering/interactive_vr.py
@@ -165,6 +165,7 @@
 
 class BlockCollection:
     def __init__(self):
+        self.vert_attrib = {}
         self.data_source = None
 
         self.blocks = {} # A collection of PartionedGrid objects
@@ -192,6 +193,23 @@
         GL.glBlendFunc(GL.GL_ONE, GL.GL_ONE)
         GL.glBlendEquation(GL.GL_MAX)
 
+
+    def add_vert_attrib(self, name, arr):
+        self.vert_attrib[name] = GL.glGenBuffers(1)
+        GL.glBindBuffer(GL.GL_ARRAY_BUFFER, self.vert_attrib[name])
+        GL.glBufferData(GL.GL_ARRAY_BUFFER, arr.nbytes, arr, GL.GL_STATIC_DRAW)
+
+    def bind_vert_attrib(self, program, name, size):
+        loc = GL.glGetAttribLocation(program, name)
+        GL.glBindBuffer(GL.GL_ARRAY_BUFFER, self.vert_attrib[name])
+        GL.glVertexAttribPointer(loc, size, GL.GL_FLOAT, False, 0, None)
+        GL.glEnableVertexAttribArray(loc)
+
+    def disable_vert_attrib(self, program, name):
+        loc = GL.glGetAttribLocation(program, name)
+        GL.glDisableVertexAttribArray(loc)
+        GL.glBindVertexArray(0)
+
     def add_data(self, data_source, field):
         r"""Adds a source of data for the block collection.
 
@@ -212,7 +230,7 @@
         self.block_order = []
         # Every time we change our data source, we wipe all existing ones.
         # We now set up our vertices into our current data source.
-        vert = []
+        vert, dx, le, re = [], [], [], []
         self.min_val = 1e60
         self.max_val = -1e60
         for i, block in enumerate(self.data_source.tiles.traverse()):
@@ -220,18 +238,27 @@
             self.max_val = max(self.max_val, block.my_data[0].max())
             self.blocks[id(block)] = (i, block)
             vert.append(self._compute_geometry(block, bbox_vertices))
+            dds = (block.RightEdge - block.LeftEdge)/block.my_data[0].shape
+            dx.append(dds.astype('f4'))
+            le.append(block.LeftEdge.astype('f4'))
+            re.append(block.RightEdge.astype('f4'))
             self.block_order.append(id(block))
 
         # Now we set up our buffer
         vert = np.concatenate(vert)
-        if self.gl_buffer_name != None:
-            GL.glDeleteBuffers(1, [self.gl_buffer_name])
-        self.gl_buffer_name = GL.glGenBuffers(1)
+        dx = np.concatenate(dx)
+        le = np.concatenate(le)
+        re = np.concatenate(re)
         if self.gl_vao_name != None:
             GL.glDeleteVertexArrays(1, [self.gl_vao_name])
         self.gl_vao_name = GL.glGenVertexArrays(1)
-        GL.glBindBuffer(GL.GL_ARRAY_BUFFER, self.gl_buffer_name)
-        GL.glBufferData(GL.GL_ARRAY_BUFFER, vert.nbytes, vert, GL.GL_STATIC_DRAW)
+
+        self.add_vert_attrib("model_vertex", vert)
+        self.add_vert_attrib("in_dx", dx)
+        self.add_vert_attrib("in_left_edge", le)
+        self.add_vert_attrib("in_right_edge", re)
+
+        # Now we set up our 
         self._load_textures()
         redraw = True
 
@@ -262,42 +289,30 @@
         GL.glUseProgram(shader_program)
 
         GL.glBindVertexArray(self.gl_vao_name)
-        GL.glBindBuffer(GL.GL_ARRAY_BUFFER, self.gl_buffer_name)
 
-        vert_location = GL.glGetAttribLocation(shader_program, "model_vertex")
+        self.bind_vert_attrib(shader_program, "model_vertex", 4)
+        self.bind_vert_attrib(shader_program, "in_dx", 3)
+        self.bind_vert_attrib(shader_program, "in_left_edge", 3)
+        self.bind_vert_attrib(shader_program, "in_right_edge", 3)
 
-        GL.glVertexAttribPointer(vert_location, 4, GL.GL_FLOAT, False, 0, None)
-        GL.glEnableVertexAttribArray(vert_location)
         GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT)
 
         self._set_uniforms(shader_program)
         GL.glActiveTexture(GL.GL_TEXTURE0)
-        dx_loc = GL.glGetUniformLocation(shader_program, "dx")
-        left_edge_loc = GL.glGetUniformLocation(shader_program, "left_edge")
-        right_edge_loc = GL.glGetUniformLocation(shader_program, "right_edge")
         camera_loc = GL.glGetUniformLocation(shader_program, "camera_pos")
         GL.glUniform3fv(camera_loc, 1, self.camera.position)
 
-        GL.glBindBuffer(GL.GL_ARRAY_BUFFER, self.gl_buffer_name)
         GL.glActiveTexture(GL.GL_TEXTURE0)
         for bi in self.block_order:
             tex_i, block = self.blocks[bi]
             ti = self.gl_texture_names[tex_i]
-            self._set_bounds(block, shader_program,
-                dx_loc, left_edge_loc, right_edge_loc)
             GL.glBindTexture(GL.GL_TEXTURE_3D, ti)
             GL.glDrawArrays(GL.GL_TRIANGLES, tex_i*36, 36)
 
-        GL.glDisableVertexAttribArray(vert_location)
-        GL.glBindVertexArray(0)
+        for n in ("model_vertex", "in_dx", "in_left_edge", "in_right_edge"):
+            self.disable_vert_attrib(shader_program, n)
         GL.glBindBuffer(GL.GL_ARRAY_BUFFER, 0)
 
-    def _set_bounds(self, block, shader_program, dx, le, re):
-        dds = (block.RightEdge - block.LeftEdge)/block.my_data[0].shape
-        GL.glUniform3fv(dx, 1, dds)
-        GL.glUniform3fv(le, 1, block.LeftEdge)
-        GL.glUniform3fv(re, 1, block.RightEdge)
-
     def _set_uniforms(self, shader_program):
         project_loc = GL.glGetUniformLocation(shader_program, "projection")
         lookat_loc = GL.glGetUniformLocation(shader_program, "lookat")
@@ -321,6 +336,7 @@
         return transformed_box
 
     def _load_textures(self):
+        print "Loading textures."
         if len(self.gl_texture_names) == 0:
             self.gl_texture_names = GL.glGenTextures(len(self.blocks))
             if len(self.blocks) == 1:

diff -r 5b36ed1f199c7aea758f189fc598990d6e4bd162 -r d0137f5ee6cb245c2cf752c275a815e911433cd4 yt/visualization/volume_rendering/shaders/max_intensity_frag.glsl
--- a/yt/visualization/volume_rendering/shaders/max_intensity_frag.glsl
+++ b/yt/visualization/volume_rendering/shaders/max_intensity_frag.glsl
@@ -1,15 +1,15 @@
 #version 330
 in vec4 v_model;
 in vec3 v_camera_pos;
+in vec3 dx;
+in vec3 left_edge;
+in vec3 right_edge;
 flat in mat4 inverse_proj;
 flat in mat4 inverse_view;
 out vec4 output_color;
 
 uniform sampler3D ds_tex;
 //layout (binding = 1) uniform sampler2D depth_tex;
-uniform vec3 dx;
-uniform vec3 left_edge;
-uniform vec3 right_edge;
 uniform vec4 viewport; // (offset_x, offset_y, 1 / screen_x, 1 / screen_y)
 
 bool within_bb(vec3 pos)

diff -r 5b36ed1f199c7aea758f189fc598990d6e4bd162 -r d0137f5ee6cb245c2cf752c275a815e911433cd4 yt/visualization/volume_rendering/shaders/vert.glsl
--- a/yt/visualization/volume_rendering/shaders/vert.glsl
+++ b/yt/visualization/volume_rendering/shaders/vert.glsl
@@ -1,9 +1,15 @@
 #version 330
 in vec4 model_vertex; // The location of the vertex in model space
+in vec3 in_dx;
+in vec3 in_left_edge;
+in vec3 in_right_edge;
 out vec4 v_model;
 out vec3 v_camera_pos;
 flat out mat4 inverse_proj;
 flat out mat4 inverse_view;
+out vec3 dx;
+out vec3 left_edge;
+out vec3 right_edge;
 
 //// Uniforms
 uniform vec3 camera_pos;
@@ -17,4 +23,8 @@
     inverse_proj = inverse(projection);
     inverse_view = inverse(lookat);
     gl_Position = projection * lookat * model_vertex;
+    dx = in_dx;
+    left_edge = in_left_edge;
+    right_edge = in_right_edge;
+
 }


https://bitbucket.org/yt_analysis/yt/commits/fe9884d37f79/
Changeset:   fe9884d37f79
Branch:      yt
User:        MatthewTurk
Date:        2016-02-05 16:02:25+00:00
Summary:     This gets us further to binding vertex attribute arrays.
Affected #:  2 files

diff -r d0137f5ee6cb245c2cf752c275a815e911433cd4 -r fe9884d37f790375818055fb08c18eb88ee7ac3b yt/visualization/volume_rendering/interactive_vr.py
--- a/yt/visualization/volume_rendering/interactive_vr.py
+++ b/yt/visualization/volume_rendering/interactive_vr.py
@@ -166,14 +166,13 @@
 class BlockCollection:
     def __init__(self):
         self.vert_attrib = {}
+        self.gl_vao_name = None
         self.data_source = None
 
         self.blocks = {} # A collection of PartionedGrid objects
         self.block_order = []
 
-        self.gl_buffer_name = None
         self.gl_texture_names = []
-        self.gl_vao_name = None
 
         self.redraw = True
 
@@ -201,9 +200,9 @@
 
     def bind_vert_attrib(self, program, name, size):
         loc = GL.glGetAttribLocation(program, name)
+        GL.glEnableVertexAttribArray(loc)
         GL.glBindBuffer(GL.GL_ARRAY_BUFFER, self.vert_attrib[name])
         GL.glVertexAttribPointer(loc, size, GL.GL_FLOAT, False, 0, None)
-        GL.glEnableVertexAttribArray(loc)
 
     def disable_vert_attrib(self, program, name):
         loc = GL.glGetAttribLocation(program, name)
@@ -239,9 +238,10 @@
             self.blocks[id(block)] = (i, block)
             vert.append(self._compute_geometry(block, bbox_vertices))
             dds = (block.RightEdge - block.LeftEdge)/block.my_data[0].shape
-            dx.append(dds.astype('f4'))
-            le.append(block.LeftEdge.astype('f4'))
-            re.append(block.RightEdge.astype('f4'))
+            n = vert[-1].size/4
+            dx.append([dds.astype('f4') for _ in range(n)])
+            le.append([block.LeftEdge.astype('f4') for _ in range(n)])
+            re.append([block.RightEdge.astype('f4') for _ in range(n)])
             self.block_order.append(id(block))
 
         # Now we set up our buffer
@@ -249,6 +249,7 @@
         dx = np.concatenate(dx)
         le = np.concatenate(le)
         re = np.concatenate(re)
+        print vert.shape, dx.shape
         if self.gl_vao_name != None:
             GL.glDeleteVertexArrays(1, [self.gl_vao_name])
         self.gl_vao_name = GL.glGenVertexArrays(1)

diff -r d0137f5ee6cb245c2cf752c275a815e911433cd4 -r fe9884d37f790375818055fb08c18eb88ee7ac3b yt/visualization/volume_rendering/shaders/vert.glsl
--- a/yt/visualization/volume_rendering/shaders/vert.glsl
+++ b/yt/visualization/volume_rendering/shaders/vert.glsl
@@ -23,8 +23,8 @@
     inverse_proj = inverse(projection);
     inverse_view = inverse(lookat);
     gl_Position = projection * lookat * model_vertex;
-    dx = in_dx;
-    left_edge = in_left_edge;
-    right_edge = in_right_edge;
+    dx = vec3(in_dx);
+    left_edge = vec3(in_left_edge);
+    right_edge = vec3(in_right_edge);
 
 }


https://bitbucket.org/yt_analysis/yt/commits/b086dd0f0231/
Changeset:   b086dd0f0231
Branch:      yt
User:        atmyers
Date:        2016-02-05 17:43:56+00:00
Summary:     we don't want to disable the vertex attribute array
Affected #:  1 file

diff -r fe9884d37f790375818055fb08c18eb88ee7ac3b -r b086dd0f0231566f60884b57b1efa00ce2dd2dfb yt/visualization/volume_rendering/interactive_loop.py
--- a/yt/visualization/volume_rendering/interactive_loop.py
+++ b/yt/visualization/volume_rendering/interactive_loop.py
@@ -200,7 +200,7 @@
             frame_start = glfw.GetTime()
             f += 1
             if f == N:
-                #print "FPS:", N / float(frame_start - fps_start)
+                print "FPS:", N / float(frame_start - fps_start)
                 fps_start = glfw.GetTime()
                 f = 0
 


https://bitbucket.org/yt_analysis/yt/commits/d3d3554e2ea5/
Changeset:   d3d3554e2ea5
Branch:      yt
User:        atmyers
Date:        2016-02-05 20:37:55+00:00
Summary:     don't disable vertex attributes - doesn't work on OS X
Affected #:  1 file

diff -r b086dd0f0231566f60884b57b1efa00ce2dd2dfb -r d3d3554e2ea53df74878cf78a8fa5eaeff901af2 yt/visualization/volume_rendering/interactive_vr.py
--- a/yt/visualization/volume_rendering/interactive_vr.py
+++ b/yt/visualization/volume_rendering/interactive_vr.py
@@ -309,9 +309,6 @@
             ti = self.gl_texture_names[tex_i]
             GL.glBindTexture(GL.GL_TEXTURE_3D, ti)
             GL.glDrawArrays(GL.GL_TRIANGLES, tex_i*36, 36)
-
-        for n in ("model_vertex", "in_dx", "in_left_edge", "in_right_edge"):
-            self.disable_vert_attrib(shader_program, n)
         GL.glBindBuffer(GL.GL_ARRAY_BUFFER, 0)
 
     def _set_uniforms(self, shader_program):


https://bitbucket.org/yt_analysis/yt/commits/18fb5ace6cd8/
Changeset:   18fb5ace6cd8
Branch:      yt
User:        atmyers
Date:        2016-02-05 21:02:43+00:00
Summary:     docstring for Trackball
Affected #:  1 file

diff -r d3d3554e2ea53df74878cf78a8fa5eaeff901af2 -r 18fb5ace6cd8c8e397794efa55e49877de6e2ec2 yt/visualization/volume_rendering/interactive_vr.py
--- a/yt/visualization/volume_rendering/interactive_vr.py
+++ b/yt/visualization/volume_rendering/interactive_vr.py
@@ -52,6 +52,19 @@
 
 
 class TrackballCamera(object):
+    """
+
+    This class implements a basic "Trackball" or "Arcball" camera control system
+    that allows for unconstrained 3D rotations without suffering from Gimbal lock.
+    Following Ken Shoemake's orginal C implementation (Graphics Gems IV, III.1)
+    we project mouse movements onto the unit sphere and use quaternions to
+    represent the corresponding rotation.
+
+    See also:
+    https://en.wikibooks.org/wiki/OpenGL_Programming/Modern_OpenGL_Tutorial_Arcball
+
+    """
+
     def __init__(self, 
                  position=(0.0, 0.0, 1.0),
                  focus=(0.0, 0.0, 0.0),
@@ -75,8 +88,7 @@
         self.orientation = rotation_matrix_to_quaternion(rotation_matrix)
 
     def _map_to_surface(self, mouse_x, mouse_y):
-        # right now this just maps to the surface of
-        # the unit sphere
+        # right now this just maps to the surface of the unit sphere
         x, y = mouse_x, mouse_y
         mag = np.sqrt(x*x + y*y)
         if (mag > 1.0):
@@ -91,7 +103,7 @@
         old = self._map_to_surface(start_x, start_y)
         new = self._map_to_surface(end_x, end_y)
 
-        # dot product
+        # dot product controls the angle of the rotation
         w = old[0]*new[0] + old[1]*new[1] + old[2]*new[2]
 
         # cross product gives the rotation axis
@@ -145,12 +157,15 @@
         self.focus = np.array(focus)
 
     def get_viewpoint(self):
-        return position
+        return self.position
+
     def get_view_matrix(self):
         return get_lookat_matrix(self.position, self.focus, self.up)
+
     def get_projection_matrix(self):
         return get_perspective_matrix(np.radians(self.fov), self.aspect_ratio, self.near_plane,
                 self.far_plane)
+
     def update_position(self, theta, phi):
         rho = np.linalg.norm(self.position)
         curr_theta = np.arctan2( self.position[1], self.position[0] )


https://bitbucket.org/yt_analysis/yt/commits/3095b2630c68/
Changeset:   3095b2630c68
Branch:      yt
User:        atmyers
Date:        2016-02-05 21:12:24+00:00
Summary:     turn off FPS printing again
Affected #:  1 file

diff -r 18fb5ace6cd8c8e397794efa55e49877de6e2ec2 -r 3095b2630c68a3b63a815a1e481ee3c419db1351 yt/visualization/volume_rendering/interactive_loop.py
--- a/yt/visualization/volume_rendering/interactive_loop.py
+++ b/yt/visualization/volume_rendering/interactive_loop.py
@@ -200,7 +200,7 @@
             frame_start = glfw.GetTime()
             f += 1
             if f == N:
-                print "FPS:", N / float(frame_start - fps_start)
+                #print "FPS:", N / float(frame_start - fps_start)
                 fps_start = glfw.GetTime()
                 f = 0
 


https://bitbucket.org/yt_analysis/yt/commits/863f86e9cc0d/
Changeset:   863f86e9cc0d
Branch:      yt
User:        MatthewTurk
Date:        2016-02-06 16:12:00+00:00
Summary:     First pass at event loop for IPython with GLFW.  Not that nice yet.
Affected #:  3 files

diff -r 3095b2630c68a3b63a815a1e481ee3c419db1351 -r 863f86e9cc0dc214cc22a0dcb751969c492dc49b doc/source/cookbook/opengl_ipython.py
--- /dev/null
+++ b/doc/source/cookbook/opengl_ipython.py
@@ -0,0 +1,38 @@
+
+import yt
+import numpy as np
+from yt.visualization.volume_rendering.interactive_loop import \
+    SceneGraph, BlockCollection, TrackballCamera, RenderingContext
+import yt.visualization.volume_rendering.glfw_inputhook
+
+rc = RenderingContext(1280, 960)
+
+scene = SceneGraph()
+collection = BlockCollection()
+
+N = 64
+x, y, z = np.mgrid[0:1:N*1j, 0:1:N*1j, 0:1:N*1j]
+c = [-0.05, -0.05, -0.05]
+oor2 = ((x-c[0])**2 + (y-c[1])**2 + (z-c[2])**2)**-0.5
+np.clip(oor2, 1e-6, 1e60, oor2)
+data = {'x_field': 10**x, 'y_field': 10**y, 'z_field': 10**z, 'sphere':oor2}
+
+ds = yt.load_uniform_grid(data, [N, N, N], 1.0, nprocs=4)
+ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030")
+
+dd = ds.all_data()
+#collection.add_data(dd, "sphere")
+collection.add_data(dd, "density")
+
+scene.add_collection(collection)
+
+position = (1.0, 1.0, 1.0)
+c = TrackballCamera(position = position, focus = ds.domain_center,
+                    near_plane = 0.1)
+
+callbacks = rc.setup_loop(scene, c)
+rl = rc(scene, c, callbacks)
+
+# To make this work from IPython, you now do:
+#
+# yt.visualization.volume_rendering.glfw_inputhook.inputhook_manager.enable_gui("glfw", app=rl)

diff -r 3095b2630c68a3b63a815a1e481ee3c419db1351 -r 863f86e9cc0dc214cc22a0dcb751969c492dc49b yt/visualization/volume_rendering/glfw_inputhook.py
--- /dev/null
+++ b/yt/visualization/volume_rendering/glfw_inputhook.py
@@ -0,0 +1,111 @@
+# encoding: utf-8
+"""
+Enable pyglet to be used interacive by setting PyOS_InputHook.
+
+Authors
+-------
+
+* Nicolas P. Rougier
+* Fernando Perez
+"""
+
+#-----------------------------------------------------------------------------
+#  Copyright (C) 2008-2011  The IPython Development Team
+#
+#  Distributed under the terms of the BSD License.  The full license is in
+#  the file COPYING, distributed as part of this software.
+#-----------------------------------------------------------------------------
+
+# This has been modified from the Pyglet and GLUT event hooks to work with
+# glfw.
+
+#-----------------------------------------------------------------------------
+# Imports
+#-----------------------------------------------------------------------------
+
+import os
+import sys
+import time
+from timeit import default_timer as clock
+
+#-----------------------------------------------------------------------------
+# Platform-dependent imports and functions
+#-----------------------------------------------------------------------------
+
+if os.name == 'posix':
+    import select
+
+    def stdin_ready():
+        infds, outfds, erfds = select.select([sys.stdin],[],[],0)
+        if infds:
+            return True
+        else:
+            return False
+
+elif sys.platform == 'win32':
+    import msvcrt
+
+    def stdin_ready():
+        return msvcrt.kbhit()
+
+#-----------------------------------------------------------------------------
+# Code
+#-----------------------------------------------------------------------------
+
+def create_inputhook_glfw(mgr, render_loop):
+    """Run the GLFW event loop by processing pending events only.
+
+    This keeps processing pending events until stdin is ready.  After
+    processing all pending events, a call to time.sleep is inserted.  This is
+    needed, otherwise, CPU usage is at 100%.  This sleep time should be tuned
+    though for best performance.
+    """
+    def inputhook_glfw():
+        # We need to protect against a user pressing Control-C when IPython is
+        # idle and this is running. We trap KeyboardInterrupt and pass.
+        import cyglfw3 as glfw
+        try:
+            t = glfw.GetTime()
+            while not stdin_ready():
+                render_loop.next()
+
+                used_time = glfw.GetTime() - t
+                if used_time > 10.0:
+                    # print 'Sleep for 1 s'  # dbg
+                    time.sleep(1.0)
+                elif used_time > 0.1:
+                    # Few GUI events coming in, so we can sleep longer
+                    # print 'Sleep for 0.05 s'  # dbg
+                    time.sleep(0.05)
+                else:
+                    # Many GUI events coming in, so sleep only very little
+                    time.sleep(0.001)
+        except KeyboardInterrupt:
+            pass
+        return 0
+    return inputhook_glfw
+
+from IPython.lib.inputhook import inputhook_manager, InputHookBase
+
+ at inputhook_manager.register('glfw')
+class GLFWInputHook(InputHookBase):
+    def enable(self, app=None):
+        """Enable event loop integration with GLFW.
+
+        Parameters
+        ----------
+        app : ignored
+           Ignored, it's only a placeholder to keep the call signature of all
+           gui activation methods consistent, which simplifies the logic of
+           supporting magics.
+
+        Notes
+        -----
+        This methods sets the ``PyOS_InputHook`` for GLFW, which allows
+        GLFW to integrate with terminal based applications like
+        IPython.
+
+        """
+        inputhook_glfw = create_inputhook_glfw(self.manager, app)
+        self.manager.set_inputhook(inputhook_glfw)
+        return

diff -r 3095b2630c68a3b63a815a1e481ee3c419db1351 -r 863f86e9cc0dc214cc22a0dcb751969c492dc49b yt/visualization/volume_rendering/interactive_loop.py
--- a/yt/visualization/volume_rendering/interactive_loop.py
+++ b/yt/visualization/volume_rendering/interactive_loop.py
@@ -144,6 +144,7 @@
         return True
         
 class RenderingContext(object):
+    should_quit = False
     def __init__(self, width = 800, height = 600, title = "vol_render"):
         glfw.Init()
         glfw.WindowHint(glfw.CONTEXT_VERSION_MAJOR, 3)
@@ -161,14 +162,12 @@
         glfw.SwapBuffers(self.window)
         glfw.PollEvents()
 
-    def start_loop(self, scene, camera):
+    def setup_loop(self, scene, camera):
         scene.set_camera(camera)
         scene.add_shader_from_file("max_intensity_frag.glsl")
         camera.compute_matrices()
         frame_start = glfw.GetTime()
         fps_start = glfw.GetTime()
-        f = 0
-        N = 10.0
         print "Starting rendering..."
         callbacks = Events(camera)
         callbacks.add_key_callback(close_window, "escape")
@@ -188,7 +187,15 @@
         glfw.SetKeyCallback(self.window, callbacks.key_call)
         glfw.SetMouseButtonCallback(self.window, callbacks.mouse_call)
         callbacks.draw = True
-        while not glfw.WindowShouldClose(self.window):
+        return callbacks
+
+    def start_loop(self, scene, camera):
+        callbacks = self.setup_loop(scene, camera)
+        for i in self(scene, camera, callbacks):
+            pass
+
+    def __call__(self, scene, camera, callbacks):
+        while not glfw.WindowShouldClose(self.window) or self.should_quit:
             callbacks(self.window)
             if callbacks.draw:
                 camera.compute_matrices()
@@ -197,12 +204,5 @@
                 glfw.SwapBuffers(self.window)
                 callbacks.draw = False
             glfw.PollEvents()
-            frame_start = glfw.GetTime()
-            f += 1
-            if f == N:
-                #print "FPS:", N / float(frame_start - fps_start)
-                fps_start = glfw.GetTime()
-                f = 0
-
-        print "Finished rendering"
+            yield self
         glfw.Terminate()


https://bitbucket.org/yt_analysis/yt/commits/988840417511/
Changeset:   988840417511
Branch:      yt
User:        xarthisius
Date:        2016-02-10 23:32:40+00:00
Summary:     Add initial support for two pass rendering
Affected #:  9 files

diff -r 863f86e9cc0dc214cc22a0dcb751969c492dc49b -r 988840417511fb6d9a42ac216ee1547c955b2bb2 yt/utilities/exceptions.py
--- a/yt/utilities/exceptions.py
+++ b/yt/utilities/exceptions.py
@@ -192,7 +192,7 @@
         self.unit = unit
         self.units_base = units_base
         YTException.__init__(self)
-        
+
     def __str__(self):
         err = "The unit '%s' cannot be reduced to a single expression within " \
           "the %s base system of units." % (self.unit, self.units_base)
@@ -203,13 +203,13 @@
         self.old_units = old_units
         self.new_units = new_units
         self.base = base
-    
+
     def __str__(self):
         err = "It looks like you're trying to convert between '%s' and '%s'. Try " \
-          "using \"to_equivalent('%s', '%s')\" instead." % (self.old_units, self.new_units, 
+          "using \"to_equivalent('%s', '%s')\" instead." % (self.old_units, self.new_units,
                                                             self.new_units, self.base)
         return err
-    
+
 class YTUfuncUnitError(YTException):
     def __init__(self, ufunc, unit1, unit2):
         self.ufunc = ufunc
@@ -530,3 +530,10 @@
     def __str__(self):
         return 'Dimensionality specified was %s but we need %s' % (
             self.wrong, self.right)
+
+class YTInvalidShaderType(YTException):
+    def __init__(self, source):
+        self.source = source
+
+    def __str__(self):
+        return "Can't identify shader_type for file '%s.'" % (self.source)

diff -r 863f86e9cc0dc214cc22a0dcb751969c492dc49b -r 988840417511fb6d9a42ac216ee1547c955b2bb2 yt/visualization/volume_rendering/interactive_loop.py
--- a/yt/visualization/volume_rendering/interactive_loop.py
+++ b/yt/visualization/volume_rendering/interactive_loop.py
@@ -1,5 +1,7 @@
 import cyglfw3 as glfw
 import numpy as np
+import random
+import matplotlib.cm as cm
 import OpenGL.GL as GL
 from collections import defaultdict
 
@@ -87,6 +89,14 @@
         np.linalg.norm(camera.position - camera.focus)
     return True
 
+def cmap_cycle(camera, window, key, scancode, action, mods):
+    cmap = ['algae', 'kamae', 'viridis', 'inferno', 'magma']
+    cmap = cm.get_cmap(random.choice(cmap))
+    camera.cmap = np.array(cmap(np.linspace(0, 1, 256)), dtype=np.float32)
+    camera.cmap_new = True
+    print("Setting colormap to {}".format(cmap.name))
+    return True
+
 def closeup(camera, window, key, scancode, action, mods):
     camera.position = (0.01, 0.01, 0.01)
     return True
@@ -142,7 +152,7 @@
                                   new_end[1])
         self.start = new_end
         return True
-        
+
 class RenderingContext(object):
     should_quit = False
     def __init__(self, width = 800, height = 600, title = "vol_render"):
@@ -164,7 +174,7 @@
 
     def setup_loop(self, scene, camera):
         scene.set_camera(camera)
-        scene.add_shader_from_file("max_intensity_frag.glsl")
+        scene.add_shader_from_file("max_intensity.fragmentshader")
         camera.compute_matrices()
         frame_start = glfw.GetTime()
         fps_start = glfw.GetTime()
@@ -174,6 +184,7 @@
         callbacks.add_key_callback(zoomin, "w")
         callbacks.add_key_callback(zoomout, "s")
         callbacks.add_key_callback(closeup, "z")
+        callbacks.add_key_callback(cmap_cycle, "c")
         callbacks.add_key_callback(reset, "r")
         mouse_callbacks = MouseRotation()
         callbacks.add_mouse_callback(mouse_callbacks.start_rotation,

diff -r 863f86e9cc0dc214cc22a0dcb751969c492dc49b -r 988840417511fb6d9a42ac216ee1547c955b2bb2 yt/visualization/volume_rendering/interactive_vr.py
--- a/yt/visualization/volume_rendering/interactive_vr.py
+++ b/yt/visualization/volume_rendering/interactive_vr.py
@@ -1,6 +1,6 @@
 import os
+import re
 import OpenGL.GL as GL
-from OpenGL.GL import shaders
 
 import numpy as np
 from yt.utilities.math_utils import \
@@ -11,7 +11,44 @@
     quaternion_mult, \
     quaternion_to_rotation_matrix, \
     rotation_matrix_to_quaternion
-    
+from yt.utilities.exceptions import YTInvalidShaderType
+
+import matplotlib.cm as cm
+
+def _compile_shader(source, shader_type=None):
+    if shader_type is None:
+        try:
+            shader_type = re.match("^.*\.(vertex|fragment)shader$",
+                                   source).groups()[0]
+        except AttributeError:
+            raise YTInvalidShaderType(source)
+
+    sh_directory = os.path.join(os.path.dirname(__file__), "shaders")
+    shader = GL.glCreateShader(
+        eval("GL.GL_{}_SHADER".format(shader_type.upper()))
+    )
+
+    with open(os.path.join(sh_directory, source), 'r') as fp:
+        GL.glShaderSource(shader, fp.read())
+    GL.glCompileShader(shader)
+    result = GL.glGetShaderiv(shader, GL.GL_COMPILE_STATUS)
+    if not(result):
+        raise RuntimeError(GL.glGetShaderInfoLog(shader))
+    return shader
+
+def link_shader_program(shaders):
+    """Create a shader program with from compiled shaders."""
+    program = GL.glCreateProgram()
+    for shader in shaders:
+        GL.glAttachShader(program, _compile_shader(shader))
+    GL.glLinkProgram(program)
+    # check linking error
+    result = GL.glGetProgramiv(program, GL.GL_LINK_STATUS)
+    if not(result):
+        raise RuntimeError(GL.glGetProgramInfoLog(program))
+    return program
+
+
 bbox_vertices = np.array(
       [[ 0.,  0.,  0.,  1.],
        [ 0.,  0.,  1.,  1.],
@@ -48,8 +85,16 @@
        [ 0.,  1.,  1.,  1.],
        [ 1.,  1.,  1.,  1.],
        [ 0.,  1.,  1.,  1.],
-       [ 1.,  0.,  1.,  1.]])
+       [ 1.,  0.,  1.,  1.]], dtype=np.float32)
 
+FULLSCREEN_QUAD = np.array(
+    [-1.0, -1.0, 0.0,
+     +1.0, -1.0, 0.0,
+     -1.0, +1.0, 0.0,
+     -1.0, +1.0, 0.0,
+     +1.0, -1.0, 0.0,
+     +1.0, +1.0, 0.0], dtype=np.float32
+)
 
 class TrackballCamera(object):
     """
@@ -65,7 +110,7 @@
 
     """
 
-    def __init__(self, 
+    def __init__(self,
                  position=(0.0, 0.0, 1.0),
                  focus=(0.0, 0.0, 0.0),
                  up=(0.0, 1.0, 0.0),
@@ -79,8 +124,11 @@
         self.far_plane = far_plane
         self.aspect_ratio = aspect_ratio
         self.up = np.array(up)
+        cmap = cm.get_cmap("algae")
+        self.cmap = np.array(cmap(np.linspace(0, 1, 256)), dtype=np.float32)
+        self.cmap_new = True
 
-        self.view_matrix = get_lookat_matrix(self.position, 
+        self.view_matrix = get_lookat_matrix(self.position,
                                              self.focus,
                                              self.up)
 
@@ -125,8 +173,8 @@
         self.position = dp + self.focus
         self.up = rotation_matrix[1]
 
-        self.view_matrix = get_lookat_matrix(self.position, 
-                                             self.focus, 
+        self.view_matrix = get_lookat_matrix(self.position,
+                                             self.focus,
                                              self.up)
 
         self.projection_matrix = get_perspective_matrix(np.radians(self.fov),
@@ -178,6 +226,7 @@
         self.position[1] = rho * np.sin(curr_phi) * np.sin(curr_theta)
         self.position[2] = rho * np.cos(curr_phi)
 
+
 class BlockCollection:
     def __init__(self):
         self.vert_attrib = {}
@@ -264,8 +313,7 @@
         dx = np.concatenate(dx)
         le = np.concatenate(le)
         re = np.concatenate(re)
-        print vert.shape, dx.shape
-        if self.gl_vao_name != None:
+        if self.gl_vao_name is not None:
             GL.glDeleteVertexArrays(1, [self.gl_vao_name])
         self.gl_vao_name = GL.glGenVertexArrays(1)
 
@@ -274,9 +322,8 @@
         self.add_vert_attrib("in_left_edge", le)
         self.add_vert_attrib("in_right_edge", re)
 
-        # Now we set up our 
+        # Now we set up our
         self._load_textures()
-        redraw = True
 
     def set_camera(self, camera):
         r"""Sets the camera for the block collection.
@@ -311,6 +358,7 @@
         self.bind_vert_attrib(shader_program, "in_left_edge", 3)
         self.bind_vert_attrib(shader_program, "in_right_edge", 3)
 
+        # clear the color and depth buffer
         GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT)
 
         self._set_uniforms(shader_program)
@@ -324,8 +372,17 @@
             ti = self.gl_texture_names[tex_i]
             GL.glBindTexture(GL.GL_TEXTURE_3D, ti)
             GL.glDrawArrays(GL.GL_TRIANGLES, tex_i*36, 36)
+
+        # # This breaks on OSX, since it was already removed once, and re-added
+        # # twice, I'm leaving this as a comment
+        # for attrib in ["model_vertex", "in_dx",
+        #                "in_left_edge", "in_right_edge"]:
+        #     self.disable_vert_attrib(shader_program, attrib)
+
+        # Release bind
         GL.glBindBuffer(GL.GL_ARRAY_BUFFER, 0)
 
+
     def _set_uniforms(self, shader_program):
         project_loc = GL.glGetUniformLocation(shader_program, "projection")
         lookat_loc = GL.glGetUniformLocation(shader_program, "lookat")
@@ -333,7 +390,6 @@
 
         project = self.camera.get_projection_matrix()
         view = self.camera.get_view_matrix()
-
         viewport = np.array(GL.glGetIntegerv(GL.GL_VIEWPORT), dtype = 'float32')
 
         GL.glUniformMatrix4fv(project_loc, 1, GL.GL_TRUE, project)
@@ -383,16 +439,127 @@
                         GL.GL_RED, GL.GL_FLOAT, n_data.T)
             GL.glGenerateMipmap(GL.GL_TEXTURE_3D)
 
+
 class SceneGraph:
     def __init__(self):
         self.collections = []
+        self.fb_uniforms = {}
+        self.fbo = None
+        self.fb_texture = None
+        self.cmap_texture = None
         self.camera = None
-
-        self.gl_vert_shader = None
-        self.gl_frag_shader = None
         self.shader_program = None
 
-        self.camera = None
+        ox, oy, width, height = GL.glGetIntegerv(GL.GL_VIEWPORT)
+        self.width = width
+        self.height = height
+
+        self.fb_shader_program = link_shader_program(
+            ["passthrough.vertexshader", "apply_colormap.fragmentshader"]
+        )
+        for key in ["fb_texture", "cmap"]:
+            self.fb_uniforms[key] = \
+                GL.glGetUniformLocation(self.fb_shader_program, key)
+
+        self.fb_vao_name = GL.glGenVertexArrays(1)
+        GL.glBindVertexArray(self.fb_vao_name)
+
+        quad_attrib = GL.glGenBuffers(1)
+        GL.glBindBuffer(GL.GL_ARRAY_BUFFER, quad_attrib)
+        GL.glBufferData(GL.GL_ARRAY_BUFFER, FULLSCREEN_QUAD.nbytes,
+                        FULLSCREEN_QUAD, GL.GL_STATIC_DRAW)
+        GL.glVertexAttribPointer(0, 3, GL.GL_FLOAT, GL.GL_FALSE, 0, None)
+
+        # unbind
+        GL.glBindBuffer(GL.GL_ARRAY_BUFFER, 0)
+        GL.glBindVertexArray(0)
+
+        self.setup_fb(self.width, self.height)
+
+    def setup_cmap_tex(self):
+        '''Creates 1D texture that will hold colormap in framebuffer'''
+        self.cmap_texture = GL.glGenTextures(1)   # create target texture
+        GL.glBindTexture(GL.GL_TEXTURE_1D, self.cmap_texture)
+        GL.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1)
+        GL.glTexParameterf(GL.GL_TEXTURE_1D, GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP_TO_EDGE)
+        GL.glTexParameteri(GL.GL_TEXTURE_1D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR)
+        GL.glTexParameteri(GL.GL_TEXTURE_1D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR)
+        GL.glTexImage1D(GL.GL_TEXTURE_1D, 0, GL.GL_RGBA, 256,
+                        0, GL.GL_RGBA, GL.GL_FLOAT, self.camera.cmap)
+        GL.glBindTexture(GL.GL_TEXTURE_1D, 0)
+
+
+    def update_cmap_tex(self):
+        '''Updates 1D texture with colormap that's used in framebuffer'''
+        if self.camera is None or not self.camera.cmap_new:
+            return
+
+        if self.cmap_texture is None:
+            self.setup_cmap_tex()
+
+        GL.glBindTexture(GL.GL_TEXTURE_1D, self.cmap_texture)
+        GL.glTexSubImage1D(GL.GL_TEXTURE_1D, 0, 0, 256,
+                           GL.GL_RGBA, GL.GL_FLOAT, self.camera.cmap)
+        GL.glBindTexture(GL.GL_TEXTURE_1D, 0)
+
+        self.camera.cmap_new = False
+
+
+    def setup_fb(self, width, height):
+        '''Setups FrameBuffer that will be used as container
+           for 1 pass of rendering'''
+        # Clean up old FB and Texture
+        if self.fb_texture is not None and \
+            GL.glIsTexture(self.fb_texture):
+                GL.glDeleteTextures([self.fb_texture])
+        if self.fbo is not None and GL.glIsFramebuffer(self.fbo):
+            GL.glDeleteFramebuffers(1, [self.fbo])
+
+
+        # initialize FrameBuffer
+        self.fbo = GL.glGenFramebuffers(1)
+        GL.glBindFramebuffer(GL.GL_FRAMEBUFFER, self.fbo)
+
+        depthbuffer = GL.glGenRenderbuffers(1)
+        GL.glBindRenderbuffer(GL.GL_RENDERBUFFER, depthbuffer)
+        GL.glRenderbufferStorage(GL.GL_RENDERBUFFER, GL.GL_DEPTH_COMPONENT24,
+                                 width, height)
+        GL.glFramebufferRenderbuffer(
+            GL.GL_FRAMEBUFFER, GL.GL_DEPTH_ATTACHMENT, GL.GL_RENDERBUFFER,
+            depthbuffer
+        )
+        # end of FrameBuffer initialization
+
+        # generate the texture we render to, and set parameters
+        self.fb_texture = GL.glGenTextures(1)   # create target texture
+        # bind to new texture, all future texture functions will modify this
+        # particular one
+        GL.glBindTexture(GL.GL_TEXTURE_2D, self.fb_texture)
+        # set how our texture behaves on x,y boundaries
+        GL.glTexParameterf(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S, GL.GL_REPEAT)
+        GL.glTexParameterf(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_T, GL.GL_REPEAT)
+        # set how our texture is filtered
+        GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR)
+        GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR)
+
+        # occupy width x height texture memory, (None at the end == empty
+        # image)
+        GL.glTexImage2D(GL.GL_TEXTURE_2D, 0, GL.GL_RGBA, width,
+                        height, 0, GL.GL_RGBA, GL.GL_UNSIGNED_INT, None)
+
+        # --- end texture init
+
+        # Set "fb_texture" as our colour attachement #0
+        GL.glFramebufferTexture2D(
+            GL.GL_FRAMEBUFFER, GL.GL_COLOR_ATTACHMENT0, GL.GL_TEXTURE_2D,
+            self.fb_texture,
+            0 # mipmap level, normally 0
+        )
+
+        # verify that everything went well
+        status = GL.glCheckFramebufferStatus(GL.GL_FRAMEBUFFER)
+        assert status == GL.GL_FRAMEBUFFER_COMPLETE, status
+
 
     def add_collection(self, collection):
         r"""Adds a block collection to the scene. Collections must not overlap.
@@ -438,23 +605,9 @@
             The location of the shader source file to read.
 
         """
-        vr_directory = os.path.dirname(__file__)
-        frag_path = "shaders/" + filename
-        vert_path = "shaders/vert.glsl"
-        frag_abs = os.path.join(vr_directory, frag_path)
-        vert_abs = os.path.join(vr_directory, vert_path)
-
-        shader_file = open(frag_abs, 'r')
-        self.gl_frag_shader = shaders.compileShader(shader_file.read(), GL.GL_FRAGMENT_SHADER)
-
-        vert_file = open(vert_abs, 'r')
-        self.gl_vert_shader = shaders.compileShader(vert_file.read(), GL.GL_VERTEX_SHADER)
-
-        self.shader_program = GL.glCreateProgram()
-        GL.glAttachShader(self.shader_program, self.gl_vert_shader)
-        GL.glAttachShader(self.shader_program, self.gl_frag_shader)
-
-        GL.glLinkProgram(self.shader_program)
+        self.shader_program = link_shader_program(
+            ['default.vertexshader', filename]
+        )
 
     def render(self):
         """ Renders one frame of the scene.
@@ -464,5 +617,49 @@
         provided to the add_shader_from_file function.
 
         """
+
+        # get size of current viewport
+        ox, oy, width, height = GL.glGetIntegerv(GL.GL_VIEWPORT)
+        if (width, height) != (self.width, self.height):
+            # size of viewport changed => fb needs to be recreated
+            self.setup_fb(width, height)
+            self.width = width
+            self.width = height
+
+        # Handle colormap
+        self.update_cmap_tex()
+
+        # bind to fb
+        GL.glBindFramebuffer(GL.GL_FRAMEBUFFER, self.fbo)
+        # clear the color and depth buffer
+        GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT)
+
+        # render collections to fb
         for collection in self.collections:
             collection.run_program(self.shader_program)
+        # unbind FB
+        GL.glBindFramebuffer(GL.GL_FRAMEBUFFER, 0)
+
+        # 2 pass of rendering
+        GL.glUseProgram(self.fb_shader_program)
+        GL.glActiveTexture(GL.GL_TEXTURE0)
+        # bind to the result of 1 pass
+        GL.glBindTexture(GL.GL_TEXTURE_2D, self.fb_texture)
+        # Set our "fb_texture" sampler to user Texture Unit 0
+        GL.glUniform1i(self.fb_uniforms["fb_texture"], 0);
+
+        GL.glActiveTexture(GL.GL_TEXTURE1)
+        GL.glBindTexture(GL.GL_TEXTURE_1D, self.cmap_texture)
+        GL.glUniform1i(self.fb_uniforms["cmap"], 1);
+
+        # clear the color and depth buffer
+        GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT)
+        # Bind to Vertex array that contains simple quad filling fullscreen,
+        # that was defined in __init__()
+        GL.glBindVertexArray(self.fb_vao_name)
+        GL.glEnableVertexAttribArray(0)
+        # Draw our 2 triangles
+        GL.glDrawArrays(GL.GL_TRIANGLES, 0, 6)
+        # Clean up
+        GL.glDisableVertexAttribArray(0)
+        GL.glBindVertexArray(0)

diff -r 863f86e9cc0dc214cc22a0dcb751969c492dc49b -r 988840417511fb6d9a42ac216ee1547c955b2bb2 yt/visualization/volume_rendering/shaders/apply_colormap.fragmentshader
--- /dev/null
+++ b/yt/visualization/volume_rendering/shaders/apply_colormap.fragmentshader
@@ -0,0 +1,12 @@
+#version 330 core
+
+in vec2 UV;
+
+out vec4 color;
+
+uniform sampler2D fb_texture;
+uniform sampler1D cmap;
+
+void main(){
+   color = texture(cmap, texture(fb_texture, UV).x);
+}

diff -r 863f86e9cc0dc214cc22a0dcb751969c492dc49b -r 988840417511fb6d9a42ac216ee1547c955b2bb2 yt/visualization/volume_rendering/shaders/default.vertexshader
--- /dev/null
+++ b/yt/visualization/volume_rendering/shaders/default.vertexshader
@@ -0,0 +1,30 @@
+#version 330
+in vec4 model_vertex; // The location of the vertex in model space
+in vec3 in_dx;
+in vec3 in_left_edge;
+in vec3 in_right_edge;
+out vec4 v_model;
+out vec3 v_camera_pos;
+flat out mat4 inverse_proj;
+flat out mat4 inverse_view;
+out vec3 dx;
+out vec3 left_edge;
+out vec3 right_edge;
+
+//// Uniforms
+uniform vec3 camera_pos;
+uniform mat4 lookat;
+uniform mat4 projection;
+
+void main()
+{
+    v_model = model_vertex;
+    v_camera_pos = camera_pos;
+    inverse_proj = inverse(projection);
+    inverse_view = inverse(lookat);
+    gl_Position = projection * lookat * model_vertex;
+    dx = vec3(in_dx);
+    left_edge = vec3(in_left_edge);
+    right_edge = vec3(in_right_edge);
+
+}

diff -r 863f86e9cc0dc214cc22a0dcb751969c492dc49b -r 988840417511fb6d9a42ac216ee1547c955b2bb2 yt/visualization/volume_rendering/shaders/max_intensity.fragmentshader
--- /dev/null
+++ b/yt/visualization/volume_rendering/shaders/max_intensity.fragmentshader
@@ -0,0 +1,60 @@
+#version 330
+in vec4 v_model;
+in vec3 v_camera_pos;
+in vec3 dx;
+in vec3 left_edge;
+in vec3 right_edge;
+flat in mat4 inverse_proj;
+flat in mat4 inverse_view;
+out vec4 output_color;
+
+uniform sampler3D ds_tex;
+//layout (binding = 1) uniform sampler2D depth_tex;
+uniform vec4 viewport; // (offset_x, offset_y, 1 / screen_x, 1 / screen_y)
+
+bool within_bb(vec3 pos)
+{
+    bvec3 left =  greaterThanEqual(pos, left_edge);
+    bvec3 right = lessThanEqual(pos, right_edge);
+    return all(left) && all(right);
+}
+
+void main()
+{
+    // Obtain screen coordinates
+    // https://www.opengl.org/wiki/Compute_eye_space_from_window_space#From_gl_FragCoord
+    vec4 ndcPos;
+    ndcPos.xy = ((2.0 * gl_FragCoord.xy) - (2.0 * viewport.xy)) / (viewport.zw) - 1;
+    ndcPos.z = (2.0 * gl_FragCoord.z - 1.0);
+    ndcPos.w = 1.0;
+
+    vec4 clipPos = ndcPos / gl_FragCoord.w;
+    vec4 eyePos = inverse_proj * clipPos;
+    eyePos /= eyePos.w;
+
+    vec4 world_location = inverse_view * eyePos;
+
+    // Five samples
+    vec3 step_size = dx / 5.0;
+    vec3 dir = normalize(world_location.xyz - v_camera_pos.xyz);
+    vec3 curr_color = vec3(0.0);
+
+    vec3 ray_position = world_location.xyz;
+
+    vec3 tex_curr_pos = vec3(0.0);
+    vec3 range = right_edge - left_edge;
+    bool ray_in_bb = true;
+    while (ray_in_bb) {
+        tex_curr_pos = (ray_position - left_edge)/range;
+
+        vec3 tex_sample = texture(ds_tex, tex_curr_pos).rgb;
+        if (length(curr_color) < length(tex_sample)) {
+            curr_color = tex_sample;
+        }
+
+        ray_position += dir * step_size;
+        ray_in_bb = within_bb(ray_position);
+    }
+
+    output_color = vec4(curr_color.rrr, 1.0);
+}

diff -r 863f86e9cc0dc214cc22a0dcb751969c492dc49b -r 988840417511fb6d9a42ac216ee1547c955b2bb2 yt/visualization/volume_rendering/shaders/max_intensity_frag.glsl
--- a/yt/visualization/volume_rendering/shaders/max_intensity_frag.glsl
+++ /dev/null
@@ -1,59 +0,0 @@
-#version 330
-in vec4 v_model;
-in vec3 v_camera_pos;
-in vec3 dx;
-in vec3 left_edge;
-in vec3 right_edge;
-flat in mat4 inverse_proj;
-flat in mat4 inverse_view;
-out vec4 output_color;
-
-uniform sampler3D ds_tex;
-//layout (binding = 1) uniform sampler2D depth_tex;
-uniform vec4 viewport; // (offset_x, offset_y, 1 / screen_x, 1 / screen_y)
-
-bool within_bb(vec3 pos)
-{
-    bvec3 left =  greaterThanEqual(pos, left_edge);
-    bvec3 right = lessThanEqual(pos, right_edge);
-    return all(left) && all(right);
-}
-
-void main()
-{
-    // Obtain screen coordinates
-    // https://www.opengl.org/wiki/Compute_eye_space_from_window_space#From_gl_FragCoord
-    vec4 ndcPos;
-    ndcPos.xy = ((2.0 * gl_FragCoord.xy) - (2.0 * viewport.xy)) / (viewport.zw) - 1;
-    ndcPos.z = (2.0 * gl_FragCoord.z - 1.0);
-    ndcPos.w = 1.0;
-
-    vec4 clipPos = ndcPos / gl_FragCoord.w;
-    vec4 eyePos = inverse_proj * clipPos;
-    eyePos /= eyePos.w;
-
-    vec4 world_location = inverse_view * eyePos;
-
-    // Five samples
-    vec3 step_size = dx / 5.0;
-    vec3 dir = normalize(world_location.xyz - v_camera_pos.xyz);
-    vec3 curr_color = vec3(0.0);
-
-    vec3 ray_position = world_location.xyz;
-
-    vec3 tex_curr_pos = vec3(0.0);
-    vec3 range = right_edge - left_edge;
-    bool ray_in_bb = true;
-    while (ray_in_bb) {
-        tex_curr_pos = (ray_position - left_edge)/range;
-
-        vec3 tex_sample = texture(ds_tex, tex_curr_pos).rgb;
-        if (length(curr_color) < length(tex_sample)) {
-            curr_color = tex_sample;
-        }
-
-        ray_position += dir * step_size;
-        ray_in_bb = within_bb(ray_position);
-    }
-    output_color = vec4(curr_color.rrr, 1.0);
-}

diff -r 863f86e9cc0dc214cc22a0dcb751969c492dc49b -r 988840417511fb6d9a42ac216ee1547c955b2bb2 yt/visualization/volume_rendering/shaders/passthrough.vertexshader
--- /dev/null
+++ b/yt/visualization/volume_rendering/shaders/passthrough.vertexshader
@@ -0,0 +1,13 @@
+#version 330 core
+
+// Input vertex data, different for all executions of this shader.
+layout(location = 0) in vec3 vertexPosition_modelspace;
+
+// Output data ; will be interpolated for each fragment.
+out vec2 UV;
+
+void main()
+{
+    gl_Position = vec4(vertexPosition_modelspace, 1);
+    UV = (vertexPosition_modelspace.xy+vec2(1,1))/2.0;
+}

diff -r 863f86e9cc0dc214cc22a0dcb751969c492dc49b -r 988840417511fb6d9a42ac216ee1547c955b2bb2 yt/visualization/volume_rendering/shaders/vert.glsl
--- a/yt/visualization/volume_rendering/shaders/vert.glsl
+++ /dev/null
@@ -1,30 +0,0 @@
-#version 330
-in vec4 model_vertex; // The location of the vertex in model space
-in vec3 in_dx;
-in vec3 in_left_edge;
-in vec3 in_right_edge;
-out vec4 v_model;
-out vec3 v_camera_pos;
-flat out mat4 inverse_proj;
-flat out mat4 inverse_view;
-out vec3 dx;
-out vec3 left_edge;
-out vec3 right_edge;
-
-//// Uniforms
-uniform vec3 camera_pos;
-uniform mat4 lookat;
-uniform mat4 projection;
-
-void main()
-{
-    v_model = model_vertex;
-    v_camera_pos = camera_pos;
-    inverse_proj = inverse(projection);
-    inverse_view = inverse(lookat);
-    gl_Position = projection * lookat * model_vertex;
-    dx = vec3(in_dx);
-    left_edge = vec3(in_left_edge);
-    right_edge = vec3(in_right_edge);
-
-}


https://bitbucket.org/yt_analysis/yt/commits/64588cbc3903/
Changeset:   64588cbc3903
Branch:      yt
User:        MatthewTurk
Date:        2016-02-11 19:03:07+00:00
Summary:     Split events into their own file, self-register them
Affected #:  2 files

diff -r 988840417511fb6d9a42ac216ee1547c955b2bb2 -r 64588cbc3903673e08e43483d27cc19946974be4 yt/visualization/volume_rendering/input_events.py
--- /dev/null
+++ b/yt/visualization/volume_rendering/input_events.py
@@ -0,0 +1,171 @@
+from collections import defaultdict
+from functools import wraps
+import OpenGL.GL as GL
+import cyglfw3 as glfw
+import numpy as np
+import matplotlib.cm as cm
+import random
+
+event_registry = {}
+
+class EventCollection(object):
+    def __init__(self, camera):
+        self.key_callbacks = defaultdict(list)
+        self.mouse_callbacks = defaultdict(list)
+        self.framebuffer_callbacks = []
+        self.render_events = []
+        self.camera = camera
+        self.draw = True
+
+    def key_call(self, window, key, scancode, action, mods):
+        draw = False
+        for f in self.key_callbacks[key, action, mods]:
+            draw = f(self.camera, window, key, scancode, action, mods) or draw
+        self.draw = self.draw or draw
+
+    def mouse_call(self, window, key, action, mods):
+        draw = False
+        for f in self.mouse_callbacks[key, action, mods]:
+            draw = f(self.camera, window, key, action, mods) or draw
+        self.draw = self.draw or draw
+
+    def framebuffer_call(self, window, width, height):
+        draw = False
+        for f in self.framebuffer_callbacks:
+            draw = f(self.camera, window, width, height) or draw
+        self.draw = self.draw or draw
+
+    def __call__(self, window):
+        draw = False
+        for f in self.render_events:
+            draw = f(self.camera, window) or draw
+        self.draw = self.draw or draw
+
+    def add_render_callback(self, func):
+        self.render_events.append(func)
+
+    def add_key_callback(self, func, key, action = "press", mods = None):
+        self._add_callback(self.key_callbacks, func, key, action, mods)
+
+    def add_mouse_callback(self, func, key, action = "press", mods = None):
+        self._add_callback(self.mouse_callbacks, func, key, action, mods)
+
+    def add_framebuffer_callback(self, func):
+        self.framebuffer_callbacks.append(func)
+
+    def _add_callback(self, d, func, key, action, mods):
+        if not callable(func):
+            func = event_registry[func]
+        if isinstance(key, str):
+            key = getattr(glfw, "KEY_%s" % key.upper())
+        if isinstance(action, str):
+            action = getattr(glfw, action.upper())
+        if not isinstance(mods, tuple):
+            mods = (mods, )
+        mod = 0
+        for m in mods:
+            if isinstance(m, str):
+                m = getattr(glfw, "MOD_%s" % m.upper())
+            elif m is None:
+                m = 0
+            mod |= m
+        # We can allow for multiple
+        d[key, action, mod].append(func)
+
+def register_event(name):
+    def _f(func):
+        event_registry[name] = func
+        return func
+    return _f
+
+ at register_event("framebuffer_size")
+def framebuffer_size_callback(camera, window, width, height):
+    GL.glViewport(0, 0, width, height)
+    camera.aspect_ratio = float(width)/height
+    return True
+
+ at register_event("close_window")
+def close_window(camera, window, key, scancode, action, mods):
+    glfw.SetWindowShouldClose(window, True)
+
+ at register_event("zoomin")
+def zoomin(camera, window, key, scancode, action, mods):
+    camera.position -= 0.05 * (camera.position - camera.focus) / \
+                np.linalg.norm(camera.position - camera.focus)
+    print camera.position, camera.focus
+    return True
+
+ at register_event("zoomout")
+def zoomout(camera, window, key, scancode, action, mods):
+    camera.position += 0.05 * (camera.position - camera.focus) / \
+        np.linalg.norm(camera.position - camera.focus)
+    return True
+
+ at register_event("cmap_cycle")
+def cmap_cycle(camera, window, key, scancode, action, mods):
+    cmap = ['algae', 'kamae', 'viridis', 'inferno', 'magma']
+    cmap = cm.get_cmap(random.choice(cmap))
+    camera.cmap = np.array(cmap(np.linspace(0, 1, 256)), dtype=np.float32)
+    camera.cmap_new = True
+    print("Setting colormap to {}".format(cmap.name))
+    return True
+
+ at register_event("closeup")
+def closeup(camera, window, key, scancode, action, mods):
+    camera.position = (0.01, 0.01, 0.01)
+    return True
+
+ at register_event("reset")
+def reset(camera, window, key, scancode, action, mods):
+    camera.position = (-1.0, -1.0, -1.0)
+    return True
+
+ at register_event("printit")
+def printit(*args):
+    print args
+
+class MouseRotation(object):
+    def __init__(self):
+        self.start = None
+        self.rotation = False
+
+    def start_rotation(self, camera, window, key, action, mods):
+        start_screen = glfw.GetCursorPos(window) # Screen coordinates
+        window_size = glfw.GetWindowSize(window)
+
+        norm_x = -1.0 + 2.0 * start_screen[0] / window_size[0]
+        norm_y = 1.0 - 2.0 * start_screen[1] / window_size[1]
+        self.start = (norm_x, norm_y)
+        self.rotation = True
+        return False
+
+    def stop_rotation(self, camera, window, key, action, mods):
+        end_screen = glfw.GetCursorPos(window)
+        window_size = glfw.GetWindowSize(window)
+
+        norm_x = -1.0 + 2.0 * end_screen[0] / window_size[0]
+        norm_y = 1.0 - 2.0 * end_screen[1] / window_size[1]
+        end = (norm_x, norm_y)
+
+        camera.update_orientation(self.start[0],
+                                  self.start[1],
+                                  end[0], end[1])
+        self.rotation = False
+        return True
+
+    def do_rotation(self, camera, window):
+        if not self.rotation: return False
+        new_end_screen = glfw.GetCursorPos(window)
+        window_size = glfw.GetWindowSize(window)
+
+        norm_x = -1.0 + 2.0 * new_end_screen[0] / window_size[0]
+        norm_y = 1.0 - 2.0 * new_end_screen[1] / window_size[1]
+        new_end = (norm_x, norm_y)
+
+        camera.update_orientation(self.start[0],
+                                  self.start[1],
+                                  new_end[0],
+                                  new_end[1])
+        self.start = new_end
+        return True
+

diff -r 988840417511fb6d9a42ac216ee1547c955b2bb2 -r 64588cbc3903673e08e43483d27cc19946974be4 yt/visualization/volume_rendering/interactive_loop.py
--- a/yt/visualization/volume_rendering/interactive_loop.py
+++ b/yt/visualization/volume_rendering/interactive_loop.py
@@ -1,158 +1,10 @@
 import cyglfw3 as glfw
 import numpy as np
-import random
-import matplotlib.cm as cm
 import OpenGL.GL as GL
-from collections import defaultdict
+from .input_events import EventCollection, MouseRotation
 
 from interactive_vr import BlockCollection, SceneGraph, TrackballCamera
 
-
-class Events(object):
-    def __init__(self, camera):
-        self.key_callbacks = defaultdict(list)
-        self.mouse_callbacks = defaultdict(list)
-        self.framebuffer_callbacks = []
-        self.render_events = []
-        self.camera = camera
-        self.draw = True
-
-    def key_call(self, window, key, scancode, action, mods):
-        draw = False
-        for f in self.key_callbacks[key, action, mods]:
-            draw = f(self.camera, window, key, scancode, action, mods) or draw
-        self.draw = self.draw or draw
-
-    def mouse_call(self, window, key, action, mods):
-        draw = False
-        for f in self.mouse_callbacks[key, action, mods]:
-            draw = f(self.camera, window, key, action, mods) or draw
-        self.draw = self.draw or draw
-
-    def framebuffer_call(self, window, width, height):
-        draw = False
-        for f in self.framebuffer_callbacks:
-            draw = f(self.camera, window, width, height) or draw
-        self.draw = self.draw or draw
-
-    def __call__(self, window):
-        draw = False
-        for f in self.render_events:
-            draw = f(self.camera, window) or draw
-        self.draw = self.draw or draw
-
-    def add_default(self, func):
-        self.render_events.append(func)
-
-    def add_key_callback(self, func, key, action = "press", mods = None):
-        self._add_callback(self.key_callbacks, func, key, action, mods)
-
-    def add_mouse_callback(self, func, key, action = "press", mods = None):
-        self._add_callback(self.mouse_callbacks, func, key, action, mods)
-
-    def add_framebuffer_callback(self, func):
-        self.framebuffer_callbacks.append(func)
-
-    def _add_callback(self, d, func, key, action, mods):
-        if isinstance(key, str):
-            key = getattr(glfw, "KEY_%s" % key.upper())
-        if isinstance(action, str):
-            action = getattr(glfw, action.upper())
-        if not isinstance(mods, tuple):
-            mods = (mods, )
-        mod = 0
-        for m in mods:
-            if isinstance(m, str):
-                m = getattr(glfw, "MOD_%s" % m.upper())
-            elif m is None:
-                m = 0
-            mod |= m
-        # We can allow for multiple
-        d[key, action, mod].append(func)
-
-def framebuffer_size_callback(camera, window, width, height):
-    GL.glViewport(0, 0, width, height)
-    camera.aspect_ratio = float(width)/height
-    return True
-
-def close_window(camera, window, key, scancode, action, mods):
-    glfw.SetWindowShouldClose(window, True)
-
-def zoomin(camera, window, key, scancode, action, mods):
-    camera.position -= 0.05 * (camera.position - camera.focus) / \
-                np.linalg.norm(camera.position - camera.focus)
-    print camera.position, camera.focus
-    return True
-
-def zoomout(camera, window, key, scancode, action, mods):
-    camera.position += 0.05 * (camera.position - camera.focus) / \
-        np.linalg.norm(camera.position - camera.focus)
-    return True
-
-def cmap_cycle(camera, window, key, scancode, action, mods):
-    cmap = ['algae', 'kamae', 'viridis', 'inferno', 'magma']
-    cmap = cm.get_cmap(random.choice(cmap))
-    camera.cmap = np.array(cmap(np.linspace(0, 1, 256)), dtype=np.float32)
-    camera.cmap_new = True
-    print("Setting colormap to {}".format(cmap.name))
-    return True
-
-def closeup(camera, window, key, scancode, action, mods):
-    camera.position = (0.01, 0.01, 0.01)
-    return True
-
-def reset(camera, window, key, scancode, action, mods):
-    camera.position = (-1.0, -1.0, -1.0)
-    return True
-
-def printit(*args):
-    print args
-
-class MouseRotation(object):
-    def __init__(self):
-        self.start = None
-        self.rotation = False
-
-    def start_rotation(self, camera, window, key, action, mods):
-        start_screen = glfw.GetCursorPos(window) # Screen coordinates
-        window_size = glfw.GetWindowSize(window)
-
-        norm_x = -1.0 + 2.0 * start_screen[0] / window_size[0]
-        norm_y = 1.0 - 2.0 * start_screen[1] / window_size[1]
-        self.start = (norm_x, norm_y)
-        self.rotation = True
-        return False
-
-    def stop_rotation(self, camera, window, key, action, mods):
-        end_screen = glfw.GetCursorPos(window)
-        window_size = glfw.GetWindowSize(window)
-
-        norm_x = -1.0 + 2.0 * end_screen[0] / window_size[0]
-        norm_y = 1.0 - 2.0 * end_screen[1] / window_size[1]
-        end = (norm_x, norm_y)
-
-        camera.update_orientation(self.start[0],
-                                  self.start[1],
-                                  end[0], end[1])
-        self.rotation = False
-        return True
-
-    def do_rotation(self, camera, window):
-        if not self.rotation: return False
-        new_end_screen = glfw.GetCursorPos(window)
-        window_size = glfw.GetWindowSize(window)
-
-        norm_x = -1.0 + 2.0 * new_end_screen[0] / window_size[0]
-        norm_y = 1.0 - 2.0 * new_end_screen[1] / window_size[1]
-        new_end = (norm_x, norm_y)
-
-        camera.update_orientation(self.start[0],
-                                  self.start[1],
-                                  new_end[0],
-                                  new_end[1])
-        self.start = new_end
-        return True
-
 class RenderingContext(object):
     should_quit = False
     def __init__(self, width = 800, height = 600, title = "vol_render"):
@@ -179,20 +31,20 @@
         frame_start = glfw.GetTime()
         fps_start = glfw.GetTime()
         print "Starting rendering..."
-        callbacks = Events(camera)
-        callbacks.add_key_callback(close_window, "escape")
-        callbacks.add_key_callback(zoomin, "w")
-        callbacks.add_key_callback(zoomout, "s")
-        callbacks.add_key_callback(closeup, "z")
-        callbacks.add_key_callback(cmap_cycle, "c")
-        callbacks.add_key_callback(reset, "r")
+        callbacks = EventCollection(camera)
+        callbacks.add_key_callback("close_window", "escape")
+        callbacks.add_key_callback("zoomin", "w")
+        callbacks.add_key_callback("zoomout", "s")
+        callbacks.add_key_callback("closeup", "z")
+        callbacks.add_key_callback("cmap_cycle", "c")
+        callbacks.add_key_callback("reset", "r")
         mouse_callbacks = MouseRotation()
         callbacks.add_mouse_callback(mouse_callbacks.start_rotation,
             glfw.MOUSE_BUTTON_LEFT)
         callbacks.add_mouse_callback(mouse_callbacks.stop_rotation,
             glfw.MOUSE_BUTTON_LEFT, action="release")
-        callbacks.add_framebuffer_callback(framebuffer_size_callback)
-        callbacks.add_default(mouse_callbacks.do_rotation)
+        callbacks.add_framebuffer_callback("framebuffer_size_callback")
+        callbacks.add_render_callback(mouse_callbacks.do_rotation)
         glfw.SetFramebufferSizeCallback(self.window,
             callbacks.framebuffer_call)
         glfw.SetKeyCallback(self.window, callbacks.key_call)


https://bitbucket.org/yt_analysis/yt/commits/276a6f5ecb47/
Changeset:   276a6f5ecb47
Branch:      yt
User:        MatthewTurk
Date:        2016-02-11 19:51:06+00:00
Summary:     Make the event collection and event accessible to all callbacks.
Affected #:  1 file

diff -r 64588cbc3903673e08e43483d27cc19946974be4 -r 276a6f5ecb47bcd907ddf644c0d793100786f040 yt/visualization/volume_rendering/input_events.py
--- a/yt/visualization/volume_rendering/input_events.py
+++ b/yt/visualization/volume_rendering/input_events.py
@@ -1,4 +1,4 @@
-from collections import defaultdict
+from collections import defaultdict, namedtuple
 from functools import wraps
 import OpenGL.GL as GL
 import cyglfw3 as glfw
@@ -8,6 +8,9 @@
 
 event_registry = {}
 
+GLFWEvent = namedtuple("GLFWEvent", ['window', 'key', 'scancode', 'action',
+                       'mods', 'width', 'height'])
+
 class EventCollection(object):
     def __init__(self, camera):
         self.key_callbacks = defaultdict(list)
@@ -19,26 +22,30 @@
 
     def key_call(self, window, key, scancode, action, mods):
         draw = False
+        event = GLFWEvent(window, key, scancode, action, mods, None, None)
         for f in self.key_callbacks[key, action, mods]:
-            draw = f(self.camera, window, key, scancode, action, mods) or draw
+            draw = f(self, event) or draw
         self.draw = self.draw or draw
 
     def mouse_call(self, window, key, action, mods):
+        event = GLFWEvent(window, key, None, action, mods, None, None)
         draw = False
         for f in self.mouse_callbacks[key, action, mods]:
-            draw = f(self.camera, window, key, action, mods) or draw
+            draw = f(self, event) or draw
         self.draw = self.draw or draw
 
     def framebuffer_call(self, window, width, height):
+        event = GLFWEvent(window, None, None, None, None, width, height)
         draw = False
         for f in self.framebuffer_callbacks:
-            draw = f(self.camera, window, width, height) or draw
+            draw = f(self, event) or draw
         self.draw = self.draw or draw
 
     def __call__(self, window):
+        event = GLFWEvent(window, None, None, None, None, None, None)
         draw = False
         for f in self.render_events:
-            draw = f(self.camera, window) or draw
+            draw = f(self, event) or draw
         self.draw = self.draw or draw
 
     def add_render_callback(self, func):
@@ -79,59 +86,57 @@
     return _f
 
 @register_event("framebuffer_size")
-def framebuffer_size_callback(camera, window, width, height):
-    GL.glViewport(0, 0, width, height)
-    camera.aspect_ratio = float(width)/height
+def framebuffer_size_callback(event_coll, event):
+    GL.glViewport(0, 0, event.width, event.height)
+    event_coll.camera.aspect_ratio = float(event.width)/event.height
     return True
 
 @register_event("close_window")
-def close_window(camera, window, key, scancode, action, mods):
-    glfw.SetWindowShouldClose(window, True)
+def close_window(event_coll, event):
+    glfw.SetWindowShouldClose(event.window, True)
 
 @register_event("zoomin")
-def zoomin(camera, window, key, scancode, action, mods):
+def zoomin(event_coll, event):
+    camera = event_coll.camera
     camera.position -= 0.05 * (camera.position - camera.focus) / \
                 np.linalg.norm(camera.position - camera.focus)
-    print camera.position, camera.focus
     return True
 
 @register_event("zoomout")
-def zoomout(camera, window, key, scancode, action, mods):
+def zoomout(event_coll, event):
+    camera = event_coll.camera
     camera.position += 0.05 * (camera.position - camera.focus) / \
         np.linalg.norm(camera.position - camera.focus)
     return True
 
 @register_event("cmap_cycle")
-def cmap_cycle(camera, window, key, scancode, action, mods):
+def cmap_cycle(event_coll, event):
     cmap = ['algae', 'kamae', 'viridis', 'inferno', 'magma']
     cmap = cm.get_cmap(random.choice(cmap))
-    camera.cmap = np.array(cmap(np.linspace(0, 1, 256)), dtype=np.float32)
-    camera.cmap_new = True
+    event_coll.camera.cmap = np.array(cmap(np.linspace(0, 1, 256)),
+        dtype=np.float32)
+    event_coll.camera.cmap_new = True
     print("Setting colormap to {}".format(cmap.name))
     return True
 
 @register_event("closeup")
-def closeup(camera, window, key, scancode, action, mods):
-    camera.position = (0.01, 0.01, 0.01)
+def closeup(event_coll, event):
+    event_coll.camera.position = (0.01, 0.01, 0.01)
     return True
 
 @register_event("reset")
-def reset(camera, window, key, scancode, action, mods):
-    camera.position = (-1.0, -1.0, -1.0)
+def reset(event_coll, event):
+    event_coll.camera.position = (-1.0, -1.0, -1.0)
     return True
 
- at register_event("printit")
-def printit(*args):
-    print args
-
 class MouseRotation(object):
     def __init__(self):
         self.start = None
         self.rotation = False
 
-    def start_rotation(self, camera, window, key, action, mods):
-        start_screen = glfw.GetCursorPos(window) # Screen coordinates
-        window_size = glfw.GetWindowSize(window)
+    def start_rotation(self, event_coll, event):
+        start_screen = glfw.GetCursorPos(event.window) # Screen coordinates
+        window_size = glfw.GetWindowSize(event.window)
 
         norm_x = -1.0 + 2.0 * start_screen[0] / window_size[0]
         norm_y = 1.0 - 2.0 * start_screen[1] / window_size[1]
@@ -139,33 +144,30 @@
         self.rotation = True
         return False
 
-    def stop_rotation(self, camera, window, key, action, mods):
-        end_screen = glfw.GetCursorPos(window)
-        window_size = glfw.GetWindowSize(window)
+    def stop_rotation(self, event_coll, event):
+        end_screen = glfw.GetCursorPos(event.window)
+        window_size = glfw.GetWindowSize(event.window)
 
         norm_x = -1.0 + 2.0 * end_screen[0] / window_size[0]
         norm_y = 1.0 - 2.0 * end_screen[1] / window_size[1]
         end = (norm_x, norm_y)
 
-        camera.update_orientation(self.start[0],
-                                  self.start[1],
-                                  end[0], end[1])
+        event_coll.camera.update_orientation(
+            self.start[0], self.start[1], end[0], end[1])
         self.rotation = False
         return True
 
-    def do_rotation(self, camera, window):
+    def do_rotation(self, event_coll, event):
         if not self.rotation: return False
-        new_end_screen = glfw.GetCursorPos(window)
-        window_size = glfw.GetWindowSize(window)
+        new_end_screen = glfw.GetCursorPos(event.window)
+        window_size = glfw.GetWindowSize(event.window)
 
         norm_x = -1.0 + 2.0 * new_end_screen[0] / window_size[0]
         norm_y = 1.0 - 2.0 * new_end_screen[1] / window_size[1]
         new_end = (norm_x, norm_y)
 
-        camera.update_orientation(self.start[0],
-                                  self.start[1],
-                                  new_end[0],
-                                  new_end[1])
+        event_coll.camera.update_orientation(
+            self.start[0], self.start[1], new_end[0], new_end[1])
         self.start = new_end
         return True
 


https://bitbucket.org/yt_analysis/yt/commits/f74910e3f9d0/
Changeset:   f74910e3f9d0
Branch:      yt
User:        xarthisius
Date:        2016-02-11 23:23:09+00:00
Summary:     Add ortographic projections and hook to switch between different projections
Affected #:  4 files

diff -r 276a6f5ecb47bcd907ddf644c0d793100786f040 -r f74910e3f9d09015dec745b127d65ec85047e7a2 yt/utilities/math_utils.py
--- a/yt/utilities/math_utils.py
+++ b/yt/utilities/math_utils.py
@@ -128,7 +128,7 @@
 
     c = np.empty((2,) + a.shape, dtype="float64")
     c[0,:] = np.abs(a - b)
-    
+
     p_directions = [i for i,p in enumerate(periodicity) if p is True]
     np_directions = [i for i,p in enumerate(periodicity) if p is False]
     for d in p_directions:
@@ -781,7 +781,7 @@
     Parameters
     ----------
     fovy : scalar
-        The angle in radians of the field of view.
+        The angle in degrees of the field of view.
 
     aspect : scalar
         The aspect ratio of width / height for the projection.
@@ -816,7 +816,7 @@
 
     """
 
-    tan_half_fovy = np.tan(fovy / 2)
+    tan_half_fovy = np.tan(np.radians(fovy) / 2)
 
     result = np.zeros( (4, 4), dtype = 'float32', order = 'C')
     result[0][0] = 1 / (aspect * tan_half_fovy)
@@ -827,6 +827,65 @@
 
     return result
 
+def get_orthographic_matrix(maxr, aspect, z_near, z_far):
+    """
+    Given a field of view in radians, an aspect ratio, and a near
+    and far plane distance, this routine computes the transformation matrix
+    corresponding to perspective projection using homogenous coordinates.
+
+    Parameters
+    ----------
+    maxr : scalar
+        should be max(|x|, |y|)
+
+    aspect : scalar
+        The aspect ratio of width / height for the projection.
+
+    z_near : scalar
+        The distance of the near plane from the camera.
+
+    z_far : scalar
+        The distance of the far plane from the camera.
+
+    Returns
+    -------
+    persp_matrix : ndarray
+        A new 4x4 2D array. Represents a perspective transformation
+        in homogeneous coordinates. Note that this matrix does not
+        actually perform the projection. After multiplying a 4D
+        vector of the form (x_0, y_0, z_0, 1.0), the point will be
+        transformed to some (x_1, y_1, z_1, w). The final projection
+        is applied by performing a divide by w, that is
+        (x_1/w, y_1/w, z_1/w, w/w). The matrix uses a row-major
+        ordering, rather than the column major ordering typically
+        used by OpenGL.
+
+    Notes
+    -----
+    The usage of 4D homogeneous coordinates is for OpenGL and GPU
+    hardware that automatically performs the divide by w operation.
+    See the following for more details about the OpenGL perpective matrices.
+
+    http://www.scratchapixel.com/lessons/3d-basic-rendering/perspective-and-orthographic-projection-matrix/orthographic-projection-matrix
+    http://www.tomdalling.com/blog/modern-opengl/explaining-homogenous-coordinates-and-projective-geometry/
+    http://www.songho.ca/opengl/gl_projectionmatrix.html
+
+    """
+
+    r = maxr * aspect
+    t = maxr
+    l = -r
+    b = -t
+
+    result = np.zeros( (4, 4), dtype = 'float32', order = 'C')
+    result[0][0] = 2.0 / (r - l)
+    result[1][1] = 2.0 / (t - b)
+    result[2][2] = -2.0 / (z_far - z_near)
+    result[3][2] = -(z_far + z_near) / (z_far - z_near)
+    result[3][3] = 1
+
+    return result
+
 def get_lookat_matrix(eye, center, up):
     """
     Given the position of a camera, the point it is looking at, and
@@ -1050,9 +1109,9 @@
 def rotation_matrix_to_quaternion(rot_matrix):
     '''
 
-    Convert a rotation matrix-based representation of an 
-    orientation to a quaternion. The input should be a 
-    3x3 rotation matrix, while the output will be a 
+    Convert a rotation matrix-based representation of an
+    orientation to a quaternion. The input should be a
+    3x3 rotation matrix, while the output will be a
     4-component numpy array. We follow the approach in
     "3D Math Primer for Graphics and Game Development" by
     Dunn and Parberry.
@@ -1069,8 +1128,8 @@
     m33 = rot_matrix[2][2]
 
     four_w_squared_minus_1 = m11 + m22 + m33
-    four_x_squared_minus_1 = m11 - m22 - m33 
-    four_y_squared_minus_1 = m22 - m11 - m33 
+    four_x_squared_minus_1 = m11 - m22 - m33
+    four_y_squared_minus_1 = m22 - m11 - m33
     four_z_squared_minus_1 = m33 - m11 - m22
     max_index = 0
     four_max_squared_minus_1 = four_w_squared_minus_1
@@ -1090,7 +1149,7 @@
     if (max_index == 0):
         w = max_val
         x = (m23 - m32) * mult
-        y = (m31 - m13) * mult 
+        y = (m31 - m13) * mult
         z = (m12 - m21) * mult
     elif (max_index == 1):
         x = max_val
@@ -1107,7 +1166,7 @@
         w = (m12 - m21) * mult
         x = (m31 + m13) * mult
         y = (m23 + m32) * mult
- 
+
     return np.array([w, x, y, z])
 
 def get_ortho_basis(normal):

diff -r 276a6f5ecb47bcd907ddf644c0d793100786f040 -r f74910e3f9d09015dec745b127d65ec85047e7a2 yt/visualization/volume_rendering/input_events.py
--- a/yt/visualization/volume_rendering/input_events.py
+++ b/yt/visualization/volume_rendering/input_events.py
@@ -1,4 +1,8 @@
 from collections import defaultdict, namedtuple
+from yt.utilities.math_utils import \
+    get_perspective_matrix, \
+    get_orthographic_matrix
+from yt.utilities.exceptions import YTInvalidShaderType
 from functools import wraps
 import OpenGL.GL as GL
 import cyglfw3 as glfw
@@ -109,6 +113,24 @@
         np.linalg.norm(camera.position - camera.focus)
     return True
 
+ at register_event("camera_orto")
+def camera_orto(event_coll, event):
+    camera = event_coll.camera
+    if camera.proj_func == get_orthographic_matrix:
+        return False
+    camera.proj_func = get_orthographic_matrix
+    camera.fov = np.tan(np.radians(camera.fov) / 2.0)
+    return True
+
+ at register_event("camera_proj")
+def camera_proj(event_coll, event):
+    camera = event_coll.camera
+    if camera.proj_func == get_perspective_matrix:
+        return False
+    camera.proj_func = get_perspective_matrix
+    camera.fov = np.degrees(np.arctan(camera.fov) * 2.0)
+    return True
+
 @register_event("cmap_cycle")
 def cmap_cycle(event_coll, event):
     cmap = ['algae', 'kamae', 'viridis', 'inferno', 'magma']

diff -r 276a6f5ecb47bcd907ddf644c0d793100786f040 -r f74910e3f9d09015dec745b127d65ec85047e7a2 yt/visualization/volume_rendering/interactive_loop.py
--- a/yt/visualization/volume_rendering/interactive_loop.py
+++ b/yt/visualization/volume_rendering/interactive_loop.py
@@ -38,6 +38,8 @@
         callbacks.add_key_callback("closeup", "z")
         callbacks.add_key_callback("cmap_cycle", "c")
         callbacks.add_key_callback("reset", "r")
+        callbacks.add_key_callback("camera_orto", "o")
+        callbacks.add_key_callback("camera_proj", "p")
         mouse_callbacks = MouseRotation()
         callbacks.add_mouse_callback(mouse_callbacks.start_rotation,
             glfw.MOUSE_BUTTON_LEFT)

diff -r 276a6f5ecb47bcd907ddf644c0d793100786f040 -r f74910e3f9d09015dec745b127d65ec85047e7a2 yt/visualization/volume_rendering/interactive_vr.py
--- a/yt/visualization/volume_rendering/interactive_vr.py
+++ b/yt/visualization/volume_rendering/interactive_vr.py
@@ -8,6 +8,7 @@
     get_scale_matrix, \
     get_lookat_matrix, \
     get_perspective_matrix, \
+    get_orthographic_matrix, \
     quaternion_mult, \
     quaternion_to_rotation_matrix, \
     rotation_matrix_to_quaternion
@@ -117,6 +118,7 @@
                  fov=45.0, near_plane=0.01, far_plane=20.0, aspect_ratio=8.0/6.0):
         self.view_matrix = np.zeros((4, 4), dtype=np.float32)
         self.proj_matrix = np.zeros((4, 4), dtype=np.float32)
+        self.proj_func = get_perspective_matrix
         self.position = np.array(position)
         self.focus = np.array(focus)
         self.fov = fov
@@ -177,10 +179,10 @@
                                              self.focus,
                                              self.up)
 
-        self.projection_matrix = get_perspective_matrix(np.radians(self.fov),
-                                                        self.aspect_ratio,
-                                                        self.near_plane,
-                                                        self.far_plane)
+        self.projection_matrix = self.proj_func(self.fov,
+                                                self.aspect_ratio,
+                                                self.near_plane,
+                                                self.far_plane)
 
     def get_viewpoint(self):
         return self.position
@@ -211,8 +213,8 @@
         return get_lookat_matrix(self.position, self.focus, self.up)
 
     def get_projection_matrix(self):
-        return get_perspective_matrix(np.radians(self.fov), self.aspect_ratio, self.near_plane,
-                self.far_plane)
+        return self.proj_func(self.fov, self.aspect_ratio, self.near_plane,
+                              self.far_plane)
 
     def update_position(self, theta, phi):
         rho = np.linalg.norm(self.position)


https://bitbucket.org/yt_analysis/yt/commits/3e827a243a23/
Changeset:   3e827a243a23
Branch:      yt
User:        xarthisius
Date:        2016-02-12 17:36:32+00:00
Summary:     Add shader for projections
Affected #:  1 file

diff -r f74910e3f9d09015dec745b127d65ec85047e7a2 -r 3e827a243a239b3a69fbe9758116cace05197466 yt/visualization/volume_rendering/shaders/projection.fragmentshader
--- /dev/null
+++ b/yt/visualization/volume_rendering/shaders/projection.fragmentshader
@@ -0,0 +1,59 @@
+#version 330
+in vec4 v_model;
+in vec3 v_camera_pos;
+in vec3 dx;
+in vec3 left_edge;
+in vec3 right_edge;
+flat in mat4 inverse_proj;
+flat in mat4 inverse_view;
+out vec4 output_color;
+
+uniform sampler3D ds_tex;
+//layout (binding = 1) uniform sampler2D depth_tex;
+uniform vec4 viewport; // (offset_x, offset_y, 1 / screen_x, 1 / screen_y)
+
+bool within_bb(vec3 pos)
+{
+    bvec3 left =  greaterThanEqual(pos, left_edge);
+    bvec3 right = lessThanEqual(pos, right_edge);
+    return all(left) && all(right);
+}
+
+void main()
+{
+    // Obtain screen coordinates
+    // https://www.opengl.org/wiki/Compute_eye_space_from_window_space#From_gl_FragCoord
+    vec4 ndcPos;
+    ndcPos.xy = ((2.0 * gl_FragCoord.xy) - (2.0 * viewport.xy)) / (viewport.zw) - 1;
+    ndcPos.z = (2.0 * gl_FragCoord.z - 1.0);
+    ndcPos.w = 1.0;
+
+    vec4 clipPos = ndcPos / gl_FragCoord.w;
+    vec4 eyePos = inverse_proj * clipPos;
+    eyePos /= eyePos.w;
+
+    vec4 world_location = inverse_view * eyePos;
+
+    // Five samples
+    vec3 step_size = dx / 5.0;
+    vec3 dir = normalize(world_location.xyz - v_camera_pos.xyz);
+    vec3 curr_color = vec3(0.0);
+
+    vec3 ray_position = world_location.xyz;
+
+    vec3 tex_curr_pos = vec3(0.0);
+    vec3 range = right_edge - left_edge;
+    bool ray_in_bb = true;
+    float total_value = 0.0;
+    float color = 0.0;
+    while (ray_in_bb) {
+        tex_curr_pos = (ray_position - left_edge) / range;
+        vec3 tex_sample = texture(ds_tex, tex_curr_pos).rgb;
+	color = (tex_sample * length(step_size * dir)).r;
+        total_value += length(step_size * dir) * tex_sample.r;
+        ray_position += dir * step_size;
+        ray_in_bb = within_bb(ray_position);
+    }
+    total_value *= 1e3;
+    output_color = vec4(total_value, total_value, total_value, 1.0);
+}


https://bitbucket.org/yt_analysis/yt/commits/2a535f27100c/
Changeset:   2a535f27100c
Branch:      yt
User:        xarthisius
Date:        2016-02-12 17:37:08+00:00
Summary:     Add Scene object to EventCollection. Allow to switch between 2 different shaders
Affected #:  2 files

diff -r 3e827a243a239b3a69fbe9758116cace05197466 -r 2a535f27100c76588226d263a2e7d21d9f493fc4 yt/visualization/volume_rendering/input_events.py
--- a/yt/visualization/volume_rendering/input_events.py
+++ b/yt/visualization/volume_rendering/input_events.py
@@ -16,12 +16,13 @@
                        'mods', 'width', 'height'])
 
 class EventCollection(object):
-    def __init__(self, camera):
+    def __init__(self, scene, camera):
         self.key_callbacks = defaultdict(list)
         self.mouse_callbacks = defaultdict(list)
         self.framebuffer_callbacks = []
         self.render_events = []
         self.camera = camera
+        self.scene = scene
         self.draw = True
 
     def key_call(self, window, key, scancode, action, mods):
@@ -131,6 +132,18 @@
     camera.fov = np.degrees(np.arctan(camera.fov) * 2.0)
     return True
 
+ at register_event("shader_max")
+def shader_max(event_coll, event):
+    scene = event_coll.scene
+    scene.add_shader_from_file("max_intensity.fragmentshader")
+    return True
+
+ at register_event("shader_proj")
+def shader_proj(event_coll, event):
+    scene = event_coll.scene
+    scene.add_shader_from_file("projection.fragmentshader")
+    return True
+
 @register_event("cmap_cycle")
 def cmap_cycle(event_coll, event):
     cmap = ['algae', 'kamae', 'viridis', 'inferno', 'magma']

diff -r 3e827a243a239b3a69fbe9758116cace05197466 -r 2a535f27100c76588226d263a2e7d21d9f493fc4 yt/visualization/volume_rendering/interactive_loop.py
--- a/yt/visualization/volume_rendering/interactive_loop.py
+++ b/yt/visualization/volume_rendering/interactive_loop.py
@@ -31,7 +31,7 @@
         frame_start = glfw.GetTime()
         fps_start = glfw.GetTime()
         print "Starting rendering..."
-        callbacks = EventCollection(camera)
+        callbacks = EventCollection(scene, camera)
         callbacks.add_key_callback("close_window", "escape")
         callbacks.add_key_callback("zoomin", "w")
         callbacks.add_key_callback("zoomout", "s")
@@ -40,6 +40,8 @@
         callbacks.add_key_callback("reset", "r")
         callbacks.add_key_callback("camera_orto", "o")
         callbacks.add_key_callback("camera_proj", "p")
+        callbacks.add_key_callback("shader_max", "1")
+        callbacks.add_key_callback("shader_proj", "2")
         mouse_callbacks = MouseRotation()
         callbacks.add_mouse_callback(mouse_callbacks.start_rotation,
             glfw.MOUSE_BUTTON_LEFT)


https://bitbucket.org/yt_analysis/yt/commits/11fe13985a96/
Changeset:   11fe13985a96
Branch:      yt
User:        xarthisius
Date:        2016-02-12 17:56:13+00:00
Summary:     Reload textures upon switching shaders, set proper blending
Affected #:  2 files

diff -r 2a535f27100c76588226d263a2e7d21d9f493fc4 -r 11fe13985a96581cd5c83a7f3b0b3ad9901ab41e yt/visualization/volume_rendering/input_events.py
--- a/yt/visualization/volume_rendering/input_events.py
+++ b/yt/visualization/volume_rendering/input_events.py
@@ -136,12 +136,18 @@
 def shader_max(event_coll, event):
     scene = event_coll.scene
     scene.add_shader_from_file("max_intensity.fragmentshader")
+    for collection in scene.collections:
+        collection.set_fields_log(True)
+    GL.glBlendEquation(GL.GL_MAX)
     return True
 
 @register_event("shader_proj")
 def shader_proj(event_coll, event):
     scene = event_coll.scene
     scene.add_shader_from_file("projection.fragmentshader")
+    for collection in scene.collections:
+        collection.set_fields_log(False)
+    GL.glBlendEquation(GL.GL_FUNC_ADD)
     return True
 
 @register_event("cmap_cycle")

diff -r 2a535f27100c76588226d263a2e7d21d9f493fc4 -r 11fe13985a96581cd5c83a7f3b0b3ad9901ab41e yt/visualization/volume_rendering/interactive_vr.py
--- a/yt/visualization/volume_rendering/interactive_vr.py
+++ b/yt/visualization/volume_rendering/interactive_vr.py
@@ -275,7 +275,10 @@
         GL.glDisableVertexAttribArray(loc)
         GL.glBindVertexArray(0)
 
-    def add_data(self, data_source, field):
+    def set_fields_log(self, log_field):
+        self.add_data(self.data_source, self.data_source.tiles.fields[0], log_field)
+
+    def add_data(self, data_source, field, log_field=True):
         r"""Adds a source of data for the block collection.
 
         Given a `data_source` and a `field` to populate from, adds the data
@@ -290,7 +293,7 @@
 
         """
         self.data_source = data_source
-        self.data_source.tiles.set_fields([field], [True], True)
+        self.data_source.tiles.set_fields([field], [log_field], log_field)
         self.blocks = {}
         self.block_order = []
         # Every time we change our data source, we wipe all existing ones.
@@ -299,8 +302,8 @@
         self.min_val = 1e60
         self.max_val = -1e60
         for i, block in enumerate(self.data_source.tiles.traverse()):
-            self.min_val = min(self.min_val, block.my_data[0].min())
-            self.max_val = max(self.max_val, block.my_data[0].max())
+            self.min_val = min(self.min_val, np.nanmin(block.my_data[0].min()))
+            self.max_val = max(self.max_val, np.nanmax(block.my_data[0].max()))
             self.blocks[id(block)] = (i, block)
             vert.append(self._compute_geometry(block, bbox_vertices))
             dds = (block.RightEdge - block.LeftEdge)/block.my_data[0].shape


https://bitbucket.org/yt_analysis/yt/commits/a10d10843244/
Changeset:   a10d10843244
Branch:      yt
User:        xarthisius
Date:        2016-02-15 18:25:34+00:00
Summary:     Compute diagonal for projection scaling, always set no_ghost to False
Affected #:  2 files

diff -r 11fe13985a96581cd5c83a7f3b0b3ad9901ab41e -r a10d10843244a6feaccc63ead82cda8e716099ba yt/visualization/volume_rendering/interactive_vr.py
--- a/yt/visualization/volume_rendering/interactive_vr.py
+++ b/yt/visualization/volume_rendering/interactive_vr.py
@@ -293,7 +293,7 @@
 
         """
         self.data_source = data_source
-        self.data_source.tiles.set_fields([field], [log_field], log_field)
+        self.data_source.tiles.set_fields([field], [log_field], no_ghost=False)
         self.blocks = {}
         self.block_order = []
         # Every time we change our data source, we wipe all existing ones.
@@ -313,6 +313,11 @@
             re.append([block.RightEdge.astype('f4') for _ in range(n)])
             self.block_order.append(id(block))
 
+        LE = np.array([b.LeftEdge
+                       for i, b in self.blocks.values()]).min(axis=0)
+        RE = np.array([b.RightEdge
+                       for i, b in self.blocks.values()]).max(axis=0)
+        self.diagonal = np.sqrt(((RE - LE) ** 2).sum())
         # Now we set up our buffer
         vert = np.concatenate(vert)
         dx = np.concatenate(dx)
@@ -433,7 +438,7 @@
             texture_name = self.gl_texture_names[tex_i]
             dx, dy, dz = block.my_data[0].shape
             n_data = block.my_data[0].copy(order="F").astype("float32")
-            n_data = (n_data - self.min_val) / (self.max_val - self.min_val)
+            n_data = (n_data - self.min_val) / ((self.max_val - self.min_val) * self.diagonal)
             GL.glBindTexture(GL.GL_TEXTURE_3D, texture_name)
             GL.glTexParameterf(GL.GL_TEXTURE_3D, GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP_TO_EDGE)
             GL.glTexParameterf(GL.GL_TEXTURE_3D, GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP_TO_EDGE)

diff -r 11fe13985a96581cd5c83a7f3b0b3ad9901ab41e -r a10d10843244a6feaccc63ead82cda8e716099ba yt/visualization/volume_rendering/shaders/projection.fragmentshader
--- a/yt/visualization/volume_rendering/shaders/projection.fragmentshader
+++ b/yt/visualization/volume_rendering/shaders/projection.fragmentshader
@@ -54,6 +54,6 @@
         ray_position += dir * step_size;
         ray_in_bb = within_bb(ray_position);
     }
-    total_value *= 1e3;
+    //total_value *= 1e3;
     output_color = vec4(total_value, total_value, total_value, 1.0);
 }


https://bitbucket.org/yt_analysis/yt/commits/332f6189e957/
Changeset:   332f6189e957
Branch:      yt
User:        MatthewTurk
Date:        2016-02-11 20:45:54+00:00
Summary:     Not quite working global limits
Affected #:  4 files

diff -r a10d10843244a6feaccc63ead82cda8e716099ba -r 332f6189e957f1b3a2486482d5e28cc24a21734b yt/visualization/volume_rendering/input_events.py
--- a/yt/visualization/volume_rendering/input_events.py
+++ b/yt/visualization/volume_rendering/input_events.py
@@ -170,6 +170,23 @@
     event_coll.camera.position = (-1.0, -1.0, -1.0)
     return True
 
+ at register_event("print_limits")
+def print_limits(event_coll, event):
+    print event_coll.scene.min_val,
+    print event_coll.scene.max_val
+    return False
+
+ at register_event("up_lower")
+def up_lower(event_coll, event):
+    event_coll.scene.min_val += 1
+    print event_coll.scene.min_val
+    return True
+
+ at register_event("debug_buffer")
+def debug_buffer(event_coll, event):
+    buffer = event_coll.scene._retrieve_framebuffer()
+    print buffer.min(), buffer.max()
+
 class MouseRotation(object):
     def __init__(self):
         self.start = None

diff -r a10d10843244a6feaccc63ead82cda8e716099ba -r 332f6189e957f1b3a2486482d5e28cc24a21734b yt/visualization/volume_rendering/interactive_loop.py
--- a/yt/visualization/volume_rendering/interactive_loop.py
+++ b/yt/visualization/volume_rendering/interactive_loop.py
@@ -42,6 +42,9 @@
         callbacks.add_key_callback("camera_proj", "p")
         callbacks.add_key_callback("shader_max", "1")
         callbacks.add_key_callback("shader_proj", "2")
+        callbacks.add_key_callback("print_limits", "l")
+        callbacks.add_key_callback("up_lower", "u")
+        callbacks.add_key_callback("debug_buffer", "d")
         mouse_callbacks = MouseRotation()
         callbacks.add_mouse_callback(mouse_callbacks.start_rotation,
             glfw.MOUSE_BUTTON_LEFT)

diff -r a10d10843244a6feaccc63ead82cda8e716099ba -r 332f6189e957f1b3a2486482d5e28cc24a21734b yt/visualization/volume_rendering/interactive_vr.py
--- a/yt/visualization/volume_rendering/interactive_vr.py
+++ b/yt/visualization/volume_rendering/interactive_vr.py
@@ -459,6 +459,7 @@
         self.cmap_texture = None
         self.camera = None
         self.shader_program = None
+        self.min_val, self.max_val = 1e60, -1e60
 
         ox, oy, width, height = GL.glGetIntegerv(GL.GL_VIEWPORT)
         self.width = width
@@ -467,7 +468,7 @@
         self.fb_shader_program = link_shader_program(
             ["passthrough.vertexshader", "apply_colormap.fragmentshader"]
         )
-        for key in ["fb_texture", "cmap"]:
+        for key in ["fb_texture", "cmap", "min_val", "max_val"]:
             self.fb_uniforms[key] = \
                 GL.glGetUniformLocation(self.fb_shader_program, key)
 
@@ -532,7 +533,7 @@
 
         depthbuffer = GL.glGenRenderbuffers(1)
         GL.glBindRenderbuffer(GL.GL_RENDERBUFFER, depthbuffer)
-        GL.glRenderbufferStorage(GL.GL_RENDERBUFFER, GL.GL_DEPTH_COMPONENT24,
+        GL.glRenderbufferStorage(GL.GL_RENDERBUFFER, GL.GL_DEPTH_COMPONENT32F,
                                  width, height)
         GL.glFramebufferRenderbuffer(
             GL.GL_FRAMEBUFFER, GL.GL_DEPTH_ATTACHMENT, GL.GL_RENDERBUFFER,
@@ -554,8 +555,8 @@
 
         # occupy width x height texture memory, (None at the end == empty
         # image)
-        GL.glTexImage2D(GL.GL_TEXTURE_2D, 0, GL.GL_RGBA, width,
-                        height, 0, GL.GL_RGBA, GL.GL_UNSIGNED_INT, None)
+        GL.glTexImage2D(GL.GL_TEXTURE_2D, 0, GL.GL_RGBA32F, width,
+                        height, 0, GL.GL_RGBA, GL.GL_FLOAT, None)
 
         # --- end texture init
 
@@ -585,6 +586,8 @@
 
         """
         self.collections.append(collection)
+        self.min_val = min(self.min_val, collection.min_val)
+        self.max_val = max(self.max_val, collection.max_val)
 
     def set_camera(self, camera):
         r""" Sets the camera orientation for the entire scene.
@@ -619,6 +622,13 @@
             ['default.vertexshader', filename]
         )
 
+    def _retrieve_framebuffer(self):
+        ox, oy, width, height = GL.glGetIntegerv(GL.GL_VIEWPORT)
+        debug_buffer = GL.glReadPixels(0, 0, width, height, GL.GL_RGB,
+                                       GL.GL_UNSIGNED_BYTE)
+        arr = np.fromstring(debug_buffer, "uint8", count = width*height*3)
+        return arr.reshape((width, height, 3))
+            
     def render(self):
         """ Renders one frame of the scene.
 
@@ -639,6 +649,7 @@
         # Handle colormap
         self.update_cmap_tex()
 
+
         # bind to fb
         GL.glBindFramebuffer(GL.GL_FRAMEBUFFER, self.fbo)
         # clear the color and depth buffer
@@ -662,6 +673,8 @@
         GL.glBindTexture(GL.GL_TEXTURE_1D, self.cmap_texture)
         GL.glUniform1i(self.fb_uniforms["cmap"], 1);
 
+        GL.glUniform1f(self.fb_uniforms["min_val"], self.min_val)
+        GL.glUniform1f(self.fb_uniforms["max_val"], self.max_val)
         # clear the color and depth buffer
         GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT)
         # Bind to Vertex array that contains simple quad filling fullscreen,

diff -r a10d10843244a6feaccc63ead82cda8e716099ba -r 332f6189e957f1b3a2486482d5e28cc24a21734b yt/visualization/volume_rendering/shaders/apply_colormap.fragmentshader
--- a/yt/visualization/volume_rendering/shaders/apply_colormap.fragmentshader
+++ b/yt/visualization/volume_rendering/shaders/apply_colormap.fragmentshader
@@ -6,7 +6,10 @@
 
 uniform sampler2D fb_texture;
 uniform sampler1D cmap;
+uniform float min_val;
+uniform float max_val;
 
 void main(){
-   color = texture(cmap, texture(fb_texture, UV).x);
+   float scaled = (texture(fb_texture, UV).x - min_val)/(max_val - min_val);
+   color = texture(cmap, scaled);
 }


https://bitbucket.org/yt_analysis/yt/commits/80afc61ee057/
Changeset:   80afc61ee057
Branch:      yt
User:        xarthisius
Date:        2016-02-16 20:39:27+00:00
Summary:     Use separate scaling for applying colormap
Affected #:  2 files

diff -r 332f6189e957f1b3a2486482d5e28cc24a21734b -r 80afc61ee0570b5f4af02e8c0d35242e3c00e725 yt/visualization/volume_rendering/interactive_vr.py
--- a/yt/visualization/volume_rendering/interactive_vr.py
+++ b/yt/visualization/volume_rendering/interactive_vr.py
@@ -460,6 +460,8 @@
         self.camera = None
         self.shader_program = None
         self.min_val, self.max_val = 1e60, -1e60
+        self.cmap_min, self.cmap_max = 0.0, 0.01
+        self.diagonal = 0.0
 
         ox, oy, width, height = GL.glGetIntegerv(GL.GL_VIEWPORT)
         self.width = width
@@ -468,7 +470,8 @@
         self.fb_shader_program = link_shader_program(
             ["passthrough.vertexshader", "apply_colormap.fragmentshader"]
         )
-        for key in ["fb_texture", "cmap", "min_val", "max_val"]:
+        for key in ["fb_texture", "cmap", "min_val", "scale",
+                    "cmap_min", "cmap_max"]:
             self.fb_uniforms[key] = \
                 GL.glGetUniformLocation(self.fb_shader_program, key)
 
@@ -588,6 +591,7 @@
         self.collections.append(collection)
         self.min_val = min(self.min_val, collection.min_val)
         self.max_val = max(self.max_val, collection.max_val)
+        self.diagonal = max(self.diagonal, collection.diagonal)
 
     def set_camera(self, camera):
         r""" Sets the camera orientation for the entire scene.
@@ -628,7 +632,7 @@
                                        GL.GL_UNSIGNED_BYTE)
         arr = np.fromstring(debug_buffer, "uint8", count = width*height*3)
         return arr.reshape((width, height, 3))
-            
+
     def render(self):
         """ Renders one frame of the scene.
 
@@ -674,7 +678,10 @@
         GL.glUniform1i(self.fb_uniforms["cmap"], 1);
 
         GL.glUniform1f(self.fb_uniforms["min_val"], self.min_val)
-        GL.glUniform1f(self.fb_uniforms["max_val"], self.max_val)
+        GL.glUniform1f(self.fb_uniforms["scale"],
+                       (self.max_val - self.min_val) * self.diagonal)
+        GL.glUniform1f(self.fb_uniforms["cmap_min"], self.cmap_min)
+        GL.glUniform1f(self.fb_uniforms["cmap_max"], self.cmap_max)
         # clear the color and depth buffer
         GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT)
         # Bind to Vertex array that contains simple quad filling fullscreen,

diff -r 332f6189e957f1b3a2486482d5e28cc24a21734b -r 80afc61ee0570b5f4af02e8c0d35242e3c00e725 yt/visualization/volume_rendering/shaders/apply_colormap.fragmentshader
--- a/yt/visualization/volume_rendering/shaders/apply_colormap.fragmentshader
+++ b/yt/visualization/volume_rendering/shaders/apply_colormap.fragmentshader
@@ -7,9 +7,11 @@
 uniform sampler2D fb_texture;
 uniform sampler1D cmap;
 uniform float min_val;
-uniform float max_val;
+uniform float scale;
+uniform float cmap_min;
+uniform float cmap_max;
 
 void main(){
-   float scaled = (texture(fb_texture, UV).x - min_val)/(max_val - min_val);
-   color = texture(cmap, scaled);
+   float scaled = texture(fb_texture, UV).x * scale + min_val;
+   color = texture(cmap, (scaled - cmap_min) / (cmap_max - cmap_min));
 }


https://bitbucket.org/yt_analysis/yt/commits/47df8e8935bc/
Changeset:   47df8e8935bc
Branch:      yt
User:        xarthisius
Date:        2016-02-16 16:39:30+00:00
Summary:     Regenerate AMRKDTree data whenever 'fields' attribute changes.
Update data in kd-Tree to respect 'log_fields' value during
AMRKDTree.set_fields call.
Affected #:  2 files

diff -r 80afc61ee0570b5f4af02e8c0d35242e3c00e725 -r 47df8e8935bc07c24cce20b428b36e566197c0cc yt/utilities/amr_kdtree/amr_kdtree.py
--- a/yt/utilities/amr_kdtree/amr_kdtree.py
+++ b/yt/utilities/amr_kdtree/amr_kdtree.py
@@ -14,6 +14,7 @@
 # The full license is in the file COPYING.txt, distributed with this software.
 #-----------------------------------------------------------------------------
 
+import operator
 import numpy as np
 
 from yt.funcs import mylog
@@ -42,19 +43,28 @@
 steps = np.array([[-1, -1, -1], [-1, -1,  0], [-1, -1,  1],
                   [-1,  0, -1], [-1,  0,  0], [-1,  0,  1],
                   [-1,  1, -1], [-1,  1,  0], [-1,  1,  1],
-                  
+
                   [ 0, -1, -1], [ 0, -1,  0], [ 0, -1,  1],
                   [ 0,  0, -1],
                   # [ 0,  0,  0],
                   [ 0,  0,  1],
                   [ 0,  1, -1], [ 0,  1,  0], [ 0,  1,  1],
-                  
+
                   [ 1, -1, -1], [ 1, -1,  0], [ 1, -1,  1],
                   [ 1,  0, -1], [ 1,  0,  0], [ 1,  0,  1],
                   [ 1,  1, -1], [ 1,  1,  0], [ 1,  1,  1] ])
 
+def _apply_log(data, log_changed, log_new):
+    '''Helper used to set log10/10^ to data in AMRKDTree'''
+    if not log_changed:
+        return
+    if log_new:
+        np.log10(data, data)
+    else:
+        np.power(10.0, data, data)
+
 class Tree(object):
-    def __init__(self, ds, comm_rank=0, comm_size=1, left=None, right=None, 
+    def __init__(self, ds, comm_rank=0, comm_size=1, left=None, right=None,
         min_level=None, max_level=None, data_source=None):
 
         self.ds = ds
@@ -155,6 +165,7 @@
         self.brick_dimensions = []
         self.sdx = ds.index.get_smallest_dx()
 
+        self.regenerate_data = True
         self._initialized = False
         try:
             self._id_offset = ds.index.grids[0]._id_offset
@@ -171,13 +182,25 @@
                          data_source=data_source)
 
     def set_fields(self, fields, log_fields, no_ghost):
-        self.fields = self.data_source._determine_fields(fields)
+        new_fields = self.data_source._determine_fields(fields)
+        self.regenerate_data = \
+            self.fields is None or \
+            len(self.fields) != len(new_fields) or \
+            self.fields != new_fields
+        self.fields = new_fields
+
+        if self.log_fields is not None:
+            flip_log = map(operator.ne, self.log_fields, log_fields)
+        else:
+            flip_log = [False] * len(log_fields)
         self.log_fields = log_fields
+
         self.no_ghost = no_ghost
         del self.bricks, self.brick_dimensions
         self.brick_dimensions = []
         bricks = []
         for b in self.traverse():
+            map(_apply_log, b.my_data, flip_log, log_fields)
             bricks.append(b)
         self.bricks = np.array(bricks)
         self.brick_dimensions = np.array(self.brick_dimensions)
@@ -261,7 +284,8 @@
         return scatter_image(self.comm, owners[1], image)
 
     def get_brick_data(self, node):
-        if node.data is not None: return node.data
+        if node.data is not None and not self.regenerate_data:
+            return node.data
         grid = self.ds.index.grids[node.grid - self._id_offset]
         dds = grid.dds.ndarray_view()
         gle = grid.LeftEdge.ndarray_view()
@@ -273,7 +297,7 @@
         assert(np.all(grid.LeftEdge <= nle))
         assert(np.all(grid.RightEdge >= nre))
 
-        if grid in self.current_saved_grids:
+        if grid in self.current_saved_grids and not self.regenerate_data:
             dds = self.current_vcds[self.current_saved_grids.index(grid)]
         else:
             dds = []
@@ -301,6 +325,7 @@
         node.data = brick
         if not self._initialized:
             self.brick_dimensions.append(dims)
+        self.regenerate_data = False
         return brick
 
     def locate_brick(self, position):
@@ -327,16 +352,16 @@
         cis: List of neighbor cell index tuples
 
         Both of these are neighbors that, relative to the current cell
-        index (i,j,k), are ordered as: 
-        
-        (i-1, j-1, k-1), (i-1, j-1, k ), (i-1, j-1, k+1), ...  
-        (i-1, j  , k-1), (i-1, j  , k ), (i-1, j  , k+1), ...  
+        index (i,j,k), are ordered as:
+
+        (i-1, j-1, k-1), (i-1, j-1, k ), (i-1, j-1, k+1), ...
+        (i-1, j  , k-1), (i-1, j  , k ), (i-1, j  , k+1), ...
         (i+1, j+1, k-1), (i-1, j-1, k ), (i+1, j+1, k+1)
 
         That is they start from the lower left and proceed to upper
         right varying the third index most frequently. Note that the
         center cell (i,j,k) is ommitted.
-        
+
         """
         ci = np.array(ci)
         center_dds = grid.dds
@@ -351,7 +376,7 @@
         new_positions = position + steps*offs
         new_positions = [periodic_position(p, self.ds) for p in new_positions]
         grids[in_grid] = grid
-                
+
         get_them = np.argwhere(in_grid).ravel()
         cis[in_grid] = new_cis[in_grid]
 
@@ -367,11 +392,11 @@
         return grids, cis
 
     def locate_neighbors_from_position(self, position):
-        r"""Given a position, finds the 26 neighbor grids 
+        r"""Given a position, finds the 26 neighbor grids
         and cell indices.
 
         This is a mostly a wrapper for locate_neighbors.
-        
+
         Parameters
         ----------
         position: array-like
@@ -383,16 +408,16 @@
         cis: List of neighbor cell index tuples
 
         Both of these are neighbors that, relative to the current cell
-        index (i,j,k), are ordered as: 
-        
-        (i-1, j-1, k-1), (i-1, j-1, k ), (i-1, j-1, k+1), ...  
-        (i-1, j  , k-1), (i-1, j  , k ), (i-1, j  , k+1), ...  
+        index (i,j,k), are ordered as:
+
+        (i-1, j-1, k-1), (i-1, j-1, k ), (i-1, j-1, k+1), ...
+        (i-1, j  , k-1), (i-1, j  , k ), (i-1, j  , k+1), ...
         (i+1, j+1, k-1), (i-1, j-1, k ), (i+1, j+1, k+1)
 
         That is they start from the lower left and proceed to upper
         right varying the third index most frequently. Note that the
         center cell (i,j,k) is ommitted.
-        
+
         """
         position = np.array(position)
         grid = self.ds.index.grids[self.locate_brick(position).grid -
@@ -421,7 +446,7 @@
         del f
         if self.comm.rank != (self.comm.size-1):
             self.comm.send_array([0],self.comm.rank+1, tag=self.comm.rank)
-        
+
     def load_kd_bricks(self,fn=None):
         if fn is None:
             fn = '%s_kd_bricks.h5' % self.ds
@@ -435,10 +460,10 @@
                     data = [f["brick_%s_%s" %
                               (hex(i), field)][:].astype('float64') for field in self.fields]
                     node.data = PartitionedGrid(node.grid.id, data,
-                                                 node.l_corner.copy(), 
-                                                 node.r_corner.copy(), 
+                                                 node.l_corner.copy(),
+                                                 node.r_corner.copy(),
                                                  node.dims.astype('int64'))
-                    
+
                     self.bricks.append(node.data)
                     self.brick_dimensions.append(node.dims)
 
@@ -457,15 +482,15 @@
         if self.comm.size == 0: return
         nid, pid, lid, rid, les, res, gid, splitdims, splitposs = \
                 self.get_node_arrays()
-        nid = self.comm.par_combine_object(nid, 'cat', 'list') 
-        pid = self.comm.par_combine_object(pid, 'cat', 'list') 
-        lid = self.comm.par_combine_object(lid, 'cat', 'list') 
-        rid = self.comm.par_combine_object(rid, 'cat', 'list') 
-        gid = self.comm.par_combine_object(gid, 'cat', 'list') 
-        les = self.comm.par_combine_object(les, 'cat', 'list') 
-        res = self.comm.par_combine_object(res, 'cat', 'list') 
-        splitdims = self.comm.par_combine_object(splitdims, 'cat', 'list') 
-        splitposs = self.comm.par_combine_object(splitposs, 'cat', 'list') 
+        nid = self.comm.par_combine_object(nid, 'cat', 'list')
+        pid = self.comm.par_combine_object(pid, 'cat', 'list')
+        lid = self.comm.par_combine_object(lid, 'cat', 'list')
+        rid = self.comm.par_combine_object(rid, 'cat', 'list')
+        gid = self.comm.par_combine_object(gid, 'cat', 'list')
+        les = self.comm.par_combine_object(les, 'cat', 'list')
+        res = self.comm.par_combine_object(res, 'cat', 'list')
+        splitdims = self.comm.par_combine_object(splitdims, 'cat', 'list')
+        splitposs = self.comm.par_combine_object(splitposs, 'cat', 'list')
         nid = np.array(nid)
         self.rebuild_tree_from_array(nid, pid, lid,
             rid, les, res, gid, splitdims, splitposs)
@@ -481,25 +506,25 @@
         splitdims = []
         splitposs = []
         for node in depth_first_touch(self.tree.trunk):
-            nids.append(node.node_id) 
-            les.append(node.get_left_edge()) 
-            res.append(node.get_right_edge()) 
+            nids.append(node.node_id)
+            les.append(node.get_left_edge())
+            res.append(node.get_right_edge())
             if node.left is None:
-                leftids.append(-1) 
+                leftids.append(-1)
             else:
-                leftids.append(node.left.node_id) 
+                leftids.append(node.left.node_id)
             if node.right is None:
-                rightids.append(-1) 
+                rightids.append(-1)
             else:
-                rightids.append(node.right.node_id) 
+                rightids.append(node.right.node_id)
             if node.parent is None:
-                parentids.append(-1) 
+                parentids.append(-1)
             else:
-                parentids.append(node.parent.node_id) 
+                parentids.append(node.parent.node_id)
             if node.grid is None:
-                gridids.append(-1) 
+                gridids.append(-1)
             else:
-                gridids.append(node.grid) 
+                gridids.append(node.grid)
             splitdims.append(node.get_split_dim())
             splitposs.append(node.get_split_pos())
 
@@ -510,10 +535,10 @@
                                rids, les, res, gids, splitdims, splitposs):
         del self.tree.trunk
 
-        self.tree.trunk = Node(None, 
+        self.tree.trunk = Node(None,
                     None,
                     None,
-                    les[0], res[0], gids[0], nids[0]) 
+                    les[0], res[0], gids[0], nids[0])
 
         N = nids.shape[0]
         for i in range(N):
@@ -521,14 +546,14 @@
             n.set_left_edge(les[i])
             n.set_right_edge(res[i])
             if lids[i] != -1 and n.left is None:
-                n.left = Node(n, None, None, 
-                              np.zeros(3, dtype='float64'),  
-                              np.zeros(3, dtype='float64'),  
+                n.left = Node(n, None, None,
+                              np.zeros(3, dtype='float64'),
+                              np.zeros(3, dtype='float64'),
                               -1, lids[i])
             if rids[i] != -1 and n.right is None:
-                n.right = Node(n, None, None, 
-                               np.zeros(3, dtype='float64'),  
-                               np.zeros(3, dtype='float64'),  
+                n.right = Node(n, None, None,
+                               np.zeros(3, dtype='float64'),
+                               np.zeros(3, dtype='float64'),
                                -1, rids[i])
             if gids[i] != -1:
                 n.grid = gids[i]
@@ -541,9 +566,9 @@
 
     def count_volume(self):
         return kd_sum_volume(self.tree.trunk)
-    
+
     def count_cells(self):
-        return self.tree.sum_cells() 
+        return self.tree.sum_cells()
 
 if __name__ == "__main__":
     import yt

diff -r 80afc61ee0570b5f4af02e8c0d35242e3c00e725 -r 47df8e8935bc07c24cce20b428b36e566197c0cc yt/utilities/tests/test_amr_kdtree.py
--- a/yt/utilities/tests/test_amr_kdtree.py
+++ b/yt/utilities/tests/test_amr_kdtree.py
@@ -19,8 +19,9 @@
 import yt.utilities.initial_conditions as ic
 import yt.utilities.flagging_methods as fm
 from yt.frontends.stream.api import load_uniform_grid, refine_amr
-from yt.testing import assert_equal
+from yt.testing import assert_equal, assert_almost_equal, fake_amr_ds
 import numpy as np
+import itertools
 
 
 def test_amr_kdtree_coverage():
@@ -61,3 +62,23 @@
         tree_ok *= np.all(dims > 0)
 
     yield assert_equal, True, tree_ok
+
+def test_amr_kdtree_set_fields():
+    ds = fake_amr_ds(fields=["density", "pressure"])
+    dd = ds.all_data()
+
+    fields = ds.field_list
+    dd.tiles.set_fields(fields, [True, True], False)
+    gold = {}
+    for i, block in enumerate(dd.tiles.traverse()):
+        gold[i] = [data.copy() for data in block.my_data]
+
+    for log_fields in itertools.product([True, False], [True, False]):
+        dd.tiles.set_fields(fields, log_fields, False)
+        for iblock, block in enumerate(dd.tiles.traverse()):
+            for i in range(len(fields)):
+                if log_fields[i]:
+                    data = block.my_data[i]
+                else:
+                    data = np.log10(block.my_data[i])
+                assert_almost_equal(gold[iblock][i], data)


https://bitbucket.org/yt_analysis/yt/commits/c7149bcecb6c/
Changeset:   c7149bcecb6c
Branch:      yt
User:        xarthisius
Date:        2016-02-16 21:03:09+00:00
Summary:     Suffix scene min/max_val to easily distinguish it from collection min/max
Affected #:  2 files

diff -r 47df8e8935bc07c24cce20b428b36e566197c0cc -r c7149bcecb6ceb3653f013a227cb668099fb2158 yt/visualization/volume_rendering/input_events.py
--- a/yt/visualization/volume_rendering/input_events.py
+++ b/yt/visualization/volume_rendering/input_events.py
@@ -138,6 +138,9 @@
     scene.add_shader_from_file("max_intensity.fragmentshader")
     for collection in scene.collections:
         collection.set_fields_log(True)
+    # That is clumsy
+    for collection in scene.collections:
+        scene.update_minmax(collection)
     GL.glBlendEquation(GL.GL_MAX)
     return True
 
@@ -147,6 +150,9 @@
     scene.add_shader_from_file("projection.fragmentshader")
     for collection in scene.collections:
         collection.set_fields_log(False)
+    # That is clumsy
+    for collection in scene.collections:
+        scene.update_minmax(collection)
     GL.glBlendEquation(GL.GL_FUNC_ADD)
     return True
 

diff -r 47df8e8935bc07c24cce20b428b36e566197c0cc -r c7149bcecb6ceb3653f013a227cb668099fb2158 yt/visualization/volume_rendering/interactive_vr.py
--- a/yt/visualization/volume_rendering/interactive_vr.py
+++ b/yt/visualization/volume_rendering/interactive_vr.py
@@ -459,7 +459,7 @@
         self.cmap_texture = None
         self.camera = None
         self.shader_program = None
-        self.min_val, self.max_val = 1e60, -1e60
+        self.scene_min_val, self.scene_max_val = 1e60, -1e60
         self.cmap_min, self.cmap_max = 0.0, 0.01
         self.diagonal = 0.0
 
@@ -515,7 +515,6 @@
         GL.glTexSubImage1D(GL.GL_TEXTURE_1D, 0, 0, 256,
                            GL.GL_RGBA, GL.GL_FLOAT, self.camera.cmap)
         GL.glBindTexture(GL.GL_TEXTURE_1D, 0)
-
         self.camera.cmap_new = False
 
 
@@ -589,9 +588,15 @@
 
         """
         self.collections.append(collection)
-        self.min_val = min(self.min_val, collection.min_val)
-        self.max_val = max(self.max_val, collection.max_val)
+        self.update_minmax(collection)
+
+    def update_minmax(self, collection):
+        self.scene_min_val = min(self.scene_min_val, collection.min_val)
+        self.scene_max_val = max(self.scene_max_val, collection.max_val)
         self.diagonal = max(self.diagonal, collection.diagonal)
+        # Figure out a way to change it on the fly
+        self.cmap_min = self.scene_min_val
+        self.cmap_max = self.scene_max_val
 
     def set_camera(self, camera):
         r""" Sets the camera orientation for the entire scene.
@@ -653,7 +658,6 @@
         # Handle colormap
         self.update_cmap_tex()
 
-
         # bind to fb
         GL.glBindFramebuffer(GL.GL_FRAMEBUFFER, self.fbo)
         # clear the color and depth buffer
@@ -677,9 +681,9 @@
         GL.glBindTexture(GL.GL_TEXTURE_1D, self.cmap_texture)
         GL.glUniform1i(self.fb_uniforms["cmap"], 1);
 
-        GL.glUniform1f(self.fb_uniforms["min_val"], self.min_val)
-        GL.glUniform1f(self.fb_uniforms["scale"],
-                       (self.max_val - self.min_val) * self.diagonal)
+        scale = (self.scene_max_val - self.scene_min_val) * self.diagonal
+        GL.glUniform1f(self.fb_uniforms["min_val"], self.scene_min_val)
+        GL.glUniform1f(self.fb_uniforms["scale"], scale)
         GL.glUniform1f(self.fb_uniforms["cmap_min"], self.cmap_min)
         GL.glUniform1f(self.fb_uniforms["cmap_max"], self.cmap_max)
         # clear the color and depth buffer


https://bitbucket.org/yt_analysis/yt/commits/6262948944c1/
Changeset:   6262948944c1
Branch:      yt
User:        xarthisius
Date:        2016-02-17 16:57:23+00:00
Summary:     Fix cmap setting
Affected #:  2 files

diff -r c7149bcecb6ceb3653f013a227cb668099fb2158 -r 6262948944c11ad6e95c28c43c1e8cffca57c7a5 yt/visualization/volume_rendering/input_events.py
--- a/yt/visualization/volume_rendering/input_events.py
+++ b/yt/visualization/volume_rendering/input_events.py
@@ -116,6 +116,7 @@
 
 @register_event("camera_orto")
 def camera_orto(event_coll, event):
+    print("Changing to orthographic camera")
     camera = event_coll.camera
     if camera.proj_func == get_orthographic_matrix:
         return False
@@ -125,6 +126,7 @@
 
 @register_event("camera_proj")
 def camera_proj(event_coll, event):
+    print("Changing to perspective camera")
     camera = event_coll.camera
     if camera.proj_func == get_perspective_matrix:
         return False
@@ -134,25 +136,27 @@
 
 @register_event("shader_max")
 def shader_max(event_coll, event):
+    print("Changing shader to max(intensity)")
     scene = event_coll.scene
     scene.add_shader_from_file("max_intensity.fragmentshader")
     for collection in scene.collections:
         collection.set_fields_log(True)
     # That is clumsy
     for collection in scene.collections:
-        scene.update_minmax(collection)
+        scene.update_minmax(collection, 1.0)
     GL.glBlendEquation(GL.GL_MAX)
     return True
 
 @register_event("shader_proj")
 def shader_proj(event_coll, event):
+    print("Changing shader to projection")
     scene = event_coll.scene
     scene.add_shader_from_file("projection.fragmentshader")
     for collection in scene.collections:
         collection.set_fields_log(False)
     # That is clumsy
     for collection in scene.collections:
-        scene.update_minmax(collection)
+        scene.update_minmax(collection, 0.01)
     GL.glBlendEquation(GL.GL_FUNC_ADD)
     return True
 
@@ -178,14 +182,14 @@
 
 @register_event("print_limits")
 def print_limits(event_coll, event):
-    print event_coll.scene.min_val,
-    print event_coll.scene.max_val
+    print event_coll.scene.scene_min_val,
+    print event_coll.scene.scene_max_val
     return False
 
 @register_event("up_lower")
 def up_lower(event_coll, event):
-    event_coll.scene.min_val += 1
-    print event_coll.scene.min_val
+    event_coll.scene.scene_min_val += 1
+    print event_coll.scene.scene_min_val
     return True
 
 @register_event("debug_buffer")

diff -r c7149bcecb6ceb3653f013a227cb668099fb2158 -r 6262948944c11ad6e95c28c43c1e8cffca57c7a5 yt/visualization/volume_rendering/interactive_vr.py
--- a/yt/visualization/volume_rendering/interactive_vr.py
+++ b/yt/visualization/volume_rendering/interactive_vr.py
@@ -590,13 +590,15 @@
         self.collections.append(collection)
         self.update_minmax(collection)
 
-    def update_minmax(self, collection):
+    def update_minmax(self, collection, scale_off=1.0):
+        self.scene_min_val, self.scene_max_val = 1e60, -1e60
+        self.cmap_min, self.cmap_max = 0.0, 0.01
         self.scene_min_val = min(self.scene_min_val, collection.min_val)
         self.scene_max_val = max(self.scene_max_val, collection.max_val)
         self.diagonal = max(self.diagonal, collection.diagonal)
         # Figure out a way to change it on the fly
         self.cmap_min = self.scene_min_val
-        self.cmap_max = self.scene_max_val
+        self.cmap_max = self.scene_max_val * scale_off
 
     def set_camera(self, camera):
         r""" Sets the camera orientation for the entire scene.


https://bitbucket.org/yt_analysis/yt/commits/19cd7ca3d578/
Changeset:   19cd7ca3d578
Branch:      yt
User:        xarthisius
Date:        2016-02-18 18:26:34+00:00
Summary:     Add key shortcuts for cmap adjustments
Affected #:  4 files

diff -r 6262948944c11ad6e95c28c43c1e8cffca57c7a5 -r 19cd7ca3d5786292f30906ddea3fd1ae6bfe602f yt/visualization/volume_rendering/input_events.py
--- a/yt/visualization/volume_rendering/input_events.py
+++ b/yt/visualization/volume_rendering/input_events.py
@@ -141,9 +141,7 @@
     scene.add_shader_from_file("max_intensity.fragmentshader")
     for collection in scene.collections:
         collection.set_fields_log(True)
-    # That is clumsy
-    for collection in scene.collections:
-        scene.update_minmax(collection, 1.0)
+    scene.update_minmax()
     GL.glBlendEquation(GL.GL_MAX)
     return True
 
@@ -154,9 +152,7 @@
     scene.add_shader_from_file("projection.fragmentshader")
     for collection in scene.collections:
         collection.set_fields_log(False)
-    # That is clumsy
-    for collection in scene.collections:
-        scene.update_minmax(collection, 0.01)
+    scene.update_minmax()
     GL.glBlendEquation(GL.GL_FUNC_ADD)
     return True
 
@@ -170,6 +166,35 @@
     print("Setting colormap to {}".format(cmap.name))
     return True
 
+ at register_event("cmap_max_up")
+def cmap_max_up(event_coll, event):
+    return _cmap_adjust(event_coll.camera, True, 2.0)
+
+ at register_event("cmap_max_down")
+def cmap_max_down(event_coll, event):
+    return _cmap_adjust(event_coll.camera, True, 0.5)
+
+ at register_event("cmap_min_up")
+def cmap_min_up(event_coll, event):
+    return _cmap_adjust(event_coll.camera, False, 2.0)
+
+ at register_event("cmap_min_down")
+def cmap_min_down(event_coll, event):
+    return _cmap_adjust(event_coll.camera, False, 0.5)
+
+ at register_event("cmap_toggle_log")
+def cmap_toggle_log(event_coll, event):
+    event_coll.camera.cmap_log = not event_coll.camera.cmap_log
+    return True
+
+def _cmap_adjust(camera, upper, factor):
+    if upper:
+        camera.cmap_max *= factor
+    else:
+        camera.cmap_min *= factor
+    print("cmap limits: ", camera.cmap_min, camera.cmap_max)
+    return True
+
 @register_event("closeup")
 def closeup(event_coll, event):
     event_coll.camera.position = (0.01, 0.01, 0.01)
@@ -182,16 +207,10 @@
 
 @register_event("print_limits")
 def print_limits(event_coll, event):
-    print event_coll.scene.scene_min_val,
-    print event_coll.scene.scene_max_val
+    print event_coll.scene.min_val,
+    print event_coll.scene.max_val
     return False
 
- at register_event("up_lower")
-def up_lower(event_coll, event):
-    event_coll.scene.scene_min_val += 1
-    print event_coll.scene.scene_min_val
-    return True
-
 @register_event("debug_buffer")
 def debug_buffer(event_coll, event):
     buffer = event_coll.scene._retrieve_framebuffer()

diff -r 6262948944c11ad6e95c28c43c1e8cffca57c7a5 -r 19cd7ca3d5786292f30906ddea3fd1ae6bfe602f yt/visualization/volume_rendering/interactive_loop.py
--- a/yt/visualization/volume_rendering/interactive_loop.py
+++ b/yt/visualization/volume_rendering/interactive_loop.py
@@ -42,9 +42,13 @@
         callbacks.add_key_callback("camera_proj", "p")
         callbacks.add_key_callback("shader_max", "1")
         callbacks.add_key_callback("shader_proj", "2")
-        callbacks.add_key_callback("print_limits", "l")
-        callbacks.add_key_callback("up_lower", "u")
+        callbacks.add_key_callback("print_limits", "h")
         callbacks.add_key_callback("debug_buffer", "d")
+        callbacks.add_key_callback("cmap_max_up", "right_bracket")
+        callbacks.add_key_callback("cmap_max_down", "left_bracket")
+        callbacks.add_key_callback("cmap_min_up", "semicolon")
+        callbacks.add_key_callback("cmap_min_down", "apostrophe")
+        callbacks.add_key_callback("cmap_toggle_log", "l")
         mouse_callbacks = MouseRotation()
         callbacks.add_mouse_callback(mouse_callbacks.start_rotation,
             glfw.MOUSE_BUTTON_LEFT)

diff -r 6262948944c11ad6e95c28c43c1e8cffca57c7a5 -r 19cd7ca3d5786292f30906ddea3fd1ae6bfe602f yt/visualization/volume_rendering/interactive_vr.py
--- a/yt/visualization/volume_rendering/interactive_vr.py
+++ b/yt/visualization/volume_rendering/interactive_vr.py
@@ -128,6 +128,9 @@
         self.up = np.array(up)
         cmap = cm.get_cmap("algae")
         self.cmap = np.array(cmap(np.linspace(0, 1, 256)), dtype=np.float32)
+        self.cmap_min = 1e-6
+        self.cmap_max = 1.0
+        self.cmap_log = True
         self.cmap_new = True
 
         self.view_matrix = get_lookat_matrix(self.position,
@@ -294,6 +297,7 @@
         """
         self.data_source = data_source
         self.data_source.tiles.set_fields([field], [log_field], no_ghost=False)
+        self.data_logged = log_field
         self.blocks = {}
         self.block_order = []
         # Every time we change our data source, we wipe all existing ones.
@@ -459,8 +463,8 @@
         self.cmap_texture = None
         self.camera = None
         self.shader_program = None
-        self.scene_min_val, self.scene_max_val = 1e60, -1e60
-        self.cmap_min, self.cmap_max = 0.0, 0.01
+        self.min_val, self.max_val = 1e60, -1e60
+        self.cmap_log = True
         self.diagonal = 0.0
 
         ox, oy, width, height = GL.glGetIntegerv(GL.GL_VIEWPORT)
@@ -471,7 +475,7 @@
             ["passthrough.vertexshader", "apply_colormap.fragmentshader"]
         )
         for key in ["fb_texture", "cmap", "min_val", "scale",
-                    "cmap_min", "cmap_max"]:
+                    "cmap_min", "cmap_max", "cmap_log"]:
             self.fb_uniforms[key] = \
                 GL.glGetUniformLocation(self.fb_shader_program, key)
 
@@ -588,17 +592,15 @@
 
         """
         self.collections.append(collection)
-        self.update_minmax(collection)
+        self.update_minmax()
 
-    def update_minmax(self, collection, scale_off=1.0):
-        self.scene_min_val, self.scene_max_val = 1e60, -1e60
-        self.cmap_min, self.cmap_max = 0.0, 0.01
-        self.scene_min_val = min(self.scene_min_val, collection.min_val)
-        self.scene_max_val = max(self.scene_max_val, collection.max_val)
-        self.diagonal = max(self.diagonal, collection.diagonal)
-        # Figure out a way to change it on the fly
-        self.cmap_min = self.scene_min_val
-        self.cmap_max = self.scene_max_val * scale_off
+    def update_minmax(self):
+        self.min_val, self.max_val, self.diagonal = 1e60, -1e60, -1e60
+        for collection in self.collections:
+            self.min_val = min(self.min_val, collection.min_val)
+            self.max_val = max(self.max_val, collection.max_val)
+            # doesn't make sense for multiple collections
+            self.diagonal = max(self.diagonal, collection.diagonal)
 
     def set_camera(self, camera):
         r""" Sets the camera orientation for the entire scene.
@@ -683,11 +685,12 @@
         GL.glBindTexture(GL.GL_TEXTURE_1D, self.cmap_texture)
         GL.glUniform1i(self.fb_uniforms["cmap"], 1);
 
-        scale = (self.scene_max_val - self.scene_min_val) * self.diagonal
-        GL.glUniform1f(self.fb_uniforms["min_val"], self.scene_min_val)
+        scale = (self.max_val - self.min_val) * self.diagonal
+        GL.glUniform1f(self.fb_uniforms["min_val"], self.min_val)
         GL.glUniform1f(self.fb_uniforms["scale"], scale)
-        GL.glUniform1f(self.fb_uniforms["cmap_min"], self.cmap_min)
-        GL.glUniform1f(self.fb_uniforms["cmap_max"], self.cmap_max)
+        GL.glUniform1f(self.fb_uniforms["cmap_min"], self.camera.cmap_min)
+        GL.glUniform1f(self.fb_uniforms["cmap_max"], self.camera.cmap_max)
+        GL.glUniform1f(self.fb_uniforms["cmap_log"], float(self.camera.cmap_log))
         # clear the color and depth buffer
         GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT)
         # Bind to Vertex array that contains simple quad filling fullscreen,

diff -r 6262948944c11ad6e95c28c43c1e8cffca57c7a5 -r 19cd7ca3d5786292f30906ddea3fd1ae6bfe602f yt/visualization/volume_rendering/shaders/apply_colormap.fragmentshader
--- a/yt/visualization/volume_rendering/shaders/apply_colormap.fragmentshader
+++ b/yt/visualization/volume_rendering/shaders/apply_colormap.fragmentshader
@@ -10,8 +10,17 @@
 uniform float scale;
 uniform float cmap_min;
 uniform float cmap_max;
+uniform float cmap_log;
 
 void main(){
    float scaled = texture(fb_texture, UV).x * scale + min_val;
-   color = texture(cmap, (scaled - cmap_min) / (cmap_max - cmap_min));
+   float cm = cmap_min;
+   float cp = cmap_max;
+
+   if (cmap_log > 0.5) {
+      scaled = log(scaled) / log(10.f);
+      cm = log(cm) / log(10.f);
+      cp = log(cp) / log(10.f);
+   }
+   color = texture(cmap, (scaled - cm) / (cp - cm));
 }


https://bitbucket.org/yt_analysis/yt/commits/1e20cff1b05c/
Changeset:   1e20cff1b05c
Branch:      yt
User:        xarthisius
Date:        2016-02-18 20:17:14+00:00
Summary:     Fix scaling for logscales
Affected #:  3 files

diff -r 19cd7ca3d5786292f30906ddea3fd1ae6bfe602f -r 1e20cff1b05cb66a5f90493bc88545ba9506e21d yt/visualization/volume_rendering/input_events.py
--- a/yt/visualization/volume_rendering/input_events.py
+++ b/yt/visualization/volume_rendering/input_events.py
@@ -168,33 +168,47 @@
 
 @register_event("cmap_max_up")
 def cmap_max_up(event_coll, event):
-    return _cmap_adjust(event_coll.camera, True, 2.0)
+    if event_coll.camera.cmap_log:
+        event_coll.camera.cmap_max += 0.5
+    else:
+        event_coll.camera.cmap_max *= 2.0
+    return True
 
 @register_event("cmap_max_down")
 def cmap_max_down(event_coll, event):
-    return _cmap_adjust(event_coll.camera, True, 0.5)
+    if event_coll.camera.cmap_log:
+        event_coll.camera.cmap_max -= 0.5
+    else:
+        event_coll.camera.cmap_max *= 0.5
+    return True
 
 @register_event("cmap_min_up")
 def cmap_min_up(event_coll, event):
-    return _cmap_adjust(event_coll.camera, False, 2.0)
+    if event_coll.camera.cmap_log:
+        event_coll.camera.cmap_min += 0.5
+    else:
+        event_coll.camera.cmap_min *= 2.0
+    return True
 
 @register_event("cmap_min_down")
 def cmap_min_down(event_coll, event):
-    return _cmap_adjust(event_coll.camera, False, 0.5)
+    if event_coll.camera.cmap_log:
+        event_coll.camera.cmap_min -= 0.5
+    else:
+        event_coll.camera.cmap_min *= 0.5
+    return True
 
 @register_event("cmap_toggle_log")
 def cmap_toggle_log(event_coll, event):
+    if not event_coll.camera.cmap_log:
+        event_coll.camera.cmap_max = np.log10(event_coll.camera.cmap_max)
+        event_coll.camera.cmap_min = np.log10(event_coll.camera.cmap_min)
+    else:
+        event_coll.camera.cmap_max = 10.0 ** event_coll.camera.cmap_max
+        event_coll.camera.cmap_min = 10.0 ** event_coll.camera.cmap_min
     event_coll.camera.cmap_log = not event_coll.camera.cmap_log
     return True
 
-def _cmap_adjust(camera, upper, factor):
-    if upper:
-        camera.cmap_max *= factor
-    else:
-        camera.cmap_min *= factor
-    print("cmap limits: ", camera.cmap_min, camera.cmap_max)
-    return True
-
 @register_event("closeup")
 def closeup(event_coll, event):
     event_coll.camera.position = (0.01, 0.01, 0.01)
@@ -207,8 +221,8 @@
 
 @register_event("print_limits")
 def print_limits(event_coll, event):
-    print event_coll.scene.min_val,
-    print event_coll.scene.max_val
+    print event_coll.scene.min_val, event_coll.scene.max_val
+    print event_coll.camera.cmap_min, event_coll.camera.cmap_max, event_coll.camera.cmap_log
     return False
 
 @register_event("debug_buffer")

diff -r 19cd7ca3d5786292f30906ddea3fd1ae6bfe602f -r 1e20cff1b05cb66a5f90493bc88545ba9506e21d yt/visualization/volume_rendering/interactive_vr.py
--- a/yt/visualization/volume_rendering/interactive_vr.py
+++ b/yt/visualization/volume_rendering/interactive_vr.py
@@ -115,7 +115,8 @@
                  position=(0.0, 0.0, 1.0),
                  focus=(0.0, 0.0, 0.0),
                  up=(0.0, 1.0, 0.0),
-                 fov=45.0, near_plane=0.01, far_plane=20.0, aspect_ratio=8.0/6.0):
+                 fov=45.0, near_plane=0.01, far_plane=20.0,
+                 aspect_ratio=8.0/6.0):
         self.view_matrix = np.zeros((4, 4), dtype=np.float32)
         self.proj_matrix = np.zeros((4, 4), dtype=np.float32)
         self.proj_func = get_perspective_matrix
@@ -128,8 +129,8 @@
         self.up = np.array(up)
         cmap = cm.get_cmap("algae")
         self.cmap = np.array(cmap(np.linspace(0, 1, 256)), dtype=np.float32)
-        self.cmap_min = 1e-6
-        self.cmap_max = 1.0
+        self.cmap_min = 1e55
+        self.cmap_max = -1e55
         self.cmap_log = True
         self.cmap_new = True
 
@@ -197,6 +198,12 @@
         return self.projection_matrix
 
 
+    def update_cmap_minmax(self, minval, maxval, iflog):
+        self.cmap_log = iflog
+        self.cmap_min = minval
+        self.cmap_max = maxval
+        print("CMAP updated to: ", self.cmap_min, self.cmap_max, self.cmap_log)
+
 class Camera:
     def __init__(self, position = (0, 0, 0), fov = 60.0, near_plane = 0.01,
             far_plane = 20, aspect_ratio = 8.0 / 6.0, focus = (0, 0, 0),
@@ -443,6 +450,7 @@
             dx, dy, dz = block.my_data[0].shape
             n_data = block.my_data[0].copy(order="F").astype("float32")
             n_data = (n_data - self.min_val) / ((self.max_val - self.min_val) * self.diagonal)
+            print n_data.min(), n_data.max()
             GL.glBindTexture(GL.GL_TEXTURE_3D, texture_name)
             GL.glTexParameterf(GL.GL_TEXTURE_3D, GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP_TO_EDGE)
             GL.glTexParameterf(GL.GL_TEXTURE_3D, GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP_TO_EDGE)
@@ -464,8 +472,8 @@
         self.camera = None
         self.shader_program = None
         self.min_val, self.max_val = 1e60, -1e60
-        self.cmap_log = True
         self.diagonal = 0.0
+        self.data_logged = True
 
         ox, oy, width, height = GL.glGetIntegerv(GL.GL_VIEWPORT)
         self.width = width
@@ -595,12 +603,21 @@
         self.update_minmax()
 
     def update_minmax(self):
+        print("update_minmax ->")
         self.min_val, self.max_val, self.diagonal = 1e60, -1e60, -1e60
+        self.data_logged = False
+
         for collection in self.collections:
             self.min_val = min(self.min_val, collection.min_val)
             self.max_val = max(self.max_val, collection.max_val)
             # doesn't make sense for multiple collections
             self.diagonal = max(self.diagonal, collection.diagonal)
+            self.data_logged = self.data_logged or collection.data_logged
+
+        if self.camera is not None:
+            self.camera.update_cmap_minmax(self.min_val, self.max_val,
+                                           self.data_logged)
+
 
     def set_camera(self, camera):
         r""" Sets the camera orientation for the entire scene.

diff -r 19cd7ca3d5786292f30906ddea3fd1ae6bfe602f -r 1e20cff1b05cb66a5f90493bc88545ba9506e21d yt/visualization/volume_rendering/shaders/apply_colormap.fragmentshader
--- a/yt/visualization/volume_rendering/shaders/apply_colormap.fragmentshader
+++ b/yt/visualization/volume_rendering/shaders/apply_colormap.fragmentshader
@@ -19,8 +19,6 @@
 
    if (cmap_log > 0.5) {
       scaled = log(scaled) / log(10.f);
-      cm = log(cm) / log(10.f);
-      cp = log(cp) / log(10.f);
    }
    color = texture(cmap, (scaled - cm) / (cp - cm));
 }


https://bitbucket.org/yt_analysis/yt/commits/7c9983ebf9dd/
Changeset:   7c9983ebf9dd
Branch:      yt
User:        xarthisius
Date:        2016-02-18 21:40:40+00:00
Summary:     Remove debug, fix all issues with linear/log scales
Affected #:  3 files

diff -r 1e20cff1b05cb66a5f90493bc88545ba9506e21d -r 7c9983ebf9dd133c43f902768f48c540739738dc yt/visualization/volume_rendering/input_events.py
--- a/yt/visualization/volume_rendering/input_events.py
+++ b/yt/visualization/volume_rendering/input_events.py
@@ -134,6 +134,7 @@
     camera.fov = np.degrees(np.arctan(camera.fov) * 2.0)
     return True
 
+
 @register_event("shader_max")
 def shader_max(event_coll, event):
     print("Changing shader to max(intensity)")
@@ -200,6 +201,10 @@
 
 @register_event("cmap_toggle_log")
 def cmap_toggle_log(event_coll, event):
+    if event_coll.scene.data_logged:
+        print("Data is logged already, can't toggle scale to linear")
+        return False
+
     if not event_coll.camera.cmap_log:
         event_coll.camera.cmap_max = np.log10(event_coll.camera.cmap_max)
         event_coll.camera.cmap_min = np.log10(event_coll.camera.cmap_min)

diff -r 1e20cff1b05cb66a5f90493bc88545ba9506e21d -r 7c9983ebf9dd133c43f902768f48c540739738dc yt/visualization/volume_rendering/interactive_loop.py
--- a/yt/visualization/volume_rendering/interactive_loop.py
+++ b/yt/visualization/volume_rendering/interactive_loop.py
@@ -26,6 +26,7 @@
 
     def setup_loop(self, scene, camera):
         scene.set_camera(camera)
+        scene.update_minmax()
         scene.add_shader_from_file("max_intensity.fragmentshader")
         camera.compute_matrices()
         frame_start = glfw.GetTime()

diff -r 1e20cff1b05cb66a5f90493bc88545ba9506e21d -r 7c9983ebf9dd133c43f902768f48c540739738dc yt/visualization/volume_rendering/interactive_vr.py
--- a/yt/visualization/volume_rendering/interactive_vr.py
+++ b/yt/visualization/volume_rendering/interactive_vr.py
@@ -202,7 +202,6 @@
         self.cmap_log = iflog
         self.cmap_min = minval
         self.cmap_max = maxval
-        print("CMAP updated to: ", self.cmap_min, self.cmap_max, self.cmap_log)
 
 class Camera:
     def __init__(self, position = (0, 0, 0), fov = 60.0, near_plane = 0.01,
@@ -450,7 +449,6 @@
             dx, dy, dz = block.my_data[0].shape
             n_data = block.my_data[0].copy(order="F").astype("float32")
             n_data = (n_data - self.min_val) / ((self.max_val - self.min_val) * self.diagonal)
-            print n_data.min(), n_data.max()
             GL.glBindTexture(GL.GL_TEXTURE_3D, texture_name)
             GL.glTexParameterf(GL.GL_TEXTURE_3D, GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP_TO_EDGE)
             GL.glTexParameterf(GL.GL_TEXTURE_3D, GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP_TO_EDGE)
@@ -603,7 +601,6 @@
         self.update_minmax()
 
     def update_minmax(self):
-        print("update_minmax ->")
         self.min_val, self.max_val, self.diagonal = 1e60, -1e60, -1e60
         self.data_logged = False
 
@@ -707,7 +704,10 @@
         GL.glUniform1f(self.fb_uniforms["scale"], scale)
         GL.glUniform1f(self.fb_uniforms["cmap_min"], self.camera.cmap_min)
         GL.glUniform1f(self.fb_uniforms["cmap_max"], self.camera.cmap_max)
-        GL.glUniform1f(self.fb_uniforms["cmap_log"], float(self.camera.cmap_log))
+        if self.data_logged:
+            GL.glUniform1f(self.fb_uniforms["cmap_log"], float(False))
+        else:
+            GL.glUniform1f(self.fb_uniforms["cmap_log"], float(self.camera.cmap_log))
         # clear the color and depth buffer
         GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT)
         # Bind to Vertex array that contains simple quad filling fullscreen,


https://bitbucket.org/yt_analysis/yt/commits/05c0d128b9b0/
Changeset:   05c0d128b9b0
Branch:      yt
User:        MatthewTurk
Date:        2016-02-23 17:41:14+00:00
Summary:     WIP addition of simple transfer functions
Affected #:  2 files

diff -r 7c9983ebf9dd133c43f902768f48c540739738dc -r 05c0d128b9b09e9d82d17be56453166cd8b39022 yt/visualization/volume_rendering/shaders/passthrough.fragmentshader
--- /dev/null
+++ b/yt/visualization/volume_rendering/shaders/passthrough.fragmentshader
@@ -0,0 +1,10 @@
+#version 330 core
+
+in vec2 UV;
+
+out vec4 color;
+uniform sampler2D fb_texture;
+
+void main(){
+   color = texture(fb_texture, UV);
+}

diff -r 7c9983ebf9dd133c43f902768f48c540739738dc -r 05c0d128b9b09e9d82d17be56453166cd8b39022 yt/visualization/volume_rendering/shaders/transfer_function.fragmentshader
--- /dev/null
+++ b/yt/visualization/volume_rendering/shaders/transfer_function.fragmentshader
@@ -0,0 +1,71 @@
+#version 330
+in vec4 v_model;
+in vec3 v_camera_pos;
+in vec3 dx;
+in vec3 left_edge;
+in vec3 right_edge;
+flat in mat4 inverse_proj;
+flat in mat4 inverse_view;
+out vec4 output_color;
+
+uniform sampler3D ds_tex;
+//layout (binding = 1) uniform sampler2D depth_tex;
+uniform vec4 viewport; // (offset_x, offset_y, 1 / screen_x, 1 / screen_y)
+
+bool within_bb(vec3 pos)
+{
+    bvec3 left =  greaterThanEqual(pos, left_edge);
+    bvec3 right = lessThanEqual(pos, right_edge);
+    return all(left) && all(right);
+}
+
+void main()
+{
+    // Obtain screen coordinates
+    // https://www.opengl.org/wiki/Compute_eye_space_from_window_space#From_gl_FragCoord
+    vec4 ndcPos;
+    ndcPos.xy = ((2.0 * gl_FragCoord.xy) - (2.0 * viewport.xy)) / (viewport.zw) - 1;
+    ndcPos.z = (2.0 * gl_FragCoord.z - 1.0);
+    ndcPos.w = 1.0;
+
+    vec4 clipPos = ndcPos / gl_FragCoord.w;
+    vec4 eyePos = inverse_proj * clipPos;
+    eyePos /= eyePos.w;
+
+    vec4 world_location = inverse_view * eyePos;
+
+    // Five samples
+    vec3 step_size = dx / 5.0;
+    vec3 dir = normalize(world_location.xyz - v_camera_pos.xyz);
+    vec4 curr_color = vec4(0.0);
+    float dist = dot(step_size, abs(dir));
+    dist = length(step_size * dir);
+
+    vec3 ray_position = world_location.xyz;
+
+    vec3 tex_curr_pos = vec3(0.0);
+    vec3 range = right_edge - left_edge;
+    bool ray_in_bb = true;
+    vec4 new_color;
+    float v;
+    while (ray_in_bb) {
+        tex_curr_pos = (ray_position - left_edge)/range;
+
+        vec3 tex_sample = texture(ds_tex, tex_curr_pos).rgb;
+        if (abs(tex_sample.r - 0.2) < 0.05) {
+            v = (tex_sample.r - 0.2);
+            v = clamp(exp(-v*v/0.0001), 0.0, 1.0);
+            new_color = v * vec4(0.0, 0.0, 1.0, 1.0);
+        } else if (abs(tex_sample.r - 0.3) < 0.1) {
+            v = (tex_sample.r - 0.3);
+            v = clamp(exp(-v*v/0.001), 0.0, 1.0);
+            new_color = v * vec4(1.0, 0.0, 0.0, 1.0);
+        }
+
+        curr_color = dist*new_color + clamp(1.0 - dist*new_color.a, 0.0, 1.0) * curr_color;
+        ray_position += dir * step_size;
+        ray_in_bb = within_bb(ray_position);
+    }
+
+    output_color = clamp(curr_color, 0.0, 1.0);
+}


https://bitbucket.org/yt_analysis/yt/commits/f7d8e1264bc4/
Changeset:   f7d8e1264bc4
Branch:      yt
User:        xarthisius
Date:        2016-02-24 18:50:39+00:00
Summary:     Properly clean up shaders and shader programs, ensure that Uniforms' location is updated after switching, add simple noop fragmentshader
Affected #:  2 files

diff -r 05c0d128b9b09e9d82d17be56453166cd8b39022 -r f7d8e1264bc4e6b79072384d7e934cdcbf300c13 yt/visualization/volume_rendering/input_events.py
--- a/yt/visualization/volume_rendering/input_events.py
+++ b/yt/visualization/volume_rendering/input_events.py
@@ -139,10 +139,16 @@
 def shader_max(event_coll, event):
     print("Changing shader to max(intensity)")
     scene = event_coll.scene
-    scene.add_shader_from_file("max_intensity.fragmentshader")
+    scene.fb_shaders = ["passthrough.vertexshader",
+                        "apply_colormap.fragmentshader"]
+    scene.update_fb_shaders()
+    scene.shaders = ["default.vertexshader",
+                     "max_intensity.fragmentshader"]
+    scene.update_shaders()
     for collection in scene.collections:
         collection.set_fields_log(True)
     scene.update_minmax()
+    GL.glBlendFunc(GL.GL_ONE, GL.GL_ONE)
     GL.glBlendEquation(GL.GL_MAX)
     return True
 
@@ -150,10 +156,34 @@
 def shader_proj(event_coll, event):
     print("Changing shader to projection")
     scene = event_coll.scene
-    scene.add_shader_from_file("projection.fragmentshader")
+    scene.fb_shaders = ["passthrough.vertexshader",
+                        "apply_colormap.fragmentshader"]
+    scene.update_fb_shaders()
+    scene.shaders = ["default.vertexshader",
+                     "projection.fragmentshader"]
+    scene.update_shaders()
     for collection in scene.collections:
         collection.set_fields_log(False)
     scene.update_minmax()
+    GL.glBlendFunc(GL.GL_ONE, GL.GL_ONE)
+    GL.glBlendEquation(GL.GL_FUNC_ADD)
+    return True
+
+ at register_event("shader_test")
+def shader_test(event_coll, event):
+    print("Changing shader to projection")
+    scene = event_coll.scene
+    scene.shaders = ["default.vertexshader",
+                     "transfer_function.fragmentshader"]
+    scene.update_shaders()
+    scene.fb_shaders = ["passthrough.vertexshader",
+                        "noop.fragmentshader"]
+    scene.update_fb_shaders()
+    for collection in scene.collections:
+        collection.set_fields_log(True)
+    #scene.update_minmax()
+    # https://www.opengl.org/sdk/docs/man/html/glBlendFunc.xhtml
+    #GL.glBlendFunc(GL.GL_ONE, GL.GL_DST_ALPHA)
     GL.glBlendEquation(GL.GL_FUNC_ADD)
     return True
 
@@ -277,3 +307,30 @@
         self.start = new_end
         return True
 
+class BlendFuncs(object):
+    possibilities = (
+        "GL_ZERO", "GL_ONE", "GL_SRC_COLOR", "GL_ONE_MINUS_SRC_COLOR",
+        "GL_DST_COLOR", "GL_ONE_MINUS_DST_COLOR", "GL_SRC_ALPHA",
+        "GL_ONE_MINUS_SRC_ALPHA", "GL_DST_ALPHA", "GL_ONE_MINUS_DST_ALPHA",
+        "GL_CONSTANT_COLOR", "GL_ONE_MINUS_CONSTANT_COLOR",
+        "GL_CONSTANT_ALPHA", "GL_ONE_MINUS_CONSTANT_ALPHA")
+    source_i = 0
+    dest_i = 0
+
+    def next_source(self, event_coll, event):
+        self.source_i = (self.source_i + 1) % len(self.possibilities)
+        s = getattr(GL, self.possibilities[self.source_i])
+        d = getattr(GL, self.possibilities[self.dest_i])
+        print "Setting source to %s and dest to %s" % (
+            self.possibilities[self.source_i], self.possibilities[self.dest_i])
+        GL.glBlendFunc(s, d)
+        return True
+
+    def next_dest(self, event_coll, event):
+        self.dest_i = (self.dest_i + 1) % len(self.possibilities)
+        s = getattr(GL, self.possibilities[self.source_i])
+        d = getattr(GL, self.possibilities[self.dest_i])
+        print "Setting source to %s and dest to %s" % (
+            self.possibilities[self.source_i], self.possibilities[self.dest_i])
+        GL.glBlendFunc(s, d)
+        return True

diff -r 05c0d128b9b09e9d82d17be56453166cd8b39022 -r f7d8e1264bc4e6b79072384d7e934cdcbf300c13 yt/visualization/volume_rendering/interactive_vr.py
--- a/yt/visualization/volume_rendering/interactive_vr.py
+++ b/yt/visualization/volume_rendering/interactive_vr.py
@@ -24,7 +24,10 @@
         except AttributeError:
             raise YTInvalidShaderType(source)
 
-    sh_directory = os.path.join(os.path.dirname(__file__), "shaders")
+    if os.path.isfile(source):
+        sh_directory = ''
+    else:
+        sh_directory = os.path.join(os.path.dirname(__file__), "shaders")
     shader = GL.glCreateShader(
         eval("GL.GL_{}_SHADER".format(shader_type.upper()))
     )
@@ -39,14 +42,18 @@
 
 def link_shader_program(shaders):
     """Create a shader program with from compiled shaders."""
+    compiled_shaders = []
     program = GL.glCreateProgram()
     for shader in shaders:
-        GL.glAttachShader(program, _compile_shader(shader))
+        compiled_shaders.append(_compile_shader(shader))
+        GL.glAttachShader(program, compiled_shaders[-1])
     GL.glLinkProgram(program)
     # check linking error
     result = GL.glGetProgramiv(program, GL.GL_LINK_STATUS)
     if not(result):
         raise RuntimeError(GL.glGetProgramInfoLog(program))
+    for shader in compiled_shaders:
+        GL.glDeleteShader(shader)
     return program
 
 
@@ -469,6 +476,7 @@
         self.cmap_texture = None
         self.camera = None
         self.shader_program = None
+        self.fb_shader_program = None
         self.min_val, self.max_val = 1e60, -1e60
         self.diagonal = 0.0
         self.data_logged = True
@@ -477,13 +485,9 @@
         self.width = width
         self.height = height
 
-        self.fb_shader_program = link_shader_program(
-            ["passthrough.vertexshader", "apply_colormap.fragmentshader"]
-        )
-        for key in ["fb_texture", "cmap", "min_val", "scale",
-                    "cmap_min", "cmap_max", "cmap_log"]:
-            self.fb_uniforms[key] = \
-                GL.glGetUniformLocation(self.fb_shader_program, key)
+        self.fb_shaders = ["passthrough.vertexshader",
+                           "apply_colormap.fragmentshader"]
+        self.update_fb_shaders()
 
         self.fb_vao_name = GL.glGenVertexArrays(1)
         GL.glBindVertexArray(self.fb_vao_name)
@@ -500,6 +504,16 @@
 
         self.setup_fb(self.width, self.height)
 
+    def update_fb_shaders(self, fb_shaders=None):
+        if self.fb_shader_program is not None:
+            GL.glDeleteProgram(self.fb_shader_program)
+        self.fb_shader_program = \
+            link_shader_program(fb_shaders or self.fb_shaders)
+        for key in ["fb_texture", "cmap", "min_val", "scale",
+                    "cmap_min", "cmap_max", "cmap_log"]:
+            self.fb_uniforms[key] = \
+                GL.glGetUniformLocation(self.fb_shader_program, key)
+
     def setup_cmap_tex(self):
         '''Creates 1D texture that will hold colormap in framebuffer'''
         self.cmap_texture = GL.glGenTextures(1)   # create target texture
@@ -645,9 +659,14 @@
             The location of the shader source file to read.
 
         """
-        self.shader_program = link_shader_program(
-            ['default.vertexshader', filename]
-        )
+        self.shaders = ['default.vertexshader', filename]
+        self.update_shaders()
+
+    def update_shaders(self, shaders=None):
+        if self.shader_program is not None:
+            GL.glDeleteProgram(self.shader_program)
+            print("Deleted program")
+        self.shader_program = link_shader_program(shaders or self.shaders)
 
     def _retrieve_framebuffer(self):
         ox, oy, width, height = GL.glGetIntegerv(GL.GL_VIEWPORT)
@@ -697,7 +716,7 @@
 
         GL.glActiveTexture(GL.GL_TEXTURE1)
         GL.glBindTexture(GL.GL_TEXTURE_1D, self.cmap_texture)
-        GL.glUniform1i(self.fb_uniforms["cmap"], 1);
+        GL.glUniform1i(self.fb_uniforms["cmap"], 1)
 
         scale = (self.max_val - self.min_val) * self.diagonal
         GL.glUniform1f(self.fb_uniforms["min_val"], self.min_val)
@@ -719,3 +738,5 @@
         # Clean up
         GL.glDisableVertexAttribArray(0)
         GL.glBindVertexArray(0)
+        GL.glBindTexture(GL.GL_TEXTURE_1D, 0)
+        GL.glBindTexture(GL.GL_TEXTURE_2D, 0)


https://bitbucket.org/yt_analysis/yt/commits/97a4ddb20949/
Changeset:   97a4ddb20949
Branch:      yt
User:        MatthewTurk
Date:        2016-02-24 21:31:18+00:00
Summary:     Beginning refactoring of the way we set uniforms.
Affected #:  2 files

diff -r f7d8e1264bc4e6b79072384d7e934cdcbf300c13 -r 97a4ddb20949ddbcd5fd4aaec864f3896f5054bc yt/utilities/exceptions.py
--- a/yt/utilities/exceptions.py
+++ b/yt/utilities/exceptions.py
@@ -537,3 +537,17 @@
 
     def __str__(self):
         return "Can't identify shader_type for file '%s.'" % (self.source)
+
+class YTUnknownUniformKind(YTException):
+    def __init__(self, kind):
+        self.kind = kind
+
+    def __str__(self):
+        return "Can't determine kind specification for %s" % (self.kind)
+
+class YTUnknownUniformSize(YTException):
+    def __init__(self, size_spec):
+        self.size_spec = size_spec
+
+    def __str__(self):
+        return "Can't determine size specification for %s" % (self.size_spec)

diff -r f7d8e1264bc4e6b79072384d7e934cdcbf300c13 -r 97a4ddb20949ddbcd5fd4aaec864f3896f5054bc yt/visualization/volume_rendering/interactive_vr.py
--- a/yt/visualization/volume_rendering/interactive_vr.py
+++ b/yt/visualization/volume_rendering/interactive_vr.py
@@ -244,9 +244,57 @@
         self.position[1] = rho * np.sin(curr_phi) * np.sin(curr_theta)
         self.position[2] = rho * np.cos(curr_phi)
 
+class SceneComponent(object):
+    name = None
+    def __init__(self):
+        self._uniform_funcs = {}
 
-class BlockCollection:
+    def _guess_uniform_func(self, value):
+        # We make a best-effort guess.
+        # This does NOT work with arrays of uniforms.
+        # First look at the dtype kind.  Fortunately, this falls into either
+        # 'f' or 'i', which matches nicely with OpenGL.
+        # Note that in some implementations, it seems there is also a 'd' type,
+        # but we will not be using that here.
+        kind = value.dtype.kind
+        if kind not in 'if':
+            raise YTUnknownUniformKind(kind)
+        if len(value.shape) == 1:
+            if value.size > 4:
+                raise YTUnknownUniformSize(value.size)
+            func = self._set_scalar_uniform(kind, value.size)
+        elif len(value.shape) == 2:
+            if value.shape[0] != value.shape[1]:
+                raise YTUnknownUniformSize(value.shape)
+            func = self._set_matrix_uniform(kind, value.shape)
+        else:
+            raise YTUnknownUniformSize(value.shape)
+        return func
+
+    def _set_scalar_uniform(self, kind, size_spec):
+        gl_func = getattr(GL, "glUniform%s%sv" % (size_spec, kind))
+        def _func(location, value):
+            return gl_func(location, 1, value)
+        return _func
+
+    def _set_matrix_uniform(self, kind, size_spec):
+        assert(size_spec[0] == size_spec[1])
+        gl_func = getattr(GL, "glUniformMatrix%s%sv" % (size_spec[0], kind))
+        def _func(location, value):
+            return gl_func(location, 1, GL.GL_TRUE, value)
+        return _func
+
+    def _set_uniform(self, shader_program, name, value):
+        # We need to figure out how to pass it in.
+        if name not in self._uniform_funcs:
+            self._uniform_funcs[name] = self._guess_uniform_func(value)
+        loc = GL.glGetUniformLocation(shader_program, name)
+        return self._uniform_funcs[name](loc, value)
+
+class BlockCollection(SceneComponent):
+    name = "block_collection"
     def __init__(self):
+        super(BlockCollection, self).__init__()
         self.vert_attrib = {}
         self.gl_vao_name = None
         self.data_source = None
@@ -411,17 +459,12 @@
 
 
     def _set_uniforms(self, shader_program):
-        project_loc = GL.glGetUniformLocation(shader_program, "projection")
-        lookat_loc = GL.glGetUniformLocation(shader_program, "lookat")
-        viewport_loc = GL.glGetUniformLocation(shader_program, "viewport")
-
-        project = self.camera.get_projection_matrix()
-        view = self.camera.get_view_matrix()
-        viewport = np.array(GL.glGetIntegerv(GL.GL_VIEWPORT), dtype = 'float32')
-
-        GL.glUniformMatrix4fv(project_loc, 1, GL.GL_TRUE, project)
-        GL.glUniformMatrix4fv(lookat_loc, 1, GL.GL_TRUE, view)
-        GL.glUniform4fv(viewport_loc, 1, viewport)
+        self._set_uniform(shader_program, "projection",
+                self.camera.get_projection_matrix())
+        self._set_uniform(shader_program, "lookat",
+                self.camera.get_view_matrix())
+        self._set_uniform(shader_program, "viewport",
+                np.array(GL.glGetIntegerv(GL.GL_VIEWPORT), dtype = 'f4'))
 
     def _compute_geometry(self, block, bbox_vertices):
         move = get_translate_matrix(*block.LeftEdge)


https://bitbucket.org/yt_analysis/yt/commits/3badfa452a90/
Changeset:   3badfa452a90
Branch:      yt
User:        MatthewTurk
Date:        2016-02-24 22:10:17+00:00
Summary:     Continuing to experiment with refactoring.  Slowdown encountered!
Affected #:  1 file

diff -r 97a4ddb20949ddbcd5fd4aaec864f3896f5054bc -r 3badfa452a90c441efede8abf56df269aa6927ef yt/visualization/volume_rendering/interactive_vr.py
--- a/yt/visualization/volume_rendering/interactive_vr.py
+++ b/yt/visualization/volume_rendering/interactive_vr.py
@@ -1,6 +1,8 @@
 import os
 import re
+import contextlib
 import OpenGL.GL as GL
+from collections import OrderedDict
 
 import numpy as np
 from yt.utilities.math_utils import \
@@ -247,7 +249,14 @@
 class SceneComponent(object):
     name = None
     def __init__(self):
-        self._uniform_funcs = {}
+        self._uniform_funcs = OrderedDict()
+        self.vert_attrib = OrderedDict()
+        self.vert_arrays = OrderedDict()
+
+    def _initialize_vertex_array(self, name):
+        if name in self.vert_arrays:
+            GL.glDeleteVertexArrays(1, [self.vert_arrays[name]])
+        self.vert_arrays[name] = GL.glGenVertexArrays(1)
 
     def _guess_uniform_func(self, value):
         # We make a best-effort guess.
@@ -291,12 +300,40 @@
         loc = GL.glGetUniformLocation(shader_program, name)
         return self._uniform_funcs[name](loc, value)
 
+    def run_program(self, shader_program):
+        GL.glUseProgram(shader_program)
+        if len(self.vert_arrays) != 1:
+            raise NotImplementedError
+        for vert_name in self.vert_arrays:
+            GL.glBindVertexArray(self.vert_arrays[vert_name])
+        for an in self.vert_attrib:
+            self.bind_vert_attrib(shader_program, an)
+        self._set_uniforms(shader_program)
+        self.draw()
+        for an in self.vert_attrib:
+            self.disable_vert_attrib(shader_program, an)
+        GL.glBindBuffer(GL.GL_ARRAY_BUFFER, 0)
+
+    def add_vert_attrib(self, name, arr, each):
+        self.vert_attrib[name] = (GL.glGenBuffers(1), each)
+        GL.glBindBuffer(GL.GL_ARRAY_BUFFER, self.vert_attrib[name][0])
+        GL.glBufferData(GL.GL_ARRAY_BUFFER, arr.nbytes, arr, GL.GL_STATIC_DRAW)
+
+    def bind_vert_attrib(self, program, name):
+        bind_loc, size = self.vert_attrib[name]
+        loc = GL.glGetAttribLocation(program, name)
+        GL.glEnableVertexAttribArray(loc)
+        GL.glBindBuffer(GL.GL_ARRAY_BUFFER, bind_loc)
+        GL.glVertexAttribPointer(loc, size, GL.GL_FLOAT, False, 0, None)
+
+    def disable_vert_attrib(self, program, name):
+        loc = GL.glGetAttribLocation(program, name)
+        GL.glDisableVertexAttribArray(loc)
+
 class BlockCollection(SceneComponent):
     name = "block_collection"
     def __init__(self):
         super(BlockCollection, self).__init__()
-        self.vert_attrib = {}
-        self.gl_vao_name = None
         self.data_source = None
 
         self.blocks = {} # A collection of PartionedGrid objects
@@ -322,23 +359,6 @@
         GL.glBlendFunc(GL.GL_ONE, GL.GL_ONE)
         GL.glBlendEquation(GL.GL_MAX)
 
-
-    def add_vert_attrib(self, name, arr):
-        self.vert_attrib[name] = GL.glGenBuffers(1)
-        GL.glBindBuffer(GL.GL_ARRAY_BUFFER, self.vert_attrib[name])
-        GL.glBufferData(GL.GL_ARRAY_BUFFER, arr.nbytes, arr, GL.GL_STATIC_DRAW)
-
-    def bind_vert_attrib(self, program, name, size):
-        loc = GL.glGetAttribLocation(program, name)
-        GL.glEnableVertexAttribArray(loc)
-        GL.glBindBuffer(GL.GL_ARRAY_BUFFER, self.vert_attrib[name])
-        GL.glVertexAttribPointer(loc, size, GL.GL_FLOAT, False, 0, None)
-
-    def disable_vert_attrib(self, program, name):
-        loc = GL.glGetAttribLocation(program, name)
-        GL.glDisableVertexAttribArray(loc)
-        GL.glBindVertexArray(0)
-
     def set_fields_log(self, log_field):
         self.add_data(self.data_source, self.data_source.tiles.fields[0], log_field)
 
@@ -388,16 +408,14 @@
         dx = np.concatenate(dx)
         le = np.concatenate(le)
         re = np.concatenate(re)
-        if self.gl_vao_name is not None:
-            GL.glDeleteVertexArrays(1, [self.gl_vao_name])
-        self.gl_vao_name = GL.glGenVertexArrays(1)
 
-        self.add_vert_attrib("model_vertex", vert)
-        self.add_vert_attrib("in_dx", dx)
-        self.add_vert_attrib("in_left_edge", le)
-        self.add_vert_attrib("in_right_edge", re)
+        self._initialize_vertex_array("block_info")
+        self.add_vert_attrib("model_vertex", vert, 4)
+        self.add_vert_attrib("in_dx", dx, 3)
+        self.add_vert_attrib("in_left_edge", le, 3)
+        self.add_vert_attrib("in_right_edge", re, 3)
 
-        # Now we set up our
+        # Now we set up our textures
         self._load_textures()
 
     def set_camera(self, camera):
@@ -415,49 +433,22 @@
         self.camera = camera
         self.redraw = True
 
-    def run_program(self, shader_program):
-        r"""Runs a given shader program on the block collection.
-
-        Parameters
-        ----------
-        shader_program : int
-            An integer name for an OpenGL shader program.
-
+    def draw(self):
+        r"""Runs a given shader program on the block collection.  It is assumed
+        that the GL Context has been set up, which is typically handled by the
+        run_program method.
         """
-        GL.glUseProgram(shader_program)
-
-        GL.glBindVertexArray(self.gl_vao_name)
-
-        self.bind_vert_attrib(shader_program, "model_vertex", 4)
-        self.bind_vert_attrib(shader_program, "in_dx", 3)
-        self.bind_vert_attrib(shader_program, "in_left_edge", 3)
-        self.bind_vert_attrib(shader_program, "in_right_edge", 3)
 
         # clear the color and depth buffer
         GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT)
+        GL.glActiveTexture(GL.GL_TEXTURE0)
 
-        self._set_uniforms(shader_program)
-        GL.glActiveTexture(GL.GL_TEXTURE0)
-        camera_loc = GL.glGetUniformLocation(shader_program, "camera_pos")
-        GL.glUniform3fv(camera_loc, 1, self.camera.position)
-
-        GL.glActiveTexture(GL.GL_TEXTURE0)
         for bi in self.block_order:
             tex_i, block = self.blocks[bi]
             ti = self.gl_texture_names[tex_i]
             GL.glBindTexture(GL.GL_TEXTURE_3D, ti)
             GL.glDrawArrays(GL.GL_TRIANGLES, tex_i*36, 36)
 
-        # # This breaks on OSX, since it was already removed once, and re-added
-        # # twice, I'm leaving this as a comment
-        # for attrib in ["model_vertex", "in_dx",
-        #                "in_left_edge", "in_right_edge"]:
-        #     self.disable_vert_attrib(shader_program, attrib)
-
-        # Release bind
-        GL.glBindBuffer(GL.GL_ARRAY_BUFFER, 0)
-
-
     def _set_uniforms(self, shader_program):
         self._set_uniform(shader_program, "projection",
                 self.camera.get_projection_matrix())
@@ -465,6 +456,8 @@
                 self.camera.get_view_matrix())
         self._set_uniform(shader_program, "viewport",
                 np.array(GL.glGetIntegerv(GL.GL_VIEWPORT), dtype = 'f4'))
+        self._set_uniform(shader_program, "camera_pos",
+                self.camera.position)
 
     def _compute_geometry(self, block, bbox_vertices):
         move = get_translate_matrix(*block.LeftEdge)


https://bitbucket.org/yt_analysis/yt/commits/4ae1114fe034/
Changeset:   4ae1114fe034
Branch:      yt
User:        MatthewTurk
Date:        2016-02-24 23:03:00+00:00
Summary:     Change to known working transfer function settings
Affected #:  1 file

diff -r 3badfa452a90c441efede8abf56df269aa6927ef -r 4ae1114fe03455eda54707e4a36fa35e2f4f5990 yt/visualization/volume_rendering/input_events.py
--- a/yt/visualization/volume_rendering/input_events.py
+++ b/yt/visualization/volume_rendering/input_events.py
@@ -184,6 +184,7 @@
     #scene.update_minmax()
     # https://www.opengl.org/sdk/docs/man/html/glBlendFunc.xhtml
     #GL.glBlendFunc(GL.GL_ONE, GL.GL_DST_ALPHA)
+    GL.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA)
     GL.glBlendEquation(GL.GL_FUNC_ADD)
     return True
 


https://bitbucket.org/yt_analysis/yt/commits/2be7f93f4eab/
Changeset:   2be7f93f4eab
Branch:      yt
User:        MatthewTurk
Date:        2016-02-25 03:10:11+00:00
Summary:     Adding a first pass at a set of shader objects
Affected #:  1 file

diff -r 4ae1114fe03455eda54707e4a36fa35e2f4f5990 -r 2be7f93f4eabfb62e8688df61ed5257f07980954 yt/visualization/volume_rendering/shader_objects.py
--- /dev/null
+++ b/yt/visualization/volume_rendering/shader_objects.py
@@ -0,0 +1,73 @@
+import OpenGL as GL
+
+class ShaderProgram(object):
+    def __init__(self, vertex_shader = None, fragment_shader = None):
+        # Don't allow just one.  Either neither or both.
+        if vertex_shader is None and fragment_shader is None:
+            pass
+        elif None not in (vertex_shader, fragment_shader):
+            self.link(vertex_shader, fragment_shader)
+        else:
+            raise RuntimeError
+
+    def link(self, vertex_shader, fragment_shader):
+        # There are more types of shaders, but for now we only allow v&f.
+        self.program = GL.glCreateProgram()
+        if not isinstance(vertex_shader, VertexShader):
+            vertex_shader = VertexShader(vertex_shader)
+        if not isinstance(fragment_shader, FragmentShader):
+            fragment_shader = FragmentShader(fragment_shader)
+        self.vertex_shader = vertex_shader
+        self.fragment_shader = fragment_shader
+        GL.glAttachShader(self.program, vertex_shader)
+        GL.glAttachShader(self.program, fragment_shader)
+        GL.glLinkProgram(program)
+        result = GL.glGetProgramiv(self.program, GL.GL_LINK_STATUS)
+        if not result:
+            raise RuntimeError(GL.glGetProgramInfoLog(self.program))
+
+class Shader(object):
+    shader = None
+    def __init__(self, source = None):
+        if source:
+            self.compile(source)
+
+    def _get_source(self, source):
+        if ";" in source:
+            # This is probably safe, right?  Enh, probably.
+            return source
+        if os.path.isfile(source):
+            sh_directory = ''
+        else:
+            sh_directory = os.path.join(os.path.dirname(__file__), "shaders")
+        fn = os.path.join(sh_directory, source)
+        if not os.path.isfile(fn):
+            raise YTInvalidShaderType(source)
+        return open(fn, 'r').read()
+
+    def compile(self, source, parameters = None):
+        if parameters is not None:
+            raise NotImplementedError
+        source = self._get_source(source)
+        shader_type_enum = getattr(GL,
+            'GL_%s_SHADER' % self.shader_type.upper())
+        shader = GL.glCreateShader(shader_type_enum)
+        # We could do templating here if we wanted.
+        self.shader_source = source
+        GL.glShaderSource(shader, source)
+        GL.glCompileShader(shader)
+        result = GL.glGetShaderiv(shader, GL.GL_COMPILE_STATUS)
+        if not(result):
+            raise RuntimeError(GL.glGetShaderInfoLog(shader))
+        self.shader = shader
+
+    def __del__(self):
+        # This is not guaranteed to be called
+        if self.shader is not None:
+            GL.glDeleteShader(self.shader)
+
+class FragmentShader(Shader):
+    shader_type = "fragment"
+
+class VertexShader(Shader):
+    shader_type = "vertex"


https://bitbucket.org/yt_analysis/yt/commits/72b79b56a242/
Changeset:   72b79b56a242
Branch:      yt
User:        MatthewTurk
Date:        2016-02-25 16:25:29+00:00
Summary:     Adding nicer source options
Affected #:  1 file

diff -r 2be7f93f4eabfb62e8688df61ed5257f07980954 -r 72b79b56a24269c2d1b1eb82a2d9b673c5de6b2c yt/visualization/volume_rendering/shader_objects.py
--- a/yt/visualization/volume_rendering/shader_objects.py
+++ b/yt/visualization/volume_rendering/shader_objects.py
@@ -28,6 +28,7 @@
 
 class Shader(object):
     shader = None
+    _source = None
     def __init__(self, source = None):
         if source:
             self.compile(source)
@@ -45,7 +46,10 @@
             raise YTInvalidShaderType(source)
         return open(fn, 'r').read()
 
-    def compile(self, source, parameters = None):
+    def compile(self, source = None, parameters = None):
+        if source is None:
+            source = self._source
+            if source is None: raise RuntimeError
         if parameters is not None:
             raise NotImplementedError
         source = self._get_source(source)
@@ -71,3 +75,27 @@
 
 class VertexShader(Shader):
     shader_type = "vertex"
+
+class ApplyColormapFragmentShader(FragmentShader):
+    _source = "apply_colormap.fragmentshader"
+
+class MaxIntensityFragmentShader(FragmentShader):
+    _source = "max_intensity.fragmentshader"
+
+class NoOpFragmentShader(FragmentShader):
+    _source = "noop.fragmentshader"
+
+class PassthroughFragmentShader(FragmentShader):
+    _source = "passthrough.fragmentshader"
+
+class ProjectionFragmentShader(FragmentShader):
+    _source = "projection.fragmentshader"
+
+class TransferFunctionFragmentShader(FragmentShader):
+    _source = "transfer_function.fragmentshader"
+
+class DefaultVertexShader(VertexShader):
+    _source = "default.vertexshader"
+
+class PassthroughVertexShader(VertexShader):
+    _source = "passthrough.vertexshader"


https://bitbucket.org/yt_analysis/yt/commits/7ec371335c2d/
Changeset:   7ec371335c2d
Branch:      yt
User:        MatthewTurk
Date:        2016-02-25 16:40:29+00:00
Summary:     Beginning refactoring of attributes
Affected #:  2 files

diff -r 72b79b56a24269c2d1b1eb82a2d9b673c5de6b2c -r 7ec371335c2d31013cdec2cf881c7a0735ad87d8 yt/visualization/volume_rendering/interactive_vr.py
--- a/yt/visualization/volume_rendering/interactive_vr.py
+++ b/yt/visualization/volume_rendering/interactive_vr.py
@@ -258,78 +258,25 @@
             GL.glDeleteVertexArrays(1, [self.vert_arrays[name]])
         self.vert_arrays[name] = GL.glGenVertexArrays(1)
 
-    def _guess_uniform_func(self, value):
-        # We make a best-effort guess.
-        # This does NOT work with arrays of uniforms.
-        # First look at the dtype kind.  Fortunately, this falls into either
-        # 'f' or 'i', which matches nicely with OpenGL.
-        # Note that in some implementations, it seems there is also a 'd' type,
-        # but we will not be using that here.
-        kind = value.dtype.kind
-        if kind not in 'if':
-            raise YTUnknownUniformKind(kind)
-        if len(value.shape) == 1:
-            if value.size > 4:
-                raise YTUnknownUniformSize(value.size)
-            func = self._set_scalar_uniform(kind, value.size)
-        elif len(value.shape) == 2:
-            if value.shape[0] != value.shape[1]:
-                raise YTUnknownUniformSize(value.shape)
-            func = self._set_matrix_uniform(kind, value.shape)
-        else:
-            raise YTUnknownUniformSize(value.shape)
-        return func
-
-    def _set_scalar_uniform(self, kind, size_spec):
-        gl_func = getattr(GL, "glUniform%s%sv" % (size_spec, kind))
-        def _func(location, value):
-            return gl_func(location, 1, value)
-        return _func
-
-    def _set_matrix_uniform(self, kind, size_spec):
-        assert(size_spec[0] == size_spec[1])
-        gl_func = getattr(GL, "glUniformMatrix%s%sv" % (size_spec[0], kind))
-        def _func(location, value):
-            return gl_func(location, 1, GL.GL_TRUE, value)
-        return _func
-
-    def _set_uniform(self, shader_program, name, value):
-        # We need to figure out how to pass it in.
-        if name not in self._uniform_funcs:
-            self._uniform_funcs[name] = self._guess_uniform_func(value)
-        loc = GL.glGetUniformLocation(shader_program, name)
-        return self._uniform_funcs[name](loc, value)
-
     def run_program(self, shader_program):
-        GL.glUseProgram(shader_program)
-        if len(self.vert_arrays) != 1:
-            raise NotImplementedError
-        for vert_name in self.vert_arrays:
-            GL.glBindVertexArray(self.vert_arrays[vert_name])
-        for an in self.vert_attrib:
-            self.bind_vert_attrib(shader_program, an)
-        self._set_uniforms(shader_program)
-        self.draw()
-        for an in self.vert_attrib:
-            self.disable_vert_attrib(shader_program, an)
-        GL.glBindBuffer(GL.GL_ARRAY_BUFFER, 0)
+        with shader_program.enable():
+            if len(self.vert_arrays) != 1:
+                raise NotImplementedError
+            for vert_name in self.vert_arrays:
+                GL.glBindVertexArray(self.vert_arrays[vert_name])
+            for an in self.vert_attrib:
+                shader_program.bind_vert_attrib(an)
+            self._set_uniforms(shader_program)
+            self.draw()
+            for an in self.vert_attrib:
+                shader_program.disable_vert_attrib(an)
+            GL.glBindBuffer(GL.GL_ARRAY_BUFFER, 0)
 
     def add_vert_attrib(self, name, arr, each):
         self.vert_attrib[name] = (GL.glGenBuffers(1), each)
         GL.glBindBuffer(GL.GL_ARRAY_BUFFER, self.vert_attrib[name][0])
         GL.glBufferData(GL.GL_ARRAY_BUFFER, arr.nbytes, arr, GL.GL_STATIC_DRAW)
 
-    def bind_vert_attrib(self, program, name):
-        bind_loc, size = self.vert_attrib[name]
-        loc = GL.glGetAttribLocation(program, name)
-        GL.glEnableVertexAttribArray(loc)
-        GL.glBindBuffer(GL.GL_ARRAY_BUFFER, bind_loc)
-        GL.glVertexAttribPointer(loc, size, GL.GL_FLOAT, False, 0, None)
-
-    def disable_vert_attrib(self, program, name):
-        loc = GL.glGetAttribLocation(program, name)
-        GL.glDisableVertexAttribArray(loc)
-
 class BlockCollection(SceneComponent):
     name = "block_collection"
     def __init__(self):
@@ -450,13 +397,13 @@
             GL.glDrawArrays(GL.GL_TRIANGLES, tex_i*36, 36)
 
     def _set_uniforms(self, shader_program):
-        self._set_uniform(shader_program, "projection",
+        shader_program._set_uniform("projection",
                 self.camera.get_projection_matrix())
-        self._set_uniform(shader_program, "lookat",
+        shader_program._set_uniform("lookat",
                 self.camera.get_view_matrix())
-        self._set_uniform(shader_program, "viewport",
+        shader_program._set_uniform("viewport",
                 np.array(GL.glGetIntegerv(GL.GL_VIEWPORT), dtype = 'f4'))
-        self._set_uniform(shader_program, "camera_pos",
+        shader_program._set_uniform("camera_pos",
                 self.camera.position)
 
     def _compute_geometry(self, block, bbox_vertices):

diff -r 72b79b56a24269c2d1b1eb82a2d9b673c5de6b2c -r 7ec371335c2d31013cdec2cf881c7a0735ad87d8 yt/visualization/volume_rendering/shader_objects.py
--- a/yt/visualization/volume_rendering/shader_objects.py
+++ b/yt/visualization/volume_rendering/shader_objects.py
@@ -21,11 +21,71 @@
         self.fragment_shader = fragment_shader
         GL.glAttachShader(self.program, vertex_shader)
         GL.glAttachShader(self.program, fragment_shader)
-        GL.glLinkProgram(program)
+        GL.glLinkProgram(self.program)
         result = GL.glGetProgramiv(self.program, GL.GL_LINK_STATUS)
         if not result:
             raise RuntimeError(GL.glGetProgramInfoLog(self.program))
 
+    def _guess_uniform_func(self, value):
+        # We make a best-effort guess.
+        # This does NOT work with arrays of uniforms.
+        # First look at the dtype kind.  Fortunately, this falls into either
+        # 'f' or 'i', which matches nicely with OpenGL.
+        # Note that in some implementations, it seems there is also a 'd' type,
+        # but we will not be using that here.
+        kind = value.dtype.kind
+        if kind not in 'if':
+            raise YTUnknownUniformKind(kind)
+        if len(value.shape) == 1:
+            if value.size > 4:
+                raise YTUnknownUniformSize(value.size)
+            func = self._set_scalar_uniform(kind, value.size)
+        elif len(value.shape) == 2:
+            if value.shape[0] != value.shape[1]:
+                raise YTUnknownUniformSize(value.shape)
+            func = self._set_matrix_uniform(kind, value.shape)
+        else:
+            raise YTUnknownUniformSize(value.shape)
+        return func
+
+    def _set_scalar_uniform(self, kind, size_spec):
+        gl_func = getattr(GL, "glUniform%s%sv" % (size_spec, kind))
+        def _func(location, value):
+            return gl_func(location, 1, value)
+        return _func
+
+    def _set_matrix_uniform(self, kind, size_spec):
+        assert(size_spec[0] == size_spec[1])
+        gl_func = getattr(GL, "glUniformMatrix%s%sv" % (size_spec[0], kind))
+        def _func(location, value):
+            return gl_func(location, 1, GL.GL_TRUE, value)
+        return _func
+
+    def _set_uniform(self, name, value):
+        # We need to figure out how to pass it in.
+        if name not in self._uniform_funcs:
+            self._uniform_funcs[name] = self._guess_uniform_func(value)
+        loc = GL.glGetUniformLocation(self.program, name)
+        return self._uniform_funcs[name](loc, value)
+
+    @contextlib.contextmanager
+    def enable(self):
+        GL.glUseProgram(self.program)
+        yield
+        GL.glUseProgram(0)
+
+    def bind_vert_attrib(self, name):
+        bind_loc, size = self.vert_attrib[name]
+        loc = GL.glGetAttribLocation(self.program, name)
+        GL.glEnableVertexAttribArray(loc)
+        GL.glBindBuffer(GL.GL_ARRAY_BUFFER, bind_loc)
+        GL.glVertexAttribPointer(loc, size, GL.GL_FLOAT, False, 0, None)
+
+    def disable_vert_attrib(self, name):
+        loc = GL.glGetAttribLocation(self.program, name)
+        GL.glDisableVertexAttribArray(loc)
+
+
 class Shader(object):
     shader = None
     _source = None


https://bitbucket.org/yt_analysis/yt/commits/74e0e12f404f/
Changeset:   74e0e12f404f
Branch:      yt
User:        MatthewTurk
Date:        2016-02-25 16:48:43+00:00
Summary:     Fixing some bad reverts!
Affected #:  2 files

diff -r 7ec371335c2d31013cdec2cf881c7a0735ad87d8 -r 74e0e12f404fb2761339c6dd8b45a4f4e438e334 yt/visualization/volume_rendering/interactive_loop.py
--- a/yt/visualization/volume_rendering/interactive_loop.py
+++ b/yt/visualization/volume_rendering/interactive_loop.py
@@ -43,6 +43,7 @@
         callbacks.add_key_callback("camera_proj", "p")
         callbacks.add_key_callback("shader_max", "1")
         callbacks.add_key_callback("shader_proj", "2")
+        callbacks.add_key_callback("shader_test", "3")
         callbacks.add_key_callback("print_limits", "h")
         callbacks.add_key_callback("debug_buffer", "d")
         callbacks.add_key_callback("cmap_max_up", "right_bracket")

diff -r 7ec371335c2d31013cdec2cf881c7a0735ad87d8 -r 74e0e12f404fb2761339c6dd8b45a4f4e438e334 yt/visualization/volume_rendering/shaders/transfer_function.fragmentshader
--- a/yt/visualization/volume_rendering/shaders/transfer_function.fragmentshader
+++ b/yt/visualization/volume_rendering/shaders/transfer_function.fragmentshader
@@ -54,18 +54,18 @@
         vec3 tex_sample = texture(ds_tex, tex_curr_pos).rgb;
         if (abs(tex_sample.r - 0.2) < 0.05) {
             v = (tex_sample.r - 0.2);
-            v = clamp(exp(-v*v/0.0001), 0.0, 1.0);
-            new_color = v * vec4(0.0, 0.0, 1.0, 1.0);
+            v = exp(-v*v/0.0005);
+            new_color = v * vec4(0.0, 0.0, 1.0, 0.0123);
         } else if (abs(tex_sample.r - 0.3) < 0.1) {
             v = (tex_sample.r - 0.3);
-            v = clamp(exp(-v*v/0.001), 0.0, 1.0);
-            new_color = v * vec4(1.0, 0.0, 0.0, 1.0);
+            v = exp(-v*v/0.0001);
+            new_color = v * vec4(1.0, 0.0, 0.0, 0.5);
         }
 
-        curr_color = dist*new_color + clamp(1.0 - dist*new_color.a, 0.0, 1.0) * curr_color;
+        curr_color = dist*new_color + clamp(1.0 - new_color.a, 0.0, 1.0) * curr_color;
         ray_position += dir * step_size;
         ray_in_bb = within_bb(ray_position);
     }
 
-    output_color = clamp(curr_color, 0.0, 1.0);
+    output_color = clamp(curr_color / dist, 0.0, 1.0);
 }


https://bitbucket.org/yt_analysis/yt/commits/29db4de7fe28/
Changeset:   29db4de7fe28
Branch:      yt
User:        MatthewTurk
Date:        2016-02-25 20:51:11+00:00
Summary:     Beginning to step through self-registration and shaders-on-objects.
Affected #:  4 files

diff -r 74e0e12f404fb2761339c6dd8b45a4f4e438e334 -r 29db4de7fe28d5a798807500e18d24f25a50a3d3 yt/visualization/volume_rendering/input_events.py
--- a/yt/visualization/volume_rendering/input_events.py
+++ b/yt/visualization/volume_rendering/input_events.py
@@ -9,6 +9,7 @@
 import numpy as np
 import matplotlib.cm as cm
 import random
+from .shader_objects import known_shaders
 
 event_registry = {}
 

diff -r 74e0e12f404fb2761339c6dd8b45a4f4e438e334 -r 29db4de7fe28d5a798807500e18d24f25a50a3d3 yt/visualization/volume_rendering/interactive_loop.py
--- a/yt/visualization/volume_rendering/interactive_loop.py
+++ b/yt/visualization/volume_rendering/interactive_loop.py
@@ -27,7 +27,6 @@
     def setup_loop(self, scene, camera):
         scene.set_camera(camera)
         scene.update_minmax()
-        scene.add_shader_from_file("max_intensity.fragmentshader")
         camera.compute_matrices()
         frame_start = glfw.GetTime()
         fps_start = glfw.GetTime()

diff -r 74e0e12f404fb2761339c6dd8b45a4f4e438e334 -r 29db4de7fe28d5a798807500e18d24f25a50a3d3 yt/visualization/volume_rendering/interactive_vr.py
--- a/yt/visualization/volume_rendering/interactive_vr.py
+++ b/yt/visualization/volume_rendering/interactive_vr.py
@@ -248,11 +248,23 @@
 
 class SceneComponent(object):
     name = None
+    _program = None
+    _program_invalid = True
+    fragment_shader = None
+    vertex_shader = None
     def __init__(self):
         self._uniform_funcs = OrderedDict()
         self.vert_attrib = OrderedDict()
         self.vert_arrays = OrderedDict()
 
+    @property
+    def program(self):
+        if self._program_invalid:
+            self._program = ShaderProgram(self.vertex_shader,
+                self.fragment_shader)
+            self._program_invalid = False
+        return self._program
+
     def _initialize_vertex_array(self, name):
         if name in self.vert_arrays:
             GL.glDeleteVertexArrays(1, [self.vert_arrays[name]])
@@ -277,10 +289,28 @@
         GL.glBindBuffer(GL.GL_ARRAY_BUFFER, self.vert_attrib[name][0])
         GL.glBufferData(GL.GL_ARRAY_BUFFER, arr.nbytes, arr, GL.GL_STATIC_DRAW)
 
+    def set_shader(self, name):
+        r""" Compiles and links a fragment shader from a set of known shaders.
+
+        Parameters
+        ----------
+        name : String
+            The name of the fragment shader to use.
+
+        """
+        shader = known_shaders[name]()
+        if shader.shader_type == "vertex":
+            self.vertex_shader = shader
+        elif shader.shader_type == "fragment":
+            self.fragment_shader = shader
+        else:
+            raise KeyError(shader.shader_type)
+
 class BlockCollection(SceneComponent):
     name = "block_collection"
     def __init__(self):
         super(BlockCollection, self).__init__()
+        self.set_shader("max_intensity.f")
         self.data_source = None
 
         self.blocks = {} # A collection of PartionedGrid objects
@@ -449,7 +479,6 @@
                         GL.GL_RED, GL.GL_FLOAT, n_data.T)
             GL.glGenerateMipmap(GL.GL_TEXTURE_3D)
 
-
 class SceneGraph:
     def __init__(self):
         self.collections = []
@@ -629,28 +658,6 @@
         for collection in self.collections:
             collection.set_camera(camera)
 
-    def add_shader_from_file(self, filename):
-        r""" Compiles and links a fragment shader.
-
-        Given a `filename`, compiles and links the given fragment shader. This
-        function also compiles a fixed vertex shader. This function then attaches
-        these shaders to the current scene.
-
-        Parameters
-        ----------
-        filename : String
-            The location of the shader source file to read.
-
-        """
-        self.shaders = ['default.vertexshader', filename]
-        self.update_shaders()
-
-    def update_shaders(self, shaders=None):
-        if self.shader_program is not None:
-            GL.glDeleteProgram(self.shader_program)
-            print("Deleted program")
-        self.shader_program = link_shader_program(shaders or self.shaders)
-
     def _retrieve_framebuffer(self):
         ox, oy, width, height = GL.glGetIntegerv(GL.GL_VIEWPORT)
         debug_buffer = GL.glReadPixels(0, 0, width, height, GL.GL_RGB,

diff -r 74e0e12f404fb2761339c6dd8b45a4f4e438e334 -r 29db4de7fe28d5a798807500e18d24f25a50a3d3 yt/visualization/volume_rendering/shader_objects.py
--- a/yt/visualization/volume_rendering/shader_objects.py
+++ b/yt/visualization/volume_rendering/shader_objects.py
@@ -1,4 +1,7 @@
 import OpenGL as GL
+from yt.extern.six import add_metaclass
+
+known_shaders = {}
 
 class ShaderProgram(object):
     def __init__(self, vertex_shader = None, fragment_shader = None):
@@ -85,10 +88,17 @@
         loc = GL.glGetAttribLocation(self.program, name)
         GL.glDisableVertexAttribArray(loc)
 
+class RegisteredShader(type):
+    def __init__(cls, name, b, d):
+        type.__init__(cls, name, b, d)
+        if getattr(cls, "_shader_name", None) is not None:
+            known_shaders[cls._shader_name] = cls
 
+ at add_metaclass(RegisteredShader)
 class Shader(object):
     shader = None
     _source = None
+    _shader_name = None
     def __init__(self, source = None):
         if source:
             self.compile(source)
@@ -138,24 +148,32 @@
 
 class ApplyColormapFragmentShader(FragmentShader):
     _source = "apply_colormap.fragmentshader"
+    _shader_name = "apply_colormap.f"
 
 class MaxIntensityFragmentShader(FragmentShader):
     _source = "max_intensity.fragmentshader"
+    _shader_name = "max_intensity.f"
 
 class NoOpFragmentShader(FragmentShader):
     _source = "noop.fragmentshader"
+    _shader_name = "noop.f"
 
 class PassthroughFragmentShader(FragmentShader):
     _source = "passthrough.fragmentshader"
+    _shader_name = "passthrough.f"
 
 class ProjectionFragmentShader(FragmentShader):
     _source = "projection.fragmentshader"
+    _shader_name = "projection.f"
 
 class TransferFunctionFragmentShader(FragmentShader):
     _source = "transfer_function.fragmentshader"
+    _shader_name = "transfer_function.f"
 
 class DefaultVertexShader(VertexShader):
     _source = "default.vertexshader"
+    _shader_name = "default.v"
 
 class PassthroughVertexShader(VertexShader):
     _source = "passthrough.vertexshader"
+    _shader_name = "passthrough.v"


https://bitbucket.org/yt_analysis/yt/commits/3b5ef25070e1/
Changeset:   3b5ef25070e1
Branch:      yt
User:        MatthewTurk
Date:        2016-02-25 21:35:35+00:00
Summary:     Shaders are now encapsulated in objects.
Affected #:  3 files

diff -r 29db4de7fe28d5a798807500e18d24f25a50a3d3 -r 3b5ef25070e102f6d5f82b110fd31616aa786945 yt/visualization/volume_rendering/input_events.py
--- a/yt/visualization/volume_rendering/input_events.py
+++ b/yt/visualization/volume_rendering/input_events.py
@@ -140,12 +140,11 @@
 def shader_max(event_coll, event):
     print("Changing shader to max(intensity)")
     scene = event_coll.scene
-    scene.fb_shaders = ["passthrough.vertexshader",
-                        "apply_colormap.fragmentshader"]
-    scene.update_fb_shaders()
-    scene.shaders = ["default.vertexshader",
-                     "max_intensity.fragmentshader"]
-    scene.update_shaders()
+    for coll in scene.collections:
+        coll.set_shader("default.v")
+        coll.set_shader("max_intensity.f")
+    scene.set_shader("passthrough.v")
+    scene.set_shader("apply_colormap.f")
     for collection in scene.collections:
         collection.set_fields_log(True)
     scene.update_minmax()
@@ -157,12 +156,11 @@
 def shader_proj(event_coll, event):
     print("Changing shader to projection")
     scene = event_coll.scene
-    scene.fb_shaders = ["passthrough.vertexshader",
-                        "apply_colormap.fragmentshader"]
-    scene.update_fb_shaders()
-    scene.shaders = ["default.vertexshader",
-                     "projection.fragmentshader"]
-    scene.update_shaders()
+    for coll in scene.collections:
+        coll.set_shader("default.v")
+        coll.set_shader("projection.f")
+    scene.set_shader("passthrough.v")
+    scene.set_shader("apply_colormap.f")
     for collection in scene.collections:
         collection.set_fields_log(False)
     scene.update_minmax()
@@ -174,12 +172,11 @@
 def shader_test(event_coll, event):
     print("Changing shader to projection")
     scene = event_coll.scene
-    scene.shaders = ["default.vertexshader",
-                     "transfer_function.fragmentshader"]
-    scene.update_shaders()
-    scene.fb_shaders = ["passthrough.vertexshader",
-                        "noop.fragmentshader"]
-    scene.update_fb_shaders()
+    for coll in scene.collections:
+        coll.set_shader("default.v")
+        coll.set_shader("transfer_function.f")
+    scene.set_shader("passthrough.v")
+    scene.set_shader("noop.f")
     for collection in scene.collections:
         collection.set_fields_log(True)
     #scene.update_minmax()

diff -r 29db4de7fe28d5a798807500e18d24f25a50a3d3 -r 3b5ef25070e102f6d5f82b110fd31616aa786945 yt/visualization/volume_rendering/interactive_vr.py
--- a/yt/visualization/volume_rendering/interactive_vr.py
+++ b/yt/visualization/volume_rendering/interactive_vr.py
@@ -15,6 +15,7 @@
     quaternion_to_rotation_matrix, \
     rotation_matrix_to_quaternion
 from yt.utilities.exceptions import YTInvalidShaderType
+from .shader_objects import known_shaders, ShaderProgram
 
 import matplotlib.cm as cm
 
@@ -253,7 +254,6 @@
     fragment_shader = None
     vertex_shader = None
     def __init__(self):
-        self._uniform_funcs = OrderedDict()
         self.vert_attrib = OrderedDict()
         self.vert_arrays = OrderedDict()
 
@@ -270,18 +270,19 @@
             GL.glDeleteVertexArrays(1, [self.vert_arrays[name]])
         self.vert_arrays[name] = GL.glGenVertexArrays(1)
 
-    def run_program(self, shader_program):
-        with shader_program.enable():
+    def run_program(self):
+        with self.program.enable():
             if len(self.vert_arrays) != 1:
                 raise NotImplementedError
             for vert_name in self.vert_arrays:
                 GL.glBindVertexArray(self.vert_arrays[vert_name])
             for an in self.vert_attrib:
-                shader_program.bind_vert_attrib(an)
-            self._set_uniforms(shader_program)
+                bind_loc, size = self.vert_attrib[an]
+                self.program.bind_vert_attrib(an, bind_loc, size)
+            self._set_uniforms(self.program)
             self.draw()
             for an in self.vert_attrib:
-                shader_program.disable_vert_attrib(an)
+                self.program.disable_vert_attrib(an)
             GL.glBindBuffer(GL.GL_ARRAY_BUFFER, 0)
 
     def add_vert_attrib(self, name, arr, each):
@@ -310,6 +311,7 @@
     name = "block_collection"
     def __init__(self):
         super(BlockCollection, self).__init__()
+        self.set_shader("default.v")
         self.set_shader("max_intensity.f")
         self.data_source = None
 
@@ -479,10 +481,10 @@
                         GL.GL_RED, GL.GL_FLOAT, n_data.T)
             GL.glGenerateMipmap(GL.GL_TEXTURE_3D)
 
-class SceneGraph:
+class SceneGraph(SceneComponent):
     def __init__(self):
+        super(SceneGraph, self).__init__()
         self.collections = []
-        self.fb_uniforms = {}
         self.fbo = None
         self.fb_texture = None
         self.cmap_texture = None
@@ -497,12 +499,14 @@
         self.width = width
         self.height = height
 
-        self.fb_shaders = ["passthrough.vertexshader",
-                           "apply_colormap.fragmentshader"]
-        self.update_fb_shaders()
+        self.set_shader("passthrough.v")
+        self.set_shader("apply_colormap.f")
 
-        self.fb_vao_name = GL.glGenVertexArrays(1)
-        GL.glBindVertexArray(self.fb_vao_name)
+        self._init_framebuffer()
+
+    def _init_framebuffer(self):
+        self._initialize_vertex_array("fb_vbo")
+        GL.glBindVertexArray(self.vert_arrays["fb_vbo"])
 
         quad_attrib = GL.glGenBuffers(1)
         GL.glBindBuffer(GL.GL_ARRAY_BUFFER, quad_attrib)
@@ -516,16 +520,6 @@
 
         self.setup_fb(self.width, self.height)
 
-    def update_fb_shaders(self, fb_shaders=None):
-        if self.fb_shader_program is not None:
-            GL.glDeleteProgram(self.fb_shader_program)
-        self.fb_shader_program = \
-            link_shader_program(fb_shaders or self.fb_shaders)
-        for key in ["fb_texture", "cmap", "min_val", "scale",
-                    "cmap_min", "cmap_max", "cmap_log"]:
-            self.fb_uniforms[key] = \
-                GL.glGetUniformLocation(self.fb_shader_program, key)
-
     def setup_cmap_tex(self):
         '''Creates 1D texture that will hold colormap in framebuffer'''
         self.cmap_texture = GL.glGenTextures(1)   # create target texture
@@ -641,7 +635,6 @@
             self.camera.update_cmap_minmax(self.min_val, self.max_val,
                                            self.data_logged)
 
-
     def set_camera(self, camera):
         r""" Sets the camera orientation for the entire scene.
 
@@ -665,7 +658,7 @@
         arr = np.fromstring(debug_buffer, "uint8", count = width*height*3)
         return arr.reshape((width, height, 3))
 
-    def render(self):
+    def run_program(self):
         """ Renders one frame of the scene.
 
         Renders the scene using the current collection and camera set by calls
@@ -692,36 +685,36 @@
 
         # render collections to fb
         for collection in self.collections:
-            collection.run_program(self.shader_program)
+            collection.run_program()
         # unbind FB
         GL.glBindFramebuffer(GL.GL_FRAMEBUFFER, 0)
 
         # 2 pass of rendering
-        GL.glUseProgram(self.fb_shader_program)
+        GL.glUseProgram(self.program.program)
         GL.glActiveTexture(GL.GL_TEXTURE0)
         # bind to the result of 1 pass
         GL.glBindTexture(GL.GL_TEXTURE_2D, self.fb_texture)
         # Set our "fb_texture" sampler to user Texture Unit 0
-        GL.glUniform1i(self.fb_uniforms["fb_texture"], 0);
+        self.program._set_uniform("fb_texture", 0)
 
         GL.glActiveTexture(GL.GL_TEXTURE1)
         GL.glBindTexture(GL.GL_TEXTURE_1D, self.cmap_texture)
-        GL.glUniform1i(self.fb_uniforms["cmap"], 1)
+        self.program._set_uniform("cmap", 1)
 
         scale = (self.max_val - self.min_val) * self.diagonal
-        GL.glUniform1f(self.fb_uniforms["min_val"], self.min_val)
-        GL.glUniform1f(self.fb_uniforms["scale"], scale)
-        GL.glUniform1f(self.fb_uniforms["cmap_min"], self.camera.cmap_min)
-        GL.glUniform1f(self.fb_uniforms["cmap_max"], self.camera.cmap_max)
+        self.program._set_uniform("min_val", self.min_val)
+        self.program._set_uniform("scale", scale)
+        self.program._set_uniform("cmap_min", self.camera.cmap_min)
+        self.program._set_uniform("cmap_max", self.camera.cmap_max)
         if self.data_logged:
-            GL.glUniform1f(self.fb_uniforms["cmap_log"], float(False))
+            self.program._set_uniform("cmap_log", float(False))
         else:
-            GL.glUniform1f(self.fb_uniforms["cmap_log"], float(self.camera.cmap_log))
+            self.program._set_uniform("cmap_log", float(self.camera.cmap_log))
         # clear the color and depth buffer
         GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT)
         # Bind to Vertex array that contains simple quad filling fullscreen,
         # that was defined in __init__()
-        GL.glBindVertexArray(self.fb_vao_name)
+        GL.glBindVertexArray(self.vert_arrays["fb_vbo"])
         GL.glEnableVertexAttribArray(0)
         # Draw our 2 triangles
         GL.glDrawArrays(GL.GL_TRIANGLES, 0, 6)
@@ -730,3 +723,5 @@
         GL.glBindVertexArray(0)
         GL.glBindTexture(GL.GL_TEXTURE_1D, 0)
         GL.glBindTexture(GL.GL_TEXTURE_2D, 0)
+
+    render = run_program

diff -r 29db4de7fe28d5a798807500e18d24f25a50a3d3 -r 3b5ef25070e102f6d5f82b110fd31616aa786945 yt/visualization/volume_rendering/shader_objects.py
--- a/yt/visualization/volume_rendering/shader_objects.py
+++ b/yt/visualization/volume_rendering/shader_objects.py
@@ -1,5 +1,8 @@
-import OpenGL as GL
+import os
+import OpenGL.GL as GL
+import contextlib
 from yt.extern.six import add_metaclass
+from collections import OrderedDict
 
 known_shaders = {}
 
@@ -12,6 +15,7 @@
             self.link(vertex_shader, fragment_shader)
         else:
             raise RuntimeError
+        self._uniform_funcs = OrderedDict()
 
     def link(self, vertex_shader, fragment_shader):
         # There are more types of shaders, but for now we only allow v&f.
@@ -22,8 +26,8 @@
             fragment_shader = FragmentShader(fragment_shader)
         self.vertex_shader = vertex_shader
         self.fragment_shader = fragment_shader
-        GL.glAttachShader(self.program, vertex_shader)
-        GL.glAttachShader(self.program, fragment_shader)
+        GL.glAttachShader(self.program, vertex_shader.shader)
+        GL.glAttachShader(self.program, fragment_shader.shader)
         GL.glLinkProgram(self.program)
         result = GL.glGetProgramiv(self.program, GL.GL_LINK_STATUS)
         if not result:
@@ -36,7 +40,12 @@
         # 'f' or 'i', which matches nicely with OpenGL.
         # Note that in some implementations, it seems there is also a 'd' type,
         # but we will not be using that here.
-        kind = value.dtype.kind
+        if isinstance(value, int):
+            return GL.glUniform1i
+        elif isinstance(value, float):
+            return GL.glUniform1f
+        else:
+            kind = value.dtype.kind
         if kind not in 'if':
             raise YTUnknownUniformKind(kind)
         if len(value.shape) == 1:
@@ -77,8 +86,7 @@
         yield
         GL.glUseProgram(0)
 
-    def bind_vert_attrib(self, name):
-        bind_loc, size = self.vert_attrib[name]
+    def bind_vert_attrib(self, name, bind_loc, size):
         loc = GL.glGetAttribLocation(self.program, name)
         GL.glEnableVertexAttribArray(loc)
         GL.glBindBuffer(GL.GL_ARRAY_BUFFER, bind_loc)
@@ -96,7 +104,7 @@
 
 @add_metaclass(RegisteredShader)
 class Shader(object):
-    shader = None
+    _shader = None
     _source = None
     _shader_name = None
     def __init__(self, source = None):
@@ -133,7 +141,13 @@
         result = GL.glGetShaderiv(shader, GL.GL_COMPILE_STATUS)
         if not(result):
             raise RuntimeError(GL.glGetShaderInfoLog(shader))
-        self.shader = shader
+        self._shader = shader
+
+    @property
+    def shader(self):
+        if self._shader is None:
+            self.compile()
+        return self._shader
 
     def __del__(self):
         # This is not guaranteed to be called


https://bitbucket.org/yt_analysis/yt/commits/6678120ed456/
Changeset:   6678120ed456
Branch:      yt
User:        MatthewTurk
Date:        2016-02-25 23:48:14+00:00
Summary:     Make program invalid when we change shaders.
Affected #:  2 files

diff -r 3b5ef25070e102f6d5f82b110fd31616aa786945 -r 6678120ed45631fff940cde9d48d94369e40c165 yt/visualization/volume_rendering/interactive_vr.py
--- a/yt/visualization/volume_rendering/interactive_vr.py
+++ b/yt/visualization/volume_rendering/interactive_vr.py
@@ -260,6 +260,8 @@
     @property
     def program(self):
         if self._program_invalid:
+            if self._program is not None:
+                self._program.delete_program()
             self._program = ShaderProgram(self.vertex_shader,
                 self.fragment_shader)
             self._program_invalid = False
@@ -306,6 +308,7 @@
             self.fragment_shader = shader
         else:
             raise KeyError(shader.shader_type)
+        self._program_invalid = True
 
 class BlockCollection(SceneComponent):
     name = "block_collection"

diff -r 3b5ef25070e102f6d5f82b110fd31616aa786945 -r 6678120ed45631fff940cde9d48d94369e40c165 yt/visualization/volume_rendering/shader_objects.py
--- a/yt/visualization/volume_rendering/shader_objects.py
+++ b/yt/visualization/volume_rendering/shader_objects.py
@@ -32,6 +32,13 @@
         result = GL.glGetProgramiv(self.program, GL.GL_LINK_STATUS)
         if not result:
             raise RuntimeError(GL.glGetProgramInfoLog(self.program))
+        vertex_shader.delete_shader()
+        fragment_shader.delete_shader()
+
+    def delete_program(self):
+        if self.program is not None:
+            GL.glDeleteProgram(self.program)
+            self.program = None
 
     def _guess_uniform_func(self, value):
         # We make a best-effort guess.
@@ -149,10 +156,14 @@
             self.compile()
         return self._shader
 
+    def delete_shader(self):
+        if self.shader is not None:
+            GL.glDeleteShader(self.shader)
+            self._shader = None
+
     def __del__(self):
         # This is not guaranteed to be called
-        if self.shader is not None:
-            GL.glDeleteShader(self.shader)
+        self.delete_shader()
 
 class FragmentShader(Shader):
     shader_type = "fragment"


https://bitbucket.org/yt_analysis/yt/commits/8a5ed993e1ba/
Changeset:   8a5ed993e1ba
Branch:      yt
User:        MatthewTurk
Date:        2016-02-25 23:48:48+00:00
Summary:     Somehow, this wasn't added before.
Affected #:  1 file

diff -r 6678120ed45631fff940cde9d48d94369e40c165 -r 8a5ed993e1bae24b857070bfeb1ec008461cb51f yt/visualization/volume_rendering/shaders/noop.fragmentshader
--- /dev/null
+++ b/yt/visualization/volume_rendering/shaders/noop.fragmentshader
@@ -0,0 +1,17 @@
+#version 330 core
+
+in vec2 UV;
+
+out vec4 color;
+
+uniform sampler2D fb_texture;
+uniform sampler1D cmap;
+uniform float min_val;
+uniform float scale;
+uniform float cmap_min;
+uniform float cmap_max;
+uniform float cmap_log;
+
+void main(){
+   color = texture(fb_texture, UV);
+}


https://bitbucket.org/yt_analysis/yt/commits/c894cdc7cf15/
Changeset:   c894cdc7cf15
Branch:      yt
User:        MatthewTurk
Date:        2016-02-26 04:39:48+00:00
Summary:     First pass at adding an EGL rendering context.
Affected #:  1 file

diff -r 8a5ed993e1bae24b857070bfeb1ec008461cb51f -r c894cdc7cf15e6a181bbbe1fc22ac2dadfd5ef36 yt/visualization/volume_rendering/interactive_loop.py
--- a/yt/visualization/volume_rendering/interactive_loop.py
+++ b/yt/visualization/volume_rendering/interactive_loop.py
@@ -1,10 +1,75 @@
 import cyglfw3 as glfw
 import numpy as np
 import OpenGL.GL as GL
+import os
 from .input_events import EventCollection, MouseRotation
 
+from yt import write_bitmap
 from interactive_vr import BlockCollection, SceneGraph, TrackballCamera
 
+
+class EGLRenderingContext(object):
+    def __init__(self, width = 800, height = 600, title = "vol_render"):
+        from OpenGL import EGL
+        self.EGL = EGL
+        self.display = EGL.eglGetDisplay(EGL.EGL_DEFAULT_DISPLAY)
+        major = np.zeros(1, "i4")
+        minor = np.zeros(1, "i4")
+        EGL.eglInitialize(self.display, major, minor)
+        num_configs = np.zeros(1, "i4")
+        config = EGL.EGLConfig()
+        # Now we create our necessary bits.
+        config_attribs = np.array([
+          EGL.EGL_SURFACE_TYPE, EGL.EGL_PBUFFER_BIT,
+          EGL.EGL_BLUE_SIZE, 8,
+          EGL.EGL_GREEN_SIZE, 8,
+          EGL.EGL_RED_SIZE, 8,
+          EGL.EGL_DEPTH_SIZE, 8,
+          EGL.EGL_RENDERABLE_TYPE,
+          EGL.EGL_OPENGL_BIT,
+          EGL.EGL_NONE,
+        ], dtype="i4")
+        self.config = EGL.eglChooseConfig(self.display, config_attribs, config, 1,
+            num_configs)
+
+        pbuffer_attribs = np.array([
+          EGL.EGL_WIDTH, width,
+          EGL.EGL_HEIGHT, height,
+          EGL.EGL_NONE
+        ], dtype="i4")
+        self.surface = EGL.eglCreatePbufferSurface(self.display, self.config,
+            pbuffer_attribs)
+        EGL.eglBindAPI(EGL.EGL_OPENGL_API)
+        
+        self.context = EGL.eglCreateContext(self.display, self.config,
+            EGL.EGL_NO_CONTEXT, None)
+
+        EGL.eglMakeCurrent(self.display, self.surface, self.surface,
+            self.context)
+
+        GL.glClearColor(0.0, 0.0, 0.0, 0.0)
+        GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT)
+
+    def setup_loop(self, scene, camera):
+        scene.set_camera(camera)
+        scene.update_minmax()
+        camera.compute_matrices()
+        callbacks = EventCollection(scene, camera)
+        callbacks.draw = True
+        return callbacks
+
+    def start_loop(self, scene, camera):
+        callbacks = self.setup_loop(scene, camera)
+        for i in self(scene, camera, callbacks):
+            pass
+
+    def __call__(self, scene, camera, callbacks):
+        camera.compute_matrices()
+        scene.set_camera(camera)
+        scene.render()
+        arr = scene._retrieve_framebuffer()
+        write_bitmap(arr, "test.png")
+
 class RenderingContext(object):
     should_quit = False
     def __init__(self, width = 800, height = 600, title = "vol_render"):


https://bitbucket.org/yt_analysis/yt/commits/89c2f12bd0a5/
Changeset:   89c2f12bd0a5
Branch:      yt
User:        MatthewTurk
Date:        2016-03-01 17:58:27+00:00
Summary:     Fixing style issues
Affected #:  5 files

diff -r c894cdc7cf15e6a181bbbe1fc22ac2dadfd5ef36 -r 89c2f12bd0a58702d71adbd40f72eb3148155b06 yt/visualization/volume_rendering/glfw_inputhook.py
--- a/yt/visualization/volume_rendering/glfw_inputhook.py
+++ b/yt/visualization/volume_rendering/glfw_inputhook.py
@@ -26,7 +26,6 @@
 import os
 import sys
 import time
-from timeit import default_timer as clock
 
 #-----------------------------------------------------------------------------
 # Platform-dependent imports and functions

diff -r c894cdc7cf15e6a181bbbe1fc22ac2dadfd5ef36 -r 89c2f12bd0a58702d71adbd40f72eb3148155b06 yt/visualization/volume_rendering/input_events.py
--- a/yt/visualization/volume_rendering/input_events.py
+++ b/yt/visualization/volume_rendering/input_events.py
@@ -2,14 +2,11 @@
 from yt.utilities.math_utils import \
     get_perspective_matrix, \
     get_orthographic_matrix
-from yt.utilities.exceptions import YTInvalidShaderType
-from functools import wraps
 import OpenGL.GL as GL
 import cyglfw3 as glfw
 import numpy as np
 import matplotlib.cm as cm
 import random
-from .shader_objects import known_shaders
 
 event_registry = {}
 

diff -r c894cdc7cf15e6a181bbbe1fc22ac2dadfd5ef36 -r 89c2f12bd0a58702d71adbd40f72eb3148155b06 yt/visualization/volume_rendering/interactive_loop.py
--- a/yt/visualization/volume_rendering/interactive_loop.py
+++ b/yt/visualization/volume_rendering/interactive_loop.py
@@ -1,12 +1,9 @@
 import cyglfw3 as glfw
 import numpy as np
 import OpenGL.GL as GL
-import os
 from .input_events import EventCollection, MouseRotation
 
 from yt import write_bitmap
-from interactive_vr import BlockCollection, SceneGraph, TrackballCamera
-
 
 class EGLRenderingContext(object):
     def __init__(self, width = 800, height = 600, title = "vol_render"):
@@ -93,8 +90,6 @@
         scene.set_camera(camera)
         scene.update_minmax()
         camera.compute_matrices()
-        frame_start = glfw.GetTime()
-        fps_start = glfw.GetTime()
         print "Starting rendering..."
         callbacks = EventCollection(scene, camera)
         callbacks.add_key_callback("close_window", "escape")

diff -r c894cdc7cf15e6a181bbbe1fc22ac2dadfd5ef36 -r 89c2f12bd0a58702d71adbd40f72eb3148155b06 yt/visualization/volume_rendering/interactive_vr.py
--- a/yt/visualization/volume_rendering/interactive_vr.py
+++ b/yt/visualization/volume_rendering/interactive_vr.py
@@ -1,6 +1,5 @@
 import os
 import re
-import contextlib
 import OpenGL.GL as GL
 from collections import OrderedDict
 
@@ -10,11 +9,11 @@
     get_scale_matrix, \
     get_lookat_matrix, \
     get_perspective_matrix, \
-    get_orthographic_matrix, \
     quaternion_mult, \
     quaternion_to_rotation_matrix, \
     rotation_matrix_to_quaternion
-from yt.utilities.exceptions import YTInvalidShaderType
+from yt.utilities.exceptions import \
+    YTInvalidShaderType
 from .shader_objects import known_shaders, ShaderProgram
 
 import matplotlib.cm as cm

diff -r c894cdc7cf15e6a181bbbe1fc22ac2dadfd5ef36 -r 89c2f12bd0a58702d71adbd40f72eb3148155b06 yt/visualization/volume_rendering/shader_objects.py
--- a/yt/visualization/volume_rendering/shader_objects.py
+++ b/yt/visualization/volume_rendering/shader_objects.py
@@ -3,6 +3,10 @@
 import contextlib
 from yt.extern.six import add_metaclass
 from collections import OrderedDict
+from yt.utilities.exceptions import \
+    YTInvalidShaderType, \
+    YTUnknownUniformKind, \
+    YTUnknownUniformSize
 
 known_shaders = {}
 


https://bitbucket.org/yt_analysis/yt/commits/85b6c36298e5/
Changeset:   85b6c36298e5
Branch:      yt
User:        MatthewTurk
Date:        2016-03-01 17:58:41+00:00
Summary:     Fixing cookbook.
Affected #:  2 files

diff -r 89c2f12bd0a58702d71adbd40f72eb3148155b06 -r 85b6c36298e571c130117964297f46b3612e1458 doc/source/cookbook/opengl_ipython.py
--- a/doc/source/cookbook/opengl_ipython.py
+++ b/doc/source/cookbook/opengl_ipython.py
@@ -10,18 +10,9 @@
 scene = SceneGraph()
 collection = BlockCollection()
 
-N = 64
-x, y, z = np.mgrid[0:1:N*1j, 0:1:N*1j, 0:1:N*1j]
-c = [-0.05, -0.05, -0.05]
-oor2 = ((x-c[0])**2 + (y-c[1])**2 + (z-c[2])**2)**-0.5
-np.clip(oor2, 1e-6, 1e60, oor2)
-data = {'x_field': 10**x, 'y_field': 10**y, 'z_field': 10**z, 'sphere':oor2}
-
-ds = yt.load_uniform_grid(data, [N, N, N], 1.0, nprocs=4)
 ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030")
 
 dd = ds.all_data()
-#collection.add_data(dd, "sphere")
 collection.add_data(dd, "density")
 
 scene.add_collection(collection)

diff -r 89c2f12bd0a58702d71adbd40f72eb3148155b06 -r 85b6c36298e571c130117964297f46b3612e1458 doc/source/cookbook/opengl_vr.py
--- a/doc/source/cookbook/opengl_vr.py
+++ b/doc/source/cookbook/opengl_vr.py
@@ -8,18 +8,9 @@
 scene = SceneGraph()
 collection = BlockCollection()
 
-N = 64
-x, y, z = np.mgrid[0:1:N*1j, 0:1:N*1j, 0:1:N*1j]
-c = [-0.05, -0.05, -0.05]
-oor2 = ((x-c[0])**2 + (y-c[1])**2 + (z-c[2])**2)**-0.5
-np.clip(oor2, 1e-6, 1e60, oor2)
-data = {'x_field': 10**x, 'y_field': 10**y, 'z_field': 10**z, 'sphere':oor2}
-
-ds = yt.load_uniform_grid(data, [N, N, N], 1.0, nprocs=4)
 ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030")
 
 dd = ds.all_data()
-#collection.add_data(dd, "sphere")
 collection.add_data(dd, "density")
 
 scene.add_collection(collection)


https://bitbucket.org/yt_analysis/yt/commits/7077ddcd55ba/
Changeset:   7077ddcd55ba
Branch:      yt
User:        MatthewTurk
Date:        2016-03-01 17:59:33+00:00
Summary:     Adding to recipe blacklist
Affected #:  1 file

diff -r 85b6c36298e571c130117964297f46b3612e1458 -r 7077ddcd55ba78e6dffda3648659470cb7ed8176 doc/helper_scripts/run_recipes.py
--- a/doc/helper_scripts/run_recipes.py
+++ b/doc/helper_scripts/run_recipes.py
@@ -19,7 +19,7 @@
 CWD = os.getcwd()
 ytcfg["yt", "serialize"] = "False"
 PARALLEL_TEST = {"rockstar_nest": "3"}
-BLACKLIST = []
+BLACKLIST = ["opengl_ipython", "opengl_vr"]
 
 
 def prep_dirs():


https://bitbucket.org/yt_analysis/yt/commits/79d8dd8b9cef/
Changeset:   79d8dd8b9cef
Branch:      yt
User:        MatthewTurk
Date:        2016-03-03 16:21:10+00:00
Summary:     Denoting which files are for the volume rendering with hardware
Affected #:  4 files

diff -r 7077ddcd55ba78e6dffda3648659470cb7ed8176 -r 79d8dd8b9cefcd97d0e559cb74aa7fc4c3744172 yt/visualization/volume_rendering/input_events.py
--- a/yt/visualization/volume_rendering/input_events.py
+++ b/yt/visualization/volume_rendering/input_events.py
@@ -1,3 +1,5 @@
+# This is for the experimental OpenGL Volume Rendering
+
 from collections import defaultdict, namedtuple
 from yt.utilities.math_utils import \
     get_perspective_matrix, \

diff -r 7077ddcd55ba78e6dffda3648659470cb7ed8176 -r 79d8dd8b9cefcd97d0e559cb74aa7fc4c3744172 yt/visualization/volume_rendering/interactive_loop.py
--- a/yt/visualization/volume_rendering/interactive_loop.py
+++ b/yt/visualization/volume_rendering/interactive_loop.py
@@ -1,3 +1,5 @@
+# This is for the experimental OpenGL Volume Rendering
+
 import cyglfw3 as glfw
 import numpy as np
 import OpenGL.GL as GL

diff -r 7077ddcd55ba78e6dffda3648659470cb7ed8176 -r 79d8dd8b9cefcd97d0e559cb74aa7fc4c3744172 yt/visualization/volume_rendering/interactive_vr.py
--- a/yt/visualization/volume_rendering/interactive_vr.py
+++ b/yt/visualization/volume_rendering/interactive_vr.py
@@ -1,3 +1,5 @@
+# This is for the experimental OpenGL Volume Rendering
+
 import os
 import re
 import OpenGL.GL as GL

diff -r 7077ddcd55ba78e6dffda3648659470cb7ed8176 -r 79d8dd8b9cefcd97d0e559cb74aa7fc4c3744172 yt/visualization/volume_rendering/shader_objects.py
--- a/yt/visualization/volume_rendering/shader_objects.py
+++ b/yt/visualization/volume_rendering/shader_objects.py
@@ -1,3 +1,5 @@
+# This is for the experimental OpenGL Volume Rendering
+
 import os
 import OpenGL.GL as GL
 import contextlib

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.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.spacepope.org/pipermail/yt-svn-spacepope.org/attachments/20160407/59b1cd0e/attachment-0001.htm>


More information about the yt-svn mailing list