Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6d7ffb5e63 | |||
| 1a0467cb1a | |||
| b0743bc0b3 | |||
| b8f6ce61ea |
+1
-1
@@ -452,7 +452,7 @@ public sealed class ReceiveComponentWorker : WorkerInstance<ReceiveAsyncComponen
|
||||
var unpackedRoot = scope.Get<RootObjectUnpacker>().Unpack(Root);
|
||||
|
||||
// separate atomic objects from block instances
|
||||
var (atomicObjects, blockInstances) = scope
|
||||
var (atomicObjects, blockInstances, atomicObjectsWithInstances) = scope
|
||||
.Get<RootObjectUnpacker>()
|
||||
.SplitAtomicObjectsAndInstances(unpackedRoot.ObjectsToConvert);
|
||||
|
||||
|
||||
+1
-1
@@ -183,7 +183,7 @@ public class ReceiveComponent : SpeckleTaskCapableComponent<ReceiveComponentInpu
|
||||
var unpackedRoot = rootObjectUnpacker.Unpack(root);
|
||||
|
||||
// split atomic objects from block components before conversion
|
||||
var (atomicObjects, blockInstances) = rootObjectUnpacker.SplitAtomicObjectsAndInstances(
|
||||
var (atomicObjects, blockInstances, atomicObjectsWithInstances) = rootObjectUnpacker.SplitAtomicObjectsAndInstances(
|
||||
unpackedRoot.ObjectsToConvert
|
||||
);
|
||||
|
||||
|
||||
+131
-7
@@ -16,6 +16,7 @@ using Speckle.Sdk.Common.Exceptions;
|
||||
using Speckle.Sdk.Logging;
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Models.Collections;
|
||||
using Speckle.Sdk.Models.GraphTraversal;
|
||||
using Speckle.Sdk.Models.Instances;
|
||||
|
||||
namespace Speckle.Connectors.Rhino.Operations.Receive;
|
||||
@@ -64,9 +65,9 @@ public class RhinoHostObjectBuilder : IHostObjectBuilder
|
||||
_conversionHandler = conversionHandler;
|
||||
}
|
||||
|
||||
#pragma warning disable CA1506
|
||||
#pragma warning disable CA1506, CA1502
|
||||
public Task<HostObjectBuilderResult> Build(
|
||||
#pragma warning restore CA1506
|
||||
#pragma warning restore CA1506, CA1502
|
||||
Base rootObject,
|
||||
string projectName,
|
||||
string modelName,
|
||||
@@ -85,13 +86,38 @@ public class RhinoHostObjectBuilder : IHostObjectBuilder
|
||||
var unpackedRoot = _rootObjectUnpacker.Unpack(rootObject);
|
||||
|
||||
// 2 - Split atomic objects and instance components with their path
|
||||
var (atomicObjectsWithoutInstanceComponentsForConverter, instanceComponents) =
|
||||
var (atomicObjectsWithoutInstanceComponents, instanceComponents, atomicObjectsWithInstanceComponents) =
|
||||
_rootObjectUnpacker.SplitAtomicObjectsAndInstances(unpackedRoot.ObjectsToConvert);
|
||||
|
||||
var atomicObjectsWithoutInstanceComponentsWithPath = _layerBaker.GetAtomicObjectsWithPath(
|
||||
atomicObjectsWithoutInstanceComponentsForConverter
|
||||
atomicObjectsWithoutInstanceComponents
|
||||
);
|
||||
var instanceComponentsWithPath = _layerBaker.GetInstanceComponentsWithPath(instanceComponents);
|
||||
var atomicObjectsWithInstanceComponentsWithPath = _layerBaker.GetAtomicObjectsWithPath(
|
||||
atomicObjectsWithInstanceComponents
|
||||
);
|
||||
|
||||
// 2.0 - POC!! this could be done with a traversal helper!!
|
||||
// create a map between atomic objects with display instances, and the display instances of that atomic object (index)
|
||||
Dictionary<int, List<int>> displayInstanceIdMap = new();
|
||||
for (int i = 0; i < atomicObjectsWithInstanceComponents.Count; i++)
|
||||
{
|
||||
TraversalContext atomicObjectWithInstanceDisplay = atomicObjectsWithInstanceComponents.ElementAt(i);
|
||||
for (int j = 0; j < instanceComponents.Count; j++)
|
||||
{
|
||||
if (atomicObjectWithInstanceDisplay == instanceComponents.ElementAt(j).Parent)
|
||||
{
|
||||
if (displayInstanceIdMap.TryGetValue(i, out List<int>? value))
|
||||
{
|
||||
value.Add(j);
|
||||
}
|
||||
else
|
||||
{
|
||||
displayInstanceIdMap.Add(i, new List<int>() { j });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2.1 - these are not captured by traversal, so we need to re-add them here
|
||||
if (unpackedRoot.DefinitionProxies != null && unpackedRoot.DefinitionProxies.Count > 0)
|
||||
@@ -99,6 +125,7 @@ public class RhinoHostObjectBuilder : IHostObjectBuilder
|
||||
var transformed = unpackedRoot.DefinitionProxies.Select(proxy =>
|
||||
(Array.Empty<Collection>(), proxy as IInstanceComponent)
|
||||
);
|
||||
|
||||
instanceComponentsWithPath.AddRange(transformed);
|
||||
}
|
||||
|
||||
@@ -129,13 +156,14 @@ public class RhinoHostObjectBuilder : IHostObjectBuilder
|
||||
{
|
||||
using var layerNoDraw = new DisableRedrawScope(_converterSettings.Current.Document.Views);
|
||||
var paths = atomicObjectsWithoutInstanceComponentsWithPath.Select(t => t.path).ToList();
|
||||
paths.AddRange(atomicObjectsWithInstanceComponentsWithPath.Select(t => t.path));
|
||||
paths.AddRange(instanceComponentsWithPath.Select(t => t.path));
|
||||
_layerBaker.CreateAllLayersForReceive(paths, baseLayerName);
|
||||
})
|
||||
.Wait(cancellationToken);
|
||||
}
|
||||
|
||||
// 5 - Convert atomic objects
|
||||
// 5 - Convert atomic objects without instances first!!
|
||||
var bakedObjectIds = new HashSet<string>();
|
||||
Dictionary<string, IReadOnlyCollection<string>> applicationIdMap = new(); // This map is used in converting blocks in stage 2. keeps track of original app id => resulting new app ids post baking
|
||||
HashSet<ReceiveConversionResult> conversionResults = new();
|
||||
@@ -146,7 +174,7 @@ public class RhinoHostObjectBuilder : IHostObjectBuilder
|
||||
foreach (var (path, obj) in atomicObjectsWithoutInstanceComponentsWithPath)
|
||||
{
|
||||
onOperationProgressed.Report(
|
||||
new("Converting objects", (double)++count / atomicObjectsWithoutInstanceComponentsForConverter.Count)
|
||||
new("Converting objects", (double)++count / atomicObjectsWithoutInstanceComponents.Count)
|
||||
);
|
||||
var ex = _conversionHandler.TryConvert(() =>
|
||||
{
|
||||
@@ -182,7 +210,7 @@ public class RhinoHostObjectBuilder : IHostObjectBuilder
|
||||
}
|
||||
else if (result is List<(GeometryBase, Base)> fallbackConversionResult) // one to many fallback conversion
|
||||
{
|
||||
var guids = BakeObjectsAsFallbackGroup(fallbackConversionResult, obj, atts, baseLayerName);
|
||||
var guids = BakeObjectsAsFallbackGroup(fallbackConversionResult, new(), obj, atts, baseLayerName);
|
||||
conversionIds.AddRange(guids.Select(id => id.ToString()));
|
||||
}
|
||||
|
||||
@@ -217,8 +245,10 @@ public class RhinoHostObjectBuilder : IHostObjectBuilder
|
||||
}
|
||||
|
||||
// 6 - Convert instances
|
||||
IReadOnlyCollection<string> createdDisplayIds;
|
||||
using (var _ = _activityFactory.Start("Converting instances"))
|
||||
{
|
||||
// bake atomic instances
|
||||
var (createdInstanceIds, consumedObjectIds, instanceConversionResults) = _instanceBaker.BakeInstances(
|
||||
instanceComponentsWithPath,
|
||||
applicationIdMap,
|
||||
@@ -226,12 +256,90 @@ public class RhinoHostObjectBuilder : IHostObjectBuilder
|
||||
onOperationProgressed
|
||||
);
|
||||
|
||||
createdDisplayIds = createdInstanceIds;
|
||||
|
||||
bakedObjectIds.RemoveWhere(id => consumedObjectIds.Contains(id)); // remove all objects that have been "consumed"
|
||||
bakedObjectIds.UnionWith(createdInstanceIds); // add instance ids
|
||||
conversionResults.RemoveWhere(result => result.ResultId != null && consumedObjectIds.Contains(result.ResultId)); // remove all conversion results for atomic objects that have been consumed (POC: not that cool, but prevents problems on object highlighting)
|
||||
conversionResults.UnionWith(instanceConversionResults); // add instance conversion results to our list
|
||||
}
|
||||
|
||||
// 7 - Convert atomic objects with instance components
|
||||
using (var _ = _activityFactory.Start("Converting objects"))
|
||||
{
|
||||
for (int i = 0; i < atomicObjectsWithInstanceComponentsWithPath.Count; i++)
|
||||
{
|
||||
var (path, obj) = atomicObjectsWithInstanceComponentsWithPath.ElementAt(i);
|
||||
onOperationProgressed.Report(
|
||||
new("Converting objects", (double)++count / atomicObjectsWithInstanceComponentsWithPath.Count)
|
||||
);
|
||||
var ex = _conversionHandler.TryConvert(() =>
|
||||
{
|
||||
// 0: get pre-created layer from cache in layer baker
|
||||
int layerIndex = _layerBaker.GetLayerIndex(path, baseLayerName);
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
// 1: create object attributes for baking
|
||||
ObjectAttributes atts = obj.GetAttributes();
|
||||
atts.LayerIndex = layerIndex;
|
||||
|
||||
// 2: convert
|
||||
var result = _converter.Convert(obj);
|
||||
|
||||
// 3: bake
|
||||
var conversionIds = new List<string>();
|
||||
List<string> createdInstanceIds = new();
|
||||
if (result is List<(GeometryBase, Base)> fallbackConversionResult) // one to many fallback conversion, this should be the only type of non instance atomic object with instances in display value
|
||||
{
|
||||
// add display instances here
|
||||
if (displayInstanceIdMap.TryGetValue(i, out List<int>? value))
|
||||
{
|
||||
foreach (var instanceIndex in value)
|
||||
{
|
||||
createdInstanceIds.Add(createdDisplayIds.ElementAt(instanceIndex));
|
||||
}
|
||||
}
|
||||
|
||||
var guids = BakeObjectsAsFallbackGroup(
|
||||
fallbackConversionResult,
|
||||
createdInstanceIds,
|
||||
obj,
|
||||
atts,
|
||||
baseLayerName
|
||||
);
|
||||
conversionIds.AddRange(guids.Select(id => id.ToString()));
|
||||
}
|
||||
|
||||
if (conversionIds.Count == 0)
|
||||
{
|
||||
// TODO: add this condition to report object - same as in autocad
|
||||
throw new ConversionException("Object did not convert to any native geometry");
|
||||
}
|
||||
|
||||
// 4: log
|
||||
var id = conversionIds[0]; // this is group id if it is a one to many conversion, otherwise id of object itself
|
||||
conversionResults.Add(new(Status.SUCCESS, obj, id, result.GetType().ToString()));
|
||||
if (conversionIds.Count == 1)
|
||||
{
|
||||
bakedObjectIds.Add(id);
|
||||
}
|
||||
else
|
||||
{
|
||||
// first item always a group id if it is a one-to-many,
|
||||
// we do not want to deal with later groups and its sub elements. It causes a huge issue on performance.
|
||||
bakedObjectIds.AddRange(conversionIds.Skip(1));
|
||||
}
|
||||
|
||||
// 5: populate app id map
|
||||
applicationIdMap[obj.applicationId ?? obj.id.NotNull()] = conversionIds;
|
||||
});
|
||||
if (ex is not null)
|
||||
{
|
||||
conversionResults.Add(new(Status.ERROR, obj, null, null, ex));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 7 - Create groups
|
||||
if (unpackedRoot.GroupProxies is not null)
|
||||
{
|
||||
@@ -334,6 +442,7 @@ public class RhinoHostObjectBuilder : IHostObjectBuilder
|
||||
|
||||
private List<Guid> BakeObjectsAsFallbackGroup(
|
||||
IEnumerable<(GeometryBase, Base)> fallbackConversionResult,
|
||||
List<string> createdInstanceIds,
|
||||
Base originatingObject,
|
||||
ObjectAttributes atts,
|
||||
string baseLayerName
|
||||
@@ -349,6 +458,21 @@ public class RhinoHostObjectBuilder : IHostObjectBuilder
|
||||
objCount++;
|
||||
}
|
||||
|
||||
// now add already created instances
|
||||
foreach (string instanceId in createdInstanceIds)
|
||||
{
|
||||
var instanceGuid = new Guid(instanceId);
|
||||
var docObject = _converterSettings.Current.Document.Objects.FindId(instanceGuid);
|
||||
if (docObject is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
docObject.Attributes = atts;
|
||||
docObject.CommitChanges();
|
||||
objCount++;
|
||||
objectIds.Add(instanceGuid);
|
||||
}
|
||||
|
||||
// only create groups if we really need to, ie if the fallback conversion result count is bigger than one.
|
||||
if (objCount > 1)
|
||||
{
|
||||
|
||||
@@ -4,6 +4,7 @@ using Speckle.Objects.Data;
|
||||
using Speckle.Sdk.Common;
|
||||
using Speckle.Sdk.Common.Exceptions;
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Models.Instances;
|
||||
|
||||
namespace Speckle.Converters.Rhino.ToHost.TopLevel;
|
||||
|
||||
@@ -83,6 +84,7 @@ public class DataObjectConverter
|
||||
SOG.Polyline polyline => new() { _polylineConverter.Convert(polyline) },
|
||||
SOG.Region region => new() { _regionConverter.Convert(region) },
|
||||
SOG.SubDX subd => _subdConverter.Convert(subd),
|
||||
InstanceProxy => [],
|
||||
_ => throw new ConversionException($"Found unsupported fallback geometry: {b.GetType()}")
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Speckle.Objects.Data;
|
||||
using Speckle.Objects.Data;
|
||||
using Speckle.Objects.Other;
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Models.Collections;
|
||||
@@ -48,13 +48,21 @@ public class RootObjectUnpacker
|
||||
public IReadOnlyCollection<LevelProxy>? TryGetLevelProxies(Base root) =>
|
||||
TryGetProxies<LevelProxy>(root, ProxyKeys.LEVEL);
|
||||
|
||||
/// <summary>
|
||||
/// POC!!! super hacky, we need an official way to differentiate between atomic instances, display value instances, atomic instance definitions, and display value instance definitions.
|
||||
/// This should be reflected on the root collection.
|
||||
/// </summary>
|
||||
/// <param name="objectsToSplit"></param>
|
||||
/// <returns></returns>
|
||||
public (
|
||||
IReadOnlyCollection<TraversalContext> atomicObjects,
|
||||
IReadOnlyCollection<TraversalContext> instanceComponents
|
||||
IReadOnlyCollection<TraversalContext> atomicNonInstanceObjects,
|
||||
IReadOnlyCollection<TraversalContext> instanceComponents,
|
||||
IReadOnlyCollection<TraversalContext> atomicNonInstanceObjectsWithInstanceComponents
|
||||
) SplitAtomicObjectsAndInstances(IEnumerable<TraversalContext> objectsToSplit)
|
||||
{
|
||||
List<TraversalContext> atomicObjects = [];
|
||||
List<TraversalContext> atomicObjectsWithoutInstanceComponents = [];
|
||||
List<TraversalContext> instanceComponents = [];
|
||||
List<TraversalContext> atomicObjectsWithInstanceComponents = [];
|
||||
foreach (TraversalContext tc in objectsToSplit)
|
||||
{
|
||||
if (tc.Current is IInstanceComponent)
|
||||
@@ -63,21 +71,35 @@ public class RootObjectUnpacker
|
||||
}
|
||||
else
|
||||
{
|
||||
atomicObjects.Add(tc);
|
||||
}
|
||||
|
||||
if (tc.Current is DataObject dataObject)
|
||||
{
|
||||
foreach (var displayValue in dataObject.displayValue)
|
||||
if (tc.Current is DataObject dataObject)
|
||||
{
|
||||
if (displayValue is IInstanceComponent)
|
||||
bool containsInstanceComponents = false;
|
||||
foreach (var displayValue in dataObject.displayValue)
|
||||
{
|
||||
instanceComponents.Add(new TraversalContext(displayValue, parent: tc));
|
||||
if (displayValue is IInstanceComponent)
|
||||
{
|
||||
containsInstanceComponents = true;
|
||||
instanceComponents.Add(new TraversalContext(displayValue, parent: tc));
|
||||
}
|
||||
}
|
||||
|
||||
if (containsInstanceComponents)
|
||||
{
|
||||
atomicObjectsWithInstanceComponents.Add(tc);
|
||||
}
|
||||
else
|
||||
{
|
||||
atomicObjectsWithoutInstanceComponents.Add(tc);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
atomicObjectsWithoutInstanceComponents.Add(tc);
|
||||
}
|
||||
}
|
||||
}
|
||||
return (atomicObjects, instanceComponents);
|
||||
|
||||
return (atomicObjectsWithoutInstanceComponents, instanceComponents, atomicObjectsWithInstanceComponents);
|
||||
}
|
||||
|
||||
private IReadOnlyCollection<T>? TryGetProxies<T>(Base root, string key) =>
|
||||
|
||||
Reference in New Issue
Block a user