#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
uGao pacakge
Calculator for the DetectorViewer

---- Original version
Emaki package
Emaki calculator
Author: ITO, Takayoshi (CROSS)
Last update: Feb. 4th, 2013
"""
__author__ = "ITO, Takayoshi (CROSS)"
__version__ = "19.03.10b"
__date__ = "25th Apr. 2019"

import numpy as np

########################################
def PutGauss( args ):
    A = args[0]
    cx = args[1]
    w = args[2]
    x = args[3]
    return A*np.exp(-(x - cx)**2/(2.0*w**2))

########################################
def PutResultOfUserDefinedFormula( args ):
    x, y, z = args[0]
    formula = args[1]
    try:
        return eval(formula)
    except NameError:
        return eval('np.'+formula)

########################################
def PutTwoTheta(coordinate):
    """
    実験室座標系の位置座標から散乱角を返す。
    ビーム軸はz軸。
    @param coordinate (list) 実験室座標
    @retval (float) 散乱角。単位は度。
    """
    return np.degrees(np.arccos(coordinate[2]/np.sqrt(np.sum(coordinate**2))))

########################################
def RotateWXYZ(params, th=None, v=None):
    """
    pをv軸回転で、th度回転。
    @param p (list) 座標を表すlist
    @param th (float) 回転角度
    @param v (list) 回転軸を表すlist
    @retval (numpy.array) 回転した結果を表すnumpy.array
    """
    if th is None and v is None:
        p, th, v = params[:]
    else:
        p = params[:]
    p_np = np.array(p)
    v_np = np.array(v)
    a_np = v_np/np.sqrt(np.sum((v_np**2))) # a_np: vの単位ベクトル。
    th_rad = np.radians(th)
    return np.cos(th_rad)*p_np + (1-np.cos(th_rad))*np.dot(p_np, a_np)*a_np+np.sin(th_rad)*np.cross(a_np, p_np)

########################################
def PutAngleBetweenTwoVectors( v0, v1):
    """
    二つのベクトルがなす角を返す．単位は度．
    @param v0 (numpy.array) 角度の基準となるベクトル。
    @param v1 (numpy.array) 調べたいベクトル。
    @retval (float) 角度。単位は度。
    """
    val = np.dot( v0, v1)/ (np.linalg.norm(v0)* np.linalg.norm(v1))
    ## 浮動小数点につきまとう誤差のために絶対値が1.0を越える場合がある．
    ## 小数点8桁までとると、0.000015 rad以内の精度になるので1.0との差が1.0e-10よりも小さい場合は1.0に丸める．
    if np.abs(val) > 1.0:
        if (np.abs(val) - 1.0) < 1.0e-10:
            val = np.rint(val)
        else:
            raise UserWarning('Cannot calculate the angle.')
    phi = np.arccos( val )
    return np.degrees(phi)

########################################
def PolarToCartesian( r, th, phi):
    """
    極座標を直交座標にして返す。単位は度。
    @param r (float) 動径
    @param th (float) 極角
    @param phi (float) 方位角
    @retval (float, float, float) 極座標
    """
    x = r*np.sin(np.radians(th))*np.cos(np.radians(phi))
    y = r*np.sin(np.radians(th))*np.sin(np.radians(phi))
    z = r*np.cos(np.radians(th))
    return x, y, z

########################################
def CartesianToPolar( x, y, z):
    """
    直交座標を極座標にして返す。単位は度。
    @param x (float) X座標
    @param y (float) Y座標
    @param z (float) Z座標
    @retval (float, float, float) 直交座標
    """
    r = np.sqrt(x**2+y**2+z**2)
    th = np.degrees(np.arccos(z/r))
    phi = np.degrees(((1, -1)[int(np.signbit(y))])*np.arccos(x/np.sqrt(x**2+y**2)))
    return r, th, phi

