def iproject(xy: np.ndarray) -> Vector3d: """Convert from 2D Gnomonic coordinates (x_g, y_g) to 3D cartesian coordiantes (x, y, z). Parameters ---------- xy 2D coordinates on the form [[x_g0, y_g0], [x_g1, y_g1], ...]. Returns ------- cartesian_coordinates Cartesian coordinates (x, y, z) on the form [[x0, y0, z0], [x1, y1, z1], ...]. Examples -------- >>> import numpy as np >>> from kikuchipy.projections.gnomonic_projection import ( ... GnomonicProjection ... ) >>> xy_g = np.random.random_sample(20).reshape((10, 2)) >>> xyz = GnomonicProjection.iproject(xy_g) """ x, y = xy[..., 0], xy[..., 1] theta = np.arctan(np.sqrt(x**2 + y**2)) phi = np.arctan2(y, x) return Vector3d.from_polar(theta=theta, phi=phi)
def get_plot_data(self): """ Produces suitable Rotations for the construction of a wireframe for self """ from orix.vector import Vector3d # gets a grid of vector directions theta = np.linspace(0, 2 * np.pi - EPSILON, 361) rho = np.linspace(0, np.pi - EPSILON, 181) theta, rho = np.meshgrid(theta, rho) g = Vector3d.from_polar(rho, theta) # get the cell vector normal norms n = Rodrigues.from_rotation(self).norm.data[:, np.newaxis, np.newaxis] if n.size == 0: return Rotation.from_neo_euler(AxAngle.from_axes_angles(g, np.pi)) d = (-self.axis).dot_outer(g.unit).data x = n * d omega = 2 * np.arctan(np.where(x != 0, x**-1, np.pi)) # keeps the smallest allowed angle omega[omega < 0] = np.pi omega = np.min(omega, axis=0) r = Rotation.from_neo_euler(AxAngle.from_axes_angles(g.unit, omega)) return r
def spherical2xy(self, azimuth, polar): r"""Return stereographic coordinates (X, Y) from 3D unit vectors created from spherical coordinates, azimuth :math:`\phi` and polar :math:`\theta`, defined as in the ISO 31-11 standard [SphericalWolfram]_. Parameters ---------- azimuth : float or numpy.ndarray Spherical azimuth coordinate. polar : float or numpy.ndarray Spherical polar coordinate. Returns ------- x : numpy.ndarray Stereographic coordinate X of shape same shape as the input vector shape. Only the vectors with :math:`z` coordinate positive (`pole` = -1) or negative (`pole` = 1) are returned. y : numpy.ndarray Stereographic coordinate Y of shape same shape as the input vector shape. Only the vectors with :math:`z` coordinate positive (`pole` = -1) or negative (`pole` = 1) are returned. See Also -------- vector2xy """ v = Vector3d.from_polar(azimuth=azimuth, polar=polar) return self.vector2xy(v)
def spherical2xy_split(self, azimuth, polar): r"""Return two sets of stereographic coordinates (X, Y) from 3D unit vectors created from spherical coordinates, azimuth :math:`\phi` and polar :math:`\theta`, defined as in the ISO 31-11 standard [SphericalWolfram]_: one set for vectors in the upper hemisphere, and one for the lower. Parameters ---------- azimuth : float or numpy.ndarray Spherical azimuth coordinate. polar : float or numpy.ndarray Spherical polar coordinate. Returns ------- x_upper : numpy.ndarray Stereographic coordinate X of upper hemisphere vectors, of shape same shape as the input vector shape. y_upper : numpy.ndarray Stereographic coordinate Y of upper hemisphere vectors, of shape same shape as the input vector shape. x_lower : numpy.ndarray Stereographic coordinate X of lower hemisphere vectors, of shape same shape as the input vector shape. y_lower : numpy.ndarray Stereographic coordinate Y of lower hemisphere vectors, of shape same shape as the input vector shape. See Also -------- vector2xy """ v = Vector3d.from_polar(azimuth=azimuth, polar=polar) return self.vector2xy_split(v)
def get_plot_data(self): from orix.vector import Vector3d theta = np.linspace(0, 2 * np.pi + 1e-9, 361) rho = np.linspace(0, np.pi, 181) theta, rho = np.meshgrid(theta, rho) g = Vector3d.from_polar(rho, theta) n = Rodrigues.from_rotation(self).norm.data[:, np.newaxis, np.newaxis] if n.size == 0: return Rotation.from_neo_euler(AxAngle.from_axes_angles(g, np.pi)) d = (-self.axis).dot_outer(g.unit).data x = n * d x = 2 * np.arctan(x**-1) x[x < 0] = np.pi x = np.min(x, axis=0) r = Rotation.from_neo_euler(AxAngle.from_axes_angles(g.unit, x)) return r
def test_polar_loop(self, vector): theta, phi, r = vector.to_polar() vector2 = Vector3d.from_polar(theta=theta.data, phi=phi.data, r=r.data) assert np.allclose(vector.data, vector2.data)
def test_polar(theta, phi, r, expected): assert np.allclose(Vector3d.from_polar(theta, phi, r).data, expected.data, atol=1e-5)
def draw_circle(self, *args, opening_angle=np.pi / 2, steps=100, **kwargs): r"""Draw great or small circles with a given `opening_angle` to one or multiple vectors. A vector must be present in the current hemisphere for its circle to be drawn. Parameters ---------- args : Vector3d or tuple of float or numpy.ndarray Vector(s), or azimuth and polar angles defining vectors, the latter two passed as separate arguments (not keyword arguments). Circles are drawn perpendicular to these with a given `opening_angle`. opening_angle : float or numpy.ndarray, optional Opening angle(s) around the vector(s). Default is :math:`\pi/2`, giving a great circle. If an array is passed, its size must be equal to the number of circles to draw. steps : int, optional Number of vectors to describe each circle, default is 100. kwargs Keyword arguments passed to :meth:`matplotlib.axes.Axes.plot` to alter the circles' appearance. See Also -------- orix.vector.Vector3d.get_circle """ azimuth, polar = self._pretransform_input(args) # Exclude vectors not visible in this hemisphere before creating # circles visible = self._visible_in_hemisphere(polar) if np.count_nonzero(visible) == 0: # No circles to draw return else: azimuth = azimuth[visible] polar = polar[visible] # Get set of `steps` vectors delineating a circle per vector v = Vector3d.from_polar(azimuth=azimuth, polar=polar) circles = v.get_circle(opening_angle=opening_angle, steps=steps).unit # Enable using one color per circle color = kwargs.pop("color", "C0") color2 = _get_array_of_values(value=color, visible=visible) # Set above which elements circles will appear (zorder) new_kwargs = dict(zorder=ZORDER["draw_circle"], clip_on=False) for k, v in new_kwargs.items(): kwargs.setdefault(k, v) hemisphere = self.hemisphere polar_cap = self._polar_cap for i, c in enumerate(circles): a, p = _sort_coords_by_shifted_bools( hemisphere=hemisphere, polar_cap=polar_cap, azimuth=c.azimuth.data, polar=c.polar.data, ) super().plot(a, p, color=color2[i], **kwargs)
def test_polar_loop(self, vector): azimuth, polar, radial = vector.to_polar() vector2 = Vector3d.from_polar(azimuth=azimuth.data, polar=polar.data, radial=radial.data) assert np.allclose(vector.data, vector2.data)
def test_polar(azimuth, polar, radial, expected): assert np.allclose( Vector3d.from_polar(azimuth=azimuth, polar=polar, radial=radial).data, expected.data, atol=1e-5, )