private IEnumerable <AdvancedWall> FindIntersectionWalls(List <AdvancedWall> sideWalls, List <AdvancedWall> allWalls)
        {
            var intersectionWalls = new List <AdvancedWall>();

            foreach (var wall in allWalls)
            {
                foreach (var sideWall in sideWalls)
                {
                    if (wall.IsAdjoinToByLocationCurveEnds(sideWall) &&
                        !AdvancedHelpers.HasWallInListById(sideWalls, wall))
                    {
                        intersectionWalls.Add(wall);
                    }
                }
            }

            return(intersectionWalls);
        }
        /// <summary>Фильтрация face'ов по условиям</summary>
        private IEnumerable <AdvancedPlanarFace> FilterFaces(
            ExtremeWallVariant extremeWallVariant,
            List <AdvancedWall> sideWalls,
            IEnumerable <AdvancedPlanarFace> selectedFaces)
        {
            var tolerance = 0.0001;

            // Нужно два списка так как разные фильтрации
            var faces = new List <AdvancedPlanarFace>();

            // Удаляю фейсы, не пересекаемые секущей плоскостью
            foreach (var face in selectedFaces)
            {
                if (face.MinZ <= _cutPlanZ && face.MaxZ >= _cutPlanZ)
                {
                    faces.Add(face);
                }
            }

            // Нужно удалить фейсы, которые совпадают по направлению
            var returnedFaces = new List <AdvancedPlanarFace>();

            bool hasFaces;

            do
            {
                hasFaces = faces.Any(f => f != null);
                for (var i = 0; i < faces.Count; i++)
                {
                    var face = faces[i];
                    if (face != null)
                    {
                        returnedFaces.Add(face);
                        for (var j = 0; j < faces.Count; j++)
                        {
                            if (i == j)
                            {
                                continue;
                            }
                            if (faces[j] == null)
                            {
                                continue;
                            }

                            if (extremeWallVariant == ExtremeWallVariant.Left ||
                                extremeWallVariant == ExtremeWallVariant.Right)
                            {
                                // same Y
                                if (Math.Abs(face.PlanarFace.Origin.Y - faces[j].PlanarFace.Origin.Y) < tolerance)
                                {
                                    faces[j] = null;
                                }
                            }
                            else if (extremeWallVariant == ExtremeWallVariant.Top ||
                                     extremeWallVariant == ExtremeWallVariant.Bottom)
                            {
                                // same X
                                if (Math.Abs(face.PlanarFace.Origin.X - faces[j].PlanarFace.Origin.X) < tolerance)
                                {
                                    faces[j] = null;
                                }
                            }
                        }

                        faces[i] = null;
                    }
                }
            }while (hasFaces);

            // Удаление по глубине проецирования
            // Глубину проецирования беру по наибольшей толщине стен в списке
            var depth = AdvancedHelpers.GetMaxWallWidthFromList(sideWalls) * 2;

            if (extremeWallVariant == ExtremeWallVariant.Bottom)
            {
                foreach (var wall in sideWalls.Where(w => w.Orientation == ElementOrientation.Horizontal))
                {
                    for (var i = returnedFaces.Count - 1; i >= 0; i--)
                    {
                        var face = returnedFaces[i];
                        if (face.MinX > wall.GetMinX() - wall.Wall.Width &&
                            face.MinX < wall.GetMaxX() + wall.Wall.Width)
                        {
                            if (face.MinY > wall.GetMinY() + depth)
                            {
                                returnedFaces.RemoveAt(i);
                            }
                        }
                    }
                }
            }

            if (extremeWallVariant == ExtremeWallVariant.Top)
            {
                foreach (var wall in sideWalls.Where(w => w.Orientation == ElementOrientation.Horizontal))
                {
                    for (var i = returnedFaces.Count - 1; i >= 0; i--)
                    {
                        var face = returnedFaces[i];
                        if (face.MinX > wall.GetMinX() - wall.Wall.Width &&
                            face.MinX < wall.GetMaxX() + wall.Wall.Width)
                        {
                            if (face.MaxY < wall.GetMaxY() - depth)
                            {
                                returnedFaces.RemoveAt(i);
                            }
                        }
                    }
                }
            }

            if (extremeWallVariant == ExtremeWallVariant.Left)
            {
                foreach (var wall in sideWalls.Where(w => w.Orientation == ElementOrientation.Vertical))
                {
                    for (var i = returnedFaces.Count - 1; i >= 0; i--)
                    {
                        var face = returnedFaces[i];
                        if (face.MinY > wall.GetMinY() - wall.Wall.Width &&
                            face.MinY < wall.GetMaxY() + wall.Wall.Width)
                        {
                            if (face.MinX > wall.GetMinX() + depth)
                            {
                                returnedFaces.RemoveAt(i);
                            }
                        }
                    }
                }
            }

            if (extremeWallVariant == ExtremeWallVariant.Right)
            {
                foreach (var wall in sideWalls.Where(w => w.Orientation == ElementOrientation.Vertical))
                {
                    for (var i = returnedFaces.Count - 1; i >= 0; i--)
                    {
                        var face = returnedFaces[i];
                        if (face.MinY > wall.GetMinY() - wall.Wall.Width &&
                            face.MinY < wall.GetMaxY() + wall.Wall.Width)
                        {
                            if (face.MaxX < wall.GetMaxX() - depth)
                            {
                                returnedFaces.RemoveAt(i);
                            }
                        }
                    }
                }
            }

            // Фильтрация ближайших граней: если расстояние между гранями меньше заданного в настройка,
            // то удалять из этой пары ту грань, которая указана в настройках (по длине грани)
            var minWidthSetting = int.TryParse(
                UserConfigFile.GetValue(LangItem, "ExteriorFaceMinWidthBetween"), out var m)
                ? m
                : 100;
            var minWidthBetween = minWidthSetting.MmToFt();

            // Вариант удаления: 0 - наименьший, 1 - наибольший
            var removeVariant = int.TryParse(
                UserConfigFile.GetValue(LangItem, "ExteriorMinWidthFaceRemove"), out m)
                ? m
                : 0;

            if (extremeWallVariant == ExtremeWallVariant.Bottom || extremeWallVariant == ExtremeWallVariant.Top)
            {
                // Сначала нужно отсортировать
                returnedFaces.Sort((f1, f2) => f1.MinX.CompareTo(f2.MinX));
                var wasRemoved = false;
                do
                {
                    for (var i = 0; i < returnedFaces.Count - 1; i++)
                    {
                        var face1    = returnedFaces[i];
                        var face2    = returnedFaces[i + 1];
                        var distance = Math.Abs(face1.MinX - face2.MinX);
                        if (distance < minWidthBetween)
                        {
                            wasRemoved = true;
                            var face1Length = Math.Abs(face1.MaxY - face1.MinY);
                            var face2Length = Math.Abs(face2.MaxY - face2.MinY);
                            if (removeVariant == 0)
                            {
                                if (face1Length < face2Length)
                                {
                                    returnedFaces.RemoveAt(i);
                                }
                                else
                                {
                                    returnedFaces.RemoveAt(i + 1);
                                }
                            }
                            else
                            {
                                if (face1Length > face2Length)
                                {
                                    returnedFaces.RemoveAt(i);
                                }
                                else
                                {
                                    returnedFaces.RemoveAt(i + 1);
                                }
                            }

                            break;
                        }

                        wasRemoved = false;
                    }
                }while (wasRemoved);
            }

            if (extremeWallVariant == ExtremeWallVariant.Left || extremeWallVariant == ExtremeWallVariant.Right)
            {
                // Сначала нужно отсортировать
                returnedFaces.Sort((f1, f2) => f1.MinY.CompareTo(f2.MinY));
                var wasRemoved = false;
                do
                {
                    for (var i = 0; i < returnedFaces.Count - 1; i++)
                    {
                        var face1    = returnedFaces[i];
                        var face2    = returnedFaces[i + 1];
                        var distance = Math.Abs(face1.MinY - face2.MinY);
                        if (distance < minWidthBetween)
                        {
                            wasRemoved = true;
                            var face1Lenght = Math.Abs(face1.MaxX - face1.MinX);
                            var face2Lenght = Math.Abs(face2.MaxX - face2.MinX);
                            if (removeVariant == 0)
                            {
                                if (face1Lenght < face2Lenght)
                                {
                                    returnedFaces.RemoveAt(i);
                                }
                                else
                                {
                                    returnedFaces.RemoveAt(i + 1);
                                }
                            }
                            else
                            {
                                if (face1Lenght > face2Lenght)
                                {
                                    returnedFaces.RemoveAt(i);
                                }
                                else
                                {
                                    returnedFaces.RemoveAt(i + 1);
                                }
                            }

                            break;
                        }

                        wasRemoved = false;
                    }
                }while (wasRemoved);
            }

            return(returnedFaces);
        }
        /// <summary>Проставить внешние размеры</summary>
        public void DoWork()
        {
            var doc = _uiApplication.ActiveUIDocument.Document;

            _cutPlanZ = GeometryHelpers.GetViewPlanCutPlaneElevation((ViewPlan)doc.ActiveView);

            // select
            var selectedElements = SelectElements();

            if (selectedElements == null)
            {
                return;
            }

            // get list of advanced elements
            foreach (var element in selectedElements)
            {
                switch (element)
                {
                case Wall wall:
                    var advancedWall = new AdvancedWall(wall);
                    if (advancedWall.IsDefined)
                    {
                        _advancedWalls.Add(advancedWall);
                    }
                    break;

                case Grid grid:
                    var advancedGrid = new AdvancedGrid(grid);
                    if (advancedGrid.IsDefined)
                    {
                        _advancedGrids.Add(advancedGrid);
                    }
                    break;
                }
            }

            if (!_advancedWalls.Any())
            {
                MessageBox.Show(Language.GetItem(LangItem, "msg7"), MessageBoxIcon.Close);
                return;
            }

            // Фильтрую стены по толщине
            AdvancedHelpers.FilterByWallWidth(_advancedWalls);
            if (!_advancedWalls.Any())
            {
                MessageBox.Show(Language.GetItem(LangItem, "msg8"), MessageBoxIcon.Close);
                return;
            }

            // Фильтрую стены, оставляя которые пересекаются секущим диапазоном
            AdvancedHelpers.FilterByCutPlan(_advancedWalls, _uiApplication.ActiveUIDocument.Document);

            // Вдруг после этого не осталось стен!
            if (!_advancedWalls.Any())
            {
                MessageBox.Show(Language.GetItem(LangItem, "msg9"), MessageBoxIcon.Close);
                return;
            }

            AdvancedHelpers.FindExtremes(
                _advancedWalls, out var leftExtreme, out var rightExtreme, out var topextreme, out var bottomExtreme);

            using (var transactionGroup = new TransactionGroup(doc, _transactionName))
            {
                transactionGroup.Start();

                var createdDimensions = new List <Dimension>();

                if (_exteriorConfiguration.RightDimensions)
                {
                    createdDimensions.AddRange(CreateSideDimensions(rightExtreme, _advancedWalls, ExtremeWallVariant.Right));
                }
                if (_exteriorConfiguration.LeftDimensions)
                {
                    createdDimensions.AddRange(CreateSideDimensions(leftExtreme, _advancedWalls, ExtremeWallVariant.Left));
                }
                if (_exteriorConfiguration.TopDimensions)
                {
                    createdDimensions.AddRange(CreateSideDimensions(topextreme, _advancedWalls, ExtremeWallVariant.Top));
                }
                if (_exteriorConfiguration.BottomDimensions)
                {
                    createdDimensions.AddRange(CreateSideDimensions(bottomExtreme, _advancedWalls, ExtremeWallVariant.Bottom));
                }

                if (createdDimensions.Any(d => d != null))
                {
                    using (var tr = new Transaction(doc, "Remove zeroes"))
                    {
                        tr.Start();

                        foreach (var createdDimension in createdDimensions.Where(d => d != null))
                        {
                            if (Dimensions.TryRemoveZeroes(createdDimension, out var referenceArray) &&
                                createdDimension.Curve is Line line)
                            {
                                doc.Delete(createdDimension.Id);
                                doc.Create.NewDimension(doc.ActiveView, line, referenceArray);
                            }
                        }

                        tr.Commit();
                    }
                }

                transactionGroup.Assimilate();
            }
        }
        private Dimension CreateDimensionByOverallWalls(
            Document doc, Line chainDimensionLine,
            IReadOnlyCollection <AdvancedWall> sideWalls,
            ExtremeWallVariant extremeWallVariant)
        {
            Dimension returnedDimension = null;
            var       verticalWalls     = sideWalls.Where(w => w.Orientation == ElementOrientation.Vertical).ToList();
            var       horizontalWalls   = sideWalls.Where(w => w.Orientation == ElementOrientation.Horizontal).ToList();

            // Если вдруг нет стен
            if (!verticalWalls.Any() && !horizontalWalls.Any())
            {
                return(null);
            }
            var referenceArray = new ReferenceArray();

            // То же самое, что и получить из стен, только взять крайние референсы
            if (extremeWallVariant == ExtremeWallVariant.Right || extremeWallVariant == ExtremeWallVariant.Left)
            {
                var faces = new List <AdvancedPlanarFace>();

                // для каждой вертикальной стены нахожу соприкасающиеся горизонтальные стены
                foreach (var verticalWall in verticalWalls)
                {
                    // Добавляю в список все фейсы самой стены
                    foreach (var face in verticalWall.AdvancedPlanarFaces)
                    {
                        if (face.IsHorizontal)
                        {
                            faces.Add(face);
                        }
                    }

                    var adjoinElements1 = ((LocationCurve)verticalWall.Wall.Location).get_ElementsAtJoin(0);
                    var adjoinElements2 = ((LocationCurve)verticalWall.Wall.Location).get_ElementsAtJoin(1);
                    var adjoinWalls     = new List <AdvancedWall>();
                    foreach (Element element in adjoinElements1)
                    {
                        var w = AdvancedHelpers.GetAdvancedWallFromListById(horizontalWalls, element.Id.IntegerValue);
                        if (w != null)
                        {
                            adjoinWalls.Add(w);
                        }
                    }

                    foreach (Element element in adjoinElements2)
                    {
                        var w = AdvancedHelpers.GetAdvancedWallFromListById(horizontalWalls, element.Id.IntegerValue);
                        if (w != null)
                        {
                            adjoinWalls.Add(w);
                        }
                    }

                    // добавляю все фейсы соприкасающихся стен
                    foreach (var wall in adjoinWalls)
                    {
                        foreach (var face in wall.AdvancedPlanarFaces)
                        {
                            if (face.IsHorizontal)
                            {
                                faces.Add(face);
                            }
                        }
                    }
                }

                faces.Sort((f1, f2) => f1.MinY.CompareTo(f2.MinY));
                referenceArray.Append(faces.First().PlanarFace.Reference);
                referenceArray.Append(faces.Last().PlanarFace.Reference);
            }

            if (extremeWallVariant == ExtremeWallVariant.Top || extremeWallVariant == ExtremeWallVariant.Bottom)
            {
                var faces = new List <AdvancedPlanarFace>();

                // для каждой вертикальной стены нахожу соприкасающиеся горизонтальные стены
                foreach (var horizontalWall in horizontalWalls)
                {
                    // Добавляю в список все фейсы самой стены
                    foreach (var face in horizontalWall.AdvancedPlanarFaces)
                    {
                        if (face.IsVertical)
                        {
                            faces.Add(face);
                        }
                    }

                    var adjoinElements1 = ((LocationCurve)horizontalWall.Wall.Location).get_ElementsAtJoin(0);
                    var adjoinElements2 = ((LocationCurve)horizontalWall.Wall.Location).get_ElementsAtJoin(1);
                    var adjoinWalls     = new List <AdvancedWall>();
                    foreach (Element element in adjoinElements1)
                    {
                        var w = AdvancedHelpers.GetAdvancedWallFromListById(verticalWalls, element.Id.IntegerValue);
                        if (w != null)
                        {
                            adjoinWalls.Add(w);
                        }
                    }

                    foreach (Element element in adjoinElements2)
                    {
                        var w = AdvancedHelpers.GetAdvancedWallFromListById(verticalWalls, element.Id.IntegerValue);
                        if (w != null)
                        {
                            adjoinWalls.Add(w);
                        }
                    }

                    // добавляю все фейсы соприкасающихся стен
                    foreach (var wall in adjoinWalls)
                    {
                        foreach (var face in wall.AdvancedPlanarFaces)
                        {
                            if (face.IsVertical)
                            {
                                faces.Add(face);
                            }
                        }
                    }
                }

                faces.Sort((f1, f2) => f1.MinX.CompareTo(f2.MinX));
                referenceArray.Append(faces.First().PlanarFace.Reference);
                referenceArray.Append(faces.Last().PlanarFace.Reference);
            }

            if (!referenceArray.IsEmpty)
            {
                using (var transaction = new Transaction(doc, _transactionName))
                {
                    transaction.Start();
                    returnedDimension = doc.Create.NewDimension(doc.ActiveView, chainDimensionLine, referenceArray);
                    transaction.Commit();
                }
            }

            return(returnedDimension);
        }
        private IEnumerable <Dimension> CreateSideDimensions(
            List <AdvancedWall> sideAdvancedWalls, List <AdvancedWall> allWalls, ExtremeWallVariant extremeWallVariant)
        {
            var doc = _uiApplication.ActiveUIDocument.Document;

            // Сумма длин отступов от крайней стены
            var chainOffsetSumm = 0;

            // Делаю цикл по цепочкам
            foreach (var chain in _exteriorConfiguration.Chains)
            {
                // Суммирую отступы
                chainOffsetSumm += chain.ElementOffset;

                // Получаю линию для построения размера с учетом масштаба
                var chainDimensionLine = AdvancedHelpers.GetDimensionLineForChain(
                    doc, sideAdvancedWalls, extremeWallVariant,
                    chainOffsetSumm.MmToFt() * doc.ActiveView.Scale);

                if (chainDimensionLine == null)
                {
                    MessageBox.Show(Language.GetItem(LangItem, "msg12"), MessageBoxIcon.Close);
                    continue;
                }

                // Крайние оси
                if (chain.ExtremeGrids)
                {
                    yield return(CreateDimensionByExtremeGrids(doc, chainDimensionLine, extremeWallVariant));
                }
                else if (chain.Overall)
                {
                    yield return(CreateDimensionByOverallWalls(doc, chainDimensionLine, sideAdvancedWalls, extremeWallVariant));
                }

                // Так как вариант "крайние оси" или "габарит" перекрывает все остальные
                else
                {
                    var referenceArray = new ReferenceArray();

                    // Собираю референсы для стен в зависимости от настроек цепочки
                    if (chain.Walls)
                    {
                        GetWallsReferences(sideAdvancedWalls, allWalls, extremeWallVariant, chain, ref referenceArray);
                    }

                    // from grids
                    if (chain.Grids)
                    {
                        GetGridsReferences(extremeWallVariant, ref referenceArray);
                    }

                    if (!referenceArray.IsEmpty)
                    {
                        using (var transaction = new Transaction(doc, _transactionName))
                        {
                            transaction.Start();
                            var dimension = doc.Create.NewDimension(doc.ActiveView, chainDimensionLine, referenceArray);
                            if (dimension != null)
                            {
                                yield return(dimension);
                            }
                            transaction.Commit();
                        }
                    }
                }
            }
        }