Beispiel #1
0
        /// <summary>
        /// Получить полигон точек по последовательности узлов графа
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        private List <Point3d> GetPoligonFromPath(List <PathElement> path)
        {
            List <Point3d> poligon           = new List <Point3d>();
            bool           polyPartFromStart = false;

            foreach (PathElement pe in path)
            {
                GraphNode       graphNode       = pe as GraphNode;
                PolylinePart    polylinePart    = pe as PolylinePart;
                VertexGraphNode vertexGraphNode = pe as VertexGraphNode;

                if (graphNode != null && graphNode.PolylinePart != null)
                {
                    polyPartFromStart = graphNode.PolylinePartConnectedByStart;
                }
                else if (polylinePart != null)
                {
                    //Для участка полилинии получить все 3d точки в нужном порядке
                    poligon.AddRange(polylinePart.GetPoints3dOrdered(polyPartFromStart));
                }
                if (vertexGraphNode != null && vertexGraphNode.PolylinePart == null)
                {
                    //Если из узла вершины не выходит участок полилинии, то добавить вершину треугольника в полигон
                    poligon.Add(verts[vertexGraphNode.VertNum].Location);
                }
            }

            return(poligon);
        }
Beispiel #2
0
        /// <summary>
        /// Составление варианта маршрута
        /// </summary>
        /// <param name="startNode"></param>
        /// <param name="endNode"></param>
        /// <param name="forward"></param>
        /// <returns></returns>
        private bool PathPreparing(GraphNode startNode, GraphNode endNode, bool forward, List <PathElement> path)
        {
            List <PolylinePart> polyPartsTraversed = new List <PolylinePart>();
            GraphNode           currNode           = startNode;

            while (!currNode.Equals(endNode) && !currNode.Visited)
            {
                //- Обходить до тех пор пока не будет дотигнут стартовый узел
                //- Нельзя заходить в те узлы которые уже посещены во время других проходов (только замыкание со стартовым узлом)
                //- Нельзя заходить в узлы вершин, которые не являются внутренними (если вершина находится на границе, то она считается внутренней)
                VertexGraphNode vertexGraphNode = currNode as VertexGraphNode;
                if (vertexGraphNode != null && !vertexGraphNode.IsInnerVertex)
                {
                    break;
                }


                if (currNode.PolylinePart != null && !currNode.PolylinePart.Visited)
                {
                    //Если из текущего узла исходит участок полилинии, который еще не обойден, то обойти его
                    PolylinePart polylinePart = currNode.PolylinePart;
                    currNode = currNode.ConnectedLinkedListNode.Value;
                    path.Add(polylinePart);
                    path.Add(currNode);
                    //Переписать свойство Visited, чтобы не было обратного прохода
                    polylinePart.Visited = true;
                    polyPartsTraversed.Add(polylinePart);
                }
                else//Обход до следующего узла вдоль границы треугольника
                {
                    LinkedListNode <GraphNode> lln     = currNode.LinkedListNode;
                    LinkedListNode <GraphNode> nextlln = forward ? lln.Next : lln.Previous;
                    if (nextlln == null)//Если дошли до конца двусвязного списка, то продолжить с противоположной стороны
                    {
                        nextlln = forward ? lln.List.First : lln.List.Last;
                    }

                    currNode = nextlln.Value;
                    path.Add(currNode);
                }
            }

            bool pathFound = false;

            //Если достигнут конечный узел то вернуть true
            if (currNode.Equals(endNode))
            {
                pathFound = true;
            }

            //После завершения составления маршрута сбросить свойство Visited у всех добавленных участков полилинии
            foreach (PolylinePart pp in polyPartsTraversed)
            {
                pp.Visited = false;
            }
            //Если начальный и конечный узлы равны, то удалить последний
            if (path.First().Equals(path.Last()))
            {
                path.RemoveAt(path.Count - 1);
            }

            return(pathFound);
        }
Beispiel #3
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);
                    }
                }
            }



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