Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/RevitParamsChecker/Models/Filtration/Filter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,15 @@ public Filter() {

public string Name { get; set; }

/// <summary>
/// Сериализованный контекст фильтра по параметрам элементов.
/// Пустое значение означает, что фильтрация не задана.
/// </summary>
public string FilterContext { get; set; }

/// <summary>
/// Сериализованный контекст фильтра по параметрам материалов элементов.
/// Пустое значение означает, что фильтрация по материалам не задана.
/// </summary>
public string MaterialsFilterContext { get; set; }
}
73 changes: 73 additions & 0 deletions src/RevitParamsChecker/Models/Filtration/FilterDataProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ public DataProvider CreateDataProvider() {
return new DataProvider(GetCategories(), GetParams, GetParamValues);
}

/// <summary>
/// Создает провайдер данных для фильтра по параметрам материалов элементов.
/// </summary>
/// <returns>Провайдер данных с единственной категорией - "Материалы".</returns>
public DataProvider CreateMaterialsDataProvider() {
return new DataProvider(GetMaterialsCategories(), GetMaterialParams, GetMaterialParamValues);
}

private ICollection<RevitParam> GetParams(ICollection<Category> categories) {
return ParameterFilterUtilities
.GetFilterableParametersInCommon(_revitRepository.Document, [..categories.Select(c => c.Id)])
Expand All @@ -43,6 +51,52 @@ private ICollection<Category> GetCategories() {
.ToArray();
}

private ICollection<Category> GetMaterialsCategories() {
var category = Category.GetCategory(_revitRepository.Document, BuiltInCategory.OST_Materials);
return category is null ? [] : [category];
}

/// <summary>
/// Возвращает параметры, доступные для фильтрации материалов.
/// </summary>
/// <remarks>
/// Материалы не входят в фильтруемые категории видов Revit, поэтому,
/// если получить параметры штатным способом не удалось,
/// они берутся из первого попавшегося материала документа.
/// </remarks>
private ICollection<RevitParam> GetMaterialParams(ICollection<Category> categories) {
ICollection<ElementId> paramIds;
try {
paramIds = ParameterFilterUtilities.GetFilterableParametersInCommon(
_revitRepository.Document,
[..categories.Select(c => c.Id)]);
} catch(Autodesk.Revit.Exceptions.ApplicationException) {
paramIds = [];
}

var revitParams = paramIds
.Select(GetFilterableParam)
.Where(p => p != null)
.ToArray();
return revitParams.Length > 0 ? revitParams : GetFirstMaterialParams();
}

private ICollection<RevitParam> GetFirstMaterialParams() {
var material = new FilteredElementCollector(_revitRepository.Document)
.OfClass(typeof(Material))
.FirstElement();
if(material is null) {
return [];
}

return material.Parameters
.OfType<Parameter>()
.Select(p => p.Id)
.Select(GetFilterableParam)
.Where(p => p != null)
.ToArray();
}

private RevitParam GetFilterableParam(ElementId paramId) {
try {
if(paramId.IsSystemId()) {
Expand Down Expand Up @@ -99,4 +153,23 @@ private ICollection<string> GetParamValues(
return [];
}
}

private ICollection<string> GetMaterialParamValues(ICollection<Category> categories, RevitParam param) {
return _revitRepository.GetDocuments()
.SelectMany(d => GetMaterialParamValues(d.Document, param))
.Distinct()
.ToArray();
}

private ICollection<string> GetMaterialParamValues(Document doc, RevitParam param) {
try {
return new FilteredElementCollector(doc)
.OfClass(typeof(Material))
.Where(e => e.IsExistsParamValue(param.Name))
.Select(e => e.GetParamValueString(param))
.ToArray();
} catch(Autodesk.Revit.Exceptions.ApplicationException) {
return [];
}
}
}
30 changes: 29 additions & 1 deletion src/RevitParamsChecker/Models/Revit/RevitRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,27 @@
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;

using Bim4Everyone.RevitFiltration;
using Bim4Everyone.RevitFiltration.Controls;
using Bim4Everyone.RevitFiltration.Extensions.Materials;

using RevitParamsChecker.Models.Filtration;

namespace RevitParamsChecker.Models.Revit;

internal class RevitRepository {
private readonly IFilterContextParser _filterParser;
private readonly ILogicalFilterFactory _filterFactory;
private DocumentModel[] _documentsCache;
private readonly Bim4Everyone.RevitFiltration.Options _filterBuildOptions = new() { Tolerance = 0.001 };
private readonly MaterialOptions _materialsBuildOptions = new() { Tolerance = 0.001 };

public RevitRepository(UIApplication uiApplication, IFilterContextParser filterParser) {
public RevitRepository(
UIApplication uiApplication,
IFilterContextParser filterParser,
ILogicalFilterFactory filterFactory) {
_filterParser = filterParser ?? throw new ArgumentNullException(nameof(filterParser));
_filterFactory = filterFactory ?? throw new ArgumentNullException(nameof(filterFactory));
UIApplication = uiApplication ?? throw new ArgumentNullException(nameof(uiApplication));
}

Expand Down Expand Up @@ -57,10 +65,12 @@ public ICollection<ElementModel> GetElements(DocumentModel doc, Filter filter) {

var paramsFilter = context!.GetFilter().Build(doc.Document, _filterBuildOptions);
var categoriesFilter = new ElementMulticategoryFilter(context.SelectedCategories);
var materialsFilter = GetMaterialsFilter(doc.Document, filter.MaterialsFilterContext);
return new FilteredElementCollector(doc.Document)
.WhereElementIsNotElementType()
.WherePasses(categoriesFilter)
.WherePasses(paramsFilter)
.PassesFilter(materialsFilter)
.Select(e => new ElementModel(
e,
doc.IsLink ? new Reference(e).CreateLinkReference(doc.Link) : new Reference(e)))
Expand Down Expand Up @@ -104,6 +114,24 @@ public void SelectElements(ICollection<ElementModel> elements) {
#endif
}

/// <summary>
/// Создает фильтр элементов по параметрам их материалов.
/// </summary>
/// <param name="doc">Документ, в котором отбираются материалы.</param>
/// <param name="serializedContext">Сериализованный контекст фильтра по материалам.</param>
/// <returns>
/// Фильтр по материалам. Если контекст не задан либо его не удалось прочитать,
/// возвращается фильтр без правил, пропускающий все элементы.
/// </returns>
private MaterialsFilter GetMaterialsFilter(Document doc, string serializedContext) {
if(!string.IsNullOrWhiteSpace(serializedContext)
&& _filterParser.TryParse(serializedContext, out var context)) {
return context!.GetFilter().BuildMaterialsFilter(doc, _materialsBuildOptions);
}

return _filterFactory.CreateAndFilter().BuildMaterialsFilter(doc, _materialsBuildOptions);
}

private ICollection<RevitLinkInstance> GetRevitLinkInstances() {
return new FilteredElementCollector(Document)
.OfClass(typeof(RevitLinkInstance))
Expand Down
5 changes: 2 additions & 3 deletions src/RevitParamsChecker/RevitParamsCheckerCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
using Autodesk.Revit.Attributes;
using Autodesk.Revit.UI;

using Bim4Everyone.RevitFiltration.Controls;
using Bim4Everyone.RevitFiltration.Ninject;

using dosymep.Bim4Everyone;
Expand Down Expand Up @@ -62,8 +61,8 @@ protected override void Execute(UIApplication uiApplication) {
kernel.UseLogicalFilterFactory();
kernel.UseLogicalFilterProviderFactory();
kernel.UseFilterContextParser();
kernel.Bind<DataProvider>()
.ToMethod(c => new FilterDataProvider(c.Kernel.Get<RevitRepository>()).CreateDataProvider())
kernel.Bind<FilterDataProvider>()
.ToSelf()
.InSingletonScope();
kernel.Bind<INavigationViewPageProvider>()
.To<NavigationViewPageProvider>()
Expand Down
66 changes: 65 additions & 1 deletion src/RevitParamsChecker/ViewModels/Filtration/FilterViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,29 @@ namespace RevitParamsChecker.ViewModels.Filtration;

internal class FilterViewModel : BaseViewModel, IEquatable<FilterViewModel>, IName {
private readonly Guid _guid;
private readonly IFilterContextParser _filterContextParser;
private string _savedFilterContext;
private string _savedMaterialsFilterContext;
private string _name;
private bool _modified;

public FilterViewModel(string name, ILogicalFilterProvider filterProvider) {
public FilterViewModel(
string name,
ILogicalFilterProvider filterProvider,
ILogicalFilterProvider materialsFilterProvider,
IFilterContextParser filterContextParser) {
if(string.IsNullOrWhiteSpace(name)) {
throw new ArgumentException(nameof(name));
}

FilterProvider = filterProvider ?? throw new ArgumentNullException(nameof(filterProvider));
MaterialsFilterProvider = materialsFilterProvider
?? throw new ArgumentNullException(nameof(materialsFilterProvider));
_filterContextParser = filterContextParser ?? throw new ArgumentNullException(nameof(filterContextParser));
_savedFilterContext = GetContext(FilterProvider);
_savedMaterialsFilterContext = GetContext(MaterialsFilterProvider);
FilterProvider.FilterContextChanged += OnFilterContextChanged;
MaterialsFilterProvider.FilterContextChanged += OnFilterContextChanged;
Name = name;
Modified = true;
_guid = Guid.NewGuid();
Expand All @@ -36,8 +50,25 @@ public bool Modified {
set => RaiseAndSetIfChanged(ref _modified, value);
}

/// <summary>
/// Провайдер фильтра по параметрам элементов.
/// </summary>
public ILogicalFilterProvider FilterProvider { get; }

/// <summary>
/// Провайдер фильтра по параметрам материалов элементов.
/// </summary>
public ILogicalFilterProvider MaterialsFilterProvider { get; }

/// <summary>
/// Запоминает текущее состояние фильтров как сохраненное и снимает пометку об изменениях.
/// </summary>
public void AcceptChanges() {
_savedFilterContext = GetContext(FilterProvider);
_savedMaterialsFilterContext = GetContext(MaterialsFilterProvider);
Modified = false;
}

public override bool Equals(object obj) {
return Equals(obj as FilterViewModel);
}
Expand All @@ -58,6 +89,39 @@ public bool Equals(FilterViewModel other) {
return _guid == other._guid;
}

/// <summary>
/// Помечает набор измененным, если фильтр действительно отличается от сохраненного.
/// </summary>
/// <remarks>
/// Контрол пересобирает контекст фильтра при загрузке провайдера, то есть при каждом выборе набора в списке,
/// поэтому одного факта события недостаточно: состояния сравниваются в том же виде, в котором сохраняются.
/// </remarks>
private void OnFilterContextChanged(object sender, FilterContextChangedEventArgs e) {
if(IsFilterChanged()) {
Modified = true;
}
}

private bool IsFilterChanged() {
return !string.Equals(
_savedFilterContext,
GetContext(FilterProvider),
StringComparison.CurrentCultureIgnoreCase)
|| !string.Equals(
_savedMaterialsFilterContext,
GetContext(MaterialsFilterProvider),
StringComparison.CurrentCultureIgnoreCase);
}

/// <summary>
/// Возвращает сериализованный контекст фильтра либо null, если фильтр задан некорректно.
/// </summary>
private string GetContext(ILogicalFilterProvider provider) {
return provider.CanGetFilter(out _)
? _filterContextParser.Serialize(provider.GetFilter())
: null;
}

private void OnModelPropertyChanged(object sender, PropertyChangedEventArgs e) {
if(e.PropertyName == nameof(Name)) {
Modified = true;
Expand Down
Loading
Loading