Skip to content

Commit

Permalink
Merge pull request #337 from specklesystems/dev
Browse files Browse the repository at this point in the history
Update `release/3.0.0` with changes from `dev`
  • Loading branch information
AlanRynne authored Oct 31, 2024
2 parents 6158739 + 9d25e61 commit de275dc
Show file tree
Hide file tree
Showing 50 changed files with 1,784 additions and 153 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -456,7 +456,7 @@ private async Task RunExpirationChecks(bool idsDeleted)
{
var objIds = sender.SendFilter.NotNull().GetObjectIds();
var intersection = objIds.Intersect(objectIdsList).ToList();
bool isExpired = sender.SendFilter.NotNull().CheckExpiry(objectIdsList);
bool isExpired = intersection.Count != 0;
if (isExpired)
{
expiredSenderIds.Add(sender.ModelCardId.NotNull());
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ namespace Speckle.Connectors.ArcGIS.Filters;

public class ArcGISSelectionFilter : DirectSelectionSendFilter
{
public override List<string> GetObjectIds() => SelectedObjectIds;
public ArcGISSelectionFilter()
{
IsDefault = true;
}

public override bool CheckExpiry(string[] changedObjectIds) => SelectedObjectIds.Intersect(changedObjectIds).Any();
public override List<string> GetObjectIds() => SelectedObjectIds;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ namespace Speckle.Connectors.Autocad.Filters;

public class AutocadSelectionFilter : DirectSelectionSendFilter
{
public override List<string> GetObjectIds() => SelectedObjectIds;
public AutocadSelectionFilter()
{
IsDefault = true;
}

public override bool CheckExpiry(string[] changedObjectIds) => SelectedObjectIds.Intersect(changedObjectIds).Any();
public override List<string> GetObjectIds() => SelectedObjectIds;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
"profiles": {
"ConnectorRevit2025": {
"commandName": "Executable",
"executablePath": "C:\\Program Files\\Autodesk\\Revit 2025\\Revit.exe"
"executablePath": "C:\\Program Files\\Autodesk\\Revit 2025\\Revit.exe",
"runtime": "net8.0-windows"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.DUI.Models.Card;
using Speckle.Connectors.Revit.HostApp;
using Speckle.Connectors.RevitShared;
using Speckle.Connectors.RevitShared.Operations.Send.Filters;
using Speckle.Converters.RevitShared.Helpers;
using Speckle.Sdk;
using Speckle.Sdk.Common;
Expand All @@ -19,12 +21,14 @@ internal sealed class BasicConnectorBindingRevit : IBasicConnectorBinding

public BasicConnectorBindingCommands Commands { get; }

private readonly APIContext _apiContext;
private readonly DocumentModelStore _store;
private readonly RevitContext _revitContext;
private readonly ISpeckleApplication _speckleApplication;
private readonly ILogger<BasicConnectorBindingRevit> _logger;

public BasicConnectorBindingRevit(
APIContext apiContext,
DocumentModelStore store,
IBrowserBridge parent,
RevitContext revitContext,
Expand All @@ -34,6 +38,7 @@ ILogger<BasicConnectorBindingRevit> logger
{
Name = "baseBinding";
Parent = parent;
_apiContext = apiContext;
_store = store;
_revitContext = revitContext;
_speckleApplication = speckleApplication;
Expand Down Expand Up @@ -101,9 +106,27 @@ public async Task HighlightModel(string modelCardId)

if (model is SenderModelCard senderModelCard)
{
elementIds = senderModelCard
.SendFilter.NotNull()
.GetObjectIds()
if (senderModelCard.SendFilter is RevitViewsFilter revitViewsFilter)
{
revitViewsFilter.SetContext(_revitContext, _apiContext);
await _apiContext
.Run(() =>
{
var view = revitViewsFilter.GetView();
if (view is not null)
{
_revitContext.UIApplication.ActiveUIDocument.ActiveView = view;
}
})
.ConfigureAwait(false);
return;
}

var selectedObjects = await _apiContext
.Run(_ => senderModelCard.SendFilter.NotNull().GetObjectIds())
.ConfigureAwait(false);

elementIds = selectedObjects
.Select(uid => ElementIdHelper.GetElementIdFromUniqueId(activeUIDoc.Document, uid))
.Where(el => el is not null)
.Cast<ElementId>()
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
using Speckle.Connectors.Revit.HostApp;
using Speckle.Connectors.Revit.Operations.Send.Settings;
using Speckle.Connectors.Revit.Plugin;
using Speckle.Connectors.RevitShared.Operations.Send.Filters;
using Speckle.Converters.Common;
using Speckle.Converters.RevitShared.Helpers;
using Speckle.Converters.RevitShared.Settings;
Expand All @@ -27,6 +28,7 @@ namespace Speckle.Connectors.Revit.Bindings;
internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
{
private readonly IRevitIdleManager _idleManager;
private readonly APIContext _apiContext;
private readonly CancellationManager _cancellationManager;
private readonly IServiceProvider _serviceProvider;
private readonly ISendConversionCache _sendConversionCache;
Expand All @@ -43,7 +45,7 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
/// As to why a concurrent dictionary, it's because it's the cheapest/easiest way to do so.
/// https://stackoverflow.com/questions/18922985/concurrent-hashsett-in-net-framework
/// </summary>
private ConcurrentDictionary<string, byte> ChangedObjectIds { get; set; } = new();
private ConcurrentDictionary<ElementId, byte> ChangedObjectIds { get; set; } = new();

/// <summary>
/// We need it to get UniqueId whenever it is not available i.e. GetDeletedElementIds returns ElementId and cannot find its Element to get UniqueId. We store them both just before send to remember later.
Expand All @@ -53,6 +55,7 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
public RevitSendBinding(
IRevitIdleManager idleManager,
RevitContext revitContext,
APIContext apiContext,
DocumentModelStore store,
CancellationManager cancellationManager,
IBrowserBridge bridge,
Expand All @@ -68,6 +71,7 @@ ISpeckleApplication speckleApplication
: base("sendBinding", store, bridge, revitContext)
{
_idleManager = idleManager;
_apiContext = apiContext;
_cancellationManager = cancellationManager;
_serviceProvider = serviceProvider;
_sendConversionCache = sendConversionCache;
Expand All @@ -91,10 +95,8 @@ ISpeckleApplication speckleApplication
topLevelExceptionHandler.FireAndForget(async () => await OnDocumentChanged().ConfigureAwait(false));
}

public List<ISendFilter> GetSendFilters()
{
return new List<ISendFilter> { new RevitSelectionFilter() { IsDefault = true } };
}
public List<ISendFilter> GetSendFilters() =>
[new RevitSelectionFilter() { IsDefault = true }, new RevitViewsFilter(RevitContext, _apiContext)];

public List<ICardSetting> GetSendSettings() =>
[
Expand All @@ -107,7 +109,9 @@ public List<ICardSetting> GetSendSettings() =>

public SendBindingUICommands Commands { get; }

#pragma warning disable CA1506
public async Task Send(string modelCardId)
#pragma warning restore CA1506
{
// Note: removed top level handling thing as it was confusing me
try
Expand All @@ -125,19 +129,26 @@ public async Task Send(string modelCardId)
.ServiceProvider.GetRequiredService<IConverterSettingsStore<RevitConversionSettings>>()
.Initialize(
_revitConversionSettingsFactory.Create(
_toSpeckleSettingsManager.GetDetailLevelSetting(modelCard),
_toSpeckleSettingsManager.GetReferencePointSetting(modelCard),
_toSpeckleSettingsManager.GetSendParameterNullOrEmptyStringsSetting(modelCard)
await _toSpeckleSettingsManager.GetDetailLevelSetting(modelCard).ConfigureAwait(false),
await _toSpeckleSettingsManager.GetReferencePointSetting(modelCard).ConfigureAwait(false),
await _toSpeckleSettingsManager.GetSendParameterNullOrEmptyStringsSetting(modelCard).ConfigureAwait(false)
)
);

var activeUIDoc =
RevitContext.UIApplication?.ActiveUIDocument
?? throw new SpeckleException("Unable to retrieve active UI document");

List<Element> elements = modelCard
.SendFilter.NotNull()
.GetObjectIds()
if (modelCard.SendFilter is RevitViewsFilter viewFilter)
{
viewFilter.SetContext(RevitContext, _apiContext);
}

var selectedObjects = await _apiContext
.Run(_ => modelCard.SendFilter.NotNull().GetObjectIds())
.ConfigureAwait(false);

List<Element> elements = selectedObjects
.Select(uid => activeUIDoc.Document.GetElement(uid))
.Where(el => el is not null)
.ToList();
Expand Down Expand Up @@ -187,34 +198,47 @@ await Commands
/// a filter refresh (e.g., views being added).
/// </summary>
/// <param name="e"></param>
private void DocChangeHandler(Autodesk.Revit.DB.Events.DocumentChangedEventArgs e)
private async Task DocChangeHandler(Autodesk.Revit.DB.Events.DocumentChangedEventArgs e)
{
ICollection<ElementId> addedElementIds = e.GetAddedElementIds();
ICollection<ElementId> deletedElementIds = e.GetDeletedElementIds();
ICollection<ElementId> modifiedElementIds = e.GetModifiedElementIds();

foreach (ElementId elementId in addedElementIds)
{
ChangedObjectIds[elementId.ToString()] = 1;
ChangedObjectIds[elementId] = 1;
}

foreach (ElementId elementId in deletedElementIds)
{
ChangedObjectIds[elementId.ToString()] = 1;
ChangedObjectIds[elementId] = 1;
}

foreach (ElementId elementId in modifiedElementIds)
{
ChangedObjectIds[elementId.ToString()] = 1;
ChangedObjectIds[elementId] = 1;
}

if (HaveUnitsChanged(e.GetDocument()))
{
var objectIds = Store.GetSenders().SelectMany(s => s.SendFilter != null ? s.SendFilter.GetObjectIds() : []);
var objectIds = new List<string>();
foreach (var sender in Store.GetSenders())
{
if (sender.SendFilter is null)
{
continue;
}
var selectedObjects = await _apiContext
.Run(_ => sender.SendFilter.NotNull().GetObjectIds())
.ConfigureAwait(false);
objectIds.AddRange(selectedObjects);
}
var unpackedObjectIds = _elementUnpacker.GetUnpackedElementIds(objectIds.ToList());
_sendConversionCache.EvictObjects(unpackedObjectIds);
}
_idleManager.SubscribeToIdle(nameof(RevitSendBinding), RunExpirationChecks);

_idleManager.SubscribeToIdle(nameof(CheckFilterExpiration), CheckFilterExpiration);
_idleManager.SubscribeToIdle(nameof(RunExpirationChecks), RunExpirationChecks);
}

// Keeps track of doc and current units
Expand Down Expand Up @@ -251,6 +275,26 @@ private bool HaveUnitsChanged(Document doc)
return false;
}

/// <summary>
/// Notifies ui if any filters need refreshing. Currently, this only applies for view filters.
/// </summary>
private async Task CheckFilterExpiration()
{
// NOTE: below code seems like more make sense in terms of performance but it causes unmanaged exception on Revit
// using var viewCollector = new FilteredElementCollector(RevitContext.UIApplication?.ActiveUIDocument.Document);
// var views = viewCollector.OfClass(typeof(View)).Cast<View>().Select(v => v.Id).ToList();
// var intersection = ChangedObjectIds.Keys.Intersect(views).ToList();
// if (intersection.Count != 0)
// {
// await Commands.RefreshSendFilters().ConfigureAwait(false);
// }

if (ChangedObjectIds.Keys.Any(e => RevitContext.UIApplication?.ActiveUIDocument.Document.GetElement(e) is View))
{
await Commands.RefreshSendFilters().ConfigureAwait(false);
}
}

private async Task RunExpirationChecks()
{
var senders = Store.GetSenders();
Expand All @@ -263,12 +307,18 @@ private async Task RunExpirationChecks()
}

var objUniqueIds = new List<string>();
foreach (string changedElementId in ChangedObjectIds.Keys.ToArray())
foreach (var changedElementId in ChangedObjectIds.Keys.ToArray())
{
if (IdMap.TryGetValue(changedElementId, out var uniqueId))
if (IdMap.TryGetValue(changedElementId.ToString(), out var uniqueId))
{
objUniqueIds.Add(uniqueId);
}
else
{
var uniqId = doc.GetElement(changedElementId).UniqueId;
objUniqueIds.Add(uniqId);
IdMap[changedElementId.ToString()] = uniqId;
}
}

var unpackedObjectIds = _elementUnpacker.GetUnpackedElementIds(objUniqueIds);
Expand All @@ -278,7 +328,14 @@ private async Task RunExpirationChecks()
List<string> expiredSenderIds = new();
foreach (SenderModelCard modelCard in senders)
{
var intersection = modelCard.SendFilter.NotNull().GetObjectIds().Intersect(objUniqueIds).ToList();
if (modelCard.SendFilter is RevitViewsFilter viewFilter)
{
viewFilter.SetContext(RevitContext, _apiContext);
}
var selectedObjects = await _apiContext
.Run(_ => modelCard.SendFilter.NotNull().GetObjectIds())
.ConfigureAwait(false);
var intersection = selectedObjects.Intersect(objUniqueIds).ToList();
bool isExpired = intersection.Count != 0;
if (isExpired)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ public static void AddRevit(this IServiceCollection serviceCollection)

// operation progress manager
serviceCollection.AddSingleton<IOperationProgressManager, OperationProgressManager>();

// API context helps us to run functions on Revit UI Thread (main)
serviceCollection.AddSingleton<APIContext>();
}

public static void RegisterUiDependencies(IServiceCollection serviceCollection)
Expand Down
Loading

0 comments on commit de275dc

Please sign in to comment.