Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a1462813d5 |
+117
@@ -112,10 +112,49 @@ public class NavisworksRootObjectBuilder(
|
||||
int processedCount = 0;
|
||||
int totalCount = navisworksModelItems.Count;
|
||||
|
||||
// Store instance definitions by their definition ID (hash of the first instance)
|
||||
Dictionary<int, Base> instanceDefinitions = [];
|
||||
|
||||
foreach (var item in navisworksModelItems)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
string applicationId = elementSelectionService.GetModelItemPath(item);
|
||||
|
||||
if (item.InstanceHashCode != 0)
|
||||
{
|
||||
var instanceDefinitionId = item.Instances.First.GetHashCode();
|
||||
|
||||
// If this instance definition is already converted, create an instance reference
|
||||
if (instanceDefinitions.TryGetValue(instanceDefinitionId, out var definitionBase))
|
||||
{
|
||||
var instanceBase = CreateInstanceReference(definitionBase, item);
|
||||
convertedBases[applicationId] = instanceBase;
|
||||
|
||||
results.Add(new SendConversionResult(Status.SUCCESS, applicationId, item.GetType().Name, instanceBase));
|
||||
processedCount++;
|
||||
onOperationProgressed.Report(new CardProgress("Converting", (double)processedCount / totalCount));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert the item (either a new instance definition or non-instance item)
|
||||
var converted = ConvertNavisworksItem(item, convertedBases, projectId);
|
||||
if (converted.Status == Status.SUCCESS)
|
||||
{
|
||||
results.Add(converted);
|
||||
|
||||
// If this is an instance definition, store it for reuse
|
||||
if (item.InstanceHashCode != 0)
|
||||
{
|
||||
var instanceDefinitionId = item.Instances.First.GetHashCode();
|
||||
instanceDefinitions[instanceDefinitionId] = convertedBases[converted.SourceId]!;
|
||||
}
|
||||
|
||||
processedCount++;
|
||||
onOperationProgressed.Report(new CardProgress("Converting", (double)processedCount / totalCount));
|
||||
continue;
|
||||
}
|
||||
|
||||
results.Add(converted);
|
||||
processedCount++;
|
||||
onOperationProgressed.Report(new CardProgress("Converting", (double)processedCount / totalCount));
|
||||
@@ -291,6 +330,84 @@ public class NavisworksRootObjectBuilder(
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance reference that points to a converted base definition with instance-specific transforms.
|
||||
/// </summary>
|
||||
/// <param name="definitionBase">The converted base definition to reference.</param>
|
||||
/// <param name="instanceItem">The Navisworks instance item.</param>
|
||||
/// <returns>A Base object representing the instance with transforms.</returns>
|
||||
private Base CreateInstanceReference(Base definitionBase, NAV.ModelItem instanceItem)
|
||||
{
|
||||
// Get the instance transform from Navisworks
|
||||
var instanceTransform = GetInstanceTransform(instanceItem);
|
||||
|
||||
// Create a new base that references the definition
|
||||
var instanceBase = new Base
|
||||
{
|
||||
applicationId = elementSelectionService.GetModelItemPath(instanceItem),
|
||||
// Store reference to the definition
|
||||
["@definition"] = definitionBase.applicationId,
|
||||
// Store the instance transform
|
||||
["transform"] = instanceTransform,
|
||||
// Copy the display value (geometry) from the definition
|
||||
["displayValue"] = definitionBase["displayValue"],
|
||||
// Copy other properties from definition if needed
|
||||
["properties"] = definitionBase["properties"]
|
||||
};
|
||||
|
||||
return instanceBase;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extracts the transform matrix from a Navisworks instance item.
|
||||
/// </summary>
|
||||
/// <param name="instanceItem">The Navisworks instance item.</param>
|
||||
/// <returns>A transform matrix as a flat array or null if no transform.</returns>
|
||||
private double[]? GetInstanceTransform(NAV.ModelItem instanceItem)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Get the transform from the instance
|
||||
var transform = instanceItem.Transform;
|
||||
if (transform == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Convert the Navisworks transform to a flat array
|
||||
// For now, return identity matrix as POC - proper transform extraction needs Navisworks API research
|
||||
var matrix = new double[16];
|
||||
// Identity matrix
|
||||
matrix[0] = 1;
|
||||
matrix[1] = 0;
|
||||
matrix[2] = 0;
|
||||
matrix[3] = 0;
|
||||
matrix[4] = 0;
|
||||
matrix[5] = 1;
|
||||
matrix[6] = 0;
|
||||
matrix[7] = 0;
|
||||
matrix[8] = 0;
|
||||
matrix[9] = 0;
|
||||
matrix[10] = 1;
|
||||
matrix[11] = 0;
|
||||
matrix[12] = 0;
|
||||
matrix[13] = 0;
|
||||
matrix[14] = 0;
|
||||
matrix[15] = 1;
|
||||
|
||||
return matrix;
|
||||
}
|
||||
catch (Exception ex) when (!ex.IsFatal())
|
||||
{
|
||||
logger.LogWarning(
|
||||
ex,
|
||||
"Failed to extract transform from instance {id}",
|
||||
elementSelectionService.GetModelItemPath(instanceItem)
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a single Navisworks item to a Speckle object.
|
||||
/// </summary>
|
||||
|
||||
+4
-1
@@ -33,6 +33,9 @@ public class DisplayValueExtractor
|
||||
return [];
|
||||
}
|
||||
|
||||
return !IsElementVisible(modelItem) ? [] : _geometryConverter.Convert(modelItem);
|
||||
// Check if this is an instance definition by looking at InstanceHashCode
|
||||
bool isInstanceDefinition = modelItem.InstanceHashCode != 0;
|
||||
|
||||
return !IsElementVisible(modelItem) ? [] : _geometryConverter.Convert(modelItem, isInstanceDefinition);
|
||||
}
|
||||
}
|
||||
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Speckle.Converter.Navisworks.Helpers;
|
||||
using static Speckle.Converter.Navisworks.Helpers.ElementSelectionHelper;
|
||||
|
||||
namespace Speckle.Converter.Navisworks.Helpers;
|
||||
|
||||
/// <summary>
|
||||
/// Helper class for extracting and working with Navisworks transforms.
|
||||
/// </summary>
|
||||
public static class NavisworksTransformHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Extracts the transform matrix from a Navisworks instance item.
|
||||
/// </summary>
|
||||
/// <param name="instanceItem">The Navisworks instance item.</param>
|
||||
/// <returns>A transform matrix as a flat array or null if no transform.</returns>
|
||||
public static double[]? GetInstanceTransform(NAV.ModelItem instanceItem)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Get the transform from the instance
|
||||
var transform = instanceItem.Transform;
|
||||
if (transform == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Convert the Navisworks transform to a flat array
|
||||
var matrix = new double[16];
|
||||
matrix[0] = transform.M00; matrix[1] = transform.M01; matrix[2] = transform.M02; matrix[3] = transform.M03;
|
||||
matrix[4] = transform.M10; matrix[5] = transform.M11; matrix[6] = transform.M12; matrix[7] = transform.M13;
|
||||
matrix[8] = transform.M20; matrix[9] = transform.M21; matrix[10] = transform.M22; matrix[11] = transform.M23;
|
||||
matrix[12] = transform.M30; matrix[13] = transform.M31; matrix[14] = transform.M32; matrix[15] = transform.M33;
|
||||
|
||||
return matrix;
|
||||
}
|
||||
catch (Exception ex) when (!ex.IsFatal())
|
||||
{
|
||||
// Note: We can't use logger here since this is a static method
|
||||
// The calling code should handle logging if needed
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
@@ -50,4 +50,5 @@ public class NavisworksRootToSpeckleConverter : IRootToSpeckleConverter
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+69
-35
@@ -39,7 +39,7 @@ public class GeometryToSpeckleConverter
|
||||
/// Converts a ModelItem's geometry to Speckle display geometry by accessing the underlying COM objects.
|
||||
/// Applies necessary transformations and unit scaling.
|
||||
/// </summary>
|
||||
internal List<Base> Convert(NAV.ModelItem modelItem)
|
||||
internal List<Base> Convert(NAV.ModelItem modelItem, bool isInstanceDefinition = false)
|
||||
{
|
||||
if (modelItem == null)
|
||||
{
|
||||
@@ -64,7 +64,7 @@ public class GeometryToSpeckleConverter
|
||||
CollectFragments(path, fragmentStack);
|
||||
}
|
||||
|
||||
return ProcessFragments(fragmentStack, paths);
|
||||
return ProcessFragments(fragmentStack, paths, isInstanceDefinition);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -103,7 +103,7 @@ public class GeometryToSpeckleConverter
|
||||
}
|
||||
}
|
||||
|
||||
private List<Base> ProcessFragments(Stack<InwOaFragment3> fragmentStack, InwSelectionPathsColl paths)
|
||||
private List<Base> ProcessFragments(Stack<InwOaFragment3> fragmentStack, InwSelectionPathsColl paths, bool isInstanceDefinition = false)
|
||||
{
|
||||
var callbackListeners = new List<PrimitiveProcessor>();
|
||||
|
||||
@@ -132,7 +132,7 @@ public class GeometryToSpeckleConverter
|
||||
callbackListeners.Add(processor);
|
||||
}
|
||||
|
||||
var baseGeometries = ProcessGeometries(callbackListeners);
|
||||
var baseGeometries = ProcessGeometries(callbackListeners, isInstanceDefinition);
|
||||
|
||||
return baseGeometries;
|
||||
}
|
||||
@@ -147,7 +147,7 @@ public class GeometryToSpeckleConverter
|
||||
return IsSameFragmentPath(fragmentPathData, pathData);
|
||||
}
|
||||
|
||||
private List<Base> ProcessGeometries(List<PrimitiveProcessor> processors)
|
||||
private List<Base> ProcessGeometries(List<PrimitiveProcessor> processors, bool isInstanceDefinition = false)
|
||||
{
|
||||
var baseGeometries = new List<Base>();
|
||||
|
||||
@@ -155,13 +155,13 @@ public class GeometryToSpeckleConverter
|
||||
{
|
||||
if (processor.Triangles.Count > 0)
|
||||
{
|
||||
var mesh = CreateMesh(processor.Triangles);
|
||||
var mesh = CreateMesh(processor.Triangles, isInstanceDefinition);
|
||||
baseGeometries.Add(mesh);
|
||||
}
|
||||
|
||||
if (processor.Lines.Count > 0)
|
||||
{
|
||||
var lines = CreateLines(processor.Lines);
|
||||
var lines = CreateLines(processor.Lines, isInstanceDefinition);
|
||||
baseGeometries.AddRange(lines);
|
||||
}
|
||||
}
|
||||
@@ -169,7 +169,7 @@ public class GeometryToSpeckleConverter
|
||||
return baseGeometries;
|
||||
}
|
||||
|
||||
private Mesh CreateMesh(IReadOnlyList<SafeTriangle> triangles)
|
||||
private Mesh CreateMesh(IReadOnlyList<SafeTriangle> triangles, bool isInstanceDefinition = false)
|
||||
{
|
||||
var vertices = new List<double>();
|
||||
var faces = new List<int>();
|
||||
@@ -178,20 +178,40 @@ public class GeometryToSpeckleConverter
|
||||
{
|
||||
var triangle = triangles[t];
|
||||
|
||||
// No need to worry about disposal of COM across boundaries - we're working with our safe structs
|
||||
vertices.AddRange(
|
||||
[
|
||||
(triangle.Vertex1.X + _transformVector.X) * SCALE,
|
||||
(triangle.Vertex1.Y + _transformVector.Y) * SCALE,
|
||||
(triangle.Vertex1.Z + _transformVector.Z) * SCALE,
|
||||
(triangle.Vertex2.X + _transformVector.X) * SCALE,
|
||||
(triangle.Vertex2.Y + _transformVector.Y) * SCALE,
|
||||
(triangle.Vertex2.Z + _transformVector.Z) * SCALE,
|
||||
(triangle.Vertex3.X + _transformVector.X) * SCALE,
|
||||
(triangle.Vertex3.Y + _transformVector.Y) * SCALE,
|
||||
(triangle.Vertex3.Z + _transformVector.Z) * SCALE
|
||||
]
|
||||
);
|
||||
// For instance definitions, don't apply global transform - only apply coordinate system and scaling
|
||||
if (isInstanceDefinition)
|
||||
{
|
||||
vertices.AddRange(
|
||||
[
|
||||
triangle.Vertex1.X * SCALE,
|
||||
triangle.Vertex1.Y * SCALE,
|
||||
triangle.Vertex1.Z * SCALE,
|
||||
triangle.Vertex2.X * SCALE,
|
||||
triangle.Vertex2.Y * SCALE,
|
||||
triangle.Vertex2.Z * SCALE,
|
||||
triangle.Vertex3.X * SCALE,
|
||||
triangle.Vertex3.Y * SCALE,
|
||||
triangle.Vertex3.Z * SCALE
|
||||
]
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
// For non-instance geometry, apply global transform as before
|
||||
vertices.AddRange(
|
||||
[
|
||||
(triangle.Vertex1.X + _transformVector.X) * SCALE,
|
||||
(triangle.Vertex1.Y + _transformVector.Y) * SCALE,
|
||||
(triangle.Vertex1.Z + _transformVector.Z) * SCALE,
|
||||
(triangle.Vertex2.X + _transformVector.X) * SCALE,
|
||||
(triangle.Vertex2.Y + _transformVector.Y) * SCALE,
|
||||
(triangle.Vertex2.Z + _transformVector.Z) * SCALE,
|
||||
(triangle.Vertex3.X + _transformVector.X) * SCALE,
|
||||
(triangle.Vertex3.Y + _transformVector.Y) * SCALE,
|
||||
(triangle.Vertex3.Z + _transformVector.Z) * SCALE
|
||||
]
|
||||
);
|
||||
}
|
||||
faces.AddRange([3, t * 3, t * 3 + 1, t * 3 + 2]);
|
||||
}
|
||||
|
||||
@@ -203,23 +223,37 @@ public class GeometryToSpeckleConverter
|
||||
};
|
||||
}
|
||||
|
||||
private List<Line> CreateLines(IReadOnlyList<SafeLine> lines) =>
|
||||
private List<Line> CreateLines(IReadOnlyList<SafeLine> lines, bool isInstanceDefinition = false) =>
|
||||
(
|
||||
from line in lines
|
||||
select new Line
|
||||
{
|
||||
start = new Point(
|
||||
(line.Start.X + _transformVector.X) * SCALE,
|
||||
(line.Start.Y + _transformVector.Y) * SCALE,
|
||||
(line.Start.Z + _transformVector.Z) * SCALE,
|
||||
_settings.Derived.SpeckleUnits
|
||||
),
|
||||
end = new Point(
|
||||
(line.End.X + _transformVector.X) * SCALE,
|
||||
(line.End.Y + _transformVector.Y) * SCALE,
|
||||
(line.End.Z + _transformVector.Z) * SCALE,
|
||||
_settings.Derived.SpeckleUnits
|
||||
),
|
||||
start = isInstanceDefinition
|
||||
? new Point(
|
||||
line.Start.X * SCALE,
|
||||
line.Start.Y * SCALE,
|
||||
line.Start.Z * SCALE,
|
||||
_settings.Derived.SpeckleUnits
|
||||
)
|
||||
: new Point(
|
||||
(line.Start.X + _transformVector.X) * SCALE,
|
||||
(line.Start.Y + _transformVector.Y) * SCALE,
|
||||
(line.Start.Z + _transformVector.Z) * SCALE,
|
||||
_settings.Derived.SpeckleUnits
|
||||
),
|
||||
end = isInstanceDefinition
|
||||
? new Point(
|
||||
line.End.X * SCALE,
|
||||
line.End.Y * SCALE,
|
||||
line.End.Z * SCALE,
|
||||
_settings.Derived.SpeckleUnits
|
||||
)
|
||||
: new Point(
|
||||
(line.End.X + _transformVector.X) * SCALE,
|
||||
(line.End.Y + _transformVector.Y) * SCALE,
|
||||
(line.End.Z + _transformVector.Z) * SCALE,
|
||||
_settings.Derived.SpeckleUnits
|
||||
),
|
||||
units = _settings.Derived.SpeckleUnits
|
||||
}
|
||||
).ToList();
|
||||
|
||||
+3
@@ -40,6 +40,7 @@ public class ModelItemToToSpeckleConverter : IToSpeckleTopLevelConverter
|
||||
public Base Convert(object target) =>
|
||||
target == null ? throw new ArgumentNullException(nameof(target)) : Convert((NAV.ModelItem)target);
|
||||
|
||||
|
||||
// Converts a Navisworks ModelItem into a Speckle Base object
|
||||
private Base Convert(NAV.ModelItem target)
|
||||
{
|
||||
@@ -51,6 +52,7 @@ public class ModelItemToToSpeckleConverter : IToSpeckleTopLevelConverter
|
||||
: CreateNonGeometryObject(target, name, handler);
|
||||
}
|
||||
|
||||
|
||||
// There are in fact only two types of objects: geometry and non-geometry, the latter being collections of other objects
|
||||
private NavisworksObject CreateGeometryObject(NAV.ModelItem target, string name, IPropertyHandler propertyHandler) =>
|
||||
new()
|
||||
@@ -61,6 +63,7 @@ public class ModelItemToToSpeckleConverter : IToSpeckleTopLevelConverter
|
||||
units = _settingsStore.Current.Derived.SpeckleUnits,
|
||||
};
|
||||
|
||||
|
||||
private Collection CreateNonGeometryObject(NAV.ModelItem target, string name, IPropertyHandler propertyHandler) =>
|
||||
new()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user