332ab25e64
* Use Sets and Freezing to make conversions faster * fmt * move class to own file
132 lines
4.4 KiB
C#
132 lines
4.4 KiB
C#
using Speckle.DoubleNumerics;
|
|
using Speckle.InterfaceGenerator;
|
|
using Speckle.Sdk.Dependencies;
|
|
using Speckle.Sdk.Models;
|
|
using Speckle.Sdk.Models.GraphTraversal;
|
|
using Speckle.Sdk.Models.Instances;
|
|
|
|
namespace Speckle.Connectors.Common.Instances;
|
|
|
|
/// <summary>
|
|
/// Utility for the connectors that doesn't support instancing.
|
|
/// An atomic object can be seen on only one definition. We search matrices from bottom to top for each atomic object.
|
|
/// DI scope -> (InstancePerLifetimeScope).
|
|
/// </summary>
|
|
[GenerateAutoInterface]
|
|
public class LocalToGlobalUnpacker : ILocalToGlobalUnpacker
|
|
{
|
|
public IReadOnlyCollection<LocalToGlobalMap> Unpack(
|
|
IReadOnlyCollection<InstanceDefinitionProxy>? instanceDefinitionProxies,
|
|
IReadOnlyCollection<TraversalContext> objectsToUnpack
|
|
)
|
|
{
|
|
var localToGlobalMaps = new HashSet<LocalToGlobalMap>();
|
|
|
|
var instanceProxies = new HashSet<(TraversalContext tc, InstanceProxy obj)>();
|
|
var atomicObjects = new HashSet<(TraversalContext tc, Base obj)>();
|
|
|
|
// 1. Split up the instances from the non-instances
|
|
foreach (TraversalContext objectToUnpack in objectsToUnpack)
|
|
{
|
|
if (objectToUnpack.Current is InstanceProxy instanceProxy)
|
|
{
|
|
instanceProxies.Add((objectToUnpack, instanceProxy));
|
|
}
|
|
else
|
|
{
|
|
atomicObjects.Add((objectToUnpack, objectToUnpack.Current));
|
|
}
|
|
}
|
|
|
|
var objectsAtAbsolute = new HashSet<(TraversalContext tc, Base obj)>();
|
|
var objectsAtRelative = new HashSet<(TraversalContext tc, Base obj)>();
|
|
|
|
// 2. Split atomic objects that in absolute or relative coordinates.
|
|
foreach ((TraversalContext tc, Base atomicObject) in atomicObjects)
|
|
{
|
|
// If we have an application id, and it's part of an instance -> go through the relative process
|
|
if (
|
|
atomicObject.applicationId is not null
|
|
&& instanceDefinitionProxies is not null
|
|
&& instanceDefinitionProxies.Any(idp => idp.objects.Contains(atomicObject.applicationId))
|
|
)
|
|
{
|
|
objectsAtRelative.Add((tc, atomicObject)); // to use in Instances only
|
|
continue;
|
|
}
|
|
|
|
// Otherwise we're on the absolute track
|
|
objectsAtAbsolute.Add((tc, atomicObject));
|
|
}
|
|
|
|
// 3. Add atomic objects that on absolute coordinates that doesn't need a transformation.
|
|
foreach ((TraversalContext tc, Base objectAtAbsolute) in objectsAtAbsolute)
|
|
{
|
|
localToGlobalMaps.Add(new LocalToGlobalMap(tc, objectAtAbsolute, new HashSet<Matrix4x4>()));
|
|
}
|
|
|
|
// 4. Return if no logic around instancing.
|
|
if (instanceDefinitionProxies is null)
|
|
{
|
|
return localToGlobalMaps;
|
|
}
|
|
|
|
// 5. Iterate each object that in relative coordinates.
|
|
foreach (var objectAtRelative in objectsAtRelative)
|
|
{
|
|
UnpackMatrix(
|
|
instanceDefinitionProxies,
|
|
instanceProxies,
|
|
objectAtRelative,
|
|
objectAtRelative,
|
|
new HashSet<Matrix4x4>(),
|
|
localToGlobalMaps
|
|
);
|
|
}
|
|
|
|
return localToGlobalMaps.Where(ltgm => ltgm.AtomicObject is not InstanceProxy).Freeze();
|
|
}
|
|
|
|
private void UnpackMatrix(
|
|
IReadOnlyCollection<InstanceDefinitionProxy> instanceDefinitionProxies,
|
|
HashSet<(TraversalContext tc, InstanceProxy instanceProxy)> instanceProxies,
|
|
(TraversalContext tc, Base obj) objectAtRelative,
|
|
(TraversalContext tc, Base obj) searchForDefinition,
|
|
HashSet<Matrix4x4> matrices,
|
|
HashSet<LocalToGlobalMap> localToGlobalMaps
|
|
)
|
|
{
|
|
if (searchForDefinition.obj.applicationId is null)
|
|
{
|
|
return;
|
|
}
|
|
InstanceDefinitionProxy? definitionProxy = instanceDefinitionProxies.FirstOrDefault(idp =>
|
|
idp.objects.Contains(searchForDefinition.obj.applicationId)
|
|
);
|
|
if (definitionProxy is null)
|
|
{
|
|
localToGlobalMaps.Add(
|
|
new LocalToGlobalMap(
|
|
new TraversalContext(objectAtRelative.obj, objectAtRelative.tc.PropName, objectAtRelative.tc.Parent),
|
|
objectAtRelative.obj,
|
|
matrices
|
|
)
|
|
);
|
|
return;
|
|
}
|
|
var instances = instanceProxies.Where(ic => ic.instanceProxy.definitionId == definitionProxy.applicationId);
|
|
foreach (var instance in instances)
|
|
{
|
|
HashSet<Matrix4x4> newMatrices = [.. matrices, instance.instanceProxy.transform]; // Do not mutate the list!
|
|
UnpackMatrix(
|
|
instanceDefinitionProxies,
|
|
instanceProxies,
|
|
objectAtRelative,
|
|
instance,
|
|
newMatrices,
|
|
localToGlobalMaps
|
|
);
|
|
}
|
|
}
|
|
}
|