Warp EEG electrode locations to spherical layout.

    EEG Electrodes are warped to a spherical layout in three steps:
        1. An ellipsoid is least-squares-fitted to the electrode locations.
        2. Electrodes are displaced to the nearest point on the ellipsoid's surface.
        3. The ellipsoid is transformed to a sphere, causing the new locations to lie exactly on a spherical surface
           with unit radius.

    This procedure intends to minimize electrode displacement in the original coordinate space. Simply projecting
    electrodes on a sphere (e.g. by normalizing the x/y/z coordinates) typically gives much larger displacements.

    Parameters
    ----------
    locations : array-like, shape = [n_electrodes, 3]
        Eeach row of `locations` corresponds to the location of an EEG electrode in cartesian x/y/z coordinates.
    y_center : float, optional
        Fix the y-coordinate of the ellipsoid's center to this value (optional). This is useful to align the ellipsoid
        with the central electrodes.
    return_ellipsoid : bool, optional
        If `true` center and radii of the ellipsoid are returned.

    Returns
    -------
    newlocs : array-like, shape = [n_electrodes, 3]
        Electrode locations on unit sphere.
    c : array-like, shape = [3], (only returned if `return_ellipsoid` evaluates to `True`)
        Center of the ellipsoid in the original location's coordinate space.
    r : array-like, shape = [3], (only returned if `return_ellipsoid` evaluates to `True`)
        Radii (x, y, z) of the ellipsoid in the original location's coordinate space.

def warp_locations(locations, y_center=None, return_ellipsoid=False, verbose=False):
    """ Warp EEG electrode locations to spherical layout.

    EEG Electrodes are warped to a spherical layout in three steps:
        1. An ellipsoid is least-squares-fitted to the electrode locations.
        2. Electrodes are displaced to the nearest point on the ellipsoid's surface.
        3. The ellipsoid is transformed to a sphere, causing the new locations to lie exactly on a spherical surface
           with unit radius.

    This procedure intends to minimize electrode displacement in the original coordinate space. Simply projecting
    electrodes on a sphere (e.g. by normalizing the x/y/z coordinates) typically gives much larger displacements.

    Parameters
    ----------
    locations : array-like, shape = [n_electrodes, 3]
        Eeach row of `locations` corresponds to the location of an EEG electrode in cartesian x/y/z coordinates.
    y_center : float, optional
        Fix the y-coordinate of the ellipsoid's center to this value (optional). This is useful to align the ellipsoid
        with the central electrodes.
    return_ellipsoid : bool, optional
        If `true` center and radii of the ellipsoid are returned.

    Returns
    -------
    newlocs : array-like, shape = [n_electrodes, 3]
        Electrode locations on unit sphere.
    c : array-like, shape = [3], (only returned if `return_ellipsoid` evaluates to `True`)
        Center of the ellipsoid in the original location's coordinate space.
    r : array-like, shape = [3], (only returned if `return_ellipsoid` evaluates to `True`)
        Radii (x, y, z) of the ellipsoid in the original location's coordinate space.
    """
    locations = np.asarray(locations)

    if y_center is None:
        c, r = _fit_ellipsoid_full(locations)
    else:
        c, r = _fit_ellipsoid_partial(locations, y_center)

    elliptic_locations = _project_on_ellipsoid(c, r, locations)

    if verbose:
        print('Head ellipsoid center:', c)
        print('Head ellipsoid radii:', r)
        distance = np.sqrt(np.sum((locations - elliptic_locations)**2, axis=1))
        print('Minimum electrode displacement:', np.min(distance))
        print('Average electrode displacement:', np.mean(distance))
        print('Maximum electrode displacement:', np.max(distance))

    spherical_locations = (elliptic_locations - c) / r

    if return_ellipsoid:
        return spherical_locations, c, r

    return spherical_locations