public static Vector4 FindTwoTetrahedronsSplittingPlane(Tetrahedron first, Tetrahedron second)
        {
            List<Vector3> potentialSplitPlanes = new List<Vector3>();

            potentialSplitPlanes.Add(new Vector3(first.m_TetPlanes[0].X, first.m_TetPlanes[0].Y, first.m_TetPlanes[0].Z));
            potentialSplitPlanes.Add(new Vector3(first.m_TetPlanes[1].X, first.m_TetPlanes[1].Y, first.m_TetPlanes[1].Z));
            potentialSplitPlanes.Add(new Vector3(first.m_TetPlanes[2].X, first.m_TetPlanes[2].Y, first.m_TetPlanes[2].Z));
            potentialSplitPlanes.Add(new Vector3(first.m_TetPlanes[3].X, first.m_TetPlanes[3].Y, first.m_TetPlanes[3].Z));

            potentialSplitPlanes.Add(new Vector3(second.m_TetPlanes[0].X, second.m_TetPlanes[0].Y, second.m_TetPlanes[0].Z));
            potentialSplitPlanes.Add(new Vector3(second.m_TetPlanes[1].X, second.m_TetPlanes[1].Y, second.m_TetPlanes[1].Z));
            potentialSplitPlanes.Add(new Vector3(second.m_TetPlanes[2].X, second.m_TetPlanes[2].Y, second.m_TetPlanes[2].Z));
            potentialSplitPlanes.Add(new Vector3(second.m_TetPlanes[3].X, second.m_TetPlanes[3].Y, second.m_TetPlanes[3].Z));

            Vector3[] tetEdgeVectorsFirst = new Vector3[6];
            Vector3[] tetEdgeVectorsSecond = new Vector3[6];

            tetEdgeVectorsFirst[0] = first.m_Vertices[1] - first.m_Vertices[0];
            tetEdgeVectorsFirst[1] = first.m_Vertices[2] - first.m_Vertices[0];
            tetEdgeVectorsFirst[2] = first.m_Vertices[3] - first.m_Vertices[0];

            tetEdgeVectorsFirst[3] = first.m_Vertices[2] - first.m_Vertices[1];
            tetEdgeVectorsFirst[4] = first.m_Vertices[3] - first.m_Vertices[2];
            tetEdgeVectorsFirst[5] = first.m_Vertices[1] - first.m_Vertices[3];

            tetEdgeVectorsSecond[0] = second.m_Vertices[1] - second.m_Vertices[0];
            tetEdgeVectorsSecond[1] = second.m_Vertices[2] - second.m_Vertices[0];
            tetEdgeVectorsSecond[2] = second.m_Vertices[3] - second.m_Vertices[0];

            tetEdgeVectorsSecond[3] = second.m_Vertices[2] - second.m_Vertices[1];
            tetEdgeVectorsSecond[4] = second.m_Vertices[3] - second.m_Vertices[2];
            tetEdgeVectorsSecond[5] = second.m_Vertices[1] - second.m_Vertices[3];

            foreach (var tetEdgeFirst in tetEdgeVectorsFirst)
            {
                foreach (var tetEdgeSecond in tetEdgeVectorsSecond)
                {
                    // Normal of plane constructed by those 2 edges
                    var planeNormal = Vector3.Cross(tetEdgeFirst, tetEdgeSecond);

                    if (planeNormal.LengthSquared() > 0.001f)
                    {
                        planeNormal.Normalize();
                        potentialSplitPlanes.Add(planeNormal);
                    }
                }
            }

            const float epsilon = 0.001f;// fixed 1mm

            foreach (var splittingPlaneNormal in potentialSplitPlanes)
            {
                Vector2 tetOneLimits = GeometricsAlgorithms.GetAxisProjectionLimits(first.m_Vertices, splittingPlaneNormal);
                Vector2 tetTwoLimits = GeometricsAlgorithms.GetAxisProjectionLimits(second.m_Vertices, splittingPlaneNormal);

                if ((tetOneLimits.X + epsilon) > tetTwoLimits.Y)
                {
                    return new Vector4(splittingPlaneNormal, -(tetOneLimits.X + tetTwoLimits.Y) / 2.0f);
                }
                if (tetOneLimits.Y < (tetTwoLimits.X + epsilon))
                {
                    return new Vector4(splittingPlaneNormal, -(tetOneLimits.Y + tetTwoLimits.X) / 2.0f);
                }
            }

            throw new Exception("no splitting plane found");
        }
        public static Vector4 FindTwoTetrahedronsSplittingPlane(Tetrahedron first, Tetrahedron second)
        {
            List <Vector3> potentialSplitPlanes = new List <Vector3>();

            potentialSplitPlanes.Add(new Vector3(first.m_TetPlanes[0].X, first.m_TetPlanes[0].Y, first.m_TetPlanes[0].Z));
            potentialSplitPlanes.Add(new Vector3(first.m_TetPlanes[1].X, first.m_TetPlanes[1].Y, first.m_TetPlanes[1].Z));
            potentialSplitPlanes.Add(new Vector3(first.m_TetPlanes[2].X, first.m_TetPlanes[2].Y, first.m_TetPlanes[2].Z));
            potentialSplitPlanes.Add(new Vector3(first.m_TetPlanes[3].X, first.m_TetPlanes[3].Y, first.m_TetPlanes[3].Z));

            potentialSplitPlanes.Add(new Vector3(second.m_TetPlanes[0].X, second.m_TetPlanes[0].Y, second.m_TetPlanes[0].Z));
            potentialSplitPlanes.Add(new Vector3(second.m_TetPlanes[1].X, second.m_TetPlanes[1].Y, second.m_TetPlanes[1].Z));
            potentialSplitPlanes.Add(new Vector3(second.m_TetPlanes[2].X, second.m_TetPlanes[2].Y, second.m_TetPlanes[2].Z));
            potentialSplitPlanes.Add(new Vector3(second.m_TetPlanes[3].X, second.m_TetPlanes[3].Y, second.m_TetPlanes[3].Z));

            Vector3[] tetEdgeVectorsFirst  = new Vector3[6];
            Vector3[] tetEdgeVectorsSecond = new Vector3[6];

            tetEdgeVectorsFirst[0] = first.m_Vertices[1] - first.m_Vertices[0];
            tetEdgeVectorsFirst[1] = first.m_Vertices[2] - first.m_Vertices[0];
            tetEdgeVectorsFirst[2] = first.m_Vertices[3] - first.m_Vertices[0];

            tetEdgeVectorsFirst[3] = first.m_Vertices[2] - first.m_Vertices[1];
            tetEdgeVectorsFirst[4] = first.m_Vertices[3] - first.m_Vertices[2];
            tetEdgeVectorsFirst[5] = first.m_Vertices[1] - first.m_Vertices[3];

            tetEdgeVectorsSecond[0] = second.m_Vertices[1] - second.m_Vertices[0];
            tetEdgeVectorsSecond[1] = second.m_Vertices[2] - second.m_Vertices[0];
            tetEdgeVectorsSecond[2] = second.m_Vertices[3] - second.m_Vertices[0];

            tetEdgeVectorsSecond[3] = second.m_Vertices[2] - second.m_Vertices[1];
            tetEdgeVectorsSecond[4] = second.m_Vertices[3] - second.m_Vertices[2];
            tetEdgeVectorsSecond[5] = second.m_Vertices[1] - second.m_Vertices[3];

            foreach (var tetEdgeFirst in tetEdgeVectorsFirst)
            {
                foreach (var tetEdgeSecond in tetEdgeVectorsSecond)
                {
                    // Normal of plane constructed by those 2 edges
                    var planeNormal = Vector3.Cross(tetEdgeFirst, tetEdgeSecond);

                    if (planeNormal.LengthSquared() > 0.001f)
                    {
                        planeNormal.Normalize();
                        potentialSplitPlanes.Add(planeNormal);
                    }
                }
            }

            const float epsilon = 0.001f;// fixed 1mm

            foreach (var splittingPlaneNormal in potentialSplitPlanes)
            {
                Vector2 tetOneLimits = GeometricsAlgorithms.GetAxisProjectionLimits(first.m_Vertices, splittingPlaneNormal);
                Vector2 tetTwoLimits = GeometricsAlgorithms.GetAxisProjectionLimits(second.m_Vertices, splittingPlaneNormal);

                if ((tetOneLimits.X + epsilon) > tetTwoLimits.Y)
                {
                    return(new Vector4(splittingPlaneNormal, -(tetOneLimits.X + tetTwoLimits.Y) / 2.0f));
                }
                if (tetOneLimits.Y < (tetTwoLimits.X + epsilon))
                {
                    return(new Vector4(splittingPlaneNormal, -(tetOneLimits.Y + tetTwoLimits.X) / 2.0f));
                }
            }

            throw new Exception("no splitting plane found");
        }