diff --git a/.github/workflows/publish.RevitRoundingOfAreas.yml b/.github/workflows/publish.RevitRoundingOfAreas.yml new file mode 100644 index 000000000..b52d46d6a --- /dev/null +++ b/.github/workflows/publish.RevitRoundingOfAreas.yml @@ -0,0 +1,37 @@ +name: publish RevitRoundingOfAreas + +on: + workflow_dispatch: + pull_request: + types: [ closed, synchronize, review_requested ] + branches: [ main, master ] + paths: + - '**RevitRoundingOfAreas**.cs' + - '**RevitRoundingOfAreas**.xaml' + +env: + plugin-name: "RevitRoundingOfAreas" + +jobs: + build: + name: build + runs-on: windows-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + + # Install the .NET workload + - name: Install .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: 8.0.x + + # Add DevExrepress package source: https://nuget.devexpress.com/ + - name: Add DevExpress package source + run: dotnet nuget add source https://nuget.devexpress.com/api -n DXFeed -u DevExpress -p ${{ secrets.DEVEXPRESS_FEED_AUTHORIZATION_KEY }} + + - name: Run './build.cmd ' + run: ./build.cmd publish --profile ${{ env.plugin-name }} --pull-request-merged ${{ github.event.pull_request.merged }} --extensions-app-token ${{ secrets.EXTENSIONS_APP_TOKEN }} --revit-plugins-app-token ${{ secrets.REVIT_PLUGINS_APP_TOKEN }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.nuke/parameters.RevitRoundingOfAreas.json b/.nuke/parameters.RevitRoundingOfAreas.json new file mode 100644 index 000000000..0e49b3e5b --- /dev/null +++ b/.nuke/parameters.RevitRoundingOfAreas.json @@ -0,0 +1,15 @@ +{ + "$schema": "./build.schema.json", + "Solution": "RevitPlugins.slnx", + "PluginName": "RevitRoundingOfAreas", + "PublishDirectory": "02.AR.extension\\АР.tab\\bin", + "RevitVersions": [ + "Rv2022", + "Rv2023", + "Rv2024" + ], + "IconUrl": "https://icons8.com/icon/79538/decrease-decimal", + "BundleName": "Рассчитать площади (соц. объекты)", + "BundleType": "InvokeButton", + "BundleOutput": "02.AR.extension\\АР.tab\\Квартирография.panel\\Помещения.pulldown" +} diff --git a/RevitPlugins.slnx b/RevitPlugins.slnx index d615afbfa..6cb2eedff 100644 --- a/RevitPlugins.slnx +++ b/RevitPlugins.slnx @@ -187,6 +187,9 @@ + + + diff --git a/src/RevitRoundingOfAreas/Models/CheckProjectParams.cs b/src/RevitRoundingOfAreas/Models/CheckProjectParams.cs new file mode 100644 index 000000000..f3d54456d --- /dev/null +++ b/src/RevitRoundingOfAreas/Models/CheckProjectParams.cs @@ -0,0 +1,26 @@ +using Autodesk.Revit.ApplicationServices; +using Autodesk.Revit.DB; + +using dosymep.Bim4Everyone.Templates; + +namespace RevitRoundingOfAreas.Models; + +internal class CheckProjectParams(SystemPluginConfig systemPluginConfig, Application application, Document document) { + private readonly ProjectParameters _projectParameters = ProjectParameters.Create(application); + private readonly bool _isChecked = true; + + /// + /// Метод проверки загрузки параметров + /// + public bool GetIsChecked() { + return _isChecked; + } + + /// + /// Метод копирования параметров проекта + /// + public CheckProjectParams CopyProjectParams() { + _projectParameters.SetupRevitParams(document, systemPluginConfig.RoomAreaParam); + return this; + } +} diff --git a/src/RevitRoundingOfAreas/Models/ConfigSettings.cs b/src/RevitRoundingOfAreas/Models/ConfigSettings.cs new file mode 100644 index 000000000..88aa8f624 --- /dev/null +++ b/src/RevitRoundingOfAreas/Models/ConfigSettings.cs @@ -0,0 +1,12 @@ +using Autodesk.Revit.DB; + +using dosymep.Bim4Everyone; + +namespace RevitRoundingOfAreas.Models; + +internal class ConfigSettings { + public ElementId SelectedPhaseId { get; set; } + public RevitParam SourceParam { get; set; } + public RevitParam TargetParam { get; set; } + public int Accuracy { get; set; } +} diff --git a/src/RevitRoundingOfAreas/Models/Enums/ElementsProviderType.cs b/src/RevitRoundingOfAreas/Models/Enums/ElementsProviderType.cs new file mode 100644 index 000000000..227a39a93 --- /dev/null +++ b/src/RevitRoundingOfAreas/Models/Enums/ElementsProviderType.cs @@ -0,0 +1,6 @@ +namespace RevitRoundingOfAreas.Models.Enums; +internal enum ElementsProviderType { + AllElementsProvider, + CurrentViewProvider, + SelectedElementsProvider +} diff --git a/src/RevitRoundingOfAreas/Models/Enums/WarningType.cs b/src/RevitRoundingOfAreas/Models/Enums/WarningType.cs new file mode 100644 index 000000000..bcfc1e195 --- /dev/null +++ b/src/RevitRoundingOfAreas/Models/Enums/WarningType.cs @@ -0,0 +1,6 @@ +namespace RevitRoundingOfAreas.Models.Enums; +internal enum WarningType { + NotEnclosed, + Redundant, + BrokenContour +} diff --git a/src/RevitRoundingOfAreas/Models/Factories/ProvidersFactory.cs b/src/RevitRoundingOfAreas/Models/Factories/ProvidersFactory.cs new file mode 100644 index 000000000..39f17eaa1 --- /dev/null +++ b/src/RevitRoundingOfAreas/Models/Factories/ProvidersFactory.cs @@ -0,0 +1,19 @@ +using RevitRoundingOfAreas.Models.Enums; +using RevitRoundingOfAreas.Models.Interfaces; +using RevitRoundingOfAreas.Models.Providers; + +namespace RevitRoundingOfAreas.Models.Factories; + +internal class ProvidersFactory(RevitRepository revitRepository) { + /// + /// Метод получения провайдера элементов по Enum + /// + public IElementsProvider GetElementsProvider(ElementsProviderType type) { + return type switch { + ElementsProviderType.AllElementsProvider => new ElementsProviderAll(revitRepository), + ElementsProviderType.CurrentViewProvider => new ElementsProviderCurrentView(revitRepository), + ElementsProviderType.SelectedElementsProvider => new ElementsProviderSelected(revitRepository), + _ => new ElementsProviderAll(revitRepository) + }; + } +} diff --git a/src/RevitRoundingOfAreas/Models/Interfaces/IElementsProvider.cs b/src/RevitRoundingOfAreas/Models/Interfaces/IElementsProvider.cs new file mode 100644 index 000000000..5d8c50c80 --- /dev/null +++ b/src/RevitRoundingOfAreas/Models/Interfaces/IElementsProvider.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; + +using Autodesk.Revit.DB; + +using RevitRoundingOfAreas.Models.Enums; + +namespace RevitRoundingOfAreas.Models.Interfaces; + +internal interface IElementsProvider { + /// + /// Тип провайдера элементов + /// + ElementsProviderType Type { get; } + /// + /// Метод получения элементов модели по стадиям + /// + List GetSpatialElements(ElementId phaseId); +} diff --git a/src/RevitRoundingOfAreas/Models/Interfaces/IWindowService.cs b/src/RevitRoundingOfAreas/Models/Interfaces/IWindowService.cs new file mode 100644 index 000000000..3f9ef52e7 --- /dev/null +++ b/src/RevitRoundingOfAreas/Models/Interfaces/IWindowService.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; + +using RevitRoundingOfAreas.Models.Warnings; + +namespace RevitRoundingOfAreas.Models.Interfaces; + +internal interface IWindowService { + /// + /// Метод закрытия основного окна + /// + /// + /// В данном методе производится закрытие основного окна при появлении предупреждений + /// + /// + /// Void. + /// + void CloseMainWindow(); + /// + /// Метод открытия окна. + /// + /// + /// В данном методе производится открытие кона предупреждений для отображения WarningElement. + /// + /// Коллекция предупреждений WarningElement. + /// + /// Void. + /// + void ShowWarningWindow(IReadOnlyCollection warningElements); +} diff --git a/src/RevitRoundingOfAreas/Models/ParamService.cs b/src/RevitRoundingOfAreas/Models/ParamService.cs new file mode 100644 index 000000000..55059cb30 --- /dev/null +++ b/src/RevitRoundingOfAreas/Models/ParamService.cs @@ -0,0 +1,83 @@ +using System.Collections.Generic; +using System.Linq; + +using Autodesk.Revit.DB; + +using dosymep.Bim4Everyone; +using dosymep.Bim4Everyone.SharedParams; +using dosymep.Bim4Everyone.SimpleServices; +using dosymep.Bim4Everyone.SystemParams; + +namespace RevitRoundingOfAreas.Models; + +internal class ParamService { + private readonly RevitRepository _revitRepository; + private readonly SystemPluginConfig _systemPluginConfig; + private readonly IRevitParamFactory _revitParamFactory; + private readonly ICollection _allParamElementIds; + + public ParamService( + RevitRepository revitRepository, + SystemPluginConfig systemPluginConfig, + IRevitParamFactory revitParamFactory) { + + _revitRepository = revitRepository; + _systemPluginConfig = systemPluginConfig; + _revitParamFactory = revitParamFactory; + + _allParamElementIds = GetAllParamElementIds(); + + AllRevitParams = GetAllRevitParams().AsReadOnly(); + + DefaultSourceParam = GetDefaultSourceParam(); + DefaultTargetParam = GetDefaultTargetParam(); + } + + public IReadOnlyList AllRevitParams { get; } + public RevitParam DefaultSourceParam { get; } + public RevitParam DefaultTargetParam { get; } + + private ICollection GetAllParamElementIds() { + var categoryId = new ElementId(BuiltInCategory.OST_Rooms); + + return ParameterFilterUtilities + .GetFilterableParametersInCommon(_revitRepository.Document, [categoryId]); + } + + private RevitParam GetDefaultSourceParam() { + return AllRevitParams + .OfType() + .FirstOrDefault(x => x.SystemParamId == _systemPluginConfig.SystemRoomAreaParamId); + } + + private RevitParam GetDefaultTargetParam() { + return AllRevitParams + .OfType() + .FirstOrDefault(x => x.Name.Equals(_systemPluginConfig.RoomAreaParam.Name)); + } + + private List GetAllRevitParams() { + if(_allParamElementIds.Count == 0) { + return []; + } + + List parameters = []; + + foreach(var elementId in _allParamElementIds) { + var revitParam = _revitParamFactory.Create(_revitRepository.Document, elementId); + + if(revitParam is null) { + continue; + } + + if(IsSupportedStorageType(revitParam)) { + parameters.Add(revitParam); + } + } + return parameters; + } + + private static bool IsSupportedStorageType(RevitParam revitParam) { + return revitParam.StorageType == StorageType.Double; + } +} diff --git a/src/RevitRoundingOfAreas/Models/PhaseModel.cs b/src/RevitRoundingOfAreas/Models/PhaseModel.cs new file mode 100644 index 000000000..7646dd52c --- /dev/null +++ b/src/RevitRoundingOfAreas/Models/PhaseModel.cs @@ -0,0 +1,8 @@ +using Autodesk.Revit.DB; + +namespace RevitRoundingOfAreas.Models; + +internal class PhaseModel { + public ElementId ElementId { get; set; } + public string Name { get; set; } +} diff --git a/src/RevitRoundingOfAreas/Models/PluginConfig.cs b/src/RevitRoundingOfAreas/Models/PluginConfig.cs new file mode 100644 index 000000000..175585939 --- /dev/null +++ b/src/RevitRoundingOfAreas/Models/PluginConfig.cs @@ -0,0 +1,68 @@ +using System; + +using Autodesk.Revit.DB; + +using dosymep.Bim4Everyone; +using dosymep.Bim4Everyone.ProjectConfigs; +using dosymep.Bim4Everyone.SharedParams; + +using pyRevitLabs.Json; + +namespace RevitRoundingOfAreas.Models; + +internal class PluginConfig : ProjectConfig { + + [JsonIgnore] + public override string ProjectConfigPath { get; set; } + + [JsonIgnore] + public override IConfigSerializer Serializer { get; set; } + + /// + /// Метод создания конфигурации плагина. + /// + /// + /// Сериализатор конфигурации. + /// Возвращает прочитанную конфигурацию плагина, либо созданный конфиг по умолчанию. + /// + public static PluginConfig GetPluginConfig(IConfigSerializer configSerializer) { + return new ProjectConfigBuilder() + .SetSerializer(configSerializer) + .SetPluginName(nameof(RevitRoundingOfAreas)) + .SetRevitVersion(ModuleEnvironment.RevitVersion) + .SetProjectConfigName(nameof(PluginConfig) + ".json") + .Build(); + } +} + +internal class RevitSettings : ProjectSettings { + /// + /// Наименование проекта. + /// + public override string ProjectName { get; set; } + + /// + /// Выбранная стадия проекта + /// + public ConfigSettings ConfigSettings { get; set; } +} + +internal class SystemPluginConfig { + // Название стадии по умолчанию + public string DefaultPhaseName => "Основная планировка"; + + // Количество вариантов знаков по умолчанию + public int DefaultAccuracyRange => 3; + + // Количество знаков по умолчанию + public int DefaultAccuracy => 1; + + // Сторона округления + public MidpointRounding MidpointRounding => MidpointRounding.AwayFromZero; + + // Системный параметр площади помещений по умолчанию + public BuiltInParameter SystemRoomAreaParamId => BuiltInParameter.ROOM_AREA; + + // Общий параметр площади помещений по умолчанию + public RevitParam RoomAreaParam => SharedParamsConfig.Instance.RoomArea; +} diff --git a/src/RevitRoundingOfAreas/Models/Providers/ElementsProviderAll.cs b/src/RevitRoundingOfAreas/Models/Providers/ElementsProviderAll.cs new file mode 100644 index 000000000..3939df5e7 --- /dev/null +++ b/src/RevitRoundingOfAreas/Models/Providers/ElementsProviderAll.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; + +using Autodesk.Revit.DB; + +using RevitRoundingOfAreas.Models.Enums; +using RevitRoundingOfAreas.Models.Interfaces; + +namespace RevitRoundingOfAreas.Models.Providers; + +internal class ElementsProviderAll(RevitRepository revitRepository) : IElementsProvider { + public ElementsProviderType Type => ElementsProviderType.AllElementsProvider; + + public List GetSpatialElements(ElementId phaseId) { + return revitRepository.GetAllSpatialModels(phaseId); + } +} diff --git a/src/RevitRoundingOfAreas/Models/Providers/ElementsProviderCurrentView.cs b/src/RevitRoundingOfAreas/Models/Providers/ElementsProviderCurrentView.cs new file mode 100644 index 000000000..4a473a248 --- /dev/null +++ b/src/RevitRoundingOfAreas/Models/Providers/ElementsProviderCurrentView.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; + +using Autodesk.Revit.DB; + +using RevitRoundingOfAreas.Models.Enums; +using RevitRoundingOfAreas.Models.Interfaces; + +namespace RevitRoundingOfAreas.Models.Providers; + +internal class ElementsProviderCurrentView(RevitRepository revitRepository) : IElementsProvider { + public ElementsProviderType Type => ElementsProviderType.CurrentViewProvider; + + public List GetSpatialElements(ElementId phaseId) { + return revitRepository.GetActiveViewSpatialModels(phaseId); + } +} diff --git a/src/RevitRoundingOfAreas/Models/Providers/ElementsProviderSelected.cs b/src/RevitRoundingOfAreas/Models/Providers/ElementsProviderSelected.cs new file mode 100644 index 000000000..83de7dc0f --- /dev/null +++ b/src/RevitRoundingOfAreas/Models/Providers/ElementsProviderSelected.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; + +using Autodesk.Revit.DB; + +using RevitRoundingOfAreas.Models.Enums; +using RevitRoundingOfAreas.Models.Interfaces; + +namespace RevitRoundingOfAreas.Models.Providers; + +internal class ElementsProviderSelected(RevitRepository revitRepository) : IElementsProvider { + public ElementsProviderType Type => ElementsProviderType.SelectedElementsProvider; + + public List GetSpatialElements(ElementId phaseId) { + return revitRepository.GetSelectedSpatialModels(phaseId); + } +} diff --git a/src/RevitRoundingOfAreas/Models/RevitRepository.cs b/src/RevitRoundingOfAreas/Models/RevitRepository.cs new file mode 100644 index 000000000..66920121e --- /dev/null +++ b/src/RevitRoundingOfAreas/Models/RevitRepository.cs @@ -0,0 +1,198 @@ +using System; +using System.Collections.Generic; + +using System.Linq; + +using Autodesk.Revit.ApplicationServices; +using Autodesk.Revit.DB; +using Autodesk.Revit.DB.Architecture; +using Autodesk.Revit.UI; + +using dosymep.Bim4Everyone; +using dosymep.Revit; +using dosymep.SimpleServices; + +namespace RevitRoundingOfAreas.Models; + +internal class RevitRepository( + UIApplication uiApplication, + ILocalizationService localizationService, + SystemPluginConfig systemPluginConfig) { + private UIApplication UiApplication { get; } = uiApplication; + private UIDocument ActiveUiDocument => UiApplication.ActiveUIDocument; + public Application Application => UiApplication.Application; + public Document Document => ActiveUiDocument.Document; + + /// + /// Основной метод округления площадей + /// + public void RoundingOfAreas(List spatialModels, RevitParam sourceParam, RevitParam targetParam, int accuracy) { + foreach(var spatialModel in spatialModels) { + double sourceValue = spatialModel.SpatialElement.GetParamValueOrDefault(sourceParam); + + if(sourceValue is 0) { + continue; + } + + double roundedValue = RoundWithAccuracy(sourceValue, accuracy); + + if(roundedValue is 0) { + continue; + } + spatialModel.SpatialElement.SetParamValue(targetParam, ConvertValueToInternalUnits(roundedValue)); + } + } + + /// + /// Метод получения всех помещений + /// + public List GetAllSpatialElements() { + return new FilteredElementCollector(Document) + .OfCategory(BuiltInCategory.OST_Rooms) + .WhereElementIsNotElementType() + .OfType() + .ToList(); + } + + /// + /// Метод получения всех помещений SpatialModel + /// + public List GetAllSpatialModels(ElementId phaseId) { + return GetAllSpatialElements() + .Where(spatial => ValidatePhase(spatial, phaseId)) + .Select(CreateSpatialModel) + .ToList(); + } + + /// + /// Метод получения всех помещений SpatialModel на активном виде + /// + public List GetActiveViewSpatialModels(ElementId phaseId) { + return new FilteredElementCollector(Document, Document.ActiveView.Id) + .OfCategory(BuiltInCategory.OST_Rooms) + .WhereElementIsNotElementType() + .OfType() + .Where(spatial => ValidatePhase(spatial, phaseId)) + .Select(CreateSpatialModel) + .ToList(); + } + + /// + /// Метод получения всех выделенных помещений SpatialModel + /// + public List GetSelectedSpatialModels(ElementId phaseId) { + return !HasSelectedRooms() + ? [] + : GetSelectedElements() + .OfType() + .Where(spatial => ValidatePhase(spatial, phaseId)) + .Select(CreateSpatialModel) + .ToList(); + } + + /// + /// Метод проверки, есть ли выделенные помещения + /// + public bool HasSelectedRooms() { + var selected = GetSelectedElements().ToArray(); + return selected.Count() != 0 + && selected.Any(element => element is Room); + } + + /// + /// Метод проверки, есть ли помещения на активном виде + /// + public bool HasRoomsOnCurrentView() { + var viewId = Document.ActiveView.Id; + + return new FilteredElementCollector(Document, viewId) + .OfCategory(BuiltInCategory.OST_Rooms) + .WhereElementIsNotElementType() + .Any(); + } + + /// + /// Метод выделения элементов в документе + /// + public void SetSelected(ElementId elementId) { + List listElements = [elementId]; + ActiveUiDocument.SetSelectedElements(listElements); + } + + /// + /// Метод получения стадий проекта + /// + public IEnumerable GetPhaseModels() { + var phases = Document.Phases; + return phases + .Cast() + .Select(phase => new PhaseModel { + ElementId = phase.Id, + Name = phase.Name + }); + } + + /// + /// Метод получения стадии по имени + /// + public ElementId GetPhaseIdByName(string name) { + return GetPhaseModels() + .FirstOrDefault(phase => phase != null && phase.Name == name) + ?.ElementId + ?? ElementId.InvalidElementId; + } + + // Метод получения всех выделенных элементов модели + private IEnumerable GetSelectedElements() { + return ActiveUiDocument.GetSelectedElements(); + } + + // Создания объекта SpatialModel + private SpatialModel CreateSpatialModel(SpatialElement spatialElement) { + return new SpatialModel { + SpatialElement = spatialElement, + LevelName = GetLevelName(spatialElement) + }; + } + + // Получение уровня помещения + private string GetLevelName(SpatialElement spatialElement) { + if(spatialElement is null) { + localizationService.GetLocalizedString("RevitRepository.NoLevel"); + } + var levelId = spatialElement?.LevelId; + if(levelId == ElementId.InvalidElementId) { + localizationService.GetLocalizedString("RevitRepository.NoLevel"); + } + return Document.GetElement(levelId)?.Name + ?? localizationService.GetLocalizedString("RevitRepository.NoLevel"); + } + + // Метод валидации стадии + private bool ValidatePhase(SpatialElement spatialElement, ElementId phaseId) { + var spatialPhaseId = spatialElement.GetParamValueOrDefault( + BuiltInParameter.ROOM_PHASE, + ElementId.InvalidElementId); + return spatialPhaseId != ElementId.InvalidElementId && spatialPhaseId == phaseId; + } + + // Метод округления числа с заданной точкой после запятой + private double RoundWithAccuracy(double? value, int accuracy) { + return value.HasValue + ? Math.Round( + ConvertValueFromInternalUnits(value.Value), + accuracy, + systemPluginConfig.MidpointRounding) + : 0; + } + + // Метод конвертации в миллиметры + private double ConvertValueFromInternalUnits(double value) { + return UnitUtils.ConvertFromInternalUnits(value, UnitTypeId.SquareMeters); + } + + // Метод конвертации в футы + private double ConvertValueToInternalUnits(double value) { + return UnitUtils.ConvertToInternalUnits(value, UnitTypeId.SquareMeters); + } +} diff --git a/src/RevitRoundingOfAreas/Models/SpatialElementCheckService.cs b/src/RevitRoundingOfAreas/Models/SpatialElementCheckService.cs new file mode 100644 index 000000000..f52329a0e --- /dev/null +++ b/src/RevitRoundingOfAreas/Models/SpatialElementCheckService.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; + +using Autodesk.Revit.DB.Architecture; + +using dosymep.Revit; + +using RevitRoundingOfAreas.Models.Enums; +using RevitRoundingOfAreas.Models.Warnings; + +namespace RevitRoundingOfAreas.Models; + +internal class SpatialElementCheckService { + + public IReadOnlyCollection CheckSpatialElements(List spatialModels) { + if(spatialModels.Count == 0) { + return []; + } + List warnings = []; + foreach(var spatialModel in spatialModels) { + var room = spatialModel.SpatialElement as Room; + if(room.IsRedundant()) { + warnings.Add(new WarningRedundantElement { + WarningType = WarningType.Redundant, + SpatialModel = spatialModel + }); + } else if(room.IsNotEnclosed()) { + warnings.Add(new WarningNotEnclosedElement { + WarningType = WarningType.NotEnclosed, + SpatialModel = spatialModel + }); + } + } + return warnings; + } +} diff --git a/src/RevitRoundingOfAreas/Models/SpatialModel.cs b/src/RevitRoundingOfAreas/Models/SpatialModel.cs new file mode 100644 index 000000000..83df94209 --- /dev/null +++ b/src/RevitRoundingOfAreas/Models/SpatialModel.cs @@ -0,0 +1,8 @@ +using Autodesk.Revit.DB; + +namespace RevitRoundingOfAreas.Models; + +internal class SpatialModel { + public SpatialElement SpatialElement { get; set; } + public string LevelName { get; set; } +} diff --git a/src/RevitRoundingOfAreas/Models/Warnings/WarningElement.cs b/src/RevitRoundingOfAreas/Models/Warnings/WarningElement.cs new file mode 100644 index 000000000..2e1e63e73 --- /dev/null +++ b/src/RevitRoundingOfAreas/Models/Warnings/WarningElement.cs @@ -0,0 +1,8 @@ +using RevitRoundingOfAreas.Models.Enums; + +namespace RevitRoundingOfAreas.Models.Warnings; + +internal class WarningElement { + public WarningType WarningType { get; set; } + public SpatialModel SpatialModel { get; set; } +} diff --git a/src/RevitRoundingOfAreas/Models/Warnings/WarningNotEnclosedElement.cs b/src/RevitRoundingOfAreas/Models/Warnings/WarningNotEnclosedElement.cs new file mode 100644 index 000000000..53e68696c --- /dev/null +++ b/src/RevitRoundingOfAreas/Models/Warnings/WarningNotEnclosedElement.cs @@ -0,0 +1,3 @@ +namespace RevitRoundingOfAreas.Models.Warnings; +internal class WarningNotEnclosedElement : WarningElement { +} diff --git a/src/RevitRoundingOfAreas/Models/Warnings/WarningRedundantElement.cs b/src/RevitRoundingOfAreas/Models/Warnings/WarningRedundantElement.cs new file mode 100644 index 000000000..afbabf923 --- /dev/null +++ b/src/RevitRoundingOfAreas/Models/Warnings/WarningRedundantElement.cs @@ -0,0 +1,3 @@ +namespace RevitRoundingOfAreas.Models.Warnings; +internal class WarningRedundantElement : WarningElement { +} diff --git a/src/RevitRoundingOfAreas/Models/WindowService.cs b/src/RevitRoundingOfAreas/Models/WindowService.cs new file mode 100644 index 000000000..71d93f80e --- /dev/null +++ b/src/RevitRoundingOfAreas/Models/WindowService.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; + +using Ninject; +using Ninject.Syntax; + +using RevitRoundingOfAreas.Models.Interfaces; +using RevitRoundingOfAreas.Models.Warnings; +using RevitRoundingOfAreas.ViewModels.Warnings; +using RevitRoundingOfAreas.Views; + +namespace RevitRoundingOfAreas.Models; + +internal class WindowService(IResolutionRoot resolutionRoot) : IWindowService { + public void CloseMainWindow() { + var mainWindow = resolutionRoot.Get(); + mainWindow.Close(); + } + + public void ShowWarningWindow(IReadOnlyCollection warningElements) { + var warningViewModel = resolutionRoot.Get(); + warningViewModel.WarningElementsCollection = warningElements; + var warningsWindow = resolutionRoot.Get(); + warningsWindow.DataContext = warningViewModel; + warningViewModel.LoadView(); + warningsWindow.Show(); + } +} diff --git a/src/RevitRoundingOfAreas/README.md b/src/RevitRoundingOfAreas/README.md new file mode 100644 index 000000000..11648ab3a --- /dev/null +++ b/src/RevitRoundingOfAreas/README.md @@ -0,0 +1,12 @@ +# RevitRoundingOfAreas (Рассчитать площади (соц. объекты)) +Плагин для автоматического округления системной площади в текстовый параметр. Для социальных объектов + +# Сборка проекта +``` +nuke compile --profile RevitRoundingOfAreas +``` + +# Публикация проекта +``` +nuke publish --profile RevitRoundingOfAreas +``` diff --git a/src/RevitRoundingOfAreas/RevitRoundingOfAreas.csproj b/src/RevitRoundingOfAreas/RevitRoundingOfAreas.csproj new file mode 100644 index 000000000..ae3efaefb --- /dev/null +++ b/src/RevitRoundingOfAreas/RevitRoundingOfAreas.csproj @@ -0,0 +1,6 @@ + + + true + 12 + + diff --git a/src/RevitRoundingOfAreas/RevitRoundingOfAreasCommand.cs b/src/RevitRoundingOfAreas/RevitRoundingOfAreasCommand.cs new file mode 100644 index 000000000..8b2804789 --- /dev/null +++ b/src/RevitRoundingOfAreas/RevitRoundingOfAreasCommand.cs @@ -0,0 +1,127 @@ +using System; +using System.Globalization; +using System.Reflection; +using System.Windows; + + +using Autodesk.Revit.Attributes; +using Autodesk.Revit.UI; + +using dosymep.Bim4Everyone; +using dosymep.Bim4Everyone.ProjectConfigs; +using dosymep.Bim4Everyone.SimpleServices; +using dosymep.SimpleServices; +using dosymep.WpfCore.Ninject; +using dosymep.WpfUI.Core.Ninject; + +using Ninject; + +using RevitRoundingOfAreas.Models; +using RevitRoundingOfAreas.Models.Interfaces; +using RevitRoundingOfAreas.ViewModels; +using RevitRoundingOfAreas.ViewModels.Warnings; +using RevitRoundingOfAreas.Views; + +namespace RevitRoundingOfAreas; + +/// +/// Класс команды Revit плагина. +/// +/// +/// В данном классе должна быть инициализация контейнера плагина и указание названия команды. +/// +[Transaction(TransactionMode.Manual)] +public class RevitRoundingOfAreasCommand : BasePluginCommand { + /// + /// Инициализирует команду плагина. + /// + public RevitRoundingOfAreasCommand() { + PluginName = "Рассчитать площади (соц. объекты)"; + } + + /// + /// Метод выполнения основного кода плагина. + /// + /// Интерфейс взаимодействия с Revit. + /// + /// В случаях, когда не используется конфигурация + /// или локализация, требуется удалять их использование полностью во всем проекте. + /// + protected override void Execute(UIApplication uiApplication) { + // Создание контейнера зависимостей плагина с сервисами из платформы + using var kernel = uiApplication.CreatePlatformServices(); + + // Создание системных настроек + kernel.Bind() + .ToSelf() + .InSingletonScope(); + + // Настройка доступа к Revit + kernel.Bind() + .ToSelf() + .InSingletonScope(); + + // Настройка доступа к классу параметров + kernel.Bind() + .ToSelf() + .InSingletonScope(); + + // Настройка доступа к классу проверок + kernel.Bind() + .ToSelf() + .InSingletonScope(); + + // Настройка доступа к сервису по управлению окнами + kernel.Bind() + .To() + .InSingletonScope(); + + // Настройка конфигурации плагина + kernel.Bind() + .ToMethod(c => PluginConfig.GetPluginConfig(c.Kernel.Get())); + + // Настройка сервиса окошек сообщений + kernel.UseWpfUIMessageBox(); + + // Используем сервис обновления тем для WinUI + kernel.UseWpfUIThemeUpdater(); + + // Настройка запуска окна + kernel.BindMainWindow(); + + // Настройка запуска окна предупреждений + kernel.BindOtherWindow(); + + // Настройка локализации, + // получение имени сборки откуда брать текст + string assemblyName = Assembly.GetExecutingAssembly().GetName().Name; + + // Настройка локализации, + // установка дефолтной локализации "ru-RU" + kernel.UseWpfLocalization( + $"/{assemblyName};component/assets/localization/language.xaml", + CultureInfo.GetCultureInfo("ru-RU")); + + var messageBoxService = kernel.Get(); + var localizationService = kernel.Get(); + var systemPluginConfig = kernel.Get(); + + // Загрузка параметров проекта + bool isParamChecked = new CheckProjectParams( + systemPluginConfig, uiApplication.Application, uiApplication.ActiveUIDocument.Document) + .CopyProjectParams() + .GetIsChecked(); + + if(!isParamChecked) { + messageBoxService.Show( + localizationService.GetLocalizedString("Common.ParamErrorMessageBody"), + localizationService.GetLocalizedString("Common.ConfigErrorMessageTitle"), + MessageBoxButton.OK, + MessageBoxImage.Exclamation); + throw new OperationCanceledException(); + } + + // Вызывает стандартное уведомление + Notification(kernel.Get()); + } +} diff --git a/src/RevitRoundingOfAreas/ViewModels/AccuracyViewModel.cs b/src/RevitRoundingOfAreas/ViewModels/AccuracyViewModel.cs new file mode 100644 index 000000000..c9743e561 --- /dev/null +++ b/src/RevitRoundingOfAreas/ViewModels/AccuracyViewModel.cs @@ -0,0 +1,14 @@ +using dosymep.WPF.ViewModels; + +namespace RevitRoundingOfAreas.ViewModels; + +internal class AccuracyViewModel : BaseViewModel { + private string _name; + + public int Accuracy { get; set; } + + public string Name { + get => _name; + set => RaiseAndSetIfChanged(ref _name, value); + } +} diff --git a/src/RevitRoundingOfAreas/ViewModels/MainViewModel.cs b/src/RevitRoundingOfAreas/ViewModels/MainViewModel.cs new file mode 100644 index 000000000..12c9c8d05 --- /dev/null +++ b/src/RevitRoundingOfAreas/ViewModels/MainViewModel.cs @@ -0,0 +1,278 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Windows.Input; + +using Autodesk.Revit.DB; + +using dosymep.Revit; +using dosymep.SimpleServices; +using dosymep.WPF.Commands; +using dosymep.WPF.ViewModels; + +using RevitRoundingOfAreas.Models; +using RevitRoundingOfAreas.Models.Enums; +using RevitRoundingOfAreas.Models.Factories; +using RevitRoundingOfAreas.Models.Interfaces; + +namespace RevitRoundingOfAreas.ViewModels; + +internal class MainViewModel : BaseViewModel { + private readonly PluginConfig _pluginConfig; + private readonly SystemPluginConfig _systemPluginConfig; + private readonly RevitRepository _revitRepository; + private readonly ParamService _paramService; + private readonly ProvidersFactory _providersFactory; + private readonly SpatialElementCheckService _spatialElementCheckService; + private readonly IWindowService _windowService; + private readonly ILocalizationService _localizationService; + private ConfigSettings _configSettings; + private ObservableCollection _range; + private RangeViewModel _selectedRange; + private ObservableCollection _phases; + private PhaseViewModel _selectedPhase; + private ObservableCollection _sourceParams; + private ParamViewModel _selectedSourceParam; + private ObservableCollection _targetParams; + private ParamViewModel _selectedTargetParam; + private ObservableCollection _accuracy; + private AccuracyViewModel _selectedAccuracy; + private string _errorText; + + public MainViewModel( + PluginConfig pluginConfig, + SystemPluginConfig systemPluginConfig, + RevitRepository revitRepository, + ParamService paramService, + ProvidersFactory providersFactory, + ILocalizationService localizationService, + IWindowService windowService, + SpatialElementCheckService spatialElementCheckService) { + + _pluginConfig = pluginConfig; + _systemPluginConfig = systemPluginConfig; + _revitRepository = revitRepository; + _paramService = paramService; + _providersFactory = providersFactory; + _localizationService = localizationService; + _windowService = windowService; + _spatialElementCheckService = spatialElementCheckService; + + LoadViewCommand = RelayCommand.Create(LoadView); + AcceptViewCommand = RelayCommand.Create(AcceptView, CanAcceptView); + } + + public ICommand LoadViewCommand { get; } + public ICommand AcceptViewCommand { get; } + + + public ObservableCollection Range { + get => _range; + set => RaiseAndSetIfChanged(ref _range, value); + } + public RangeViewModel SelectedRange { + get => _selectedRange; + set => RaiseAndSetIfChanged(ref _selectedRange, value); + } + public ObservableCollection Phases { + get => _phases; + set => RaiseAndSetIfChanged(ref _phases, value); + } + public PhaseViewModel SelectedPhase { + get => _selectedPhase; + set => RaiseAndSetIfChanged(ref _selectedPhase, value); + } + public ObservableCollection SourceParams { + get => _sourceParams; + set => RaiseAndSetIfChanged(ref _sourceParams, value); + } + public ParamViewModel SelectedSourceParam { + get => _selectedSourceParam; + set => RaiseAndSetIfChanged(ref _selectedSourceParam, value); + } + public ObservableCollection TargetParams { + get => _targetParams; + set => RaiseAndSetIfChanged(ref _targetParams, value); + } + public ParamViewModel SelectedTargetParam { + get => _selectedTargetParam; + set => RaiseAndSetIfChanged(ref _selectedTargetParam, value); + } + public ObservableCollection Accuracy { + get => _accuracy; + set => RaiseAndSetIfChanged(ref _accuracy, value); + } + public AccuracyViewModel SelectedAccuracy { + get => _selectedAccuracy; + set => RaiseAndSetIfChanged(ref _selectedAccuracy, value); + } + public string ErrorText { + get => _errorText; + set => RaiseAndSetIfChanged(ref _errorText, value); + } + + // Метод получения коллекции AccuracyViewModel для Accuracy + private IEnumerable GetAccuracyViewModels() { + for(int i = 1; i <= _systemPluginConfig.DefaultAccuracyRange; i++) { + yield return new AccuracyViewModel { + Accuracy = i, + Name = i.ToString(), + }; + } + } + + // Метод получения коллекции ParamViewModel для SourceParams + private IEnumerable GetSourceParamViewModels() { + return _paramService.AllRevitParams + .Select(param => new ParamViewModel { + Name = param.Name, + RevitParam = param + }); + } + + // Метод получения коллекции ParamViewModel для TargetParams + private IEnumerable GetTargetParamViewModels() { + return _paramService.AllRevitParams + .Where(revitParam => { + var spatialElements = _revitRepository.GetAllSpatialElements(); + var randomSpatialElement = spatialElements.FirstOrDefault(); + if(randomSpatialElement is null) { + return false; + } + var param = revitParam.GetParam(randomSpatialElement); + return !param.IsReadOnly; + }) + .Select(param => new ParamViewModel { + Name = param.Name, + RevitParam = param + }); + } + + // Метод получения коллекции PhaseViewModel для Phases + private IEnumerable GetPhaseViewModels() { + return _revitRepository.GetPhaseModels() + .Select(phase => new PhaseViewModel { + Name = phase.Name, + ElementId = phase.ElementId + }); + } + + // Метод получения коллекции RangeElementsViewModel для RangeElements + private IEnumerable GetRangeViewModels() { + var providers = Enum.GetValues(typeof(ElementsProviderType)).Cast(); + return providers + .Select(provider => new RangeViewModel { + Name = _localizationService.GetLocalizedString($"MainViewModel.{provider}"), + ElementsProvider = _providersFactory.GetElementsProvider(provider) + }); + } + + private void LoadView() { + LoadConfig(); + + Range = new ObservableCollection(GetRangeViewModels()); + SelectedRange = ResolveDefaultRange() ?? Range.FirstOrDefault(); + + Phases = new ObservableCollection(GetPhaseViewModels()); + SelectedPhase = Phases + .FirstOrDefault(phase => phase.ElementId == _configSettings.SelectedPhaseId) + ?? Phases.LastOrDefault(); + + SourceParams = new ObservableCollection(GetSourceParamViewModels()); + SelectedSourceParam = SourceParams + .FirstOrDefault(param => param.RevitParam.Id == _configSettings.SourceParam?.Id) + ?? SourceParams.FirstOrDefault(); + + TargetParams = new ObservableCollection(GetTargetParamViewModels()); + SelectedTargetParam = TargetParams + .FirstOrDefault(param => param.RevitParam.Id == _configSettings.TargetParam?.Id) + ?? TargetParams.FirstOrDefault(); + + Accuracy = new ObservableCollection(GetAccuracyViewModels()); + SelectedAccuracy = Accuracy + .FirstOrDefault(accuracy => accuracy.Accuracy == _configSettings.Accuracy) + ?? Accuracy.FirstOrDefault(); + } + + private RangeViewModel ResolveDefaultRange() { + return _revitRepository.HasSelectedRooms() + ? Range.FirstOrDefault(x => x.ElementsProvider.Type == ElementsProviderType.SelectedElementsProvider) + : _revitRepository.HasRoomsOnCurrentView() + ? Range.FirstOrDefault(x => x.ElementsProvider.Type == ElementsProviderType.CurrentViewProvider) + : Range.FirstOrDefault(x => x.ElementsProvider.Type == ElementsProviderType.AllElementsProvider); + } + + private void AcceptView() { + SaveConfig(); + var spatialElements = SelectedRange.ElementsProvider.GetSpatialElements(SelectedPhase.ElementId); + var warnings = _spatialElementCheckService.CheckSpatialElements(spatialElements); + + if(warnings.Count != 0) { + _windowService.CloseMainWindow(); + _windowService.ShowWarningWindow(warnings); + return; + } + + string transactionName = _localizationService.GetLocalizedString("MainViewModel.TransactionName"); + using var t = _revitRepository.Document.StartTransaction(transactionName); + _revitRepository.RoundingOfAreas( + spatialElements, + SelectedSourceParam.RevitParam, + SelectedTargetParam.RevitParam, + SelectedAccuracy.Accuracy + ); + t.Commit(); + } + + private bool CanAcceptView() { + if(SelectedRange != null) { + if(SelectedRange.ElementsProvider.Type == ElementsProviderType.SelectedElementsProvider + && !_revitRepository.HasSelectedRooms()) { + ErrorText = _localizationService.GetLocalizedString("MainViewModel.NoSelection"); + return false; + } + if(SelectedRange.ElementsProvider.Type == ElementsProviderType.CurrentViewProvider + && !_revitRepository.HasRoomsOnCurrentView()) { + ErrorText = _localizationService.GetLocalizedString("MainViewModel.NoRoomsOnView"); + return false; + } + } + ErrorText = null; + return true; + } + + private void LoadConfig() { + var setting = _pluginConfig.GetSettings(_revitRepository.Document); + _configSettings = setting?.ConfigSettings ?? new ConfigSettings(); + ApplyDefaultsConfig(); + } + + // Метод применения дефолтных значений настроек. + private void ApplyDefaultsConfig() { + if(_configSettings.SelectedPhaseId is null || _configSettings.SelectedPhaseId == ElementId.InvalidElementId) { + _configSettings.SelectedPhaseId = _revitRepository.GetPhaseIdByName(_systemPluginConfig.DefaultPhaseName); + } + + _configSettings.SourceParam ??= _paramService.DefaultSourceParam; + _configSettings.TargetParam ??= _paramService.DefaultTargetParam; + + if(_configSettings.Accuracy <= 0) { + _configSettings.Accuracy = _systemPluginConfig.DefaultAccuracy; + } + } + + private void SaveConfig() { + var setting = _pluginConfig.GetSettings(_revitRepository.Document) + ?? _pluginConfig.AddSettings(_revitRepository.Document); + + setting.ConfigSettings = new ConfigSettings { + SelectedPhaseId = SelectedPhase?.ElementId ?? ElementId.InvalidElementId, + SourceParam = SelectedSourceParam?.RevitParam, + TargetParam = SelectedTargetParam?.RevitParam, + Accuracy = SelectedAccuracy?.Accuracy ?? _systemPluginConfig.DefaultAccuracy + }; + + _pluginConfig.SaveProjectConfig(); + } +} diff --git a/src/RevitRoundingOfAreas/ViewModels/ParamViewModel.cs b/src/RevitRoundingOfAreas/ViewModels/ParamViewModel.cs new file mode 100644 index 000000000..bf4d2f9f4 --- /dev/null +++ b/src/RevitRoundingOfAreas/ViewModels/ParamViewModel.cs @@ -0,0 +1,15 @@ +using dosymep.Bim4Everyone; +using dosymep.WPF.ViewModels; + +namespace RevitRoundingOfAreas.ViewModels; + +internal class ParamViewModel : BaseViewModel { + private string _name; + + public RevitParam RevitParam { get; set; } + + public string Name { + get => _name; + set => RaiseAndSetIfChanged(ref _name, value); + } +} diff --git a/src/RevitRoundingOfAreas/ViewModels/PhaseViewModel.cs b/src/RevitRoundingOfAreas/ViewModels/PhaseViewModel.cs new file mode 100644 index 000000000..3bee7900d --- /dev/null +++ b/src/RevitRoundingOfAreas/ViewModels/PhaseViewModel.cs @@ -0,0 +1,16 @@ +using Autodesk.Revit.DB; + +using dosymep.WPF.ViewModels; + +namespace RevitRoundingOfAreas.ViewModels; + +internal class PhaseViewModel : BaseViewModel { + private string _name; + + public ElementId ElementId { get; set; } + + public string Name { + get => _name; + set => RaiseAndSetIfChanged(ref _name, value); + } +} diff --git a/src/RevitRoundingOfAreas/ViewModels/RangeViewModel.cs b/src/RevitRoundingOfAreas/ViewModels/RangeViewModel.cs new file mode 100644 index 000000000..2c076ce3e --- /dev/null +++ b/src/RevitRoundingOfAreas/ViewModels/RangeViewModel.cs @@ -0,0 +1,17 @@ +using dosymep.WPF.ViewModels; + +using RevitRoundingOfAreas.Models.Interfaces; + +namespace RevitRoundingOfAreas.ViewModels; + +internal class RangeViewModel : BaseViewModel { + + private string _name; + + public IElementsProvider ElementsProvider { get; set; } + + public string Name { + get => _name; + set => RaiseAndSetIfChanged(ref _name, value); + } +} diff --git a/src/RevitRoundingOfAreas/ViewModels/Warnings/WarningElementViewModel.cs b/src/RevitRoundingOfAreas/ViewModels/Warnings/WarningElementViewModel.cs new file mode 100644 index 000000000..a9377cb21 --- /dev/null +++ b/src/RevitRoundingOfAreas/ViewModels/Warnings/WarningElementViewModel.cs @@ -0,0 +1,36 @@ +using System.Windows.Input; + +using Autodesk.Revit.DB; + +using dosymep.WPF.ViewModels; + +using RevitRoundingOfAreas.Models; + +namespace RevitRoundingOfAreas.ViewModels.Warnings; + +internal class WarningElementViewModel : BaseViewModel { + private ElementId _elementId; + private string _name; + private string _levelName; + + public WarningElementViewModel(SpatialModel spatialModel) { + Name = spatialModel.SpatialElement.Name; + LevelName = spatialModel.LevelName; + ElementId = spatialModel.SpatialElement.Id; + } + + public ICommand ShowElementCommand { get; set; } + + public string Name { + get => _name; + set => RaiseAndSetIfChanged(ref _name, value); + } + public string LevelName { + get => _levelName; + set => RaiseAndSetIfChanged(ref _levelName, value); + } + public ElementId ElementId { + get => _elementId; + set => RaiseAndSetIfChanged(ref _elementId, value); + } +} diff --git a/src/RevitRoundingOfAreas/ViewModels/Warnings/WarningGroupViewModel.cs b/src/RevitRoundingOfAreas/ViewModels/Warnings/WarningGroupViewModel.cs new file mode 100644 index 000000000..97fbc55f2 --- /dev/null +++ b/src/RevitRoundingOfAreas/ViewModels/Warnings/WarningGroupViewModel.cs @@ -0,0 +1,52 @@ +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Windows.Input; + +using dosymep.WPF.ViewModels; + +using RevitRoundingOfAreas.Models.Warnings; + +namespace RevitRoundingOfAreas.ViewModels.Warnings; + +internal class WarningGroupViewModel : BaseViewModel { + private ObservableCollection _warnings; + private string _caption; + private string _description; + private string _warningQuantity; + + public IReadOnlyCollection WarningElements { get; set; } = []; + public ICommand ShowElementCommand { get; set; } + + public ObservableCollection Warnings { + get => _warnings; + set => RaiseAndSetIfChanged(ref _warnings, value); + } + public string Caption { + get => _caption; + set => RaiseAndSetIfChanged(ref _caption, value); + } + public string Description { + get => _description; + set => RaiseAndSetIfChanged(ref _description, value); + } + public string WarningQuantity { + get => _warningQuantity; + set => RaiseAndSetIfChanged(ref _warningQuantity, value); + } + + /// + /// Метод загрузки окна + /// + public void LoadView() { + Warnings = new(GetWarningElementViewModel()); + } + + // Метод получения списка WarningElementViewModel + private IEnumerable GetWarningElementViewModel() { + return WarningElements + .Select(warningElement => new WarningElementViewModel(warningElement.SpatialModel) { + ShowElementCommand = ShowElementCommand + }); + } +} diff --git a/src/RevitRoundingOfAreas/ViewModels/Warnings/WarningsViewModel.cs b/src/RevitRoundingOfAreas/ViewModels/Warnings/WarningsViewModel.cs new file mode 100644 index 000000000..7f9750181 --- /dev/null +++ b/src/RevitRoundingOfAreas/ViewModels/Warnings/WarningsViewModel.cs @@ -0,0 +1,72 @@ +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Windows.Input; + +using Autodesk.Revit.DB; + +using dosymep.SimpleServices; +using dosymep.WPF.Commands; +using dosymep.WPF.ViewModels; + +using RevitRoundingOfAreas.Models; +using RevitRoundingOfAreas.Models.Warnings; + +namespace RevitRoundingOfAreas.ViewModels.Warnings; +internal class WarningsViewModel : BaseViewModel { + private readonly ILocalizationService _localizationService; + private readonly RevitRepository _revitRepository; + private ObservableCollection _warningViewModels; + private string _warningQuantity; + + public WarningsViewModel(ILocalizationService localizationService, RevitRepository revitRepository) { + _localizationService = localizationService; + _revitRepository = revitRepository; + + ShowElementCommand = RelayCommand.Create(ShowElement); + } + + public ICommand ShowElementCommand { get; } + public IReadOnlyCollection WarningElementsCollection { get; set; } + + public ObservableCollection WarningViewModels { + get => _warningViewModels; + set => RaiseAndSetIfChanged(ref _warningViewModels, value); + } + public string WarningQuantity { + get => _warningQuantity; + set => RaiseAndSetIfChanged(ref _warningQuantity, value); + } + + /// + /// Метод загрузки окна + /// + public void LoadView() { + WarningViewModels = new ObservableCollection(GetWarningGroupViewModels()); + WarningQuantity = WarningViewModels.Count.ToString(); + } + + // Метод выделения элемента + private void ShowElement(ElementId elementId) { + _revitRepository.SetSelected(elementId); + } + + // Метод формирования списка WarningGroupViewModel + private IEnumerable GetWarningGroupViewModels() { + var regularGroups = WarningElementsCollection + .GroupBy(w => w.WarningType) + .Select(group => { + var vm = new WarningGroupViewModel { + Caption = _localizationService.GetLocalizedString($"WarningsViewModel.{group.Key}"), + Description = _localizationService.GetLocalizedString($"WarningsViewModel.{group.Key}Description"), + WarningElements = group.ToList(), + WarningQuantity = group.Count().ToString(), + ShowElementCommand = ShowElementCommand + }; + vm.LoadView(); + return vm; + }); + + return regularGroups; + } +} diff --git a/src/RevitRoundingOfAreas/Views/Edits/CustomEditControl.xaml b/src/RevitRoundingOfAreas/Views/Edits/CustomEditControl.xaml new file mode 100644 index 000000000..801d9a3d9 --- /dev/null +++ b/src/RevitRoundingOfAreas/Views/Edits/CustomEditControl.xaml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/RevitRoundingOfAreas/Views/Edits/CustomEditControl.xaml.cs b/src/RevitRoundingOfAreas/Views/Edits/CustomEditControl.xaml.cs new file mode 100644 index 000000000..e4275cfa5 --- /dev/null +++ b/src/RevitRoundingOfAreas/Views/Edits/CustomEditControl.xaml.cs @@ -0,0 +1,47 @@ +using System.ComponentModel; +using System.Windows; +using System.Windows.Markup; + +using Wpf.Ui.Controls; + +namespace RevitRoundingOfAreas.Views.Edits; + +[DefaultProperty(nameof(EditControl))] +[ContentProperty(nameof(EditControl))] +public partial class CustomEditControl { + public static readonly DependencyProperty IconProperty = DependencyProperty.Register( + nameof(Icon), typeof(IconElement), typeof(CustomEditControl), new PropertyMetadata(default(IconElement))); + + public static readonly DependencyProperty HeaderProperty = DependencyProperty.Register( + nameof(Header), typeof(string), typeof(CustomEditControl), new PropertyMetadata(default(string))); + + public static readonly DependencyProperty DescriptionProperty = DependencyProperty.Register( + nameof(Description), typeof(string), typeof(CustomEditControl), new PropertyMetadata(default(string))); + + public static readonly DependencyProperty EditControlProperty = DependencyProperty.Register( + nameof(EditControl), typeof(object), typeof(CustomEditControl), new PropertyMetadata(default(object))); + + public CustomEditControl() { + InitializeComponent(); + } + + public IconElement Icon { + get => (IconElement) GetValue(IconProperty); + set => SetValue(IconProperty, value); + } + + public string Header { + get => (string) GetValue(HeaderProperty); + set => SetValue(HeaderProperty, value); + } + + public string Description { + get => (string) GetValue(DescriptionProperty); + set => SetValue(DescriptionProperty, value); + } + + public object EditControl { + get => GetValue(EditControlProperty); + set => SetValue(EditControlProperty, value); + } +} diff --git a/src/RevitRoundingOfAreas/Views/Edits/CustomGroupEditControl.xaml b/src/RevitRoundingOfAreas/Views/Edits/CustomGroupEditControl.xaml new file mode 100644 index 000000000..2d408dc76 --- /dev/null +++ b/src/RevitRoundingOfAreas/Views/Edits/CustomGroupEditControl.xaml @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/RevitRoundingOfAreas/Views/Edits/CustomGroupEditControl.xaml.cs b/src/RevitRoundingOfAreas/Views/Edits/CustomGroupEditControl.xaml.cs new file mode 100644 index 000000000..48d3a00fe --- /dev/null +++ b/src/RevitRoundingOfAreas/Views/Edits/CustomGroupEditControl.xaml.cs @@ -0,0 +1,112 @@ +using System.ComponentModel; +using System.Windows; +using System.Windows.Markup; + +using Wpf.Ui.Controls; + +namespace RevitRoundingOfAreas.Views.Edits; + +[DefaultProperty(nameof(EditControl))] +[ContentProperty(nameof(EditControl))] +public partial class CustomGroupEditControl { + public static readonly DependencyProperty IconProperty = DependencyProperty.Register( + nameof(Icon), typeof(IconElement), typeof(CustomGroupEditControl), new PropertyMetadata(default(IconElement))); + + public static readonly DependencyProperty ExtraIconProperty = DependencyProperty.Register( + nameof(ExtraIcon), typeof(IconElement), typeof(CustomGroupEditControl), new PropertyMetadata(default(IconElement))); + + public static readonly DependencyProperty ExtraTextProperty = DependencyProperty.Register( + nameof(ExtraText), typeof(string), typeof(CustomGroupEditControl), new PropertyMetadata(default(string))); + + public static readonly DependencyProperty HeaderProperty = DependencyProperty.Register( + nameof(Header), typeof(string), typeof(CustomGroupEditControl), new PropertyMetadata(default(string))); + + public static readonly DependencyProperty DescriptionProperty = DependencyProperty.Register( + nameof(Description), typeof(string), typeof(CustomGroupEditControl), new PropertyMetadata(default(string))); + + public static readonly DependencyProperty EditControlProperty = DependencyProperty.Register( + nameof(EditControl), typeof(object), typeof(CustomGroupEditControl), new PropertyMetadata(default(object))); + + public static readonly DependencyProperty IsCheckedProperty = DependencyProperty.Register( + nameof(IsChecked), typeof(bool), typeof(CustomGroupEditControl), new PropertyMetadata(true)); + + public static readonly DependencyProperty ToggleSwitchVisibleProperty = DependencyProperty.Register( + nameof(ToggleSwitchVisible), typeof(bool), typeof(CustomGroupEditControl), new PropertyMetadata(false)); + + public static readonly DependencyProperty ExtraIconVisibleProperty = DependencyProperty.Register( + nameof(ExtraIconVisible), typeof(bool), typeof(CustomGroupEditControl), new PropertyMetadata(false)); + + public static readonly DependencyProperty ExtraTextVisibleProperty = DependencyProperty.Register( + nameof(ExtraTextVisible), typeof(bool), typeof(CustomGroupEditControl), new PropertyMetadata(false)); + + public static readonly DependencyProperty SwitchOnContentProperty = DependencyProperty.Register( + nameof(SwitchOnContent), typeof(string), typeof(CustomGroupEditControl), new PropertyMetadata(default(string))); + + public static readonly DependencyProperty SwitchOffContentProperty = DependencyProperty.Register( + nameof(SwitchOffContent), typeof(string), typeof(CustomGroupEditControl), new PropertyMetadata(default(string))); + + + public CustomGroupEditControl() { + InitializeComponent(); + } + + public IconElement Icon { + get => (IconElement) GetValue(IconProperty); + set => SetValue(IconProperty, value); + } + + public IconElement ExtraIcon { + get => (IconElement) GetValue(ExtraIconProperty); + set => SetValue(ExtraIconProperty, value); + } + + public string ExtraText { + get => (string) GetValue(ExtraTextProperty); + set => SetValue(ExtraIconProperty, value); + } + + public string Header { + get => (string) GetValue(HeaderProperty); + set => SetValue(HeaderProperty, value); + } + + public string Description { + get => (string) GetValue(DescriptionProperty); + set => SetValue(DescriptionProperty, value); + } + + public object EditControl { + get => GetValue(EditControlProperty); + set => SetValue(EditControlProperty, value); + } + + public bool IsChecked { + get => (bool) GetValue(IsCheckedProperty); + set => SetValue(IsCheckedProperty, value); + } + + public bool ToggleSwitchVisible { + get => (bool) GetValue(ToggleSwitchVisibleProperty); + set => SetValue(ToggleSwitchVisibleProperty, value); + } + + public string SwitchOnContent { + get => (string) GetValue(SwitchOnContentProperty); + set => SetValue(SwitchOnContentProperty, value); + } + + public string SwitchOffContent { + get => (string) GetValue(SwitchOffContentProperty); + set => SetValue(SwitchOffContentProperty, value); + } + + public bool ExtraIconVisible { + get => (bool) GetValue(ExtraIconVisibleProperty); + set => SetValue(ExtraIconVisibleProperty, value); + } + + public bool ExtraTextVisible { + get => (bool) GetValue(ExtraTextVisibleProperty); + set => SetValue(ExtraTextVisibleProperty, value); + } +} diff --git a/src/RevitRoundingOfAreas/Views/Edits/ItemControl.xaml b/src/RevitRoundingOfAreas/Views/Edits/ItemControl.xaml new file mode 100644 index 000000000..2e8b3ac4c --- /dev/null +++ b/src/RevitRoundingOfAreas/Views/Edits/ItemControl.xaml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/RevitRoundingOfAreas/Views/Edits/ItemControl.xaml.cs b/src/RevitRoundingOfAreas/Views/Edits/ItemControl.xaml.cs new file mode 100644 index 000000000..e410d36de --- /dev/null +++ b/src/RevitRoundingOfAreas/Views/Edits/ItemControl.xaml.cs @@ -0,0 +1,38 @@ +using System.Windows; + +namespace RevitRoundingOfAreas.Views.Edits; + +public partial class ItemControl { + public static readonly DependencyProperty ItemNameProperty = DependencyProperty.Register( + nameof(ItemName), typeof(string), typeof(ItemControl), new PropertyMetadata(default(string))); + + public static readonly DependencyProperty WarningDescriptionProperty = DependencyProperty.Register( + nameof(WarningDescription), typeof(string), typeof(ItemControl), new PropertyMetadata(default(string))); + + public static readonly DependencyProperty IsCheckedProperty = DependencyProperty.Register( + nameof(IsChecked), typeof(bool), typeof(ItemControl), new PropertyMetadata(true)); + + public static readonly DependencyProperty HasWarningProperty = DependencyProperty.Register( + nameof(HasWarning), typeof(bool), typeof(ItemControl), new PropertyMetadata(true)); + + public ItemControl() { + InitializeComponent(); + } + + public string ItemName { + get => (string) GetValue(ItemNameProperty); + set => SetValue(ItemNameProperty, value); + } + public string WarningDescription { + get => (string) GetValue(WarningDescriptionProperty); + set => SetValue(WarningDescriptionProperty, value); + } + public bool IsChecked { + get => (bool) GetValue(IsCheckedProperty); + set => SetValue(IsCheckedProperty, value); + } + public bool HasWarning { + get => (bool) GetValue(HasWarningProperty); + set => SetValue(HasWarningProperty, value); + } +} diff --git a/src/RevitRoundingOfAreas/Views/Edits/ModeSwitcherControl.xaml b/src/RevitRoundingOfAreas/Views/Edits/ModeSwitcherControl.xaml new file mode 100644 index 000000000..8677dc138 --- /dev/null +++ b/src/RevitRoundingOfAreas/Views/Edits/ModeSwitcherControl.xaml @@ -0,0 +1,152 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/RevitRoundingOfAreas/Views/Edits/ModeSwitcherControl.xaml.cs b/src/RevitRoundingOfAreas/Views/Edits/ModeSwitcherControl.xaml.cs new file mode 100644 index 000000000..23854aa81 --- /dev/null +++ b/src/RevitRoundingOfAreas/Views/Edits/ModeSwitcherControl.xaml.cs @@ -0,0 +1,67 @@ +using System.Collections; +using System.Windows; +using System.Windows.Controls; + +namespace RevitRoundingOfAreas.Views.Edits; + +public partial class ModeSwitcherControl : UserControl { + /// + /// Источник данных. + /// + public static readonly DependencyProperty ItemsSourceProperty = + DependencyProperty.Register( + nameof(ItemsSource), + typeof(IEnumerable), + typeof(ModeSwitcherControl), + new PropertyMetadata(null)); + + /// + /// Выбранный элемент. + /// + public static readonly DependencyProperty SelectedItemProperty = + DependencyProperty.Register( + nameof(SelectedItem), + typeof(object), + typeof(ModeSwitcherControl), + new FrameworkPropertyMetadata( + null, + FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); + + /// + /// Путь к отображаемому свойству. + /// + public static readonly DependencyProperty DisplayMemberPathProperty = + DependencyProperty.Register( + nameof(DisplayMemberPath), + typeof(string), + typeof(ModeSwitcherControl), + new PropertyMetadata("Name")); + + public ModeSwitcherControl() { + InitializeComponent(); + } + + /// + /// Источник данных. + /// + public IEnumerable ItemsSource { + get => (IEnumerable) GetValue(ItemsSourceProperty); + set => SetValue(ItemsSourceProperty, value); + } + + /// + /// Выбранный элемент. + /// + public object SelectedItem { + get => GetValue(SelectedItemProperty); + set => SetValue(SelectedItemProperty, value); + } + + /// + /// Путь к отображаемому свойству. + /// + public string DisplayMemberPath { + get => (string) GetValue(DisplayMemberPathProperty); + set => SetValue(DisplayMemberPathProperty, value); + } +} diff --git a/src/RevitRoundingOfAreas/Views/MainWindow.xaml b/src/RevitRoundingOfAreas/Views/MainWindow.xaml new file mode 100644 index 000000000..617d2328d --- /dev/null +++ b/src/RevitRoundingOfAreas/Views/MainWindow.xaml @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/RevitRoundingOfAreas/Views/MainWindow.xaml.cs b/src/RevitRoundingOfAreas/Views/MainWindow.xaml.cs new file mode 100644 index 000000000..d0c42a1ab --- /dev/null +++ b/src/RevitRoundingOfAreas/Views/MainWindow.xaml.cs @@ -0,0 +1,49 @@ +using System.Windows; + +using dosymep.SimpleServices; + +namespace RevitRoundingOfAreas.Views; + +/// +/// Класс главного окна плагина. +/// +public partial class MainWindow { + /// + /// Иницализирует главное окно плагина. + /// + public MainWindow( + ILoggerService loggerService, + ISerializationService serializationService, + ILanguageService languageService, ILocalizationService localizationService, + IUIThemeService uiThemeService, IUIThemeUpdaterService themeUpdaterService) + : base(loggerService, + serializationService, + languageService, localizationService, + uiThemeService, themeUpdaterService) { + InitializeComponent(); + } + + /// + /// Наименование плагина. + /// + /// + /// Используется для сохранения положения окна. + /// + public override string PluginName => nameof(RevitRoundingOfAreas); + + /// + /// Наименование файла конфигурации. + /// + /// + /// Используется для сохранения положения окна. + /// + public override string ProjectConfigName => nameof(MainWindow); + + private void ButtonOk_Click(object sender, RoutedEventArgs e) { + DialogResult = true; + } + + private void ButtonCancel_Click(object sender, RoutedEventArgs e) { + DialogResult = false; + } +} diff --git a/src/RevitRoundingOfAreas/Views/Styles.xaml b/src/RevitRoundingOfAreas/Views/Styles.xaml new file mode 100644 index 000000000..69e6613b9 --- /dev/null +++ b/src/RevitRoundingOfAreas/Views/Styles.xaml @@ -0,0 +1,287 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/RevitRoundingOfAreas/Views/WarningsWindow.xaml b/src/RevitRoundingOfAreas/Views/WarningsWindow.xaml new file mode 100644 index 000000000..ed5547611 --- /dev/null +++ b/src/RevitRoundingOfAreas/Views/WarningsWindow.xaml @@ -0,0 +1,164 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/RevitRoundingOfAreas/Views/WarningsWindow.xaml.cs b/src/RevitRoundingOfAreas/Views/WarningsWindow.xaml.cs new file mode 100644 index 000000000..7f784e503 --- /dev/null +++ b/src/RevitRoundingOfAreas/Views/WarningsWindow.xaml.cs @@ -0,0 +1,35 @@ +using dosymep.SimpleServices; + +namespace RevitRoundingOfAreas.Views; +/// +/// Interaction logic for WarningWindow.xaml +/// +public partial class WarningsWindow { + public WarningsWindow( + ILoggerService loggerService, + ISerializationService serializationService, + ILanguageService languageService, ILocalizationService localizationService, + IUIThemeService uiThemeService, IUIThemeUpdaterService themeUpdaterService) + : base(loggerService, + serializationService, + languageService, localizationService, + uiThemeService, themeUpdaterService) { + InitializeComponent(); + } + + /// + /// Наименование плагина. + /// + /// + /// Используется для сохранения положения окна. + /// + public override string PluginName => nameof(RevitRoundingOfAreas); + + /// + /// Наименование файла конфигурации. + /// + /// + /// Используется для сохранения положения окна. + /// + public override string ProjectConfigName => nameof(WarningsWindow); +} diff --git a/src/RevitRoundingOfAreas/assets/Localization/Language.en-US.xaml b/src/RevitRoundingOfAreas/assets/Localization/Language.en-US.xaml new file mode 100644 index 000000000..868b85ee6 --- /dev/null +++ b/src/RevitRoundingOfAreas/assets/Localization/Language.en-US.xaml @@ -0,0 +1,50 @@ + + + Calculate Areas (Social Facilities) + Model Element Processing Range + Select how to process model elements + + Room Phase + Select the phase for the rooms to be processed + + Read Parameter + Parameter from which the area will be read + + Write Parameter + Parameter to which the area will be written + + Number of Decimal Places + Number of decimal places to round to + + Calculate + Cancel + + All elements + Selected + Visible in view + + Area Calculation (Social Facilities) + + Error loading parameters + Plugin configuration error + + Redundant zones + Zones located within the same enclosed area + Unenclosed zones / Unplaced zones + Zones that have no boundaries or are not placed on the plan + + No rooms selected + No rooms on the view + + - No level - + + Calculate Areas (Social Facilities) - Warnings + + Highlight + Room ID + Room name + Room level + + diff --git a/src/RevitRoundingOfAreas/assets/Localization/Language.ru-RU.xaml b/src/RevitRoundingOfAreas/assets/Localization/Language.ru-RU.xaml new file mode 100644 index 000000000..0ee9a769a --- /dev/null +++ b/src/RevitRoundingOfAreas/assets/Localization/Language.ru-RU.xaml @@ -0,0 +1,50 @@ + + + Рассчитать площади (соц. объекты) + Диапазон обработки элементов модели + Выбор варианта обработки элементов модели + + Стадия помещений + Выбор стадии для обрабатываемых помещений + + Параметр для чтения + Параметр, с которого будет считываться площадь + + Параметр для записи + Параметр, в который будет записываться площадь + + Количество знаков после запятой + До которого будет происходить округление + + Рассчитать + Отмена + + Все элементы + Выделенные + Видимые на виде + + Расчёт площадей (соц. объекты) + + Ошибка загрузки параметров + Ошибка настройки плагина + + Избыточные зоны + Зоны, находящиеся в одной окруженной области + Не окруженные зоны / Не размещенные зоны + Зоны, не имеющие границ или не размещенные на плане + + Не выделены помещения + Нет помещений на виде + + - Нет уровня - + + Рассчитать площади (соц. объекты) - предупреждения + + Выделить + ИД помещения + Имя помещения + Уровень помещения + +