/********************************************************
 *  ██████╗  ██████╗████████╗██╗
 * ██╔════╝ ██╔════╝╚══██╔══╝██║
 * ██║  ███╗██║        ██║   ██║
 * ██║   ██║██║        ██║   ██║
 * ╚██████╔╝╚██████╗   ██║   ███████╗
 *  ╚═════╝  ╚═════╝   ╚═╝   ╚══════╝
 * Geophysical Computational Tools & Library (GCTL)
 *
 * Copyright (c) 2023  Yi Zhang (yizhang-geo@zju.edu.cn)
 *
 * GCTL is distributed under a dual licensing scheme. You can redistribute 
 * it and/or modify it under the terms of the GNU Lesser General Public 
 * License as published by the Free Software Foundation, either version 2 
 * of the License, or (at your option) any later version. You should have 
 * received a copy of the GNU Lesser General Public License along with this 
 * program. If not, see <http://www.gnu.org/licenses/>.
 * 
 * If the terms and conditions of the LGPL v.2. would prevent you from using 
 * the GCTL, please consider the option to obtain a commercial license for a 
 * fee. These licenses are offered by the GCTL's original author. As a rule, 
 * licenses are provided "as-is", unlimited in time for a one time fee. Please 
 * send corresponding requests to: yizhang-geo@zju.edu.cn. Please do not forget 
 * to include some description of your company and the realm of its activities. 
 * Also add information on how to contact you by electronic and paper mail.
 ******************************************************/

#ifndef _GCTL_GEOMETRY3D_H
#define _GCTL_GEOMETRY3D_H

#include "../poly/triangle.h"
#include "../poly/tetrahedron.h"
#include "gmath.h"

namespace gctl
{
	namespace geometry3d
	{
		/**
		 * @brief      两个矢量以坐标原点为顶点的夹角（弧度）
		 *
		 * @param[in]  a     三维空间内的一个向量或实点a。
		 * @param[in]  b     三维空间内的一个向量或实点b。
		 *
		 * @return     两个矢量之间的夹角。
		 */
		double angle(const point3dc &a, const point3dc &b);

		/**
		 * @brief 计算三角形的面积
		 * 
		 * @param a 三维空间内的一个实点a
		 * @param b 三维空间内的一个实点b
		 * @param c 三维空间内的一个实点c
		 * @return double 面积
		 */
		double triangle_area(const point3dc &a, const point3dc &b, const point3dc &c);

		/**
		 * @brief 计算四面体的体积
		 * 
		 * @param a 三维空间内的一个实点a
		 * @param b 三维空间内的一个实点b
		 * @param c 三维空间内的一个实点c
		 * @param d 三维空间内的一个实点d
		 * @return double 体积
		 */
		double tetrahedron_volume(const point3dc &a, const point3dc &b, const point3dc &c, const point3dc &d);

		/**
		 * @brief      点到线段的距离
		 *
		 * @param[in]  line_start  线段的起点
		 * @param[in]  line_end    线段的终点
		 * @param[in]  dot         线段外的一个点
		 *
		 * @return     点到线段的距离
		 */
		double dot2line(const point3dc &line_start, const point3dc &line_end, const point3dc &dot);
		/**
		 * @brief      点在直线上的投影
		 *
		 * @param[in]  line_start  线段的起点
		 * @param[in]  line_end    线段的终点
		 * @param[in]  dot         线段外的一个点
		 *
		 * @return     点在直线上的投影
		 */
		point3dc dot_on_line(const point3dc &line_start, const point3dc &line_end, const point3dc &dot);
		/**
		 * @brief       点到平面的距离。
		 *
		 * @param[in]  c     平面上的一个点
		 * @param[in]  n     平面的单位外法线矢量
		 * @param[in]  d     平面外的一点
		 *
		 * @return     平面外的点到平面的距离
		 */
		double dot2plane(const point3dc &c, const point3dc &n, const point3dc &d);
		/**
		 * @brief Calculate the coefficients of a 3D plane using coordinates of three points on the plane. 
		 * The plane's equation is given as Ax + By + Cz + D = 0
		 * 
		 * @param x1  x coordinate of the first point
		 * @param x2  x coordinate of the second point
		 * @param x3  x coordinate of the third point
		 * @param y1  y coordinate of the first point
		 * @param y2  y coordinate of the second point
		 * @param y3  y coordinate of the third point
		 * @param z1  z coordinate of the first point
		 * @param z2  z coordinate of the second point
		 * @param z3  z coordinate of the third point
		 * @param A   coefficient A
		 * @param B   coefficient B
		 * @param C   coefficient C
		 * @param D   coefficient D
		 */
		void get_plane_coeff(double x1, double x2, double x3, double y1, double y2, double y3, 
			double z1, double z2, double z3, double &A, double &B, double &C, double &D);

		/**
		 * @brief Calculate the coefficients of a 3D plane defined by two points on a sphercial surface and the origin. 
		 * The plane's equation is given as Ax + By + Cz + D = 0
		 * 
		 * @param p1 point 1 on the spherical surface.
		 * @param p2 point 2 on the spherical surface.
		 * @param A  coefficient A
		 * @param B  coefficient B
		 * @param C  coefficient C
		 * @param D  coefficient D
		 */
		void get_plane_coeff(const point3ds &p1, const point3ds &p2, double &A, double &B, double &C, double &D);
		
		/**
		 * @brief      计算直线与平面的交点。
		 *
		 * @param[in]  face_p  平面上的一个点。
		 * @param[in]  face_nor  平面的单位外法线矢量。
		 * @param[in]  line_p  直线上的一个点。
		 * @param[in]  line_nor  直线的单位方向矢量。
		 *
		 * @return     返回的矢量。
		 */
		point3dc line_on_plane(const point3dc &face_p, const point3dc &face_nor, 
			const point3dc &line_p, const point3dc &line_nor);
		/**
		 * @brief      计算球面上两点之间大圆弧（劣弧）上的任意一点，给定计算点与起点的夹角可以获得其坐标。
		 *
		 * @param[in]  v1    起点
		 * @param[in]  v2    终点
		 * @param[in]  arc   计算点与起点之间的夹角(弧度)
		 *
		 * @return     返回计算点坐标
		 */
		point3ds track_sphere_arc(const point3ds &v1, const point3ds &v2, double arc);
		/**
		 * @brief      点在平面上的投影
		 *
		 * @param[in]  face_dot  平面上一点
		 * @param[in]  face_nor  平面的法向量
		 * @param[in]  dot       平面外一点
		 *
		 * @return     平面外一点在平面上的投影点
		 */
		point3dc dot_on_plane(const point3dc &face_dot, const point3dc &face_nor, const point3dc &dot);
		/**
		 * @brief      距离平方反比形式的三角形内插值函数
		 *
		 * @param[in]  p     待插值点坐标
		 * @param[in]  p1    三角形顶点坐标1
		 * @param[in]  p2    三角形顶点坐标2
		 * @param[in]  p3    三角形顶点坐标3
		 * @param[in]  d1    顶点1的值
		 * @param[in]  d2    顶点2的值
		 * @param[in]  d3    顶点3的值
		 *
		 * @return     插值点的值
		 */
		double tri_interp_dis(const point3dc &p, const point3dc &p1, const point3dc &p2, 
			const point3dc &p3, double d1, double d2, double d3);
		/**
		 * @brief      球心角平方反比形式的三角形内插值函数
		 *
		 * @param[in]  p     待插值点坐标
		 * @param[in]  p1    三角形顶点坐标1
		 * @param[in]  p2    三角形顶点坐标2
		 * @param[in]  p3    三角形顶点坐标3
		 * @param[in]  d1    顶点1的值
		 * @param[in]  d2    顶点2的值
		 * @param[in]  d3    顶点3的值
		 *
		 * @return     插值点的值
		 */
		double tri_interp_ang(const point3dc &p, const point3dc &p1, const point3dc &p2, 
			const point3dc &p3, double d1, double d2, double d3);
		/**
		 * @brief      细分三角形面积比形式的三角形内插值函数
		 *
		 * @param[in]  p     待插值点坐标
		 * @param[in]  p1    三角形顶点坐标1
		 * @param[in]  p2    三角形顶点坐标2
		 * @param[in]  p3    三角形顶点坐标3
		 * @param[in]  d1    顶点1的值
		 * @param[in]  d2    顶点2的值
		 * @param[in]  d3    顶点3的值
		 *
		 * @return     插值点的值
		 */
		double tri_interp_area(const point3dc &p, const point3dc &p1, const point3dc &p2, 
			const point3dc &p3, double d1, double d2, double d3);
		/**
		 * @brief      距离平方反比形式的三角形内插值函数
		 *
		 * @param[in]  p     待插值点坐标
		 * @param[in]  p1    三角形顶点坐标1
		 * @param[in]  p2    三角形顶点坐标2
		 * @param[in]  p3    三角形顶点坐标3
		 * @param[in]  p4    三角形顶点坐标4
		 * @param[in]  d1    顶点1的值
		 * @param[in]  d2    顶点2的值
		 * @param[in]  d3    顶点3的值
		 * @param[in]  d4    顶点4的值
		 *
		 * @return     插值点的值
		 */
		double tet_interp_dis(const point3dc &p, const point3dc &p1, const point3dc &p2, 
			const point3dc &p3, const point3dc &p4, double d1, double d2, double d3, double d4);
		/**
		 * @brief      球坐标转直角坐标
		 *
		 * @param[in]  sp_ptr  球坐标点的指针
		 * @param      cp_ptr  直角坐标点的指针
		 *
		 * @return     执行是否成功
		 */
		int sph2car(const point3ds *sp_ptr, point3dc *cp_ptr);
		/**
		 * @brief      直角坐标转球坐标
		 *
		 * @param[in]  cp_ptr  直角坐标点的指针
		 * @param      sp_ptr  球坐标点的指针
		 *
		 * @return     执行是否成功
		 */
		int car2sph(const point3dc *cp_ptr, point3ds *sp_ptr);
		/**
		 * @brief     椭球坐标转直角坐标
		 * 
		 * @param lon    经度
		 * @param lat    维度
		 * @param r      短半径
		 * @param R      长半径
		 * @return point3dc  直角坐标系点   
		 */
		point3dc ellip2car(double lon, double lat, double r, double R);

		/**
		 * @brief      判断射线是否过三角形 在order_triangular_surface中调用
		 *
		 * @param[in]  origin        射线的起点
		 * @param[in]  direct        射线的方向
		 * @param[in]  tri_facet     待检测的三角形
		 * @param[in]  active_edge   长度为3的数组。表示对应三角形的边是否为激活的状态（激活表示如果射线与此边相交也算与三角形相交）
		 * @param[in]  cutoff_limit  计算叉乘的零值精度，小于此值即认为是0
		 * @param[in]  cross_loc     返回交点的位置
		 *
		 * @return     是否在三角形内
		 */
		bool crossed_tri_facet(const point3dc &origin, const point3dc &direct, const triangle &tri_facet, 
			const bool *active_edge, double cutoff_limit = 1e-10, point3dc *cross_loc = nullptr);

		/**
		 * @brief     监测点t1与t2是否在点p1，p2与p3构成的平面的同一侧
		 * 
		 * @param p1  平面上一点
		 * @param p2  平面上一点
		 * @param p3  平面上一点
		 * @param t1  待检测点
		 * @param t2  待检测点
		 * @return true  在同一侧
		 * @return false 不在同一侧
		 */
		bool same_side(const point3dc &p1, const point3dc &p2, const point3dc &p3, const point3dc &t1, const point3dc &t2);
		/**
		 * @brief      判断一个点是否在四面体内
		 * 
		 * 注意点落在四面体的顶点，边或者面上也算是在四面体内
		 *
		 * @param      test_p  待检测点
		 * @param      p1      第一个点
		 * @param      p2      第二个点
		 * @param      p3      第三个点
		 * @param      p4      第四个点
		 *
		 * @return     是否在四面体内
		 */
		bool node_in_tetrahedron(const point3dc &p1, const point3dc &p2, 
			const point3dc &p3, const point3dc &p4, const point3dc &test_p);
		/**
		 * @brief      对多面体表面的三角形的顶点进行排序，保证所有三角形顶点都是逆时针排序的
		 *
		 * @param      poly_tri_ptr   Pointer of the triangle array
		 *
		 * @return     status of the function
		 */
		void order_triangular_surface(array<triangle> &poly_tri, double cutoff_limit = 1e-10);
		/**
		 * @brief      获取四面体网络中的公共面及公共面两侧的四面体指针
		 *
		 * @param[in]  ele            四面体网络的元素数组
		 * @param      out_face       返回的公共三角形列表数组
		 * @param      out_neigh_ptr  返回的公共三角形两侧邻接四面体的指针数组的指针，
		 * 若指针不为空则返回邻接四面体指针数组。否则不计算此数组。
		 */
		void get_tetra_mesh_common_faces(const array<tetrahedron> &ele, array<triangle> &out_face, 
			array<tetrahedron*> *out_neigh_ptr = nullptr);
		/**
		 * @brief      计算四面体网络的顶点数量
		 * 
		 */
		size_t sort_node_number(const array<tetrahedron> &ele);
		/**
		 * @brief      计算四面体网络中与顶点相连的四面体的指针列表
		 *
		 * @param[in]  ele         四面体数组
		 * @param      neigh_list  返回的四面体指针列表
		 * @param      node_nump   四面体数组内顶点个数的指针
		 */
		void sort_node_neighbor(const array<tetrahedron> &ele, std::vector<std::vector<tetrahedron*> > &neigh_list, size_t *node_nump = nullptr);
		/**
		 * @brief      计算四面体网络内的各个四面体之间的相邻关系
		 *
		 * @param     ele    四面体数组
		 * @param      node_nump   四面体数组内顶点个数的指针
		 */
		void sort_tet_neighbor(array<tetrahedron> &ele, size_t *node_nump = nullptr);

		/**
		 * @brief      使用平面切割三角网络
		 * 
		 * @note       平面法线正方向的三角网络将被保留
		 *
		 * @param[in]  in_eles    输入的三维三角形数组（一般为成片的三角网）
		 * @param[in]  nor        平面的法线方向
		 * @param[in]  surf       平面上的一点
		 * @param      out_nodes  切割后的新网络顶点集
		 * @param      out_eles   切割后的新网络三角形元素集
		 */
		void cut_triangular_mesh(const array<triangle> &in_eles, const point3dc &nor, const point3dc &surf, 
			array<vertex3dc> &out_nodes, array<triangle> &out_eles);
	}
};

#endif // _GCTL_GEOMETRY3D_H