Exemplo n.º 1
0
        /// <summary>
        /// Расчитать полигоны для построения сети
        /// </summary>
        public void CalculatePoligons()
        {
            //Определить внутренние вершины в этом треугольнике
            foreach (LinkedListNode <GraphNode> lln in vertexNodes)
            {
                VertexGraphNode vgn = (VertexGraphNode)lln.Value;
                vgn.IsInnerVertex = polylineNesting.InnerVerts.Contains(verts[vgn.VertNum]);
            }


            List <List <Point3d> > holes = new List <List <Point3d> >();

            //Добавление оставшихся узлов и ребер в граф
            foreach (PolylinePart pp in PolylineParts)
            {
                PolylinePt check = pp.PolylinePts.First();
                if (check.TinSurfaceEdge != null || check.TinSurfaceVertex != null) //Эта полилиния пересекает треугольник?
                {
                    PolylinePt[] nodePts = new PolylinePt[]                         //Точки присоединения полилинии к границам треугольника
                    {
                        pp.PolylinePts.First(),
                                 pp.PolylinePts.Last()
                    };
                    for (short i = 0; i < 2; i++)
                    {
                        PolylinePt pt        = nodePts[i];
                        GraphNode  graphNode = null;
                        if (pt.TinSurfaceVertex != null)
                        {
                            //Определить номер этой вершины в этом треугольнике и получить ссылку на узел графа
                            short vertNum = GetVertNum(pt.TinSurfaceVertex);
                            if (vertNum == -1)
                            {
                                throw new Exception();
                            }
                            graphNode = vertexNodes[vertNum].Value;
                        }
                        else
                        {
                            //Определить номер этого ребра, добавить узел этого ребра
                            short edgeNum = GetEdgeNum(pt.TinSurfaceEdge);
                            if (edgeNum == -1)
                            {
                                throw new Exception();
                            }
                            graphNode = new EdgeGraphNode(this, edgeNum, pt.Point2D);
                        }

                        //дополнить свойства graphNode указателями на участок полилинии
                        graphNode.PolylinePart = pp;
                        graphNode.PolylinePartConnectedByStart = i == 0;
                        if (i == 0)
                        {
                            pp.StartNode = graphNode;
                        }
                        else
                        {
                            pp.EndNode = graphNode;
                        }
                    }
                    //Соединения для созданных узлов
                    pp.StartNode.ConnectedLinkedListNode = pp.EndNode.LinkedListNode;
                    pp.EndNode.ConnectedLinkedListNode   = pp.StartNode.LinkedListNode;
                }
                else
                {
                    //Эта полилиния полностью находится внутри треугоьника
                    //Если эта полилиния - внешняя граница, то добавить полигон по всем точкам полилинии
                    if (pp.PolylineNestingNode.IsOuterBoundary)
                    {
                        List <Point3d> poligon = new List <Point3d>();
                        Polygons.Add(poligon);
                        foreach (PolylinePt pt in pp.PolylinePts)
                        {
                            poligon.Add(new Point3d(pt.Point2D.X, pt.Point2D.Y, pt.Z));
                        }
                    }
                    else
                    {
                        //Если эта полилиния ограничивает островок, то отложить этот полигон для дальнейшей обработки
                        List <Point3d> hole = new List <Point3d>();
                        holes.Add(hole);
                        foreach (PolylinePt pt in pp.PolylinePts)
                        {
                            hole.Add(new Point3d(pt.Point2D.X, pt.Point2D.Y, pt.Z));
                        }
                    }
                }
            }



            //Составление маршрутов обхода графа
            //Правила
            //- Начинать обход с любого еще не обойденнго узла, из которого исходит участок полилинии. Запомнить ссылку на PolylineNesting.Node
            //- Если из текущего узла исходит участок полилинии, который еще не обойден, то обойти его
            //- Из текущего узла не исходит такого участка полилинии (например, мы только что обошли участок полилинии и пришли в узел на ребре) =>
            //  - Есть 2 варианта куда идти дальше: либо вперед до следующего узла, либо назад
            //  Проверяются оба варианта. При этом:
            //      - Обходить до тех пор пока не будет дотигнут стартовый узел
            //      - Нельзя заходить в те узлы которые уже посещены (только замыкание со стартовым узлом)
            //      - Нельзя заходить в узлы вершин, которые не являются внутренними
            //  Если в итоге получаются 2 варината обхода, взять точку изнутри каждого из обойденных полигонов и с помощью алгоритма WindingNumber проверить,
            //  попадают ли они внутрь PolylineNesting.Node. Если PolylineNesting.Node.IsOuterBoundary = true,
            //  то принять обход, точка которого попала внутрь PolylineNesting.Node, иначе принять обход, точка которого не попала внутрь PolylineNesting.Node


            //DisplayUtils.Polyline(vert2dLocs, true, 1, SurfaceMeshByBoundaryCommand.DB, null, SurfaceMeshByBoundaryCommand.ED);
            for (LinkedListNode <GraphNode> lln = graphNodes.First; lln != null; lln = lln.Next)
            {
                GraphNode startNode = lln.Value;
                if (!startNode.Visited && startNode.PolylinePart != null)
                {
                    startNode.Visited = true;
                    PolylinePart startPolyPart = startNode.PolylinePart;
                    GraphNode    nextNode      = startNode.ConnectedLinkedListNode.Value;
                    if (nextNode.Visited || startPolyPart.Visited)
                    {
                        //Такой ситуации не должно быть. Отметить проблемный треугольник
                        DisplayUtils.Polyline(vert2dLocs, true, 1, SurfaceMeshByBoundaryCommand.DB);
                        continue;
                    }

                    PolylineNesting.Node pNNode = startPolyPart.PolylineNestingNode;

                    //Начать составление маршрутов 2 вариантов замкнутого пути. Начинается всегда с прохода по участку полилинии
                    List <PathElement> path1 = new List <PathElement>()
                    {
                        startNode, startPolyPart, nextNode
                    };
                    List <PathElement> path2 = new List <PathElement>()
                    {
                        startNode, startPolyPart, nextNode
                    };

                    startPolyPart.Visited = true;//необходимо для правильной работы PathPreparing
                    bool path1Prepared = PathPreparing(nextNode, startNode, true, path1);
                    bool path2Prepared = PathPreparing(nextNode, startNode, false, path2);


                    List <Point3d> poligon1 = null;
                    List <Point3d> poligon2 = null;
                    if (path1Prepared)
                    {
                        //Заполнить полигон 1
                        poligon1 = GetPoligonFromPath(path1);
                        //DisplayUtils.Polyline(Utils.Poligon3DTo2D(poligon1), true, 1, SurfaceMeshByBoundaryCommand.DB);
                    }
                    if (path2Prepared)
                    {
                        //Заполнить полигон 2
                        poligon2 = GetPoligonFromPath(path2);
                        //DisplayUtils.Polyline(Utils.Poligon3DTo2D(poligon2), true, 1, SurfaceMeshByBoundaryCommand.DB);
                    }

                    List <PathElement> actualPath    = null;
                    List <Point3d>     actualPoligon = null;
                    if (path1Prepared && path2Prepared)//Если составлено 2 возможных путя
                    {
                        //Для 1-го варианта обхода найти внутреннюю точку (не на границе)
                        IList <Point2d> poligon1_2d = Utils.Poligon3DTo2D(poligon1);
                        //IList<Point2d> poligon2_2d = Utils.Poligon3DTo2D(poligon2);
                        Point2d?p1 = null;
                        try
                        {
                            p1 = Utils.GetAnyPointInsidePoligon(poligon1_2d, Utils.DirectionIsClockwise(poligon1_2d));
                        }
                        catch (Exception)
                        {
                            //Такой ситуации не должно быть. Отметить проблемный треугольник
                            DisplayUtils.Polyline(vert2dLocs, true, 1, SurfaceMeshByBoundaryCommand.DB);
                            continue;
                        }
                        //Point2d p2 = Utils.GetAnyPointInsidePoligon(poligon2_2d, Utils.DirectionIsClockwise(poligon2_2d));

                        if (p1 != null)
                        {
                            //Определить находится ли эта точка внутри полилинии
                            bool p1InsidePolyline = Utils.PointIsInsidePolylineWindingNumber(p1.Value, pNNode.Point2DCollection);

                            //Проверить, подходит ли 1-й вариант с учетом свойства IsOuterBoundary
                            if ((pNNode.IsOuterBoundary && p1InsidePolyline) || (!pNNode.IsOuterBoundary && !p1InsidePolyline))
                            {
                                //Первый вариант правильный
                                actualPoligon = poligon1;
                                actualPath    = path1;
                            }
                            else
                            {
                                //Второй вариант правильный
                                actualPoligon = poligon2;
                                actualPath    = path2;
                            }
                        }
                    }
                    else if (path1Prepared)
                    {
                        actualPoligon = poligon1;
                        actualPath    = path1;
                    }
                    else if (path2Prepared)
                    {
                        actualPoligon = poligon2;
                        actualPath    = path2;
                    }

                    if (actualPoligon != null)
                    {
                        //Для принятого пути обхода:
                        // - Добавить полигон в набор
                        // - Присвоить свойству Visited значение true
                        Polygons.Add(actualPoligon);
                        foreach (PathElement pe in actualPath)
                        {
                            pe.Visited = true;
                        }
                    }
                }
            }



            //~//~//~//~//~//~//~//~//~//~//~//~//~//~//~//~//~//~//~//~//~//~//~//~//~//~//~//~//~//~//~
            //ОБРАБОТКА ОТВЕРСТИЙ ВНУТРИ ПОЛИГОНОВ
            if (holes.Count > 0)
            {
                //Каждое отверстие находится внутри одного из рассчитанных полигонов. Определить полигон, в который вложено отверстие
                //Эти полигоны будут дополнены участками, включающими в себя отверстия
                //в соответствии с https://www.geometrictools.com/Documentation/TriangulationByEarClipping.pdf п 3 - 5
                List <PolygonWithNested> poligonsWithHoles = PolygonWithHoles.ResolveHoles(Polygons, holes);


                foreach (PolygonWithNested p in poligonsWithHoles)
                {
                    if (p.Polygon == null)
                    {
                        //Если отверстия есть в корневом узле, значит нужно рассматривать полигон равный текущему треугольнику с отверстиями
                        p.Polygon = new List <Point3d>()
                        {
                            tinSurfaceTriangle.Vertex1.Location,
                            tinSurfaceTriangle.Vertex2.Location,
                            tinSurfaceTriangle.Vertex3.Location
                        };
                    }
                    else
                    {
                        //Если оказывается, что полигон включает в себя отверстие, то он должен быть удален из Polygons
                        //Такие полигоны будут разбиваться на треугольники
                        Polygons.Remove(p.Polygon);
                    }


                    p.MakeSimple();
                    //Polygons.Add(p.Polygon);
                    //Нельзя просто добавить простой полигон к общему списку полигонов (SubDMesh создается неправильно).
                    //Необходимо сделать триангуляцию
                    //TEST
                    #region Отрисовка простого полигона
                    //using (Transaction tr
                    //    = SurfaceMeshByBoundaryCommand.DB.TransactionManager.StartTransaction())
                    //using (Polyline pline = new Polyline())
                    //{
                    //    BlockTable bt = tr.GetObject(SurfaceMeshByBoundaryCommand.DB.BlockTableId, OpenMode.ForWrite) as BlockTable;
                    //    BlockTableRecord ms = (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite);

                    //    pline.Color = Color.FromColorIndex(ColorMethod.ByAci, 5);
                    //    foreach (Point3d pt in p.Polygon)
                    //    {
                    //        pline.AddVertexAt(0, new Point2d(pt.X, pt.Y), 0, 0, 0);
                    //    }
                    //    pline.Closed = true;

                    //    ms.AppendEntity(pline);
                    //    tr.AddNewlyCreatedDBObject(pline, true);

                    //    tr.Commit();
                    //}
                    #endregion
                    //TEST


                    EarClippingTriangulator triangulator = new EarClippingTriangulator(p.Polygon);
                    foreach (List <Point3d> tr in triangulator.Triangles)
                    {
                        Polygons.Add(tr);
                    }
                }
            }



            //~//~//~//~//~//~//~//~//~//~//~//~//~//~//~//~//~//~//~//~//~//~//~//~//~//~//~//~//~//~//~
        }