"""
Morph one shape into another using spherical harmonics package shtools.

In this example we morph a sphere into a octahedron and vice-versa.
"""
import numpy as np
from vedo import settings, Plotter, Points, Sphere, cos, dataurl, mag, sin, Mesh

try:
    import pyshtools
    print(__doc__)
except ModuleNotFoundError:
    print("Please install pyshtools to run this example")
    print("Follow instructions at https://shtools.oca.eu/shtools")
    exit(0)


##########################################################
N = 100  # number of sample points on the unit sphere
lmax = 15  # maximum degree of the sph. harm. expansion
rbias = 0.5  # subtract a constant average value
x0 = [0, 0, 0]  # set object at this position
##########################################################


def makeGrid(shape, N):
    rmax = 2.0  # line length
    agrid, pts = [], []
    for th in np.linspace(0, np.pi, N, endpoint=True):
        lats = []
        for ph in np.linspace(0, 2 * np.pi, N, endpoint=True):
            p = np.array([sin(th) * cos(ph), sin(th) * sin(ph), cos(th)]) * rmax
            intersections = shape.intersect_with_line([0, 0, 0], p)
            if len(intersections):
                value = mag(intersections[0])
                lats.append(value - rbias)
                pts.append(intersections[0])
            else:
                lats.append(rmax - rbias)
                pts.append(p)
        agrid.append(lats)
    agrid = np.array(agrid)
    actor = Points(pts, c="k", alpha=0.4, r=1)
    return agrid, actor


def morph(clm1, clm2, t, lmax):
    """Interpolate linearly the two sets of sph harm. coeeficients."""
    clm = (1 - t) * clm1 + t * clm2
    grid_reco = clm.expand(lmax=lmax)  # cut "high frequency" components
    agrid_reco = grid_reco.to_array()
    pts = []
    for i, longs in enumerate(agrid_reco):
        ilat = grid_reco.lats()[i]
        for j, value in enumerate(longs):
            ilong = grid_reco.lons()[j]
            th = np.deg2rad(90 - ilat)
            ph = np.deg2rad(ilong)
            r = value + rbias
            p = np.array([sin(th) * cos(ph), sin(th) * sin(ph), cos(th)]) * r
            pts.append(p)
    return pts

settings.use_depth_peeling = True

plt = Plotter(shape=[2, 2], axes=3, interactive=0)

shape1 = Sphere(alpha=0.2)
shape2 = Mesh(dataurl + "icosahedron.vtk").normalize().linewidth(1)
plt += shape2

agrid1, actorpts1 = makeGrid(shape1, N)

plt.at(0).show(shape1, actorpts1)

agrid2, actorpts2 = makeGrid(shape2, N)
plt.at(1).show(shape2, actorpts2)

plt.camera.Zoom(1.2)

clm1 = pyshtools.SHGrid.from_array(agrid1).expand()
clm2 = pyshtools.SHGrid.from_array(agrid2).expand()
# clm1.plot_spectrum2d()  # plot the value of the sph harm. coefficients
# clm2.plot_spectrum2d()

for t in np.arange(0, 1, 0.005):
    act21 = Points(morph(clm2, clm1, t, lmax), c="r", r=4)
    act12 = Points(morph(clm1, clm2, t, lmax), c="g", r=4)

    plt.at(2).show(act21, resetcam=False)
    plt.at(3).show(act12)
    plt.azimuth(2)

plt.interactive().close()
