# Home

## 16. Orientation explorer

## `sasmodels.jitter`

### Jitter Explorer

Application to explore orientation angle and angular dispersity.

From the command line:

    # Show docs
    python -m sasmodels.jitter --help

    # Guyou projection jitter, uniform over 20 degree theta and 10 in phi
    python -m sasmodels.jitter --projection=guyou --dist=uniform --jitter=20,10,0

From a jupyter cell:

    import ipyvolume as ipv
    from sasmodels import jitter
    import importlib; importlib.reload(jitter)
    jitter.set_plotter("ipv")

    size = (10, 40, 100)
    view = (20, 0, 0)

    #size = (15, 15, 100)
    #view = (60, 60, 0)

    dview = (0, 0, 0)
    #dview = (5, 5, 0)
    #dview = (15, 180, 0)
    #dview = (180, 15, 0)

    projection = 'equirectangular'
    #projection = 'azimuthal_equidistance'
    #projection = 'guyou'
    #projection = 'sinusoidal'
    #projection = 'azimuthal_equal_area'

    dist = 'uniform'
    #dist = 'gaussian'

    jitter.run(size=size, view=view, jitter=dview, dist=dist, projection=projection)
    #filename = projection+('_theta' if dview[0] == 180 else '_phi' if dview[1] == 180 else '')
    #ipv.savefig(filename+'.png')

class sasmodels.jitter.Quaternion(w, r)

  Bases: `object`

  Quaternion(w, r) = w + ir[0] + jr[1] + kr[2]

  Quaternion.from_angle_axis(theta, r) for a rotation of angle theta about an axis oriented toward the direction r. This defines a unit quaternion, normalizing \(r\) to the unit vector \(\hat r\), and setting quaternion \(Q = \cos \theta + \sin \theta \hat r\)

  Quaternion objects can be multiplied, which applies a rotation about the given axis, allowing composition of rotations without risk of gimbal lock. The resulting quaternion is applied to a set of points using Q.rot(v).

  static from_angle_axis(theta, r)

    Build quaternion as rotation theta about axis r

  conj()

    Conjugate quaternion

  inv()

    Inverse quaternion

  norm()

    Quaternion length

  rot(v)

    Transform point v by quaternion

sasmodels.jitter.PLOT_ENGINE(calculator, draw_shape, size, view, jitter, dist, mesh, projection)

sasmodels.jitter.R_to_xyz(R)

  Return phi, theta, psi Tait-Bryan angles corresponding to the given rotation matrix.

  Extracting Euler Angles from a Rotation Matrix Mike Day, Insomniac Games https://d3cw3dd2w32x2b.cloudfront.net/wp-content/uploads/2012/07/euler-angles1.pdf Based on: Shoemake’s “Euler Angle Conversion”, Graphics Gems IV, pp. 222-229

sasmodels.jitter.Rx(angle)

  Construct a matrix to rotate points about x by angle degrees.

sasmodels.jitter.Ry(angle)

  Construct a matrix to rotate points about y by angle degrees.

sasmodels.jitter.Rz(angle)

  Construct a matrix to rotate points about z by angle degrees.

sasmodels.jitter.apply_jitter(jitter, points)

  Apply the jitter transform to a set of points.

  Points are stored in a 3 x n numpy matrix, not a numpy array or tuple.

sasmodels.jitter.build_model(model_name, n=150, qmax=0.5, **pars)

  Build a calculator for the given shape.

  model_name is any sasmodels model. n and qmax define an n x n mesh on which to evaluate the model. The remaining parameters are stored in the returned calculator as calculator.pars. They are used by `draw_scattering()` to set the non-orientation parameters in the calculation.

  Returns a calculator function which takes a dictionary or parameters and produces Iqxy. The Iqxy value needs to be reshaped to an n x n matrix for plotting. See the `direct_model.DirectModel` class for details.

sasmodels.jitter.clipped_range(data, portion=1.0, mode='central')

  Determine range from data.

  If portion is 1, use full range, otherwise use the center of the range or the top of the range, depending on whether mode is ‘central’ or ‘top’.

sasmodels.jitter.draw_axes(axes, origin=(-1, -1, -1), length=(2, 2, 2))

  Draw wireframe axes lines, with given origin and length

sasmodels.jitter.draw_bcc(axes, size, view, jitter, steps=None, alpha=1)

  Draw points for body-centered cubic paracrystal

sasmodels.jitter.draw_beam(axes, view=(0, 0), alpha=0.5, steps=25)

  Draw the beam going from source at (0, 0, 1) to detector at (0, 0, -1)

sasmodels.jitter.draw_box(axes, size, view)

  Draw a wireframe box at a particular view.

sasmodels.jitter.draw_ellipsoid(axes, size, view, jitter, steps=25, alpha=1)

  Draw an ellipsoid.

sasmodels.jitter.draw_fcc(axes, size, view, jitter, steps=None, alpha=1)

  Draw points for face-centered cubic paracrystal

sasmodels.jitter.draw_jitter(axes, view, jitter, dist='gaussian', size=(0.1, 0.4, 1.0), draw_shape=<function draw_parallelepiped>, projection='equirectangular', alpha=0.8, views=None)

  Represent jitter as a set of shapes at different orientations.

sasmodels.jitter.draw_labels(axes, view, jitter, text)

  Draw text at a particular location.

sasmodels.jitter.draw_mesh(axes, view, jitter, radius=1.2, n=11, dist='gaussian', projection='equirectangular')

  Draw the dispersion mesh showing the theta-phi orientations at which the model will be evaluated.

sasmodels.jitter.draw_parallelepiped(axes, size, view, jitter, steps=None, color=(0.6, 1.0, 0.6), alpha=1)

  Draw a parallelepiped surface, with view and jitter.

sasmodels.jitter.draw_person_on_sphere(axes, view, height=0.5, radius=1.0)

  Draw a person on the surface of a sphere.

  view indicates (latitude, longitude, orientation)

sasmodels.jitter.draw_sc(axes, size, view, jitter, steps=None, alpha=1)

  Draw points for simple cubic paracrystal

sasmodels.jitter.draw_scattering(calculator, axes, view, jitter, dist='gaussian')

  Plot the scattering for the particular view.

  calculator is returned from `build_model()`. axes are the 3D axes on which the data will be plotted. view and jitter are the current orientation and orientation dispersity. dist is one of the sasmodels weight distributions.

sasmodels.jitter.draw_sphere(axes, radius=1.0, steps=25, center=(0, 0, 0), color='w', alpha=1.0)

  Draw a sphere

sasmodels.jitter.get_projection(projection)

  jitter projections <https://en.wikipedia.org/wiki/List_of_map_projections>

  equirectangular (standard latitude-longitude mesh)

    <https://en.wikipedia.org/wiki/Equirectangular_projection> Allows free movement in phi (around the equator), but theta is limited to +/- 90, and points are cos-weighted. Jitter in phi is uniform in weight along a line of latitude. With small theta and phi ranging over +/- 180 this forms a wobbling disk. With small phi and theta ranging over +/- 90 this forms a wedge like a slice of an orange.

  azimuthal_equidistance (Postel)

    <https://en.wikipedia.org/wiki/Azimuthal_equidistant_projection> Preserves distance from center, and so is an excellent map for representing a bivariate gaussian on the surface. Theta and phi operate identically, cutting wegdes from the antipode of the viewing angle. This unfortunately does not allow free movement in either theta or phi since the orthogonal wobble decreases to 0 as the body rotates through 180 degrees.

  sinusoidal (Sanson-Flamsteed, Mercator equal-area)

    <https://en.wikipedia.org/wiki/Sinusoidal_projection> Preserves arc length with latitude, giving bad behaviour at theta near +/- 90. Theta and phi operate somewhat differently, so a system with a-b-c dtheta-dphi-dpsi will not give the same value as one with b-a-c dphi-dtheta-dpsi, as would be the case for azimuthal equidistance. Free movement using theta or phi uniform over +/- 180 will work, but not as well as equirectangular phi, with theta being slightly worse. Computationally it is much cheaper for wide theta-phi meshes since it excludes points which lie outside the sinusoid near theta +/- 90 rather than packing them close together as in equirectangle. Note that the poles will be slightly overweighted for theta > 90 with the circle from theta at 90+dt winding backwards around the pole, overlapping the circle from theta at 90-dt.

  Guyou (hemisphere-in-a-square) not weighted

    <https://en.wikipedia.org/wiki/Guyou_hemisphere-in-a-square_projection> With tiling, allows rotation in phi or theta through +/- 180, with uniform spacing. Both theta and phi allow free rotation, with wobble in the orthogonal direction reasonably well behaved (though not as good as equirectangular phi). The forward/reverse transformations relies on elliptic integrals that are somewhat expensive, so the behaviour has to be very good to justify the cost and complexity. The weighting function for each point has not yet been computed. Note: run the module guyou.py directly and it will show the forward and reverse mappings.

  azimuthal_equal_area incomplete

    <https://en.wikipedia.org/wiki/Lambert_azimuthal_equal-area_projection> Preserves the relative density of the surface patches. Not that useful and not completely implemented

  Gauss-Kreuger not implemented

    <https://en.wikipedia.org/wiki/Transverse_Mercator_projection#Ellipsoidal_transverse_Mercator> Should allow free movement in theta, but phi is distorted.

sasmodels.jitter.ipv_axes()

  Build a matplotlib style Axes interface for ipyvolume

sasmodels.jitter.main()

  Command line interface to the jitter viewer.

sasmodels.jitter.make_image(z, kw)

  Convert numpy array z into a PIL RGB image.

sasmodels.jitter.make_vec(*args)

  Turn all elements of args into numpy arrays

sasmodels.jitter.map_colors(z, kw)

  Process matplotlib-style colour arguments.

  Pulls ‘cmap’, ‘alpha’, ‘vmin’, and ‘vmax’ from th kw dictionary, setting the kw[‘color’] to an RGB array. These are ignored if ‘c’ or ‘color’ are set inside kw.

sasmodels.jitter.orient_relative_to_beam(view, points)

  Apply the view transform to a set of points.

  Points are stored in a 3 x n numpy matrix, not a numpy array or tuple.

sasmodels.jitter.orient_relative_to_beam_quaternion(view, points)

  Apply the view transform to a set of points.

  Points are stored in a 3 x n numpy matrix, not a numpy array or tuple.

  This variant uses quaternions rather than rotation matrices for the computation. It works but it is not used because it doesn’t solve any problems. The challenge of mapping theta/phi/psi to SO(3) does not disappear by calculating the transform differently.

sasmodels.jitter.run(model_name='parallelepiped', size=(10, 40, 100), view=(0, 0, 0), jitter=(0, 0, 0), dist='gaussian', mesh=30, projection='equirectangular')

  Show an interactive orientation and jitter demo.

  model_name is one of: sphere, ellipsoid, triaxial_ellipsoid, parallelepiped, cylinder, or sc/fcc/bcc_paracrystal

  size gives the dimensions (a, b, c) of the shape.

  view gives the initial view (theta, phi, psi) of the shape.

  view gives the initial jitter (dtheta, dphi, dpsi) of the shape.

  dist is the type of dispersition: gaussian, rectangle, or uniform.

  mesh is the number of points in the dispersion mesh.

  projection is the map projection to use for the mesh: equirectangular, sinusoidal, guyou, azimuthal_equidistance, or azimuthal_equal_area.

sasmodels.jitter.select_calculator(model_name, n=150, size=(10, 40, 100))

  Create a model calculator for the given shape.

  model_name is one of sphere, cylinder, ellipsoid, triaxial_ellipsoid, parallelepiped or bcc_paracrystal. n is the number of points to use in the q range. qmax is chosen based on model parameters for the given model to show something intersting.

  Returns calculator and tuple size (a,b,c) giving minor and major equitorial axes and polar axis respectively. See `build_model()` for details on the returned calculator.

sasmodels.jitter.set_plotter(name)

  Setting the plotting engine to matplotlib/ipyvolume or equivalently mpl/ipv.

sasmodels.jitter.test_qrot()

  Quaternion checks

sasmodels.jitter.transform_xyz(view, jitter, x, y, z)

  Send a set of (x,y,z) points through the jitter and view transforms.