Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 604dc00ef0 | |||
| 1c24a81d58 | |||
| b751dd0756 | |||
| 39ba1f8066 | |||
| 31a40931c7 | |||
| c68ac2a63d | |||
| dbd35ac31a | |||
| 0deea75204 | |||
| 44bec42198 | |||
| 6fd1f05680 | |||
| 3861da4347 | |||
| 90c38a28a5 | |||
| e4de8c47b5 | |||
| 78d7814351 | |||
| 17a320ee53 | |||
| 396ef981ee | |||
| 763c413871 | |||
| 57a5b41ec1 | |||
| 8385532b96 | |||
| e109515852 | |||
| fdf2425ec6 | |||
| c067cf6f91 | |||
| 60e26d85c6 |
@@ -10,7 +10,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
file_version: ${{ steps.set-version.outputs.file_version }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
|
||||
+1
-6
@@ -17,12 +17,7 @@ public class NavisworksColorUnpacker(
|
||||
)
|
||||
{
|
||||
private static T SelectByRepresentationMode<T>(
|
||||
RepresentationMode mode,
|
||||
T active,
|
||||
T permanent,
|
||||
T original,
|
||||
T defaultValue
|
||||
) =>
|
||||
RepresentationMode mode, T active, T permanent, T original, T defaultValue) =>
|
||||
mode switch
|
||||
{
|
||||
RepresentationMode.Active => active,
|
||||
|
||||
+11
-16
@@ -20,12 +20,7 @@ public class NavisworksMaterialUnpacker(
|
||||
)
|
||||
{
|
||||
private static T SelectByRepresentationMode<T>(
|
||||
RepresentationMode mode,
|
||||
T active,
|
||||
T permanent,
|
||||
T original,
|
||||
T defaultValue
|
||||
) =>
|
||||
RepresentationMode mode, T active, T permanent, T original, T defaultValue) =>
|
||||
mode switch
|
||||
{
|
||||
RepresentationMode.Active => active,
|
||||
@@ -125,12 +120,9 @@ public class NavisworksMaterialUnpacker(
|
||||
}
|
||||
}
|
||||
catch (Exception ex) when (!ex.IsFatal())
|
||||
{ // If COM interop fails during hash generation, fall back to using finalId
|
||||
logger.LogWarning(
|
||||
ex,
|
||||
"Failed to generate fragment hash ID for item {ItemId}, using finalId as fallback",
|
||||
finalId
|
||||
);
|
||||
{
|
||||
// If COM interop fails during hash generation, fall back to using finalId
|
||||
logger.LogWarning(ex, "Failed to generate fragment hash ID for item {ItemId}, using finalId as fallback", finalId);
|
||||
hashId = "";
|
||||
}
|
||||
|
||||
@@ -194,7 +186,12 @@ public class NavisworksMaterialUnpacker(
|
||||
{
|
||||
renderMaterialProxies[renderMaterialId.ToString()] = new RenderMaterialProxy()
|
||||
{
|
||||
value = CreateRenderMaterial(materialName, renderTransparency, renderColor, renderMaterialId),
|
||||
value = CreateRenderMaterial(
|
||||
materialName,
|
||||
renderTransparency,
|
||||
renderColor,
|
||||
renderMaterialId
|
||||
),
|
||||
objects = [!string.IsNullOrEmpty(hashId) ? hashId : finalId]
|
||||
};
|
||||
}
|
||||
@@ -219,9 +216,7 @@ public class NavisworksMaterialUnpacker(
|
||||
|
||||
var speckleRenderMaterial = new RenderMaterial()
|
||||
{
|
||||
name = !string.IsNullOrEmpty(name)
|
||||
? name
|
||||
: $"{MaterialConstants.DEFAULT_MATERIAL_NAME_PREFIX}{Math.Abs(color.ToArgb())}",
|
||||
name = !string.IsNullOrEmpty(name) ? name : $"{MaterialConstants.DEFAULT_MATERIAL_NAME_PREFIX}{Math.Abs(color.ToArgb())}",
|
||||
opacity = 1 - transparency,
|
||||
metalness = 0,
|
||||
roughness = 1,
|
||||
|
||||
+2
-5
@@ -1,4 +1,4 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Speckle.Connector.Navisworks.HostApp;
|
||||
using Speckle.Connector.Navisworks.Services;
|
||||
using Speckle.Connectors.Common.Builders;
|
||||
@@ -27,7 +27,6 @@ public class NavisworksRootObjectBuilder(
|
||||
NavisworksMaterialUnpacker materialUnpacker,
|
||||
NavisworksColorUnpacker colorUnpacker,
|
||||
IElementSelectionService elementSelectionService,
|
||||
IUiUnitsCache uiUnitsCache,
|
||||
InstanceStoreManager instanceStoreManager
|
||||
) : IRootObjectBuilder<NAV.ModelItem>
|
||||
{
|
||||
@@ -256,14 +255,12 @@ public class NavisworksRootObjectBuilder(
|
||||
|
||||
(string name, string path) = GetElementNameAndPath(convertedBase.applicationId);
|
||||
|
||||
var units = uiUnitsCache.Ensure();
|
||||
|
||||
return new NavisworksObject
|
||||
{
|
||||
name = name,
|
||||
displayValue = convertedBase["displayValue"] as List<Base> ?? [],
|
||||
properties = convertedBase["properties"] as Dictionary<string, object?> ?? [],
|
||||
units = units.ToString(),
|
||||
units = converterSettings.Current.Derived.SpeckleUnits,
|
||||
applicationId = convertedBase.applicationId,
|
||||
["path"] = path
|
||||
};
|
||||
|
||||
-1
@@ -58,7 +58,6 @@ public static class ServiceRegistration
|
||||
serviceCollection.AddScoped<SendOperation<DocumentToConvert>>();
|
||||
serviceCollection.AddScoped<ElementUnpacker>();
|
||||
serviceCollection.AddScoped<LevelUnpacker>();
|
||||
serviceCollection.AddScoped<ViewUnpacker>();
|
||||
serviceCollection.AddScoped<SendCollectionManager>();
|
||||
serviceCollection.AddScoped<IRootObjectBuilder<DocumentToConvert>, RevitRootObjectBuilder>();
|
||||
serviceCollection.AddSingleton<ISendConversionCache, SendConversionCache>();
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
using Autodesk.Revit.DB;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Speckle.Objects.Other;
|
||||
using Speckle.Sdk;
|
||||
|
||||
namespace Speckle.Connectors.Revit.HostApp;
|
||||
|
||||
/// <summary>
|
||||
/// Unpacks Revit Views for sending
|
||||
/// </summary>
|
||||
|
||||
public class ViewUnpacker
|
||||
{
|
||||
private readonly ILogger<ViewUnpacker> _logger;
|
||||
private readonly Converters.Common.IRootToSpeckleConverter _rootToSpeckleConverter;
|
||||
|
||||
public ViewUnpacker(Converters.Common.IRootToSpeckleConverter rootToSpeckleConverter, ILogger<ViewUnpacker> logger)
|
||||
{
|
||||
_rootToSpeckleConverter = rootToSpeckleConverter;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
private Camera? ConvertViewToCamera(View3D view)
|
||||
{
|
||||
try
|
||||
{
|
||||
var converted = (Camera)_rootToSpeckleConverter.Convert(view);
|
||||
if (converted is null)
|
||||
{
|
||||
_logger.LogError("Failed to create a view from {view}", view.Name);
|
||||
return null;
|
||||
}
|
||||
|
||||
return converted;
|
||||
}
|
||||
catch (Exception ex) when (!ex.IsFatal())
|
||||
{
|
||||
_logger.LogError(ex, "Failed to create a view from {view}", view.Name);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Iterates through the 3D views in the provided document to create cameras
|
||||
/// </summary>
|
||||
/// <param name="doc">Document to retrieve 3D views from</param>
|
||||
/// <returns></returns>
|
||||
public List<Camera> Unpack(Document doc)
|
||||
{
|
||||
List<Camera> cameras = new();
|
||||
using FilteredElementCollector collector = new(doc);
|
||||
List<View> views = collector
|
||||
.WhereElementIsNotElementType()
|
||||
.OfCategory(BuiltInCategory.OST_Views)
|
||||
.Cast<View>()
|
||||
.Where(x => x.ViewType == ViewType.ThreeD)
|
||||
.ToList();
|
||||
|
||||
foreach (View view in views)
|
||||
{
|
||||
if (view is not View3D view3D)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// not supporting parallel project yet, since it is too complex to match in the viewer for now
|
||||
try
|
||||
{
|
||||
if (!view3D.IsPerspective)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
catch (Autodesk.Revit.Exceptions.InvalidOperationException)
|
||||
{
|
||||
continue; // some threed views will throw an exception: returns true if view is not a view template
|
||||
}
|
||||
|
||||
if (ConvertViewToCamera(view3D) is Camera camera)
|
||||
{
|
||||
cameras.Add(camera);
|
||||
}
|
||||
}
|
||||
|
||||
return cameras;
|
||||
}
|
||||
}
|
||||
-9
@@ -24,7 +24,6 @@ public class RevitRootObjectBuilder(
|
||||
ISendConversionCache sendConversionCache,
|
||||
ElementUnpacker elementUnpacker,
|
||||
LevelUnpacker levelUnpacker,
|
||||
ViewUnpacker viewUnpacker,
|
||||
IThreadContext threadContext,
|
||||
SendCollectionManager sendCollectionManager,
|
||||
ILogger<RevitRootObjectBuilder> logger,
|
||||
@@ -241,7 +240,6 @@ public class RevitRootObjectBuilder(
|
||||
throw new SpeckleException("Failed to convert all objects.");
|
||||
}
|
||||
|
||||
// STEP 5: Unpack proxies to attach to root collection
|
||||
var flatElements = atomicObjectsByDocumentAndTransform.SelectMany(t => t.Elements).ToList();
|
||||
var idsAndSubElementIds = elementUnpacker.GetElementsAndSubelementIdsFromAtomicObjects(flatElements);
|
||||
|
||||
@@ -262,13 +260,6 @@ public class RevitRootObjectBuilder(
|
||||
}
|
||||
);
|
||||
|
||||
// STEP 6: Unpack all other objects to attach to root collection
|
||||
List<Objects.Other.Camera> views = viewUnpacker.Unpack(converterSettings.Current.Document);
|
||||
if (views.Count > 0)
|
||||
{
|
||||
rootObject[RootKeys.VIEW] = views;
|
||||
}
|
||||
|
||||
// NOTE: these are currently not used anywhere, we'll skip them until someone calls for it back
|
||||
// rootObject[ProxyKeys.PARAMETER_DEFINITIONS] = _parameterDefinitionHandler.Definitions;
|
||||
|
||||
|
||||
+1
-2
@@ -24,7 +24,6 @@
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\LevelUnpacker.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\LinkedModelHandler.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\RevitMaterialBaker.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\ViewUnpacker.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\SupportedCategoriesUtils.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\RevitViewManager.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Operations\Receive\HideWarningsFailuresPreprocessor.cs" />
|
||||
@@ -61,4 +60,4 @@
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Plugin\RevitCefPlugin.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Plugin\SpeckleRevitTaskException.cs" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
+2
-2
@@ -29,8 +29,8 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="GrasshopperAsyncComponent" />
|
||||
<PackageReference Include="RhinoCommon" IncludeAssets="compile; build" PrivateAssets="all" VersionOverride="8.9.24194.18121"/>
|
||||
<PackageReference Include="Grasshopper" IncludeAssets="compile; build" PrivateAssets="all" VersionOverride="8.9.24194.18121"/>
|
||||
<PackageReference Include="RhinoCommon" IncludeAssets="compile; build" PrivateAssets="all" />
|
||||
<PackageReference Include="Grasshopper" IncludeAssets="compile; build" PrivateAssets="all" />
|
||||
<PackageReference Include="System.Resources.Extensions" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ public class TokenUrlComponent : GH_Component
|
||||
{
|
||||
public TokenUrlComponent()
|
||||
: base(
|
||||
"Speckle Model URL with Token",
|
||||
"Speckle Model URL",
|
||||
"URL",
|
||||
"Create a Speckle model link using URL and developer token",
|
||||
ComponentCategories.PRIMARY_RIBBON,
|
||||
|
||||
+3
-1
@@ -27,7 +27,8 @@ public class ExpandSpeckleProperties : GH_Component, IGH_VariableParameterCompon
|
||||
protected override Bitmap Icon => Resources.speckle_properties_expand;
|
||||
public override GH_Exposure Exposure => GH_Exposure.secondary;
|
||||
|
||||
protected override void RegisterInputParams(GH_InputParamManager pManager) =>
|
||||
protected override void RegisterInputParams(GH_InputParamManager pManager)
|
||||
{
|
||||
pManager.AddParameter(
|
||||
new SpecklePropertyGroupParam(),
|
||||
"Properties",
|
||||
@@ -35,6 +36,7 @@ public class ExpandSpeckleProperties : GH_Component, IGH_VariableParameterCompon
|
||||
"Speckle Properties to expand",
|
||||
GH_ParamAccess.item
|
||||
);
|
||||
}
|
||||
|
||||
protected override void RegisterOutputParams(GH_OutputParamManager pManager) { }
|
||||
|
||||
|
||||
+64
-80
@@ -130,7 +130,7 @@ public class ReceiveAsyncComponent : GH_AsyncComponent<ReceiveAsyncComponent>
|
||||
{
|
||||
var autoReceiveMi = Menu_AppendItem(
|
||||
menu,
|
||||
"Load new versions automatically",
|
||||
"Load automatically",
|
||||
(s, e) =>
|
||||
{
|
||||
AutoReceive = !AutoReceive;
|
||||
@@ -447,89 +447,73 @@ public sealed class ReceiveComponentWorker : WorkerInstance<ReceiveAsyncComponen
|
||||
}
|
||||
|
||||
using var scope = PriorityLoader.CreateScopeForActiveDocument();
|
||||
try
|
||||
Root = await scope
|
||||
.Get<GrasshopperReceiveOperation>()
|
||||
.ReceiveCommitObject(receiveInfo, progress, CancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
CancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
SpecklePropertyGroupGoo? rootPropertiesGoo = null;
|
||||
if (Root is RootCollection rootCollection && rootCollection.properties.Count > 0)
|
||||
{
|
||||
Root = await scope
|
||||
.Get<GrasshopperReceiveOperation>()
|
||||
.ReceiveCommitObject(receiveInfo, progress, CancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
CancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
SpecklePropertyGroupGoo? rootPropertiesGoo = null;
|
||||
if (Root is RootCollection rootCollection && rootCollection.properties.Count > 0)
|
||||
{
|
||||
rootPropertiesGoo = new SpecklePropertyGroupGoo(rootCollection.properties);
|
||||
}
|
||||
|
||||
// Step 2 - CONVERT
|
||||
//receiveComponent.Message = $"Unpacking...";
|
||||
SpeckleConversionContext.SetupCurrent(scope);
|
||||
|
||||
var unpackedRoot = scope.Get<RootObjectUnpacker>().Unpack(Root);
|
||||
|
||||
// separate atomic objects from block instances
|
||||
var (atomicObjects, blockInstances) = scope
|
||||
.Get<RootObjectUnpacker>()
|
||||
.SplitAtomicObjectsAndInstances(unpackedRoot.ObjectsToConvert);
|
||||
|
||||
// initialize unpackers and collection builder (data holders - created with new)
|
||||
var colorUnpacker = new GrasshopperColorUnpacker(unpackedRoot);
|
||||
var materialUnpacker = new GrasshopperMaterialUnpacker(unpackedRoot);
|
||||
var collectionRebuilder = new GrasshopperCollectionRebuilder(
|
||||
(Root as Collection) ?? new Collection { name = "unnamed" }
|
||||
);
|
||||
|
||||
// get handler from DI and initialize with per-operation data
|
||||
var mapHandler = scope
|
||||
.Get<LocalToGlobalMapHandler>()
|
||||
.Initialize(
|
||||
scope.Get<TraversalContextUnpacker>(),
|
||||
colorUnpacker,
|
||||
materialUnpacker,
|
||||
collectionRebuilder,
|
||||
unpackedRoot.DefinitionProxies
|
||||
);
|
||||
|
||||
// handler deals with two-pass conversion: normal objects first, then DataObjects with InstanceProxies
|
||||
mapHandler.ConvertAtomicObjects(atomicObjects);
|
||||
|
||||
// process block instances using converted atomic objects
|
||||
// internally filters out InstanceProxies that belong to registered DataObjects
|
||||
// block processing needs converted objects, but object filtering needs block definitions.
|
||||
mapHandler.ConvertBlockInstances(blockInstances);
|
||||
|
||||
Result = new SpeckleCollectionWrapperGoo(collectionRebuilder.RootCollectionWrapper);
|
||||
RootProperties = rootPropertiesGoo;
|
||||
|
||||
// TODO: If we have NodeRun events later, better to have `ComponentTracker` to use across components
|
||||
var customProperties = new Dictionary<string, object>()
|
||||
{
|
||||
{ "isAsync", true },
|
||||
{ "sourceHostApp", HostApplications.GetSlugFromHostAppNameAndVersion(receiveInfo.SourceApplication) },
|
||||
{ "auto", Parent.AutoReceive }
|
||||
};
|
||||
if (receiveInfo.WorkspaceId != null)
|
||||
{
|
||||
customProperties.Add("workspace_id", receiveInfo.WorkspaceId);
|
||||
}
|
||||
|
||||
if (receiveInfo.SelectedVersionUserId != null)
|
||||
{
|
||||
customProperties.Add(
|
||||
"isMultiplayer",
|
||||
receiveInfo.SelectedVersionUserId != Parent.ApiClient.Account.userInfo.id
|
||||
);
|
||||
}
|
||||
|
||||
await scope
|
||||
.Get<IMixPanelManager>()
|
||||
.TrackEvent(MixPanelEvents.Receive, Parent.ApiClient.Account, customProperties);
|
||||
rootPropertiesGoo = new SpecklePropertyGroupGoo(rootCollection.properties);
|
||||
}
|
||||
finally
|
||||
|
||||
// Step 2 - CONVERT
|
||||
//receiveComponent.Message = $"Unpacking...";
|
||||
TraversalContextUnpacker traversalContextUnpacker = new();
|
||||
var unpackedRoot = scope.Get<RootObjectUnpacker>().Unpack(Root);
|
||||
|
||||
// separate atomic objects from block instances
|
||||
var (atomicObjects, blockInstances) = scope
|
||||
.Get<RootObjectUnpacker>()
|
||||
.SplitAtomicObjectsAndInstances(unpackedRoot.ObjectsToConvert);
|
||||
|
||||
// initialize unpackers and collection builder
|
||||
var colorUnpacker = new GrasshopperColorUnpacker(unpackedRoot);
|
||||
var materialUnpacker = new GrasshopperMaterialUnpacker(unpackedRoot);
|
||||
var collectionRebuilder = new GrasshopperCollectionRebuilder(
|
||||
(Root as Collection) ?? new Collection { name = "unnamed" }
|
||||
);
|
||||
|
||||
// convert atomic objects directly
|
||||
var mapHandler = new LocalToGlobalMapHandler(
|
||||
traversalContextUnpacker,
|
||||
collectionRebuilder,
|
||||
colorUnpacker,
|
||||
materialUnpacker
|
||||
);
|
||||
|
||||
foreach (var atomicContext in atomicObjects)
|
||||
{
|
||||
SpeckleConversionContext.EndCurrent();
|
||||
mapHandler.ConvertAtomicObject(atomicContext);
|
||||
}
|
||||
|
||||
// process block instances using converted atomic objects
|
||||
// block processing needs converted objects, but object filtering needs block definitions.
|
||||
mapHandler.ConvertBlockInstances(blockInstances, unpackedRoot.DefinitionProxies);
|
||||
|
||||
Result = new SpeckleCollectionWrapperGoo(collectionRebuilder.RootCollectionWrapper);
|
||||
RootProperties = rootPropertiesGoo;
|
||||
|
||||
// TODO: If we have NodeRun events later, better to have `ComponentTracker` to use across components
|
||||
var customProperties = new Dictionary<string, object>()
|
||||
{
|
||||
{ "isAsync", true },
|
||||
{ "sourceHostApp", HostApplications.GetSlugFromHostAppNameAndVersion(receiveInfo.SourceApplication) },
|
||||
{ "auto", Parent.AutoReceive }
|
||||
};
|
||||
if (receiveInfo.WorkspaceId != null)
|
||||
{
|
||||
customProperties.Add("workspace_id", receiveInfo.WorkspaceId);
|
||||
}
|
||||
|
||||
if (receiveInfo.SelectedVersionUserId != null)
|
||||
{
|
||||
customProperties.Add("isMultiplayer", receiveInfo.SelectedVersionUserId != Parent.ApiClient.Account.userInfo.id);
|
||||
}
|
||||
await scope.Get<IMixPanelManager>().TrackEvent(MixPanelEvents.Receive, Parent.ApiClient.Account, customProperties);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+84
-91
@@ -143,102 +143,95 @@ public class ReceiveComponent : SpeckleTaskCapableComponent<ReceiveComponentInpu
|
||||
}
|
||||
|
||||
using var scope = PriorityLoader.CreateScopeForActiveDocument();
|
||||
var clientFactory = scope.ServiceProvider.GetRequiredService<IClientFactory>();
|
||||
var receiveOperation = scope.ServiceProvider.GetRequiredService<GrasshopperReceiveOperation>();
|
||||
|
||||
try
|
||||
// Do the thing 👇🏼
|
||||
|
||||
Account? account = input.Resource.Account.GetAccount(scope);
|
||||
if (account is null)
|
||||
{
|
||||
var clientFactory = scope.ServiceProvider.GetRequiredService<IClientFactory>();
|
||||
var receiveOperation = scope.ServiceProvider.GetRequiredService<GrasshopperReceiveOperation>();
|
||||
|
||||
// Do the thing 👇🏼
|
||||
|
||||
Account? account = input.Resource.Account.GetAccount(scope);
|
||||
if (account is null)
|
||||
{
|
||||
throw new SpeckleAccountManagerException("No default account was found");
|
||||
}
|
||||
|
||||
using var client = clientFactory.Create(account);
|
||||
var receiveInfo = await input.Resource.GetReceiveInfo(client, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
// store version id for tracking
|
||||
_lastVersionId = receiveInfo.SelectedVersionId;
|
||||
|
||||
var progress = new Progress<CardProgress>(_ =>
|
||||
{
|
||||
// TODO: Progress only makes sense in non-blocking async receive, which is not supported yet.
|
||||
// Message = $"{progress.Status}: {progress.Progress}";
|
||||
});
|
||||
|
||||
var root = await receiveOperation
|
||||
.ReceiveCommitObject(receiveInfo, progress, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
// extract model-wide root properties (see cnx-2722)
|
||||
SpecklePropertyGroupGoo? rootPropertiesGoo = null;
|
||||
if (root is RootCollection rootCollection && rootCollection.properties.Count > 0)
|
||||
{
|
||||
rootPropertiesGoo = new SpecklePropertyGroupGoo(rootCollection.properties);
|
||||
}
|
||||
|
||||
// TODO: If we have NodeRun events later, better to have `ComponentTracker` to use across components
|
||||
var customProperties = new Dictionary<string, object>
|
||||
{
|
||||
{ "isAsync", false },
|
||||
{ "sourceHostApp", HostApplications.GetSlugFromHostAppNameAndVersion(receiveInfo.SourceApplication) }
|
||||
};
|
||||
if (receiveInfo.WorkspaceId != null)
|
||||
{
|
||||
customProperties.Add("workspace_id", receiveInfo.WorkspaceId);
|
||||
}
|
||||
if (receiveInfo.SelectedVersionUserId != null)
|
||||
{
|
||||
customProperties.Add("isMultiplayer", receiveInfo.SelectedVersionUserId != client.Account.userInfo.id);
|
||||
}
|
||||
var mixpanel = PriorityLoader.Container.GetRequiredService<IMixPanelManager>();
|
||||
await mixpanel.TrackEvent(MixPanelEvents.Receive, account, customProperties);
|
||||
|
||||
// Setup conversion context BEFORE unpacking (which triggers DataObjectConverter)
|
||||
SpeckleConversionContext.SetupCurrent(scope);
|
||||
|
||||
var rootObjectUnpacker = scope.ServiceProvider.GetService<RootObjectUnpacker>();
|
||||
var unpackedRoot = rootObjectUnpacker.Unpack(root);
|
||||
|
||||
// split atomic objects from block components before conversion
|
||||
var (atomicObjects, blockInstances) = rootObjectUnpacker.SplitAtomicObjectsAndInstances(
|
||||
unpackedRoot.ObjectsToConvert
|
||||
);
|
||||
|
||||
// Initialize unpackers and collection builder (data holders - created with new)
|
||||
var colorUnpacker = new GrasshopperColorUnpacker(unpackedRoot);
|
||||
var materialUnpacker = new GrasshopperMaterialUnpacker(unpackedRoot);
|
||||
var collectionRebuilder = new GrasshopperCollectionRebuilder(
|
||||
(root as Collection) ?? new Collection { name = "unnamed" }
|
||||
);
|
||||
|
||||
// get handler from DI and initialize with per-operation data
|
||||
var mapHandler = scope
|
||||
.ServiceProvider.GetRequiredService<LocalToGlobalMapHandler>()
|
||||
.Initialize(
|
||||
scope.ServiceProvider.GetRequiredService<TraversalContextUnpacker>(),
|
||||
colorUnpacker,
|
||||
materialUnpacker,
|
||||
collectionRebuilder,
|
||||
unpackedRoot.DefinitionProxies
|
||||
);
|
||||
|
||||
// two-pass conversion: normal objects first, then DataObjects with InstanceProxies
|
||||
mapHandler.ConvertAtomicObjects(atomicObjects);
|
||||
|
||||
// process block instances (internally filters InstanceProxies belonging to registered DataObjects)
|
||||
mapHandler.ConvertBlockInstances(blockInstances);
|
||||
|
||||
var goo = new SpeckleCollectionWrapperGoo(collectionRebuilder.RootCollectionWrapper);
|
||||
return new ReceiveComponentOutput { RootObject = goo, RootProperties = rootPropertiesGoo };
|
||||
throw new SpeckleAccountManagerException("No default account was found");
|
||||
}
|
||||
finally
|
||||
|
||||
using var client = clientFactory.Create(account);
|
||||
var receiveInfo = await input.Resource.GetReceiveInfo(client, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
// store version id for tracking
|
||||
_lastVersionId = receiveInfo.SelectedVersionId;
|
||||
|
||||
var progress = new Progress<CardProgress>(_ =>
|
||||
{
|
||||
SpeckleConversionContext.EndCurrent();
|
||||
// TODO: Progress only makes sense in non-blocking async receive, which is not supported yet.
|
||||
// Message = $"{progress.Status}: {progress.Progress}";
|
||||
});
|
||||
|
||||
var root = await receiveOperation
|
||||
.ReceiveCommitObject(receiveInfo, progress, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
// extract model-wide root properties (see cnx-2722)
|
||||
SpecklePropertyGroupGoo? rootPropertiesGoo = null;
|
||||
if (root is RootCollection rootCollection && rootCollection.properties.Count > 0)
|
||||
{
|
||||
rootPropertiesGoo = new SpecklePropertyGroupGoo(rootCollection.properties);
|
||||
}
|
||||
|
||||
// TODO: If we have NodeRun events later, better to have `ComponentTracker` to use across components
|
||||
var customProperties = new Dictionary<string, object>
|
||||
{
|
||||
{ "isAsync", false },
|
||||
{ "sourceHostApp", HostApplications.GetSlugFromHostAppNameAndVersion(receiveInfo.SourceApplication) }
|
||||
};
|
||||
if (receiveInfo.WorkspaceId != null)
|
||||
{
|
||||
customProperties.Add("workspace_id", receiveInfo.WorkspaceId);
|
||||
}
|
||||
if (receiveInfo.SelectedVersionUserId != null)
|
||||
{
|
||||
customProperties.Add("isMultiplayer", receiveInfo.SelectedVersionUserId != client.Account.userInfo.id);
|
||||
}
|
||||
var mixpanel = PriorityLoader.Container.GetRequiredService<IMixPanelManager>();
|
||||
await mixpanel.TrackEvent(MixPanelEvents.Receive, account, customProperties);
|
||||
|
||||
// We need to rethink these lovely unpackers, there's a bit too many of 'em
|
||||
var rootObjectUnpacker = scope.ServiceProvider.GetService<RootObjectUnpacker>();
|
||||
var traversalContextUnpacker = new TraversalContextUnpacker();
|
||||
|
||||
var unpackedRoot = rootObjectUnpacker.Unpack(root);
|
||||
|
||||
// split atomic objects from block components before conversion
|
||||
var (atomicObjects, blockInstances) = rootObjectUnpacker.SplitAtomicObjectsAndInstances(
|
||||
unpackedRoot.ObjectsToConvert
|
||||
);
|
||||
|
||||
// Initialize unpackers and collection builder
|
||||
var colorUnpacker = new GrasshopperColorUnpacker(unpackedRoot);
|
||||
var materialUnpacker = new GrasshopperMaterialUnpacker(unpackedRoot);
|
||||
var collectionRebuilder = new GrasshopperCollectionRebuilder(
|
||||
(root as Collection) ?? new Collection { name = "unnamed" }
|
||||
);
|
||||
|
||||
// convert atomic objects directly
|
||||
var mapHandler = new LocalToGlobalMapHandler(
|
||||
traversalContextUnpacker,
|
||||
collectionRebuilder,
|
||||
colorUnpacker,
|
||||
materialUnpacker
|
||||
);
|
||||
|
||||
foreach (var atomicContext in atomicObjects)
|
||||
{
|
||||
mapHandler.ConvertAtomicObject(atomicContext);
|
||||
}
|
||||
|
||||
// process block instances using converted atomic objects
|
||||
// block processing needs converted objects, but object filtering needs block definitions.
|
||||
mapHandler.ConvertBlockInstances(blockInstances, unpackedRoot.DefinitionProxies);
|
||||
|
||||
// var x = new SpeckleCollectionGoo { Value = collGen.RootCollection };
|
||||
var goo = new SpeckleCollectionWrapperGoo(collectionRebuilder.RootCollectionWrapper);
|
||||
return new ReceiveComponentOutput { RootObject = goo, RootProperties = rootPropertiesGoo };
|
||||
}
|
||||
|
||||
private void SetupSubscription(SpeckleUrlModelResource resource)
|
||||
|
||||
+2
-3
@@ -29,13 +29,13 @@ public class SpeckleConversionContext(IRootToSpeckleConverter speckleConverter,
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetupCurrent(IServiceScope? scope = null)
|
||||
public static void SetupCurrent()
|
||||
{
|
||||
if (s_currentContext != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
s_scope = scope ?? PriorityLoader.CreateScopeForActiveDocument();
|
||||
s_scope = PriorityLoader.CreateScopeForActiveDocument();
|
||||
s_currentContext = s_scope.Get<SpeckleConversionContext>();
|
||||
}
|
||||
|
||||
@@ -60,7 +60,6 @@ public class SpeckleConversionContext(IRootToSpeckleConverter speckleConverter,
|
||||
{
|
||||
GeometryBase geometry => [(geometry, input)],
|
||||
List<GeometryBase> geometryList => geometryList.Select(o => ((object)o, input)).ToList(),
|
||||
List<(GeometryBase, Base)> pairList when pairList.Count == 0 => [],
|
||||
IEnumerable<(GeometryBase, Base)> fallbackConversionResult
|
||||
=> fallbackConversionResult.Select(o => ((object)o.Item1, o.Item2)).ToList(),
|
||||
object obj => [(obj, input)],
|
||||
|
||||
+121
-346
@@ -1,20 +1,12 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Rhino.Geometry;
|
||||
using Speckle.Connectors.Common.Operations.Receive;
|
||||
using Speckle.Connectors.GrasshopperShared.HostApp;
|
||||
using Speckle.Connectors.GrasshopperShared.Operations.Receive;
|
||||
using Speckle.Connectors.GrasshopperShared.Parameters;
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Converters.Common.ToHost;
|
||||
using Speckle.Converters.Rhino;
|
||||
using Speckle.Sdk;
|
||||
using Speckle.Sdk.Common;
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Models.Collections;
|
||||
using Speckle.Sdk.Models.GraphTraversal;
|
||||
using Speckle.Sdk.Models.Instances;
|
||||
using DataObject = Speckle.Objects.Data.DataObject;
|
||||
|
||||
namespace Speckle.Connectors.GrasshopperShared.Operations.Receive;
|
||||
|
||||
/// <summary>
|
||||
/// Handles conversion of atomic objects from TraversalContexts into Grasshopper wrapper objects.
|
||||
@@ -27,401 +19,184 @@ namespace Speckle.Connectors.GrasshopperShared.Operations.Receive;
|
||||
internal sealed class LocalToGlobalMapHandler
|
||||
{
|
||||
public Dictionary<string, SpeckleGeometryWrapper> ConvertedObjectsMap { get; } = new();
|
||||
public readonly GrasshopperCollectionRebuilder CollectionRebuilder;
|
||||
|
||||
// injected via constructor (DI-managed)
|
||||
private readonly IDataObjectInstanceRegistry _dataObjectInstanceRegistry;
|
||||
private readonly ILogger<LocalToGlobalMapHandler> _logger;
|
||||
private readonly IConverterSettingsStore<RhinoConversionSettings> _settingsStore;
|
||||
|
||||
// set via Initialize() method (per-operation data)
|
||||
private TraversalContextUnpacker _traversalContextUnpacker = null!;
|
||||
private GrasshopperColorUnpacker _colorUnpacker = null!;
|
||||
private GrasshopperMaterialUnpacker _materialUnpacker = null!;
|
||||
private IReadOnlyCollection<InstanceDefinitionProxy>? _definitionProxies;
|
||||
|
||||
// auto property (fixes IDE0032)
|
||||
public GrasshopperCollectionRebuilder CollectionRebuilder { get; private set; } = null!;
|
||||
private readonly TraversalContextUnpacker _traversalContextUnpacker;
|
||||
private readonly GrasshopperColorUnpacker _colorUnpacker;
|
||||
private readonly GrasshopperMaterialUnpacker _materialUnpacker;
|
||||
|
||||
public LocalToGlobalMapHandler(
|
||||
IDataObjectInstanceRegistry dataObjectInstanceRegistry,
|
||||
ILogger<LocalToGlobalMapHandler> logger,
|
||||
IConverterSettingsStore<RhinoConversionSettings> settingsStore
|
||||
)
|
||||
{
|
||||
_dataObjectInstanceRegistry = dataObjectInstanceRegistry;
|
||||
_logger = logger;
|
||||
_settingsStore = settingsStore;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the handler with per-operation data.
|
||||
/// Must be called before using ConvertAtomicObjects or ConvertBlockInstances.
|
||||
/// </summary>
|
||||
public LocalToGlobalMapHandler Initialize(
|
||||
TraversalContextUnpacker traversalContextUnpacker,
|
||||
GrasshopperColorUnpacker colorUnpacker,
|
||||
GrasshopperMaterialUnpacker materialUnpacker,
|
||||
GrasshopperCollectionRebuilder collectionRebuilder,
|
||||
IReadOnlyCollection<InstanceDefinitionProxy>? definitionProxies
|
||||
GrasshopperColorUnpacker colorUnpacker,
|
||||
GrasshopperMaterialUnpacker materialUnpacker
|
||||
)
|
||||
{
|
||||
_traversalContextUnpacker = traversalContextUnpacker;
|
||||
_colorUnpacker = colorUnpacker;
|
||||
_materialUnpacker = materialUnpacker;
|
||||
CollectionRebuilder = collectionRebuilder;
|
||||
_definitionProxies = definitionProxies;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts all atomic objects in two passes:
|
||||
/// Pass 1 - Convert normal objects and populate ConvertedObjectsMap
|
||||
/// Pass 2 - Resolve registered DataObjects with InstanceProxies using the populated map
|
||||
/// Converts atomic object from TraversalContext to SpeckleObjectWrapper.
|
||||
/// </summary>
|
||||
public void ConvertAtomicObjects(IEnumerable<TraversalContext> atomicContexts)
|
||||
{
|
||||
// Cache to avoid re-iterating for registered check
|
||||
var atomicList = atomicContexts as IList<TraversalContext> ?? atomicContexts.ToList();
|
||||
|
||||
// Pass 1: Convert all non-registered DataObjects to populate ConvertedObjectsMap
|
||||
foreach (var atomicContext in atomicList)
|
||||
{
|
||||
ConvertObjectToCache(atomicContext);
|
||||
}
|
||||
|
||||
// Pass 2: Process registered DataObjects (definitions now available in ConvertedObjectsMap)
|
||||
foreach (var atomicContext in atomicList)
|
||||
{
|
||||
if (atomicContext.Current is DataObject dataObject)
|
||||
{
|
||||
var dataObjectId = dataObject.applicationId ?? dataObject.id;
|
||||
if (dataObjectId is not null && _dataObjectInstanceRegistry.IsRegistered(dataObjectId))
|
||||
{
|
||||
ResolveDataObjectInstanceProxies(atomicContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts and caches an atomic object for later lookup.
|
||||
/// Skips registered DataObjects (displayValue is InstanceProxy) - they are resolved in ResolveDataObjectInstanceProxies.
|
||||
/// </summary>
|
||||
private void ConvertObjectToCache(TraversalContext atomicContext)
|
||||
public void ConvertAtomicObject(TraversalContext atomicContext)
|
||||
{
|
||||
var obj = atomicContext.Current;
|
||||
var objId = obj.applicationId ?? obj.id;
|
||||
|
||||
if (objId is null || ConvertedObjectsMap.ContainsKey(objId))
|
||||
if (objId == null || ConvertedObjectsMap.ContainsKey(objId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// skip registered DataObjects - they'll be processed in second pass
|
||||
if (obj is DataObject dataObject)
|
||||
{
|
||||
var id = dataObject.applicationId ?? dataObject.id.NotNull();
|
||||
if (_dataObjectInstanceRegistry.IsRegistered(id))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
List<(object, Base)> converted = SpeckleConversionContext.Current.ConvertToHost(obj);
|
||||
|
||||
// get path and collection
|
||||
var path = _traversalContextUnpacker.GetCollectionPath(atomicContext).ToList();
|
||||
var objectCollection = CollectionRebuilder.GetOrCreateSpeckleCollectionFromPath(
|
||||
path,
|
||||
_colorUnpacker,
|
||||
_materialUnpacker
|
||||
);
|
||||
|
||||
// nothing converted - nothing to do
|
||||
if (converted.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// handle normal DataObject (has converted geometry)
|
||||
if (obj is DataObject normalDataObject)
|
||||
{
|
||||
var geometries = ConvertToGeometryWrappers(converted);
|
||||
var dataObjectWrapper = CreateDataObjectWrapper(normalDataObject, geometries, path, objectCollection);
|
||||
|
||||
CollectionRebuilder.AppendSpeckleGrasshopperObject(dataObjectWrapper, path, _colorUnpacker, _materialUnpacker);
|
||||
return;
|
||||
}
|
||||
|
||||
// handle normal geometry (not DataObject)
|
||||
SpecklePropertyGroupGoo propertyGroup = new();
|
||||
if (obj[Constants.PROPERTIES_PROP] is Dictionary<string, object?> props)
|
||||
{
|
||||
propertyGroup.CastFrom(props);
|
||||
}
|
||||
|
||||
foreach ((object convertedObj, Base original) in converted)
|
||||
{
|
||||
if (convertedObj is GeometryBase geometryBase)
|
||||
{
|
||||
var wrapper = new SpeckleGeometryWrapper()
|
||||
{
|
||||
Base = original,
|
||||
Path = path.Select(p => p.name).ToList(),
|
||||
Parent = objectCollection,
|
||||
GeometryBase = geometryBase,
|
||||
Properties = propertyGroup,
|
||||
Name = obj[Constants.NAME_PROP] as string ?? "",
|
||||
Color = _colorUnpacker.Cache.TryGetValue(original.applicationId ?? "", out var cachedObjColor)
|
||||
? cachedObjColor
|
||||
: null,
|
||||
Material = _materialUnpacker.Cache.TryGetValue(original.applicationId ?? "", out var cachedObjMaterial)
|
||||
? cachedObjMaterial
|
||||
: null,
|
||||
ApplicationId = objId
|
||||
};
|
||||
|
||||
ConvertedObjectsMap[objId] = wrapper;
|
||||
CollectionRebuilder.AppendSpeckleGrasshopperObject(wrapper, path, _colorUnpacker, _materialUnpacker);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex) when (!ex.IsFatal())
|
||||
{
|
||||
// don't throw - continue processing other objects
|
||||
_logger.LogError(ex, "Failed to convert object {objectId} of type {objectType}", objId, obj.speckle_type);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolves a registered DataObject by transforming its InstanceProxy definition objects.
|
||||
/// Requires definition objects to exist in ConvertedObjectsMap (populated by ConvertObjectToCache).
|
||||
/// </summary>
|
||||
private void ResolveDataObjectInstanceProxies(TraversalContext atomicContext)
|
||||
{
|
||||
var obj = atomicContext.Current;
|
||||
if (obj is not DataObject dataObject)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var dataObjectId = dataObject.applicationId ?? dataObject.id.NotNull();
|
||||
if (!_dataObjectInstanceRegistry.IsRegistered(dataObjectId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var path = _traversalContextUnpacker.GetCollectionPath(atomicContext).ToList();
|
||||
|
||||
// Always create collection - consumed objects will be cleaned up later
|
||||
var objectCollection = CollectionRebuilder.GetOrCreateSpeckleCollectionFromPath(
|
||||
path,
|
||||
_colorUnpacker,
|
||||
_materialUnpacker
|
||||
);
|
||||
|
||||
var entry = _dataObjectInstanceRegistry.GetEntries()[dataObjectId];
|
||||
var resolvedGeometries = ResolveInstanceProxiesToGeometries(entry.InstanceProxies);
|
||||
var dataObjectWrapper = CreateDataObjectWrapper(dataObject, resolvedGeometries, path, objectCollection);
|
||||
if (obj is Speckle.Objects.Data.DataObject dataObject)
|
||||
{
|
||||
// get color and mat on dataobject first
|
||||
Color? dataObjColor = _colorUnpacker.Cache.TryGetValue(
|
||||
dataObject.applicationId ?? "",
|
||||
out var cachedDataObjColor
|
||||
)
|
||||
? cachedDataObjColor
|
||||
: null;
|
||||
|
||||
CollectionRebuilder.AppendSpeckleGrasshopperObject(dataObjectWrapper, path, _colorUnpacker, _materialUnpacker);
|
||||
SpeckleMaterialWrapper? dataObjMat = _materialUnpacker.Cache.TryGetValue(
|
||||
dataObject.applicationId ?? "",
|
||||
out var cachedDataObjMaterial
|
||||
)
|
||||
? cachedDataObjMaterial
|
||||
: null;
|
||||
|
||||
// get geometries
|
||||
List<SpeckleGeometryWrapper> geometries = new();
|
||||
foreach ((object convertedObj, Base original) in converted)
|
||||
{
|
||||
if (convertedObj is GeometryBase geometryBase)
|
||||
{
|
||||
SpeckleGeometryWrapper wrapper =
|
||||
new()
|
||||
{
|
||||
Base = original,
|
||||
GeometryBase = geometryBase,
|
||||
Color = _colorUnpacker.Cache.TryGetValue(original.applicationId ?? "", out var cachedObjColor)
|
||||
? cachedObjColor
|
||||
: dataObjColor,
|
||||
Material = _materialUnpacker.Cache.TryGetValue(original.applicationId ?? "", out var cachedObjMaterial)
|
||||
? cachedObjMaterial
|
||||
: dataObjMat,
|
||||
};
|
||||
|
||||
geometries.Add(wrapper);
|
||||
}
|
||||
}
|
||||
|
||||
SpecklePropertyGroupGoo propertyGroup = new();
|
||||
propertyGroup.CastFrom(dataObject.properties);
|
||||
|
||||
// remove the displayvalue of the original dataobject since these are now processed and stored on the wrapper
|
||||
// to prevent storing of duplicate Base
|
||||
dataObject.displayValue.Clear();
|
||||
|
||||
var dataObjectWrapper = new SpeckleDataObjectWrapper()
|
||||
{
|
||||
Base = dataObject,
|
||||
Geometries = geometries,
|
||||
Path = path.Select(p => p.name).ToList(),
|
||||
Parent = objectCollection,
|
||||
Name = dataObject.name,
|
||||
Properties = propertyGroup,
|
||||
ApplicationId = dataObject.applicationId,
|
||||
};
|
||||
|
||||
// Add to collections (not to map since these won't be definition objects)
|
||||
CollectionRebuilder.AppendSpeckleGrasshopperObject(dataObjectWrapper, path, _colorUnpacker, _materialUnpacker);
|
||||
}
|
||||
else
|
||||
{
|
||||
SpecklePropertyGroupGoo propertyGroup = new();
|
||||
if (obj[Constants.PROPERTIES_PROP] is Dictionary<string, object?> props)
|
||||
{
|
||||
propertyGroup.CastFrom(props);
|
||||
}
|
||||
|
||||
foreach ((object convertedObj, Base original) in converted)
|
||||
{
|
||||
if (convertedObj is GeometryBase geometryBase)
|
||||
{
|
||||
var wrapper = new SpeckleGeometryWrapper()
|
||||
{
|
||||
Base = original,
|
||||
Path = path.Select(p => p.name).ToList(),
|
||||
Parent = objectCollection,
|
||||
GeometryBase = geometryBase,
|
||||
Properties = propertyGroup,
|
||||
Name = obj[Constants.NAME_PROP] as string ?? "",
|
||||
Color = _colorUnpacker.Cache.TryGetValue(original.applicationId ?? "", out var cachedObjColor)
|
||||
? cachedObjColor
|
||||
: null,
|
||||
Material = _materialUnpacker.Cache.TryGetValue(original.applicationId ?? "", out var cachedObjMaterial)
|
||||
? cachedObjMaterial
|
||||
: null,
|
||||
ApplicationId = objId
|
||||
};
|
||||
|
||||
// Always add to both map and collections
|
||||
ConvertedObjectsMap[objId] = wrapper;
|
||||
CollectionRebuilder.AppendSpeckleGrasshopperObject(wrapper, path, _colorUnpacker, _materialUnpacker);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex) when (!ex.IsFatal())
|
||||
{
|
||||
// don't throw - continue processing other DataObjects
|
||||
_logger.LogError(ex, "Failed to resolve DataObject {dataObjectId} with InstanceProxies", dataObjectId);
|
||||
// TODO: throw?
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts block instances and definitions from traversal contexts into Grasshopper wrapper objects.
|
||||
/// Automatically filters out InstanceProxies belonging to registered DataObjects.
|
||||
/// Automatically handles cleanup of consumed objects from the collection hierarchy.
|
||||
/// </summary>
|
||||
public void ConvertBlockInstances(IReadOnlyCollection<TraversalContext> blockInstances)
|
||||
/// <remarks>
|
||||
/// Deliberately handles both block conversion AND consumed object cleanup in a single operation.
|
||||
/// Too much, I know, BUT it ensures the cleanup always occurs immediately after block processing without
|
||||
/// requiring receive components to call a separate cleanup method in the correct order.
|
||||
/// </remarks>
|
||||
public void ConvertBlockInstances(
|
||||
IReadOnlyCollection<TraversalContext> blocks,
|
||||
IReadOnlyCollection<InstanceDefinitionProxy>? definitionProxies
|
||||
)
|
||||
{
|
||||
// build set of registered InstanceProxy IDs for fast lookup
|
||||
var registeredProxyIds = new HashSet<string>();
|
||||
foreach (var entry in _dataObjectInstanceRegistry.GetEntries().Values)
|
||||
{
|
||||
foreach (var proxy in entry.InstanceProxies)
|
||||
{
|
||||
var proxyId = proxy.applicationId ?? proxy.id;
|
||||
if (proxyId is not null)
|
||||
{
|
||||
registeredProxyIds.Add(proxyId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// filter out InstanceProxies that belong to registered DataObjects
|
||||
var filteredBlockInstances = blockInstances
|
||||
.Where(tc =>
|
||||
{
|
||||
if (tc.Current is InstanceProxy proxy)
|
||||
{
|
||||
var proxyId = proxy.applicationId ?? proxy.id;
|
||||
return proxyId is null || !registeredProxyIds.Contains(proxyId);
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.ToList();
|
||||
|
||||
var blockUnpacker = new GrasshopperBlockUnpacker(_traversalContextUnpacker, _colorUnpacker, _materialUnpacker);
|
||||
|
||||
// get consumed object IDs from unpacker
|
||||
// Get consumed object IDs from unpacker
|
||||
var consumedObjectIds = blockUnpacker.UnpackBlocks(
|
||||
filteredBlockInstances,
|
||||
_definitionProxies,
|
||||
blocks,
|
||||
definitionProxies,
|
||||
ConvertedObjectsMap,
|
||||
CollectionRebuilder
|
||||
);
|
||||
|
||||
// clean up consumed objects from collections
|
||||
// Clean up consumed objects from collections
|
||||
CollectionRebuilder.RemoveConsumedObjects(consumedObjectIds);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a DataObjectWrapper from a DataObject and its geometries.
|
||||
/// Handles color/material inheritance and property extraction.
|
||||
/// </summary>
|
||||
private SpeckleDataObjectWrapper CreateDataObjectWrapper(
|
||||
DataObject dataObject,
|
||||
List<SpeckleGeometryWrapper> geometries,
|
||||
List<Collection> path,
|
||||
SpeckleCollectionWrapper objectCollection
|
||||
)
|
||||
{
|
||||
// Get color and material on DataObject
|
||||
Color? dataObjColor = _colorUnpacker.Cache.TryGetValue(dataObject.applicationId ?? "", out var cachedDataObjColor)
|
||||
? cachedDataObjColor
|
||||
: null;
|
||||
|
||||
SpeckleMaterialWrapper? dataObjMat = _materialUnpacker.Cache.TryGetValue(
|
||||
dataObject.applicationId ?? "",
|
||||
out var cachedDataObjMaterial
|
||||
)
|
||||
? cachedDataObjMaterial
|
||||
: null;
|
||||
|
||||
// Apply DataObject color/material to geometries that don't have their own
|
||||
foreach (var geometry in geometries)
|
||||
{
|
||||
geometry.Color ??= dataObjColor;
|
||||
geometry.Material ??= dataObjMat;
|
||||
}
|
||||
|
||||
// Create property group
|
||||
SpecklePropertyGroupGoo propertyGroup = new();
|
||||
propertyGroup.CastFrom(dataObject.properties);
|
||||
|
||||
// Clear the displayValue to prevent storing duplicate Base
|
||||
dataObject.displayValue.Clear();
|
||||
|
||||
return new SpeckleDataObjectWrapper()
|
||||
{
|
||||
Base = dataObject,
|
||||
Geometries = geometries,
|
||||
Path = path.Select(p => p.name).ToList(),
|
||||
Parent = objectCollection,
|
||||
Name = dataObject.name,
|
||||
Properties = propertyGroup,
|
||||
ApplicationId = dataObject.applicationId,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolves InstanceProxy displayValues to transformed geometries.
|
||||
/// Returns the list of resolved geometries that can be used as DataObject displayValue replacements.
|
||||
/// </summary>
|
||||
private List<SpeckleGeometryWrapper> ResolveInstanceProxiesToGeometries(List<InstanceProxy> instanceProxies)
|
||||
{
|
||||
var resolvedGeometries = new List<SpeckleGeometryWrapper>();
|
||||
|
||||
// build a lookup of definitionId -> definition objects for quick access
|
||||
var definitionObjectsMap = new Dictionary<string, List<string>>();
|
||||
if (_definitionProxies is not null)
|
||||
{
|
||||
foreach (var defProxy in _definitionProxies)
|
||||
{
|
||||
var defId = defProxy.applicationId ?? defProxy.id;
|
||||
if (defId is not null)
|
||||
{
|
||||
definitionObjectsMap[defId] = defProxy.objects;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var instanceProxy in instanceProxies)
|
||||
{
|
||||
// get the definition objects for this instance
|
||||
if (!definitionObjectsMap.TryGetValue(instanceProxy.definitionId, out var definitionObjectIds))
|
||||
{
|
||||
continue; // definition not found, skip this proxy
|
||||
}
|
||||
|
||||
// get transform from the instance proxy
|
||||
var transform = GrasshopperHelpers.MatrixToTransform(instanceProxy.transform, instanceProxy.units);
|
||||
|
||||
// apply transform to each definition object
|
||||
foreach (var objectId in definitionObjectIds)
|
||||
{
|
||||
if (ConvertedObjectsMap.TryGetValue(objectId, out var definitionObject))
|
||||
{
|
||||
// deep copy and transform the geometry
|
||||
var transformedWrapper = definitionObject.DeepCopy();
|
||||
|
||||
// transform the GeometryBase
|
||||
transformedWrapper.GeometryBase.NotNull().Transform(transform);
|
||||
|
||||
// keep Base and GeometryBase in sync (fixed as of CNX-2847)
|
||||
transformedWrapper.Base = SpeckleConversionContext.Current.ConvertToSpeckle(transformedWrapper.GeometryBase);
|
||||
|
||||
// preserve metadata from original Base
|
||||
transformedWrapper.Base.applicationId = definitionObject.Base.applicationId;
|
||||
transformedWrapper.Base["units"] = _settingsStore.Current.SpeckleUnits;
|
||||
|
||||
resolvedGeometries.Add(transformedWrapper);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return resolvedGeometries;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the raw converted objects to SpeckleGeometryWrappers for DataObject display values.
|
||||
/// Does NOT apply DataObject-level colors/materials - that's handled by CreateDataObjectWrapper.
|
||||
/// </summary>
|
||||
private List<SpeckleGeometryWrapper> ConvertToGeometryWrappers(List<(object, Base)> converted)
|
||||
{
|
||||
var geometries = new List<SpeckleGeometryWrapper>();
|
||||
|
||||
foreach ((object convertedObj, Base original) in converted)
|
||||
{
|
||||
if (convertedObj is GeometryBase geometryBase)
|
||||
{
|
||||
SpeckleGeometryWrapper wrapper =
|
||||
new()
|
||||
{
|
||||
Base = original,
|
||||
GeometryBase = geometryBase,
|
||||
// try to get color/material from the individual geometry first
|
||||
Color = _colorUnpacker.Cache.TryGetValue(original.applicationId ?? "", out var cachedObjColor)
|
||||
? cachedObjColor
|
||||
: null,
|
||||
Material = _materialUnpacker.Cache.TryGetValue(original.applicationId ?? "", out var cachedObjMaterial)
|
||||
? cachedObjMaterial
|
||||
: null,
|
||||
};
|
||||
|
||||
geometries.Add(wrapper);
|
||||
}
|
||||
}
|
||||
|
||||
return geometries;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,9 +79,6 @@ public class SpecklePropertyGoo : GH_Goo<object>, ISpecklePropertyGoo
|
||||
case int i:
|
||||
Value = i;
|
||||
return true;
|
||||
case long l:
|
||||
Value = l;
|
||||
return true;
|
||||
case string s:
|
||||
Value = s;
|
||||
return true;
|
||||
|
||||
@@ -15,7 +15,6 @@ using Speckle.Connectors.GrasshopperShared.Operations.Send;
|
||||
using Speckle.Connectors.GrasshopperShared.Parameters;
|
||||
using Speckle.Connectors.GrasshopperShared.Properties;
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Converters.Common.ToHost;
|
||||
using Speckle.Converters.Rhino;
|
||||
using Speckle.Sdk;
|
||||
using Speckle.Sdk.Models.GraphTraversal;
|
||||
@@ -59,8 +58,6 @@ public class PriorityLoader : GH_AssemblyPriority
|
||||
services.AddTransient<GrasshopperReceiveOperation>();
|
||||
services.AddSingleton(DefaultTraversal.CreateTraversalFunc());
|
||||
services.AddTransient<TraversalContextUnpacker>();
|
||||
services.AddScoped<IDataObjectInstanceRegistry, DataObjectInstanceRegistry>();
|
||||
services.AddTransient<LocalToGlobalMapHandler>();
|
||||
|
||||
// send
|
||||
services.AddTransient<IRootObjectBuilder<SpeckleCollectionWrapperGoo>, GrasshopperRootObjectBuilder>();
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
<StartProgram>$(ProgramFiles)\Rhino $(RhinoVersion)\System\Rhino.exe</StartProgram>
|
||||
<EnableWindowsTargeting>true</EnableWindowsTargeting>
|
||||
<UseWpf>true</UseWpf>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<GenerateResourceUsePreserializedResources>true</GenerateResourceUsePreserializedResources>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -7,15 +7,14 @@
|
||||
<TargetExt>.rhp</TargetExt>
|
||||
<StartProgram>$(ProgramFiles)\Rhino $(RhinoVersion)\System\Rhino.exe</StartProgram>
|
||||
<EnableWindowsTargeting>true</EnableWindowsTargeting>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<UseWpf>true</UseWpf>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<GenerateResourceUsePreserializedResources>true</GenerateResourceUsePreserializedResources>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="RhinoCommon" IncludeAssets="compile; build" PrivateAssets="all" VersionOverride="8.9.24194.18121"/>
|
||||
<PackageReference Include="RhinoWindows" IncludeAssets="compile; build" PrivateAssets="all" VersionOverride="8.9.24194.18121"/>
|
||||
<PackageReference Include="RhinoCommon" IncludeAssets="compile; build" PrivateAssets="all" />
|
||||
<PackageReference Include="RhinoWindows" IncludeAssets="compile; build" PrivateAssets="all" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
+2
-2
@@ -19,8 +19,8 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="RhinoCommon" IncludeAssets="compile; build" PrivateAssets="all"/>
|
||||
<PackageReference Include="RhinoWindows" IncludeAssets="compile; build" PrivateAssets="all"/>
|
||||
<PackageReference Include="RhinoCommon" IncludeAssets="compile; build" PrivateAssets="all" VersionOverride="8.21.25188.17001"/>
|
||||
<PackageReference Include="RhinoWindows" IncludeAssets="compile; build" PrivateAssets="all" VersionOverride="8.21.25188.17001"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -29,20 +29,20 @@
|
||||
},
|
||||
"RhinoCommon": {
|
||||
"type": "Direct",
|
||||
"requested": "[8.25.25328.11001, )",
|
||||
"resolved": "8.25.25328.11001",
|
||||
"contentHash": "PDKR9GwqyUXUkTulV4J0dzDIf/aWqSJkL7nkS8ReAx8xhnt/+RQpE8gTjOSCmkSU2tjG6WzclowbTxwMTU7VAA==",
|
||||
"requested": "[8.21.25188.17001, )",
|
||||
"resolved": "8.21.25188.17001",
|
||||
"contentHash": "Wo6JXheyDBvilyIwDY6xZCQJC4170jzAlTSlMgh8yokUY+vYyCl4KJVXZofIOynNt/xx5wLqb2On5gZZekXR6w==",
|
||||
"dependencies": {
|
||||
"System.Drawing.Common": "7.0.0"
|
||||
}
|
||||
},
|
||||
"RhinoWindows": {
|
||||
"type": "Direct",
|
||||
"requested": "[8.25.25328.11001, )",
|
||||
"resolved": "8.25.25328.11001",
|
||||
"contentHash": "I/+++piwtYTue+iAAQqcMF5QlontqwNnC7Leyhiv2FiF8JpAl6K44ZsJqB7ZEUC6ns0LDfa3mbFzQwUfHwYumQ==",
|
||||
"requested": "[8.21.25188.17001, )",
|
||||
"resolved": "8.21.25188.17001",
|
||||
"contentHash": "9zqCorcLRBeiW/j1RTwUS4E7bnZetAdA9WDdtd/AQccjOpxdtw76wdN+ciyQ6qslseWkwZ9qSBeh7QaM800Ntw==",
|
||||
"dependencies": {
|
||||
"RhinoCommon": "[8.25.25328.11001]"
|
||||
"RhinoCommon": "[8.21.25188.17001]"
|
||||
}
|
||||
},
|
||||
"Speckle.InterfaceGenerator": {
|
||||
|
||||
@@ -123,12 +123,6 @@ public class RhinoLayerBaker : TraversalContextUnpacker
|
||||
}
|
||||
|
||||
var cleanNewLayerName = RhinoUtils.CleanLayerName(collection.name);
|
||||
|
||||
if (!ModelComponent.IsValidComponentName(cleanNewLayerName))
|
||||
{
|
||||
throw new SpeckleException($"Layer name '{currentLayerName}' is not valid");
|
||||
}
|
||||
|
||||
Layer newLayer = new() { Name = cleanNewLayerName, ParentLayerId = previousLayer?.Id ?? Guid.Empty };
|
||||
|
||||
// set material
|
||||
@@ -156,7 +150,7 @@ public class RhinoLayerBaker : TraversalContextUnpacker
|
||||
int index = currentDocument.Layers.Add(newLayer);
|
||||
if (index == -1)
|
||||
{
|
||||
throw new SpeckleException($"Could not create layer '{currentLayerName}'");
|
||||
throw new SpeckleException($"Could not create layer '{currentLayerName}'.");
|
||||
}
|
||||
|
||||
_hostLayerCache.Add(currentLayerName, index);
|
||||
|
||||
@@ -1,41 +1,27 @@
|
||||
using System.Text;
|
||||
|
||||
namespace Speckle.Connectors.Rhino.HostApp;
|
||||
|
||||
public static class RhinoUtils
|
||||
{
|
||||
private static readonly HashSet<char> s_skipChars = ['[', ']', '(', ')', '{', '}'];
|
||||
private static readonly HashSet<char> s_replaceWithHyphen = [':', ';'];
|
||||
|
||||
public static string CleanBlockDefinitionName(string str) => str.Replace('/', '_').Replace('\\', '_');
|
||||
public static string CleanBlockDefinitionName(string str)
|
||||
{
|
||||
return ReplaceChars(str, @"\/", "_");
|
||||
}
|
||||
|
||||
// Cleans up layer names to be "rhino" proof. Note this can be improved, as "()[] and {}" are illegal only at the start.
|
||||
// https://docs.mcneel.com/rhino/6/help/en-us/index.htm#information/namingconventions.htm?Highlight=naming
|
||||
public static string CleanLayerName(string str)
|
||||
{
|
||||
var sb = new StringBuilder(str.Length);
|
||||
str = ReplaceChars(str, @"[](){}", "");
|
||||
return ReplaceChars(str, @":;", "-");
|
||||
}
|
||||
|
||||
foreach (char c in str)
|
||||
private static string ReplaceChars(string str, string invalidChars, string replaceString)
|
||||
{
|
||||
foreach (char c in invalidChars)
|
||||
{
|
||||
if (char.IsControl(c))
|
||||
{
|
||||
continue; // skip control characters (shoutout cnx-2809)
|
||||
}
|
||||
|
||||
if (s_skipChars.Contains(c))
|
||||
{
|
||||
continue; // skip brackets
|
||||
}
|
||||
|
||||
if (s_replaceWithHyphen.Contains(c))
|
||||
{
|
||||
sb.Append('-');
|
||||
continue;
|
||||
}
|
||||
|
||||
sb.Append(c);
|
||||
str = str.Replace(c.ToString(), replaceString);
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Rhino.DocObjects;
|
||||
using Rhino.DocObjects.Tables;
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Objects.Other;
|
||||
using Speckle.Sdk;
|
||||
|
||||
namespace Speckle.Connectors.Rhino.HostApp;
|
||||
|
||||
public class RhinoViewUnpacker
|
||||
{
|
||||
private readonly IRootToSpeckleConverter _rootToSpeckleConverter;
|
||||
private readonly ILogger<RhinoViewUnpacker> _logger;
|
||||
|
||||
public RhinoViewUnpacker(IRootToSpeckleConverter rootToSpeckleConverter, ILogger<RhinoViewUnpacker> logger)
|
||||
{
|
||||
_rootToSpeckleConverter = rootToSpeckleConverter;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
private Camera? ConvertViewToCamera(ViewInfo view)
|
||||
{
|
||||
try
|
||||
{
|
||||
var converted = (Speckle.Objects.Other.Camera)_rootToSpeckleConverter.Convert(view);
|
||||
if (converted is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return converted;
|
||||
}
|
||||
catch (Exception ex) when (!ex.IsFatal())
|
||||
{
|
||||
_logger.LogError(ex, "Failed to create a view from {view}", view.Name);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Iterates through a given set of rhino named views to create proxies
|
||||
/// </summary>
|
||||
/// <param name="views">current document named views</param>
|
||||
/// <returns></returns>
|
||||
public List<Camera> UnpackViews(NamedViewTable views)
|
||||
{
|
||||
List<Camera> cameras = new();
|
||||
foreach (ViewInfo view in views)
|
||||
{
|
||||
// skip isometric views for now.
|
||||
// getting the orthographic match between host apps and the viewer requires too much effort atm.
|
||||
if (view.Viewport.IsParallelProjection)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ConvertViewToCamera(view) is Camera camera)
|
||||
{
|
||||
cameras.Add(camera);
|
||||
}
|
||||
}
|
||||
|
||||
return cameras;
|
||||
}
|
||||
}
|
||||
+2
-15
@@ -33,7 +33,6 @@ public class RhinoRootObjectBuilder : IRootObjectBuilder<RhinoObject>
|
||||
private readonly RhinoGroupUnpacker _groupUnpacker;
|
||||
private readonly RhinoMaterialUnpacker _materialUnpacker;
|
||||
private readonly RhinoColorUnpacker _colorUnpacker;
|
||||
private readonly RhinoViewUnpacker _viewUnpacker;
|
||||
private readonly PropertiesExtractor _propertiesExtractor;
|
||||
private readonly ILogger<RhinoRootObjectBuilder> _logger;
|
||||
private readonly ISdkActivityFactory _activityFactory;
|
||||
@@ -47,7 +46,6 @@ public class RhinoRootObjectBuilder : IRootObjectBuilder<RhinoObject>
|
||||
RhinoGroupUnpacker groupUnpacker,
|
||||
RhinoMaterialUnpacker materialUnpacker,
|
||||
RhinoColorUnpacker colorUnpacker,
|
||||
RhinoViewUnpacker viewUnpacker,
|
||||
PropertiesExtractor propertiesExtractor,
|
||||
ILogger<RhinoRootObjectBuilder> logger,
|
||||
ISdkActivityFactory activityFactory
|
||||
@@ -61,7 +59,6 @@ public class RhinoRootObjectBuilder : IRootObjectBuilder<RhinoObject>
|
||||
_rootToSpeckleConverter = rootToSpeckleConverter;
|
||||
_materialUnpacker = materialUnpacker;
|
||||
_colorUnpacker = colorUnpacker;
|
||||
_viewUnpacker = viewUnpacker;
|
||||
_propertiesExtractor = propertiesExtractor;
|
||||
_logger = logger;
|
||||
_activityFactory = activityFactory;
|
||||
@@ -125,30 +122,20 @@ public class RhinoRootObjectBuilder : IRootObjectBuilder<RhinoObject>
|
||||
throw new SpeckleException("Failed to convert all objects."); // fail fast instead creating empty commit! It will appear as model card error with red color.
|
||||
}
|
||||
|
||||
// 4 - Unpack all proxies for the root
|
||||
// Get all layers from the created collections on the root object commit for proxy processing
|
||||
List<Layer> layers = _layerUnpacker.GetUsedLayers().ToList();
|
||||
|
||||
using (var _ = _activityFactory.Start("UnpackRenderMaterials"))
|
||||
{
|
||||
// 4 - Unpack the render material proxies
|
||||
rootObjectCollection[ProxyKeys.RENDER_MATERIAL] = _materialUnpacker.UnpackRenderMaterials(atomicObjects, layers);
|
||||
}
|
||||
|
||||
using (var _ = _activityFactory.Start("UnpackColors"))
|
||||
{
|
||||
// 5 - Unpack the color proxies
|
||||
rootObjectCollection[ProxyKeys.COLOR] = _colorUnpacker.UnpackColors(atomicObjects, layers);
|
||||
}
|
||||
|
||||
// 5 - Unpack all other objects for the root
|
||||
using (var _ = _activityFactory.Start("UnpackViews"))
|
||||
{
|
||||
List<Objects.Other.Camera> views = _viewUnpacker.UnpackViews(_converterSettings.Current.Document.NamedViews);
|
||||
if (views.Count > 0)
|
||||
{
|
||||
rootObjectCollection[RootKeys.VIEW] = views;
|
||||
}
|
||||
}
|
||||
|
||||
return new RootObjectBuilderResult(rootObjectCollection, results);
|
||||
}
|
||||
|
||||
|
||||
@@ -92,8 +92,6 @@ public static class ServiceRegistration
|
||||
serviceCollection.AddScoped<RhinoColorBaker>();
|
||||
serviceCollection.AddScoped<RhinoColorUnpacker>();
|
||||
|
||||
serviceCollection.AddScoped<RhinoViewUnpacker>();
|
||||
|
||||
serviceCollection.AddScoped<PropertiesExtractor>();
|
||||
serviceCollection.AddScoped<RevitMappingResolver>();
|
||||
|
||||
|
||||
+1
-2
@@ -25,7 +25,6 @@
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Extensions\AttributeExtensions.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\DataObjectInstanceGrouper.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Properties\PropertiesExtractor.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\RhinoViewUnpacker.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\RhinoIdleManager.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\RhinoLayerHelper.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\RhinoObjectHelper.cs" />
|
||||
@@ -64,4 +63,4 @@
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Plugin\Speckle.Connectors.RhinoCommand.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Plugin\Speckle.Connectors.RhinoPlugin.cs" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
+1
-1
@@ -17,4 +17,4 @@
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToSpeckle\Helpers\EtabsShellSectionResolver.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToSpeckle\TopLevel\EtabsObjectToSpeckleConverter.cs" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
+11
-31
@@ -1,14 +1,10 @@
|
||||
using Speckle.Converter.Navisworks.Services;
|
||||
using Speckle.Converter.Navisworks.Settings;
|
||||
using Speckle.Converter.Navisworks.Settings;
|
||||
using Speckle.Converters.Common;
|
||||
using static Speckle.Converter.Navisworks.Helpers.PropertyHelpers;
|
||||
|
||||
namespace Speckle.Converter.Navisworks.ToSpeckle;
|
||||
|
||||
public class PropertySetsExtractor(
|
||||
IConverterSettingsStore<NavisworksConversionSettings> settingsStore,
|
||||
IPropertyConverter propertyConverter
|
||||
)
|
||||
public class PropertySetsExtractor(IConverterSettingsStore<NavisworksConversionSettings> settingsStore)
|
||||
{
|
||||
internal Dictionary<string, object?>? GetPropertySets(NAV.ModelItem modelItem)
|
||||
{
|
||||
@@ -22,30 +18,9 @@ public class PropertySetsExtractor(
|
||||
return propertyDictionary;
|
||||
}
|
||||
|
||||
private static NAV.Units GetModelUnits(NAV.ModelItem modelItem)
|
||||
{
|
||||
NAV.ModelItem? ancestor = modelItem;
|
||||
while (ancestor != null && !ancestor.HasModel)
|
||||
{
|
||||
ancestor = ancestor.Parent;
|
||||
}
|
||||
|
||||
return ancestor != null ? ancestor.Model.Units : NAV.Units.Meters;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extracts property sets from a NAV.ModelItem and adds them to a dictionary,
|
||||
/// PropertySets are specific to the host application source appended to Navisworks and therefore
|
||||
/// arbitrary in nature.
|
||||
/// </summary>
|
||||
/// <param name="modelItem">The NAV.ModelItem from which property sets are extracted.</param>
|
||||
/// <returns>A dictionary containing property sets of the modelItem.</returns>
|
||||
private Dictionary<string, object?> ExtractPropertySets(NAV.ModelItem modelItem)
|
||||
{
|
||||
var propertySetDictionary = new Dictionary<string, object?>();
|
||||
var modelUnits = GetModelUnits(modelItem);
|
||||
|
||||
propertyConverter.Reset();
|
||||
|
||||
foreach (var propertyCategory in modelItem.PropertyCategories)
|
||||
{
|
||||
@@ -58,18 +33,23 @@ public class PropertySetsExtractor(
|
||||
|
||||
foreach (var property in propertyCategory.Properties)
|
||||
{
|
||||
var sanitizedName = SanitizePropertyName(property.DisplayName);
|
||||
var propertyValue = propertyConverter.ConvertPropertyValue(property.Value, modelUnits, property.DisplayName);
|
||||
string sanitizedName = SanitizePropertyName(property.DisplayName);
|
||||
var propertyValue = ConvertPropertyValue(property.Value, settingsStore.Current.Derived.SpeckleUnits);
|
||||
|
||||
if (propertyValue != null)
|
||||
{
|
||||
propertySet[sanitizedName] = propertyValue;
|
||||
}
|
||||
}
|
||||
|
||||
if (propertySet.Count > 0)
|
||||
if (propertySet.Count <= 0)
|
||||
{
|
||||
propertySetDictionary[SanitizePropertyName(propertyCategory.DisplayName)] = propertySet;
|
||||
continue;
|
||||
}
|
||||
|
||||
string categoryName = SanitizePropertyName(propertyCategory.DisplayName);
|
||||
|
||||
propertySetDictionary[categoryName] = propertySet;
|
||||
}
|
||||
|
||||
return propertySetDictionary;
|
||||
|
||||
+11
-13
@@ -1,10 +1,8 @@
|
||||
using Speckle.Converter.Navisworks.Services;
|
||||
using Speckle.InterfaceGenerator;
|
||||
using static Speckle.Converter.Navisworks.Helpers.PropertyHelpers;
|
||||
|
||||
namespace Speckle.Converter.Navisworks.ToSpeckle;
|
||||
|
||||
[GenerateAutoInterface]
|
||||
public class RevitBuiltInCategoryExtractor(IPropertyConverter converter) : IRevitBuiltInCategoryExtractor
|
||||
public sealed class RevitBuiltInCategoryExtractor
|
||||
{
|
||||
private const int ANCESTOR_AND_SELF_COUNT = 4; // It seems like this is the maximum depth found needed in practice
|
||||
private const string REVIT_CAT_GROUP = "LcRevitData_Element";
|
||||
@@ -15,28 +13,28 @@ public class RevitBuiltInCategoryExtractor(IPropertyConverter converter) : IRevi
|
||||
/// Attempts to map a Navisworks/Revit display category from the given model item or its ancestors
|
||||
/// to a known Revit built-in category constant (e.g., "OST_Walls").
|
||||
/// </summary>
|
||||
public bool TryGetBuiltInCategory(NAV.ModelItem item, out string mapped, int maxDepth = ANCESTOR_AND_SELF_COUNT)
|
||||
internal static bool TryGetBuiltInCategory(
|
||||
NAV.ModelItem item,
|
||||
out string mapped,
|
||||
int maxDepth = ANCESTOR_AND_SELF_COUNT
|
||||
)
|
||||
{
|
||||
mapped = string.Empty;
|
||||
|
||||
// Find the category VariantData up the hierarchy
|
||||
// Look up the category value, starting at this item and walking up to maxDepth ancestors
|
||||
var v = FindRevitCategoryInHierarchy(item, maxDepth);
|
||||
if (v is null)
|
||||
if (v == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
converter.Reset();
|
||||
|
||||
// Convert using per-object model units and current UI units
|
||||
var nameObj = converter.ConvertPropertyValue(v, item.Model.Units, item.DisplayName);
|
||||
var name = nameObj?.ToString();
|
||||
var name = ConvertPropertyValue(v, "")?.ToString();
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
name = name!.Trim();
|
||||
name = name?.Trim();
|
||||
|
||||
// Map display name to OST_* built-in category constant
|
||||
var builtInCategory = DisplayNameToRevitBuiltInCategory(name);
|
||||
|
||||
+2
-3
@@ -11,8 +11,7 @@ public class HierarchicalPropertyHandler(
|
||||
PropertySetsExtractor propertySetsExtractor,
|
||||
ModelPropertiesExtractor modelPropertiesExtractor,
|
||||
ClassPropertiesExtractor classPropertiesExtractor,
|
||||
IConverterSettingsStore<NavisworksConversionSettings> settingsStore,
|
||||
IRevitBuiltInCategoryExtractor revitCategoryExtractor
|
||||
IConverterSettingsStore<NavisworksConversionSettings> settingsStore
|
||||
) : BasePropertyHandler(propertySetsExtractor, modelPropertiesExtractor)
|
||||
{
|
||||
private static string PseudoClassPropertiesKey => "_pseudoClassProperties";
|
||||
@@ -23,7 +22,7 @@ public class HierarchicalPropertyHandler(
|
||||
var propertyDict = classPropertiesExtractor.GetClassProperties(modelItem) ?? [];
|
||||
|
||||
// Interop-lite mapping for Revit built-in categories
|
||||
if (_mapRevit && revitCategoryExtractor.TryGetBuiltInCategory(modelItem, out var builtInCategory))
|
||||
if (_mapRevit && RevitBuiltInCategoryExtractor.TryGetBuiltInCategory(modelItem, out var builtInCategory))
|
||||
{
|
||||
PropertyHelpers.AddPropertyIfNotNullOrEmpty(
|
||||
propertyDict,
|
||||
|
||||
+1
-28
@@ -1,4 +1,4 @@
|
||||
using System.Globalization;
|
||||
using System.Globalization;
|
||||
using System.Text.RegularExpressions;
|
||||
using Speckle.Objects.Geometry;
|
||||
|
||||
@@ -8,9 +8,6 @@ public static class PropertyHelpers
|
||||
{
|
||||
private static readonly HashSet<string> s_excludedCategories = ["Geometry", "Metadata"];
|
||||
|
||||
/// <summary>
|
||||
/// Adds a property to an object (either a Base object or a Dictionary) if the value is not null or empty.
|
||||
/// </summary>
|
||||
private static readonly Dictionary<NAV.VariantDataType, Func<NAV.VariantData, string, dynamic?>> s_typeHandlers =
|
||||
new()
|
||||
{
|
||||
@@ -95,27 +92,3 @@ public static class PropertyHelpers
|
||||
internal static bool ShouldSkipCategory(NAV.PropertyCategory propertyCategory) =>
|
||||
s_excludedCategories.Contains(propertyCategory.DisplayName);
|
||||
}
|
||||
|
||||
internal static class UnitLabels
|
||||
{
|
||||
internal static string Linear(NAV.Units u) =>
|
||||
u switch
|
||||
{
|
||||
NAV.Units.Kilometers => "Kilometers",
|
||||
NAV.Units.Meters => "Metres",
|
||||
NAV.Units.Centimeters => "Centimeters",
|
||||
NAV.Units.Millimeters => "Millimeters",
|
||||
NAV.Units.Micrometers => "Micrometers",
|
||||
NAV.Units.Miles => "Miles",
|
||||
NAV.Units.Yards => "Yards",
|
||||
NAV.Units.Feet => "Feet",
|
||||
NAV.Units.Inches => "Inches",
|
||||
NAV.Units.Mils => "Mils",
|
||||
NAV.Units.Microinches => "Microinches",
|
||||
_ => "Metres"
|
||||
};
|
||||
|
||||
internal static string Area(NAV.Units u) => $"Square {Linear(u).ToLower()}";
|
||||
|
||||
public static string Volume(NAV.Units u) => $"Cubic {Linear(u).ToLower()}";
|
||||
}
|
||||
|
||||
+17
@@ -103,6 +103,23 @@ public class InstanceStoreManager(ILogger<InstanceStoreManager> logger)
|
||||
// Add instance definition proxy if not exists
|
||||
if (!InstanceDefinitionProxiesStore.Contains(definitionId))
|
||||
{
|
||||
if (geometry.applicationId == null)
|
||||
{
|
||||
_logger.LogWarning(
|
||||
"Cannot create instance definition proxy - geometry.id is null for FragmentId={FragmentId}",
|
||||
fragmentId
|
||||
);
|
||||
var result = geometryAdded || proxyAdded;
|
||||
_logger.LogDebug(
|
||||
"AddSharedGeometry completed: FragmentId={FragmentId}, Result={Result}, GeometryAdded={GeometryAdded}, ProxyAdded={ProxyAdded}",
|
||||
fragmentId,
|
||||
result,
|
||||
geometryAdded,
|
||||
proxyAdded
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
var definitionProxy = new InstanceDefinitionProxy
|
||||
{
|
||||
applicationId = definitionId,
|
||||
|
||||
-91
@@ -1,91 +0,0 @@
|
||||
using System.Globalization;
|
||||
using Speckle.Converter.Navisworks.Helpers;
|
||||
using Speckle.InterfaceGenerator;
|
||||
|
||||
namespace Speckle.Converter.Navisworks.Services;
|
||||
|
||||
[GenerateAutoInterface]
|
||||
public class PropertyConverter(IUiUnitsCache uiUnitsCache) : IPropertyConverter
|
||||
{
|
||||
public void Reset() => uiUnitsCache.Reset();
|
||||
|
||||
public object? ConvertPropertyValue(NAV.VariantData? value, NAV.Units modelUnits, string propDisplayName) =>
|
||||
value == null
|
||||
? null
|
||||
: _handlers.TryGetValue(value.DataType, out var f)
|
||||
? f(value, (modelUnits, propDisplayName))
|
||||
: value.DataType is NAV.VariantDataType.None or NAV.VariantDataType.Point2D
|
||||
? null
|
||||
: value.ToString();
|
||||
|
||||
private readonly Dictionary<
|
||||
NAV.VariantDataType,
|
||||
Func<NAV.VariantData, (NAV.Units model, string name), object?>
|
||||
> _handlers =
|
||||
new()
|
||||
{
|
||||
{ NAV.VariantDataType.Boolean, (v, _) => v.ToBoolean() },
|
||||
{ NAV.VariantDataType.DisplayString, (v, _) => v.ToDisplayString() },
|
||||
{ NAV.VariantDataType.IdentifierString, (v, _) => v.ToIdentifierString() },
|
||||
{ NAV.VariantDataType.Int32, (v, _) => v.ToInt32() },
|
||||
{ NAV.VariantDataType.Double, (v, _) => v.ToDouble() },
|
||||
// Angle as dictionary with units
|
||||
{ NAV.VariantDataType.DoubleAngle, (v, t) => NumObj(t.name, v.ToDoubleAngle(), "Degrees") },
|
||||
// Length → dictionary in UI units
|
||||
{
|
||||
NAV.VariantDataType.DoubleLength,
|
||||
(v, t) =>
|
||||
{
|
||||
var uiUnits = uiUnitsCache.Ensure();
|
||||
|
||||
var k = NAV.UnitConversion.ScaleFactor(t.model, uiUnits);
|
||||
return NumObj(t.name, v.ToDoubleLength() * k, UnitLabels.Linear(uiUnits));
|
||||
}
|
||||
},
|
||||
// Area → dictionary in UI units^2
|
||||
{
|
||||
NAV.VariantDataType.DoubleArea,
|
||||
(v, t) =>
|
||||
{
|
||||
var uiUnits = uiUnitsCache.Ensure();
|
||||
var k = NAV.UnitConversion.ScaleFactor(t.model, uiUnits);
|
||||
k *= k;
|
||||
return NumObj(t.name, v.ToDoubleArea() * k, UnitLabels.Area(uiUnits));
|
||||
}
|
||||
},
|
||||
// Volume → dictionary in UI units^3
|
||||
{
|
||||
NAV.VariantDataType.DoubleVolume,
|
||||
(v, t) =>
|
||||
{
|
||||
var uiUnits = uiUnitsCache.Ensure();
|
||||
var k = NAV.UnitConversion.ScaleFactor(t.model, uiUnits);
|
||||
k = k * k * k;
|
||||
return NumObj(t.name, v.ToDoubleVolume() * k, UnitLabels.Volume(uiUnits));
|
||||
}
|
||||
},
|
||||
{ NAV.VariantDataType.DateTime, (v, _) => v.ToDateTime().ToString(CultureInfo.InvariantCulture) },
|
||||
{ NAV.VariantDataType.NamedConstant, (v, _) => v.ToNamedConstant().DisplayName },
|
||||
{ NAV.VariantDataType.None, (_, _) => null },
|
||||
{ NAV.VariantDataType.Point2D, (_, _) => null },
|
||||
{
|
||||
NAV.VariantDataType.Point3D,
|
||||
(v, t) =>
|
||||
{
|
||||
var uiUnits = uiUnitsCache.Ensure();
|
||||
var k = NAV.UnitConversion.ScaleFactor(t.model, uiUnits);
|
||||
var p = v.ToPoint3D();
|
||||
|
||||
return new Speckle.Objects.Geometry.Point(p.X * k, p.Y * k, p.Z * k, UnitLabels.Linear(uiUnits));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private static Dictionary<string, object> NumObj(string name, double value, string units) =>
|
||||
new()
|
||||
{
|
||||
["name"] = name,
|
||||
["value"] = value,
|
||||
["units"] = units
|
||||
};
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
using Autodesk.Navisworks.Api.Interop;
|
||||
using Speckle.InterfaceGenerator;
|
||||
using static Autodesk.Navisworks.Api.Interop.LcUOption;
|
||||
|
||||
namespace Speckle.Converter.Navisworks.Services;
|
||||
|
||||
[GenerateAutoInterface]
|
||||
public class UiUnitsCache : IUiUnitsCache
|
||||
{
|
||||
private NAV.Units? _ui;
|
||||
|
||||
public NAV.Units Ensure()
|
||||
{
|
||||
if (_ui.HasValue)
|
||||
{
|
||||
return _ui.Value;
|
||||
}
|
||||
|
||||
UiUnitsUtil.TryGetUiLinearUnits(out var ui);
|
||||
_ui = ui;
|
||||
return _ui.Value;
|
||||
}
|
||||
|
||||
public void Reset() => _ui = null;
|
||||
}
|
||||
|
||||
public static class UiUnitsUtil
|
||||
{
|
||||
// disp_units: 0=linear_format
|
||||
public static bool TryGetUiLinearUnits(out NAV.Units uiUnits)
|
||||
{
|
||||
using var opt = new LcUOptionLock();
|
||||
var root = GetRoot(opt);
|
||||
var disp = root.GetSubOptions("interface").GetSubOptions("disp_units");
|
||||
|
||||
int code = -1;
|
||||
|
||||
using var v = new NAV.VariantData();
|
||||
disp.GetValue(0, v);
|
||||
var s = v.ToString();
|
||||
var colon = s.LastIndexOf(':');
|
||||
var open = s.IndexOf('(', colon + 1);
|
||||
if (colon >= 0 && open > colon && !int.TryParse(s.Substring(colon + 1, open - colon - 1), out code))
|
||||
{
|
||||
code = -1;
|
||||
}
|
||||
|
||||
uiUnits = code switch
|
||||
{
|
||||
0 => NAV.Units.Kilometers,
|
||||
1 => NAV.Units.Meters,
|
||||
2 => NAV.Units.Centimeters,
|
||||
3 => NAV.Units.Millimeters,
|
||||
4 => NAV.Units.Micrometers,
|
||||
5 => NAV.Units.Miles,
|
||||
6 => NAV.Units.Miles,
|
||||
7 => NAV.Units.Yards,
|
||||
8 => NAV.Units.Yards,
|
||||
9 => NAV.Units.Feet,
|
||||
10 => NAV.Units.Feet,
|
||||
11 => NAV.Units.Feet,
|
||||
12 => NAV.Units.Inches,
|
||||
13 => NAV.Units.Inches,
|
||||
14 => NAV.Units.Mils,
|
||||
15 => NAV.Units.Microinches,
|
||||
_ => NAV.Units.Meters
|
||||
};
|
||||
|
||||
return code >= 0;
|
||||
}
|
||||
}
|
||||
-2
@@ -30,8 +30,6 @@
|
||||
<Compile Include="$(MSBuildThisFileDirectory)GlobalUsing.cs"/>
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Helpers\GeometryHelpers.cs"/>
|
||||
<Compile Include="$(MSBuildThisFileDirectory)PathConstants.cs"/>
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Services\PropertyConversion.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Services\UIUnits.cs"/>
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Services\SharedGeometryStores.cs"/>
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Services\InstanceStoreManager.cs"/>
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToSpeckle\Raw\GeometryToSpeckleConverter.cs"/>
|
||||
|
||||
+37
-26
@@ -10,6 +10,7 @@ using Speckle.Converter.Navisworks.Services;
|
||||
using Speckle.Converter.Navisworks.Settings;
|
||||
using Speckle.DoubleNumerics;
|
||||
using Speckle.Objects.Geometry;
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Models.Instances;
|
||||
using ComApiBridge = Autodesk.Navisworks.Api.ComApi.ComApiBridge;
|
||||
|
||||
@@ -41,7 +42,7 @@ public class GeometryToSpeckleConverter(
|
||||
private readonly ILogger<GeometryToSpeckleConverter> _logger =
|
||||
logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
|
||||
internal List<SSM.Base> Convert(NAV.ModelItem modelItem)
|
||||
internal List<Base> Convert(NAV.ModelItem modelItem)
|
||||
{
|
||||
if (modelItem == null)
|
||||
{
|
||||
@@ -58,10 +59,6 @@ public class GeometryToSpeckleConverter(
|
||||
{
|
||||
var fragmentStack = new Stack<InwOaFragment3>();
|
||||
var paths = comSelection.Paths();
|
||||
if (paths == null)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
try
|
||||
{
|
||||
if (paths.Count > 0)
|
||||
@@ -93,7 +90,10 @@ public class GeometryToSpeckleConverter(
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.ReleaseComObject(paths);
|
||||
if (paths != null)
|
||||
{
|
||||
Marshal.ReleaseComObject(paths);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
@@ -127,7 +127,7 @@ public class GeometryToSpeckleConverter(
|
||||
}
|
||||
}
|
||||
|
||||
private List<SSM.Base> ProcessSharedGeometry(InwSelectionPathsColl paths, Stack<InwOaFragment3> fragmentStack)
|
||||
private List<Base> ProcessSharedGeometry(InwSelectionPathsColl paths, Stack<InwOaFragment3> fragmentStack)
|
||||
{
|
||||
var fragmentId = GenerateFragmentId(paths);
|
||||
|
||||
@@ -153,12 +153,20 @@ public class GeometryToSpeckleConverter(
|
||||
|
||||
var baseGeometry = ExtractUntransformedGeometry(fragmentStack);
|
||||
|
||||
return baseGeometry == null || !_instanceStoreManager.AddSharedGeometry(fragmentId, baseGeometry)
|
||||
? ProcessFragments(fragmentStack, paths) // default false flag for isSingleObject
|
||||
: CreateInstanceReference(fragmentId, paths);
|
||||
if (baseGeometry == null)
|
||||
{
|
||||
return ProcessFragments(fragmentStack, paths);
|
||||
}
|
||||
|
||||
if (!_instanceStoreManager.AddSharedGeometry(fragmentId, baseGeometry))
|
||||
{
|
||||
return ProcessFragments(fragmentStack, paths);
|
||||
}
|
||||
|
||||
return CreateInstanceReference(fragmentId, paths);
|
||||
}
|
||||
|
||||
private List<SSM.Base> ProcessFragments(
|
||||
private List<Base> ProcessFragments(
|
||||
Stack<InwOaFragment3> fragmentStack,
|
||||
InwSelectionPathsColl paths,
|
||||
bool isSingleObject = false
|
||||
@@ -183,7 +191,7 @@ public class GeometryToSpeckleConverter(
|
||||
int fragmentCount;
|
||||
try
|
||||
{
|
||||
fragmentCount = fragmentsForCount?.Count ?? 0;
|
||||
fragmentCount = fragmentsForCount.Count;
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -196,8 +204,14 @@ public class GeometryToSpeckleConverter(
|
||||
double[] makeNoChange = s_identityTransform;
|
||||
double[] transformMatrix = ConvertArrayToDouble(matrixArray);
|
||||
|
||||
processor.LocalToWorldTransformation =
|
||||
isSingleObject || fragmentCount == 1 ? transformMatrix : (IEnumerable<double>)makeNoChange;
|
||||
if (isSingleObject || fragmentCount == 1)
|
||||
{
|
||||
processor.LocalToWorldTransformation = transformMatrix;
|
||||
}
|
||||
else
|
||||
{
|
||||
processor.LocalToWorldTransformation = makeNoChange;
|
||||
}
|
||||
|
||||
fragment.GenerateSimplePrimitives(nwEVertexProperty.eNORMAL, processor);
|
||||
}
|
||||
@@ -213,9 +227,9 @@ public class GeometryToSpeckleConverter(
|
||||
&& path.ArrayData is Array pathData
|
||||
&& AreFragmentPathsEqual(fragmentPathData, pathData);
|
||||
|
||||
private List<SSM.Base> ProcessGeometries(List<PrimitiveProcessor> processors)
|
||||
private List<Base> ProcessGeometries(List<PrimitiveProcessor> processors)
|
||||
{
|
||||
var baseGeometries = new List<SSM.Base>();
|
||||
var baseGeometries = new List<Base>();
|
||||
|
||||
foreach (var processor in processors)
|
||||
{
|
||||
@@ -359,12 +373,7 @@ public class GeometryToSpeckleConverter(
|
||||
RankException => "Array rank mismatch",
|
||||
_ => "Error"
|
||||
};
|
||||
_logger.LogDebug(
|
||||
ex,
|
||||
"{ErrorType} processing fragment {FragmentIndex}, trying simple enumeration",
|
||||
errorType,
|
||||
fragmentIndex
|
||||
);
|
||||
_logger.LogDebug(ex, "{ErrorType} processing fragment {FragmentIndex}, trying simple enumeration", errorType, fragmentIndex);
|
||||
|
||||
var fragmentHash = TrySimpleArrayEnumeration(pathData, fragmentIndex);
|
||||
if (!string.IsNullOrEmpty(fragmentHash))
|
||||
@@ -395,8 +404,10 @@ public class GeometryToSpeckleConverter(
|
||||
var fragmentId = HashRawData(rawData);
|
||||
return fragmentId;
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
else
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
catch (Exception ex) when (ex is COMException or InvalidCastException or IndexOutOfRangeException)
|
||||
{
|
||||
@@ -459,7 +470,7 @@ public class GeometryToSpeckleConverter(
|
||||
return BitConverter.ToString(hashBytes).Replace("-", "").ToLowerInvariant();
|
||||
}
|
||||
|
||||
private SSM.Base? ExtractUntransformedGeometry(Stack<InwOaFragment3> fragmentStack)
|
||||
private Base? ExtractUntransformedGeometry(Stack<InwOaFragment3> fragmentStack)
|
||||
{
|
||||
var processor = new PrimitiveProcessor(_isUpright);
|
||||
|
||||
@@ -473,7 +484,7 @@ public class GeometryToSpeckleConverter(
|
||||
return processor.Triangles.Count > 0 ? CreateMesh(processor.Triangles) : null;
|
||||
}
|
||||
|
||||
private List<SSM.Base> CreateInstanceReference(string fragmentId, InwSelectionPathsColl paths)
|
||||
private List<Base> CreateInstanceReference(string fragmentId, InwSelectionPathsColl paths)
|
||||
{
|
||||
var transform = ExtractInstanceTransform(paths);
|
||||
|
||||
|
||||
@@ -74,18 +74,15 @@ public sealed class DisplayValueExtractor
|
||||
}
|
||||
return areaDisplay;
|
||||
|
||||
// Rebar: get_Geometry() returns null, use GetTransformedCenterlineCurves/GetFullGeometryForView + apply reference point transform
|
||||
// NOTE: this is only for Rebar and not AreaReinforcement, RebarInSystem
|
||||
// AreaReinforcement and RebarInSystem pass through GetGeometryDisplayValue which get DisplayValues as per hostApp
|
||||
// Rebar elements need special handling as get_Geometry() doesn't work properly
|
||||
// We either represent them as centerlines or as solids based on settings
|
||||
case DB.Structure.Rebar rebar:
|
||||
return _converterSettings.Current.SendRebarsAsVolumetric
|
||||
? GetRebarVolumetricDisplayValue(rebar)
|
||||
: GetRebarCenterlineDisplayValue(rebar);
|
||||
|
||||
// AreaReinforcement/PathReinforcement get_Geometry() returns curves in document coordinates
|
||||
// unlike Rebar which needs reference point transform applied, these are already correct
|
||||
case DB.Structure.AreaReinforcement:
|
||||
case DB.Structure.PathReinforcement:
|
||||
return GetAreaReinforcementDisplayValue(element);
|
||||
|
||||
// handle specific types of objects with multiple parts or children
|
||||
// curtain and stacked walls should have their display values in their children
|
||||
case DB.Wall wall:
|
||||
@@ -110,7 +107,7 @@ public sealed class DisplayValueExtractor
|
||||
using DB.Transform? compoundTransform =
|
||||
localToDocument is not null && documentToWorld is not null
|
||||
? documentToWorld.Multiply(localToDocument)
|
||||
: localToDocument;
|
||||
: localToDocument; // don't want to accidentally dispose of the ReferencePointTransform
|
||||
|
||||
DB.Transform? localToWorld = compoundTransform ?? documentToWorld;
|
||||
|
||||
@@ -194,51 +191,20 @@ public sealed class DisplayValueExtractor
|
||||
);
|
||||
}
|
||||
|
||||
// transform curves, polylines, and points to world coordinates before conversion.
|
||||
// Unlike meshes/solids which are proxified with transform matrices, these geometry
|
||||
// types must have their final world coordinates baked directly into their geometry.
|
||||
// add rest of geometry (always without transform)
|
||||
foreach (var curve in collections.Curves)
|
||||
{
|
||||
if (localToWorld is not null)
|
||||
{
|
||||
using var transformedCurve = curve.CreateTransformed(localToWorld);
|
||||
displayValue.Add(DisplayValueResult.WithoutTransform(GetCurveDisplayValue(transformedCurve)));
|
||||
}
|
||||
else
|
||||
{
|
||||
displayValue.Add(DisplayValueResult.WithoutTransform(GetCurveDisplayValue(curve)));
|
||||
}
|
||||
displayValue.Add(DisplayValueResult.WithoutTransform(GetCurveDisplayValue(curve)));
|
||||
}
|
||||
|
||||
// Note: Creating new polyline/point instances for transformation isn't ideal for perf,
|
||||
// but Revit API doesn't provide in-place transform methods. Trade-off is acceptable since
|
||||
// family instances typically don't have massive numbers of raw polylines/points in their geometry.
|
||||
foreach (var polyline in collections.Polylines)
|
||||
{
|
||||
if (localToWorld is not null)
|
||||
{
|
||||
var coords = polyline.GetCoordinates();
|
||||
var transformedCoords = coords.Select(coord => localToWorld.OfPoint(coord)).ToList();
|
||||
using var transformedPolyline = DB.PolyLine.Create(transformedCoords);
|
||||
displayValue.Add(DisplayValueResult.WithoutTransform(_polylineConverter.Convert(transformedPolyline)));
|
||||
}
|
||||
else
|
||||
{
|
||||
displayValue.Add(DisplayValueResult.WithoutTransform(_polylineConverter.Convert(polyline)));
|
||||
}
|
||||
displayValue.Add(DisplayValueResult.WithoutTransform(_polylineConverter.Convert(polyline)));
|
||||
}
|
||||
|
||||
foreach (var point in collections.Points)
|
||||
{
|
||||
if (localToWorld is not null)
|
||||
{
|
||||
using var transformedPoint = DB.Point.Create(localToWorld.OfPoint(point.Coord));
|
||||
displayValue.Add(DisplayValueResult.WithoutTransform(_pointConverter.Convert(transformedPoint)));
|
||||
}
|
||||
else
|
||||
{
|
||||
displayValue.Add(DisplayValueResult.WithoutTransform(_pointConverter.Convert(point)));
|
||||
}
|
||||
displayValue.Add(DisplayValueResult.WithoutTransform(_pointConverter.Convert(point)));
|
||||
}
|
||||
|
||||
return displayValue;
|
||||
@@ -380,8 +346,7 @@ public sealed class DisplayValueExtractor
|
||||
collections.Meshes.Add(mesh);
|
||||
break;
|
||||
|
||||
// curves, polylines, and points are transformed to world space in ProcessGeometryCollections,
|
||||
// not here, because they cannot be proxified like meshes.
|
||||
//Note, we're not applying transforms to curves/polylines/points because ProcessGeometryCollections expects them in world coordinates
|
||||
case DB.Curve curve:
|
||||
collections.Curves.Add(curve);
|
||||
break;
|
||||
@@ -426,7 +391,7 @@ public sealed class DisplayValueExtractor
|
||||
return false; // exit fast on a potential hot path
|
||||
}
|
||||
|
||||
DB.GraphicsStyle? bjk; // ask ogu why this variable is named like this
|
||||
DB.GraphicsStyle? bjk = null; // ask ogu why this variable is named like this
|
||||
|
||||
if (!_graphicStyleCache.ContainsKey(geomObj.GraphicsStyleId.ToString().NotNull()))
|
||||
{
|
||||
@@ -550,9 +515,8 @@ public sealed class DisplayValueExtractor
|
||||
|
||||
if (geometryElements != null)
|
||||
{
|
||||
DB.Transform? documentToWorld = _converterSettings.Current.ReferencePointTransform?.Inverse;
|
||||
SortGeometry(rebar, collections, geometryElements, null);
|
||||
return ProcessGeometryCollections(rebar, collections, documentToWorld);
|
||||
return ProcessGeometryCollections(rebar, collections, null);
|
||||
}
|
||||
|
||||
// Return empty list if no geometry is found - imo not critical
|
||||
@@ -593,49 +557,24 @@ public sealed class DisplayValueExtractor
|
||||
)
|
||||
);
|
||||
}
|
||||
DB.Transform? documentToWorld = _converterSettings.Current.ReferencePointTransform?.Inverse;
|
||||
|
||||
List<DisplayValueResult> displayValue = new();
|
||||
foreach (var curve in curves)
|
||||
{
|
||||
if (documentToWorld is not null)
|
||||
{
|
||||
using var transformedCurve = curve.CreateTransformed(documentToWorld);
|
||||
displayValue.Add(DisplayValueResult.WithoutTransform(GetCurveDisplayValue(transformedCurve)));
|
||||
}
|
||||
else
|
||||
{
|
||||
displayValue.Add(DisplayValueResult.WithoutTransform(GetCurveDisplayValue(curve)));
|
||||
}
|
||||
displayValue.Add(DisplayValueResult.WithoutTransform(GetCurveDisplayValue(curve)));
|
||||
}
|
||||
|
||||
return displayValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets display value for AreaReinforcement and PathReinforcement.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// These elements' get_Geometry() returns curves already in document coordinates.
|
||||
/// Unlike Rebar.GetTransformedCenterlineCurves() which requires reference point transform,
|
||||
/// these curves should not be transformed - they're already in the correct space.
|
||||
/// </remarks>
|
||||
private List<DisplayValueResult> GetAreaReinforcementDisplayValue(DB.Element element)
|
||||
{
|
||||
var collections = GetSortedGeometryFromElement(element, null, null);
|
||||
// pass null for transform - curves are already in correct document coordinates
|
||||
return ProcessGeometryCollections(element, collections, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents sorted collections of different geometry types extracted from an element.
|
||||
/// Used to pass multiple geometry collections as a single parameter to improve code readability
|
||||
/// and reduce the risk of parameter ordering errors.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <see cref="Solids"/> and <see cref="Meshes"/> are transformed to local coordinate space in SortGeometry.
|
||||
/// <see cref="Curves"/>, <see cref="Polylines"/>, and <see cref="Points"/> remain in their original coordinate space
|
||||
/// and are transformed to world space during processing in ProcessGeometryCollections.
|
||||
/// <see cref="Solids"/> and <see cref="Meshes"/> potentially in local coordinate space.
|
||||
/// For now, <see cref="Curves"/>, <see cref="Polylines"/>, <see cref="Points"/> will always be in world space
|
||||
/// </remarks>
|
||||
private sealed record GeometryCollections
|
||||
{
|
||||
|
||||
+1
-2
@@ -81,9 +81,8 @@
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToSpeckle\TopLevel\RevitElementTopLevelConverterToSpeckle.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Helpers\ParameterValueExtractor.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Helpers\RevitContext.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToSpeckle\TopLevel\View3DTopLevelConverterToSpeckle.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)\Helpers\SendSelection.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Services\RevitToSpeckleUnitConverter.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)RevitRootToSpeckleConverter.cs" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
-11
@@ -88,17 +88,6 @@ public class ClassPropertiesExtractor
|
||||
{
|
||||
elementProperties.Add("spaceId", familyInstance.Space.Id.ToString());
|
||||
}
|
||||
|
||||
// get toRoom and fromRoom for FamilyInstance elements with those properties (e.g. Doors)
|
||||
if (familyInstance.ToRoom is not null)
|
||||
{
|
||||
elementProperties.Add("toRoomId", familyInstance.ToRoom.Id.ToString());
|
||||
}
|
||||
|
||||
if (familyInstance.FromRoom is not null)
|
||||
{
|
||||
elementProperties.Add("fromRoomId", familyInstance.FromRoom.Id.ToString());
|
||||
}
|
||||
}
|
||||
catch (Exception e) when (!e.IsFatal())
|
||||
{
|
||||
|
||||
-49
@@ -1,49 +0,0 @@
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Converters.Common.Objects;
|
||||
using Speckle.Objects.Other;
|
||||
using Speckle.Sdk.Common.Exceptions;
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Converters.RevitShared.ToSpeckle;
|
||||
|
||||
[NameAndRankValue(typeof(DB.View3D), 0)]
|
||||
public class View3DTopLevelConverterToSpeckle : IToSpeckleTopLevelConverter, ITypedConverter<DB.View3D, Camera>
|
||||
{
|
||||
private readonly ITypedConverter<DB.XYZ, SOG.Point> _xyzToPointConverter;
|
||||
private readonly ITypedConverter<DB.XYZ, SOG.Vector> _xyzToVectorConverter;
|
||||
|
||||
public View3DTopLevelConverterToSpeckle(
|
||||
ITypedConverter<DB.XYZ, SOG.Point> xyzToPointConverter,
|
||||
ITypedConverter<DB.XYZ, SOG.Vector> xyzToVectorConverter
|
||||
)
|
||||
{
|
||||
_xyzToPointConverter = xyzToPointConverter;
|
||||
_xyzToVectorConverter = xyzToVectorConverter;
|
||||
}
|
||||
|
||||
public Base Convert(object target) => Convert((DB.View3D)target);
|
||||
|
||||
public Camera Convert(DB.View3D target)
|
||||
{
|
||||
if (!target.IsPerspective)
|
||||
{
|
||||
throw new ConversionException("Non-Perspective views are not supported");
|
||||
}
|
||||
|
||||
// some views have null origin, not sure why
|
||||
if (target.Origin == null)
|
||||
{
|
||||
throw new ConversionException("Views with no origin are not supported");
|
||||
}
|
||||
|
||||
DB.ViewOrientation3D orientation = target.GetSavedOrientation();
|
||||
|
||||
return new()
|
||||
{
|
||||
name = target.Title,
|
||||
position = _xyzToPointConverter.Convert(target.Origin),
|
||||
forward = _xyzToVectorConverter.Convert(orientation.ForwardDirection),
|
||||
up = _xyzToVectorConverter.Convert(orientation.UpDirection)
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -7,11 +7,11 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'net48'">
|
||||
<PackageReference Include="RhinoCommon" IncludeAssets="compile; build" PrivateAssets="all" VersionOverride="8.9.24194.18121"/>
|
||||
<PackageReference Include="RhinoCommon" IncludeAssets="compile; build" PrivateAssets="all" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' != 'net48'">
|
||||
<PackageReference Include="RhinoCommon" IncludeAssets="compile; build" PrivateAssets="all" />
|
||||
<PackageReference Include="RhinoCommon" IncludeAssets="compile; build" PrivateAssets="all" VersionOverride="8.21.25188.17001"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -346,9 +346,9 @@
|
||||
},
|
||||
"RhinoCommon": {
|
||||
"type": "Direct",
|
||||
"requested": "[8.25.25328.11001, )",
|
||||
"resolved": "8.25.25328.11001",
|
||||
"contentHash": "PDKR9GwqyUXUkTulV4J0dzDIf/aWqSJkL7nkS8ReAx8xhnt/+RQpE8gTjOSCmkSU2tjG6WzclowbTxwMTU7VAA==",
|
||||
"requested": "[8.21.25188.17001, )",
|
||||
"resolved": "8.21.25188.17001",
|
||||
"contentHash": "Wo6JXheyDBvilyIwDY6xZCQJC4170jzAlTSlMgh8yokUY+vYyCl4KJVXZofIOynNt/xx5wLqb2On5gZZekXR6w==",
|
||||
"dependencies": {
|
||||
"System.Drawing.Common": "7.0.0"
|
||||
}
|
||||
|
||||
@@ -2,5 +2,4 @@ global using RG = Rhino.Geometry;
|
||||
global using SA = Speckle.Objects.Annotation;
|
||||
global using SO = Speckle.Objects.Other;
|
||||
global using SOG = Speckle.Objects.Geometry;
|
||||
global using SOO = Speckle.Objects.Other;
|
||||
global using SOP = Speckle.Objects.Primitive;
|
||||
|
||||
+4
-5
@@ -74,13 +74,12 @@ public class DataObjectConverter
|
||||
{
|
||||
var resultPairs = new List<(RG.GeometryBase, Base)>();
|
||||
|
||||
// check if displayValue contains ANY InstanceProxies - register for special handling
|
||||
var instanceProxies = target.displayValue.OfType<InstanceProxy>().ToList();
|
||||
if (instanceProxies.Count > 0)
|
||||
// check if display value contains InstanceProxies - register for special handling
|
||||
if (target.displayValue.Count > 0 && target.displayValue[0] is InstanceProxy)
|
||||
{
|
||||
var instanceProxies = target.displayValue.Cast<InstanceProxy>().ToList();
|
||||
_dataObjectInstanceRegistry.Register(target.applicationId ?? target.id.NotNull(), target, instanceProxies);
|
||||
|
||||
return resultPairs;
|
||||
return resultPairs; // empty - will be handled by instance baker
|
||||
}
|
||||
|
||||
// normal display value conversion
|
||||
|
||||
-41
@@ -1,41 +0,0 @@
|
||||
using Rhino.DocObjects;
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Converters.Common.Objects;
|
||||
using Speckle.Sdk.Common.Exceptions;
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Converters.Rhino.ToSpeckle.TopLevel;
|
||||
|
||||
[NameAndRankValue(typeof(ViewInfo), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)]
|
||||
public class ViewInfoToSpeckleTopLevelConverter : IToSpeckleTopLevelConverter, ITypedConverter<ViewInfo, SOO.Camera>
|
||||
{
|
||||
private readonly ITypedConverter<RG.Point3d, SOG.Point> _pointConverter;
|
||||
private readonly ITypedConverter<RG.Vector3d, SOG.Vector> _vectorConverter;
|
||||
|
||||
public ViewInfoToSpeckleTopLevelConverter(
|
||||
ITypedConverter<RG.Point3d, SOG.Point> pointConverter,
|
||||
ITypedConverter<RG.Vector3d, SOG.Vector> vectorConverter
|
||||
)
|
||||
{
|
||||
_pointConverter = pointConverter;
|
||||
_vectorConverter = vectorConverter;
|
||||
}
|
||||
|
||||
public Base Convert(object target) => Convert((ViewInfo)target);
|
||||
|
||||
public SOO.Camera Convert(ViewInfo target)
|
||||
{
|
||||
if (target.Viewport.IsParallelProjection)
|
||||
{
|
||||
throw new ConversionException("Parallel projection views are not supported.");
|
||||
}
|
||||
|
||||
return new()
|
||||
{
|
||||
name = target.Name,
|
||||
position = _pointConverter.Convert(target.Viewport.CameraLocation),
|
||||
up = _vectorConverter.Convert(target.Viewport.CameraY),
|
||||
forward = _vectorConverter.Convert(target.Viewport.CameraZ),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
using Speckle.Connectors.DUI.Bridge;
|
||||
using Speckle.Newtonsoft.Json;
|
||||
using Speckle.Sdk.Credentials;
|
||||
using Speckle.Sdk.SQLite;
|
||||
|
||||
namespace Speckle.Connectors.DUI.Bindings;
|
||||
|
||||
@@ -11,25 +9,14 @@ public class AccountBinding : IBinding
|
||||
public IBrowserBridge Parent { get; }
|
||||
|
||||
private readonly IAccountManager _accountManager;
|
||||
private readonly ISqLiteJsonCacheManager _jsonCacheManager;
|
||||
|
||||
public AccountBinding(
|
||||
IBrowserBridge bridge,
|
||||
IAccountManager accountManager,
|
||||
ISqLiteJsonCacheManagerFactory sqLiteJsonCacheManagerFactory
|
||||
)
|
||||
public AccountBinding(IBrowserBridge bridge, IAccountManager accountManager)
|
||||
{
|
||||
Parent = bridge;
|
||||
_accountManager = accountManager;
|
||||
_jsonCacheManager = sqLiteJsonCacheManagerFactory.CreateForUser("Accounts");
|
||||
}
|
||||
|
||||
public Account[] GetAccounts() => _accountManager.GetAccounts().ToArray();
|
||||
|
||||
public void AddAccount(string accountId, Account account)
|
||||
{
|
||||
_jsonCacheManager.SaveObject(accountId, JsonConvert.SerializeObject(account));
|
||||
}
|
||||
|
||||
public void RemoveAccount(string accountId) => _accountManager.RemoveAccount(accountId);
|
||||
}
|
||||
|
||||
@@ -28,10 +28,10 @@
|
||||
<PackageVersion Include="NUnit.Analyzers" Version="4.2.0" />
|
||||
<PackageVersion Include="NUnit3TestAdapter" version="4.6.0" />
|
||||
<PackageVersion Include="Revit.Async" Version="2.1.1" />
|
||||
<PackageVersion Include="RhinoCommon" Version="8.25.25328.11001" />
|
||||
<PackageVersion Include="RhinoCommon" Version="8.9.24194.18121" />
|
||||
<PackageVersion Include="Rhino.Inside" Version="8.0.7-beta" />
|
||||
<PackageVersion Include="Grasshopper" Version="8.9.24194.18121" />
|
||||
<PackageVersion Include="RhinoWindows" Version="8.25.25328.11001" />
|
||||
<PackageVersion Include="RhinoWindows" Version="8.9.24194.18121" />
|
||||
<PackageVersion Include="Semver" Version="3.0.0" />
|
||||
<PackageVersion Include="Serilog.Formatting.Compact" Version="3.0.0" />
|
||||
<PackageVersion Include="Speckle.CSI.API" Version="2.4.0" />
|
||||
|
||||
@@ -8,7 +8,6 @@ using Speckle.Connectors.Logging;
|
||||
using Speckle.Importers.JobProcessor.Domain;
|
||||
using Speckle.Importers.JobProcessor.JobHandlers;
|
||||
using Speckle.Importers.JobProcessor.JobQueue;
|
||||
using Speckle.Sdk;
|
||||
using Speckle.Sdk.Api;
|
||||
using Speckle.Sdk.Api.GraphQL.Inputs;
|
||||
using Speckle.Sdk.Common;
|
||||
@@ -30,24 +29,6 @@ internal sealed class JobProcessorInstance(
|
||||
private static readonly TimeSpan s_idleTimeout = TimeSpan.FromSeconds(1);
|
||||
|
||||
protected override async Task ExecuteAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
await RunJobProcessorLoop(cancellationToken);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, "Job Processor crashed");
|
||||
Environment.Exit(1); //This is the only reliable way I've managed to figure out how to get windows services retry policy to actually kick in (see https://github.com/dotnet/runtime/issues/67146)
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RunJobProcessorLoop(CancellationToken cancellationToken)
|
||||
{
|
||||
await using var connection = await repository.SetupConnection(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
@@ -199,11 +180,6 @@ internal sealed class JobProcessorInstance(
|
||||
{
|
||||
logger.LogError(new AggregateException(ex, ex2), "Failed to report failure status");
|
||||
await repository.ReturnJobToQueued(connection, job.Id, cancellationToken);
|
||||
|
||||
if (ex2.IsFatal())
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
|
||||
@@ -10,14 +10,27 @@ namespace Speckle.Importers.JobProcessor;
|
||||
|
||||
public static class Program
|
||||
{
|
||||
public static async Task Main(string[] args)
|
||||
public static async Task<int> Main(string[] args)
|
||||
{
|
||||
// Dapper doesn't understand how to handle JSON deserialization, so we need to tell it what types can be deserialzied
|
||||
SqlMapper.AddTypeHandler(new JsonHandler<FileimportPayload>());
|
||||
|
||||
var host = ConfigureAppHost(args);
|
||||
|
||||
var backgroundServiceTasks = host
|
||||
.Services.GetServices<IHostedService>()
|
||||
.OfType<BackgroundService>()
|
||||
.Select(s => s.ExecuteTask);
|
||||
|
||||
await host.RunAsync();
|
||||
|
||||
if (backgroundServiceTasks.Any(t => t?.IsFaulted == true))
|
||||
{
|
||||
//https://github.com/dotnet/runtime/issues/67146
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static IHost ConfigureAppHost(string[] args)
|
||||
@@ -37,4 +50,15 @@ public static class Program
|
||||
|
||||
return builder.Build();
|
||||
}
|
||||
|
||||
private static void ConfigureTopLevelLogs(ILogger logger)
|
||||
{
|
||||
TaskScheduler.UnobservedTaskException += (_, eventArgs) =>
|
||||
logger.LogCritical(eventArgs.Exception, "Unobserved Task Exception");
|
||||
|
||||
AppDomain.CurrentDomain.UnhandledException += (sender, eventArgs) =>
|
||||
{
|
||||
logger.LogCritical(eventArgs.ExceptionObject as Exception, "Unhandled exception occurred in the AppDomain");
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="RhinoCommon" IncludeAssets="compile; build" PrivateAssets="all" VersionOverride="8.25.25328.11001"/>
|
||||
<PackageReference Include="Grasshopper" IncludeAssets="compile; build" PrivateAssets="all" VersionOverride="8.25.25328.11001"/>
|
||||
<PackageReference Include="RhinoWindows" IncludeAssets="compile; build" PrivateAssets="all" VersionOverride="8.25.25328.11001"/>
|
||||
<PackageReference Include="RhinoCommon" IncludeAssets="compile; build" PrivateAssets="all" VersionOverride="8.24.25281.15001"/>
|
||||
<PackageReference Include="Grasshopper" IncludeAssets="compile; build" PrivateAssets="all" VersionOverride="8.24.25281.15001"/>
|
||||
<PackageReference Include="RhinoWindows" IncludeAssets="compile; build" PrivateAssets="all" VersionOverride="8.24.25281.15001"/>
|
||||
<PackageReference Include="Rhino.Inside" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
"net8.0-windows7.0": {
|
||||
"Grasshopper": {
|
||||
"type": "Direct",
|
||||
"requested": "[8.25.25328.11001, )",
|
||||
"resolved": "8.25.25328.11001",
|
||||
"contentHash": "1uFL9pmgCEbYFd1b3JFGHaLEjQuRgFsiRHmP5u73mdEMdO6+5F/pNV1dQZr3YGLDovUmgrc+jmpsme7wr/J01A==",
|
||||
"requested": "[8.24.25281.15001, )",
|
||||
"resolved": "8.24.25281.15001",
|
||||
"contentHash": "TEtB8nElTvhMJctLhv8UC1v6jscYdTgsoRQIr31ewGZr6cpgGtQTBmUk26/9ZvQxXCgOp7Y4EZdcEZkmqCm1SQ==",
|
||||
"dependencies": {
|
||||
"RhinoCommon": "[8.25.25328.11001]"
|
||||
"RhinoCommon": "[8.24.25281.15001]"
|
||||
}
|
||||
},
|
||||
"Microsoft.NETFramework.ReferenceAssemblies": {
|
||||
@@ -48,20 +48,20 @@
|
||||
},
|
||||
"RhinoCommon": {
|
||||
"type": "Direct",
|
||||
"requested": "[8.25.25328.11001, )",
|
||||
"resolved": "8.25.25328.11001",
|
||||
"contentHash": "PDKR9GwqyUXUkTulV4J0dzDIf/aWqSJkL7nkS8ReAx8xhnt/+RQpE8gTjOSCmkSU2tjG6WzclowbTxwMTU7VAA==",
|
||||
"requested": "[8.24.25281.15001, )",
|
||||
"resolved": "8.24.25281.15001",
|
||||
"contentHash": "K8dd7DJvEUUYHpwkyyxr/ojK3e8swlE0STeyG+ulVWkWNHK6gIRDxMYCwB7kNyHHMgpr/vpQlMgR3SVD1GoMTA==",
|
||||
"dependencies": {
|
||||
"System.Drawing.Common": "7.0.0"
|
||||
}
|
||||
},
|
||||
"RhinoWindows": {
|
||||
"type": "Direct",
|
||||
"requested": "[8.25.25328.11001, )",
|
||||
"resolved": "8.25.25328.11001",
|
||||
"contentHash": "I/+++piwtYTue+iAAQqcMF5QlontqwNnC7Leyhiv2FiF8JpAl6K44ZsJqB7ZEUC6ns0LDfa3mbFzQwUfHwYumQ==",
|
||||
"requested": "[8.24.25281.15001, )",
|
||||
"resolved": "8.24.25281.15001",
|
||||
"contentHash": "JbG98P80Hskpomzx1xhqFoz2WmVnhjda0noQyZE+dE678ZKmw+O3i/iIjaN5jydXvTu/fZXRzQmEDL7M7Ura8g==",
|
||||
"dependencies": {
|
||||
"RhinoCommon": "[8.25.25328.11001]"
|
||||
"RhinoCommon": "[8.24.25281.15001]"
|
||||
}
|
||||
},
|
||||
"Speckle.InterfaceGenerator": {
|
||||
|
||||
@@ -4,5 +4,4 @@ public static class RootKeys
|
||||
{
|
||||
public const string ANALYSIS_RESULTS = "analysisResults";
|
||||
public const string REFERENCE_POINT_TRANSFORM = "referencePointTransform";
|
||||
public const string VIEW = "views";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user