"""This module contains general useful functions."""

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.mplot3d.art3d import Poly3DCollection


def vec2vec_rotmat(v, k):
    """Return a rotation matrix defining a rotation that aligns v with k.

    Parameters
    -----------
    v : numpy.ndarray
        1D array of length 3.
    k : numpy.ndarray
        1D array of length.

    Returns
    ---------
    R : numpy.ndarray
        3 by 3 rotation matrix.
    """
    v = v / np.linalg.norm(v)
    k = k / np.linalg.norm(k)
    axis = np.cross(v, k).astype(float)
    if np.linalg.norm(axis) < np.finfo(float).eps:
        if np.linalg.norm(v - k) > np.linalg.norm(v):
            return -np.eye(3)
        else:
            return np.eye(3)
    axis /= np.linalg.norm(axis)
    angle = np.arcsin(
        np.linalg.norm(np.cross(v, k)) / (np.linalg.norm(k) * np.linalg.norm(v))
    )
    if np.dot(v, k) < 0:
        angle = np.pi - angle
    K = np.array(
        [[0, -axis[2], axis[1]], [axis[2], 0, -axis[0]], [-axis[1], axis[0], 0]]
    )
    R = (
        np.eye(3) + np.sin(angle) * K + (1 - np.cos(angle)) * np.matmul(K, K)
    )  # Rodrigues' rotation formula
    return R


def show_traj(traj_file):
    """Visualize walker trajectories saved in a trajectories file.

    Parameters
    ----------
    traj_file : str
        Path to trajectories file that contains walker trajectories. Every line
        represents a time point. Every line contains the positions as follows:
        walker_1_x walker_1_y walker_1_z walker_2_x walker_2_y walker_2_z...
    """
    trajectories = np.loadtxt(traj_file)
    trajectories = trajectories.reshape(
        (trajectories.shape[0], int(trajectories.shape[1] / 3), 3)
    )
    fig = plt.figure()
    ax = fig.add_subplot(111, projection="3d")
    for i in range(trajectories.shape[1]):
        ax.plot(
            trajectories[:, i, 0],
            trajectories[:, i, 1],
            trajectories[:, i, 2],
            alpha=0.5,
        )
    ax.set_xlabel("x")
    ax.set_ylabel("y")
    ax.set_zlabel("z")
    ax.ticklabel_format(style="sci", scilimits=(0, 0))
    fig.tight_layout()
    plt.show()
    return


def show_mesh(substrate, alpha=0.5):
    """Visualize a triangular mesh with random triangle colours.

    Parameters
    ----------
    substrate : disimpy.substrates._Substrate
        Substrate object containing the triangular mesh.
    alpha : float
        Parameter controlling the triangle transparency.
    """
    np.random.seed(123)
    fig = plt.figure()
    ax = fig.add_subplot(111, projection="3d")
    for idx in substrate.faces:
        tri = Poly3DCollection(substrate.vertices[idx], alpha=alpha)
        face_color = np.random.random(3)
        tri.set_facecolor(face_color)
        ax.add_collection3d(tri)
    ax.set_xlim([0, substrate.voxel_size[0]])
    ax.set_ylim([0, substrate.voxel_size[1]])
    ax.set_zlim([0, substrate.voxel_size[2]])
    ax.set_xlabel("x")
    ax.set_ylabel("y")
    ax.set_zlabel("z")
    ax.ticklabel_format(style="sci", scilimits=(0, 0))
    fig.tight_layout()
    plt.show()
    return

"""This module contains general useful functions."""

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.mplot3d.art3d import Poly3DCollection


def vec2vec_rotmat(v, k):
    """Return a rotation matrix defining a rotation that aligns v with k.

    Parameters
    -----------
    v : numpy.ndarray
        1D array of length 3.
    k : numpy.ndarray
        1D array of length.

    Returns
    ---------
    R : numpy.ndarray
        3 by 3 rotation matrix.
    """
    v = v / np.linalg.norm(v)
    k = k / np.linalg.norm(k)
    axis = np.cross(v, k).astype(float)
    if np.linalg.norm(axis) < np.finfo(float).eps:
        if np.linalg.norm(v - k) > np.linalg.norm(v):
            return -np.eye(3)
        else:
            return np.eye(3)
    axis /= np.linalg.norm(axis)
    angle = np.arcsin(
        np.linalg.norm(np.cross(v, k)) / (np.linalg.norm(k) * np.linalg.norm(v))
    )
    if np.dot(v, k) < 0:
        angle = np.pi - angle
    K = np.array(
        [[0, -axis[2], axis[1]], [axis[2], 0, -axis[0]], [-axis[1], axis[0], 0]]
    )
    R = (
        np.eye(3) + np.sin(angle) * K + (1 - np.cos(angle)) * np.matmul(K, K)
    )  # Rodrigues' rotation formula
    return R


def show_traj(traj_file):
    """Visualize walker trajectories saved in a trajectories file.

    Parameters
    ----------
    traj_file : str
        Path to trajectories file that contains walker trajectories. Every line
        represents a time point. Every line contains the positions as follows:
        walker_1_x walker_1_y walker_1_z walker_2_x walker_2_y walker_2_z...
    """
    trajectories = np.loadtxt(traj_file)
    trajectories = trajectories.reshape(
        (trajectories.shape[0], int(trajectories.shape[1] / 3), 3)
    )
    fig = plt.figure()
    ax = fig.add_subplot(111, projection="3d")
    for i in range(trajectories.shape[1]):
        ax.plot(
            trajectories[:, i, 0],
            trajectories[:, i, 1],
            trajectories[:, i, 2],
            alpha=0.5,
        )
    ax.set_xlabel("x")
    ax.set_ylabel("y")
    ax.set_zlabel("z")
    ax.ticklabel_format(style="sci", scilimits=(0, 0))
    fig.tight_layout()
    plt.show()
    return


def show_mesh(substrate, alpha=0.5):
    """Visualize a triangular mesh with random triangle colours.

    Parameters
    ----------
    substrate : disimpy.substrates._Substrate
        Substrate object containing the triangular mesh.
    alpha : float
        Parameter controlling the triangle transparency.
    """
    np.random.seed(123)
    fig = plt.figure()
    ax = fig.add_subplot(111, projection="3d")
    for idx in substrate.faces:
        tri = Poly3DCollection(substrate.vertices[idx], alpha=alpha)
        face_color = np.random.random(3)
        tri.set_facecolor(face_color)
        ax.add_collection3d(tri)
    ax.set_xlim([0, substrate.voxel_size[0]])
    ax.set_ylim([0, substrate.voxel_size[1]])
    ax.set_zlim([0, substrate.voxel_size[2]])
    ax.set_xlabel("x")
    ax.set_ylabel("y")
    ax.set_zlabel("z")
    ax.ticklabel_format(style="sci", scilimits=(0, 0))
    fig.tight_layout()
    plt.show()
    return

This module contains general useful functions. Return a rotation matrix defining a rotation that aligns v with k.      Parameters     -----------     v : numpy.ndarray         1D array of length 3.     k : numpy.ndarray         1D array of length.      Returns     ---------     R : numpy.ndarray         3 by 3 rotation matrix. # Rodrigues' rotation formula Visualize walker trajectories saved in a trajectories file.      Parameters     ----------     traj_file : str         Path to trajectories file that contains walker trajectories. Every line         represents a time point. Every line contains the positions as follows:         walker_1_x walker_1_y walker_1_z walker_2_x walker_2_y walker_2_z... Visualize a triangular mesh with random triangle colours.      Parameters     ----------     substrate : disimpy.substrates._Substrate         Substrate object containing the triangular mesh.     alpha : float         Parameter controlling the triangle transparency.