diff --git a/Install/Program Files to Install/Autodesk.SteelConnections.ASIFC.dll b/Install/Program Files to Install/Autodesk.SteelConnections.ASIFC.dll
index 5d1825ae..40dfb8f3 100644
Binary files a/Install/Program Files to Install/Autodesk.SteelConnections.ASIFC.dll and b/Install/Program Files to Install/Autodesk.SteelConnections.ASIFC.dll differ
diff --git a/Install/Program Files to Install/bundle/Contents/Resources/ADSKIFCExporterHelp.htm b/Install/Program Files to Install/bundle/Contents/Resources/ADSKIFCExporterHelp.htm
index 4e267742..7fe0da32 100644
--- a/Install/Program Files to Install/bundle/Contents/Resources/ADSKIFCExporterHelp.htm
+++ b/Install/Program Files to Install/bundle/Contents/Resources/ADSKIFCExporterHelp.htm
@@ -236,6 +236,73 @@
Support Information
or if you have an inquiry specific to this add-in, send us an e-mail to: Revit.apps@autodesk.com
Version History
+
24.2.20.0
+
+ General:
+
+
This is the an update of IFC Exporter for Revit 2024.
+
It contains a various improvements and bug fixes for the basic Revit 2024.
+
+
+
+
+ Improvements:
+
+
Added filename to the journal when export IFC if the default is used.
+
Added information to IfcOwnerHistory when exporting to IFC.
+
Add new user defined units export.
+
Added Revit Content Identifier to IFC export.
+
Added Width as an exported quantity to IFC for some assembly-based walls.
+
Allowed export of some elements to IFC4 Reference View [Structural] that had no material assignments.
+
Allowed some old IFC configuration settings to be upgraded.
+
Changed the behavior of the warning message when exporting to IFC 4 if elements in a Revit model are far from the origin so that the warning appears only if an EPSG code is also set.
+
Implemented unique and consistent naming for IfcShapeAspect and IfcMaterialConstituent pairs.
+
Improved area and volume base quantities export for slabs as part of the roof.
+
Improved exporting all layers of some walls to IFC 4 when the option to split by level was chosen.
+
Improved stability of exporting to IFC when the Revit model has corrupted extensible storage data.
+
Improved stability when exporting files to IFC with some short, invalid IFC entity names set in the Export to IFC As and Export Type to IFC As parameters.
+
Improved support for adding properties to PSet_ZoneCommon.
+
Improved the color assignments for some elements exported to IFC 4.
+
Improved the export of current view only IFC files when the phase of the view has changed since it was first set in the IFC export settings.
+
+
+
+
+ Bug Fixes:
+
+
Added type parameter set export for Revit elements whose corresponding IFC 2x3 entity didn't have a type entity associated with it, such as ramps, stairs, and footings.
+
Corrected placement of some families inside assemblies when exporting to IFC.
+
Fix IfcCountMeasure for IFC4x3.
+
Fixed a shifting of TriangulatedFaceSet geometry elements.
+
Fixed body representation for walls export to the IFC4RV.
+
Fixed bug with renaming IFC configuration makes the revit-session remember the old and the new IFC configuration.
+
Fixed classifications export for systems.
+
Fixed export of linked file orientation when exporting linked files to the same site.
+
Fixed export of model line placement elevation for 2D representation.
+
Fixed export of room elevation.
+
Fixed export of walls with openings placed next to clippings.
+
Fixed export of GrossArea to IFC4 QTO base quantities for Revit walls exported as IfcCovering.
+
Fixed a IsExternal parameter for windows exported to IFC.
+
Fixed a placement of beam openings.
+
Fixed the processing of the Export to IFC parameter for the assemblies.
+
Fixed Width parameter for IfcSlab and IfcCovering exported to IFC4 QTO base quantities.
+
Improved base quantities calculation for slab elements with openings.
+
Improved base quantities calculation for spatial elements.
+
Improved calculations of gross volume and gross side area when exporting some walls with openings to IFC 4.
+
Improved error handling for invalid Revit file with missing project base and survey points.
+
Improved export of some beams to IFC where the geometry was previously inverted in the IFC file.
+
Improved export of some beams with openings to IFC 4.
+
Improved export of some missing elements when exporting linked documents to IFC.
+
Improved the export of some walls that had doors or windows with multiple voids in the original family.
+
Improved performance of some elongated elements export to IFC 4.
+
Improved linking invalid IFC files with unbounded curves as part of their geometric representations.
+
Removed some IFC export configuration settings that didn't get properly renamed or deleted by Revit.
+
+
+
+
+
+
24.2.0.49
General:
diff --git a/Install/Program Files to Install/bundle/PackageContents.xml b/Install/Program Files to Install/bundle/PackageContents.xml
index 3ec722c5..c3d6a9be 100644
--- a/Install/Program Files to Install/bundle/PackageContents.xml
+++ b/Install/Program Files to Install/bundle/PackageContents.xml
@@ -4,7 +4,7 @@
-
-
+
+
\ No newline at end of file
diff --git a/Install/RevitIFCSetupWix/Product.wxs b/Install/RevitIFCSetupWix/Product.wxs
index 32dd1740..ca94b1e4 100644
--- a/Install/RevitIFCSetupWix/Product.wxs
+++ b/Install/RevitIFCSetupWix/Product.wxs
@@ -2,7 +2,7 @@
-
+
diff --git a/Install/RevitIFCSetupWix/RevitIFCSetupWix.wixproj b/Install/RevitIFCSetupWix/RevitIFCSetupWix.wixproj
index 4556867e..a6d1ef85 100644
--- a/Install/RevitIFCSetupWix/RevitIFCSetupWix.wixproj
+++ b/Install/RevitIFCSetupWix/RevitIFCSetupWix.wixproj
@@ -6,7 +6,7 @@
3.87dfbd495-c588-4c7b-b8f6-5b793adb06f22.0
- IFC for Revit 2024.2.0.49
+ IFC for Revit 2024.2.20.0Package$(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets$(MSBuildExtensionsPath)\Microsoft\WiX\v3.x\Wix.targets
diff --git a/Install/RevitIFCSetupWix/buildInstaller.bat b/Install/RevitIFCSetupWix/buildInstaller.bat
index 4f114331..d304abe6 100644
--- a/Install/RevitIFCSetupWix/buildInstaller.bat
+++ b/Install/RevitIFCSetupWix/buildInstaller.bat
@@ -11,9 +11,9 @@ rem It is necessary to add the Wix bin directory to the system path temporarily
SET PATH=%PATH%;%WixRoot%
candle.exe -dProjectDir=%2 -ext WixUtilExtension %2Product.wxs
-light.exe -ext WixUtilExtension -out RevitIFC2024.2.0.msi product.wixobj -ext WixUIExtension
+light.exe -ext WixUtilExtension -out RevitIFC2024.2.20.msi product.wixobj -ext WixUIExtension
-copy RevitIFC2024.2.0.msi %1..\Releasex64
-del RevitIFC2024.2.0.msi
+copy RevitIFC2024.2.20.msi %1..\Releasex64
+del RevitIFC2024.2.20.msi
-echo %1..\Releasex64\RevitIFC2024.2.0.msi
+echo %1..\Releasex64\RevitIFC2024.2.20.msi
diff --git a/Source/IFCExporterUIOverride/IFCCommandOverrideApplication.cs b/Source/IFCExporterUIOverride/IFCCommandOverrideApplication.cs
index b33d30a1..7cd8cbe7 100644
--- a/Source/IFCExporterUIOverride/IFCCommandOverrideApplication.cs
+++ b/Source/IFCExporterUIOverride/IFCCommandOverrideApplication.cs
@@ -33,7 +33,6 @@
using Autodesk.Revit.DB.ExternalService;
-using SaveFileDialog = Microsoft.Win32.SaveFileDialog;
using View = Autodesk.Revit.DB.View;
using System.Windows.Forms;
@@ -61,7 +60,6 @@ public Result OnShutdown(UIControlledApplication application)
{
// Clean up
m_ifcCommandBinding.Executed -= OnIFCExport;
-
return Result.Succeeded;
}
@@ -106,7 +104,7 @@ private void ApplicationInitialized(object sender, ApplicationInitializedEventAr
entUIService.AddServer(browseIFCEntityServer);
entUIService.SetActiveServer(browseIFCEntityServer.GetServerId());
}
- catch {}
+ catch { }
}
}
@@ -255,7 +253,7 @@ public void OnIFCExport(object sender, CommandEventArgs args)
// one of the exports. Prevent that by keeping track of the exported file names.
ISet exportedFileNames = new HashSet();
- bool exportLinks =
+ bool exportLinks =
selectedConfig.ExportLinkedFiles != LinkedFileExportAs.DontExport;
bool exportSeparateLinks =
selectedConfig.ExportLinkedFiles == LinkedFileExportAs.ExportAsSeparate;
@@ -295,10 +293,7 @@ public void OnIFCExport(object sender, CommandEventArgs args)
selectedConfig.ActiveViewId = selectedConfig.UseActiveViewGeometry ? activeViewId : ElementId.InvalidElementId;
selectedConfig.UpdateOptions(exportOptions, activeViewId);
- // This will eventually become an option. Hardwired for testing to be
- // NOT exporting linked files as separate IFC files.
-
- IDictionary linkGUIDsCache =
+ IDictionary linkGUIDsCache =
new Dictionary();
IDictionary linkInstanceTranforms = null;
@@ -540,14 +535,14 @@ private string GetLinkFileName(Document linkDocument, string linkPathName)
if (tr.HasReflection)
{
instHasReflection.Add(instanceId);
- numBadInstances++;
+ numBadInstances++;
continue;
}
if (!MathUtil.IsAlmostEqual(tr.Determinant, 1.0))
{
scaledInst.Add(instanceId);
- numBadInstances++;
+ numBadInstances++;
continue;
}
@@ -584,10 +579,10 @@ public bool IsLinkVisible(Element element, View filterView)
return false;
return filterView.IsElementVisibleInTemporaryViewMode(TemporaryViewMode.TemporaryHideIsolate, element.Id);
- }
+ }
public void ExportLinkedDocuments(Document document, string fileName,
- IDictionary linkGUIDsCache,
+ IDictionary linkGUIDsCache,
IDictionary idToTransform,
IFCExportOptions exportOptions, ElementId originalFilterViewId)
{
diff --git a/Source/IFCExporterUIOverride/IFCExport.xaml.cs b/Source/IFCExporterUIOverride/IFCExport.xaml.cs
index 820e867e..3a6508a4 100644
--- a/Source/IFCExporterUIOverride/IFCExport.xaml.cs
+++ b/Source/IFCExporterUIOverride/IFCExport.xaml.cs
@@ -507,6 +507,8 @@ private void buttonExport_Click(object sender, RoutedEventArgs args)
return;
}
+ TheDocument.Application.WriteJournalComment(@"Jrn.Data ""File Name"", ""IDOK"", " + @"""" + textBoxSetupFileName.Text + @"""", true);
+
IFCExportConfiguration selectedConfig = GetSelectedConfiguration();
if (OptionsUtil.ExportAs4DesignTransferView(selectedConfig.IFCVersion))
{
@@ -527,7 +529,8 @@ private void buttonExport_Click(object sender, RoutedEventArgs args)
selectedConfig = LastSelectedConfig[selectedConfig.Name];
// This check will be done only for IFC4 and above as this only affects IfcMapConversion use that starts in IFC4 onward
- if (!OptionsUtil.ExportAsOlderThanIFC4(selectedConfig.IFCVersion))
+ if (!OptionsUtil.ExportAsOlderThanIFC4(selectedConfig.IFCVersion) &&
+ !string.IsNullOrWhiteSpace(selectedConfig.GeoRefEPSGCode))
{
// Check whether the resulting offset (to wcs) will be too large due to geo-reference information, raise warning
BasePoint surveyPoint = BasePoint.GetSurveyPoint(TheDocument);
diff --git a/Source/IFCExporterUIOverride/IFCExportConfiguration.cs b/Source/IFCExporterUIOverride/IFCExportConfiguration.cs
index 89035c52..a108d108 100644
--- a/Source/IFCExporterUIOverride/IFCExportConfiguration.cs
+++ b/Source/IFCExporterUIOverride/IFCExportConfiguration.cs
@@ -58,13 +58,13 @@ public class IFCExportConfiguration
///
public IFCVersion IFCVersion { get; set; } = IFCVersion.IFC2x3CV2;
- private KnownERNames exchangeRequirement = KnownERNames.NotDefined;
+ private KnownERNames m_ExchangeRequirement = KnownERNames.NotDefined;
public KnownERNames ExchangeRequirement
{
get
{
- return exchangeRequirement;
+ return m_ExchangeRequirement;
}
set
{
@@ -72,7 +72,9 @@ public KnownERNames ExchangeRequirement
{
IList erList = IFCExchangeRequirements.ExchangeRequirements[IFCVersion];
if (erList != null && erList.Contains(value))
- exchangeRequirement = value;
+ {
+ m_ExchangeRequirement = value;
+ }
}
}
}
@@ -286,6 +288,15 @@ public KnownERNames ExchangeRequirement
///
public bool UseTypeNameOnlyForIfcType { get; set; } = false;
+ ///
+ /// Don't create a container entity for floors and roofs unless exporting parts
+ ///
+ public bool ExportHostAsSingleEntity { get; set; } = false;
+
+ ///
+ /// Use Author field in Project Information to set IfcOwnerHistory LastModified attribute
+ ///
+ public bool OwnerHistoryLastModified { get; set; } = false;
///
/// Value indicating whether the IFC Entity Name will use visible Revit Name
///
@@ -593,7 +604,9 @@ public void UpdateBuiltInConfiguration(IFCExportConfiguration updatedConfig)
/// The id of the view that will be used to select which elements to export.
public void UpdateOptions(IFCExportOptions options, ElementId filterViewId)
{
- JavaScriptSerializer ser = new JavaScriptSerializer();
+ JavaScriptSerializer ser = new JavaScriptSerializer();
+ options.FilterViewId = VisibleElementsOfCurrentView ? filterViewId : ElementId.InvalidElementId;
+
foreach (var prop in GetType().GetProperties())
{
switch (prop.Name)
@@ -605,7 +618,7 @@ public void UpdateOptions(IFCExportOptions options, ElementId filterViewId)
options.FileVersion = IFCVersion;
break;
case "ActivePhaseId":
- if (IFCPhaseAttributes.Validate(ActivePhaseId))
+ if (options.FilterViewId == ElementId.InvalidElementId && IFCPhaseAttributes.Validate(ActivePhaseId))
options.AddOption(prop.Name, ActivePhaseId.ToString());
break;
case "SpaceBoundaries":
@@ -632,8 +645,6 @@ public void UpdateOptions(IFCExportOptions options, ElementId filterViewId)
break;
}
}
-
- options.FilterViewId = VisibleElementsOfCurrentView ? filterViewId : ElementId.InvalidElementId;
}
diff --git a/Source/IFCExporterUIOverride/IFCExportConfigurationsMap.cs b/Source/IFCExporterUIOverride/IFCExportConfigurationsMap.cs
index 6e86b488..19e7e3c4 100644
--- a/Source/IFCExporterUIOverride/IFCExportConfigurationsMap.cs
+++ b/Source/IFCExporterUIOverride/IFCExportConfigurationsMap.cs
@@ -202,6 +202,9 @@ public void AddSavedConfigurations()
Field fieldTessellationLevelOfDetail = m_OldSchema.GetField(s_setupTessellationLevelOfDetail);
if (fieldTessellationLevelOfDetail != null)
configuration.TessellationLevelOfDetail = configEntity.Get(s_setupTessellationLevelOfDetail);
+ Field fieldOwnerHistoryLastModified = m_OldSchema.GetField(s_ownerHistoryLastModified);
+ if (fieldOwnerHistoryLastModified != null)
+ configuration.OwnerHistoryLastModified = configEntity.Get(s_ownerHistoryLastModified);
AddOrReplace(configuration);
}
@@ -286,6 +289,8 @@ public void AddSavedConfigurations()
configuration.UseVisibleRevitNameAsEntityName = bool.Parse(configMap[s_useVisibleRevitNameAsEntityName]);
if (configMap.ContainsKey(s_useOnlyTriangulation))
configuration.UseOnlyTriangulation = bool.Parse(configMap[s_useOnlyTriangulation]);
+ if (configMap.ContainsKey(s_ownerHistoryLastModified))
+ configuration.OwnerHistoryLastModified = bool.Parse(configMap[s_ownerHistoryLastModified]);
if (configMap.ContainsKey(s_setupTessellationLevelOfDetail))
configuration.TessellationLevelOfDetail = double.Parse(configMap[s_setupTessellationLevelOfDetail]);
if (configMap.ContainsKey(s_setupSitePlacement))
@@ -333,6 +338,7 @@ public void AddSavedConfigurations()
catch (Exception)
{
// don't skip all configurations if an exception occurs for one
+ IFCCommandOverrideApplication.TheDocument.Application.WriteJournalComment("IFC error: Cannot read IFCExportConfigurationMap schema", true);
}
}
}
@@ -393,6 +399,7 @@ public void AddSavedConfigurations()
private const string s_setupSitePlacement = "SitePlacement";
private const string s_useTypeNameOnlyForIfcType = "UseTypeNameOnlyForIfcType";
private const string s_useVisibleRevitNameAsEntityName = "UseVisibleRevitNameAsEntityName";
+ private const string s_ownerHistoryLastModified = "OwnerHistoryLastModified";
// Used for COBie 2.4
private const string s_cobieCompanyInfo = "COBieCompanyInfo";
private const string s_cobieProjectInfo = "COBieProjectInfo";
@@ -514,52 +521,48 @@ public void UpdateSavedConfigurations(IFCExportConfigurationsMap initialConfigs)
m_jsonSchema = builder.Finish();
}
- // It won't start any transaction if there is no change to the configurations
- if (setupsToSave.Count > 0)
+ // Overwrite all saved configs with the new list
+ Transaction transaction = new Transaction(IFCCommandOverrideApplication.TheDocument, Properties.Resources.UpdateExportSetups);
+ try
{
- // Overwrite all saved configs with the new list
- Transaction transaction = new Transaction(IFCCommandOverrideApplication.TheDocument, Properties.Resources.UpdateExportSetups);
- try
+ transaction.Start(Properties.Resources.SaveConfigurationChanges);
+ IList savedConfigurations = GetSavedConfigurations(m_jsonSchema);
+ int savedConfigurationCount = savedConfigurations.Count();
+ int savedConfigurationIndex = 0;
+ foreach (IFCExportConfiguration configuration in setupsToSave)
{
- transaction.Start(Properties.Resources.SaveConfigurationChanges);
- IList savedConfigurations = GetSavedConfigurations(m_jsonSchema);
- int savedConfigurationCount = savedConfigurations.Count();
- int savedConfigurationIndex = 0;
- foreach (IFCExportConfiguration configuration in setupsToSave)
+ DataStorage configStorage;
+ if (savedConfigurationIndex >= savedConfigurationCount)
{
- DataStorage configStorage;
- if (savedConfigurationIndex >= savedConfigurationCount)
- {
- configStorage = DataStorage.Create(IFCCommandOverrideApplication.TheDocument);
- }
- else
- {
- configStorage = savedConfigurations[savedConfigurationIndex];
- savedConfigurationIndex++;
- }
-
- Entity mapEntity = new Entity(m_jsonSchema);
- string configData = configuration.SerializeConfigToJson();
- mapEntity.Set(s_configMapField, configData);
- configStorage.SetEntity(mapEntity);
+ configStorage = DataStorage.Create(IFCCommandOverrideApplication.TheDocument);
}
-
- List elementsToDelete = new List();
- for (; savedConfigurationIndex < savedConfigurationCount; savedConfigurationIndex++)
+ else
{
- DataStorage configStorage = savedConfigurations[savedConfigurationIndex];
- elementsToDelete.Add(configStorage.Id);
+ configStorage = savedConfigurations[savedConfigurationIndex];
+ savedConfigurationIndex++;
}
- if (elementsToDelete.Count > 0)
- IFCCommandOverrideApplication.TheDocument.Delete(elementsToDelete);
-
- transaction.Commit();
+
+ Entity mapEntity = new Entity(m_jsonSchema);
+ string configData = configuration.SerializeConfigToJson();
+ mapEntity.Set(s_configMapField, configData);
+ configStorage.SetEntity(mapEntity);
}
- catch (System.Exception)
+
+ List elementsToDelete = new List();
+ for (; savedConfigurationIndex < savedConfigurationCount; savedConfigurationIndex++)
{
- if (transaction.HasStarted())
- transaction.RollBack();
+ DataStorage configStorage = savedConfigurations[savedConfigurationIndex];
+ elementsToDelete.Add(configStorage.Id);
}
+ if (elementsToDelete.Count > 0)
+ IFCCommandOverrideApplication.TheDocument.Delete(elementsToDelete);
+
+ transaction.Commit();
+ }
+ catch (System.Exception)
+ {
+ if (transaction.HasStarted())
+ transaction.RollBack();
}
}
diff --git a/Source/IFCExporterUIOverride/IFCExporterUIOverride.csproj b/Source/IFCExporterUIOverride/IFCExporterUIOverride.csproj
index bdccdf2f..2fe2f263 100644
--- a/Source/IFCExporterUIOverride/IFCExporterUIOverride.csproj
+++ b/Source/IFCExporterUIOverride/IFCExporterUIOverride.csproj
@@ -47,16 +47,20 @@
bin\Releasex64\
-
+
+ False..\..\..\..\Program Files\Autodesk\Revit 2024\Autodesk.UI.Windows.dll
-
+
+ False..\..\..\..\Program Files\Autodesk\Revit 2024\RevitAPI.dll
-
+
+ False..\..\..\..\Program Files\Autodesk\Revit 2024\RevitAPIIFC.dll
-
+
+ False..\..\..\..\Program Files\Autodesk\Revit 2024\RevitAPIUI.dll
@@ -73,7 +77,8 @@
4.0
-
+
+ False..\..\..\..\Program Files\Autodesk\Revit 2024\UserInterfaceUtility.dll
diff --git a/Source/IFCExporterUIOverride/IFCExporterUIWindow.xaml b/Source/IFCExporterUIOverride/IFCExporterUIWindow.xaml
index be07def3..0aa42857 100644
--- a/Source/IFCExporterUIOverride/IFCExporterUIWindow.xaml
+++ b/Source/IFCExporterUIOverride/IFCExporterUIWindow.xaml
@@ -84,7 +84,8 @@
-
+
+
diff --git a/Source/IFCExporterUIOverride/IFCExporterUIWindow.xaml.cs b/Source/IFCExporterUIOverride/IFCExporterUIWindow.xaml.cs
index 80e58e2d..d43442db 100644
--- a/Source/IFCExporterUIOverride/IFCExporterUIWindow.xaml.cs
+++ b/Source/IFCExporterUIOverride/IFCExporterUIWindow.xaml.cs
@@ -191,6 +191,8 @@ private void UpdateConfigurationsList(String currentConfigName)
///
private void InitializeConfigurationOptions()
{
+ Document document = IFCExport.TheDocument;
+
if (!comboboxIfcType.HasItems)
{
comboboxIfcType.Items.Add(new IFCVersionAttributes(IFCVersion.IFC2x2));
@@ -230,7 +232,7 @@ private void InitializeConfigurationOptions()
if (!comboboxActivePhase.HasItems)
{
- PhaseArray phaseArray = IFCCommandOverrideApplication.TheDocument.Phases;
+ PhaseArray phaseArray = document.Phases;
comboboxActivePhase.Items.Add(new IFCPhaseAttributes(ElementId.InvalidElementId)); // Default.
foreach (Phase phase in phaseArray)
{
@@ -249,8 +251,7 @@ private void InitializeConfigurationOptions()
if (!comboBoxProjectSite.HasItems)
{
- Document doc = IFCExport.TheDocument;
- foreach (ProjectLocation pLoc in doc.ProjectLocations.Cast().ToList())
+ foreach (ProjectLocation pLoc in document.ProjectLocations.Cast().ToList())
{
// There seem to be a possibility that the Site Locations can have the same name (UI does not allow it though)
// In this case, it will skip the duplicate since there is no way for this to know which one is exactly selected
@@ -286,7 +287,7 @@ private void UpdatePhaseAttributes(IFCExportConfiguration configuration)
{
if (configuration.VisibleElementsOfCurrentView)
{
- UIDocument uiDoc = new UIDocument(IFCCommandOverrideApplication.TheDocument);
+ UIDocument uiDoc = new UIDocument(IFCExport.TheDocument);
Parameter currPhase = uiDoc.ActiveView.get_Parameter(BuiltInParameter.VIEW_PHASE);
if (currPhase != null)
configuration.ActivePhaseId = currPhase.AsElementId().Value;
@@ -409,6 +410,8 @@ private void UpdateActiveConfigurationOptions(IFCExportConfiguration configurati
userDefinedParameterMappingTable.Text = configuration.ExportUserDefinedParameterMappingFileName;
checkBoxExportUserDefinedParameterMapping.IsChecked = configuration.ExportUserDefinedParameterMapping;
+ checkbox_OwnerHistoryLastModified.IsChecked = configuration.OwnerHistoryLastModified;
+
// Keep old behavior where by default we looked for ParameterMappingTable.txt in the current directory if ExportUserDefinedParameterMappingFileName
// isn't set.
if (string.IsNullOrWhiteSpace(configuration.ExportUserDefinedParameterMappingFileName))
@@ -454,7 +457,9 @@ private void UpdateActiveConfigurationOptions(IFCExportConfiguration configurati
checkBoxExportSpecificSchedules,
checkBox_TriangulationOnly,
checkbox_UseTypeNameOnly,
- checkbox_UseVisibleRevitNameAsEntityName
+ checkbox_UseVisibleRevitNameAsEntityName,
+ checkbox_OwnerHistoryLastModified
+
};
foreach (UIElement element in configurationElements)
@@ -466,7 +471,7 @@ private void UpdateActiveConfigurationOptions(IFCExportConfiguration configurati
userDefinedParameterMappingTable.IsEnabled = userDefinedParameterMappingTable.IsEnabled && configuration.ExportUserDefinedParameterMapping;
buttonBrowse.IsEnabled = buttonBrowse.IsEnabled && configuration.ExportUserDefinedPsets;
buttonParameterMappingBrowse.IsEnabled = buttonParameterMappingBrowse.IsEnabled && configuration.ExportUserDefinedParameterMapping;
-
+
// ExportRoomsInView option will only be enabled if it is not currently disabled AND the "export elements visible in view" option is checked
bool? cboVisibleElementInCurrentView = checkboxVisibleElementsCurrView.IsChecked;
checkBoxExportRoomsInView.IsEnabled = checkBoxExportRoomsInView.IsEnabled && cboVisibleElementInCurrentView.HasValue ? cboVisibleElementInCurrentView.Value : false;
@@ -497,6 +502,9 @@ private void UpdateActiveConfigurationOptions(IFCExportConfiguration configurati
checkbox_UseVisibleRevitNameAsEntityName.IsChecked = configuration.UseVisibleRevitNameAsEntityName;
checkbox_UseVisibleRevitNameAsEntityName.IsEnabled = true;
+ checkbox_OwnerHistoryLastModified.IsChecked = configuration.OwnerHistoryLastModified;
+ checkbox_OwnerHistoryLastModified.IsEnabled = true;
+
if (configuration.IFCVersion.Equals(IFCVersion.IFC2x3FM))
{
DoCOBieSpecificSetup(configuration);
@@ -677,6 +685,7 @@ private void buttonDeleteSetup_Click(object sender, RoutedEventArgs e)
m_configurationsMap.Remove(configuration.Name);
listBoxConfigurations.Items.Remove(configuration);
listBoxConfigurations.SelectedIndex = 0;
+ IFCExport.LastSelectedConfig.Remove(configuration.Name);
}
///
@@ -789,6 +798,8 @@ private void buttonRenameSetup_Click(object sender, RoutedEventArgs e)
m_configurationsMap.Remove(oldName);
m_configurationsMap.AddOrReplace(configuration);
UpdateConfigurationsList(newName);
+ if (IFCExport.LastSelectedConfig.ContainsKey(oldName))
+ IFCExport.LastSelectedConfig.Remove(oldName);
}
}
@@ -1562,6 +1573,18 @@ private void Checkbox_UseTypeNameOnly_Unchecked(object sender, RoutedEventArgs e
configuration.UseTypeNameOnlyForIfcType = false;
}
+ private void Checkbox_OwnerHistoryLastModified_Checked(object sender, RoutedEventArgs e)
+ {
+ IFCExportConfiguration configuration = GetSelectedConfiguration();
+ configuration.OwnerHistoryLastModified = true;
+ }
+
+ private void Checkbox_OwnerHistoryLastModified_Unchecked(object sender, RoutedEventArgs e)
+ {
+ IFCExportConfiguration configuration = GetSelectedConfiguration();
+ configuration.OwnerHistoryLastModified = false;
+ }
+
private void Checkbox_UseVisibleRevitName_Checked(object sender, RoutedEventArgs e)
{
IFCExportConfiguration configuration = GetSelectedConfiguration();
diff --git a/Source/IFCExporterUIOverride/IFCPhaseAttributes.cs b/Source/IFCExporterUIOverride/IFCPhaseAttributes.cs
index 7f30b2ed..08c8f392 100644
--- a/Source/IFCExporterUIOverride/IFCPhaseAttributes.cs
+++ b/Source/IFCExporterUIOverride/IFCPhaseAttributes.cs
@@ -61,7 +61,7 @@ static public bool Validate(long phaseId)
return false;
Element checkPhase = IFCCommandOverrideApplication.TheDocument.GetElement(checkPhaseId);
- return (checkPhase != null && (checkPhase is Phase));
+ return checkPhase is Phase;
}
///
diff --git a/Source/IFCExporterUIOverride/IFCRenameExportSetup.xaml b/Source/IFCExporterUIOverride/IFCRenameExportSetup.xaml
index 7822b4a2..e3635f00 100644
--- a/Source/IFCExporterUIOverride/IFCRenameExportSetup.xaml
+++ b/Source/IFCExporterUIOverride/IFCRenameExportSetup.xaml
@@ -9,7 +9,7 @@
-
+
diff --git a/Source/IFCExporterUIOverride/Properties/AssemblyInfo.cs b/Source/IFCExporterUIOverride/Properties/AssemblyInfo.cs
index f18cf7be..8b9527ea 100644
--- a/Source/IFCExporterUIOverride/Properties/AssemblyInfo.cs
+++ b/Source/IFCExporterUIOverride/Properties/AssemblyInfo.cs
@@ -66,6 +66,6 @@
// The following information is used in the Open Source version as the release version number.
// The number will show up in the Title bar of the export dialog as well as at the IFC header file
// This number must be manually updated prior to releasing the new version
-[assembly: AssemblyVersion("24.2.0.49")]
-[assembly: AssemblyFileVersion("24.2.0.49")]
+[assembly: AssemblyVersion("24.2.20.0")]
+[assembly: AssemblyFileVersion("24.2.20.0")]
#endif
\ No newline at end of file
diff --git a/Source/IFCExporterUIOverride/Properties/Resources.Designer.cs b/Source/IFCExporterUIOverride/Properties/Resources.Designer.cs
index aca80384..6da99e74 100644
--- a/Source/IFCExporterUIOverride/Properties/Resources.Designer.cs
+++ b/Source/IFCExporterUIOverride/Properties/Resources.Designer.cs
@@ -1743,6 +1743,24 @@ public static string Override {
}
}
+ ///
+ /// Looks up a localized string similar to Set "Last Modified" user to the Author in Project Information.
+ ///
+ public static string OwnerHistoryLastModified {
+ get {
+ return ResourceManager.GetString("OwnerHistoryLastModified", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Populate the IfcOwnerHistory "LastModified" attribute with the Author information set in Project Information.
+ ///
+ public static string OwnerHistoryLastModifiedTooltip {
+ get {
+ return ResourceManager.GetString("OwnerHistoryLastModifiedTooltip", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Phase to export.
///
@@ -2373,6 +2391,33 @@ public static string StreetAddress {
}
}
+ ///
+ /// Looks up a localized string similar to Template name.
+ ///
+ public static string TemplateName {
+ get {
+ return ResourceManager.GetString("TemplateName", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Enter unique template name..
+ ///
+ public static string TemplateNameTooltip {
+ get {
+ return ResourceManager.GetString("TemplateNameTooltip", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Template Settings.
+ ///
+ public static string TemplateSettings {
+ get {
+ return ResourceManager.GetString("TemplateSettings", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Level of detail for some element geometry.
///
diff --git a/Source/IFCExporterUIOverride/Properties/Resources.resx b/Source/IFCExporterUIOverride/Properties/Resources.resx
index 2ecaee70..610b2a06 100644
--- a/Source/IFCExporterUIOverride/Properties/Resources.resx
+++ b/Source/IFCExporterUIOverride/Properties/Resources.resx
@@ -971,6 +971,15 @@
Use this option to export the IFC type name as the Revit type name without the family name.
+
+ Set "Last Modified" user to the Author in Project Information
+
+
+ Populate the IfcOwnerHistory "LastModified" attribute with the Author information set in Project Information
+
+
+ Enter unique template name.
+
Use this option to export IFC entity name as the visible Revit name.
diff --git a/Source/IFCExporterUIOverride/icons/load.ico b/Source/IFCExporterUIOverride/icons/load.ico
index 38ebb711..181d7662 100644
Binary files a/Source/IFCExporterUIOverride/icons/load.ico and b/Source/IFCExporterUIOverride/icons/load.ico differ
diff --git a/Source/IFCExporterUIOverride/icons/save.ico b/Source/IFCExporterUIOverride/icons/save.ico
index 23a921bd..d11562e4 100644
Binary files a/Source/IFCExporterUIOverride/icons/save.ico and b/Source/IFCExporterUIOverride/icons/save.ico differ
diff --git a/Source/Revit.IFC.Common/Enums/IFCKnownMVDAndER.cs b/Source/Revit.IFC.Common/Enums/IFCKnownMVDAndER.cs
index a42e4748..3a438965 100644
--- a/Source/Revit.IFC.Common/Enums/IFCKnownMVDAndER.cs
+++ b/Source/Revit.IFC.Common/Enums/IFCKnownMVDAndER.cs
@@ -6,10 +6,6 @@
namespace Revit.IFC.Common.Enums
{
- //public enum KnownMVDNames
- //{
- //}
-
///
/// Enumeration for known Exchange Requirements
///
diff --git a/Source/Revit.IFC.Common/Extension/IFCFileHeader.cs b/Source/Revit.IFC.Common/Extension/IFCFileHeader.cs
index 6a6b152b..66134ceb 100644
--- a/Source/Revit.IFC.Common/Extension/IFCFileHeader.cs
+++ b/Source/Revit.IFC.Common/Extension/IFCFileHeader.cs
@@ -149,27 +149,34 @@ public bool GetSavedFileHeader(Document document, out IFCFileHeaderItem fileHead
if (fileHeaderStorage.Count == 0)
return false;
- // expected only one File Header information in the storage
- Entity savedFileHeader = fileHeaderStorage[0].GetEntity(m_schema);
- IDictionary savedFileHeaderMap = savedFileHeader.Get>(s_FileHeaderMapField);
- if (savedFileHeaderMap.ContainsKey(s_FileDescription))
- fileHeader.FileDescriptions = savedFileHeaderMap[s_FileDescription].Split('|').ToList();
- if (savedFileHeaderMap.ContainsKey(s_SourceFileName))
- fileHeader.SourceFileName = savedFileHeaderMap[s_SourceFileName];
- if (savedFileHeaderMap.ContainsKey(s_AuthorName))
- fileHeader.AuthorName = savedFileHeaderMap[s_AuthorName];
- if (savedFileHeaderMap.ContainsKey(s_AuthorEmail))
- fileHeader.AuthorEmail = savedFileHeaderMap[s_AuthorEmail];
- if (savedFileHeaderMap.ContainsKey(s_Organization))
- fileHeader.Organization = savedFileHeaderMap[s_Organization];
- if (savedFileHeaderMap.ContainsKey(s_Authorization))
- fileHeader.Authorization = savedFileHeaderMap[s_Authorization];
- if (savedFileHeaderMap.ContainsKey(s_ApplicationName))
- fileHeader.ApplicationName = savedFileHeaderMap[s_ApplicationName];
- if (savedFileHeaderMap.ContainsKey(s_VersionNumber))
- fileHeader.VersionNumber = savedFileHeaderMap[s_VersionNumber];
- if (savedFileHeaderMap.ContainsKey(s_FileSchema))
- fileHeader.FileSchema = savedFileHeaderMap[s_FileSchema];
+ try
+ {
+ // expected only one File Header information in the storage
+ Entity savedFileHeader = fileHeaderStorage[0].GetEntity(m_schema);
+ IDictionary savedFileHeaderMap = savedFileHeader.Get>(s_FileHeaderMapField);
+ if (savedFileHeaderMap.ContainsKey(s_FileDescription))
+ fileHeader.FileDescriptions = savedFileHeaderMap[s_FileDescription].Split('|').ToList();
+ if (savedFileHeaderMap.ContainsKey(s_SourceFileName))
+ fileHeader.SourceFileName = savedFileHeaderMap[s_SourceFileName];
+ if (savedFileHeaderMap.ContainsKey(s_AuthorName))
+ fileHeader.AuthorName = savedFileHeaderMap[s_AuthorName];
+ if (savedFileHeaderMap.ContainsKey(s_AuthorEmail))
+ fileHeader.AuthorEmail = savedFileHeaderMap[s_AuthorEmail];
+ if (savedFileHeaderMap.ContainsKey(s_Organization))
+ fileHeader.Organization = savedFileHeaderMap[s_Organization];
+ if (savedFileHeaderMap.ContainsKey(s_Authorization))
+ fileHeader.Authorization = savedFileHeaderMap[s_Authorization];
+ if (savedFileHeaderMap.ContainsKey(s_ApplicationName))
+ fileHeader.ApplicationName = savedFileHeaderMap[s_ApplicationName];
+ if (savedFileHeaderMap.ContainsKey(s_VersionNumber))
+ fileHeader.VersionNumber = savedFileHeaderMap[s_VersionNumber];
+ if (savedFileHeaderMap.ContainsKey(s_FileSchema))
+ fileHeader.FileSchema = savedFileHeaderMap[s_FileSchema];
+ }
+ catch (Exception)
+ {
+ document.Application.WriteJournalComment("IFC error: Cannot read IFCFileHeader schema", true);
+ }
return true;
}
diff --git a/Source/Revit.IFC.Common/Properties/AssemblyInfo.cs b/Source/Revit.IFC.Common/Properties/AssemblyInfo.cs
index 3b7c3cfb..b044a20e 100644
--- a/Source/Revit.IFC.Common/Properties/AssemblyInfo.cs
+++ b/Source/Revit.IFC.Common/Properties/AssemblyInfo.cs
@@ -13,8 +13,8 @@
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
-[assembly: AssemblyVersion("24.2.0.49")]
-[assembly: AssemblyFileVersion("24.2.0.49")]
+[assembly: AssemblyVersion("24.2.20.0")]
+[assembly: AssemblyFileVersion("24.2.20.0")]
#endif
diff --git a/Source/Revit.IFC.Common/Revit.IFC.Common.csproj b/Source/Revit.IFC.Common/Revit.IFC.Common.csproj
index 4a4ad893..64871dfd 100644
--- a/Source/Revit.IFC.Common/Revit.IFC.Common.csproj
+++ b/Source/Revit.IFC.Common/Revit.IFC.Common.csproj
@@ -47,10 +47,12 @@
x64
-
+
+ False..\..\..\..\Program Files\Autodesk\Revit 2024\RevitAPI.dll
-
+
+ False..\..\..\..\Program Files\Autodesk\Revit 2024\RevitAPIIFC.dll
diff --git a/Source/Revit.IFC.Common/Utility/IFCAnyHandleUtil.cs b/Source/Revit.IFC.Common/Utility/IFCAnyHandleUtil.cs
index 629facd0..11f30b2c 100644
--- a/Source/Revit.IFC.Common/Utility/IFCAnyHandleUtil.cs
+++ b/Source/Revit.IFC.Common/Utility/IFCAnyHandleUtil.cs
@@ -24,9 +24,70 @@
using Autodesk.Revit.DB.IFC;
using Autodesk.Revit.DB;
using Revit.IFC.Common.Enums;
+using Autodesk.Revit.DB.Visual;
namespace Revit.IFC.Common.Utility
{
+ public static class ListExtensions
+ {
+ ///
+ /// Add an IFCAnyHandle to a list if the handle is valid.
+ ///
+ ///
+ /// The list.
+ /// The handle to conditionally add.
+ /// True if an item was added, false if not.
+ public static bool AddIfNotNull(this IList myList, T hnd) where T : IFCAnyHandle
+ {
+ if (IFCAnyHandleUtil.IsNullOrHasNoValue(hnd))
+ return false;
+
+ myList.Add(hnd);
+ return true;
+ }
+ }
+
+ ///
+ /// Class containing convenience function for IDictionary of IFCAnyHandle.
+ ///
+ public static class DictionaryExtensionsClass
+ {
+ public static bool AddIfNotNullAndNewKey(this IDictionary myDictionary,
+ string key, T hnd) where T : IFCAnyHandle
+ {
+ if (IFCAnyHandleUtil.IsNullOrHasNoValue(hnd))
+ return false;
+
+ if (myDictionary.ContainsKey(key))
+ return false;
+
+ myDictionary[key] = hnd;
+ return true;
+ }
+ }
+
+ ///
+ /// Class containing convenience function for ISet of IFCAnyHandle.
+ ///
+ public static class SetExtensions
+ {
+ ///
+ /// Add an IFCAnyHandle to a set if the handle is valid.
+ ///
+ ///
+ /// The set.
+ /// The handle to conditionally add.
+ /// True if an item was added, false if not.
+ public static bool AddIfNotNull(this ISet mySet, T hnd) where T : IFCAnyHandle
+ {
+ if (IFCAnyHandleUtil.IsNullOrHasNoValue(hnd))
+ return false;
+
+ mySet.Add(hnd);
+ return true;
+ }
+ }
+
public class IFCLimits
{
///
diff --git a/Source/Revit.IFC.Common/Utility/IfcSchemaEntityTree.cs b/Source/Revit.IFC.Common/Utility/IfcSchemaEntityTree.cs
index 715efc90..c505b0f1 100644
--- a/Source/Revit.IFC.Common/Utility/IfcSchemaEntityTree.cs
+++ b/Source/Revit.IFC.Common/Utility/IfcSchemaEntityTree.cs
@@ -432,8 +432,7 @@ static public IfcSchemaEntityNode FindNonAbsInstanceSuperType(string context, st
return res;
}
- string theTypeName =
- typeName.Substring(typeName.Length - 4, 4).Equals("Type", StringComparison.CurrentCultureIgnoreCase) ?
+ string theTypeName = typeName.EndsWith("Type", StringComparison.CurrentCultureIgnoreCase) ?
typeName : GetTypeNameFromInstanceName(typeName);
IfcSchemaEntityNode entNode = ifcEntitySchemaTree.Find(theTypeName);
@@ -716,7 +715,9 @@ static public IList GetPredefinedTypeList(IfcSchemaEntityTree ifcEntityS
public static bool IsDeprecatedOrUnsupported(string schemaName, string entityName)
{
if (DeprecatedOrUnsupportedDict.ContainsKey(schemaName))
+ {
return DeprecatedOrUnsupportedDict[schemaName].Contains(entityName);
+ }
return false;
}
diff --git a/Source/Revit.IFC.Common/Utility/MathUtil.cs b/Source/Revit.IFC.Common/Utility/MathUtil.cs
index be3a1c1b..4d24f71d 100644
--- a/Source/Revit.IFC.Common/Utility/MathUtil.cs
+++ b/Source/Revit.IFC.Common/Utility/MathUtil.cs
@@ -51,13 +51,8 @@ public class MathUtil
///
/// Returns a small value for use in comparing doubles.
///
- ///
- /// The value.
- ///
- public static double Eps()
- {
- return 1.0e-9;
- }
+ /// The value.
+ public static double Eps() => 1.0e-9;
public static double SmallGap()
{
diff --git a/Source/Revit.IFC.Common/Utility/OptionsUtil.cs b/Source/Revit.IFC.Common/Utility/OptionsUtil.cs
index f991fedb..2b6b581d 100644
--- a/Source/Revit.IFC.Common/Utility/OptionsUtil.cs
+++ b/Source/Revit.IFC.Common/Utility/OptionsUtil.cs
@@ -459,6 +459,9 @@ public static (double eastings, double northings, double orthogonalHeight, doubl
BasePoint surveyPoint = BasePoint.GetSurveyPoint(doc);
BasePoint projectBasePoint = BasePoint.GetProjectBasePoint(doc);
+ if (surveyPoint == null || projectBasePoint == null)
+ return (eastings, northings, orthogonalHeight, angleTN, origAngleTN);
+
(double svNorthings, double svEastings, double svElevation, double svAngle, double pbNorthings,
double pbEastings, double pbElevation, double pbAngle) = ProjectLocationInfo(doc, surveyPoint.Position, projectBasePoint.Position);
origAngleTN = pbAngle;
diff --git a/Source/Revit.IFC.Export/Exporter/AssemblyInstanceExporter.cs b/Source/Revit.IFC.Export/Exporter/AssemblyInstanceExporter.cs
index d83448e4..0b57fc2b 100644
--- a/Source/Revit.IFC.Export/Exporter/AssemblyInstanceExporter.cs
+++ b/Source/Revit.IFC.Export/Exporter/AssemblyInstanceExporter.cs
@@ -232,9 +232,7 @@ static void ExportAssemblyInstanceWithMembers(ExporterIFC exporterIFC, Element a
foreach (ElementId memberId in memberIds)
{
- IFCAnyHandle memberHnd = ExporterCacheManager.ElementToHandleCache.Find(memberId);
- if (!IFCAnyHandleUtil.IsNullOrHasNoValue(memberHnd))
- memberHnds.Add(memberHnd);
+ memberHnds.AddIfNotNull(ExporterCacheManager.ElementToHandleCache.Find(memberId));
}
if (memberHnds.Count == 0)
diff --git a/Source/Revit.IFC.Export/Exporter/BeamExporter.cs b/Source/Revit.IFC.Export/Exporter/BeamExporter.cs
index ca653156..2dc7b48e 100644
--- a/Source/Revit.IFC.Export/Exporter/BeamExporter.cs
+++ b/Source/Revit.IFC.Export/Exporter/BeamExporter.cs
@@ -224,9 +224,8 @@ private static IFCAnyHandle CreateBeamAxis(ExporterIFC exporterIFC, Element elem
{
IFCAnyHandle axisHnd = GeometryUtil.CreatePolyCurveFromCurve(exporterIFC, curve);
axis_items = new List();
- if (!IFCAnyHandleUtil.IsNullOrHasNoValue(axisHnd))
+ if (axis_items.AddIfNotNull(axisHnd))
{
- axis_items.Add(axisHnd);
representationTypeOpt = "Curve3D"; // We use Curve3D for IFC4RV Axis
}
}
@@ -374,10 +373,7 @@ public static void ExportBeamType(ExporterIFC exporterIFC, ProductWrapper wrappe
if (elementType == null)
return;
- string preDefinedTypeSearch = predefinedType;
- if (string.IsNullOrEmpty(preDefinedTypeSearch))
- preDefinedTypeSearch = "NULL";
- IFCExportInfoPair exportType = new IFCExportInfoPair(IFCEntityType.IfcBeamType, preDefinedTypeSearch);
+ IFCExportInfoPair exportType = new IFCExportInfoPair(IFCEntityType.IfcBeamType, predefinedType);
IFCAnyHandle beamType = ExporterCacheManager.ElementTypeToHandleCache.Find(elementType, exportType);
if (!IFCAnyHandleUtil.IsNullOrHasNoValue(beamType))
{
@@ -538,8 +534,7 @@ public static IFCAnyHandle ExportBeamAsStandardElement(ExporterIFC exporterIFC,
IList representations = new List();
double elevation = (setter.LevelInfo != null) ? setter.LevelInfo.Elevation : 0.0;
IFCAnyHandle axisRep = CreateBeamAxis(exporterIFC, element, catId, axisInfo, offsetTransform, elevation);
- if (!IFCAnyHandleUtil.IsNullOrHasNoValue(axisRep))
- representations.Add(axisRep);
+ representations.AddIfNotNull(axisRep);
representations.Add(repHnd);
Transform boundingBoxTrf = offsetTransform?.Inverse ?? Transform.Identity;
@@ -550,16 +545,14 @@ public static IFCAnyHandle ExportBeamAsStandardElement(ExporterIFC exporterIFC,
IFCAnyHandle prodRep = IFCInstanceExporter.CreateProductDefinitionShape(file, null, null, representations);
string instanceGUID = GUIDUtil.CreateGUID(element);
- beam = IFCInstanceExporter.CreateBeam(exporterIFC, element, instanceGUID, ExporterCacheManager.OwnerHistoryHandle, extrusionCreationData.GetLocalPlacement(), prodRep, exportType.ValidatedPredefinedType);
-
-
+ beam = IFCInstanceExporter.CreateBeam(exporterIFC, element, instanceGUID, ExporterCacheManager.OwnerHistoryHandle, extrusionCreationData.GetLocalPlacement(), prodRep, exportType.GetPredefinedTypeOrDefault());
IFCAnyHandle mpSetUsage;
if (materialProfileSet != null)
mpSetUsage = IFCInstanceExporter.CreateMaterialProfileSetUsage(file, materialProfileSet, null, null);
productWrapper.AddElement(element, beam, setter, extrusionCreationData, true, exportType);
- ExportBeamType(exporterIFC, productWrapper, beam, element, exportType.ValidatedPredefinedType);
+ ExportBeamType(exporterIFC, productWrapper, beam, element, exportType.GetPredefinedTypeOrDefault());
OpeningUtil.CreateOpeningsIfNecessary(beam, element, extrusionCreationData, offsetTransform, exporterIFC,
extrusionCreationData.GetLocalPlacement(), setter, productWrapper);
diff --git a/Source/Revit.IFC.Export/Exporter/BodyExporter.cs b/Source/Revit.IFC.Export/Exporter/BodyExporter.cs
index 3dba025d..44ef9b99 100644
--- a/Source/Revit.IFC.Export/Exporter/BodyExporter.cs
+++ b/Source/Revit.IFC.Export/Exporter/BodyExporter.cs
@@ -28,7 +28,6 @@
using Revit.IFC.Common.Enums;
using Revit.IFC.Common.Utility;
-
namespace Revit.IFC.Export.Exporter
{
///
@@ -166,15 +165,18 @@ public static ElementId GetBestMaterialIdForGeometry(GeometryObject geometryObje
if (!(geometryObject is Solid))
{
+ ElementId matId = ElementId.InvalidElementId;
if (geometryObject is Mesh)
{
- ElementId matID = (geometryObject as Mesh).MaterialElementId;
- if (ExporterUtil.IsElementIdBuiltInOrInvalid(matID))
- return ElementId.InvalidElementId;
-
- return matID;
+ matId = (geometryObject as Mesh).MaterialElementId;
}
- return ElementId.InvalidElementId;
+ else if (geometryObject is Face)
+ {
+ matId = (geometryObject as Face).MaterialElementId;
+ }
+
+ return ExporterUtil.IsElementIdBuiltInOrInvalid(matId) ? ElementId.InvalidElementId :
+ matId;
}
Solid solid = geometryObject as Solid;
@@ -244,7 +246,7 @@ public static ElementId GetBestMaterialIdForGeometry(GeometryObject geometryObje
private static bool IsDuctCategory(ElementId categoryId)
{
long categoryValue = categoryId.Value;
- return categoryValue == (long) BuiltInCategory.OST_DuctAccessory ||
+ return categoryValue == (long)BuiltInCategory.OST_DuctAccessory ||
categoryValue == (long)BuiltInCategory.OST_DuctCurves ||
categoryValue == (long)BuiltInCategory.OST_DuctFitting ||
categoryValue == (long)BuiltInCategory.OST_DuctInsulations ||
@@ -273,7 +275,7 @@ private static bool CategoryHasMaterialIdParam(ElementId categoryId)
// OST_Cornices also has a MaterialId parameter, but Revit doesn't want us
// to ask for it.
long categoryValue = categoryId.Value;
- return categoryValue == (long) BuiltInCategory.OST_Rebar ||
+ return categoryValue == (long)BuiltInCategory.OST_Rebar ||
categoryValue == (long)BuiltInCategory.OST_FabricReinforcement ||
categoryValue == (long)BuiltInCategory.OST_Fascia ||
categoryValue == (long)BuiltInCategory.OST_Gutter ||
@@ -305,10 +307,30 @@ private static bool CategoryHasStructuralMaterialParam(ElementId categoryId)
}
private static ElementId GetBestMaterialIdFromParameter(Element element)
+ {
+ if (element == null)
+ {
+ return ElementId.InvalidElementId;
+ }
+
+ ElementId id = element.Id;
+ if (ExporterCacheManager.ElementIdMaterialParameterCache.TryGetValue(id, out ElementId matId))
+ {
+ return matId;
+ }
+
+ matId = CalcBestMaterialIdFromParameter(element);
+ ExporterCacheManager.ElementIdMaterialParameterCache[id] = matId;
+ return matId;
+ }
+
+ private static ElementId CalcBestMaterialIdFromParameter(Element element)
{
ElementId matId = ExporterUtil.GetSingleMaterial(element);
if (matId != ElementId.InvalidElementId)
+ {
return matId;
+ }
// Try to get it from the category of the element first.
ElementId categoryId = CategoryUtil.GetSafeCategoryId(element);
@@ -322,25 +344,34 @@ private static ElementId GetBestMaterialIdFromParameter(Element element)
}
if (matId != ElementId.InvalidElementId)
+ {
return matId;
+ }
// If not, try to get it from the system.
ElementId systemTypeId = ElementId.InvalidElementId;
if (IsDuctCategory(categoryId))
+ {
ParameterUtil.GetElementIdValueFromElement(element, BuiltInParameter.RBS_DUCT_SYSTEM_TYPE_PARAM, out systemTypeId);
+ }
else if (IsPipeCategory(categoryId))
+ {
ParameterUtil.GetElementIdValueFromElement(element, BuiltInParameter.RBS_PIPING_SYSTEM_TYPE_PARAM, out systemTypeId);
+ }
if (systemTypeId != ElementId.InvalidElementId)
{
Element systemType = element.Document.GetElement(systemTypeId);
if (systemType != null)
- return GetBestMaterialIdFromParameter(systemType);
+ {
+ matId = GetBestMaterialIdFromParameter(systemType);
+ return matId;
+ }
}
return matId;
}
-
+
///
/// Gets the best material id from the geometry or its structural material parameter.
///
@@ -1569,9 +1600,15 @@ public static IFCAnyHandle ExportBodyAsAdvancedBrep(ExporterIFC exporterIFC, Ele
return null;
}
- var sortedEdgeLoop = GeometryUtil.GetOuterLoopsWithInnerLoops(face);
+ var sortedEdgeLoop = GeometryUtil.GetOuterLoopsWithInnerLoops(face);
+ if (sortedEdgeLoop == null)
+ {
+ return null;
+ }
+
// check that we get back the same number of edgeloop
int numberOfSortedEdgeLoop = 0;
+
foreach (var (outerLoop, innerLoops) in sortedEdgeLoop)
{
numberOfSortedEdgeLoop += 1 + innerLoops.Count;
@@ -1581,7 +1618,7 @@ public static IFCAnyHandle ExportBodyAsAdvancedBrep(ExporterIFC exporterIFC, Ele
{
return null;
}
-
+
foreach (var (outerLoop, loops) in sortedEdgeLoop)
{
if (outerLoop == null || loops == null)
@@ -1937,17 +1974,26 @@ private static IList GetGeometriesFromGeometryElement(
return geomObjectPrimitives;
}
- private static IFCAnyHandle ExportPlanarSolidAsPolygonalFaceSet(ExporterIFC exporterIFC, Solid solid, IFCAnyHandle ifcColourRgbList, double opacity,
- Transform trfToUse = null)
+ private static IEnumerable GetDataForExportPlanarSolidAsPolygonalFaceSet(Solid solid)
{
- IFCFile file = exporterIFC.GetFile();
-
- bool isClosed = true;
-
IEnumerable faces = solid.Faces.OfType();
if (faces.Count() != solid.Faces.Size) // Not all faces in the solid were planar
return null;
+ return faces;
+ }
+
+ private static (IFCAnyHandle, int) ExportPlanarSolidAsPolygonalFaceSet(ExporterIFC exporterIFC,
+ IEnumerable faces, Transform trfToUse)
+ {
+ if (faces == null)
+ {
+ return (null, 0);
+ }
+
+ IFCFile file = exporterIFC.GetFile();
+ bool isClosed = true;
+
List ifcFaceHandles = new List();
// List of all unique vertex XYZs. Each item in this list is an average of all XYZs obtained from adjacent edge end-points.
List vertexPositions = new List();
@@ -1973,7 +2019,7 @@ private static IFCAnyHandle ExportPlanarSolidAsPolygonalFaceSet(ExporterIFC expo
{
// This function isn't applicable to breps with non-linear curves
if (!(edge.AsCurve() is Line))
- return null;
+ return (null, 0);
isClosed = isClosed && (edge.GetFace(0) != null && edge.GetFace(1) != null);
@@ -1986,7 +2032,7 @@ private static IFCAnyHandle ExportPlanarSolidAsPolygonalFaceSet(ExporterIFC expo
{
IList allEndPnts = SolidUtils.FindAllEdgeEndPointsAtVertex(endPnt);
if (allEndPnts.Count == 0)
- return null;
+ return (null, 0);
// Use the average of all positions for the vertex location
XYZ vertexPosition = XYZ.Zero;
@@ -2023,13 +2069,13 @@ private static IFCAnyHandle ExportPlanarSolidAsPolygonalFaceSet(ExporterIFC expo
if (indexedLoops.Count > 0)
loopsCache.Add(new Tuple>, int>(indexedLoops, outerEdgeLoopIndex));
else
- return null;
+ return (null, 0);
}
}
catch
{
// If anything unexpected handles the caller should move on to export the body as a tessellation
- return null;
+ return (null, 0);
}
// Create faces
@@ -2050,10 +2096,25 @@ private static IFCAnyHandle ExportPlanarSolidAsPolygonalFaceSet(ExporterIFC expo
vertexCoords.Add(new List() { vertexScaled.X, vertexScaled.Y, vertexScaled.Z });
}
- if (ifcFaceHandles.Count == 0 || vertexCoords.Count == 0)
- return null;
+ int numFaces = ifcFaceHandles.Count;
+ if (numFaces == 0 || vertexCoords.Count == 0)
+ return (null, 0);
+
+ IFCAnyHandle coordinatesHnd = IFCInstanceExporter.CreateCartesianPointList3D(file, vertexCoords);
+ IFCAnyHandle polygonalFaceSet = IFCInstanceExporter.CreatePolygonalFaceSet(file, coordinatesHnd, isClosed, ifcFaceHandles, null);
+ return (polygonalFaceSet, numFaces);
+ }
- return ExportIfcFacesAsPolygonalFaceSet(file, ifcFaceHandles, vertexCoords, isClosed, ifcColourRgbList, opacity);
+ private static void AddStyleToFaceSet(ExporterIFC exporterIFC, IFCFile file,
+ Document document, BodyExporterOptions options, IFCAnyHandle polygonalFaceSet,
+ IFCAnyHandle ifcColourRgbList, double opacity, ElementId matId, int numFaces)
+ {
+ if (!IFCAnyHandleUtil.IsNullOrHasNoValue(ifcColourRgbList))
+ {
+ IList colourIndex = Enumerable.Repeat(1, numFaces).ToList();
+ IFCInstanceExporter.CreateIndexedColourMap(file, polygonalFaceSet, opacity, ifcColourRgbList, colourIndex);
+ }
+ CreateSurfaceStyleForRepItem(exporterIFC, document, options.CreatingVoid, polygonalFaceSet, matId);
}
///
@@ -2069,11 +2130,8 @@ public static IList ExportBodyAsPolygonalFaceSet(ExporterIFC expor
{
IFCFile file = exporterIFC.GetFile();
- IFCAnyHandle ifcColourRgbList = GetBestColourAndOpacity(file, element, geomObject,
- out double opacity, out ElementId matId);
-
Document document = element.Document;
- IList polygonalFaceSetList = new List();
+ List polygonalFaceSetList = new List();
// If the geomObject is GeometryELement or GeometryInstance, we need to collect their primitive Solid and Mesh first
IList geomObjectPrimitives = GetGeometriesFromGeometryElement(
@@ -2084,41 +2142,56 @@ public static IList ExportBodyAsPolygonalFaceSet(ExporterIFC expor
{
try
{
+ IList polygonalFaceSets = new List();
+
+ IList<(TriangleMergeUtil, bool)> triangleComponents =
+ new List<(TriangleMergeUtil, bool)>();
+ IEnumerable planarFaceData = null;
+
if (geom is Solid)
{
Solid solid = geom as Solid;
- IFCAnyHandle polygonalFaceSet = ExportPlanarSolidAsPolygonalFaceSet(exporterIFC, solid, ifcColourRgbList, opacity, trfToUse);
- if (IFCAnyHandleUtil.IsNullOrHasNoValue(polygonalFaceSet))
+ planarFaceData = GetDataForExportPlanarSolidAsPolygonalFaceSet(solid);
+ if (planarFaceData == null)
{
- TriangulatedSolidOrShell solidFacetation = SolidUtils.TessellateSolidOrShell(solid, options.TessellationControls);
+ TriangulatedSolidOrShell solidFacetation = GetOptimalTessellation(solid, options);
+
for (int ii = 0; ii < solidFacetation.ShellComponentCount; ++ii)
{
TriangulatedShellComponent component = solidFacetation.GetShellComponent(ii);
- TriangleMergeUtil triMerge = new TriangleMergeUtil(component);
- IList ifcFaces = MergeAndCreateIfcFaces(file, triMerge);
-
- IList> coordList = new List>();
- foreach (XYZ vertex in triMerge.GetVertices())
- {
- XYZ vertexScaled = TransformAndScalePoint(exporterIFC, vertex, trfToUse);
- coordList.Add(new List() { vertexScaled.X, vertexScaled.Y, vertexScaled.Z });
- }
-
- polygonalFaceSet = ExportIfcFacesAsPolygonalFaceSet(file, ifcFaces, coordList, component.IsClosed, ifcColourRgbList, opacity);
- if (!IFCAnyHandleUtil.IsNullOrHasNoValue(polygonalFaceSet))
- polygonalFaceSetList.Add(polygonalFaceSet);
+ triangleComponents.Add((new TriangleMergeUtil(component), component.IsClosed));
}
}
- else
- {
- polygonalFaceSetList.Add(polygonalFaceSet);
- }
}
else if (geom is Mesh)
{
Mesh mesh = geom as Mesh;
- TriangleMergeUtil triMerge = new TriangleMergeUtil(mesh);
+ triangleComponents.Add((new TriangleMergeUtil(mesh), mesh.IsClosed));
+ }
+
+ if (planarFaceData == null && triangleComponents.Count == 0)
+ {
+ continue;
+ }
+
+ IFCAnyHandle ifcColourRgbList = GetBestColourAndOpacity(file, element, geom,
+ out double opacity, out ElementId matId);
+
+ if (planarFaceData != null)
+ {
+ (IFCAnyHandle polygonalFaceSet, int numFaces) =
+ ExportPlanarSolidAsPolygonalFaceSet(exporterIFC, planarFaceData, trfToUse);
+ if (!IFCAnyHandleUtil.IsNullOrHasNoValue(polygonalFaceSet))
+ {
+ polygonalFaceSets.Add(polygonalFaceSet);
+ AddStyleToFaceSet(exporterIFC, file, document, options, polygonalFaceSet,
+ ifcColourRgbList, opacity, matId, numFaces);
+ }
+ }
+
+ foreach ((TriangleMergeUtil triMerge, bool isClosed) in triangleComponents)
+ {
IList ifcFaces = MergeAndCreateIfcFaces(file, triMerge);
IList> coordList = new List>();
@@ -2128,36 +2201,75 @@ public static IList ExportBodyAsPolygonalFaceSet(ExporterIFC expor
coordList.Add(new List() { vertexScaled.X, vertexScaled.Y, vertexScaled.Z });
}
- IFCAnyHandle polygonalFaceSet = ExportIfcFacesAsPolygonalFaceSet(file, ifcFaces, coordList, mesh.IsClosed, ifcColourRgbList, opacity);
+ IFCAnyHandle coordinatesHnd = IFCInstanceExporter.CreateCartesianPointList3D(file, coordList);
+ IFCAnyHandle polygonalFaceSet = IFCInstanceExporter.CreatePolygonalFaceSet(file, coordinatesHnd, isClosed, ifcFaces, null);
if (!IFCAnyHandleUtil.IsNullOrHasNoValue(polygonalFaceSet))
- polygonalFaceSetList.Add(polygonalFaceSet);
+ {
+ polygonalFaceSets.Add(polygonalFaceSet);
+ AddStyleToFaceSet(exporterIFC, file, document, options, polygonalFaceSet,
+ ifcColourRgbList, opacity, matId, ifcFaces.Count);
+ }
}
+
+ polygonalFaceSetList.AddRange(polygonalFaceSets);
}
catch
{
- // Failed! Likely because either the tessellation or coplanar face merge failed. Try to create from the faceset instead
- IFCAnyHandle triangulatedMesh = ExportSurfaceAsTriangulatedFaceSet(exporterIFC, element, options, geomObject, trfToUse);
- if (!IFCAnyHandleUtil.IsNullOrHasNoValue(triangulatedMesh))
- polygonalFaceSetList.Add(triangulatedMesh);
+ // Failed! Likely because either the tessellation or coplanar face merge failed.
+ // Try to create from the FaceSet instead.
+ polygonalFaceSetList.Clear();
+ break;
}
}
if (polygonalFaceSetList.Count == 0 && !allNotToBeExported)
{
// It is not from Solid, so we will use the faces to export. It works for Surface export too
- IFCAnyHandle triangulatedMesh = ExportSurfaceAsTriangulatedFaceSet(exporterIFC, element, options, geomObject, trfToUse);
- if (!IFCAnyHandleUtil.IsNullOrHasNoValue(triangulatedMesh))
- polygonalFaceSetList.Add(triangulatedMesh);
+ polygonalFaceSetList.AddRange(ExportSurfaceAsTriangulatedFaceSet(exporterIFC, element,
+ options, geomObject, trfToUse));
}
- foreach (IFCAnyHandle polygonalFaceSet in polygonalFaceSetList)
- CreateSurfaceStyleForRepItem(exporterIFC, document, options.CreatingVoid, polygonalFaceSet, matId);
-
return polygonalFaceSetList;
}
///
- /// COllect Solid and/or Mesh from GeometryElement
+ /// Tesselate the solid decreasing the number of facets if necessary.
+ ///
+ /// The Solid
+ /// The body exported options.
+ /// Solid tessellation
+ private static TriangulatedSolidOrShell GetOptimalTessellation(Solid solid, BodyExporterOptions options)
+ {
+ TriangulatedSolidOrShell solidFacetation = SolidUtils.TessellateSolidOrShell(solid, options.TessellationControls);
+
+ SolidOrShellTessellationControls coarseTessellationControls = ExporterUtil.CopyTessellationControls(options.TessellationControls);
+ BodyExporterOptions.SetDefaultCoarseTessellationControls(coarseTessellationControls);
+
+ if (AreTessellationControlsEqual(coarseTessellationControls, options.TessellationControls))
+ {
+ // The tessellation controls are already at the coarsest level.
+ return solidFacetation;
+ }
+
+ bool useCoarseTessellation = false;
+ for (int ii = 0; ii < solidFacetation.ShellComponentCount; ii++)
+ {
+ TriangulatedShellComponent component = solidFacetation.GetShellComponent(ii);
+ if (component.TriangleCount > MaximumAllowedFacets(options))
+ {
+ useCoarseTessellation = true;
+ break;
+ }
+ }
+
+ if (useCoarseTessellation)
+ solidFacetation = SolidUtils.TessellateSolidOrShell(solid, coarseTessellationControls);
+
+ return solidFacetation;
+ }
+
+ ///
+ /// Collect Solid and/or Mesh from GeometryElement
///
/// the GeometryElement
/// list of Solid and/or Mesh
@@ -2183,24 +2295,13 @@ private static List GetGeometryObjectListFromGeometryElement(Doc
private static IList MergeAndCreateIfcFaces(IFCFile file, TriangleMergeUtil triMerge)
{
// TODO: Look at performance implications of large facetations.
- bool ignoreMerge = false;
IList faces = new List();
- try
- {
- triMerge.SimplifyAndMergeFaces(ignoreMerge);
- }
- catch
+ const bool tryToMerge = true;
+ if (!triMerge.SimplifyAndMergeFaces(tryToMerge))
{
- if (ignoreMerge)
- {
- return faces;
- }
- else
- {
- triMerge.Reset();
- triMerge.SimplifyAndMergeFaces(false);
- }
+ triMerge.Reset();
+ triMerge.SimplifyAndMergeFaces(!tryToMerge);
}
@@ -2230,27 +2331,6 @@ private static IList MergeAndCreateIfcFaces(IFCFile file, Triangle
return faces;
}
- ///
- /// Exports Ifc Faces as a single IfcPolygonalFaceSet with an associated colour map
- ///
- /// the File
- /// IFC face handles
- /// coordinate list
- /// indicates whether the mesh is closed
- /// Handle of the RGB colour list
- /// Opacity of the colour map
- /// IFC handle for the PolygeonalFaceSet
- private static IFCAnyHandle ExportIfcFacesAsPolygonalFaceSet(IFCFile file, IList ifcFaceHandles, IList> coordList, bool isClosed, IFCAnyHandle ifcColourRgbList, double? opacity)
- {
- IFCAnyHandle coordinatesHnd = IFCInstanceExporter.CreateCartesianPointList3D(file, coordList);
- IFCAnyHandle polygonalFaceSet = IFCInstanceExporter.CreatePolygonalFaceSet(file, coordinatesHnd, isClosed, ifcFaceHandles, null);
- IList colourIndex = Enumerable.Repeat(1, ifcFaceHandles.Count).ToList();
- if (!IFCAnyHandleUtil.IsNullOrHasNoValue(ifcColourRgbList) && !IFCAnyHandleUtil.IsNullOrHasNoValue(polygonalFaceSet))
- IFCInstanceExporter.CreateIndexedColourMap(file, polygonalFaceSet, opacity, ifcColourRgbList, colourIndex);
-
- return polygonalFaceSet;
- }
-
private static int MaximumAllowedFacets(BodyExporterOptions options)
{
// We are going to limit the number of triangles to 25000 for Coarse tessellation, and 50000 otherwise.
@@ -2259,6 +2339,121 @@ private static int MaximumAllowedFacets(BodyExporterOptions options)
return (options.TessellationLevel == BodyExporterOptions.BodyTessellationLevel.Coarse) ? 25000 : 50000;
}
+ private static (IList>, IList>) GetMeshCoordinateInfo(
+ ExporterIFC exporterIFC, Mesh mesh, Transform lcs, BodyExporterOptions options)
+ {
+ // Note that this function has two possible return values:
+ // (coordList, coordIdx) == (empty, empty): Unhandled or empty mesh.
+ // Anything else: valid data.
+ IList> coordList = new List>();
+ IList> coordIdx = new List>();
+
+ int numberOfTriangles = mesh.NumTriangles;
+ int numberOfVertices = mesh.Vertices.Count;
+
+ // We are going to limit the number of triangles to prevent the solid faceter from creating too many extra triangles to sew the surfaces.
+ if (numberOfTriangles == 0 || numberOfVertices == 0 || numberOfTriangles >= MaximumAllowedFacets(options))
+ {
+ return (coordList, coordIdx);
+ }
+
+ // create list of vertices first.
+ foreach (XYZ vertex in mesh.Vertices)
+ {
+ XYZ vertexScaled = TransformAndScalePoint(exporterIFC, vertex, lcs);
+ coordList.Add(new List(3) { vertexScaled.X, vertexScaled.Y, vertexScaled.Z });
+ }
+ // Create the entity IfcCartesianPointList3D from the List of List and assign it to attribute Coordinates of IfcTriangulatedFaceSet
+
+ // Export all of the triangles
+ for (int ii = 0; ii < numberOfTriangles; ii++)
+ {
+ MeshTriangle triangle = mesh.get_Triangle(ii);
+ // IFC uses index that starts with 1 instead of 0 (following similar standard in X3D)
+ coordIdx.Add(new List(3)
+ {
+ (int)triangle.get_Index(0) + 1,
+ (int)triangle.get_Index(1) + 1,
+ (int)triangle.get_Index(2) + 1
+ });
+ }
+
+ return (coordList, coordIdx);
+ }
+
+ private static (IList>, IList>) GetSolidCoordinateInfo(
+ ExporterIFC exporterIFC, Solid solid, Transform lcs, BodyExporterOptions options)
+ {
+ // Note that this function has three possible return values:
+ // (coordList, coordIdx) == (null, null): Exception thrown because of invalid data.
+ // (coordList, coordIdx) == (empty, empty): Unhandled or empty geometry.
+ // Anything else: valid data.
+ IList> coordList = new List>();
+ IList> coordIdx = new List>();
+
+ try
+ {
+ SolidOrShellTessellationControls tessellationControls = options.TessellationControls;
+ TriangulatedSolidOrShell solidFacetation =
+ SolidUtils.TessellateSolidOrShell(solid, tessellationControls);
+
+ // Only handle one solid or shell.
+ if (solidFacetation.ShellComponentCount != 1)
+ {
+ return (coordList, coordIdx);
+ }
+
+ TriangulatedShellComponent component = solidFacetation.GetShellComponent(0);
+ int numberOfTriangles = component.TriangleCount;
+ int numberOfVertices = component.VertexCount;
+
+ // We are going to limit the number of triangles to prevent the solid faceter from creating too many extra triangles to sew the surfaces.
+ if (numberOfTriangles == 0 || numberOfVertices == 0 || numberOfTriangles >= MaximumAllowedFacets(options))
+ {
+ return (coordList, coordIdx);
+ }
+
+ // create list of vertices first.
+ for (int ii = 0; ii < numberOfVertices; ii++)
+ {
+ XYZ vertex = component.GetVertex(ii);
+ XYZ vertexScaled = TransformAndScalePoint(exporterIFC, vertex, lcs);
+ coordList.Add(new List(3) { vertexScaled.X, vertexScaled.Y, vertexScaled.Z });
+ }
+ // Create the entity IfcCartesianPointList3D from the List of List and assign it to attribute Coordinates of IfcTriangulatedFaceSet
+
+ // Export all of the triangles
+ for (int ii = 0; ii < numberOfTriangles; ii++)
+ {
+ TriangleInShellComponent triangle = component.GetTriangle(ii);
+ // IFC uses index that starts with 1 instead of 0 (following similar standard in X3D)
+ coordIdx.Add(new List(3) { triangle.VertexIndex0 + 1, triangle.VertexIndex1 + 1, triangle.VertexIndex2 + 1 });
+ }
+ }
+ catch
+ {
+ return (null, null);
+ }
+
+ return (coordList, coordIdx);
+ }
+
+ private static (IList>, IList>) GetGeometryCoordinateInfo(
+ ExporterIFC exporterIFC, GeometryObject geom, Transform lcs, BodyExporterOptions options)
+ {
+ if (geom is Solid)
+ {
+ return GetSolidCoordinateInfo(exporterIFC, geom as Solid, lcs, options);
+ }
+ else if (geom is Mesh)
+ {
+ return GetMeshCoordinateInfo(exporterIFC, geom as Mesh, lcs, options);
+ }
+
+ // Return an empty list, to signify no error, just ignored. Really shouldn't get here.
+ return (new List>(), new List>());
+ }
+
///
/// Export Geometry in IFC4 Triangulated tessellation
///
@@ -2273,152 +2468,76 @@ public static IList ExportBodyAsTriangulatedFaceSet(ExporterIFC ex
IFCFile file = exporterIFC.GetFile();
Document document = element.Document;
- IFCAnyHandle ifcColourRgbList = GetBestColourAndOpacity(file, element,
- geomObject, out double opacity, out ElementId matId);
-
- IList triangulatedBodyList = new List();
+ List triangulatedBodyList = new List();
List colourIndex = new List();
- // We need to collect all SOlids and Meshes from the GeometryObject if it is of types GeometryElement or GeometryInstance
+ // We need to collect all Solids and Meshes from the GeometryObject if it is of types GeometryElement or GeometryInstance
// If the geomObject is GeometryELement or GeometryInstance, we need to collect their primitive Solid and Mesh first
IList geomObjectPrimitives = GetGeometriesFromGeometryElement(
exporterIFC, document, geomObject, true, out bool allNotToBeExported);
- // At this point the collection will only contains Solids and/or Meshes. Loop through each of them
+ // At this point the collection will only contains Solids and/or Meshes.
+ // Loop through each of them.
+ // Note that we will collect all of the coordList and coordIdx first since if any fail,
+ // we will fall back to ExportSurfaceAsTriangulatedFaceSet.
+
+ IList<(GeometryObject, IList>, IList>)> coordListsAndIndices =
+ new List<(GeometryObject, IList>, IList>)>();
foreach (GeometryObject geom in geomObjectPrimitives)
{
- if (geom is Solid)
- {
- try
- {
- Solid solid = geom as Solid;
-
- SolidOrShellTessellationControls tessellationControls = options.TessellationControls;
- TriangulatedSolidOrShell solidFacetation =
- SolidUtils.TessellateSolidOrShell(solid, tessellationControls);
-
- // Only handle one solid or shell.
- if (solidFacetation.ShellComponentCount == 1)
- {
- TriangulatedShellComponent component = solidFacetation.GetShellComponent(0);
- int numberOfTriangles = component.TriangleCount;
- int numberOfVertices = component.VertexCount;
-
- // We are going to limit the number of triangles to prevent the solid faceter from creating too many extra triangles to sew the surfaces.
- if ((numberOfTriangles > 0 && numberOfVertices > 0) && (numberOfTriangles < MaximumAllowedFacets(options)))
- {
- IList> coordList = new List>();
- IList> coordIdx = new List>();
-
- // create list of vertices first.
- for (int ii = 0; ii < numberOfVertices; ii++)
- {
- XYZ vertex = component.GetVertex(ii);
- XYZ vertexScaled = TransformAndScalePoint(exporterIFC, vertex, lcs);
- coordList.Add(new List(3) { vertexScaled.X, vertexScaled.Y, vertexScaled.Z });
- }
- // Create the entity IfcCartesianPointList3D from the List of List and assign it to attribute Coordinates of IfcTriangulatedFaceSet
-
- // Export all of the triangles
- for (int ii = 0; ii < numberOfTriangles; ii++)
- {
- TriangleInShellComponent triangle = component.GetTriangle(ii);
- // IFC uses index that starts with 1 instead of 0 (following similar standard in X3D)
- coordIdx.Add(new List(3) { triangle.VertexIndex0 + 1, triangle.VertexIndex1 + 1, triangle.VertexIndex2 + 1 });
- }
+ IList> coordList = new List>();
+ IList> coordIdx = new List>();
+ (coordList, coordIdx) = GetGeometryCoordinateInfo(exporterIFC, geom, lcs, options);
- // Create attribute CoordIndex from the List of List of the IfcTriangulatedFaceSet
-
- IFCAnyHandle coordPointLists = IFCAnyHandleUtil.CreateInstance(file, IFCEntityType.IfcCartesianPointList3D);
- IFCAnyHandleUtil.SetAttribute(coordPointLists, "CoordList", coordList, 1, null, 3, 3);
-
- IFCAnyHandle triangulatedBody = IFCAnyHandleUtil.CreateInstance(file, IFCEntityType.IfcTriangulatedFaceSet);
- IFCAnyHandleUtil.SetAttribute(triangulatedBody, "Coordinates", coordPointLists);
- IFCAnyHandleUtil.SetAttribute(triangulatedBody, "CoordIndex", coordIdx, 1, null, 3, 3);
-
- // Currently each face will refer to just a single color in ColourRgbList
- colourIndex.AddRange(Enumerable.Repeat(1, numberOfTriangles));
- if (!IFCAnyHandleUtil.IsNullOrHasNoValue(ifcColourRgbList) && !IFCAnyHandleUtil.IsNullOrHasNoValue(triangulatedBody))
- IFCInstanceExporter.CreateIndexedColourMap(file, triangulatedBody, opacity, ifcColourRgbList, colourIndex);
-
- triangulatedBodyList.Add(triangulatedBody);
- }
- }
- }
- catch
- {
- // Failed! Likely because of the tessellation failed. Try to create from the faceset instead
- IFCAnyHandle triangulatedMesh = ExportSurfaceAsTriangulatedFaceSet(exporterIFC, element, options, geomObject, lcs);
- if (!IFCAnyHandleUtil.IsNullOrHasNoValue(triangulatedMesh))
- triangulatedBodyList.Add(triangulatedMesh);
- }
- }
- else if (geom is Mesh)
+ // Failed; go to fallback.
+ if (coordList == null || coordIdx == null)
{
- Mesh mesh = geom as Mesh;
-
- int numberOfTriangles = mesh.NumTriangles;
- int numberOfVertices = mesh.Vertices.Count;
-
- // We are going to limit the number of triangles to prevent the solid faceter from creating too many extra triangles to sew the surfaces.
- if ((numberOfTriangles > 0 && numberOfVertices > 0) && (numberOfTriangles < MaximumAllowedFacets(options)))
- {
- IList> coordList = new List>();
- IList> coordIdx = new List>();
-
- // create list of vertices first.
- foreach (XYZ vertex in mesh.Vertices)
- {
- XYZ vertexScaled = TransformAndScalePoint(exporterIFC, vertex, lcs);
- coordList.Add(new List(3) { vertexScaled.X, vertexScaled.Y, vertexScaled.Z });
- }
- // Create the entity IfcCartesianPointList3D from the List of List and assign it to attribute Coordinates of IfcTriangulatedFaceSet
+ coordListsAndIndices.Clear();
+ break;
+ }
- // Export all of the triangles
- for (int ii = 0; ii < numberOfTriangles; ii++)
- {
- MeshTriangle triangle = mesh.get_Triangle(ii);
- // IFC uses index that starts with 1 instead of 0 (following similar standard in X3D)
- coordIdx.Add(new List(3)
- {
- (int)triangle.get_Index(0) + 1,
- (int)triangle.get_Index(1) + 1,
- (int)triangle.get_Index(2) + 1
- });
- }
+ if (coordList.Count > 0 && coordIdx.Count > 0)
+ {
+ coordListsAndIndices.Add((geom, coordList, coordIdx));
+ }
+ }
- // Create attribute CoordIndex from the List of List of the IfcTriangulatedFaceSet
+ foreach ((GeometryObject geom, IList> coordList, IList> coordIdx) in coordListsAndIndices)
+ {
+ IFCAnyHandle coordPointLists = IFCAnyHandleUtil.CreateInstance(file, IFCEntityType.IfcCartesianPointList3D);
+ IFCAnyHandleUtil.SetAttribute(coordPointLists, "CoordList", coordList, 1, null, 3, 3);
- IFCAnyHandle coordPointLists = IFCAnyHandleUtil.CreateInstance(file, IFCEntityType.IfcCartesianPointList3D);
- IFCAnyHandleUtil.SetAttribute(coordPointLists, "CoordList", coordList, 1, null, 3, 3);
+ IFCAnyHandle triangulatedBody = IFCAnyHandleUtil.CreateInstance(file, IFCEntityType.IfcTriangulatedFaceSet);
+ IFCAnyHandleUtil.SetAttribute(triangulatedBody, "Coordinates", coordPointLists);
+ IFCAnyHandleUtil.SetAttribute(triangulatedBody, "CoordIndex", coordIdx, 1, null, 3, 3);
- IFCAnyHandle triangulatedBody = IFCAnyHandleUtil.CreateInstance(file, IFCEntityType.IfcTriangulatedFaceSet);
- IFCAnyHandleUtil.SetAttribute(triangulatedBody, "Coordinates", coordPointLists);
- IFCAnyHandleUtil.SetAttribute(triangulatedBody, "CoordIndex", coordIdx, 1, null, 3, 3);
+ triangulatedBodyList.Add(triangulatedBody);
- // Currently each face will refer to just a single color in ColourRgbList
- colourIndex.AddRange(Enumerable.Repeat(1, numberOfTriangles));
- if (!IFCAnyHandleUtil.IsNullOrHasNoValue(ifcColourRgbList) && !IFCAnyHandleUtil.IsNullOrHasNoValue(triangulatedBody))
- IFCInstanceExporter.CreateIndexedColourMap(file, triangulatedBody, opacity, ifcColourRgbList, colourIndex);
+ IFCAnyHandle ifcColourRgbList = GetBestColourAndOpacity(file, element,
+ geom, out double opacity, out ElementId matId);
- triangulatedBodyList.Add(triangulatedBody);
- }
+ // Currently each face will refer to just a single color in ColourRgbList
+ colourIndex.AddRange(Enumerable.Repeat(1, coordIdx.Count));
+ if (!IFCAnyHandleUtil.IsNullOrHasNoValue(ifcColourRgbList) &&
+ !IFCAnyHandleUtil.IsNullOrHasNoValue(triangulatedBody))
+ {
+ IFCInstanceExporter.CreateIndexedColourMap(file, triangulatedBody, opacity, ifcColourRgbList, colourIndex);
+ }
+ if (matId != ElementId.InvalidElementId)
+ {
+ CreateSurfaceStyleForRepItem(exporterIFC, document, options.CreatingVoid,
+ triangulatedBody, matId);
}
}
- if ((triangulatedBodyList == null || triangulatedBodyList.Count == 0) && !allNotToBeExported)
+ if (triangulatedBodyList.Count == 0 && !allNotToBeExported)
{
- // It is not from Solid, so we will use the faces to export. It works for Surface export too
- IFCAnyHandle triangulatedMesh = ExportSurfaceAsTriangulatedFaceSet(exporterIFC, element, options, geomObject, lcs);
- if (!IFCAnyHandleUtil.IsNullOrHasNoValue(triangulatedMesh))
- triangulatedBodyList.Add(triangulatedMesh);
+ triangulatedBodyList.AddRange(ExportSurfaceAsTriangulatedFaceSet(exporterIFC, element,
+ options, geomObject, lcs));
}
-
- foreach (IFCAnyHandle triangulatedBody in triangulatedBodyList)
- CreateSurfaceStyleForRepItem(exporterIFC, document, options.CreatingVoid, triangulatedBody, matId);
-
+
return triangulatedBodyList;
}
@@ -2431,7 +2550,7 @@ public static IList ExportBodyAsTriangulatedFaceSet(ExporterIFC ex
/// geometry objects
/// returns a handle
public static IList ExportBodyAsTessellatedFaceSet(ExporterIFC exporterIFC, Element element, BodyExporterOptions options,
- GeometryObject geomObject, Transform lcs = null)
+ GeometryObject geomObject, Transform lcs = null)
{
IList tessellatedBodyList = null;
@@ -2447,10 +2566,10 @@ public static IList ExportBodyAsTessellatedFaceSet(ExporterIFC exp
return tessellatedBodyList;
}
- private static IFCAnyHandle GetBestColourAndOpacity(IFCFile file, Element element,
- GeometryObject geometryObject, out double opacity, out ElementId bestMaterialId)
+ private static (Color, double, ElementId) GetBestColourAndOpacity(Element element,
+ GeometryObject geometryObject)
{
- bestMaterialId = GetBestMaterialIdFromGeometryOrParameter(geometryObject, element);
+ ElementId bestMaterialId = GetBestMaterialIdFromGeometryOrParameter(geometryObject, element);
Color exportColor = null;
Material matElem = (bestMaterialId != null && bestMaterialId != ElementId.InvalidElementId) ?
@@ -2466,68 +2585,117 @@ private static IFCAnyHandle GetBestColourAndOpacity(IFCFile file, Element elemen
exportColor = CategoryUtil.GetSafeColor(matElem.Color);
}
- opacity = (double)(100 - (matElem?.Transparency ?? 0)) / 100;
+ double opacity = (double)(100 - (matElem?.Transparency ?? 0)) / 100;
// For now we will only support a single color for the tessellation since there is no
// good way to associate the face and the color.
+ return (exportColor, opacity, bestMaterialId);
+ }
+
+ private static IFCAnyHandle GetBestColourAndOpacity(IFCFile file, Element element,
+ GeometryObject geometryObject, out double opacity, out ElementId bestMaterialId)
+ {
+ Color exportColor;
+ (exportColor, opacity, bestMaterialId) = GetBestColourAndOpacity(element, geometryObject);
+
return (exportColor == null) ? null : ColourRgbListFromColor(file, exportColor);
}
///
- /// Return a triangulated face set from the list of faces
+ /// Return a list of triangulated face sets from the geometry.
///
- /// exporter IFC
- /// the element
- /// the body export options
- /// the geometry object
- /// returns the handle
- private static IFCAnyHandle ExportSurfaceAsTriangulatedFaceSet(ExporterIFC exporterIFC, Element element, BodyExporterOptions options,
- GeometryObject geomObject, Transform trfToUse = null)
+ /// The exporterIFC class.
+ /// The element.
+ /// The body exporter options.
+ /// The geometry object.
+ /// Returns a list of handles.
+ private static IList ExportSurfaceAsTriangulatedFaceSet(
+ ExporterIFC exporterIFC, Element element, BodyExporterOptions options,
+ GeometryObject geomObject, Transform trfToUse = null)
{
IFCFile file = exporterIFC.GetFile();
+ IList listOfIndexedTriangles = new List();
- IFCAnyHandle ifcColourRgbList = GetBestColourAndOpacity(file, element, geomObject,
- out double opacity, out _);
-
- IList colourIndex = new List();
-
- List> triangleList = new List>();
+ List<(List>, Color, double, ElementId)> triangleLists =
+ new List<(List>, Color, double, ElementId)>();
- if (geomObject is Solid)
- {
- triangleList = GetTriangleListFromSolid(geomObject, options, trfToUse);
- }
- else if (geomObject is Mesh)
- {
- triangleList = GetTriangleListFromMesh(geomObject, trfToUse);
- }
- // There is also a possibility that the geomObject is an GeometryElement thaat is a collection of GeometryObjects. Go through the collection and get the Mesh, Solid, or Face in it
- else if (geomObject is GeometryElement)
+ if (geomObject is GeometryElement)
{
- // We will skip the line geometries if they are in the IEnumerable
+ // There is also a possibility that the geomObject is an GeometryElement thaat is a
+ // collection of GeometryObjects. Go through the collection and get the Meshes, Solids,
+ // and Faces. We will skip everything else.
+
+ // NOTE: We might have some "duplicate" colors in the list below. For now, we will
+ // allow that, since it is really a very minor optimization to compact the colors.
foreach (GeometryObject geom in (geomObject as GeometryElement))
{
if (geom is Solid)
- triangleList.AddRange(GetTriangleListFromSolid(geom, options, trfToUse));
- if (geom is Mesh)
- triangleList.AddRange(GetTriangleListFromMesh(geom, trfToUse));
- if (geom is Face)
+ {
+ triangleLists.AddRange(GetTriangleListsFromSolid(exporterIFC, element, geom, options, trfToUse));
+ }
+ else if (geom is Mesh)
+ {
+ triangleLists.Add(GetTriangleListFromMesh(exporterIFC, element, geom, trfToUse, null));
+ }
+ else if (geom is Face)
{
Mesh faceMesh = (geom as Face).Triangulate();
- triangleList.AddRange(GetTriangleListFromMesh(faceMesh, trfToUse));
+ triangleLists.Add(GetTriangleListFromMesh(exporterIFC, element, faceMesh, trfToUse, geom));
+ }
+ else
+ {
+ continue;
}
}
}
+ else
+ {
+ if (geomObject is Solid)
+ {
+ triangleLists.AddRange(GetTriangleListsFromSolid(exporterIFC, element, geomObject, options, trfToUse));
+ }
+ else if (geomObject is Mesh)
+ {
+ triangleLists.Add(GetTriangleListFromMesh(exporterIFC, element, geomObject, trfToUse, null));
+ }
+ else
+ {
+ return listOfIndexedTriangles;
+ }
+ }
- IFCAnyHandle indexedTriangles = GeometryUtil.GetIndexedTriangles(file, triangleList);
- for (int faceCnt = 0; faceCnt < triangleList.Count; ++faceCnt)
+ Document document = element?.Document;
+ foreach ((List> triangleList, Color color, double opacity,
+ ElementId matId) in triangleLists)
{
- colourIndex.Add(1); // Currently each face will refer to just a single color in ColourRgbList
+ if (triangleList.Count == 0)
+ {
+ continue;
+ }
+
+ IFCAnyHandle indexedTriangles = GeometryUtil.GetIndexedTriangles(file, triangleList);
+
+ IFCAnyHandle ifcColourRgbList = ColourRgbListFromColor(file, color);
+
+ if (!IFCAnyHandleUtil.IsNullOrHasNoValue(indexedTriangles))
+ {
+ List colourIndex = Enumerable.Repeat(1, triangleList.Count).ToList();
+
+ listOfIndexedTriangles.Add(indexedTriangles);
+ if (!IFCAnyHandleUtil.IsNullOrHasNoValue(ifcColourRgbList))
+ {
+ IFCInstanceExporter.CreateIndexedColourMap(file, indexedTriangles, opacity, ifcColourRgbList, colourIndex);
+ }
+
+ if (matId != ElementId.InvalidElementId)
+ {
+ CreateSurfaceStyleForRepItem(exporterIFC, document, options.CreatingVoid,
+ indexedTriangles, matId);
+ }
+ }
}
- if (!IFCAnyHandleUtil.IsNullOrHasNoValue(ifcColourRgbList) && !IFCAnyHandleUtil.IsNullOrHasNoValue(indexedTriangles))
- IFCInstanceExporter.CreateIndexedColourMap(file, indexedTriangles, opacity, ifcColourRgbList, colourIndex);
- return indexedTriangles;
+ return listOfIndexedTriangles;
}
private static bool AreTessellationControlsEqual(SolidOrShellTessellationControls first, SolidOrShellTessellationControls second)
@@ -2752,7 +2920,8 @@ private static BodyData ExportBodyAsBRep(ExporterIFC exporterIFC, IList facetHnds = sweptSolidExporter?.Facets;
if (facetHnds != null && facetHnds.Count != 0)
{
@@ -2782,7 +2951,7 @@ private static BodyData ExportBodyAsBRep(ExporterIFC exporterIFC, IList triangulatedBodyItems = ExportBodyAsTessellatedFaceSet(exporterIFC, element, options, geomObject, trfToUse);
- if (triangulatedBodyItems != null && triangulatedBodyItems.Count > 0)
+ if ((triangulatedBodyItems?.Count ?? 0) > 0)
{
GraphicsStyle style = document.GetElement(geomObject.GraphicsStyleId) as GraphicsStyle;
foreach (IFCAnyHandle triangulatedBodyItem in triangulatedBodyItems)
@@ -3113,6 +3282,7 @@ public static BodyData ExportBody(ExporterIFC exporterIFC,
// If we are exporting a coarse tessellation, or regardless if the level of detail isn't set to the highest level,
// we will try to see if we can use an optimized BRep created from a swept solid.
+ bool isCoarse = options.TessellationLevel == BodyExporterOptions.BodyTessellationLevel.Coarse;
bool allowExportAsOptimizedBRep = (options.TessellationLevel == BodyExporterOptions.BodyTessellationLevel.Coarse ||
ExporterCacheManager.ExportOptionsCache.LevelOfDetail < ExportOptionsCache.ExportTessellationLevel.High);
bool allowAdvancedBReps = !ExporterCacheManager.ExportOptionsCache.ExportAsOlderThanIFC4
@@ -3374,9 +3544,8 @@ public static BodyData ExportBody(ExporterIFC exporterIFC,
Transform lcs = Transform.Identity;
IFCAnyHandle extrusionHandle = ExtrusionExporter.CreateExtrudedSolidFromExtrusionData(exporterIFC,
element, extrusionLists[ii][0], out lcs, profileName: profileName);
- if (!IFCAnyHandleUtil.IsNullOrHasNoValue(extrusionHandle))
+ if (bodyItems.AddIfNotNull(extrusionHandle))
{
- bodyItems.Add(extrusionHandle);
materialIdsForExtrusions.Add(exporterIFC.GetMaterialIdForCurrentExportState());
IList curveLoops = extrusionLists[ii][0].GetLoops();
@@ -3412,7 +3581,7 @@ public static BodyData ExportBody(ExporterIFC exporterIFC,
exportBodyParams.ScaledWidth = UnitUtil.ScaleLength(width);
}
- double area = ExporterIFCUtils.ComputeAreaOfCurveLoops(curveLoops);
+ double area = ExporterIFCUtils.ComputeAreaOfCurveLoops(new[] { curveLoops[0] });
if (area > 0.0)
{
exportBodyParams.ScaledArea = UnitUtil.ScaleArea(area);
@@ -3476,12 +3645,12 @@ public static BodyData ExportBody(ExporterIFC exporterIFC,
if (options.CollectFootprintHandle)
addInfo |= GenerateAdditionalInfo.GenerateFootprint;
- SweptSolidExporter sweptSolidExporter = SweptSolidExporter.Create(exporterIFC, element, simpleSweptSolidAnalyzer, solid, addInfo: addInfo);
+ SweptSolidExporter sweptSolidExporter = SweptSolidExporter.Create(exporterIFC, element,
+ simpleSweptSolidAnalyzer, solid, addInfo, isCoarse);
IFCAnyHandle sweptHandle = sweptSolidExporter?.RepresentationItem;
- if (!IFCAnyHandleUtil.IsNullOrHasNoValue(sweptHandle))
+ if (bodyItems.AddIfNotNull(sweptHandle))
{
- bodyItems.Add(sweptHandle);
ElementId matId = exporterIFC.GetMaterialIdForCurrentExportState();
materialIdsForExtrusions.Add(matId);
GraphicsStyle style = document.GetElement(solid.GraphicsStyleId) as GraphicsStyle;
@@ -3754,76 +3923,99 @@ static BodyData SaveMaterialAndFootprintInfo(BodyData bodyData, MaterialAndProfi
return bodyData;
}
- static List> GetTriangleListFromSolid(GeometryObject geomObject, BodyExporterOptions options, Transform trfToUse)
+ static IList<(List>, Color, double, ElementId)> GetTriangleListsFromSolid(
+ ExporterIFC exporterIFC, Element element, GeometryObject geomObject,
+ BodyExporterOptions options, Transform trfToUse)
{
- List> triangleList = new List>();
+ IDictionary>, Color, double>> triangleListDict =
+ new SortedDictionary>, Color, double>>();
+
Solid geomSolid = geomObject as Solid;
FaceArray faces = geomSolid.Faces;
- double scale = UnitUtil.ScaleLengthForRevitAPI();
// The default tessellationLevel is -1, which is illegal for Triangulate. Get a value in range.
double tessellationLevel = options.TessellationControls.LevelOfDetail;
if (tessellationLevel < 0.0)
+ {
tessellationLevel = ((double)ExporterCacheManager.ExportOptionsCache.LevelOfDetail) / 4.0;
+ }
foreach (Face face in faces)
{
Mesh faceTriangulation = face.Triangulate(tessellationLevel);
- if (faceTriangulation != null)
+ if (faceTriangulation == null)
{
- for (int ii = 0; ii < faceTriangulation.NumTriangles; ++ii)
- {
- List triangleVertices = new List();
- MeshTriangle triangle = faceTriangulation.get_Triangle(ii);
- for (int tri = 0; tri < 3; ++tri)
- {
- XYZ vert = scale * triangle.get_Vertex(tri);
- if (trfToUse != null)
- vert = trfToUse.OfPoint(vert);
-
- triangleVertices.Add(vert);
- }
- triangleList.Add(triangleVertices);
- }
+ continue;
}
- else
+
+ (List> triangleList, Color color, double opacity, ElementId matId) =
+ GetTriangleListFromMesh(exporterIFC, element, faceTriangulation, trfToUse, face);
+ long matIdAsLong = matId.Value;
+ if (!triangleListDict.TryGetValue(matIdAsLong, out Tuple>, Color, double> currList))
{
- // TODO: log the information to the user since it will mean missing face for this geometry though the failure is probably because the face is too thin or self intersecting
+ List> emptyTriangleList = new List>();
+ currList = Tuple.Create(emptyTriangleList, color, opacity);
+ triangleListDict[matIdAsLong] = currList;
}
+ currList.Item1.AddRange(triangleList);
+ }
+
+ IList<(List>, Color, double, ElementId)> triangleLists = new
+ List<(List>, Color, double, ElementId)>();
+
+ foreach (KeyValuePair>, Color, double>> data in triangleListDict)
+ {
+ (List>, Color, double, ElementId) triangleList =
+ (data.Value.Item1, data.Value.Item2, data.Value.Item3, new ElementId(data.Key));
+ triangleLists.Add(triangleList);
}
- return triangleList;
+
+ return triangleLists;
}
- static List> GetTriangleListFromMesh(GeometryObject geomObject, Transform trfToUse)
+ static (List>, Color, double, ElementId) GetTriangleListFromMesh(
+ ExporterIFC exporterIFC, Element element, GeometryObject geomObject, Transform trfToUse, GeometryObject parentObject)
{
List> triangleList = new List>();
Mesh geomMesh = geomObject as Mesh;
- double scale = UnitUtil.ScaleLengthForRevitAPI();
+
for (int ii = 0; ii < geomMesh.NumTriangles; ++ii)
{
List triangleVertices = new List();
MeshTriangle triangle = geomMesh.get_Triangle(ii);
for (int tri = 0; tri < 3; ++tri)
{
- XYZ vert = scale * triangle.get_Vertex(tri);
- if (trfToUse != null)
- vert = trfToUse.OfPoint(vert);
-
+ XYZ vert = TransformAndScalePoint(exporterIFC, triangle.get_Vertex(tri), trfToUse);
triangleVertices.Add(vert);
}
triangleList.Add(triangleVertices);
}
- return triangleList;
+
+ (Color color, double opacity, ElementId matId) = GetBestColourAndOpacity(element, parentObject ?? geomObject);
+ return (triangleList, color, opacity, matId);
+ }
+
+ static IList ColorToRgb(Color color)
+ {
+ double blueVal = (color?.Blue ?? 127.0) / 255.0;
+ double greenVal = (color?.Green ?? 127.0) / 255.0;
+ double redVal = (color?.Red ?? 127.0) / 255.0;
+ return new List() { redVal, greenVal, blueVal };
}
static IFCAnyHandle ColourRgbListFromColor(IFCFile file, Color matColor)
{
- double blueVal = matColor.Blue / 255.0;
- double greenVal = matColor.Green / 255.0;
- double redVal = matColor.Red / 255.0;
+ IList> colourRgbList = new List>() { ColorToRgb(matColor) };
+ return IFCInstanceExporter.CreateColourRgbList(file, colourRgbList);
+ }
+
+ static IFCAnyHandle ColourRgbListFromColors(IFCFile file, IList matColors)
+ {
IList> colourRgbList = new List>();
- IList rgbVal = new List() { redVal, greenVal, blueVal };
- colourRgbList.Add(rgbVal);
+ foreach (Color matColor in matColors)
+ {
+ colourRgbList.Add(ColorToRgb(matColor));
+ }
return IFCInstanceExporter.CreateColourRgbList(file, colourRgbList);
}
diff --git a/Source/Revit.IFC.Export/Exporter/CeilingExporter.cs b/Source/Revit.IFC.Export/Exporter/CeilingExporter.cs
index eefce354..7c3418c4 100644
--- a/Source/Revit.IFC.Export/Exporter/CeilingExporter.cs
+++ b/Source/Revit.IFC.Export/Exporter/CeilingExporter.cs
@@ -106,20 +106,15 @@ public static void ExportCovering(ExporterIFC exporterIFC, Element element, ref
ecData.PossibleExtrusionAxes = (element is FamilyInstance) ? IFCExtrusionAxes.TryXYZ : IFCExtrusionAxes.TryZ;
BodyExporterOptions bodyExporterOptions = new BodyExporterOptions(true, ExportOptionsCache.ExportTessellationLevel.ExtraLow);
- if (exportByComponents)
- {
- prodRep = RepresentationUtil.CreateProductDefinitionShapeWithoutBodyRep(exporterIFC, element, categoryId, geomElem, representations);
- }
- else
+ if (!exportByComponents)
{
prodRep = RepresentationUtil.CreateAppropriateProductDefinitionShape(exporterIFC, element,
categoryId, geomElem, bodyExporterOptions, null, ecData, true);
- }
-
- if (IFCAnyHandleUtil.IsNullOrHasNoValue(prodRep))
- {
- ecData.ClearOpenings();
- return;
+ if (IFCAnyHandleUtil.IsNullOrHasNoValue(prodRep))
+ {
+ ecData.ClearOpenings();
+ return;
+ }
}
}
@@ -136,6 +131,13 @@ public static void ExportCovering(ExporterIFC exporterIFC, Element element, ref
string instanceGUID = GUIDUtil.CreateGUID(element);
string coveringType = IFCValidateEntry.GetValidIFCPredefinedTypeType(ifcEnumType, defaultCoveringEnumType, "IfcCoveringType");
+ if (exportByComponents)
+ {
+ prodRep = RepresentationUtil.CreateProductDefinitionShapeWithoutBodyRep(exporterIFC, element, categoryId, geomElem, representations);
+ IFCAnyHandle hostShapeRepFromParts = PartExporter.ExportHostPartAsShapeAspects(exporterIFC, element, prodRep,
+ productWrapper, setter, setter.LocalPlacement, ElementId.InvalidElementId, layersetInfo, ecData);
+ }
+
IFCAnyHandle covering = IFCInstanceExporter.CreateCovering(exporterIFC, element, instanceGUID, ExporterCacheManager.OwnerHistoryHandle,
setter.LocalPlacement, prodRep, coveringType);
@@ -143,11 +145,7 @@ public static void ExportCovering(ExporterIFC exporterIFC, Element element, ref
{
PartExporter.ExportHostPart(exporterIFC, element, covering, productWrapper, setter, setter.LocalPlacement, null, setMaterialNameToPartName);
}
- else if (exportByComponents)
- {
- IFCAnyHandle hostShapeRepFromParts = PartExporter.ExportHostPartAsShapeAspects(exporterIFC, element, prodRep,
- productWrapper, setter, setter.LocalPlacement, ElementId.InvalidElementId, layersetInfo, ecData);
- }
+
ExporterUtil.AddIntoComplexPropertyCache(covering, layersetInfo);
@@ -191,7 +189,7 @@ public static void ExportCovering(ExporterIFC exporterIFC, Element element, ref
if (ceiling != null)
{
HostObjectExporter.ExportHostObjectMaterials(exporterIFC, ceiling, covering,
- geomElem, productWrapper, ElementId.InvalidElementId, Toolkit.IFCLayerSetDirection.Axis3, null, null);
+ geomElem, productWrapper, ElementId.InvalidElementId, IFCLayerSetDirection.Axis3, null, null);
}
else
{
diff --git a/Source/Revit.IFC.Export/Exporter/CurtainSystemExporter.cs b/Source/Revit.IFC.Export/Exporter/CurtainSystemExporter.cs
index 5b7ada15..119cd62b 100644
--- a/Source/Revit.IFC.Export/Exporter/CurtainSystemExporter.cs
+++ b/Source/Revit.IFC.Export/Exporter/CurtainSystemExporter.cs
@@ -53,9 +53,7 @@ public static void ExportCurtainObjectCommonAsContainer(ICollection a
{
if (wallElement == null)
return;
-
string overrideCADLayer = RepresentationUtil.GetPresentationLayerOverride(wallElement);
-
using (ExporterStateManager.CADLayerOverrideSetter layerSetter = new ExporterStateManager.CADLayerOverrideSetter(overrideCADLayer))
{
HashSet alreadyVisited = new HashSet(); // just in case.
@@ -68,25 +66,20 @@ public static void ExportCurtainObjectCommonAsContainer(ICollection a
Element subElem = wallElement.Document.GetElement(subElemId);
if (subElem == null)
continue;
-
if (alreadyVisited.Contains(subElem.Id))
continue;
alreadyVisited.Add(subElem.Id);
-
// Respect element visibility settings.
if (!ElementFilteringUtil.CanExportElement(exporterIFC, subElem, false) || !ElementFilteringUtil.IsElementVisible(subElem))
continue;
-
GeometryElement geomElem = subElem.get_Geometry(geomOptions);
if (geomElem == null)
continue;
-
try
{
if (subElem is FamilyInstance)
{
- string ifcEnumType;
- IFCExportInfoPair exportType = ExporterUtil.GetProductExportType(exporterIFC, subElem, out ifcEnumType);
+ IFCExportInfoPair exportType = ExporterUtil.GetProductExportType(exporterIFC, subElem, out _);
if (subElem is Mullion)
{
@@ -100,10 +93,10 @@ public static void ExportCurtainObjectCommonAsContainer(ICollection a
{
// By default, panels and mullions are set to the same category as their parent. In this case,
// ask to get the exportType from the category id, since we don't want to inherit the parent class.
- exportType.SetValueWithPair(IFCEntityType.IfcMemberType, "MULLION");
+ exportType.SetByTypeAndPredefinedType(IFCEntityType.IfcMemberType, "MULLION");
}
- FamilyInstanceExporter.ExportFamilyInstanceAsMappedItem(exporterIFC, subElem as Mullion, exportType, exportType.ValidatedPredefinedType, productWrapper,
+ FamilyInstanceExporter.ExportFamilyInstanceAsMappedItem(exporterIFC, subElem as Mullion, exportType, productWrapper,
ElementId.InvalidElementId, null, currLocalPlacement);
}
}
@@ -122,23 +115,23 @@ public static void ExportCurtainObjectCommonAsContainer(ICollection a
if (ExporterCacheManager.ExportOptionsCache.ExportAs2x2)
{
- if ((exportType.ExportInstance == IFCEntityType.UnKnown) ||
+ if ((exportType.ExportInstance == IFCEntityType.UnKnown) ||
(exportType.ExportInstance == IFCEntityType.IfcPlate) ||
(exportType.ExportInstance == IFCEntityType.IfcMember))
- exportType.SetValueWithPair(IFCEntityType.IfcBuildingElementProxy, ifcEnumType);
+ exportType.SetByType(IFCEntityType.IfcBuildingElementProxy);
}
else
{
if (exportType.ExportInstance == IFCEntityType.UnKnown)
{
- exportType.SetValueWithPair(IFCEntityType.IfcPlateType, "CURTAIN_PANEL");
+ exportType.SetByTypeAndPredefinedType(IFCEntityType.IfcPlateType, "CURTAIN_PANEL");
}
}
IFCAnyHandle currLocalPlacement = currSetter.LocalPlacement;
using (IFCExportBodyParams extraParams = new IFCExportBodyParams())
{
- FamilyInstanceExporter.ExportFamilyInstanceAsMappedItem(exporterIFC, subFamInst, exportType, ifcEnumType, productWrapper,
+ FamilyInstanceExporter.ExportFamilyInstanceAsMappedItem(exporterIFC, subFamInst, exportType, productWrapper,
ElementId.InvalidElementId, null, currLocalPlacement);
}
}
@@ -205,12 +198,11 @@ public static IFCAnyHandle ExportCurtainObjectCommonAsOneBRep(ICollection 0)
{
foreach (IFCAnyHandle triFaceSetItem in triFaceSet)
+ {
bodyItems.Add(triFaceSetItem);
+ }
useFallbackBREP = false; // no need to do Brep since it is successful
}
}
@@ -229,9 +223,8 @@ public static IFCAnyHandle ExportCurtainObjectCommonAsOneBRep(ICollection allSu
FilteredElementCollector collector = new FilteredElementCollector(document, allSubElements);
- List curtainWallSubElementTypes = new List();
- curtainWallSubElementTypes.Add(typeof(FamilyInstance));
- curtainWallSubElementTypes.Add(typeof(CurtainGridLine));
- curtainWallSubElementTypes.Add(typeof(Wall));
+ List curtainWallSubElementTypes = new List()
+ { typeof(FamilyInstance), typeof(CurtainGridLine), typeof(Wall) };
ElementMulticlassFilter multiclassFilter = new ElementMulticlassFilter(curtainWallSubElementTypes, true);
collector.WherePasses(multiclassFilter);
@@ -494,12 +495,16 @@ private static void ExportBaseWithGrids(ExporterIFC exporterIFC, Element hostEle
if (gridSet == null)
{
if (hostElement is Wall)
+ {
ExportLegacyCurtainElement(exporterIFC, hostElement as Wall, productWrapper);
+ }
return;
}
if (gridSet.Size == 0)
+ {
return;
+ }
ICollection allSubElements = GetSubElements(gridSet, hostElement.Document);
ExportBase(exporterIFC, allSubElements, hostElement, productWrapper);
@@ -605,7 +610,7 @@ public static bool IsLegacyCurtainWall(Wall wall)
if (ex.Message == "The host object is obsolete.")
return true;
else
- throw ex;
+ throw;
}
return false;
diff --git a/Source/Revit.IFC.Export/Exporter/CurveElementExporter.cs b/Source/Revit.IFC.Export/Exporter/CurveElementExporter.cs
index 5382632f..88ef0343 100644
--- a/Source/Revit.IFC.Export/Exporter/CurveElementExporter.cs
+++ b/Source/Revit.IFC.Export/Exporter/CurveElementExporter.cs
@@ -159,9 +159,8 @@ public static void ExportCurveElement(ExporterIFC exporterIFC, CurveElement curv
List curvesFromGeomElem = GeometryUtil.GetCurvesFromGeometryElement(geometryElement);
foreach (Curve curve in curvesFromGeomElem)
{
- IFCAnyHandle curveHnd = GeometryUtil.CreatePolyCurveFromCurve(exporterIFC, curve, trf);
- if (!IFCAnyHandleUtil.IsNullOrHasNoValue(curveHnd))
- curves.Add(curveHnd);
+ curves.AddIfNotNull(GeometryUtil.CreatePolyCurveFromCurve(exporterIFC,
+ curve, trf));
}
}
else
diff --git a/Source/Revit.IFC.Export/Exporter/Exporter.cs b/Source/Revit.IFC.Export/Exporter/Exporter.cs
index 27732f69..5c6e77bf 100644
--- a/Source/Revit.IFC.Export/Exporter/Exporter.cs
+++ b/Source/Revit.IFC.Export/Exporter/Exporter.cs
@@ -181,6 +181,8 @@ public void ExportIFC(Document document, ExporterIFC exporterIFC, View filterVie
try
{
+ ExporterCacheManager.ExporterIFC = exporterIFC;
+
IFCAnyHandleUtil.IFCStringTooLongWarn += (_1) => { document.Application.WriteJournalComment(_1, true); };
IFCDataUtil.IFCStringTooLongWarn += (_1) => { document.Application.WriteJournalComment(_1, true); };
@@ -378,6 +380,21 @@ private bool SpatialElementInSectionBox(BoundingBoxXYZ sectionBox, Element eleme
return GeometryUtil.BoundingBoxesOverlap(elementBBox, sectionBox);
}
+ private bool NeedBuilding()
+ {
+ if (!IFCAnyHandleUtil.IsNullOrHasNoValue(ExporterCacheManager.BuildingHandle))
+ {
+ return false;
+ }
+
+ if (IFCAnyHandleUtil.IsNullOrHasNoValue(ExporterCacheManager.SiteHandle))
+ {
+ return true;
+ }
+
+ return ExporterCacheManager.ExportOptionsCache.ExportLinkedFileAs == LinkedFileExportAs.ExportSameSite;
+ }
+
protected void ExportSpatialElements(ExporterIFC exporterIFC, Document document)
{
// Create IfcSite first here using the first visible TopographySurface if any, if not create a default one.
@@ -409,12 +426,11 @@ protected void ExportSpatialElements(ExporterIFC exporterIFC, Document document)
}
// Create IfcBuilding first here
- if (IFCAnyHandleUtil.IsNullOrHasNoValue(ExporterCacheManager.BuildingHandle) && IFCAnyHandleUtil.IsNullOrHasNoValue(ExporterCacheManager.SiteHandle))
+ if (NeedBuilding())
{
IFCAnyHandle buildingPlacement = CreateBuildingPlacement(exporterIFC.GetFile());
IFCAnyHandle buildingHnd = CreateBuildingFromProjectInfo(exporterIFC, document, buildingPlacement);
- ExporterCacheManager.BuildingHandle = buildingHnd;
- }
+ }
ExportOptionsCache exportOptionsCache = ExporterCacheManager.ExportOptionsCache;
View filterView = exportOptionsCache.FilterViewForExport;
@@ -691,11 +707,6 @@ public virtual bool ExportElement(ExporterIFC exporterIFC, Element element)
{
using (ProductWrapper productWrapper = ProductWrapper.Create(exporterIFC, true))
{
- if (element.AssemblyInstanceId != null && element.AssemblyInstanceId != ElementId.InvalidElementId)
- {
- Element assemblyElem = element.Document.GetElement(element.AssemblyInstanceId);
- ExportElementImpl(exporterIFC, assemblyElem, productWrapper);
- }
ExportElementImpl(exporterIFC, element, productWrapper);
ExporterUtil.ExportRelatedProperties(exporterIFC, element, productWrapper);
}
@@ -1031,10 +1042,14 @@ public virtual void ExportElementImpl(ExporterIFC exporterIFC, Element element,
{
// For ducts and pipes, we will add a IfcRelCoversBldgElements during the end of export.
if (element is Duct || element is Pipe)
- ExporterCacheManager.MEPCache.CoveredElementsCache.Add(element.Id);
+ {
+ ExporterCacheManager.MEPCache.CoveredElementsCache[element.Id] = element.Category?.Id ?? ElementId.InvalidElementId;
+ }
// For cable trays and conduits, we might create systems during the end of export.
if (element is CableTray || element is Conduit)
+ {
ExporterCacheManager.MEPCache.CableElementsCache.Add(element.Id);
+ }
}
}
@@ -1320,9 +1335,7 @@ private void GetElementHandles(ICollection ids, ISet ha
{
foreach (ElementId id in ids)
{
- IFCAnyHandle handle = ExporterCacheManager.ElementToHandleCache.Find(id);
- if (!IFCAnyHandleUtil.IsNullOrHasNoValue(handle))
- handles.Add(handle);
+ handles.AddIfNotNull(ExporterCacheManager.ElementToHandleCache.Find(id));
}
}
}
@@ -1398,9 +1411,8 @@ private void CreatePresentationLayerAssignments(ExporterIFC exporterIFC, IFCFile
ISet validHandles = new HashSet();
foreach (IFCAnyHandle handle in presentationLayerSet.Value)
{
- if (!IFCAnyHandleUtil.IsNullOrHasNoValue(handle))
+ if (validHandles.AddIfNotNull(handle))
{
- validHandles.Add(handle);
assignedRepresentations.Add(handle);
}
}
@@ -1493,8 +1505,10 @@ private void EndDocumentExportCommon(ExporterIFC exporterIFC, Document document,
using (IFCTransaction transaction = new IFCTransaction(file))
{
// Relate Ducts and Pipes to their coverings (insulations and linings)
- foreach (ElementId ductOrPipeId in ExporterCacheManager.MEPCache.CoveredElementsCache)
+ foreach (KeyValuePair ductOrPipe in ExporterCacheManager.MEPCache.CoveredElementsCache)
{
+ ElementId ductOrPipeId = ductOrPipe.Key;
+
IFCAnyHandle ductOrPipeHandle = ExporterCacheManager.MEPCache.Find(ductOrPipeId);
if (IFCAnyHandleUtil.IsNullOrHasNoValue(ductOrPipeHandle))
continue;
@@ -1503,8 +1517,11 @@ private void EndDocumentExportCommon(ExporterIFC exporterIFC, Document document,
try
{
- ICollection liningIds = InsulationLiningBase.GetLiningIds(document, ductOrPipeId);
- GetElementHandles(liningIds, coveringHandles);
+ if (FamilyInstanceExporter.CategoryCanHaveLining(ductOrPipe.Value))
+ {
+ ICollection liningIds = InsulationLiningBase.GetLiningIds(document, ductOrPipeId);
+ GetElementHandles(liningIds, coveringHandles);
+ }
}
catch
{
@@ -1705,7 +1722,7 @@ private void EndDocumentExportCommon(ExporterIFC exporterIFC, Document document,
ElementId elementId = ExporterCacheManager.HandleToElementCache.Find(elemHnd);
Element elem = document.GetElement(elementId);
- // if there is override, use the override otherwise use default from site
+ // if there is override, use the override otherwise use default
IFCAnyHandle overrideContainer = null;
ParameterUtil.OverrideContainmentParameter(exporterIFC, elem, out overrideContainer);
@@ -1784,10 +1801,10 @@ private void EndDocumentExportCommon(ExporterIFC exporterIFC, Document document,
containerInvTrf = defContainerInvTrf;
}
- if (containerIsSite)
- relatedElementSetForSite.Add(indivSpace);
- else if (containerIsBuilding)
+ if (containerIsBuilding)
relatedElementSetForBuilding.Add(indivSpace);
+ else if (containerIsSite)
+ relatedElementSetForSite.Add(indivSpace);
UpdateLocalPlacementForElement(indivSpace, file, containerObjectPlacement, containerInvTrf);
}
@@ -2101,10 +2118,11 @@ private void EndDocumentExportCommon(ExporterIFC exporterIFC, Document document,
relGuid, classificationReference.Key, null, zoneHnds);
}
- if (!IFCAnyHandleUtil.IsNullOrHasNoValue(zoneInfo.ZoneCommonProperySetHandle))
+ IFCAnyHandle zoneCommonProperySetHandle = zoneInfo.CreateZoneCommonPSetData(file);
+ if (!IFCAnyHandleUtil.IsNullOrHasNoValue(zoneCommonProperySetHandle))
{
ExporterUtil.CreateRelDefinesByProperties(file,
- ownerHistory, null, null, zoneHnds, zoneInfo.ZoneCommonProperySetHandle);
+ ownerHistory, null, null, zoneHnds, zoneCommonProperySetHandle);
}
string groupName = zoneInfo.GroupName;
@@ -2120,20 +2138,6 @@ private void EndDocumentExportCommon(ExporterIFC exporterIFC, Document document,
}
}
- // Create RelAssociatesClassifications.
- foreach (var relAssociatesInfo in ExporterCacheManager.ClassificationCache.ClassificationRelations)
- {
- if (IFCAnyHandleUtil.IsNullOrHasNoValue(relAssociatesInfo.Key))
- continue;
-
- IFCInstanceExporter.CreateRelAssociatesClassification(file,
- relAssociatesInfo.Value.GlobalId, ownerHistory,
- relAssociatesInfo.Value.Name,
- relAssociatesInfo.Value.Description,
- relAssociatesInfo.Value.RelatedObjects,
- relAssociatesInfo.Key);
- }
-
// now create any zone groups.
string relAssignsToZoneGroupName = "Zone Group Assignment";
foreach (KeyValuePair> zoneGroup in zoneGroups)
@@ -2215,6 +2219,20 @@ private void EndDocumentExportCommon(ExporterIFC exporterIFC, Document document,
}
}
+ // Create RelAssociatesClassifications.
+ foreach (var relAssociatesInfo in ExporterCacheManager.ClassificationCache.ClassificationRelations)
+ {
+ if (IFCAnyHandleUtil.IsNullOrHasNoValue(relAssociatesInfo.Key))
+ continue;
+
+ IFCInstanceExporter.CreateRelAssociatesClassification(file,
+ relAssociatesInfo.Value.GlobalId, ownerHistory,
+ relAssociatesInfo.Value.Name,
+ relAssociatesInfo.Value.Description,
+ relAssociatesInfo.Value.RelatedObjects,
+ relAssociatesInfo.Key);
+ }
+
// Delete handles that are marked for removal
foreach (IFCAnyHandle handleToDel in ExporterCacheManager.HandleToDeleteCache)
{
@@ -2244,6 +2262,10 @@ private void EndDocumentExportCommon(ExporterIFC exporterIFC, Document document,
if (ExporterCacheManager.ExportOptionsCache.PropertySetOptions.ExportMaterialPsets)
MaterialPropertiesUtil.ExportMaterialProperties(file, exporterIFC);
+ // Create unit assignement
+ IFCAnyHandle units = IFCInstanceExporter.CreateUnitAssignment(file, UnitMappingUtil.GetUnitsToAssign());
+ ExporterCacheManager.ProjectHandle.SetAttribute("UnitsInContext", units);
+
// Allow native code to remove some unused handles and clear internal caches.
ExporterIFCUtils.EndExportInternal(exporterIFC);
transaction.Commit();
@@ -2252,6 +2274,8 @@ private void EndDocumentExportCommon(ExporterIFC exporterIFC, Document document,
private class IFCFileDocumentInfo
{
+ public string ContentGUIDString { get; private set; } = null;
+
public string VersionGUIDString { get; private set; } = null;
public int NumberOfSaves { get; private set; } = 0;
@@ -2279,11 +2303,12 @@ public IFCFileDocumentInfo(Document document)
ExportOptionsCache exportOptionsCache = ExporterCacheManager.ExportOptionsCache;
+ ContentGUIDString = document?.CreationGUID.ToString() ?? string.Empty;
VersionGUIDString = documentVersion?.VersionGUID.ToString() ?? string.Empty;
NumberOfSaves = documentVersion?.NumberOfSaves ?? 0;
ProjectNumber = projectInfo?.Number ?? string.Empty;
- ProjectName = projectInfo?.Name ?? exportOptionsCache.FileName;
+ ProjectName = projectInfo?.Name ?? exportOptionsCache.FileNameOnly;
ProjectStatus = projectInfo?.Status ?? string.Empty;
VersionName = application?.VersionName;
@@ -2305,8 +2330,7 @@ private void OrientLink(IFCFile file, bool canUseSitePlacement,
// When exporting Link, the relative position of the Link instance in the model needs to be transformed with
// the offset from the main model site transform
SiteTransformBasis transformBasis = ExporterCacheManager.ExportOptionsCache.SiteTransformation;
- bool useSitePlacement = canUseSitePlacement && (transformBasis != SiteTransformBasis.Internal &&
- transformBasis != SiteTransformBasis.InternalInTN);
+ bool useSitePlacement = canUseSitePlacement && (transformBasis != SiteTransformBasis.Internal);
bool useRotation = transformBasis == SiteTransformBasis.InternalInTN ||
transformBasis == SiteTransformBasis.ProjectInTN ||
transformBasis == SiteTransformBasis.Shared ||
@@ -2315,7 +2339,7 @@ private void OrientLink(IFCFile file, bool canUseSitePlacement,
new Transform(CoordReferenceInfo.MainModelCoordReferenceOffset ?? Transform.Identity);
XYZ siteOffset = useSitePlacement ? sitePl.Origin : XYZ.Zero;
- if (useRotation)
+ if (useRotation && useSitePlacement)
{
// For those that oriented in the TN, a rotation is needed to compute a correct offset in TN orientation
Transform rotationTrfAtInternal = Transform.CreateRotationAtPoint(XYZ.BasisZ, CoordReferenceInfo.MainModelTNAngle, XYZ.Zero);
@@ -2327,7 +2351,7 @@ private void OrientLink(IFCFile file, bool canUseSitePlacement,
}
sitePl.Origin = XYZ.Zero;
linkTrf.Origin = XYZ.Zero;
- Transform linkTotTrf = sitePl.Multiply(linkTrf);
+ Transform linkTotTrf = useSitePlacement ? sitePl.Multiply(linkTrf) : linkTrf;
linkTotTrf.Origin = siteOffset;
IFCAnyHandle relativePlacement = ExporterUtil.CreateAxis2Placement3D(file,
@@ -2396,8 +2420,8 @@ private void WriteIFCFile(IFCFile file, IFCFileDocumentInfo ifcFileDocumentInfo)
}
- string versionLine = string.Format("RevitIdentifiers [VersionGUID: {0}, NumberOfSaves: {1}]",
- ifcFileDocumentInfo.VersionGUIDString, ifcFileDocumentInfo.NumberOfSaves);
+ string versionLine = string.Format("RevitIdentifiers [ContentGUID: {0}, VersionGUID: {1}, NumberOfSaves: {2}]",
+ ifcFileDocumentInfo.ContentGUIDString, ifcFileDocumentInfo.VersionGUIDString, ifcFileDocumentInfo.NumberOfSaves);
descriptions.Add(versionLine);
@@ -2449,14 +2473,14 @@ private void WriteIFCFile(IFCFile file, IFCFileDocumentInfo ifcFileDocumentInfo)
if (fHItem.Authorization == null)
fHItem.Authorization = string.Empty;
- IFCInstanceExporter.CreateFileName(file, projectNumber, author, organization,
+ IFCInstanceExporter.CreateFileName(file, exportOptionsCache.FileNameOnly, author, organization,
ifcFileDocumentInfo.VersionName, versionInfos, fHItem.Authorization);
transaction.Commit();
IFCFileWriteOptions writeOptions = new IFCFileWriteOptions()
{
- FileName = exportOptionsCache.FileName,
+ FileName = exportOptionsCache.FullFileName,
FileFormat = exportOptionsCache.IFCFileFormat
};
@@ -2846,13 +2870,16 @@ private void CreateProject(ExporterIFC exporterIFC, Document doc, IFCAnyHandle a
List prefixTitles;
List suffixTitles;
- string author = String.Empty;
+ string author = string.Empty;
+ bool hasPotentialLastUser = false;
+
ProjectInfo projectInfo = doc.ProjectInformation;
if (projectInfo != null)
{
try
{
author = projectInfo.Author;
+ hasPotentialLastUser = !string.IsNullOrWhiteSpace(author);
}
catch (Autodesk.Revit.Exceptions.InvalidOperationException)
{
@@ -2888,12 +2915,11 @@ private void CreateProject(ExporterIFC exporterIFC, Document doc, IFCAnyHandle a
}
else
{
- IFCAnyHandle telecomAddress = GetTelecomAddressFromExtStorage(file);
IList telecomAddresses = null;
+ IFCAnyHandle telecomAddress = GetTelecomAddressFromExtStorage(file);
if (telecomAddress != null)
{
- telecomAddresses = new List();
- telecomAddresses.Add(telecomAddress);
+ telecomAddresses = new List() { telecomAddress };
}
person = IFCInstanceExporter.CreatePerson(file, null, familyName, givenName, middleNames,
@@ -2917,8 +2943,11 @@ private void CreateProject(ExporterIFC exporterIFC, Document doc, IFCAnyHandle a
}
IFCAnyHandle owningUser = IFCInstanceExporter.CreatePersonAndOrganization(file, person, organization, null);
+ IFCAnyHandle lastModifyingUser = hasPotentialLastUser && ExporterCacheManager.ExportOptionsCache.OwnerHistoryLastModified
+ ? owningUser : null;
+
ownerHistory = IFCInstanceExporter.CreateOwnerHistory(file, owningUser, application, null,
- Toolkit.IFCChangeAction.NoChange, null, null, null, creationDate);
+ IFCChangeAction.NoChange, null, lastModifyingUser, null, creationDate);
exporterIFC.SetOwnerHistoryHandle(ownerHistory); // For use by native code only.
ExporterCacheManager.OwnerHistoryHandle = ownerHistory;
@@ -2926,7 +2955,8 @@ private void CreateProject(ExporterIFC exporterIFC, Document doc, IFCAnyHandle a
// Getting contact information from Revit extensible storage that COBie extension tool creates
GetCOBieContactInfo(file, doc);
- IFCAnyHandle units = CreateDefaultUnits(exporterIFC, doc);
+ UnitMappingUtil.CreateCobieUnits();
+
IList directionRatios = null;
HashSet repContexts = CreateContextInformation(exporterIFC, doc, out directionRatios);
@@ -2958,7 +2988,7 @@ private void CreateProject(ExporterIFC exporterIFC, Document doc, IFCAnyHandle a
string projectGUID = GUIDUtil.CreateProjectLevelGUID(doc, GUIDUtil.ProjectLevelGUIDType.Project);
IFCAnyHandle projectHandle = IFCInstanceExporter.CreateProject(exporterIFC, projectInfo, projectGUID, ownerHistory,
- projectName, projectDescription, projectLongName, projectPhase, repContexts, units);
+ projectName, projectDescription, projectLongName, projectPhase, repContexts, null);
ExporterCacheManager.ProjectHandle = projectHandle;
@@ -3191,1140 +3221,7 @@ static public IFCAnyHandle CreateIFCAddress(IFCFile file, Document document, Pro
return postalAddress;
}
-
- private IFCAnyHandle CreateSIUnit(IFCFile file, ForgeTypeId specTypeId, IFCUnit ifcUnitType, IFCSIUnitName unitName, IFCSIPrefix? prefix, ForgeTypeId unitTypeId)
- {
- IFCAnyHandle siUnit = IFCInstanceExporter.CreateSIUnit(file, ifcUnitType, prefix, unitName);
- if (specTypeId != null && unitTypeId != null)
- {
- double scaleFactor = UnitUtils.ConvertFromInternalUnits(1.0, unitTypeId);
- ExporterCacheManager.UnitsCache.AddUnit(specTypeId, siUnit, scaleFactor, 0.0);
- }
-
- return siUnit;
- }
-
- ///
- /// Creates the IfcUnitAssignment. This is a long list of units that we correctly translate from our internal units to known units.
- ///
- /// The IFC exporter object.
- /// The document provides ProjectUnit and DisplayUnitSystem.
- /// The IFC handle.
- private IFCAnyHandle CreateDefaultUnits(ExporterIFC exporterIFC, Document doc)
- {
- HashSet unitSet = new HashSet();
- IFCFile file = exporterIFC.GetFile();
- bool exportToCOBIE = ExporterCacheManager.ExportOptionsCache.ExportAsCOBIE;
-
- Dictionary, IFCAnyHandle> addedDerivedUnitElements = new Dictionary, IFCAnyHandle>();
- Action, IFCAnyHandle, int> createDerivedUnitElement = (elements, unit, exponent) =>
- {
- var pair = new Tuple(unit, exponent);
- if (!addedDerivedUnitElements.ContainsKey(pair))
- {
- var element = IFCInstanceExporter.CreateDerivedUnitElement(file, unit, exponent);
- elements.Add(addedDerivedUnitElements[pair] = element);
- }
-
- elements.Add(addedDerivedUnitElements[pair]);
- };
-
- IFCAnyHandle lenSIBaseUnit = null;
- {
- bool lenConversionBased = false;
- bool lenUseDefault = false;
-
- IFCUnit lenUnitType = IFCUnit.LengthUnit;
- IFCSIPrefix? lenPrefix = null;
- IFCSIUnitName lenUnitName = IFCSIUnitName.Metre;
- string lenConvName = null;
-
- FormatOptions lenFormatOptions = doc.GetUnits().GetFormatOptions(SpecTypeId.Length);
- ForgeTypeId lengthUnit = lenFormatOptions.GetUnitTypeId();
- if (lengthUnit.Equals(UnitTypeId.Meters) ||
- lengthUnit.Equals(UnitTypeId.MetersCentimeters))
- {
- // This space intentionally left blank
- }
- else if (lengthUnit.Equals(UnitTypeId.Centimeters))
- {
- lenPrefix = IFCSIPrefix.Centi;
- }
- else if (lengthUnit.Equals(UnitTypeId.Millimeters))
- {
- lenPrefix = IFCSIPrefix.Milli;
- }
- else if (lengthUnit.Equals(UnitTypeId.Feet) ||
- lengthUnit.Equals(UnitTypeId.FeetFractionalInches))
- {
- if (exportToCOBIE)
- lenConvName = "foot";
- else
- lenConvName = "FOOT";
- lenConversionBased = true;
- }
- else if (lengthUnit.Equals(UnitTypeId.FractionalInches) ||
- lengthUnit.Equals(UnitTypeId.Inches))
- {
- if (exportToCOBIE)
- lenConvName = "inch";
- else
- lenConvName = "INCH";
- lenConversionBased = true;
- }
- else
- {
- //Couldn't find display unit type conversion -- assuming foot
- if (exportToCOBIE)
- lenConvName = "foot";
- else
- lenConvName = "FOOT";
- lenConversionBased = true;
- lenUseDefault = true;
- }
-
- double lengthScaleFactor = UnitUtils.ConvertFromInternalUnits(1.0, lenUseDefault ? UnitTypeId.Feet : lenFormatOptions.GetUnitTypeId());
- IFCAnyHandle lenSIUnit = IFCInstanceExporter.CreateSIUnit(file, lenUnitType, lenPrefix, lenUnitName);
- if (lenPrefix == null)
- lenSIBaseUnit = lenSIUnit;
- else
- lenSIBaseUnit = IFCInstanceExporter.CreateSIUnit(file, lenUnitType, null, lenUnitName);
-
- if (lenConversionBased)
- {
- double lengthSIScaleFactor = UnitUtils.ConvertFromInternalUnits(1.0, UnitTypeId.Meters) / lengthScaleFactor;
- IFCAnyHandle lenDims = IFCInstanceExporter.CreateDimensionalExponents(file, 1, 0, 0, 0, 0, 0, 0); // length
- IFCAnyHandle lenConvFactor = IFCInstanceExporter.CreateMeasureWithUnit(file, Toolkit.IFCDataUtil.CreateAsLengthMeasure(lengthSIScaleFactor),
- lenSIUnit);
- lenSIUnit = IFCInstanceExporter.CreateConversionBasedUnit(file, lenDims, lenUnitType, lenConvName, lenConvFactor);
- }
-
- unitSet.Add(lenSIUnit); // created above, so unique.
- ExporterCacheManager.UnitsCache.AddUnit(SpecTypeId.Length, lenSIUnit, lengthScaleFactor, 0.0);
- }
-
- {
- bool areaConversionBased = false;
- bool areaUseDefault = false;
-
- IFCUnit areaUnitType = IFCUnit.AreaUnit;
- IFCSIPrefix? areaPrefix = null;
- IFCSIUnitName areaUnitName = IFCSIUnitName.Square_Metre;
- string areaConvName = null;
-
- FormatOptions areaFormatOptions = doc.GetUnits().GetFormatOptions(SpecTypeId.Area);
- ForgeTypeId areaUnit = areaFormatOptions.GetUnitTypeId();
- if (areaUnit.Equals(UnitTypeId.SquareMeters))
- {
- // This space intentionally left blank.
- }
- else if (areaUnit.Equals(UnitTypeId.SquareCentimeters))
- {
- areaPrefix = IFCSIPrefix.Centi;
- }
- else if (areaUnit.Equals(UnitTypeId.SquareMillimeters))
- {
- areaPrefix = IFCSIPrefix.Milli;
- }
- else if (areaUnit.Equals(UnitTypeId.SquareFeet))
- {
- if (exportToCOBIE)
- areaConvName = "foot";
- else
- areaConvName = "SQUARE FOOT";
- areaConversionBased = true;
- }
- else if (areaUnit.Equals(UnitTypeId.SquareInches))
- {
- if (exportToCOBIE)
- areaConvName = "inch";
- else
- areaConvName = "SQUARE INCH";
- areaConversionBased = true;
- }
- else
- {
- //Couldn't find display unit type conversion -- assuming foot
- if (exportToCOBIE)
- areaConvName = "foot";
- else
- areaConvName = "SQUARE FOOT";
- areaConversionBased = true;
- areaUseDefault = true;
- }
-
- double areaScaleFactor = UnitUtils.ConvertFromInternalUnits(1.0, areaUseDefault ? UnitTypeId.SquareFeet : areaFormatOptions.GetUnitTypeId());
- IFCAnyHandle areaSiUnit = IFCInstanceExporter.CreateSIUnit(file, areaUnitType, areaPrefix, areaUnitName);
- if (areaConversionBased)
- {
- double areaSIScaleFactor = areaScaleFactor * UnitUtils.ConvertFromInternalUnits(1.0, UnitTypeId.SquareMeters);
- IFCAnyHandle areaDims = IFCInstanceExporter.CreateDimensionalExponents(file, 2, 0, 0, 0, 0, 0, 0); // area
- IFCAnyHandle areaConvFactor = IFCInstanceExporter.CreateMeasureWithUnit(file, Toolkit.IFCDataUtil.CreateAsAreaMeasure(areaSIScaleFactor), areaSiUnit);
- areaSiUnit = IFCInstanceExporter.CreateConversionBasedUnit(file, areaDims, areaUnitType, areaConvName, areaConvFactor);
- }
-
- unitSet.Add(areaSiUnit); // created above, so unique.
- ExporterCacheManager.UnitsCache.AddUnit(SpecTypeId.Area, areaSiUnit, areaScaleFactor, 0.0);
- }
-
- {
- bool volumeConversionBased = false;
- bool volumeUseDefault = false;
-
- IFCUnit volumeUnitType = IFCUnit.VolumeUnit;
- IFCSIPrefix? volumePrefix = null;
- IFCSIUnitName volumeUnitName = IFCSIUnitName.Cubic_Metre;
- string volumeConvName = null;
-
- FormatOptions volumeFormatOptions = doc.GetUnits().GetFormatOptions(SpecTypeId.Volume);
- ForgeTypeId volumeUnit = volumeFormatOptions.GetUnitTypeId();
- if (volumeUnit.Equals(UnitTypeId.CubicMeters))
- {
- // This space intentionally left blank.
- }
- else if (volumeUnit.Equals(UnitTypeId.Liters))
- {
- volumePrefix = IFCSIPrefix.Deci;
- }
- else if (volumeUnit.Equals(UnitTypeId.CubicCentimeters))
- {
- volumePrefix = IFCSIPrefix.Centi;
- }
- else if (volumeUnit.Equals(UnitTypeId.CubicMillimeters))
- {
- volumePrefix = IFCSIPrefix.Milli;
- }
- else if (volumeUnit.Equals(UnitTypeId.CubicFeet))
- {
- if (exportToCOBIE)
- volumeConvName = "foot";
- else
- volumeConvName = "CUBIC FOOT";
- volumeConversionBased = true;
- }
- else if (volumeUnit.Equals(UnitTypeId.CubicInches))
- {
- if (exportToCOBIE)
- volumeConvName = "inch";
- else
- volumeConvName = "CUBIC INCH";
- volumeConversionBased = true;
- }
- else
- {
- //Couldn't find display unit type conversion -- assuming foot
- if (exportToCOBIE)
- volumeConvName = "foot";
- else
- volumeConvName = "CUBIC FOOT";
- volumeConversionBased = true;
- volumeUseDefault = true;
- }
-
- double volumeScaleFactor =
- UnitUtils.ConvertFromInternalUnits(1.0, volumeUseDefault ? UnitTypeId.CubicFeet : volumeFormatOptions.GetUnitTypeId());
- IFCAnyHandle volumeSiUnit = IFCInstanceExporter.CreateSIUnit(file, volumeUnitType, volumePrefix, volumeUnitName);
- if (volumeConversionBased)
- {
- double volumeSIScaleFactor = volumeScaleFactor * UnitUtils.ConvertFromInternalUnits(1.0, UnitTypeId.CubicMeters);
- IFCAnyHandle volumeDims = IFCInstanceExporter.CreateDimensionalExponents(file, 3, 0, 0, 0, 0, 0, 0); // volume
- IFCAnyHandle volumeConvFactor = IFCInstanceExporter.CreateMeasureWithUnit(file, Toolkit.IFCDataUtil.CreateAsVolumeMeasure(volumeSIScaleFactor), volumeSiUnit);
- volumeSiUnit = IFCInstanceExporter.CreateConversionBasedUnit(file, volumeDims, volumeUnitType, volumeConvName, volumeConvFactor);
- }
-
- unitSet.Add(volumeSiUnit); // created above, so unique.
- ExporterCacheManager.UnitsCache.AddUnit(SpecTypeId.Volume, volumeSiUnit, volumeScaleFactor, 0.0);
- }
-
- IFCAnyHandle angleSIUnit = null;
- {
- IFCUnit unitType = IFCUnit.PlaneAngleUnit;
- IFCSIUnitName unitName = IFCSIUnitName.Radian;
-
- angleSIUnit = IFCInstanceExporter.CreateSIUnit(file, unitType, null, unitName);
-
- string convName = null;
-
- FormatOptions angleFormatOptions = doc.GetUnits().GetFormatOptions(SpecTypeId.Angle);
- bool angleUseDefault = false;
- ForgeTypeId angleUnit = angleFormatOptions.GetUnitTypeId();
- if (angleUnit.Equals(UnitTypeId.Degrees) ||
- angleUnit.Equals(UnitTypeId.DegreesMinutes))
- {
- convName = "DEGREE";
- }
- else if (angleUnit.Equals(UnitTypeId.Gradians))
- {
- convName = "GRAD";
- }
- else if (angleUnit.Equals(UnitTypeId.Radians))
- {
- // This space intentionally left blank.
- }
- else
- {
- angleUseDefault = true;
- convName = "DEGREE";
- }
-
- IFCAnyHandle dims = IFCInstanceExporter.CreateDimensionalExponents(file, 0, 0, 0, 0, 0, 0, 0);
-
- IFCAnyHandle planeAngleUnit = angleSIUnit;
- double angleScaleFactor = UnitUtils.Convert(1.0, angleUseDefault ? UnitTypeId.Degrees : angleFormatOptions.GetUnitTypeId(), UnitTypeId.Radians);
- if (convName != null)
- {
- IFCAnyHandle convFactor = IFCInstanceExporter.CreateMeasureWithUnit(file, Toolkit.IFCDataUtil.CreateAsPlaneAngleMeasure(angleScaleFactor), planeAngleUnit);
- planeAngleUnit = IFCInstanceExporter.CreateConversionBasedUnit(file, dims, unitType, convName, convFactor);
- }
- unitSet.Add(planeAngleUnit); // created above, so unique.
- ExporterCacheManager.UnitsCache.AddUnit(SpecTypeId.Angle, planeAngleUnit, 1.0 / angleScaleFactor, 0.0);
- }
-
- // Mass
- IFCAnyHandle massSIUnit = null;
- {
- massSIUnit = CreateSIUnit(file, SpecTypeId.Mass, IFCUnit.MassUnit, IFCSIUnitName.Gram, IFCSIPrefix.Kilo, null);
- // If we are exporting to GSA standard, we will override kg with pound below.
- if (!exportToCOBIE)
- unitSet.Add(massSIUnit); // created above, so unique.
- }
-
- // Mass density - support metric kg/(m^3) only.
- {
- ISet elements = new HashSet();
- createDerivedUnitElement(elements, massSIUnit, 1);
- createDerivedUnitElement(elements, lenSIBaseUnit, -3);
-
- IFCAnyHandle massDensityUnit = IFCInstanceExporter.CreateDerivedUnit(file, elements,
- IFCDerivedUnitEnum.MassDensityUnit, null);
- unitSet.Add(massDensityUnit);
-
- double massDensityFactor = UnitUtils.ConvertFromInternalUnits(1.0, UnitTypeId.KilogramsPerCubicMeter);
- ExporterCacheManager.UnitsCache.AddUnit(SpecTypeId.MassDensity, massDensityUnit, massDensityFactor, 0.0);
- }
-
- // Ion concentration - support metric kg/(m^3) only.
- {
- ISet elements = new HashSet();
- createDerivedUnitElement(elements, massSIUnit, 1);
- createDerivedUnitElement(elements, lenSIBaseUnit, -3);
-
- IFCAnyHandle massDensityUnit = IFCInstanceExporter.CreateDerivedUnit(file, elements,
- IFCDerivedUnitEnum.IonConcentrationUnit, null);
- unitSet.Add(massDensityUnit);
-
- double massDensityFactor = UnitUtils.ConvertFromInternalUnits(1.0, UnitTypeId.KilogramsPerCubicMeter);
- ExporterCacheManager.UnitsCache.AddUnit(SpecTypeId.PipingDensity, massDensityUnit, massDensityFactor, 0.0);
- }
-
- // Moment of inertia - support metric m^4.
- {
- ISet elements = new HashSet();
- createDerivedUnitElement(elements, lenSIBaseUnit, 4);
-
- IFCAnyHandle momentOfInertiaUnit = IFCInstanceExporter.CreateDerivedUnit(file, elements,
- IFCDerivedUnitEnum.MomentOfInertiaUnit, null);
- unitSet.Add(momentOfInertiaUnit);
-
- double momentOfInertiaFactor = UnitUtils.ConvertFromInternalUnits(1.0, UnitTypeId.MetersToTheFourthPower);
- ExporterCacheManager.UnitsCache.AddUnit(SpecTypeId.MomentOfInertia, momentOfInertiaUnit, momentOfInertiaFactor, 0.0);
- }
-
- // Time -- support seconds only.
- IFCAnyHandle timeSIUnit = null;
- {
- timeSIUnit = CreateSIUnit(file, null, IFCUnit.TimeUnit, IFCSIUnitName.Second, null, null);
- unitSet.Add(timeSIUnit); // created above, so unique.
- }
-
- // Frequency = support Hertz only.
- {
- IFCAnyHandle frequencySIUnit = CreateSIUnit(file, null, IFCUnit.FrequencyUnit, IFCSIUnitName.Hertz, null, null);
- unitSet.Add(frequencySIUnit); // created above, so unique.
- }
-
- // Temperature
- IFCAnyHandle tempBaseSIUnit = null;
- {
- // Base SI unit for temperature.
- tempBaseSIUnit = CreateSIUnit(file, null, IFCUnit.ThermoDynamicTemperatureUnit, IFCSIUnitName.Kelvin, null, null);
-
- // We are going to have two entries: one for thermodynamic temperature (default), and one for color temperature.
- FormatOptions tempFormatOptions = doc.GetUnits().GetFormatOptions(SpecTypeId.HvacTemperature);
- IFCSIUnitName thermalTempUnit;
- double offset = 0.0;
- ForgeTypeId tempUnit = tempFormatOptions.GetUnitTypeId();
- if (tempUnit.Equals(UnitTypeId.Celsius) ||
- tempUnit.Equals(UnitTypeId.Fahrenheit))
- {
- thermalTempUnit = IFCSIUnitName.Degree_Celsius;
- offset = -273.15;
- }
- else
- {
- thermalTempUnit = IFCSIUnitName.Kelvin;
- }
-
- IFCAnyHandle temperatureSIUnit = null;
- if (thermalTempUnit != IFCSIUnitName.Kelvin)
- temperatureSIUnit = IFCInstanceExporter.CreateSIUnit(file, IFCUnit.ThermoDynamicTemperatureUnit, null, thermalTempUnit);
- else
- temperatureSIUnit = tempBaseSIUnit;
- ExporterCacheManager.UnitsCache.AddUnit(SpecTypeId.HvacTemperature, temperatureSIUnit, 1.0, offset);
-
- unitSet.Add(temperatureSIUnit); // created above, so unique.
-
- // Color temperature.
- // We don't add the color temperature to the unit set; it will be explicitly used.
- IFCAnyHandle colorTempSIUnit = tempBaseSIUnit;
- ExporterCacheManager.UnitsCache["COLORTEMPERATURE"] = colorTempSIUnit;
- }
-
- // Thermal transmittance - support metric W/(m^2 * K) = kg/(K * s^3) only.
- {
- ISet elements = new HashSet();
- createDerivedUnitElement(elements, massSIUnit, 1);
- createDerivedUnitElement(elements, tempBaseSIUnit, -1);
- createDerivedUnitElement(elements, timeSIUnit, -3);
-
- IFCAnyHandle thermalTransmittanceUnit = IFCInstanceExporter.CreateDerivedUnit(file, elements,
- IFCDerivedUnitEnum.ThermalTransmittanceUnit, null);
- unitSet.Add(thermalTransmittanceUnit);
-
- double thermalTransmittanceFactor = UnitUtils.ConvertFromInternalUnits(1.0, UnitTypeId.WattsPerSquareMeterKelvin);
- ExporterCacheManager.UnitsCache.AddUnit(SpecTypeId.HeatTransferCoefficient, thermalTransmittanceUnit, thermalTransmittanceFactor, 0.0);
- }
-
- // Thermal conductivity - support metric W/(m * K) = (kg * m)/(K * s^3) only.
- {
- ISet elements = new HashSet();
- createDerivedUnitElement(elements, massSIUnit, 1);
- createDerivedUnitElement(elements, lenSIBaseUnit, 1);
- createDerivedUnitElement(elements, tempBaseSIUnit, -1);
- createDerivedUnitElement(elements, timeSIUnit, -3);
-
- IFCAnyHandle thermaConductivityUnit = IFCInstanceExporter.CreateDerivedUnit(file, elements,
- IFCDerivedUnitEnum.ThermalConductanceUnit, null);
- unitSet.Add(thermaConductivityUnit);
-
- double thermalConductivityFactor = UnitUtils.ConvertFromInternalUnits(1.0, UnitTypeId.WattsPerMeterKelvin);
- ExporterCacheManager.UnitsCache.AddUnit(SpecTypeId.ThermalConductivity, thermaConductivityUnit, thermalConductivityFactor, 0.0);
- }
-
- // Volumetric Flow Rate - support metric L/s or m^3/s only.
- {
- IFCAnyHandle volumetricFlowRateLenUnit = null;
-
- FormatOptions flowFormatOptions = doc.GetUnits().GetFormatOptions(SpecTypeId.AirFlow);
- ForgeTypeId forgeTypeId = flowFormatOptions.GetUnitTypeId();
- if (forgeTypeId.Equals(UnitTypeId.LitersPerSecond))
- {
- volumetricFlowRateLenUnit = IFCInstanceExporter.CreateSIUnit(file, IFCUnit.LengthUnit, IFCSIPrefix.Deci, IFCSIUnitName.Metre);
- }
- else
- {
- volumetricFlowRateLenUnit = lenSIBaseUnit; // use m^3/s by default.
- forgeTypeId = UnitTypeId.CubicMetersPerSecond;
- }
- double volumetricFlowRateFactor = UnitUtils.ConvertFromInternalUnits(1.0, forgeTypeId);
-
- ISet elements = new HashSet();
- createDerivedUnitElement(elements, volumetricFlowRateLenUnit, 3);
- createDerivedUnitElement(elements, timeSIUnit, -1);
-
- IFCAnyHandle volumetricFlowRateUnit = IFCInstanceExporter.CreateDerivedUnit(file, elements,
- IFCDerivedUnitEnum.VolumetricFlowRateUnit, null);
- unitSet.Add(volumetricFlowRateUnit);
-
- ExporterCacheManager.UnitsCache.AddUnit(SpecTypeId.AirFlow, volumetricFlowRateUnit, volumetricFlowRateFactor, 0.0);
- }
-
- // Mass flow rate - support kg/s only.
- {
- ISet elements = new HashSet();
- createDerivedUnitElement(elements, massSIUnit, 1);
- createDerivedUnitElement(elements, timeSIUnit, -1);
-
- IFCAnyHandle massFlowRateUnit = IFCInstanceExporter.CreateDerivedUnit(file, elements,
- IFCDerivedUnitEnum.MassFlowRateUnit, null);
- unitSet.Add(massFlowRateUnit);
-
- double massFlowRateFactor = UnitUtils.ConvertFromInternalUnits(1.0, UnitTypeId.KilogramsPerSecond);
- ExporterCacheManager.UnitsCache.AddUnit(SpecTypeId.PipingMassPerTime, massFlowRateUnit, massFlowRateFactor, 0.0);
- }
-
- // Rotational frequency - support cycles/s only.
- {
- ISet elements = new HashSet();
- createDerivedUnitElement(elements, timeSIUnit, -1);
-
- IFCAnyHandle rotationalFrequencyUnit = IFCInstanceExporter.CreateDerivedUnit(file, elements,
- IFCDerivedUnitEnum.RotationalFrequencyUnit, null);
- unitSet.Add(rotationalFrequencyUnit);
-
- double rotationalFrequencyFactor = UnitUtils.ConvertFromInternalUnits(1.0, UnitTypeId.RevolutionsPerSecond);
- ExporterCacheManager.UnitsCache.AddUnit(SpecTypeId.AngularSpeed, rotationalFrequencyUnit, rotationalFrequencyFactor, 0.0);
- }
-
- // Electrical current - support metric ampere only.
- IFCAnyHandle currentSIUnit = null;
- {
- currentSIUnit = CreateSIUnit(file, SpecTypeId.Current, IFCUnit.ElectricCurrentUnit, IFCSIUnitName.Ampere,
- null, UnitTypeId.Amperes);
- unitSet.Add(currentSIUnit); // created above, so unique.
- }
-
- // Electrical voltage - support metric volt only.
- {
- IFCAnyHandle voltageSIUnit = CreateSIUnit(file, SpecTypeId.ElectricalPotential, IFCUnit.ElectricVoltageUnit, IFCSIUnitName.Volt,
- null, UnitTypeId.Volts);
- unitSet.Add(voltageSIUnit); // created above, so unique.
- }
-
- // Power - support metric watt only.
- IFCAnyHandle powerSIUnit = null;
- {
- powerSIUnit = CreateSIUnit(file, SpecTypeId.HvacPower, IFCUnit.PowerUnit, IFCSIUnitName.Watt,
- null, UnitTypeId.Watts);
- unitSet.Add(powerSIUnit); // created above, so unique.
- }
-
- // Force - support newtons (N), metric weight-force, us-weight-force and kips.
- {
- bool forceConversionBased = false;
- bool forceUseDefault = false;
-
- IFCUnit forceUnitType = IFCUnit.ForceUnit;
- IFCSIPrefix? forcePrefix = null;
- IFCSIUnitName forceUnitName = IFCSIUnitName.Newton;
- string forceConvName = null;
-
- FormatOptions forceFormatOptions = doc.GetUnits().GetFormatOptions(SpecTypeId.Force);
- ForgeTypeId forceUnit = forceFormatOptions.GetUnitTypeId();
- if (forceUnit.Equals(UnitTypeId.Newtons))
- {
- // This space intentionally left blank.
- }
- else if (forceUnit.Equals(UnitTypeId.Dekanewtons))
- {
- forcePrefix = IFCSIPrefix.Deca;
- }
- else if (forceUnit.Equals(UnitTypeId.Kilonewtons))
- {
- forcePrefix = IFCSIPrefix.Kilo;
- }
- else if (forceUnit.Equals(UnitTypeId.Meganewtons))
- {
- forcePrefix = IFCSIPrefix.Mega;
- }
- else if (forceUnit.Equals(UnitTypeId.KilogramsForce))
- {
- forceConvName = "KILOGRAM-FORCE";
- }
- else if (forceUnit.Equals(UnitTypeId.TonnesForce))
- {
- forceConvName = "TONN-FORCE";
- }
- else if (forceUnit.Equals(UnitTypeId.UsTonnesForce))
- {
- forceConvName = "USTONN-FORCE";
- forceConversionBased = true;
- }
- else if (forceUnit.Equals(UnitTypeId.PoundsForce))
- {
- forceConvName = "POUND-FORCE";
- forceConversionBased = true;
- }
- else if (forceUnit.Equals(UnitTypeId.Kips))
- {
- forceConvName = "KIP";
- forceConversionBased = true;
- }
- else
- {
- //Couldn't find display unit type conversion -- assuming newton
- forceUnit = UnitTypeId.Newtons;
- forceUseDefault = true;
- }
-
- if (exportToCOBIE && forceConvName != null)
- forceConvName = forceConvName.ToLower();
-
- double forceScaleFactor = UnitUtils.ConvertFromInternalUnits(1.0, forceUseDefault ? UnitTypeId.Newtons : forceFormatOptions.GetUnitTypeId());
- IFCAnyHandle forceSiUnit = IFCInstanceExporter.CreateSIUnit(file, forceUnitType, forcePrefix, forceUnitName);
- if (forceConversionBased)
- {
- double forceSIScaleFactor = forceScaleFactor * UnitUtils.ConvertFromInternalUnits(1.0, UnitTypeId.Newtons);
- IFCAnyHandle forceDims = IFCInstanceExporter.CreateDimensionalExponents(file, 1, 1, -2, 0, 0, 0, 0); // force
- IFCAnyHandle forceConvFactor = IFCInstanceExporter.CreateMeasureWithUnit(file, Toolkit.IFCDataUtil.CreateAsForceMeasure(forceSIScaleFactor), forceSiUnit);
- forceSiUnit = IFCInstanceExporter.CreateConversionBasedUnit(file, forceDims, forceUnitType, forceConvName, forceConvFactor);
- }
-
- unitSet.Add(forceSiUnit); // created above, so unique.
- ExporterCacheManager.UnitsCache.AddUnit(SpecTypeId.Force, forceSiUnit, forceScaleFactor, 0.0);
- }
-
- // Illuminance
- {
- IFCSIPrefix? prefix = null;
- IFCAnyHandle luxSIUnit = CreateSIUnit(file, SpecTypeId.Illuminance, IFCUnit.IlluminanceUnit, IFCSIUnitName.Lux,
- prefix, UnitTypeId.Lux);
- unitSet.Add(luxSIUnit); // created above, so unique.
- ExporterCacheManager.UnitsCache["LUX"] = luxSIUnit;
- }
-
- // Luminous Flux
- IFCAnyHandle lumenSIUnit = null;
- {
- IFCSIPrefix? prefix = null;
- lumenSIUnit = CreateSIUnit(file, SpecTypeId.LuminousFlux, IFCUnit.LuminousFluxUnit, IFCSIUnitName.Lumen,
- prefix, UnitTypeId.Lumens);
- unitSet.Add(lumenSIUnit); // created above, so unique.
- }
-
- // Luminous Intensity
- {
- IFCSIPrefix? prefix = null;
- IFCAnyHandle candelaSIUnit = CreateSIUnit(file, SpecTypeId.LuminousIntensity, IFCUnit.LuminousIntensityUnit, IFCSIUnitName.Candela,
- prefix, UnitTypeId.Candelas);
- unitSet.Add(candelaSIUnit); // created above, so unique.
- }
-
- // Energy
- {
- IFCSIPrefix? prefix = null;
- IFCAnyHandle jouleSIUnit = CreateSIUnit(file, SpecTypeId.Energy, IFCUnit.EnergyUnit, IFCSIUnitName.Joule,
- prefix, UnitTypeId.Joules);
- unitSet.Add(jouleSIUnit); // created above, so unique.
- }
-
- // Luminous Efficacy - support lm/W only.
- {
- ISet elements = new HashSet