From 928bf2cece470cd324a25666456f95b82e4bf3cd Mon Sep 17 00:00:00 2001 From: Vladimir Stroganov Date: Mon, 21 Oct 2024 10:11:47 +0300 Subject: [PATCH] =?UTF-8?q?RevitOpeningPlacement:=20=D0=A0=D0=B5=D1=84?= =?UTF-8?q?=D0=B0=D0=BA=D1=82=D0=BE=D1=80=D0=B8=D0=BD=D0=B3=20=D1=81=D1=82?= =?UTF-8?q?=D1=80=D1=83=D0=BA=D1=82=D1=83=D1=80=D1=8B=20=D0=BA=D0=BE=D0=BC?= =?UTF-8?q?=D0=B0=D0=BD=D0=B4=D1=8B=20"=D0=9D=D0=B0=D0=B2=D0=B8=D0=B3?= =?UTF-8?q?=D0=B0=D1=82=D0=BE=D1=80"=20(#99)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../GetOpeningTasksCmd.cs | 410 +++-------- .../Extensions/ISolidProviderExtension.cs | 5 +- .../RealOpeningArPlacer.cs | 8 + .../RealOpeningKrPlacer.cs | 8 + .../Models/RevitRepository.cs | 6 +- .../OpeningModels/OpeningArTaskIncoming.cs | 2 + .../OpeningModels/OpeningMepTaskIncoming.cs | 2 + .../OpeningModels/OpeningMepTaskOutcoming.cs | 467 +------------ .../OpeningModels/OpeningRealKr.cs | 2 + .../Services/ConstantsProvider.cs | 40 ++ .../Services/IConstantsProvider.cs | 36 + .../Services/IOpeningInfoUpdater.cs | 14 + .../Services/ISolidProviderUtils.cs | 28 + .../Services/MepTaskOutcomingInfoUpdater.cs | 638 ++++++++++++++++++ .../Services/SolidProviderUtils.cs | 147 ++++ ...ctureNavigatorForIncomingTasksViewModel.cs | 151 ++++- ...ctureNavigatorForIncomingTasksViewModel.cs | 223 +++++- .../MepNavigatorForOutcomingTasksViewModel.cs | 71 +- .../Views/NavigatorArIncomingView.xaml | 7 + .../Views/NavigatorMepIncomingView.xaml | 7 + .../Views/NavigatorMepOutcomingView.xaml | 7 + 21 files changed, 1446 insertions(+), 833 deletions(-) create mode 100644 src/RevitOpeningPlacement/Services/ConstantsProvider.cs create mode 100644 src/RevitOpeningPlacement/Services/IConstantsProvider.cs create mode 100644 src/RevitOpeningPlacement/Services/IOpeningInfoUpdater.cs create mode 100644 src/RevitOpeningPlacement/Services/ISolidProviderUtils.cs create mode 100644 src/RevitOpeningPlacement/Services/MepTaskOutcomingInfoUpdater.cs create mode 100644 src/RevitOpeningPlacement/Services/SolidProviderUtils.cs diff --git a/src/RevitOpeningPlacement/GetOpeningTasksCmd.cs b/src/RevitOpeningPlacement/GetOpeningTasksCmd.cs index 9ed8cf254..1c2a4fc11 100644 --- a/src/RevitOpeningPlacement/GetOpeningTasksCmd.cs +++ b/src/RevitOpeningPlacement/GetOpeningTasksCmd.cs @@ -1,6 +1,5 @@ using System; -using System.Collections.Generic; -using System.Linq; +using System.Windows; using System.Windows.Interop; using Autodesk.Revit.Attributes; @@ -9,7 +8,6 @@ using dosymep.Bim4Everyone; using dosymep.Bim4Everyone.SimpleServices; -using dosymep.SimpleServices; using Ninject; @@ -18,9 +16,9 @@ using RevitOpeningPlacement.Models; using RevitOpeningPlacement.Models.Configs; -using RevitOpeningPlacement.Models.Interfaces; using RevitOpeningPlacement.Models.Navigator.Checkers; using RevitOpeningPlacement.OpeningModels; +using RevitOpeningPlacement.Services; using RevitOpeningPlacement.ViewModels.Navigator; using RevitOpeningPlacement.Views; @@ -65,7 +63,10 @@ protected override void Execute(UIApplication uiApplication) { if(!ModelCorrect(repo)) { return; } - GetOpeningsTask(uiApplication, repo); + if(!repo.ContinueIfNotAllLinksLoaded()) { + throw new OperationCanceledException(); + } + GetOpeningsTask(kernel); } } @@ -73,23 +74,24 @@ protected override void Execute(UIApplication uiApplication) { /// /// Логика вывода окна навигатора по заданиям на отверстия в зависимости от раздела проекта /// - private void GetOpeningsTask(UIApplication uiApplication, RevitRepository revitRepository) { + private void GetOpeningsTask(IKernel kernel) { + var revitRepository = kernel.Get(); var docType = revitRepository.GetDocumentType(); switch(docType) { case DocTypeEnum.AR: - GetOpeningsTaskInDocumentAR(uiApplication, revitRepository); + GetOpeningsTaskInDocumentAR(kernel); break; case DocTypeEnum.KR: - GetOpeningsTaskInDocumentKR(uiApplication, revitRepository); + GetOpeningsTaskInDocumentKR(kernel); break; case DocTypeEnum.MEP: - GetOpeningsTaskInDocumentMEP(uiApplication, revitRepository); + GetOpeningsTaskInDocumentMEP(kernel); break; case DocTypeEnum.KOORD: - GetOpeningsTaskInDocumentKoord(revitRepository); + GetOpeningsTaskInDocumentKoord(kernel); break; default: - GetOpeningsTaskInDocumentNotDefined(revitRepository); + GetOpeningsTaskInDocumentNotDefined(kernel); break; } } @@ -97,249 +99,26 @@ private void GetOpeningsTask(UIApplication uiApplication, RevitRepository revitR /// /// Логика вывода окна навигатора по заданиям на отверстия в файле архитектуры /// - private void GetOpeningsTaskInDocumentAR(UIApplication uiApplication, RevitRepository revitRepository) { - GetIncomingTaskInDocAR(uiApplication, revitRepository); - } - - /// - /// Запуск окна навигатора по входящим заданиям на отверстия в файле архитектуры - /// - private void GetIncomingTaskInDocAR(UIApplication uiApplication, RevitRepository revitRepository) { - if(!revitRepository.ContinueIfNotAllLinksLoaded()) { - throw new OperationCanceledException(); - } - ICollection incomingTasks = revitRepository.GetOpeningsMepTasksIncoming(); - ICollection realOpenings = revitRepository.GetRealOpeningsAr(); - ICollection constructureElementsIds = revitRepository.GetConstructureElementsIds(); - ICollection mepLinks = revitRepository - .GetMepLinks() - .Select(link => new MepLinkElementsProvider(link) as IMepLinkElementsProvider) - .ToArray(); - - var incomingTasksViewModels = GetOpeningsMepIncomingTasksViewModels( - incomingTasks, - realOpenings.ToArray(), - constructureElementsIds); - var openingsRealViewModels = GetOpeningsRealArViewModels(mepLinks, realOpenings); - - var navigatorViewModel = new ArchitectureNavigatorForIncomingTasksViewModel( - revitRepository, - incomingTasksViewModels, - openingsRealViewModels); - - var window = new NavigatorMepIncomingView() { Title = PluginName, DataContext = navigatorViewModel }; + private void GetOpeningsTaskInDocumentAR(IKernel kernel) { + kernel.Bind() + .To() + .InSingletonScope(); + kernel.Bind() + .ToSelf() + .InSingletonScope(); + kernel.Bind() + .ToSelf() + .InSingletonScope() + .WithPropertyValue(nameof(Window.DataContext), + c => c.Kernel.Get()) + .WithPropertyValue(nameof(Window.Title), PluginName); + + var window = kernel.Get(); + var uiApplication = kernel.Get(); var helper = new WindowInteropHelper(window) { Owner = uiApplication.MainWindowHandle }; - window.Show(); } - /// - /// Просмотр входящих заданий на отверстия от ВИС в файле КР - /// - /// Исключение, если пользователь прервал операцию - private void GetIncomingTasksFromMepInDocKR(UIApplication uiApplication, RevitRepository revitRepository) { - if(!revitRepository.ContinueIfNotAllLinksLoaded()) { - throw new OperationCanceledException(); - } - ICollection incomingTasks = revitRepository.GetOpeningsMepTasksIncoming(); - ICollection realOpenings = revitRepository.GetRealOpeningsKr(); - ICollection constructureElementsIds = revitRepository.GetConstructureElementsIds(); - ICollection mepLinks = revitRepository - .GetMepLinks() - .Select(link => new MepLinkElementsProvider(link) as IMepLinkElementsProvider) - .ToArray(); - - var incomingTasksViewModels = GetOpeningsMepIncomingTasksViewModels( - incomingTasks, - realOpenings.ToArray(), - constructureElementsIds) - .ToArray(); - var openingsRealViewModels = GetOpeningsRealKrViewModels( - realOpenings, - (OpeningRealKr opening) => { opening.UpdateStatus(mepLinks); }); - - var navigatorViewModel = new ConstructureNavigatorForIncomingTasksViewModel( - revitRepository, - incomingTasksViewModels, - openingsRealViewModels); - - var window = new NavigatorArIncomingView() { Title = PluginName, DataContext = navigatorViewModel }; - var helper = new WindowInteropHelper(window) { Owner = uiApplication.MainWindowHandle }; - - window.Show(); - } - - /// - /// Просмотр входящих заданий на отверстия от АР в файле КР - /// - /// Исключение, если пользователь прервал операцию - private void GetIncomingTasksFromArInDocKR(UIApplication uiApplication, RevitRepository revitRepository) { - if(!revitRepository.ContinueIfNotAllLinksLoaded()) { - throw new OperationCanceledException(); - } - ICollection incomingTasks = revitRepository.GetOpeningsArTasksIncoming(); - ICollection realOpenings = revitRepository.GetRealOpeningsKr(); - ICollection constructureElementsIds = revitRepository.GetConstructureElementsIds(); - ICollection arLinks = revitRepository - .GetArLinks() - .Select(link => new ConstructureLinkElementsProvider(revitRepository, link) as IConstructureLinkElementsProvider) - .ToArray(); - - var incomingTasksViewModels = GetOpeningsArIncomingTasksViewModels( - incomingTasks, - realOpenings, - constructureElementsIds); - var openingsRealViewModels = GetOpeningsRealKrViewModels( - realOpenings, - (OpeningRealKr opening) => { opening.UpdateStatus(arLinks); }); - - var navigatorViewModel = new ConstructureNavigatorForIncomingTasksViewModel( - revitRepository, - incomingTasksViewModels, - openingsRealViewModels); - - var window = new NavigatorArIncomingView() { Title = PluginName, DataContext = navigatorViewModel }; - var helper = new WindowInteropHelper(window) { Owner = uiApplication.MainWindowHandle }; - - window.Show(); - } - - /// - /// Возвращает коллекцию моделей представления для входящих заданий на отверстия из АР - /// - /// Входящие задания на отверстия из связей - /// Чистовые отверстия из текущего документа - /// Элементы конструкций из текущего документа - private ICollection GetOpeningsArIncomingTasksViewModels( - ICollection incomingTasks, - ICollection realOpenings, - ICollection constructureElementsIds) { - - var incomintTasksViewModels = new HashSet(); - - using(var pb = GetPlatformService()) { - pb.StepValue = _progressBarStepSmall; - pb.DisplayTitleFormat = "Анализ заданий... [{0}]\\[{1}]"; - var progress = pb.CreateProgress(); - pb.MaxValue = incomingTasks.Count; - var ct = pb.CreateCancellationToken(); - pb.Show(); - - int i = 0; - foreach(var incomingTask in incomingTasks) { - ct.ThrowIfCancellationRequested(); - progress.Report(i); - incomingTask.UpdateStatus(realOpenings, constructureElementsIds); - incomintTasksViewModels.Add(new OpeningArTaskIncomingViewModel(incomingTask)); - i++; - } - } - return incomintTasksViewModels; - } - - /// - /// Возвращает коллекцию моделей представления для входящих заданий на отверстия из ВИС - /// - /// Входящие задания на отверстия из связей - /// Чистовые отверстия из текущего документа - /// Элементы конструкций из текущего документа - private ICollection GetOpeningsMepIncomingTasksViewModels( - ICollection incomingTasks, - ICollection realOpenings, - ICollection constructureElementsIds) { - - var incomingTasksViewModels = new HashSet(); - - using(var pb = GetPlatformService()) { - pb.StepValue = _progressBarStepLarge; - pb.DisplayTitleFormat = "Анализ заданий... [{0}\\{1}]"; - var progress = pb.CreateProgress(); - pb.MaxValue = incomingTasks.Count; - var ct = pb.CreateCancellationToken(); - pb.Show(); - - int i = 0; - foreach(var incomingTask in incomingTasks) { - ct.ThrowIfCancellationRequested(); - progress.Report(i); - try { - incomingTask.UpdateStatusAndHostName(realOpenings, constructureElementsIds); - } catch(ArgumentException) { - //не удалось получить солид у задания на отверстие. Например, если его толщина равна 0 - continue; - } - incomingTasksViewModels.Add(new OpeningMepTaskIncomingViewModel(incomingTask)); - i++; - } - } - return incomingTasksViewModels; - } - - /// - /// Возвращает коллекцию моделей представления чистовых отверстий, размещенных в активном документе КР - /// - /// Чистовые отверстия, размещенные в активном документе КР - /// Делегат для обновления статусов размещенных чистовых отверстий КР - private ICollection GetOpeningsRealKrViewModels( - ICollection openingsReal, - Action updateStatus) { - - var openingsRealViewModels = new HashSet(); - - using(var pb = GetPlatformService()) { - pb.StepValue = _progressBarStepSmall; - pb.DisplayTitleFormat = "Анализ отверстий... [{0}]\\[{1}]"; - var progress = pb.CreateProgress(); - pb.MaxValue = openingsReal.Count; - var ct = pb.CreateCancellationToken(); - pb.Show(); - - int i = 0; - foreach(var openingReal in openingsReal) { - ct.ThrowIfCancellationRequested(); - progress.Report(i); - updateStatus.Invoke(openingReal); - if(openingReal.Status != OpeningModels.Enums.OpeningRealStatus.Correct) { - openingsRealViewModels.Add(new OpeningRealKrViewModel(openingReal)); - } - i++; - } - } - return openingsRealViewModels; - } - - /// - /// Возвращает коллекцию моделей представления чистовых отверстий, размещенных в активном документа АР - /// - /// Связи ВИС - /// Чистовые отверстия, размещенные в активном документе АР - private ICollection GetOpeningsRealArViewModels( - ICollection mepLinks, - ICollection openingsReal) { - - var openingsRealViewModels = new HashSet(); - - using(var pb = GetPlatformService()) { - pb.StepValue = _progressBarStepSmall; - pb.DisplayTitleFormat = "Анализ отверстий... [{0}\\{1}]"; - var progress = pb.CreateProgress(); - pb.MaxValue = openingsReal.Count; - var ct = pb.CreateCancellationToken(); - pb.Show(); - - var i = 0; - foreach(var openingReal in openingsReal) { - ct.ThrowIfCancellationRequested(); - progress.Report(i); - openingReal.UpdateStatus(mepLinks); - if(openingReal.Status != OpeningModels.Enums.OpeningRealStatus.Correct) { - openingsRealViewModels.Add(new OpeningRealArViewModel(openingReal)); - } - i++; - } - } - return openingsRealViewModels; - } private bool ModelCorrect(RevitRepository revitRepository) { var checker = new NavigatorCheckers(revitRepository); @@ -374,107 +153,81 @@ private KrNavigatorMode GetKrNavigatorMode() { /// /// Логика вывода окна навигатора по заданиям на отверстия в файле несущих конструкций /// - private void GetOpeningsTaskInDocumentKR(UIApplication uiApplication, RevitRepository revitRepository) { + private void GetOpeningsTaskInDocumentKR(IKernel kernel) { + kernel.Bind() + .ToMethod(c => { + var repo = c.Kernel.Get(); + return OpeningRealsKrConfig.GetOpeningConfig(repo.Doc); + }); + var navigatorMode = GetKrNavigatorMode(); - var config = OpeningRealsKrConfig.GetOpeningConfig(revitRepository.Doc); + var config = kernel.Get(); switch(navigatorMode) { case KrNavigatorMode.IncomingAr: { config.PlacementType = OpeningRealKrPlacementType.PlaceByAr; config.SaveProjectConfig(); - GetIncomingTasksFromArInDocKR(uiApplication, revitRepository); break; } case KrNavigatorMode.IncomingMep: { config.PlacementType = OpeningRealKrPlacementType.PlaceByMep; config.SaveProjectConfig(); - GetIncomingTasksFromMepInDocKR(uiApplication, revitRepository); break; } default: throw new OperationCanceledException(); } - } - /// - /// Логика вывода окна навигатора по заданиям на отверстия в файле инженерных систем - /// - private void GetOpeningsTaskInDocumentMEP(UIApplication uiApplication, RevitRepository revitRepository) { - if(!revitRepository.ContinueIfNotAllLinksLoaded()) { - throw new OperationCanceledException(); - } - var outcomingTasks = revitRepository.GetOpeningsMepTasksOutcoming(); - IList outcomingTasksIds = outcomingTasks.Select(task => task.Id).ToList(); - var mepElementsIds = revitRepository.GetMepElementsIds(); - var constructureLinks = GetLinkProviders(revitRepository); - var openingTaskOutcomingViewModels = GetMepTaskOutcomingViewModels( - outcomingTasks, - ref outcomingTasksIds, - mepElementsIds, - constructureLinks); - - var navigatorViewModel = new MepNavigatorForOutcomingTasksViewModel( - revitRepository, - openingTaskOutcomingViewModels); - - var window = new NavigatorMepOutcomingView() { Title = PluginName, DataContext = navigatorViewModel }; + kernel.Bind() + .To() + .InSingletonScope(); + kernel.Bind() + .ToSelf() + .InSingletonScope(); + kernel.Bind() + .ToSelf() + .InSingletonScope() + .WithPropertyValue(nameof(Window.DataContext), + c => c.Kernel.Get()) + .WithPropertyValue(nameof(Window.Title), PluginName); + + var window = kernel.Get(); + var uiApplication = kernel.Get(); var helper = new WindowInteropHelper(window) { Owner = uiApplication.MainWindowHandle }; - window.Show(); } - private ICollection GetMepTaskOutcomingViewModels( - ICollection outcomingTasks, - ref IList outcomingTasksIds, - ICollection mepElementsIds, - ICollection constructureLinks - ) { - var openingTaskOutcomingViewModels = new List(); - - using(var pb = GetPlatformService()) { - pb.StepValue = _progressBarStepLarge; - pb.DisplayTitleFormat = "Анализ заданий... [{0}\\{1}]"; - var progress = pb.CreateProgress(); - pb.MaxValue = outcomingTasks.Count; - var ct = pb.CreateCancellationToken(); - pb.Show(); - - int i = 0; - foreach(var outcomingTask in outcomingTasks) { - ct.ThrowIfCancellationRequested(); - progress.Report(i); - outcomingTask.UpdateStatus(ref outcomingTasksIds, mepElementsIds, constructureLinks); - openingTaskOutcomingViewModels.Add(new OpeningMepTaskOutcomingViewModel(outcomingTask)); - i++; - } - } - return openingTaskOutcomingViewModels; - } - - private ICollection GetLinkProviders(RevitRepository revitRepository) { - var constructureLinks = revitRepository.GetConstructureLinks(); - List providers = new List(); - - using(var pb = GetPlatformService()) { - pb.StepValue = 1; - pb.DisplayTitleFormat = "Анализ связей... [{0}\\{1}]"; - var progress = pb.CreateProgress(); - pb.MaxValue = constructureLinks.Count; - var ct = pb.CreateCancellationToken(); - pb.Show(); - int i = 0; - foreach(var constructureLink in constructureLinks) { - ct.ThrowIfCancellationRequested(); - providers.Add(new ConstructureLinkElementsProvider(revitRepository, constructureLink)); - progress.Report(i++); - } - } - return providers; + private void GetOpeningsTaskInDocumentMEP(IKernel kernel) { + kernel.Bind() + .To() + .InSingletonScope(); + kernel.Bind() + .To() + .InSingletonScope(); + kernel.Bind>() + .To() + .InTransientScope(); + + kernel.Bind() + .ToSelf() + .InSingletonScope(); + kernel.Bind() + .ToSelf() + .InSingletonScope() + .WithPropertyValue(nameof(Window.DataContext), + c => c.Kernel.Get()) + .WithPropertyValue(nameof(Window.Title), PluginName); + + var window = kernel.Get(); + var uiApplication = kernel.Get(); + var helper = new WindowInteropHelper(window) { Owner = uiApplication.MainWindowHandle }; + window.Show(); } /// /// Логика вывода окна навигатора по заданиям на отверстия в файле с неопределенным разделом проектирования /// - private void GetOpeningsTaskInDocumentNotDefined(RevitRepository revitRepository) { + private void GetOpeningsTaskInDocumentNotDefined(IKernel kernel) { + var revitRepository = kernel.Get(); TaskDialog.Show("BIM", $"Название файла: \"{revitRepository.GetDocumentName()}\" не удовлетворяет BIM стандарту А101. " + $"Скорректируйте название и запустите команду снова."); @@ -484,7 +237,8 @@ private void GetOpeningsTaskInDocumentNotDefined(RevitRepository revitRepository /// /// Логика вывода окна навигатора по заданиям на отверстия в координационном файле /// - private void GetOpeningsTaskInDocumentKoord(RevitRepository revitRepository) { + private void GetOpeningsTaskInDocumentKoord(IKernel kernel) { + var revitRepository = kernel.Get(); TaskDialog.Show( "BIM", $"Команда не может быть запущена в координационном файле \"{revitRepository.GetDocumentName()}\""); @@ -493,7 +247,7 @@ private void GetOpeningsTaskInDocumentKoord(RevitRepository revitRepository) { } /// - /// Перечисление режимов навигатора по заданиям на отверстиям в файле КР + /// Перечисление режимов навигатора по заданиям на отверстия в файле КР /// internal enum KrNavigatorMode { /// diff --git a/src/RevitOpeningPlacement/Models/Extensions/ISolidProviderExtension.cs b/src/RevitOpeningPlacement/Models/Extensions/ISolidProviderExtension.cs index df9d888ea..5b303be3a 100644 --- a/src/RevitOpeningPlacement/Models/Extensions/ISolidProviderExtension.cs +++ b/src/RevitOpeningPlacement/Models/Extensions/ISolidProviderExtension.cs @@ -38,6 +38,8 @@ internal static class ISolidProviderExtension { /// True, если разница объемов текущего и поданного ISolidProvider меньше, либо равна 1 см3, /// и если разница координат ограничивающих их меньше, либо равна ; /// Иначе False +#pragma warning disable 0618 + [Obsolete("Use ISolidProviderUtils.EqualsSolid", error: false)] internal static bool EqualsSolid(this ISolidProvider solidProvider, Solid otherSolid, double tolerance) { var thisSolid = solidProvider.GetSolid(); @@ -86,6 +88,7 @@ internal static bool SolidsIntersect(Solid firstSolid, BoundingBoxXYZ firstBBox, return false; } + [Obsolete("Use ISolidProviderUtils.IntersectsSolid", error: false)] internal static bool IntersectsSolid(this ISolidProvider thisSolidProvider, Solid otherSolid, BoundingBoxXYZ otherSolidBBox) { var thisSolid = thisSolidProvider.GetSolid(); var thisBBox = thisSolidProvider.GetTransformedBBoxXYZ(); @@ -117,7 +120,7 @@ internal static bool IntersectsSolidProvider(this ISolidProvider thisSolidProvid internal static bool EqualsSolid(this ISolidProvider solidProvider, Solid otherSolid) { return EqualsSolid(solidProvider, otherSolid, _toleranceDistance); } - +#pragma warning restore 0618 /// /// Проверяет на равенство текущего и поданного . diff --git a/src/RevitOpeningPlacement/Models/RealOpeningArPlacement/RealOpeningArPlacer.cs b/src/RevitOpeningPlacement/Models/RealOpeningArPlacement/RealOpeningArPlacer.cs index 8485ffe6d..c9dd8f1d4 100644 --- a/src/RevitOpeningPlacement/Models/RealOpeningArPlacement/RealOpeningArPlacer.cs +++ b/src/RevitOpeningPlacement/Models/RealOpeningArPlacement/RealOpeningArPlacer.cs @@ -49,6 +49,7 @@ public RealOpeningArPlacer(RevitRepository revitRepository) { /// /// Размещение чистового отверстия по одному заданию на отверстие из связи в одном хосте /// +#pragma warning disable 0618 public void PlaceSingleOpeningByOneTask() { Element host = _revitRepository.PickHostForRealOpening(); OpeningMepTaskIncoming openingTask = _revitRepository.PickSingleOpeningMepTaskIncoming(); @@ -68,10 +69,12 @@ public void PlaceSingleOpeningByOneTask() { transaction.Commit(); } } +#pragma warning restore 0618 /// /// Размещение объединенного чистового отверстия по одному или нескольким заданиям на отверстия из связи(ей) в одном хосте /// +#pragma warning disable 0618 public void PlaceUnitedOpeningByManyTasks() { Element host = _revitRepository.PickHostForRealOpening(); HashSet openingTasks = _revitRepository.PickManyOpeningMepTasksIncoming().Where(opening => opening.IntersectsSolid(host.GetSolid(), host.GetBoundingBox())).ToHashSet(); @@ -92,10 +95,12 @@ public void PlaceUnitedOpeningByManyTasks() { throw new OperationCanceledException(); } } +#pragma warning restore 0618 /// /// Размещение нескольких одиночных чистовых отверстий по нескольким заданиям на отверстия из связи(ей) без их объединения в одном хосте /// +#pragma warning disable 0618 public void PlaceSingleOpeningsInOneHost() { Element host = _revitRepository.PickHostForRealOpening(); HashSet openingTasks = _revitRepository.PickManyOpeningMepTasksIncoming().Where(opening => opening.IntersectsSolid(host.GetSolid(), host.GetBoundingBox())).ToHashSet(); @@ -118,10 +123,12 @@ public void PlaceSingleOpeningsInOneHost() { _revitRepository.ShowErrorMessage(sb.ToString()); } } +#pragma warning restore 0618 /// /// Размещение нескольких одиночных чистовых отверстий в выбранных хостах по всем заданиям на отверстия из связи(ей), которые пересекаются с этими хостами /// +#pragma warning disable 0618 public void PlaceSingleOpeningsInManyHosts() { ICollection hosts = _revitRepository.PickHostsForRealOpenings(); ICollection allOpeningTasks = _revitRepository.GetOpeningsMepTasksIncoming(); @@ -166,6 +173,7 @@ public void PlaceSingleOpeningsInManyHosts() { _revitRepository.ShowErrorMessage(sb.ToString()); } } +#pragma warning restore 0618 /// diff --git a/src/RevitOpeningPlacement/Models/RealOpeningKrPlacement/RealOpeningKrPlacer.cs b/src/RevitOpeningPlacement/Models/RealOpeningKrPlacement/RealOpeningKrPlacer.cs index beff31c84..e95da6c18 100644 --- a/src/RevitOpeningPlacement/Models/RealOpeningKrPlacement/RealOpeningKrPlacer.cs +++ b/src/RevitOpeningPlacement/Models/RealOpeningKrPlacement/RealOpeningKrPlacer.cs @@ -52,6 +52,7 @@ public void PlaceSingleOpeningByOneTask() { PlaceSingleOpeningByOneIncomingTask(host, openingTask); } +#pragma warning disable 0618 public void PlaceUnitedOpeningByManyTasks() { Element host = _revitRepository.PickHostForRealOpening(); ICollection openingTasks = PickOpeningsTaskIncoming(_config); @@ -60,7 +61,9 @@ public void PlaceUnitedOpeningByManyTasks() { openingTasks.Where(opening => opening.IntersectsSolid(host.GetSolid(), host.GetBoundingBox())).ToArray() ); } +#pragma warning restore 0618 +#pragma warning disable 0618 public void PlaceSingleOpeningsInOneHost() { Element host = _revitRepository.PickHostForRealOpening(); ICollection openingTasks = PickOpeningsTaskIncoming(_config); @@ -69,6 +72,7 @@ public void PlaceSingleOpeningsInOneHost() { openingTasks.Where(opening => opening.IntersectsSolid(host.GetSolid(), host.GetBoundingBox())).ToArray() ); } +#pragma warning restore 0618 public void PlaceSingleOpeningsInManyHosts() { ICollection hosts = _revitRepository.PickHostsForRealOpenings(); @@ -125,6 +129,7 @@ private ICollection GetAllOpeningsTaskIncoming(OpeningReal /// /// Размещение чистового отверстия КР по одному заданию на отверстие из связи в одном хосте /// +#pragma warning disable 0618 private void PlaceSingleOpeningByOneIncomingTask(Element host, IOpeningTaskIncoming openingTask) { using(var transaction = _revitRepository.GetTransaction("Размещение одиночного отверстия")) { try { @@ -141,6 +146,7 @@ private void PlaceSingleOpeningByOneIncomingTask(Element host, IOpeningTaskIncom transaction.Commit(); } } +#pragma warning restore 0618 /// /// Размещение объединенного чистового отверстия КР по одному или нескольким заданиям на отверстия из связи(ей) в одном хосте @@ -188,6 +194,7 @@ private void PlaceSingleOpeningsInOneHostByIncomingTasks(Element host, ICollecti /// /// Размещение нескольких одиночных чистовых отверстий КР в выбранных хостах по всем заданиям на отверстия из связи(ей), которые пересекаются с этими хостами /// +#pragma warning disable 0618 private void PlaceSingleOpeningsInManyHostsByIncomingTasks(ICollection hosts, ICollection allOpeningTasks) { StringBuilder sb = new StringBuilder(); using(var transaction = _revitRepository.GetTransaction("Одиночные отверстия в нескольких хостах")) { @@ -226,6 +233,7 @@ private void PlaceSingleOpeningsInManyHostsByIncomingTasks(ICollection _revitRepository.ShowErrorMessage(sb.ToString()); } } +#pragma warning restore 0618 /// diff --git a/src/RevitOpeningPlacement/Models/RevitRepository.cs b/src/RevitOpeningPlacement/Models/RevitRepository.cs index 669f10c69..2f0a49cdb 100644 --- a/src/RevitOpeningPlacement/Models/RevitRepository.cs +++ b/src/RevitOpeningPlacement/Models/RevitRepository.cs @@ -482,10 +482,10 @@ public FamilyInstance CreateInstance(XYZ point, FamilySymbol familySymbol, Eleme } public FamilyInstance CreateInstance(FamilySymbol type, XYZ point, Level level) { + if(!type.IsActive) { + type.Activate(); + } if(level != null) { - if(!type.IsActive) { - type.Activate(); - } point = point - XYZ.BasisZ * level.ProjectElevation; return _document.Create.NewFamilyInstance(point, type, level, StructuralType.NonStructural); } diff --git a/src/RevitOpeningPlacement/OpeningModels/OpeningArTaskIncoming.cs b/src/RevitOpeningPlacement/OpeningModels/OpeningArTaskIncoming.cs index b8c99ec00..7da98001f 100644 --- a/src/RevitOpeningPlacement/OpeningModels/OpeningArTaskIncoming.cs +++ b/src/RevitOpeningPlacement/OpeningModels/OpeningArTaskIncoming.cs @@ -207,6 +207,7 @@ private ICollection GetIntersectingStructureElementsIds( /// Солид текущего задания на отверстие в координатах активного файла - получателя заданий /// /// Бокс текущего задания на отверстие в координатах активного файла - получателя заданий +#pragma warning disable 0618 private ICollection GetIntersectingOpeningsIds( ICollection realOpenings, Solid thisOpeningSolid, @@ -223,5 +224,6 @@ private ICollection GetIntersectingOpeningsIds( } } } +#pragma warning restore 0618 } } diff --git a/src/RevitOpeningPlacement/OpeningModels/OpeningMepTaskIncoming.cs b/src/RevitOpeningPlacement/OpeningModels/OpeningMepTaskIncoming.cs index f10c139f5..3cdd703e8 100644 --- a/src/RevitOpeningPlacement/OpeningModels/OpeningMepTaskIncoming.cs +++ b/src/RevitOpeningPlacement/OpeningModels/OpeningMepTaskIncoming.cs @@ -336,6 +336,7 @@ private ICollection GetIntersectingStructureElementsIds(Solid thisOpe /// Коллекция чистовых отверстий из активного документа ревита /// Солид текущего задания на отверстие в координатах активного файла - получателя заданий /// Бокс текущего задания на отверстие в координатах активного файла - получателя заданий +#pragma warning disable 0618 private ICollection GetIntersectingOpeningsIds(ICollection realOpenings, Solid thisOpeningSolid, BoundingBoxXYZ thisOpeningBBox) { if((thisOpeningSolid is null) || (thisOpeningSolid.Volume <= 0)) { return Array.Empty(); @@ -349,6 +350,7 @@ private ICollection GetIntersectingOpeningsIds(ICollection /// Возвращает Id элемента конструкции, который наиболее похож на хост для задания на отверстие. diff --git a/src/RevitOpeningPlacement/OpeningModels/OpeningMepTaskOutcoming.cs b/src/RevitOpeningPlacement/OpeningModels/OpeningMepTaskOutcoming.cs index 9d9fd5cee..4c8a67877 100644 --- a/src/RevitOpeningPlacement/OpeningModels/OpeningMepTaskOutcoming.cs +++ b/src/RevitOpeningPlacement/OpeningModels/OpeningMepTaskOutcoming.cs @@ -7,7 +7,6 @@ using dosymep.Bim4Everyone; using dosymep.Bim4Everyone.SystemParams; using dosymep.Revit; -using dosymep.Revit.Geometry; using RevitClashDetective.Models.Extensions; @@ -37,14 +36,11 @@ internal class OpeningMepTaskOutcoming : ISolidProvider, IEquatable private static readonly double _volumeTolerance = _distance3dTolerance * _distance3dTolerance * _distance3dTolerance; - /// - /// Кэш для хранения результата метода - /// - private (ICollection Value, DateTime CacheTime) _intersectingMepElementsCache; /// /// Создает экземпляр класса - /// Примечание: конструктор не обновляет свойство . Для обновления этого свойства нужно вызвать + /// Примечание: конструктор не обновляет свойство и . + /// Для обновления этого свойства нужно вызвать /// /// Экземпляр семейства задания на отверстие, расположенного в текущем документе Revit public OpeningMepTaskOutcoming(FamilyInstance openingTaskOutcoming) { @@ -114,8 +110,9 @@ public OpeningMepTaskOutcoming(FamilyInstance openingTaskOutcoming) { /// /// Элемент из связи, который можно считать хостом текущего задания на отверстие. /// "Можно считать" - потому что текущее задание на отверстие может пересекаться с несколькими конструкциями из связи, из которых определяется один элемент + /// Для обновления использовать /// - public Element Host { get; private set; } + public Element Host { get; set; } /// /// Флаг, обозначающий, удален ли экземпляр семейства задания на отверстие из проекта @@ -124,9 +121,9 @@ public OpeningMepTaskOutcoming(FamilyInstance openingTaskOutcoming) { /// /// Флаг, обозначающий статус исходящего задания на отверстие - /// Для обновления использовать + /// Для обновления использовать /// - public OpeningTaskOutcomingStatus Status { get; private set; } = OpeningTaskOutcomingStatus.NotActual; + public OpeningTaskOutcomingStatus Status { get; set; } = OpeningTaskOutcomingStatus.NotActual; /// /// Тип проема @@ -141,72 +138,6 @@ public FamilyInstance GetFamilyInstance() { return _familyInstance; } - /// - /// Обновляет задания на отверстие. - /// - /// Обновление происходит по соотношению объема пересекаемого Solid задания на отверстие элементами инженерных систем из файла задания на отверстие и исходным Solid этого задания на отверстие. - /// Если соотношение объемов >= 0.95, то статус , - /// Если соотношение объемов в диапазоне [0.2, 0.95), то статус , - /// Если соотношение объемов в диапазоне (0; 0.2), то статус , - /// Если соотношение объемов равно 0, то статус . - /// - /// Если же объем Solid самого задания на отверстие равен 0, то статус - /// - /// Коллекция Id экземпляров семейств заданий на отверстия из активного файла для проверки - /// Коллекция Id всех элементов инженерных систем из активного файла - public void UpdateStatus( - ref IList openingsOutcomingTasksIdsForChecking, - ICollection allMepElementsIds, - ICollection constructureLinkElementsProviders) { - - if(openingsOutcomingTasksIdsForChecking is null) { - throw new ArgumentNullException(nameof(openingsOutcomingTasksIdsForChecking)); - } - if(allMepElementsIds is null) { - throw new ArgumentNullException(nameof(allMepElementsIds)); - } - if(!IsRemoved) { - var openingSolid = GetSolid(); - if((openingSolid is null) || (openingSolid.Volume < _volumeTolerance)) { - Status = OpeningTaskOutcomingStatus.Invalid; - return; - } - - if(IsManuallyPlaced()) { - FindAndSetHost(openingSolid, constructureLinkElementsProviders); - Status = OpeningTaskOutcomingStatus.ManuallyPlaced; - return; - } - - if(ThisOpeningTaskIsNotActual(openingSolid, constructureLinkElementsProviders, allMepElementsIds)) { - Status = OpeningTaskOutcomingStatus.NotActual; - return; - } - - var intersectingOpeningIds = GetIntersectingOpeningsTasks(openingSolid, openingsOutcomingTasksIdsForChecking); - if(intersectingOpeningIds.Count > 0) { - Status = OpeningTaskOutcomingStatus.Intersects; - return; - } else { - openingsOutcomingTasksIdsForChecking.Remove(Id); - } - - try { - Solid openingSolidAfterIntersection = GetOpeningAndMepsSolidsDifference(openingSolid, allMepElementsIds); - if(openingSolidAfterIntersection is null) { - Status = OpeningTaskOutcomingStatus.Invalid; - return; - } - double volumeRatio = (openingSolid.Volume - openingSolidAfterIntersection.Volume) / openingSolid.Volume; - Status = GetOpeningTaskOutcomingStatus(volumeRatio); - - } catch(InvalidOperationException) { - Status = OpeningTaskOutcomingStatus.Invalid; - return; - } - } - } - /// /// Возвращает Solid экземпляра семейства задания на отверстие с трансформированными координатами, /// если элемент был удален из документа, будет возвращено значение по умолчанию @@ -239,6 +170,7 @@ public BoundingBoxXYZ GetTransformedBBoxXYZ() { /// /// Существующие задания на отверстия в проекте /// Исключение, если обязательный параметр null +#pragma warning disable 0618 public bool IsAlreadyPlaced(ICollection placedOpenings) { if(IsRemoved || placedOpenings.Count == 0) { return false; @@ -258,6 +190,7 @@ public bool IsAlreadyPlaced(ICollection placedOpenings) } return false; } +#pragma warning restore 0618 public override bool Equals(object obj) { return !IsRemoved && (obj is OpeningMepTaskOutcoming otherTask) && Equals(otherTask); @@ -471,389 +404,5 @@ private string GetFamilyInstanceStringParamValueOrEmpty(string paramName) { } return value; } - - /// - /// Возвращает статус задания на отверстие по отношению объема пересечения задания на отверстие с элементом инженерной системы к исходному объему задания на отверстие - /// - /// Исключение, если коэффициент отношения объемов меньше 0 или больше 1 - private OpeningTaskOutcomingStatus GetOpeningTaskOutcomingStatus(double volumeRatio) { - if((volumeRatio < 0) || (volumeRatio > 1)) { - throw new ArgumentOutOfRangeException($"Значение параметра {nameof(volumeRatio)} должно находиться в интервале [0; 1]"); - } - OpeningTaskOutcomingStatus status; - if(0.95 <= volumeRatio) { - status = OpeningTaskOutcomingStatus.TooSmall; - } else if((0.2 <= volumeRatio) && (volumeRatio < 0.95)) { - status = OpeningTaskOutcomingStatus.Correct; - } else if((0.001 <= volumeRatio) && (volumeRatio < 0.2)) { - status = OpeningTaskOutcomingStatus.TooBig; - } else { - status = OpeningTaskOutcomingStatus.NotActual; - } - return status; - } - - /// - /// Возвращает Id всех исходящих заданий на отверстия, которые пересекаются с данным заданием на отверстие из текущего документа - /// - /// Солид текущего задания на отверстие - /// Id всех экземпляров семейств заданий на отверстия из активного документа ревита для проверки - /// Коллекция Id экземпляров семейств исходящих заданий на отверстия, которые пересекаются с текущим заданием на отверстие - private ICollection GetIntersectingOpeningsTasks(Solid thisOpeningTaskSolid, ICollection allOpeningTasksInDoc) { - if((thisOpeningTaskSolid is null) || (thisOpeningTaskSolid.Volume <= 0) || (!allOpeningTasksInDoc.Any())) { - return Array.Empty(); - } else { - return new FilteredElementCollector(GetDocument(), allOpeningTasksInDoc) - .Excluding(new ElementId[] { Id }) - .WherePasses(new BoundingBoxIntersectsFilter(thisOpeningTaskSolid.GetOutline())) - .WherePasses(new ElementIntersectsSolidFilter(thisOpeningTaskSolid)) - .ToElementIds(); - } - } - - /// - /// Проверяет, размещено ли текущее задание на отверстие вручную - /// - /// True, если присутствует и включен параметр , иначе False - private bool IsManuallyPlaced() { - if(!IsRemoved) { - try { - return _familyInstance.GetSharedParamValue(RevitRepository.OpeningIsManuallyPlaced) == 1; - - } catch(ArgumentException) { - return false; - } - } else { - return false; - } - } - - /// - /// Назначает хост задания на отверстие - /// - private void FindAndSetHost(Solid thisOpeningTaskSolid, ICollection constructureLinkElementsProviders) { - foreach(var link in constructureLinkElementsProviders) { - var hostConstructions = GetHostConstructionsForThisOpeningTask(thisOpeningTaskSolid, link, out _); - if(hostConstructions.Count > 0) { - break; - } - } - } - - - /// - /// Возвращает список солидов элементов инженерных систем, которые пересекаются с данным заданием на отверстие, из текущего документа - /// - /// Солид текущего задания на отверстие - /// Коллекция Id всех элементов инженерных систем из файла, в котором размещено задание на отверстие - private ICollection GetIntersectingMepSolids(Solid thisOpeningTaskSolid, ICollection allMepElementsIds) { - if((thisOpeningTaskSolid is null) || (thisOpeningTaskSolid.Volume <= 0)) { - return Array.Empty(); - } else { - return GetIntersectingMepElementsIds(thisOpeningTaskSolid, allMepElementsIds) - .Select(elementId => GetDocument().GetElement(elementId).GetSolid()) - .ToHashSet(); - } - } - - /// - /// Возвращает список Id элементов инженерных систем, которые пересекаются с данным заданием на отверстие, из текущего документа - /// - private ICollection GetIntersectingMepElementsIds(Solid thisOpeningSolid, ICollection allMepElementsIds) { - if(!IsRemoved && (thisOpeningSolid != null) && (allMepElementsIds != null) && allMepElementsIds.Any()) { - if((_intersectingMepElementsCache.Value != null) && (_intersectingMepElementsCache.CacheTime.CompareTo(DateTime.Now) >= 0)) { - return _intersectingMepElementsCache.Value; - } else { - _intersectingMepElementsCache.Value = new FilteredElementCollector(GetDocument(), allMepElementsIds) - .WherePasses(new BoundingBoxIntersectsFilter(thisOpeningSolid.GetOutline())) - .WherePasses(new ElementIntersectsSolidFilter(thisOpeningSolid)) - .ToElementIds(); - _intersectingMepElementsCache.CacheTime = DateTime.Now.AddSeconds(1); - return _intersectingMepElementsCache.Value; - } - } else { - return Array.Empty(); - } - } - - /// - /// Возвращает солид, образованный вычитанием из исходного солида задания на отверстие всех пересекающих его элементов инженерных систем - /// - /// Солид текущего задания на отверстие - /// Коллекция Id всех элементов инженерных систем из файла, в котором размещено задание на отверстие - /// Исключение, если не удалось выполнить вычитание солида отверстия и солида элемента инженерной системы - private Solid GetOpeningAndMepsSolidsDifference(Solid thisOpeningSolid, ICollection allMepElementsIds) { - var intersectingMepSolids = GetIntersectingMepSolids(thisOpeningSolid, allMepElementsIds); - Solid openingSolidAfterIntersection = thisOpeningSolid; - foreach(var mepSolid in intersectingMepSolids) { - //здесь определяется солид, образованный вычитанием из исходного солида задания на отверстие всех пересекающих его элементов инженерных систем - try { - openingSolidAfterIntersection = BooleanOperationsUtils.ExecuteBooleanOperation(openingSolidAfterIntersection, mepSolid, BooleanOperationsType.Difference); - } catch(Autodesk.Revit.Exceptions.InvalidOperationException) { - try { - // если один солид касается другого солида изнутри, то будет исключение InvalidOperationException - // здесь производится попытка слегка подвинуть один из солидов, чтобы избежать этого касания - double coordinateOffset = 0.001; - var mepTransformedSolid = SolidUtils.CreateTransformed(mepSolid, Transform.CreateTranslation(new XYZ(coordinateOffset, coordinateOffset, coordinateOffset))); - openingSolidAfterIntersection = BooleanOperationsUtils.ExecuteBooleanOperation(openingSolidAfterIntersection, mepTransformedSolid, BooleanOperationsType.Difference); - - } catch(Autodesk.Revit.Exceptions.InvalidOperationException) { - try { - // здесь производится попытка слегка подвинуть один из солидов в другую сторону - double coordinateOffset = -0.001; - var mepTransformedSolid = SolidUtils.CreateTransformed(mepSolid, Transform.CreateTranslation(new XYZ(coordinateOffset, coordinateOffset, coordinateOffset))); - openingSolidAfterIntersection = BooleanOperationsUtils.ExecuteBooleanOperation(openingSolidAfterIntersection, mepTransformedSolid, BooleanOperationsType.Difference); - - } catch(Autodesk.Revit.Exceptions.InvalidOperationException) { - throw new InvalidOperationException(); - } - } - } - } - return openingSolidAfterIntersection; - } - - /// - /// Проверяет, является ли данное задание на отверстие НЕ актуальным. Если задание не актуально, возвращается False, иначе True. - /// Метод может установить только НЕкорректность задания, но корректность абсолютно точно подтвердить не может. - /// - /// Солид текущего задания на отверстие - /// Коллекция объектов-оберток связанных файлов с элементами конструкций: стенами, перекрытиями и т.п. - /// True - задание точно некорректно; False - задание не некорректно, но утверждать, что оно корректно нельзя - private bool ThisOpeningTaskIsNotActual( - Solid thisOpeningTaskSolid, - ICollection constructureLinkElementsProviders, - ICollection allMepElementsIds - ) { - - if((thisOpeningTaskSolid is null) || (thisOpeningTaskSolid.Volume <= 0)) { - // геометрия задания на отверстие не корректна - задание не актуально - return true; - } - if(constructureLinkElementsProviders is null) { throw new ArgumentNullException(nameof(constructureLinkElementsProviders)); } - if(allMepElementsIds is null) { throw new ArgumentNullException(nameof(allMepElementsIds)); } - - bool mepElementsNotIntersectThisTask = false; - var mepElementsIntersectingThisTask = GetIntersectingMepElementsIds(thisOpeningTaskSolid, allMepElementsIds); - if(mepElementsIntersectingThisTask.Count == 0) { - // задание на отверстие не пересекается ни с одним элементом инженерной системы - задание не актуально - mepElementsNotIntersectThisTask = true; - } - - // проверка на то, что: - // во-первых есть конструкции в связанных файлах, внутри которых расположено задание на отверстие, - // во-вторых, что ни один элемент ВИС, проходящий через текущее задание на отверстие не пересекается с этими конструкциями - foreach(var link in constructureLinkElementsProviders) { - var hostConstructions = GetHostConstructionsForThisOpeningTask(thisOpeningTaskSolid, link, out ICollection intersectingOpenings); - if(hostConstructions.Count > 0) { - return mepElementsNotIntersectThisTask - || MepElementsIntersectConstructionsOrOpenings( - thisOpeningTaskSolid, - mepElementsIntersectingThisTask, - hostConstructions, - intersectingOpenings, - link - ); - } else { - // если не найдены конструкции, которые можно считать хостами текущего задания на отверстие, - // то либо задание на отверстие висит в воздухе, либо задание на отверстие пересекается с другой связью - continue; - } - } - - // корректная ситуация не найдена, отверстие считается не актуальным - return true; - } - - /// - /// Назначает свойство хоста текущего задания на отверстие - /// - /// Связанный файл с конструкциями - /// Элементы из связанного файла с конструкциями, которые пересекаются с текущим заданием на отверстие - /// Солид текущего задания на отверстие в координатах связанного файла - private void SetHostConstruction( - IConstructureLinkElementsProvider link, - ICollection intersectingLinkedElements, - Solid thisOpeningTaskSolidInLinkCoordinates) { - - if((link != null) - && intersectingLinkedElements.Any() - && (thisOpeningTaskSolidInLinkCoordinates != null) - && (thisOpeningTaskSolidInLinkCoordinates.Volume >= _volumeTolerance)) { - - // поиск элемента конструкции, с которым пересечение текущего задания на отверстие имеет наибольший объем - double halfOpeningTaskVolume = thisOpeningTaskSolidInLinkCoordinates.Volume / 2; - var elements = intersectingLinkedElements.Select(id => link.Document.GetElement(id)).ToHashSet(); - Element hostCandidate = elements.FirstOrDefault(); - double intersectingVolumePrevious = 0; - foreach(Element element in elements) { - var structureSolid = element?.GetSolid(); - if((structureSolid != null) && (structureSolid.Volume > _volumeTolerance)) { - try { - double intersectingVolumeCurrent - = BooleanOperationsUtils.ExecuteBooleanOperation( - thisOpeningTaskSolidInLinkCoordinates, - structureSolid, - BooleanOperationsType.Intersect)?.Volume - ?? 0; - if(intersectingVolumeCurrent >= halfOpeningTaskVolume) { - Host = element; - return; - } - if(intersectingVolumeCurrent > intersectingVolumePrevious) { - intersectingVolumePrevious = intersectingVolumeCurrent; - hostCandidate = element; - } - } catch(Autodesk.Revit.Exceptions.InvalidOperationException) { - continue; - } - } - } - Host = hostCandidate; - } else { - return; - } - } - - /// - /// Возвращает коллекцию Id элементов конструкций из связанного файла, которые пересекаются с текущим заданием на отверстие - /// - /// Солид текущего задания на отверстие в координатах связанного файла - /// Связанный файл для проверки на пересечение - private ICollection GetIntersectingLinkConstructionElementsIds(Solid thisOpeningSolidInLinkCoordinates, IConstructureLinkElementsProvider constructureLinkElementsProvider) { - ICollection ids = constructureLinkElementsProvider.GetConstructureElementIds(); - if(!ids.Any()) { - return Array.Empty(); - } - return new FilteredElementCollector(constructureLinkElementsProvider.Document, ids) - .WherePasses(new BoundingBoxIntersectsFilter(thisOpeningSolidInLinkCoordinates.GetOutline())) - .WherePasses(new ElementIntersectsSolidFilter(thisOpeningSolidInLinkCoordinates)) - .ToElementIds(); - } - - /// - /// Поиск конструкций из связанного файла, в которых расположено задание на отверстие. - /// Если конструкции будут найдены, то также будет вызован метод - /// - /// Солид текущего задания на отверстие - /// Связь с конструкциями - /// Чистовые отверстия из связи, которые пересекаются с солидом текущего задания на отверстие - /// - /// Коллекция Id конструкций (стен или перекрытий) из связанного файла, которые пересекаются с солидом текущего задания на отверстие - /// или коллекция Id конструкций, которые являются хостами чистовых отверстий, с которыми пересекается солид текущего задания на отверстие - /// - private ICollection GetHostConstructionsForThisOpeningTask(Solid thisOpeningTaskSolid, IConstructureLinkElementsProvider link, out ICollection intersectingOpeningsReal) { - var thisSolidInLinkCoordinates = SolidUtils.CreateTransformed(thisOpeningTaskSolid, link.DocumentTransform.Inverse); - - intersectingOpeningsReal = GetIntersectingLinkOpeningsReal(link, thisOpeningTaskSolid); - - // поиск конструкций из связи, в которых находится текущее задание на отверстие - var intersectingConstructions = GetIntersectingLinkConstructionElementsIds(thisSolidInLinkCoordinates, link); - if(intersectingConstructions.Count == 0) { - // задание на отверстие не пересекается с конструкциями из связей - - // поиск чистовых отверстий из связи, которые пересекаются с текущим заданием на отверстие - if(intersectingOpeningsReal.Count > 0) { - // пересечение с чистовыми отверстиями из связей найдено - - // поиск элементов конструкций - основ чистовых отверстий из связей - intersectingConstructions = intersectingOpeningsReal.Select(opening => opening.GetHost().Id).Distinct().ToHashSet(); - } - } else { - // если задание на отверстие пересекается с конструкциями из связей, то будем считать, что даже если задание также пересекается с чистовыми отверстиями, - // то хосты этих чистовых отверстий - это и есть конструкции, с которым пересекается само задание на отверстие. - // Поэтому можно не делать долгую проверку на поиск пересекающих чистовых отверстий (см. ниже). - - // Если эта логика окажется неверной, то надо убрать комментарии ниже - //intersectingOpeningsReal = GetIntersectingLinkOpeningsReal(link, thisOpeningTaskSolid); - //if(intersectingOpeningsReal.Count > 0) { - - // var openingsHosts = intersectingOpeningsReal.Select(opening => opening.GetHost().Id).Distinct().ToHashSet(); - // var constructions = new List(intersectingConstructions); - // constructions.AddRange(openingsHosts); - // intersectingConstructions = constructions; - //} - } - if(intersectingConstructions.Count > 0) { - SetHostConstruction(link, intersectingConstructions, thisSolidInLinkCoordinates); - } - - return intersectingConstructions; - } - - /// - /// Поиск чистовых отверстий из связанного файла, в которых расположено задание на отверстие - /// - /// Связь с конструкциями и чистовыми отверстиями - /// Солид текущего задания на отверстие - /// Коллекция чистовых отверстий из связи, которые пересекаются с солидом текущего задания на отверстие - private ICollection GetIntersectingLinkOpeningsReal(IConstructureLinkElementsProvider link, Solid thisOpeningTaskSolid) { - var thisSolidInLinkCoordinates = SolidUtils.CreateTransformed(thisOpeningTaskSolid, link.DocumentTransform.Inverse); - var thisBBoxInLinkCoordinates = GetTransformedBBoxXYZ().TransformBoundingBox(link.DocumentTransform.Inverse); - return link.GetOpeningsReal().Where(realOpening => realOpening.IntersectsSolid(thisSolidInLinkCoordinates, thisBBoxInLinkCoordinates)).ToHashSet(); - } - - /// - /// Проверяет, пересекаются ли элементы ВИС из текущего файла с конструкциями или чистовыми отверстиями, причем место пересечения находится вне солида задания на отверстие. - /// При этом проверяемые элементы ВИС, а также проверяемые конструкции и чистовые отверстия из связи должны пересекать солид текущего задания на отверстие - /// - /// Солид текущего задания на отверстие - /// Элементы ВИС из текущего файла с заданиями на отверстия, которые пересекают солид текущего задания на отверстие - /// Конструкции из , которые пересекаются с текущим заданием на отверстие - /// Чистовые отверстия из , которые пересекаются с текущим заданием на отверстие - /// Связанный файл с конструкциями - /// True, если найдено пересечение элементов ВИС и конструкций (или чистовых отверстий) вне солида задания на отверстие, иначе False - private bool MepElementsIntersectConstructionsOrOpenings( - Solid thisOpeningTaskSolid, - ICollection intersectingMepElementsIds, - ICollection intersectingLinkConstructions, - ICollection intersectingLinkOpeningsReal, - IConstructureLinkElementsProvider link - ) { - if(!IsRemoved - && (link != null) - && (intersectingMepElementsIds != null) - && (intersectingMepElementsIds.Count > 0) - && (intersectingLinkConstructions != null) - && (intersectingLinkConstructions.Count > 0) - && (intersectingLinkOpeningsReal != null) - && (thisOpeningTaskSolid != null) - && (thisOpeningTaskSolid.Volume > 0)) { - - var thisSolidInLinkCoordinates = SolidUtils.CreateTransformed(thisOpeningTaskSolid, link.DocumentTransform.Inverse); - - // получение объединенного солида для элементов ВИС в координатах текущего файла с заданиями на отверстия - var mepElements = intersectingMepElementsIds.Select(mepId => GetDocument().GetElement(mepId)).ToHashSet(); - var mepSolids = mepElements.Select(el => el.GetSolid()).Where(solid => (solid != null) && (solid.Volume > 0)).ToList(); - var mepUnitedSolid = RevitClashDetective.Models.Extensions.ElementExtensions.UniteSolids(mepSolids); - if((mepUnitedSolid is null) || (mepUnitedSolid.Volume < _volumeTolerance)) { - return false; - } - - try { - // трансформация объединенного солида элементов ВИС в координаты связанного файла с конструкциями - var mepSolidInLinkCoordinates = SolidUtils.CreateTransformed(mepUnitedSolid, link.DocumentTransform.Inverse); - // вычитание из объединенного солида элементов ВИС солида задания на отверстие, - // чтобы исключить из проверки места пересечения элементов ВИС с конструкциями внутри тела солида задания на отверстие - var mepSolidMinusOpeningTask = BooleanOperationsUtils.ExecuteBooleanOperation(mepSolidInLinkCoordinates, thisSolidInLinkCoordinates, BooleanOperationsType.Difference); - - // поиск конструкций (стен и перекрытий) и чистовых отверстий из связанного файла, которые пересекаются с проходящими через задание на отверстие элементами ВИС из текущего файла, - // с учетом того, что место пересечения находится вне тела задания на отверстие. - BoundingBoxXYZ mepElementsBBox = mepElements.Select(el => el.GetBoundingBox()).GetCommonBoundingBox().TransformBoundingBox(link.DocumentTransform.Inverse); - bool mepElementsIntersectConstructions = - new FilteredElementCollector(link.Document, intersectingLinkConstructions) - .WherePasses(new ElementIntersectsSolidFilter(mepSolidMinusOpeningTask)) - .Any(); - bool mepElementsIntersectOpeningsReal = intersectingLinkOpeningsReal - .Any(openingReal => openingReal.IntersectsSolid(mepSolidMinusOpeningTask, mepElementsBBox)); - return mepElementsIntersectConstructions || mepElementsIntersectOpeningsReal; - - } catch(Autodesk.Revit.Exceptions.InvalidOperationException) { - return false; - } - } else { - return false; - } - } } } diff --git a/src/RevitOpeningPlacement/OpeningModels/OpeningRealKr.cs b/src/RevitOpeningPlacement/OpeningModels/OpeningRealKr.cs index 08ccdcef6..e6b588de7 100644 --- a/src/RevitOpeningPlacement/OpeningModels/OpeningRealKr.cs +++ b/src/RevitOpeningPlacement/OpeningModels/OpeningRealKr.cs @@ -137,6 +137,7 @@ private double GetSolidsVolumesRatio(Solid thisOpeningRealSolid, Solid thisOpeni /// /// Флаг, показывающий, /// полностью ли текущее чистовое отверстие закрывает собой пересекающие его задания на отверстия +#pragma warning disable 0618 private Solid SubtractLinkOpenings( IConstructureLinkElementsProvider link, Solid thisOpeningSolidForSubtraction, @@ -176,6 +177,7 @@ private Solid SubtractLinkOpenings( } return solidAfterSubtraction; } +#pragma warning restore 0618 /// /// Возвращает статус текущего чистового отверстия по коэффициенту пересекаемого объема. diff --git a/src/RevitOpeningPlacement/Services/ConstantsProvider.cs b/src/RevitOpeningPlacement/Services/ConstantsProvider.cs new file mode 100644 index 000000000..f76bfb958 --- /dev/null +++ b/src/RevitOpeningPlacement/Services/ConstantsProvider.cs @@ -0,0 +1,40 @@ +namespace RevitOpeningPlacement.Services { + internal class ConstantsProvider : IConstantsProvider { + /// + /// Точность для определения расстояний и координат 1 мм в футах + /// + private const double _toleranceDistance = 1 / 304.8; + + /// + /// Точность для определения объемов 1 см3 в футах + /// + private const double _toleranceVolume = (10 / 304.8) * (10 / 304.8) * (10 / 304.8); + + /// + /// Минимальное значение габарита задания на отверстие в футах (~5 мм) + /// + private const double _minGeometryFeetSize = 0.015; + + /// + /// Процент толерантности объемов солидов + /// + private const double _toleranceVolumePercentage = 0.01; + + private const int _progressBarStepLarge = 100; + + private const int _progressBarStepSmall = 25; + + + public double ToleranceDistanceFeet => _toleranceDistance; + + public double ToleranceVolumeFeetCube => _toleranceVolume; + + public double ToleranceVolumePercentage => _toleranceVolumePercentage; + + public double OpeningTaskSizeMinValueFeet => _minGeometryFeetSize; + + public int ProgressBarStepLarge => _progressBarStepLarge; + + public int ProgressBarStepSmall => _progressBarStepSmall; + } +} diff --git a/src/RevitOpeningPlacement/Services/IConstantsProvider.cs b/src/RevitOpeningPlacement/Services/IConstantsProvider.cs new file mode 100644 index 000000000..e03703b94 --- /dev/null +++ b/src/RevitOpeningPlacement/Services/IConstantsProvider.cs @@ -0,0 +1,36 @@ +namespace RevitOpeningPlacement.Services { + /// + /// Сервис с константами, которые использует плагин + /// + public interface IConstantsProvider { + /// + /// Точность для определения расстояний в единицах Revit + /// + double ToleranceDistanceFeet { get; } + + /// + /// Точность для определения объемов в единицах Revit + /// + double ToleranceVolumeFeetCube { get; } + + /// + /// Процент толерантности объемов солидов в долях от 1 + /// + double ToleranceVolumePercentage { get; } + + /// + /// Минимальное значение габарита задания на отверстие в футах + /// + double OpeningTaskSizeMinValueFeet { get; } + + /// + /// Укрупненное значение шага прогресса + /// + int ProgressBarStepLarge { get; } + + /// + /// Уменьшенное значение шага прогресса + /// + int ProgressBarStepSmall { get; } + } +} diff --git a/src/RevitOpeningPlacement/Services/IOpeningInfoUpdater.cs b/src/RevitOpeningPlacement/Services/IOpeningInfoUpdater.cs new file mode 100644 index 000000000..b709d663b --- /dev/null +++ b/src/RevitOpeningPlacement/Services/IOpeningInfoUpdater.cs @@ -0,0 +1,14 @@ +using RevitOpeningPlacement.Models.Interfaces; + +namespace RevitOpeningPlacement.Services { + /// + /// Сервис для обновления информации по заданиям на отверстия и чистовым отверстиям + /// + internal interface IOpeningInfoUpdater where T : class, ISolidProvider { + /// + /// Обновляет информацию по заданному элементу + /// + /// Задание на отверстие или чистовое отверстие + void UpdateInfo(T opening); + } +} diff --git a/src/RevitOpeningPlacement/Services/ISolidProviderUtils.cs b/src/RevitOpeningPlacement/Services/ISolidProviderUtils.cs new file mode 100644 index 000000000..69a5c17ce --- /dev/null +++ b/src/RevitOpeningPlacement/Services/ISolidProviderUtils.cs @@ -0,0 +1,28 @@ +using Autodesk.Revit.DB; + +using RevitOpeningPlacement.Models.Interfaces; + +namespace RevitOpeningPlacement.Services { + internal interface ISolidProviderUtils { + /// + /// Проверяет на равенство 2 тела.
+ /// Под равенством понимается приближенное равенство объемов и приближенное равенство координат.
+ ///
+ /// Первое тело. + /// Второе тело. + /// Погрешность для определения равенства координат в единицах длины Revit (футах). + /// True, если разница объемов тел меньше, либо равна 1% от объема меньшего солида,
+ /// и если разница координат меньше, либо равна ;
+ /// Иначе False
+ bool EqualsSolid(ISolidProvider solidProvider, Solid otherSolid, double tolerance); + + /// + /// Метод проверяет тела на пересечение. + /// + /// Первое тело. + /// Второе тело. + /// Бокс второго тела. + /// True, если тела пересекаются, иначе False + bool IntersectsSolid(ISolidProvider solidProvider, Solid otherSolid, BoundingBoxXYZ otherSolidBBox); + } +} diff --git a/src/RevitOpeningPlacement/Services/MepTaskOutcomingInfoUpdater.cs b/src/RevitOpeningPlacement/Services/MepTaskOutcomingInfoUpdater.cs new file mode 100644 index 000000000..433891022 --- /dev/null +++ b/src/RevitOpeningPlacement/Services/MepTaskOutcomingInfoUpdater.cs @@ -0,0 +1,638 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using Autodesk.Revit.DB; + +using dosymep.Revit; +using dosymep.Revit.Geometry; + +using RevitClashDetective.Models.Extensions; + +using RevitOpeningPlacement.Models; +using RevitOpeningPlacement.Models.Extensions; +using RevitOpeningPlacement.Models.Interfaces; +using RevitOpeningPlacement.OpeningModels; +using RevitOpeningPlacement.OpeningModels.Enums; + +namespace RevitOpeningPlacement.Services { + /// + /// Класс для обновления информации по исходящим заданиям на отверстия от ВИС из активного файла + /// + internal class MepTaskOutcomingInfoUpdater : IOpeningInfoUpdater { + /// + /// Репозиторий активного файла + /// + private readonly RevitRepository _revitRepository; + private readonly ISolidProviderUtils _solidProviderUtils; + + /// + /// Все исходящие задания на отверстия от ВИС из активного файла + /// + private readonly ICollection _outcomingTasksIds; + + /// + /// Все элементы ВИС из активного файла + /// + private readonly ICollection _mepElementsIds; + + /// + /// Все связи с конструкциями, загруженные в активный файл + /// + private readonly ICollection _constructureLinks; + + /// + /// Допустимое расстояние между экземплярами семейств заданий на отверстия, при котором считается, что они размещены в одном и том же месте + /// + private readonly double _distance3dTolerance = Math.Sqrt(3 * XYZExtension.FeetRound * XYZExtension.FeetRound); + + /// + /// Допустимый объем, равный кубу + /// + private readonly double _volumeTolerance; + + private readonly Dictionary> _intersectingMepElementsCache; + + + public MepTaskOutcomingInfoUpdater(RevitRepository revitRepository, ISolidProviderUtils solidProviderUtils) { + _revitRepository = revitRepository ?? throw new ArgumentNullException(nameof(revitRepository)); + _solidProviderUtils = solidProviderUtils ?? throw new ArgumentNullException(nameof(solidProviderUtils)); + + _outcomingTasksIds = GetOpeningsMepTasksOutcoming(_revitRepository); + _mepElementsIds = revitRepository.GetMepElementsIds(); + _constructureLinks = GetLinkProviders(revitRepository); + _intersectingMepElementsCache = new Dictionary>(); + + _volumeTolerance = _distance3dTolerance * _distance3dTolerance * _distance3dTolerance; + } + + + /// + /// Обновляет статус и хост + /// исходящего задания на отверстие от ВИС из активного файла. + /// + /// Исходящее задание н отверстие от ВИС из активного файла. + public void UpdateInfo(OpeningMepTaskOutcoming outcomingTask) { + if(!outcomingTask.IsRemoved) { + var openingSolid = outcomingTask.GetSolid(); + if((openingSolid is null) || (openingSolid.Volume < _volumeTolerance)) { + outcomingTask.Status = OpeningTaskOutcomingStatus.Invalid; + return; + } + + if(IsManuallyPlaced(outcomingTask)) { + FindAndSetHost(openingSolid, _constructureLinks, outcomingTask); + outcomingTask.Status = OpeningTaskOutcomingStatus.ManuallyPlaced; + return; + } + + if(ThisOpeningTaskIsNotActual(openingSolid, _constructureLinks, _mepElementsIds, outcomingTask)) { + outcomingTask.Status = OpeningTaskOutcomingStatus.NotActual; + return; + } + + var intersectingOpeningIds = GetIntersectingOpeningsTasks( + openingSolid, + _outcomingTasksIds, + outcomingTask); + if(intersectingOpeningIds.Count > 0) { + outcomingTask.Status = OpeningTaskOutcomingStatus.Intersects; + return; + } else { + _outcomingTasksIds.Remove(outcomingTask.Id); + } + + try { + Solid openingSolidAfterIntersection = GetOpeningAndMepsSolidsDifference( + openingSolid, + _mepElementsIds, + outcomingTask); + if(openingSolidAfterIntersection is null) { + outcomingTask.Status = OpeningTaskOutcomingStatus.Invalid; + return; + } + double volumeRatio = + (openingSolid.Volume - openingSolidAfterIntersection.Volume) / openingSolid.Volume; + outcomingTask.Status = GetOpeningTaskOutcomingStatus(volumeRatio); + return; + + } catch(InvalidOperationException) { + outcomingTask.Status = OpeningTaskOutcomingStatus.Invalid; + return; + } + } else { + outcomingTask.Status = OpeningTaskOutcomingStatus.Invalid; + return; + } + } + + /// + /// Возвращает статус задания на отверстие по отношению объема пересечения задания на отверстие с элементом инженерной системы к исходному объему задания на отверстие + /// + /// Исключение, если коэффициент отношения объемов меньше 0 или больше 1 + private OpeningTaskOutcomingStatus GetOpeningTaskOutcomingStatus(double volumeRatio) { + if((volumeRatio < 0) || (volumeRatio > 1)) { + throw new ArgumentOutOfRangeException( + $"Значение параметра {nameof(volumeRatio)} должно находиться в интервале [0; 1]"); + } + OpeningTaskOutcomingStatus status; + if(0.95 <= volumeRatio) { + status = OpeningTaskOutcomingStatus.TooSmall; + } else if((0.2 <= volumeRatio) && (volumeRatio < 0.95)) { + status = OpeningTaskOutcomingStatus.Correct; + } else if((0.001 <= volumeRatio) && (volumeRatio < 0.2)) { + status = OpeningTaskOutcomingStatus.TooBig; + } else { + status = OpeningTaskOutcomingStatus.NotActual; + } + return status; + } + + /// + /// Возвращает солид, образованный вычитанием из исходного солида задания на отверстие всех пересекающих его элементов инженерных систем + /// + /// Солид текущего задания на отверстие + /// Коллекция Id всех элементов инженерных систем из файла, в котором размещено задание на отверстие + /// Исключение, если не удалось выполнить вычитание солида отверстия и солида элемента инженерной системы + private Solid GetOpeningAndMepsSolidsDifference( + Solid thisOpeningSolid, + ICollection allMepElementsIds, + OpeningMepTaskOutcoming mepTaskOutcoming) { + + var intersectingMepSolids = GetIntersectingMepSolids(thisOpeningSolid, allMepElementsIds, mepTaskOutcoming); + Solid openingSolidAfterIntersection = thisOpeningSolid; + foreach(var mepSolid in intersectingMepSolids) { + //здесь определяется солид, образованный вычитанием из исходного солида задания на отверстие всех пересекающих его элементов инженерных систем + try { + openingSolidAfterIntersection = BooleanOperationsUtils.ExecuteBooleanOperation( + openingSolidAfterIntersection, + mepSolid, BooleanOperationsType.Difference); + } catch(Autodesk.Revit.Exceptions.InvalidOperationException) { + try { + // если один солид касается другого солида изнутри, то будет исключение InvalidOperationException + // здесь производится попытка слегка подвинуть один из солидов, чтобы избежать этого касания + double coordinateOffset = 0.001; + var mepTransformedSolid = SolidUtils.CreateTransformed( + mepSolid, + Transform.CreateTranslation( + new XYZ(coordinateOffset, coordinateOffset, coordinateOffset))); + openingSolidAfterIntersection = BooleanOperationsUtils.ExecuteBooleanOperation( + openingSolidAfterIntersection, + mepTransformedSolid, + BooleanOperationsType.Difference); + + } catch(Autodesk.Revit.Exceptions.InvalidOperationException) { + try { + // здесь производится попытка слегка подвинуть один из солидов в другую сторону + double coordinateOffset = -0.001; + var mepTransformedSolid = SolidUtils.CreateTransformed( + mepSolid, + Transform.CreateTranslation( + new XYZ(coordinateOffset, coordinateOffset, coordinateOffset))); + openingSolidAfterIntersection = BooleanOperationsUtils.ExecuteBooleanOperation( + openingSolidAfterIntersection, + mepTransformedSolid, + BooleanOperationsType.Difference); + + } catch(Autodesk.Revit.Exceptions.InvalidOperationException) { + throw new InvalidOperationException(); + } + } + } + } + return openingSolidAfterIntersection; + } + + + /// + /// Возвращает список солидов элементов инженерных систем, которые пересекаются с данным заданием на отверстие, из текущего документа + /// + /// Солид текущего задания на отверстие + /// Коллекция Id всех элементов инженерных систем из файла, в котором размещено задание на отверстие + private ICollection GetIntersectingMepSolids( + Solid thisOpeningTaskSolid, + ICollection allMepElementsIds, + OpeningMepTaskOutcoming mepTaskOutcoming) { + + if((thisOpeningTaskSolid is null) || (thisOpeningTaskSolid.Volume <= 0)) { + return Array.Empty(); + } else { + return GetIntersectingMepElementsIds(thisOpeningTaskSolid, allMepElementsIds, mepTaskOutcoming) + .Select(elementId => _revitRepository.Doc.GetElement(elementId).GetSolid()) + .ToHashSet(); + } + } + + /// + /// Возвращает Id всех исходящих заданий на отверстия, которые пересекаются с данным заданием на отверстие из текущего документа + /// + /// Солид текущего задания на отверстие + /// Id всех экземпляров семейств заданий на отверстия из активного документа ревита для проверки + /// Коллекция Id экземпляров семейств исходящих заданий на отверстия, которые пересекаются с текущим заданием на отверстие + private ICollection GetIntersectingOpeningsTasks( + Solid thisOpeningTaskSolid, + ICollection allOpeningTasksInDoc, + OpeningMepTaskOutcoming mepTaskOutcoming) { + + if((thisOpeningTaskSolid is null) || (thisOpeningTaskSolid.Volume <= 0) || (!allOpeningTasksInDoc.Any())) { + return Array.Empty(); + } else { + return new FilteredElementCollector(_revitRepository.Doc, allOpeningTasksInDoc) + .Excluding(new ElementId[] { mepTaskOutcoming.Id }) + .WherePasses(new BoundingBoxIntersectsFilter(thisOpeningTaskSolid.GetOutline())) + .WherePasses(new ElementIntersectsSolidFilter(thisOpeningTaskSolid)) + .ToElementIds(); + } + } + + /// + /// Проверяет, является ли данное задание на отверстие НЕ актуальным. Если задание не актуально, возвращается False, иначе True. + /// Метод может установить только НЕкорректность задания, но корректность абсолютно точно подтвердить не может. + /// + /// Солид текущего задания на отверстие + /// Коллекция объектов-оберток связанных файлов с элементами конструкций: стенами, перекрытиями и т.п. + /// True - задание точно некорректно; False - задание не некорректно, но утверждать, что оно корректно нельзя + private bool ThisOpeningTaskIsNotActual( + Solid thisOpeningTaskSolid, + ICollection constructureLinkElementsProviders, + ICollection allMepElementsIds, + OpeningMepTaskOutcoming mepTaskOutcoming + ) { + + if((thisOpeningTaskSolid is null) || (thisOpeningTaskSolid.Volume <= 0)) { + // геометрия задания на отверстие не корректна - задание не актуально + return true; + } + if(constructureLinkElementsProviders is null) { + throw new ArgumentNullException(nameof(constructureLinkElementsProviders)); + } + if(allMepElementsIds is null) { + throw new ArgumentNullException(nameof(allMepElementsIds)); + } + + bool mepElementsNotIntersectThisTask = false; + var mepElementsIntersectingThisTask = GetIntersectingMepElementsIds( + thisOpeningTaskSolid, + allMepElementsIds, + mepTaskOutcoming); + if(mepElementsIntersectingThisTask.Count == 0) { + // задание на отверстие не пересекается ни с одним элементом инженерной системы - задание не актуально + mepElementsNotIntersectThisTask = true; + } + + // проверка на то, что: + // во-первых есть конструкции в связанных файлах, внутри которых расположено задание на отверстие, + // во-вторых, что ни один элемент ВИС, проходящий через текущее задание на отверстие не пересекается с этими конструкциями + foreach(var link in constructureLinkElementsProviders) { + var hostConstructions = GetHostConstructionsForThisOpeningTask( + thisOpeningTaskSolid, link, + out ICollection intersectingOpenings, + mepTaskOutcoming); + if(hostConstructions.Count > 0) { + return mepElementsNotIntersectThisTask + || MepElementsIntersectConstructionsOrOpenings( + thisOpeningTaskSolid, + mepElementsIntersectingThisTask, + hostConstructions, + intersectingOpenings, + link, + mepTaskOutcoming + ); + } else { + // если не найдены конструкции, которые можно считать хостами текущего задания на отверстие, + // то либо задание на отверстие висит в воздухе, либо задание на отверстие пересекается с другой связью + continue; + } + } + + // корректная ситуация не найдена, отверстие считается не актуальным + return true; + } + + /// + /// Проверяет, пересекаются ли элементы ВИС из текущего файла с конструкциями или чистовыми отверстиями, причем место пересечения находится вне солида задания на отверстие. + /// При этом проверяемые элементы ВИС, а также проверяемые конструкции и чистовые отверстия из связи должны пересекать солид текущего задания на отверстие + /// + /// Солид текущего задания на отверстие + /// Элементы ВИС из текущего файла с заданиями на отверстия, которые пересекают солид текущего задания на отверстие + /// Конструкции из , которые пересекаются с текущим заданием на отверстие + /// Чистовые отверстия из , которые пересекаются с текущим заданием на отверстие + /// Связанный файл с конструкциями + /// True, если найдено пересечение элементов ВИС и конструкций (или чистовых отверстий) вне солида задания на отверстие, иначе False + private bool MepElementsIntersectConstructionsOrOpenings( + Solid thisOpeningTaskSolid, + ICollection intersectingMepElementsIds, + ICollection intersectingLinkConstructions, + ICollection intersectingLinkOpeningsReal, + IConstructureLinkElementsProvider link, + OpeningMepTaskOutcoming mepTaskOutcoming + ) { + if(!mepTaskOutcoming.IsRemoved + && (link != null) + && (intersectingMepElementsIds != null) + && (intersectingMepElementsIds.Count > 0) + && (intersectingLinkConstructions != null) + && (intersectingLinkConstructions.Count > 0) + && (intersectingLinkOpeningsReal != null) + && (thisOpeningTaskSolid != null) + && (thisOpeningTaskSolid.Volume > 0)) { + + var thisSolidInLinkCoordinates = SolidUtils + .CreateTransformed(thisOpeningTaskSolid, link.DocumentTransform.Inverse); + + // получение объединенного солида для элементов ВИС в координатах текущего файла с заданиями на отверстия + var mepElements = intersectingMepElementsIds + .Select(mepId => _revitRepository.Doc.GetElement(mepId)) + .ToHashSet(); + var mepSolids = mepElements + .Select(el => el.GetSolid()) + .Where(solid => (solid != null) && (solid.Volume > 0)) + .ToList(); + var mepUnitedSolid = RevitClashDetective.Models.Extensions.ElementExtensions.UniteSolids(mepSolids); + if((mepUnitedSolid is null) || (mepUnitedSolid.Volume < _volumeTolerance)) { + return false; + } + + try { + // трансформация объединенного солида элементов ВИС в координаты связанного файла с конструкциями + var mepSolidInLinkCoordinates = SolidUtils + .CreateTransformed(mepUnitedSolid, link.DocumentTransform.Inverse); + // вычитание из объединенного солида элементов ВИС солида задания на отверстие, + // чтобы исключить из проверки места пересечения элементов ВИС с конструкциями внутри тела солида задания на отверстие + var mepSolidMinusOpeningTask = BooleanOperationsUtils + .ExecuteBooleanOperation( + mepSolidInLinkCoordinates, + thisSolidInLinkCoordinates, + BooleanOperationsType.Difference); + + // поиск конструкций (стен и перекрытий) и чистовых отверстий из связанного файла, которые пересекаются с проходящими через задание на отверстие элементами ВИС из текущего файла, + // с учетом того, что место пересечения находится вне тела задания на отверстие. + BoundingBoxXYZ mepElementsBBox = mepElements + .Select(el => el.GetBoundingBox()) + .GetCommonBoundingBox() + .TransformBoundingBox(link.DocumentTransform.Inverse); + bool mepElementsIntersectConstructions = + new FilteredElementCollector(link.Document, intersectingLinkConstructions) + .WherePasses(new ElementIntersectsSolidFilter(mepSolidMinusOpeningTask)) + .Any(); + bool mepElementsIntersectOpeningsReal = intersectingLinkOpeningsReal + .Any(openingReal => _solidProviderUtils + .IntersectsSolid(openingReal, mepSolidMinusOpeningTask, mepElementsBBox)); + return mepElementsIntersectConstructions || mepElementsIntersectOpeningsReal; + + } catch(Autodesk.Revit.Exceptions.InvalidOperationException) { + return false; + } + } else { + return false; + } + } + + /// + /// Возвращает список Id элементов инженерных систем, которые пересекаются с данным заданием на отверстие, из текущего документа + /// + private ICollection GetIntersectingMepElementsIds( + Solid thisOpeningSolid, + ICollection allMepElementsIds, + OpeningMepTaskOutcoming mepTaskOutcoming) { + + if(!mepTaskOutcoming.IsRemoved + && (thisOpeningSolid != null) + && (allMepElementsIds != null) + && allMepElementsIds.Any()) { + + if(!_intersectingMepElementsCache.ContainsKey(mepTaskOutcoming.Id)) { + var ids = new FilteredElementCollector(_revitRepository.Doc, allMepElementsIds) + .WherePasses(new BoundingBoxIntersectsFilter(thisOpeningSolid.GetOutline())) + .WherePasses(new ElementIntersectsSolidFilter(thisOpeningSolid)) + .ToElementIds(); + _intersectingMepElementsCache.Add(mepTaskOutcoming.Id, ids); + } + return _intersectingMepElementsCache[mepTaskOutcoming.Id]; + } else { + return Array.Empty(); + } + } + + /// + /// Проверяет, размещено ли текущее задание на отверстие вручную + /// + /// True, если присутствует и включен параметр , + /// иначе False + private bool IsManuallyPlaced(OpeningMepTaskOutcoming outcomingTask) { + if(!outcomingTask.IsRemoved) { + try { + return outcomingTask.GetFamilyInstance() + .GetSharedParamValue(RevitRepository.OpeningIsManuallyPlaced) == 1; + + } catch(ArgumentException) { + return false; + } + } else { + return false; + } + } + + private ICollection GetLinkProviders(RevitRepository revitRepository) { + return revitRepository.GetConstructureLinks() + .Select(link => new ConstructureLinkElementsProvider(revitRepository, link)) + .ToArray(); + } + + private ICollection GetOpeningsMepTasksOutcoming(RevitRepository revitRepository) { + var openingInWalls = revitRepository.GetWallOpeningsMepTasksOutcoming(); + var openingsInFloor = revitRepository.GetFloorOpeningsMepTasksOutcoming(); + openingsInFloor.AddRange(openingInWalls); + return openingsInFloor.Select(famInst => famInst.Id).ToHashSet(); + } + + /// + /// Назначает хост задания на отверстие + /// + private void FindAndSetHost( + Solid thisOpeningTaskSolid, + ICollection constructureLinkElementsProviders, + OpeningMepTaskOutcoming mepTaskOutcoming) { + + foreach(var link in constructureLinkElementsProviders) { + var hostConstructions = GetHostConstructionsForThisOpeningTask( + thisOpeningTaskSolid, + link, + out _, + mepTaskOutcoming); + if(hostConstructions.Count > 0) { + break; + } + } + } + + /// + /// Возвращает коллекцию Id элементов конструкций из связанного файла, которые пересекаются с текущим заданием на отверстие + /// + /// Солид текущего задания на отверстие в координатах связанного файла + /// Связанный файл для проверки на пересечение + private ICollection GetIntersectingLinkConstructionElementsIds( + Solid thisOpeningSolidInLinkCoordinates, + IConstructureLinkElementsProvider constructureLinkElementsProvider) { + + ICollection ids = constructureLinkElementsProvider.GetConstructureElementIds(); + if(!ids.Any()) { + return Array.Empty(); + } + return new FilteredElementCollector(constructureLinkElementsProvider.Document, ids) + .WherePasses(new BoundingBoxIntersectsFilter(thisOpeningSolidInLinkCoordinates.GetOutline())) + .WherePasses(new ElementIntersectsSolidFilter(thisOpeningSolidInLinkCoordinates)) + .ToElementIds(); + } + + /// + /// Поиск конструкций из связанного файла, в которых расположено задание на отверстие. + /// Если конструкции будут найдены, то также будет вызван метод + /// + /// Солид текущего задания на отверстие + /// Связь с конструкциями + /// Чистовые отверстия из связи, которые пересекаются с солидом текущего задания на отверстие + /// + /// Коллекция Id конструкций (стен или перекрытий) из связанного файла, которые пересекаются с солидом текущего задания на отверстие + /// или коллекция Id конструкций, которые являются хостами чистовых отверстий, с которыми пересекается солид текущего задания на отверстие + /// + private ICollection GetHostConstructionsForThisOpeningTask( + Solid thisOpeningTaskSolid, + IConstructureLinkElementsProvider link, + out ICollection intersectingOpeningsReal, + OpeningMepTaskOutcoming mepTaskOutcoming) { + + var thisSolidInLinkCoordinates = SolidUtils.CreateTransformed( + thisOpeningTaskSolid, + link.DocumentTransform.Inverse); + + intersectingOpeningsReal = GetIntersectingLinkOpeningsReal(link, thisOpeningTaskSolid, mepTaskOutcoming); + + // поиск конструкций из связи, в которых находится текущее задание на отверстие + var intersectingConstructions = GetIntersectingLinkConstructionElementsIds( + thisSolidInLinkCoordinates, + link); + if(intersectingConstructions.Count == 0) { + // задание на отверстие не пересекается с конструкциями из связей + + // поиск чистовых отверстий из связи, которые пересекаются с текущим заданием на отверстие + if(intersectingOpeningsReal.Count > 0) { + // пересечение с чистовыми отверстиями из связей найдено + + // поиск элементов конструкций - основ чистовых отверстий из связей + intersectingConstructions = intersectingOpeningsReal + .Select(opening => opening.GetHost().Id) + .Distinct() + .ToHashSet(); + } + } else { + // если задание на отверстие пересекается с конструкциями из связей, то будем считать, что даже если задание также пересекается с чистовыми отверстиями, + // то хосты этих чистовых отверстий - это и есть конструкции, с которым пересекается само задание на отверстие. + // Поэтому можно не делать долгую проверку на поиск пересекающих чистовых отверстий (см. ниже). + + // Если эта логика окажется неверной, то надо убрать комментарии ниже + + // поиск конструкций - основ чистовых отверстий, в которых находится текущее задание на отверстие + //intersectingOpeningsReal = GetIntersectingLinkOpeningsReal(link, thisOpeningTaskSolid); + //if(intersectingOpeningsReal.Count > 0) { + + // var openingsHosts = intersectingOpeningsReal.Select(opening => opening.GetHost().Id).Distinct().ToHashSet(); + // var constructions = new List(intersectingConstructions); + // constructions.AddRange(openingsHosts); + // intersectingConstructions = constructions; + //} + } + if(intersectingConstructions.Count > 0) { + SetHostConstruction(link, intersectingConstructions, thisSolidInLinkCoordinates, mepTaskOutcoming); + } + + return intersectingConstructions; + } + + /// + /// Назначает свойство хоста текущего задания на отверстие + /// + /// Связанный файл с конструкциями + /// Элементы из связанного файла с конструкциями, которые пересекаются с текущим заданием на отверстие + /// Солид текущего задания на отверстие в координатах связанного файла + private void SetHostConstruction( + IConstructureLinkElementsProvider link, + ICollection intersectingLinkedElements, + Solid thisOpeningTaskSolidInLinkCoordinates, + OpeningMepTaskOutcoming mepTaskOutcoming) { + + if((link != null) + && intersectingLinkedElements.Any() + && (thisOpeningTaskSolidInLinkCoordinates != null) + && (thisOpeningTaskSolidInLinkCoordinates.Volume >= _volumeTolerance)) { + + // поиск элемента конструкции, с которым пересечение текущего задания на отверстие имеет наибольший объем + double halfOpeningTaskVolume = thisOpeningTaskSolidInLinkCoordinates.Volume / 2; + var elements = intersectingLinkedElements.Select(id => link.Document.GetElement(id)).ToHashSet(); + Element hostCandidate = elements.FirstOrDefault(); + double intersectingVolumePrevious = 0; + foreach(Element element in elements) { + var structureSolid = element?.GetSolid(); + if((structureSolid != null) && (structureSolid.Volume > _volumeTolerance)) { + try { + double intersectingVolumeCurrent + = BooleanOperationsUtils.ExecuteBooleanOperation( + thisOpeningTaskSolidInLinkCoordinates, + structureSolid, + BooleanOperationsType.Intersect)?.Volume + ?? 0; + if(intersectingVolumeCurrent >= halfOpeningTaskVolume) { + mepTaskOutcoming.Host = element; + return; + } + if(intersectingVolumeCurrent > intersectingVolumePrevious) { + intersectingVolumePrevious = intersectingVolumeCurrent; + hostCandidate = element; + } + } catch(Autodesk.Revit.Exceptions.InvalidOperationException) { + continue; + } + } + } + mepTaskOutcoming.Host = hostCandidate; + } else { + return; + } + } + + /// + /// Поиск чистовых отверстий из связанного файла, в которых расположено задание на отверстие + /// + /// Связь с конструкциями и чистовыми отверстиями + /// Солид текущего задания на отверстие + /// Коллекция чистовых отверстий из связи, которые пересекаются с солидом текущего задания на отверстие + private ICollection GetIntersectingLinkOpeningsReal( + IConstructureLinkElementsProvider link, + Solid thisOpeningTaskSolid, + OpeningMepTaskOutcoming mepTaskOutcoming + ) { + + var thisSolidInLinkCoordinates = SolidUtils + .CreateTransformed(thisOpeningTaskSolid, link.DocumentTransform.Inverse); + var thisBBoxInLinkCoordinates = GetTransformedBBoxXYZ(mepTaskOutcoming) + .TransformBoundingBox(link.DocumentTransform.Inverse); + return link.GetOpeningsReal() + .Where(realOpening => _solidProviderUtils.IntersectsSolid( + realOpening, + thisSolidInLinkCoordinates, + thisBBoxInLinkCoordinates)) + .ToHashSet(); + } + + /// + /// Возвращает BoundingBoxXYZ с учетом расположения в файле Revit, + /// если элемент был удален из документа, будет возвращено значение по умолчанию + /// + public BoundingBoxXYZ GetTransformedBBoxXYZ(OpeningMepTaskOutcoming mepTaskOutcoming) { + if(mepTaskOutcoming.IsRemoved) { + return default; + } + return mepTaskOutcoming.GetFamilyInstance().GetBoundingBox(); + } + } +} diff --git a/src/RevitOpeningPlacement/Services/SolidProviderUtils.cs b/src/RevitOpeningPlacement/Services/SolidProviderUtils.cs new file mode 100644 index 000000000..30adf4506 --- /dev/null +++ b/src/RevitOpeningPlacement/Services/SolidProviderUtils.cs @@ -0,0 +1,147 @@ +using System; + +using Autodesk.Revit.DB; + +using dosymep.Revit.Geometry; + +using RevitOpeningPlacement.Models.Interfaces; + +namespace RevitOpeningPlacement.Services { + internal class SolidProviderUtils : ISolidProviderUtils { + /// + /// Точность для определения расстояний и координат 1 мм. + /// + private const double _toleranceDistance = 1 / 304.8; + + /// + /// Точность для определения объемов 1 см3 + /// + private const double _toleranceVolume = (10 / 304.8) * (10 / 304.8) * (10 / 304.8); + + /// + /// Процент толерантности объемов солидов + /// + private const double _toleranceVolumePercentage = 0.01; + + + public SolidProviderUtils() { + } + + + /// + /// Проверяет на равенство 2 тела.
+ /// Под равенством понимается равенство объемов с точностью до 1%
+ /// и равенство координат боксов тел () с точностью до в единицах длины Revit (футах). + ///
+ /// Первое тело. + /// Второе тело. + /// Допустимое расстояние в единицах длины Revit (футах) между телами. + /// True, если разница объемов тел меньше, либо равна 1% от объема меньшего солида,
+ /// и если разница координат ограничивающих боксов () меньше, либо равна ;
+ /// Иначе False
+ public bool EqualsSolid(ISolidProvider solidProvider, Solid otherSolid, double tolerance) { + var thisSolid = solidProvider.GetSolid(); + + if(!SolidsVolumesEqual(thisSolid, otherSolid)) { + return false; + } + + var thisSolidBBox = thisSolid.GetTransformedBoundingBox(); + var otherSolidBBox = otherSolid.GetTransformedBoundingBox(); + + return BBoxesEqual(thisSolidBBox, otherSolidBBox, tolerance); + } + + public bool IntersectsSolid(ISolidProvider thisSolidProvider, Solid otherSolid, BoundingBoxXYZ otherSolidBBox) { + var thisSolid = thisSolidProvider.GetSolid(); + var thisBBox = thisSolidProvider.GetTransformedBBoxXYZ(); + return SolidsIntersect(thisSolid, thisBBox, otherSolid, otherSolidBBox); + } + + + private bool SolidsIntersect(Solid firstSolid, BoundingBoxXYZ firstBBox, Solid secondSolid, BoundingBoxXYZ secondBBox) { + if((firstSolid is null) || (secondSolid is null)) { + return false; + } + + bool firstBBoxIntersectsSecond = firstBBox.IsIntersected(secondBBox); + if(!firstBBoxIntersectsSecond) { + return false; + } + + // Проверка объектов на совпадение без использования BooleanOperationUtils + if(SolidEquals(firstSolid, firstBBox, secondSolid, secondBBox)) { + // Оставить проверку на равенство через ISolidProviderExtension.EqualsSolidProvider, + // т.к. при солидах, смещенных друг относительно друга на [0.16-0.17] мм когда один солид внутри другого, + // методы BooleanOperationUtils.ExecuteBooleanOperation могут выбрасывать Autodesk.Revit.Exceptions.InvalidOperationException, + // если солиды будет смещены на 0.15 мм или меньше, то BooleanOperationUtils.ExecuteBooleanOperation не будет учитывать эту разницу в координатах, + // при разнице 0.18 мм и больше методы BooleanOperationUtils.ExecuteBooleanOperation работают в соответствии со своими названиями операций. + // + // Если же солиды заходят друг в друга на расстояние, меньше 0.15953 мм, то методы BooleanOperationUtils.ExecuteBooleanOperation не будут находить пересечение. + // При пересечении 0.15953 и более методы будут работать в соответствии с названиями своих операций. + return true; + } + + // Итоговая проверка на пересечение объектов + try { + Solid intersectSolid = BooleanOperationsUtils.ExecuteBooleanOperation(firstSolid, secondSolid, BooleanOperationsType.Intersect); + if(intersectSolid?.Volume > _toleranceVolume) { + return true; + } + } catch(Autodesk.Revit.Exceptions.InvalidOperationException) { + return firstBBoxIntersectsSecond; + } + return false; + } + + /// + /// Проверяет на равенство текущего и поданного . + /// Под равенством понимается равенство объемов с точностью до 1% объема от меньшего солида и равенство координат ограничивающих с точностью до 1 мм. + /// + /// Первый Solid + /// BoundingBox первого Solid + /// Второй Solid + /// BoundingBox второго Solid + /// True, если разница объемов текущего и поданного Solid меньше, либо равна 1 см3, + /// и если разница координат ограничивающих их меньше, либо равна 1 мм; + /// Иначе False + private bool SolidEquals(Solid thisSolid, BoundingBoxXYZ thisSolidBBox, Solid otherSolid, BoundingBoxXYZ otherSolidBBox) { + if(!SolidsVolumesEqual(thisSolid, otherSolid)) { + return false; + } + return BBoxesEqual(thisSolidBBox, otherSolidBBox); + } + + /// + /// Проверяет объемы солидов на равенство. + /// Если абсолютная разность объемов солидов не превышает 1% объема меньшего солида, возвращается True, иначе False + /// + /// Первый солид + /// Второй солид + /// True, если разница объемов не превышает процент объема меньшего солида + private bool SolidsVolumesEqual(Solid solid1, Solid solid2) { + if((solid1 is null) || (solid2 is null)) { + return false; + } + var minVolume = Math.Min(solid1.Volume, solid2.Volume); + var volumeTolerance = minVolume * _toleranceVolumePercentage; + return Math.Abs(solid1.Volume - solid2.Volume) <= volumeTolerance; + } + + private bool BBoxesEqual(BoundingBoxXYZ bbox1, BoundingBoxXYZ bbox2) { + return BBoxesEqual(bbox1, bbox2, _toleranceDistance); + } + + private bool BBoxesEqual(BoundingBoxXYZ bbox1, BoundingBoxXYZ bbox2, double tolerance) { + var minDistance = (bbox1.Min - bbox2.Min).GetLength(); + if(minDistance > tolerance) { + return false; + } + var maxDistance = (bbox1.Max - bbox2.Max).GetLength(); + if(maxDistance > tolerance) { + return false; + } + return true; + } + } +} diff --git a/src/RevitOpeningPlacement/ViewModels/Navigator/ArchitectureNavigatorForIncomingTasksViewModel.cs b/src/RevitOpeningPlacement/ViewModels/Navigator/ArchitectureNavigatorForIncomingTasksViewModel.cs index 73c5af433..951f3c4c0 100644 --- a/src/RevitOpeningPlacement/ViewModels/Navigator/ArchitectureNavigatorForIncomingTasksViewModel.cs +++ b/src/RevitOpeningPlacement/ViewModels/Navigator/ArchitectureNavigatorForIncomingTasksViewModel.cs @@ -1,14 +1,20 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Linq; using System.Windows.Data; using System.Windows.Input; +using Autodesk.Revit.DB; + +using dosymep.SimpleServices; using dosymep.WPF.Commands; using dosymep.WPF.ViewModels; using RevitOpeningPlacement.Models; using RevitOpeningPlacement.Models.Interfaces; +using RevitOpeningPlacement.OpeningModels; +using RevitOpeningPlacement.Services; namespace RevitOpeningPlacement.ViewModels.Navigator { /// @@ -16,34 +22,35 @@ namespace RevitOpeningPlacement.ViewModels.Navigator { /// internal class ArchitectureNavigatorForIncomingTasksViewModel : BaseViewModel { private readonly RevitRepository _revitRepository; + private readonly IConstantsProvider _constantsProvider; public ArchitectureNavigatorForIncomingTasksViewModel( RevitRepository revitRepository, - ICollection openingsMepTasksIncomingViewModels, - ICollection openingsRealViewModels) { - - if(revitRepository is null) { - throw new ArgumentNullException(nameof(revitRepository)); - } - if(openingsMepTasksIncomingViewModels is null) { - throw new ArgumentNullException(nameof(openingsMepTasksIncomingViewModels)); - } - - _revitRepository = revitRepository; + IConstantsProvider constantsProvider) { - OpeningsMepTaskIncoming = new ObservableCollection(openingsMepTasksIncomingViewModels); + _revitRepository = revitRepository ?? throw new ArgumentNullException(nameof(revitRepository)); + _constantsProvider = constantsProvider ?? throw new ArgumentNullException(nameof(constantsProvider)); + OpeningsMepTaskIncoming = new ObservableCollection(); OpeningsMepTasksIncomingViewSource = new CollectionViewSource() { Source = OpeningsMepTaskIncoming }; - OpeningsReal = new ObservableCollection(openingsRealViewModels); + OpeningsReal = new ObservableCollection(); OpeningsRealViewSource = new CollectionViewSource() { Source = OpeningsReal }; - SelectCommand = RelayCommand.Create(SelectElement, CanSelect); - RenewCommand = RelayCommand.Create(Renew); - PlaceRealOpeningBySingleTaskCommand = RelayCommand.Create(PlaceRealOpeningBySingleTask); - PlaceOneRealOpeningByManyTasksCommand = RelayCommand.Create(PlaceOneRealOpeningByManyTasks); - PlaceManyRealOpeningsByManyTasksCommand = RelayCommand.Create(PlaceManyRealOpeningsByManyTasks); - PlaceManyRealOpeningsByManyTasksInManyHostsCommand = RelayCommand.Create(PlaceManyRealOpeningsByManyTasksInManyHosts); + LoadViewCommand + = RelayCommand.Create(LoadView); + SelectCommand + = RelayCommand.Create(SelectElement, CanSelect); + RenewCommand + = RelayCommand.Create(Renew); + PlaceRealOpeningBySingleTaskCommand + = RelayCommand.Create(PlaceRealOpeningBySingleTask); + PlaceOneRealOpeningByManyTasksCommand + = RelayCommand.Create(PlaceOneRealOpeningByManyTasks); + PlaceManyRealOpeningsByManyTasksCommand + = RelayCommand.Create(PlaceManyRealOpeningsByManyTasks); + PlaceManyRealOpeningsByManyTasksInManyHostsCommand + = RelayCommand.Create(PlaceManyRealOpeningsByManyTasksInManyHosts); } @@ -72,6 +79,8 @@ public OpeningRealArViewModel SelectedOpeningReal { } + public ICommand LoadViewCommand { get; } + public ICommand SelectCommand { get; } public ICommand RenewCommand { get; } @@ -132,5 +141,109 @@ private void PlaceManyRealOpeningsByManyTasksInManyHosts() { }; _revitRepository.DoAction(action); } + + private void LoadView() { + ICollection realOpenings = _revitRepository.GetRealOpeningsAr(); + + LoadIncomingTasks(realOpenings); + LoadOpeningsReal(realOpenings); + } + + private void LoadIncomingTasks(ICollection realOpenings) { + ICollection incomingTasks = _revitRepository.GetOpeningsMepTasksIncoming(); + ICollection constructureElementsIds = _revitRepository.GetConstructureElementsIds(); + var incomingTasksViewModels = GetOpeningsMepIncomingTasksViewModels( + incomingTasks, + realOpenings.ToArray(), + constructureElementsIds); + OpeningsMepTaskIncoming.Clear(); + foreach(var incomingTask in incomingTasksViewModels) { + OpeningsMepTaskIncoming.Add(incomingTask); + } + } + + private void LoadOpeningsReal(ICollection realOpenings) { + ICollection mepLinks = _revitRepository + .GetMepLinks() + .Select(link => new MepLinkElementsProvider(link) as IMepLinkElementsProvider) + .ToArray(); + var openingsRealViewModels = GetOpeningsRealArViewModels(mepLinks, realOpenings); + OpeningsReal.Clear(); + foreach(var openingReal in openingsRealViewModels) { + OpeningsReal.Add(openingReal); + } + OnPropertyChanged(nameof(ShowOpeningsReal)); + } + + /// + /// Возвращает коллекцию моделей представления для входящих заданий на отверстия из ВИС + /// + /// Входящие задания на отверстия из связей + /// Чистовые отверстия из текущего документа + /// Элементы конструкций из текущего документа + private ICollection GetOpeningsMepIncomingTasksViewModels( + ICollection incomingTasks, + ICollection realOpenings, + ICollection constructureElementsIds) { + + var incomingTasksViewModels = new HashSet(); + + using(var pb = GetPlatformService()) { + pb.StepValue = _constantsProvider.ProgressBarStepLarge; + pb.DisplayTitleFormat = "Анализ заданий... [{0}\\{1}]"; + var progress = pb.CreateProgress(); + pb.MaxValue = incomingTasks.Count; + var ct = pb.CreateCancellationToken(); + pb.Show(); + + int i = 0; + foreach(var incomingTask in incomingTasks) { + ct.ThrowIfCancellationRequested(); + progress.Report(i); + try { + incomingTask.UpdateStatusAndHostName(realOpenings, constructureElementsIds); + } catch(ArgumentException) { + //не удалось получить солид у задания на отверстие. Например, если его толщина равна 0 + continue; + } + incomingTasksViewModels.Add(new OpeningMepTaskIncomingViewModel(incomingTask)); + i++; + } + } + return incomingTasksViewModels; + } + + /// + /// Возвращает коллекцию моделей представления чистовых отверстий, размещенных в активном документа АР + /// + /// Связи ВИС + /// Чистовые отверстия, размещенные в активном документе АР + private ICollection GetOpeningsRealArViewModels( + ICollection mepLinks, + ICollection openingsReal) { + + var openingsRealViewModels = new HashSet(); + + using(var pb = GetPlatformService()) { + pb.StepValue = _constantsProvider.ProgressBarStepSmall; + pb.DisplayTitleFormat = "Анализ отверстий... [{0}\\{1}]"; + var progress = pb.CreateProgress(); + pb.MaxValue = openingsReal.Count; + var ct = pb.CreateCancellationToken(); + pb.Show(); + + var i = 0; + foreach(var openingReal in openingsReal) { + ct.ThrowIfCancellationRequested(); + progress.Report(i); + openingReal.UpdateStatus(mepLinks); + if(openingReal.Status != OpeningModels.Enums.OpeningRealStatus.Correct) { + openingsRealViewModels.Add(new OpeningRealArViewModel(openingReal)); + } + i++; + } + } + return openingsRealViewModels; + } } } diff --git a/src/RevitOpeningPlacement/ViewModels/Navigator/ConstructureNavigatorForIncomingTasksViewModel.cs b/src/RevitOpeningPlacement/ViewModels/Navigator/ConstructureNavigatorForIncomingTasksViewModel.cs index af0f765c9..f1b55d475 100644 --- a/src/RevitOpeningPlacement/ViewModels/Navigator/ConstructureNavigatorForIncomingTasksViewModel.cs +++ b/src/RevitOpeningPlacement/ViewModels/Navigator/ConstructureNavigatorForIncomingTasksViewModel.cs @@ -1,14 +1,21 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Linq; using System.Windows.Data; using System.Windows.Input; +using Autodesk.Revit.DB; + +using dosymep.SimpleServices; using dosymep.WPF.Commands; using dosymep.WPF.ViewModels; using RevitOpeningPlacement.Models; +using RevitOpeningPlacement.Models.Configs; using RevitOpeningPlacement.Models.Interfaces; +using RevitOpeningPlacement.OpeningModels; +using RevitOpeningPlacement.Services; namespace RevitOpeningPlacement.ViewModels.Navigator { /// @@ -16,33 +23,42 @@ namespace RevitOpeningPlacement.ViewModels.Navigator { /// internal class ConstructureNavigatorForIncomingTasksViewModel : BaseViewModel { private readonly RevitRepository _revitRepository; + private readonly OpeningRealsKrConfig _config; + private readonly IConstantsProvider _constantsProvider; public ConstructureNavigatorForIncomingTasksViewModel( RevitRepository revitRepository, - ICollection openingsTasksIncomingViewModels, - ICollection openingsRealViewModels) { + OpeningRealsKrConfig config, + IConstantsProvider constantsProvider) { _revitRepository = revitRepository ?? throw new ArgumentNullException(nameof(revitRepository)); - if(openingsTasksIncomingViewModels is null) { throw new ArgumentNullException(nameof(openingsTasksIncomingViewModels)); } - if(openingsRealViewModels is null) { throw new ArgumentNullException(nameof(openingsRealViewModels)); } - - OpeningsTasksIncoming = new ObservableCollection(openingsTasksIncomingViewModels); + _config = config ?? throw new ArgumentNullException(nameof(config)); + _constantsProvider = constantsProvider ?? throw new ArgumentNullException(nameof(constantsProvider)); + OpeningsTasksIncoming = new ObservableCollection(); OpeningsTasksIncomingViewSource = new CollectionViewSource() { Source = OpeningsTasksIncoming }; - OpeningsReal = new ObservableCollection(openingsRealViewModels); + OpeningsReal = new ObservableCollection(); OpeningsRealViewSource = new CollectionViewSource() { Source = OpeningsReal }; - SelectCommand = RelayCommand.Create(SelectElement, CanSelect); - RenewCommand = RelayCommand.Create(Renew); - PlaceRealOpeningBySingleTaskCommand = RelayCommand.Create(PlaceRealOpeningBySingleTask); - PlaceOneRealOpeningByManyTasksCommand = RelayCommand.Create(PlaceOneRealOpeningByManyTasks); - PlaceManyRealOpeningsByManyTasksCommand = RelayCommand.Create(PlaceManyRealOpeningsByManyTasks); - PlaceManyRealOpeningsByManyTasksInManyHostsCommand = RelayCommand.Create(PlaceManyRealOpeningsByManyTasksInManyHosts); + LoadViewCommand + = RelayCommand.Create(LoadView); + SelectCommand + = RelayCommand.Create(SelectElement, CanSelect); + RenewCommand + = RelayCommand.Create(Renew); + PlaceRealOpeningBySingleTaskCommand + = RelayCommand.Create(PlaceRealOpeningBySingleTask); + PlaceOneRealOpeningByManyTasksCommand + = RelayCommand.Create(PlaceOneRealOpeningByManyTasks); + PlaceManyRealOpeningsByManyTasksCommand + = RelayCommand.Create(PlaceManyRealOpeningsByManyTasks); + PlaceManyRealOpeningsByManyTasksInManyHostsCommand + = RelayCommand.Create(PlaceManyRealOpeningsByManyTasksInManyHosts); } - // Входящие задания на отверстия из АР + // Входящие задания на отверстия из АР/ВИС public ObservableCollection OpeningsTasksIncoming { get; } public CollectionViewSource OpeningsTasksIncomingViewSource { get; private set; } @@ -70,6 +86,8 @@ public OpeningRealKrViewModel SelectedOpeningReal { } + public ICommand LoadViewCommand { get; } + public ICommand SelectCommand { get; } public ICommand RenewCommand { get; } @@ -130,5 +148,182 @@ private void PlaceManyRealOpeningsByManyTasksInManyHosts() { }; _revitRepository.DoAction(action); } + + private void LoadView() { + var mode = _config.PlacementType; + switch(mode) { + case OpeningRealKrPlacementType.PlaceByMep: + LoadIncomingMepTasks(); + break; + case OpeningRealKrPlacementType.PlaceByAr: + LoadIncomingArTasks(); + break; + default: + throw new InvalidOperationException($"Режим обработки заданий для КР: '{mode}' не поддерживается."); + } + } + + private void LoadIncomingArTasks() { + ICollection incomingTasks = _revitRepository.GetOpeningsArTasksIncoming(); + ICollection realOpenings = _revitRepository.GetRealOpeningsKr(); + ICollection constructureElementsIds = _revitRepository.GetConstructureElementsIds(); + ICollection arLinks = _revitRepository + .GetArLinks() + .Select(link => new ConstructureLinkElementsProvider(_revitRepository, link) + as IConstructureLinkElementsProvider) + .ToArray(); + + var incomingTasksViewModels = GetOpeningsArIncomingTasksViewModels( + incomingTasks, + realOpenings, + constructureElementsIds); + UpdateOpeningsTasksIncoming(incomingTasksViewModels); + + var openingsRealViewModels = GetOpeningsRealKrViewModels( + realOpenings, + (OpeningRealKr opening) => { opening.UpdateStatus(arLinks); }); + UpdateOpeningsReal(openingsRealViewModels); + } + + private void LoadIncomingMepTasks() { + ICollection incomingTasks = _revitRepository.GetOpeningsMepTasksIncoming(); + ICollection realOpenings = _revitRepository.GetRealOpeningsKr(); + ICollection constructureElementsIds = _revitRepository.GetConstructureElementsIds(); + ICollection mepLinks = _revitRepository + .GetMepLinks() + .Select(link => new MepLinkElementsProvider(link) as IMepLinkElementsProvider) + .ToArray(); + + var incomingTasksViewModels = GetOpeningsMepIncomingTasksViewModels( + incomingTasks, + realOpenings.ToArray(), + constructureElementsIds) + .ToArray(); + UpdateOpeningsTasksIncoming(incomingTasksViewModels); + + var openingsRealViewModels = GetOpeningsRealKrViewModels( + realOpenings, + (OpeningRealKr opening) => { opening.UpdateStatus(mepLinks); }); + UpdateOpeningsReal(openingsRealViewModels); + } + + private void UpdateOpeningsTasksIncoming(ICollection incomingTasks) { + OpeningsTasksIncoming.Clear(); + foreach(var incomingTask in incomingTasks) { + OpeningsTasksIncoming.Add(incomingTask); + } + } + + private void UpdateOpeningsReal(ICollection openingsReal) { + OpeningsReal.Clear(); + foreach(var openingReal in openingsReal) { + OpeningsReal.Add(openingReal); + } + OnPropertyChanged(nameof(ShowOpeningsReal)); + } + + /// + /// Возвращает коллекцию моделей представления для входящих заданий на отверстия из АР + /// + /// Входящие задания на отверстия из связей + /// Чистовые отверстия из текущего документа + /// Элементы конструкций из текущего документа + private ICollection GetOpeningsArIncomingTasksViewModels( + ICollection incomingTasks, + ICollection realOpenings, + ICollection constructureElementsIds) { + + var incomintTasksViewModels = new HashSet(); + + using(var pb = GetPlatformService()) { + pb.StepValue = _constantsProvider.ProgressBarStepSmall; + pb.DisplayTitleFormat = "Анализ заданий... [{0}]\\[{1}]"; + var progress = pb.CreateProgress(); + pb.MaxValue = incomingTasks.Count; + var ct = pb.CreateCancellationToken(); + pb.Show(); + + int i = 0; + foreach(var incomingTask in incomingTasks) { + ct.ThrowIfCancellationRequested(); + progress.Report(i); + incomingTask.UpdateStatus(realOpenings, constructureElementsIds); + incomintTasksViewModels.Add(new OpeningArTaskIncomingViewModel(incomingTask)); + i++; + } + } + return incomintTasksViewModels; + } + + /// + /// Возвращает коллекцию моделей представления для входящих заданий на отверстия из ВИС + /// + /// Входящие задания на отверстия из связей + /// Чистовые отверстия из текущего документа + /// Элементы конструкций из текущего документа + private ICollection GetOpeningsMepIncomingTasksViewModels( + ICollection incomingTasks, + ICollection realOpenings, + ICollection constructureElementsIds) { + + var incomingTasksViewModels = new HashSet(); + + using(var pb = GetPlatformService()) { + pb.StepValue = _constantsProvider.ProgressBarStepLarge; + pb.DisplayTitleFormat = "Анализ заданий... [{0}\\{1}]"; + var progress = pb.CreateProgress(); + pb.MaxValue = incomingTasks.Count; + var ct = pb.CreateCancellationToken(); + pb.Show(); + + int i = 0; + foreach(var incomingTask in incomingTasks) { + ct.ThrowIfCancellationRequested(); + progress.Report(i); + try { + incomingTask.UpdateStatusAndHostName(realOpenings, constructureElementsIds); + } catch(ArgumentException) { + //не удалось получить солид у задания на отверстие. Например, если его толщина равна 0 + continue; + } + incomingTasksViewModels.Add(new OpeningMepTaskIncomingViewModel(incomingTask)); + i++; + } + } + return incomingTasksViewModels; + } + + /// + /// Возвращает коллекцию моделей представления чистовых отверстий, размещенных в активном документе КР + /// + /// Чистовые отверстия, размещенные в активном документе КР + /// Делегат для обновления статусов размещенных чистовых отверстий КР + private ICollection GetOpeningsRealKrViewModels( + ICollection openingsReal, + Action updateStatus) { + + var openingsRealViewModels = new HashSet(); + + using(var pb = GetPlatformService()) { + pb.StepValue = _constantsProvider.ProgressBarStepSmall; + pb.DisplayTitleFormat = "Анализ отверстий... [{0}]\\[{1}]"; + var progress = pb.CreateProgress(); + pb.MaxValue = openingsReal.Count; + var ct = pb.CreateCancellationToken(); + pb.Show(); + + int i = 0; + foreach(var openingReal in openingsReal) { + ct.ThrowIfCancellationRequested(); + progress.Report(i); + updateStatus.Invoke(openingReal); + if(openingReal.Status != OpeningModels.Enums.OpeningRealStatus.Correct) { + openingsRealViewModels.Add(new OpeningRealKrViewModel(openingReal)); + } + i++; + } + } + return openingsRealViewModels; + } } } diff --git a/src/RevitOpeningPlacement/ViewModels/Navigator/MepNavigatorForOutcomingTasksViewModel.cs b/src/RevitOpeningPlacement/ViewModels/Navigator/MepNavigatorForOutcomingTasksViewModel.cs index 01a92483b..ccb57da87 100644 --- a/src/RevitOpeningPlacement/ViewModels/Navigator/MepNavigatorForOutcomingTasksViewModel.cs +++ b/src/RevitOpeningPlacement/ViewModels/Navigator/MepNavigatorForOutcomingTasksViewModel.cs @@ -1,14 +1,23 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Linq; using System.Windows.Data; using System.Windows.Input; +using Autodesk.Revit.DB; + +using dosymep.SimpleServices; using dosymep.WPF.Commands; using dosymep.WPF.ViewModels; +using Ninject; +using Ninject.Syntax; + using RevitOpeningPlacement.Models; using RevitOpeningPlacement.Models.Interfaces; +using RevitOpeningPlacement.OpeningModels; +using RevitOpeningPlacement.Services; namespace RevitOpeningPlacement.ViewModels.Navigator { /// @@ -16,24 +25,26 @@ namespace RevitOpeningPlacement.ViewModels.Navigator { /// internal class MepNavigatorForOutcomingTasksViewModel : BaseViewModel { private readonly RevitRepository _revitRepository; + private readonly IConstantsProvider _constantsProvider; + private readonly IResolutionRoot _resolutionRoot; private OpeningMepTaskOutcomingViewModel _selectedOpeningMepTaskOutcoming; - public MepNavigatorForOutcomingTasksViewModel(RevitRepository revitRepository, ICollection openingsMepTasksOutcoming) { - if(revitRepository is null) { - throw new ArgumentNullException(nameof(revitRepository)); - } - if(openingsMepTasksOutcoming is null) { - throw new ArgumentNullException(nameof(openingsMepTasksOutcoming)); - } + public MepNavigatorForOutcomingTasksViewModel( + RevitRepository revitRepository, + IResolutionRoot resolutionRoot, + IConstantsProvider constantsProvider) { - _revitRepository = revitRepository; + _revitRepository = revitRepository ?? throw new ArgumentNullException(nameof(revitRepository)); + _constantsProvider = constantsProvider ?? throw new ArgumentNullException(nameof(constantsProvider)); + _resolutionRoot = resolutionRoot ?? throw new ArgumentNullException(nameof(resolutionRoot)); - OpeningsMepTaskOutcoming = new ObservableCollection(openingsMepTasksOutcoming); + OpeningsMepTaskOutcoming = new ObservableCollection(); OpeningsMepTasksOutcomingViewSource = new CollectionViewSource() { Source = OpeningsMepTaskOutcoming }; SelectCommand = RelayCommand.Create(SelectElement, CanSelect); RenewCommand = RelayCommand.Create(Renew); + LoadViewCommand = RelayCommand.Create(LoadView); } @@ -51,6 +62,8 @@ public OpeningMepTaskOutcomingViewModel SelectedOpeningMepTaskOutcoming { public ICommand RenewCommand { get; } + public ICommand LoadViewCommand { get; } + private void SelectElement(ISelectorAndHighlighter p) { _revitRepository.SelectAndShowElement(p); @@ -67,5 +80,45 @@ private void Renew() { }; _revitRepository.DoAction(action); } + + + private void LoadView() { + var outcomingTasks = _revitRepository.GetOpeningsMepTasksOutcoming(); + IList outcomingTasksIds = outcomingTasks.Select(task => task.Id).ToList(); + var mepElementsIds = _revitRepository.GetMepElementsIds(); + var openingTaskOutcomingViewModels = GetMepTaskOutcomingViewModels(outcomingTasks); + + OpeningsMepTaskOutcoming.Clear(); + foreach(var item in openingTaskOutcomingViewModels) { + OpeningsMepTaskOutcoming.Add(item); + } + } + + private ICollection GetMepTaskOutcomingViewModels( + ICollection outcomingTasks) { + + var service = _resolutionRoot.Get>(); + + var openingTaskOutcomingViewModels = new List(); + + using(var pb = GetPlatformService()) { + pb.StepValue = _constantsProvider.ProgressBarStepLarge; + pb.DisplayTitleFormat = "Анализ заданий... [{0}\\{1}]"; + var progress = pb.CreateProgress(); + pb.MaxValue = outcomingTasks.Count; + var ct = pb.CreateCancellationToken(); + pb.Show(); + + int i = 0; + foreach(var outcomingTask in outcomingTasks) { + ct.ThrowIfCancellationRequested(); + progress.Report(i); + service.UpdateInfo(outcomingTask); + openingTaskOutcomingViewModels.Add(new OpeningMepTaskOutcomingViewModel(outcomingTask)); + i++; + } + } + return openingTaskOutcomingViewModels; + } } } diff --git a/src/RevitOpeningPlacement/Views/NavigatorArIncomingView.xaml b/src/RevitOpeningPlacement/Views/NavigatorArIncomingView.xaml index ece0d0b12..3267931ca 100644 --- a/src/RevitOpeningPlacement/Views/NavigatorArIncomingView.xaml +++ b/src/RevitOpeningPlacement/Views/NavigatorArIncomingView.xaml @@ -12,6 +12,7 @@ xmlns:dxgt="http://schemas.devexpress.com/winfx/2008/xaml/grid/themekeys" xmlns:dxdo="http://schemas.devexpress.com/winfx/2008/xaml/docking" xmlns:mvvm="http://schemas.devexpress.com/winfx/2008/xaml/mvvm" + xmlns:dxmvvm="http://schemas.devexpress.com/winfx/2008/xaml/mvvm" x:Name="_this" mc:Ignorable="d" Title="NavigatorArIncomingView" @@ -25,6 +26,12 @@ WindowStartupLocation="Manual" Left="50" Top="50"> + + + + diff --git a/src/RevitOpeningPlacement/Views/NavigatorMepIncomingView.xaml b/src/RevitOpeningPlacement/Views/NavigatorMepIncomingView.xaml index 66c90b41d..99fd1a254 100644 --- a/src/RevitOpeningPlacement/Views/NavigatorMepIncomingView.xaml +++ b/src/RevitOpeningPlacement/Views/NavigatorMepIncomingView.xaml @@ -12,6 +12,7 @@ xmlns:dxgt="http://schemas.devexpress.com/winfx/2008/xaml/grid/themekeys" xmlns:dxdo="http://schemas.devexpress.com/winfx/2008/xaml/docking" xmlns:mvvm="http://schemas.devexpress.com/winfx/2008/xaml/mvvm" + xmlns:dxmvvm="http://schemas.devexpress.com/winfx/2008/xaml/mvvm" x:Name="_this" mc:Ignorable="d" Title="NavigatorMepIncomingView" @@ -25,6 +26,12 @@ WindowStartupLocation="Manual" Left="50" Top="50"> + + + + diff --git a/src/RevitOpeningPlacement/Views/NavigatorMepOutcomingView.xaml b/src/RevitOpeningPlacement/Views/NavigatorMepOutcomingView.xaml index cf304ec74..b3acf1d92 100644 --- a/src/RevitOpeningPlacement/Views/NavigatorMepOutcomingView.xaml +++ b/src/RevitOpeningPlacement/Views/NavigatorMepOutcomingView.xaml @@ -10,6 +10,7 @@ xmlns:dx="http://schemas.devexpress.com/winfx/2008/xaml/core" xmlns:dxg="http://schemas.devexpress.com/winfx/2008/xaml/grid" xmlns:dxgt="http://schemas.devexpress.com/winfx/2008/xaml/grid/themekeys" + xmlns:dxmvvm="http://schemas.devexpress.com/winfx/2008/xaml/mvvm" xmlns:mvvm="http://schemas.devexpress.com/winfx/2008/xaml/mvvm" x:Name="_this" mc:Ignorable="d" @@ -19,6 +20,12 @@ MinHeight="300" MinWidth="500" d:DataContext="{d:DesignInstance {x:Type vm:MepNavigatorForOutcomingTasksViewModel}, IsDesignTimeCreatable=False}"> + + + +