Skip to content

Commit

Permalink
Dim/acad groups (#61)
Browse files Browse the repository at this point in the history
* wip group creation in acad

* in progress changes from fieldtrip

* feat(dui3): extracts group from acad

* fix(dui3): post dev merge edits
  • Loading branch information
didimitrie authored Jul 23, 2024
1 parent 722d091 commit ba26ee0
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ public static void LoadSend(SpeckleContainerBuilder builder)
IInstanceObjectsManager<AutocadRootObject, List<Entity>>,
InstanceObjectsManager<AutocadRootObject, List<Entity>>
>();
builder.AddScoped<AutocadGroupUnpacker>();
builder.AddScoped<AutocadInstanceObjectManager>();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using Autodesk.AutoCAD.DatabaseServices;
using Speckle.Connectors.Autocad.Operations.Send;
using Speckle.Core.Models.Instances;

namespace Speckle.Connectors.Autocad.HostApp;

/// <summary>
/// Unpacks a selection of atomic objects into their groups. This resource expects to be injected "fresh" in each send operation (scoped lifetime).
/// </summary>
public class AutocadGroupUnpacker
{
public List<GroupProxy> UnpackGroups(IEnumerable<AutocadRootObject> autocadObjects)
{
var groupProxies = new Dictionary<string, GroupProxy>();

using var transaction = Application.DocumentManager.CurrentDocument.Database.TransactionManager.StartTransaction();

foreach (var (dbObject, applicationId) in autocadObjects)
{
var persistentReactorIds = dbObject.GetPersistentReactorIds();
foreach (ObjectId oReactorId in persistentReactorIds)
{
var obj = transaction.GetObject(oReactorId, OpenMode.ForRead);
if (obj is not Group group)
{
continue;
}
var groupAppId = group.Handle.ToString();
if (groupProxies.TryGetValue(groupAppId, out GroupProxy groupProxy))
{
groupProxy.objects.Add(applicationId);
}
else
{
groupProxies[groupAppId] = new()
{
applicationId = groupAppId,
name = group.Name,
objects = [applicationId]
};
}
}
}

return groupProxies.Values.ToList();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,18 +59,15 @@ CancellationToken cancellationToken
List<ReceiveConversionResult> results = new();
List<string> bakedObjectIds = new();

// return new(bakedObjectIds, results);

var objectGraph = _traversalFunction.Traverse(rootObject).Where(obj => obj.Current is not Collection);

// POC: these are not captured by traversal, so we need to re-add them here
var instanceDefinitionProxies = (rootObject["instanceDefinitionProxies"] as List<object>)
?.Cast<InstanceDefinitionProxy>()
.ToList();

List<(Collection[] collectionPath, IInstanceComponent obj)> instanceComponents = new();
// POC: these definitions are not captured by traversal, so we need to re-add them here
// POC: claire doesn't like this - it's confusing to have block definitions in the same instanceComponents list as block instances since they don't have layers
var instanceComponents = new List<(Collection[] path, IInstanceComponent obj)>();
// POC: these are not captured by traversal, so we need to re-add them here
if (instanceDefinitionProxies != null && instanceDefinitionProxies.Count > 0)
{
var transformed = instanceDefinitionProxies.Select(proxy =>
Expand All @@ -79,19 +76,24 @@ CancellationToken cancellationToken
instanceComponents.AddRange(transformed);
}

// POC: get group proxies
var groupProxies = (rootObject["groupProxies"] as List<object>)?.Cast<GroupProxy>().ToList();

var atomicObjects = new List<(Layer layer, Base obj)>();

foreach (TraversalContext tc in objectGraph)
{
Layer layerCollection = _autocadLayerManager.GetLayerPath(tc, baseLayerPrefix);

if (tc.Current is IInstanceComponent instanceComponent)
{
instanceComponents.Add(([layerCollection], instanceComponent));
}
else
var layer = _autocadLayerManager.GetLayerPath(tc, baseLayerPrefix);
switch (tc.Current)
{
atomicObjects.Add((layerCollection, tc.Current));
case IInstanceComponent instanceComponent:
instanceComponents.Add(([new() { name = layer.name }], instanceComponent));
break;
case GroupProxy:
continue;
default:
atomicObjects.Add((layer, tc.Current));
break;
}
}

Expand Down Expand Up @@ -140,6 +142,48 @@ CancellationToken cancellationToken
results.RemoveAll(result => result.ResultId != null && consumedObjectIds.Contains(result.ResultId));
results.AddRange(instanceConversionResults);

// Stage 3: Create group
// using var transactionContext = TransactionContext.StartTransaction(Application.DocumentManager.MdiActiveDocument);


if (groupProxies != null)
{
using var groupCreationTransaction =
Application.DocumentManager.CurrentDocument.Database.TransactionManager.StartTransaction();
var groupDictionary = (DBDictionary)
groupCreationTransaction.GetObject(
Application.DocumentManager.CurrentDocument.Database.GroupDictionaryId,
OpenMode.ForWrite
);

foreach (var gp in groupProxies.OrderBy(group => group.objects.Count))
{
try
{
var entities = gp.objects.SelectMany(oldObjId => applicationIdMap[oldObjId]);
var ids = new ObjectIdCollection();

foreach (var entity in entities)
{
ids.Add(entity.ObjectId);
}

var newGroup = new Group(gp.name, true); // NOTE: this constructor sets both the description (as it says) but also the name at the same time
newGroup.Append(ids);

groupDictionary.UpgradeOpen();
groupDictionary.SetAt(gp.name, newGroup);

groupCreationTransaction.AddNewlyCreatedDBObject(newGroup, true);
}
catch (Exception e) when (!e.IsFatal())
{
results.Add(new ReceiveConversionResult(Status.ERROR, gp, null, null, e));
}
}
groupCreationTransaction.Commit();
}

return new(bakedObjectIds, results);
}

Expand All @@ -153,7 +197,7 @@ private IEnumerable<Entity> ConvertObject(Base obj, Layer layerCollection)
{
using TransactionContext transactionContext = TransactionContext.StartTransaction(
Application.DocumentManager.MdiActiveDocument
);
); // POC: is this used/needed?

_autocadLayerManager.CreateLayerForReceive(layerCollection);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,19 @@ public class AutocadRootObjectBuilder : IRootObjectBuilder<AutocadRootObject>
private readonly string[] _documentPathSeparator = ["\\"];
private readonly ISendConversionCache _sendConversionCache;
private readonly AutocadInstanceObjectManager _instanceObjectsManager;
private readonly AutocadGroupUnpacker _groupUnpacker;

public AutocadRootObjectBuilder(
IRootToSpeckleConverter converter,
ISendConversionCache sendConversionCache,
AutocadInstanceObjectManager instanceObjectManager
AutocadInstanceObjectManager instanceObjectManager,
AutocadGroupUnpacker groupUnpacker
)
{
_converter = converter;
_sendConversionCache = sendConversionCache;
_instanceObjectsManager = instanceObjectManager;
_groupUnpacker = groupUnpacker;
}

public RootObjectBuilderResult Build(
Expand Down Expand Up @@ -89,7 +92,7 @@ public RootObjectBuilderResult Build(
{
string layerName = entity.Layer;

if (!collectionCache.TryGetValue(layerName, out Layer? speckleLayer))
if (!collectionCache.TryGetValue(layerName, out Layer speckleLayer))
{
if (tr.GetObject(entity.LayerId, OpenMode.ForRead) is LayerTableRecord autocadLayer)
{
Expand All @@ -99,14 +102,15 @@ public RootObjectBuilderResult Build(
}
else
{
// TODO: error
speckleLayer = new Layer("Unknown layer", System.Drawing.Color.Black.ToArgb());
}
}

speckleLayer.elements.Add(converted);
}
else
{
// Dims note: do we really need this if else clause here? imho not, as we'd fail in the upper stage of conversion?
// TODO: error
}

Expand All @@ -126,6 +130,8 @@ public RootObjectBuilderResult Build(
$"Cache hit count {cacheHitCount} out of {objects.Count} ({(double)cacheHitCount / objects.Count})"
);

var groupProxies = _groupUnpacker.UnpackGroups(atomicObjects);
modelWithLayers["groupProxies"] = groupProxies;
return new(modelWithLayers, results);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
<Compile Include="$(MSBuildThisFileDirectory)DependencyInjection\AutocadConnectorModule.cs" />
<Compile Include="$(MSBuildThisFileDirectory)DependencyInjection\Civil3dConnectorModule.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Filters\AutocadSelectionFilter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\AutocadGroupUnpacker.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\AutocadInstanceObjectManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\AutocadContext.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\AutocadDocumentManager.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@ public void CreateBaseLayer(string baseLayerName)
/// <summary>
/// <para>For receive: Use this method to construct layers in the host app when receiving.</para>.
/// </summary>
/// <param name="baseLayerName"></param>
/// <returns></returns>
public int GetAndCreateLayerFromPath(Collection[] collectionPath, string baseLayerName)
{
var layerPath = collectionPath.Select(o => string.IsNullOrWhiteSpace(o.name) ? "unnamed" : o.name);
Expand Down

0 comments on commit ba26ee0

Please sign in to comment.