Skip to content

Commit

Permalink
feat(rhino/autocad): cnx-18 add layer colors to send and receive in a…
Browse files Browse the repository at this point in the history
…utocad and rhino (#47)

* adds layer colors on send

* adds collection color on receive

* adds layer colors on receive

* fixes null transaction

* adds layer colors to rhino send and receive
  • Loading branch information
clairekuang authored Jul 18, 2024
1 parent 132aaf6 commit 3a322ee
Show file tree
Hide file tree
Showing 9 changed files with 134 additions and 64 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ private void SubscribeToObjectChanges(Document doc)
doc.Database.ObjectModified += (_, e) => OnObjectChanged(e.DBObject);
}

void OnObjectChanged(DBObject dbObject)
private void OnObjectChanged(DBObject dbObject)
{
_topLevelExceptionHandler.CatchUnhandled(() => OnChangeChangedObjectIds(dbObject));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Speckle.Core.Kits;
using Speckle.Core.Logging;
using Speckle.Core.Models;
using Speckle.Core.Models.Collections;
using Speckle.Core.Models.Instances;
using Speckle.DoubleNumerics;

Expand Down Expand Up @@ -133,7 +134,7 @@ out List<InstanceProxy> instanceProxiesWithSameDefinition
}

public BakeResult BakeInstances(
List<(string[] layerPath, IInstanceComponent obj)> instanceComponents,
List<(Collection[] collectionPath, IInstanceComponent obj)> instanceComponents,
Dictionary<string, List<Entity>> applicationIdMap,
string baseLayerName,
Action<string, double?>? onOperationProgressed
Expand All @@ -152,7 +153,7 @@ public BakeResult BakeInstances(
var consumedObjectIds = new List<string>();
var count = 0;

foreach (var (path, instanceOrDefinition) in sortedInstanceComponents)
foreach (var (collectionPath, instanceOrDefinition) in sortedInstanceComponents)
{
try
{
Expand Down Expand Up @@ -199,11 +200,13 @@ instanceOrDefinition is InstanceProxy instanceProxy
var modelSpaceBlockTableRecord = Application.DocumentManager.CurrentDocument.Database.GetModelSpace(
OpenMode.ForWrite
);
_autocadLayerManager.CreateLayerForReceive(path[0]);

// POC: collectionPath for instances should be an array of size 1, because we are flattening collections on traversal
_autocadLayerManager.CreateLayerForReceive(collectionPath[0]);
var blockRef = new BlockReference(insertionPoint, definitionId)
{
BlockTransform = matrix3d,
Layer = path[0],
Layer = collectionPath[0].name,
};

modelSpaceBlockTableRecord.AppendEntity(blockRef);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,30 +28,41 @@ public AutocadLayerManager(AutocadContext autocadContext)
/// This ensures we're creating the new objects we've just received rather than overlaying them.
/// </summary>
/// <param name="layerName">Name to search layer for purge and create.</param>
public void CreateLayerForReceive(string layerName)
public void CreateLayerForReceive(Collection layerCollection)
{
string layerName = layerCollection.name;
if (!_uniqueLayerNames.Add(layerName))
{
return;
}

// get layer color
int layerColorInt = layerCollection is IHasColor coloredLayer ? coloredLayer.color : -1; // default is white
var systemColor = System.Drawing.Color.FromArgb(layerColorInt);
Autodesk.AutoCAD.Colors.Color layerColor = Autodesk.AutoCAD.Colors.Color.FromRgb(
systemColor.R,
systemColor.G,
systemColor.B
);

Doc.LockDocument();
using Transaction transaction = Doc.TransactionManager.StartTransaction();

LayerTable? layerTable =
transaction.TransactionManager.GetObject(Doc.Database.LayerTableId, OpenMode.ForRead) as LayerTable;
LayerTableRecord layerTableRecord = new() { Name = layerName };
LayerTableRecord layerTableRecord = new() { Name = layerName, Color = layerColor };

bool hasLayer = layerTable != null && layerTable.Has(layerName);
if (hasLayer)
{
TypedValue[] tvs = { new((int)DxfCode.LayerName, layerName) };
TypedValue[] tvs = [new((int)DxfCode.LayerName, layerName)];
SelectionFilter selectionFilter = new(tvs);
SelectionSet selectionResult = Doc.Editor.SelectAll(selectionFilter).Value;
if (selectionResult == null)
{
return;
}

foreach (SelectedObject selectedObject in selectionResult)
{
transaction.GetObject(selectedObject.ObjectId, OpenMode.ForWrite).Erase();
Expand Down Expand Up @@ -79,7 +90,7 @@ public void DeleteAllLayersByPrefix(string prefix)
if (layer.Name.Contains(prefix))
{
// Delete objects from this layer
TypedValue[] tvs = { new((int)DxfCode.LayerName, layerName) };
TypedValue[] tvs = [new((int)DxfCode.LayerName, layerName)];
SelectionFilter selectionFilter = new(tvs);
SelectionSet selectionResult = Doc.Editor.SelectAll(selectionFilter).Value;
if (selectionResult == null)
Expand Down Expand Up @@ -144,17 +155,31 @@ public void CreateLayerFilter(string projectName, string modelName)
}

/// <summary>
/// Gets a valid layer name for a given context.
/// Gets a valid collection representing a layer for a given context.
/// </summary>
/// <param name="context"></param>
/// <param name="baseLayerPrefix"></param>
/// <param name="color"> Returns the color if found on a collection-based path, or null</param>
/// <returns></returns>
public string GetLayerPath(TraversalContext context, string baseLayerPrefix)
public Layer GetLayerPath(TraversalContext context, string baseLayerPrefix)
{
string[] collectionBasedPath = context.GetAscendantOfType<Collection>().Select(c => c.name).Reverse().ToArray();
string[] path = collectionBasedPath.Length != 0 ? collectionBasedPath : context.GetPropertyPath().ToArray();
Collection[] collectionBasedPath = context.GetAscendantOfType<Collection>().Reverse().ToArray();
int lastColor = -1;
foreach (Collection collection in collectionBasedPath)
{
if (collection is IHasColor coloredCollection)
{
lastColor = coloredCollection.color;
}
}

string[] path =
collectionBasedPath.Length != 0
? collectionBasedPath.Select(c => c.name).ToArray()
: context.GetPropertyPath().Reverse().ToArray();

string name = _autocadContext.RemoveInvalidChars(baseLayerPrefix + string.Join("-", path));

var name = baseLayerPrefix + string.Join("-", path);
return _autocadContext.RemoveInvalidChars(name);
return new Layer(name, lastColor);
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
using Autodesk.AutoCAD.DatabaseServices;
using Speckle.Connectors.Autocad.HostApp;
using Speckle.Connectors.Autocad.HostApp.Extensions;
using Speckle.Connectors.Autocad.Operations.Send;
using Speckle.Connectors.Utils.Builders;
using Speckle.Connectors.Utils.Conversion;
using Speckle.Connectors.Utils.Instances;
using Speckle.Converters.Common;
using Speckle.Core.Logging;
using Speckle.Core.Models;
Expand Down Expand Up @@ -70,38 +68,40 @@ CancellationToken cancellationToken
?.Cast<InstanceDefinitionProxy>()
.ToList();

var instanceComponents = new List<(string[] path, IInstanceComponent obj)>();
// POC: these are not captured by traversal, so we need to re-add them here
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
if (instanceDefinitionProxies != null && instanceDefinitionProxies.Count > 0)
{
var transformed = instanceDefinitionProxies.Select(proxy => (Array.Empty<string>(), proxy as IInstanceComponent));
var transformed = instanceDefinitionProxies.Select(proxy => (new Collection[] { }, proxy as IInstanceComponent));
instanceComponents.AddRange(transformed);
}

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

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

if (tc.Current is IInstanceComponent instanceComponent)
{
instanceComponents.Add((new string[] { layerName }, instanceComponent));
instanceComponents.Add((new Collection[] { layerCollection }, instanceComponent));
}
else
{
atomicObjects.Add((layerName, tc.Current));
atomicObjects.Add((layerCollection, tc.Current));
}
}

// Stage 1: Convert atomic objects
Dictionary<string, List<Entity>> applicationIdMap = new();
var count = 0;
foreach (var (layerName, atomicObject) in atomicObjects)
foreach (var (layerCollection, atomicObject) in atomicObjects)
{
onOperationProgressed?.Invoke("Converting objects", (double)++count / atomicObjects.Count);
try
{
var convertedObjects = ConvertObject(atomicObject, layerName).ToList();
var convertedObjects = ConvertObject(atomicObject, layerCollection).ToList();

if (atomicObject.applicationId != null)
{
Expand Down Expand Up @@ -147,13 +147,13 @@ private void PreReceiveDeepClean(string baseLayerPrefix)
_instanceObjectsManager.PurgeInstances(baseLayerPrefix);
}

private IEnumerable<Entity> ConvertObject(Base obj, string layerName)
private IEnumerable<Entity> ConvertObject(Base obj, Layer layerCollection)
{
using TransactionContext transactionContext = TransactionContext.StartTransaction(
Application.DocumentManager.MdiActiveDocument
);

_autocadLayerManager.CreateLayerForReceive(layerName);
_autocadLayerManager.CreateLayerForReceive(layerCollection);

object converted;
using (var tr = Application.DocumentManager.CurrentDocument.Database.TransactionManager.StartTransaction())
Expand All @@ -172,7 +172,7 @@ private IEnumerable<Entity> ConvertObject(Base obj, string layerName)
continue;
}

conversionResult.AppendToDb(layerName);
conversionResult.AppendToDb(layerCollection.name);
yield return conversionResult;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ namespace Speckle.Connectors.Autocad.Operations.Send;
public class AutocadRootObjectBuilder : IRootObjectBuilder<AutocadRootObject>
{
private readonly IRootToSpeckleConverter _converter;
private readonly string[] _documentPathSeparator = { "\\" };
private readonly string[] _documentPathSeparator = ["\\"];
private readonly ISendConversionCache _sendConversionCache;
private readonly AutocadInstanceObjectManager _instanceObjectsManager;

Expand Down Expand Up @@ -45,12 +45,15 @@ public RootObjectBuilderResult Build(
.DocumentManager.CurrentDocument.Name // POC: https://spockle.atlassian.net/browse/CNX-9319
.Split(_documentPathSeparator, StringSplitOptions.None)
.Reverse()
.First(),
collectionType = "root"
.First()
};

// TODO: better handling for document and transactions!!
Document doc = Application.DocumentManager.CurrentDocument;
using Transaction tr = doc.Database.TransactionManager.StartTransaction();

// Cached dictionary to create Collection for autocad entity layers. We first look if collection exists. If so use it otherwise create new one for that layer.
Dictionary<string, Collection> collectionCache = new();
Dictionary<string, Layer> collectionCache = new();
int count = 0;

var (atomicObjects, instanceProxies, instanceDefinitionProxies) = _instanceObjectsManager.UnpackSelection(objects);
Expand Down Expand Up @@ -82,16 +85,29 @@ public RootObjectBuilderResult Build(
}

// Create and add a collection for each layer if not done so already.
if ((dbObject as Entity)?.Layer is string layer)
if (dbObject is Entity entity)
{
if (!collectionCache.TryGetValue(layer, out Collection? collection))
string layerName = entity.Layer;

if (!collectionCache.TryGetValue(layerName, out Layer? speckleLayer))
{
collection = new Collection() { name = layer, collectionType = "layer" };
collectionCache[layer] = collection;
modelWithLayers.elements.Add(collectionCache[layer]);
if (tr.GetObject(entity.LayerId, OpenMode.ForRead) is LayerTableRecord autocadLayer)
{
speckleLayer = new Layer(layerName, autocadLayer.Color.ColorValue.ToArgb());
collectionCache[layerName] = speckleLayer;
modelWithLayers.elements.Add(collectionCache[layerName]);
}
else
{
// TODO: error
}
}

collection.elements.Add(converted);
speckleLayer.elements.Add(converted);
}
else
{
// TODO: error
}

results.Add(new(Status.SUCCESS, applicationId, dbObject.GetType().ToString(), converted));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Speckle.Core.Kits;
using Speckle.Core.Logging;
using Speckle.Core.Models;
using Speckle.Core.Models.Collections;
using Speckle.Core.Models.Instances;
using Speckle.DoubleNumerics;

Expand Down Expand Up @@ -125,7 +126,7 @@ out List<InstanceProxy> instanceProxiesWithSameDefinition
/// <param name="applicationIdMap">A dict mapping { original application id -> [resulting application ids post conversion] }</param>
/// <param name="onOperationProgressed"></param>
public BakeResult BakeInstances(
List<(string[] layerPath, IInstanceComponent obj)> instanceComponents,
List<(Collection[] collectionPath, IInstanceComponent obj)> instanceComponents,
Dictionary<string, List<string>> applicationIdMap,
string baseLayerName,
Action<string, double?>? onOperationProgressed
Expand All @@ -144,7 +145,7 @@ public BakeResult BakeInstances(
var conversionResults = new List<ReceiveConversionResult>();
var createdObjectIds = new List<string>();
var consumedObjectIds = new List<string>();
foreach (var (path, instanceOrDefinition) in sortedInstanceComponents)
foreach (var (layerCollection, instanceOrDefinition) in sortedInstanceComponents)
{
onOperationProgressed?.Invoke("Converting blocks", (double)++count / sortedInstanceComponents.Count);
try
Expand Down Expand Up @@ -200,7 +201,7 @@ instanceOrDefinition is InstanceProxy instanceProxy
)
{
var transform = MatrixToTransform(instanceProxy.transform, instanceProxy.units);
var layerIndex = _layerManager.GetAndCreateLayerFromPath(path, baseLayerName);
var layerIndex = _layerManager.GetAndCreateLayerFromPath(layerCollection, baseLayerName);
var id = doc.Objects.AddInstanceObject(index, transform, new ObjectAttributes() { LayerIndex = layerIndex });
if (instanceProxy.applicationId != null)
{
Expand Down
Loading

0 comments on commit 3a322ee

Please sign in to comment.