"""Draw a z = BesselJ(x,y) surface with a custom color map
and a custom scalar bar with labels in radians"""
import numpy as np
from scipy import special
from scipy.special import jn_zeros
from vedo import ScalarBar3D, Line, show, settings
from vedo.colors import color_map, build_lut
from vedo.pyplot import plot

Nr = 1
Nθ = 3

settings.default_font = "Theemim"
settings.interpolate_scalars_before_mapping = False
axes_opts = dict(
    xtitle="x", ytitle="y", ztitle="|f(x,y)|",
    xlabel_rotation=90, ylabel_rotation=90, zlabel_rotation=90,
    xtitle_rotation=90, ytitle_rotation=90, zaxis_rotation=45,
    ztitle_offset=0.03,
)

def custom_lut_surface(name, vmin=0, vmax=1, N=256):
    # Create a custom look-up-table for the surface
    table = []
    x = np.linspace(vmin, vmax, N)
    for i in range(N):
        rgb = color_map(i, name, 0, N-1)
        table.append([x[i], rgb])
    return build_lut(table)

def custom_table_scalarbar(name):
    # Create a custom table of colors and labels for the scalarbar
    table = []
    x = np.linspace(-np.pi,np.pi, 401)
    labs = ["-:pi"  , "-3:pi/4", "-:pi/2", "-:pi/4", "0",
            "+:pi/4",  "+:pi/2", "+3:pi/4","+:pi"]  
    for i in range(401):
        rgb = color_map(i, name, 0, 400)
        if i%50 == 0:
            table.append([x[i], rgb, 1, labs[i//50]])
        else:
            table.append([x[i], rgb])
    return table, build_lut(table)

######################################################################
def f(x, y):
    d2 = x**2 + y**2
    if d2 > 1:
        return np.nan
    else:
        r = np.sqrt(d2)
        θ = np.arctan2(y, x)
        kr = jn_zeros(Nθ, 4)[Nr]
        return special.jn(Nθ, kr * r) * np.exp(1j * Nθ * θ)

p1 = plot(
    lambda x,y: np.abs(f(x,y)),
    xlim=[-1, 1], ylim=[-1, 1],
    bins=(100, 100),
    show_nan=False,
    axes=axes_opts,
)

# Unpack the 0-element (the surface of the plot) to customize it
msh = p1[0].lighting('glossy')

pts = msh.points   # get the points
zvals = pts[:,2]   # get the z values
θvals = [np.angle(f(*p[:2])) for p in pts]  # get the phases

lut = custom_lut_surface("hsv", vmin=-np.pi, vmax=np.pi)
msh.cmap(lut, θvals) # apply the color map

table, lut = custom_table_scalarbar("hsv")
line = Line((1,-1), (1,1))  # a dummy line to attach the scalarbar to
line.cmap("hsv", [0, 1])
scbar = ScalarBar3D(
    line,
    title=f"N_r ={Nr}, N_θ ={Nθ}, phase :theta in radians",
    label_rotation=90,
    categories=table,
    c='black',
)
# convert the scalarbar to a 2D object and place it to the bottom
scbar = scbar.clone2d([-0.6,-0.7], size=0.2, rotation=-90, ontop=True)

# Set a specific camera position and orientation (shift-C to see it)
cam = dict(
    position=(3.88583, 0.155949, 3.88584),
    focal_point=(0, 0, 0), viewup=(-0.7, 0, 0.7), distance=5.4,
)
show(p1, scbar, __doc__, camera=cam).close()
