Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 58c6370cda | |||
| 0e72adba36 | |||
| d5084dc334 | |||
| 2c13c4ff79 |
+13
-55
@@ -20,23 +20,8 @@ public class CsiFrameSectionPropertyExtractor : IFrameSectionPropertyExtractor
|
||||
_settingsStore = settingsStore;
|
||||
}
|
||||
|
||||
public void ExtractProperties(string sectionName, Dictionary<string, object?> properties)
|
||||
{
|
||||
GetMaterialName(sectionName, properties);
|
||||
public void ExtractProperties(string sectionName, Dictionary<string, object?> properties) =>
|
||||
GetSectionProperties(sectionName, properties);
|
||||
GetPropertyModifiers(sectionName, properties);
|
||||
}
|
||||
|
||||
private void GetMaterialName(string sectionName, Dictionary<string, object?> properties)
|
||||
{
|
||||
// get material name
|
||||
string materialName = string.Empty;
|
||||
_settingsStore.Current.SapModel.PropFrame.GetMaterial(sectionName, ref materialName);
|
||||
|
||||
// append to General Data of properties dictionary
|
||||
Dictionary<string, object?> generalData = properties.EnsureNested(SectionPropertyCategory.GENERAL_DATA);
|
||||
generalData["Material"] = materialName;
|
||||
}
|
||||
|
||||
private void GetSectionProperties(string sectionName, Dictionary<string, object?> properties)
|
||||
{
|
||||
@@ -69,47 +54,20 @@ public class CsiFrameSectionPropertyExtractor : IFrameSectionPropertyExtractor
|
||||
ref radiusOfGyrationAboutMinorAxis
|
||||
);
|
||||
|
||||
string distanceUnit = _settingsStore.Current.SpeckleUnits;
|
||||
string areaUnit = $"{distanceUnit}²"; // // TODO: Formalize this better
|
||||
string modulusUnit = $"{distanceUnit}³"; // // TODO: Formalize this better
|
||||
string inertiaUnit = $"{distanceUnit}\u2074"; // TODO: Formalize this better
|
||||
|
||||
Dictionary<string, object?> mechanicalProperties = properties.EnsureNested(
|
||||
SectionPropertyCategory.SECTION_PROPERTIES
|
||||
);
|
||||
mechanicalProperties.AddWithUnits("Area", crossSectionalArea, areaUnit);
|
||||
mechanicalProperties.AddWithUnits("As2", shearAreaInMajorAxisDirection, areaUnit);
|
||||
mechanicalProperties.AddWithUnits("As3", shearAreaInMinorAxisDirection, areaUnit);
|
||||
mechanicalProperties.AddWithUnits("J", torsionalConstant, inertiaUnit);
|
||||
mechanicalProperties.AddWithUnits("I22", momentOfInertiaAboutMajorAxis, inertiaUnit);
|
||||
mechanicalProperties.AddWithUnits("I33", momentOfInertiaAboutMinorAxis, inertiaUnit);
|
||||
mechanicalProperties.AddWithUnits("S22", sectionModulusAboutMajorAxis, modulusUnit);
|
||||
mechanicalProperties.AddWithUnits("S33", sectionModulusAboutMinorAxis, modulusUnit);
|
||||
mechanicalProperties.AddWithUnits("Z22", plasticModulusAboutMajorAxis, modulusUnit);
|
||||
mechanicalProperties.AddWithUnits("Z33", plasticModulusAboutMinorAxis, modulusUnit);
|
||||
mechanicalProperties.AddWithUnits("R22", radiusOfGyrationAboutMajorAxis, distanceUnit);
|
||||
mechanicalProperties.AddWithUnits("R33", radiusOfGyrationAboutMinorAxis, distanceUnit);
|
||||
}
|
||||
|
||||
private void GetPropertyModifiers(string sectionName, Dictionary<string, object?> properties)
|
||||
{
|
||||
double[] stiffnessModifiersArray = [];
|
||||
_settingsStore.Current.SapModel.PropFrame.GetModifiers(sectionName, ref stiffnessModifiersArray);
|
||||
|
||||
Dictionary<string, object?> modifiers =
|
||||
new()
|
||||
{
|
||||
["Cross-section (Axial) Area"] = stiffnessModifiersArray[0],
|
||||
["Shear Area in 2 Direction"] = stiffnessModifiersArray[1],
|
||||
["Shear Area in 3 Direction"] = stiffnessModifiersArray[2],
|
||||
["Torsional Constant"] = stiffnessModifiersArray[3],
|
||||
["Moment of Inertia about 2 Axis"] = stiffnessModifiersArray[4],
|
||||
["Moment of Inertia about 3 Axis"] = stiffnessModifiersArray[5],
|
||||
["Mass"] = stiffnessModifiersArray[6],
|
||||
["Weight"] = stiffnessModifiersArray[7],
|
||||
};
|
||||
|
||||
Dictionary<string, object?> generalData = properties.EnsureNested(SectionPropertyCategory.GENERAL_DATA);
|
||||
generalData["Modifiers"] = modifiers;
|
||||
mechanicalProperties.Add("Area", crossSectionalArea);
|
||||
mechanicalProperties.Add("As2", shearAreaInMajorAxisDirection);
|
||||
mechanicalProperties.Add("As3", shearAreaInMinorAxisDirection);
|
||||
mechanicalProperties.Add("J", torsionalConstant);
|
||||
mechanicalProperties.Add("I22", momentOfInertiaAboutMajorAxis);
|
||||
mechanicalProperties.Add("I33", momentOfInertiaAboutMinorAxis);
|
||||
mechanicalProperties.Add("S22", sectionModulusAboutMajorAxis);
|
||||
mechanicalProperties.Add("S33", sectionModulusAboutMinorAxis);
|
||||
mechanicalProperties.Add("Z22", plasticModulusAboutMajorAxis);
|
||||
mechanicalProperties.Add("Z33", plasticModulusAboutMinorAxis);
|
||||
mechanicalProperties.Add("R22", radiusOfGyrationAboutMajorAxis);
|
||||
mechanicalProperties.Add("R33", radiusOfGyrationAboutMinorAxis);
|
||||
}
|
||||
}
|
||||
|
||||
+1
@@ -23,6 +23,7 @@ public class CsiResultsExtractorFactory
|
||||
ResultsKey.PIER_FORCES => _serviceProvider.GetRequiredService<CsiPierForceResultsExtractor>(),
|
||||
ResultsKey.SPANDREL_FORCES => _serviceProvider.GetRequiredService<CsiSpandrelForceResultsExtractor>(),
|
||||
ResultsKey.STORY_DRIFTS => _serviceProvider.GetRequiredService<CsiStoryDriftsResultsExtractor>(),
|
||||
ResultsKey.STORY_FORCES => _serviceProvider.GetRequiredService<CsiStoryForceResultsExtractor>(),
|
||||
_ => throw new InvalidOperationException($"{resultsKey} not accounted for in CsiResultsExtractorFactory")
|
||||
};
|
||||
}
|
||||
|
||||
@@ -47,18 +47,17 @@ public class EtabsSectionUnpacker : ISectionUnpacker
|
||||
string sectionName = entry.Key;
|
||||
List<string> frameIds = entry.Value;
|
||||
|
||||
// Initialize properties outside the if statement
|
||||
Dictionary<string, object?> properties = new Dictionary<string, object?>();
|
||||
// initialize properties
|
||||
Dictionary<string, object?> properties = [];
|
||||
|
||||
// get the properties of the section
|
||||
// openings will have objects assigned to them, but won't have properties
|
||||
// sectionName is initialized with string.Empty, but api ref returns string "None"
|
||||
if (sectionName != "None")
|
||||
// Extract properties if valid section name
|
||||
// "None" is weird but api returns that string if an opening, null element etc.
|
||||
if (sectionName != "None" && !string.IsNullOrEmpty(sectionName))
|
||||
{
|
||||
properties = _propertyExtractor.ExtractFrameSectionProperties(sectionName);
|
||||
}
|
||||
|
||||
// create the section proxy
|
||||
// create section proxy
|
||||
GroupProxy sectionProxy =
|
||||
new()
|
||||
{
|
||||
@@ -66,8 +65,8 @@ public class EtabsSectionUnpacker : ISectionUnpacker
|
||||
name = sectionName,
|
||||
applicationId = sectionName,
|
||||
objects = frameIds,
|
||||
["type"] = "Frame Section", // since sectionProxies are a flat list, need some way to distinguish from shell
|
||||
["properties"] = properties // openings will just have an empty dict here
|
||||
["type"] = "Frame Section",
|
||||
["properties"] = properties
|
||||
};
|
||||
|
||||
yield return sectionProxy;
|
||||
@@ -81,8 +80,8 @@ public class EtabsSectionUnpacker : ISectionUnpacker
|
||||
string sectionName = entry.Key;
|
||||
List<string> frameIds = entry.Value;
|
||||
|
||||
// Initialize properties outside the if statement
|
||||
Dictionary<string, object?> properties = new Dictionary<string, object?>();
|
||||
// initialize properties outside the if statement
|
||||
Dictionary<string, object?> properties = [];
|
||||
|
||||
// get the properties of the section
|
||||
// openings will have objects assigned to them, but won't have properties
|
||||
@@ -92,7 +91,7 @@ public class EtabsSectionUnpacker : ISectionUnpacker
|
||||
properties = _propertyExtractor.ExtractShellSectionProperties(sectionName);
|
||||
}
|
||||
|
||||
// create the section proxy
|
||||
// create section proxy
|
||||
GroupProxy sectionProxy =
|
||||
new()
|
||||
{
|
||||
|
||||
+75
-47
@@ -1,4 +1,5 @@
|
||||
using Speckle.Connectors.CSiShared.HostApp.Helpers;
|
||||
using Speckle.Connectors.ETABSShared.HostApp.Services;
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Converters.CSiShared;
|
||||
using Speckle.Converters.CSiShared.Utils;
|
||||
@@ -8,69 +9,96 @@ namespace Speckle.Connectors.ETABSShared.HostApp.Helpers;
|
||||
/// <summary>
|
||||
/// Extracts ETABS-specific frame section properties.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The bulk loading strategy is necessary here because we can't know which database table contains which section
|
||||
/// beforehand - there are multiple tables like "Frame Section Property Definitions - Steel",
|
||||
/// "Frame Section Property Definitions - Concrete", etc.
|
||||
/// </remarks>
|
||||
public class EtabsFrameSectionPropertyExtractor : IApplicationFrameSectionPropertyExtractor
|
||||
{
|
||||
private readonly IConverterSettingsStore<CsiConversionSettings> _settingsStore;
|
||||
private readonly EtabsSectionPropertyDefinitionService _definitionService;
|
||||
|
||||
public EtabsFrameSectionPropertyExtractor(IConverterSettingsStore<CsiConversionSettings> settingsStore)
|
||||
public EtabsFrameSectionPropertyExtractor(
|
||||
IConverterSettingsStore<CsiConversionSettings> settingsStore,
|
||||
EtabsSectionPropertyDefinitionService definitionService
|
||||
)
|
||||
{
|
||||
_settingsStore = settingsStore;
|
||||
_definitionService = definitionService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets generalised frame section properties
|
||||
/// Gets frame section properties from preloaded database table data
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Sap2000 doesn't support this method, unfortunately
|
||||
/// Alternative is to account for extraction according to section type - we're talking over 40 section types!
|
||||
/// This way, we get basic information with minimal computational costs.
|
||||
/// Property categorization is done heuristically - order matters in the parsing logic.
|
||||
/// </remarks>
|
||||
public void ExtractProperties(string sectionName, Dictionary<string, object?> properties)
|
||||
{
|
||||
// Get all frame properties
|
||||
int numberOfNames = 0;
|
||||
string[] names = [];
|
||||
eFramePropType[] propTypes = [];
|
||||
double[] t3 = [],
|
||||
t2 = [],
|
||||
tf = [],
|
||||
tw = [],
|
||||
t2b = [],
|
||||
tfb = [],
|
||||
area = [];
|
||||
|
||||
_settingsStore.Current.SapModel.PropFrame.GetAllFrameProperties_2(
|
||||
ref numberOfNames,
|
||||
ref names,
|
||||
ref propTypes,
|
||||
ref t3,
|
||||
ref t2,
|
||||
ref tf,
|
||||
ref tw,
|
||||
ref t2b,
|
||||
ref tfb,
|
||||
ref area
|
||||
);
|
||||
|
||||
// Find the index of the current section
|
||||
int sectionIndex = Array.IndexOf(names, sectionName);
|
||||
|
||||
if (sectionIndex != -1)
|
||||
// get frame definitions from the service (which uses database table extraction)
|
||||
// this is a fast dictionary lookup since all data is preloaded
|
||||
if (!_definitionService.FrameDefinitions.TryGetValue(sectionName, out var rawDatabaseTableProperties))
|
||||
{
|
||||
// General Data
|
||||
var generalData = properties.EnsureNested(SectionPropertyCategory.GENERAL_DATA);
|
||||
generalData["Section Shape"] = propTypes[sectionIndex].ToString();
|
||||
return; // no definitions found for this section
|
||||
}
|
||||
|
||||
// Section Dimensions
|
||||
string unit = _settingsStore.Current.SpeckleUnits;
|
||||
var sectionDimensions = properties.EnsureNested(SectionPropertyCategory.SECTION_DIMENSIONS);
|
||||
sectionDimensions.AddWithUnits("t3", t3[sectionIndex], unit);
|
||||
sectionDimensions.AddWithUnits("t2", t2[sectionIndex], unit);
|
||||
sectionDimensions.AddWithUnits("tf", tf[sectionIndex], unit);
|
||||
sectionDimensions.AddWithUnits("tw", tw[sectionIndex], unit);
|
||||
sectionDimensions.AddWithUnits("t2b", t2b[sectionIndex], unit);
|
||||
sectionDimensions.AddWithUnits("tfb", tfb[sectionIndex], unit);
|
||||
sectionDimensions.AddWithUnits("Area", area[sectionIndex], $"{unit}²");
|
||||
// define table keys that we don't want to include in the section proxy properties
|
||||
var keysToExclude = new HashSet<string>
|
||||
{
|
||||
"GUID",
|
||||
"Name",
|
||||
"Color",
|
||||
"Notes",
|
||||
"FileName",
|
||||
"FromFile",
|
||||
"SectInFile",
|
||||
"NotAutoFact"
|
||||
};
|
||||
|
||||
// get the section type / shape using the dedicated api query (exception to the database approach)
|
||||
// this specific property isn't available in the database table extraction
|
||||
eFramePropType framePropType = 0;
|
||||
_settingsStore.Current.SapModel.PropFrame.GetTypeOAPI(sectionName, ref framePropType);
|
||||
Dictionary<string, object?> generalProperties = properties.EnsureNested(SectionPropertyCategory.GENERAL_DATA);
|
||||
generalProperties.Add("Section Shape", framePropType.ToString());
|
||||
|
||||
// heuristic property categorization based on key patterns and parse-ability
|
||||
// NOTE: this is gross and quite dangerous 🤨 but beats specific frame prop sect. property extractions imo
|
||||
// order matters here! we check for known string props first, then modifiers, then assume doubles are dimensions
|
||||
foreach (KeyValuePair<string, string> rawDatabaseTableProperty in rawDatabaseTableProperties)
|
||||
{
|
||||
string key = rawDatabaseTableProperty.Key;
|
||||
string value = rawDatabaseTableProperty.Value;
|
||||
|
||||
// skip metadata fields we don't care about
|
||||
if (!keysToExclude.Contains(key))
|
||||
{
|
||||
// material is always a string, grab it first
|
||||
if (key == "Material")
|
||||
{
|
||||
generalProperties.Add(key, value);
|
||||
}
|
||||
// modifier properties end with "Mod" and should be numeric
|
||||
else if (key.EndsWith("Mod") && double.TryParse(value, out double parsedModValue))
|
||||
{
|
||||
Dictionary<string, object?> modificationProperties = properties.EnsureNested(
|
||||
SectionPropertyCategory.MODIFIERS
|
||||
);
|
||||
modificationProperties.Add(key, parsedModValue);
|
||||
}
|
||||
// anything else that parses as a double is assumed to be a section dimension
|
||||
// this covers things like t3, t2, tf, tw, area, etc. without having to enumerate them all
|
||||
else if (double.TryParse(value, out double parsedDimensionValue))
|
||||
{
|
||||
Dictionary<string, object?> sectionDimensions = properties.EnsureNested(
|
||||
SectionPropertyCategory.SECTION_DIMENSIONS
|
||||
);
|
||||
sectionDimensions.Add(key, parsedDimensionValue);
|
||||
}
|
||||
// if it doesn't parse as double and isn't a known string property, we skip it
|
||||
// this is acceptable - we'd rather miss some edge case properties than crash
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Converters.CSiShared;
|
||||
using Speckle.Converters.CSiShared.ToSpeckle.Helpers;
|
||||
|
||||
namespace Speckle.Connectors.ETABSShared.HostApp.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Loads and caches section property definitions from database tables for both frame and shell sections.
|
||||
/// </summary>
|
||||
public class EtabsSectionPropertyDefinitionService
|
||||
{
|
||||
private readonly IConverterSettingsStore<CsiConversionSettings> _settingsStore;
|
||||
public IReadOnlyDictionary<string, IReadOnlyDictionary<string, string>> FrameDefinitions { get; }
|
||||
public IReadOnlyDictionary<string, IReadOnlyDictionary<string, string>> ShellDefinitions { get; }
|
||||
|
||||
public EtabsSectionPropertyDefinitionService(
|
||||
DatabaseTableExtractor databaseTableExtractor,
|
||||
IConverterSettingsStore<CsiConversionSettings> settingsStore
|
||||
)
|
||||
{
|
||||
_settingsStore = settingsStore;
|
||||
|
||||
var availableTableKeys = GetAvailableTableKeys();
|
||||
|
||||
FrameDefinitions = LoadFrameDefinitions(databaseTableExtractor, availableTableKeys);
|
||||
ShellDefinitions = LoadShellDefinitions(databaseTableExtractor, availableTableKeys);
|
||||
}
|
||||
|
||||
private static IReadOnlyDictionary<string, IReadOnlyDictionary<string, string>> LoadFrameDefinitions(
|
||||
DatabaseTableExtractor databaseTableExtractor,
|
||||
string[] availableTableKeys
|
||||
)
|
||||
{
|
||||
var frameTableKeys = GetFrameSectionPropertyDefinitionTableKeys(availableTableKeys);
|
||||
return LoadDefinitionsFromTables(databaseTableExtractor, frameTableKeys);
|
||||
}
|
||||
|
||||
private static IReadOnlyDictionary<string, IReadOnlyDictionary<string, string>> LoadShellDefinitions(
|
||||
DatabaseTableExtractor databaseTableExtractor,
|
||||
string[] availableTableKeys
|
||||
)
|
||||
{
|
||||
var shellTableKeys = GetShellSectionPropertyDefinitionTableKeys(availableTableKeys);
|
||||
return LoadDefinitionsFromTables(databaseTableExtractor, shellTableKeys);
|
||||
}
|
||||
|
||||
private static IReadOnlyDictionary<string, IReadOnlyDictionary<string, string>> LoadDefinitionsFromTables(
|
||||
DatabaseTableExtractor databaseTableExtractor,
|
||||
IEnumerable<string> tableKeys
|
||||
)
|
||||
{
|
||||
var definitions = new Dictionary<string, IReadOnlyDictionary<string, string>>();
|
||||
|
||||
foreach (string tableKey in tableKeys)
|
||||
{
|
||||
var tableData = databaseTableExtractor.GetTableData(tableKey, "Name");
|
||||
foreach (var row in tableData.Rows)
|
||||
{
|
||||
definitions[row.Key] = row.Value;
|
||||
}
|
||||
}
|
||||
|
||||
return definitions;
|
||||
}
|
||||
|
||||
private static IEnumerable<string> GetFrameSectionPropertyDefinitionTableKeys(string[] availableTableKeys)
|
||||
{
|
||||
var keysToExclude = new HashSet<string>
|
||||
{
|
||||
"Frame Section Property Definitions - Summary",
|
||||
"Frame Section Property Definitions - Concrete Beam Reinforcing",
|
||||
"Frame Section Property Definitions - Concrete Column Reinforcing"
|
||||
};
|
||||
|
||||
return availableTableKeys.Where(key =>
|
||||
key.StartsWith("Frame Section Property Definitions") && !keysToExclude.Contains(key)
|
||||
);
|
||||
}
|
||||
|
||||
private static IEnumerable<string> GetShellSectionPropertyDefinitionTableKeys(string[] availableTableKeys)
|
||||
{
|
||||
var keysToExclude = new HashSet<string> { "Area Section Property Definitions - Summary" };
|
||||
|
||||
return availableTableKeys.Where(key =>
|
||||
key.StartsWith("Area Section Property Definitions") && !keysToExclude.Contains(key)
|
||||
);
|
||||
}
|
||||
|
||||
private string[] GetAvailableTableKeys()
|
||||
{
|
||||
int numberTables = 0;
|
||||
string[] tableKey = [],
|
||||
tableName = [];
|
||||
int[] importType = [];
|
||||
|
||||
_ = _settingsStore.Current.SapModel.DatabaseTables.GetAvailableTables(
|
||||
ref numberTables,
|
||||
ref tableKey,
|
||||
ref tableName,
|
||||
ref importType
|
||||
);
|
||||
|
||||
return tableKey;
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -34,7 +34,7 @@ public class EtabsSectionPropertyExtractor
|
||||
/// </summary>
|
||||
public Dictionary<string, object?> ExtractFrameSectionProperties(string sectionName)
|
||||
{
|
||||
Dictionary<string, object?> properties = new();
|
||||
Dictionary<string, object?> properties = [];
|
||||
_csiFrameExtractor.ExtractProperties(sectionName, properties);
|
||||
_etabsFrameExtractor.ExtractProperties(sectionName, properties);
|
||||
return properties;
|
||||
@@ -45,7 +45,7 @@ public class EtabsSectionPropertyExtractor
|
||||
/// </summary>
|
||||
public Dictionary<string, object?> ExtractShellSectionProperties(string sectionName)
|
||||
{
|
||||
Dictionary<string, object?> properties = new();
|
||||
Dictionary<string, object?> properties = [];
|
||||
_csiShellExtractor.ExtractProperties(sectionName, properties);
|
||||
_etabsShellExtractor.ExtractProperties(sectionName, properties);
|
||||
return properties;
|
||||
|
||||
@@ -3,6 +3,7 @@ using Speckle.Connectors.CSiShared.HostApp;
|
||||
using Speckle.Connectors.CSiShared.HostApp.Helpers;
|
||||
using Speckle.Connectors.ETABSShared.HostApp;
|
||||
using Speckle.Connectors.ETABSShared.HostApp.Helpers;
|
||||
using Speckle.Connectors.ETABSShared.HostApp.Services;
|
||||
using Speckle.Converters.ETABSShared;
|
||||
|
||||
namespace Speckle.Connectors.ETABSShared;
|
||||
@@ -12,11 +13,12 @@ public static class ServiceRegistration
|
||||
public static IServiceCollection AddEtabs(this IServiceCollection services)
|
||||
{
|
||||
services.AddEtabsConverters();
|
||||
services.AddScoped<IApplicationFrameSectionPropertyExtractor, EtabsFrameSectionPropertyExtractor>();
|
||||
services.AddScoped<IApplicationShellSectionPropertyExtractor, EtabsShellSectionPropertyExtractor>();
|
||||
services.AddScoped<CsiSendCollectionManager, EtabsSendCollectionManager>();
|
||||
services.AddScoped<EtabsSectionPropertyDefinitionService>();
|
||||
services.AddScoped<EtabsSectionPropertyExtractor>();
|
||||
services.AddScoped<EtabsShellSectionResolver>();
|
||||
services.AddScoped<CsiSendCollectionManager, EtabsSendCollectionManager>();
|
||||
services.AddScoped<IApplicationFrameSectionPropertyExtractor, EtabsFrameSectionPropertyExtractor>();
|
||||
services.AddScoped<IApplicationShellSectionPropertyExtractor, EtabsShellSectionPropertyExtractor>();
|
||||
services.AddScoped<ISectionUnpacker, EtabsSectionUnpacker>();
|
||||
|
||||
return services;
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\EtabsSectionUnpacker.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\EtabsSendCollectionManager.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers\EtabsFrameSectionPropertyExtractor.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers\EtabsSectionPropertyDefinitionService.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers\EtabsSectionPropertyExtractor.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers\EtabsShellSectionPropertyExtractor.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers\EtabsShellSectionResolver.cs" />
|
||||
|
||||
-117
@@ -112,49 +112,10 @@ 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));
|
||||
@@ -330,84 +291,6 @@ 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>
|
||||
|
||||
@@ -32,6 +32,7 @@ public static class ServiceRegistration
|
||||
serviceCollection.AddScoped<CsiPierForceResultsExtractor>();
|
||||
serviceCollection.AddScoped<CsiSpandrelForceResultsExtractor>();
|
||||
serviceCollection.AddScoped<CsiStoryDriftsResultsExtractor>();
|
||||
serviceCollection.AddScoped<CsiStoryForceResultsExtractor>();
|
||||
serviceCollection.AddScoped<ResultsArrayProcessor>();
|
||||
|
||||
// Register connector caches
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToSpeckle\Helpers\CsiShellPropertiesExtractor.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToSpeckle\Helpers\CsiSpandrelForceResultsExtractor.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToSpeckle\Helpers\CsiStoryDriftsResultsExtractor.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToSpeckle\Helpers\CsiStoryForceResultsExtractor.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToSpeckle\Helpers\CsiToSpeckleCacheSingleton.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToSpeckle\Helpers\DatabaseTableExtractor.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToSpeckle\Helpers\IApplicationResultsExtractor.cs" />
|
||||
|
||||
+156
@@ -0,0 +1,156 @@
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Converters.CSiShared.Utils;
|
||||
|
||||
namespace Speckle.Converters.CSiShared.ToSpeckle.Helpers;
|
||||
|
||||
public class CsiStoryForceResultsExtractor : IApplicationResultsExtractor
|
||||
{
|
||||
private readonly IConverterSettingsStore<CsiConversionSettings> _settingsStore;
|
||||
private readonly DatabaseTableExtractor _databaseTableExtractor;
|
||||
private readonly ResultsArrayProcessor _resultsArrayProcessor;
|
||||
|
||||
private const string AXIAL_FORCE = "P";
|
||||
private const string LOAD_CASE = "LoadCase";
|
||||
private const string LOCATION = "Location";
|
||||
private const string MAJOR_MOMENT = "MX";
|
||||
private const string MAJOR_SHEAR = "VX";
|
||||
private const string MINOR_MOMENT = "MY";
|
||||
private const string MINOR_SHEAR = "VY";
|
||||
private const string OUTPUT_CASE = "OutputCase";
|
||||
private const string STORY = "Story";
|
||||
private const string STORY_FORCES = "Story Forces";
|
||||
private const string TORSION = "T";
|
||||
|
||||
public string ResultsKey => "storyForces";
|
||||
public ModelObjectType TargetObjectType => ModelObjectType.NONE;
|
||||
public ResultsConfiguration Configuration { get; } =
|
||||
new([STORY, LOAD_CASE, LOCATION], [AXIAL_FORCE, MAJOR_SHEAR, MINOR_SHEAR, TORSION, MAJOR_MOMENT, MINOR_MOMENT]);
|
||||
|
||||
public CsiStoryForceResultsExtractor(
|
||||
IConverterSettingsStore<CsiConversionSettings> settingsStore,
|
||||
DatabaseTableExtractor databaseTableExtractor,
|
||||
ResultsArrayProcessor resultsArrayProcessor
|
||||
)
|
||||
{
|
||||
_settingsStore = settingsStore;
|
||||
_databaseTableExtractor = databaseTableExtractor;
|
||||
_resultsArrayProcessor = resultsArrayProcessor;
|
||||
}
|
||||
|
||||
// NOTE: these aren't object specific, they're independent of the user selection, therefore discared
|
||||
public Dictionary<string, object> GetResults(IEnumerable<string>? objectNames = null)
|
||||
{
|
||||
// Step 1: use DatabaseTableExtractor to get results
|
||||
// NOTE: this differs from other results since Story Forces doesn't have a SapModel.Results.StoryForces method
|
||||
var tableData = _databaseTableExtractor
|
||||
.GetTableData(STORY_FORCES, STORY, additionalKeyColumns: [OUTPUT_CASE, LOCATION])
|
||||
.Rows;
|
||||
|
||||
// Get user selected load cases and combinations for filtering
|
||||
var userSelectedLoadCases = _settingsStore.Current.SelectedLoadCasesAndCombinations?.ToHashSet();
|
||||
|
||||
if (userSelectedLoadCases == null)
|
||||
{
|
||||
// NOTE: this should never happen as we validate in root object builder
|
||||
throw new InvalidOperationException("No load cases or combinations selected");
|
||||
}
|
||||
|
||||
// Step 2: Filter out entries that don't match user's selected load cases/combinations
|
||||
// and organize arrays for dictionary processor
|
||||
var filteredEntries = tableData
|
||||
.Where(entry => userSelectedLoadCases.Count == 0 || userSelectedLoadCases.Contains(GetOutputCase(entry.Value)))
|
||||
.ToList();
|
||||
|
||||
if (filteredEntries.Count == 0)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"No load cases or combinations in database match user-selected load cases and combinations"
|
||||
); // shouldn't fail silently
|
||||
}
|
||||
|
||||
// Step 3: Extract arrays from the nested dictionary structure
|
||||
var stories = new string[filteredEntries.Count];
|
||||
var loadCases = new string[filteredEntries.Count];
|
||||
var locations = new string[filteredEntries.Count];
|
||||
var pValues = new double[filteredEntries.Count];
|
||||
var vxValues = new double[filteredEntries.Count];
|
||||
var vyValues = new double[filteredEntries.Count];
|
||||
var tValues = new double[filteredEntries.Count];
|
||||
var mxValues = new double[filteredEntries.Count];
|
||||
var myValues = new double[filteredEntries.Count];
|
||||
|
||||
for (int i = 0; i < filteredEntries.Count; i++)
|
||||
{
|
||||
var entry = filteredEntries[i];
|
||||
var nestedDict = entry.Value;
|
||||
|
||||
// Extract Story, OutputCase, and Location directly from the nested dictionary
|
||||
if (!nestedDict.TryGetValue(STORY, out var story) || string.IsNullOrEmpty(story))
|
||||
{
|
||||
throw new InvalidOperationException($"Missing or empty 'Story' column in database row {i}");
|
||||
}
|
||||
stories[i] = story;
|
||||
loadCases[i] = nestedDict.TryGetValue(OUTPUT_CASE, out var loadCase) ? loadCase : string.Empty;
|
||||
locations[i] = nestedDict.TryGetValue(LOCATION, out var location) ? location : string.Empty;
|
||||
|
||||
// Extract force values directly from nested dictionary using field names as keys
|
||||
pValues[i] = TryParseDouble(nestedDict.TryGetValue(AXIAL_FORCE, out var p) ? p : null);
|
||||
vxValues[i] = TryParseDouble(nestedDict.TryGetValue(MAJOR_SHEAR, out var vx) ? vx : null);
|
||||
vyValues[i] = TryParseDouble(nestedDict.TryGetValue(MINOR_SHEAR, out var vy) ? vy : null);
|
||||
tValues[i] = TryParseDouble(nestedDict.TryGetValue(TORSION, out var t) ? t : null);
|
||||
mxValues[i] = TryParseDouble(nestedDict.TryGetValue(MAJOR_MOMENT, out var mx) ? mx : null);
|
||||
myValues[i] = TryParseDouble(nestedDict.TryGetValue(MINOR_MOMENT, out var my) ? my : null);
|
||||
}
|
||||
|
||||
var rawArrays = new Dictionary<string, object>
|
||||
{
|
||||
[STORY] = stories,
|
||||
[LOAD_CASE] = loadCases,
|
||||
[LOCATION] = locations,
|
||||
[AXIAL_FORCE] = pValues,
|
||||
[MAJOR_SHEAR] = vxValues,
|
||||
[MINOR_SHEAR] = vyValues,
|
||||
[TORSION] = tValues,
|
||||
[MAJOR_MOMENT] = mxValues,
|
||||
[MINOR_MOMENT] = myValues
|
||||
};
|
||||
|
||||
// Step 4: return sorted and processed dictionary
|
||||
return _resultsArrayProcessor.ProcessArrays(rawArrays, Configuration);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extracts the OutputCase from the nested dictionary structure.
|
||||
/// This is used for filtering against user selected load cases.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// All database values are strings
|
||||
/// </remarks>
|
||||
private static string GetOutputCase(IReadOnlyDictionary<string, string> nestedDict) =>
|
||||
nestedDict.TryGetValue(OUTPUT_CASE, out var outputCase) ? outputCase : string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Safely parses a value to double, returning 0.0 if parsing fails.
|
||||
/// Database returns all values as strings, so conversion is needed.
|
||||
/// </summary>
|
||||
private static double TryParseDouble(object? value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot parse null value to double in story force results");
|
||||
}
|
||||
|
||||
var stringValue = value.ToString();
|
||||
if (string.IsNullOrEmpty(stringValue))
|
||||
{
|
||||
throw new InvalidOperationException("Cannot parse empty string to double in story force results");
|
||||
}
|
||||
|
||||
if (!double.TryParse(stringValue, out double result))
|
||||
{
|
||||
throw new InvalidOperationException($"Failed to parse '{stringValue}' as double in story force results");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -5,15 +5,15 @@ public class CsiToSpeckleCacheSingleton
|
||||
/// <summary>
|
||||
/// A map of (material id, section ids). Assumes the material id is the unique name of the material
|
||||
/// </summary>
|
||||
public Dictionary<string, List<string>> MaterialCache { get; set; } = new();
|
||||
public Dictionary<string, List<string>> MaterialCache { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// A map of (section id, frame object id). Assumes the section id is the unique name of the section
|
||||
/// </summary>
|
||||
public Dictionary<string, List<string>> FrameSectionCache { get; set; } = new();
|
||||
public Dictionary<string, List<string>> FrameSectionCache { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// A map of (section id, shell object id). Assumes the section id is the unique name of the section
|
||||
/// </summary>
|
||||
public Dictionary<string, List<string>> ShellSectionCache { get; set; } = new();
|
||||
public Dictionary<string, List<string>> ShellSectionCache { get; set; } = [];
|
||||
}
|
||||
|
||||
@@ -31,9 +31,10 @@ public static class ObjectPropertyKey
|
||||
public static class SectionPropertyCategory
|
||||
{
|
||||
public const string GENERAL_DATA = "General Data";
|
||||
public const string MODIFIERS = "Modifiers";
|
||||
public const string PROPERTY_DATA = "Property Data";
|
||||
public const string SECTION_PROPERTIES = "Section Properties";
|
||||
public const string SECTION_DIMENSIONS = "Section Dimensions";
|
||||
public const string PROPERTY_DATA = "Property Data";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -65,6 +66,7 @@ public static class ResultsKey
|
||||
public const string PIER_FORCES = "Pier Forces";
|
||||
public const string SPANDREL_FORCES = "Spandrel Forces";
|
||||
public const string STORY_DRIFTS = "Story Drifts";
|
||||
public const string STORY_FORCES = "Story Forces";
|
||||
|
||||
// Used by ResultTypeSetting to get all defined result keys
|
||||
public static readonly string[] All =
|
||||
@@ -75,6 +77,7 @@ public static class ResultsKey
|
||||
MODAL_PERIOD,
|
||||
PIER_FORCES,
|
||||
SPANDREL_FORCES,
|
||||
STORY_DRIFTS
|
||||
STORY_DRIFTS,
|
||||
STORY_FORCES
|
||||
];
|
||||
}
|
||||
|
||||
+1
-4
@@ -33,9 +33,6 @@ public class DisplayValueExtractor
|
||||
return [];
|
||||
}
|
||||
|
||||
// Check if this is an instance definition by looking at InstanceHashCode
|
||||
bool isInstanceDefinition = modelItem.InstanceHashCode != 0;
|
||||
|
||||
return !IsElementVisible(modelItem) ? [] : _geometryConverter.Convert(modelItem, isInstanceDefinition);
|
||||
return !IsElementVisible(modelItem) ? [] : _geometryConverter.Convert(modelItem);
|
||||
}
|
||||
}
|
||||
|
||||
-44
@@ -1,44 +0,0 @@
|
||||
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,5 +50,4 @@ public class NavisworksRootToSpeckleConverter : IRootToSpeckleConverter
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+35
-69
@@ -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, bool isInstanceDefinition = false)
|
||||
internal List<Base> Convert(NAV.ModelItem modelItem)
|
||||
{
|
||||
if (modelItem == null)
|
||||
{
|
||||
@@ -64,7 +64,7 @@ public class GeometryToSpeckleConverter
|
||||
CollectFragments(path, fragmentStack);
|
||||
}
|
||||
|
||||
return ProcessFragments(fragmentStack, paths, isInstanceDefinition);
|
||||
return ProcessFragments(fragmentStack, paths);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -103,7 +103,7 @@ public class GeometryToSpeckleConverter
|
||||
}
|
||||
}
|
||||
|
||||
private List<Base> ProcessFragments(Stack<InwOaFragment3> fragmentStack, InwSelectionPathsColl paths, bool isInstanceDefinition = false)
|
||||
private List<Base> ProcessFragments(Stack<InwOaFragment3> fragmentStack, InwSelectionPathsColl paths)
|
||||
{
|
||||
var callbackListeners = new List<PrimitiveProcessor>();
|
||||
|
||||
@@ -132,7 +132,7 @@ public class GeometryToSpeckleConverter
|
||||
callbackListeners.Add(processor);
|
||||
}
|
||||
|
||||
var baseGeometries = ProcessGeometries(callbackListeners, isInstanceDefinition);
|
||||
var baseGeometries = ProcessGeometries(callbackListeners);
|
||||
|
||||
return baseGeometries;
|
||||
}
|
||||
@@ -147,7 +147,7 @@ public class GeometryToSpeckleConverter
|
||||
return IsSameFragmentPath(fragmentPathData, pathData);
|
||||
}
|
||||
|
||||
private List<Base> ProcessGeometries(List<PrimitiveProcessor> processors, bool isInstanceDefinition = false)
|
||||
private List<Base> ProcessGeometries(List<PrimitiveProcessor> processors)
|
||||
{
|
||||
var baseGeometries = new List<Base>();
|
||||
|
||||
@@ -155,13 +155,13 @@ public class GeometryToSpeckleConverter
|
||||
{
|
||||
if (processor.Triangles.Count > 0)
|
||||
{
|
||||
var mesh = CreateMesh(processor.Triangles, isInstanceDefinition);
|
||||
var mesh = CreateMesh(processor.Triangles);
|
||||
baseGeometries.Add(mesh);
|
||||
}
|
||||
|
||||
if (processor.Lines.Count > 0)
|
||||
{
|
||||
var lines = CreateLines(processor.Lines, isInstanceDefinition);
|
||||
var lines = CreateLines(processor.Lines);
|
||||
baseGeometries.AddRange(lines);
|
||||
}
|
||||
}
|
||||
@@ -169,7 +169,7 @@ public class GeometryToSpeckleConverter
|
||||
return baseGeometries;
|
||||
}
|
||||
|
||||
private Mesh CreateMesh(IReadOnlyList<SafeTriangle> triangles, bool isInstanceDefinition = false)
|
||||
private Mesh CreateMesh(IReadOnlyList<SafeTriangle> triangles)
|
||||
{
|
||||
var vertices = new List<double>();
|
||||
var faces = new List<int>();
|
||||
@@ -178,40 +178,20 @@ public class GeometryToSpeckleConverter
|
||||
{
|
||||
var triangle = triangles[t];
|
||||
|
||||
// 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
|
||||
]
|
||||
);
|
||||
}
|
||||
// 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
|
||||
]
|
||||
);
|
||||
faces.AddRange([3, t * 3, t * 3 + 1, t * 3 + 2]);
|
||||
}
|
||||
|
||||
@@ -223,37 +203,23 @@ public class GeometryToSpeckleConverter
|
||||
};
|
||||
}
|
||||
|
||||
private List<Line> CreateLines(IReadOnlyList<SafeLine> lines, bool isInstanceDefinition = false) =>
|
||||
private List<Line> CreateLines(IReadOnlyList<SafeLine> lines) =>
|
||||
(
|
||||
from line in lines
|
||||
select new Line
|
||||
{
|
||||
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
|
||||
),
|
||||
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
|
||||
),
|
||||
units = _settings.Derived.SpeckleUnits
|
||||
}
|
||||
).ToList();
|
||||
|
||||
-3
@@ -40,7 +40,6 @@ 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)
|
||||
{
|
||||
@@ -52,7 +51,6 @@ 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()
|
||||
@@ -63,7 +61,6 @@ 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