コード例 #1
0
    def get_fitted_shell(self, azimuth, zenith):
        r_scaled = reconstruct_shell(self.modes, self.coefficients, azimuth,
                                     zenith)
        x_scaled, y_scaled, z_scaled = coordinate_tools.spherical_to_cartesian(
            azimuth, zenith, r_scaled)
        # need to scale things "down" since they were scaled "up" in the fit
        # scaling_factors = 1. / self.scaling_factors

        scaled_axes = self.principal_axes / self.scaling_factors[:, None]

        coords = x_scaled.ravel()[:, None] * scaled_axes[
            0, :] + y_scaled.ravel()[:, None] * scaled_axes[
                1, :] + z_scaled.ravel()[:, None] * scaled_axes[2, :]
        x, y, z = coords.T

        return x.reshape(x_scaled.shape) + self.x0, y.reshape(
            y_scaled.shape) + self.y0, z.reshape(z_scaled.shape) + self.z0
コード例 #2
0
    def approximate_normal(self,
                           x,
                           y,
                           z,
                           d_azimuth=1e-6,
                           d_zenith=1e-6,
                           return_orthogonal_vectors=False):
        """

        Numerically approximate a vector(s) normal to the spherical harmonic shell at the query point(s).

        For input point(s), scale and convert to spherical coordinates, shift by +/- d_azimuth and d_zenith to get
        'phantom' points in the plane tangent to the spherical harmonic expansion on either side of the query point(s).
        Scale back, convert to cartesian, make vectors from the phantom points (which are by definition not parallel)
        and cross them to get a vector perpindicular to the plane.

        Returns
        -------

        Parameters
        ----------
        x : ndarray, float
            cartesian x location of point(s) on the surface to calculate the normal at
        y : ndarray, float
            cartesian y location of point(s) on the surface to calculate the normal at
        z : ndarray, float
            cartesian z location of point(s) on the surface to calculate the normal at
        d_azimuth : float
            azimuth step size for generating vector in plane of the shell [radians]
        d_zenith : float
            zenith step size for generating vector in plane of the shell [radians]

        Returns
        -------
        normal_vector : ndarray
            cartesian unit vector(s) normal to query point(s). size (len(x), 3)
        orth0 : ndarray
            cartesian unit vector(s) in the plane of the spherical harmonic shell at the query point(s), and
            perpendicular to normal_vector
        orth1 : ndarray
            cartesian unit vector(s) orthogonal to normal_vector and orth0

        """
        # scale the query points and convert them to spherical
        x_qs, y_qs, z_qs = coordinate_tools.scaled_projection(
            np.atleast_1d(x - self.x0), np.atleast_1d(y - self.y0),
            np.atleast_1d(z - self.z0), self.scaling_factors,
            self.principal_axes)
        azimuth, zenith, r = coordinate_tools.cartesian_to_spherical(
            x_qs, y_qs, z_qs)

        # get scaled shell radius at +/- points for azimuthal and zenith shifts
        azimuths = np.array(
            [azimuth - d_azimuth, azimuth + d_azimuth, azimuth, azimuth])
        zeniths = np.array(
            [zenith, zenith, zenith - d_zenith, zenith + d_zenith])
        r_scaled = reconstruct_shell(self.modes, self.coefficients, azimuths,
                                     zeniths)

        # convert shifted points to cartesian and scale back. shape = (4, #points)
        x_scaled, y_scaled, z_scaled = coordinate_tools.spherical_to_cartesian(
            azimuths, zeniths, r_scaled)
        # scale things "down" since they were scaled "up" in the fit
        scaled_axes = self.principal_axes / self.scaling_factors[:, None]
        coords = x_scaled.ravel()[:, None] * scaled_axes[0, :] + \
                 y_scaled.ravel()[:, None] * scaled_axes[1, :] + \
                 z_scaled.ravel()[:, None] * scaled_axes[2, :]
        x_p, y_p, z_p = coords.T
        # skip adding x0, y0, z0 back on, since we'll subtract it off in a second
        x_p, y_p, z_p = x_p.reshape(x_scaled.shape), y_p.reshape(
            y_scaled.shape), z_p.reshape(z_scaled.shape)

        # make two vectors in the plane centered at the query point
        v0 = np.array([x_p[1] - x_p[0], y_p[1] - y_p[0], z_p[1] - z_p[0]])
        v1 = np.array([x_p[3] - x_p[2], y_p[3] - y_p[2], z_p[3] - z_p[2]])
        if not np.any(v0) or not np.any(v1):
            raise RuntimeWarning(
                'failed to generate two vectors in the plane - likely precision error in sph -> cart'
            )
        # cross them to get a normal vector NOTE - direction could be negative of true normal
        normal = np.cross(v0, v1, axis=0)
        # return as unit vector(s) along each row
        normal = np.atleast_2d(normal / np.linalg.norm(normal, axis=0)).T
        # make sure normals point outwards, by dotting it with the vector to the point on the shell from the center
        points = np.stack([
            np.atleast_1d(x - self.x0),
            np.atleast_1d(y - self.y0),
            np.atleast_1d(z - self.z0)
        ]).T
        outwards = np.array([
            np.dot(normal[ind], points[ind]) > 0
            for ind in range(normal.shape[0])
        ])
        normal[~outwards, :] *= -1
        if np.isnan(normal).any():
            raise RuntimeError('Failed to calculate normal vector')
        if return_orthogonal_vectors:
            orth0 = np.atleast_2d(v0 / np.linalg.norm(v0, axis=0)).T
            # v0 and v1 are both in a plane perpendicular to normal, but not strictly orthogonal to each other
            orth1 = np.cross(
                normal, orth0, axis=1
            )  # replace v1 with a unit vector orth. to both normal and v0
            return normal.squeeze(), orth0.squeeze(), orth1.squeeze()
        return normal.squeeze()
コード例 #3
0
def test_spherical_to_cartesian():
    azi, zen, r = coordinate_tools.cartesian_to_spherical(X_C, Y_C, Z_C)
    x, y, z = coordinate_tools.spherical_to_cartesian(azi, zen, r)
    np.testing.assert_array_almost_equal(X_C, x)
    np.testing.assert_array_almost_equal(Y_C, y)
    np.testing.assert_array_almost_equal(Z_C, z)