// 2018.12.12
        public void getPaths1()
        {
            for (int i = 0; i < m_leaves.Count; i++)
            {
                Rays rays = new Rays();

                Vector3 s = new Vector3(ref m_source.m_position);

                int nodeIndex = m_leaves[i];

                // 收集多边形
                int order = 0;
                // 只有靠近发射源的面 nodeIndex == 0
                List <Vector3> pts = new List <Vector3>();
                while (nodeIndex != 0)  // m_polygonCache 中,第一个面是靠近目标的面,最后一个面是靠近发射源的面
                {
                    m_polygonCache[order++] = new Polygon(ref m_solutionNodes[nodeIndex].m_polygon);

                    // 得到 poly 的中心
                    Polygon poly = m_solutionNodes[nodeIndex].m_clipedPolygon;
                    float   cx = 0, cy = 0, cz = 0;
                    int     n = poly.m_points.Count;
                    for (int k = 0; k < n; k++)
                    {
                        cx += poly.m_points[k].x;
                        cy += poly.m_points[k].y;
                        cz += poly.m_points[k].z;
                    }
                    Vector3 pt = new Vector3(cx / n, cy / n, cz / n);
                    pts.Add(pt);

                    nodeIndex = m_solutionNodes[nodeIndex].m_parent;
                }

                bool    ok = true;
                Vector3 t;
                int     prePolyId = -1;
                for (int j = order - 1; j >= 0; j--)  // 从靠近源的面开始
                {
                    Polygon poly = m_polygonCache[j];

                    t = pts[j];

                    Ray ray = new Ray(ref s, ref t);
                    if (m_room.getKD().rayCastAny(ref ray, poly.m_id, prePolyId))  // 非法路径
                    {
                        ok = false;
                        break;
                    }

                    s         = t;
                    prePolyId = poly.m_id;

                    NodeInfo rayInfo;
                    if (j == order - 1)
                    {
                        rayInfo = Ray.createRay(ref ray);
                    }
                    else
                    {
                        Vector4 pleq = poly.getPleq();
                        rayInfo = Ray.createRay(ref ray, ref pleq);
                    }
                    rayInfo.buildingID = poly.m_buildingID;
                    rays.m_rays.Add(rayInfo);
                }

                if (ok)  // 整个路径段合法
                {
                    double rayAzimuth          = 0;
                    double rayIncination       = 0;
                    LTE.Geometric.Point startp = new Geometric.Point(rays.m_rays[0].PointOfIncidence.m_position.x, rays.m_rays[0].PointOfIncidence.m_position.y, rays.m_rays[0].PointOfIncidence.m_position.z);
                    LTE.Geometric.Point endp   = new Geometric.Point(rays.m_rays[0].CrossPoint.m_position.x, rays.m_rays[0].CrossPoint.m_position.y, rays.m_rays[0].CrossPoint.m_position.z);
                    LTE.Geometric.GeometricUtilities.getAzimuth_Inclination(startp, endp, out rayAzimuth, out rayIncination);
                    double[] ret = this.calcStrength.calcRayStrengthBeam(rayAzimuth, rayIncination, ref rays.m_rays);
                    rays.emitPwrDbm = this.calcStrength.convertw2dbm(ret[2]);
                    rays.recvPwrDbm = this.calcStrength.convertw2dbm(ret[0]);

                    m_rays.Add(rays);
                }
            }
        }
        // failPlane 也是输出结果
        public void validatePath(ref Vector3 source,
                                 ref Vector3 target,
                                 int nodeIndex,
                                 ref Vector4 failPlane)
        {
            // 收集多边形
            int order = 0;

            // 只有靠近发射源的面 nodeIndex == 0
            while (nodeIndex != 0)  // m_polygonCache 中,第一个面是靠近目标的面,最后一个面是靠近发射源的面
            {
                m_polygonCache[order++] = m_solutionNodes[nodeIndex].m_polygon;
                nodeIndex = m_solutionNodes[nodeIndex].m_parent;
            }

            // 重建虚拟源
            Vector3 imgSource = source;

            for (int i = order - 1; i >= 0; i--)  // 从发射源开始重建虚拟源
            {
                imgSource = Vector4.mirror(ref imgSource, m_polygonCache[i].getPleq());
            }

            // 失败面测试
            Vector3 s = imgSource;
            Vector3 t = target;

            bool    missed    = false;
            int     missOrder = -1;
            Polygon missPoly  = null;
            Ray     missRay   = new Ray(new Vector3(0, 0, 0), new Vector3(0, 0, 0));
            bool    missSide  = false;

            for (int i = 0; i < order; i++)  // 从靠近目标的面开始
            {
                Polygon poly = m_polygonCache[i];
                Vector4 pleq = poly.getPleq();
                Ray     ray  = new Ray(ref s, ref t);

                // 射线完全位于障碍物的一边,不可能发射反射
                if (Vector4.dot(ref s, ref pleq) * Vector4.dot(ref t, ref pleq) > 0)
                {
                    missed    = true;
                    missSide  = true;
                    missOrder = i;
                    missPoly  = poly;
                    missRay   = ray;
                    break;
                }

                // 射线没有与障碍物产生交点,不可能发生反射
                if (!ray.intersectExt(ref poly))
                {
                    missed    = true;
                    missSide  = false;
                    missOrder = i;
                    missPoly  = poly;
                    missRay   = ray;
                    break;
                }

                // 射线与障碍物的交点  2018.12.04
                Vector3  isect   = new Vector3();
                NodeInfo rayInfo = Ray.intersect(ref ray, ref pleq, out isect);
                rayInfo.buildingID = poly.m_buildingID;
                m_raysCache[i]     = rayInfo;

                s = Vector4.mirror(ref s, ref pleq);  // 新的虚拟源
                t = isect;

                m_validateCache[i * 2]     = isect;
                m_validateCache[i * 2 + 1] = s;
            }

            // 传播失败面
            if (missed)
            {
                Vector4 missPlane = new Vector4(0, 0, 0, 0);
                if (missSide)
                {
                    // 根据面方程重建
                    missPlane = missPoly.getPleq();
                    if (Vector4.dot(ref missRay.m_a, ref missPlane) > 0)
                    {
                        missPlane.opNegative();
                    }
                }
                else
                {
                    // 根据失败的 beam 边重建
                    Beam beam = new Beam(ref missRay.m_a, ref missPoly);
                    missPlane = beam.getPleq(1);
                    for (int i = 2; i < beam.numPleqs(); i++)
                    {
                        if (Vector4.dot(ref missRay.m_b, beam.getPleq(i)) < Vector4.dot(ref missRay.m_b, ref missPlane))
                        {
                            missPlane = beam.getPleq(i);
                        }
                    }
                }

                // 传播失败面
                for (int i = missOrder - 1; i >= 0; i--)  // 从当前面到接近目标的面
                {
                    missPlane = Vector3.mirror(ref missPlane, m_polygonCache[i].getPleq());
                }

                // 由于浮点精度,可能出错,重新计算
                if (Vector4.dot(ref target, ref missPlane) > 0)
                {
                    // 从失败面重建 beam
                    Beam beam = new Beam();
                    imgSource = source;
                    for (int i = order - 1; i >= 0; i--)
                    {
                        Polygon poly = m_polygonCache[i];
                        poly.clip(ref beam);

                        imgSource = Vector4.mirror(ref imgSource, poly.getPleq());
                        beam      = new Beam(ref imgSource, ref poly);
                    }

                    // 更新失败面
                    missPlane = getFailPlane(ref beam, ref target);
                }

                // 归一化
                missPlane.normalize();
                failPlane = missPlane;
                return;
            }

            // 检测路径是否合法
            t = target;
            for (int i = 0; i < order; i++)  // 从接收点开始检测
            {
                Vector3 isect = m_validateCache[i * 2];
                Ray     ray   = new Ray(ref isect, ref t);
                if (m_room.getKD().rayCastAny(ref ray))
                {
                    return;
                }

                t = isect;
            }
            Ray ray1 = new Ray(ref source, ref t);

            if (m_room.getKD().rayCastAny(ref ray1))  // 检测到发射源的路径是否合法
            {
                return;
            }

            // 将合法路径加入结果
            Path path = new Path();

            path.m_order  = order;
            path.m_points = new List <Vector3>(new Vector3[order + 2]);
            //path.m_polygons = new List<Polygon>(new Polygon[order]);

            // 2018.12.04
            Rays rays = new Rays(order);

            if (order > 0)
            {
                m_raysCache[order - 1].PointOfIncidence = new Point(source.x, source.y, source.z);
            }

            t = target;
            for (int i = 0; i < order; i++)
            {
                path.m_points[order - i + 1] = t;
                // path.m_polygons[order - i - 1] = m_polygonCache[i];

                t = m_validateCache[i * 2];
                rays.m_rays[order - i - 1] = m_raysCache[i];  // 2018.12.04
            }

            path.m_points[0] = source;
            path.m_points[1] = t;

            #region 将相似的路径移除---效果不明显
            //float fval = Vector3.dot(path.m_points[1], new Vector3(1, 1, 1));  //
            //float fmin = fval - 2 * EPS_SIMILAR_PATHS;
            //float fmax = fval + 2 * EPS_SIMILAR_PATHS;

            //foreach (List<int> paths in m_pathFirstSet.Values)
            //{
            //    for (int i = 0; i < paths.Count; i++)
            //    {
            //        Path p = m_paths[paths[i]];
            //        if (p.m_order != order)
            //            continue;
            //        bool safe = false;
            //        for (int k = 1; k < (int)p.m_points.Count - 1; k++)
            //        {
            //            if ((p.m_points[k] - path.m_points[k]).lengthSqr() > EPS_SIMILAR_PATHS * EPS_SIMILAR_PATHS)
            //            {
            //                safe = true;
            //                break;
            //            }
            //        }
            //        if (!safe)
            //            return;
            //    }
            //}

            //if (m_pathFirstSet.Keys.Contains(fval))
            //{
            //    m_pathFirstSet[fval].Add(m_paths.Count);
            //}
            //else
            //{
            //    m_pathFirstSet[fval] = new List<int>();
            //    m_pathFirstSet[fval].Add(m_paths.Count);
            //}
            #endregion

            m_paths.Add(path);

            // 2018.12.04  最后一段射线
            Vector3  pt0 = path.m_points[path.m_points.Count - 2];
            Vector3  pt1 = path.m_points[path.m_points.Count - 1];
            NodeInfo ray2;
            if (rays.m_rays.Count > 0)
            {
                ray2 = Ray.oneRay(pt0, pt1, RayType.VReflection);
            }
            else
            {
                ray2 = Ray.oneRay(pt0, pt1, RayType.Direction);
            }

            rays.m_rays.Add(ray2);
            m_rays.Add(rays);
        }
        // 2018.12.13
        public void getPaths()
        {
            for (int i = 0; i < m_leaves.Count; i++)
            {
                int nodeIndex = m_leaves[i];

                // 收集 beam 多边形
                int order = 0;
                List <List <Vector3> > pts = new List <List <Vector3> >(); // 每个多边形内的点
                int maxCnt = 0, minCnt = 1000000;
                // 只有靠近发射源的面 nodeIndex == 0
                while (nodeIndex != 0)  // m_polygonCache 中,第一个面是靠近目标的面,最后一个面是靠近发射源的面
                {
                    m_polygonCache[order++] = new Polygon(ref m_solutionNodes[nodeIndex].m_polygon);

                    List <Vector3> pt = m_solutionNodes[nodeIndex].m_polygon.getInerPoints(10, ref m_solutionNodes[nodeIndex].m_clipedPolygon.m_points);

                    if (pt.Count == 0)
                    {
                        // 得到 poly 的中心
                        Polygon poly = m_solutionNodes[nodeIndex].m_clipedPolygon;
                        float   cx = 0, cy = 0, cz = 0;
                        int     n = poly.m_points.Count;
                        for (int k = 0; k < n; k++)
                        {
                            cx += poly.m_points[k].x;
                            cy += poly.m_points[k].y;
                            cz += poly.m_points[k].z;
                        }
                        Vector3 tmp = new Vector3(cx / n, cy / n, cz / n);
                        pt.Add(tmp);
                    }

                    pts.Add(pt);
                    maxCnt = Math.Max(maxCnt, pt.Count);
                    minCnt = Math.Min(minCnt, pt.Count);

                    nodeIndex = m_solutionNodes[nodeIndex].m_parent;
                }

                int times = (maxCnt + minCnt) / 2;
                if (times == 0)
                {
                    times = 1;
                }

                int pre   = m_rays.Count;
                HashSet <String> pathExist = new HashSet <string>();

                #region 随机组合 times 条主路径
                for (int k = 0; k < times; k++)
                {
                    string route = "";

                    bool    ok = true;
                    Vector3 t;
                    int     prePolyId = -1;
                    Rays    rays      = new Rays();
                    Vector3 s         = new Vector3(ref m_source.m_position);
                    for (int j = order - 1; j >= 0; j--)  // 从靠近源的面开始
                    {
                        Polygon poly = m_polygonCache[j];

                        int id = rand.Next(0, pts[j].Count);
                        t      = pts[j][id];
                        route += string.Format("{0}", id) + ",";

                        Ray ray = new Ray(ref s, ref t);
                        if (m_room.getKD().rayCastAny(ref ray, poly.m_id, prePolyId))  // 非法路径
                        {
                            ok = false;
                            break;
                        }

                        s         = t;
                        prePolyId = poly.m_id;

                        NodeInfo rayInfo;
                        if (j == order - 1)
                        {
                            rayInfo = Ray.createRay(ref ray);
                        }
                        else
                        {
                            Vector4 pleq = poly.getPleq();
                            rayInfo = Ray.createRay(ref ray, ref pleq);
                        }
                        rayInfo.buildingID = poly.m_buildingID;

                        if (double.IsNaN(rayInfo.Angle) || double.IsInfinity(rayInfo.Angle))
                        {
                            ok = false;
                            break;
                        }

                        if (pathExist.Contains(route))  // 该路径已经存在过了
                        {
                            ok = false;
                        }
                        else
                        {
                            rays.m_rays.Add(rayInfo);
                        }
                    }

                    if (ok)  // 整个路径段合法
                    {
                        pathExist.Add(route);

                        double rayAzimuth          = 0;
                        double rayIncination       = 0;
                        LTE.Geometric.Point startp = new Geometric.Point(rays.m_rays[0].PointOfIncidence.m_position.x, rays.m_rays[0].PointOfIncidence.m_position.y, rays.m_rays[0].PointOfIncidence.m_position.z);
                        LTE.Geometric.Point endp   = new Geometric.Point(rays.m_rays[0].CrossPoint.m_position.x, rays.m_rays[0].CrossPoint.m_position.y, rays.m_rays[0].CrossPoint.m_position.z);
                        LTE.Geometric.GeometricUtilities.getAzimuth_Inclination(startp, endp, out rayAzimuth, out rayIncination);
                        double[] ret = this.calcStrength.calcRayStrengthBeam(rayAzimuth, rayIncination, ref rays.m_rays);
                        rays.emitPwrDbm = this.calcStrength.convertw2dbm(ret[2]);
                        rays.recvPwrDbm = this.calcStrength.convertw2dbm(ret[0]);

                        m_rays.Add(rays);
                        rayCount += rays.m_rays.Count;
                    }
                }
                #endregion

                #region 加入最短主路径  2018.12.20
                //if (m_rays.Count - pre > 0 && times > 1)
                //{
                //    List<Vector3> ss = new List<Vector3>();
                //    ss.Add(new Vector3(ref m_source.m_position));
                //    pts.Add(ss);
                //    string route = "";
                //    Vector3[] path = plane(ref pts, maxCnt, ref route);  // 多阶段图最短路径
                //    if (!pathExist.Contains(route))  // 该路径未被加入
                //    {
                //        bool ok = true;
                //        Vector3 t;
                //        int prePolyId = -1;
                //        Rays rays = new Rays();
                //        Vector3 s = path[order];
                //        for (int j = order - 1; j >= 0; j--)  // 从靠近源的面开始
                //        {
                //            Polygon poly = m_polygonCache[j];

                //            t = path[j];

                //            Ray ray = new Ray(ref s, ref t);
                //            if (m_room.getKD().rayCastAny(ref ray, poly.m_id, prePolyId))  // 非法路径
                //            {
                //                ok = false;
                //                break;
                //            }

                //            s = t;
                //            prePolyId = poly.m_id;

                //            NodeInfo rayInfo;
                //            if (j == order - 1)
                //            {
                //                rayInfo = Ray.createRay(ref ray);
                //            }
                //            else
                //            {
                //                Vector4 pleq = poly.getPleq();
                //                rayInfo = Ray.createRay(ref ray, ref pleq);
                //            }
                //            rayInfo.buildingID = poly.m_buildingID;

                //            if (double.IsNaN(rayInfo.Angle) || double.IsInfinity(rayInfo.Angle))
                //            {
                //                ok = false;
                //                break;
                //            }

                //            rays.m_rays.Add(rayInfo);
                //        }

                //        if (ok)  // 整个路径段合法
                //        {
                //            double rayAzimuth = 0;
                //            double rayIncination = 0;
                //            LTE.Geometric.Point startp = new Geometric.Point(rays.m_rays[0].PointOfIncidence.m_position.x, rays.m_rays[0].PointOfIncidence.m_position.y, rays.m_rays[0].PointOfIncidence.m_position.z);
                //            LTE.Geometric.Point endp = new Geometric.Point(rays.m_rays[0].CrossPoint.m_position.x, rays.m_rays[0].CrossPoint.m_position.y, rays.m_rays[0].CrossPoint.m_position.z);
                //            LTE.Geometric.GeometricUtilities.getAzimuth_Inclination(startp, endp, out rayAzimuth, out rayIncination);
                //            double[] ret = this.calcStrength.calcRayStrengthBeam(rayAzimuth, rayIncination, ref rays.m_rays);
                //            rays.emitPwrDbm = this.calcStrength.convertw2dbm(ret[2]);
                //            rays.recvPwrDbm = this.calcStrength.convertw2dbm(ret[0]);

                //            m_rays.Add(rays);
                //        }
                //    }
                //}
                #endregion
            }
        }