Skip to content

Commit

Permalink
fix(civil3d): refactors corridors to only use extracted solids once (#…
Browse files Browse the repository at this point in the history
…318)

* refactors corridors to only use extracted solids once

changes applied assemblies and subassemblies to dicts

* Update CorridorHandler.cs

* refactors corridor display value extractor to its own class

* changes scope
  • Loading branch information
clairekuang authored Oct 24, 2024
1 parent 3088fae commit 68f7cb8
Show file tree
Hide file tree
Showing 5 changed files with 292 additions and 183 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
using Speckle.Converters.Common;
using Speckle.Converters.Common.Objects;
using Speckle.Sdk;

namespace Speckle.Converters.Civil3dShared.Helpers;

/// <summary>
/// Constructs a the Corridor Key for a subassembly with the Corridor Handle, Baseline Guid, Region Guid, Assembly Handle, and Subassembly Handle.
/// This order and type is determined by the structure of corridors and the available information on extracted corridor solid property sets.
/// </summary>
public readonly struct SubassemblyCorridorKey
{
public string CorridorId { get; }
public string BaselineId { get; }
public string RegionId { get; }
public string AssemblyId { get; }
public string SubassemblyId { get; }

public SubassemblyCorridorKey(
string corridorHandle,
string baselineGuid,
string regionGuid,
string assemblyHandle,
string subassemblyHandle
)
{
CorridorId = corridorHandle.ToLower();
BaselineId = CleanGuidString(baselineGuid.ToLower());
RegionId = CleanGuidString(regionGuid.ToLower());
AssemblyId = assemblyHandle.ToLower();
SubassemblyId = subassemblyHandle.ToLower();
}

// Removes brackets from guid strings - property sets will return guids with brackets (unlike when retrieved from api)
private string CleanGuidString(string guid)
{
guid = guid.Replace("{", "").Replace("}", "");
return guid;
}

public override readonly string ToString() => $"{CorridorId}-{BaselineId}-{RegionId}-{AssemblyId}-{SubassemblyId}";
}

/// <summary>
/// Extracts and stores the display value meshes of a Corridor by the corridor's subassembly corridor keys.
/// </summary>
public sealed class CorridorDisplayValueExtractor
{
/// <summary>
/// Keeps track of all corridor solids by their hierarchy of (corridor, baseline, region, applied assembly, applied subassembly) in the current send operation.
/// This should be added to the display value of the corridor applied subassemblies after they are processed
/// Handles should be used instead of Handle.Value (as is typically used for speckle app ids) since the exported solid property sets only stores the handle
/// </summary>
public Dictionary<string, List<SOG.Mesh>> CorridorSolidsCache { get; } = new();

// these ints are used to retrieve the correct values from the exported corridor solids property sets to cache them
// they were determined via trial and error
#pragma warning disable CA1805 // Initialized explicitly to 0
private readonly int _corridorHandleIndex = 0;
#pragma warning restore CA1805 // Initialized explicitly to 0
private readonly int _baselineGuidIndex = 6;
private readonly int _regionGuidIndex = 7;
private readonly int _assemblyHandleIndex = 3;
private readonly int _subassemblyHandleIndex = 4;

private readonly ITypedConverter<ADB.Solid3d, SOG.Mesh> _solidConverter;
private readonly ITypedConverter<ADB.Body, SOG.Mesh> _bodyConverter;
private readonly IConverterSettingsStore<Civil3dConversionSettings> _settingsStore;

public CorridorDisplayValueExtractor(
ITypedConverter<ADB.Solid3d, SOG.Mesh> solidConverter,
ITypedConverter<ADB.Body, SOG.Mesh> bodyConverter,
IConverterSettingsStore<Civil3dConversionSettings> settingsStore
)
{
_solidConverter = solidConverter;
_bodyConverter = bodyConverter;
_settingsStore = settingsStore;
}

/// <summary>
/// Extracts the solids from a corridor and stores them in <see cref="CorridorSolidsCache"/> by their subassembly corridor id.
/// Api method is only available for 2024 or greater.
/// </summary>
/// <param name="corridor"></param>
/// <remarks>This is pretty complicated because we need to match each extracted solid to a corridor subassembly by inspecting its property sets for identifying information</remarks>
public void ProcessCorridorSolids(CDB.Corridor corridor)
{
#if CIVIL3D2024_OR_GREATER

CDB.ExportCorridorSolidsParams param = new();

using (var tr = _settingsStore.Current.Document.Database.TransactionManager.StartTransaction())
{
foreach (ADB.ObjectId solidId in corridor.ExportSolids(param, corridor.Database))
{
SOG.Mesh? mesh = null;
var solid = tr.GetObject(solidId, ADB.OpenMode.ForRead);
if (solid is ADB.Solid3d solid3d)
{
// get the solid mesh
mesh = _solidConverter.Convert(solid3d);
}
else if (solid is ADB.Body body)
{
mesh = _bodyConverter.Convert(body);
}

if (mesh is null)
{
continue;
}

// get the subassembly corridor key from the solid property sets
if (GetSubassemblyKeyFromDBObject(solid, tr) is SubassemblyCorridorKey solidKey)
{
if (CorridorSolidsCache.TryGetValue(solidKey.ToString(), out List<SOG.Mesh>? display))
{
display.Add(mesh);
}
else
{
CorridorSolidsCache[solidKey.ToString()] = new() { mesh };
}
}
}

tr.Commit();
}
#endif
}

private SubassemblyCorridorKey? GetSubassemblyKeyFromDBObject(ADB.DBObject obj, ADB.Transaction tr)
{
ADB.ObjectIdCollection? propertySetIds;

try
{
propertySetIds = AAECPDB.PropertyDataServices.GetPropertySets(obj);
}
catch (Exception e) when (!e.IsFatal())
{
return null;
}

if (propertySetIds is null || propertySetIds.Count == 0)
{
return null;
}

foreach (ADB.ObjectId id in propertySetIds)
{
AAECPDB.PropertySet propertySet = (AAECPDB.PropertySet)tr.GetObject(id, ADB.OpenMode.ForRead);

if (propertySet.PropertySetDefinitionName == "Corridor Identity")
{
if (propertySet.PropertySetData[_corridorHandleIndex].GetData() is not string corridorHandle)
{
return null;
}
if (propertySet.PropertySetData[_baselineGuidIndex].GetData() is not string baselineGuid)
{
return null;
}
if (propertySet.PropertySetData[_regionGuidIndex].GetData() is not string regionGuid)
{
return null;
}
if (propertySet.PropertySetData[_assemblyHandleIndex].GetData() is not string assemblyHandle)
{
return null;
}
if (propertySet.PropertySetData[_subassemblyHandleIndex].GetData() is not string subassemblyHandle)
{
return null;
}

return new SubassemblyCorridorKey(corridorHandle, baselineGuid, regionGuid, assemblyHandle, subassemblyHandle);
}
}

return null;
}
}
Loading

0 comments on commit 68f7cb8

Please sign in to comment.