# -*- coding: utf-8 -*-
"""
Basic geometry functions
"""

# pylint: disable=C0103

from numpy import (matrix, array, arccos, sign, cos, sin, linalg, vdot,
                   pi, sqrt, fabs, arctan)


#===============================================================================
# Transform matrix functions
#===============================================================================

def translate(tx, ty):
    """Return translation matrix (NumPy matrix object)"""
    return matrix([[1, 0, tx],
                   [0, 1, ty],
                   [0, 0, 1 ]], float)

def scale(sx, sy):
    """Return scale matrix (NumPy matrix object)"""
    return matrix([[sx, 0,  0],
                   [0,  sy, 0],
                   [0,  0,  1]], float)

def rotate(alpha):
    """Return rotation matrix (NumPy matrix object)"""
    return matrix([[cos(alpha), -sin(alpha), 0],
                   [sin(alpha),  cos(alpha), 0],
                   [0,           0,          1]], float)

def colvector(x, y):
    """Return vector (NumPy matrix object) from coordinates"""
    return matrix([x, y, 1]).T


#===============================================================================
# Operations on vectors (from coordinates)
#===============================================================================

def vector_norm(xa, ya, xb, yb):
    """Return vector norm: (xa, xb)-->(ya, yb)"""
    return linalg.norm(array((xb-xa, yb-ya)))

def vector_projection(dv, xa, ya, xb, yb):
    """Return vector projection on *dv*: (xa, xb)-->(ya, yb)"""
    assert dv.shape == (2,)
    v_ab = array((xb-xa, yb-ya))
    u_ab = v_ab/linalg.norm(v_ab)
    return vdot(u_ab, dv)*u_ab+array((xb, yb))

def vector_rotation(theta, dx, dy):
    """Compute theta-rotation on vector *v*, returns vector coordinates"""
    return array( rotate(theta)*colvector(dx, dy) ).ravel()[:2]

def vector_angle(dx, dy):
    """Return vector angle with X-axis"""
    # sign(dy) ==  1 --> return Arccos()
    # sign(dy) ==  0 --> return  0 if sign(dx) ==  1
    # sign(dy) ==  0 --> return pi if sign(dx) == -1
    # sign(dy) == -1 --> return 2pi-Arccos()
    if dx == 0 and dy == 0:
        return 0.
    else:
        sx, sy = sign(dx), sign(dy)
        acos = arccos(dx/sqrt(dx**2+dy**2))
        return sy*(pi*(sy-1)+acos)+pi*(1-sy**2)*(1-sx)*.5


#===============================================================================
# Misc.
#===============================================================================

def compute_center(x1, y1, x2, y2):
    return .5*(x1+x2), .5*(y1+y2)
    
def compute_rect_size(x1, y1, x2, y2):
    return x2-x1, fabs(y2-y1)

def compute_distance(x1, y1, x2, y2):
    return sqrt((x2-x1)**2+(y2-y1)**2)
    
def compute_angle(x1, y1, x2, y2, reverse=False):
    sign = -1 if reverse else 1
    if x2 == x1:
        return 0.
    else:
        return arctan(-sign*(y2-y1)/(x2-x1))*180/pi
